Author Topic: [FF7] Battle Operations - Some clarifications required  (Read 337 times)

dangarfield

  • *
  • Posts: 23
    • View Profile
I have a few questions about battle operations that would be great to get clarity with on:

Note: DWORD = 4 bytes. But I'm just using 1BYTE, 2BYTE, 3BYTE, 4BYTE notation because it's clearer for the non-windows programmer in me.

1. Does a stack only exist for each single 'turn' of an actor?
    Eg, is it correct to say:
    - A stack only exists for an actor's turn and is only processing that actor's commands
    - Before an actor's turn, the stack is cleared
    - Multiple 'attacks' etc, can be chained, the actor's turn only ends when it hits the END op code (0x73). The last position of the last action is lost, eg always go from 0x0000 of main script etc for each main turn
    - All local variables (0x0000 - 0x03FF) (and global variables) are persisted between each actor's turns. Local variables are set to 0 at beginning of battle only

2. Types and what is pushed on stack from address. 2x mainly
Is it correct to say the following when invoking some commands:

LOAD ADDRESS: 1x commands
    The following commands are effectively all the same:
Code: [Select]
    - 10 03 83
    - 11 03 83
    - 12 03 83
    - 13 03 83
  ->   push address '0x0383' to the stack ALWAYS as a 2BYTE even though the type '10' -> 1bit as 1BYTE, '11' -> 1BYTE, '12' -> 2BYTE, '13' -> 4BYTE.
  ->   stack:    12 03 83
    eg, is the x in `1x` always a 2 in what is added to the stack? is does the x remain as the same in the op code, but the engine just knows that it will always be a 2BYTE value regardless of what the 1x's x value is?

LOAD VALUES: 0x commands

    If arg is 0x0000-0x3FFF, the values are fetched and pushed as 0x variable, eg: (assume arg 0x0123 has value of 0x2345)
Code: [Select]
        Command -> 00 01 23 -> get bit value of 0x2345 store as 00 (bit) -> stack: 00 01 (0x2345 first bit = 1)
        Command -> 01 01 23 -> get byte value of 0x2345 store as 01 (byte) -> stack: 01 45 (0x2345 first byte 0x45)
        Command -> 02 01 23 -> get 2byte value of 0x2345 store as 02 (2byte) -> stack: 02 23 45
        Command -> 03 01 23 -> get 4byte value of 0x2345 store as 03 (4byte) -> stack: 03 00 00 23 45

    If arg is 0x4000-0xFFFF, the values are for ALL objects in battle eg: (assume only player 3 & enemy 1, with current HP of 0x567 and 0x789 respectively)
Code: [Select]
        Command -> 00 41 60 -> get bit value for each actor store as 20 (bit) -> stack: 20 00 00 01 00 01 00 00 00 00 00
              or is this a simple bit mask in a 2BYTE, eg 0xb0000010100 -> 0x0014 (eg, 20 00 14, up to a potential max of 20 03 FF)
        Command -> 01 41 60 -> get byte value for each actor store as 21 (byte) -> stack: 21 00 00 67 00 89 00 00 00 00 00
        Command -> 02 41 60 -> get 2byte value for each actor store as 22 (2byte) -> stack: 22 0000 0000 0567 0000 0789 0000 0000 0000 0000 0000
        Command -> 03 41 60 -> get 4byte value for each actor store as 24 (4byte) -> stack: 23 00000000 00000000 00000567 00000000 00000789 00000000 00000000 00000000 00000000 00000000

3. Global values bit masks

Take 0x20A0 - Bit mask of actors indicating actors the current actor considers as enemies
Assumed command - 02 20 A0 -> Push value to stack of 0x20A0, store value as a 2Byte value
Assumed enemies - All 3 players only
Is the result value added to stack 0b0000000111 (eg 0x0007) -> 20 00 07
 or because it's a x=2 command, each actor has a 2 byte value, eg:
 -> 22 0001 0001 0001 0000 0000 0000 0000 0000 0000 0000



4. Example reversing walkthrough

I'm sure that I will have a myriad more questions, but I would love to able to fish for myself. Would anyone be able to make a brief video / tutorial to show how I can step through this in debugger? I'm also willing to document and video the process for others once I know.

I can code and have decomposed most field and scene assets, but have very little experience with windows based reverse engineering (of running applications), but I think that a smaller tutorial would benefit a lot of people.
A good example would be: Stepping through the Grand Horn's AI in game, op by op, showing the ops and the resultant stack values (Grand Horn appears to be the simplest / shortest AI).

Thanks as always.

Note: I'm actually writing the whole of FF7 in a web based engine. All fields and menu engines are working. I've extracted and created models for all battle models etc, pretty much extracted in a readable way most field assets, now, I'm interested in making the battle engine. All is promising and it will work, just need some pointers in the battle stack.

Additonal links that I've found useful for future reference:
https://pastebin.com/raw/mjfRFNsZ
https://forums.qhimm.com/index.php?topic=3290.msg45951#msg45951
https://forums.qhimm.com/index.php?topic=18668.75
https://wiki.ffrtt.ru/index.php/FF7/Battle/Battle_Scenes/Battle_AI_Addresses
https://wiki.ffrtt.ru/index.php/FF7/Battle/Battle_Mechanics
https://wiki.ffrtt.ru/index.php/FF7/Battle/Battle_Scenes#AI_Data
https://wiki.ffrtt.ru/index.php/FF7/Battle/Battle_Scenes/Battle_Script
https://faqs.neoseeker.com/Games/PS4/final_fantasy_vii_dynamixdj.txt
« Last Edit: 2022-12-16 12:31:33 by dangarfield »

nfitc1

  • *
  • Posts: 3003
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FF7] Battle Operations - Some clarifications required
« Reply #1 on: 2022-12-19 13:45:07 »
Lot to unpack here.

1. You are correct on all counts. Keep in mind the game will process an entire script in a single frame. Rather, it will not draw the next actor/background animation frame until the current script is complete. For the PC that means halting the entire battle, for the PSX that just means halting the actor models. Only one script is run per frame just by design. When an enemy's timer is full, it will run its main script after the current active battle action is complete. If everyone is idle then the script will execute immediately. Enemies get queued on their order to execute their scripts based on these timers. Counter action scripts will run in battle index order (Actor 4, Actor 5, etc). General counters first, then physical/magic counter script depending on the type of damage that they took.

2. This is actually an excellent question and one I didn't look into much. NOT masking a bit range has limited uses so I never saw a 02 data type on the stack. However, keep in mind that these are word arguments. They are represented as, say,
Code: [Select]
01  4160 in most disassemblers it will look like
Code: [Select]
01 60 41 in a hex editor.

3. There is a distinction between global values and global variables. It's might be more accurate to call the 2XXX addresses to be the actions' metadata. The 20A0 address contains a bitmap of all the active targets the current script owner considers an opponent. This does not include dead/nullified opponents. There is only room for a two-byte value here so an enemy will find the value of 0007 during their turn (assuming all playable characters are alive and not petrified). This is set at the beginning of script execution and can be modified, though that's not the desired function.

4. I'm not great with video editing, but I have done a considerable amount of reversing the scripts and can tell you how all of it works. We can keep the conversation here rather than DMs or Discord. I just don't check those as often.

dangarfield

  • *
  • Posts: 23
    • View Profile
Re: [FF7] Battle Operations - Some clarifications required
« Reply #2 on: 2022-12-19 15:56:18 »
Thanks for this.

1. Thanks, yep, I've got all that. I'm happy.
2. Yep, agree with the notation, that's my bad, but I think you get the point with the logical ordering of args. I've also noticed since writing this that the op code actually comes last, so yes, it.

Regarding not seeing 02 data type on the stack, I 'think' I can read this from the scripts, eg, an 0x81 (RWRD - Random) pushes an 02 type onto the stack. I think I can read a lot in the Terry Ferguson docs about 02s being added, but I might just be misunderstanding, https://forums.qhimm.com/index.php?topic=3290

Either way, I think what I mean by this is that I'm not sure what to push onto my 'stack reconstructed' if I get a '00 41 60', but I'll go with the a stack value of a 10-digit binary saved as a word. We'll see, but this is really for me. I've tested a number of AI scripts so far and it seems to give me the results I'm looking for.

3. I think this is the same as above, but it's throwing me off because it's a 22 (eg, x=2 -> 2 byte return type), but if I understand what you're saying, it should always be (in the instance of 2xxx commands) a single 2byte array returned, even though the x=2 'implies' a 2byte per record. I'll have to find an example specifically look into it.

4. Will do, much appreciated.

I do, however, have another small question:

5. How does the preScriptSetup execution affect the stack?

Eg, is an actor's stack a singleton or is it a new instance between different invocations of different script types.

During the execution of a stack, there is a 0x92 command with an argument less than 0x20 that results in the triggering of all active actors' preActionSetup scripts. Is it fair to say the following:

A)
- After the enemy turnTimer indicates that a enemy has a turn, the enemy 'turn' is added to a prioritised turn queue
- When it is the enemy turn get priority it:
- Executes the 'main' script, which may or may not result in a 0x92 command (with < 0x20 arg), if it does
- All actors sequentially run their preActionSetup scripts, in their own 'new stack instance', most importantly, the current actor has two stacks in this point, or at least, there is an index maintained that allows it continue after the preActionSetup script(s) have finished
- The execution and animation of the enemy 0x92 happens
- The script picks up from where it was until it reach an 0x73 end code

OR B)
- After the enemy turnTimer indicates that a enemy has a turn, the enemy 'turn' is added to a prioritised turn queue
- When it is the enemy turn get priority it:
- Executes the 'main' script, which may or may not result in a 0x92 command (with < 0x20 arg), if it does, effectively this is the end of the enemy stack turn because:
- All actors sequentially run their preActionSetup scripts, thus destroying the one stack
- The execution and animation of the enemy 0x92 happens
 (eg, the 0x92 is effectively equal to the 0x73 end code, and nothing can happen in the stack after) - I really don't think this is the case, Diamond Weapon queues commands after the 0x92

OR C)
- After the enemy turnTimer indicates that a enemy has a turn, the enemy 'turn' is added to a prioritised turn queue
- When it is the enemy turn get priority it:
- All actors sequentially run their preActionSetup scripts
- Executes the the 'main' script, which may or may not result in a 0x92 command (with < 0x20 arg), if it does:
- The execution and animation of the enemy 0x92 happens
- The script picks up from where it was until it reach an 0x73 end code

OR Something else. I'm really looking for a logical view here.

Much appreciated!

Update: I'm pretty sure that it's the first as I get very similar results to what I see in game here.
« Last Edit: 2022-12-19 16:06:01 by dangarfield »

nfitc1

  • *
  • Posts: 3003
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FF7] Battle Operations - Some clarifications required
« Reply #3 on: 2022-12-19 18:37:24 »
5. There's really only Diamond Weapon that uses this script so that's all I have to go on. I don't know of a case where there are other enemies in the battle that have preActionScripts, but here's what I can glean from my limited testing:

- After the enemy turnTimer indicates that a enemy has a turn, the enemy 'turn' is added to a prioritised turn queue
- When it is the enemy turn get priority it:
- Executes the 'main' script, which may or may not result in a 0x92 command (with < 0x20 arg)
- The script continues processing until it reaches the end code
- Before executing any action, all TARGETS of the queued action run each of their preActionSetup scripts prior to the action performing damage calculation/status change. Multiple scripts are never run asynchronously and damage calculations wait until all preActionSetup scripts are resolved.
- Damage calculations/status changes are performed (though not applied immediately) and animation scripts of all queued actions are executed.
- postActionScripts are run sequentially on all active actors (not just targets).
- The previous three steps are repeated until the action queue is empty.

dangarfield

  • *
  • Posts: 23
    • View Profile
Re: [FF7] Battle Operations - Some clarifications required
« Reply #4 on: 2022-12-19 19:20:21 »
Thanks my friend. There are 11 enemies that use them, but that's all fine. What I have seems to work pretty well so far.

Much appreciated.

I'm sure I'll be back, specifically with some questions about the animation / effects texture mechanics and lookups, but I saw that Akari had some nice stuff worked out already - https://github.com/Akari1982/q-gears_reverse/blob/master/ffvii/documents/battle/models/data/dump_cloud.lua - But I'm nowhere near there yet.

Thanks again!