mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 05:45:38 +00:00
Removed last vestiges of CINIFile from input system and removed Games.cpp, INIFile.cpp, and ROMLoad.cpp
This commit is contained in:
parent
df283d9aa3
commit
cc32e0bf8e
|
@ -88,8 +88,8 @@ endif
|
|||
# Objects and Dependencies
|
||||
#
|
||||
HEADERS = Src/Supermodel.h Src/Games.h Src/OSD/SDL/Types.h
|
||||
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
|
||||
$(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
|
||||
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
|
||||
$(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
|
||||
$(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Legacy3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/TextureRefs.o \
|
||||
$(OBJ_DIR)/New3D.o $(OBJ_DIR)/Mat4.o $(OBJ_DIR)/Model.o $(OBJ_DIR)/PolyHeader.o $(OBJ_DIR)/Texture.o $(OBJ_DIR)/TextureSheet.o $(OBJ_DIR)/VBO.o $(OBJ_DIR)/Vec.o $(OBJ_DIR)/R3DShader.o $(OBJ_DIR)/R3DFloat.o \
|
||||
$(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
|
||||
|
|
|
@ -95,8 +95,8 @@ endif
|
|||
# Objects and Dependencies
|
||||
#
|
||||
HEADERS = Src/Supermodel.h Src/Games.h Src/OSD/SDL/Types.h
|
||||
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
|
||||
$(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
|
||||
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
|
||||
$(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
|
||||
$(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Legacy3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/TextureRefs.o \
|
||||
$(OBJ_DIR)/New3D.o $(OBJ_DIR)/Mat4.o $(OBJ_DIR)/Model.o $(OBJ_DIR)/PolyHeader.o $(OBJ_DIR)/Texture.o $(OBJ_DIR)/TextureSheet.o $(OBJ_DIR)/VBO.o $(OBJ_DIR)/Vec.o $(OBJ_DIR)/R3DShader.o $(OBJ_DIR)/R3DFloat.o \
|
||||
$(OBJ_DIR)/Render2D.o $(OBJ_DIR)/TileGen.o \
|
||||
|
|
|
@ -119,8 +119,8 @@ endif
|
|||
#
|
||||
# Objects and Dependencies
|
||||
#
|
||||
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/INIFile.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
|
||||
$(OBJ_DIR)/ROMLoad.o $(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
|
||||
OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/BlockFile.o $(OBJ_DIR)/93C46.o \
|
||||
$(OBJ_DIR)/unzip.o $(OBJ_DIR)/ioapi.o $(OBJ_DIR)/Error.o $(OBJ_DIR)/glew.o $(OBJ_DIR)/Shader.o \
|
||||
$(OBJ_DIR)/Real3D.o $(OBJ_DIR)/Legacy3D.o $(OBJ_DIR)/Models.o $(OBJ_DIR)/TextureRefs.o \
|
||||
$(OBJ_DIR)/New3D.o $(OBJ_DIR)/Mat4.o $(OBJ_DIR)/Model.o $(OBJ_DIR)/PolyHeader.o $(OBJ_DIR)/Texture.o $(OBJ_DIR)/TextureSheet.o $(OBJ_DIR)/VBO.o $(OBJ_DIR)/Vec.o $(OBJ_DIR)/R3DShader.o $(OBJ_DIR)/R3DFloat.o \
|
||||
$(OBJ_DIR)/R3DScrollFog.o \
|
||||
|
|
3191
Src/Games.cpp
3191
Src/Games.cpp
File diff suppressed because it is too large
Load diff
109
Src/Games.h
109
Src/Games.h
|
@ -1,109 +0,0 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
** Supermodel 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 3 of the License, or (at your option)
|
||||
** any later version.
|
||||
**
|
||||
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
/*
|
||||
* Games.h
|
||||
*
|
||||
* Header file containing Model 3 game and ROM file information.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_GAMES_H
|
||||
#define INCLUDED_GAMES_H
|
||||
|
||||
|
||||
#include "ROMLoad.h" // ROMInfo structure
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Definitions
|
||||
******************************************************************************/
|
||||
|
||||
// Input flags
|
||||
#define GAME_INPUT_COMMON 0x000000001 // game has common controls
|
||||
#define GAME_INPUT_VEHICLE 0x000000002 // game has vehicle controls
|
||||
#define GAME_INPUT_JOYSTICK1 0x000000004 // game has joystick 1
|
||||
#define GAME_INPUT_JOYSTICK2 0x000000008 // game has joystick 2
|
||||
#define GAME_INPUT_FIGHTING 0x000000010 // game has fighting game controls
|
||||
#define GAME_INPUT_VR4 0x000000020 // game has four VR view buttons
|
||||
#define GAME_INPUT_VIEWCHANGE 0x000000040 // game has single view change button
|
||||
#define GAME_INPUT_SHIFT4 0x000000080 // game has 4-speed shifter
|
||||
#define GAME_INPUT_SHIFTUPDOWN 0x000000100 // game has up/down shifter
|
||||
#define GAME_INPUT_HANDBRAKE 0x000000200 // game has handbrake
|
||||
#define GAME_INPUT_HARLEY 0x000000400 // game has Harley Davidson controls
|
||||
#define GAME_INPUT_GUN1 0x000000800 // game has light gun 1
|
||||
#define GAME_INPUT_GUN2 0x000001000 // game has light gun 2
|
||||
#define GAME_INPUT_ANALOG_JOYSTICK 0x000002000 // game has analog joystick
|
||||
#define GAME_INPUT_TWIN_JOYSTICKS 0x000004000 // game has twin joysticks
|
||||
#define GAME_INPUT_SOCCER 0x000008000 // game has soccer controls
|
||||
#define GAME_INPUT_SPIKEOUT 0x000010000 // game has Spikeout buttons
|
||||
#define GAME_INPUT_ANALOG_GUN1 0x000020000 // game has analog gun 1
|
||||
#define GAME_INPUT_ANALOG_GUN2 0x000040000 // game has analog gun 2
|
||||
#define GAME_INPUT_SKI 0x000080000 // game has ski controls
|
||||
#define GAME_INPUT_MAGTRUCK 0x000100000 // game has magical truck controls
|
||||
#define GAME_INPUT_FISHING 0x000200000 // game has fishing controls
|
||||
#define GAME_INPUT_ALL 0x0003FFFFF
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Data Structures
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
* GameInfo:
|
||||
*
|
||||
* Describes a Model 3 game. List is terminated when title == NULL.
|
||||
*/
|
||||
struct GameInfo
|
||||
{
|
||||
// Game information
|
||||
const char id[10]; // 9-character game identifier (also serves as zip archive file name)
|
||||
const char *parent; // parent game identifier
|
||||
const char *title; // complete game title
|
||||
const char *mfgName; // name of manufacturer
|
||||
unsigned year; // year released (in decimal)
|
||||
int step; // Model 3 hardware stepping: 0x10 = 1.0, 0x15 = 1.5, 0x20 = 2.0, 0x21 = 2.1
|
||||
uint32_t cromSize; // size of fixed CROM (up to 8 MB)
|
||||
bool mirrorLow64MB; // mirror low 64 MB of banked CROM space to upper 64 MB
|
||||
uint32_t vromSize; // size of video ROMs (32 or 64 MB; if 32 MB, will have to be mirrored)
|
||||
uint32_t sampleSize; // size of sample ROMS (8 or 16 MB; if 8 MB, will have to be mirrored)
|
||||
uint32_t inputFlags; // game input types
|
||||
int mpegBoard; // MPEG music board type: 0 = none, 1 = DSB1 (Z80), 2 = DSB2 (68K).
|
||||
bool driveBoard; // drive board (supported if true)
|
||||
uint32_t encryptionKey; // security board encryption device key (or 0 if not applicable)
|
||||
|
||||
// ROM files
|
||||
struct ROMInfo ROM[48];
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Model 3 Game List
|
||||
|
||||
All games supported by Supermodel. All ROMs are loaded according to their
|
||||
native endianness. That is, the PowerPC ROMs are loaded just as a real
|
||||
PowerPC would see them. The emulator may reorder the bytes on its own for
|
||||
performance reasons (but the ROMs are not specified that way here).
|
||||
******************************************************************************/
|
||||
|
||||
extern const struct GameInfo g_Model3GameList[];
|
||||
|
||||
|
||||
#endif // INCLUDED_GAMES_H
|
704
Src/INIFile.cpp
704
Src/INIFile.cpp
|
@ -1,704 +0,0 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
** Supermodel 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 3 of the License, or (at your option)
|
||||
** any later version.
|
||||
**
|
||||
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
/*
|
||||
* INIFile.cpp
|
||||
*
|
||||
* INI file management. Implementation of the CINIFile class.
|
||||
*
|
||||
* To-Do List
|
||||
* ----------
|
||||
* - Add an iterator to retrieve all settings associated with a given section.
|
||||
* This will allow detection of invalid setting names, if the caller desires.
|
||||
* - Add boolean on/off, true/false keywords.
|
||||
* - Note that linePtr does not necessarily correspond to actual lines in the
|
||||
* file (newlines should be counted by the tokenizer for that).
|
||||
*
|
||||
* Grammar
|
||||
* -------
|
||||
*
|
||||
* Section: '[' Identifier ']'
|
||||
* Line: Identifier '=' Argument
|
||||
* Argument: Number
|
||||
* String
|
||||
*
|
||||
* Overview
|
||||
* --------
|
||||
* INI files are linear in nature, divided into sections. Each section has
|
||||
* settings associated with it which may be either numerical values or strings.
|
||||
* Presently, only unsigned 32-bit integers are supported.
|
||||
*
|
||||
* INI files are first opened, then parsed, and finally closed. The parse tree
|
||||
* is cleared only during object construction and when the INI file is closed.
|
||||
* Default settings may be inserted before a file is opened or parsed. When an
|
||||
* 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.
|
||||
*
|
||||
* 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"
|
||||
using namespace std;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Basic Functions
|
||||
******************************************************************************/
|
||||
|
||||
bool CINIFile::Write(const char *comment)
|
||||
{
|
||||
bool writeSuccess;
|
||||
|
||||
// In order to truncate, we must close and reopen as truncated
|
||||
File.close();
|
||||
File.clear(); // required to clear EOF flag (open() does not clear)
|
||||
File.open(FileName.c_str(),fstream::out|fstream::trunc);
|
||||
if (File.fail())
|
||||
{
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Output comment
|
||||
if (comment != NULL)
|
||||
File << comment << endl;
|
||||
|
||||
// Iterate through all sections sequentially
|
||||
for (unsigned i = 0; i < Sections.size(); i++)
|
||||
{
|
||||
if (Sections[i].Settings.size() != 0)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Close and reopen as read/write
|
||||
File.close();
|
||||
File.open(FileName.c_str(),fstream::in|fstream::out);
|
||||
if (File.fail())
|
||||
{
|
||||
//printf("unable to re-open %s for reading/writing\n", FileName.c_str());
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Report any errors that occurred during writing
|
||||
return writeSuccess;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
void CINIFile::Close(void)
|
||||
{
|
||||
File.close();
|
||||
Sections.clear(); // clear the parse tree!
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Management of Settings
|
||||
******************************************************************************/
|
||||
|
||||
// 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) ||
|
||||
((Sections[i].Name == "") && (SectionName == DefaultSectionName)) || // if default section, also accept its alias
|
||||
((Sections[i].Name == DefaultSectionName) && (SectionName == ""))) // ...
|
||||
{
|
||||
*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)
|
||||
{
|
||||
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());
|
||||
struct Section NewSection;
|
||||
|
||||
NewSection.Name = SectionName;
|
||||
Sections.push_back(NewSection);
|
||||
sectionIdx = Sections.size()-1; // the new section will be at the last index
|
||||
}
|
||||
|
||||
// Search through all sections with the requested name for the first occurance of the desired setting
|
||||
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)
|
||||
{
|
||||
// Found it! Update value of this setting
|
||||
sectionIdx = i;
|
||||
settingIdx = j;
|
||||
goto UpdateIntValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = "";
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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());
|
||||
struct Section NewSection;
|
||||
|
||||
NewSection.Name = SectionName;
|
||||
Sections.push_back(NewSection);
|
||||
sectionIdx = Sections.size()-1; // the new section will be at the last index
|
||||
}
|
||||
|
||||
// Search through all sections with the requested name for the first occurance of the desired setting
|
||||
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)
|
||||
{
|
||||
// Found it! Update value of this setting
|
||||
sectionIdx = i;
|
||||
settingIdx = j;
|
||||
goto UpdateString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Obtains a numerical setting, if it exists, otherwise does nothing.
|
||||
bool CINIFile::Get(string SectionName, string SettingName, int& value)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int intVal;
|
||||
if (Get(SectionName, SettingName, intVal) == FAIL || intVal < 0)
|
||||
return FAIL;
|
||||
|
||||
value = (unsigned)intVal;
|
||||
|
||||
return OKAY;
|
||||
}
|
||||
|
||||
bool CINIFile::Get(string SectionName, string SettingName, bool& value)
|
||||
{
|
||||
int intVal;
|
||||
if (Get(SectionName, SettingName, intVal) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
value = intVal > 0;
|
||||
|
||||
return OKAY;
|
||||
}
|
||||
|
||||
// Obtains a string setting, if it exists, otherwise does nothing.
|
||||
bool CINIFile::Get(string SectionName, string SettingName, string& String)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Tokenizer
|
||||
|
||||
Never need to check for newlines because it is assumed these have been
|
||||
stripped by getline().
|
||||
******************************************************************************/
|
||||
|
||||
// Token types
|
||||
#define TOKEN_INVALID -1
|
||||
#define TOKEN_NULL 0
|
||||
#define TOKEN_IDENTIFIER 1
|
||||
#define TOKEN_NUMBER 2
|
||||
#define TOKEN_STRING 3
|
||||
|
||||
|
||||
// Token constructor (initializes token to null)
|
||||
CINIFile::CToken::CToken(void)
|
||||
{
|
||||
type = TOKEN_NULL;
|
||||
}
|
||||
|
||||
// Returns true for white space, comment symbol, or null terminator.
|
||||
static bool IsBlank(char c)
|
||||
{
|
||||
if (isspace(c) || (c==';') || (c=='\0'))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetches a string. Tolerates all characters between quotes, except \n.
|
||||
CINIFile::CToken CINIFile::GetString(void)
|
||||
{
|
||||
CToken T;
|
||||
|
||||
T.type = TOKEN_STRING;
|
||||
|
||||
// Search for next quote
|
||||
++linePtr;
|
||||
while (1)
|
||||
{
|
||||
if (linePtr[0] == '\"')
|
||||
{
|
||||
++linePtr; // so we can find next token
|
||||
break;
|
||||
}
|
||||
else if ((linePtr[0] == '\0') || (linePtr[0] == '\n'))
|
||||
{
|
||||
//printf("tokenizer: warning: string is missing end quote\n");
|
||||
break;
|
||||
}
|
||||
else
|
||||
T.String += linePtr[0];
|
||||
|
||||
++linePtr;
|
||||
}
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
//static bool IsOverflown(unsigned long long number, bool isNeg)
|
||||
//{
|
||||
// return (!isNeg && number > 0x000000007FFFFFFFULL) || (isNeg && number > 0x0000000080000000ULL);
|
||||
//}
|
||||
|
||||
// Fetch number (decimal or hexadecimal positive/negative integer).
|
||||
// linePtr must point to a character and therefore linePtr[1] is guaranteed to be within bounds.
|
||||
CINIFile::CToken CINIFile::GetNumber(void)
|
||||
{
|
||||
CToken T;
|
||||
unsigned long long number = 0;
|
||||
bool isNeg = false;
|
||||
//int overflow = 0;
|
||||
|
||||
T.type = TOKEN_NUMBER;
|
||||
|
||||
// See if begins with minus sign
|
||||
if (linePtr[0]=='-')
|
||||
{
|
||||
isNeg = true;
|
||||
linePtr++;
|
||||
}
|
||||
|
||||
// Hexadecimal?
|
||||
if ((linePtr[0]=='0') && ((linePtr[1]=='X') || (linePtr[1]=='x')))
|
||||
{
|
||||
linePtr += 2; // advance to digits
|
||||
|
||||
// Ensure that at we have at least one digit
|
||||
if (!isxdigit(linePtr[0]))
|
||||
{
|
||||
//printf("tokenizer: invalid hexadecimal number\n");
|
||||
T.type = TOKEN_INVALID;
|
||||
return T;
|
||||
}
|
||||
|
||||
// Read number digit by digit
|
||||
while (1)
|
||||
{
|
||||
if (isxdigit(linePtr[0]))
|
||||
{
|
||||
number <<= 4;
|
||||
if (isdigit(linePtr[0]))
|
||||
number |= (linePtr[0]-'0');
|
||||
else if (isupper(linePtr[0]))
|
||||
number |= (linePtr[0]-'A');
|
||||
else // must be lowercase...
|
||||
number |= (linePtr[0]-'a');
|
||||
++linePtr;
|
||||
|
||||
// Check for overflows
|
||||
//if (IsOverflown(number, isNeg))
|
||||
// overflow = 1;
|
||||
}
|
||||
else if (IsBlank(linePtr[0]))
|
||||
break;
|
||||
else
|
||||
{
|
||||
//printf("tokenizer: invalid hexadecimal number\n");
|
||||
T.type = TOKEN_INVALID;
|
||||
return T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decimal?
|
||||
else
|
||||
{
|
||||
// Read number digit by digit
|
||||
while (1)
|
||||
{
|
||||
if (isdigit(linePtr[0]))
|
||||
{
|
||||
number *= 10;
|
||||
number += (linePtr[0]-'0');
|
||||
++linePtr;
|
||||
|
||||
// Check for overflows
|
||||
//if (IsOverflown(number, isNeg))
|
||||
// overflow = 1;
|
||||
}
|
||||
else if (IsBlank(linePtr[0]))
|
||||
break;
|
||||
else
|
||||
{
|
||||
//printf("tokenizer: invalid number\n");
|
||||
T.type = TOKEN_INVALID;
|
||||
return T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if (overflow)
|
||||
// printf("tokenizer: number exceeds 32 bits and has been truncated\n");
|
||||
|
||||
T.number = (isNeg ? -(int)number : (int)number);
|
||||
return T;
|
||||
}
|
||||
|
||||
// Fetch identifier
|
||||
CINIFile::CToken CINIFile::GetIdentifier(void)
|
||||
{
|
||||
CToken T;
|
||||
|
||||
T.type = TOKEN_IDENTIFIER;
|
||||
while (1)
|
||||
{
|
||||
if (isalpha(linePtr[0]) || isdigit(linePtr[0]) || (linePtr[0]=='_'))
|
||||
{
|
||||
T.String += linePtr[0];
|
||||
++linePtr;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
// Fetch token
|
||||
CINIFile::CToken CINIFile::GetToken(void)
|
||||
{
|
||||
CToken T;
|
||||
|
||||
while (1)
|
||||
{
|
||||
// Gobble up whitespace
|
||||
if (isspace(linePtr[0]))
|
||||
++linePtr;
|
||||
|
||||
// Comment or end of line
|
||||
else if ((linePtr[0]==';') || (linePtr[0]=='\0'))
|
||||
{
|
||||
T.type = TOKEN_NULL;
|
||||
return T; // do not advance linePtr (so we do not de-sync the parser)
|
||||
}
|
||||
|
||||
// Delimiters
|
||||
else if ((linePtr[0]=='[') || (linePtr[0]==']') || (linePtr[0]=='='))
|
||||
{
|
||||
T.type = *linePtr++;
|
||||
return T;
|
||||
}
|
||||
|
||||
// Identifier?
|
||||
else if (isalpha(linePtr[0]) || (linePtr[0] == '_'))
|
||||
{
|
||||
T = GetIdentifier();
|
||||
return T;
|
||||
}
|
||||
|
||||
// Number? (+/-?)
|
||||
else if (linePtr[0]=='-' || isdigit(linePtr[0]))
|
||||
{
|
||||
T = GetNumber();
|
||||
return T;
|
||||
}
|
||||
|
||||
// String?
|
||||
else if (linePtr[0]=='\"')
|
||||
{
|
||||
T = GetString();
|
||||
return T;
|
||||
}
|
||||
|
||||
// Illegal symbol
|
||||
else
|
||||
break; // return null token
|
||||
}
|
||||
|
||||
// If we got here, invalid token
|
||||
T.type = TOKEN_INVALID;
|
||||
return T;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
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;
|
||||
|
||||
lineNum = 0;
|
||||
|
||||
if (!File.is_open())
|
||||
return FAIL;
|
||||
File.clear();
|
||||
if (!File.good())
|
||||
return FAIL;
|
||||
|
||||
while (!File.eof())
|
||||
{
|
||||
++lineNum;
|
||||
File.getline(lineBuf,2048);
|
||||
if (File.fail())
|
||||
return FAIL;
|
||||
linePtr = lineBuf; // beginning of line
|
||||
|
||||
// Top level
|
||||
T = GetToken();
|
||||
U = GetToken();
|
||||
V = GetToken();
|
||||
W = GetToken(); // should always be null
|
||||
switch (T.type)
|
||||
{
|
||||
// [ Identifier ]
|
||||
case '[':
|
||||
if ((U.type==TOKEN_IDENTIFIER) && (V.type==']') && (W.type==TOKEN_NULL))
|
||||
{
|
||||
//printf("Section: %s\n", U.String.c_str());
|
||||
currentSection = U.String;
|
||||
}
|
||||
else
|
||||
{
|
||||
parseStatus = FAIL;
|
||||
//printf("%d: parse error\n", lineNum);
|
||||
}
|
||||
break;
|
||||
|
||||
// Identifier '=' Argument
|
||||
case TOKEN_IDENTIFIER:
|
||||
if (U.type != '=')
|
||||
{
|
||||
parseStatus = FAIL;
|
||||
//printf("%d: expected '=' after identifier\n", lineNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (((V.type==TOKEN_NUMBER) || (V.type==TOKEN_STRING)) && (W.type==TOKEN_NULL))
|
||||
{
|
||||
if (V.type == TOKEN_NUMBER)
|
||||
{
|
||||
//printf("\t%s = %X\n", T.String.c_str(), V.number);
|
||||
Set(currentSection, T.String, V.number);
|
||||
}
|
||||
else if (V.type == TOKEN_STRING)
|
||||
{
|
||||
//printf("\t%s = %s\n", T.String.c_str(), V.String.c_str());
|
||||
Set(currentSection, T.String, V.String);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parseStatus = FAIL;
|
||||
//printf("%d: expected a number or string after '='\n", lineNum);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Blank line
|
||||
case TOKEN_NULL:
|
||||
break;
|
||||
|
||||
// Illegal
|
||||
default:
|
||||
parseStatus = FAIL;
|
||||
//printf("%d: parse error\n", lineNum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("end of file reached\n");
|
||||
return parseStatus;
|
||||
}
|
245
Src/INIFile.h
245
Src/INIFile.h
|
@ -1,245 +0,0 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
** Supermodel 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 3 of the License, or (at your option)
|
||||
** any later version.
|
||||
**
|
||||
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
/*
|
||||
* INIFile.h
|
||||
*
|
||||
* Header file for INI file management.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_INIFILE_H
|
||||
#define INCLUDED_INIFILE_H
|
||||
|
||||
// Standard C++ and STL headers
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
/*
|
||||
* CINIFile:
|
||||
*
|
||||
* INI file parser.
|
||||
*/
|
||||
class CINIFile
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Get(SectionName, SettingName, value):
|
||||
* Get(SectionName, SettingName, String):
|
||||
*
|
||||
* Obtains the value of a setting associated with a particular section. It
|
||||
* is assumed that the caller knows whether the setting should be a string
|
||||
* or an integer value. If the setting was specified as a string in the
|
||||
* INI file, the value will be set to 0. Otherwise, the string will be set
|
||||
* to "".
|
||||
*
|
||||
* Parameters:
|
||||
* SectionName String defining the section name.
|
||||
* SettingName String defining the setting name.
|
||||
* value Reference to where the setting value will be
|
||||
* copied.
|
||||
* String Reference to where the string will be copied.
|
||||
*
|
||||
* Returns:
|
||||
* OKAY if the setting was found, FAIL otherwise. The type is not
|
||||
* checked. If the setting is not found, the output parameter will not
|
||||
* be modified.
|
||||
*/
|
||||
bool Get(string SectionName, string SettingName, int& value);
|
||||
bool Get(string SectionName, string SettingName, unsigned& value);
|
||||
bool Get(string SectionName, string SettingName, bool& value);
|
||||
bool Get(string SectionName, string SettingName, string& String);
|
||||
|
||||
/*
|
||||
* Set(SectionName, SettingName, value):
|
||||
* Set(SectionName, SettingName, String):
|
||||
*
|
||||
* Sets a setting associated with a particular section. For each overload
|
||||
* type (string or integer), the opposite type is cleard (0 is written to
|
||||
* values when a string is set, "" is written to strings when a value is
|
||||
* set). If the setting does not exist, it is created. If the section does
|
||||
* not exist, it will be added as well.
|
||||
*
|
||||
* Parameters:
|
||||
* SectionName String defining the section name.
|
||||
* SettingName String defining the setting name.
|
||||
* value Value to write. String will be set to "".
|
||||
* String String to write. Value will be set to 0.
|
||||
*/
|
||||
void Set(string SectionName, string SettingName, int value);
|
||||
void Set(string SectionName, string SettingName, string String);
|
||||
|
||||
/*
|
||||
* Write(comment):
|
||||
*
|
||||
* 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. 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
|
||||
* the beginning of the file, otherwise NULL. The comment
|
||||
* lines must include semicolons at the beginning, they
|
||||
* will not be inserted automatically. The string is
|
||||
* output directly, as-is.
|
||||
*
|
||||
* Returns:
|
||||
* OKAY if successful. FAIL if an error occurred at any point. In
|
||||
* order to truncate the original contents of the file, this function
|
||||
* closes the file, reopends it as write-only, then reopens it again
|
||||
* as a read/write file. If an error occurs during the reopening
|
||||
* procedure, it is possible that nothing will be output and the
|
||||
* previous contents will be lost.
|
||||
*/
|
||||
bool Write(const char *comment);
|
||||
|
||||
/*
|
||||
* Parse(void):
|
||||
*
|
||||
* Parses the contents of the file, building the parse tree. Settings
|
||||
* 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. 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).
|
||||
*
|
||||
* Returns:
|
||||
* OKAY if successful, FAIL if unable to open for reading and writing.
|
||||
*/
|
||||
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):
|
||||
*
|
||||
* Closes the INI file and clears the parse tree.
|
||||
*/
|
||||
void Close(void);
|
||||
|
||||
private:
|
||||
// Token
|
||||
class CToken
|
||||
{
|
||||
public:
|
||||
int type; // token type (defined privately in INIFile.cpp)
|
||||
int number; // numbers and bools
|
||||
string String; // strings and identifiers
|
||||
|
||||
// Constructor (initialize to null token)
|
||||
CToken(void);
|
||||
};
|
||||
|
||||
// Parse tree
|
||||
bool LookUpSection(unsigned *idx, string SectionName);
|
||||
void InitParseTree(void);
|
||||
|
||||
// Tokenizer
|
||||
CToken GetString(void);
|
||||
CToken GetNumber(void);
|
||||
CToken GetIdentifier(void);
|
||||
CToken GetToken(void);
|
||||
|
||||
// File state
|
||||
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)
|
||||
unsigned lineNum; // line number
|
||||
|
||||
// Parse tree: a list of sections each of which is a list of settings for that section
|
||||
struct Setting // it is up to caller to determine whether to use value or string
|
||||
{
|
||||
string Name; // setting name
|
||||
bool isNumber; // internal flag: true if the setting is a number, false if it is a string
|
||||
int value; // value of number
|
||||
string String; // string
|
||||
|
||||
Setting(void)
|
||||
{
|
||||
value = 0; // initialize value to 0
|
||||
isNumber = true; // indicate the setting is initially a number
|
||||
}
|
||||
};
|
||||
struct Section
|
||||
{
|
||||
string Name; // section name
|
||||
vector<struct Setting> Settings; // list of settings associated w/ this section
|
||||
};
|
||||
vector<struct Section> Sections; // a list of sections
|
||||
};
|
||||
|
||||
|
||||
#endif // INCLUDED_INIFILE_H
|
|
@ -179,36 +179,6 @@ void CInput::StoreToConfig(Util::Config::Node *config)
|
|||
config->Set(key, m_mapping);
|
||||
}
|
||||
|
||||
void CInput::ReadFromINIFile(CINIFile *ini, const char *section)
|
||||
{
|
||||
// See if input is configurable
|
||||
if (IsConfigurable())
|
||||
{
|
||||
// If so, check INI file for mapping string
|
||||
string key("Input");
|
||||
key.append(id);
|
||||
string mapping;
|
||||
if (ini->Get(section, key, mapping) == OKAY)
|
||||
{
|
||||
// If found, then set mapping string
|
||||
SetMapping(mapping.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If input has not been configured, then force recreation of source anyway since input system settings may have changed
|
||||
CreateSource();
|
||||
}
|
||||
|
||||
void CInput::WriteToINIFile(CINIFile *ini, const char *section)
|
||||
{
|
||||
if (!IsConfigurable())
|
||||
return;
|
||||
string key("Input");
|
||||
key.append(id);
|
||||
ini->Set(section, key, m_mapping);
|
||||
}
|
||||
|
||||
void CInput::InputSystemChanged()
|
||||
{
|
||||
// If input system or its settings have changed, then force recreation of source
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
|
||||
class CInputSource;
|
||||
class CInputSystem;
|
||||
class CINIFile;
|
||||
|
||||
// Flags for inputs
|
||||
#define INPUT_FLAGS_SWITCH 0x0001
|
||||
|
@ -154,13 +153,11 @@ public:
|
|||
* Loads the input's mapping(s) from the given config object, as well as any other settings.
|
||||
*/
|
||||
virtual void LoadFromConfig(const Util::Config::Node &config);
|
||||
virtual void ReadFromINIFile(CINIFile *ini, const char *section);
|
||||
|
||||
/*
|
||||
* Stores the current input mapping(s) to the given config object, as well as any other settings.
|
||||
*/
|
||||
virtual void StoreToConfig(Util::Config::Node *config);
|
||||
virtual void WriteToINIFile(CINIFile *ini, const char *section);
|
||||
|
||||
void InputSystemChanged();
|
||||
|
||||
|
|
|
@ -1115,39 +1115,6 @@ void CInputSystem::StoreKeySettings(Util::Config::Node *config, KeySettings *set
|
|||
config->Set(baseKey + "DecaySpeed", settings->decaySpeed);
|
||||
}
|
||||
|
||||
KeySettings *CInputSystem::ReadKeySettings(CINIFile *ini, const char *section, int kbdNum)
|
||||
{
|
||||
// Get common key settings and create new key settings based on that
|
||||
KeySettings *common = (kbdNum != ANY_KEYBOARD ? GetKeySettings(ANY_KEYBOARD, true) : &m_defKeySettings);
|
||||
KeySettings *settings = new KeySettings(*common);
|
||||
settings->kbdNum = kbdNum;
|
||||
|
||||
// Read settings from ini file
|
||||
string baseKey("InputKey");
|
||||
if (kbdNum != ANY_KEYBOARD)
|
||||
baseKey.append(IntToString(kbdNum + 1));
|
||||
bool read = false;
|
||||
read |= ini->Get(section, baseKey + "Sensitivity", settings->sensitivity) == OKAY;
|
||||
read |= ini->Get(section, baseKey + "DecaySpeed", settings->decaySpeed) == OKAY;
|
||||
if (read)
|
||||
return settings;
|
||||
delete settings;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CInputSystem::WriteKeySettings(CINIFile *ini, const char *section, KeySettings *settings)
|
||||
{
|
||||
// Get common key settings
|
||||
KeySettings *common = (settings->kbdNum != ANY_KEYBOARD ? GetKeySettings(ANY_KEYBOARD, true) : &m_defKeySettings);
|
||||
|
||||
// Write to ini file any settings that are different to common settings
|
||||
string baseKey("InputKey");
|
||||
if (settings->kbdNum != ANY_KEYBOARD)
|
||||
baseKey.append(IntToString(settings->kbdNum + 1));
|
||||
if (settings->sensitivity != common->sensitivity) ini->Set(section, baseKey + "Sensitivity", settings->sensitivity);
|
||||
if (settings->decaySpeed != common->decaySpeed) ini->Set(section, baseKey + "DecaySpeed", settings->decaySpeed);
|
||||
}
|
||||
|
||||
void CInputSystem::PrintMouseSettings(int mseNum, MouseSettings *settings)
|
||||
{
|
||||
for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++)
|
||||
|
@ -1198,42 +1165,6 @@ void CInputSystem::StoreMouseSettings(Util::Config::Node *config, MouseSettings
|
|||
}
|
||||
}
|
||||
|
||||
MouseSettings *CInputSystem::ReadMouseSettings(CINIFile *ini, const char *section, int mseNum)
|
||||
{
|
||||
// Get common mouse settings and create new mouse settings based on that
|
||||
MouseSettings *common = (mseNum != ANY_MOUSE ? GetMouseSettings(ANY_MOUSE, true) : &m_defMseSettings);
|
||||
MouseSettings *settings = new MouseSettings(*common);
|
||||
settings->mseNum = mseNum;
|
||||
|
||||
// Read settings from ini file
|
||||
string baseKey("InputMouse");
|
||||
if (mseNum != ANY_MOUSE)
|
||||
baseKey.append(IntToString(mseNum + 1));
|
||||
bool read = false;
|
||||
for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++)
|
||||
read |= ini->Get(section, baseKey + s_axisIds[axisNum] + "DeadZone", settings->deadZones[axisNum]) == OKAY;
|
||||
if (read)
|
||||
return settings;
|
||||
delete settings;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CInputSystem::WriteMouseSettings(CINIFile *ini, const char *section, MouseSettings *settings)
|
||||
{
|
||||
// Get common mouse settings
|
||||
MouseSettings *common = (settings->mseNum != ANY_MOUSE ? GetMouseSettings(ANY_MOUSE, true) : &m_defMseSettings);
|
||||
|
||||
// Write to ini file any settings that are different to common/default settings
|
||||
string baseKey("InputMouse");
|
||||
if (settings->mseNum != ANY_MOUSE)
|
||||
baseKey.append(IntToString(settings->mseNum + 1));
|
||||
for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++)
|
||||
{
|
||||
if (settings->deadZones[axisNum] != common->deadZones[axisNum])
|
||||
ini->Set(section, baseKey + s_axisIds[axisNum] + "DeadZone", settings->deadZones[axisNum]);
|
||||
}
|
||||
}
|
||||
|
||||
void CInputSystem::PrintJoySettings(int joyNum, JoySettings *settings)
|
||||
{
|
||||
const JoyDetails *joyDetails = (joyNum != ANY_JOYSTICK ? GetJoyDetails(joyNum) : NULL);
|
||||
|
@ -1327,58 +1258,6 @@ void CInputSystem::StoreJoySettings(Util::Config::Node *config, JoySettings *set
|
|||
}
|
||||
}
|
||||
|
||||
JoySettings *CInputSystem::ReadJoySettings(CINIFile *ini, const char *section, int joyNum)
|
||||
{
|
||||
// Get common/default joystick settings and create new joystick settings based on that
|
||||
JoySettings *common = (joyNum != ANY_JOYSTICK ? GetJoySettings(ANY_JOYSTICK, true) : &m_defJoySettings);
|
||||
JoySettings *settings = new JoySettings(*common);
|
||||
settings->joyNum = joyNum;
|
||||
|
||||
// Read settings from ini file
|
||||
string baseKey("InputJoy");
|
||||
if (joyNum != ANY_JOYSTICK)
|
||||
baseKey.append(IntToString(joyNum + 1));
|
||||
bool read = false;
|
||||
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
|
||||
{
|
||||
const char *axisId = s_axisIds[axisNum];
|
||||
read |= ini->Get(section, baseKey + axisId + "MinVal", settings->axisMinVals[axisNum]) == OKAY;
|
||||
read |= ini->Get(section, baseKey + axisId + "OffVal", settings->axisOffVals[axisNum]) == OKAY;
|
||||
read |= ini->Get(section, baseKey + axisId + "MaxVal", settings->axisMaxVals[axisNum]) == OKAY;
|
||||
read |= ini->Get(section, baseKey + axisId + "DeadZone", settings->deadZones[axisNum]) == OKAY;
|
||||
read |= ini->Get(section, baseKey + axisId + "Saturation", settings->saturations[axisNum]) == OKAY;
|
||||
}
|
||||
if (read)
|
||||
return settings;
|
||||
delete settings;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CInputSystem::WriteJoySettings(CINIFile *ini, const char *section, JoySettings *settings)
|
||||
{
|
||||
// Get common/default joystick settings
|
||||
JoySettings *common = (settings->joyNum != ANY_JOYSTICK ? GetJoySettings(ANY_JOYSTICK, true) : &m_defJoySettings);
|
||||
|
||||
// Write to ini file any settings that are different to common/default settings
|
||||
string baseKey("InputJoy");
|
||||
if (settings->joyNum != ANY_JOYSTICK)
|
||||
baseKey.append(IntToString(settings->joyNum + 1));
|
||||
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
|
||||
{
|
||||
const char *axisId = s_axisIds[axisNum];
|
||||
if (settings->axisMinVals[axisNum] != common->axisMinVals[axisNum])
|
||||
ini->Set(section, baseKey + axisId + "MinVal", settings->axisMinVals[axisNum]);
|
||||
if (settings->axisOffVals[axisNum] != common->axisOffVals[axisNum])
|
||||
ini->Set(section, baseKey + axisId + "OffVal", settings->axisOffVals[axisNum]);
|
||||
if (settings->axisMaxVals[axisNum] != common->axisMaxVals[axisNum])
|
||||
ini->Set(section, baseKey + axisId + "MaxVal", settings->axisMaxVals[axisNum]);
|
||||
if (settings->deadZones[axisNum] != common->deadZones[axisNum])
|
||||
ini->Set(section, baseKey + axisId + "DeadZone", settings->deadZones[axisNum]);
|
||||
if (settings->saturations[axisNum] != common->saturations[axisNum])
|
||||
ini->Set(section, baseKey + axisId + "Saturation", settings->saturations[axisNum]);
|
||||
}
|
||||
}
|
||||
|
||||
KeySettings *CInputSystem::GetKeySettings(int kbdNum, bool useDefault)
|
||||
{
|
||||
KeySettings *common = NULL;
|
||||
|
@ -1844,60 +1723,6 @@ void CInputSystem::StoreToConfig(Util::Config::Node *config)
|
|||
StoreJoySettings(config, *it);
|
||||
}
|
||||
|
||||
void CInputSystem::ReadFromINIFile(CINIFile *ini, const char *section)
|
||||
{
|
||||
ClearSettings();
|
||||
ClearSourceCache();
|
||||
|
||||
// Read all key settings for attached keyboards
|
||||
KeySettings *keySettings = ReadKeySettings(ini, section, ANY_KEYBOARD);
|
||||
if (keySettings != NULL)
|
||||
m_keySettings.push_back(keySettings);
|
||||
for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++)
|
||||
{
|
||||
keySettings = ReadKeySettings(ini, section, kbdNum);
|
||||
if (keySettings != NULL)
|
||||
m_keySettings.push_back(keySettings);
|
||||
}
|
||||
|
||||
// Read all mouse settings for attached mice
|
||||
MouseSettings *mseSettings = ReadMouseSettings(ini, section, ANY_MOUSE);
|
||||
if (mseSettings != NULL)
|
||||
m_mseSettings.push_back(mseSettings);
|
||||
for (int mseNum = 0; mseNum < m_numMice; mseNum++)
|
||||
{
|
||||
mseSettings = ReadMouseSettings(ini, section, mseNum);
|
||||
if (mseSettings != NULL)
|
||||
m_mseSettings.push_back(mseSettings);
|
||||
}
|
||||
|
||||
// Read all joystick settings for attached joysticks
|
||||
JoySettings *joySettings = ReadJoySettings(ini, section, ANY_JOYSTICK);
|
||||
if (joySettings != NULL)
|
||||
m_joySettings.push_back(joySettings);
|
||||
for (int joyNum = 0; joyNum < m_numJoys; joyNum++)
|
||||
{
|
||||
joySettings = ReadJoySettings(ini, section, joyNum);
|
||||
if (joySettings != NULL)
|
||||
m_joySettings.push_back(joySettings);
|
||||
}
|
||||
}
|
||||
|
||||
void CInputSystem::WriteToINIFile(CINIFile *ini, const char *section)
|
||||
{
|
||||
// Write all key settings
|
||||
for (vector<KeySettings*>::iterator it = m_keySettings.begin(); it != m_keySettings.end(); it++)
|
||||
WriteKeySettings(ini, section, *it);
|
||||
|
||||
// Write all mouse settings
|
||||
for (vector<MouseSettings*>::iterator it = m_mseSettings.begin(); it != m_mseSettings.end(); it++)
|
||||
WriteMouseSettings(ini, section, *it);
|
||||
|
||||
// Write all joystick settings
|
||||
for (vector<JoySettings*>::iterator it = m_joySettings.begin(); it != m_joySettings.end(); it++)
|
||||
WriteJoySettings(ini, section, *it);
|
||||
}
|
||||
|
||||
bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly, unsigned readFlags, const char *escapeMapping)
|
||||
{
|
||||
// Map given escape mapping to an input source
|
||||
|
|
|
@ -39,7 +39,6 @@ using namespace std;
|
|||
|
||||
class CInput;
|
||||
class CInputSource;
|
||||
class CINIFile;
|
||||
|
||||
#define MAX_NAME_LENGTH 255
|
||||
|
||||
|
@ -480,13 +479,11 @@ private:
|
|||
* Returns NULL if no relevant settings were found in the INI file.
|
||||
*/
|
||||
KeySettings *LoadKeySettings(const Util::Config::Node &config, int kbdNum);
|
||||
KeySettings *ReadKeySettings(CINIFile *ini, const char *section, int kbdNum);
|
||||
|
||||
|
||||
/*
|
||||
* Writes the given key settings to an INI file, only writing out settings that are different to their defaults.
|
||||
*/
|
||||
void StoreKeySettings(Util::Config::Node *config, KeySettings *settings);
|
||||
void WriteKeySettings(CINIFile *ini, const char *section, KeySettings *settings);
|
||||
|
||||
/*
|
||||
* Prints the given mouse settings to stdout.
|
||||
|
@ -498,13 +495,11 @@ private:
|
|||
* Returns NULL if no relevant settings were found in the config object.
|
||||
*/
|
||||
MouseSettings *LoadMouseSettings(const Util::Config::Node &config, int mseNum);
|
||||
MouseSettings *ReadMouseSettings(CINIFile *ini, const char *section, int mseNum);
|
||||
|
||||
/*
|
||||
* Stores the given mouse settings to a config object, only storing settings that differ from their defaults.
|
||||
*/
|
||||
void StoreMouseSettings(Util::Config::Node *config, MouseSettings *settings);
|
||||
void WriteMouseSettings(CINIFile *ini, const char *section, MouseSettings *settings);
|
||||
|
||||
/*
|
||||
* Prints the given joystick settings to stdout.
|
||||
|
@ -516,13 +511,11 @@ private:
|
|||
* Returns NULL if no relevant settings were found in the config object.
|
||||
*/
|
||||
JoySettings *LoadJoySettings(const Util::Config::Node &config, int joyNum);
|
||||
JoySettings *ReadJoySettings(CINIFile *ini, const char *section, int joyNum);
|
||||
|
||||
/*
|
||||
* Stores the given joystick settings to a config object, only storing settings that differ from their defaults.
|
||||
*/
|
||||
void StoreJoySettings(Util::Config::Node *config, JoySettings *settings);
|
||||
void WriteJoySettings(CINIFile *ini, const char *section, JoySettings *settings);
|
||||
|
||||
protected:
|
||||
// Current display geometry
|
||||
|
@ -806,13 +799,11 @@ public:
|
|||
* Reads all keyboard, mouse and joystick settings (and any additional system-specific additional settings) from the given config object.
|
||||
*/
|
||||
virtual void LoadFromConfig(const Util::Config::Node &config);
|
||||
virtual void ReadFromINIFile(CINIFile *ini, const char *section);
|
||||
|
||||
/*
|
||||
* Stores all keyboard, mouse and joystick settings (and any additional system-specific settings) to the given config object.
|
||||
*/
|
||||
virtual void StoreToConfig(Util::Config::Node *config);
|
||||
virtual void WriteToINIFile(CINIFile *ini, const char *section);
|
||||
|
||||
/*
|
||||
* Returns the current key settings for given keyboard number, or common settings if ANY_KEYBOARD specified.
|
||||
|
|
|
@ -182,24 +182,21 @@ CTriggerInput::CTriggerInput(const char *inputId, const char *inputLabel, unsign
|
|||
//
|
||||
}
|
||||
|
||||
void CTriggerInput::ReadFromINIFile(CINIFile *ini, const char *section)
|
||||
void CTriggerInput::LoadFromConfig(const Util::Config::Node &config)
|
||||
{
|
||||
CInput::ReadFromINIFile(ini, section);
|
||||
|
||||
string key("Input");
|
||||
key.append(id);
|
||||
unsigned autoTrigger;
|
||||
if (ini->Get(section, key, autoTrigger) == OKAY)
|
||||
m_autoTrigger = !!autoTrigger;
|
||||
string key("Input");
|
||||
key.append(id);
|
||||
unsigned autoTrigger;
|
||||
auto *node = config.TryGet(key);
|
||||
if (node)
|
||||
m_autoTrigger = !!node->ValueAs<unsigned>();
|
||||
}
|
||||
|
||||
void CTriggerInput::WriteToINIFile(CINIFile *ini, const char *section)
|
||||
void CTriggerInput::StoreToConfig(Util::Config::Node *config)
|
||||
{
|
||||
CInput::WriteToINIFile(ini, section);
|
||||
|
||||
string key("Input");
|
||||
key.append(id);
|
||||
ini->Set(section, key, (unsigned)m_autoTrigger);
|
||||
string key("Input");
|
||||
key.append(id);
|
||||
config->Set(key, (unsigned) m_autoTrigger);
|
||||
}
|
||||
|
||||
void CTriggerInput::Poll()
|
||||
|
|
|
@ -159,9 +159,9 @@ public:
|
|||
CTriggerInput(const char *inputId, const char *inputLabel, unsigned inputGameFlags, CSwitchInput *triggerInput, CSwitchInput *offscreenInput,
|
||||
UINT16 offVal = 0x00, UINT16 onVal = 0x01);
|
||||
|
||||
void ReadFromINIFile(CINIFile *ini, const char *section);
|
||||
void LoadFromConfig(const Util::Config::Node &config);
|
||||
|
||||
void WriteToINIFile(CINIFile *ini, const char *section);
|
||||
void StoreToConfig(Util::Config::Node *config);
|
||||
|
||||
/*
|
||||
* Polls (updates) the input, updating its trigger value and offscreen value from the switch inputs
|
||||
|
|
|
@ -421,22 +421,6 @@ void CInputs::StoreToConfig(Util::Config::Node *config)
|
|||
(*it)->StoreToConfig(config);
|
||||
}
|
||||
|
||||
void CInputs::ReadFromINIFile(CINIFile *ini, const char *section)
|
||||
{
|
||||
m_system->ReadFromINIFile(ini, section);
|
||||
|
||||
for (vector<CInput*>::iterator it = m_inputs.begin(); it != m_inputs.end(); it++)
|
||||
(*it)->ReadFromINIFile(ini, section);
|
||||
}
|
||||
|
||||
void CInputs::WriteToINIFile(CINIFile *ini, const char *section)
|
||||
{
|
||||
m_system->WriteToINIFile(ini, section);
|
||||
|
||||
for (vector<CInput*>::iterator it = m_inputs.begin(); it != m_inputs.end(); it++)
|
||||
(*it)->WriteToINIFile(ini, section);
|
||||
}
|
||||
|
||||
bool CInputs::ConfigureInputs(const Game *game)
|
||||
{
|
||||
m_system->UngrabMouse();
|
||||
|
|
|
@ -41,7 +41,6 @@ class CAxisInput;
|
|||
class CSwitchInput;
|
||||
class CGearShift4Input;
|
||||
class CTriggerInput;
|
||||
class CINIFile;
|
||||
struct Game;
|
||||
|
||||
/*
|
||||
|
@ -270,13 +269,11 @@ public:
|
|||
* Loads the input mapping assignments from the given config object.
|
||||
*/
|
||||
void LoadFromConfig(const Util::Config::Node &config);
|
||||
void ReadFromINIFile(CINIFile *ini, const char *section);
|
||||
|
||||
|
||||
/*
|
||||
* Stores the current input mapping assignments to the given config object.
|
||||
*/
|
||||
void StoreToConfig(Util::Config::Node *config);
|
||||
void WriteToINIFile(CINIFile *ini, const char *section);
|
||||
|
||||
/*
|
||||
* Configures the current input mapping assignments for the given game, or all inputs if game is NULL, by asking the user for input.
|
||||
|
|
509
Src/ROMLoad.cpp
509
Src/ROMLoad.cpp
|
@ -1,509 +0,0 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
** Supermodel 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 3 of the License, or (at your option)
|
||||
** any later version.
|
||||
**
|
||||
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
/*
|
||||
* ROMLoad.cpp
|
||||
*
|
||||
* ROM loading functions.
|
||||
*/
|
||||
|
||||
#include <new>
|
||||
#include <cstring>
|
||||
#include "Supermodel.h"
|
||||
#include "Pkgs/unzip.h"
|
||||
|
||||
static bool IsPowerOfTwo(long x)
|
||||
{
|
||||
while (((x & 1) == 0) && x > 1) // while x is even and > 1
|
||||
x >>= 1;
|
||||
return (x == 1);
|
||||
}
|
||||
|
||||
static struct GameInfo cromInfo =
|
||||
{
|
||||
"crom.bin",
|
||||
NULL,
|
||||
"Custom CROM Image",
|
||||
"Bart Trzynadlowski",
|
||||
2015,
|
||||
0x10,
|
||||
0, // size of CROM image (filled in at run time)
|
||||
false, // no need to mirror anything
|
||||
0, // no VROM
|
||||
0, // no sample ROMs
|
||||
GAME_INPUT_COMMON|GAME_INPUT_JOYSTICK1,
|
||||
0, // no MPEG board
|
||||
false, // no drive board
|
||||
0, // no security board
|
||||
{
|
||||
{ NULL, false, NULL, 0, 0, 0, 0, 0, false }
|
||||
}
|
||||
};
|
||||
|
||||
static GameInfo * LoadCROMDirect(const struct ROMMap *Map, const char *file)
|
||||
{
|
||||
FILE *fp = fopen(file, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
ErrorLog("Could not open '%s'.", file);
|
||||
return NULL;
|
||||
}
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long fileSize = ftell(fp);
|
||||
rewind(fp);
|
||||
if (fileSize > 0x800000)
|
||||
{
|
||||
ErrorLog("CROM image exceeds 8MB.");
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
if (!IsPowerOfTwo(fileSize))
|
||||
{
|
||||
ErrorLog("CROM image size is not a power of 2.");
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
while (Map->region && strcmp(Map->region, "CROM"))
|
||||
++Map;
|
||||
if (!Map->region)
|
||||
{
|
||||
ErrorLog("Internal error: No CROM region in ROM map!");
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
// Read file into upper part of CROM region
|
||||
fread(Map->ptr + 0x800000 - fileSize, sizeof(UINT8), fileSize, fp);
|
||||
fclose(fp);
|
||||
// Return fake game info structure
|
||||
cromInfo.cromSize = fileSize;
|
||||
return &cromInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* CopyRegion(dest, destOffset, destSize, src, srcSize):
|
||||
*
|
||||
* Repeatedly mirror (copy) to destination from source until destination is
|
||||
* filled.
|
||||
*
|
||||
* Parameters:
|
||||
* dest Destination region.
|
||||
* destOffset Offset within destination to begin mirroring to.
|
||||
* destSize Size in bytes of destination region.
|
||||
* src Source region to copy from.
|
||||
* srcSize Size of region to copy from.
|
||||
*/
|
||||
void CopyRegion(UINT8 *dest, unsigned destOffset, unsigned destSize, UINT8 *src, unsigned srcSize)
|
||||
{
|
||||
if (!destSize || !srcSize)
|
||||
return;
|
||||
while (destOffset < destSize)
|
||||
{
|
||||
// If we'll overrun the destination, trim the copy length
|
||||
if ((destOffset+srcSize) >= destSize)
|
||||
srcSize = destSize-destOffset;
|
||||
|
||||
// Copy!
|
||||
memcpy(&dest[destOffset], src, srcSize);
|
||||
|
||||
destOffset += srcSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Search for a ROM within a single game based on its CRC
|
||||
static bool FindROMByCRCInGame(const struct GameInfo **gamePtr, int *romIdxPtr, const struct GameInfo *Game, UINT32 crc)
|
||||
{
|
||||
for (int j = 0; Game->ROM[j].region != NULL; j++)
|
||||
{
|
||||
if (crc == Game->ROM[j].crc) // found it!
|
||||
{
|
||||
*gamePtr = Game; // I know this seems redundant, but we do it here because FindROMByCRC() uses this function
|
||||
*romIdxPtr = j;
|
||||
return OKAY;
|
||||
}
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Search for a ROM in the complete game list based on CRC32 and return its GameInfo and ROMInfo entries
|
||||
static bool FindROMByCRC(const struct GameInfo **gamePtr, int *romIdxPtr, const struct GameInfo *GameList, const struct GameInfo *TryGame, UINT32 crc)
|
||||
{
|
||||
if (TryGame != NULL)
|
||||
{
|
||||
if (FindROMByCRCInGame(gamePtr, romIdxPtr, TryGame, crc) == OKAY)
|
||||
return OKAY;
|
||||
}
|
||||
|
||||
for (int i = 0; GameList[i].title != NULL; i++)
|
||||
{
|
||||
if (FindROMByCRCInGame(gamePtr, romIdxPtr, &(GameList[i]), crc) == OKAY)
|
||||
return OKAY;
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Returns true if this ROM appears only a single time in the entire game list (ie., it is not shared between games)
|
||||
static bool ROMIsUnique(const struct GameInfo *GameList, UINT32 crc)
|
||||
{
|
||||
int timesFound = 0;
|
||||
|
||||
for (int i = 0; GameList[i].title != NULL; i++)
|
||||
{
|
||||
for (int j = 0; GameList[i].ROM[j].region != NULL; j++)
|
||||
{
|
||||
if (crc == GameList[i].ROM[j].crc)
|
||||
timesFound++;
|
||||
}
|
||||
}
|
||||
|
||||
return (timesFound == 1) ? true : false;
|
||||
}
|
||||
|
||||
static void ByteSwap(UINT8 *buf, unsigned size)
|
||||
{
|
||||
for (size_t i = 0; i < size; i += 2)
|
||||
{
|
||||
UINT8 x = buf[i+0];
|
||||
buf[i+0] = buf[i+1];
|
||||
buf[i+1] = x;
|
||||
}
|
||||
}
|
||||
|
||||
// Load a single ROM file
|
||||
static bool LoadROM(UINT8 *buf, unsigned bufSize, const struct ROMMap *Map, const struct ROMInfo *ROM, unzFile zf, const char *zipFile, bool loadAll)
|
||||
{
|
||||
char file[2048+1];
|
||||
unz_file_info fileInfo;
|
||||
|
||||
// Read the file into the buffer
|
||||
int err = unzGetCurrentFileInfo(zf, &fileInfo, file, 2048, NULL, 0, NULL, 0);
|
||||
if (err != UNZ_OK)
|
||||
return ErrorLog("Unable to extract a file name from '%s'.", zipFile);
|
||||
if (fileInfo.uncompressed_size != ROM->fileSize)
|
||||
return ErrorLog("'%s' in '%s' is not the correct size (must be %d bytes).", file, zipFile, ROM->fileSize);
|
||||
err = unzOpenCurrentFile(zf);
|
||||
if (UNZ_OK != err)
|
||||
return ErrorLog("Unable to read '%s' from '%s'.", file, zipFile);
|
||||
size_t bytes = unzReadCurrentFile(zf, buf, bufSize);
|
||||
if (bytes != ROM->fileSize)
|
||||
{
|
||||
unzCloseCurrentFile(zf);
|
||||
return ErrorLog("Unable to read '%s' from '%s'.", file, zipFile);
|
||||
}
|
||||
err = unzCloseCurrentFile(zf);
|
||||
if (UNZ_CRCERROR == err)
|
||||
ErrorLog("CRC error reading '%s' from '%s'. File may be corrupt.", file, zipFile);
|
||||
|
||||
// Byte swap
|
||||
if (ROM->byteSwap)
|
||||
ByteSwap(buf, ROM->fileSize);
|
||||
|
||||
// Find out how to map the ROM and do it
|
||||
for (size_t i = 0; Map[i].region != NULL; i++)
|
||||
{
|
||||
if (!strcmp(Map[i].region, ROM->region))
|
||||
{
|
||||
size_t destIdx = ROM->offset;
|
||||
for (size_t srcIdx = 0; srcIdx < ROM->fileSize; )
|
||||
{
|
||||
for (size_t j = 0; j < ROM->groupSize; j++)
|
||||
Map[i].ptr[destIdx+j] = buf[srcIdx++];
|
||||
destIdx += ROM->stride;
|
||||
}
|
||||
return OKAY;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadAll) // need to load all ROMs, so there should be no unmapped regions
|
||||
return ErrorLog("%s:%d: No mapping for '%s'.", __FILE__, __LINE__, ROM->region);
|
||||
else
|
||||
return OKAY;
|
||||
}
|
||||
|
||||
/*
|
||||
* LoadROMSetFromZIPFile(Map, GameList, zipFile):
|
||||
*
|
||||
* Loads a complete ROM set from a ZIP archive. Automatically detects the game.
|
||||
* If multiple games exist within the archive, an error will be printed and all
|
||||
* but the first detected game will be ignored.
|
||||
*
|
||||
* Parameters:
|
||||
* Map A list of pointers to the memory buffers for each ROM
|
||||
* region.
|
||||
* GameList List of all supported games and their ROMs.
|
||||
* zipFile ZIP file to load from.
|
||||
* loadAll If true, will check to ensure all ROMs were loaded.
|
||||
* Otherwise, omits this check and loads only specified
|
||||
* regions.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to GameInfo struct for loaded game if successful, NULL
|
||||
* otherwise. Prints errors.
|
||||
*/
|
||||
const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const struct GameInfo *GameList, const char *zipFile, bool loadAll)
|
||||
{
|
||||
if (!strcmp(zipFile, "crom.bin"))
|
||||
return LoadCROMDirect(Map, zipFile);
|
||||
|
||||
const struct GameInfo *Game = NULL;
|
||||
const struct GameInfo *CurGame; // this is the game to which the last ROM found is thought to belong
|
||||
unz_file_info fileInfo;
|
||||
string zipFileParent, zfpErr = "";
|
||||
int romIdx = 0; // index within Game->ROM
|
||||
unsigned romsFound[sizeof(Game->ROM)/sizeof(struct ROMInfo)];
|
||||
bool multipleGameError = false;
|
||||
|
||||
// Try to open file
|
||||
unzFile zf = unzOpen(zipFile);
|
||||
if (NULL == zf)
|
||||
{
|
||||
ErrorLog("Could not open '%s'.", zipFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// First pass: scan every file and determine the game
|
||||
int err = unzGoToFirstFile(zf);
|
||||
if (UNZ_OK != err)
|
||||
{
|
||||
ErrorLog("Unable to read the contents of '%s' (code %X)", zipFile, err);
|
||||
return NULL;
|
||||
}
|
||||
for (; err != UNZ_END_OF_LIST_OF_FILE; err = unzGoToNextFile(zf))
|
||||
{
|
||||
// Identify the file we're looking at
|
||||
err = unzGetCurrentFileInfo(zf, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
||||
if (err != UNZ_OK)
|
||||
continue;
|
||||
if (OKAY != FindROMByCRC(&CurGame, &romIdx, GameList, Game, fileInfo.crc))
|
||||
continue;
|
||||
|
||||
// If the ROM appears in multiple games, do not use it to identify the game!
|
||||
if (!ROMIsUnique(GameList, fileInfo.crc))
|
||||
continue;
|
||||
|
||||
// We have a unique ROM used by a single game; identify that game
|
||||
if (Game == NULL) // this is the first game we've identified within the ZIP
|
||||
{
|
||||
Game = CurGame;
|
||||
DebugLog("ROM set identified: %s (%s), %s\n", Game->id, Game->title, zipFile);
|
||||
}
|
||||
else // we've already identified a game
|
||||
{
|
||||
if (CurGame != Game) // another game?
|
||||
{
|
||||
DebugLog("%s also contains: %s (%s)\n", zipFile, CurGame->id, CurGame->title);
|
||||
if (multipleGameError == false) // only warn about this once
|
||||
{
|
||||
ErrorLog("Multiple games were found in '%s'; loading '%s'.", zipFile, Game->title);
|
||||
multipleGameError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Game == NULL)
|
||||
{
|
||||
ErrorLog("No Model 3 games found in '%s'.", zipFile);
|
||||
unzClose(zf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unzFile zfp = 0;
|
||||
if (CurGame->parent)
|
||||
{
|
||||
// Create parent zip file name
|
||||
string path = "";
|
||||
if (strstr(zipFile, "/"))
|
||||
{
|
||||
path = string(zipFile);
|
||||
path = path.substr(0, path.find_last_of("/") + 1);
|
||||
}
|
||||
if (strstr(zipFile, "\\"))
|
||||
{
|
||||
path = string(zipFile);
|
||||
path = path.substr(0, path.find_last_of("\\") + 1);
|
||||
}
|
||||
zipFileParent = path + CurGame->parent + ".zip";
|
||||
|
||||
// Create error message
|
||||
zfpErr = " or '" + string(zipFileParent) + "'";
|
||||
|
||||
// Try to open file
|
||||
zfp = unzOpen(zipFileParent.c_str());
|
||||
if (NULL == zfp)
|
||||
{
|
||||
ErrorLog("Parent ROM set '%s' is missing.", zipFileParent.c_str());
|
||||
unzClose(zf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: check if all ROM files for the identified game are present
|
||||
err = unzGoToFirstFile(zf);
|
||||
if (UNZ_OK != err)
|
||||
{
|
||||
ErrorLog("Unable to read the contents of '%s' (code %X)", zipFile, err);
|
||||
unzClose(zf);
|
||||
return NULL;
|
||||
}
|
||||
memset(romsFound, 0, sizeof(romsFound)); // here, romsFound[] indicates which ROMs were found in the ZIP file for the game
|
||||
for (; err != UNZ_END_OF_LIST_OF_FILE; err = unzGoToNextFile(zf))
|
||||
{
|
||||
// Identify the file we're looking at
|
||||
err = unzGetCurrentFileInfo(zf, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
||||
if (err != UNZ_OK)
|
||||
continue;
|
||||
|
||||
// If it's not part of the game we've identified, skip it
|
||||
if (OKAY != FindROMByCRCInGame(&CurGame, &romIdx, Game, fileInfo.crc))
|
||||
continue;
|
||||
|
||||
// If we have found a ROM for the correct game, mark its corresponding indicator
|
||||
romsFound[romIdx] = 1;
|
||||
}
|
||||
if (zfp)
|
||||
{
|
||||
err = unzGoToFirstFile(zfp);
|
||||
if (UNZ_OK != err)
|
||||
{
|
||||
ErrorLog("Unable to read the contents of '%s' (code %X)", zipFileParent.c_str(), err);
|
||||
unzClose(zf);
|
||||
return NULL;
|
||||
}
|
||||
for (; err != UNZ_END_OF_LIST_OF_FILE; err = unzGoToNextFile(zfp))
|
||||
{
|
||||
// Identify the file we're looking at
|
||||
err = unzGetCurrentFileInfo(zfp, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
||||
if (err != UNZ_OK)
|
||||
continue;
|
||||
|
||||
// If it's not part of the game we've identified, skip it
|
||||
if (OKAY != FindROMByCRCInGame(&CurGame, &romIdx, Game, fileInfo.crc))
|
||||
continue;
|
||||
|
||||
// If we have found a ROM for the correct game, mark its corresponding indicator
|
||||
romsFound[romIdx] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute how many ROM files this game has
|
||||
size_t numROMs = 0;
|
||||
for (; Game->ROM[numROMs].region != NULL; numROMs++)
|
||||
;
|
||||
|
||||
// If not all ROMs were present, tell the user
|
||||
err = OKAY;
|
||||
for (size_t i = 0; i < numROMs; i++)
|
||||
{
|
||||
if ((0 == romsFound[i]) && !Game->ROM[i].optional) // if not found and also not optional
|
||||
err |= (int) ErrorLog("'%s' (CRC=%08X) is missing from '%s'%s.", Game->ROM[i].fileName, Game->ROM[i].crc, zipFile, zfp ? zfpErr.c_str() : "");
|
||||
}
|
||||
if (err != OKAY)
|
||||
{
|
||||
unzClose(zf);
|
||||
if (zfp) unzClose(zfp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate a scratch buffer big enough to hold the biggest ROM
|
||||
size_t maxSize = 0;
|
||||
for (size_t i = 0; i < numROMs; i++)
|
||||
{
|
||||
if (Game->ROM[i].fileSize > maxSize)
|
||||
maxSize = Game->ROM[i].fileSize;
|
||||
}
|
||||
UINT8 *buf = new(std::nothrow) UINT8[maxSize];
|
||||
if (NULL == buf)
|
||||
{
|
||||
unzClose(zf);
|
||||
if (zfp) unzClose(zfp);
|
||||
ErrorLog("Insufficient memory to load ROM files (%d bytes).", maxSize);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Third pass: load the ROMs
|
||||
memset(romsFound, 0, sizeof(romsFound)); // now, romsFound[] is used to indicate whether we successfully loaded the ROM
|
||||
err = unzGoToFirstFile(zf);
|
||||
if (UNZ_OK != err)
|
||||
{
|
||||
ErrorLog("Unable to read the contents of '%s' (code %X).", zipFile, err);
|
||||
err = FAIL;
|
||||
goto Quit;
|
||||
}
|
||||
for (; err != UNZ_END_OF_LIST_OF_FILE; err = unzGoToNextFile(zf))
|
||||
{
|
||||
err = unzGetCurrentFileInfo(zf, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
||||
if (err != UNZ_OK)
|
||||
continue;
|
||||
|
||||
// If this ROM is not part of the game we're loading, skip it
|
||||
if (OKAY != FindROMByCRCInGame(&CurGame, &romIdx, Game, fileInfo.crc))
|
||||
continue;
|
||||
|
||||
// Load the ROM and mark that we did so successfully
|
||||
if (OKAY == LoadROM(buf, maxSize, Map, &Game->ROM[romIdx], zf, zipFile, loadAll))
|
||||
romsFound[romIdx] = 1; // success! mark as loaded
|
||||
}
|
||||
if (zfp)
|
||||
{
|
||||
err = unzGoToFirstFile(zfp);
|
||||
if (UNZ_OK != err)
|
||||
{
|
||||
ErrorLog("Unable to read the contents of '%s' (code %X).", zipFileParent.c_str(), err);
|
||||
err = FAIL;
|
||||
goto Quit;
|
||||
}
|
||||
for (; err != UNZ_END_OF_LIST_OF_FILE; err = unzGoToNextFile(zfp))
|
||||
{
|
||||
err = unzGetCurrentFileInfo(zfp, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
||||
if (err != UNZ_OK)
|
||||
continue;
|
||||
|
||||
// If this ROM is not part of the game we're loading, skip it
|
||||
if (OKAY != FindROMByCRCInGame(&CurGame, &romIdx, Game, fileInfo.crc))
|
||||
continue;
|
||||
|
||||
// Load the ROM and mark that we did so successfully
|
||||
if (OKAY == LoadROM(buf, maxSize, Map, &Game->ROM[romIdx], zfp, zipFileParent.c_str(), loadAll))
|
||||
romsFound[romIdx] = 1; // success! mark as loaded
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure all ROMs were loaded
|
||||
if (loadAll)
|
||||
{
|
||||
// See if any ROMs (that are not optional) could not be found
|
||||
err = OKAY;
|
||||
for (size_t i = 0; i < numROMs; i++)
|
||||
{
|
||||
if (!(romsFound[i] || Game->ROM[i].optional)) // if ROM not found and also not optional
|
||||
err = ErrorLog("Could not load '%s' (CRC=%08X) from '%s'%s.", Game->ROM[i].fileName, Game->ROM[i].crc, zipFile, zfp ? zfpErr.c_str() : "");
|
||||
}
|
||||
}
|
||||
else
|
||||
err = OKAY;
|
||||
|
||||
Quit:
|
||||
unzClose(zf);
|
||||
delete [] buf;
|
||||
return (err == OKAY) ? Game : NULL;
|
||||
}
|
120
Src/ROMLoad.h
120
Src/ROMLoad.h
|
@ -1,120 +0,0 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
** Supermodel 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 3 of the License, or (at your option)
|
||||
** any later version.
|
||||
**
|
||||
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
/*
|
||||
* ROMLoad.h
|
||||
*
|
||||
* Header file for ROM loading functions.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_ROMLOAD_H
|
||||
#define INCLUDED_ROMLOAD_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Data Structures
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
* ROMInfo:
|
||||
*
|
||||
* Describes a single ROM file.
|
||||
*/
|
||||
struct ROMInfo
|
||||
{
|
||||
// Function
|
||||
const char *region; // ROM region identifier (used as a key to search ROMMap)
|
||||
bool optional; // whether needs to be present or not
|
||||
|
||||
// Information used to identify files
|
||||
const char *fileName; // file name
|
||||
uint32_t crc; // CRC-32 checksum (same as zip format)
|
||||
unsigned fileSize; // file size in bytes (must be the same as all other ROMs with same region ID)
|
||||
|
||||
// Interleaving information
|
||||
unsigned groupSize; // number of consecutive bytes to fetch each time (groupSize%2 must = 0, must be consistent for region)
|
||||
unsigned offset; // starting offset within ROM region
|
||||
unsigned stride; // number of bytes to skip before loading next group of bytes from file (must be >= groupSize)
|
||||
bool byteSwap; // swap every pair of bytes if true
|
||||
};
|
||||
|
||||
/*
|
||||
* ROMMap:
|
||||
*
|
||||
* Describes how to map ROM regions (where to load them). This structure is
|
||||
* assembled after memory allocation is completed and tells the ROM loading
|
||||
* functions where to load files by matching region identifiers with memory
|
||||
* pointers.
|
||||
*
|
||||
* Both pointers must be set to NULL to terminate an array of ROM maps.
|
||||
*/
|
||||
struct ROMMap
|
||||
{
|
||||
const char *region; // ROM region identifier
|
||||
uint8_t *ptr; // pointer to memory region
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Functions
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
* CopyRegion(dest, destOffset, destSize, src, srcSize):
|
||||
*
|
||||
* Repeatedly mirror (copy) to destination from source until destination is
|
||||
* filled.
|
||||
*
|
||||
* Parameters:
|
||||
* dest Destination region.
|
||||
* destOffset Offset within destination to begin mirroring to.
|
||||
* destSize Size in bytes of destination region.
|
||||
* src Source region to copy from.
|
||||
* srcSize Size of region to copy from.
|
||||
*/
|
||||
extern void CopyRegion(uint8_t *dest, unsigned destOffset, unsigned destSize, uint8_t *src, unsigned srcSize);
|
||||
|
||||
/*
|
||||
* LoadROMSetFromZIPFile(Map, GameList, zipFile):
|
||||
*
|
||||
* Loads a complete ROM set from a zip archive. Automatically detects the game.
|
||||
* If multiple games exist within the archive, an error will be printed and all
|
||||
* but the first detected game will be ignored.
|
||||
*
|
||||
* Parameters:
|
||||
* Map A list of pointers to the memory buffers for each ROM
|
||||
* region.
|
||||
* GameList List of all supported games and their ROMs.
|
||||
* zipFile ZIP file to load from.
|
||||
* loadAll If true, will check to ensure all ROMs were loaded.
|
||||
* Otherwise, omits this check and loads only specified
|
||||
* regions.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to GameInfo struct for loaded game if successful, NULL
|
||||
* otherwise. Prints errors.
|
||||
*/
|
||||
extern const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const struct GameInfo *GameList, const char *zipFile, bool loadAll);
|
||||
|
||||
|
||||
#endif // INCLUDED_ROMLOAD_H
|
|
@ -111,9 +111,6 @@
|
|||
here, except for external packages and APIs.
|
||||
******************************************************************************/
|
||||
|
||||
#include "Games.h"
|
||||
#include "ROMLoad.h"
|
||||
#include "INIFile.h"
|
||||
#include "BlockFile.h"
|
||||
#include "Graphics/New3D/New3D.h"
|
||||
#include "Graphics/Render2D.h"
|
||||
|
|
Loading…
Reference in a new issue