• Welcome to Final Fantasy Hacktics. Please login or sign up.
 
April 23, 2024, 10:50:44 pm

How do ASM Tables work?

Started by Choto, November 30, 2011, 02:10:23 pm

Choto

Ok, so i've noticed various hacks use tables, and I know square has its own tables in the game, but i'm wondering how they work. I was looking at Xifanie's compressed table proposal thread which shed a little bit of light on things, but they seem like they range from simple to potentially mindboggling.

It seems as though they store static data to be referenced throughout the game (Weapon Proc/Crit chance). Or, In the ARH's case, they store different conditions to be checked against the current conditions of the unit and allow X ability to be used if the conditions are the same. I guess i'll list my questions

1. Can they store dynamic data, or have their data changed in game without overwriting the values that are initially entered?

2. What exactly is the pointer table? I'm assuming it functions like unit data pointers?

3. Do the categories vary in length based on the number of subcategories (Parameters?) held therein?

4. Are the rows simply the number of offset rows taken up by the table?

5. Is this also how the game stores all of the data of each job, skillset, item stats, etc.?

Now would initializing the table in game look something like this? I'm using a situation where one unit is attacking another, and how the game would load the new chance for a critical hit. This is just how I see it in my head...

...
Load item ID
Load item WP
etc.
...
Jump to table (Weapon crit chance hack)
Look up crit chance based on Item ID, store in some register
// Return from table with weapon Crit chance
...
Jump to critical hit routine
// Use weapon crit instead of game's default crit chance
....

Any clarification would be much appreciated. Thanks!

formerdeathcorps

November 30, 2011, 10:23:28 pm #1 Last Edit: November 30, 2011, 10:30:20 pm by formerdeathcorps
Square's tables come in a few kinds:

1) Data tables.  These are static and hard-coded.  They are only read from.
2) Address tables.  These are like data tables but are used to determine where to jump for more arbitrary routines (like the one determining all the reactions by type).
3) Temporary tables, or what is commonly known as RAM tables.  These are modified in actions in FFT and go in a hierarchy.  By no means is the following a list of all of them, just the ones I and SA have found.

a) BATTLE RAM.  These are changed directly by battle actions and are the basic reference for all mechanics.
i) Permanent unit RAM stores stuff about things that only change by actions in battle.  (HP is stored here.)
ii) Temporary unit RAM stores stuff about the current action as it affects that unit only.  (HP DMG is stored here.)  Note that this is still arranged to follow the Perm. Unit RAM of any given unit.
iii) Action RAM stores data about the current action in question (like hit chance, WP, XA...)  This functions like the "global" version of Temp. Unit RAM but these two do not share data.
iv) AI RAM stores data about the moves the AI is currently considering.  This only changes when the AI controls a unit or when actions occur (like HP DMG).  To decide one action, the AI will sort through some sections of these tables 60+ times.

b) SCUS RAM.  These are changed by actions that affect the permanent state of your units (equip/unequip/deaths/dismissal...) or party (gaining gil...)  Battle conditions do simultaneously update this table as well (so broken items are removed from BATTLE mechanics equips and SCUS unit equips and inventory), but some things are dealt with in-between (like permanent brave changes).  The code for the latter is in REQUIRE.OUT but much of it references BATTLE.BIN.
i) Permanent SCUS tables contain stuff like system time, number of deaths, items in inventory, etc.
ii) Loading SCUS tables contain essentially duplicates/compressed versions of BATTLE tables.  Sometimes, these are actually on the stack (0x1F????).  These are used in-between battles to transfer data and are usually cleared afterwords.

c) WORLD RAM.  I don't know much about this, so I'll let Pokeytax post what he knows.  I'll just say that many of the screens dictating stuff like job change or EXP gain have RAM tables in WORLD.BIN.

Since most tables have initial values, these are hard-coded by the initialization routines in SCUS.  Indeed, many of these routines also are the ones generating the SCUS loading tables.  FFT tables are of fixed size and point to fixed locations in RAM.  We do not have dynamic offsets.

To give an example of hierarchy and data flow:
1) Loading SCUS routines generate temp table (say...unit ID), which is read from ENTD or SCUS unit data table.
2) Values from temp table are translated to another temp table to determine the right values for unit in question.
3) These values are loaded onto BATTLE permanent unit RAM.
4) When this unit attempts an attack, BATTLE permanent unit RAM is read for say...XA and WP and that is loaded to action RAM.
5) ACtion RAM data then performs math which yields the DMG.  This is then loaded to the Temp. Unit RAM of the specified unit in question.
6) If action is not committed, erase the above assignation.  If DMG goes through, update permanent unit RAM with data from Temp. Unit RAM.  If this triggers condition...say, KO and is on player side, trigger change in SCUS permanent RAM.

Most custom tables are static tables.  ARH and ALMAScript are the only ones that try to use a non-ASM language, and ALMAScript is the only one that uses symbols and a decoder for its logic in a non-binary manner.  It's also why these hack are very large.
The destruction of the will is the rape of the mind.
The dogmas of every era are nothing but the fantasies of those in power; their dreams are our waking nightmares.

Choto

Thanks again for this response FDC, didn't make much sense the first time I read it but now its alot clearer. So the how do almascript and ARH use symbols and logic? How do you translate that to ASM language?


Choto

I have a couple more questions to add to this. I was checking out the formula hacking page to look at potential for expansion of status effects and a couple things popped up.

1. For values that go up in the format: 0x01, 0x02, 0x04, 0x08...etc, can values be extrapolated upward to include whatever the next value would be above 0x80, in order to create more flags?

2. I'm assuming blank flags are either not yet known, or not used. I'm sure its touchy ground to know FOR SURE if something is not used or not, but can these be used for additional flags barring any issues? ie: Zodiac sign

0x0009=

    * 0xF0 =
    * 0xE0 =
    * 0xD0 =
    * 0xC0 = Serpentarius (neutral to all signs)
    * 0xB0 = Pisces
    * 0xA0 = Aquarius
    * 0x90 = Capricorn
    * 0x80 = Sagittarius
    * 0x70 = Scorpio
    * 0x60 = Libra
    * 0x50 = Virgo
    * 0x40 = Leo
    * 0x30 = Cancer
    * 0x20 = Gemini
    * 0x10 = Taurus
    * 0x00 = Aries

Also, why do these values go up by 10 instead of 1? Can unused integers be used as additional flags? ie: 0x11-0x19

3. Immediately after the status lists are

0x005D Poison CT
0x005E Regen CT

Since most people prefer poison to be infinitely lasting, could 0x005D be used as another list of status effects? I'm assuming you would have to break whatever feature decrements poison CT each turn in order for this to work. 2 birds with one stone if its possible.

4. What table do the offsets from the beginning (In formula hacking) down to the temporary battle offset belong to? Are these values loaded from the SCUS "Permanant" (out of battle) stat table? Are these offsets also used to modify the out of battle stat table in order to mark changes? ie: Brave changes, broken/stolen equipment, etc.

5. For a given offset, what is the maximum value that can be assigned? Is this restricted simply by how the game uses the offset?

Glain

1. No, that would go beyond the bounds of the byte. That is, you've read in a byte (8 bits), which is a list of bits (flags), like so:

00100110

Binary place value uses powers of two, starting from the right. The last digit is the ones digit (2^0=1=0x01), the next to last is the twos digit (2^1=2=0x02), then the next is the fours (2^2=4=0x04), etc, up to (2^7=128=0x80). That's all the space you have in one byte.

This value would be read in hex as 0x20 + 0x04 + 0x02 = 0x26; a bitwise AND operation on that value and either of 0x20,0x04,or 0x02 would result in a non-zero value, which would indicate the flag is set.

This isn't even an assembly trick; C/C++/C#/Java/VB/etc refuse to allocate any memory of less size than one byte... if you ask for a boolean (true/false) value, you get back an entire byte, so if you want true flags that only take up one bit, you have to do it this way even in high level code.

2. I don't believe the Zodiac value contains any flags; it's just a straight-up integer value, so it can only be one zodiac sign at once. I don't know why they counted up by tens; that's probably arbitrary. You could make different values mean different things but you'd have to hack the game to recognize that.

3. Yes, probably. If you freed up that byte, you'd have space for 8 flags. You'd have to hack the game code to recognize their new purpose, though.

4. That's for in-battle unit data (BATTLE RAM), which itself is probably loaded from the SCUS data, yes (at least for your party; the enemy's data would come from the ENTD or be generated by SCUS/BATTLE code). I don't know all the details of exactly how it's loaded in; it should start at 0x1908cc with the first enemy unit, and IIRC Ramza's is at 0x1924cc; check the FFT Gameshark handbook (In-battle codes) for details of which units the addresses correspond to.

5. It depends on the size of the variable and whether it's treated as signed or unsigned. For the most part, we're dealing with unsigned bytes, so your maximum value is 255 (0xFF). If the number can be negative then the minimum value is -128 (0x80) and the maximum is 127 (0x7F).

For signed integers, we're using two's complement binary values.

Two bytes: Unsigned: Max = 65535 (0xFFFF); Signed: Max = 32767 (0x7FFF) Min= -32768 (0x8000).

Most values are unsigned. If you look through the list, you'll see certain values that have gaps of two, like 0x0A, 0x0C, etc, which correspond to ability IDs; ability IDs take up two bytes, thus why there's a gap of two between them.
  • Modding version: Other/Unknown

Choto

Quote from: Glain on January 21, 2012, 11:40:48 am
1. No, that would go beyond the bounds of the byte. That is, you've read in a byte (8 bits), which is a list of bits (flags), like so:

00100110

Binary place value uses powers of two, starting from the right. The last digit is the ones digit (2^0=1=0x01), the next to last is the twos digit (2^1=2=0x02), then the next is the fours (2^2=4=0x04), etc, up to (2^7=128=0x80). That's all the space you have in one byte.

This value would be read in hex as 0x20 + 0x04 + 0x02 = 0x26; a bitwise AND operation on that value and either of 0x20,0x04,or 0x02 would result in a non-zero value, which would indicate the flag is set.

This isn't even an assembly trick; C/C++/C#/Java/VB/etc refuse to allocate any memory of less size than one byte... if you ask for a boolean (true/false) value, you get back an entire byte, so if you want true flags that only take up one bit, you have to do it this way even in high level code.



Ahh duh, I shoulda known that. Regardless, thanks for the explanation!

Quote from: Glain on January 21, 2012, 11:40:48 am
2. I don't believe the Zodiac value contains any flags; it's just a straight-up integer value, so it can only be one zodiac sign at once. I don't know why they counted up by tens; that's probably arbitrary. You could make different values mean different things but you'd have to hack the game to recognize that.



Hmm, interesting. So if you were to nuke the compatibility system, you'd be left with 255 free flags to use for some kind of function. Then what decides wether to use the address as a series of flags (0x01,0x02,0x04) rather than just straight integers? Obviously, you can use multiple values in a flag system and only one with an integer system, but i'm not seeing why.

Quote from: Glain on January 21, 2012, 11:40:48 am
3. Yes, probably. If you freed up that byte, you'd have space for 8 flags. You'd have to hack the game code to recognize their new purpose, though.



Seems like a great tradeoff, I wonder how painful adding/recoding would be. I heard SA was working on status effects... what exactly is he examining with them?

Quote from: Glain on January 21, 2012, 11:40:48 am
5. It depends on the size of the variable and whether it's treated as signed or unsigned. For the most part, we're dealing with unsigned bytes, so your maximum value is 255 (0xFF). If the number can be negative then the minimum value is -128 (0x80) and the maximum is 127 (0x7F).

For signed integers, we're using two's complement binary values.

Two bytes: Unsigned: Max = 65535 (0xFFFF); Signed: Max = 32767 (0x7FFF) Min= -32768 (0x8000).

Most values are unsigned. If you look through the list, you'll see certain values that have gaps of two, like 0x0A, 0x0C, etc, which correspond to ability IDs; ability IDs take up two bytes, thus why there's a gap of two between them.



This clarifies things quite well. Its tough thinking in hex/binary after 24 years of decimal. So then HP and max HP allocate for up to 65535 (Which makes sense for ??? monsters), and I'm sure this is answered somewhere in the archives, but where does the 999 cap come from?

Thanks much for your response Glain, you guys'll make an asm'er out of me yet.



Glain

Quote from: Choto on January 21, 2012, 12:23:19 pm
Hmm, interesting. So if you were to nuke the compatibility system, you'd be left with 255 free flags to use for some kind of function. Then what decides wether to use the address as a series of flags (0x01,0x02,0x04) rather than just straight integers? Obviously, you can use multiple values in a flag system and only one with an integer system, but i'm not seeing why.


Wait, it's 256 possible values for one integer or 8 flags (true/false values). If you treat each bit as a separate value (flag), there are 8 of them. If you treat the entire byte as one big value, there are 256 (2^8) different bit patterns for that one value (0-255). Basically, you can mix and match flags, whereas one value is just one value.

Say we have this in a byte: 01101011

Flag interpretation: There are 8 flags here; the second, third, fifth, seventh, and eighth are true (from the left).
Number interpretation: This entire byte is an integer and its value is 107.

Quote from: Choto on January 21, 2012, 12:23:19 pm
This clarifies things quite well. Its tough thinking in hex/binary after 24 years of decimal. So then HP and max HP allocate for up to 65535 (Which makes sense for ??? monsters), and I'm sure this is answered somewhere in the archives, but where does the 999 cap come from?


999 is probably just a cap they put in because they didn't want to display more than 3 digits for HP, damage, etc. It was just convenient for graphics. It's not limited by the variable size, as you say.

Quote from: Choto on January 21, 2012, 12:23:19 pm
Thanks much for your response Glain, you guys'll make an asm'er out of me yet.


Quite welcome, glad to be of help!
  • Modding version: Other/Unknown

Choto

Quote from: Glain on January 21, 2012, 12:55:09 pm
Wait, it's 256 possible values for one integer or 8 flags (true/false values). If you treat each bit as a separate value (flag), there are 8 of them. If you treat the entire byte as one big value, there are 256 (2^8) different bit patterns for that one value (0-255). Basically, you can mix and match flags, whereas one value is just one value.

Say we have this in a byte: 01101011

Flag interpretation: There are 8 flags here; the second, third, fifth, seventh, and eighth are true (from the left).
Number interpretation: This entire byte is an integer and its value is 107.



Ah ok, so then one number can be both and integer or set of flags simultaneously, and the way it is interpreted depends on the code that is reading or writing to the address?
[/quote]


999 is probably just a cap they put in because they didn't want to display more than 3 digits for HP, damage, etc. It was just convenient for graphics. It's not limited by the variable size, as you say.
[/quote]

I should have worded this question a bit better. Do we know where in the code prevents units from increasing HP beyond the 999 cap, and where the code limits the display of 4 digits?

Also, I've seen the kanji table mentioned quite a bit. Does that just refer to the free unused space in Battle.bin, or is that space filled with kanji that we don't understand?

Glain

Quote from: Choto on January 21, 2012, 01:12:39 pm
Ah ok, so then one number can be both and integer or set of flags simultaneously, and the way it is interpreted depends on the code that is reading or writing to the address?


You've got it, that's it exactly. Generally for each address it's one or the other though; i.e. it's meant to be an integer or it's meant to be a set of flags.

Quote from: Choto on January 21, 2012, 01:12:39 pm
I should have worded this question a bit better. Do we know where in the code prevents units from increasing HP beyond the 999 cap, and where the code limits the display of 4 digits?


I'd imagine it's in multiple places all over the code. There's probably code explicitly preventing it on the lines of "if (damage > 999) damage = 999"

Quote from: Choto on January 21, 2012, 01:12:39 pm
Also, I've seen the kanji table mentioned quite a bit. Does that just refer to the free unused space in Battle.bin, or is that space filled with kanji that we don't understand?


It's filled with kanji that never gets displayed in the US version so it's free/unused space. Sometimes our hacks need more space than the routines we try to write them in have, so writing code in the kanji table and then jumping there is a way to get around that.
  • Modding version: Other/Unknown