From d318efe58c3a960b8341ff9ca29d03717a194419 Mon Sep 17 00:00:00 2001 From: Bart Trzynadlowski Date: Fri, 19 Aug 2011 20:43:07 +0000 Subject: [PATCH] - Program settings are now managed by the CConfig class. - Moved Logger.h to OSD/, cleaned up comments. - Command line and config file parsing have been rewritten. - Began replacing C standard library headers with C++ versions (eg. stdio.h -> cstdio). I think I got most of them, but not all. --- Makefiles/Makefile.SDL.Win32.MSVC | 2 +- Src/Config.cpp | 31 ++ Src/Config.h | 71 ++++ Src/Graphics/Render3D.cpp | 4 +- Src/Graphics/Render3D.h | 31 +- Src/INIFile.cpp | 6 +- Src/INIFile.h | 3 +- Src/Logger.h | 45 --- Src/Model3/DSB.cpp | 42 ++- Src/Model3/DSB.h | 65 ++++ Src/Model3/Model3.cpp | 28 +- Src/Model3/Model3.h | 48 ++- Src/Model3/SoundBoard.cpp | 17 +- Src/Model3/SoundBoard.h | 17 + Src/OSD/Logger.h | 286 +++++++++++++++ Src/OSD/SDL/Main.cpp | 589 ++++++++++++++---------------- Src/OSD/SDL/Types.h | 2 + Src/Sound/MPEG/audio.cpp | 2 +- Src/Sound/MPEG/misc2.cpp | 4 +- Src/Sound/MPEG/transform.cpp | 2 +- Src/Supermodel.h | 75 ++-- 21 files changed, 908 insertions(+), 462 deletions(-) create mode 100644 Src/Config.cpp create mode 100644 Src/Config.h delete mode 100644 Src/Logger.h create mode 100644 Src/OSD/Logger.h 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" /******************************************************************************