+  RHDN Forum Archive
|-+  Romhacking
| |-+  General Romhacking
| | |-+  Final Fantasy Mystic Quest (v 1.0): Looking for Level Up Data
Pages: [1]
Author Topic: Final Fantasy Mystic Quest (v 1.0): Looking for Level Up Data  (Read 1 times)
« on: January 25, 2010, 06:31:03 pm »

Having two problems here:

1) I'm not sure if FF:MQ even -uses- a table to look up values for stat increases at level up; said increases are rather...consistent (+2/+3 Atk (this goes back and forth), +2 Def, +2 Spd, +1 Mag, all every level; +1 Acc every level divisible by 2; +1 WhiteSlot every level; +1 BlackSlot every level divisible by 2; +1WizardSlot every level divisible by 4), and increasing the hero's starting stats has no effect on level up bonuses.  What would I be looking for if the game simply used a hard-coded formula for increasing stats rather than a table?  If it -does- still use a table, I have had -zero- luck finding it (need help either way).

2) The amount of Experience needed to go to the next level is consistent and represented by a normal-seeming number in-game (384, 900, 1808, etc.).  However, I've been looking for these values in all different sorts of byte permutations (sequential, inverted, putting spaces between them, searching for values = (original/2), etc.) and I simply cannot find them anywhere in the ROM. 

I'm going to guess that both of these pieces of data are either before $20100 or after $64000, if that narrows anyone else's search fields.  I've also tried corruption, but when I do that to the areas I'm interested in the game crashes (this ROM's rather small, actually).  Anyone have any idea where this stuff is?  Would greatly appreciate hearing about it.

Oh, and for JCE: the latest MQME's equipment editor seems to be looking in the wrong place for armor edits.  For some reason, Square saw fit to have the data for armor start at $14328, a good ways away from the weapon data (the stuff in between seems to repeat in 7-byte increments, I have no idea what it does).  I noticed this when I used the editor and saw the values for some of the armor pieces mirroring this mystery sequence.
« Reply #1 on: January 25, 2010, 06:56:32 pm »

I can only suggest using a tracing emulator. (I still use Geiger's SNES9x tracer because it's the one I'm used to)

First, find the RAM address where the stats are. Set a "write breakpoint" for the address range (probably 7Exxxx or 7Fxxxx). Go gain an experience level to find out at about what point the stats are changed (before? after the level up message). Reload a save and gain the level again, this time enable tracing just before your stats change.
Open the trace log and look for the stat offset "[$7E:xxxx]" or "[$7F:xxxx]".
If it's using a table, the command that stores the updated values to RAM will likely be a STA, and right before it will likely be a LDA (if it's using exact values) or a ADC (if it's storing a relative value, like +2).

The same for EXP values. Use Cheat Search functions to find how level-up experience is handled. Do a cheat search for the amount of EXP remaining to level-up.
If you find a RAM address for this: while gaining a level, use tracing to find out where the EXP remaining is reset.
If using exact searches for EXP remaining doesn't work, then it might be comparing current EXP to required EXP.
Find the RAM address for current EXP, set a "read breakpoint" for that address.
Likely it'd be using a LDA command to read the RAM, then using a CMP to compare to either a RAM address holding the next level EXP (if so, trace at level-up to find how it's set) or a ROM address that contains the level-up table.
« Reply #2 on: January 25, 2010, 07:54:23 pm »

Long ago, there existed a forum called Omniplay Gaming, and SharkESP was looking for this same data. What piqued his interest was that somehow damage was increasing by 10 at times.

If at all possible, the WayBack Machine at archive.org may have those posts saved.
« Reply #3 on: January 25, 2010, 08:07:09 pm »

Address range detected for [Hero]'s Magic score: $7e1025
Breakpoint activated the moment "[Hero]'s Level Up!" window was confirmed.

Trace results for Magic increasing at level-up (I still have both of these logs if more data is required):
$02/879F AD 9E 00    LDA $009E  [$02:009E]   A:0028 X:D325 Y:004E P:envmxdizc
$02/87A2 85 14       STA $14    [$00:1014]   A:002C X:D325 Y:004E P:envmxdizc
$02/87A4 E2 20       SEP #$20                A:002C X:D325 Y:004E P:envmxdizc
$02/87A6 C2 10       REP #$10                A:002C X:D325 Y:004E P:envMxdizc
$02/87A8 A5 10       LDA $10    [$00:1010]   A:002C X:D325 Y:004E P:envMxdizc
$02/87AA 8D A2 04    STA $04A2  [$02:04A2]   A:0002 X:D325 Y:004E P:envMxdizc
$02/87AD A5 1B       LDA $1B    [$00:101B]   A:0002 X:D325 Y:004E P:envMxdizc
$02/87AF 38          SEC                     A:0003 X:D325 Y:004E P:envMxdizc
$02/87B0 ED A0 04    SBC $04A0  [$02:04A0]   A:0003 X:D325 Y:004E P:envMxdizC
$02/87B3 18          CLC                     A:0002 X:D325 Y:004E P:envMxdizC
$02/87B4 65 10       ADC $10    [$00:1010]   A:0002 X:D325 Y:004E P:envMxdizc
$02/87B6 85 1B       STA $1B    [$00:101B]   A:0004 X:D325 Y:004E P:envMxdizc
$02/87B8 4E A0 04    LSR $04A0  [$02:04A0]   A:0004 X:D325 Y:004E P:envMxdizc
$02/87BB 4E A2 04    LSR $04A2  [$02:04A2]   A:0004 X:D325 Y:004E P:envMxdiZC
$02/87BE A5 1C       LDA $1C    [$00:101C]   A:0004 X:D325 Y:004E P:envMxdizc
$02/87C0 38          SEC                     A:0001 X:D325 Y:004E P:envMxdizc
$02/87C1 ED A0 04    SBC $04A0  [$02:04A0]   A:0001 X:D325 Y:004E P:envMxdizC
$02/87C4 18          CLC                     A:0001 X:D325 Y:004E P:envMxdizC
$02/87C5 6D A2 04    ADC $04A2  [$02:04A2]   A:0001 X:D325 Y:004E P:envMxdizc
$02/87C8 85 1C       STA $1C    [$00:101C]   A:0002 X:D325 Y:004E P:envMxdizc
$02/87CA 4E A0 04    LSR $04A0  [$02:04A0]   A:0002 X:D325 Y:004E P:envMxdizc
$02/87CD 4E A2 04    LSR $04A2  [$02:04A2]   A:0002 X:D325 Y:004E P:envMxdiZc
$02/87D0 A5 1D       LDA $1D    [$00:101D]   A:0002 X:D325 Y:004E P:envMxdiZC
$02/87D2 38          SEC                     A:0000 X:D325 Y:004E P:envMxdiZC
$02/87D3 ED A0 04    SBC $04A0  [$02:04A0]   A:0000 X:D325 Y:004E P:envMxdiZC
$02/87D6 18          CLC                     A:0000 X:D325 Y:004E P:envMxdiZC
$02/87D7 6D A2 04    ADC $04A2  [$02:04A2]   A:0000 X:D325 Y:004E P:envMxdiZc
$02/87DA 85 1D       STA $1D    [$00:101D]   A:0000 X:D325 Y:004E P:envMxdiZc
$02/87DC E6 4C       INC $4C    [$00:104C]   A:0000 X:D325 Y:004E P:envMxdiZc
$02/87DE E6 4C       INC $4C    [$00:104C]   A:0000 X:D325 Y:004E P:envMxdizc
$02/87E0 E6 4C       INC $4C    [$00:104C]   A:0000 X:D325 Y:004E P:envMxdizc
$02/87E2 E6 4D       INC $4D    [$00:104D]   A:0000 X:D325 Y:004E P:envMxdizc
$02/87E4 E6 4D       INC $4D    [$00:104D]   A:0000 X:D325 Y:004E P:envMxdizc
$02/87E6 E6 4E       INC $4E    [$00:104E]   A:0000 X:D325 Y:004E P:envMxdizc
$02/87E8 E6 4E       INC $4E    [$00:104E]   A:0000 X:D325 Y:004E P:envMxdizc
$02/87EA E6 4F       INC $4F    [$00:104F]   A:0000 X:D325 Y:004E P:envMxdizc
$02/87EC A2 26 10    LDX #$1026              A:0000 X:D325 Y:004E P:envMxdizc
$02/87EF A0 04 00    LDY #$0004              A:0000 X:1026 Y:004E P:envMxdizc
$02/87F2 BD 00 00    LDA $0000,x[$02:1026]   A:0000 X:1026 Y:0004 P:envMxdizc
$02/87F5 C9 63       CMP #$63                A:0007 X:1026 Y:0004 P:envMxdizc
$02/87F7 90 02       BCC $02    [$87FB]      A:0007 X:1026 Y:0004 P:eNvMxdizc
$02/87FB 9D 00 00    STA $0000,x[$02:1026]   A:0007 X:1026 Y:0004 P:eNvMxdizc
$02/87FE E8          INX                     A:0007 X:1026 Y:0004 P:eNvMxdizc
$02/87FF 88          DEY                     A:0007 X:1027 Y:0004 P:envMxdizc
$02/8800 D0 F0       BNE $F0    [$87F2]      A:0007 X:1027 Y:0003 P:envMxdizc
$02/87F2 BD 00 00    LDA $0000,x[$02:1027]   A:0007 X:1027 Y:0003 P:envMxdizc
$02/87F5 C9 63       CMP #$63                A:0006 X:1027 Y:0003 P:envMxdizc
$02/87F7 90 02       BCC $02    [$87FB]      A:0006 X:1027 Y:0003 P:eNvMxdizc
$02/87FB 9D 00 00    STA $0000,x[$02:1027]   A:0006 X:1027 Y:0003 P:eNvMxdizc
$02/87FE E8          INX                     A:0006 X:1027 Y:0003 P:eNvMxdizc
$02/87FF 88          DEY                     A:0006 X:1028 Y:0003 P:envMxdizc
$02/8800 D0 F0       BNE $F0    [$87F2]      A:0006 X:1028 Y:0002 P:envMxdizc
$02/87F2 BD 00 00    LDA $0000,x[$02:1028]   A:0006 X:1028 Y:0002 P:envMxdizc
$02/87F5 C9 63       CMP #$63                A:0008 X:1028 Y:0002 P:envMxdizc
$02/87F7 90 02       BCC $02    [$87FB]      A:0008 X:1028 Y:0002 P:eNvMxdizc
$02/87FB 9D 00 00    STA $0000,x[$02:1028]   A:0008 X:1028 Y:0002 P:eNvMxdizc
$02/87FE E8          INX                     A:0008 X:1028 Y:0002 P:eNvMxdizc
$02/87FF 88          DEY                     A:0008 X:1029 Y:0002 P:envMxdizc
$02/8800 D0 F0       BNE $F0    [$87F2]      A:0008 X:1029 Y:0001 P:envMxdizc
$02/87F2 BD 00 00    LDA $0000,x[$02:1029]   A:0008 X:1029 Y:0001 P:envMxdizc
$02/87F5 C9 63       CMP #$63                A:000A X:1029 Y:0001 P:envMxdizc
$02/87F7 90 02       BCC $02    [$87FB]      A:000A X:1029 Y:0001 P:eNvMxdizc
$02/87FB 9D 00 00    STA $0000,x[$02:1029]   A:000A X:1029 Y:0001 P:eNvMxdizc
$02/87FE E8          INX                     A:000A X:1029 Y:0001 P:eNvMxdizc
$02/87FF 88          DEY                     A:000A X:102A Y:0001 P:envMxdizc
$02/8800 D0 F0       BNE $F0    [$87F2]      A:000A X:102A Y:0000 P:envMxdiZc
$02/8802 A5 4C       LDA $4C    [$00:104C]   A:000A X:102A Y:0000 P:envMxdiZc
$02/8804 85 26       STA $26    [$00:1026]   A:000A X:102A Y:0000 P:envMxdizc
$02/8806 18          CLC                     A:000A X:102A Y:0000 P:envMxdizc
$02/8807 65 2A       ADC $2A    [$00:102A]   A:000A X:102A Y:0000 P:envMxdizc
$02/8809 85 22       STA $22    [$00:1022]   A:000A X:102A Y:0000 P:envMxdizc
$02/880B A5 4D       LDA $4D    [$00:104D]   A:000A X:102A Y:0000 P:envMxdizc
$02/880D 85 27       STA $27    [$00:1027]   A:0008 X:102A Y:0000 P:envMxdizc
$02/880F 65 2B       ADC $2B    [$00:102B]   A:0008 X:102A Y:0000 P:envMxdizc
$02/8811 85 23       STA $23    [$00:1023]   A:000E X:102A Y:0000 P:envMxdizc
$02/8813 A5 4E       LDA $4E    [$00:104E]   A:000E X:102A Y:0000 P:envMxdizc
$02/8815 85 28       STA $28    [$00:1028]   A:000A X:102A Y:0000 P:envMxdizc
$02/8817 65 2C       ADC $2C    [$00:102C]   A:000A X:102A Y:0000 P:envMxdizc
$02/8819 85 24       STA $24    [$00:1024]   A:000A X:102A Y:0000 P:envMxdizc
$02/881B A5 4F       LDA $4F    [$00:104F]   A:000A X:102A Y:0000 P:envMxdizc
$02/881D 85 29       STA $29    [$00:1029]   A:000B X:102A Y:0000 P:envMxdizc
$02/881F 65 2D       ADC $2D    [$00:102D]   A:000B X:102A Y:0000 P:envMxdizc
$02/8821 85 25       STA $25    [$00:1025]   A:000B X:102A Y:0000 P:envMxdizc
$02/8823 E2 20       SEP #$20                A:000B X:102A Y:0000 P:envMxdizc
$02/8825 C2 10       REP #$10                A:000B X:102A Y:0000 P:envMxdizc
$02/8827 A5 10       LDA $10    [$00:1010]   A:000B X:102A Y:0000 P:envMxdizc
$02/8829 4A          LSR A                   A:0002 X:102A Y:0000 P:envMxdizc
$02/882A 18          CLC                     A:0001 X:102A Y:0000 P:envMxdizc
$02/882B 69 4B       ADC #$4B                A:0001 X:102A Y:0000 P:envMxdizc
$02/882D 85 40       STA $40    [$00:1040]   A:004C X:102A Y:0000 P:envMxdizc
$02/882F 2B          PLD                     A:004C X:102A Y:0000 P:envMxdizc
$02/8830 22 02 9B 00 JSL $009B02[$00:9B02]   A:004C X:102A Y:0000 P:envMxdizc

"7E:" wasn't found anywhere, so either I screwed it up (likely) or the notation's different.  Otherwise just as you said, in fact it looks like it's using both operations o_o.

Before I go looking for EXP (will have to use current XP, XP to level is the very thing I'm having hell finding ;_; ), very basic newb question: is there a document available that'll give me a quick rundown on how to read this output?  I understand these are assembly opcodes/etc., but I just need a little nudge to tell me how to translate that into data I can find on the ROM.

Thank you for suggesting this, btw; I would never've thought I'd need to do something like tracing just to find stat data -_-. 

Hmm...(*goes to check out the WayBack Machine*)

EDIT: ah, "$02/8807" translates to $10a07 when looking at it in a hex editor.  Couldn't find anything at the WayBack Machine.

EDIT 2: ...oh, no :/.  I'm noticing the INC(rement) opcodes up there...they match -perfectly- with the number added to each stat (3/2/2/1) at that level.  So level up stats seem to be relative; I need to be able to insert more INC operations in order to make the stats go up at a different rate.  This sounds like something that might not be feasible for me to do Sad.  I guess find whatever's pointing to $1099f, change it to point somewhere that's full of freespace/expand the ROM, copy the whole block of code over to that new location, then mess around with how many INC operations it uses?  Maybe I can simply change which stats get which of the set increments (so I could do, say, (1/2/2/3), making a magic-biased hero)...

EDIT 3: Should I try to remedy this using a JMP instruction, instead?  I've never done assembly level hacking before so I don't know the most efficient/effective way to go about this.
« Last Edit: January 25, 2010, 10:07:53 pm by Edea »
« Reply #4 on: January 25, 2010, 10:17:28 pm »

The whole "7E" or "7F" thing is weird. Alot of times, the address that stuff is happening at will appear to not be RAM (for instance, that first instruction, LDA $009E [02:009E]), but it is actually happening in RAM (which is banks 7E and 7F). Chances are that's happening at $7E009E and not $02009E. I could be wrong, but you can check and see if that's what is happening by setting a breakpoint for that particular instruction (read breakpoint at $02879F) and see if whatever is at $7E009E matches what shows up in the accumulator after that instruction (see if the value at $7E009E is 2C).

Hope that helps. I'm not in the code deciphering mood, but that's something about SNES ASM that can be tricky if you don't know it.

« Reply #5 on: January 26, 2010, 09:08:05 am »

It's not weird if you understand what's happening. It's called memory mapping. This document is invaluable reference on the subject as it applies to the SNES.


WRAM locations 0000-0x1FFF (7E:0000 - 7E:1FFF) can be accessed from many banks. So say, 7E:0000, 02:0000, and 00:0000 would access the same WRAM location. There's a few reasons this design was used. Just look at how it's accessed in the code Edea posted above for one example. The first page of WRAM can then be addressed via just 8-bits.


You pretty much nailed it if your WRAM offsets for the stats are correct. That's exactly what it's doing. The code is explicitly incrementing. I hope the stat increase is the same for each level up or that's a pretty inefficient way for the game to do such a thing.

Anyway, you also got it right on how you would modify it. You need to find some free space and then you need to insert a JSR (16-bit) or JSL (24-bit) statement to jump to your new code to execute and return back to the original game code. Remember to NOP after your JSR or JSL if necessary so your routine returns on an even byte to resume execution on the correct instruction.

You'd use a cross assembler like xkas (available at RHDN or byuu.org) to assemble (or effectively change) code to anywhere in the ROM.

You COULD use a hex editor if you just wanted to swap around which stat increments. You can see how the opcodes are made up there with their affected increment locations on the left of the trace. But if you want to do anything beyond that, you will probably need to use the cross assembler.
« Reply #6 on: January 26, 2010, 01:06:56 pm »

I didn't mean that it's weird always, I meant that to someone who is just starting out it can be kinda odd at first. I probably should've been more direct in what I meant. Under most circumstances, I understand it without even thinking about it. =P

« Reply #7 on: January 26, 2010, 08:06:40 pm »

Yah, for now I've just 'evened them out' (changed the distribution from 3/2/2/1 to 2/2/2/2) and raised some of the hero's starting stats.  Will be going on a Spell Hunt, soon, though XD.

I'll look into the cross assembler, thanks for the tips.
Pages: [1]  

Powered by SMF 1.1.4 | SMF © 2006-2007, Simple Machines LLC