Jump to content

Recommended Posts

Posted

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.

Posted

  • 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

Posted (edited)

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
Posted

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 ?

Posted

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 ?

Posted

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##

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...