diff --git a/src/api/apiCore.cpp b/src/api/apiCore.cpp index d2528a1b..6136a58d 100644 --- a/src/api/apiCore.cpp +++ b/src/api/apiCore.cpp @@ -1258,7 +1258,7 @@ RGL_API rgl_status_t rgl_tape_play(const char* path) throw RecordError("rgl_tape_play: recording active"); } else { TapePlayer player(path); - player.playRealtime(); + player.playApproximatelyRealtime(); } }); #endif //_WIN32 diff --git a/src/tape/TapePlayer.cpp b/src/tape/TapePlayer.cpp index f6a72f44..1157c393 100644 --- a/src/tape/TapePlayer.cpp +++ b/src/tape/TapePlayer.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -23,14 +24,11 @@ namespace fs = std::filesystem; -TapePlayer::TapePlayer(const char* path) +TapePlayer::TapePlayer(const char* path) : path(path) { - std::string pathYaml = fs::path(path).concat(YAML_EXTENSION).string(); - std::string pathBin = fs::path(path).concat(BIN_EXTENSION).string(); - - playbackState = std::make_unique(pathBin.c_str()); + playbackState = std::make_unique(getBinPath().c_str()); - yamlRoot = YAML::LoadFile(pathYaml); + yamlRoot = YAML::LoadFile(getYamlPath()); if (yamlRoot.IsNull()) { throw RecordError("Invalid Tape: Empty YAML file detected"); } @@ -43,6 +41,10 @@ TapePlayer::TapePlayer(const char* path) nextCallIdx = 0; } +std::string TapePlayer::getBinPath() const { return fs::path(path).concat(BIN_EXTENSION).string(); } + +std::string TapePlayer::getYamlPath() const { return fs::path(path).concat(YAML_EXTENSION).string(); } + void TapePlayer::checkTapeVersion() { auto versionCallIdx = findFirst({RGL_VERSION}); @@ -110,8 +112,6 @@ void TapePlayer::playUntil(std::optional breakpoint) } } -void TapePlayer::rewindTo(APICallIdx nextCall) { this->nextCallIdx = nextCall; } - void TapePlayer::playThis(APICallIdx idx) { const TapeCall& call = getTapeCall(idx); @@ -121,15 +121,30 @@ void TapePlayer::playThis(APICallIdx idx) tapeFunctions[call.getFnName()](call.getArgsNode(), *playbackState); } -#include -void TapePlayer::playRealtime() + +void TapePlayer::playApproximatelyRealtime() { + // Approximation comes from the fact that we don't account for the time it takes to execute the function + // This could be fixed by moving TAPE_HOOK to the beginning of the API call + // It might be a good idea to do so, because it would record failed calls. auto beginTimestamp = std::chrono::steady_clock::now(); - TapeCall nextCall = getTapeCall(nextCallIdx); for (; nextCallIdx < yamlRoot.size(); ++nextCallIdx) { + TapeCall nextCall = getTapeCall(nextCallIdx); auto nextCallNs = std::chrono::nanoseconds(nextCall.getTimestamp().asNanoseconds()); auto elapsed = std::chrono::steady_clock::now() - beginTimestamp; std::this_thread::sleep_for(nextCallNs - elapsed); playThis(nextCallIdx); } } + +void TapePlayer::reset() +{ + auto status = rgl_cleanup(); + if (status != RGL_SUCCESS) { + const char* error = nullptr; + rgl_get_last_error_string(&error); + throw RecordError(fmt::format("Failed to reset playback state: {}", error)); + } + nextCallIdx = 0; + playbackState = std::make_unique(getBinPath().c_str()); +} diff --git a/src/tape/TapePlayer.hpp b/src/tape/TapePlayer.hpp index 87b5d2c3..4ef7f72f 100644 --- a/src/tape/TapePlayer.hpp +++ b/src/tape/TapePlayer.hpp @@ -46,8 +46,8 @@ struct TapePlayer void playThis(APICallIdx idx); void playThrough(APICallIdx last); void playUntil(std::optional breakpoint = std::nullopt); - void playRealtime(); - void rewindTo(APICallIdx nextCall = 0); + void playApproximatelyRealtime(); + void reset(); rgl_node_t getNodeHandle(TapeAPIObjectID key) { return playbackState->nodes.at(key); } @@ -55,6 +55,9 @@ struct TapePlayer YAML::Node yamlRoot{}; APICallIdx nextCallIdx{}; std::unique_ptr playbackState; + std::string path; static inline std::map tapeFunctions = {}; + std::string getBinPath() const; + std::string getYamlPath() const; }; diff --git a/tools/tapePlayer.cpp b/tools/tapePlayer.cpp index 5bcf301e..8c241805 100644 --- a/tools/tapePlayer.cpp +++ b/tools/tapePlayer.cpp @@ -22,6 +22,9 @@ int main(int argc, char** argv) return 1; } TapePlayer player{argv[1]}; - player.playUntil(); + while (true) { + player.playApproximatelyRealtime(); + player.reset(); + } return 0; } \ No newline at end of file