diff --git a/Makefiles/Makefile.SDL.Win32.MSVC b/Makefiles/Makefile.SDL.Win32.MSVC
index 0d9191a..5ecdcc3 100644
--- a/Makefiles/Makefile.SDL.Win32.MSVC
+++ b/Makefiles/Makefile.SDL.Win32.MSVC
@@ -156,7 +156,7 @@ endif
# Objects and Dependencies
#
HEADERS = Src/Supermodel.h Src/OSD/SDL/Types.h
-OBJ = $(OBJ_DIR)/PPCDisasm.obj $(OBJ_DIR)/Games.obj $(OBJ_DIR)/INIFile.obj $(OBJ_DIR)/BlockFile.obj $(OBJ_DIR)/93C46.obj \
+OBJ = $(OBJ_DIR)/PPCDisasm.obj $(OBJ_DIR)/Games.obj $(OBJ_DIR)/Config.obj $(OBJ_DIR)/INIFile.obj $(OBJ_DIR)/BlockFile.obj $(OBJ_DIR)/93C46.obj \
$(OBJ_DIR)/ROMLoad.obj $(OBJ_DIR)/unzip.obj $(OBJ_DIR)/ioapi.obj $(OBJ_DIR)/Error.obj $(OBJ_DIR)/glew.obj $(OBJ_DIR)/Shader.obj \
$(OBJ_DIR)/Real3D.obj $(OBJ_DIR)/Render3D.obj $(OBJ_DIR)/Models.obj $(OBJ_DIR)/Render2D.obj $(OBJ_DIR)/TileGen.obj \
$(OBJ_DIR)/Model3.obj $(OBJ_DIR)/ppc.obj $(OBJ_DIR)/Main.obj $(OBJ_DIR)/Audio.obj $(OBJ_DIR)/Thread.obj $(OBJ_DIR)/SoundBoard.obj \
diff --git a/Src/Config.cpp b/Src/Config.cpp
new file mode 100644
index 0000000..2fe93c0
--- /dev/null
+++ b/Src/Config.cpp
@@ -0,0 +1,31 @@
+/**
+ ** Supermodel
+ ** A Sega Model 3 Arcade Emulator.
+ ** Copyright 2011 Bart Trzynadlowski
+ **
+ ** 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 .
+ **/
+
+/*
+ * Config.cpp
+ *
+ * Program-wide configuration settings.
+ */
+
+#include "Config.h"
+
+
+CConfig g_Config;
diff --git a/Src/Config.h b/Src/Config.h
new file mode 100644
index 0000000..d2e9262
--- /dev/null
+++ b/Src/Config.h
@@ -0,0 +1,71 @@
+/**
+ ** Supermodel
+ ** A Sega Model 3 Arcade Emulator.
+ ** Copyright 2011 Bart Trzynadlowski
+ **
+ ** 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 .
+ **/
+
+/*
+ * Config.h
+ *
+ * Header file for program-wide configuration settings.
+ */
+
+#ifndef INCLUDED_CONFIG_H
+#define INCLUDED_CONFIG_H
+
+
+#include "Supermodel.h"
+
+
+/*
+ * CConfig:
+ *
+ * Class defining all configuration settings. Inherits settings from classes
+ * defined throughout the program.
+ *
+ * Conventions
+ * -----------
+ * - Modules and classes should only use the members of their own configuration
+ * class, so long as this is practical. This is left to programmer
+ * discretion. The intent is that one class, CReal3D for example, should not
+ * need information from another unrelated class, CModel3, unless explicitly
+ * passed to it. On the other hand, the OSD layer may have a legitimate
+ * need to use the renderer's video settings rather than maintaining its own.
+ * - Member variables that have a limited range of allowable values must be
+ * private and accessed through accessor members (SetVar(), GetVar()). The
+ * accessors must ensure that a value outside the allowable range is never
+ * returned and must clamp or reset to a default when an invalid setting
+ * is passed. Warnings may be printed.
+ * - Strings should be copied and retained locally.
+ * - Constructors must be defined and must initialize to the program default.
+ * - User-tunable settings should be stored here, not necessarily every
+ * concievable parameter a class initializer might take.
+ */
+class CConfig: public COSDConfig, public CRender3DConfig, public CModel3Config, public CSoundBoardConfig, public CDSBConfig
+{
+};
+
+/*
+ * g_Config:
+ *
+ * Program-wide configuration settings object.
+ */
+extern CConfig g_Config;
+
+
+#endif // INCLUDED_CONFIG_H
\ No newline at end of file
diff --git a/Src/Graphics/Render3D.cpp b/Src/Graphics/Render3D.cpp
index bd55e84..9da025e 100644
--- a/Src/Graphics/Render3D.cpp
+++ b/Src/Graphics/Render3D.cpp
@@ -1283,7 +1283,7 @@ void CRender3D::SetStep(int stepID)
DebugLog("Render3D set to Step %d.%d\n", (step>>4)&0xF, step&0xF);
}
-BOOL CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, const char *vsFile, const char *fsFile)
+BOOL CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes)
{
// Allocate memory for texture buffer
textureBuffer = new(std::nothrow) GLfloat[512*512*4];
@@ -1331,6 +1331,8 @@ BOOL CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
yOffs = yOffset;
// Load shaders
+ const char *vsFile = g_Config.vertexShaderFile.size() ? g_Config.vertexShaderFile.c_str() : NULL;
+ const char *fsFile = g_Config.fragmentShaderFile.size() ? g_Config.fragmentShaderFile.c_str() : NULL;
if (OKAY != LoadShaderProgram(&shaderProgram,&vertexShader,&fragmentShader,vsFile,fsFile,vertexShaderSource,fragmentShaderSource))
return FAIL;
diff --git a/Src/Graphics/Render3D.h b/Src/Graphics/Render3D.h
index c37ee5c..71f4bde 100644
--- a/Src/Graphics/Render3D.h
+++ b/Src/Graphics/Render3D.h
@@ -163,9 +163,27 @@ struct ModelCache
/******************************************************************************
- CRender3D Class Definition
+ CRender3D Classes
******************************************************************************/
+/*
+ * CRender3DConfig:
+ *
+ * Settings used by CRender3D.
+ */
+class CRender3DConfig
+{
+public:
+ string vertexShaderFile; // path to vertex shader or "" to use internal shader
+ string fragmentShaderFile; // fragment shader
+
+ // Defaults
+ CRender3DConfig(void)
+ {
+ // nothing to do, strings will be clear to begin with
+ }
+};
+
/*
* CRender3D:
*
@@ -244,30 +262,27 @@ public:
void SetStep(int stepID);
/*
- * Init(xOffset, yOffset, xRes, yRes, vsFile, fsFile):
+ * Init(xOffset, yOffset, xRes, yRes):
*
* One-time initialization of the context. Must be called before any other
* members (meaning it should be called even before being attached to any
* other objects that want to use it).
*
+ * External shader files are loaded according to configuration settings.
+ *
* Parameters:
* xOffset X offset of display surface in pixels (in case resolution
* is smaller than the true display surface).
* yOffset Y offset.
* xRes Horizontal resolution of display surface in pixels.
* yRes Vertical resolution.
- * vsFile External vertex shader path. If NULL, the internal
- * shader is used.
- * fsFile External fragment shader path. If NULL, the internal
- * shader is used.
*
* Returns:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Any allocated memory will not be freed until the
* destructor is called. Prints own error messages.
*/
- BOOL Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes,
- const char *vsFile, const char *fsFile);
+ BOOL Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes);
/*
* CRender3D(void):
diff --git a/Src/INIFile.cpp b/Src/INIFile.cpp
index 217b700..dc59e06 100644
--- a/Src/INIFile.cpp
+++ b/Src/INIFile.cpp
@@ -26,10 +26,8 @@
*
* To-Do List
* ----------
- * - 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 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).
diff --git a/Src/INIFile.h b/Src/INIFile.h
index beca8db..716b84f 100644
--- a/Src/INIFile.h
+++ b/Src/INIFile.h
@@ -63,7 +63,8 @@ public:
*
* Returns:
* OKAY if the setting was found, FAIL otherwise. The type is not
- * checked.
+ * 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);
diff --git a/Src/Logger.h b/Src/Logger.h
deleted file mode 100644
index c800483..0000000
--- a/Src/Logger.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef INCLUDED_LOGGER_H
-#define INCLUDED_LOGGER_H
-
-#include
-
-/*
- * CLogger
- *
- * Abstract class that receives log messages from Supermodel.
- */
-class CLogger
-{
-public:
- void DebugLog(const char *fmt, ...)
- {
- va_list vl;
- va_start(vl, fmt);
- DebugLog(fmt, vl);
- va_end(vl);
- }
-
- void InfoLog(const char *fmt, ...)
- {
- va_list vl;
- va_start(vl, fmt);
- InfoLog(fmt, vl);
- va_end(vl);
- }
-
- void ErrorLog(const char *fmt, ...)
- {
- va_list vl;
- va_start(vl, fmt);
- ErrorLog(fmt, vl);
- va_end(vl);
- }
-
- virtual void DebugLog(const char *fmt, va_list vl) = 0;
-
- virtual void InfoLog(const char *fmt, va_list vl) = 0;
-
- virtual void ErrorLog(const char *fmt, va_list vl) = 0;
-};
-
-#endif // INCLUDED_LOGGER_H
diff --git a/Src/Model3/DSB.cpp b/Src/Model3/DSB.cpp
index 5ed3fc2..a5211c2 100644
--- a/Src/Model3/DSB.cpp
+++ b/Src/Model3/DSB.cpp
@@ -139,8 +139,14 @@ int CDSBResampler::UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *i
int delta = (inRate<<8)/outRate; // (1/fout)/(1/fin)=fin/fout, 24.8 fixed point
int outIdx = 0;
int inIdx = 0;
- INT16 leftSample, rightSample;
- INT16 v[2];
+ INT32 leftSample, rightSample, leftSoundSample, rightSoundSample;
+ INT32 v[2], musicVol, soundVol;
+
+ // Obtain program volume settings and convert to 24.8 fixed point (0-200 -> 0x00-0x200)
+ musicVol = g_Config.GetMusicVolume();
+ soundVol = g_Config.GetSoundVolume();
+ musicVol = (INT32) ((float) 0x100 * (float) musicVol / 100.0f);
+ soundVol = (INT32) ((float) 0x100 * (float) soundVol / 100.0f);
// Scale volume from 0x00-0xFF -> 0x00-0x100 (24.8 fixed point)
v[0] = (INT16) ((float) 0x100 * (float) volumeL / 255.0f);
@@ -153,13 +159,17 @@ int CDSBResampler::UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *i
leftSample = ((int)inL[inIdx]*pFrac+(int)inL[inIdx+1]*nFrac) >> 8; // left channel
rightSample = ((int)inR[inIdx]*pFrac+(int)inR[inIdx+1]*nFrac) >> 8; // right channel
- // Apply volume
- leftSample = (leftSample*v[0]) >> 8;
- rightSample = (rightSample*v[0]) >> 8;
+ // Apply DSB volume and then overall music volume setting
+ leftSample = (leftSample*v[0]*musicVol) >> 16; // multiplied by two 24.8 numbers, shift back by 16
+ rightSample = (rightSample*v[0]*musicVol) >> 16;
+
+ // Apply sound volume setting
+ leftSoundSample = (outL[outIdx]*soundVol) >> 8;
+ rightSoundSample = (outR[outIdx]*soundVol) >> 8;
// Mix and output
- outL[outIdx] = MixAndClip(outL[outIdx], leftSample);
- outR[outIdx] = MixAndClip(outR[outIdx], rightSample);
+ outL[outIdx] = MixAndClip(leftSoundSample, leftSample);
+ outR[outIdx] = MixAndClip(rightSoundSample, rightSample);
outIdx++;
// Time step
@@ -413,6 +423,15 @@ void CDSB1::RunFrame(INT16 *audioL, INT16 *audioR)
int cycles;
UINT8 v;
+ if (!g_Config.emulateDSB)
+ {
+ // DSB code applies SCSP volume, too, so we must still mix
+ memset(mpegL, 0, (32000/60+2)*sizeof(INT16));
+ memset(mpegR, 0, (32000/60+2)*sizeof(INT16));
+ retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, 0, 0, 44100/60, 32000/60+2, 44100, 32000);
+ return;
+ }
+
// While FIFO not empty, fire interrupts, run for up to one frame
for (cycles = (4000000/60)/4; (cycles > 0) && (fifoIdxR != fifoIdxW); )
{
@@ -948,6 +967,15 @@ void CDSB2::SendCommand(UINT8 data)
void CDSB2::RunFrame(INT16 *audioL, INT16 *audioR)
{
#ifdef SUPERMODEL_SOUND
+ if (!g_Config.emulateDSB)
+ {
+ // DSB code applies SCSP volume, too, so we must still mix
+ memset(mpegL, 0, (32000/60+2)*sizeof(INT16));
+ memset(mpegR, 0, (32000/60+2)*sizeof(INT16));
+ retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, volume[0], volume[1], 44100/60, 32000/60+2, 44100, 32000);
+ return;
+ }
+
M68KSetContext(&M68K);
//printf("DSB2 run frame PC=%06X\n", M68KGetPC());
diff --git a/Src/Model3/DSB.h b/Src/Model3/DSB.h
index 86bf725..d32aff6 100644
--- a/Src/Model3/DSB.h
+++ b/Src/Model3/DSB.h
@@ -35,6 +35,71 @@
#include "CPU/Bus.h"
+/******************************************************************************
+ Configuration
+
+ Because DSB code mixes both the sound board SCSP and MPEG audio together, both
+ volume settings are stored here (for now).
+******************************************************************************/
+
+/*
+ * CDSBConfig:
+ *
+ * Settings used by CDSB.
+ */
+class CDSBConfig
+{
+public:
+ bool emulateDSB; // DSB emulation (enabled if TRUE)
+
+ // Sound (SCSP) volume (0-200, 100 being full amplitude)
+ inline void SetSoundVolume(unsigned vol)
+ {
+ if (vol > 200)
+ {
+ ErrorLog("Sound volume cannot exceed 200%%; setting to 100%%.\n");
+ vol = 100;
+ }
+
+ soundVol = vol;
+ }
+
+ inline unsigned GetSoundVolume(void)
+ {
+ return soundVol;
+ }
+
+ // Music (DSB MPEG) volume (0-200)
+ inline void SetMusicVolume(unsigned vol)
+ {
+ if (vol > 200)
+ {
+ ErrorLog("Music volume cannot exceed 200%%; setting to 100%%.\n");
+ vol = 100;
+ }
+
+ musicVol = vol;
+ }
+
+ inline unsigned GetMusicVolume(void)
+ {
+ return musicVol;
+ }
+
+ // Defaults
+ CDSBConfig(void)
+ {
+ emulateDSB = true;
+ soundVol = 100;
+ musicVol = 100;
+ }
+
+private:
+ unsigned soundVol;
+ unsigned musicVol;
+};
+
+
/******************************************************************************
Resampling
diff --git a/Src/Model3/Model3.cpp b/Src/Model3/Model3.cpp
index 053440e..572620d 100644
--- a/Src/Model3/Model3.cpp
+++ b/Src/Model3/Model3.cpp
@@ -184,9 +184,9 @@
*/
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include "Supermodel.h"
/******************************************************************************
@@ -1870,7 +1870,7 @@ void CModel3::ClearNVRAM(void)
void CModel3::RunFrame(void)
{
// See if currently running multi-threaded
- if (multiThreaded)
+ if (g_Config.multiThreaded)
{
// If so, check all threads are up and running
if (!StartThreads())
@@ -1924,7 +1924,7 @@ void CModel3::RunFrame(void)
ThreadError:
ErrorLog("Threading error in CModel3::RunFrame: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
- multiThreaded = false;
+ g_Config.multiThreaded = false;
}
bool CModel3::StartThreads()
@@ -1966,7 +1966,7 @@ bool CModel3::StartThreads()
ThreadError:
ErrorLog("Unable to create threads and/or synchronization objects: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
DeleteThreadObjects();
- multiThreaded = false;
+ g_Config.multiThreaded = false;
return false;
}
@@ -2066,7 +2066,7 @@ void CModel3::RunSoundBoardThread()
ThreadError:
ErrorLog("Threading error in sound board thread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
- multiThreaded = false;
+ g_Config.multiThreaded = false;
}
#ifdef SUPERMODEL_DRIVEBOARD
@@ -2097,14 +2097,14 @@ void CModel3::RunDriveBoardThread()
ThreadError:
ErrorLog("Threading error in drive board thread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
- multiThreaded = false;
+ g_Config.multiThreaded = false;
}
#endif
void CModel3::RunMainBoardFrame(void)
{
// Run the PowerPC for a frame
- ppc_execute(ppcFrequency/60-10000);
+ ppc_execute(g_Config.GetPowerPCFrequency()*1000000/60-10000);
//printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr());
// VBlank
@@ -2612,16 +2612,10 @@ void CModel3::AttachInputs(CInputs *InputsPtr)
}
// Model 3 initialization. Some initialization is deferred until ROMs are loaded in LoadROMSet()
-BOOL CModel3::Init(unsigned ppcFrequencyParam, BOOL multiThreadedParam)
+BOOL CModel3::Init(void)
{
float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000;
- // PowerPC frequency
- ppcFrequency = ppcFrequencyParam;
- if (ppcFrequency < 1000000)
- ppcFrequency = 1000000;
- multiThreaded = !!multiThreadedParam;
-
// Allocate all memory for ROMs and PPC RAM
memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE];
if (NULL == memoryPool)
@@ -2689,7 +2683,7 @@ CModel3::CModel3(void)
securityPtr = 0;
- multiThreaded = true;
+ g_Config.multiThreaded = true;
startedThreads = false;
sndBrdThread = NULL;
#ifdef SUPERMODEL_DRIVEBOARD
diff --git a/Src/Model3/Model3.h b/Src/Model3/Model3.h
index 51b054c..a4ed9e2 100644
--- a/Src/Model3/Model3.h
+++ b/Src/Model3/Model3.h
@@ -22,12 +22,48 @@
/*
* Model3.h
*
- * Header file defining the CModel3 and CModel3Inputs classes.
+ * Header file defining the CModel3, CModel3Config, and CModel3Inputs classes.
*/
#ifndef INCLUDED_MODEL3_H
#define INCLUDED_MODEL3_H
+/*
+ * CModel3Config:
+ *
+ * Settings used by CModel3.
+ */
+class CModel3Config
+{
+public:
+ bool multiThreaded; // Multi-threading (enabled if TRUE)
+
+ // PowerPC clock frequency in MHz (minimum: 1 MHz)
+ inline void SetPowerPCFrequency(unsigned f)
+ {
+ if ((f<1) || (f>1000))
+ {
+ ErrorLog("PowerPC frequency must be between 1 and 1000 MHz; setting to 40 MHz.");
+ f = 40;
+ }
+ ppcFrequency = f*1000000;
+ }
+ inline unsigned GetPowerPCFrequency(void)
+ {
+ return ppcFrequency/1000000;
+ }
+
+ // Defaults
+ CModel3Config(void)
+ {
+ multiThreaded = false; // disable by default
+ ppcFrequency = 40*1000000; // 40 MHz
+ }
+
+private:
+ unsigned ppcFrequency; // in Hz
+};
+
/*
* CModel3:
*
@@ -228,20 +264,16 @@ public:
void AttachInputs(CInputs *InputsPtr);
/*
- * Init(ppcFrequencyParam, multiThreadedParam):
+ * Init(void):
*
* One-time initialization of the context. Must be called prior to all
* other members. Allocates memory and initializes device states.
*
- * Parameters:
- * ppcFrequencyParam PowerPC frequency in Hz. If less than 1 MHz,
- * will be clamped to 1 MHz.
- *
* Returns:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Prints own error messages.
*/
- BOOL Init(unsigned ppcFrequencyParam, BOOL multiThreadedParam);
+ BOOL Init(void);
/*
* CModel3(void):
@@ -321,10 +353,8 @@ private:
// PowerPC
PPC_FETCH_REGION PPCFetchRegions[3];
- unsigned ppcFrequency; // clock frequency (Hz)
// Multiple threading
- bool multiThreaded; // True if should run CPUs in multiple threads, otherwise everything is run in a single thread
bool startedThreads; // True if threads have been created and started
CThread *sndBrdThread; // Sound board thread
#ifdef SUPERMODEL_DRIVEBOARD
diff --git a/Src/Model3/SoundBoard.cpp b/Src/Model3/SoundBoard.cpp
index 423cb53..ca51b09 100644
--- a/Src/Model3/SoundBoard.cpp
+++ b/Src/Model3/SoundBoard.cpp
@@ -58,7 +58,7 @@
#include "Supermodel.h"
// DEBUG
-//#define SUPERMODEL_LOG_AUDIO // define this to log all audio to sound.bin
+#define SUPERMODEL_LOG_AUDIO // define this to log all audio to sound.bin
#ifdef SUPERMODEL_LOG_AUDIO
static FILE *soundFP;
#endif
@@ -362,10 +362,17 @@ void CSoundBoard::RunFrame(void)
{
#ifdef SUPERMODEL_SOUND
// Run sound board first to generate SCSP audio
- M68KSetContext(&M68K);
- SCSP_Update();
- M68KGetContext(&M68K);
- //memset(audioL, 0, 44100/60*sizeof(INT16));memset(audioR, 0, 44100/60*sizeof(INT16)); // clear, I want DSB only
+ if (g_Config.emulateSCSP)
+ {
+ M68KSetContext(&M68K);
+ SCSP_Update();
+ M68KGetContext(&M68K);
+ }
+ else
+ {
+ memset(audioL, 0, 44100/60*sizeof(INT16));
+ memset(audioR, 0, 44100/60*sizeof(INT16));
+ }
// Run DSB and mix with existing audio
if (NULL != DSB)
diff --git a/Src/Model3/SoundBoard.h b/Src/Model3/SoundBoard.h
index 16fc0a0..15fb63c 100644
--- a/Src/Model3/SoundBoard.h
+++ b/Src/Model3/SoundBoard.h
@@ -32,6 +32,23 @@
#include "CPU/Bus.h"
#include "Model3/DSB.h"
+/*
+ * CSoundBoardConfig:
+ *
+ * Settings used by CSoundBoard.
+ */
+class CSoundBoardConfig
+{
+public:
+ bool emulateSCSP; // SCSP emulation (enabled if TRUE)
+
+ // Defaults
+ CSoundBoardConfig(void)
+ {
+ emulateSCSP = true;
+ }
+};
+
/*
* CSoundBoard:
*
diff --git a/Src/OSD/Logger.h b/Src/OSD/Logger.h
new file mode 100644
index 0000000..57260f0
--- /dev/null
+++ b/Src/OSD/Logger.h
@@ -0,0 +1,286 @@
+/*
+ * Logger.h
+ *
+ * Header file for message logging. The OSD code is expected to set up a
+ * default logger (CFileLogger).
+ */
+
+#ifndef INCLUDED_LOGGER_H
+#define INCLUDED_LOGGER_H
+
+#include "Types.h"
+#include
+#include
+#include
+
+
+/******************************************************************************
+ Class Definitions
+******************************************************************************/
+
+/*
+ * CLogger
+ *
+ * Abstract class that receives log messages from Supermodel's log functions.
+ * The logger object handles actual output of messages. Use the function-based
+ * message logging interface to generate messages.
+ */
+class CLogger
+{
+public:
+ /*
+ * DebugLog(fmt, ...):
+ * DebugLog(fmt, vl):
+ *
+ * Prints to debug log. If DEBUG is not defined, will end up doing nothing.
+ *
+ * Parameters:
+ * fmt printf()-style format string.
+ * ... Variable number of parameters, corresponding to format
+ * string.
+ * vl Variable arguments already stored in a list.
+ */
+ void DebugLog(const char *fmt, ...)
+ {
+ va_list vl;
+ va_start(vl, fmt);
+ DebugLog(fmt, vl);
+ va_end(vl);
+ }
+
+ virtual void DebugLog(const char *fmt, va_list vl) = 0;
+
+ /*
+ * InfoLog(fmt, ...):
+ * InfoLog(fmt, vl):
+ *
+ * Prints to error log but does not output an error to stderr. This is
+ * useful for printing session information to the error log.
+ *
+ * Parameters:
+ * fmt printf()-style format string.
+ * ... Variable number of parameters, corresponding to format
+ * string.
+ * vl Variable arguments already stored in a list.
+ */
+ void InfoLog(const char *fmt, ...)
+ {
+ va_list vl;
+ va_start(vl, fmt);
+ InfoLog(fmt, vl);
+ va_end(vl);
+ }
+
+ virtual void InfoLog(const char *fmt, va_list vl) = 0;
+
+ /*
+ * ErrorLog(fmt, ...):
+ * ErrorLog(fmt, vl):
+ *
+ * Prints to error log and outputs an error message to stderr.
+ *
+ * Parameters:
+ * fmt printf()-style format string.
+ * ... Variable number of parameters, corresponding to format
+ * string.
+ * vl Variable arguments already stored in a list.
+ */
+ void ErrorLog(const char *fmt, ...)
+ {
+ va_list vl;
+ va_start(vl, fmt);
+ ErrorLog(fmt, vl);
+ va_end(vl);
+ }
+
+ virtual void ErrorLog(const char *fmt, va_list vl) = 0;
+};
+
+/*
+ * CFileLogger:
+ *
+ * Default logger that logs to debug and error log files. Files are opened and
+ * closed for each message in order to preserve contents in case of program
+ * crash.
+ */
+class CFileLogger : public CLogger
+{
+public:
+
+ void DebugLog(const char *fmt, va_list vl)
+ {
+#ifdef DEBUG
+ char string[1024];
+ FILE *fp;
+
+ fp = fopen(m_debugLogFile, "ab");
+ if (NULL != fp)
+ {
+ vsprintf(string, fmt, vl);
+ fprintf(fp, string);
+ fclose(fp);
+ }
+#endif // DEBUG
+ }
+
+ void InfoLog(const char *fmt, va_list vl)
+ {
+ char string[4096];
+ FILE *fp;
+
+ vsprintf(string, fmt, vl);
+
+ fp = fopen(m_errorLogFile, "ab");
+ if (NULL != fp)
+ {
+ fprintf(fp, "%s\n", string);
+ fclose(fp);
+ }
+
+ CLogger::DebugLog("Info: ");
+ CLogger::DebugLog(string);
+ CLogger::DebugLog("\n");
+ }
+
+ void ErrorLog(const char *fmt, va_list vl)
+ {
+ char string[4096];
+ FILE *fp;
+
+ vsprintf(string, fmt, vl);
+ fprintf(stderr, "Error: %s\n", string);
+
+ fp = fopen(m_errorLogFile, "ab");
+ if (NULL != fp)
+ {
+ fprintf(fp, "%s\n", string);
+ fclose(fp);
+ }
+
+ CLogger::DebugLog("Error: ");
+ CLogger::DebugLog(string);
+ CLogger::DebugLog("\n");
+ }
+
+ /*
+ * ClearLogs():
+ *
+ * Clears all log files.
+ */
+ void ClearLogs(void)
+ {
+#ifdef DEBUG
+ ClearLog(m_debugLogFile, "Supermodel v"SUPERMODEL_VERSION" Debug Log");
+#endif // DEBUG
+ ClearLog(m_errorLogFile, "Supermodel v"SUPERMODEL_VERSION" Error Log");
+ }
+
+ /*
+ * ClearLog(file, title):
+ *
+ * Clears a log file.
+ *
+ * Parameters:
+ * file File name.
+ * title A string that is written to the file after it is cleared.
+ */
+ void ClearLog(const char *file, const char *title)
+ {
+ FILE *fp = fopen(file, "w");
+ if (NULL != fp)
+ {
+ unsigned i;
+ fprintf(fp, "%s\n", title);
+ for (i = 0; i < strlen(title); i++)
+ fputc('-', fp);
+ fprintf(fp, "\n\n");
+ fclose(fp);
+ }
+ }
+
+ /*
+ * CFileLogger(debugLogFile, errorLogFile):
+ *
+ * Constructor. Specifies debug and error log files to use.
+ */
+ CFileLogger(const char *debugLogFile, const char *errorLogFile) :
+ m_debugLogFile(debugLogFile), m_errorLogFile(errorLogFile)
+ {
+ }
+
+private:
+ const char *m_debugLogFile;
+ const char *m_errorLogFile;
+};
+
+
+/******************************************************************************
+ Log Functions
+
+ Message logging interface. All messages are passed by the OSD layer to the
+ currently active logger object.
+******************************************************************************/
+
+/*
+ * DebugLog(fmt, ...):
+ *
+ * Prints debugging information. The OSD layer may choose to print this to a
+ * log file, the screen, neither, or both. Newlines and other formatting codes
+ * must be explicitly included.
+ *
+ * Parameters:
+ * fmt A format string (the same as printf()).
+ * ... Variable number of arguments, as required by format string.
+ */
+extern void DebugLog(const char *fmt, ...);
+
+/*
+ * ErrorLog(fmt, ...):
+ *
+ * Prints error information. Errors need not require program termination and
+ * may simply be informative warnings to the user. Newlines should not be
+ * included in the format string -- they are automatically added at the end of
+ * a line.
+ *
+ * Parameters:
+ * fmt A format string (the same as printf()).
+ * ... Variable number of arguments, as required by format string.
+ *
+ * Returns:
+ * Must always return FAIL.
+ */
+extern BOOL ErrorLog(const char *fmt, ...);
+
+/*
+ * InfoLog(fmt, ...);
+ *
+ * Prints information to the error log file but does not print to stderr. This
+ * is useful for logging non-error information. Newlines are automatically
+ * appended.
+ *
+ * Parameters:
+ * fmt Format string (same as printf()).
+ * ... Variable number of arguments as required by format string.
+ */
+extern void InfoLog(const char *fmt, ...);
+
+/*
+ * SetLogger(Logger):
+ *
+ * Sets the logger object to use.
+ *
+ * Parameters:
+ * Logger New logger object. If NULL, log messages will not be output.
+ */
+extern void SetLogger(CLogger *Logger);
+
+/*
+ * GetLogger(void):
+ *
+ * Returns:
+ * Current logger object (NULL if none has been set).
+ */
+extern CLogger *GetLogger(void);
+
+
+#endif // INCLUDED_LOGGER_H
diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp
index c23c4bd..3ce489c 100644
--- a/Src/OSD/SDL/Main.cpp
+++ b/Src/OSD/SDL/Main.cpp
@@ -40,10 +40,10 @@
*/
#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
#include "Pkgs/glew.h"
#ifdef SUPERMODEL_OSX
#include
@@ -57,15 +57,69 @@
#include "DirectInputSystem.h"
#endif
-CLogger *GetLogger();
-void SetLogger(CLogger *logger);
+
+/******************************************************************************
+ Error and Debug Logging
+******************************************************************************/
+
+// Log file names
+#define DEBUG_LOG_FILE "debug.log"
+#define ERROR_LOG_FILE "error.log"
+
+// Logger object is used to redirect log messages appropriately
+static CLogger *s_Logger = NULL;
+
+CLogger *GetLogger()
+{
+ return s_Logger;
+}
+
+void SetLogger(CLogger *Logger)
+{
+ s_Logger = Logger;
+}
+
+void DebugLog(const char *fmt, ...)
+{
+ if (s_Logger == NULL)
+ return;
+ va_list vl;
+ va_start(vl, fmt);
+ s_Logger->DebugLog(fmt, vl);
+ va_end(vl);
+}
+
+void InfoLog(const char *fmt, ...)
+{
+ if (s_Logger == NULL)
+ return;
+ va_list vl;
+ va_start(vl, fmt);
+ s_Logger->InfoLog(fmt, vl);
+ va_end(vl);
+}
+
+BOOL ErrorLog(const char *fmt, ...)
+{
+ if (s_Logger == NULL)
+ return FAIL;
+ va_list vl;
+ va_start(vl, fmt);
+ s_Logger->ErrorLog(fmt, vl);
+ va_end(vl);
+ return FAIL;
+}
+
/******************************************************************************
Display Management
******************************************************************************/
/*
- * Position and size of rectangular region within OpenGL display to render to
+ * Position and size of rectangular region within OpenGL display to render to.
+ * Unlike the g_Config object, these end up containing the actual resolution
+ * (and computed offsets within the viewport) that will be rendered based on
+ * what was obtained from SDL.
*/
unsigned xOffset, yOffset; // offset of renderer output within OpenGL viewport
unsigned xRes, yRes; // renderer output resolution (can be smaller than GL viewport)
@@ -162,8 +216,11 @@ static BOOL CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *
return 0;
}
+
/******************************************************************************
Configuration
+
+ Configuration file management and input settings.
******************************************************************************/
#define CONFIG_FILE_PATH "Config/Supermodel.ini"
@@ -172,19 +229,12 @@ static BOOL CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *
";\n"
// Create and configure inputs
-static CInputs *CreateInputs(CInputSystem *InputSystem, BOOL configure)
+static bool ConfigureInputs(CInputs *Inputs, bool configure)
{
- // Create and initialize inputs
- CInputs* Inputs = new CInputs(InputSystem);
- if (!Inputs->Initialize())
- {
- ErrorLog("Unable to initalize inputs.\n");
- return NULL;
- }
-
// Open and parse configuration file
CINIFile INI;
INI.Open(CONFIG_FILE_PATH); // doesn't matter if it exists or not, will get overwritten
+ INI.SetDefaultSectionName("Global");
INI.Parse();
Inputs->ReadFromINIFile(&INI, "Global");
@@ -195,10 +245,7 @@ static CInputs *CreateInputs(CInputSystem *InputSystem, BOOL configure)
// Open an SDL window
unsigned xOffset, yOffset, xRes=496, yRes=384;
if (OKAY != CreateGLScreen("Supermodel - Configuring Inputs...",&xOffset,&yOffset,&xRes,&yRes,FALSE,FALSE))
- {
- ErrorLog("Unable to start SDL to configure inputs.\n");
- return NULL;
- }
+ return (bool) ErrorLog("Unable to start SDL to configure inputs.\n");
// Configure the inputs
if (Inputs->ConfigureInputs(NULL, xOffset, yOffset, xRes, yRes))
@@ -216,9 +263,82 @@ static CInputs *CreateInputs(CInputSystem *InputSystem, BOOL configure)
puts("");
}
- return Inputs;
+ INI.Close();
+ return OKAY;
}
+
+// Apply configuration settings from configuration file
+static void ApplySettings(CInputs *Inputs, CINIFile *INI, const char *section)
+{
+ unsigned x;
+ string String;
+ // Model 3
+ if (OKAY == INI->Get(section, "MultiThreaded", x))
+ g_Config.multiThreaded = x ? true : false;
+ if (OKAY == INI->Get(section, "PowerPCFrequency", x))
+ g_Config.SetPowerPCFrequency(x);
+
+ // 3D renderer
+ INI->Get(section, "VertexShader", g_Config.vertexShaderFile);
+ INI->Get(section, "FragmentShader", g_Config.fragmentShaderFile);
+
+ // SCSP and DSB
+ if (OKAY == INI->Get(section, "SoundVolume", x))
+ g_Config.SetSoundVolume(x);
+ if (OKAY == INI->Get(section, "MusicVolume", x))
+ g_Config.SetMusicVolume(x);
+ if (OKAY == INI->Get(section, "EmulateSCSP", x))
+ g_Config.emulateSCSP = x ? true : false;
+ if (OKAY == INI->Get(section, "EmulateDSB", x))
+ g_Config.emulateDSB = x ? true : false;
+
+ // OSD
+ INI->Get(section, "XResolution", g_Config.xRes);
+ INI->Get(section, "YResolution", g_Config.yRes);
+ if (OKAY == INI->Get(section, "FullScreen", x))
+ g_Config.fullScreen = x ? true : false;
+ if (OKAY == INI->Get(section, "Throttle", x))
+ g_Config.throttle = x ? true : false;
+ if (OKAY == INI->Get(section, "ShowFrameRate", x))
+ g_Config.showFPS = x ? true : false;
+
+ // Inputs
+ if (Inputs != NULL)
+ Inputs->ReadFromINIFile(INI, section);
+}
+
+// Read settings (from a specific section) from the config file
+static void ReadConfigFile(CInputs *Inputs, const char *section)
+{
+ CINIFile INI;
+
+ INI.Open(CONFIG_FILE_PATH);
+ INI.SetDefaultSectionName("Global"); // required to read settings not associated with a specific section
+ INI.Parse();
+ ApplySettings(Inputs, &INI, section);
+ INI.Close();
+}
+
+// Debug
+static void DumpConfig(void)
+{
+ printf("MultiThreaded = %d\n", g_Config.multiThreaded);
+ printf("PowerPCFrequency = %d\n", g_Config.GetPowerPCFrequency());
+ printf("EmulateSCSP = %d\n", g_Config.emulateSCSP);
+ printf("EmulateDSB = %d\n", g_Config.emulateDSB);
+ printf("VertexShader = %s\n", g_Config.vertexShaderFile.c_str());
+ printf("FragmentShader = %s\n", g_Config.fragmentShaderFile.c_str());
+ printf("XResolution = %d\n", g_Config.xRes);
+ printf("YResolution = %d\n", g_Config.yRes);
+ printf("FullScreen = %d\n", g_Config.fullScreen);
+ printf("Throttle = %d\n", g_Config.throttle);
+ printf("ShowFrameRate = %d\n", g_Config.showFPS);
+ printf("InputSystem = %s\n", g_Config.GetInputSystem());
+ printf("\n");
+}
+
+
/******************************************************************************
Save States and NVRAM
@@ -364,14 +484,11 @@ static void LoadNVRAM(CModel3 *Model3)
******************************************************************************/
#ifdef SUPERMODEL_DEBUGGER
-int Supermodel(const char *zipFile, CModel3 *Model3, CInputs *Inputs, Debugger::CDebugger *Debugger, unsigned ppcFrequency, BOOL multiThreaded,
- unsigned xResParam, unsigned yResParam, BOOL keepAspectRatio, BOOL fullScreen, BOOL noThrottle, BOOL showFPS,
- const char *vsFile, const char *fsFile)
+int Supermodel(const char *zipFile, CModel3 *Model3, CInputs *Inputs, Debugger::CDebugger *Debugger, CINIFile *CmdLine)
{
CLogger *oldLogger;
#else
-int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL multiThreaded, unsigned xResParam, unsigned yResParam,
- BOOL keepAspectRatio, BOOL fullScreen, BOOL noThrottle, BOOL showFPS, const char *vsFile, const char *fsFile)
+int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
{
CModel3 *Model3 = new CModel3();
#endif // SUPERMODEL_DEBUGGER
@@ -385,23 +502,30 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL
BOOL paused = 0;
// Info log user options
- InfoLog("PowerPC frequency: %d Hz", ppcFrequency);
- InfoLog("Resolution: %dx%d (%s)", xResParam, yResParam, fullScreen?"full screen":"windowed");
- InfoLog("Frame rate limiting: %s", noThrottle?"Disabled":"Enabled");
+ InfoLog("PowerPC frequency: %d Hz", g_Config.GetPowerPCFrequency());
+ InfoLog("Resolution requested: %dx%d (%s)", g_Config.xRes, g_Config.yRes, g_Config.fullScreen?"full screen":"windowed");
+ InfoLog("Frame rate limiting: %s", g_Config.throttle?"Enabled":"Disabled");
// Initialize and load ROMs
- Model3->Init(ppcFrequency, multiThreaded);
+ if (OKAY != Model3->Init())
+ return 1;
if (OKAY != Model3->LoadROMSet(Model3GameList, zipFile))
return 1;
+ // Apply game-specific settings and then, lastly, command line settings
+ ReadConfigFile(Inputs, Model3->GetGameInfo()->id);
+ //DumpConfig();
+ ApplySettings(Inputs, CmdLine, "Global");
+ //DumpConfig();
+
// Load NVRAM
LoadNVRAM(Model3);
// Start up SDL and open a GL window
- xRes = xResParam;
- yRes = yResParam;
+ xRes = g_Config.xRes;
+ yRes = g_Config.yRes;
sprintf(titleStr, "Supermodel - %s", Model3->GetGameInfo()->title);
- if (OKAY != CreateGLScreen(titleStr,&xOffset,&yOffset,&xRes,&yRes,keepAspectRatio,fullScreen))
+ if (OKAY != CreateGLScreen(titleStr,&xOffset,&yOffset,&xRes,&yRes,TRUE,g_Config.fullScreen))
return 1;
// Initialize audio system
@@ -409,7 +533,7 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL
return 1;
// Hide mouse if fullscreen
- Inputs->GetInputSystem()->SetMouseVisibility(!fullScreen);
+ Inputs->GetInputSystem()->SetMouseVisibility(!g_Config.fullScreen);
// Attach the inputs to the emulator
Model3->AttachInputs(Inputs);
@@ -417,7 +541,7 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL
// Initialize the renderer
if (OKAY != Render2D->Init(xOffset, yOffset, xRes, yRes))
goto QuitError;
- if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes, vsFile, fsFile))
+ if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes))
goto QuitError;
Model3->AttachRenderers(Render2D,Render3D);
@@ -521,7 +645,7 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL
// Dump input states
Inputs->DumpState(Model3->GetGameInfo());
}
- else if (Inputs->uiToggleCursor->Pressed() && fullScreen)
+ else if (Inputs->uiToggleCursor->Pressed() && g_Config.fullScreen)
{
// Toggle cursor in full screen mode
showCursor = !showCursor;
@@ -536,8 +660,8 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL
else if (Inputs->uiToggleFrLimit->Pressed())
{
// Toggle frame limiting
- noThrottle = !noThrottle;
- printf("Frame limiting: %s\n", noThrottle?"Off":"On");
+ g_Config.throttle = !g_Config.throttle;
+ printf("Frame limiting: %s\n", g_Config.throttle?"On":"Off");
}
#ifdef SUPERMODEL_DEBUGGER
else if (Inputs->uiEnterDebugger->Pressed())
@@ -554,7 +678,7 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL
currentTicks = currentFPSTicks;
// FPS
- if (showFPS)
+ if (g_Config.showFPS)
{
++fpsFramesElapsed;
if((currentFPSTicks-prevFPSTicks) >= 1000) // update FPS every 1 second (each tick is 1 ms)
@@ -567,7 +691,7 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL
}
// Frame limiting/paused
- if (paused || !noThrottle)
+ if (paused || g_Config.throttle)
{
++framesElapsed;
targetTicks = startTicks + (unsigned) ((float)framesElapsed * 1000.0f/60.0f);
@@ -643,204 +767,6 @@ QuitError:
}
-/******************************************************************************
- Error and Debug Logging
-******************************************************************************/
-
-static CLogger *s_logger = NULL;
-
-/*
- * Returns the current logger.
- */
-CLogger *GetLogger()
-{
- return s_logger;
-}
-
-/*
- * Sets the current logger.
- */
-void SetLogger(CLogger *logger)
-{
- s_logger = logger;
-}
-
-/*
- * DebugLog(fmt, ...):
- *
- * Logs a debug message with the logger.
- *
- * Parameters:
- * fmt Format string (same as printf()).
- * ... Variable number of arguments as required by format string.
- */
-void DebugLog(const char *fmt, ...)
-{
- if (s_logger == NULL)
- return;
- va_list vl;
- va_start(vl, fmt);
- s_logger->DebugLog(fmt, vl);
- va_end(vl);
-}
-
-/*
- * InfoLog(fmt, ...);
- *
- * Logs an info message with the logger.
- *
- * Parameters:
- * fmt Format string (same as printf()).
- * ... Variable number of arguments as required by format string.
- */
-void InfoLog(const char *fmt, ...)
-{
- if (s_logger == NULL)
- return;
- va_list vl;
- va_start(vl, fmt);
- s_logger->InfoLog(fmt, vl);
- va_end(vl);
-}
-
-/*
- * ErrorLog(fmt, ...):
- *
- * Logs an error message with the logger.
- *
- * Parameters:
- * fmt Format string (same as printf()).
- * ... Variable number of arguments as required by format string.
- *
- * Returns:
- * Always returns FAIL.
- */
-BOOL ErrorLog(const char *fmt, ...)
-{
- if (s_logger == NULL)
- return FAIL;
- va_list vl;
- va_start(vl, fmt);
- s_logger->ErrorLog(fmt, vl);
- va_end(vl);
- return FAIL;
-}
-
-#define DEBUG_LOG_FILE "debug.log"
-#define ERROR_LOG_FILE "error.log"
-
-/*
- * Default logger that logs to debug and error log files.
- */
-class CFileLogger : public CLogger
-{
-private:
- const char *m_debugLogFile;
- const char *m_errorLogFile;
-
-public:
- CFileLogger(const char *debugLogFile, const char *errorLogFile) :
- m_debugLogFile(debugLogFile), m_errorLogFile(errorLogFile)
- {
- //
- }
-
- /*
- * DebugLog(fmt, ...):
- *
- * Prints to debug log. The file is opened and closed each time so that its
- * contents are preserved even if the program crashes.
- */
- void DebugLog(const char *fmt, va_list vl)
- {
-#ifdef DEBUG
- char string[1024];
- FILE *fp;
-
- fp = fopen(m_debugLogFile, "ab");
- if (NULL != fp)
- {
- vsprintf(string, fmt, vl);
- fprintf(fp, string);
- fclose(fp);
- }
-#endif // DEBUG
- }
-
- /*
- * InfoLog(fmt, ...);
- *
- * Prints information to the error log file but does not print to stderr. This
- * is useful for logging non-error information.
- */
- void InfoLog(const char *fmt, va_list vl)
- {
- char string[4096];
- FILE *fp;
-
- vsprintf(string, fmt, vl);
-
- fp = fopen(m_errorLogFile, "ab");
- if (NULL != fp)
- {
- fprintf(fp, "%s\n", string);
- fclose(fp);
- }
-
- CLogger::DebugLog("Info: ");
- CLogger::DebugLog(string);
- CLogger::DebugLog("\n");
- }
-
- /*
- * ErrorLog(fmt, ...):
- *
- * Prints an error to stderr and the error log file.
- */
- void ErrorLog(const char *fmt, va_list vl)
- {
- char string[4096];
- FILE *fp;
-
- vsprintf(string, fmt, vl);
- fprintf(stderr, "Error: %s\n", string);
-
- fp = fopen(m_errorLogFile, "ab");
- if (NULL != fp)
- {
- fprintf(fp, "%s\n", string);
- fclose(fp);
- }
-
- CLogger::DebugLog("Error: ");
- CLogger::DebugLog(string);
- CLogger::DebugLog("\n");
- }
-
- void ClearLogs()
- {
-#ifdef DEBUG
- ClearLog(DEBUG_LOG_FILE, "Supermodel v"SUPERMODEL_VERSION" Debug Log");
-#endif // DEBUG
- ClearLog(ERROR_LOG_FILE, "Supermodel v"SUPERMODEL_VERSION" Error Log");
- }
-
- // Clear log file
- void ClearLog(const char *file, const char *title)
- {
- FILE *fp = fopen(file, "w");
- if (NULL != fp)
- {
- unsigned i;
- fprintf(fp, "%s\n", title);
- for (i = 0; i < strlen(title); i++)
- fputc('-', fp);
- fprintf(fp, "\n\n");
- fclose(fp);
- }
- }
-};
-
/******************************************************************************
Diagnostic Commands
******************************************************************************/
@@ -988,10 +914,12 @@ static void Help(void)
puts("");
puts("General Options:");
puts(" -?, -h Print this help text");
- puts(" -print-games List supported games");
+ puts(" -print-games List supported games and quit");
puts("");
puts("Emulation Options:");
- puts(" -ppc-frequency= Set PowerPC frequency in MHz [Default: 25]");
+ puts(" -ppc-frequency= Set PowerPC frequency in MHz [Default: 40]");
+ puts(" -no-scsp Disable Sega Custom Sound Processor (sound effects)");
+ puts(" -no-dsb Disable Digital Sound Board (MPEG music)");
puts(" -multi-threaded Enable multi-threading");
#ifdef SUPERMODEL_DEBUGGER
puts(" -disable-debugger Completely disable debugger functionality");
@@ -1007,6 +935,10 @@ static void Help(void)
puts(" -vert-shader= Load 3D vertex shader from external file");
puts(" -frag-shader= Load 3D fragment shader from external file");
#endif
+ puts("");
+ puts("Audio Options:");
+ puts(" -sound-volume= Set volume of sound effects in % [Default: 100]");
+ puts(" -music-volume= Set volume of MPEG music in % [Default: 100]");
puts("");
puts("Input Options:");
puts(" -input-system= Set input system [Default: SDL]");
@@ -1017,7 +949,7 @@ static void Help(void)
#ifdef DEBUG
puts(" -dis=[,n] Disassemble PowerPC code from CROM");
#endif
- puts(" -print-gl-info Print extensive OpenGL information\n");
+ puts(" -print-gl-info Print extensive OpenGL information and quit\n");
}
// Print game list
@@ -1045,13 +977,14 @@ static void PrintGameList(void)
*/
int main(int argc, char **argv)
{
+ CINIFile CmdLine; // not associated with any files, holds command line options
int i, ret;
- int cmd=0, fileIdx=0, cmdMultiThreaded=0, cmdFullScreen=0, cmdNoThrottle=0, cmdShowFPS=0, cmdPrintInputs=0, cmdConfigInputs=0, cmdPrintGames=0, cmdDis=0, cmdPrintGLInfo=0;
+ int fileIdx=0;
+ bool cmdPrintInputs=false, cmdConfigInputs=false, cmdDis=false;
#ifdef SUPERMODEL_DEBUGGER
- int cmdDisableDebugger = 0, cmdEnterDebugger=0;
+ int cmdEnterDebugger=false;
#endif // SUPERMODEL_DEBUGGER
- unsigned n, xRes=496, yRes=384, ppcFrequency=25000000;
- char *vsFile = NULL, *fsFile = NULL, *inpSysName = NULL;
+ unsigned n;
UINT32 addr;
Title();
@@ -1065,8 +998,21 @@ int main(int argc, char **argv)
CFileLogger Logger(DEBUG_LOG_FILE, ERROR_LOG_FILE);
Logger.ClearLogs();
SetLogger(&Logger);
+
+ // Read global settings from INI file
+ ReadConfigFile(NULL, "Global");
+ //DumpConfig();
- // Parse command line
+ /*
+ * Parse command line.
+ *
+ * Settings are stored in CmdLine so that they can be applied later, after
+ * game-specific settings are read from the configuration file (which
+ * requires the ROM set to be identified and therefore is done later).
+ *
+ * Some commands are processed here directly.
+ */
+ CmdLine.SetDefaultSectionName("Global"); // command line settings are global-level
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i],"-h") || !strcmp(argv[i],"-?"))
@@ -1075,7 +1021,10 @@ int main(int argc, char **argv)
return 0;
}
else if (!strcmp(argv[i],"-print-games"))
- cmd = cmdPrintGames = 1;
+ {
+ PrintGameList();
+ return 0;
+ }
else if (!strncmp(argv[i],"-ppc-frequency",14))
{
int f;
@@ -1083,21 +1032,45 @@ int main(int argc, char **argv)
if (ret != 1)
ErrorLog("-ppc-frequency requires a frequency.");
else
- {
- if ((f<1) || (f>1000)) // limit to 1-1000MHz
- ErrorLog("PowerPC frequency must be between 1 and 1000 MHz. Ignoring.");
- else
- ppcFrequency = f*1000000;
- }
+ CmdLine.Set("Global", "PowerPCFrequency", f);
+ }
+ else if (!strcmp(argv[i],"-multi-threaded"))
+ {
+ n = 1;
+ CmdLine.Set("Global", "MultiThreaded", n);
}
- else if (!strncmp(argv[i],"-multi-threaded", 16))
- cmd = cmdMultiThreaded = 1;
#ifdef SUPERMODEL_DEBUGGER
- else if (!strncmp(argv[i],"-disable-debugger",17))
- cmd = cmdDisableDebugger = 1;
- else if (!strncmp(argv[i],"-enter-debugger",15))
- cmd = cmdEnterDebugger = 1;
+ else if (!strncmp(argv[i],"-disable-debugger"))
+ g_Config.disableDebugger = true;
+ else if (!strcmp(argv[i],"-enter-debugger"))
+ cmdEnterDebugger = true;
#endif // SUPERMODEL_DEBUGGER
+ else if (!strncmp(argv[i],"-sound-vol",10))
+ {
+ ret = sscanf(&argv[i][10],"=%d",&n);
+ if (ret != 1)
+ ErrorLog("-sound-vol requires a volume setting.");
+ else
+ CmdLine.Set("Global", "SoundVolume", n);
+ }
+ else if (!strncmp(argv[i],"-music-vol",10))
+ {
+ ret = sscanf(&argv[i][10],"=%d",&n);
+ if (ret != 1)
+ ErrorLog("-music-vol requires a volume setting.");
+ else
+ CmdLine.Set("Global", "MusicVolume", n);
+ }
+ else if (!strcmp(argv[i], "-no-scsp"))
+ {
+ n = 0;
+ CmdLine.Set("Global", "EmulateSCSP", n);
+ }
+ else if (!strcmp(argv[i], "-no-dsb"))
+ {
+ n = 0;
+ CmdLine.Set("Global", "EmulateDSB", n);
+ }
else if (!strncmp(argv[i],"-res",4))
{
unsigned x, y;
@@ -1107,56 +1080,68 @@ int main(int argc, char **argv)
ErrorLog("-res requires both a width and a height.");
else
{
- xRes = x;
- yRes = y;
+ CmdLine.Set("Global", "XResolution", x);
+ CmdLine.Set("Global", "YResolution", y);
}
}
else if (!strcmp(argv[i],"-fullscreen"))
- cmd = cmdFullScreen = 1;
+ {
+ n = 1;
+ CmdLine.Set("Global", "FullScreen", n);
+ }
else if (!strcmp(argv[i],"-no-throttle"))
- cmd = cmdNoThrottle = 1;
+ {
+ n = 0;
+ CmdLine.Set("Global", "Throttle", n);
+ }
else if (!strcmp(argv[i],"-show-fps"))
- cmd = cmdShowFPS = 1;
+ {
+ n = 1;
+ CmdLine.Set("Global", "ShowFrameRate", n);
+ }
else if (!strncmp(argv[i],"-vert-shader=",13))
{
if (argv[i][13] == '\0')
ErrorLog("-vert-shader requires a file path.");
else
- vsFile = &argv[i][13];
+ CmdLine.Set("Global", "VertexShader", &argv[i][13]);
}
else if (!strncmp(argv[i],"-frag-shader=",13))
{
if (argv[i][13] == '\0')
ErrorLog("-frag-shader requires a file path.");
else
- fsFile = &argv[i][13];
+ CmdLine.Set("Global", "FragmentShader", &argv[i][13]);
}
else if (!strncmp(argv[i],"-input-system=", 14))
{
if (argv[i][14] == '\0')
ErrorLog("-input-system requires an input system name.");
else
- inpSysName = &argv[i][14];
+ CmdLine.Set("Global", "InputSystem", &argv[i][14]);
}
else if (!strcmp(argv[i],"-print-inputs"))
- cmd = cmdPrintInputs = 1;
+ cmdPrintInputs = true;
else if (!strcmp(argv[i],"-config-inputs"))
- cmd = cmdConfigInputs = 1;
+ cmdConfigInputs = true;
else if (!strncmp(argv[i],"-dis",4))
{
ret = sscanf(&argv[i][4],"=%X,%X",&addr,&n);
if (ret == 1)
{
n = 16;
- cmd = cmdDis = 1;
+ cmdDis = true;
}
else if (ret == 2)
- cmd = cmdDis = 1;
+ cmdDis = true;
else
ErrorLog("-dis requires address and, optionally, number of instructions.");
}
else if (!strcmp(argv[i],"-print-gl-info"))
- cmd = cmdPrintGLInfo = 1;
+ {
+ PrintGLInfo(FALSE);
+ return 0;
+ }
else if (argv[i][0] == '-')
ErrorLog("Ignoring invalid option: %s.", argv[i]);
else
@@ -1175,6 +1160,7 @@ int main(int argc, char **argv)
return 1;
}
+ // Create input system (default is SDL) and debugger
CInputSystem *InputSystem = NULL;
CInputs *Inputs = NULL;
int exitCode = 0;
@@ -1183,27 +1169,33 @@ int main(int argc, char **argv)
Debugger::CSupermodelDebugger *Debugger = NULL;
#endif // SUPERMODEL_DEBUGGER
- // Create input system (default is SDL)
- if (inpSysName == NULL || stricmp(inpSysName, "sdl") == 0)
+ if (stricmp(g_Config.GetInputSystem(), "sdl") == 0)
InputSystem = new CSDLInputSystem();
#ifdef SUPERMODEL_WIN32
- else if (stricmp(inpSysName, "dinput") == 0)
+ else if (stricmp(g_Config.GetInputSystem(), "dinput") == 0)
InputSystem = new CDirectInputSystem(false, false, false);
- else if (stricmp(inpSysName, "xinput") == 0)
+ else if (stricmp(g_Config.GetInputSystem(), "xinput") == 0)
InputSystem = new CDirectInputSystem(false, true, false);
- else if (stricmp(inpSysName, "rawinput") == 0)
+ else if (stricmp(g_Config.GetInputSystem(), "rawinput") == 0)
InputSystem = new CDirectInputSystem(true, false, false);
#endif // SUPERMODEL_WIN32
else
{
- ErrorLog("Unknown input system: '%s'.\n", inpSysName);
+ ErrorLog("Unknown input system: '%s'.\n", g_Config.GetInputSystem());
exitCode = 1;
goto Exit;
}
// Create inputs from input system (configuring them if required)
- Inputs = CreateInputs(InputSystem, cmdConfigInputs);
- if (Inputs == NULL)
+ Inputs = new CInputs(InputSystem);
+ if (!Inputs->Initialize())
+ {
+ ErrorLog("Unable to initalize inputs.\n");
+ exitCode = 1;
+ goto Exit;
+ }
+
+ if (ConfigureInputs(Inputs, cmdConfigInputs))
{
exitCode = 1;
goto Exit;
@@ -1215,22 +1207,7 @@ int main(int argc, char **argv)
InputSystem->PrintSettings();
}
- // Process commands that don't require ROM set
- if (cmd)
- {
- if (cmdPrintGames)
- {
- PrintGameList();
- goto Exit;
- }
-
- if (cmdPrintGLInfo)
- {
- PrintGLInfo(FALSE);
- goto Exit;
- }
- }
-
+ // From this point onwards, a ROM set is needed
if (fileIdx == 0)
{
ErrorLog("No ROM set specified.");
@@ -1238,22 +1215,18 @@ int main(int argc, char **argv)
goto Exit;
}
- // Process commands that require ROMs
- if (cmd)
- {
- if (cmdDis)
- {
- if (OKAY != DisassembleCROM(argv[fileIdx], addr, n))
- exitCode = 1;
- goto Exit;
- }
+ if (cmdDis)
+ {
+ if (OKAY != DisassembleCROM(argv[fileIdx], addr, n))
+ exitCode = 1;
+ goto Exit;
}
#ifdef SUPERMODEL_DEBUGGER
// Create Model3
Model3 = new CModel3();
// Create Supermodel debugger unless debugging is disabled
- if (!cmdDisableDebugger)
+ if (!g_Config.disableDebugger)
{
Debugger = new Debugger::CSupermodelDebugger(Model3, Inputs, &Logger);
// If -enter-debugger option was set force debugger to break straightaway
@@ -1261,13 +1234,13 @@ int main(int argc, char **argv)
Debugger->ForceBreak(true);
}
// Fire up Supermodel with debugger
- exitCode = Supermodel(argv[fileIdx],Model3,Inputs,Debugger,ppcFrequency,cmdMultiThreaded,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile);
+ exitCode = Supermodel(argv[fileIdx],Model3,Inputs,Debugger,&CmdLine);
if (Debugger != NULL)
delete Debugger;
delete Model3;
#else
// Fire up Supermodel
- exitCode = Supermodel(argv[fileIdx],Inputs,ppcFrequency,cmdMultiThreaded,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile);
+ exitCode = Supermodel(argv[fileIdx],Inputs,&CmdLine);
#endif // SUPERMODEL_DEBUGGER
Exit:
diff --git a/Src/OSD/SDL/Types.h b/Src/OSD/SDL/Types.h
index f809a5d..db6c014 100644
--- a/Src/OSD/SDL/Types.h
+++ b/Src/OSD/SDL/Types.h
@@ -25,6 +25,8 @@
* Fundamental data types. This file is used by both C++ and C modules, so it
* must NOT include any C++-specific constructs. Some modules may elect to
* include it directly rather than through Supermodel.h.
+ *
+ * All ports must define this file.
*/
#ifndef INCLUDED_TYPES_H
diff --git a/Src/Sound/MPEG/audio.cpp b/Src/Sound/MPEG/audio.cpp
index 9a79cc9..9bd7f65 100644
--- a/Src/Sound/MPEG/audio.cpp
+++ b/Src/Sound/MPEG/audio.cpp
@@ -119,7 +119,7 @@ int g,snd_eof=0;
if (header.protection_bit==0) getcrc();
- printf("%d Hz, layer %d\n", t_sampling_frequency[header.ID][header.sampling_frequency], header.layer);
+ //printf("%d Hz, layer %d\n", t_sampling_frequency[header.ID][header.sampling_frequency], header.layer);
if (setup_audio(&header)!=0) {
warn("Cannot set up audio. Exiting\n");
diff --git a/Src/Sound/MPEG/misc2.cpp b/Src/Sound/MPEG/misc2.cpp
index fd5b7e5..c1359ff 100644
--- a/Src/Sound/MPEG/misc2.cpp
+++ b/Src/Sound/MPEG/misc2.cpp
@@ -523,7 +523,7 @@ static int min_cycles = 99999999;
if (cnt2-cnt1 < min_cycles) {
min_cycles = cnt2-cnt1;
- printf("%d cycles\n", min_cycles);
+ //printf("%d cycles\n", min_cycles);
}
#endif
@@ -546,7 +546,7 @@ static int min_cycles = 99999999;
if (cnt2-cnt1 < min_cycles) {
min_cycles = cnt2-cnt1;
- printf("%d cycles, sfb %d\n", min_cycles, sfb);
+ //printf("%d cycles, sfb %d\n", min_cycles, sfb);
}
#endif
diff --git a/Src/Sound/MPEG/transform.cpp b/Src/Sound/MPEG/transform.cpp
index 03dfbb3..28a2c90 100644
--- a/Src/Sound/MPEG/transform.cpp
+++ b/Src/Sound/MPEG/transform.cpp
@@ -1503,7 +1503,7 @@ static int min_cycles = 99999999;
if (cnt2-cnt1 < min_cycles) {
min_cycles = cnt2-cnt1;
- printf("%d, %d cycles, %d\n", cnt3-cnt1, min_cycles, start);
+ //printf("%d, %d cycles, %d\n", cnt3-cnt1, min_cycles, start);
}
#endif
}
diff --git a/Src/Supermodel.h b/Src/Supermodel.h
index 3b2c94d..8ad0ecc 100644
--- a/Src/Supermodel.h
+++ b/Src/Supermodel.h
@@ -28,6 +28,12 @@
#ifndef INCLUDED_SUPERMODEL_H
#define INCLUDED_SUPERMODEL_H
+// Used throughout Supermodel
+#include
+#include
+#include
+
+
/******************************************************************************
Program-Wide Definitions
******************************************************************************/
@@ -38,8 +44,11 @@
/******************************************************************************
OS-Dependent (OSD) Items
- Everything here must be provided by the OSD layer. Include files should be
- located in the OSD directories for each port.
+ Everything here must be provided by the OSD layer. The following include files
+ must be located in the OSD directories for each port:
+
+ Types.h Defines fundamental data types.
+ OSDConfig.h COSDConfig class (OSD-specific configuration settings).
******************************************************************************/
// stricmp() is non-standard, apparently...
@@ -49,6 +58,7 @@
#define stricmp strcasecmp
#endif
+
/*
* Fundamental Data Types:
*
@@ -68,58 +78,21 @@
* specific stuff. Some modules may choose to include it directly rather than
* use Supermodel.h, so it must exist.
*/
-#include "Types.h"
-
-// OSD Interfaces
-#include "Thread.h"
-#include "Audio.h"
+#include "Types.h" // located in OSD// directory
/*
- * Error and Debug Logging
+ * OSD Header Files
*/
-/*
- * DebugLog(fmt, ...):
- *
- * Prints debugging information. The OSD layer may choose to print this to a
- * log file, the screen, neither, or both. Newlines and other formatting codes
- * must be explicitly included.
- *
- * Parameters:
- * fmt A format string (the same as printf()).
- * ... Variable number of arguments, as required by format string.
- */
-extern void DebugLog(const char *fmt, ...);
+// Error logging interface
+#include "OSD/Logger.h"
-/*
- * ErrorLog(fmt, ...):
- *
- * Prints error information. Errors need not require program termination and
- * may simply be informative warnings to the user. Newlines should not be
- * included in the format string -- they are automatically added at the end of
- * a line.
- *
- * Parameters:
- * fmt A format string (the same as printf()).
- * ... Variable number of arguments, as required by format string.
- *
- * Returns:
- * Must always return FAIL.
- */
-extern BOOL ErrorLog(const char *fmt, ...);
+// OSD configuration
+#include "OSDConfig.h" // located in OSD// directory
-/*
- * InfoLog(fmt, ...);
- *
- * Prints information to the error log file but does not print to stderr. This
- * is useful for logging non-error information. Newlines are automatically
- * appended.
- *
- * Parameters:
- * fmt Format string (same as printf()).
- * ... Variable number of arguments as required by format string.
- */
-extern void InfoLog(const char *fmt, ...);
+// OSD Interfaces
+#include "OSD/Thread.h"
+#include "OSD/Audio.h"
/******************************************************************************
@@ -128,10 +101,7 @@ extern void InfoLog(const char *fmt, ...);
All primary header files for modules used throughout Supermodel are included
here, except for external packages and APIs.
******************************************************************************/
-
-#include
-#include
-#include
+
#include "Games.h"
#include "ROMLoad.h"
#include "INIFile.h"
@@ -168,6 +138,7 @@ extern void InfoLog(const char *fmt, ...);
#include "Model3/SoundBoard.h"
#include "Model3/DSB.h"
#include "Model3/Model3.h"
+#include "Config.h"
/******************************************************************************