/**************************************************************************** * Memory Based Load/Save State Manager * * These are simply the state routines, brought together as GCxxxxx * The original file I/O is replaced with Memory Read/Writes to the * statebuffer below ****************************************************************************/ #include #include #include #include #include "../../types.h" #include "../../state.h" #include "saveicon.h" #include "intl.h" #define FCEUDIR "fceu" #define SAVEDIR "saves" /*** External functions ***/ extern void FCEUPPU_SaveState(void); extern void FCEUSND_SaveState(void); extern void WaitPrompt( char *text ); extern void FlipByteOrder(uint8 *src, uint32 count); extern void ShowAction( char *text ); extern void FCEUD_SetPalette(unsigned char index, unsigned char r, unsigned char g, unsigned char b); extern void FCEU_ResetPalette(void); extern void FCEUI_DisableSpriteLimitation( int a ); /*** External save structures ***/ extern SFORMAT SFCPU[]; extern SFORMAT SFCPUC[]; extern SFORMAT FCEUPPU_STATEINFO[]; extern SFORMAT FCEUCTRL_STATEINFO[]; extern SFORMAT FCEUSND_STATEINFO[]; extern SFORMAT SFMDATA[64]; extern u32 iNESGameCRC32; int CARDSLOT = CARD_SLOTA; #define RLSB 0x80000000 #define FILESIZEOFFSET 2116 unsigned char statebuffer[64 * 1024] ATTRIBUTE_ALIGN(32); /*** Never had one this big ! ***/ int sboffset; /*** Used as a basic fileptr ***/ int mcversion = 0x981211; static u8 SysArea[CARD_WORKAREA] ATTRIBUTE_ALIGN(32); extern u8 ChosenSlot; extern u8 ChosenDevice; extern u8 screenscaler; extern u8 currpal; extern u8 slimit; extern u8 timing; extern u8 mpads[6]; extern u8 FSDisable; extern u8 PADCAL; extern u8 PADTUR; extern u8 UseSDCARD; extern u8 UseWiiSDCARD; extern struct st_palettes { char *name, *desc; unsigned int data[64]; } *palettes; /**************************************************************************** * Memory based file functions ****************************************************************************/ /*** Open a file ***/ void memopen() { sboffset = 0; memset(statebuffer, 0, sizeof(statebuffer)); } /*** Close a file ***/ void memclose() { sboffset = 0; } /*** Write to the file ***/ void memfwrite( void *buffer, int len ) { if ( (sboffset + len ) > sizeof(statebuffer)) WaitPrompt("Buffer Exceeded"); if ( len > 0 ) { memcpy(&statebuffer[sboffset], buffer, len ); sboffset += len; } } /*** Read from a file ***/ void memfread( void *buffer, int len ) { if ( ( sboffset + len ) > sizeof(statebuffer)) WaitPrompt("Buffer exceeded"); if ( len > 0 ) { memcpy(buffer, &statebuffer[sboffset], len); sboffset += len; } } /**************************************************************************** * GCReadChunk * * Read the array of SFORMAT structures to memory ****************************************************************************/ int GCReadChunk( int chunkid, SFORMAT *sf ) { int csize; static char chunk[6]; int chunklength; int thischunk; char info[128]; memfread(&chunk, 4); memfread(&thischunk, 4); memfread(&chunklength, 4); if (strcmp(chunk, "CHNK") == 0) { if (chunkid == thischunk) { /*** Now decode the array of chunks to this one ***/ while (sf->v) { memfread(&chunk, 4); if ( memcmp(&chunk, "CHKE", 4) == 0 ) return 1; if (memcmp(&chunk, sf->desc, 4) == 0) { memfread(&csize, 4); if (csize == (sf->s & (~RLSB ))) { memfread( sf->v, csize ); sprintf(info,"%s %d", chunk, csize); } else { WaitPrompt("Bad chunk link"); return 0; } } else { sprintf(info, "No Sync %s %s", chunk, sf->desc); WaitPrompt(info); return 0; } sf++; } } else return 0; } else return 0; return 1; } /**************************************************************************** * GCFCEUSS_Load * * Reads the SFORMAT arrays ****************************************************************************/ int GCFCEUSS_Load() { int totalsize = 0; sboffset = 16 + sizeof(saveicon) + 64; /*** Reset memory file pointer ***/ memcpy(&totalsize, &statebuffer[FILESIZEOFFSET], 4); /*** Now read the chunks back ***/ if (GCReadChunk(1, SFCPU)) { if (GCReadChunk(2, SFCPUC)) { if (GCReadChunk(3, FCEUPPU_STATEINFO)) { if (GCReadChunk(4, FCEUCTRL_STATEINFO)) { if (GCReadChunk(5, FCEUSND_STATEINFO)) { if (GCReadChunk(0x10, SFMDATA)) return 1; } } } } } return 0; } /**************************************************************************** * GCSaveChunk * * Write the array of SFORMAT structures to the file ****************************************************************************/ int GCSaveChunk(int chunkid, SFORMAT *sf) { int chnkstart; int csize = 0; int chsize = 0; char chunk[] = "CHNK"; /*** Add chunk marker ***/ memfwrite(&chunk, 4); memfwrite(&chunkid, 4); chnkstart = sboffset; /*** Save ptr ***/ sboffset += 4; /*** Space for length ***/ csize += 12; /*** Now run through this structure ***/ while (sf->v) { /*** Check that there is a decription ***/ if ( sf->desc == NULL) break; /*** Write out the description ***/ memfwrite( sf->desc, 4); /*** Write the length of this chunk ***/ chsize = ( sf->s & (~RLSB) ); memfwrite( &chsize, 4); if ( chsize > 0 ) /*** Write the actual data ***/ memfwrite( sf->v, chsize ); csize += 8; csize += chsize; sf++; } /*** Update CHNK length ***/ memcpy(&statebuffer[chnkstart], &csize, 4); return csize; } /**************************************************************************** * GCFCEUSS_Save * * This is a modified version of FCEUSS_Save * It uses memory for it's I/O and has an added CHNK block. * The file is terminated with CHNK length of 0. ****************************************************************************/ int GCFCEUSS_Save() { int totalsize = 0; static unsigned char header[16] = "FCS\xff"; char chunk[] = "CHKE"; int zero = 0; char Comment[2][100] = { { MENU_CREDITS_TITLE }, { "A GAME" } }; memopen(); /*** Reset Memory File ***/ /*** Add version ID ***/ memcpy(&header[8], &mcversion, 4); /*** Do internal Saving ***/ FCEUPPU_SaveState(); FCEUSND_SaveState(); /*** Write Icon ***/ memfwrite(&saveicon, sizeof(saveicon)); totalsize += sizeof(saveicon); /*** And Comments ***/ sprintf(Comment[1], "NES CRC 0x%08x", iNESGameCRC32); memfwrite(&Comment[0], 64); totalsize += 64; /*** Write header ***/ memfwrite(&header, 16); totalsize += 16; totalsize += GCSaveChunk(1, SFCPU); totalsize += GCSaveChunk(2, SFCPUC); totalsize += GCSaveChunk(3, FCEUPPU_STATEINFO); totalsize += GCSaveChunk(4, FCEUCTRL_STATEINFO); totalsize += GCSaveChunk(5, FCEUSND_STATEINFO); totalsize += GCSaveChunk(0x10, SFMDATA); /*** Add terminating CHNK ***/ memfwrite(&chunk,4); memfwrite(&zero,4); totalsize += 8; /*** Update size element ***/ memcpy(&statebuffer[FILESIZEOFFSET], &totalsize, 4); return totalsize; } /**************************************************************************** * Card Removed * * Straight copy from MemCard demo ****************************************************************************/ int CardReady = 0; void CardRemoved(s32 chn,s32 result) { CARD_Unmount(chn); CardReady = 0; } /**************************************************************************** * Snes9xGX Memcard ****************************************************************************/ void uselessinquiry() { volatile long *udvd = ( volatile long *)0xCC006000; udvd[0] = 0; udvd[1] = 0; udvd[2] = 0x12000000; udvd[3] = 0; udvd[4] = 0x20; udvd[5] = 0x80000000; udvd[6] = 0x20; udvd[7] = 1; while (udvd[7] & 1); } int MountTheCard() { int tries = 0; int CardError; while (tries < 10) { *(unsigned long*)(0xcc006800) |= 1<<13; /*** Disable Encryption ***/ uselessinquiry(); VIDEO_WaitVSync(); //CardError = CARD_Mount(CARDSLOT, SysArea, NULL); /*** Don't need or want a callback ***/ CardError = CARD_Mount(CARDSLOT, SysArea, CardRemoved); if (CardError == 0) return 0; else { EXI_ProbeReset(); } tries++; } return -1; } /**************************************************************************** * MemCard Save * * This is based on the code from libogc ****************************************************************************/ void MC_ManageState(int mode, int slot) { char mcFilename[80]; int CardError; card_dir CardDir; card_file CardFile; int SectorSize; int found = 0; int FileSize; int actualSize; int savedBytes=0; char debug[128]; CARDSLOT = slot; /*** Build the file name ***/ sprintf(mcFilename, "FCEU-%08x.fcs", iNESGameCRC32); /*** Mount the Card ***/ CARD_Init("FCEU", "00"); /*** Try for memory card in slot A ***/ CardError = MountTheCard(); if (CardError >= 0) { /*** Get card sector size ***/ CardError = CARD_GetSectorSize(CARDSLOT, &SectorSize); switch (mode) { case 0 : { /*** Save Game ***/ /*** Look for this file ***/ CardError = CARD_FindFirst(CARDSLOT, &CardDir, true); found = 0; card_stat CardStatus; while (CardError != CARD_ERROR_NOFILE) { CardError = CARD_FindNext(&CardDir); if ( strcmp(CardDir.filename, mcFilename) == 0 ) found = 1; } /*** Determine number of sectors required ***/ savedBytes = actualSize = GCFCEUSS_Save(); sprintf(debug, "Saving in MC ..."); ShowAction(debug); FileSize = ( actualSize / SectorSize ) * SectorSize; if (actualSize % SectorSize) FileSize += SectorSize; /*** Now write the file out ***/ if (!found) CardError = CARD_Create(CARDSLOT, mcFilename, FileSize, &CardFile); else CardError = CARD_Open(CARDSLOT, mcFilename, &CardFile); CARD_GetStatus(CARDSLOT, CardFile.filenum, &CardStatus); CardStatus.icon_addr = 0; CardStatus.icon_fmt = 2; CardStatus.icon_speed = 1; CardStatus.comment_addr = sizeof(saveicon); CARD_SetStatus(CARDSLOT, CardFile.filenum, &CardStatus); /*** Haha! libogc only write one block at a time! ***/ if (CardError == 0) { int sbo = 0; while (actualSize > 0) { CardError = CARD_Write(&CardFile, &statebuffer[sbo], SectorSize, sbo ); actualSize -= SectorSize; sbo += SectorSize; } CardError = CARD_Close(&CardFile); sprintf(debug, "Saved %d bytes successfully!", savedBytes); ShowAction(debug); } else { WaitPrompt("Save Failed"); } CARD_Unmount(CARDSLOT); } break; /*** End save ***/ case 1: { /*** Load state ***/ /*** Look for this file ***/ CardError = CARD_FindFirst(CARDSLOT, &CardDir, true); memopen(); /*** Clear the buffer ***/ found = 0; while (CardError != CARD_ERROR_NOFILE) { CardError = CARD_FindNext(&CardDir); if (strcmp(CardDir.filename, mcFilename) == 0) found = 1; } if (found == 0) { WaitPrompt("No Save Game Found"); CARD_Unmount(CARDSLOT); return; } /*** Load the file into memory ***/ CardError = CARD_Open(CARDSLOT, mcFilename, &CardFile); CardError = CARD_Read(&CardFile, &statebuffer, SectorSize, 0); /*** Get actual size of the file ***/ memcpy(&actualSize, &statebuffer[FILESIZEOFFSET], 4); savedBytes = actualSize; int sbo = SectorSize; actualSize -= SectorSize; while(actualSize > 0) { CARD_Read(&CardFile, &statebuffer[sbo], SectorSize, sbo); actualSize -= SectorSize; sbo += SectorSize; } CARD_Close(&CardFile); /*** Finally, do load ***/ GCFCEUSS_Load(); CARD_Unmount(CARDSLOT); sprintf(debug, "Loaded %d bytes successfully!", savedBytes); ShowAction(debug); } break; /*** End load ***/ default: break; } } else { WaitPrompt("Cannot Mount Memory Card!"); } } void SD_ManageState(int mode, int slot) { char path[1024]; char msg[128]; int filesize = 0; int len = 0; int offset = 0; FILE *handle; if (mode == 0) ShowAction ("Saving STATE to SD..."); else ShowAction ("Loading STATE from SD..."); sprintf (path, "/%s/%s/%08x.fcs", FCEUDIR, SAVEDIR, iNESGameCRC32); handle = fopen(path, (mode == 0) ? "wb" : "rb"); if (handle == NULL) { sprintf(msg, "Couldn't open %s", path); WaitPrompt(msg); return; } if (mode == 0) { //Save filesize = GCFCEUSS_Save(); len = fwrite(statebuffer, filesize, 1, handle); fclose(handle); if (len != filesize){ sprintf (msg, "Error writing %s", path); WaitPrompt (msg); return; } sprintf (msg, "Saved %d bytes successfully", filesize); WaitPrompt (msg); } else { //Load memopen(); while ((len = fread(&statebuffer[offset], 1, 1024, handle)) > 0) offset += len; fclose(handle); sprintf (msg, "Loaded %d bytes successfully", offset); ShowAction(msg); GCFCEUSS_Load(); return; } } void ManageState(int mode, int slot, int device) { if (device == 0) { MC_ManageState(mode, slot); } else { SD_ManageState(mode, slot); } } /* u8 screenscaler * u8 currpal * u8 slimit * u8 timing * u8 mpads[6] * int FSDisable (int is u32, 4 bytes) * u8 PADCAL * u8 PADTUR * u8 ChosenSlot * u8 ChosenDevice * u8 UseSDCARD * u8 UseWiiSDCARD */ int SaveSettings(u8 *buffer) { int filesize = 0; buffer[filesize++] = screenscaler; buffer[filesize++] = currpal; buffer[filesize++] = slimit; buffer[filesize++] = timing; buffer[filesize++] = mpads[0]; buffer[filesize++] = mpads[1]; buffer[filesize++] = mpads[2]; buffer[filesize++] = mpads[3]; buffer[filesize++] = mpads[4]; buffer[filesize++] = mpads[5]; buffer[filesize++] = FSDisable; buffer[filesize++] = PADCAL; buffer[filesize++] = PADTUR; buffer[filesize++] = ChosenSlot; buffer[filesize++] = ChosenDevice; buffer[filesize++] = UseSDCARD; buffer[filesize++] = UseWiiSDCARD; return filesize+1; } int LoadSettings(u8 *buffer) { int filesize = 0; screenscaler = buffer[filesize++]; currpal = buffer[filesize++]; if (currpal == 0) FCEU_ResetPalette(); else { u8 r, g, b, i; /*** Now setup this palette ***/ for ( i = 0; i < 64; i++ ) { r = palettes[currpal-1].data[i] >> 16; g = ( palettes[currpal-1].data[i] & 0xff00 ) >> 8; b = ( palettes[currpal-1].data[i] & 0xff ); FCEUD_SetPalette( i, r, g, b); FCEUD_SetPalette( i+64, r, g, b); FCEUD_SetPalette( i+128, r, g, b); FCEUD_SetPalette( i+192, r, g, b); } } slimit = buffer[filesize++]; FCEUI_DisableSpriteLimitation(slimit); timing = buffer[filesize++]; FCEUI_SetVidSystem(timing); mpads[0] = buffer[filesize++]; mpads[1] = buffer[filesize++]; mpads[2] = buffer[filesize++]; mpads[3] = buffer[filesize++]; mpads[4] = buffer[filesize++]; mpads[5] = buffer[filesize++]; FSDisable = buffer[filesize++]; FCEUI_DisableFourScore(FSDisable); PADCAL = buffer[filesize++]; PADTUR = buffer[filesize++]; ChosenSlot = buffer[filesize++]; ChosenDevice = buffer[filesize++]; UseSDCARD = buffer[filesize++]; UseWiiSDCARD = buffer[filesize++]; return filesize+1; } /**************************************************************************** * Save Settings to MemCard ****************************************************************************/ void MC_ManageSettings(int mode, int slot, int quiet) { char mcFilename[80]; int CardError; card_dir CardDir; card_file CardFile; int SectorSize; int found = 0; int FileSize; int actualSize; int savedBytes=0; char msg[128]; /*** Build the file name ***/ strcpy(mcFilename, "FCEU-Settings.fcs"); /*** Mount the Card ***/ CARD_Init("FCEU", "00"); /*** Try for memory card in slot A ***/ CardError = MountTheCard(); if (CardError >= 0) { /*** Get card sector size ***/ CardError = CARD_GetSectorSize(slot, &SectorSize); switch (mode) { case 0 : { /*** Save Game ***/ /*** Look for this file ***/ CardError = CARD_FindFirst(slot, &CardDir, true); found = 0; card_stat CardStatus; while (CardError != CARD_ERROR_NOFILE) { CardError = CARD_FindNext(&CardDir); if ( strcmp(CardDir.filename, mcFilename) == 0 ) found = 1; } /*** Determine number of bytes required ***/ u8 buffer[SectorSize]; memset(buffer, 0, SectorSize); actualSize = SaveSettings(buffer); savedBytes = actualSize; sprintf(msg, "Saving Settings to MC ..."); ShowAction(msg); FileSize = (actualSize / SectorSize) * SectorSize; if ((actualSize % SectorSize) || (FileSize == 0)) FileSize += SectorSize; /*** Now write the file out ***/ if (!found) CardError = CARD_Create(CARDSLOT, mcFilename, FileSize, &CardFile); else CardError = CARD_Open(CARDSLOT, mcFilename, &CardFile); CARD_GetStatus(CARDSLOT, CardFile.filenum, &CardStatus); CardStatus.icon_addr = 0; CardStatus.icon_fmt = 2; CardStatus.icon_speed = 1; CardStatus.comment_addr = sizeof(saveicon); CARD_SetStatus(CARDSLOT, CardFile.filenum, &CardStatus); /*** Haha! libogc only write one block at a time! ***/ if (CardError == 0) { int sbo = 0; while (actualSize > 0) { CardError = CARD_Write(&CardFile, &buffer[sbo], SectorSize, sbo ); actualSize -= SectorSize; sbo += SectorSize; } CardError = CARD_Close(&CardFile); strcpy(msg, "Saved settings successfully"); if (quiet) ShowAction(msg); else WaitPrompt(msg); } else { strcpy(msg, "Save Settings Failed!"); if (quiet) ShowAction(msg); else WaitPrompt(msg); } CARD_Unmount(CARDSLOT); } break; /*** End save ***/ case 1: { /*** Load state ***/ /*** Look for this file ***/ CardError = CARD_FindFirst(CARDSLOT, &CardDir, true); found = 0; while ( CardError != CARD_ERROR_NOFILE ) { CardError = CARD_FindNext(&CardDir); if (strcmp(CardDir.filename, mcFilename) == 0) found = 1; } if (found == 0) { strcpy(msg, "No Settings Found"); if (quiet) ShowAction(msg); else WaitPrompt(msg); CARD_Unmount(CARDSLOT); return; } u8 buffer[SectorSize]; /*** Load the file into memory ***/ CardError = CARD_Open(CARDSLOT, mcFilename, &CardFile); CardError = CARD_Read(&CardFile, &buffer, SectorSize, 0); CARD_Close(&CardFile); /*** Finally, do load ***/ savedBytes = LoadSettings(buffer); sprintf(msg, "Loaded settings successfully!"); ShowAction(msg); CARD_Unmount(CARDSLOT); } break; /*** End load ***/ default: break; } } else { strcpy(msg, "Cannot mount Memory Card!"); if (quiet) ShowAction(msg); else WaitPrompt(msg); } } void SD_ManageSettings(int mode, int slot, int quiet) { char path[1024]; char msg[128]; int filesize = 0; int len = 0; u8 buffer[128]; if (mode == 0) ShowAction ("Saving Settings to SD..."); else ShowAction ("Loading Settings from SD..."); sprintf(path, "/%s/%s/Settings.fcs", FCEUDIR, SAVEDIR); FILE *f = fopen(path, (mode == 0) ? "wb" : "rb"); if (f == NULL) { sprintf(msg, "Couldn't open %s", path); if (quiet) ShowAction(msg); else WaitPrompt(msg); return; } if (mode == 0) { // Save filesize = SaveSettings(buffer); len = fwrite(buffer, filesize, 1, f); fclose(f); sprintf(msg, "Saved settings successfully"); if (quiet) ShowAction(msg); else WaitPrompt(msg); } else { // Load fread(buffer, 128, 1, f); fclose(f); /*** Finally, do load ***/ filesize = LoadSettings(buffer); sprintf (msg, "Loaded settings successfully"); ShowAction(msg); return; } } void ManageSettings(int mode, int slot, int device, int quiet) { if (device == 0) { MC_ManageSettings(mode, slot, quiet); } else { SD_ManageSettings(mode, slot, quiet); } }