mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-03-06 14:27:44 +00:00
- Fixed DSB2 save states: M68K context wasn't being set.
- INI file changes: added support for opening and creating files, for a default section alias, and for sections appearing multiple times in the section list (may prevent some bugs).
This commit is contained in:
parent
12e4d82471
commit
4e7b7cc59b
252
Src/INIFile.cpp
252
Src/INIFile.cpp
|
@ -26,14 +26,11 @@
|
|||
*
|
||||
* To-Do List
|
||||
* ----------
|
||||
* - Fix file writing. Null sections are not printed but it is currently
|
||||
* possible to create a new INI, create a non-null section as the first one,
|
||||
* and then create a null section. When the file is written, the null section
|
||||
* header will not be output and all of its settings will be interpreted as
|
||||
* a continuation of the previous section, which is incorrect. Can easily be
|
||||
* fixed by always initializing with a null section.
|
||||
* - Allow default section name to be set at any time. To support this, must
|
||||
* allow for multiple sections with the same name to co-exist. The search
|
||||
* procedure must look through all sections rather than stopping at the
|
||||
* first section match. This is easy enough to add.
|
||||
* - Add boolean on/off, true/false keywords.
|
||||
* - Allow a "default" section to be specified (other than "").
|
||||
* - Note that linePtr does not necessarily correspond to actual lines in the
|
||||
* file (newlines should be counted by the tokenizer for that).
|
||||
*
|
||||
|
@ -57,10 +54,11 @@
|
|||
* INI file is written out, the current INI file on the disk is cleared. If an
|
||||
* error occurs during this process, the data will be lost.
|
||||
*
|
||||
* Blank sections (sections for which no settings are defined) are not stored
|
||||
* in the parse tree. Sections are added only when a setting is found in that
|
||||
* section. A default section name can be specified (for settings that are not
|
||||
* associated with any section), otherwise an empty string ("") is used.
|
||||
* Sections are added only when a setting is found in that section. Empty
|
||||
* sections are not added to the tree with the exception of the default ("")
|
||||
* section. A default section name can be specified, which creates an alias
|
||||
* for the default section. The alias will be output when the file is
|
||||
* written (there will be no settings without an explicit section).
|
||||
*/
|
||||
|
||||
#include "Supermodel.h"
|
||||
|
@ -91,23 +89,31 @@ BOOL CINIFile::Write(const char *comment)
|
|||
// Iterate through all sections sequentially
|
||||
for (unsigned i = 0; i < Sections.size(); i++)
|
||||
{
|
||||
// Output section name
|
||||
if (Sections[i].Name != "") // if null name, don't output section at all
|
||||
File << "[ " << Sections[i].Name << " ]" << endl << endl;
|
||||
|
||||
// Iterate through all settings within this section
|
||||
for (unsigned j = 0; j < Sections[i].Settings.size(); j++)
|
||||
if (Sections[i].Settings.size() != 0)
|
||||
{
|
||||
// Output setting
|
||||
File << Sections[i].Settings[j].Name << " = ";
|
||||
if (Sections[i].Settings[j].isNumber)
|
||||
File << Sections[i].Settings[j].value << endl;
|
||||
else
|
||||
File << '\"' << Sections[i].Settings[j].String << '\"' << endl;
|
||||
}
|
||||
// Output section name
|
||||
if (Sections[i].Name != "")
|
||||
File << "[ " << Sections[i].Name << " ]" << endl << endl;
|
||||
else // if null name, use default section name (if exists) or nothing at all
|
||||
{
|
||||
if (DefaultSectionName != "")
|
||||
File << "[ " << DefaultSectionName << " ]" << endl << endl;
|
||||
}
|
||||
|
||||
// New line
|
||||
File << endl;
|
||||
// Iterate through all settings within this section
|
||||
for (unsigned j = 0; j < Sections[i].Settings.size(); j++)
|
||||
{
|
||||
// Output setting
|
||||
File << Sections[i].Settings[j].Name << " = ";
|
||||
if (Sections[i].Settings[j].isNumber)
|
||||
File << Sections[i].Settings[j].value << endl;
|
||||
else
|
||||
File << '\"' << Sections[i].Settings[j].String << '\"' << endl;
|
||||
}
|
||||
|
||||
// New line
|
||||
File << endl;
|
||||
}
|
||||
}
|
||||
|
||||
writeSuccess = File.good()?OKAY:FAIL;
|
||||
|
@ -128,12 +134,32 @@ BOOL CINIFile::Write(const char *comment)
|
|||
BOOL CINIFile::Open(const char *fileNameStr)
|
||||
{
|
||||
FileName = fileNameStr;
|
||||
|
||||
// Try to open for reading AND writing
|
||||
File.open(fileNameStr, fstream::in|fstream::out);
|
||||
if (File.fail())
|
||||
return FAIL;
|
||||
|
||||
InitParseTree();
|
||||
return OKAY;
|
||||
}
|
||||
|
||||
BOOL CINIFile::OpenAndCreate(const char *fileNameStr)
|
||||
{
|
||||
FileName = fileNameStr;
|
||||
|
||||
// Try to open for reading and writing
|
||||
File.open(fileNameStr, fstream::in|fstream::out);
|
||||
if (File.fail())
|
||||
{
|
||||
//printf("unable to open %s\n", fileNameStr);
|
||||
return FAIL;
|
||||
// File does not exist, try opening as write only (create it)
|
||||
File.clear();
|
||||
File.open(fileNameStr, fstream::out);
|
||||
if (File.fail())
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
InitParseTree();
|
||||
return OKAY;
|
||||
}
|
||||
|
||||
|
@ -148,12 +174,14 @@ void CINIFile::Close(void)
|
|||
Management of Settings
|
||||
******************************************************************************/
|
||||
|
||||
// Finds index of section in the section list. Returns FAIL if not found.
|
||||
// Finds index of first matching section in the section list. Returns FAIL if not found.
|
||||
BOOL CINIFile::LookUpSection(unsigned *idx, string SectionName)
|
||||
{
|
||||
for (unsigned i = 0; i < Sections.size(); i++)
|
||||
{
|
||||
if (Sections[i].Name == SectionName)
|
||||
if ((Sections[i].Name == SectionName) ||
|
||||
((Sections[i].Name == "") && (SectionName == DefaultSectionName)) || // if default section, also accept its alias
|
||||
((Sections[i].Name == DefaultSectionName) && (SectionName == ""))) // ...
|
||||
{
|
||||
*idx = i;
|
||||
return OKAY;
|
||||
|
@ -162,26 +190,13 @@ BOOL CINIFile::LookUpSection(unsigned *idx, string SectionName)
|
|||
return FAIL;
|
||||
}
|
||||
|
||||
// Finds index of the setting in the given section. Returns FAIL if not found.
|
||||
BOOL CINIFile::LookUpSetting(unsigned *idx, unsigned sectionIdx, string SettingName)
|
||||
{
|
||||
for (unsigned i = 0; i < Sections[sectionIdx].Settings.size(); i++)
|
||||
{
|
||||
if (Sections[sectionIdx].Settings[i].Name == SettingName)
|
||||
{
|
||||
*idx = i;
|
||||
return OKAY;
|
||||
}
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
// Assigns a value to the given setting, creating the setting if it does not exist. Nulls out the string (sets it to "").
|
||||
void CINIFile::Set(string SectionName, string SettingName, int value)
|
||||
{
|
||||
unsigned sectionIdx, settingIdx;
|
||||
struct Setting NewSetting;
|
||||
unsigned sectionIdx, settingIdx;
|
||||
|
||||
// Check if the section exists anywhere in parse tree. If not, create it
|
||||
if (OKAY != LookUpSection(§ionIdx, SectionName))
|
||||
{
|
||||
//printf("unable to find %s:%s, creating section\n", SectionName.c_str(), SettingName.c_str());
|
||||
|
@ -192,17 +207,33 @@ void CINIFile::Set(string SectionName, string SettingName, int value)
|
|||
sectionIdx = Sections.size()-1; // the new section will be at the last index
|
||||
}
|
||||
|
||||
if (OKAY != LookUpSetting(&settingIdx, sectionIdx, SettingName))
|
||||
// Search through all sections with the requested name for the first occurance of the desired setting
|
||||
for (unsigned i = 0; i < Sections.size(); i++)
|
||||
{
|
||||
//printf("unable to find %s:%s, creating setting\n", SectionName.c_str(), SettingName.c_str());
|
||||
struct Setting NewSetting;
|
||||
|
||||
NewSetting.Name = SettingName;
|
||||
Sections[sectionIdx].Settings.push_back(NewSetting);
|
||||
settingIdx = Sections[sectionIdx].Settings.size()-1;
|
||||
if ((Sections[i].Name == SectionName) ||
|
||||
((Sections[i].Name == "") && (SectionName == DefaultSectionName)) || // accept alias for default section
|
||||
((Sections[i].Name == DefaultSectionName) && (SectionName == ""))) // ...
|
||||
{
|
||||
for (unsigned j = 0; j < Sections[i].Settings.size(); j++)
|
||||
{
|
||||
if (Sections[i].Settings[j].Name == SettingName)
|
||||
{
|
||||
// Found it! Update value of this setting
|
||||
sectionIdx = i;
|
||||
settingIdx = j;
|
||||
goto UpdateIntValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update value of this setting
|
||||
// Couldn't find setting, create it in the first matching section found earlier
|
||||
NewSetting.Name = SettingName;
|
||||
Sections[sectionIdx].Settings.push_back(NewSetting);
|
||||
settingIdx = Sections[sectionIdx].Settings.size()-1;
|
||||
|
||||
// Update the setting!
|
||||
UpdateIntValue:
|
||||
Sections[sectionIdx].Settings[settingIdx].isNumber = TRUE;
|
||||
Sections[sectionIdx].Settings[settingIdx].value = value;
|
||||
Sections[sectionIdx].Settings[settingIdx].String = "";
|
||||
|
@ -211,8 +242,10 @@ void CINIFile::Set(string SectionName, string SettingName, int value)
|
|||
// Assigns the string to the given setting, creating the setting if it does not exist. Zeros out the value.
|
||||
void CINIFile::Set(string SectionName, string SettingName, string String)
|
||||
{
|
||||
unsigned sectionIdx, settingIdx;
|
||||
struct Setting NewSetting;
|
||||
unsigned sectionIdx, settingIdx;
|
||||
|
||||
// Check if the section exists anywhere in parse tree. If not, create it
|
||||
if (OKAY != LookUpSection(§ionIdx, SectionName))
|
||||
{
|
||||
//printf("unable to find %s:%s, creating section\n", SectionName.c_str(), SettingName.c_str());
|
||||
|
@ -223,17 +256,33 @@ void CINIFile::Set(string SectionName, string SettingName, string String)
|
|||
sectionIdx = Sections.size()-1; // the new section will be at the last index
|
||||
}
|
||||
|
||||
if (OKAY != LookUpSetting(&settingIdx, sectionIdx, SettingName))
|
||||
// Search through all sections with the requested name for the first occurance of the desired setting
|
||||
for (unsigned i = 0; i < Sections.size(); i++)
|
||||
{
|
||||
//printf("unable to find %s:%s, creating setting\n", SectionName.c_str(), SettingName.c_str());
|
||||
struct Setting NewSetting;
|
||||
|
||||
NewSetting.Name = SettingName;
|
||||
Sections[sectionIdx].Settings.push_back(NewSetting);
|
||||
settingIdx = Sections[sectionIdx].Settings.size()-1;
|
||||
if ((Sections[i].Name == SectionName) ||
|
||||
((Sections[i].Name == "") && (SectionName == DefaultSectionName)) || // accept alias for default section
|
||||
((Sections[i].Name == DefaultSectionName) && (SectionName == ""))) // ...
|
||||
{
|
||||
for (unsigned j = 0; j < Sections[i].Settings.size(); j++)
|
||||
{
|
||||
if (Sections[i].Settings[j].Name == SettingName)
|
||||
{
|
||||
// Found it! Update value of this setting
|
||||
sectionIdx = i;
|
||||
settingIdx = j;
|
||||
goto UpdateString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update string
|
||||
// Couldn't find setting, create it in the first matching section found earlier
|
||||
NewSetting.Name = SettingName;
|
||||
Sections[sectionIdx].Settings.push_back(NewSetting);
|
||||
settingIdx = Sections[sectionIdx].Settings.size()-1;
|
||||
|
||||
// Update the setting!
|
||||
UpdateString:
|
||||
Sections[sectionIdx].Settings[settingIdx].isNumber = FALSE;
|
||||
Sections[sectionIdx].Settings[settingIdx].String = String;
|
||||
Sections[sectionIdx].Settings[settingIdx].value = 0;
|
||||
|
@ -242,16 +291,24 @@ void CINIFile::Set(string SectionName, string SettingName, string String)
|
|||
// Obtains a numerical setting, if it exists, otherwise does nothing.
|
||||
BOOL CINIFile::Get(string SectionName, string SettingName, int& value)
|
||||
{
|
||||
unsigned sectionIdx, settingIdx;
|
||||
|
||||
if (OKAY != LookUpSection(§ionIdx, SectionName))
|
||||
return FAIL;
|
||||
if (OKAY != LookUpSetting(&settingIdx, sectionIdx, SettingName))
|
||||
return FAIL;
|
||||
|
||||
value = Sections[sectionIdx].Settings[settingIdx].value;
|
||||
|
||||
return OKAY;
|
||||
for (unsigned i = 0; i < Sections.size(); i++)
|
||||
{
|
||||
if ((Sections[i].Name == SectionName) ||
|
||||
((Sections[i].Name == "") && (SectionName == DefaultSectionName)) || // accept alias for default section
|
||||
((Sections[i].Name == DefaultSectionName) && (SectionName == ""))) // ...
|
||||
{
|
||||
for (unsigned j = 0; j < Sections[i].Settings.size(); j++)
|
||||
{
|
||||
if (Sections[i].Settings[j].Name == SettingName)
|
||||
{
|
||||
value = Sections[i].Settings[j].value;
|
||||
return OKAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value)
|
||||
|
@ -268,16 +325,29 @@ BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value)
|
|||
// Obtains a string setting, if it exists, otherwise does nothing.
|
||||
BOOL CINIFile::Get(string SectionName, string SettingName, string& String)
|
||||
{
|
||||
unsigned sectionIdx, settingIdx;
|
||||
|
||||
if (OKAY != LookUpSection(§ionIdx, SectionName))
|
||||
return FAIL;
|
||||
if (OKAY != LookUpSetting(&settingIdx, sectionIdx, SettingName))
|
||||
return FAIL;
|
||||
|
||||
String = Sections[sectionIdx].Settings[settingIdx].String;
|
||||
|
||||
return OKAY;
|
||||
for (unsigned i = 0; i < Sections.size(); i++)
|
||||
{
|
||||
if ((Sections[i].Name == SectionName) ||
|
||||
((Sections[i].Name == "") && (SectionName == DefaultSectionName)) || // accept alias for default section
|
||||
((Sections[i].Name == DefaultSectionName) && (SectionName == ""))) // ...
|
||||
{
|
||||
for (unsigned j = 0; j < Sections[i].Settings.size(); j++)
|
||||
{
|
||||
if (Sections[i].Settings[j].Name == SettingName)
|
||||
{
|
||||
String = Sections[i].Settings[j].String;
|
||||
return OKAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
void CINIFile::SetDefaultSectionName(string SectionName)
|
||||
{
|
||||
DefaultSectionName = SectionName;
|
||||
}
|
||||
|
||||
|
||||
|
@ -515,13 +585,27 @@ CINIFile::CToken CINIFile::GetToken(void)
|
|||
Parser
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
* Parse tree is initialized with a blank section in case the user adds
|
||||
* settings with defined sections before adding settings without an explicit
|
||||
* section. If this is not done, settings without a section will be wrongly
|
||||
* output as part of the previous section in the parse tree.
|
||||
*/
|
||||
void CINIFile::InitParseTree(void)
|
||||
{
|
||||
struct Section FirstSection;
|
||||
|
||||
FirstSection.Name = "";
|
||||
Sections.clear();
|
||||
Sections.push_back(FirstSection);
|
||||
}
|
||||
|
||||
BOOL CINIFile::Parse(void)
|
||||
{
|
||||
CToken T, U, V, W;
|
||||
string currentSection; // current section we're processing
|
||||
BOOL parseStatus = OKAY;
|
||||
|
||||
//currentSection = defaultSection; // default "global" section
|
||||
lineNum = 0;
|
||||
|
||||
if (!File.is_open())
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
/*
|
||||
* CINIFile:
|
||||
*
|
||||
|
@ -91,7 +93,7 @@ public:
|
|||
*
|
||||
* Outputs the parse tree to the INI file. The current contents of the file
|
||||
* on the disk are discarded and overwritten. This means that comments are
|
||||
* lost.
|
||||
* lost. The file is also put into write mode and cannot be re-parsed.
|
||||
*
|
||||
* Parameters:
|
||||
* comment An optional C-string containing a comment to insert at
|
||||
|
@ -117,14 +119,39 @@ public:
|
|||
* already present in the parse tree will continue to exist and will be
|
||||
* overwritten if new, matching settings are found in the file.
|
||||
*
|
||||
* This should be done once and at the beginning.
|
||||
*
|
||||
* Returns:
|
||||
* OKAY if successful, FAIL if there was a file or parse error.
|
||||
*/
|
||||
BOOL Parse(void);
|
||||
|
||||
/*
|
||||
* SetDefaultSectionName(SectionName):
|
||||
*
|
||||
* Sets the default section name. Any settings not associated with a
|
||||
* particular section (ie. those assigned to a blank section, "") will be
|
||||
* output under this section when Write() is called.
|
||||
*
|
||||
* This should be called before parsing and before adding any settings.
|
||||
* If the same setting is added to the default section, "", and a section
|
||||
* named "Global", and afterwards "Global" is set as the default name,
|
||||
* the setting originally in the default section will be the one that is
|
||||
* subsequently accessible (the Get()/Set() methods look for the first
|
||||
* match). However, when the file is written out, the second setting
|
||||
* will be written out as well using the same section name and, thus,
|
||||
* when the INI file is re-parsed, its value will be used instead.
|
||||
*
|
||||
* Parameters:
|
||||
* SectionName String defining the section name.
|
||||
*/
|
||||
void SetDefaultSectionName(string SectionName);
|
||||
|
||||
/*
|
||||
* Open(fileNameStr):
|
||||
*
|
||||
* Opens an INI file.
|
||||
* Opens an INI file. The file must already exist. A new one will not be
|
||||
* created. Do not open another file before closing the current one!
|
||||
*
|
||||
* Parameters:
|
||||
* fileNameStr File path (C string).
|
||||
|
@ -134,6 +161,20 @@ public:
|
|||
*/
|
||||
BOOL Open(const char *fileNameStr);
|
||||
|
||||
/*
|
||||
* OpenAndCreate(fileNameStr):
|
||||
*
|
||||
* Opens an INI file and, if it does not exist, creates a new one. Do not
|
||||
* open another file before closing the current one!
|
||||
*
|
||||
* Parameters:
|
||||
* fileNameStr File path (C string).
|
||||
*
|
||||
* Returns:
|
||||
* OKAY if successful, FAIL if unable to open file.
|
||||
*/
|
||||
BOOL OpenAndCreate(const char *fileNameStr);
|
||||
|
||||
/*
|
||||
* Close(void):
|
||||
*
|
||||
|
@ -154,9 +195,9 @@ private:
|
|||
CToken(void);
|
||||
};
|
||||
|
||||
// Searching/modification of settings
|
||||
// Parse tree
|
||||
BOOL LookUpSection(unsigned *idx, string SectionName);
|
||||
BOOL LookUpSetting(unsigned *idx, unsigned sectionIdx, string SettingName);
|
||||
void InitParseTree(void);
|
||||
|
||||
// Tokenizer
|
||||
CToken GetString(void);
|
||||
|
@ -168,6 +209,9 @@ private:
|
|||
fstream File;
|
||||
string FileName; // name of last file opened
|
||||
|
||||
// Default section name (name to use for the blank section)
|
||||
string DefaultSectionName;
|
||||
|
||||
// Parser state
|
||||
char lineBuf[2048]; // holds current line
|
||||
char *linePtr; // points to current position within line (for tokenization)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//TODO: DSB2 save states not working!
|
||||
//TODO: amp can print error messages -- change them to Supermodel error messages
|
||||
/**
|
||||
** Supermodel
|
||||
|
@ -1029,6 +1030,7 @@ void CDSB2::SaveState(CBlockFile *StateFile)
|
|||
StateFile->Write(volume, sizeof(volume));
|
||||
|
||||
// 68K CPU state
|
||||
M68KSetContext(&M68K);
|
||||
M68KSaveState(StateFile, "DSB2 68K");
|
||||
}
|
||||
|
||||
|
@ -1061,7 +1063,9 @@ void CDSB2::LoadState(CBlockFile *StateFile)
|
|||
StateFile->Read(&playing, sizeof(playing));
|
||||
StateFile->Read(volume, sizeof(volume));
|
||||
|
||||
M68KSetContext(&M68K);
|
||||
M68KLoadState(StateFile, "DSB2 68K");
|
||||
M68KGetContext(&M68K);
|
||||
|
||||
// Restart MPEG audio at the appropriate position
|
||||
if (isPlaying)
|
||||
|
|
Loading…
Reference in a new issue