Users browsing this thread: 1 Guest(s)
FF4 SNES SRM Checksum

#1
Posts: 7
Threads: 1
Thanks Received: 0
Thanks Given: 0
Joined: Feb 2017
Reputation: 0
Status
None
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.
  Find
Quote  

#2
Posts: 3,966
Threads: 279
Thanks Received: 234
Thanks Given: 56
Joined: Oct 2011
Reputation: 65
Status
Tissue-aware
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.
  Find
Quote  

#3
Posts: 7
Threads: 1
Thanks Received: 0
Thanks Given: 0
Joined: Feb 2017
Reputation: 0
Status
None
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.
  Find
Quote  

#4
Posts: 3,966
Threads: 279
Thanks Received: 234
Thanks Given: 56
Joined: Oct 2011
Reputation: 65
Status
Tissue-aware
(02-12-2017, 12:15 AM)stonerbot613 Wrote: I'll go take a look at Zyrthofar's tool and see if I can contact him on that forum.

I'm a 100% sure he's not active anymore. However Slick Forums has a few FF4 experts that could likely help.
  Find
Quote  

#5
Posts: 39
Threads: 4
Thanks Received: 0
Thanks Given: 0
Joined: Feb 2017
Reputation: 2
Status
None
Hmm. This may prove useful for my project Smile Thanks people! Need more references. Cheers!


Remember to use Omnislash in Remake Kungfu!
  Find
Quote  

#6
Posts: 7
Threads: 1
Thanks Received: 0
Thanks Given: 0
Joined: Feb 2017
Reputation: 0
Status
None
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?
  Find
Quote  

#7
Posts: 127
Threads: 8
Thanks Received: 21
Thanks Given: 12
Joined: Jan 2012
Reputation: 13
Status
None
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
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
Note: CPU bank 01 is at 00 in the ROM because FFIV is LoROM.

Good luck and happy hacking!
  Find
Quote  
[-] The following 1 user says Thank You to m06 for this post:
  • Vertigo (02-13-2017)

#8
Posts: 7
Threads: 1
Thanks Received: 0
Thanks Given: 0
Joined: Feb 2017
Reputation: 0
Status
None
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?
  Find
Quote  

#9
Posts: 127
Threads: 8
Thanks Received: 21
Thanks Given: 12
Joined: Jan 2012
Reputation: 13
Status
None
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.
  Find
Quote  
[-] The following 1 user says Thank You to m06 for this post:
  • Vertigo (02-13-2017)

#10
Posts: 200
Threads: 1
Thanks Received: 10
Thanks Given: 0
Joined: Oct 2015
Reputation: 18
Status
None
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?
Quote  



Forum Jump:

Users browsing this thread: 1 Guest(s)


Theme by Madsiur2017Custom Graphics by JamesWhite