2024-01-12 18:14:57 +00:00
# include "argparse/argparse.hpp"
2024-01-20 10:21:34 +00:00
# include "nlohmann/json.hpp"
2024-01-12 18:14:57 +00:00
# include "sha1/sha1.hpp"
# include "utils.hpp"
2024-01-20 10:21:34 +00:00
# include <chrono>
# include <sstream>
2024-01-15 19:56:58 +00:00
# ifdef _USE_QUICKNES
2024-01-20 10:21:34 +00:00
# include "quickNESInstance.hpp"
2024-01-15 19:56:58 +00:00
# endif
# ifdef _USE_QUICKERNES
2024-01-20 10:21:34 +00:00
# include "quickerNESInstance.hpp"
2024-01-15 19:56:58 +00:00
# endif
2024-01-12 18:14:57 +00:00
int main ( int argc , char * argv [ ] )
{
2024-01-20 10:21:34 +00:00
// 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-19 16:03:47 +00:00
program . add_argument ( " --cycleType " )
. help ( " Specifies the emulation actions to be performed per each input. Possible values: 'Simple': performs only advance state, 'Rerecord': performs load/advance/save, and 'Full': performs load/advance/save/advance. " )
. default_value ( std : : string ( " Simple " ) ) ;
2024-01-16 21:22:36 +00:00
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
2024-01-20 10:21:34 +00:00
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 ( ) ) ;
}
2024-01-12 18:14:57 +00:00
// 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
2024-01-19 16:03:47 +00:00
std : : string cycleType = program . get < std : : string > ( " --cycleType " ) ;
2024-01-16 21:22:36 +00:00
2024-01-12 18:14:57 +00:00
// Loading script file
std : : string scriptJsonRaw ;
2024-01-20 10:21:34 +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
2024-01-20 10:21:34 +00:00
// Parsing script
2024-01-12 18:14:57 +00:00
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 > ( ) ;
2024-01-20 10:21:34 +00:00
// Creating emulator instance
# ifdef _USE_QUICKNES
2024-01-15 19:56:58 +00:00
auto e = QuickNESInstance ( ) ;
2024-01-20 10:21:34 +00:00
# endif
2024-01-15 19:56:58 +00:00
2024-01-20 10:21:34 +00:00
# ifdef _USE_QUICKERNES
2024-01-15 19:56:58 +00:00
auto e = QuickerNESInstance ( ) ;
2024-01-20 10:21:34 +00:00
# endif
2024-01-15 19:56:58 +00:00
// Loading ROM File
e . loadROMFile ( romFilePath ) ;
// If an initial state is provided, load it now
if ( initialStateFilePath ! = " " ) e . loadStateFile ( initialStateFilePath ) ;
2024-01-20 10:21:34 +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 ( ) ;
2024-01-18 18:56:30 +00:00
// Getting full state size
2024-01-12 20:13:51 +00:00
const auto stateSize = e . getStateSize ( ) ;
2024-01-12 18:14:57 +00:00
2024-01-18 18:56:30 +00:00
// Getting lite state size
const auto liteStateSize = e . getLiteStateSize ( ) ;
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-19 16:03:47 +00:00
printf ( " [] Cycle Type: '%s' \n " , cycleType . c_str ( ) ) ;
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-18 18:56:30 +00:00
printf ( " [] Full State Size: %lu bytes \n " , stateSize ) ;
printf ( " [] Lite State Size: %lu bytes \n " , liteStateSize ) ;
2024-01-13 12:35:54 +00:00
printf ( " [] ********** Running Test ********** \n " ) ;
2024-01-20 10:21:34 +00:00
2024-01-12 18:14:57 +00:00
fflush ( stdout ) ;
2024-01-20 10:21:34 +00:00
2024-01-16 21:22:36 +00:00
// Serializing initial state
2024-01-20 10:21:34 +00:00
uint8_t * currentState = ( uint8_t * ) malloc ( stateSize ) ;
2024-01-16 21:22:36 +00:00
e . serializeState ( currentState ) ;
2024-01-19 16:03:47 +00:00
// Check whether to perform each action
bool doPreAdvance = cycleType = = " Full " ;
bool doDeserialize = cycleType = = " Rerecord " | | cycleType = = " Full " ;
bool doSerialize = cycleType = = " Rerecord " | | cycleType = = " Full " ;
2024-01-12 18:14:57 +00:00
// Actually running the sequence
auto t0 = std : : chrono : : high_resolution_clock : : now ( ) ;
2024-01-20 10:21:34 +00:00
for ( const std : : string & input : sequence )
2024-01-16 21:22:36 +00:00
{
2024-01-19 16:03:47 +00:00
if ( doPreAdvance = = true ) e . advanceState ( input ) ;
if ( doDeserialize = = true ) e . deserializeState ( currentState ) ;
2024-01-16 21:22:36 +00:00
e . advanceState ( input ) ;
2024-01-19 16:03:47 +00:00
if ( doSerialize = = true ) e . serializeState ( currentState ) ;
2024-01-20 10:21:34 +00:00
}
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
}