Qhimm.com Forums
Miscellaneous Forums => Scripting and Reverse Engineering => Topic started by: gjoerulv on 2010-09-14 00:11:48
-
I'm working on a FF9 savegame editor (anyone got a good idea for a name :P). I need some help understanding how the psx memorycard works and where the data is. I've tried some searching with some luck. But lets see if I've gotten the gist of things. I've worked on a .mcr file.
PART 1. MEMORY CARD HEADERS
When I open a .mcr file with a hex-editor, the header -0x80 bytes- looks like this:
?? ?? ?? ?? ?? ?? .... ?? ?? ?? CS
4D 43 00 00 00 00 .... 00 00 00 0E
Question1: What do the "??" represent? Are they even important when making a save editor (pointers etc)?
The last one being a checksum, the bytes are XORed 'til the end of the header.
Then the header for each slot follows, 0x80 bytes each (if I got it down right). The checksum is on the last byte and is calculated the same way. Here are the 1st FF9 saveslot header on my card:
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? [<--- Region/DiscNr code --->] [SlotNr]
51 00 00 00 00 20 00 00 FF FF 42 45 53 4C 45 53 2D 30 32 39 36 35 30 30 30 30 30 2D 30 30 00 00
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? CS
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 77
Question2: What are the "??" here? Specially the ones before Region/DiscNr code. Nuber of slots to use?
Region/DiscNr code + SlotNr are read like this as a string:
BESLES-0296500000-00
I'm guessing this means PAL disc1 code stuff, and that it is slot nr 1 (read as 0. The 3 last bytes here are slot nr, so no need to include 'em further).
Below is what I've read testing saves from all discs. And I'm pretty sure I'm correct.
PAL disc 1 = BESLES-0296500000
PAL disc 2 = BESLES-1296500000
PAL disc 3 = BESLES-2296500000
PAL disc 4 = BESLES-3296500000
Question 3: How do the NTSC codes look like, and are there other versions (Spanish, German, etc)?
It would be great if there only were 2 versions of this: NTSC and PAL. :-D
PART 2. SAVE MAP OFFSETS
Here I could use much help. I dunno what the stuff from after the last slot 'til the beginning of save-data is (offset 0x800 - 0x1FFF). Before the 1st save-data, starting at 0x2000, there seems to be another header equal to the 1st one in the file. I guess it doesn't matter much though, as it's mostly just 0x00 bytes. :roll: I'm guessing it might be something to do with games which use multiple slots. Or not...
Anyway, here are the data I've found by searching the web and analyzing saves. Each save block are 2000h bytes long.
0x0105: BYTE Level; // Party leader level
0x0106: BYTE Name[8]; // Party leader name
0x010E: BYTE Unknown; // ???
0x010F: BYTE Unknown; // ???
0x0106: BYTE Location[28]; // Name of current location
0x012C: BYTE Gametime; // Gametime (expressed in tenth of a second)
0x0130: BYTE Gil; // Total amount of Gil
0x0ee8 | 00 00 00 00 = 0 // Number of Gil
// ============================================================================
// Final Fantasy IX | Savegame | Character Stats
// Start Pointer: 0x09d0
// End Pointer: 0x0EDF
// Block Count: 9
// Block Size: 144 bytes
// Total Size: 1296 bytes
// ============================================================================
0x0000: BYTE Name[8]; // Character name
0x0008: BYTE Unknown; // ???
0x0009: BYTE Unknown; // ???
0x000A: BYTE Unknown; // ???
0x000B: BYTE Level; // Character level
0x000C: DWORD Experience; // Total amount of experience ?
0x0010: WORD CurrentHP; // Current amount of HP
0x0012: WORD CurrentMP; // Current amount of MP
0x0014: BYTE Unknown; // ???
0x0015: BYTE Unknown; // ???
0x0016: BYTE Unknown; // ???
0x0017: BYTE CurrentMS; // Current amount of magical stones
0x0018: BYTE MaxHP; // Max amount of HP
0x001A: BYTE MaxMP; // Max amount of MP
0x001C: BYTE Unknown; // ???
0x001D: BYTE Unknown; // ???
0x001E: BYTE Unknown; // ???
0x001F: BYTE MaxMS; // Max amount of magical stones
0x0020: BYTE TranceLevel; // Trance Level
0x0021: BYTE Unknown; // ???
0x0022: BYTE Unknown; // ???
0x0023: BYTE Unknown; // ???
0x0024: BYTE Speed; // Speed (total value incl. gear bonuses)
0x0025: BYTE Strength; // Strength (total value incl. gear bonuses)
0x0026: BYTE Magic; // Magic (total value incl. gear bonuses)
0x0027: BYTE Spirit; // Spirit (total value incl. gear bonuses)
0x0028: BYTE Defence; // Defence
0x0029: BYTE Evade; // Evade
0x002A: BYTE MagicDefence; // Magic defence
0x002B: BYTE MagicEvade; // Magic evade
0x002C: WORD 2nd HP max; // Max HP with Bonus
0x002E: WORD 2nd MP max; // Max MP with Bonus
0x0030: BYTE BaseSpeed; // Base speed (excl. gear bonuses)
0x0031: BYTE BaseStrength; // Base strength (excl. gear bonuses)
0x0032: BYTE BaseMagic; // Base magic (excl. gear bonuses)
0x0033: BYTE BaseSpirit; // Base spirit (excl. gear bonuses)
0x0034: BYTE Unknown; // ???
0x0035: BYTE Unknown; // ???
0x0036: BYTE Unknown; // ???
0x0037: BYTE Unknown; // ???
0x0038: BYTE Status; // Status bits.
0x0039: BYTE Weapon; // Equiped weapon
0x003A: BYTE Headgear; // Equiped head gear
0x003B: BYTE Armgear; // Equiped arm gear
0x003C: BYTE Armor; // Equiped armor
0x003D: BYTE Addon; // Equiped add-on
0x003E: BYTE Unknown; // ???
0x003F: BYTE Unknown; // ???
0x0040: BYTE Unknown; // ???
0x0041: BYTE Unknown; // ???
0x0042: BYTE Unknown; // ???
0x0043: BYTE Unknown; // ???
0x0044: BYTE Unknown; // ???
0x0045: BYTE Unknown; // ???
0x0046: BYTE Unknown; // ???
0x0047: BYTE Unknown; // ???
0x0048: BYTE Unknown; // ???
0x0049: BYTE Unknown; // ???
0x004A: BYTE Unknown; // ???
0x004B: BYTE Unknown; // ???
0x004C: BYTE Unknown; // ???
0x004D: BYTE Unknown; // ???
0x004E: BYTE Unknown; // ???
0x004F: BYTE Unknown; // ???
0x0050: BYTE Unknown; // ???
0x0051: BYTE Unknown; // ???
0x0052: BYTE Unknown; // ???
0x0053: BYTE Unknown; // ???
0x0054: BYTE Unknown; // ???
0x0055: BYTE Unknown; // ???
0x0056: BYTE Unknown; // ???
0x0057: BYTE Unknown; // ???
0x0058: BYTE Unknown; // ???
0x0058: BYTE AbilityAP[48]; // AP for action/support abilities
0x0088: BYTE Support[8]; // Support abilities equiped
// ============================================================================
// Final Fantasy IX | Savegame | Item List
// Start Pointer: 0x0F20
// End Pointer: 0x111F
// Block Count: 256
// Block Size: 2 bytes
// Total Size: 512 bytes
// ============================================================================
0x0000: BYTE ID; // Item ID
0x0001: BYTE Count; // Item Count
CARDS:
0x1178: WORD WINS?
0x117A: WORD LOSSES?
0x117C: WORD DRAWS?
0x119A: BYTE Card Count? // Number of cards in stock
0x119B: BYTE Type Count? // Number of card types
// ============================================================================
// Final Fantasy IX | Savegame | Cards
// Start Pointer: 0x119C
// End Pointer: 0x13F3
// Block Count: 100
// Block Size: 6 bytes
// Total Size: 600 bytes
// ============================================================================
0x0000: BYTE ID? ; // Type ID (FFh = empty block)?
0x0001: BYTE Arrow bits?; // Bitmap representing arrows?
0x0002: BYTE Attack type?; // Type of attack (physical, magical etch.)?
0x0003: BYTE Attack?; // Atack Power?
0x0004: BYTE P.Def?; // Physical defence power?
0x0005: BYTE M.Def?; // Magical defence power?
I see there are some errors here (BYTE instead of WORD etc), but I'm too lazy to correct 'em. The bit length should be obvious in most cases.
-
May I suggest Freya as the name for this program? Assuming its not already taken by some other tool.
-
Ye, that might work. Freya is one smexy rat!
-
I would personally recommend Blank as the name of the editor, not a main character, but he is the first to truly save Zidane, Steiner and Garnet by sacrificing himself to keep them alive.
-
Dibs on calling the FF9 Enemy Editor Garland.
-
Obviously, you should call it Kefka_Leonhart37. Just sayin'.
-
I would personally recommend Blank as the name of the editor, not a main character, but he is the first to truly save Zidane, Steiner and Garnet by sacrificing himself to keep them alive.
Actually that's not a bad idea.
Dibs on calling the FF9 Enemy Editor Garland.
As a long term plan I was planing on making one myself. Are you making one? Anyway, Garland would be THE perfect name for one. :-D
Obviously, you should call it Kefka_Leonhart37. Just sayin'.
Consider it done! ;D
EDIT:
Actually I found out each slot header has a game ID and a product code. And, yay, I revealed the NTCS codes.
PAL disc 1 = BESLES-02965
PAL disc 2 = BESLES-12965
PAL disc 3 = BESLES-22965
PAL disc 4 = BESLES-32965
NTSC disc 1 = BASLUS-01251
NTSC disc 2 = BASLUS-01295
NTSC disc 3 = BASLUS-01296
NTSC disc 4 = BASLUS-01297
Game ID = 00000-[saveNr on memcard]
If someone has any info. on the psx memcard structure I would be glad.
-
Question 3: How do the NTSC codes look like, and are there other versions (Spanish, German, etc)?
It would be great if there only were 2 versions of this: NTSC and PAL. :-D
There's seven different versions of the game, here's the disc ID's for them all:
Japanese SLPS_020.00, SLPS_020.01, SLPS_020.02, SLPS_020.03
English (USA) SLUS_012.51, SLUS_012.95, SLUS_012.96, SLUS_012.97
English (EU) SLES_029.65, SLES_129.65, SLES_229.65, SLES_329.65
French SLES_029.66, SLES_129.66, SLES_229.66, SLES_329.66
German SLES_029.67, SLES_129.67, SLES_229.67, SLES_329.67
Italian SLES_029.68, SLES_129.68, SLES_229.68, SLES_329.68
Spanish SLES_029.69, SLES_129.69, SLES_229.69, SLES_329.69
For the savefiles, the first 2 characters (region code?) in the ID is BE for all european discs (eg. BESLES-02965), BA for the USA ones (eg. BASLUS-01251), and BI for the japanese ones (eg. BISLPS-02000). And they all use the same save file format structure (but the japanese version uses a different character table (http://wiki.qhimm.com/FF9/CharTables) for strings of course), which is probably also the reason サラマンダー wasn't called Salamander (but Amarant) outside of Japan as it doesn't fit the 8 byte character name limit.
-
Thanks Zande, this'll help. I figured those BEs and BAs where regional stuff. The stuff you listed are the product codes, while the rest of the header info (I know of) are the game-IDs (the end is not slot# as I typed in the opening post, but part of the game id).
EDIT:
ok, so I've compared some saves and figured out the bitmap at the end of the character data. Each ability equipped has a flag, and it looks like this (must not be read as memory, but like you would see it in an hex editor):
Auto-Potion 00 00 00 00 00 00 00 01
Locomotion 00 00 00 00 00 00 00 02
Clear Headed 00 00 00 00 00 00 00 04
Boost 00 00 00 00 00 00 00 08
Odin's Sword 00 00 00 00 00 00 00 10
Mug 00 00 00 00 00 00 00 20
Bandit 00 00 00 00 00 00 00 40
?????? 00 00 00 00 00 00 00 80
Insomniac 00 00 00 00 00 00 01 00
Antibody 00 00 00 00 00 00 02 00
Bright Eyes 00 00 00 00 00 00 04 00
Loudmouth 00 00 00 00 00 00 08 00
Restore HP 00 00 00 00 00 00 10 00
Jelly 00 00 00 00 00 00 20 00
Return Magic 00 00 00 00 00 00 40 00
Absorb MP 00 00 00 00 00 00 80 00
Body Temp 00 00 00 00 00 01 00 00
Alert 00 00 00 00 00 02 00 00
Initiative 00 00 00 00 00 04 00 00
Level Up 00 00 00 00 00 08 00 00
Ability Up 00 00 00 00 00 10 00 00
Millionaire 00 00 00 00 00 20 00 00
Flee-Gil 00 00 00 00 00 40 00 00
Guardian Mog 00 00 00 00 00 80 00 00
Mag Elem Null 00 00 00 00 01 00 00 00
Concentrate 00 00 00 00 02 00 00 00
Half MP 00 00 00 00 04 00 00 00
High Tide 00 00 00 00 08 00 00 00
Counter 00 00 00 00 10 00 00 00
Cover 00 00 00 00 20 00 00 00
Protect Girls 00 00 00 00 40 00 00 00
Eye 4 Eye 00 00 00 00 80 00 00 00
Healer 00 00 00 01 00 00 00 00
Add Status 00 00 00 02 00 00 00 00
Gamble Defence 00 00 00 04 00 00 00 00
?????? 00 00 00 08 00 00 00 00
Power Throw 00 00 00 10 00 00 00 00
Power Up 00 00 00 20 00 00 00 00
Reflect-Null 00 00 00 40 00 00 00 00
Reflectx2 00 00 00 80 00 00 00 00
Undead Killer 00 00 01 00 00 00 00 00
Dragon Killer 00 00 02 00 00 00 00 00
Devil Killer 00 00 04 00 00 00 00 00
Beast killer 00 00 08 00 00 00 00 00
Man Eater 00 00 10 00 00 00 00 00
High Jump 00 00 20 00 00 00 00 00
Master Thief 00 00 40 00 00 00 00 00
Steal Gil 00 00 80 00 00 00 00 00
MP+20% 00 01 00 00 00 00 00 00
Accuracy+ 00 02 00 00 00 00 00 00
Distract 00 04 00 00 00 00 00 00
Long Reach 00 08 00 00 00 00 00 00
MP Attack 00 10 00 00 00 00 00 00
Bird Killer 00 20 00 00 00 00 00 00
Bug Killer 00 40 00 00 00 00 00 00
Stone Killer 00 80 00 00 00 00 00 00
Auto-Reflect 01 00 00 00 00 00 00 00
Auto-Float 02 00 00 00 00 00 00 00
Auto-Haste 04 00 00 00 00 00 00 00
Auto-Regen 08 00 00 00 00 00 00 00
Auto-Life 10 00 00 00 00 00 00 00
HP+10% 20 00 00 00 00 00 00 00
HP+20% 40 00 00 00 00 00 00 00
MP+10% 80 00 00 00 00 00 00 00
?????? 00 00 00 00 00 00 00 80
?????? 00 00 00 08 00 00 00 00
Can someone see what abilities I've might forgotten (the ? ?? ?? ?, 2 of 'em). Or maybe they're dummied out? I can't check 'em be setting these flags 'cause then I get a bad checksum. sigh, I gotta figure out that checksum out too. As I see it, it's not a crc, but a 2 byte checksum at offset 0x13FE. As far as I'm concerned, FF9 doesn't use any space from 0x1400 and out.
-
Those are all abilities in file 08_04a.txt.... (dont ask what the hell that name means!)
this is how they are sorted in data file
Ability Up
Absorb MP
Accuracy+
Add Status
Alert
Antibody
Auto-Float
Auto-Haste
Auto-Life
Auto-Potion
Auto-Reflect
Auto-Regen
Bandit
Beast Killer
Bird Killer
Body Temp
Boost
Bright Eyes
Bug Killer
Chemist
Clear Headed
Concentrate
Counter
Cover
Devil Killer
Distract
Dragon Killer
Eye 4 Eye
Flee-Gil
Gamble Defense
Guardian Mog
HP+10%
HP+20%
Half MP
Healer
High Jump
High Tide
Initiative
Insomniac
Jelly
Level Up
Locomotion
Long Reach
Loudmouth
MP Attack
MP+10%
MP+20%
Mag Elem Null
Man Eater
Master Thief
Millionaire
Mug
Odin’s Sword
Power Throw
Power Up
Protect Girls
Reflect-Null
Reflectx2
Restore HP
Return Magic
Steal Gil
Stone Killer
Undead Killer
Void
Missing
Chemist
Void
Supposing that Void is not ability, but void place..... (played only start of FF9)
In mine NTSC version is 'Gamble Defense' (you have CE in end:P)
header file in SLUS_012.51
0x5D92A 0x5DB2A, format 2B pointer 6B other chaos, maybe useful for something...
data file in file 0008 (9th file in first folder....:P) 0x8640 0x88D4
-
Missing
Chemist
Void
Supposing that Void is not ability, but void place..... (played only start of FF9)
In mine NTSC version is 'Gamble Defense' (you have CE in end:P)
Thanks! I thought I had chemist... Must've forgotten. And, yes, it's defenCE in the PAL version. British English vs US English.
header file in SLUS_012.51
0x5D92A 0x5DB2A, format 2B pointer 6B other chaos, maybe useful for something...
data file in file 0008 (9th file in first folder....:P) 0x8640 0x88D4
I'm not sure what you mean here. Inside the .img file on that disc?
oh, and anyone got any intel (lol) on how the checksum/crc?
-
Great, great stuff ! And might I suggest 'Memoria' for its name ? I think it'd be fitting for a FFIX save editor.
-
So, the save wasn't compressed / encrypted? I just assuned it'd be a complete arseache to decode.
I may be able to help with this.
-
Great, great stuff ! And might I suggest 'Memoria' for its name ? I think it'd be fitting for a FFIX save editor.
Best suggestion so far imo. Using an app called memoria sounds epic in some way...
So, the save wasn't compressed / encrypted? I just assuned it'd be a complete arseache to decode.
I may be able to help with this.
No, as far as I'm concerned there's no compression. At least not the parts I've figured out. Besides, FF9 don't use any space after offset 0x13FF. I have downloaded some saves that have some data after this point, but when I erase this data (zero it to hell), the save is still 100% usable with no difference in-game, no error message when loading the game etc.
I dunno how anybody got data here, 'cause when I play with these saves, and overwrite 'em, I only get zeroes after 0x13FF.
Even when you slightly alter the block's title you get an error (not the header, but the title inside the actual save data), thus I highly doubt FF9 use any space after 0x13FF. Which leads to my conclusion that there is no reason for compression.
I must admit the card-section of the savemap seems encrypted. Either that or the cards are stored in a different order in the savemap. I'm yet to figure it out, but it would help to get the checksum right first. Then It'll be way easier to see differences when I save with my editor and play the game.
-
Hunch? I'd guess there were a set of "Seen card?" flags and then data for the quantities.
I'd like to see how that data changes when you pick up and lose cards.
-
header file in SLUS_012.51
0x5D92A 0x5DB2A, format 2B pointer 6B other chaos, maybe useful for something...
data file in file 0008 (9th file in first folder....:P) 0x8640 0x88D4
I'm not sure what you mean here. Inside the .img file on that disc?
Yeah, that's inside .img file.
I thought that .img file structure is 'common' knowledge since it was long time ago posted here by someone (maybe in wiki too).
Its structure is something like 'folders and files' - that's why I refer to '9th file in first folder'.
There was some extractor of .img file posted here, seen in q-gears svn if I am not wrong.
(But it lack import, which I needed, so I made one myself:P)
-
I just wanted to say good luck on the save file editor. If you get far enough that it can change an attribute and save back to a useable save file, maybe we can get an alpha/beta to play with? I think the hardest part of these type of editors is always checksum related. Some other game I was reading about stored the checksum twice, and it was a checksum of only part of the save file. I think 90% of the developement time was checksum related.
-
(http://www.imagenerd.com/uploads/ff9se-k2ltu.jpg)
Ok, a small update as I've started working on it again.
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. :-\
-
Looking really nice gjoerulv :)
Would it be possible to make a 'hard mode' by modding the save files in any way?
I ask because I did this with FFX, by lowing the stats you earn as you progress.
Obviously FFIX has a totally different system, but I would so love to make it more difficult!
Any ideas?
-
The savemap (parts of it) is basically the only knowledge I have 'bout the "inner workings of FF9" lol, so besides lowering stats, level, etch, nothing else pops out from the top of my head.
I would love to have a hard mod for FF9. Any FF needs a hard mode. If I am to make a FF9 enemy editor it's name will certainly be Garland. :evil:
-
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
(http://www.imagenerd.com/uploads/ff9_mog_poop-Ampeo.jpg)
-
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.
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.
-
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:.
-
Agreed. I've really been enjoying working with Qt recently.
-
I don't care much how the interface looks, as long as it is user friendly. I was going to use bloodshed, but I'm curious on qt so I'll try it.
For further discussion please go here: http://forums.qhimm.com/index.php?topic=11494.0 (http://forums.qhimm.com/index.php?topic=11494.0)
-
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:
1000 0000 (Status flag, whatever this means)
OR
0100 0000 (Zombie flag)
=
1100 0000 (192)
Then Petrification + Venom should be 131.
1000 0000 (Status flag, again, whatever that means)
OR
0000 0001 (Petrification flag)
OR
0000 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.