2011-08-03 04:29:33 +00:00
/**
* * Supermodel
* * A Sega Model 3 Arcade Emulator .
2020-07-27 10:28:48 +00:00
* * Copyright 2011 - 2020 Bart Trzynadlowski , Nik Henson , Ian Curtis ,
2019-02-19 09:24:31 +00:00
* * Harry Tuttle , and Spindizzi
2011-08-03 04:29:33 +00:00
* *
* * This file is part of Supermodel .
* *
* * Supermodel is free software : you can redistribute it and / or modify it under
2020-07-27 10:28:48 +00:00
* * the terms of the GNU General Public License as published by the Free
2011-08-03 04:29:33 +00:00
* * Software Foundation , either version 3 of the License , or ( at your option )
* * any later version .
* *
* * Supermodel is distributed in the hope that it will be useful , but WITHOUT
* * ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* * FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* * more details .
* *
* * You should have received a copy of the GNU General Public License along
* * with Supermodel . If not , see < http : //www.gnu.org/licenses/>.
* */
2020-07-27 10:28:48 +00:00
2011-08-03 04:29:33 +00:00
/*
* Main . cpp
2020-07-27 10:28:48 +00:00
*
2011-08-03 04:29:33 +00:00
* Main program driver for the SDL port .
*
2012-02-09 17:12:13 +00:00
* To Do Before Next Release
* - - - - - - - - - - - - - - - - - - - - - - - - -
2017-03-27 03:19:15 +00:00
* - Thoroughly test config system ( do overrides work as expected ? XInput
* force settings ? )
* - Remove all occurrences of " using namespace std " from Nik ' s code .
* - Standardize variable naming ( recently introduced vars_like_this should be
* converted back to varsLikeThis ) .
* - Update save state file revision ( strings > 1024 chars are now supported ) .
2020-07-27 10:28:48 +00:00
* - Fix BlockFile . cpp to use fstream !
2016-04-29 23:57:15 +00:00
* - Check to make sure save states use explicitly - sized types for 32 / 64 - bit
* compatibility ( i . e . , size_t , int , etc . not allowed ) .
* - Make sure quitting while paused works .
2020-07-27 10:28:48 +00:00
* - Add UI keys for balance setting ?
2012-02-09 17:12:13 +00:00
* - 5.1 audio support ?
*
2011-08-03 04:29:33 +00:00
* Compile - Time Options
* - - - - - - - - - - - - - - - - - - - -
* - SUPERMODEL_WIN32 : Define this if compiling on Windows .
* - SUPERMODEL_OSX : Define this if compiling on Mac OS X .
2011-09-22 17:28:49 +00:00
* - SUPERMODEL_DEBUGGER : Enable the debugger .
* - DEBUG : Debug mode ( use with caution , produces large logs of game behavior )
2011-08-03 04:29:33 +00:00
*/
2018-01-07 14:07:59 +00:00
2011-08-03 04:29:33 +00:00
# include <new>
2011-08-19 20:43:07 +00:00
# include <cmath>
# include <cstdio>
# include <cstring>
# include <cstdarg>
2016-04-27 04:09:50 +00:00
# include <memory>
# include <vector>
2017-03-27 22:01:31 +00:00
# include <algorithm>
2020-07-31 19:18:51 +00:00
# include <GL/glew.h>
2011-08-03 04:29:33 +00:00
# include "Supermodel.h"
2016-04-27 04:09:50 +00:00
# include "Util/Format.h"
2017-03-27 03:19:15 +00:00
# include "Util/NewConfig.h"
# include "Util/ConfigBuilders.h"
# include "GameLoader.h"
2011-08-03 04:29:33 +00:00
# include "SDLInputSystem.h"
# ifdef SUPERMODEL_WIN32
# include "DirectInputSystem.h"
2012-07-15 21:04:46 +00:00
# include "WinOutputs.h"
2011-08-03 04:29:33 +00:00
# endif
2020-07-27 10:28:48 +00:00
# include "SDLIncludes.h"
2011-08-03 04:29:33 +00:00
2017-03-27 03:19:15 +00:00
# include <iostream>
2011-08-19 20:43:07 +00:00
// Log file names
2016-04-24 01:40:16 +00:00
# define DEBUG_LOG_FILE "debug.log"
# define ERROR_LOG_FILE "error.log"
2011-08-19 20:43:07 +00:00
2011-08-03 04:29:33 +00:00
2017-03-27 03:19:15 +00:00
/******************************************************************************
Global Run - time Config
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Util : : Config : : Node s_runtime_config ( " Global " ) ;
2011-08-03 04:29:33 +00:00
/******************************************************************************
Display Management
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-04-19 08:34:58 +00:00
static SDL_Window * s_window = nullptr ;
2011-08-03 04:29:33 +00:00
/*
2011-08-19 20:43:07 +00:00
* Position and size of rectangular region within OpenGL display to render to .
2017-03-27 03:19:15 +00:00
* Unlike the config tree , these end up containing the actual resolution ( and
* computed offsets within the viewport ) that will be rendered based on what
* was obtained from SDL .
2011-08-03 04:29:33 +00:00
*/
2016-04-24 01:40:16 +00:00
static unsigned xOffset , yOffset ; // offset of renderer output within OpenGL viewport
static unsigned xRes , yRes ; // renderer output resolution (can be smaller than GL viewport)
static unsigned totalXRes , totalYRes ; // total resolution (the whole GL viewport)
2011-08-03 04:29:33 +00:00
2012-07-15 21:04:46 +00:00
static bool SetGLGeometry ( unsigned * xOffsetPtr , unsigned * yOffsetPtr , unsigned * xResPtr , unsigned * yResPtr , unsigned * totalXResPtr , unsigned * totalYResPtr , bool keepAspectRatio )
2011-08-03 04:29:33 +00:00
{
2016-04-24 01:40:16 +00:00
// What resolution did we actually get?
2020-04-19 08:34:58 +00:00
int actualWidth ;
int actualHeight ;
SDL_GetWindowSize ( s_window , & actualWidth , & actualHeight ) ;
* totalXResPtr = actualWidth ;
* totalYResPtr = actualHeight ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// If required, fix the aspect ratio of the resolution that the user passed to match Model 3 ratio
float xRes = float ( * xResPtr ) ;
float yRes = float ( * yResPtr ) ;
if ( keepAspectRatio )
{
float model3Ratio = 496.0f / 384.0f ;
if ( yRes < ( xRes / model3Ratio ) )
xRes = yRes * model3Ratio ;
if ( xRes < ( yRes * model3Ratio ) )
2018-05-03 03:46:44 +00:00
yRes = xRes / model3Ratio ;
2016-04-24 01:40:16 +00:00
}
2020-07-27 10:28:48 +00:00
// Center the visible area
2016-04-24 01:40:16 +00:00
* xOffsetPtr = ( * xResPtr - ( unsigned ) xRes ) / 2 ;
* yOffsetPtr = ( * yResPtr - ( unsigned ) yRes ) / 2 ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// If the desired resolution is smaller than what we got, re-center again
2020-04-19 08:34:58 +00:00
if ( int ( * xResPtr ) < actualWidth )
* xOffsetPtr + = ( actualWidth - * xResPtr ) / 2 ;
if ( int ( * yResPtr ) < actualHeight )
* yOffsetPtr + = ( actualHeight - * yResPtr ) / 2 ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// OpenGL initialization
glViewport ( 0 , 0 , * xResPtr , * yResPtr ) ;
glClearColor ( 0.0 , 0.0 , 0.0 , 0.0 ) ;
glClearDepth ( 1.0 ) ;
glDepthFunc ( GL_LESS ) ;
glEnable ( GL_DEPTH_TEST ) ;
glDisable ( GL_CULL_FACE ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Clear both buffers to ensure a black border
for ( int i = 0 ; i < 2 ; i + + )
{
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
2020-04-19 08:34:58 +00:00
SDL_GL_SwapWindow ( s_window ) ;
2016-04-24 01:40:16 +00:00
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Write back resolution parameters
* xResPtr = ( unsigned ) xRes ;
* yResPtr = ( unsigned ) yRes ;
2017-03-19 01:33:45 +00:00
UINT32 correction = ( UINT32 ) ( ( ( yRes / 384.f ) * 2 ) + 0.5f ) ;
glEnable ( GL_SCISSOR_TEST ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Scissor box (to clip visible area)
2017-03-27 03:19:15 +00:00
if ( s_runtime_config [ " WideScreen " ] . ValueAsDefault < bool > ( false ) )
2016-04-24 01:40:16 +00:00
{
2017-03-22 20:38:44 +00:00
glScissor ( 0 , correction , * totalXResPtr , * totalYResPtr - ( correction * 2 ) ) ;
2016-04-24 01:40:16 +00:00
}
2020-04-19 08:34:58 +00:00
else
{
2017-03-22 20:38:44 +00:00
glScissor ( * xOffsetPtr + correction , * yOffsetPtr + correction , * xResPtr - ( correction * 2 ) , * yResPtr - ( correction * 2 ) ) ;
2017-03-19 01:33:45 +00:00
}
2016-04-24 01:40:16 +00:00
return OKAY ;
2012-07-15 21:04:46 +00:00
}
/*
* CreateGLScreen ( ) :
*
* Creates an OpenGL display surface of the requested size . xOffset and yOffset
* are used to return a display surface offset ( for OpenGL viewport commands )
2020-07-27 10:28:48 +00:00
* because the actual drawing area may need to be adjusted to preserve the
2012-07-15 21:04:46 +00:00
* Model 3 aspect ratio . The new resolution will be passed back as well - - both
* the adjusted viewable area resolution and the total resolution .
*
* NOTE : keepAspectRatio should always be true . It has not yet been tested with
* the wide screen hack .
*/
2020-04-19 08:34:58 +00:00
static bool CreateGLScreen ( const std : : string & caption , bool focusWindow , unsigned * xOffsetPtr , unsigned * yOffsetPtr , unsigned * xResPtr , unsigned * yResPtr , unsigned * totalXResPtr , unsigned * totalYResPtr , bool keepAspectRatio , bool fullScreen )
2012-07-15 21:04:46 +00:00
{
2016-04-24 01:40:16 +00:00
GLenum err ;
2020-07-27 10:28:48 +00:00
2020-04-19 08:34:58 +00:00
// Call only once per program session (this is because of issues with
// DirectInput when the window is destroyed and a new one created). Use
// ResizeGLScreen() to change resolutions instead.
if ( s_window ! = nullptr )
{
return ErrorLog ( " Internal error: CreateGLScreen() called more than once " ) ;
}
2016-04-24 01:40:16 +00:00
// Initialize video subsystem
2020-04-19 08:34:58 +00:00
if ( SDL_Init ( SDL_INIT_VIDEO ) ! = 0 )
2016-04-24 01:40:16 +00:00
return ErrorLog ( " Unable to initialize SDL video subsystem: %s \n " , SDL_GetError ( ) ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Important GL attributes
SDL_GL_SetAttribute ( SDL_GL_RED_SIZE , 8 ) ;
SDL_GL_SetAttribute ( SDL_GL_GREEN_SIZE , 8 ) ;
SDL_GL_SetAttribute ( SDL_GL_BLUE_SIZE , 8 ) ;
SDL_GL_SetAttribute ( SDL_GL_DEPTH_SIZE , 24 ) ;
2016-05-27 19:30:40 +00:00
SDL_GL_SetAttribute ( SDL_GL_STENCIL_SIZE , 8 ) ;
2016-04-24 01:40:16 +00:00
SDL_GL_SetAttribute ( SDL_GL_DOUBLEBUFFER , 1 ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Set video mode
2020-04-19 08:34:58 +00:00
s_window = SDL_CreateWindow ( caption . c_str ( ) , SDL_WINDOWPOS_CENTERED , SDL_WINDOWPOS_CENTERED , * xResPtr , * yResPtr , SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | ( fullScreen ? SDL_WINDOW_FULLSCREEN : 0 ) ) ;
if ( nullptr = = s_window )
2016-04-24 01:40:16 +00:00
{
ErrorLog ( " Unable to create an OpenGL display: %s \n " , SDL_GetError ( ) ) ;
return FAIL ;
}
2020-07-27 10:28:48 +00:00
2020-04-19 08:34:58 +00:00
if ( focusWindow )
{
SDL_RaiseWindow ( s_window ) ;
}
2020-07-27 10:28:48 +00:00
2020-04-19 08:34:58 +00:00
// Create OpenGL context
SDL_GLContext context = SDL_GL_CreateContext ( s_window ) ;
if ( nullptr = = context )
{
ErrorLog ( " Unable to create OpenGL context: %s \n " , SDL_GetError ( ) ) ;
return FAIL ;
}
// Set vsync
SDL_GL_SetSwapInterval ( s_runtime_config [ " VSync " ] . ValueAsDefault < bool > ( false ) ? 1 : 0 ) ;
2020-07-27 10:28:48 +00:00
2020-04-19 08:34:58 +00:00
// Set the context as the current window context
SDL_GL_MakeCurrent ( s_window , context ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Initialize GLEW, allowing us to use features beyond OpenGL 1.2
err = glewInit ( ) ;
if ( GLEW_OK ! = err )
{
ErrorLog ( " OpenGL initialization failed: %s \n " , glewGetErrorString ( err ) ) ;
return FAIL ;
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
return SetGLGeometry ( xOffsetPtr , yOffsetPtr , xResPtr , yResPtr , totalXResPtr , totalYResPtr , keepAspectRatio ) ;
2012-07-15 21:04:46 +00:00
}
2020-04-19 08:34:58 +00:00
static void DestroyGLScreen ( )
{
if ( s_window ! = nullptr )
{
SDL_GL_DeleteContext ( SDL_GL_GetCurrentContext ( ) ) ;
SDL_DestroyWindow ( s_window ) ;
}
}
2012-07-15 21:04:46 +00:00
static bool ResizeGLScreen ( unsigned * xOffsetPtr , unsigned * yOffsetPtr , unsigned * xResPtr , unsigned * yResPtr , unsigned * totalXResPtr , unsigned * totalYResPtr , bool keepAspectRatio , bool fullScreen )
{
2020-04-19 08:34:58 +00:00
// Set full screen mode
if ( SDL_SetWindowFullscreen ( s_window , fullScreen ? SDL_WINDOW_FULLSCREEN : 0 ) < 0 )
2016-04-24 01:40:16 +00:00
{
2020-04-19 08:34:58 +00:00
ErrorLog ( " Unable to enter %s mode: %s \n " , fullScreen ? " fullscreen " : " windowed " , SDL_GetError ( ) ) ;
2016-04-24 01:40:16 +00:00
return FAIL ;
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
return SetGLGeometry ( xOffsetPtr , yOffsetPtr , xResPtr , yResPtr , totalXResPtr , totalYResPtr , keepAspectRatio ) ;
2011-08-03 04:29:33 +00:00
}
2011-08-20 16:53:52 +00:00
/*
* PrintGLInfo ( ) :
*
* Queries and prints OpenGL information . A full list of extensions can
* optionally be printed .
*/
static void PrintGLInfo ( bool createScreen , bool infoLog , bool printExtensions )
{
2020-07-27 10:28:48 +00:00
unsigned xOffset , yOffset , xRes = 496 , yRes = 384 , totalXRes , totalYRes ;
2016-04-24 01:40:16 +00:00
if ( createScreen )
{
2020-04-19 08:34:58 +00:00
if ( OKAY ! = CreateGLScreen ( " Supermodel - Querying OpenGL Information... " , false , & xOffset , & yOffset , & xRes , & yRes , & totalXRes , & totalYRes , false , false ) )
2016-04-24 01:40:16 +00:00
{
ErrorLog ( " Unable to query OpenGL. \n " ) ;
return ;
}
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
GLint value ;
if ( infoLog ) InfoLog ( " OpenGL information: " ) ;
else puts ( " OpenGL information: \n " ) ;
const GLubyte * str = glGetString ( GL_VENDOR ) ;
if ( infoLog ) InfoLog ( " Vendor : %s " , str ) ;
else printf ( " Vendor : %s \n " , str ) ;
str = glGetString ( GL_RENDERER ) ;
if ( infoLog ) InfoLog ( " Renderer : %s " , str ) ;
else printf ( " Renderer : %s \n " , str ) ;
str = glGetString ( GL_VERSION ) ;
if ( infoLog ) InfoLog ( " Version : %s " , str ) ;
else printf ( " Version : %s \n " , str ) ;
str = glGetString ( GL_SHADING_LANGUAGE_VERSION ) ;
if ( infoLog ) InfoLog ( " Shading Language Version : %s " , str ) ;
else printf ( " Shading Language Version : %s \n " , str ) ;
glGetIntegerv ( GL_MAX_ELEMENTS_VERTICES , & value ) ;
if ( infoLog ) InfoLog ( " Maximum Vertex Array Size: %d vertices " , value ) ;
else printf ( " Maximum Vertex Array Size: %d vertices \n " , value ) ;
glGetIntegerv ( GL_MAX_TEXTURE_SIZE , & value ) ;
if ( infoLog ) InfoLog ( " Maximum Texture Size : %d texels " , value ) ;
else printf ( " Maximum Texture Size : %d texels \n " , value ) ;
glGetIntegerv ( GL_MAX_VERTEX_ATTRIBS , & value ) ;
if ( infoLog ) InfoLog ( " Maximum Vertex Attributes: %d " , value ) ;
else printf ( " Maximum Vertex Attributes: %d \n " , value ) ;
glGetIntegerv ( GL_MAX_VERTEX_UNIFORM_COMPONENTS , & value ) ;
if ( infoLog ) InfoLog ( " Maximum Vertex Uniforms : %d " , value ) ;
else printf ( " Maximum Vertex Uniforms : %d \n " , value ) ;
glGetIntegerv ( GL_MAX_TEXTURE_IMAGE_UNITS , & value ) ;
if ( infoLog ) InfoLog ( " Maximum Texture Img Units: %d " , value ) ;
else printf ( " Maximum Texture Img Units: %d \n " , value ) ;
if ( printExtensions )
{
str = glGetString ( GL_EXTENSIONS ) ;
char * strLocal = ( char * ) malloc ( ( strlen ( ( char * ) str ) + 1 ) * sizeof ( char ) ) ;
if ( NULL = = strLocal )
{
if ( infoLog ) InfoLog ( " Supported Extensions : %s " , str ) ;
else printf ( " Supported Extensions : %s \n " , str ) ;
}
else
{
strcpy ( strLocal , ( char * ) str ) ;
if ( infoLog ) InfoLog ( " Supported Extensions : %s " , ( strLocal = strtok ( strLocal , " \t \n " ) ) ) ;
else printf ( " Supported Extensions : %s \n " , ( strLocal = strtok ( strLocal , " \t \n " ) ) ) ;
while ( ( strLocal = strtok ( NULL , " \t \n " ) ) ! = NULL )
{
if ( infoLog ) InfoLog ( " %s " , strLocal ) ;
else printf ( " %s \n " , strLocal ) ;
}
}
2018-09-13 12:50:34 +00:00
free ( strLocal ) ;
2016-04-24 01:40:16 +00:00
}
if ( infoLog ) InfoLog ( " " ) ;
else printf ( " \n " ) ;
2011-08-20 16:53:52 +00:00
}
2016-04-24 01:40:16 +00:00
# ifdef DEBUG
2016-03-21 04:10:14 +00:00
static void PrintBAT ( unsigned regu , unsigned regl )
{
2016-04-24 01:40:16 +00:00
uint32_t batu = ppc_read_spr ( regu ) ;
uint32_t batl = ppc_read_spr ( regl ) ;
uint32_t bepi = batu > > ( 31 - 14 ) ;
uint32_t bl = ( batu > > ( 31 - 29 ) ) & 0x7ff ;
2016-03-21 04:10:14 +00:00
bool vs = batu & 2 ;
bool vp = batu & 1 ;
2016-04-24 01:40:16 +00:00
uint32_t brpn = batl > > ( 31 - 14 ) ;
uint32_t wimg = ( batl > > ( 31 - 28 ) ) & 0xf ;
uint32_t pp = batl & 3 ;
uint32_t size = ( bl + 1 ) * 128 * 1024 ;
uint32_t ea_base = bepi < < ( 31 - 14 ) ;
uint32_t ea_limit = ea_base + size - 1 ;
uint32_t pa_base = brpn < < ( 31 - 14 ) ;
2020-07-27 10:28:48 +00:00
uint32_t pa_limit = pa_base + size - 1 ;
2016-03-21 04:10:14 +00:00
printf ( " %08X-%08X -> %08X-%08X " , ea_base , ea_limit , pa_base , pa_limit ) ;
printf ( " %c%c%c%c " , ( wimg & 8 ) ? ' W ' : ' - ' , ( wimg & 4 ) ? ' I ' : ' - ' , ( wimg & 2 ) ? ' M ' : ' - ' , ( wimg & 1 ) ? ' G ' : ' - ' ) ;
printf ( " PP= " ) ;
if ( pp = = 0 )
printf ( " NA " ) ;
else if ( pp = = 2 )
printf ( " RW " ) ;
else
printf ( " RO " ) ;
printf ( " Vs=%d Vp=%d " , vs , vp ) ;
}
2016-04-24 01:40:16 +00:00
# endif
2016-03-21 04:10:14 +00:00
2016-04-24 01:40:16 +00:00
# ifdef DEBUG
2016-04-10 03:42:41 +00:00
static void DumpPPCRegisters ( IBus * bus )
2016-03-21 04:10:14 +00:00
{
2016-04-24 01:40:16 +00:00
for ( int i = 0 ; i < 32 ; i + = 4 )
{
printf ( " R%d=%08X \t R%d=%08X \t R%d=%08X \t R%d=%08X \n " ,
i + 0 , ppc_get_gpr ( i + 0 ) ,
i + 1 , ppc_get_gpr ( i + 1 ) ,
i + 2 , ppc_get_gpr ( i + 2 ) ,
i + 3 , ppc_get_gpr ( i + 3 ) ) ;
}
printf ( " PC =%08X \n " , ppc_get_pc ( ) ) ;
printf ( " LR =%08X \n " , ppc_get_lr ( ) ) ;
printf ( " DBAT0U=%08X \t IBAT0U=%08X \n " , ppc_read_spr ( SPR603E_DBAT0U ) , ppc_read_spr ( SPR603E_IBAT0U ) ) ;
printf ( " DBAT0L=%08X \t IBAT0L=%08X \n " , ppc_read_spr ( SPR603E_DBAT0L ) , ppc_read_spr ( SPR603E_IBAT0L ) ) ;
printf ( " DBAT1U=%08X \t IBAT1U=%08X \n " , ppc_read_spr ( SPR603E_DBAT1U ) , ppc_read_spr ( SPR603E_IBAT1U ) ) ;
printf ( " DBAT1L=%08X \t IBAT1L=%08X \n " , ppc_read_spr ( SPR603E_DBAT1L ) , ppc_read_spr ( SPR603E_IBAT1L ) ) ;
printf ( " DBAT2U=%08X \t IBAT2U=%08X \n " , ppc_read_spr ( SPR603E_DBAT2U ) , ppc_read_spr ( SPR603E_IBAT2U ) ) ;
printf ( " DBAT2L=%08X \t IBAT2L=%08X \n " , ppc_read_spr ( SPR603E_DBAT2L ) , ppc_read_spr ( SPR603E_IBAT2L ) ) ;
printf ( " DBAT3U=%08X \t IBAT3U=%08X \n " , ppc_read_spr ( SPR603E_DBAT3U ) , ppc_read_spr ( SPR603E_IBAT3U ) ) ;
printf ( " DBAT3L=%08X \t IBAT3L=%08X \n " , ppc_read_spr ( SPR603E_DBAT3L ) , ppc_read_spr ( SPR603E_IBAT3L ) ) ;
for ( int i = 0 ; i < 10 ; i + + )
printf ( " SR%d =%08X VSID=%06X \n " , i , ppc_read_sr ( i ) , ppc_read_sr ( i ) & 0x00ffffff ) ;
for ( int i = 10 ; i < 16 ; i + + )
printf ( " SR%d=%08X VSID=%06X \n " , i , ppc_read_sr ( i ) , ppc_read_sr ( i ) & 0x00ffffff ) ;
printf ( " SDR1=%08X \n " , ppc_read_spr ( SPR603E_SDR1 ) ) ;
printf ( " \n " ) ;
printf ( " DBAT0: " ) ; PrintBAT ( SPR603E_DBAT0U , SPR603E_DBAT0L ) ; printf ( " \n " ) ;
printf ( " DBAT1: " ) ; PrintBAT ( SPR603E_DBAT1U , SPR603E_DBAT1L ) ; printf ( " \n " ) ;
printf ( " DBAT2: " ) ; PrintBAT ( SPR603E_DBAT2U , SPR603E_DBAT2L ) ; printf ( " \n " ) ;
printf ( " DBAT3: " ) ; PrintBAT ( SPR603E_DBAT3U , SPR603E_DBAT3L ) ; printf ( " \n " ) ;
printf ( " IBAT0: " ) ; PrintBAT ( SPR603E_IBAT0U , SPR603E_IBAT0L ) ; printf ( " \n " ) ;
printf ( " IBAT1: " ) ; PrintBAT ( SPR603E_IBAT1U , SPR603E_IBAT1L ) ; printf ( " \n " ) ;
printf ( " IBAT2: " ) ; PrintBAT ( SPR603E_IBAT2U , SPR603E_IBAT2L ) ; printf ( " \n " ) ;
printf ( " IBAT3: " ) ; PrintBAT ( SPR603E_IBAT3U , SPR603E_IBAT3L ) ; printf ( " \n " ) ;
printf ( " \n " ) ;
/*
printf ( " First PTEG: \n " ) ;
uint32_t ptab = ppc_read_spr ( SPR603E_SDR1 ) & 0xffff0000 ;
for ( int i = 0 ; i < 65536 / 8 ; i + + )
{
uint64_t pte = bus - > Read64 ( ptab + i * 8 ) ;
uint32_t vsid = ( pte > > ( 32 + ( 31 - 24 ) ) ) & 0x00ffffff ;
uint32_t rpn = pte & 0xfffff000 ;
int wimg = ( pte > > 3 ) & 0xf ;
bool v = pte & 0x8000000000000000ULL ;
printf ( " %d: %016llX V=%d VSID=%06X RPN=%08X WIMG=%c%c%c%c \n " , i , pte , v , vsid , rpn , ( wimg & 8 ) ? ' W ' : ' - ' , ( wimg & 4 ) ? ' I ' : ' - ' , ( wimg & 2 ) ? ' M ' : ' - ' , ( wimg & 1 ) ? ' G ' : ' - ' ) ;
2016-03-21 04:10:14 +00:00
}
*/
}
2016-04-24 01:40:16 +00:00
# endif
2016-03-21 04:10:14 +00:00
2016-04-27 04:09:50 +00:00
/******************************************************************************
Render State Analysis
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef DEBUG
# include "Model3/Model3GraphicsState.h"
# include "Util/BMPFile.h"
# include "OSD/SDL/PolyAnalysis.h"
2020-07-27 10:28:48 +00:00
# include <fstream>
2016-04-27 04:09:50 +00:00
static void SaveFrameBuffer ( const std : : string & file )
{
std : : shared_ptr < uint8_t > pixels ( new uint8_t [ totalXRes * totalYRes * 4 ] , std : : default_delete < uint8_t [ ] > ( ) ) ;
glReadPixels ( 0 , 0 , totalXRes , totalYRes , GL_RGBA , GL_UNSIGNED_BYTE , pixels . get ( ) ) ;
2016-05-03 00:28:56 +00:00
Util : : WriteSurfaceToBMP < Util : : RGBA8 > ( file , pixels . get ( ) , totalXRes , totalYRes , true ) ;
2016-04-27 04:09:50 +00:00
}
static std : : string s_gfxStatePath ;
2020-07-27 10:28:48 +00:00
2016-04-27 04:09:50 +00:00
static std : : string GetFileBaseName ( const std : : string & file )
{
std : : string base = file ;
size_t pos = file . find_last_of ( ' / ' ) ;
if ( pos ! = std : : string : : npos )
base = file . substr ( pos + 1 ) ;
pos = file . find_last_of ( ' \\ ' ) ;
if ( pos ! = std : : string : : npos )
base = file . substr ( pos + 1 ) ;
return base ;
}
static void TestPolygonHeaderBits ( IEmulator * Emu )
{
const static std : : vector < uint32_t > unknownPolyBits
{
2017-04-07 15:47:36 +00:00
0xffffffff ,
2016-05-14 06:21:56 +00:00
0x000000ab , // actual color
2016-04-27 04:09:50 +00:00
0x000000fc ,
0x000000c0 ,
0x000000a0 ,
0xffffff60 ,
2016-05-14 06:21:56 +00:00
0xff0300ff // contour, luminous, etc.
2016-04-27 04:09:50 +00:00
} ;
2020-07-27 10:28:48 +00:00
2017-04-11 07:03:47 +00:00
const std : : vector < uint32_t > unknownCullingNodeBits
{
0xffffffff ,
0x00000000 ,
0x00000000 ,
0x00000000 ,
0x00000000 ,
0x00000000 ,
0x00000000 ,
0x00000000 ,
0x00000000 ,
0x00000000
} ;
2016-04-28 22:15:36 +00:00
GLint readBuffer ;
glGetIntegerv ( GL_READ_BUFFER , & readBuffer ) ;
glReadBuffer ( GL_FRONT ) ;
2016-04-27 04:09:50 +00:00
// Render separate image for each unknown bit
2017-04-11 07:03:47 +00:00
s_runtime_config . Set ( " Debug/ForceFlushModels " , true ) ;
2016-04-27 04:09:50 +00:00
for ( int idx = 0 ; idx < 7 ; idx + + )
{
for ( int bit = 0 ; bit < 32 ; bit + + )
{
uint32_t mask = 1 < < bit ;
2017-04-11 07:03:47 +00:00
s_runtime_config . Set ( " Debug/HighlightPolyHeaderIdx " , idx ) ;
s_runtime_config . Set ( " Debug/HighlightPolyHeaderMask " , mask ) ;
2016-04-27 04:09:50 +00:00
if ( ( unknownPolyBits [ idx ] & mask ) )
{
Emu - > RenderFrame ( ) ;
2017-04-11 07:03:47 +00:00
std : : string file = Util : : Format ( ) < < " Analysis/ " < < GetFileBaseName ( s_gfxStatePath ) < < " . " < < " poly " < < " . " < < idx < < " _ " < < Util : : Hex ( mask ) < < " .bmp " ;
SaveFrameBuffer ( file ) ;
}
}
}
2020-07-27 10:28:48 +00:00
2017-04-11 07:03:47 +00:00
for ( int idx = 0 ; idx < 10 ; idx + + )
{
for ( int bit = 0 ; bit < 32 ; bit + + )
{
uint32_t mask = 1 < < bit ;
s_runtime_config . Set ( " Debug/HighlightCullingNodeIdx " , idx ) ;
s_runtime_config . Set ( " Debug/HighlightCullingNodeMask " , mask ) ;
if ( ( unknownCullingNodeBits [ idx ] & mask ) )
{
Emu - > RenderFrame ( ) ;
std : : string file = Util : : Format ( ) < < " Analysis/ " < < GetFileBaseName ( s_gfxStatePath ) < < " . " < < " culling " < < " . " < < idx < < " _ " < < Util : : Hex ( mask ) < < " .bmp " ;
2016-04-27 04:09:50 +00:00
SaveFrameBuffer ( file ) ;
}
}
}
2016-04-28 22:15:36 +00:00
glReadBuffer ( readBuffer ) ;
2016-04-27 04:09:50 +00:00
// Generate the HTML GUI
std : : string file = Util : : Format ( ) < < " Analysis/_ " < < GetFileBaseName ( s_gfxStatePath ) < < " .html " ;
2020-07-27 10:28:48 +00:00
std : : ofstream fs ( file ) ;
2016-04-27 04:09:50 +00:00
if ( ! fs . good ( ) )
ErrorLog ( " Unable to open '%s' for writing. " , file . c_str ( ) ) ;
else
{
std : : string contents = s_polyAnalysisHTMLPrologue ;
2020-07-27 10:28:48 +00:00
contents + = " var g_file_base_name = ' " + GetFileBaseName ( s_gfxStatePath ) + " '; \n " ;
2017-04-11 07:03:47 +00:00
contents + = " var g_unknown_poly_bits = [ " + std : : string ( Util : : Format ( " , " ) . Join ( unknownPolyBits ) ) + " ]; \n " ;
contents + = " var g_unknown_culling_bits = [ " + std : : string ( Util : : Format ( " , " ) . Join ( unknownCullingNodeBits ) ) + " ]; \n " ;
2016-04-27 04:09:50 +00:00
contents + = s_polyAnalysisHTMLEpilogue ;
fs < < contents ;
2016-04-28 13:18:56 +00:00
printf ( " Produced: %s \n " , file . c_str ( ) ) ;
2016-04-27 04:09:50 +00:00
}
}
# endif
2011-08-03 04:29:33 +00:00
/******************************************************************************
Save States and NVRAM
2020-07-27 10:28:48 +00:00
2011-08-09 18:36:29 +00:00
Save states and NVRAM use the same basic format . When anything changes that
breaks compatibility with previous versions of Supermodel , the save state
and NVRAM version numbers must be incremented as needed .
2020-07-27 10:28:48 +00:00
2011-08-03 04:29:33 +00:00
Header block name : " Supermodel Save State " or " Supermodel NVRAM State "
2020-07-27 10:28:48 +00:00
Data : Save state file version ( 4 - byte integer ) , ROM set ID ( up to 9 bytes ,
2011-08-03 04:29:33 +00:00
including terminating \ 0 ) .
2020-07-27 10:28:48 +00:00
2011-08-03 04:29:33 +00:00
Different subsystems output their own blocks .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-02-05 10:03:50 +00:00
static const int STATE_FILE_VERSION = 3 ; // save state file version
2016-04-24 01:50:03 +00:00
static const int NVRAM_FILE_VERSION = 0 ; // NVRAM file version
static unsigned s_saveSlot = 0 ; // save state slot #
2011-08-03 04:29:33 +00:00
2016-04-24 03:01:42 +00:00
static void SaveState ( IEmulator * Model3 )
2011-08-03 04:29:33 +00:00
{
2016-04-24 01:40:16 +00:00
CBlockFile SaveState ;
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
std : : string file_path = Util : : Format ( ) < < " Saves/ " < < Model3 - > GetGame ( ) . name < < " .st " < < s_saveSlot ;
if ( OKAY ! = SaveState . Create ( file_path , " Supermodel Save State " , " Supermodel Version " SUPERMODEL_VERSION ) )
2016-04-24 01:40:16 +00:00
{
2017-03-27 03:19:15 +00:00
ErrorLog ( " Unable to save state to '%s'. " , file_path . c_str ( ) ) ;
2016-04-24 01:40:16 +00:00
return ;
}
2020-07-27 10:28:48 +00:00
// Write file format version and ROM set ID to header block
2016-04-24 01:50:03 +00:00
int32_t fileVersion = STATE_FILE_VERSION ;
2016-04-24 01:40:16 +00:00
SaveState . Write ( & fileVersion , sizeof ( fileVersion ) ) ;
2017-03-27 03:19:15 +00:00
SaveState . Write ( Model3 - > GetGame ( ) . name ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Save state
Model3 - > SaveState ( & SaveState ) ;
SaveState . Close ( ) ;
2017-03-27 03:19:15 +00:00
printf ( " Saved state to '%s'. \n " , file_path . c_str ( ) ) ;
DebugLog ( " Saved state to '%s'. \n " , file_path . c_str ( ) ) ;
2011-08-03 04:29:33 +00:00
}
2018-03-26 22:59:06 +00:00
static void LoadState ( IEmulator * Model3 , std : : string file_path = std : : string ( ) )
2011-08-03 04:29:33 +00:00
{
2016-04-24 01:40:16 +00:00
CBlockFile SaveState ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Generate file path
2018-03-26 22:59:06 +00:00
if ( file_path . empty ( ) )
file_path = Util : : Format ( ) < < " Saves/ " < < Model3 - > GetGame ( ) . name < < " .st " < < s_saveSlot ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Open and check to make sure format is correct
2017-03-27 03:19:15 +00:00
if ( OKAY ! = SaveState . Load ( file_path ) )
2016-04-24 01:40:16 +00:00
{
2017-03-27 03:19:15 +00:00
ErrorLog ( " Unable to load state from '%s'. " , file_path . c_str ( ) ) ;
2016-04-24 01:40:16 +00:00
return ;
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
if ( OKAY ! = SaveState . FindBlock ( " Supermodel Save State " ) )
{
2017-03-27 03:19:15 +00:00
ErrorLog ( " '%s' does not appear to be a valid save state file. " , file_path . c_str ( ) ) ;
2016-04-24 01:40:16 +00:00
return ;
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:50:03 +00:00
int32_t fileVersion ;
2016-04-24 01:40:16 +00:00
SaveState . Read ( & fileVersion , sizeof ( fileVersion ) ) ;
if ( fileVersion ! = STATE_FILE_VERSION )
{
2017-03-27 03:19:15 +00:00
ErrorLog ( " '%s' is incompatible with this version of Supermodel. " , file_path . c_str ( ) ) ;
2016-04-24 01:40:16 +00:00
return ;
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Load
Model3 - > LoadState ( & SaveState ) ;
SaveState . Close ( ) ;
2017-03-27 03:19:15 +00:00
printf ( " Loaded state from '%s'. \n " , file_path . c_str ( ) ) ;
DebugLog ( " Loaded state from '%s'. \n " , file_path . c_str ( ) ) ;
2011-08-03 04:29:33 +00:00
}
2016-04-24 03:01:42 +00:00
static void SaveNVRAM ( IEmulator * Model3 )
2011-08-03 04:29:33 +00:00
{
2016-04-24 01:40:16 +00:00
CBlockFile NVRAM ;
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
std : : string file_path = Util : : Format ( ) < < " NVRAM/ " < < Model3 - > GetGame ( ) . name < < " .nv " ;
if ( OKAY ! = NVRAM . Create ( file_path , " Supermodel NVRAM State " , " Supermodel Version " SUPERMODEL_VERSION ) )
2016-04-24 01:40:16 +00:00
{
2017-03-27 03:19:15 +00:00
ErrorLog ( " Unable to save NVRAM to '%s'. Make sure directory exists! " , file_path . c_str ( ) ) ;
2016-04-24 01:40:16 +00:00
return ;
}
2020-07-27 10:28:48 +00:00
// Write file format version and ROM set ID to header block
2016-04-24 01:50:03 +00:00
int32_t fileVersion = NVRAM_FILE_VERSION ;
2016-04-24 01:40:16 +00:00
NVRAM . Write ( & fileVersion , sizeof ( fileVersion ) ) ;
2017-03-27 03:19:15 +00:00
NVRAM . Write ( Model3 - > GetGame ( ) . name ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Save NVRAM
Model3 - > SaveNVRAM ( & NVRAM ) ;
NVRAM . Close ( ) ;
2017-03-27 03:19:15 +00:00
DebugLog ( " Saved NVRAM to '%s'. \n " , file_path . c_str ( ) ) ;
2011-08-03 04:29:33 +00:00
}
2016-04-24 03:01:42 +00:00
static void LoadNVRAM ( IEmulator * Model3 )
2011-08-03 04:29:33 +00:00
{
2016-04-24 01:40:16 +00:00
CBlockFile NVRAM ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Generate file path
2017-03-27 03:19:15 +00:00
std : : string file_path = Util : : Format ( ) < < " NVRAM/ " < < Model3 - > GetGame ( ) . name < < " .nv " ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Open and check to make sure format is correct
2017-03-27 03:19:15 +00:00
if ( OKAY ! = NVRAM . Load ( file_path ) )
2016-04-24 01:40:16 +00:00
{
//ErrorLog("Unable to restore NVRAM from '%s'.", filePath);
return ;
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
if ( OKAY ! = NVRAM . FindBlock ( " Supermodel NVRAM State " ) )
{
2017-03-27 03:19:15 +00:00
ErrorLog ( " '%s' does not appear to be a valid NVRAM file. " , file_path . c_str ( ) ) ;
2016-04-24 01:40:16 +00:00
return ;
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:50:03 +00:00
int32_t fileVersion ;
2016-04-24 01:40:16 +00:00
NVRAM . Read ( & fileVersion , sizeof ( fileVersion ) ) ;
if ( fileVersion ! = NVRAM_FILE_VERSION )
{
2017-03-27 03:19:15 +00:00
ErrorLog ( " '%s' is incompatible with this version of Supermodel. " , file_path . c_str ( ) ) ;
2016-04-24 01:40:16 +00:00
return ;
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Load
Model3 - > LoadNVRAM ( & NVRAM ) ;
NVRAM . Close ( ) ;
2017-03-27 03:19:15 +00:00
DebugLog ( " Loaded NVRAM from '%s'. \n " , file_path . c_str ( ) ) ;
2011-08-03 04:29:33 +00:00
}
2011-08-27 21:37:37 +00:00
/******************************************************************************
UI Rendering
2020-07-27 10:28:48 +00:00
2011-08-27 21:37:37 +00:00
Currently , only does crosshairs for light gun games .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void GunToViewCoords ( float * x , float * y )
{
2016-04-24 01:40:16 +00:00
* x = ( * x - 150.0f ) / ( 651.0f - 150.0f ) ; // Scale [150,651] -> [0.0,1.0]
* y = ( * y - 80.0f ) / ( 465.0f - 80.0f ) ; // Scale [80,465] -> [0.0,1.0]
2011-08-27 21:37:37 +00:00
}
static void DrawCrosshair ( float x , float y , float r , float g , float b )
{
2016-04-24 01:40:16 +00:00
float base = 0.01f , height = 0.02f ; // geometric parameters of each triangle
float dist = 0.004f ; // distance of triangle tip from center
float a = ( float ) xRes / ( float ) yRes ; // aspect ratio (to square the crosshair)
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
glColor3f ( r , g , b ) ;
glVertex2f ( x , y + dist ) ; // bottom triangle
glVertex2f ( x + base / 2.0f , y + ( dist + height ) * a ) ;
2020-07-27 10:28:48 +00:00
glVertex2f ( x - base / 2.0f , y + ( dist + height ) * a ) ;
2016-04-24 01:40:16 +00:00
glVertex2f ( x , y - dist ) ; // top triangle
glVertex2f ( x - base / 2.0f , y - ( dist + height ) * a ) ;
glVertex2f ( x + base / 2.0f , y - ( dist + height ) * a ) ;
glVertex2f ( x - dist , y ) ; // left triangle
glVertex2f ( x - dist - height , y + ( base / 2.0f ) * a ) ;
glVertex2f ( x - dist - height , y - ( base / 2.0f ) * a ) ;
glVertex2f ( x + dist , y ) ; // right triangle
glVertex2f ( x + dist + height , y - ( base / 2.0f ) * a ) ;
glVertex2f ( x + dist + height , y + ( base / 2.0f ) * a ) ;
2011-08-27 21:37:37 +00:00
}
2016-04-24 01:40:16 +00:00
/*
2012-07-12 06:13:24 +00:00
static void PrintGLError ( GLenum error )
{
2016-04-24 01:40:16 +00:00
switch ( error )
{
case GL_INVALID_ENUM : printf ( " invalid enum \n " ) ; break ;
case GL_INVALID_VALUE : printf ( " invalid value \n " ) ; break ;
case GL_INVALID_OPERATION : printf ( " invalid operation \n " ) ; break ;
case GL_STACK_OVERFLOW : printf ( " stack overflow \n " ) ; break ;
case GL_STACK_UNDERFLOW : printf ( " stack underflow \n " ) ; break ;
case GL_OUT_OF_MEMORY : printf ( " out of memory \n " ) ; break ;
case GL_TABLE_TOO_LARGE : printf ( " table too large \n " ) ; break ;
case GL_NO_ERROR : break ;
default : printf ( " unknown error \n " ) ; break ;
}
2018-07-04 22:15:44 +00:00
}
*/
static void UpdateCrosshairs ( uint32_t currentInputs , CInputs * Inputs , unsigned crosshairs )
{
bool offscreenTrigger [ 2 ] ;
float x [ 2 ] , y [ 2 ] ;
crosshairs & = 3 ;
if ( ! crosshairs )
return ;
// Set up the viewport and orthogonal projection
glUseProgram ( 0 ) ; // no shaders
glViewport ( xOffset , yOffset , xRes , yRes ) ;
2016-04-24 01:40:16 +00:00
glMatrixMode ( GL_PROJECTION ) ;
glLoadIdentity ( ) ;
gluOrtho2D ( 0.0 , 1.0 , 1.0 , 0.0 ) ;
glMatrixMode ( GL_MODELVIEW ) ;
glLoadIdentity ( ) ;
glDisable ( GL_TEXTURE_2D ) ; // no texture mapping
2018-07-04 22:15:44 +00:00
glDisable ( GL_BLEND ) ; // no blending
2020-07-27 10:28:48 +00:00
glDisable ( GL_DEPTH_TEST ) ; // no Z-buffering needed
2018-07-04 22:15:44 +00:00
glDisable ( GL_LIGHTING ) ;
// Convert gun coordinates to viewspace coordinates
if ( currentInputs & Game : : INPUT_ANALOG_GUN1 )
{
x [ 0 ] = ( ( float ) Inputs - > analogGunX [ 0 ] - > value / 255.0f ) ;
y [ 0 ] = ( ( 255.0f - ( float ) Inputs - > analogGunY [ 0 ] - > value ) / 255.0f ) ;
offscreenTrigger [ 0 ] = Inputs - > analogTriggerLeft [ 0 ] - > value | | Inputs - > analogTriggerRight [ 0 ] - > value ;
}
else if ( currentInputs & Game : : INPUT_GUN1 )
{
x [ 0 ] = ( float ) Inputs - > gunX [ 0 ] - > value ;
y [ 0 ] = ( float ) Inputs - > gunY [ 0 ] - > value ;
GunToViewCoords ( & x [ 0 ] , & y [ 0 ] ) ;
offscreenTrigger [ 0 ] = ( Inputs - > trigger [ 0 ] - > offscreenValue ) > 0 ;
}
if ( currentInputs & Game : : INPUT_ANALOG_GUN2 )
{
x [ 1 ] = ( ( float ) Inputs - > analogGunX [ 1 ] - > value / 255.0f ) ;
y [ 1 ] = ( ( 255.0f - ( float ) Inputs - > analogGunY [ 1 ] - > value ) / 255.0f ) ;
offscreenTrigger [ 1 ] = Inputs - > analogTriggerLeft [ 1 ] - > value | | Inputs - > analogTriggerRight [ 1 ] - > value ;
}
else if ( currentInputs & Game : : INPUT_GUN2 )
{
x [ 1 ] = ( float ) Inputs - > gunX [ 1 ] - > value ;
y [ 1 ] = ( float ) Inputs - > gunY [ 1 ] - > value ;
GunToViewCoords ( & x [ 1 ] , & y [ 1 ] ) ;
offscreenTrigger [ 1 ] = ( Inputs - > trigger [ 1 ] - > offscreenValue ) > 0 ;
}
2020-07-27 10:28:48 +00:00
// Draw visible crosshairs
2018-07-04 22:15:44 +00:00
glBegin ( GL_TRIANGLES ) ;
if ( ( crosshairs & 1 ) & & ! offscreenTrigger [ 0 ] ) // Player 1
DrawCrosshair ( x [ 0 ] , y [ 0 ] , 1.0f , 0.0f , 0.0f ) ;
if ( ( crosshairs & 2 ) & & ! offscreenTrigger [ 1 ] ) // Player 2
DrawCrosshair ( x [ 1 ] , y [ 1 ] , 0.0f , 1.0f , 0.0f ) ;
glEnd ( ) ;
//PrintGLError(glGetError());
}
2020-07-27 10:28:48 +00:00
2012-02-13 23:37:48 +00:00
/******************************************************************************
Video Callbacks
2018-07-04 22:15:44 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static CInputs * videoInputs = NULL ;
static uint32_t currentInputs = 0 ;
bool BeginFrameVideo ( )
{
2016-04-24 01:40:16 +00:00
return true ;
2012-02-13 23:37:48 +00:00
}
void EndFrameVideo ( )
2018-07-04 22:15:44 +00:00
{
// Show crosshairs for light gun games
if ( videoInputs )
UpdateCrosshairs ( currentInputs , videoInputs , s_runtime_config [ " Crosshairs " ] . ValueAs < unsigned > ( ) ) ;
// Swap the buffers
2020-04-19 08:34:58 +00:00
SDL_GL_SwapWindow ( s_window ) ;
2012-02-13 23:37:48 +00:00
}
2016-10-21 00:04:12 +00:00
static void SuperSleep ( UINT32 time )
{
2017-03-27 03:19:15 +00:00
UINT32 start = SDL_GetTicks ( ) ;
UINT32 tics = start ;
2016-10-21 00:04:12 +00:00
2017-03-27 03:19:15 +00:00
while ( start + time > tics ) {
tics = SDL_GetTicks ( ) ;
}
2016-10-21 00:04:12 +00:00
}
2011-08-27 21:37:37 +00:00
2011-08-03 04:29:33 +00:00
/******************************************************************************
2011-08-26 05:06:34 +00:00
Main Program Loop
2011-08-03 04:29:33 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef SUPERMODEL_DEBUGGER
2017-03-27 03:19:15 +00:00
int Supermodel ( const Game & game , ROMSet * rom_set , IEmulator * Model3 , CInputs * Inputs , COutputs * Outputs , Debugger : : CDebugger * Debugger )
2011-08-03 04:29:33 +00:00
{
2016-06-13 00:53:39 +00:00
CLogger * oldLogger = 0 ;
2011-08-03 04:29:33 +00:00
# else
2017-03-27 03:19:15 +00:00
int Supermodel ( const Game & game , ROMSet * rom_set , IEmulator * Model3 , CInputs * Inputs , COutputs * Outputs )
2020-07-27 10:28:48 +00:00
{
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_DEBUGGER
2018-03-26 22:59:06 +00:00
std : : string initialState = s_runtime_config [ " InitStateFile " ] . ValueAs < std : : string > ( ) ;
unsigned prevFPSTicks ;
unsigned fpsFramesElapsed ;
bool gameHasLightguns = false ;
bool quit = false ;
bool paused = false ;
bool dumpTimings = false ;
2016-04-24 01:40:16 +00:00
// Initialize and load ROMs
if ( OKAY ! = Model3 - > Init ( ) )
return 1 ;
2017-03-27 03:19:15 +00:00
if ( Model3 - > LoadGame ( game , * rom_set ) )
2016-04-24 01:40:16 +00:00
return 1 ;
2017-03-27 03:19:15 +00:00
* rom_set = ROMSet ( ) ; // free up this memory we won't need anymore
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Load NVRAM
LoadNVRAM ( Model3 ) ;
2020-07-27 10:28:48 +00:00
2020-04-19 08:34:58 +00:00
// Set the video mode
2016-04-24 01:40:16 +00:00
char baseTitleStr [ 128 ] ;
char titleStr [ 128 ] ;
2017-03-27 03:19:15 +00:00
totalXRes = xRes = s_runtime_config [ " XResolution " ] . ValueAs < unsigned > ( ) ;
totalYRes = yRes = s_runtime_config [ " YResolution " ] . ValueAs < unsigned > ( ) ;
sprintf ( baseTitleStr , " Supermodel - %s " , game . title . c_str ( ) ) ;
2020-04-19 08:34:58 +00:00
SDL_SetWindowTitle ( s_window , baseTitleStr ) ;
2020-04-24 07:04:41 +00:00
SDL_SetWindowSize ( s_window , totalXRes , totalYRes ) ;
SDL_SetWindowPosition ( s_window , SDL_WINDOWPOS_CENTERED , SDL_WINDOWPOS_CENTERED ) ;
2018-05-03 03:46:44 +00:00
bool stretch = s_runtime_config [ " Stretch " ] . ValueAs < bool > ( ) ;
bool fullscreen = s_runtime_config [ " FullScreen " ] . ValueAs < bool > ( ) ;
2020-04-19 08:34:58 +00:00
if ( OKAY ! = ResizeGLScreen ( & xOffset , & yOffset , & xRes , & yRes , & totalXRes , & totalYRes , ! stretch , fullscreen ) )
2016-04-24 01:40:16 +00:00
return 1 ;
2017-03-27 03:19:15 +00:00
2020-07-27 10:28:48 +00:00
// Info log GL information
2016-04-24 01:40:16 +00:00
PrintGLInfo ( false , true , false ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Initialize audio system
if ( OKAY ! = OpenAudio ( ) )
return 1 ;
2018-07-04 22:15:44 +00:00
// Hide mouse if fullscreen, enable crosshairs for gun games
Inputs - > GetInputSystem ( ) - > SetMouseVisibility ( ! s_runtime_config [ " FullScreen " ] . ValueAs < bool > ( ) ) ;
gameHasLightguns = ! ! ( game . inputs & ( Game : : INPUT_GUN1 | Game : : INPUT_GUN2 ) ) ;
gameHasLightguns | = game . name = = " lostwsga " ;
currentInputs = game . inputs ;
if ( gameHasLightguns )
videoInputs = Inputs ;
else
2016-04-24 01:40:16 +00:00
videoInputs = NULL ;
// Attach the inputs to the emulator
Model3 - > AttachInputs ( Inputs ) ;
// Attach the outputs to the emulator
if ( Outputs ! = NULL )
Model3 - > AttachOutputs ( Outputs ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Initialize the renderers
2017-08-11 00:41:10 +00:00
CRender2D * Render2D = new CRender2D ( s_runtime_config ) ;
2017-08-15 23:21:57 +00:00
IRender3D * Render3D = s_runtime_config [ " New3DEngine " ] . ValueAs < bool > ( ) ? ( ( IRender3D * ) new New3D : : CNew3D ( s_runtime_config , Model3 - > GetGame ( ) . name ) ) : ( ( IRender3D * ) new Legacy3D : : CLegacy3D ( s_runtime_config ) ) ;
2016-04-24 01:40:16 +00:00
if ( OKAY ! = Render2D - > Init ( xOffset , yOffset , xRes , yRes , totalXRes , totalYRes ) )
goto QuitError ;
if ( OKAY ! = Render3D - > Init ( xOffset , yOffset , xRes , yRes , totalXRes , totalYRes ) )
goto QuitError ;
Model3 - > AttachRenderers ( Render2D , Render3D ) ;
// Reset emulator
Model3 - > Reset ( ) ;
2020-07-27 10:28:48 +00:00
2018-03-26 22:59:06 +00:00
// Load initial save state if requested
if ( initialState . length ( ) > 0 )
LoadState ( Model3 , initialState ) ;
2020-07-27 10:28:48 +00:00
2011-08-03 04:29:33 +00:00
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
// If debugger was supplied, set it as logger and attach it to system
2016-06-13 00:53:39 +00:00
oldLogger = GetLogger ( ) ;
2016-04-24 01:40:16 +00:00
if ( Debugger ! = NULL )
{
SetLogger ( Debugger ) ;
Debugger - > Attach ( ) ;
}
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
// Emulate!
fpsFramesElapsed = 0 ;
prevFPSTicks = SDL_GetTicks ( ) ;
quit = false ;
paused = false ;
dumpTimings = false ;
2016-04-27 04:09:50 +00:00
# ifdef DEBUG
if ( dynamic_cast < CModel3GraphicsState * > ( Model3 ) )
{
TestPolygonHeaderBits ( Model3 ) ;
quit = true ;
}
# endif
2016-04-24 01:40:16 +00:00
while ( ! quit )
{
2017-03-27 03:19:15 +00:00
auto startTime = SDL_GetTicks ( ) ;
2016-10-21 00:04:12 +00:00
2016-04-24 01:40:16 +00:00
// Render if paused, otherwise run a frame
if ( paused )
Model3 - > RenderFrame ( ) ;
else
Model3 - > RunFrame ( ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Poll the inputs
2017-03-27 03:19:15 +00:00
if ( ! Inputs - > Poll ( & game , xOffset , yOffset , xRes , yRes ) )
2016-04-24 01:40:16 +00:00
quit = true ;
2020-07-27 10:28:48 +00:00
2011-08-03 04:29:33 +00:00
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
bool processUI = true ;
if ( Debugger ! = NULL )
{
Debugger - > Poll ( ) ;
// Check if debugger requests exit or pause
if ( Debugger - > CheckExit ( ) )
{
quit = true ;
processUI = false ;
}
2020-07-27 10:28:48 +00:00
else if ( Debugger - > CheckPause ( ) )
2016-04-24 01:40:16 +00:00
{
paused = true ;
processUI = false ;
}
}
if ( processUI )
{
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
// Check UI controls
if ( Inputs - > uiExit - > Pressed ( ) )
{
// Quit emulator
2020-07-27 10:28:48 +00:00
quit = true ;
2016-04-24 01:40:16 +00:00
}
else if ( Inputs - > uiReset - > Pressed ( ) )
{
if ( ! paused )
{
Model3 - > PauseThreads ( ) ;
SetAudioEnabled ( false ) ;
}
// Reset emulator
Model3 - > Reset ( ) ;
2020-07-27 10:28:48 +00:00
2011-08-03 04:29:33 +00:00
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
// If debugger was supplied, reset it too
if ( Debugger ! = NULL )
Debugger - > Reset ( ) ;
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
if ( ! paused )
{
Model3 - > ResumeThreads ( ) ;
SetAudioEnabled ( true ) ;
}
puts ( " Model 3 reset. " ) ;
}
else if ( Inputs - > uiPause - > Pressed ( ) )
{
// Toggle emulator paused flag
paused = ! paused ;
if ( paused )
{
Model3 - > PauseThreads ( ) ;
SetAudioEnabled ( false ) ;
sprintf ( titleStr , " %s (Paused) " , baseTitleStr ) ;
2020-04-19 08:34:58 +00:00
SDL_SetWindowTitle ( s_window , titleStr ) ;
2016-04-24 01:40:16 +00:00
}
else
{
Model3 - > ResumeThreads ( ) ;
SetAudioEnabled ( true ) ;
2020-04-19 08:34:58 +00:00
SDL_SetWindowTitle ( s_window , baseTitleStr ) ;
2016-04-24 01:40:16 +00:00
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Send paused value as output
if ( Outputs ! = NULL )
Outputs - > SetValue ( OutputPause , paused ) ;
}
else if ( Inputs - > uiFullScreen - > Pressed ( ) )
{
// Toggle emulator fullscreen
2017-03-27 03:19:15 +00:00
s_runtime_config . Get ( " FullScreen " ) . SetValue ( ! s_runtime_config [ " FullScreen " ] . ValueAs < bool > ( ) ) ;
2016-04-24 01:40:16 +00:00
// Delete renderers and recreate them afterwards since GL context will most likely be lost when switching from/to fullscreen
delete Render2D ;
delete Render3D ;
Render2D = NULL ;
Render3D = NULL ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Resize screen
2017-03-27 03:19:15 +00:00
totalXRes = xRes = s_runtime_config [ " XResolution " ] . ValueAs < unsigned > ( ) ;
totalYRes = yRes = s_runtime_config [ " YResolution " ] . ValueAs < unsigned > ( ) ;
2018-05-03 03:46:44 +00:00
bool stretch = s_runtime_config [ " Stretch " ] . ValueAs < bool > ( ) ;
bool fullscreen = s_runtime_config [ " FullScreen " ] . ValueAs < bool > ( ) ;
if ( OKAY ! = ResizeGLScreen ( & xOffset , & yOffset , & xRes , & yRes , & totalXRes , & totalYRes , ! stretch , fullscreen ) )
2016-04-24 01:40:16 +00:00
goto QuitError ;
// Recreate renderers and attach to the emulator
2017-08-11 00:41:10 +00:00
Render2D = new CRender2D ( s_runtime_config ) ;
2020-04-19 08:34:58 +00:00
Render3D = s_runtime_config [ " New3DEngine " ] . ValueAs < bool > ( ) ? ( ( IRender3D * ) new New3D : : CNew3D ( s_runtime_config , Model3 - > GetGame ( ) . name ) ) : ( ( IRender3D * ) new Legacy3D : : CLegacy3D ( s_runtime_config ) ) ;
2016-04-24 01:40:16 +00:00
if ( OKAY ! = Render2D - > Init ( xOffset , yOffset , xRes , yRes , totalXRes , totalYRes ) )
goto QuitError ;
if ( OKAY ! = Render3D - > Init ( xOffset , yOffset , xRes , yRes , totalXRes , totalYRes ) )
goto QuitError ;
Model3 - > AttachRenderers ( Render2D , Render3D ) ;
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
Inputs - > GetInputSystem ( ) - > SetMouseVisibility ( ! s_runtime_config [ " FullScreen " ] . ValueAs < bool > ( ) ) ;
2016-04-24 01:40:16 +00:00
}
else if ( Inputs - > uiSaveState - > Pressed ( ) )
{
if ( ! paused )
{
Model3 - > PauseThreads ( ) ;
SetAudioEnabled ( false ) ;
}
// Save game state
SaveState ( Model3 ) ;
if ( ! paused )
{
Model3 - > ResumeThreads ( ) ;
SetAudioEnabled ( true ) ;
}
}
else if ( Inputs - > uiChangeSlot - > Pressed ( ) )
{
// Change save slot
2016-04-24 01:50:03 +00:00
+ + s_saveSlot ;
s_saveSlot % = 10 ; // clamp to [0,9]
printf ( " Save slot: %d \n " , s_saveSlot ) ;
2016-04-24 01:40:16 +00:00
}
else if ( Inputs - > uiLoadState - > Pressed ( ) )
{
if ( ! paused )
{
Model3 - > PauseThreads ( ) ;
SetAudioEnabled ( false ) ;
}
// Load game state
LoadState ( Model3 ) ;
2020-07-27 10:28:48 +00:00
2011-08-03 04:29:33 +00:00
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
// If debugger was supplied, reset it after loading state
if ( Debugger ! = NULL )
Debugger - > Reset ( ) ;
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_DEBUGGER
2011-09-12 05:43:37 +00:00
2016-04-24 01:40:16 +00:00
if ( ! paused )
{
Model3 - > ResumeThreads ( ) ;
SetAudioEnabled ( true ) ;
}
}
else if ( Inputs - > uiMusicVolUp - > Pressed ( ) )
{
// Increase music volume by 10%
2017-03-27 03:19:15 +00:00
if ( ! Model3 - > GetGame ( ) . mpeg_board . empty ( ) )
2016-04-24 01:40:16 +00:00
{
2017-03-27 22:06:23 +00:00
int vol = ( std : : min ) ( 200 , s_runtime_config [ " MusicVolume " ] . ValueAs < int > ( ) + 10 ) ;
2017-03-27 03:19:15 +00:00
s_runtime_config . Get ( " MusicVolume " ) . SetValue ( vol ) ;
2016-04-24 01:40:16 +00:00
printf ( " Music volume: %d%% " , vol ) ;
if ( 200 = = vol )
puts ( " (maximum) " ) ;
else
printf ( " \n " ) ;
}
else
puts ( " This game does not have an MPEG music board. " ) ;
}
else if ( Inputs - > uiMusicVolDown - > Pressed ( ) )
{
// Decrease music volume by 10%
2017-03-27 03:19:15 +00:00
if ( ! Model3 - > GetGame ( ) . mpeg_board . empty ( ) )
2016-04-24 01:40:16 +00:00
{
2017-03-27 22:06:23 +00:00
int vol = ( std : : max ) ( 0 , s_runtime_config [ " MusicVolume " ] . ValueAs < int > ( ) - 10 ) ;
2017-03-27 03:19:15 +00:00
s_runtime_config . Get ( " MusicVolume " ) . SetValue ( vol ) ;
2016-04-24 01:40:16 +00:00
printf ( " Music volume: %d%% " , vol ) ;
if ( 0 = = vol )
puts ( " (muted) " ) ;
else
printf ( " \n " ) ;
2020-07-27 10:28:48 +00:00
}
2016-04-24 01:40:16 +00:00
else
puts ( " This game does not have an MPEG music board. " ) ;
}
else if ( Inputs - > uiSoundVolUp - > Pressed ( ) )
{
// Increase sound volume by 10%
2018-05-03 03:46:44 +00:00
int vol = ( std : : min ) ( 200 , s_runtime_config [ " SoundVolume " ] . ValueAs < int > ( ) + 10 ) ;
2017-03-27 03:19:15 +00:00
s_runtime_config . Get ( " SoundVolume " ) . SetValue ( vol ) ;
2016-04-24 01:40:16 +00:00
printf ( " Sound volume: %d%% " , vol ) ;
if ( 200 = = vol )
puts ( " (maximum) " ) ;
else
printf ( " \n " ) ;
}
else if ( Inputs - > uiSoundVolDown - > Pressed ( ) )
{
// Decrease sound volume by 10%
2017-03-27 22:06:23 +00:00
int vol = ( std : : max ) ( 0 , s_runtime_config [ " SoundVolume " ] . ValueAs < int > ( ) - 10 ) ;
2017-03-27 03:19:15 +00:00
s_runtime_config . Get ( " SoundVolume " ) . SetValue ( vol ) ;
2016-04-24 01:40:16 +00:00
printf ( " Sound volume: %d%% " , vol ) ;
if ( 0 = = vol )
puts ( " (muted) " ) ;
else
printf ( " \n " ) ;
}
2019-02-19 09:24:31 +00:00
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
else if ( Inputs - > uiDumpInpState - > Pressed ( ) )
{
// Dump input states
2017-03-27 03:19:15 +00:00
Inputs - > DumpState ( & game ) ;
2016-04-24 01:40:16 +00:00
}
else if ( Inputs - > uiDumpTimings - > Pressed ( ) )
{
dumpTimings = ! dumpTimings ;
}
2019-02-19 09:24:31 +00:00
# endif
2016-04-24 01:40:16 +00:00
else if ( Inputs - > uiSelectCrosshairs - > Pressed ( ) & & gameHasLightguns )
{
2017-03-27 03:19:15 +00:00
int crosshairs = ( s_runtime_config [ " Crosshairs " ] . ValueAs < unsigned > ( ) + 1 ) & 3 ;
s_runtime_config . Get ( " Crosshairs " ) . SetValue ( crosshairs ) ;
switch ( crosshairs )
2016-04-24 01:40:16 +00:00
{
case 0 : puts ( " Crosshairs disabled. " ) ; break ;
case 3 : puts ( " Crosshairs enabled. " ) ; break ;
case 1 : puts ( " Showing Player 1 crosshair only. " ) ; break ;
case 2 : puts ( " Showing Player 2 crosshair only. " ) ; break ;
}
}
else if ( Inputs - > uiClearNVRAM - > Pressed ( ) )
{
// Clear NVRAM
Model3 - > ClearNVRAM ( ) ;
puts ( " NVRAM cleared. " ) ;
}
else if ( Inputs - > uiToggleFrLimit - > Pressed ( ) )
{
// Toggle frame limiting
2017-03-27 03:19:15 +00:00
s_runtime_config . Get ( " Throttle " ) . SetValue ( ! s_runtime_config [ " Throttle " ] . ValueAs < bool > ( ) ) ;
printf ( " Frame limiting: %s \n " , s_runtime_config [ " Throttle " ] . ValueAs < bool > ( ) ? " On " : " Off " ) ;
2016-04-24 01:40:16 +00:00
}
2011-08-03 04:29:33 +00:00
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
else if ( Debugger ! = NULL & & Inputs - > uiEnterDebugger - > Pressed ( ) )
{
// Break execution and enter debugger
Debugger - > ForceBreak ( true ) ;
}
}
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_DEBUGGER
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Frame rate and limiting
unsigned currentFPSTicks = SDL_GetTicks ( ) ;
2017-03-27 03:19:15 +00:00
if ( s_runtime_config [ " ShowFrameRate " ] . ValueAs < bool > ( ) )
2016-04-24 01:40:16 +00:00
{
+ + fpsFramesElapsed ;
if ( ( currentFPSTicks - prevFPSTicks ) > = 1000 ) // update FPS every 1 second (each tick is 1 ms)
{
sprintf ( titleStr , " %s - %1.1f FPS%s " , baseTitleStr , ( float ) fpsFramesElapsed / ( ( float ) ( currentFPSTicks - prevFPSTicks ) / 1000.0f ) , paused ? " (Paused) " : " " ) ;
2020-04-19 08:34:58 +00:00
SDL_SetWindowTitle ( s_window , titleStr ) ;
2016-04-24 01:40:16 +00:00
prevFPSTicks = currentFPSTicks ; // reset tick count
fpsFramesElapsed = 0 ; // reset frame count
}
}
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
if ( paused | | s_runtime_config [ " Throttle " ] . ValueAs < bool > ( ) )
2016-04-24 01:40:16 +00:00
{
2017-03-27 03:19:15 +00:00
UINT32 endTime = SDL_GetTicks ( ) ;
UINT32 diff = endTime - startTime ;
UINT32 frameTime = ( UINT32 ) ( 1000 / 60.f ) ; // 60 fps, we could roll with 57.5? that would be a jerk fest on 60hz screens though
2016-10-21 00:04:12 +00:00
if ( diff < frameTime ) {
SuperSleep ( frameTime - diff ) ;
}
2016-04-24 01:40:16 +00:00
}
if ( dumpTimings & & ! paused )
2016-04-24 03:01:42 +00:00
{
CModel3 * M = dynamic_cast < CModel3 * > ( Model3 ) ;
if ( M )
M - > DumpTimings ( ) ;
}
2016-04-24 01:40:16 +00:00
}
// Make sure all threads are paused before shutting down
2020-07-27 10:28:48 +00:00
Model3 - > PauseThreads ( ) ;
2011-08-03 04:29:33 +00:00
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
// If debugger was supplied, detach it from system and restore old logger
if ( Debugger ! = NULL )
{
Debugger - > Detach ( ) ;
SetLogger ( oldLogger ) ;
}
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_DEBUGGER
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Save NVRAM
SaveNVRAM ( Model3 ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
// Close audio
CloseAudio ( ) ;
2016-04-24 03:01:42 +00:00
// Shut down renderers
2016-04-24 01:40:16 +00:00
delete Render2D ;
delete Render3D ;
2016-04-24 03:01:42 +00:00
2016-04-24 01:40:16 +00:00
return 0 ;
2011-08-03 04:29:33 +00:00
2016-04-24 01:40:16 +00:00
// Quit with an error
2011-08-03 04:29:33 +00:00
QuitError :
2016-04-24 01:40:16 +00:00
delete Render2D ;
delete Render3D ;
return 1 ;
2011-08-03 04:29:33 +00:00
}
/******************************************************************************
2017-03-27 03:19:15 +00:00
Entry Point and Command Line Procesing
2011-08-03 04:29:33 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-03-27 03:19:15 +00:00
static const char s_configFilePath [ ] = { " Config/Supermodel.ini " } ;
static const char s_gameXMLFilePath [ ] = { " Config/Games.xml " } ;
// Create and configure inputs
2019-01-13 16:20:19 +00:00
static bool ConfigureInputs ( CInputs * Inputs , Util : : Config : : Node * fileConfig , Util : : Config : : Node * runtimeConfig , const Game & game , bool configure )
2011-08-03 04:29:33 +00:00
{
2017-03-27 03:19:15 +00:00
static const char configFileComment [ ] = {
" ; \n "
" ; Supermodel Configuration File \n "
" ; \n "
2016-04-24 01:40:16 +00:00
} ;
2020-07-27 10:28:48 +00:00
2019-01-13 16:20:19 +00:00
Inputs - > LoadFromConfig ( * runtimeConfig ) ;
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
// If the user wants to configure the inputs, do that now
if ( configure )
2016-04-24 01:40:16 +00:00
{
2020-04-19 08:34:58 +00:00
std : : string title ( " Supermodel - " ) ;
if ( game . name . empty ( ) )
title . append ( " Configuring Default Inputs... " ) ;
else
title . append ( Util : : Format ( ) < < " Configuring Inputs for: " < < game . title ) ;
SDL_SetWindowTitle ( s_window , title . c_str ( ) ) ;
2019-01-13 16:20:19 +00:00
// Extract the relevant INI section (which will be the global section if no
// game was specified, otherwise the game's node) in the file config, which
// will be written back to disk
Util : : Config : : Node * fileConfigRoot = game . name . empty ( ) ? fileConfig : fileConfig - > TryGet ( game . name ) ;
if ( fileConfigRoot = = nullptr )
{
fileConfigRoot = & fileConfig - > Add ( game . name ) ;
}
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
// Configure the inputs
2019-01-13 16:20:19 +00:00
if ( Inputs - > ConfigureInputs ( game , xOffset , yOffset , xRes , yRes ) )
2016-04-24 01:40:16 +00:00
{
2017-03-27 03:19:15 +00:00
// Write input configuration and input system settings to config file
2019-01-13 16:20:19 +00:00
Inputs - > StoreToConfig ( fileConfigRoot ) ;
Util : : Config : : WriteINIFile ( s_configFilePath , * fileConfig , configFileComment ) ;
2020-07-27 10:28:48 +00:00
2019-01-13 16:20:19 +00:00
// Also save to runtime configuration in case we proceed and play
Inputs - > StoreToConfig ( runtimeConfig ) ;
2016-04-24 01:40:16 +00:00
}
else
2017-03-27 03:19:15 +00:00
puts ( " Configuration aborted... " ) ;
puts ( " " ) ;
2016-04-24 01:40:16 +00:00
}
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
return OKAY ;
2011-08-03 04:29:33 +00:00
}
2017-03-27 03:19:15 +00:00
// Print game list
static void PrintGameList ( const std : : string & xml_file , const std : : map < std : : string , Game > & games )
{
if ( games . empty ( ) )
{
puts ( " No games defined. " ) ;
return ;
}
printf ( " Games defined in %s: \n " , xml_file . c_str ( ) ) ;
puts ( " " ) ;
puts ( " ROM Set Title " ) ;
puts ( " ------- ----- " ) ;
for ( auto & v : games )
{
const Game & game = v . second ;
2017-04-05 05:35:45 +00:00
printf ( " %s " , game . name . c_str ( ) ) ;
for ( int i = game . name . length ( ) ; i < 9 ; i + + ) // pad for alignment (no game ID should be more than 9 letters)
printf ( " " ) ;
if ( ! game . version . empty ( ) )
printf ( " %s (%s) \n " , game . title . c_str ( ) , game . version . c_str ( ) ) ;
else
printf ( " %s \n " , game . title . c_str ( ) ) ;
}
}
2017-03-27 03:19:15 +00:00
static void LogConfig ( const Util : : Config : : Node & config )
{
InfoLog ( " Runtime configuration: " ) ;
for ( auto & child : config )
{
if ( child . Empty ( ) )
InfoLog ( " %s=<empty> " , child . Key ( ) . c_str ( ) ) ;
else
InfoLog ( " %s=%s " , child . Key ( ) . c_str ( ) , child . ValueAs < std : : string > ( ) . c_str ( ) ) ;
}
InfoLog ( " " ) ;
}
static Util : : Config : : Node DefaultConfig ( )
{
Util : : Config : : Node config ( " Global " ) ;
config . Set ( " GameXMLFile " , s_gameXMLFilePath ) ;
2018-03-26 22:59:06 +00:00
config . Set ( " InitStateFile " , " " ) ;
2017-03-27 03:19:15 +00:00
// CModel3
config . Set ( " MultiThreaded " , true ) ;
config . Set ( " GPUMultiThreaded " , true ) ;
config . Set ( " PowerPCFrequency " , " 50 " ) ;
2017-08-11 00:41:10 +00:00
// 2D and 3D graphics engines
2017-03-27 03:19:15 +00:00
config . Set ( " MultiTexture " , false ) ;
config . Set ( " VertexShader " , " " ) ;
config . Set ( " FragmentShader " , " " ) ;
2017-08-11 00:41:10 +00:00
config . Set ( " VertexShaderFog " , " " ) ;
config . Set ( " FragmentShaderFog " , " " ) ;
config . Set ( " VertexShader2D " , " " ) ;
config . Set ( " FragmentShader2D " , " " ) ;
2017-03-27 03:19:15 +00:00
// CSoundBoard
config . Set ( " EmulateSound " , true ) ;
config . Set ( " Balance " , false ) ;
// CDSB
config . Set ( " EmulateDSB " , true ) ;
config . Set ( " SoundVolume " , " 100 " ) ;
config . Set ( " MusicVolume " , " 100 " ) ;
2020-08-22 20:41:47 +00:00
// Other sound options
config . Set ( " LegacySoundDSP " , false ) ; // New config option for games that do not play correctly with MAME's SCSP sound core.
2017-03-27 03:19:15 +00:00
// CDriveBoard
# ifdef SUPERMODEL_WIN32
config . Set ( " ForceFeedback " , false ) ;
# endif
2020-07-27 10:28:48 +00:00
// Platform-specific/UI
2017-08-10 15:59:16 +00:00
config . Set ( " New3DEngine " , true ) ;
2018-09-13 12:50:34 +00:00
config . Set ( " QuadRendering " , false ) ;
2017-03-27 03:19:15 +00:00
config . Set ( " XResolution " , " 496 " ) ;
config . Set ( " YResolution " , " 384 " ) ;
config . Set ( " FullScreen " , false ) ;
config . Set ( " WideScreen " , false ) ;
2018-05-03 03:46:44 +00:00
config . Set ( " Stretch " , false ) ;
2017-03-27 03:19:15 +00:00
config . Set ( " VSync " , true ) ;
config . Set ( " Throttle " , true ) ;
config . Set ( " ShowFrameRate " , false ) ;
config . Set ( " Crosshairs " , int ( 0 ) ) ;
config . Set ( " FlipStereo " , false ) ;
# ifdef SUPERMODEL_WIN32
config . Set ( " InputSystem " , " dinput " ) ;
// DirectInput ForceFeedback
config . Set ( " DirectInputConstForceLeftMax " , " 100 " ) ;
config . Set ( " DirectInputConstForceRightMax " , " 100 " ) ;
config . Set ( " DirectInputSelfCenterMax " , " 100 " ) ;
config . Set ( " DirectInputFrictionMax " , " 100 " ) ;
config . Set ( " DirectInputVibrateMax " , " 100 " ) ;
// XInput ForceFeedback
config . Set ( " XInputConstForceThreshold " , " 30 " ) ;
config . Set ( " XInputConstForceMax " , " 100 " ) ;
config . Set ( " XInputVibrateMax " , " 100 " ) ;
2018-01-07 14:07:59 +00:00
# ifdef NET_BOARD
// NetBoard
config . Set ( " EmulateNet " , false ) ;
# endif
2017-03-27 03:19:15 +00:00
# else
config . Set ( " InputSystem " , " sdl " ) ;
# endif
config . Set ( " Outputs " , " none " ) ;
return config ;
}
2011-08-03 04:29:33 +00:00
static void Title ( void )
{
2016-04-24 01:40:16 +00:00
puts ( " Supermodel: A Sega Model 3 Arcade Emulator (Version " SUPERMODEL_VERSION " ) " ) ;
2020-07-27 10:28:48 +00:00
puts ( " Copyright 2011-2020 by Bart Trzynadlowski, Nik Henson, Ian Curtis, " ) ;
2018-05-03 03:46:44 +00:00
puts ( " Harry Tuttle, and Spindizzi \n " ) ;
2011-08-03 04:29:33 +00:00
}
static void Help ( void )
{
2017-03-27 03:19:15 +00:00
Util : : Config : : Node defaultConfig = DefaultConfig ( ) ;
2016-04-24 01:40:16 +00:00
puts ( " Usage: Supermodel <romset> [options] " ) ;
puts ( " ROM set must be a valid ZIP file containing a single game. " ) ;
puts ( " " ) ;
puts ( " General Options: " ) ;
2017-08-11 00:41:10 +00:00
puts ( " -?, -h, -help, --help Print this help text " ) ;
puts ( " -print-games List supported games and quit " ) ;
printf ( " -game-xml-file=<file> ROM set definition file [Default: %s] \n " , s_gameXMLFilePath ) ;
2016-04-24 01:40:16 +00:00
puts ( " " ) ;
puts ( " Core Options: " ) ;
2017-08-11 00:41:10 +00:00
printf ( " -ppc-frequency=<freq> PowerPC frequency in MHz [Default: %d] \n " , defaultConfig [ " PowerPCFrequency " ] . ValueAs < unsigned > ( ) ) ;
puts ( " -no-threads Disable multi-threading entirely " ) ;
puts ( " -gpu-multi-threaded Run graphics rendering in separate thread [Default] " ) ;
puts ( " -no-gpu-thread Run graphics rendering in main thread " ) ;
2018-03-26 22:59:06 +00:00
puts ( " -load-state=<file> Load save state after starting " ) ;
2016-04-24 01:40:16 +00:00
puts ( " " ) ;
puts ( " Video Options: " ) ;
2017-08-11 00:41:10 +00:00
puts ( " -res=<x>,<y> Resolution [Default: 496,384] " ) ;
puts ( " -window Windowed mode [Default] " ) ;
puts ( " -fullscreen Full screen mode " ) ;
puts ( " -wide-screen Expand 3D field of view to screen width " ) ;
2018-05-03 03:46:44 +00:00
puts ( " -stretch Fit viewport to resolution, ignoring aspect ratio " ) ;
2017-08-11 00:41:10 +00:00
puts ( " -no-throttle Disable 60 Hz frame rate lock " ) ;
puts ( " -vsync Lock to vertical refresh rate [Default] " ) ;
puts ( " -no-vsync Do not lock to vertical refresh rate " ) ;
puts ( " -show-fps Display frame rate in window title bar " ) ;
puts ( " -crosshairs=<n> Crosshairs configuration for gun games: " ) ;
puts ( " 0=none [Default], 1=P1 only, 2=P2 only, 3=P1 & P2 " ) ;
puts ( " -new3d New 3D engine by Ian Curtis [Default] " ) ;
2018-09-14 20:31:34 +00:00
puts ( " -quad-rendering Enable proper quad rendering " ) ;
2017-08-11 00:41:10 +00:00
puts ( " -legacy3d Legacy 3D engine (faster but less accurate) " ) ;
puts ( " -multi-texture Use 8 texture maps for decoding (legacy engine) " ) ;
puts ( " -no-multi-texture Decode to single texture (legacy engine) [Default] " ) ;
puts ( " -vert-shader=<file> Load Real3D vertex shader for 3D rendering " ) ;
puts ( " -frag-shader=<file> Load Real3D fragment shader for 3D rendering " ) ;
puts ( " -vert-shader-fog=<file> Load Real3D scroll fog vertex shader (new engine) " ) ;
puts ( " -frag-shader-fog=<file> Load Real3D scroll fog fragment shader (new engine) " ) ;
puts ( " -vert-shader-2d=<file> Load tile map vertex shader " ) ;
puts ( " -frag-shader-2d=<file> Load tile map fragment shader " ) ;
puts ( " -print-gl-info Print OpenGL driver information and quit " ) ;
2016-04-24 01:40:16 +00:00
puts ( " " ) ;
puts ( " Audio Options: " ) ;
2017-08-11 00:41:10 +00:00
puts ( " -sound-volume=<vol> Volume of SCSP-generated sound in %, applies only " ) ;
puts ( " when Digital Sound Board is present [Default: 100] " ) ;
puts ( " -music-volume=<vol> Digital Sound Board volume in % [Default: 100] " ) ;
puts ( " -balance=<bal> Relative front/rear balance in % [Default: 0] " ) ;
puts ( " -flip-stereo Swap left and right audio channels " ) ;
puts ( " -no-sound Disable sound board emulation (sound effects) " ) ;
puts ( " -no-dsb Disable Digital Sound Board (MPEG music) " ) ;
2020-08-22 20:41:47 +00:00
puts ( " -legacy-sound Enable ElSemi's legacy SCSP DSP emulator from 0.2a. Recommended for Sega Rally 2 as engine sound does not work with MAME's implementation. " ) ;
puts ( " -no-legacy-sound Disable ElSemi's legacy SCSP DSP emulator and use MAME's implementation instead " ) ;
2016-04-24 01:40:16 +00:00
puts ( " " ) ;
2018-01-07 14:07:59 +00:00
# ifdef NET_BOARD
puts ( " Net Options: " ) ;
puts ( " -no-net Disable net board emulation (default) " ) ;
puts ( " -net Enable net board emulation (not working ATM - need -no-threads) " ) ;
puts ( " " ) ;
# endif
2016-04-24 01:40:16 +00:00
puts ( " Input Options: " ) ;
2011-09-22 07:24:15 +00:00
# ifdef SUPERMODEL_WIN32
2017-08-11 00:41:10 +00:00
puts ( " -force-feedback Enable force feedback (DirectInput, XInput) " ) ;
2011-09-22 07:24:15 +00:00
# endif
2017-08-11 00:41:10 +00:00
puts ( " -config-inputs Configure keyboards, mice, and game controllers " ) ;
2011-08-27 21:37:37 +00:00
# ifdef SUPERMODEL_WIN32
2017-08-11 00:41:10 +00:00
printf ( " -input-system=<s> Input system [Default: %s] \n " , defaultConfig [ " InputSystem " ] . ValueAs < std : : string > ( ) . c_str ( ) ) ;
printf ( " -outputs=<s> Outputs [Default: %s] \n " , defaultConfig [ " Outputs " ] . ValueAs < std : : string > ( ) . c_str ( ) ) ;
2011-08-27 21:37:37 +00:00
# endif
2017-08-11 00:41:10 +00:00
puts ( " -print-inputs Prints current input configuration " ) ;
2016-04-24 01:40:16 +00:00
puts ( " " ) ;
2011-09-17 23:42:42 +00:00
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
puts ( " Debug Options: " ) ;
2017-08-11 00:41:10 +00:00
puts ( " -disable-debugger Completely disable debugger functionality " ) ;
puts ( " -enter-debugger Enter debugger at start of emulation " ) ;
2016-04-24 01:40:16 +00:00
puts ( " " ) ;
2011-09-17 23:41:12 +00:00
# endif // SUPERMODEL_DEBUGGER
2011-08-03 04:29:33 +00:00
}
2017-03-27 03:19:15 +00:00
struct ParsedCommandLine
2011-08-03 04:29:33 +00:00
{
2017-03-27 03:19:15 +00:00
Util : : Config : : Node config = Util : : Config : : Node ( " CommandLine " ) ;
std : : vector < std : : string > rom_files ;
2017-04-09 15:08:02 +00:00
bool print_help = false ;
2017-03-27 03:19:15 +00:00
bool print_games = false ;
bool print_gl_info = false ;
bool config_inputs = false ;
bool print_inputs = false ;
bool disable_debugger = false ;
bool enter_debugger = false ;
# ifdef DEBUG
std : : string gfx_state ;
# endif
} ;
static ParsedCommandLine ParseCommandLine ( int argc , char * * argv )
{
ParsedCommandLine cmd_line ;
const std : : map < std : : string , std : : string > valued_options
{ // -option=value
{ " -game-xml-file " , " GameXMLFile " } ,
2018-03-26 22:59:06 +00:00
{ " -load-state " , " InitStateFile " } ,
2017-03-27 03:19:15 +00:00
{ " -ppc-frequency " , " PowerPCFrequency " } ,
{ " -crosshairs " , " Crosshairs " } ,
{ " -vert-shader " , " VertexShader " } ,
{ " -frag-shader " , " FragmentShader " } ,
2017-08-11 00:41:10 +00:00
{ " -vert-shader-fog " , " VertexShaderFog " } ,
{ " -frag-shader-fog " , " FragmentShaderFog " } ,
{ " -vert-shader-2d " , " VertexShader2D " } ,
{ " -frag-shader-2d " , " FragmentShader2D " } ,
2017-03-27 03:19:15 +00:00
{ " -sound-volume " , " SoundVolume " } ,
{ " -music-volume " , " MusicVolume " } ,
{ " -balance " , " Balance " } ,
{ " -input-system " , " InputSystem " } ,
{ " -outputs " , " Outputs " }
} ;
const std : : map < std : : string , std : : pair < std : : string , bool > > bool_options
{ // -option
{ " -threads " , { " MultiThreaded " , true } } ,
{ " -no-threads " , { " MultiThreaded " , false } } ,
{ " -gpu-multi-threaded " , { " GPUMultiThreaded " , true } } ,
{ " -no-gpu-thread " , { " GPUMultiThreaded " , false } } ,
{ " -window " , { " FullScreen " , false } } ,
{ " -fullscreen " , { " FullScreen " , true } } ,
{ " -no-wide-screen " , { " WideScreen " , false } } ,
{ " -wide-screen " , { " WideScreen " , true } } ,
2018-05-03 03:46:44 +00:00
{ " -stretch " , { " Stretch " , true } } ,
{ " -no-stretch " , { " Stretch " , false } } ,
2017-03-27 03:19:15 +00:00
{ " -no-multi-texture " , { " MultiTexture " , false } } ,
{ " -multi-texture " , { " MultiTexture " , true } } ,
{ " -throttle " , { " Throttle " , true } } ,
{ " -no-throttle " , { " Throttle " , false } } ,
{ " -vsync " , { " VSync " , true } } ,
{ " -no-vsync " , { " VSync " , false } } ,
{ " -show-fps " , { " ShowFrameRate " , true } } ,
{ " -no-fps " , { " ShowFrameRate " , false } } ,
{ " -new3d " , { " New3DEngine " , true } } ,
2018-09-13 12:50:34 +00:00
{ " -quad-rendering " , { " QuadRendering " , true } } ,
2017-03-27 03:19:15 +00:00
{ " -legacy3d " , { " New3DEngine " , false } } ,
{ " -no-flip-stereo " , { " FlipStereo " , false } } ,
{ " -flip-stereo " , { " FlipStereo " , true } } ,
{ " -sound " , { " EmulateSound " , true } } ,
{ " -no-sound " , { " EmulateSound " , false } } ,
{ " -dsb " , { " EmulateDSB " , true } } ,
{ " -no-dsb " , { " EmulateDSB " , false } } ,
2018-01-07 14:07:59 +00:00
# ifdef NET_BOARD
2018-05-03 03:46:44 +00:00
{ " -net " , { " EmulateNet " , true } } ,
{ " -no-net " , { " EmulateNet " , false } } ,
2018-01-07 14:07:59 +00:00
# endif
2017-03-27 03:19:15 +00:00
# ifdef SUPERMODEL_WIN32
{ " -no-force-feedback " , { " ForceFeedback " , false } } ,
{ " -force-feedback " , { " ForceFeedback " , true } } ,
# endif
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
} ;
for ( int i = 1 ; i < argc ; i + + )
2016-04-24 01:40:16 +00:00
{
2017-03-27 03:19:15 +00:00
std : : string arg ( argv [ i ] ) ;
if ( arg [ 0 ] = = ' - ' )
{
// First, check maps
size_t idx_equals = arg . find_first_of ( ' = ' ) ;
if ( idx_equals ! = std : : string : : npos )
{
std : : string option ( arg . begin ( ) , arg . begin ( ) + idx_equals ) ;
std : : string value ( arg . begin ( ) + idx_equals + 1 , arg . end ( ) ) ;
if ( value . length ( ) = = 0 )
{
ErrorLog ( " Argument to '%s' cannot be blank. " , option . c_str ( ) ) ;
continue ;
}
auto it = valued_options . find ( option ) ;
if ( it ! = valued_options . end ( ) )
{
const std : : string & config_key = it - > second ;
cmd_line . config . Set ( config_key , value ) ;
continue ;
}
}
else
{
auto it = bool_options . find ( arg ) ;
if ( it ! = bool_options . end ( ) )
{
const std : : string & config_key = it - > second . first ;
bool value = it - > second . second ;
cmd_line . config . Set ( config_key , value ) ;
continue ;
}
else if ( valued_options . find ( arg ) ! = valued_options . end ( ) )
{
ErrorLog ( " '%s' requires an argument. " , argv [ i ] ) ;
continue ;
}
}
// Fell through -- handle special cases
if ( arg = = " -? " | | arg = = " -h " | | arg = = " -help " | | arg = = " --help " )
2017-04-09 15:08:02 +00:00
cmd_line . print_help = true ;
2017-03-27 03:19:15 +00:00
else if ( arg = = " -print-games " )
cmd_line . print_games = true ;
else if ( arg = = " -res " | | arg . find ( " -res= " ) = = 0 )
{
std : : vector < std : : string > parts = Util : : Format ( arg ) . Split ( ' = ' ) ;
if ( parts . size ( ) ! = 2 )
ErrorLog ( " '-res' requires both a width and height (e.g., '-res=496,384'). " ) ;
else
{
unsigned x , y ;
if ( 2 = = sscanf ( & argv [ i ] [ 4 ] , " =%d,%d " , & x , & y ) )
{
std : : string xres = Util : : Format ( ) < < x ;
std : : string yres = Util : : Format ( ) < < y ;
cmd_line . config . Set ( " XResolution " , xres ) ;
cmd_line . config . Set ( " YResolution " , yres ) ;
}
else
ErrorLog ( " '-res' requires both a width and height (e.g., '-res=496,384'). " ) ;
}
}
else if ( arg = = " -print-gl-info " )
cmd_line . print_gl_info = true ;
else if ( arg = = " -config-inputs " )
cmd_line . config_inputs = true ;
else if ( arg = = " -print-inputs " )
cmd_line . print_inputs = true ;
# ifdef SUPERMODEL_DEBUGGER
else if ( arg = = " -disable-debugger " )
cmd_line . disable_debugger = true ;
else if ( arg = = " -enter-debugger " )
cmd_line . enter_debugger = true ;
# endif
# ifdef DEBUG
2017-04-04 23:28:06 +00:00
else if ( arg = = " -gfx-state " | | arg . find ( " -gfx-state= " ) = = 0 )
2017-03-27 03:19:15 +00:00
{
std : : vector < std : : string > parts = Util : : Format ( arg ) . Split ( ' = ' ) ;
if ( parts . size ( ) ! = 2 )
ErrorLog ( " '-gfx-state' requires a file name. " ) ;
else
cmd_line . gfx_state = parts [ 1 ] ;
}
# endif
else
ErrorLog ( " Ignoring unrecognized option: %s " , argv [ i ] ) ;
}
else
cmd_line . rom_files . emplace_back ( arg ) ;
2016-04-24 01:40:16 +00:00
}
2017-03-27 03:19:15 +00:00
return cmd_line ;
2011-08-03 04:29:33 +00:00
}
/*
* main ( argc , argv ) :
*
* Program entry point .
*/
2016-07-05 01:15:58 +00:00
2011-08-03 04:29:33 +00:00
int main ( int argc , char * * argv )
{
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
bool cmdEnterDebugger = false ;
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
Title ( ) ;
if ( argc < = 1 )
{
Help ( ) ;
return 0 ;
}
// Create default logger
CFileLogger Logger ( DEBUG_LOG_FILE , ERROR_LOG_FILE ) ;
Logger . ClearLogs ( ) ;
SetLogger ( & Logger ) ;
InfoLog ( " Started as: " ) ;
for ( int i = 0 ; i < argc ; i + + )
2017-03-27 03:19:15 +00:00
InfoLog ( " argv[%d] = %s " , i , argv [ i ] ) ;
2016-04-24 01:40:16 +00:00
InfoLog ( " " ) ;
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
// Load config and parse command line
auto cmd_line = ParseCommandLine ( argc , argv ) ;
2017-04-09 15:08:02 +00:00
if ( cmd_line . print_help )
{
Help ( ) ;
return 0 ;
}
if ( cmd_line . print_gl_info )
2016-04-24 01:40:16 +00:00
{
2020-04-19 08:34:58 +00:00
// We must exit after this because CreateGLScreen() is used
2017-03-27 03:19:15 +00:00
PrintGLInfo ( true , false , false ) ;
return 0 ;
}
# ifdef DEBUG
s_gfxStatePath . assign ( cmd_line . gfx_state ) ;
2011-09-22 07:24:15 +00:00
# endif
2017-03-27 03:19:15 +00:00
bool print_games = cmd_line . print_games ;
bool rom_specified = ! cmd_line . rom_files . empty ( ) ;
2019-02-19 09:24:31 +00:00
if ( ! rom_specified & & ! print_games & & ! cmd_line . config_inputs & & ! cmd_line . print_inputs )
2017-03-27 03:19:15 +00:00
{
2020-07-27 10:28:48 +00:00
ErrorLog ( " No ROM file specified. " ) ;
2017-03-27 03:19:15 +00:00
return 0 ;
}
// Load game and resolve run-time config
Game game ;
ROMSet rom_set ;
Util : : Config : : Node fileConfig ( " Global " ) ;
{
2019-01-13 16:20:19 +00:00
Util : : Config : : Node fileConfigWithDefaults ( " Global " ) ;
2017-03-27 03:19:15 +00:00
Util : : Config : : Node config3 ( " Global " ) ;
Util : : Config : : Node config4 ( " Global " ) ;
Util : : Config : : FromINIFile ( & fileConfig , s_configFilePath ) ;
Util : : Config : : MergeINISections ( & fileConfigWithDefaults , DefaultConfig ( ) , fileConfig ) ; // apply .ini file's global section over defaults
Util : : Config : : MergeINISections ( & config3 , fileConfigWithDefaults , cmd_line . config ) ; // apply command line overrides
if ( rom_specified | | print_games )
2016-04-24 01:40:16 +00:00
{
2017-03-27 03:19:15 +00:00
std : : string xml_file = config3 [ " GameXMLFile " ] . ValueAs < std : : string > ( ) ;
GameLoader loader ( xml_file ) ;
if ( print_games )
2016-04-24 01:40:16 +00:00
{
2017-03-27 03:19:15 +00:00
PrintGameList ( xml_file , loader . GetGames ( ) ) ;
return 0 ;
2016-04-24 01:40:16 +00:00
}
2017-03-27 03:19:15 +00:00
if ( loader . Load ( & game , & rom_set , * cmd_line . rom_files . begin ( ) ) )
return 1 ;
Util : : Config : : MergeINISections ( & config4 , config3 , fileConfig [ game . name ] ) ; // apply game-specific config
2016-04-24 17:06:14 +00:00
}
2016-04-24 01:40:16 +00:00
else
2017-03-27 03:19:15 +00:00
config4 = config3 ;
Util : : Config : : MergeINISections ( & s_runtime_config , config4 , cmd_line . config ) ; // apply command line overrides once more
2016-04-24 01:40:16 +00:00
}
2017-03-27 03:19:15 +00:00
LogConfig ( s_runtime_config ) ;
2020-04-19 08:34:58 +00:00
std : : string selectedInputSystem = s_runtime_config [ " InputSystem " ] . ValueAs < std : : string > ( ) ;
2017-03-27 03:19:15 +00:00
2016-04-24 01:40:16 +00:00
// Initialize SDL (individual subsystems get initialized later)
if ( SDL_Init ( 0 ) ! = 0 )
{
ErrorLog ( " Unable to initialize SDL: %s \n " , SDL_GetError ( ) ) ;
return 1 ;
}
2020-04-19 08:34:58 +00:00
2020-07-27 10:28:48 +00:00
// Begin initializing various subsystems...
2020-04-19 08:34:58 +00:00
int exitCode = 0 ;
IEmulator * Model3 = nullptr ;
CInputSystem * InputSystem = nullptr ;
CInputs * Inputs = nullptr ;
COutputs * Outputs = nullptr ;
# ifdef SUPERMODEL_DEBUGGER
Debugger : : CSupermodelDebugger * Debugger = NULL ;
# endif // SUPERMODEL_DEBUGGER
2020-07-27 10:28:48 +00:00
2020-04-19 08:34:58 +00:00
// Create a window
xRes = 496 ;
yRes = 384 ;
if ( OKAY ! = CreateGLScreen ( " Supermodel " , false , & xOffset , & yOffset , & xRes , & yRes , & totalXRes , & totalYRes , false , false ) )
{
exitCode = 1 ;
goto Exit ;
}
2020-07-27 10:28:48 +00:00
2016-04-24 03:01:42 +00:00
// Create Model 3 emulator
2016-04-24 17:06:14 +00:00
# ifdef DEBUG
2020-04-19 08:34:58 +00:00
Model3 = s_gfxStatePath . empty ( ) ? static_cast < IEmulator * > ( new CModel3 ( s_runtime_config ) ) : static_cast < IEmulator * > ( new CModel3GraphicsState ( s_runtime_config , s_gfxStatePath ) ) ;
2016-04-24 17:06:14 +00:00
# else
2020-04-19 08:34:58 +00:00
Model3 = new CModel3 ( s_runtime_config ) ;
2016-04-24 17:06:14 +00:00
# endif
2011-08-03 04:29:33 +00:00
2016-04-24 01:40:16 +00:00
// Create input system
2017-03-27 03:19:15 +00:00
if ( selectedInputSystem = = " sdl " )
2016-04-24 01:40:16 +00:00
InputSystem = new CSDLInputSystem ( ) ;
2011-08-03 04:29:33 +00:00
# ifdef SUPERMODEL_WIN32
2017-03-27 03:19:15 +00:00
else if ( selectedInputSystem = = " dinput " )
2020-04-19 08:34:58 +00:00
InputSystem = new CDirectInputSystem ( s_runtime_config , s_window , false , false ) ;
2017-03-27 03:19:15 +00:00
else if ( selectedInputSystem = = " xinput " )
2020-04-19 08:34:58 +00:00
InputSystem = new CDirectInputSystem ( s_runtime_config , s_window , false , true ) ;
2017-03-27 03:19:15 +00:00
else if ( selectedInputSystem = = " rawinput " )
2020-04-19 08:34:58 +00:00
InputSystem = new CDirectInputSystem ( s_runtime_config , s_window , true , false ) ;
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_WIN32
2016-04-24 01:40:16 +00:00
else
{
2017-03-27 03:19:15 +00:00
ErrorLog ( " Unknown input system: %s \n " , selectedInputSystem . c_str ( ) ) ;
2016-04-24 01:40:16 +00:00
exitCode = 1 ;
goto Exit ;
}
// Create inputs from input system (configuring them if required)
Inputs = new CInputs ( InputSystem ) ;
if ( ! Inputs - > Initialize ( ) )
{
ErrorLog ( " Unable to initalize inputs. \n " ) ;
exitCode = 1 ;
goto Exit ;
}
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
// NOTE: fileConfig is passed so that the global section is used for input settings
// and because this function may write out a new config file, which must preserve
// all sections. We don't want to pollute the output with built-in defaults.
2019-01-13 16:20:19 +00:00
if ( ConfigureInputs ( Inputs , & fileConfig , & s_runtime_config , game , cmd_line . config_inputs ) )
2016-04-24 01:40:16 +00:00
{
exitCode = 1 ;
goto Exit ;
}
2017-03-27 03:19:15 +00:00
if ( cmd_line . print_inputs )
2016-04-24 01:40:16 +00:00
{
Inputs - > PrintInputs ( NULL ) ;
InputSystem - > PrintSettings ( ) ;
}
2020-07-27 10:28:48 +00:00
2017-03-27 03:19:15 +00:00
if ( ! rom_specified )
goto Exit ;
2016-04-24 01:40:16 +00:00
2020-07-27 10:28:48 +00:00
// Create outputs
2012-07-15 21:04:46 +00:00
# ifdef SUPERMODEL_WIN32
2016-04-24 01:40:16 +00:00
{
2017-03-27 03:19:15 +00:00
std : : string outputs = s_runtime_config [ " Outputs " ] . ValueAs < std : : string > ( ) ;
if ( outputs = = " none " )
Outputs = NULL ;
else if ( outputs = = " win " )
Outputs = new CWinOutputs ( ) ;
else
{
ErrorLog ( " Unknown outputs: %s \n " , outputs . c_str ( ) ) ;
exitCode = 1 ;
goto Exit ;
}
2016-04-24 01:40:16 +00:00
}
2012-07-15 21:04:46 +00:00
# endif // SUPERMODEL_WIN32
2016-04-24 01:40:16 +00:00
// Initialize outputs
if ( Outputs ! = NULL & & ! Outputs - > Initialize ( ) )
{
ErrorLog ( " Unable to initialize outputs. \n " ) ;
exitCode = 1 ;
goto Exit ;
}
2020-07-27 10:28:48 +00:00
2011-08-03 04:29:33 +00:00
# ifdef SUPERMODEL_DEBUGGER
2016-04-24 01:40:16 +00:00
// Create Supermodel debugger unless debugging is disabled
2017-03-27 03:19:15 +00:00
if ( ! cmd_line . disable_debugger )
2016-04-24 01:40:16 +00:00
{
2016-06-13 00:53:39 +00:00
Debugger = new Debugger : : CSupermodelDebugger ( dynamic_cast < CModel3 * > ( Model3 ) , Inputs , & Logger ) ;
2016-04-24 01:40:16 +00:00
// If -enter-debugger option was set force debugger to break straightaway
if ( cmdEnterDebugger )
Debugger - > ForceBreak ( true ) ;
}
// Fire up Supermodel with debugger
2017-03-27 03:19:15 +00:00
exitCode = Supermodel ( game , & rom_set , Model3 , Inputs , Outputs , Debugger ) ;
2016-04-24 01:40:16 +00:00
if ( Debugger ! = NULL )
delete Debugger ;
2011-08-03 04:29:33 +00:00
# else
2016-04-24 01:40:16 +00:00
// Fire up Supermodel
2017-03-27 03:19:15 +00:00
exitCode = Supermodel ( game , & rom_set , Model3 , Inputs , Outputs ) ;
2011-08-03 04:29:33 +00:00
# endif // SUPERMODEL_DEBUGGER
2016-04-24 03:01:42 +00:00
delete Model3 ;
2011-08-03 04:29:33 +00:00
Exit :
2016-04-24 01:40:16 +00:00
if ( Inputs ! = NULL )
delete Inputs ;
if ( InputSystem ! = NULL )
delete InputSystem ;
if ( Outputs ! = NULL )
delete Outputs ;
2020-04-19 08:34:58 +00:00
DestroyGLScreen ( ) ;
2016-04-24 01:40:16 +00:00
SDL_Quit ( ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
if ( exitCode )
InfoLog ( " Program terminated due to an error. " ) ;
else
InfoLog ( " Program terminated normally. " ) ;
2020-07-27 10:28:48 +00:00
2016-04-24 01:40:16 +00:00
return exitCode ;
2011-08-03 04:29:33 +00:00
}