圣魔之光石吧 关注:24,554贴子:371,004

(随缘更) FE8U的Pre-Battle阶段程序解读

只看楼主收藏回复

刚好这两天很咸,索性把之前自己之前琢磨IDA的笔记整理整理。不过工程量比较大,所以可能会是年番。内容有点硬核,看不懂的话就当图一乐好了


IP属地:上海来自Android客户端1楼2021-06-25 19:53回复
    【劝退预警】
    本文内容主要围绕ASM程序展开,主要讲述从玩家回合阶段人物移动阶段(E_PLAYERPHASE的Label1)开始,至战斗开始前(Action_Combat),这段过程中涉及的函数。并且会涉及一些小的Hack程序做示例。
    全文要求较高的对FEGBA的ASM Hack能力。如果你对如何使用Event Assembler烧录程序都不了解的话,建议优先点亮前置技能再来看这个帖子。
    前置技能树点亮路径推荐:
    1.首先去看Tutorial ( https://tutorial.feuniverse.us/ );
    2.随后去看Tequila的ASM入门教程 ( https://feuniverse.us/t/gbafe-assembly-for-dummies-by-dummies/ )
    3.去看我之前发的入门教程 ( https://tieba.baidu.com/p/6918143764 )
    3.了解StanH引入的lyn组件的用法 ( https://feuniverse.us/t/ea-asm-tool-lyn-elf2ea-if-you-will/ )
    4.学习StanH关于并行程序PROC的教程 ( https://feuniverse.us/t/guide-doc-asm-procs-or-6cs-coroutines-threads-fibers-funky-structs-whatever/ )


    IP属地:上海3楼2021-06-25 20:11
    回复
      3L备用


      IP属地:上海4楼2021-06-25 20:14
      回复
        4L备用,回头一些下载链接可能会用到


        IP属地:上海5楼2021-06-25 20:15
        回复
          【0】关于武器射程的问题
          先把武器射程的修改过程作为引例。
          之所以选择这一问题为引例,一方面是因为这是我之前已经实现的功能 ( https://feuniverse.us/t/asm-item-weapon-attack-range-remake-by-mokha/ );另一方面,对武器射程的研究并不会涉及到整个系统层面的问题,我们只需要聚焦在单点上即可解决全局的问题;
          先看一下效果:


          IP属地:上海6楼2021-06-25 20:16
          回复
            先简单说一下武器射程设定的方法,用FEB就能完成:在道具窗口,“范围“这一栏,武器射程数据存储格式为min-max range:

            把上图中的11修改为13,那么西洋剑就变成了射程为1-3的武器了:

            原本的武器射程存在一个很**的问题,武器的攻击范围只可以在1(剑斧枪)、2(弓)、3(长弓)、A(雷暴)、F(钢弩)以及str/2(远程杖)这六个射程范围内选择。假如打算制作一个射程为4的弓,那么将无法正确展示对应武器的攻击范围(你可以通过在FEB中将铁弓的射程设定为44)。我们现在尝试来解决这个问题,以彻底解锁武器射程,获得任意射程的武器。


            IP属地:上海7楼2021-06-25 20:18
            回复
              展现武器射程的话涉及三个问题:
              1.角色移动后,一级菜单 (Unit-Menu, UM. 选择攻击/物品/待机)时展示攻击范围;
              2.选择”攻击“后,在武器选择菜单 (Sub-AttackMenu )展示不同武器的攻击范围;
              3.角色移动前会展示移动范围+外圈的最大攻击范围。
              其中1/2两种情况对应的是同一个函数组,我们这里就以情况1为例做讨论:
              在FEB的“菜单”一栏下可以查询到展示攻击范围的函数:

              在UM中展示攻击范围的函数位于 [0x8022C30+1] 的位置,我们将这一函数命名为”DisplayUnitStandingAttackRange”,意思显而易见。另外提一下,后续可能我也会顺手将其称为”UMAttack_Hover”之类的名称。
              用IDA对这个Hover函数分析一下,长这样:

              函数最开始的一些初始化的问题我们暂且不去关注(我想后续会提到的,如果我能写到类风花雪月的战技系统制作部分的话);中间的判定部分是在判定当前道具是魔法(Ballista,理光暗)还是武器(NoInBallista,剑斧枪弓);如果是武器的话,随后引入了两个函数, Get-UnitRangeMask [0x80171E8]与Fill-RangeMap-By-RangeMask[0x801B460];


              IP属地:上海8楼2021-06-25 20:22
              收起回复
                -> 首先看一下函数GetUnitRangeMask [0x80171E8]:
                我们拿刚才那个射程为1-3的西洋剑的ROM,用NO$GBA跑一下看看寄存器的情况:

                运行过GetUnitRangeMask [0x80171E8],返回的值是R0=07H,对应二进制是0111B。其他的武器可以自行尝试。只有铁剑的话,返回的值是0001B=01H;只有铁弓的话是0010B=02H;如果是游牧骑兵拿着长弓和铁剑,则返回值是0101B=05H。找到规律了么?二进制每一个进位的1/0分别表示角色在该距离能否攻击,同样的,如果是铁剑+铁弓的组合,那么对应的返回值就是R0=0011B=03H。0111B这样可以做逻辑与/或运算的二进制数据即为Mask (讲个代码都能提到光刻掩膜板……信科三院不分家诚不我欺……)。
                实际上,如果你再去分析GetUnitRangeMask函数的话,会发现内部有一个循环长这样:

                这个循环是在遍历整个角色物品列表,并通过GetWeaponRangeMask [0x80170D4]函数,对角色能使用的每个武器获取对应的Mask。


                IP属地:上海9楼2021-06-25 20:24
                回复
                  GetWeaponRangeMask [0x80170D4]才是重头戏:
                  这个函数属实有点长,长这个样子:

                  可以看出来当年I$的打工人在处理Mask的时候的偷了懒的:反正我知道射程只能在六个数字里选,所以就用一个Switch结构分了6种情况,分情况直接录入Mask了。
                  据此,我们提出针对GetWeaponRangeMask的解决方案:
                  首先写一个函数Num2Mask,使该函数获得如下输出:Num2Mask(3)=0111B=07H;Num2Mask(2)=0011B=03H;Num2Mask(1)=0001B=01H;
                  将Num2Mask(MinRange-1)的运算结果取反,并将其与Num2Mask(MaxRange)做“与“运算。例如,Min-Max=2-2:Num2Mask(min-1)=0001B取补码为1110B;Num2Mask(max)=0011B;1110B && 0011B=0010B,即为我们所需要的WeaponRangeMask。对应的程序为:

                  至于Num2Mask函数,由于我并不是计算机类学生,所以码起代码来肯定达不到规范的程度,所以我仅仅贴出来供大家参考好了,就不去讲解实现过程了:


                  IP属地:上海10楼2021-06-25 20:26
                  回复
                    -> 解决了GetMask的问题之后,接下来是Hover中的另一个函数,FillRangeMapByRangeMask;
                    回到Hover函数,我们看一下这个函数的输入变量:

                    前半段程序中给R4录入了值,R4=gActiveUnit=0x3004E50,也即输入值为:
                    FillRangeMapByRangeMask :R0=RangeMask;R1=[gActiveUnit]=ptr-CurrentUnit。
                    随后进入这一函数中,可以发现它也是用了一个恼人的Switch结构做分叉。我们取其中的一种情况(Mask=0101B,同时持有铁剑/长弓)举例:


                    其中,MapAddInRange函数就不再做解读,直接给结论:

                    上述过程这一这样解读:首先填充半径为1的圈;然后挖去半径为0的圈;在填充半径为3的圈,再挖去半径为3的圆,像这样:

                    这样便成了铁剑+长弓对应的半径为1和3的圆环组成的攻击范围。


                    IP属地:上海11楼2021-06-25 20:30
                    回复
                      参考源程序的思路,我们也可以去根据任意类型的Mask来做FillMap的操作了:
                      我们的思路是这样:
                      先做一个函数,把原本的Mask(以10110B为例)分解成10与110,然后输出分解剩下的部分10B,并将分解出的低位部分110的min/Max Range输出,即满足:

                      然后利用上述函数做成一个Loop,对Mask不停分解,直到整个Mask全部分解完成:


                      IP属地:上海12楼2021-06-25 20:31
                      回复
                        以上便是整个程序修改的思路。另外,在角色移动环节的外圈攻击范围的展示过程和我们之前提到的,移动结束后的攻击范围展示过程还不太一样。涉及到另一个函数FillMapAttackRangeForUnit(0x801ACBC),整体思路和FillRangeMapByRangeMask没有差异,这里便不再详细展开了。可以自行用IDA把这个函数展开看一下。
                        上述程序在这里下载:
                        链接: https://pan.baidu.com/s/1Ct-2llLRhZpGfQBvNJ41Qw 提取码: crju


                        IP属地:上海13楼2021-06-25 20:39
                        收起回复
                          这是用美版做的程序吗?和日版地址不是会冲突吗?


                          IP属地:四川来自Android客户端15楼2021-06-26 11:12
                          收起回复
                            【1】战斗阶段总览

                            整个Battle阶段可以分成五个部分,
                            1. PlayerPhase阶段:从玩家回合(PlayPhase Proc)开始,到角色移动,止于角色菜单跳出;
                            2. Menu阶段:从玩家在主菜单进行选择开始,进入武器菜单,止于目标选择;
                            3. TargetSelect阶段:从目标选择开始,止于目标选择结束;
                            4. ApplyAction阶段:系统回到PlayerPhase Proc并判定当前进入战斗阶段;
                            5. Battle阶段:分别录入Attacker与Defender的数据,并判定战斗过程与战斗结果;
                            6. Anime阶段:展示战斗动画;
                            7. Save阶段:将战斗结果保存会角色数据;
                            我们将上述7个阶段做一下分类,依照返回gProc_EPlayerPhase的时间节点,将战斗前的阶段分为包含1、2、3部分的PlayPhase阶段;在战斗结算结束前的4、5阶段分为PreBattle阶段;战斗结算完成后的6、7部分分为PostBattle阶段。
                            掐头去尾,后续我会主要介绍Menu、TargetSelect、ApplyAction-Battle这些部分。PlayerPhase Proc与Anime部分相对而言比较繁琐,涉及的内容太多,并且里面有一些我也没有搞明白的部分,如果未来想起来填坑的话再补上吧。


                            IP属地:上海16楼2021-07-07 12:57
                            回复
                              上述地址针对FE8U,要想转换成FE8J的话,需要参考MisakaMikoto@FEU制作的Function Lib完成美版对日版函数的地址映射:https://feuniverse.us/t/function-library/4632


                              IP属地:上海17楼2021-07-07 13:04
                              回复