Refactoring for better use in JaffarPlus

This commit is contained in:
Sergio Martin 2024-01-28 16:02:58 +01:00
parent caf55de8c2
commit b1fbe9ddc5
9 changed files with 106 additions and 189 deletions

2
extern/hqn/hqn.h vendored
View File

@ -3,7 +3,7 @@
#include <cstdint>
#include <stdio.h>
#include <emuInstance.hpp>
#include <nesInstance.hpp>
#define BLIT_SIZE 65536

34
source/logger.hpp Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <stdarg.h>
#include <stdexcept>
#include <stdio.h>
// If we use NCurses, we need to use the appropriate printing function
#ifdef NCURSES
#include <ncurses.h>
#define LOG printw
#else
#define LOG printf
#endif
#define EXIT_WITH_ERROR(...) exitWithError(__FILE__, __LINE__, __VA_ARGS__)
inline void exitWithError [[noreturn]] (const char *fileName, const int lineNumber, const char *format, ...)
{
char *outstr = 0;
va_list ap;
va_start(ap, format);
int ret = vasprintf(&outstr, format, ap);
if (ret < 0) exit(-1);
std::string outString = outstr;
free(outstr);
char info[1024];
snprintf(info, sizeof(info) - 1, " + From %s:%d\n", fileName, lineNumber);
outString += info;
throw std::runtime_error(outString.c_str());
}

View File

@ -1,8 +1,7 @@
#pragma once
#include "sha1/sha1.hpp"
#include <utils.hpp>
#include <controller.hpp>
#include "logger.hpp"
#include "controller.hpp"
#define _LOW_MEM_SIZE 0x800
#define _HIGH_MEM_SIZE 0x2000
@ -12,12 +11,12 @@
static const uint16_t image_width = 256;
static const uint16_t image_height = 240;
class EmuInstanceBase
class NESInstanceBase
{
public:
EmuInstanceBase() = default;
virtual ~EmuInstanceBase() = default;
NESInstanceBase() = default;
virtual ~NESInstanceBase() = default;
inline void advanceState(const std::string &move)
{
@ -61,56 +60,19 @@ class EmuInstanceBase
if (isTypeRecognized == false) EXIT_WITH_ERROR("Input type not recognized: '%s'\n", type.c_str());
}
inline std::string getRomSHA1() const { return _romSHA1String; }
inline hash_t getStateHash() const
{
MetroHash128 hash;
hash.Update(getLowMem(), _LOW_MEM_SIZE);
// hash.Update(getHighMem(), _HIGH_MEM_SIZE);
// hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
// hash.Update(getChrMem(), getChrMemSize());
hash_t result;
hash.Finalize(reinterpret_cast<uint8_t *>(&result));
return result;
}
inline void enableRendering() { _doRendering = true; };
inline void disableRendering() { _doRendering = false; };
inline void loadStateFile(const std::string &stateFilePath)
inline bool loadROM(const uint8_t* romData, const size_t romSize)
{
std::string stateData;
bool status = loadStringFromFile(stateData, stateFilePath);
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
deserializeState((uint8_t *)stateData.data());
}
inline void saveStateFile(const std::string &stateFilePath) const
{
std::string stateData;
stateData.resize(_stateSize);
serializeState((uint8_t *)stateData.data());
saveStringToFile(stateData, stateFilePath.c_str());
}
inline void loadROMFile(const std::string &romFilePath)
{
// Loading ROM data
bool status = loadStringFromFile(_romData, romFilePath);
if (status == false) EXIT_WITH_ERROR("Could not find/read ROM file: %s\n", romFilePath.c_str());
// Calculating ROM hash value
_romSHA1String = SHA1::GetHash((uint8_t *)_romData.data(), _romData.size());
// Actually loading rom file
status = loadROMFileImpl(_romData);
if (status == false) EXIT_WITH_ERROR("Could not process ROM file: %s\n", romFilePath.c_str());
auto status = loadROMImpl(romData, romSize);
// Detecting full state size
_stateSize = getStateSize();
// Returning status
return status;
}
void enableStateBlock(const std::string& block)
@ -151,7 +113,7 @@ class EmuInstanceBase
virtual void enableStateBlockImpl(const std::string& block) = 0;
virtual void disableStateBlockImpl(const std::string& block) = 0;
virtual bool loadROMFileImpl(const std::string &romFilePath) = 0;
virtual bool loadROMImpl(const uint8_t* romData, const size_t romSize) = 0;
virtual void advanceStateImpl(const Controller::port_t controller1, const Controller::port_t controller2) = 0;
// Storage for the light state size
@ -161,11 +123,6 @@ class EmuInstanceBase
bool _doRendering = true;
private:
// Storage for the ROM data
std::string _romData;
// SHA1 rom hash
std::string _romSHA1String;
// Controller class for input parsing
Controller _controller;

View File

@ -1,6 +1,6 @@
#pragma once
#include "emuInstance.hpp"
#include "nesInstance.hpp"
#include <SDL.h>
#include <SDL_image.h>
#include <hqn/hqn.h>
@ -30,14 +30,14 @@ class PlaybackInstance
step.input = input;
step.stateData = (uint8_t *)malloc(_emu->getStateSize());
_emu->serializeState(step.stateData);
step.hash = _emu->getStateHash();
step.hash = calculateStateHash(_emu);
// Adding the step into the sequence
_stepSequence.push_back(step);
}
// Initializes the playback module instance
PlaybackInstance(EmuInstance *emu, const std::vector<std::string> &sequence, const std::string &overlayPath = "") : _emu(emu)
PlaybackInstance(NESInstance *emu, const std::vector<std::string> &sequence, const std::string &overlayPath = "") : _emu(emu)
{
// Enabling emulation rendering
_emu->enableRendering();
@ -234,7 +234,7 @@ class PlaybackInstance
hqn::GUIController *_hqnGUI;
// Pointer to the contained emulator instance
EmuInstance *const _emu;
NESInstance *const _emu;
// Flag to store whether to use the button overlay
bool _useOverlay = false;

View File

@ -1,5 +1,5 @@
#include "argparse/argparse.hpp"
#include "emuInstance.hpp"
#include "nesInstance.hpp"
#include "playbackInstance.hpp"
#include "utils.hpp"
#include <cstdlib>
@ -91,17 +91,24 @@ int main(int argc, char *argv[])
refreshTerminal();
// Creating emulator instance
EmuInstance e;
NESInstance e;
// Setting controller types
e.setController1Type(controller1Type);
e.setController2Type(controller2Type);
// Loading ROM File
e.loadROMFile(romFilePath);
std::string romFileData;
if (loadStringFromFile(romFileData, romFilePath) == false) EXIT_WITH_ERROR("Could not rom file: %s\n", romFilePath.c_str());
e.loadROM((uint8_t*)romFileData.data(), romFileData.size());
// If an initial state is provided, load it now
if (stateFilePath != "") e.loadStateFile(stateFilePath);
if (stateFilePath != "")
{
std::string stateFileData;
if (loadStringFromFile(stateFileData, stateFilePath) == false) EXIT_WITH_ERROR("Could not initial state file: %s\n", stateFilePath.c_str());
e.deserializeState((uint8_t*)stateFileData.data());
}
// Creating playback instance
auto p = PlaybackInstance(&e, sequence);

View File

@ -1,8 +1,8 @@
#pragma once
#include <Nes_Emu.h>
#include <Nes_State.h>
#include <emuInstanceBase.hpp>
#include <quickNES/core/nes_emu/Nes_Emu.h>
#include <quickNES/core/nes_emu/Nes_State.h>
#include <nesInstanceBase.hpp>
#define _DUMMY_SIZE 65536
@ -12,10 +12,10 @@ extern void register_misc_mappers();
extern void register_extra_mappers();
extern void register_mapper_70();
class EmuInstance : public EmuInstanceBase
class NESInstance : public NESInstanceBase
{
public:
EmuInstance() : EmuInstanceBase()
NESInstance() : NESInstanceBase()
{
// Creating new emulator
_nes = new Nes_Emu;
@ -32,10 +32,10 @@ class EmuInstance : public EmuInstanceBase
register_mapper_70();
}
virtual bool loadROMFileImpl(const std::string &romData) override
virtual bool loadROMImpl(const uint8_t* romData, const size_t romSize) override
{
// Loading rom data
Mem_File_Reader romReader(romData.data(), (int)romData.size());
Mem_File_Reader romReader(romData, (int)romSize);
Auto_File_Reader romFile(romReader);
auto result = _nes->load_ines(romFile);
return result == 0;

View File

@ -1,14 +1,14 @@
#pragma once
#include <core/emu.hpp>
#include <emuInstanceBase.hpp>
#include <quickerNES/core/emu.hpp>
#include <nesInstanceBase.hpp>
typedef quickerNES::Emu emulator_t;
class EmuInstance : public EmuInstanceBase
class NESInstance : public NESInstanceBase
{
public:
EmuInstance() : EmuInstanceBase()
NESInstance() : NESInstanceBase()
{
// Creating new emulator
_nes = new emulator_t;
@ -20,12 +20,12 @@ class EmuInstance : public EmuInstanceBase
_nes->set_pixels(video_buffer, image_width + 8);
}
~EmuInstance() = default;
~NESInstance() = default;
virtual bool loadROMFileImpl(const std::string &romData) override
virtual bool loadROMImpl(const uint8_t* romData, const size_t romSize) override
{
// Loading rom data
_nes->load_ines((uint8_t *)romData.data());
_nes->load_ines(romData);
return true;
}

View File

@ -2,7 +2,7 @@
#include "nlohmann/json.hpp"
#include "sha1/sha1.hpp"
#include "utils.hpp"
#include "emuInstance.hpp"
#include "nesInstance.hpp"
#include <chrono>
#include <sstream>
#include <vector>
@ -94,18 +94,27 @@ int main(int argc, char *argv[])
std::string controller2Type = scriptJson["Controller 2 Type"].get<std::string>();
// Creating emulator instance
EmuInstance e;
NESInstance e;
// Setting controller types
e.setController1Type(controller1Type);
e.setController2Type(controller2Type);
// Loading ROM File
e.loadROMFile(romFilePath);
std::string romFileData;
if (loadStringFromFile(romFileData, romFilePath) == false) EXIT_WITH_ERROR("Could not rom file: %s\n", romFilePath.c_str());
e.loadROM((uint8_t*)romFileData.data(), romFileData.size());
// Calculating ROM SHA1
auto romSHA1 = SHA1::GetHash((uint8_t *)romFileData.data(), romFileData.size());
// If an initial state is provided, load it now
if (initialStateFilePath != "") e.loadStateFile(initialStateFilePath);
if (initialStateFilePath != "")
{
std::string stateFileData;
if (loadStringFromFile(stateFileData, initialStateFilePath) == false) EXIT_WITH_ERROR("Could not initial state file: %s\n", initialStateFilePath.c_str());
e.deserializeState((uint8_t*)stateFileData.data());
}
// Disabling requested blocks from state serialization
for (const auto& block : stateDisabledBlocks) e.disableStateBlock(block);
@ -115,9 +124,6 @@ int main(int argc, char *argv[])
// Getting state size
const auto stateSize = e.getStateSize();
// 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());
@ -174,11 +180,11 @@ int main(int argc, char *argv[])
double elapsedTimeSeconds = (double)dt * 1.0e-9;
// Calculating final state hash
const auto finalStateHash = e.getStateHash();
auto result = calculateStateHash(&e);
// Creating hash string
char hashStringBuffer[256];
sprintf(hashStringBuffer, "0x%lX%lX", finalStateHash.first, finalStateHash.second);
sprintf(hashStringBuffer, "0x%lX%lX", result.first, result.second);
// Printing time information
printf("[] Elapsed time: %3.3fs\n", (double)dt * 1.0e-9);

View File

@ -2,25 +2,20 @@
#include <algorithm>
#include <fstream>
#include <metrohash128/metrohash128.h>
#include <sstream>
#include <stdarg.h>
#include <stdexcept>
#include <stdio.h>
#include <unistd.h>
#include <vector>
// If we use NCurses, we need to use the appropriate printing function
#ifdef NCURSES
#include <ncurses.h>
#define LOG printw
#else
#define LOG printf
#endif
#include <metrohash128/metrohash128.h>
#include "nesInstance.hpp"
// If we use NCurses, define the following useful functions
#ifdef NCURSES
#include <ncurses.h>
// Function to check for keypress taken from https://github.com/ajpaulson/learning-ncurses/blob/master/kbhit.c
inline int kbhit()
{
@ -84,16 +79,6 @@ inline void refreshTerminal()
#endif // NCURSES
typedef _uint128_t hash_t;
inline hash_t calculateMetroHash(uint8_t *data, size_t size)
{
MetroHash128 hash;
hash.Update(data, size);
hash_t result;
hash.Finalize(reinterpret_cast<uint8_t *>(&result));
return result;
}
// Function to split a string into a sub-strings delimited by a character
// Taken from stack overflow answer to https://stackoverflow.com/questions/236129/how-do-i-iterate-over-the-words-of-a-string
// By Evan Teran
@ -126,26 +111,6 @@ inline std::string slurp(std::ifstream &in)
return sstr.str();
}
#define EXIT_WITH_ERROR(...) exitWithError(__FILE__, __LINE__, __VA_ARGS__)
inline void exitWithError [[noreturn]] (const char *fileName, const int lineNumber, const char *format, ...)
{
char *outstr = 0;
va_list ap;
va_start(ap, format);
int ret = vasprintf(&outstr, format, ap);
if (ret < 0) exit(-1);
std::string outString = outstr;
free(outstr);
char info[1024];
snprintf(info, sizeof(info) - 1, " + From %s:%d\n", fileName, lineNumber);
outString += info;
throw std::runtime_error(outString.c_str());
}
// Loads a string from a given file
inline bool loadStringFromFile(std::string &dst, const std::string path)
{
@ -191,70 +156,18 @@ inline std::vector<T> splitVector(const T size, const T n)
return subSizes;
}
inline std::string simplifyMove(const std::string &move)
{
std::string simpleMove;
typedef _uint128_t hash_t;
bool isEmptyMove = true;
for (size_t i = 0; i < move.size(); i++)
if (move[i] != '.' && move[i] != '|')
inline hash_t calculateMetroHash(uint8_t *data, size_t size)
{
simpleMove += move[i];
isEmptyMove = false;
}
if (isEmptyMove) return ".";
return simpleMove;
MetroHash128 hash;
hash.Update(data, size);
hash_t result;
hash.Finalize(reinterpret_cast<uint8_t *>(&result));
return result;
}
inline bool getBitFlag(const uint8_t value, const uint8_t idx)
inline hash_t calculateStateHash(const NESInstance* nes)
{
if (((idx == 7) && (value & 0b10000000)) ||
((idx == 6) && (value & 0b01000000)) ||
((idx == 5) && (value & 0b00100000)) ||
((idx == 4) && (value & 0b00010000)) ||
((idx == 3) && (value & 0b00001000)) ||
((idx == 2) && (value & 0b00000100)) ||
((idx == 1) && (value & 0b00000010)) ||
((idx == 0) && (value & 0b00000001))) return true;
return false;
return calculateMetroHash(nes->getLowMem(), _LOW_MEM_SIZE);
}
inline size_t countButtonsPressedString(const std::string &input)
{
size_t count = 0;
for (size_t i = 0; i < input.size(); i++)
if (input[i] != '.') count++;
return count;
};
template <typename T>
inline uint16_t countButtonsPressedNumber(const T &input)
{
uint16_t count = 0;
if (input & 0b0000000000000001) count++;
if (input & 0b0000000000000010) count++;
if (input & 0b0000000000000100) count++;
if (input & 0b0000000000001000) count++;
if (input & 0b0000000000010000) count++;
if (input & 0b0000000000100000) count++;
if (input & 0b0000000001000000) count++;
if (input & 0b0000000010000000) count++;
if (input & 0b0000000100000000) count++;
if (input & 0b0000001000000000) count++;
if (input & 0b0000010000000000) count++;
if (input & 0b0000100000000000) count++;
if (input & 0b0001000000000000) count++;
if (input & 0b0010000000000000) count++;
if (input & 0b0100000000000000) count++;
if (input & 0b1000000000000000) count++;
return count;
};
static auto moveCountComparerString = [](const std::string &a, const std::string &b)
{
return countButtonsPressedString(a) < countButtonsPressedString(b);
};
static auto moveCountComparerNumber = [](const uint8_t a, const uint8_t b)
{
return countButtonsPressedNumber(a) < countButtonsPressedNumber(b);
};