Jump to content

Some questions about save files


Tux

Recommended Posts

Hi,

I'm currently developing a software which reads Pokémon save file data. Also, I have a few questions :

  • How does work the small/big blocks mechanics in a 256-kilobytes file ?
  • How does work the save file data in Generation III ?
  • Is the block footer structure the same beetween D/P/Pl/HG/SS/B/W ?
  • Where are located the ID, sID, 'Number of Party Pokémon', the Party Pokémon data, the PC Pokémon data in B/W? Same question for Generation III save file data.
  • Is something new about these save file informations beetween B/W and HG/SS ?
  • Is it the same blocks mechanics between B/W and HG/SS ? What are the blocks limits offsets in B/W ?

Tux.

Link to comment
Share on other sites

  • For a 256 KB save file, I imagine there would be no backup blocks. But the game was not meant to save files of this size, so I never bothered with them personally.
  • The Gen III save structure is sort of complicated... I am looking for the documentation I used to figure it out so I don't have to try to explain it (since I'm sure I'd only befuddle you if I tried).
    EDIT: Ah, look what a quick Google search yields: http://furlocks-forest.net/wiki/?page=Pokemon_GBA_Save_Format
  • The block footers are very similar between all Gen IV games... I think there are minor differences in HGSS, but nothing too complicated IIRC.
  • Your best bet for locating the PKM data in B/W is to just use a save file containing a few PKM you know the PID of, and opening the save file in HxD or some other hex program and doing a search for those PIDs.
  • Um, yes?
  • I don't think B/W switch the blocks around like Gen IV did, making it much more straightforward to find the "current" information. I dunno anything specific about the B/W save data though.

Also, don't forget that the Gen III PKM data looks different:

http://bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_data_structure_in_Generation_III

http://bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_data_substructures_in_Generation_III

Link to comment
Share on other sites

Thanks a lot ^^ !

Also, I'm unable to recalculate the save file checksums --' . I have readen your PKMDS vb.net library (by the way, this is a excellent and useful library) source code, and have transcripted the checksums algorithm in C++, but I get wrong checksums ...

For example, with 0 for begining block offset, and (0xC0EC + 20) for ending offset (inclusive) and 20 for the footer size, I get 0x5663 (or 0x6356) instead of 0x2306 (EDIT: 0x7A0A or 0x0A7A, offset 0xC0FE to 0xC0FF, sorry)

Here is a part my source code :

int  Savefile::getSeeds(int * seeds) //Output is the 'seeds' parameter array
   {


            int v0 = 0; //Signed 32-bit Integer
            int v1;
            int v2;
            int result;

           do
           {   v1 = v0 << 8;
               v2 = 0;
               do
               {    if ((unsigned char)(v1 >> 8 & 0x80) != 0) //unsigned char : unsigned 8-bit integer = Byte
                       v1 = (2 * v1) ^ 0x1021;
                   else
                       v1 *= 2;

                   v2 += 1;
               }
               while(v2 < 8);
               result = (unsigned short) v1; //Unsigned 16-bit integer
               seeds[v0] = result;
               v0 ++;
           }
           while(v0 <= 0xFF);
           return result;
   }
unsigned short calculateChecksum(FILE * savefile, unsigned int begin, unsigned int end, unsigned int footerSize)
   {
            int seeds[0xFF];
           getSeeds(seeds);
           unsigned char * data = new unsigned char[end-begin-footerSize];
           fseek(savefile, begin, SEEK_SET); //Set the cursor at the beginning of the savefile + begin
           fread(data, 1, (end-begin) - footerSize, savefile); //Read end-begin-footerSize bytes of data into the array 'data'

            int checksum = 0xFFFF;
            long long unsigned int i; //64-bit unsigned integer
            int v4 = end - footerSize;
            unsigned char v6;

           i = 0;
           while (v4 > 0)
           {
               v4 -= 1;
               v6 = (unsigned char)(data[i] ^ (unsigned char)(checksum >> 8));
               checksum = (checksum << 8) ^ seeds[v6];
               i++;
           }
    unsigned short retour = (unsigned short) checksum;
           delete[] data;
           // retour ^= 0;
           return retour;
   }

poke..sav

poke..sav

Edited by Tux
Link to comment
Share on other sites

I use my Pokémon Pearl's savefile.

I don't understand what I got wrong ... Is The temporary variable(initializated at 0xFFFF), which is used to calculate the checksum , a signed 32-bit integer ?

Which pairs of offsets (inclusive) should I use to calculate the checksum ?

Link to comment
Share on other sites

Issue fixed ! (my program's generated checksum and Pokegen's are the same)

The equivalent of the changes made in C++ are the following in VB .Net :

 seeds = New Integer(&H100) {} 'instead of :  seeds = New Integer(&H100 - 1) {}, line 13304 in PKMDSLIB.vb of your library
If (CType((v1 >> 8) And &H80, [byte]) And &H80) <> 0 Then ' Instead of  : If (CType(v1 >> 8, [byte]) And &H80) <> 0 Then, l. 13315, supposing CType() returns the 8 firts bits starting from right

Also, thanks a lot for your help and for your library :smile: .

PS : How is the checksum calculated in Gen III save files ?

Link to comment
Share on other sites

There are save file editors for gen 3, namely Pokemon Enciclopedia (spanish, translated on forums) and PokeStock (japanese).

PSavFix (by loadingNOW iirc) corrects the Chksum, here's the code:

####MAKEFILE####
#Change Dir to your GCC-bin dir

DIR   := C:/MinGW/bin/
CC      := $(DIR)gcc


# --- proj
PSavFix : Main.o 
$(CC) -o PSavFix Main.o

PSavFIX.o : Main.c
$(CC) -c -O3 Main Main.c
##END MAKEFILE##
---------------------------------------------------------------
####MAIN.C####
/*CODE C++*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int Chksum(int length, int *Data)
{
int Chk,i,tmp;
length = length>>2;
Chk=0;
for(i=0; i<length; i++)
	Chk += Data[i];

tmp = Chk>>16;
tmp +=Chk;

Chk = (tmp&0xFFFF);

return Chk;
}


int main(int argc, char** argv)
{


char *Map,*p,*header;
int *Imap,*Data;
int Found,id,fix,CHK,OK,i;
FILE *fpm, *fp;
short *MapPtr;

short FLMAP[] = { 0xF24,0xF80,0xF80,0xF80,0xEC0,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0x7D0,0x01C,0x100};
short RSMAP[] = { 0x890,0xF80,0xF80,0xF80,0xC40,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0x7D0,0xF80,0xF80};
short MAX[]   = { 0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80,0xF80};

OK   = 1;
fix  = 0;

if(argc!=4)
{
	printf("Usage: %s [-fix|-nofix] [-RS|-FL|-MAX] [infile]\n", argv[0]);
	return 0;
}


if(!strcmp(argv[2], "-RS"))
{
	printf("Running in RS Mode\r\n");
	MapPtr = &RSMAP[0];
}
else if(!strcmp(argv[2], "-FL"))
{
	printf("Running in FL Mode\r\n");
	MapPtr = &FLMAP[0];
}
else if(!strcmp(argv[2], "-MAX"))
{
	printf("Running in MAXCompat Mode - note this mode is NOT recommented\r\n");
	MapPtr = &MAX[0];
}
else
{
	printf("Please select a mode (-RS -FL -MAX). Read readme for more Information. \r\n");
	return 0;
}



// allocate main ram and load sav
header = (char *)malloc(0x1000);
Data = (int*)header;

printf("Loading: %s...\r\n", argv[3]);
fp = fopen(argv[3], "rb+");
if (!fp)
   {
	printf("error loading sav \r\n");
       return 0;
   }	


//check if fix is ON
if(!strcmp(argv[1], "-fix"))
{
	printf("Autofix: ON\r\n");
	fix = 1;
}
else
	printf("Autofix: OFF\r\n");



for(i = 0; i<0x20; i++)
{
	fread(header, 1, 0x1000, fp);

	CHK=Chksum(MapPtr[Data[1021]&0xF], Data);
	Found = (Data[1021]>>16)&0xFFFF;
	id = (Data[1021])&0xFF;

	if((CHK==Found && Data[1022]==0x8012025) || (id==0 && CHK==0 && Found==0))
		printf("Size: %03X Segment: %02X Calc: %04X Found: %04X Sig: %08X - OK\r\n",MapPtr[Data[1021]&0xF],id, CHK,Found, Data[1022]);
	else
	{
		printf("Size: %03X Segment: %02X Calc: %04X Found: %04X Sig: %08X \r\n",MapPtr[Data[1021]&0xF],id, CHK,Found, Data[1022]);
		OK = 0;
		if(fix)
		{
			fseek(fp, -0xa,SEEK_CUR);
			header[0xff6]= (0xFF &CHK);
			header[0xff7]= (0xFF &(CHK>>8));
			Data[1022]=0x8012025;		
			p = &header[0xff6];
			fwrite(p,1,6,fp);
			fseek(fp, 0x4,SEEK_CUR);
		}

	}
}

fclose(fp);

if(OK)
	printf("No Problems found \r\n");
else
	printf("Problems found!  (remember Sig=08012025)\r\n");

return 0;
}
##END MAIN.C##

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...