2024-01-12 18:14:57 +00:00
# include <sstream>
# include <chrono>
# include "argparse/argparse.hpp"
# include "sha1/sha1.hpp"
# include "utils.hpp"
# include "nlohmann/json.hpp"
2024-01-15 19:56:58 +00:00
# ifdef _USE_QUICKNES
# include "quickNESInstance.hpp"
# endif
# ifdef _USE_QUICKERNES
# include "quickerNESInstance.hpp"
# endif
2024-01-12 18:14:57 +00:00
int main ( int argc , char * argv [ ] )
{
// Parsing command line arguments
2024-01-16 21:22:36 +00:00
argparse : : ArgumentParser program ( " tester " , " 1.0 " ) ;
2024-01-12 18:14:57 +00:00
program . add_argument ( " scriptFile " )
. help ( " Path to the test script file to run. " )
. required ( ) ;
2024-01-16 21:22:36 +00:00
program . add_argument ( " --fullCycle " )
. help ( " Performs the full load / advance frame / save cycle, as opposed to just advance frame " )
. default_value ( false )
. implicit_value ( true ) ;
program . add_argument ( " --hashOutputFile " )
. help ( " Path to write the hash output to. " )
. default_value ( std : : string ( " " ) ) ;
2024-01-12 18:14:57 +00:00
// Try to parse arguments
try { program . parse_args ( argc , argv ) ; }
catch ( const std : : runtime_error & err ) { EXIT_WITH_ERROR ( " %s \n %s " , err . what ( ) , program . help ( ) . str ( ) . c_str ( ) ) ; }
// Getting test script file path
std : : string scriptFilePath = program . get < std : : string > ( " scriptFile " ) ;
2024-01-16 21:22:36 +00:00
// Getting path where to save the hash output (if any)
std : : string hashOutputFile = program . get < std : : string > ( " --hashOutputFile " ) ;
// Getting reproduce flag
bool isFullCycle = program . get < bool > ( " --fullCycle " ) ;
2024-01-12 18:14:57 +00:00
// Loading script file
std : : string scriptJsonRaw ;
2024-01-15 19:56:58 +00:00
if ( loadStringFromFile ( scriptJsonRaw , scriptFilePath ) = = false ) EXIT_WITH_ERROR ( " Could not find/read script file: %s \n " , scriptFilePath . c_str ( ) ) ;
2024-01-12 18:14:57 +00:00
// Parsing script
const auto scriptJson = nlohmann : : json : : parse ( scriptJsonRaw ) ;
// Getting rom file path
if ( scriptJson . contains ( " Rom File " ) = = false ) EXIT_WITH_ERROR ( " Script file missing 'Rom File' entry \n " ) ;
if ( scriptJson [ " Rom File " ] . is_string ( ) = = false ) EXIT_WITH_ERROR ( " Script file 'Rom File' entry is not a string \n " ) ;
std : : string romFilePath = scriptJson [ " Rom File " ] . get < std : : string > ( ) ;
// Getting initial state file path
if ( scriptJson . contains ( " Initial State File " ) = = false ) EXIT_WITH_ERROR ( " Script file missing 'Initial State File' entry \n " ) ;
if ( scriptJson [ " Initial State File " ] . is_string ( ) = = false ) EXIT_WITH_ERROR ( " Script file 'Initial State File' entry is not a string \n " ) ;
std : : string initialStateFilePath = scriptJson [ " Initial State File " ] . get < std : : string > ( ) ;
// Getting sequence file path
if ( scriptJson . contains ( " Sequence File " ) = = false ) EXIT_WITH_ERROR ( " Script file missing 'Sequence File' entry \n " ) ;
if ( scriptJson [ " Sequence File " ] . is_string ( ) = = false ) EXIT_WITH_ERROR ( " Script file 'Sequence File' entry is not a string \n " ) ;
std : : string sequenceFilePath = scriptJson [ " Sequence File " ] . get < std : : string > ( ) ;
// Getting expected ROM SHA1 hash
if ( scriptJson . contains ( " Expected ROM SHA1 " ) = = false ) EXIT_WITH_ERROR ( " Script file missing 'Expected ROM SHA1' entry \n " ) ;
if ( scriptJson [ " Expected ROM SHA1 " ] . is_string ( ) = = false ) EXIT_WITH_ERROR ( " Script file 'Expected ROM SHA1' entry is not a string \n " ) ;
std : : string expectedROMSHA1 = scriptJson [ " Expected ROM SHA1 " ] . get < std : : string > ( ) ;
// Creating emulator instance
2024-01-15 19:56:58 +00:00
# ifdef _USE_QUICKNES
auto e = QuickNESInstance ( ) ;
# endif
# ifdef _USE_QUICKERNES
auto e = QuickerNESInstance ( ) ;
# endif
// Loading ROM File
e . loadROMFile ( romFilePath ) ;
// If an initial state is provided, load it now
if ( initialStateFilePath ! = " " ) e . loadStateFile ( initialStateFilePath ) ;
2024-01-12 20:13:51 +00:00
2024-01-13 10:10:42 +00:00
// Disable rendering
2024-01-14 05:12:32 +00:00
e . disableRendering ( ) ;
2024-01-13 10:10:42 +00:00
// Getting initial hash
2024-01-12 20:13:51 +00:00
auto initialHash = e . getStateHash ( ) ;
// Getting state size
const auto stateSize = e . getStateSize ( ) ;
2024-01-12 18:14:57 +00:00
// Getting actual ROM SHA1
auto romSHA1 = e . getRomSHA1 ( ) ;
// Checking with the expected SHA1 hash
if ( romSHA1 ! = expectedROMSHA1 ) EXIT_WITH_ERROR ( " Wrong ROM SHA1. Found: '%s', Expected: '%s' \n " , romSHA1 . c_str ( ) , expectedROMSHA1 . c_str ( ) ) ;
// Loading sequence file
std : : string sequenceRaw ;
2024-01-12 20:13:51 +00:00
if ( loadStringFromFile ( sequenceRaw , sequenceFilePath ) = = false ) EXIT_WITH_ERROR ( " [ERROR] Could not find or read from input sequence file: %s \n " , sequenceFilePath . c_str ( ) ) ;
2024-01-12 18:14:57 +00:00
// Building sequence information
const auto sequence = split ( sequenceRaw , ' ' ) ;
// Getting sequence lenght
const auto sequenceLength = sequence . size ( ) ;
2024-01-15 19:56:58 +00:00
// Getting emulation core name
std : : string emulationCoreName = e . getCoreName ( ) ;
2024-01-14 10:48:26 +00:00
2024-01-12 18:14:57 +00:00
// Printing test information
2024-01-13 12:35:54 +00:00
printf ( " [] ----------------------------------------- \n " ) ;
2024-01-12 20:13:51 +00:00
printf ( " [] Running Script: '%s' \n " , scriptFilePath . c_str ( ) ) ;
2024-01-17 17:21:44 +00:00
printf ( " [] Cycle Type: '%s' \n " , isFullCycle ? " Advance / Load / Advance / Save " : " Advance Only " ) ;
2024-01-15 19:56:58 +00:00
printf ( " [] Emulation Core: '%s' \n " , emulationCoreName . c_str ( ) ) ;
2024-01-12 20:13:51 +00:00
printf ( " [] ROM File: '%s' \n " , romFilePath . c_str ( ) ) ;
printf ( " [] ROM SHA1: '%s' \n " , romSHA1 . c_str ( ) ) ;
printf ( " [] Sequence File: '%s' \n " , sequenceFilePath . c_str ( ) ) ;
printf ( " [] Sequence Length: %lu \n " , sequenceLength ) ;
2024-01-13 10:10:42 +00:00
printf ( " [] Initial State Hash: 0x%lX%lX \n " , initialHash . first , initialHash . second ) ;
2024-01-12 20:13:51 +00:00
printf ( " [] State Size: %lu bytes \n " , stateSize ) ;
2024-01-13 12:35:54 +00:00
printf ( " [] ********** Running Test ********** \n " ) ;
2024-01-12 20:13:51 +00:00
2024-01-12 18:14:57 +00:00
fflush ( stdout ) ;
2024-01-16 21:22:36 +00:00
// Serializing initial state
2024-01-17 06:09:34 +00:00
uint8_t * currentState = ( uint8_t * ) malloc ( stateSize ) ;
2024-01-16 21:22:36 +00:00
e . serializeState ( currentState ) ;
2024-01-12 18:14:57 +00:00
// Actually running the sequence
auto t0 = std : : chrono : : high_resolution_clock : : now ( ) ;
2024-01-16 21:22:36 +00:00
for ( const std : : string & input : sequence )
{
2024-01-17 17:21:44 +00:00
if ( isFullCycle = = true ) e . advanceState ( input ) ;
2024-01-16 21:22:36 +00:00
if ( isFullCycle = = true ) e . deserializeState ( currentState ) ;
e . advanceState ( input ) ;
if ( isFullCycle = = true ) e . serializeState ( currentState ) ;
}
2024-01-12 18:14:57 +00:00
auto tf = std : : chrono : : high_resolution_clock : : now ( ) ;
// Calculating running time
auto dt = std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( tf - t0 ) . count ( ) ;
double elapsedTimeSeconds = ( double ) dt * 1.0e-9 ;
2024-01-12 20:13:51 +00:00
// Calculating final state hash
const auto finalStateHash = e . getStateHash ( ) ;
2024-01-16 21:22:36 +00:00
// Creating hash string
char hashStringBuffer [ 256 ] ;
sprintf ( hashStringBuffer , " 0x%lX%lX " , finalStateHash . first , finalStateHash . second ) ;
2024-01-12 20:13:51 +00:00
// Printing time information
printf ( " [] Elapsed time: %3.3fs \n " , ( double ) dt * 1.0e-9 ) ;
2024-01-17 17:21:44 +00:00
printf ( " [] Performance: %.3f inputs / s \n " , ( double ) sequenceLength / elapsedTimeSeconds ) ;
2024-01-16 21:22:36 +00:00
printf ( " [] Final State Hash: %s \n " , hashStringBuffer ) ;
// If saving hash, do it now
if ( hashOutputFile ! = " " ) saveStringToFile ( std : : string ( hashStringBuffer ) , hashOutputFile . c_str ( ) ) ;
2024-01-12 20:13:51 +00:00
2024-01-16 21:22:36 +00:00
// If reached this point, everything ran ok
return 0 ;
2024-01-12 18:14:57 +00:00
}