mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-11-09 04:15:14 +01:00
f2a7b4cb8a
--------------- * added support for CUE files * added CD-DA tracks emulation (needs CUE+BIN or ISO+WAV images) * added CD fader emulation * added CDD "Fast FW" & "Fast RW" commands emulation * improved CDD TOC emulation (random freezes in Sonic CD, Switch/Panic, Final Fight CD and probably many others) * improved PCM chip synchronization with SUB-CPU (missing speeches in Willy Beamish) * fixed PCM chip emulation (random hangs in Snatcher, missing sound effects in Switch/Panic, Final Fight CD, Wonderdog...) * fixed Word-RAM memory mode on soft-reset (missing logo gfx effects) * fixed SUB-CPU access to unused areas when using PC-relative instructions (Final Fight CD first boss random crash) * fixed CPU idle loop detection on memory mode register access (Pugsy CD first boss slowdown) * fixed Mode 1 emulation (cartridge boot mode) [Core/Sound] --------------- * replaced FIR resampler by Blip Buffer for FM resampling * modified SN76489 core for use of Blip Buffer * improved PSG & FM chips synchronization using Blip Buffer * added Game Gear PSG stereo support * fixed SG-1000 specific PSG noise * fixed YM2612 LFO AM waveform (California Games surfing event) * fixed YM2612 phase precision * minor optimizations to YM2612 core [Core/Game Gear] --------------- * added support for CJ Elephant Fugitive (recently released by SMS Power) * added Game Gear extended screen option [Core/Genesis] --------------- * added support for a few recently dumped (but unreleased) games [Core/General] --------------- * improved ROM & CD image file loading * various code cleanup [Gamecube/Wii] --------------- * added automatic disc swap feature * removed automatic frameskipping (no use) * improved general audio/video sync * various code cleanup & bugfixes
831 lines
22 KiB
C
831 lines
22 KiB
C
/*
|
|
* file_slot.c
|
|
*
|
|
* FAT and Memory Card SRAM/State slots managment
|
|
*
|
|
* Copyright Eke-Eke (2008-2012), based on original code from Softdev (2006)
|
|
*
|
|
* Redistribution and use of this code or any derivative works are permitted
|
|
* provided that the following conditions are met:
|
|
*
|
|
* - Redistributions may not be sold, nor may they be used in a commercial
|
|
* product or activity.
|
|
*
|
|
* - Redistributions that are modified from the original source must include the
|
|
* complete source code, including the source code for all components used by a
|
|
* binary built from the modified sources. However, as a special exception, the
|
|
* source code distributed need not include anything that is normally distributed
|
|
* (in either source or binary form) with the major components (compiler, kernel,
|
|
* and so on) of the operating system on which the executable runs, unless that
|
|
* component itself accompanies the executable.
|
|
*
|
|
* - Redistributions must reproduce the above copyright notice, this list of
|
|
* conditions and the following disclaimer in the documentation and/or other
|
|
* materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************************/
|
|
|
|
#include "shared.h"
|
|
#include "file_slot.h"
|
|
#include "file_load.h"
|
|
#include "gui.h"
|
|
#include "filesel.h"
|
|
#include "saveicon.h"
|
|
|
|
/**
|
|
* libOGC CARD System Work Area
|
|
*/
|
|
static u8 SysArea[CARD_WORKAREA] ATTRIBUTE_ALIGN (32);
|
|
|
|
/* Mega CD backup RAM stuff */
|
|
static u32 brm_crc[2];
|
|
static char brm_filename[3][32] = {CD_BRAM_JP, CD_BRAM_EU, CD_BRAM_US};
|
|
static u8 brm_format[0x40] =
|
|
{
|
|
0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x00,0x00,0x00,0x00,0x40,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x53,0x45,0x47,0x41,0x5f,0x43,0x44,0x5f,0x52,0x4f,0x4d,0x00,0x01,0x00,0x00,0x00,
|
|
0x52,0x41,0x4d,0x5f,0x43,0x41,0x52,0x54,0x52,0x49,0x44,0x47,0x45,0x5f,0x5f,0x5f
|
|
};
|
|
|
|
/****************************************************************************
|
|
* CardMount
|
|
*
|
|
* libOGC provides the CARD_Mount function, and it should be all you need.
|
|
* However, experience with previous emulators has taught me that you are
|
|
* better off doing a little bit more than that!
|
|
*
|
|
*****************************************************************************/
|
|
static int CardMount(int slot)
|
|
{
|
|
int tries = 0;
|
|
#ifdef HW_RVL
|
|
*(unsigned long *) (0xCD006800) |= 1 << 13; /*** Disable Encryption ***/
|
|
#else
|
|
*(unsigned long *) (0xCC006800) |= 1 << 13; /*** Disable Encryption ***/
|
|
#endif
|
|
while (tries < 10)
|
|
{
|
|
VIDEO_WaitVSync ();
|
|
if (CARD_Mount(slot, SysArea, NULL) == CARD_ERROR_READY)
|
|
return 1;
|
|
else
|
|
EXI_ProbeReset ();
|
|
tries++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Slot Management
|
|
*
|
|
*
|
|
****************************************************************************/
|
|
void slot_autoload(int slot, int device)
|
|
{
|
|
/* Mega CD backup RAM specific */
|
|
if (!slot && (system_hw == SYSTEM_MCD))
|
|
{
|
|
/* automatically load internal backup RAM */
|
|
FILE *fp = fopen(brm_filename[((region_code ^ 0x40) >> 6) - 1], "rb");
|
|
if (fp != NULL)
|
|
{
|
|
fread(scd.bram, 0x2000, 1, fp);
|
|
fclose(fp);
|
|
|
|
/* update CRC */
|
|
brm_crc[0] = crc32(0, scd.bram, 0x2000);
|
|
}
|
|
else
|
|
{
|
|
/* force internal backup RAM format (does not use previous region backup RAM) */
|
|
scd.bram[0x1fff] = 0;
|
|
}
|
|
|
|
/* check if internal backup RAM is correctly formatted */
|
|
if (memcmp(scd.bram + 0x2000 - 0x20, brm_format + 0x20, 0x20))
|
|
{
|
|
/* clear internal backup RAM */
|
|
memset(scd.bram, 0x00, 0x2000 - 0x40);
|
|
|
|
/* internal Backup RAM size fields */
|
|
brm_format[0x10] = brm_format[0x12] = brm_format[0x14] = brm_format[0x16] = 0x00;
|
|
brm_format[0x11] = brm_format[0x13] = brm_format[0x15] = brm_format[0x17] = (sizeof(scd.bram) / 64) - 3;
|
|
|
|
/* format internal backup RAM */
|
|
memcpy(scd.bram + 0x2000 - 0x40, brm_format, 0x40);
|
|
|
|
/* clear CRC to force file saving (in case previous region backup RAM was also formatted) */
|
|
brm_crc[0] = 0;
|
|
}
|
|
|
|
/* automatically load cartridge backup RAM (if enabled) */
|
|
if (scd.cartridge.id)
|
|
{
|
|
fp = fopen(CART_BRAM, "rb");
|
|
if (fp != NULL)
|
|
{
|
|
int filesize = scd.cartridge.mask + 1;
|
|
int done = 0;
|
|
|
|
/* Read into buffer (2k blocks) */
|
|
while (filesize > CHUNKSIZE)
|
|
{
|
|
fread(scd.cartridge.area + done, CHUNKSIZE, 1, fp);
|
|
done += CHUNKSIZE;
|
|
filesize -= CHUNKSIZE;
|
|
}
|
|
|
|
/* Read remaining bytes */
|
|
if (filesize)
|
|
{
|
|
fread(scd.cartridge.area + done, filesize, 1, fp);
|
|
}
|
|
|
|
/* close file */
|
|
fclose(fp);
|
|
|
|
/* update CRC */
|
|
brm_crc[1] = crc32(0, scd.cartridge.area, scd.cartridge.mask + 1);
|
|
}
|
|
|
|
/* check if cartridge backup RAM is correctly formatted */
|
|
if (memcmp(scd.cartridge.area + scd.cartridge.mask + 1 - 0x20, brm_format + 0x20, 0x20))
|
|
{
|
|
/* clear cartridge backup RAM */
|
|
memset(scd.cartridge.area, 0x00, scd.cartridge.mask + 1);
|
|
|
|
/* Cartridge Backup RAM size fields */
|
|
brm_format[0x10] = brm_format[0x12] = brm_format[0x14] = brm_format[0x16] = (((scd.cartridge.mask + 1) / 64) - 3) >> 8;
|
|
brm_format[0x11] = brm_format[0x13] = brm_format[0x15] = brm_format[0x17] = (((scd.cartridge.mask + 1) / 64) - 3) & 0xff;
|
|
|
|
/* format cartridge backup RAM */
|
|
memcpy(scd.cartridge.area + scd.cartridge.mask + 1 - 0x40, brm_format, 0x40);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* configurable SRAM & State auto-saving */
|
|
if ((slot && !(config.s_auto & 2)) || (!slot && !(config.s_auto & 1)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (strlen(rom_filename))
|
|
{
|
|
SILENT = 1;
|
|
slot_load(slot, device);
|
|
SILENT = 0;
|
|
}
|
|
}
|
|
|
|
void slot_autosave(int slot, int device)
|
|
{
|
|
/* Mega CD backup RAM specific */
|
|
if (!slot && (system_hw == SYSTEM_MCD))
|
|
{
|
|
/* verify that internal backup RAM has been modified */
|
|
if (crc32(0, scd.bram, 0x2000) != brm_crc[0])
|
|
{
|
|
/* check if it is correctly formatted before saving */
|
|
if (!memcmp(scd.bram + 0x2000 - 0x20, brm_format + 0x20, 0x20))
|
|
{
|
|
FILE *fp = fopen(brm_filename[((region_code ^ 0x40) >> 6) - 1], "wb");
|
|
if (fp != NULL)
|
|
{
|
|
fwrite(scd.bram, 0x2000, 1, fp);
|
|
fclose(fp);
|
|
|
|
/* update CRC */
|
|
brm_crc[0] = crc32(0, scd.bram, 0x2000);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* verify that cartridge backup RAM has been modified */
|
|
if (scd.cartridge.id && (crc32(0, scd.cartridge.area, scd.cartridge.mask + 1) != brm_crc[1]))
|
|
{
|
|
/* check if it is correctly formatted before saving */
|
|
if (!memcmp(scd.cartridge.area + scd.cartridge.mask + 1 - 0x20, brm_format + 0x20, 0x20))
|
|
{
|
|
FILE *fp = fopen(CART_BRAM, "wb");
|
|
if (fp != NULL)
|
|
{
|
|
int filesize = scd.cartridge.mask + 1;
|
|
int done = 0;
|
|
|
|
/* Write to file (2k blocks) */
|
|
while (filesize > CHUNKSIZE)
|
|
{
|
|
fwrite(scd.cartridge.area + done, CHUNKSIZE, 1, fp);
|
|
done += CHUNKSIZE;
|
|
filesize -= CHUNKSIZE;
|
|
}
|
|
|
|
/* Write remaining bytes */
|
|
if (filesize)
|
|
{
|
|
fwrite(scd.cartridge.area + done, filesize, 1, fp);
|
|
}
|
|
|
|
/* Close file */
|
|
fclose(fp);
|
|
|
|
/* update CRC */
|
|
brm_crc[1] = crc32(0, scd.cartridge.area, scd.cartridge.mask + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* configurable SRAM & State auto-saving */
|
|
if ((slot && !(config.s_auto & 2)) || (!slot && !(config.s_auto & 1)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (strlen(rom_filename))
|
|
{
|
|
SILENT = 1;
|
|
slot_save(slot, device);
|
|
SILENT = 0;
|
|
}
|
|
}
|
|
|
|
void slot_autodetect(int slot, int device, t_slot *ptr)
|
|
{
|
|
if (!ptr) return;
|
|
|
|
char filename[MAXPATHLEN];
|
|
memset(ptr,0,sizeof(t_slot));
|
|
|
|
if (!device)
|
|
{
|
|
/* FAT support */
|
|
if (slot > 0)
|
|
{
|
|
sprintf (filename,"%s/saves/%s.gp%d", DEFAULT_PATH, rom_filename, slot - 1);
|
|
}
|
|
else
|
|
{
|
|
sprintf (filename,"%s/saves/%s.srm", DEFAULT_PATH, rom_filename);
|
|
}
|
|
|
|
/* Open file */
|
|
FILE *fp = fopen(filename, "rb");
|
|
if (fp)
|
|
{
|
|
/* Retrieve date & close */
|
|
struct stat filestat;
|
|
stat(filename, &filestat);
|
|
struct tm *timeinfo = localtime(&filestat.st_mtime);
|
|
ptr->year = 1900 + timeinfo->tm_year;
|
|
ptr->month = timeinfo->tm_mon;
|
|
ptr->day = timeinfo->tm_mday;
|
|
ptr->hour = timeinfo->tm_hour;
|
|
ptr->min = timeinfo->tm_min;
|
|
fclose(fp);
|
|
ptr->valid = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Memory Card support */
|
|
if (slot > 0)
|
|
sprintf(filename,"MD-%04X.gp%d", rominfo.realchecksum, slot - 1);
|
|
else
|
|
sprintf(filename,"MD-%04X.srm", rominfo.realchecksum);
|
|
|
|
/* Initialise the CARD system */
|
|
memset(&SysArea, 0, CARD_WORKAREA);
|
|
CARD_Init("GENP", "00");
|
|
|
|
/* CARD slot */
|
|
device--;
|
|
|
|
/* Mount CARD */
|
|
if (CardMount(device))
|
|
{
|
|
/* Open file */
|
|
card_file CardFile;
|
|
if (CARD_Open(device, filename, &CardFile) == CARD_ERROR_READY)
|
|
{
|
|
/* Retrieve date & close */
|
|
card_stat CardStatus;
|
|
CARD_GetStatus(device, CardFile.filenum, &CardStatus);
|
|
time_t rawtime = CardStatus.time;
|
|
struct tm *timeinfo = localtime(&rawtime);
|
|
ptr->year = 1900 + timeinfo->tm_year;
|
|
ptr->month = timeinfo->tm_mon;
|
|
ptr->day = timeinfo->tm_mday;
|
|
ptr->hour = timeinfo->tm_hour;
|
|
ptr->min = timeinfo->tm_min;
|
|
CARD_Close(&CardFile);
|
|
ptr->valid = 1;
|
|
}
|
|
CARD_Unmount(device);
|
|
}
|
|
}
|
|
}
|
|
|
|
int slot_delete(int slot, int device)
|
|
{
|
|
char filename[MAXPATHLEN];
|
|
int ret = 0;
|
|
|
|
if (!device)
|
|
{
|
|
/* FAT support */
|
|
if (slot > 0)
|
|
{
|
|
/* remove screenshot */
|
|
sprintf(filename,"%s/saves/%s__%d.png", DEFAULT_PATH, rom_filename, slot - 1);
|
|
remove(filename);
|
|
|
|
sprintf (filename,"%s/saves/%s.gp%d", DEFAULT_PATH, rom_filename, slot - 1);
|
|
}
|
|
else
|
|
{
|
|
sprintf (filename,"%s/saves/%s.srm", DEFAULT_PATH, rom_filename);
|
|
}
|
|
|
|
/* Delete file */
|
|
ret = remove(filename);
|
|
}
|
|
else
|
|
{
|
|
/* Memory Card support */
|
|
if (slot > 0)
|
|
sprintf(filename,"MD-%04X.gp%d", rominfo.realchecksum, slot - 1);
|
|
else
|
|
sprintf(filename,"MD-%04X.srm", rominfo.realchecksum);
|
|
|
|
/* Initialise the CARD system */
|
|
memset(&SysArea, 0, CARD_WORKAREA);
|
|
CARD_Init("GENP", "00");
|
|
|
|
/* CARD slot */
|
|
device--;
|
|
|
|
/* Mount CARD */
|
|
if (CardMount(device))
|
|
{
|
|
/* Delete file */
|
|
ret = CARD_Delete(device,filename);
|
|
CARD_Unmount(device);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int slot_load(int slot, int device)
|
|
{
|
|
char filename[MAXPATHLEN];
|
|
unsigned long filesize, done = 0;
|
|
u8 *buffer;
|
|
|
|
/* File Type */
|
|
if (slot > 0)
|
|
{
|
|
GUI_MsgBoxOpen("Information","Loading State ...",1);
|
|
}
|
|
else
|
|
{
|
|
if (!sram.on)
|
|
{
|
|
GUI_WaitPrompt("Error","Backup RAM is disabled !");
|
|
return 0;
|
|
}
|
|
|
|
GUI_MsgBoxOpen("Information","Loading Backup RAM ...",1);
|
|
}
|
|
|
|
/* Device Type */
|
|
if (!device)
|
|
{
|
|
/* FAT file */
|
|
if (slot > 0)
|
|
{
|
|
sprintf (filename,"%s/saves/%s.gp%d", DEFAULT_PATH, rom_filename, slot - 1);
|
|
}
|
|
else
|
|
{
|
|
sprintf (filename,"%s/saves/%s.srm", DEFAULT_PATH, rom_filename);
|
|
}
|
|
|
|
/* Open file */
|
|
FILE *fp = fopen(filename, "rb");
|
|
if (!fp)
|
|
{
|
|
GUI_WaitPrompt("Error","Unable to open file !");
|
|
return 0;
|
|
}
|
|
|
|
/* Get file size */
|
|
fseek(fp, 0, SEEK_END);
|
|
filesize = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
/* allocate buffer */
|
|
buffer = (u8 *)memalign(32,filesize);
|
|
if (!buffer)
|
|
{
|
|
GUI_WaitPrompt("Error","Unable to allocate memory !");
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
/* Read into buffer (2k blocks) */
|
|
while (filesize > CHUNKSIZE)
|
|
{
|
|
fread(buffer + done, CHUNKSIZE, 1, fp);
|
|
done += CHUNKSIZE;
|
|
filesize -= CHUNKSIZE;
|
|
}
|
|
|
|
/* Read remaining bytes */
|
|
fread(buffer + done, filesize, 1, fp);
|
|
done += filesize;
|
|
|
|
/* Close file */
|
|
fclose(fp);
|
|
}
|
|
else
|
|
{
|
|
/* Memory Card file */
|
|
if (slot > 0)
|
|
{
|
|
sprintf(filename, "MD-%04X.gp%d", rominfo.realchecksum, slot - 1);
|
|
}
|
|
else
|
|
{
|
|
sprintf(filename, "MD-%04X.srm", rominfo.realchecksum);
|
|
}
|
|
|
|
/* Initialise the CARD system */
|
|
char action[64];
|
|
memset(&SysArea, 0, CARD_WORKAREA);
|
|
CARD_Init("GENP", "00");
|
|
|
|
/* CARD slot */
|
|
device--;
|
|
|
|
/* Attempt to mount the card */
|
|
if (!CardMount(device))
|
|
{
|
|
GUI_WaitPrompt("Error","Unable to mount memory card");
|
|
return 0;
|
|
}
|
|
|
|
/* Retrieve the sector size */
|
|
u32 SectorSize = 0;
|
|
int CardError = CARD_GetSectorSize(device, &SectorSize);
|
|
if (!SectorSize)
|
|
{
|
|
sprintf(action, "Invalid sector size (%d)", CardError);
|
|
GUI_WaitPrompt("Error",action);
|
|
CARD_Unmount(device);
|
|
return 0;
|
|
}
|
|
|
|
/* Open file */
|
|
card_file CardFile;
|
|
CardError = CARD_Open(device, filename, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf(action, "Unable to open file (%d)", CardError);
|
|
GUI_WaitPrompt("Error",action);
|
|
CARD_Unmount(device);
|
|
return 0;
|
|
}
|
|
|
|
/* Get file size */
|
|
filesize = CardFile.len;
|
|
if (filesize % SectorSize)
|
|
{
|
|
filesize = ((filesize / SectorSize) + 1) * SectorSize;
|
|
}
|
|
|
|
/* Allocate buffer */
|
|
u8 *in = (u8 *)memalign(32, filesize);
|
|
if (!in)
|
|
{
|
|
GUI_WaitPrompt("Error","Unable to allocate memory !");
|
|
CARD_Close(&CardFile);
|
|
CARD_Unmount(device);
|
|
return 0;
|
|
}
|
|
|
|
/* Read file sectors */
|
|
while (filesize > 0)
|
|
{
|
|
CARD_Read(&CardFile, &in[done], SectorSize, done);
|
|
done += SectorSize;
|
|
filesize -= SectorSize;
|
|
}
|
|
|
|
/* Close file */
|
|
CARD_Close(&CardFile);
|
|
CARD_Unmount(device);
|
|
|
|
/* Uncompressed file size */
|
|
memcpy(&filesize, in + 2112, 4);
|
|
buffer = (u8 *)memalign(32, filesize);
|
|
if (!buffer)
|
|
{
|
|
free(in);
|
|
GUI_WaitPrompt("Error","Unable to allocate memory !");
|
|
return 0;
|
|
}
|
|
|
|
/* Uncompress file */
|
|
uncompress ((Bytef *)buffer, &filesize, (Bytef *)(in + 2112 + 4), done - 2112 - 4);
|
|
free(in);
|
|
}
|
|
|
|
if (slot > 0)
|
|
{
|
|
/* Load state */
|
|
if (state_load(buffer) <= 0)
|
|
{
|
|
free(buffer);
|
|
GUI_WaitPrompt("Error","Invalid state file !");
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* load SRAM */
|
|
memcpy(sram.sram, buffer, 0x10000);
|
|
|
|
/* update CRC */
|
|
sram.crc = crc32(0, sram.sram, 0x10000);
|
|
}
|
|
|
|
free(buffer);
|
|
GUI_MsgBoxClose();
|
|
return 1;
|
|
}
|
|
|
|
int slot_save(int slot, int device)
|
|
{
|
|
char filename[MAXPATHLEN];
|
|
unsigned long filesize, done = 0;
|
|
u8 *buffer;
|
|
|
|
if (slot > 0)
|
|
{
|
|
GUI_MsgBoxOpen("Information","Saving State ...",1);
|
|
|
|
/* allocate buffer */
|
|
buffer = (u8 *)memalign(32,STATE_SIZE);
|
|
if (!buffer)
|
|
{
|
|
GUI_WaitPrompt("Error","Unable to allocate memory !");
|
|
return 0;
|
|
}
|
|
|
|
filesize = state_save(buffer);
|
|
}
|
|
else
|
|
{
|
|
/* only save if SRAM is enabled */
|
|
if (!sram.on)
|
|
{
|
|
GUI_WaitPrompt("Error","Backup RAM disabled !");
|
|
return 0;
|
|
}
|
|
|
|
/* only save if SRAM has been modified */
|
|
if (crc32(0, &sram.sram[0], 0x10000) == sram.crc)
|
|
{
|
|
GUI_WaitPrompt("Warning","Backup RAM not modified !");
|
|
return 0;
|
|
}
|
|
|
|
GUI_MsgBoxOpen("Information","Saving Backup RAM ...",1);
|
|
|
|
/* allocate buffer */
|
|
buffer = (u8 *)memalign(32, 0x10000);
|
|
if (!buffer)
|
|
{
|
|
GUI_WaitPrompt("Error","Unable to allocate memory !");
|
|
return 0;
|
|
}
|
|
|
|
/* copy SRAM data */
|
|
memcpy(buffer, sram.sram, 0x10000);
|
|
filesize = 0x10000;
|
|
|
|
/* update CRC */
|
|
sram.crc = crc32(0, sram.sram, 0x10000);
|
|
}
|
|
|
|
/* Device Type */
|
|
if (!device)
|
|
{
|
|
/* FAT filename */
|
|
if (slot > 0)
|
|
{
|
|
sprintf(filename, "%s/saves/%s.gp%d", DEFAULT_PATH, rom_filename, slot - 1);
|
|
}
|
|
else
|
|
{
|
|
sprintf(filename, "%s/saves/%s.srm", DEFAULT_PATH, rom_filename);
|
|
}
|
|
|
|
/* Open file */
|
|
FILE *fp = fopen(filename, "wb");
|
|
if (!fp)
|
|
{
|
|
GUI_WaitPrompt("Error","Unable to open file !");
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
/* Write from buffer (2k blocks) */
|
|
while (filesize > CHUNKSIZE)
|
|
{
|
|
fwrite(buffer + done, CHUNKSIZE, 1, fp);
|
|
done += CHUNKSIZE;
|
|
filesize -= CHUNKSIZE;
|
|
}
|
|
|
|
/* Write remaining bytes */
|
|
fwrite(buffer + done, filesize, 1, fp);
|
|
done += filesize;
|
|
|
|
/* Close file */
|
|
fclose(fp);
|
|
free(buffer);
|
|
|
|
/* Close message box */
|
|
GUI_MsgBoxClose();
|
|
|
|
/* Save state screenshot */
|
|
if (slot > 0)
|
|
{
|
|
sprintf(filename,"%s/saves/%s__%d.png", DEFAULT_PATH, rom_filename, slot - 1);
|
|
gxSaveScreenshot(filename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Memory Card filename */
|
|
if (slot > 0)
|
|
{
|
|
sprintf(filename, "MD-%04X.gp%d", rominfo.realchecksum, slot - 1);
|
|
}
|
|
else
|
|
{
|
|
sprintf(filename, "MD-%04X.srm", rominfo.realchecksum);
|
|
}
|
|
|
|
/* Initialise the CARD system */
|
|
char action[64];
|
|
memset(&SysArea, 0, CARD_WORKAREA);
|
|
CARD_Init("GENP", "00");
|
|
|
|
/* CARD slot */
|
|
device--;
|
|
|
|
/* Attempt to mount the card */
|
|
if (!CardMount(device))
|
|
{
|
|
GUI_WaitPrompt("Error","Unable to mount memory card");
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
/* Retrieve sector size */
|
|
u32 SectorSize = 0;
|
|
int CardError = CARD_GetSectorSize(device, &SectorSize);
|
|
if (!SectorSize)
|
|
{
|
|
sprintf(action, "Invalid sector size (%d)", CardError);
|
|
GUI_WaitPrompt("Error",action);
|
|
CARD_Unmount(device);
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
/* Build output buffer */
|
|
u8 *out = (u8 *)memalign(32, filesize + 2112 + 4);
|
|
if (!out)
|
|
{
|
|
GUI_WaitPrompt("Error","Unable to allocate memory !");
|
|
CARD_Unmount(device);
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
/* Memory Card file header */
|
|
char comment[2][32] = { {"Genesis Plus GX"}, {"SRAM Save"} };
|
|
strcpy (comment[1], filename);
|
|
memcpy (&out[0], &icon, 2048);
|
|
memcpy (&out[2048], &comment[0], 64);
|
|
|
|
/* uncompressed size */
|
|
done = filesize;
|
|
memcpy(&out[2112], &done, 4);
|
|
|
|
/* compress file */
|
|
compress2 ((Bytef *)&out[2112 + 4], &filesize, (Bytef *)buffer, done, 9);
|
|
|
|
/* Adjust file size */
|
|
filesize = filesize + 4 + 2112;
|
|
if (filesize % SectorSize)
|
|
{
|
|
filesize = ((filesize / SectorSize) + 1) * SectorSize;
|
|
}
|
|
|
|
/* Check if file already exists */
|
|
card_file CardFile;
|
|
if (CARD_Open(device, filename, &CardFile) == CARD_ERROR_READY)
|
|
{
|
|
int size = filesize - CardFile.len;
|
|
CARD_Close(&CardFile);
|
|
memset(&CardFile,0,sizeof(CardFile));
|
|
|
|
/* Check file new size */
|
|
if (size > 0)
|
|
{
|
|
CardError = CARD_Create(device, "TEMP", size, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf(action, "Unable to increase file size (%d)", CardError);
|
|
GUI_WaitPrompt("Error",action);
|
|
CARD_Unmount(device);
|
|
free(out);
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
/* delete temporary file */
|
|
CARD_Close(&CardFile);
|
|
memset(&CardFile,0,sizeof(CardFile));
|
|
CARD_Delete(device, "TEMP");
|
|
}
|
|
|
|
/* delete previously existing file */
|
|
CARD_Delete(device, filename);
|
|
}
|
|
|
|
/* Create a new file */
|
|
CardError = CARD_Create(device, filename, filesize, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf(action, "Unable to create file (%d)", CardError);
|
|
GUI_WaitPrompt("Error",action);
|
|
CARD_Unmount(device);
|
|
free(out);
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
/* Update file informations */
|
|
time_t rawtime;
|
|
time(&rawtime);
|
|
card_stat CardStatus;
|
|
CARD_GetStatus(device, CardFile.filenum, &CardStatus);
|
|
CardStatus.icon_addr = 0x0;
|
|
CardStatus.icon_fmt = 2;
|
|
CardStatus.icon_speed = 1;
|
|
CardStatus.comment_addr = 2048;
|
|
CardStatus.time = rawtime;
|
|
CARD_SetStatus(device, CardFile.filenum, &CardStatus);
|
|
|
|
/* Write file sectors */
|
|
while (filesize > 0)
|
|
{
|
|
CARD_Write(&CardFile, &out[done], SectorSize, done);
|
|
filesize -= SectorSize;
|
|
done += SectorSize;
|
|
}
|
|
|
|
/* Close file */
|
|
CARD_Close(&CardFile);
|
|
CARD_Unmount(device);
|
|
free(out);
|
|
free(buffer);
|
|
|
|
/* Close message box */
|
|
GUI_MsgBoxClose();
|
|
}
|
|
|
|
return 1;
|
|
}
|