gcmm/source/mcard.c
2012-09-28 13:20:06 +00:00

936 lines
24 KiB
C

/****************************************************************************
* Memory Card
*
* Supporting functions.
*
* MAXFILEBUFFER is set to 2MB as it's the largest file I've seen.
*
* CARDBACKUP FILE
*
* 0 - Copy of CardDir
* 64 - Copy of CardStatus
* 192 - Memory Card File Data
****************************************************************************/
#include <gccore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "card.h"
#include "mcard.h"
#include "gci.h"
#include "freetype.h"
/*** Memory Card Work Area ***/
static u8 SysArea[CARD_WORKAREA] ATTRIBUTE_ALIGN (32);
/*** Memory File Buffer ***/
#define MAXFILEBUFFER (1024 * 2048) /*** 2MB Buffer ***/
u8 FileBuffer[MAXFILEBUFFER] ATTRIBUTE_ALIGN (32);
u8 CommentBuffer[64] ATTRIBUTE_ALIGN (32);
u16 tlut[9][256] ATTRIBUTE_ALIGN (32);
u16 tlutbanner[256] ATTRIBUTE_ALIGN (32);
u8 icondata[8][1024] ATTRIBUTE_ALIGN (32);
u16 icondataRGB[8][1024] ATTRIBUTE_ALIGN (32);
/*** This array holds the 16-bit banner data for the current save
Needs decoding by bannerloadRGB function before we can show it ***/
u16 bannerdata[CARD_BANNER_W*CARD_BANNER_H] ATTRIBUTE_ALIGN (32);
/*** This array holds the 8-bit banner data for the current save
Needs decoding by bannerloadCI function before we can show it ***/
u8 bannerdataCI[CARD_BANNER_W*CARD_BANNER_H] ATTRIBUTE_ALIGN (32);
int numicons;
int frametable[8];
int lastframe;
/*** This matrix will serve as our array of filenames for each file on the card
We add 10 to filenamelen since we add on game company info***/
u8 filelist[1024][1024];
int maxfile;
extern int cancel;
extern bool offsetchanged;
/*** Card lib ***/
card_dir CardList[CARD_MAXFILES]; /*** Directory listing ***/
static card_dir CardDir;
static card_file CardFile;
static card_stat CardStatus;
static int cardcount = 0;
static u8 permission;
s32 memsize, sectsize;
GCI gci;
//The following code is made by Ralf at GSCentral forums (gscentral.org)
//http://board.gscentral.org/retro-hacking/53093.htm#post188949
s32 FZEROGX_MakeSaveGameValid(s32 chn);
s32 PSO_MakeSaveGameValid(s32 chn);
/*---------------------------------------------------------------------------------
This function is called if a card is physically removed
---------------------------------------------------------------------------------*/
void card_removed(s32 chn,s32 result)
{
if (chn == CARD_SLOTA){
//printf("Card was removed from slot A");
}
else{
//printf("Card was removed from slot B");
}
CARD_Unmount(chn);
}
/****************************************************************************
* MountCard
*
* Mounts the memory card in the given slot.
* CARD_Mount is called for a maximum of 10 tries
* Returns the result of the last attempted CARD_Mount command.
***************************************************************************/
int MountCard(int cslot)
{
s32 ret = -1;
int tries = 0;
int isMounted;
// Mount the card, try several times as they are tricky
while ( (tries < 10) && (ret < 0))
{
/*** We already initialized the Memory Card subsystem with CARD_Init() in select_memcard_slot(). Let's reset the
EXI subsystem, just to make sure we have no problems mounting the card ***/
EXI_ProbeReset();
CARD_Init (NULL, NULL);
/*** Mount the card ***/
ret = CARD_Mount (cslot, SysArea, card_removed);
if (ret >= 0) break;
VIDEO_WaitVSync ();
tries++;
}
/*** Make sure the card is really mounted ***/
isMounted = CARD_ProbeEx(cslot, &memsize, &sectsize);
if (memsize > 0 && sectsize > 0)//then we really mounted de card
{
return isMounted;
}
/*** If this point is reached, something went wrong ***/
return ret;
}
/****************************************************************************
* GCIMakeHeader
*
* Create a GCI compatible header from libOGC card_stat
****************************************************************************/
void GCIMakeHeader()
{
/*** Clear out the cgi header ***/
memset(&gci, 0xff, sizeof(GCI) );
/*** Populate ***/
memcpy(&gci.gamecode, &CardStatus.gamecode, 4);
memcpy(&gci.company, &CardStatus.company, 2);
gci.banner_fmt = CardStatus.banner_fmt;
memcpy(&gci.filename, &CardStatus.filename, CARD_FILENAMELEN);
gci.time = CardStatus.time;
gci.icon_addr = CardStatus.icon_addr;
gci.icon_fmt = CardStatus.icon_fmt;
gci.icon_speed = CardStatus.icon_speed;
/*** Permission key has to be gotten separately. Make it 0 for normal privileges ***/
gci.unknown1 = permission;
/*** Who cares about copy counter ***/
gci.unknown2 = 0;
/*** Block index does not matter, it won't be restored at the same spot ***/
gci.index = 32;
gci.filesize8 = (CardStatus.len / 8192);
gci.comment_addr = CardStatus.comment_addr;
/*** Copy to head of buffer ***/
memcpy(FileBuffer, &gci, sizeof(GCI));
}
/****************************************************************************
* ExtractGCIHeader
*
* Extract a GCI Header to libOGC card_stat
****************************************************************************/
void ExtractGCIHeader()
{
/*** Clear out the status ***/
memset(&CardStatus, 0, sizeof(card_stat));
memcpy(&gci, FileBuffer, sizeof(GCI));
memcpy(&CardStatus.gamecode, &gci.gamecode, 4);
memcpy(&CardStatus.company, &gci.company, 2);
CardStatus.banner_fmt = gci.banner_fmt;
memcpy(&CardStatus.filename, &gci.filename, CARD_FILENAMELEN);
CardStatus.time = gci.time;
CardStatus.icon_addr = gci.icon_addr;
CardStatus.icon_fmt = gci.icon_fmt;
CardStatus.icon_speed = gci.icon_speed;
permission = gci.unknown1;
CardStatus.len = gci.filesize8 * 8192;
CardStatus.comment_addr = gci.comment_addr;
}
/****************************************************************************
* CardGetDirectory
*
* Returns number of files found on a card.
****************************************************************************/
int CardGetDirectory (int slot)
{
int err;
char company[4];
char gamecode[6];
company[2] = gamecode[4] = 0;
/*** Clear the work area ***/
memset (SysArea, 0, CARD_WORKAREA);
/*** Initialise the Card system, show all ***/
CARD_Init(NULL, NULL);
/*** Try to mount the card ***/
err = MountCard(slot);
if (err < 0)
{
WaitCardError("CardMount", err);
return 0; /*** Unable to mount the card ***/
}
/*** Retrieve the directory listing ***/
cardcount = 0;
err = CARD_FindFirst (slot, &CardDir, true); //true means we want to showall
while (err != CARD_ERROR_NOFILE)
{
memcpy (&CardList[cardcount], &CardDir, sizeof (card_dir));
memset (filelist[cardcount], 0, 1024);
memcpy (company, &CardDir.company, 2);
memcpy (gamecode, &CardDir.gamecode, 4);
//This array will store what will show in left window
sprintf ((char*)filelist[cardcount], "%s-%s-%s", company, gamecode, CardDir.filename);
cardcount++;
err = CARD_FindNext (&CardDir);
}
/*** Release as soon as possible ***/
CARD_Unmount (slot);
maxfile = cardcount;
return cardcount;
}
/****************************************************************************
* CardListFiles
****************************************************************************/
void CardListFiles ()
{
int i;
char company[4];
char gamecode[6];
company[2] = 0;
gamecode[4] = 0;
for (i = 0; i < cardcount; i++)
{
memcpy (company, &CardList[i].company, 2);
memcpy (gamecode, &CardList[i].gamecode, 4);
printf ("%s %s %s\n", company, gamecode, CardList[i].filename);
}
}
/****************************************************************************
* CardReadFileHeader
*
* Retrieve a file header from the previously populated list.
* Reads in banner and icon data now
****************************************************************************/
//TODO: get icon and banner settings
int CardReadFileHeader (int slot, int id)
{
int bytesdone = 0;
int err;
u32 SectorSize;
char company[4];
char gamecode[6];
int filesize;
int i;
u16 check_fmt, check_speed;
if (id >= cardcount)
{
WaitPrompt("Bad id");
return 0; /*** Bad id ***/
}
/*** Clear the work buffers ***/
memset (FileBuffer, 0, MAXFILEBUFFER);
memset (CommentBuffer, 0, 64);
memset (SysArea, 0, CARD_WORKAREA);
company[2] = 0;
gamecode[4] = 0;
memcpy (company, &CardList[id].company, 2);
memcpy (gamecode, &CardList[id].gamecode, 4);
/*** Initialise for this company ***/
CARD_Init (gamecode, company);
/*** Mount the card ***/
err = MountCard(slot);
if (err < 0)
{
WaitCardError("CardMount", err);
return 0; /*** Unable to mount the card ***/
}
/*** Retrieve sector size ***/
CARD_GetSectorSize (slot, &SectorSize);
/*** Open the file ***/
err = CARD_Open (slot, (char *) &CardList[id].filename, &CardFile);
if (err < 0)
{
CARD_Unmount (slot);
WaitCardError("CardOpen", err);
return 0;
}
#ifdef STATUSOGC
/*** Get card status info ***/
CARD_GetStatus (slot, CardFile.filenum, &CardStatus);
CARD_GetAttributes(slot,CardFile.filenum, &permission);
GCIMakeHeader();
#else
//get directory entry (same as gci header, but with all the data)
memset(&gci,0,sizeof(GCI));
__card_getstatusex(slot,CardFile.filenum,&gci);
/*** Copy to head of buffer ***/
memcpy(FileBuffer, &gci, sizeof(GCI));
#endif
/*** Copy the file contents to the buffer ***/
filesize = CardFile.len;
while (bytesdone < filesize)
{
CARD_Read (&CardFile, FileBuffer + MCDATAOFFSET + bytesdone, SectorSize, bytesdone);
bytesdone += SectorSize;
}
/***
Get the Banner/Icon Data from the memory card file.
Very specific if/else setup to minimize data copies.
***/
u8* offset = FileBuffer + MCDATAOFFSET + gci.icon_addr;
/*** Get the Banner/Icon Data from the save file ***/
if ((gci.banner_fmt&CARD_BANNER_MASK) == CARD_BANNER_RGB) {
//RGB banners are 96*32*2 in size
memcpy(bannerdata, offset, 6144);
offset += 6144;
}
else if ((gci.banner_fmt&CARD_BANNER_MASK) == CARD_BANNER_CI) {
memcpy(bannerdataCI, offset, 3072);
offset += 3072;
memcpy(tlutbanner, offset, 512);
offset += 512;
}
//Icon data
check_fmt = gci.icon_fmt;
check_speed = gci.icon_speed;
int shared_pal = 0;
lastframe = 0;
numicons = 0;
for (i=0;i<8;i++){
//this stores if the frame has a real icon or not
//no need to clear all values since we will only use the ones until last frame
frametable[i] = 0;
//Animation speed is mandatory to be set even for a single icon
//When a speed is 0 there are no more icons
//Some games may have bits set after the "blank icon" both in
//speed (Baten Kaitos) and format (Wario Ware Inc.) bytes, which are just garbage
if (!(check_speed&CARD_ICON_MASK)){
break;
}else
{//We've got a frame
lastframe=i;
if (check_fmt & CARD_ICON_MASK){
//count the number of real icons
numicons++;
frametable[i]=1; //There's a real icon
//CI with shared palette
if ((check_fmt&CARD_ICON_MASK) == 1) {
memcpy(icondata[i], offset, 1024);
offset += 1024;
shared_pal = 1;
}
//CI with palette after the icon
else if ((check_fmt&CARD_ICON_MASK) == 3)
{
memcpy(icondata[i], offset, 1024);
offset += 1024;
memcpy(tlut[i], offset, 512);
offset += 512;
}
//RGB 16 bit icon
else if ((check_fmt&CARD_ICON_MASK) == 2)
{
memcpy(icondataRGB[i], offset, 2048);
offset += 2048;
}
}
}
check_fmt = check_fmt >> 2;
check_speed = check_speed >> 2;
}
//Get the shared palette
if (shared_pal) memcpy(tlut[8], offset, 512);
/*** Get the comment (two 32 byte strings) into buffer ***/
memcpy(CommentBuffer, FileBuffer + MCDATAOFFSET + gci.comment_addr, 64);
/*** Close the file ***/
CARD_Close (&CardFile);
/*** Unmount the card ***/
CARD_Unmount (slot);
return filesize + MCDATAOFFSET;
}
/****************************************************************************
* CardReadFile
*
* Retrieve a file from the previously populated list.
* Place in filebuffer space, for collection by SMB write.
****************************************************************************/
int CardReadFile (int slot, int id)
{
int bytesdone = 0;
int err;
u32 SectorSize;
char company[4];
char gamecode[6];
int filesize;
if (id >= cardcount)
{
WaitPrompt("Bad id");
return 0; /*** Bad id ***/
}
/*** Clear the work buffers ***/
memset (FileBuffer, 0, MAXFILEBUFFER);
memset (SysArea, 0, CARD_WORKAREA);
company[2] = 0;
gamecode[4] = 0;
memcpy (company, &CardList[id].company, 2);
memcpy (gamecode, &CardList[id].gamecode, 4);
/*** Initialise for this company ***/
CARD_Init (gamecode, company);
/*** Mount the card ***/
err = MountCard(slot);
if (err < 0)
{
WaitCardError("CardMount", err);
return 0; /*** Unable to mount the card ***/
}
/*** Retrieve sector size ***/
CARD_GetSectorSize (slot, &SectorSize);
/*** Open the file ***/
err = CARD_Open (slot, (char *) &CardList[id].filename, &CardFile);
if (err < 0)
{
CARD_Unmount (slot);
WaitCardError("CardOpen", err);
return 0;
}
#ifdef STATUSOGC
/*** Get card status info ***/
CARD_GetStatus (slot, CardFile.filenum, &CardStatus);
CARD_GetAttributes(slot,CardFile.filenum, &permission);
GCIMakeHeader();
#else
//get directory entry (same as gci header, but with all the data)
memset(&gci,0,sizeof(GCI));
__card_getstatusex(slot,CardFile.filenum,&gci);
/*** Copy to head of buffer ***/
memcpy(FileBuffer, &gci, sizeof(GCI));
#endif
/*** Copy the file contents to the buffer ***/
filesize = CardFile.len;
while (bytesdone < filesize)
{
CARD_Read (&CardFile, FileBuffer + MCDATAOFFSET + bytesdone, SectorSize, bytesdone);
bytesdone += SectorSize;
}
/*** Close the file ***/
CARD_Close (&CardFile);
/*** Unmount the card ***/
CARD_Unmount (slot);
return filesize + MCDATAOFFSET;
}
/****************************************************************************
* CardWriteFile
*
* Relies on *GOOD* data being placed in the FileBuffer prior to calling.
* See ReadSMBImage
****************************************************************************/
int CardWriteFile (int slot)
{
char company[4];
char gamecode[6];
char filename[CARD_FILENAMELEN];
int err, ret;
u32 SectorSize;
int offset;
int written;
int filelen;
company[2] = gamecode[4] = 0;
memset (SysArea, 0, CARD_WORKAREA);
memset(filename, 0, CARD_FILENAMELEN);
ExtractGCIHeader();
memcpy(company, &gci.company, 2);
memcpy(gamecode, &gci.gamecode, 4);
memcpy(filename, &gci.filename, CARD_FILENAMELEN);
filelen = gci.filesize8 * 8192;
/*** Init system ***/
CARD_Init(gamecode, company);
/*** Mount the card ***/
err = MountCard(slot);
if (err < 0)
{
WaitCardError("CardMount", err);
return 0; /*** Unable to mount the card ***/
}
CARD_GetSectorSize (slot, &SectorSize);
/*** If this file exists, abort ***/
err = CARD_FindFirst (slot, &CardDir, false);
while (err != CARD_ERROR_NOFILE)
{
if (((u32)CardDir.gamecode == (u32)gamecode) && (strcmp ((char *) CardDir.filename, (char *)filename) == 0))
{
/*** Found the file - prompt user ***/
ret = WaitPromptChoice("File already exists. Overwrite?", "Overwrite", "Cancel");
if (!ret){
ret = WaitPromptChoiceAZ("Are you -SURE- you want to overwrite?", "Overwrite", "Cancel");
if(!ret){
err = CARD_Delete(slot, (char *) &filename);
if (err < 0)
{
WaitCardError("MCDel", err);
CARD_Unmount (slot);
return 0;
}
err = CARD_FindFirst (slot, &CardDir, false);
continue;
}
}
/*** User canceled - abort ***/
CARD_Unmount (slot);
WaitCardError("File already exists", err);
return 0;
}
err = CARD_FindNext (&CardDir);
}
//This is needed for propper restoring
CARD_SetCompany(company);
CARD_SetGamecode(gamecode);
tryagain:
/*** Now restore the file from backup ***/
err = CARD_Create (slot, (char *) filename, filelen, &CardFile);
if (err < 0)
{
if (err == CARD_ERROR_EXIST)
{
/*** Found the file - prompt user ***/
ret = WaitPromptChoice("File already exists. Overwrite?", "Overwrite", "Cancel");
if (!ret){
ret = WaitPromptChoiceAZ("Are you -SURE- you want to overwrite?", "Overwrite", "Cancel");
if(!ret){
err = CARD_Delete(slot, (char *) &filename);
if (err < 0)
{
WaitCardError("MCDel", err);
CARD_SetCompany(NULL);
CARD_SetGamecode(NULL);
CARD_Unmount (slot);
return 0;
}
goto tryagain;
}
}
}
//Return To show all so we don't have errors
CARD_SetCompany(NULL);
CARD_SetGamecode(NULL);
CARD_Unmount (slot);
WaitCardError("CardCreate", err);
return 0;
}
//Thanks to Ralf, validate F-zero and PSO savegames
FZEROGX_MakeSaveGameValid(slot);
PSO_MakeSaveGameValid(slot);
/*** Now write the file data, in sector sized chunks ***/
offset = 0;
while (offset < filelen)
{
if ((offset + SectorSize) <= filelen)
{
written = CARD_Write (&CardFile, FileBuffer + MCDATAOFFSET + offset + OFFSET, SectorSize, offset);
}
else
{
written = CARD_Write (&CardFile, FileBuffer + MCDATAOFFSET + offset + OFFSET, ((offset + SectorSize) - filelen), offset);
}
offset += SectorSize;
}
OFFSET = 0;
#ifdef STATUSOGC
/*** Finally, update the status ***/
CARD_SetStatus (slot, CardFile.filenum, &CardStatus);
//For some reason this sets the file to Move->allowed, Copy->not allowed, Public file instead of the actual permission value
CARD_SetAttributes(slot, CardFile.filenum, &permission);
#else
__card_setstatusex(slot, CardFile.filenum, &gci);
#endif
//Return To show all so we don't have errors
CARD_SetCompany(NULL);
CARD_SetGamecode(NULL);
CARD_Close (&CardFile);
CARD_Unmount (slot);
return 1;
}
void WaitCardError(char *src, int error)
{
char msg[1024], err[256];
//Error message possibilites
switch (error)
{
case CARD_ERROR_BUSY:
sprintf(err, "Busy");
break;
case CARD_ERROR_WRONGDEVICE:
sprintf(err, "Wrong device in slot");
break;
case CARD_ERROR_NOCARD:
sprintf(err, "No Card");
break;
case CARD_ERROR_NOFILE:
sprintf(err, "No File");
break;
case CARD_ERROR_IOERROR:
sprintf(err, "Internal EXI I/O error");
break;
case CARD_ERROR_BROKEN:
sprintf(err, "File/Dir entry broken");
break;
case CARD_ERROR_EXIST:
sprintf(err, "File already exists");
break;
case CARD_ERROR_NOENT:
sprintf(err, "No empty blocks");
break;
case CARD_ERROR_INSSPACE:
sprintf(err, "Not enough space");
break;
case CARD_ERROR_NOPERM:
sprintf(err, "No permission");
break;
case CARD_ERROR_LIMIT:
sprintf(err, "Card size limit reached");
break;
case CARD_ERROR_NAMETOOLONG:
sprintf(err, "Filename too long");
break;
case CARD_ERROR_ENCODING:
sprintf(err, "Font encoding mismatch");
break;
case CARD_ERROR_CANCELED:
sprintf(err, "Card operation cancelled");
break;
case CARD_ERROR_FATAL_ERROR:
sprintf(err, "Fatal error");
break;
case CARD_ERROR_READY:
sprintf(err, "Ready");
break;
default:
sprintf(err, "Unknown");
break;
}
//Here we build the full error message
sprintf(msg, "MemCard Error: %s - %d %s", src,error, err);
WaitPrompt(msg);
}
void MC_DeleteMode(int slot)
{
int memitems, err;
int selected = 0;
int erase;
clearRightPane();
DrawText(390,130,"D e l e t e M o d e");
writeStatusBar("Choose a file with UP button or DOWN button ", "Press A button to delete ") ;
char msg[1024];
/*** Get the directory listing from the memory card ***/
memitems = CardGetDirectory (slot);
/*** If it's a blank card, get out of here ***/
if (!memitems)
{
WaitPrompt ("No saved games to delete !");
}
else
{
while(1)
{
// TODO: implement showselector
selected = ShowSelector(1);
if (cancel)
{
WaitPrompt ("Delete action cancelled !");
return;
}
//0 = B wass pressed -> delete the file
erase = WaitPromptChoice("Are you sure you want to delete the file?", "Delete", "Cancel");
if (!erase)
{
// selected = 1;
/*** Delete the file ***/
sprintf(msg, "Deleting \"%s\"", CardList[selected].filename);
writeStatusBar(msg,"");
//WaitPrompt(msg);
CARD_Init((const char*)CardList[selected].gamecode, (const char*)CardList[selected].company);
/*** Try to mount the card ***/
err = MountCard(slot);
if (err < 0)
{
WaitCardError("MCDel Mount", err);
return; /*** Unable to mount the card ***/
}
err = CARD_Delete(slot, (char *) &CardList[selected].filename);
if (err < 0)
{
WaitCardError("MCDel", err);
}
else
{
WaitPrompt("Delete complete");
}
CARD_Unmount(slot);
return;
}
offsetchanged = true;
}
}
}
void MC_FormatMode(s32 slot)
{
int erase = 1;
int err;
//0 = B wass pressed -> ask again
if (!slot){
erase = WaitPromptChoice("Are you sure you want to format memory card in slot A?", "Format", "Cancel");
}else{
erase = WaitPromptChoice("Are you sure you want to format memory card in slot B?", "Format", "Cancel");
}
if (!erase){
if (!slot){
erase = WaitPromptChoiceAZ("All contents of memory card in slot A will be erased!", "Format", "Cancel");
}else{
erase = WaitPromptChoiceAZ("All contents of memory card in slot B will be erased!", "Format", "Cancel");
}
if (!erase)
{
/*** Try to mount the card ***/
err = MountCard(slot);
if (err < 0)
{
WaitCardError("MCFormat Mount", err);
return; /*** Unable to mount the card ***/
}
ShowAction("Formatting card...");
/*** Format the card ***/
CARD_Format(slot);
usleep(1000*1000);
/*** Try to mount the card ***/
err = MountCard(slot);
if (err < 0)
{
WaitCardError("MCFormat Mount", err);
return; /*** Unable to mount the card ***/
}else
{
WaitPrompt("Format completed successfully");
}
CARD_Unmount(slot);
return;
}
}
WaitPrompt("Format operation cancelled");
return;
}
//The following code is made by Ralf at GSCentral forums (gscentral.org)
//http://board.gscentral.org/retro-hacking/53093.htm#post188949
// u8 FileBuffer[MAXFILEBUFFER]: .gci file buffer
// MCDATAOFFSET: .gci header size (0x40 bytes)
/*************************************************************/
/* FZEROGX_MakeSaveGameValid */
/* (use just before writing a F-Zero GX system .gci file) */
/* */
/* chn: Destination memory card port */
/* ret: Error code */
/*************************************************************/
s32 FZEROGX_MakeSaveGameValid(s32 chn)
{
s32 ret;
u32 i,j;
u32 serial1,serial2;
u16 chksum = 0xFFFF;
if(stricmp(&FileBuffer[0x08],"f_zero.dat")!=0) return CARD_ERROR_READY; // check for F-Zero GX system file
if((ret=CARD_GetSerialNo(chn,&serial1,&serial2))<0) return ret; // get encrypted destination memory card serial numbers
*(u16*)&FileBuffer[0x2066+MCDATAOFFSET] = serial1 >> 16; // set new serial numbers
*(u16*)&FileBuffer[0x7580+MCDATAOFFSET] = serial2 >> 16;
*(u16*)&FileBuffer[0x2060+MCDATAOFFSET] = serial1 & 0xFFFF;
*(u16*)&FileBuffer[0x2200+MCDATAOFFSET] = serial2 & 0xFFFF;
for(i=0x02+MCDATAOFFSET;i<0x8000+MCDATAOFFSET;i++) { // calc 16-bit checksum
chksum ^= (FileBuffer[i]&0xFF);
for(j=8;j>0;j--) {
if (chksum&1) chksum = (chksum>>1)^0x8408;
else chksum >>= 1;
}
}
*(u16*)&FileBuffer[0x00+MCDATAOFFSET] = ~chksum; // set new checksum
return ret;
}
/***********************************************************/
/* PSO_MakeSaveGameValid */
/* (use just before writing a PSO system .gci file) */
/* */
/* chn: Destination memory card port */
/* ret: Error code */
/***********************************************************/
s32 PSO_MakeSaveGameValid(s32 chn)
{
s32 ret;
u32 i,j;
u32 chksum;
u32 crc32LUT[256];
u32 serial1,serial2;
u32 pso3offset;
if(stricmp(&FileBuffer[0x08],"PSO_SYSTEM")==0) { // check for PSO1&2 system file
pso3offset = 0x00;
goto exit;
}
if(stricmp(&FileBuffer[0x08],"PSO3_SYSTEM")==0) { // check for PSO3 system file
pso3offset = 0x10; // PSO3 data block size adjustment
goto exit;
}
return CARD_ERROR_READY; // nothing to do
exit:
if((ret=CARD_GetSerialNo(chn,&serial1,&serial2))<0) return ret; // get encrypted destination memory card serial numbers
*(u32*)&FileBuffer[0x2158+MCDATAOFFSET] = serial1; // set new serial numbers
*(u32*)&FileBuffer[0x215C+MCDATAOFFSET] = serial2;
for(i=0;i<256;i++) { // generate crc32 LUT
chksum = i;
for(j=8;j>0;j--) {
if (chksum&1) chksum = (chksum>>1)^0xEDB88320;
else chksum >>= 1;
}
crc32LUT[i] = chksum;
}
chksum = 0xDEBB20E3; // PSO initial crc32 value
for (i=0x204C+MCDATAOFFSET;i<0x2164+pso3offset+MCDATAOFFSET;i++) { // calc 32-bit checksum
chksum = ((chksum>>8)&0xFFFFFF)^crc32LUT[(chksum^FileBuffer[i])&0xFF];
}
*(u32*)&FileBuffer[0x2048+MCDATAOFFSET] = chksum^0xFFFFFFFF; // set new checksum
return ret;
}