mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-12-27 03:31:49 +01:00
588 lines
16 KiB
C
588 lines
16 KiB
C
/****************************************************************************
|
|
* Genesis Plus 1.2a
|
|
*
|
|
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
***************************************************************************/
|
|
#include "shared.h"
|
|
#include "dvd.h"
|
|
#include "font.h"
|
|
#include "rominfo.h"
|
|
#include "saveicon.h" /*** Nice little icon - thanks brakken! ***/
|
|
|
|
/* Support for MemCards */
|
|
/**
|
|
* libOGC System Work Area
|
|
*/
|
|
static u8 SysArea[CARD_WORKAREA] ATTRIBUTE_ALIGN (32);
|
|
|
|
/**
|
|
* DMA Transfer Area.
|
|
* Must be 32-byte aligned.
|
|
* 64k SRAM + 2k Icon
|
|
*/
|
|
unsigned char savebuffer[0x24000] ATTRIBUTE_ALIGN (32);
|
|
|
|
card_dir CardDir;
|
|
card_file CardFile;
|
|
card_stat CardStatus;
|
|
extern int CARDSLOT;
|
|
extern int use_SDCARD;
|
|
|
|
int ManageSRAM (int direction);
|
|
int ManageState (int direction);
|
|
|
|
/****************************************************************************
|
|
* SRAM autoload
|
|
*
|
|
* the detection order is the following:
|
|
* MCARD (SLOTA) > MCARD (SLOTB) > SDCARD (SLOTA) > SDCARD (SLOTB)
|
|
*
|
|
*****************************************************************************/
|
|
extern u8 SILENT;
|
|
|
|
void sram_autoload()
|
|
{
|
|
int ret = 0;
|
|
int i = 0;
|
|
int old_cardslot = CARDSLOT;
|
|
int old_sdcard = use_SDCARD;
|
|
|
|
SILENT = 1; /* this should be transparent to the user */
|
|
while ((i < 4) && (!ret))
|
|
{
|
|
use_SDCARD = (i&2) ? 1 : 0;
|
|
CARDSLOT = (i&1) ? CARD_SLOTB : CARD_SLOTA;
|
|
ret = ManageSRAM(1);
|
|
i++;
|
|
}
|
|
if (!ret)
|
|
{
|
|
/* restore old settings if not found */
|
|
CARDSLOT = old_cardslot;
|
|
use_SDCARD = old_sdcard;
|
|
}
|
|
SILENT = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* SDCARD Access functions
|
|
*
|
|
* We use the same buffer as for Memory Card manager
|
|
* Function returns TRUE on success.
|
|
*****************************************************************************/
|
|
int SD_ManageFile(char *filename, int direction, int filetype)
|
|
{
|
|
char name[1024];
|
|
sd_file *handle;
|
|
int len = 0;
|
|
int offset = 0;
|
|
int filesize;
|
|
unsigned long inbytes,outbytes;
|
|
|
|
/* build complete SDCARD filename */
|
|
sprintf (name, "dev%d:\\genplus\\saves\\%s", CARDSLOT, filename);
|
|
|
|
/* open file */
|
|
handle = direction ? SDCARD_OpenFile (name, "rb") :
|
|
SDCARD_OpenFile (name, "wb");
|
|
|
|
if (handle == NULL)
|
|
{
|
|
sprintf (filename, "Error opening %s", name);
|
|
WaitPrompt(filename);
|
|
return 0;
|
|
}
|
|
|
|
switch (direction)
|
|
{
|
|
case 0: /* SAVING */
|
|
|
|
if (filetype) /* SRAM */
|
|
{
|
|
inbytes = 0x10000;
|
|
outbytes = 0x12000;
|
|
compress2 ((char *) &savebuffer[sizeof(outbytes)], &outbytes, (char *) &sram.sram, inbytes, 9);
|
|
memcpy(&savebuffer[0], &outbytes, sizeof(outbytes));
|
|
filesize = outbytes + sizeof(outbytes);
|
|
}
|
|
else filesize = state_save(&savebuffer[0]); /* STATE */
|
|
|
|
len = SDCARD_WriteFile (handle, savebuffer, filesize);
|
|
SDCARD_CloseFile (handle);
|
|
|
|
if (len != filesize)
|
|
{
|
|
sprintf (filename, "Error writing %s", name);
|
|
WaitPrompt (filename);
|
|
return 0;
|
|
}
|
|
|
|
sprintf (filename, "Saved %d bytes successfully", filesize);
|
|
WaitPrompt (filename);
|
|
return 1;
|
|
|
|
case 1: /* LOADING */
|
|
|
|
while ((len = SDCARD_ReadFile (handle, &savebuffer[offset], 2048)) > 0) offset += len;
|
|
if (filetype) /* SRAM */
|
|
{
|
|
memcpy(&inbytes,&savebuffer[0],sizeof(inbytes));
|
|
outbytes = 0x10000;
|
|
uncompress ((char *) &sram.sram, &outbytes, (char *) &savebuffer[sizeof(inbytes)], inbytes);
|
|
sram.crc = crc32 (0, &sram.sram[0], outbytes);
|
|
system_reset ();
|
|
}
|
|
else state_load(&savebuffer[0]); /* STATE */
|
|
SDCARD_CloseFile (handle);
|
|
|
|
sprintf (filename, "Loaded %d bytes successfully", offset);
|
|
WaitPrompt (filename);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* MountTheCard
|
|
*
|
|
* 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!
|
|
*
|
|
* Function returns TRUE on success.
|
|
*****************************************************************************/
|
|
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 ***/
|
|
if (CardError == 0) return 1;
|
|
else EXI_ProbeReset ();
|
|
tries++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CardFileExists
|
|
*
|
|
* Wrapper to search through the files on the card.
|
|
* Returns TRUE if found.
|
|
****************************************************************************/
|
|
int CardFileExists (char *filename)
|
|
{
|
|
int CardError = CARD_FindFirst (CARDSLOT, &CardDir, TRUE);
|
|
while (CardError != CARD_ERROR_NOFILE)
|
|
{
|
|
CardError = CARD_FindNext (&CardDir);
|
|
if (strcmp ((char *) CardDir.filename, filename) == 0) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* ManageSRAM
|
|
*
|
|
* Here is the main SRAM Management stuff.
|
|
* The output file contains an icon (2K), 64 bytes comment and the SRAM (64k).
|
|
* As memcards are allocated in blocks of 8k or more, you have a around
|
|
* 6k bytes to save/load any other data you wish without altering any of the
|
|
* main save / load code.
|
|
*
|
|
* direction == 0 save, 1 load.
|
|
****************************************************************************/
|
|
int ManageSRAM (int direction)
|
|
{
|
|
char filename[128];
|
|
char action[80];
|
|
int CardError;
|
|
unsigned int SectorSize;
|
|
int blocks;
|
|
char comment[2][32] = { {"Genesis Plus 1.2a"}, {"SRAM Save"} };
|
|
int outbytes = 0;
|
|
int sbo;
|
|
int i;
|
|
unsigned long inzipped,outzipped;
|
|
|
|
if (!genromsize) return 0;
|
|
|
|
if (direction) ShowAction ("Loading SRAM ...");
|
|
else ShowAction ("Saving SRAM ...");
|
|
|
|
/* First, build a filename based on the information retrieved for this ROM */
|
|
if (strlen (rominfo.domestic)) strcpy (filename, rominfo.domestic);
|
|
else strcpy (filename, rominfo.international);
|
|
|
|
/* As these names tend to be bulked with spaces, let's strip them */
|
|
/* Name should be in 16 character blocks, so take first 16 */
|
|
filename[16] = 0;
|
|
for (i = strlen (filename) - 1; i > 0; i--)
|
|
if (filename[i] != 32) break;
|
|
filename[i + 1] = 0;
|
|
|
|
/* Now add the 16bit checksum, to ensure it's the same ROM */
|
|
sprintf (filename, "%s%02X.srm", filename, rominfo.checksum);
|
|
strcpy (comment[1], filename);
|
|
|
|
/* device is SDCARD, let's go */
|
|
if (use_SDCARD) return SD_ManageFile(filename,direction,1);
|
|
|
|
/* device is MCARD, we continue */
|
|
if (direction == 0) /*** Saving ***/
|
|
{
|
|
/*** Build the output buffer ***/
|
|
memcpy (&savebuffer, &icon, 2048);
|
|
memcpy (&savebuffer[2048], &comment[0], 64);
|
|
|
|
inzipped = 0x10000;
|
|
outzipped = 0x12000;
|
|
compress2 ((char *) &savebuffer[2112+sizeof(outzipped)], &outzipped, (char *) &sram.sram, inzipped, 9);
|
|
memcpy(&savebuffer[2112], &outzipped, sizeof(outzipped));
|
|
}
|
|
|
|
outbytes = 2048 + 64 + outzipped + sizeof(outzipped);
|
|
|
|
/*** Initialise the CARD system ***/
|
|
memset (&SysArea, 0, CARD_WORKAREA);
|
|
CARD_Init ("GENP", "00");
|
|
|
|
/*** Attempt to mount the card ***/
|
|
CardError = MountTheCard ();
|
|
|
|
if (CardError)
|
|
{
|
|
/*** Retrieve the sector size ***/
|
|
CardError = CARD_GetSectorSize (CARDSLOT, &SectorSize);
|
|
|
|
switch (direction)
|
|
{
|
|
case 0: /*** Saving ***/
|
|
/*** Determine number of blocks on this card ***/
|
|
blocks = (outbytes / SectorSize) * SectorSize;
|
|
if (outbytes % SectorSize) blocks += SectorSize;
|
|
|
|
/*** Check if a previous save exists ***/
|
|
if (CardFileExists (filename))
|
|
{
|
|
CardError = CARD_Open (CARDSLOT, filename, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf (action, "Error Open : %d", CardError);
|
|
WaitPrompt (action);
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
|
|
int size = CardFile.len;
|
|
CARD_Close (&CardFile);
|
|
|
|
if (size < blocks)
|
|
{
|
|
/* new size is bigger: check if there is enough space left */
|
|
CardError = CARD_Create (CARDSLOT, "TEMP", blocks-size, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf (action, "Error Update : %d", CardError);
|
|
WaitPrompt (action);
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
CARD_Close (&CardFile);
|
|
CARD_Delete(CARDSLOT, "TEMP");
|
|
}
|
|
|
|
/* always delete existing slot */
|
|
CARD_Delete(CARDSLOT, filename);
|
|
}
|
|
|
|
/*** Create a new slot ***/
|
|
CardError = CARD_Create (CARDSLOT, filename, blocks, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf (action, "Error create : %d %d", CardError, CARDSLOT);
|
|
WaitPrompt (action);
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
|
|
/*** Continue and save ***/
|
|
CARD_GetStatus (CARDSLOT, CardFile.filenum, &CardStatus);
|
|
CardStatus.icon_addr = 0x0;
|
|
CardStatus.icon_fmt = 2;
|
|
CardStatus.icon_speed = 1;
|
|
CardStatus.comment_addr = 2048;
|
|
CARD_SetStatus (CARDSLOT, CardFile.filenum, &CardStatus);
|
|
|
|
/*** And write the blocks out ***/
|
|
sbo = 0;
|
|
while (outbytes > 0)
|
|
{
|
|
CardError = CARD_Write (&CardFile, &savebuffer[sbo], SectorSize, sbo);
|
|
outbytes -= SectorSize;
|
|
sbo += SectorSize;
|
|
}
|
|
|
|
CARD_Close (&CardFile);
|
|
CARD_Unmount (CARDSLOT);
|
|
sram.crc = crc32 (0, &sram.sram[0], 0x10000);
|
|
sprintf (action, "Saved %d bytes successfully", blocks);
|
|
WaitPrompt (action);
|
|
return 1;
|
|
|
|
default: /*** Loading ***/
|
|
if (!CardFileExists (filename))
|
|
{
|
|
WaitPrompt ("No SRAM File Found");
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
|
|
memset (&CardFile, 0, sizeof (CardFile));
|
|
CardError = CARD_Open (CARDSLOT, filename, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf (action, "Error Open : %d", CardError);
|
|
WaitPrompt (action);
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
|
|
blocks = CardFile.len;
|
|
if (blocks < SectorSize) blocks = SectorSize;
|
|
if (blocks % SectorSize) blocks++;
|
|
|
|
/*** Just read the file back in ***/
|
|
sbo = 0;
|
|
int size = blocks;
|
|
while (blocks > 0)
|
|
{
|
|
CARD_Read (&CardFile, &savebuffer[sbo], SectorSize, sbo);
|
|
sbo += SectorSize;
|
|
blocks -= SectorSize;
|
|
}
|
|
CARD_Close (&CardFile);
|
|
CARD_Unmount (CARDSLOT);
|
|
|
|
/* Copy to SRAM */
|
|
memcpy(&inzipped,&savebuffer[2112],sizeof(inzipped));
|
|
outzipped = 0x10000;
|
|
uncompress ((char *) &sram.sram, &outzipped, (char *) &savebuffer[2112+sizeof(inzipped)], inzipped);
|
|
sram.crc = crc32 (0, &sram.sram[0], 0x10000);
|
|
system_reset ();
|
|
|
|
/*** Inform user ***/
|
|
sprintf (action, "Loaded %d bytes successfully", size);
|
|
WaitPrompt (action);
|
|
return 1;
|
|
}
|
|
}
|
|
else WaitPrompt ("Unable to mount memory card");
|
|
return 0; /*** Signal failure ***/
|
|
}
|
|
|
|
/****************************************************************************
|
|
* ManageSTATE
|
|
*
|
|
* Here is the main Freeze File Management stuff.
|
|
* The output file contains an icon (2K), 64 bytes comment and the STATE (~128k)
|
|
*
|
|
* direction == 0 save, 1 load.
|
|
****************************************************************************/
|
|
int ManageState (int direction)
|
|
{
|
|
char filename[128];
|
|
char action[80];
|
|
int CardError;
|
|
unsigned int SectorSize;
|
|
int blocks;
|
|
char comment[2][32] = { {"Genesis Plus 1.2a [FRZ]"}, {"Freeze State"} };
|
|
int outbytes;
|
|
int sbo;
|
|
int i;
|
|
int state_size = 0;
|
|
|
|
if (!genromsize) return 0;
|
|
|
|
if (direction) ShowAction ("Loading State ...");
|
|
else ShowAction ("Saving State ...");
|
|
|
|
/* First, build a filename based on the information retrieved for this ROM */
|
|
if (strlen (rominfo.domestic)) strcpy (filename, rominfo.domestic);
|
|
else strcpy (filename, rominfo.international);
|
|
|
|
/* As these names tend to be bulked with spaces, let's strip them */
|
|
/* Name should be in 16 character blocks, so take first 16 */
|
|
filename[16] = 0;
|
|
for (i = strlen (filename) - 1; i > 0; i--) if (filename[i] != 32) break;
|
|
filename[i + 1] = 0;
|
|
|
|
/* Now add the 16bit checksum, to ensure it's the same ROM */
|
|
sprintf (filename, "%s%02X.gpz", filename, rominfo.checksum);
|
|
strcpy (comment[1], filename);
|
|
|
|
/* device is SDCARD, let's go */
|
|
if (use_SDCARD) return SD_ManageFile(filename,direction,0);
|
|
|
|
/* device is MCARD, we continue */
|
|
if (direction == 0) /* Saving */
|
|
{
|
|
/* Build the output buffer */
|
|
memcpy (&savebuffer, &icon, 2048);
|
|
memcpy (&savebuffer[2048], &comment[0], 64);
|
|
state_size = state_save(&savebuffer[2112]);
|
|
}
|
|
|
|
outbytes = 2048 + 64 + state_size;
|
|
|
|
/*** Initialise the CARD system ***/
|
|
memset (&SysArea, 0, CARD_WORKAREA);
|
|
CARD_Init ("GENP", "00");
|
|
|
|
/*** Attempt to mount the card ***/
|
|
CardError = MountTheCard ();
|
|
|
|
if (CardError)
|
|
{
|
|
/*** Retrieve the sector size ***/
|
|
CardError = CARD_GetSectorSize (CARDSLOT, &SectorSize);
|
|
|
|
switch (direction)
|
|
{
|
|
case 0: /*** Saving ***/
|
|
/*** Determine number of blocks on this card ***/
|
|
blocks = (outbytes / SectorSize) * SectorSize;
|
|
if (outbytes % SectorSize) blocks += SectorSize;
|
|
|
|
/*** Check if a previous save exists ***/
|
|
if (CardFileExists (filename))
|
|
{
|
|
CardError = CARD_Open (CARDSLOT, filename, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf (action, "Error Open : %d", CardError);
|
|
WaitPrompt (action);
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
|
|
int size = CardFile.len;
|
|
CARD_Close (&CardFile);
|
|
|
|
if (size < blocks)
|
|
{
|
|
/* new size is bigger: check if there is enough space left */
|
|
CardError = CARD_Create (CARDSLOT, "TEMP", blocks-size, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf (action, "Error Update : %d", CardError);
|
|
WaitPrompt (action);
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
CARD_Close (&CardFile);
|
|
CARD_Delete(CARDSLOT, "TEMP");
|
|
}
|
|
|
|
/* always delete existing slot */
|
|
CARD_Delete(CARDSLOT, filename);
|
|
}
|
|
|
|
/*** Create a new slot ***/
|
|
CardError = CARD_Create (CARDSLOT, filename, blocks, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf (action, "Error create : %d %d", CardError, CARDSLOT);
|
|
WaitPrompt (action);
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
|
|
/*** Continue and save ***/
|
|
CARD_GetStatus (CARDSLOT, CardFile.filenum, &CardStatus);
|
|
CardStatus.icon_addr = 0x0;
|
|
CardStatus.icon_fmt = 2;
|
|
CardStatus.icon_speed = 1;
|
|
CardStatus.comment_addr = 2048;
|
|
CARD_SetStatus (CARDSLOT, CardFile.filenum, &CardStatus);
|
|
|
|
/*** And write the blocks out ***/
|
|
sbo = 0;
|
|
while (outbytes > 0)
|
|
{
|
|
CardError = CARD_Write (&CardFile, &savebuffer[sbo], SectorSize, sbo);
|
|
outbytes -= SectorSize;
|
|
sbo += SectorSize;
|
|
}
|
|
|
|
CARD_Close (&CardFile);
|
|
CARD_Unmount (CARDSLOT);
|
|
sprintf (action, "Saved %d bytes successfully", blocks);
|
|
WaitPrompt (action);
|
|
return 1;
|
|
|
|
default: /*** Loading ***/
|
|
if (!CardFileExists (filename))
|
|
{
|
|
WaitPrompt ("No Savestate Found");
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
|
|
memset (&CardFile, 0, sizeof (CardFile));
|
|
CardError = CARD_Open (CARDSLOT, filename, &CardFile);
|
|
if (CardError)
|
|
{
|
|
sprintf (action, "Error Open : %d", CardError);
|
|
WaitPrompt (action);
|
|
CARD_Unmount (CARDSLOT);
|
|
return 0;
|
|
}
|
|
|
|
blocks = CardFile.len;
|
|
if (blocks < SectorSize) blocks = SectorSize;
|
|
if (blocks % SectorSize) blocks++;
|
|
|
|
/*** Just read the file back in ***/
|
|
sbo = 0;
|
|
int size = blocks;
|
|
while (blocks > 0)
|
|
{
|
|
CARD_Read (&CardFile, &savebuffer[sbo], SectorSize, sbo);
|
|
sbo += SectorSize;
|
|
blocks -= SectorSize;
|
|
}
|
|
CARD_Close (&CardFile);
|
|
CARD_Unmount (CARDSLOT);
|
|
|
|
state_load(&savebuffer[2112]);
|
|
|
|
/*** Inform user ***/
|
|
sprintf (action, "Loaded %d bytes successfully", size);
|
|
WaitPrompt (action);
|
|
return 1;
|
|
}
|
|
}
|
|
else WaitPrompt ("Unable to mount memory card");
|
|
return 0; /*** Signal failure ***/
|
|
}
|