2024-01-09 19:12:22 +00:00
|
|
|
#include <cstdlib>
|
2024-01-10 19:11:49 +00:00
|
|
|
#include "argparse/argparse.hpp"
|
2024-01-09 19:12:22 +00:00
|
|
|
#include "utils.hpp"
|
2024-01-10 19:11:49 +00:00
|
|
|
#include "emuInstance.hpp"
|
|
|
|
#include "playbackInstance.hpp"
|
2024-01-09 19:12:22 +00:00
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
// Parsing command line arguments
|
|
|
|
argparse::ArgumentParser program("player", "1.0");
|
|
|
|
|
|
|
|
program.add_argument("romFile")
|
2024-01-09 19:59:59 +00:00
|
|
|
.help("Path to the rom file to run.")
|
2024-01-09 19:12:22 +00:00
|
|
|
.required();
|
|
|
|
|
2024-01-09 19:59:59 +00:00
|
|
|
program.add_argument("sequenceFile")
|
|
|
|
.help("Path to the input sequence file (.sol) to reproduce.")
|
2024-01-09 19:12:22 +00:00
|
|
|
.required();
|
|
|
|
|
|
|
|
program.add_argument("stateFile")
|
|
|
|
.help("(Optional) Path to the initial state file to load.")
|
2024-01-10 19:11:49 +00:00
|
|
|
.default_value(std::string(""));
|
2024-01-09 19:12:22 +00:00
|
|
|
|
|
|
|
program.add_argument("--reproduce")
|
|
|
|
.help("Plays the entire sequence without interruptions and exit at the end.")
|
|
|
|
.default_value(false)
|
|
|
|
.implicit_value(true);
|
|
|
|
|
|
|
|
program.add_argument("--disableRender")
|
|
|
|
.help("Do not render game window.")
|
|
|
|
.default_value(false)
|
|
|
|
.implicit_value(true);
|
|
|
|
|
|
|
|
// 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()); }
|
2024-01-09 19:59:59 +00:00
|
|
|
|
|
|
|
// Getting ROM file path
|
|
|
|
std::string romFilePath = program.get<std::string>("romFile");
|
|
|
|
|
|
|
|
// Getting sequence file path
|
2024-01-10 19:11:49 +00:00
|
|
|
std::string sequenceFilePath = program.get<std::string>("sequenceFile");
|
2024-01-09 19:59:59 +00:00
|
|
|
|
|
|
|
// If initial state file is specified, load it
|
2024-01-10 19:11:49 +00:00
|
|
|
std::string stateFilePath = program.get<std::string>("stateFile");
|
2024-01-09 19:59:59 +00:00
|
|
|
|
|
|
|
// Getting reproduce flag
|
|
|
|
bool isReproduce = program.get<bool>("--reproduce");
|
|
|
|
|
|
|
|
// Getting reproduce flag
|
|
|
|
bool disableRender = program.get<bool>("--disableRender");
|
2024-01-10 19:11:49 +00:00
|
|
|
|
2024-01-11 18:44:54 +00:00
|
|
|
// Loading sequence file
|
|
|
|
std::string inputSequence;
|
|
|
|
auto status = loadStringFromFile(inputSequence, sequenceFilePath.c_str());
|
|
|
|
if (status == false) EXIT_WITH_ERROR("[ERROR] Could not find or read from sequence file: %s\n", sequenceFilePath.c_str());
|
|
|
|
|
2024-01-11 19:23:32 +00:00
|
|
|
// Building sequence information
|
|
|
|
const auto sequence = split(inputSequence, ' ');
|
|
|
|
|
2024-01-11 18:44:54 +00:00
|
|
|
// Initializing terminal
|
|
|
|
initializeTerminal();
|
|
|
|
|
2024-01-10 19:11:49 +00:00
|
|
|
// Printing provided parameters
|
2024-01-11 18:44:54 +00:00
|
|
|
printw("[] Rom File Path: '%s'\n", romFilePath.c_str());
|
|
|
|
printw("[] Sequence File Path: '%s'\n", sequenceFilePath.c_str());
|
2024-01-13 10:10:42 +00:00
|
|
|
printw("[] Sequence Length: %lu\n", sequence.size());
|
2024-01-11 18:44:54 +00:00
|
|
|
printw("[] State File Path: '%s'\n", stateFilePath.empty() ? "<Boot Start>" : stateFilePath.c_str());
|
|
|
|
printw("[] Generating Sequence...\n");
|
|
|
|
|
|
|
|
refreshTerminal();
|
2024-01-10 19:11:49 +00:00
|
|
|
|
|
|
|
// Creating emulator instance
|
|
|
|
auto e = EmuInstance(romFilePath, stateFilePath);
|
|
|
|
|
2024-01-12 20:13:51 +00:00
|
|
|
auto stateHash = e.getStateHash();
|
|
|
|
|
2024-01-10 19:11:49 +00:00
|
|
|
// Creating playback instance
|
2024-01-11 19:23:32 +00:00
|
|
|
auto p = PlaybackInstance(&e, sequence);
|
2024-01-11 18:44:54 +00:00
|
|
|
|
2024-01-12 20:13:51 +00:00
|
|
|
// Getting state size
|
|
|
|
auto stateSize = e.getStateSize();
|
|
|
|
|
2024-01-11 18:44:54 +00:00
|
|
|
// Flag to continue running playback
|
|
|
|
bool continueRunning = true;
|
|
|
|
|
|
|
|
// Variable for current step in view
|
2024-01-12 20:13:51 +00:00
|
|
|
ssize_t sequenceLength = p.getSequenceLength();
|
2024-01-11 18:44:54 +00:00
|
|
|
ssize_t currentStep = 0;
|
|
|
|
|
|
|
|
// Flag to display frame information
|
|
|
|
bool showFrameInfo = true;
|
|
|
|
|
|
|
|
// Interactive section
|
|
|
|
while(continueRunning)
|
|
|
|
{
|
|
|
|
// Updating display
|
|
|
|
if (disableRender == false) p.renderFrame(currentStep);
|
|
|
|
|
|
|
|
// Getting input
|
2024-01-12 20:13:51 +00:00
|
|
|
const auto& input = p.getStateInput(currentStep);
|
|
|
|
|
|
|
|
// Getting state hash
|
|
|
|
const auto hash = p.getStateHash(currentStep);
|
2024-01-11 18:44:54 +00:00
|
|
|
|
|
|
|
// Getting state data
|
2024-01-12 20:13:51 +00:00
|
|
|
const auto stateData = p.getStateData(currentStep);
|
2024-01-11 18:44:54 +00:00
|
|
|
|
|
|
|
// Printing data and commands
|
|
|
|
if (showFrameInfo)
|
|
|
|
{
|
|
|
|
clearTerminal();
|
|
|
|
|
|
|
|
printw("[] ----------------------------------------------------------------\n");
|
|
|
|
printw("[] Current Step #: %lu / %lu\n", currentStep + 1, sequenceLength);
|
2024-01-12 20:13:51 +00:00
|
|
|
printw("[] Input: %s\n", input.c_str());
|
|
|
|
printw("[] State Hash: 0x%lX%lX\n", hash.first, hash.second);
|
2024-01-11 18:44:54 +00:00
|
|
|
|
|
|
|
// Only print commands if not in reproduce mode
|
|
|
|
if (isReproduce == false) printw("[] Commands: n: -1 m: +1 | h: -10 | j: +10 | y: -100 | u: +100 | k: -1000 | i: +1000 | s: quicksave | p: play | q: quit\n");
|
|
|
|
|
|
|
|
refreshTerminal();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resetting show frame info flag
|
|
|
|
showFrameInfo = true;
|
|
|
|
|
|
|
|
// Get command
|
|
|
|
auto command = getKeyPress();
|
|
|
|
|
|
|
|
// Advance/Rewind commands
|
|
|
|
if (command == 'n') currentStep = currentStep - 1;
|
|
|
|
if (command == 'm') currentStep = currentStep + 1;
|
|
|
|
if (command == 'h') currentStep = currentStep - 10;
|
|
|
|
if (command == 'j') currentStep = currentStep + 10;
|
|
|
|
if (command == 'y') currentStep = currentStep - 100;
|
|
|
|
if (command == 'u') currentStep = currentStep + 100;
|
|
|
|
if (command == 'k') currentStep = currentStep - 1000;
|
|
|
|
if (command == 'i') currentStep = currentStep + 1000;
|
|
|
|
|
|
|
|
// Correct current step if requested more than possible
|
|
|
|
if (currentStep < 0) currentStep = 0;
|
|
|
|
if (currentStep >= sequenceLength) currentStep = sequenceLength-1;
|
|
|
|
|
|
|
|
// Quicksave creation command
|
|
|
|
if (command == 's')
|
|
|
|
{
|
|
|
|
// Storing state file
|
|
|
|
std::string saveFileName = "quicksave.state";
|
2024-01-12 20:13:51 +00:00
|
|
|
|
|
|
|
std::string saveData;
|
|
|
|
saveData.resize(stateSize);
|
|
|
|
memcpy(saveData.data(), stateData, stateSize);
|
|
|
|
if (saveStringToFile(saveData, saveFileName.c_str()) == false) EXIT_WITH_ERROR("[ERROR] Could not save state file: %s\n", saveFileName.c_str());
|
2024-01-11 18:44:54 +00:00
|
|
|
printw("[] Saved state to %s\n", saveFileName.c_str());
|
|
|
|
|
|
|
|
// Do no show frame info again after this action
|
|
|
|
showFrameInfo = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start playback from current point
|
|
|
|
if (command == 'p') isReproduce = true;
|
|
|
|
|
|
|
|
// Start playback from current point
|
|
|
|
if (command == 'q') continueRunning = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ending ncurses window
|
|
|
|
finalizeTerminal();
|
2024-01-09 19:12:22 +00:00
|
|
|
}
|
|
|
|
|