FF4 SNES SRM Checksum
02-10-2017, 09:44 PM
Hello everybody,
Long time FF player. First time poster.
I'm trying to figure out the algorithm for generating the save game checksum for Final Fantasy II on SNES (or 4 however you look at it). I know the file is 8kB long and is divided into 4 equally spaced 2kB blocks. The checksum from what I gather is at the end of this block. I've tried sums of the words, XORs of the words, and I'm really sort of lost as to how it's computed. I found a tool by Lord J - FF4H version 3B that implements this. That's great, but I'd like to know the calculation behind it. From looking at his freely available source code for FF3H, it does look like it's more or less a simple sum of the words in each slot, XORd with all 1's at the end. Is FF4 the same?
Thanks for any help you can provide.
Long time FF player. First time poster.
I'm trying to figure out the algorithm for generating the save game checksum for Final Fantasy II on SNES (or 4 however you look at it). I know the file is 8kB long and is divided into 4 equally spaced 2kB blocks. The checksum from what I gather is at the end of this block. I've tried sums of the words, XORs of the words, and I'm really sort of lost as to how it's computed. I found a tool by Lord J - FF4H version 3B that implements this. That's great, but I'd like to know the calculation behind it. From looking at his freely available source code for FF3H, it does look like it's more or less a simple sum of the words in each slot, XORd with all 1's at the end. Is FF4 the same?
Thanks for any help you can provide.
I don't know if FF4 is the same. There is no public disassembly of the code to my knowledge. Aside of Lord J, Zyrtophar also made a SRM editor a while back, so the algorithm is known: http://slickproductions.org/forum/index....10#msg2110
However, there is no source. You could ask in the FF4 forum on that board.
However, there is no source. You could ask in the FF4 forum on that board.
02-12-2017, 12:15 AM
Thanks Madsiur for the direction. I'll go take a look at Zyrthofar's tool and see if I can contact him on that forum.
02-12-2017, 12:46 AM
02-12-2017, 12:48 AM
Hmm. This may prove useful for my project Thanks people! Need more references. Cheers!
Remember to use Omnislash in Remake
02-12-2017, 01:11 AM
By changing one value at a time in Zyrthofar's tool (ie: Max HP from 1 to 2), I noticed that both bytes of the checksum independently increment by the same amount. For instance, if I increase Cecil's Max HP field by 1, then both checksum bytes increase by 1. If I increase another field by 2, then both checksum bytes increase by 2. They seem to be somewhat independent of each other.
I don't know what the final two bytes (or depending on endianness the 3rd and 4th bytes from the end) are 0xE4 and 0x1B. Interestingly these add up to 0xFF, which I could see factoring into some sort of checksum. They could also just as well be an 'end of slot' marker. The 5th byte from the end also changes - maybe as part of the checksum calculation. I haven't figured that out yet.
It'd be great if you could take a look at it. Maybe it's a really easy problem and I've just overlooked the obvious solution?
I don't know what the final two bytes (or depending on endianness the 3rd and 4th bytes from the end) are 0xE4 and 0x1B. Interestingly these add up to 0xFF, which I could see factoring into some sort of checksum. They could also just as well be an 'end of slot' marker. The 5th byte from the end also changes - maybe as part of the checksum calculation. I haven't figured that out yet.
It'd be great if you could take a look at it. Maybe it's a really easy problem and I've just overlooked the obvious solution?
02-12-2017, 03:05 PM
I checked this out since I'm also curious.
The two bytes at the end $07FE and $07FF are set to #$1BE4 if the save slot is in use.
The checksum is calculated word by word but each word will overlap as the checksum counter is only increased by one byte. This is why both bytes of the checksum increase when one byte is increased.
Partial disassembly
Note: CPU bank 01 is at 00 in the ROM because FFIV is LoROM.
Good luck and happy hacking!
The two bytes at the end $07FE and $07FF are set to #$1BE4 if the save slot is in use.
The checksum is calculated word by word but each word will overlap as the checksum counter is only increased by one byte. This is why both bytes of the checksum increase when one byte is increased.
Partial disassembly
Code:
Transfer A to X through $43
0187B4 85 43 STA $43
0187B6 A6 43 LDX $43
0187B8 60 RTS
Sanity check a save slot
01995D BF FE 07 70 LDA $7007FE.l,x ; SRAM $07FE for save slot X
019961 A8 TAY ; A to Y
019962 E2 20 SEP #$20 ; 8-bit A
019964 C0 E4 1B CPY #$1BE4 ; Is save slot in use?
019967 F0 04 BEQ $996D ; if so get checksum
019969 E2 20 SEP #$20 ; 8-bit A
01996B 18 CLC ; Carry clear, no save file
01996C 60 RTS ; Return
Checksum check a save slot
01996D A5 51 LDA $51
01996F 1A INC a
019970 20 5F CC JSR $CC5F ; Calculate checksum to X
019973 C2 20 REP #$20 ; 16-bit A
019975 8A TXA ; Checksum to A
019976 A6 45 LDX $45
019978 DF FC 07 70 CMP $7007FC.l,x ; SRAM checksum for save slot
01997C D0 EB BNE $9969 ; exit if no match, with carry clear
01997E E2 20 SEP #$20 ; 8-bit A
019980 38 SEC ; Carry set, save file is ok
019981 60 RTS ; Return
Calculate checksum for save slot
01CC5F 85 4E STA $4E ; Save slot index
01CC61 0A ASL a ; Multiply by 2
01CC62 65 4E ADC $4E ; Multiply by 3
01CC64 20 B4 87 JSR $87B4 ; TAX through $43
01CC67 BF 86 CC 01 LDA $01CC86.l,x ; High byte of pointer to save slot
01CC6B 85 4E STA $4E ; store it
01CC6D C2 20 REP #$20 ; 16-bit A
01CC6F BF 87 CC 01 LDA $01CC87.l,x ; Low 2 bytes of pointer
01CC73 85 4F STA $4F ; store it
01CC75 A5 41 LDA $41
01CC77 A0 FA 07 LDY #$07FA ; Size of save slot
01CC7A 18 CLC ; Carry clear
01CC7B 67 4E ADC [$4E] ; Add next byte and carry
01CC7D E6 4E INC $4E ; next byte
01CC7F 88 DEY ; decrease size left
01CC80 D0 F9 BNE $CC7B ; Loop for all bytes
01CC82 AA TAX ; Set checksum to X
01CC83 E2 20 SEP #$20 ; 8-bit A
01CC85 60 RTS ; Return
Pointers to save slots
01CC86 00 10 7E
00 00 70 ; Slot 1 at $700000
00 08 70 ; Slot 2 at $700800
00 10 70 ; Slot 3 at $701000
00 18 70 ; Slot 4 at $701800
Good luck and happy hacking!
02-12-2017, 03:50 PM
Awesome! Thanks for the source assembly code. It's been a long time since I've dealt with assembly code (~17 years or so), so I'll need to brush up on it to get familiar with what these op codes and instructions do on the 65816.
If I understand what you said correctly, and from looking at the main loop (0x1cc7b to 0x1cc80) it treats the data as 16-bit (accumulator set to 16 bits at 0x1cc6d), but only increments the byte address by 1?
So if my 16-bit words are as follows,
0x1122
0x3344
0x5566
It would add 0x1122, 0x2233, 0x3344, 0x4455, 0x5566 and then set the accumulator to 8-bit mode (effectively dropping all higher bits) and return that as the 16(?)-bit checksum?
Am I kind of on the right track?
If I understand what you said correctly, and from looking at the main loop (0x1cc7b to 0x1cc80) it treats the data as 16-bit (accumulator set to 16 bits at 0x1cc6d), but only increments the byte address by 1?
So if my 16-bit words are as follows,
0x1122
0x3344
0x5566
It would add 0x1122, 0x2233, 0x3344, 0x4455, 0x5566 and then set the accumulator to 8-bit mode (effectively dropping all higher bits) and return that as the 16(?)-bit checksum?
Am I kind of on the right track?
02-12-2017, 04:09 PM
Yep, you pretty much got it.
Nothing is dropped though. When the accumulator is set to 8-bit mode the checksum has already been transferred to X so all 16-bits are stored.
I'm not sure about all of this as I have not properly tested anything but I found the routines with breakpoints, disassembled them and commented by hand.
The checksum starts with RAM value $41, I have no idea what it contains but I hope it's zero. More breakpoints could figure this out though.
I think we're on the right track, you're understanding what I have very well. Let us know if you get something working.
Nothing is dropped though. When the accumulator is set to 8-bit mode the checksum has already been transferred to X so all 16-bits are stored.
I'm not sure about all of this as I have not properly tested anything but I found the routines with breakpoints, disassembled them and commented by hand.
The checksum starts with RAM value $41, I have no idea what it contains but I hope it's zero. More breakpoints could figure this out though.
I think we're on the right track, you're understanding what I have very well. Let us know if you get something working.
02-12-2017, 04:41 PM
glad to see you're making short work of this. so it's essentially two separate 8-bit checksums, with the bottom one having all but the last data value, and the top one having all but the first?
« Next Oldest | Next Newest »
Users browsing this thread: 1 Guest(s)