Progress with testing
This commit is contained in:
parent
b346a9da5b
commit
147d0d4f52
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2013-2021 Niels Lohmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,23 +0,0 @@
|
|||
Taken from https://github.com/SourMesen/Mesen2/blob/master/Utilities/sha1.cpp
|
||||
|
||||
/*
|
||||
sha1.hpp - source code of
|
||||
|
||||
============
|
||||
SHA-1 in C++
|
||||
============
|
||||
|
||||
100% Public Domain.
|
||||
|
||||
Original C Code
|
||||
-- Steve Reid <steve@edmweb.com>
|
||||
Small changes to fit into bglibs
|
||||
-- Bruce Guenter <bruce@untroubled.org>
|
||||
Translation to simpler C++ Code
|
||||
-- Volker Diels-Grabsch <v@njh.eu>
|
||||
Safety fixes
|
||||
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
|
||||
Header-only library
|
||||
-- Zlatko Michailov <zlatko@michailov.org>
|
||||
*/
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
sha1.h - header of
|
||||
|
||||
============
|
||||
SHA-1 in C++
|
||||
============
|
||||
|
||||
100% Public Domain.
|
||||
|
||||
Original C Code
|
||||
-- Steve Reid <steve@edmweb.com>
|
||||
Small changes to fit into bglibs
|
||||
-- Bruce Guenter <bruce@untroubled.org>
|
||||
Translation to simpler C++ Code
|
||||
-- Volker Grabsch <vog@notjusthosting.com>
|
||||
Safety fixes
|
||||
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
class SHA1
|
||||
{
|
||||
public:
|
||||
SHA1();
|
||||
void update(const std::string &s);
|
||||
void update(std::istream &is);
|
||||
std::string final();
|
||||
static std::string GetHash(const std::string &filename);
|
||||
static std::string GetHash(std::istream &stream);
|
||||
static std::string GetHash(uint8_t* data, size_t size);
|
||||
|
||||
private:
|
||||
uint32_t digest[5];
|
||||
std::string buffer;
|
||||
uint64_t transforms;
|
||||
};
|
|
@ -1,27 +1,34 @@
|
|||
/*
|
||||
sha1.cpp - source code of
|
||||
sha1.h - header of
|
||||
|
||||
============
|
||||
SHA-1 in C++
|
||||
============
|
||||
============
|
||||
SHA-1 in C++
|
||||
============
|
||||
|
||||
100% Public Domain.
|
||||
100% Public Domain.
|
||||
|
||||
Original C Code
|
||||
-- Steve Reid <steve@edmweb.com>
|
||||
Small changes to fit into bglibs
|
||||
-- Bruce Guenter <bruce@untroubled.org>
|
||||
Translation to simpler C++ Code
|
||||
-- Volker Grabsch <vog@notjusthosting.com>
|
||||
Safety fixes
|
||||
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
|
||||
|
||||
Taken and adapted to be a single header from https://github.com/SourMesen/Mesen2/blob/master/Utilities/sha1.cpp
|
||||
by Sergio Martin (eien86)
|
||||
|
||||
Original C Code
|
||||
-- Steve Reid <steve@edmweb.com>
|
||||
Small changes to fit into bglibs
|
||||
-- Bruce Guenter <bruce@untroubled.org>
|
||||
Translation to simpler C++ Code
|
||||
-- Volker Grabsch <vog@notjusthosting.com>
|
||||
Safety fixes
|
||||
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
|
||||
*/
|
||||
|
||||
#include "sha1.h"
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
static const size_t BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */
|
||||
static const size_t BLOCK_BYTES = BLOCK_INTS * 4;
|
||||
|
@ -215,21 +222,23 @@ static void buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
SHA1::SHA1()
|
||||
class SHA1
|
||||
{
|
||||
public:
|
||||
SHA1()
|
||||
{
|
||||
reset(digest, buffer, transforms);
|
||||
}
|
||||
|
||||
|
||||
void SHA1::update(const std::string &s)
|
||||
void update(const std::string &s)
|
||||
{
|
||||
std::istringstream is(s);
|
||||
update(is);
|
||||
}
|
||||
|
||||
|
||||
void SHA1::update(std::istream &is)
|
||||
void update(std::istream &is)
|
||||
{
|
||||
char sbuf[BLOCK_BYTES];
|
||||
uint32_t block[BLOCK_INTS];
|
||||
|
@ -252,7 +261,7 @@ void SHA1::update(std::istream &is)
|
|||
* Add padding and return the message digest.
|
||||
*/
|
||||
|
||||
std::string SHA1::final()
|
||||
std::string final()
|
||||
{
|
||||
/* Total number of hashed bits */
|
||||
uint64_t total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8;
|
||||
|
@ -293,7 +302,7 @@ std::string SHA1::final()
|
|||
}
|
||||
|
||||
|
||||
std::string SHA1::GetHash(uint8_t* data, size_t size)
|
||||
static std::string GetHash(uint8_t* data, size_t size)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.write((char*)data, size);
|
||||
|
@ -303,17 +312,24 @@ std::string SHA1::GetHash(uint8_t* data, size_t size)
|
|||
return checksum.final();
|
||||
}
|
||||
|
||||
std::string SHA1::GetHash(std::istream &stream)
|
||||
static std::string GetHash(std::istream &stream)
|
||||
{
|
||||
SHA1 checksum;
|
||||
checksum.update(stream);
|
||||
return checksum.final();
|
||||
}
|
||||
|
||||
std::string SHA1::GetHash(const std::string &filename)
|
||||
static std::string GetHash(const std::string &filename)
|
||||
{
|
||||
std::ifstream stream(filename.c_str(), std::ios::binary);
|
||||
SHA1 checksum;
|
||||
checksum.update(stream);
|
||||
return checksum.final();
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t digest[5];
|
||||
std::string buffer;
|
||||
uint64_t transforms;
|
||||
};
|
||||
|
23
meson.build
23
meson.build
|
@ -35,8 +35,7 @@ quickerNESCoreSrc = [
|
|||
'source/core/Multi_Buffer.cpp',
|
||||
'source/core/Nes_Emu.cpp',
|
||||
'source/core/nes_ntsc.cpp',
|
||||
'source/core/Nes_Vrc7.cpp',
|
||||
'extern/sha1/sha1.cpp'
|
||||
'source/core/Nes_Vrc7.cpp'
|
||||
]
|
||||
|
||||
# quickerNES Core Configuration
|
||||
|
@ -47,7 +46,7 @@ quickerNESCoreSrc = [
|
|||
sources : quickerNESCoreSrc,
|
||||
)
|
||||
|
||||
# Building playback/validation tool
|
||||
# Building playback tool
|
||||
|
||||
quickerNESPlayerSrc = [
|
||||
'extern/hqn/hqn.cpp',
|
||||
|
@ -57,19 +56,23 @@ quickerNESPlayerSrc = [
|
|||
'extern/hqn/options.cpp'
|
||||
]
|
||||
|
||||
quickerNESPlayerDependency = declare_dependency(
|
||||
compile_args : [ '-DNCURSES' ],
|
||||
executable('player',
|
||||
'source/player.cpp',
|
||||
cpp_args : [ '-DNCURSES' ],
|
||||
dependencies : [ quickerNESCoreDependency, dependency('sdl2'), dependency('SDL2_image') ],
|
||||
include_directories : include_directories(['source']),
|
||||
link_args : [ '-lncurses' ],
|
||||
sources : quickerNESPlayerSrc
|
||||
)
|
||||
|
||||
executable('player',
|
||||
'source/player.cpp',
|
||||
dependencies: quickerNESPlayerDependency,
|
||||
)
|
||||
|
||||
# Building tester tool
|
||||
|
||||
tester = executable('tester',
|
||||
'source/tester.cpp',
|
||||
dependencies: [ quickerNESCoreDependency ],
|
||||
include_directories: include_directories(['../extern/json'])
|
||||
)
|
||||
|
||||
# Building tests
|
||||
if get_option('buildTests') == true
|
||||
subdir('tests')
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <core/Nes_State.h>
|
||||
#include <string>
|
||||
#include <utils.hpp>
|
||||
#include "sha1/sha1.hpp"
|
||||
|
||||
#define _LOW_MEM_SIZE 0x800
|
||||
#define _HIGH_MEM_SIZE 0x2000
|
||||
|
@ -26,7 +27,13 @@ class EmuInstance
|
|||
|
||||
// Loading ROM
|
||||
std::string romData;
|
||||
loadStringFromFile(romData, romFilePath.c_str());
|
||||
bool status = loadStringFromFile(romData, romFilePath.c_str());
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", romFilePath.c_str());
|
||||
|
||||
// Calculating ROM hash value
|
||||
_romSHA1String = SHA1::GetHash((uint8_t*)romData.data(), romData.size());
|
||||
|
||||
// Loading the rom into the emulator
|
||||
Mem_File_Reader romReader(romData.data(), (int)romData.size());
|
||||
Auto_File_Reader romFile(romReader);
|
||||
auto result = _nes->load_ines(romFile);
|
||||
|
@ -42,12 +49,14 @@ class EmuInstance
|
|||
uint8_t* getLowMem() { return _nes->low_mem(); };
|
||||
uint8_t* getNametableMem() { return _nes->nametable_mem(); };
|
||||
uint8_t* getHighMem() { return _nes->high_mem();};
|
||||
|
||||
const std::string getRomSHA1() const { return _romSHA1String; };
|
||||
|
||||
void loadStateFile(const std::string& stateFilePath)
|
||||
{
|
||||
// Loading state data
|
||||
std::string stateData;
|
||||
if (loadStringFromFile(stateData, stateFilePath.c_str()) == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
|
||||
bool status = loadStringFromFile(stateData, stateFilePath.c_str());
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
|
||||
Mem_File_Reader stateReader(stateData.data(), (int)stateData.size());
|
||||
Auto_File_Reader stateFile(stateReader);
|
||||
|
||||
|
@ -169,4 +178,7 @@ class EmuInstance
|
|||
// State size for the given rom
|
||||
size_t _stateSize;
|
||||
|
||||
// SHA1 rom hash
|
||||
std::string _romSHA1String;
|
||||
|
||||
};
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include "argparse/argparse.hpp"
|
||||
#include "sha1/sha1.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "emuInstance.hpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Parsing command line arguments
|
||||
argparse::ArgumentParser program("player", "1.0");
|
||||
|
||||
program.add_argument("scriptFile")
|
||||
.help("Path to the test script file to run.")
|
||||
.required();
|
||||
|
||||
// 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");
|
||||
|
||||
// Loading script file
|
||||
std::string scriptJsonRaw;
|
||||
if (loadStringFromFile(scriptJsonRaw, scriptFilePath.c_str()) == false) EXIT_WITH_ERROR("Could not find/read script file: %s\n", scriptFilePath.c_str());
|
||||
|
||||
// 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
|
||||
auto e = EmuInstance(romFilePath, initialStateFilePath);
|
||||
|
||||
// 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;
|
||||
if (loadStringFromFile(sequenceRaw, sequenceFilePath.c_str()) == false) EXIT_WITH_ERROR("[ERROR] Could not find or read from input sequence file: %s\n", sequenceFilePath.c_str());
|
||||
|
||||
// Building sequence information
|
||||
const auto sequence = split(sequenceRaw, ' ');
|
||||
|
||||
// Getting sequence lenght
|
||||
const auto sequenceLength = sequence.size();
|
||||
|
||||
// Printing test information
|
||||
printf("[] Running Script: '%s'\n", scriptFilePath.c_str());
|
||||
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);
|
||||
fflush(stdout);
|
||||
|
||||
// Actually running the sequence
|
||||
auto t0 = std::chrono::high_resolution_clock::now();
|
||||
for (const auto& input : sequence) e.advanceState(input);
|
||||
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;
|
||||
|
||||
// Printing time information
|
||||
printf("[] Elapsed time: %3.3fs\n", (double)dt * 1.0e-9);
|
||||
printf("[] Performance: %.3f steps / s\n", (double)sequenceLength / elapsedTimeSeconds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#include "argparse/argparse.hpp"
|
||||
#include "sha1/sha1.h"
|
||||
#include "utils.hpp"
|
||||
#include "emuInstance.hpp"
|
||||
#include <sstream>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Parsing command line arguments
|
||||
argparse::ArgumentParser program("player", "1.0");
|
||||
|
||||
program.add_argument("romFile")
|
||||
.help("Path to the rom file to run.")
|
||||
.required();
|
||||
|
||||
// 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 ROM file path
|
||||
std::string romFilePath = program.get<std::string>("romFile");
|
||||
|
||||
// Getting ROM data
|
||||
std::string romData;
|
||||
auto status = loadStringFromFile(romData, romFilePath.c_str());
|
||||
if (status == false) EXIT_WITH_ERROR("Could not read rom file: '%s'\n", romFilePath.c_str());
|
||||
|
||||
// Getting SHA1 String
|
||||
std::string sha1String = SHA1::GetHash((uint8_t*)romData.data(), romData.size());
|
||||
|
||||
// Printing ROM SHA1
|
||||
printf("%s\n", sha1String.c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"Rom File": "Castlevania (U) (PRG0) [!].nes",
|
||||
"Expected ROM SHA1": "A31B8BD5B370A9103343C866F3C2B2998E889341",
|
||||
"Initial State File": "",
|
||||
"Final State File": "anyPercent.final.state",
|
||||
"Sequence File": "anyPercent.sol"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
nomalloc = environment({'MALLOC_PERTURB_': '0'})
|
||||
|
||||
# Castlevania 1 Tests
|
||||
game = 'castlevania1'
|
||||
goal = 'anyPercent'
|
||||
test(goal, tester, workdir : meson.current_source_dir(), args : [ goal + '.test'], suite: [ game, goal ])
|
|
@ -0,0 +1 @@
|
|||
subdir('castlevania1')
|
|
@ -1,10 +1,3 @@
|
|||
nomalloc = environment({'MALLOC_PERTURB_': '0'})
|
||||
|
||||
# Adding game ending checks
|
||||
endingChecker = executable('endingChecker',
|
||||
files(['endingChecker.cpp']),
|
||||
dependencies: [ quickerNESCoreDependency ]
|
||||
)
|
||||
|
||||
testSuite = [ 'gameEndings' ]
|
||||
test('castlevania1AnyPercent', endingChecker, workdir : meson.current_source_dir(), args : ['roms/Castlevania (U) (PRG0) [!].nes'], suite: testSuite)
|
||||
subdir('games')
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
Solution sequence taken from https://tasvideos.org/UserFiles/Info/637879644789320048 by eien86
|
||||
|
||||
Rom Information
|
||||
Name: Castlevania (U) (PRG0) [!].nes
|
||||
SHA1: A31B8BD5B370A9103343C866F3C2B2998E889341
|
||||
MD5:00D93C9F6B8AEFB8B6C02B20147DF4EC
|
Loading…
Reference in New Issue