Skip to content

Commit

Permalink
Add BIOS state to savestates and make them more robust
Browse files Browse the repository at this point in the history
  • Loading branch information
vircon32 committed Oct 16, 2024
1 parent 4269931 commit 9a44a83
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 23 deletions.
15 changes: 15 additions & 0 deletions ConsoleLogic/V32Console.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ namespace V32
// now load the bios from the stream
LoadBiosData( FileInput );

// save the file name
BiosFileName = GetPathFileName( FilePath );

// close the file
FileInput.close();
}
Expand Down Expand Up @@ -431,6 +434,12 @@ namespace V32
// discard the temporary buffer
LoadedSound.clear();

// only when loading was successful:
// copy BIOS metadata
BiosTitle = ROMHeader.Title;
BiosVersion = ROMHeader.ROMVersion;
BiosRevision = ROMHeader.ROMRevision;

// report success
Callbacks::LogLine( "Finished loading BIOS" );
}
Expand All @@ -445,6 +454,10 @@ namespace V32

// release bios program ROM
BiosProgramROM.Disconnect();
BiosFileName = "";
BiosTitle = "";
BiosVersion = 0;
BiosRevision = 0;

// release the bios texture
Callbacks::UnloadBiosTexture();
Expand Down Expand Up @@ -718,6 +731,8 @@ namespace V32
CartridgeController.NumberOfSounds = 0;
CartridgeController.CartridgeFileName = "";
CartridgeController.CartridgeTitle = "";
CartridgeController.CartridgeVersion = 0;
CartridgeController.CartridgeRevision = 0;

// tell GPU to release all cartridge textures
GPU.RemoveCartridgeTextures();
Expand Down
6 changes: 6 additions & 0 deletions ConsoleLogic/V32Console.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ namespace V32
// internal state
bool PowerIsOn;

// additional data about the connected bios
std::string BiosFileName;
std::string BiosTitle;
uint32_t BiosVersion;
uint32_t BiosRevision;

// performance info (given in %)
float LastCPULoads[ 2 ];
float LastGPULoads[ 2 ];
Expand Down
58 changes: 48 additions & 10 deletions Savestates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ void SaveGPUState( GPUState& State )
// read all registers as adjacent
memcpy( State.Registers, &GPU.Command, sizeof(State.Registers) );

// copy the BIOS texture
memcpy( &State.BiosTexture, &GPU.BiosTexture, sizeof(GPUTexture) );

// copy only the needed cartridge textures
unsigned TexturesSize = sizeof(GPUTexture) * GPU.LoadedCartridgeTextures;
memcpy( State.CartridgeTextures, &GPU.CartridgeTextures[ 0 ], TexturesSize );
Expand All @@ -54,14 +57,17 @@ void SaveSPUState( SPUState& State )
// read all channels as adjacent
memcpy( State.Channels, &SPU.Channels, sizeof(State.Channels) );

// copy the BIOS sound
memcpy( &State.BiosSound, &SPU.BiosSound, sizeof(SPUSoundState) );

// copy only the needed cartridge sounds
// (size will stay the same, but speed will increase)
unsigned CartridgeSounds = SPU.LoadedCartridgeSounds;

// do not read data for all sounds as a block!!
// we don't want to copy the sample vector in each sound
for( unsigned SoundID = 0; SoundID < CartridgeSounds; SoundID++ )
memcpy( &State.CartridgeSounds[ SoundID ], &SPU.CartridgeSounds[ SoundID ], 4 * sizeof(int32_t) );
memcpy( &State.CartridgeSounds[ SoundID ], &SPU.CartridgeSounds[ SoundID ], sizeof(SPUSoundState) );
}

// -----------------------------------------------------------------------------
Expand All @@ -88,24 +94,43 @@ void SaveOtherConsoleState( OtherConsoleState& State )

// -----------------------------------------------------------------------------

void SaveGameInfo( GameInfo& Info )
void SaveGameInfo( ROMInfo& Info )
{
// ensure title does not exceed 64 bytes and
// that its unused characters are all null
memset( Info.CartridgeTitle, 0, sizeof(Info.CartridgeTitle) );
strncpy( Info.CartridgeTitle, Console.CartridgeController.CartridgeTitle.c_str(), sizeof(Info.CartridgeTitle) - 1 );
memset( Info.Title, 0, sizeof(Info.Title) );
strncpy( Info.Title, Console.CartridgeController.CartridgeTitle.c_str(), sizeof(Info.Title) - 1 );

Info.Version = Console.CartridgeController.CartridgeVersion;
Info.Revision = Console.CartridgeController.CartridgeRevision;
Info.ProgramROMSize = Console.CartridgeController.MemorySize;
Info.NumberOfTextures = Console.CartridgeController.NumberOfTextures;
Info.NumberOfSounds = Console.CartridgeController.NumberOfSounds;
}

// -----------------------------------------------------------------------------

void SaveBiosInfo( ROMInfo& Info )
{
// ensure title does not exceed 64 bytes and
// that its unused characters are all null
memset( Info.Title, 0, sizeof(Info.Title) );
strncpy( Info.Title, Console.BiosTitle.c_str(), sizeof(Info.Title) - 1 );

Info.Version = Console.BiosVersion;
Info.Revision = Console.BiosRevision;
Info.ProgramROMSize = Console.BiosProgramROM.MemorySize;
Info.NumberOfTextures = 1;
Info.NumberOfSounds = 1;
}

// -----------------------------------------------------------------------------

bool SaveState( ConsoleState* State )
{
// save info to identify the game
// save info to identify the game and BIOS
SaveGameInfo( State->Game );
SaveBiosInfo( State->Bios );

// save console state
SaveCPUState( State->CPU );
Expand Down Expand Up @@ -140,6 +165,9 @@ bool LoadGPUState( const GPUState& State )
// write all registers as adjacent
memcpy( &GPU.Command, State.Registers, sizeof(State.Registers) );

// copy the BIOS texture
memcpy( &GPU.BiosTexture, &State.BiosTexture, sizeof(GPUTexture) );

// copy only the needed cartridge textures
unsigned TexturesSize = sizeof(GPUTexture) * GPU.LoadedCartridgeTextures;
memcpy( &GPU.CartridgeTextures[ 0 ], State.CartridgeTextures, TexturesSize );
Expand Down Expand Up @@ -179,14 +207,17 @@ void LoadSPUState( const SPUState& State )
// write all channels as adjacent
memcpy( &SPU.Channels, State.Channels, sizeof(State.Channels) );

// copy the BIOS sound
memcpy( &SPU.BiosSound, &State.BiosSound, sizeof(SPUSoundState) );

// copy only the needed cartridge sounds
// (size will stay the same, but speed will increase)
unsigned CartridgeSounds = SPU.LoadedCartridgeSounds;

// do not load data for all sounds as a block!!
// we must not overwrite the sample vector in each sound
for( unsigned SoundID = 0; SoundID < CartridgeSounds; SoundID++ )
memcpy( &SPU.CartridgeSounds[ SoundID ], &State.CartridgeSounds[ SoundID ], 4 * sizeof(int32_t) );
memcpy( &SPU.CartridgeSounds[ SoundID ], &State.CartridgeSounds[ SoundID ], sizeof(SPUSoundState) );

// make the needed updates in audio objects
V32Word WordValue;
Expand Down Expand Up @@ -233,17 +264,24 @@ void LoadOtherConsoleState( const OtherConsoleState& State )

bool LoadState( const ConsoleState* State )
{
// try to identify the game and see it it matches the
// current one, to avoid loading incompatible states
GameInfo CurrentGame;
// try to identify the game and BIOS and see if they
// match current ones, to avoid loading incompatible states
ROMInfo CurrentGame, CurrentBios;
SaveGameInfo( CurrentGame );
SaveBiosInfo( CurrentBios );

if( memcmp( &State->Game, &CurrentGame, sizeof(GameInfo) ) )
if( memcmp( &State->Game, &CurrentGame, sizeof(ROMInfo) ) )
{
LOG( "ERROR: Cannot load saved state. Current cartridge is not the same one that was saved" );
return false;
}

if( memcmp( &State->Bios, &CurrentBios, sizeof(ROMInfo) ) )
{
LOG( "ERROR: Cannot load saved state. Current BIOS is not the same one that was used when saving" );
return false;
}

// load console state
// (first do stages that cannot fail)
LoadCPUState( State->CPU );
Expand Down
44 changes: 32 additions & 12 deletions Savestates.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,29 @@ typedef struct
// (12 words in total)
V32::V32Word Registers[ 12 ];

// configuration for the BIOS texture
// (note that this ties each savestate to a particular BIOS)
V32::GPUTexture BiosTexture;

// configuration for cartridge textures
V32::GPUTexture CartridgeTextures[ V32::Constants::GPUMaximumCartridgeTextures ];

// do not include the BIOS texture; that may
// break savestates if different BIOSes are
// used, and games should not alter BIOS anyway
}
GPUState;

// -----------------------------------------------------------------------------

typedef struct
{
// same as SPUSound but not including the sound samples
int32_t Length;
int32_t PlayWithLoop;
int32_t LoopStart;
int32_t LoopEnd;
}
SPUSoundState;

// -----------------------------------------------------------------------------

typedef struct
{
// all exposed SPU registers that are not
Expand All @@ -57,12 +69,12 @@ typedef struct
// all SPU channels
V32::SPUChannel Channels[ V32::Constants::SPUSoundChannels ];

// configuration for cartridge sounds
V32::SPUSound CartridgeSounds[ V32::Constants::SPUMaximumCartridgeSounds ];
// configuration for the BIOS sound
// (note that this ties each savestate to a particular BIOS)
SPUSoundState BiosSound;

// do not include the BIOS sound; that may
// break savestates if different BIOSes are
// used, and games should not alter BIOS anyway
// configuration for cartridge sounds
SPUSoundState CartridgeSounds[ V32::Constants::SPUMaximumCartridgeSounds ];
}
SPUState;

Expand Down Expand Up @@ -106,12 +118,14 @@ OtherConsoleState;

typedef struct
{
char CartridgeTitle[ 64 ];
char Title[ 64 ];
uint32_t Version;
uint32_t Revision;
int32_t ProgramROMSize;
int32_t NumberOfTextures;
int32_t NumberOfSounds;
}
GameInfo;
ROMInfo;

// -----------------------------------------------------------------------------

Expand All @@ -121,7 +135,13 @@ typedef struct
// basic attempt at distinguish different
// games to prevent loading an incompatible
// savestate and messing things up
GameInfo Game;
ROMInfo Game;

// data to identify the BIOS; this is much
// less important than the game itself, but
// if different BIOSes are used to save and
// load it can produce graphic errors
ROMInfo Bios;

// data for all stateful console components;
// make GPU last so that we can adjust size
Expand Down
2 changes: 1 addition & 1 deletion libretro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ void retro_get_system_info( struct retro_system_info *info )
{
memset( info, 0, sizeof( *info ) );
info->library_name = "Vircon32";
info->library_version = "2024.08.28";
info->library_version = "2024.10.16";
info->need_fullpath = true; // games can be too large to hold in memory
info->valid_extensions = "v32|V32"; // target system may be case sensitive
}
Expand Down

0 comments on commit 9a44a83

Please sign in to comment.