火焰之纹章吧 关注:289,745贴子:4,669,327

回复:【研究】关于火纹GBA三作哈夫曼编码的新发现[改版相关][多图]

只看楼主收藏回复

  再看看输出结果,初步成功。


IP属地:广东19楼2015-03-06 19:18
回复
      虽然依照汇编写出了解码程序,但是,因为不懂得其中的原理,这就导致我研究编码程序时遇到了很大的困难。因为这个解码程序无法直接逆向成编码程序,因为指针是不知道自己的地址的,通过搜索的方式逆回去效率太低。我一直在思考到底这样一步一步地读取下去是什么意思,还有它判断 r2 & 0x1 是0还是1的意义所在,我甚至想过是不是判断奇偶数。3个多小时过去了,还是没有进展。我开始怀疑这是一个不可能完成的事,甚至萌生放弃的念头。但是一想到,汉化版火纹的rom中也是用哈夫曼编码压缩,这是可以实现的,于是我坚持了下去。我之后意识到这应该就是“哈夫曼编码”的意义所在了,我又从哈夫曼编码的原理开始学习。


    IP属地:广东20楼2015-03-06 19:19
    回复
      2025-07-29 22:12:48
      广告
      不感兴趣
      开通SVIP免广告
        其实我在看到无聊之士的帖子之前就已经去找过一些哈夫曼编码的原理以及程序源码来看了,但是百度来的东西基本都是抄来抄去,只讲了最表面的原理,我根本无法把它们和火纹的rom内的数据联系在一起。浏览了很多资料后,终于,我在Java学习室的这篇"学习哈夫曼压缩与解压缩(01)"(网址见附录)中看到:
        “解压缩过程与压缩过程相反,从压缩文件中读一字节(八位)缓存,然后一位一位的解码,直到读到一个哈夫曼编码,用其对应的字节数据替换写入解压文件,这样边读边解码边写,直到文件末尾. ”
        我如梦初醒,一位一位的解码!上面我贴出的程序中的(r2 & 0x1)不就是获取当前字节的最低一位是么!因此我确定我的方向是正确的。


      IP属地:广东22楼2015-03-06 19:22
      回复
          该文中还说到:
          “在树中令所有左分支取编码为 0 ,令所有右分支取编码为1。将从根结点起到某个叶子结点路径上的各左、右分支的编码顺序排列,就得这个叶子结点所代表的字符的二进制编码(哈夫曼编码)”,
          也就是说哈夫曼编码解码时,从字节最低位开始读取,是0就读左节点,是1就读右节点,直到没有子节点。这不就刚好对应上文中的“直到r6是负数,判断是码表编码的话跳出内层循环”么!


        IP属地:广东23楼2015-03-06 19:24
        回复
          楼主,好强大


          IP属地:辽宁24楼2015-03-06 19:25
          回复
            刚才吃饭去了,现在继续。
              看到这里,我仿佛发现新大陆般地从床上爬起来(作为 放假中|床居动物|大一狗|准程序员 在床上打码很奇怪么?)抓起纸和笔画起火纹的哈夫曼树。结果发现火纹的哈夫曼树似乎不是标准的最优树,因为它的根节点左右支都有子节点再向下拓展。但至此我已经明白了:
              火纹是在查字时通过指针的方式动态构建哈夫曼树,用指针连接父节点和两个子节点,而表示码表编码的节点都没有子节点!这说明火纹的哈夫曼树是不变的,不会因为要压缩的文字的不同而变化!既然是这样,那我们根据这个规律自己构建一个哈夫曼树,不就可以直接通过树来编码和解码了么,还能提高查字的效率。


            IP属地:广东25楼2015-03-06 19:41
            回复
              仁兄这么有才,不如来fe制造吧转转,那里都是修改者,想看这些研究的一定很多


              26楼2015-03-06 19:41
              收起回复
                  补充一点,18楼的图中程序的36行出现的0x6E0,rom在此处存储码表编码的指针,举个栗子,在圣魔中是
                0x08147DD4,我们来看看这个位置到底存了什么:

                看到这么多的FF你应该也会和我一样联想到上文中出现的FFFFC290,
                再对比一下这两张图,你是不是感觉自己发现了什么?

                还有48到57行,再看一下16楼,提到了0x0014d088处又存储了另外一个重要的指针——字符频率结尾的指针,这个指针就是r4的初始值,圣魔中是0x0014d084,内圈循环的第一次用的就是这个值,例如先假设r2 & 0x1 不是0,就会在(r4 + 2 = 0x0014d086)处读取值存入r6,此时r6 = 0x14AB,
                r4 = r5 + (r6 << 2) 就是 r4=r5+r6*4,而这里的r5是码表编码的指针(首地址),然后再在得到的新地址r4处取新的r6,是负数的话就说明找到的是码表编码,不是负数的话就再接着内循环,如果是0的话(上图中的0000ffff转置后就是0了),就作为结束符。退出两层循环。


                IP属地:广东27楼2015-03-06 20:05
                回复
                  2025-07-29 22:06:48
                  广告
                  不感兴趣
                  开通SVIP免广告
                  拜读一下大神的文章 有好多看不懂


                  IP属地:美国28楼2015-03-06 20:09
                  收起回复
                      五、完善程序
                      思路:
                      1. 构造哈夫曼树:
                      把 0x14D084 和 0x14D08C 处的两个数值分别作为哈夫曼树根节点的左右节点并向下拓展,直到遇到值为负数的节点,这样就保证了码表编码都是叶子节点了(没有子节点的称为叶子节点),于是想到用递归来实现,又是漫长的调试...
                      2. 编码
                      哈夫曼构造好后后面的事就容易多了,但是我还是遇到了不少麻烦。编码是从叶子节点到根节点,因为哈夫曼编码是按照二进制位来存储的,也就是01串,写满8位后再作为一个字节输出。码表编码是左节点的话就写入0,否则写入1,但是有个问题,有两个写缓冲的过程,两次的顺序处理有点微妙。我不知怎样用文字描述。一开始我把顺序弄反了两次,最后还是靠“学习哈夫曼压缩与解压缩”的第二篇文章(地址见附录)才得以成功。
                      2. 解码
                      前面提到了一些,解码是从根节点到叶子节点,把当前字节按位读取,读到0就读左节点,是1就读右节点,直到没有子节点。而叶子节点的字符就是当前子的码表编码了,另外读完八位还没读出一个字的话就会再读一个字节继续读。这就是哈夫曼编码是不等长编码,能实现压缩的原因所在。


                    IP属地:广东29楼2015-03-06 20:10
                    回复
                      这是改后的程序,但是还没加注释...第一张是构建哈夫曼树,第二章是编码,输出哈夫曼编码的字节数组,第三张是解码,输出码表编码数组。




                      IP属地:广东30楼2015-03-06 20:15
                      回复
                        结束语:
                          历时三天,我终于独自解开了火纹的哈夫曼压缩算法,当然多亏了写那些资料的作者们还有无聊之士给出的汇编追踪记录。而且可以肯定已经早就有前辈找到了这个算法,只是他们没有公布(至少我还没在网上看到),汉化组也是写出了算法才能使用压缩的文本汉化,无聊之士在贴子中说的方法,按字节转置二进制的位,说明他是知道这个算法的。
                          作为一个业余爱好者,这个过程中我没有问别人,而是用搜索引擎在网上找各种资料来看,对这点我比较自豪。
                          我想到了既然得到了算法就干脆做一个手机版的静态修改器吧,电脑端已经有L大做的Night Mare的圣魔之光石修改组件了嘛。后面近一周的时间我都一直在做这个,结果功能添加越多就要修复越多的bug,个人开发真的很累啊...
                          明天我就要回学校了,时间太仓促,所以这篇文章写的很乱。本来我是想写的详细一点,每个方面都涉及一点,但结果越写越复杂,写到后面我都不知道怎样用语言来表达了。所以本文只是把过程大概过了一遍,想了解更多细节的话可以私信我,但是去学校之后我应该没多少空闲时间了,大一下学期的课表满得让我内牛满面。
                          不知道这个修改器我什么时候才能完善好发布,但愿不会成坑...


                        IP属地:广东31楼2015-03-06 20:17
                        回复
                          附录:
                          1. 无聊之士的帖子地址:http://bbs.fireemblem.net/read.php?tid=217366
                          2. "学习哈夫曼压缩与解压缩(01)"网址: http://www.java3z.com/cwbwebhome/article/article8/83578.html?id=4721
                          3. "学习哈夫曼压缩与解压缩(02)"网址: 把上面结尾的1改成2


                          IP属地:广东33楼2015-03-06 20:22
                          回复
                            全文完。


                            IP属地:广东34楼2015-03-06 20:23
                            回复
                              2025-07-29 22:00:48
                              广告
                              不感兴趣
                              开通SVIP免广告
                                本来把没写详细的成果发出来是不负责任的,但是明天要去学校了,接下来的一学期可能都没有时间来写,所以就提前发出来了,大家见谅。
                                就全当是楼主的自娱自乐吧,看不懂很正常,我原来也是什么也不懂,而且涉及一些计算机专业的知识。大家酌情看看就好...


                              IP属地:广东35楼2015-03-06 20:27
                              回复