Added state saving and loading using the F9, F10, and F11 keys. The

game state is stored in a 'state' sub-directory of the current working
directory or in a 'state' sub-directory of the directory specified by
the 'STELLA_HOME' environment variable if it is set.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@163 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
bwmott 2003-01-08 05:19:07 +00:00
parent 7ca813cc1b
commit 8d530f38a2
1 changed files with 216 additions and 3 deletions

View File

@ -8,18 +8,19 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
// Copyright (c) 1995-2003 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: mainDOS.cxx,v 1.8 2002-11-13 03:47:55 bwmott Exp $
// $Id: mainDOS.cxx,v 1.9 2003-01-08 05:19:07 bwmott Exp $
//============================================================================
#include <go32.h>
#include <dpmi.h>
#include <sys/farptr.h>
#include <sys/nearptr.h>
#include <sys/stat.h>
#include <dos.h>
#include <pc.h>
#include <unistd.h>
@ -88,6 +89,8 @@ bool theUseModeXFlag = false;
// Pointer to the joysticks object
PCJoysticks* theJoysticks;
#define MESSAGE_INTERVAL 2
// Mouse IRQ
#define MOUSE_BIOS 0x33
@ -110,6 +113,122 @@ _go32_dpmi_seginfo theOldKeyboardHandler;
_go32_dpmi_seginfo theKeyboardHandler;
static void keyboardInterruptServiceRoutine(void);
// Indicates the current state to use for state saving
static uInt32 theCurrentState = 0;
// The locations for various required files
static string theHomeDir;
static string theStateDir;
/**
Changes the current state slot.
*/
void changeState(uInt32 change)
{
// Calculate new state slot
theCurrentState = (theCurrentState + ((change == 0) ? 9 : 1)) % 10;
// Print appropriate message
ostringstream buf;
buf << "Changed to slot " << theCurrentState;
string message = buf.str();
theConsole->mediaSource().showMessage(message,
MESSAGE_INTERVAL * theDesiredFrameRate);
}
/**
Saves state of the current game in the current slot.
*/
void saveState()
{
ostringstream buf;
string md5 = theConsole->properties().get("Cartridge.MD5");
string sub1 = theStateDir + '\\' + md5.substr(0, 8);
string sub2 = sub1 + '\\' + md5.substr(8, 8);
string sub3 = sub2 + '\\' + md5.substr(16, 8);
buf << sub3 << '\\' << md5.substr(24, 8) << ".st" << theCurrentState;
string filename = buf.str();
// If needed create the sub-directory corresponding to the characters
// of the MD5 checksum
if(access(sub1.c_str(), F_OK) != 0)
{
if(mkdir(sub1.c_str(), S_IWUSR) != 0)
{
theConsole->mediaSource().showMessage(string() = "Error Saving State",
MESSAGE_INTERVAL * theDesiredFrameRate);
return;
}
}
if(access(sub2.c_str(), F_OK) != 0)
{
if(mkdir(sub2.c_str(), S_IWUSR) != 0)
{
theConsole->mediaSource().showMessage(string() = "Error Saving State",
MESSAGE_INTERVAL * theDesiredFrameRate);
return;
}
}
if(access(sub3.c_str(), F_OK) != 0)
{
if(mkdir(sub3.c_str(), S_IWUSR) != 0)
{
theConsole->mediaSource().showMessage(string() = "Error Saving State",
MESSAGE_INTERVAL * theDesiredFrameRate);
return;
}
}
// Do a state save using the System
int result = theConsole->system().saveState(filename, md5);
// Print appropriate message
buf.str("");
if(result == 1)
buf << "State " << theCurrentState << " saved";
else if(result == 2)
buf << "Error saving state " << theCurrentState;
else if(result == 3)
buf << "Invalid state " << theCurrentState << " file";
string message = buf.str();
theConsole->mediaSource().showMessage(message, MESSAGE_INTERVAL *
theDesiredFrameRate);
}
/**
Loads state from the current slot for the current game.
*/
void loadState()
{
ostringstream buf;
string md5 = theConsole->properties().get("Cartridge.MD5");
string sub1 = theStateDir + '\\' + md5.substr(0, 8);
string sub2 = sub1 + '\\' + md5.substr(8, 8);
string sub3 = sub2 + '\\' + md5.substr(16, 8);
buf << sub3 << '\\' << md5.substr(24, 8) << ".st" << theCurrentState;
string filename = buf.str();
// Do a state load using the System
int result = theConsole->system().loadState(filename, md5);
// Print appropriate message
buf.str("");
if(result == 1)
buf << "State " << theCurrentState << " loaded";
else if(result == 2)
buf << "Error loading state " << theCurrentState;
else if(result == 3)
buf << "Invalid state " << theCurrentState << " file";
string message = buf.str();
theConsole->mediaSource().showMessage(message, MESSAGE_INTERVAL *
theDesiredFrameRate);
}
/**
This routine should be called once the console is create to setup
@ -510,6 +629,47 @@ void updateEventsUsingKeyboardState()
theQuitIndicator = true;
}
// Handle switching save state slots
static bool changedState = false;
if((theKeyboardKeyState[SCAN_F10]) && !changedState)
{
if(theKeyboardKeyState[SCAN_LSHIFT] || theKeyboardKeyState[SCAN_RSHIFT])
{
changeState(0);
}
else
{
changeState(1);
}
changedState = true;
}
else if(!theKeyboardKeyState[SCAN_F10])
{
changedState = false;
}
static bool savedState = false;
if(!savedState && theKeyboardKeyState[SCAN_F9])
{
saveState();
savedState = true;
}
else if(!theKeyboardKeyState[SCAN_F9])
{
savedState = false;
}
static bool loadedState = false;
if(!loadedState && theKeyboardKeyState[SCAN_F11])
{
loadState();
loadedState = true;
}
else if(!theKeyboardKeyState[SCAN_F11])
{
loadedState = false;
}
// First we clear all of the keyboard events
for(unsigned int k = 0; k < sizeof(list) / sizeof(Switches); ++k)
{
@ -697,6 +857,53 @@ static void keyboardInterruptServiceRoutine(void)
outp(0x20, 0x20);
}
/**
Ensure that the necessary directories are created for Stella under
STELLA_HOME or the current working directory if STELLA_HOME is not
defined.
Required directories are $STELLA_HOME/state.
This must be called before any other function.
*/
bool setupDirs()
{
// Get the directory to use
theHomeDir = getenv("STELLA_HOME");
if(theHomeDir == "")
{
theHomeDir = ".";
}
// Remove trailing backslash
if((theHomeDir.length() >= 1) &&
(theHomeDir[theHomeDir.length() - 1] == '\\'))
{
theHomeDir = theHomeDir.substr(0, theHomeDir.length() - 1);
}
// Create state saving directory if needed
theStateDir = theHomeDir + "\\state";
if(access(theStateDir.c_str(), F_OK) != 0)
{
if(mkdir(theStateDir.c_str(), S_IWUSR) != 0)
{
cerr << "ERROR: Creating state save directory "
<< theStateDir << endl;
return false;
}
}
if(access(theStateDir.c_str(), R_OK | W_OK | X_OK | D_OK) != 0)
{
cerr << "ERROR: Access not allowed to the state save directory "
<< theStateDir << endl;
return false;
}
return true;
}
/**
Display a usage message and exit the program
*/
@ -704,7 +911,7 @@ void usage()
{
static const char* message[] = {
"",
"Stella for DOS version 1.2.1",
"Stella for DOS version 1.3",
"",
"Usage: stella [option ...] file",
"",
@ -818,6 +1025,12 @@ void handleCommandLineArguments(int argc, char* argv[])
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int main(int argc, char* argv[])
{
// First set up the directories where Stella will find RC and state files
if(!setupDirs())
{
return 0;
}
// Handle the command line arguments
handleCommandLineArguments(argc, argv);