[WIP] FF9 Save editor. Memory card and save offset stuff.

  • Thread starter Thread starter gjoerulv
  • Start date Start date
Status
Not open for further replies.
I implemented a "CRC16 assuming checksum finder" (lol). As you can see on the status strip on the form it goes through every polynomial, starting at offset 0 - 256, with every possible initial values on 2 different savegames (block 11 and 12). No need to say it's gonna take hours. And even if I find enough matches it's based on certain assumptions. May very well be a waste of time.  :-\
I did the same for the CRC16 checksum in F-Zero FX emblem files, and it didn't take particularly long.
 
I implemented a "CRC16 assuming checksum finder" (lol). As you can see on the status strip on the form it goes through every polynomial, starting at offset 0 - 256, with every possible initial values on 2 different savegames (block 11 and 12). No need to say it's gonna take hours. And even if I find enough matches it's based on certain assumptions. May very well be a waste of time.  :-\
Not to steal Micky's thunder, but if you're moderately skilled at debugging you could find out where the game checks the CRC and figures out how it's calculated. One thing I've seen that kind of check do is generate a CRC based on the data as it loads then checks the CRC that's saved in the file. There's bound to be a place where it generates the test case so you could check that out.
Alternately, you could find where it writes the save file and see where it pulls the CRC from and how THAT'S generated. If I had a good PSX debugger/emulator I'd help you look. Can you recommend one?
 
Not to steal Micky's thunder, but if you're moderately skilled at debugging you could find out where the game checks the CRC and figures out how it's calculated.
Don't worry, other than saying that it is a valid approach to try all polynomials for a CRC16 I didn't mean to do much thundering.
 
I thought I could go the easy way out to just check every poly, calculating from different start offsets. Didn't work with normal crc16. I tried a few algorithms to no avail. My attempts assume a crc16 stored at 0x13FE, that all the data is raw (which shouldn't matter anyway), and that all the data in one saveblock only are checked in one segment. Since my attempts didn't work I tried a Crc16ccitt algorithm (pic), which... didn't work.

Either my assumptions are wrong or my calculations are wrong. I'm pretty sure it's the latter. If you look through the save data you would agree, at least at first glance. Then again, if it's this easy, why isn't there a FF9 save editor available anywhere?

I haven't put much effort into the crc/checksum, but debugging will help if you know what you're doing. Though I'm kinda new to psx emulation, it shouldn't be too hard finding a good emulator. Any help breaking the code would be great.

I'm kinda busy these days however, so I don't expect too much work from me. I'll get to it when I get to it. :P

EDIT: lol, done some debugging
ff9_mog_poop-Ampeo.jpg
 
Last edited:
Ok so I'm going to resurrect this topic abit, don't know whenever you're working on this anymore, but for anyone interested...

So out of boredom I started to dig alittle thru FF9 again yesterday, and I started to look for a checksum rutine in the load/save module, and it didn't take too long to discover what I wanted (though I did have some luck aswell :P). The checksum used for the savegames is a standard CRC-16-CCITT algorithm (x16 + x12 + x5 + 1), and besides the CRC calulation rutine there's also a initation rutine for the lookup table which uses the polynomial 0x8404 for the generation. I wrote two C++ equivalents for the generation and calculation so I could try it with a save file. The calculation is done using all bytes from the beginning of a saveslot and up to (but ofcourse excluding) the checksum value, that's 5118 (0x13FE) bytes for the calculation. I would assume that the game data also use the same CRC, but I haven't tried that so I can't confirm that's the case.

Here's the C++ code I wrote for the checksum, I also included a complete lookup table (which do make the initiation function totally pointless, but still Oo), have fun.

Code: [Select]
Code:
unsigned short LookupTable[256] = { 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78};void InitiateCRC(void) {    for(unsigned short Index = 0; Index < 256; Index++) {        unsigned short Value = Index;        for(unsigned short Bit = 0; Bit < 8; Bit++) {            if(Value & 0x0001) {                Value >>= 1;                Value ^= 0x8408;            } else {                Value >>= 1;            }            LookupTable[Index] = Value;        }    }}unsigned short CalculateCRC(unsigned char *Pointer, unsigned short Size = 0x13FE) {    unsigned short Checksum = 0xFFFF;    while(Size--)        Checksum = (Checksum >> 8) ^ LookupTable[(Checksum ^ *Pointer++) & 0xFF];   return Checksum;}
 
Great! Good to see some interest. I figured it was a Crc16Ccitt. I'll try your method as I see most of my assumptions were right. 0x8404 huh?

EDIT:
Looks like it wooorks. So far I only tested it editing the header (psx file number; part of those 5118 bytes). To be honest I kinda figured it out a while ago. As you look at the data a crc16 type is obvious.

If anyone is interested in developing a save editor, let me know. As of now my project is developed in C#, but I do picture a multi platform application. unless microsoft buys all rights to C++ within a short space of time, I'll most likely aim for a cross-platform editor.
 
Last edited:
If anyone is interested in developing a save editor, let me know. As of now my project is developed in C#, but I do picture a multi platform application. unless microsoft buys all rights to C++ within a short space of time, I'll most likely aim for a cross-platform editor.
if you want it to run crossplatform and use C++ i would suggest using Qt for your toolkit its under the lgpl so its free.. also you can easily make a save editor using it. (see Black Chocobo/Hyne) they are both done in C++ using Qt. If you intrested Let me know it shouldn't be to hard to help you get started using it.
 
If anyone is interested in developing a save editor, let me know. As of now my project is developed in C#, but I do picture a multi platform application. unless microsoft buys all rights to C++ within a short space of time, I'll most likely aim for a cross-platform editor.
Qt is really convenient, it can easily make beautiful things. Everyone agrees that Black Chocobo or Hyne have a great interface :mrgreen:.
 
Hey, I was wondering if there is an explanation on how to deal with the "Status bits".
 
The status bit stores the state of the seven negative status effects that doesn't disappear after a battle. The first bit is Petrification, second is Venom, third Virus, fourth Silence, fifth Darkness, sixth Trouble, seventh Zombie. I don't know whenever the last bit is used for something, but I don't think so.
 
So, does this mean Petrification=1, Venom=2, ..., Trouble=32, Zombie=64? I thought so, however, trying these numbers does not genearte the results I expected. Does anyone know which numbers correspond to the respective status effects?
 
Yes that's correct, so OR'ing (or adding I guess) them together to set more conditions, like 127 would result in all seven conditions been set.


Also just for the record, at offset 0x3E of each character slot there's four 16 bit values which holds a bonus stats pool that are used in the algorithm for calculating character's stats. I haven't looked very carefully how the pools works, but at least they acculumate any +stats from gear every time a character levels up. They are stored like this:

0x003E: Speed Pool
0x0040: Strength Pool
0x0042: Magic Pool
0x0044: Spirit Pool
 
Unfortunately, it seems not so. For example, Zombie=192 and all status effects=255. I have not tried anything else but it seems rather counterintuitive.
 
I haven't checked, but how about this:

Code: [Select]
Code:
1000 0000 (Status flag, whatever this means)   OR0100 0000 (Zombie flag)   =1100 0000 (192)
Then Petrification + Venom should be 131.

Code: [Select]
Code:
1000 0000 (Status flag, again, whatever that means)   OR0000 0001 (Petrification flag)   OR0000 0010 (Venom flag)   =1000 0011 (131)
All status = 255, no status = 0;
I may as well be wrong though...

...
Also just for the record, at offset 0x3E of each character slot there's four 16 bit values which holds a bonus stats pool that are used in the algorithm for calculating character's stats. I haven't looked very carefully how the pools works, but at least they acculumate any +stats from gear every time a character levels up. They are stored like this:

0x003E: Speed Pool
0x0040: Strength Pool
0x0042: Magic Pool
0x0044: Spirit Pool
Thanks again Zande. I'll get to test this sometime.
 
Unfortunately, it seems not so. For example, Zombie=192 and all status effects=255. I have not tried anything else but it seems rather counterintuitive.

I suspect you have a bit aggregator, as GJoerulv suggests.


You're probably looking at a byte where each bit represents one of eight statuses.


You'll have to continue to experiment to find out which bit signifies which status, though. You probably can't just poke the figure and expect an entity's statuses to visibly change - each statuses' visual effects are probably only polled after an attack / spell. Instead, you'll have to inflict the status effect via spells and the like, then watch the byte for signs of change.


You may have multiple bytes used to refer to statuses at different times, too.
 
Status
Not open for further replies.
Back
Top