diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index aebab03b..fee069f6 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -25,6 +25,10 @@ void Core::Stop() { cpu->mem.Reset(); } +bool Core::LoadTAS(const fs::path &path) { + return cpu->mem.mmio.si.pif.movie.Load(path); +} + void Core::LoadROM(const std::string& rom_) { pause = true; rom = rom_; diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index a5f47597..f6968135 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -11,6 +11,7 @@ struct Core { Core(); void Stop(); void LoadROM(const std::string&); + bool LoadTAS(const fs::path&); void Run(float volumeL, float volumeR); void Serialize(); void Deserialize(); diff --git a/src/backend/core/mmio/PIF.hpp b/src/backend/core/mmio/PIF.hpp index 1f51b90a..07e91775 100644 --- a/src/backend/core/mmio/PIF.hpp +++ b/src/backend/core/mmio/PIF.hpp @@ -4,6 +4,7 @@ #include #include #include +#include "MupenMovie.hpp" namespace fs = std::filesystem; @@ -21,10 +22,10 @@ struct Controller { union { u8 byte1; struct { - bool dp_right: 1; - bool dp_left: 1; - bool dp_down: 1; - bool dp_up: 1; + bool dpRight: 1; + bool dpLeft: 1; + bool dpDown: 1; + bool dpUp: 1; bool start: 1; bool z: 1; bool b: 1; @@ -34,19 +35,19 @@ struct Controller { union { u8 byte2; struct { - bool c_right: 1; - bool c_left: 1; - bool c_down: 1; - bool c_up: 1; + bool cRight: 1; + bool cLeft: 1; + bool cDown: 1; + bool cUp: 1; bool r: 1; bool l: 1; bool zero: 1; - bool joy_reset: 1; + bool joyReset: 1; }; }; - s8 joy_x; - s8 joy_y; + s8 joyX; + s8 joyY; }; u32 raw; @@ -54,8 +55,8 @@ struct Controller { Controller& operator=(const Controller& other) { byte1 = other.byte1; byte2 = other.byte2; - joy_x = other.joy_x; - joy_y = other.joy_y; + joyX = other.joyX; + joyY = other.joyY; return *this; } @@ -73,14 +74,14 @@ struct Controller { case B: b = state; break; case Z: z = state; break; case Start: start = state; break; - case DUp: dp_up = state; break; - case DDown: dp_down = state; break; - case DLeft: dp_left = state; break; - case DRight: dp_right = state; break; - case CUp: c_up = state; break; - case CDown: c_down = state; break; - case CLeft: c_left = state; break; - case CRight: c_right = state; break; + case DUp: dpUp = state; break; + case DDown: dpDown = state; break; + case DLeft: dpLeft = state; break; + case DRight: dpRight = state; break; + case CUp: cUp = state; break; + case CDown: cDown = state; break; + case CLeft: cLeft = state; break; + case CRight: cRight = state; break; case LT: l = state; break; case RT: r = state; break; } @@ -88,14 +89,14 @@ struct Controller { void UpdateAxis(Axis a, s8 state) { switch(a) { - case X: joy_x = state; break; - case Y: joy_y = state; break; + case X: joyX = state; break; + case Y: joyY = state; break; } } Controller& operator=(u32 v) { - joy_y = v & 0xff; - joy_x = v >> 8; + joyY = v & 0xff; + joyX = v >> 8; byte2 = v >> 16; byte1 = v >> 24; @@ -161,7 +162,7 @@ struct PIF { void CICChallenge(); static void ExecutePIF(Mem& mem, Registers& regs); static void DoPIFHLE(Mem& mem, Registers& regs, bool pal, CICType cicType); - bool ReadButtons(u8*) const; + bool ReadButtons(u8*); void ControllerID(u8*) const; void MempakRead(const u8*, u8*); void MempakWrite(u8*, u8*); @@ -176,6 +177,7 @@ struct PIF { int channel = 0; std::string mempakPath{}, eepromPath{}; size_t eepromSize{}; + MupenMovie movie; FORCE_INLINE u8 Read(u32 addr) { addr &= 0x7FF; diff --git a/src/backend/core/mmio/PIF/Device.cpp b/src/backend/core/mmio/PIF/Device.cpp index 92628e24..441108be 100644 --- a/src/backend/core/mmio/PIF/Device.cpp +++ b/src/backend/core/mmio/PIF/Device.cpp @@ -76,7 +76,7 @@ void PIF::ControllerID(u8 *res) const { } } -bool PIF::ReadButtons(u8* res) const { +bool PIF::ReadButtons(u8* res) { if(channel >= 6) { res[0] = 0; res[1] = 0; @@ -95,17 +95,17 @@ bool PIF::ReadButtons(u8* res) const { case JOYBUS_4KB_EEPROM: case JOYBUS_16KB_EEPROM: case JOYBUS_CONTROLLER: - if (TasMovieLoaded()) { - Controller controller = TasNextInputs(); + if (movie.IsLoaded()) { + Controller controller = movie.NextInputs(); res[0] = controller.byte1; res[1] = controller.byte2; - res[2] = controller.joy_x; - res[3] = controller.joy_y; + res[2] = controller.joyX; + res[3] = controller.joyY; } else { res[0] = joybusDevices[channel].controller.byte1; res[1] = joybusDevices[channel].controller.byte2; - res[2] = joybusDevices[channel].controller.joy_x; - res[3] = joybusDevices[channel].controller.joy_y; + res[2] = joybusDevices[channel].controller.joyX; + res[3] = joybusDevices[channel].controller.joyY; } return true; case JOYBUS_DANCEPAD: diff --git a/src/backend/core/mmio/PIF/MupenMovie.cpp b/src/backend/core/mmio/PIF/MupenMovie.cpp index 99dc31bf..2ce74620 100644 --- a/src/backend/core/mmio/PIF/MupenMovie.cpp +++ b/src/backend/core/mmio/PIF/MupenMovie.cpp @@ -1,178 +1,128 @@ #include #include - -struct TASMovieHeader { - u8 signature[4]; - u32 version; - u32 uid; - u32 numFrames; - u32 rerecords; - u8 fps; - u8 numControllers; - u8 reserved1; - u8 reserved2; - u32 numInputSamples; - uint16_t startType; - u8 reserved3; - u8 reserved4; - u32 controllerFlags; - u8 reserved5[160]; - char romName[32]; - u32 romCrc32; - uint16_t romCountryCode; - u8 reserved6[56]; - // 122 64-byte ASCII string: name of video plugin used when recording, directly from plugin - char video_plugin_name[64]; - // 162 64-byte ASCII string: name of sound plugin used when recording, directly from plugin - char audio_plugin_name[64]; - // 1A2 64-byte ASCII string: name of input plugin used when recording, directly from plugin - char input_plugin_name[64]; - // 1E2 64-byte ASCII string: name of rsp plugin used when recording, directly from plugin - char rsp_plugin_name[64]; - // 222 222-byte UTF-8 string: author name info - char author_name[222]; - // 300 256-byte UTF-8 string: author movie description info - char movie_description[256]; -} __attribute__((packed)); - -static_assert(sizeof(TASMovieHeader) == 1024); +#include "File.hpp" +#include "PIF.hpp" union TASMovieControllerData { struct { - bool dpad_right: 1; - bool dpad_left: 1; - bool dpad_down: 1; - bool dpad_up: 1; - bool start: 1; - bool z: 1; - bool b: 1; - bool a: 1; - bool c_right: 1; - bool c_left: 1; - bool c_down: 1; - bool c_up: 1; - bool r: 1; - bool l: 1; - u8: 2; - s8 analog_x: 8; - s8 analog_y: 8; + unsigned dpadRight: 1; + unsigned dpadLeft: 1; + unsigned dpadDown: 1; + unsigned dpadUp: 1; + unsigned start: 1; + unsigned z: 1; + unsigned b: 1; + unsigned a: 1; + unsigned cRight: 1; + unsigned cLeft: 1; + unsigned cDown: 1; + unsigned cUp: 1; + unsigned r: 1; + unsigned l: 1; + unsigned : 2; + signed analogX : 8; + signed analogY : 8; }; u32 raw; } __attribute__((packed)); static_assert(sizeof(TASMovieControllerData) == 4); -static u8* loaded_tas_movie = nullptr; -static size_t loaded_tas_movie_size = 0; -TASMovieHeader loaded_tas_movie_header; -uint32_t loaded_tas_movie_index = 0; - -void LoadTAS(const char* filename) { - FILE *fp = fopen(filename, "rb"); - - if (!fp) { - Util::panic("Error opening the movie file {}! Are you sure it's a valid movie and that it exists?", filename); +bool MupenMovie::Load(const fs::path &path) { + loadedTasMovie = Util::ReadFileBinary(path.string()); + if(!IsLoaded()) { + Util::error("Error loading movie!"); + return false; } - fseek(fp, 0, SEEK_END); - size_t size = ftell(fp); + memcpy(&loadedTasMovieHeader, loadedTasMovie.data(), loadedTasMovie.size()); - fseek(fp, 0, SEEK_SET); - u8 *buf = (u8*)malloc(size); - fread(buf, size, 1, fp); - - loaded_tas_movie = buf; - loaded_tas_movie_size = size; - - if (!loaded_tas_movie) { - Util::panic("Error loading movie!"); + if (loadedTasMovieHeader.signature[0] != 0x4D || loadedTasMovieHeader.signature[1] != 0x36 || loadedTasMovieHeader.signature[2] != 0x34 || loadedTasMovieHeader.signature[3] != 0x1A) { + Util::error("Failed to load movie: incorrect signature. Are you sure this is a valid movie?"); + return false; } - memcpy(&loaded_tas_movie_header, buf, sizeof(TASMovieHeader)); - - if (loaded_tas_movie_header.signature[0] != 0x4D || loaded_tas_movie_header.signature[1] != 0x36 || loaded_tas_movie_header.signature[2] != 0x34 || loaded_tas_movie_header.signature[3] != 0x1A) { - Util::panic("Failed to load movie: incorrect signature. Are you sure this is a valid movie?"); + if (loadedTasMovieHeader.version != 3) { + Util::error("This movie is version {}: only version 3 is supported.", loadedTasMovieHeader.version); + return false; } - if (loaded_tas_movie_header.version != 3) { - Util::panic("This movie is version {}: only version 3 is supported.", loaded_tas_movie_header.version); + if (loadedTasMovieHeader.startType != 2) { + Util::error("Movie start type is {} - only movies with a start type of 2 are supported (start at power on)", loadedTasMovieHeader.startType); + return false; } - if (loaded_tas_movie_header.startType != 2) { - Util::panic("Movie start type is {} - only movies with a start type of 2 are supported (start at power on)", loaded_tas_movie_header.startType); + Util::info("Loaded movie '{}' ", loadedTasMovieHeader.movie_description); + Util::info("by {}", loadedTasMovieHeader.author_name); + Util::info("{} controller(s) connected", loadedTasMovieHeader.numControllers); + + if (loadedTasMovieHeader.numControllers != 1) { + Util::error("Currently, only movies with 1 controller connected are supported."); + return false; } - // TODO: check ROM CRC32 here - - Util::info("Loaded movie '{}' ", loaded_tas_movie_header.movie_description); - Util::info("by {}", loaded_tas_movie_header.author_name); - Util::info("{} controller(s) connected", loaded_tas_movie_header.numControllers); - - if (loaded_tas_movie_header.numControllers != 1) { - Util::panic("Currently, only movies with 1 controller connected are supported."); - } - - loaded_tas_movie_index = sizeof(TASMovieHeader) - 4; // skip header + loadedTasMovieIndex = sizeof(TASMovieHeader) - 4; // skip header + return true; } -bool TasMovieLoaded() { - return loaded_tas_movie != nullptr; +MupenMovie::MupenMovie(const fs::path &path) { + if(!Load(path)) { + Util::panic(""); + } } FORCE_INLINE void LogController(const n64::Controller& controller) { - Util::debug("c_right: {}", controller.c_right); - Util::debug("c_left: {}", controller.c_left); - Util::debug("c_down: {}", controller.c_down); - Util::debug("c_up: {}", controller.c_up); + Util::debug("c_right: {}", controller.cRight); + Util::debug("c_left: {}", controller.cLeft); + Util::debug("c_down: {}", controller.cDown); + Util::debug("c_up: {}", controller.cUp); Util::debug("r: {}", controller.r); Util::debug("l: {}", controller.l); - Util::debug("dp_right: {}", controller.dp_right); - Util::debug("dp_left: {}", controller.dp_left); - Util::debug("dp_down: {}", controller.dp_down); - Util::debug("dp_up: {}", controller.dp_up); + Util::debug("dp_right: {}", controller.dpRight); + Util::debug("dp_left: {}", controller.dpLeft); + Util::debug("dp_down: {}", controller.dpDown); + Util::debug("dp_up: {}", controller.dpUp); Util::debug("z: {}", controller.z); Util::debug("b: {}", controller.b); Util::debug("a: {}", controller.a); Util::debug("start: {}", controller.start); - Util::debug("joy_x: {}", controller.joy_x); - Util::debug("joy_y: {}", controller.joy_y); + Util::debug("joy_x: {}", controller.joyX); + Util::debug("joy_y: {}", controller.joyY); } -n64::Controller TasNextInputs() { - if (loaded_tas_movie_index + sizeof(TASMovieControllerData) > loaded_tas_movie_size) { - loaded_tas_movie = nullptr; - n64::Controller empty_controller{}; - memset(&empty_controller, 0, sizeof(n64::Controller)); - return empty_controller; +n64::Controller MupenMovie::NextInputs() { + if (loadedTasMovieIndex + sizeof(TASMovieControllerData) > loadedTasMovie.size()) { + loadedTasMovie.clear(); + n64::Controller emptyController{}; + return emptyController; } - TASMovieControllerData movie_cdata{}; - memcpy(&movie_cdata, loaded_tas_movie + loaded_tas_movie_index, sizeof(TASMovieControllerData)); + TASMovieControllerData movieCData{}; + memcpy(&movieCData, &loadedTasMovie[loadedTasMovieIndex], sizeof(TASMovieControllerData)); - loaded_tas_movie_index += sizeof(TASMovieControllerData); + loadedTasMovieIndex += sizeof(TASMovieControllerData); n64::Controller controller{}; - memset(&controller, 0, sizeof(controller)); - controller.c_right = movie_cdata.c_right; - controller.c_left = movie_cdata.c_left; - controller.c_down = movie_cdata.c_down; - controller.c_up = movie_cdata.c_up; - controller.r = movie_cdata.r; - controller.l = movie_cdata.l; + controller.cRight = movieCData.cRight; + controller.cLeft = movieCData.cLeft; + controller.cDown = movieCData.cDown; + controller.cUp = movieCData.cUp; + controller.r = movieCData.r; + controller.l = movieCData.l; - controller.dp_right = movie_cdata.dpad_right; - controller.dp_left = movie_cdata.dpad_left; - controller.dp_down = movie_cdata.dpad_down; - controller.dp_up = movie_cdata.dpad_up; + controller.dpRight = movieCData.dpadRight; + controller.dpLeft = movieCData.dpadLeft; + controller.dpDown = movieCData.dpadDown; + controller.dpUp = movieCData.dpadUp; - controller.z = movie_cdata.z; - controller.b = movie_cdata.b; - controller.a = movie_cdata.a; - controller.start = movie_cdata.start; + controller.z = movieCData.z; + controller.b = movieCData.b; + controller.a = movieCData.a; + controller.start = movieCData.start; - controller.joy_x = movie_cdata.analog_x; - controller.joy_y = movie_cdata.analog_y; + controller.joyX = movieCData.analogX; + controller.joyY = movieCData.analogY; LogController(controller); diff --git a/src/backend/core/mmio/PIF/MupenMovie.hpp b/src/backend/core/mmio/PIF/MupenMovie.hpp index ca73c3f2..acdc7b60 100644 --- a/src/backend/core/mmio/PIF/MupenMovie.hpp +++ b/src/backend/core/mmio/PIF/MupenMovie.hpp @@ -1,6 +1,60 @@ #pragma once -#include +#include +#include +#include -void LoadTAS(const char* filename); -n64::Controller TasNextInputs(); -bool TasMovieLoaded(); \ No newline at end of file +namespace fs = std::filesystem; + +namespace n64 { +struct Controller; +} + +struct TASMovieHeader { + u8 signature[4]; + u32 version; + u32 uid; + u32 numFrames; + u32 rerecords; + u8 fps; + u8 numControllers; + u8 reserved1; + u8 reserved2; + u32 numInputSamples; + uint16_t startType; + u8 reserved3; + u8 reserved4; + u32 controllerFlags; + u8 reserved5[160]; + char romName[32]; + u32 romCrc32; + uint16_t romCountryCode; + u8 reserved6[56]; + // 122 64-byte ASCII string: name of video plugin used when recording, directly from plugin + char video_plugin_name[64]; + // 162 64-byte ASCII string: name of sound plugin used when recording, directly from plugin + char audio_plugin_name[64]; + // 1A2 64-byte ASCII string: name of input plugin used when recording, directly from plugin + char input_plugin_name[64]; + // 1E2 64-byte ASCII string: name of rsp plugin used when recording, directly from plugin + char rsp_plugin_name[64]; + // 222 222-byte UTF-8 string: author name info + char author_name[222]; + // 300 256-byte UTF-8 string: author movie description info + char movie_description[256]; +} __attribute__((packed)); + +static_assert(sizeof(TASMovieHeader) == 1024); + +struct MupenMovie { + MupenMovie() = default; + MupenMovie(const fs::path&); + bool Load(const fs::path&); + n64::Controller NextInputs(); + bool IsLoaded() const { return !loadedTasMovie.empty(); } +private: + std::string filename = ""; + std::string game = ""; + std::vector loadedTasMovie = {}; + TASMovieHeader loadedTasMovieHeader = {}; + uint32_t loadedTasMovieIndex = 0; +}; \ No newline at end of file diff --git a/src/backend/core/registers/Cop0.cpp b/src/backend/core/registers/Cop0.cpp index bf041c05..5663af0c 100644 --- a/src/backend/core/registers/Cop0.cpp +++ b/src/backend/core/registers/Cop0.cpp @@ -254,7 +254,7 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) { bool old_exl = regs.cop0.status.exl; if(!regs.cop0.status.exl) { - if(regs.cop0.cause.branchDelay = regs.prevDelaySlot) { + if((regs.cop0.cause.branchDelay = regs.prevDelaySlot)) { pc -= 4; } diff --git a/src/frontend/EmuThread.hpp b/src/frontend/EmuThread.hpp index 9538c890..bce4155c 100644 --- a/src/frontend/EmuThread.hpp +++ b/src/frontend/EmuThread.hpp @@ -24,6 +24,10 @@ public: SettingsWindow* settings; bool running = false; + bool LoadTAS(const fs::path& path) { + return core->LoadTAS(path); + } + void TogglePause() { running = !running; diff --git a/src/frontend/JSONUtils.hpp b/src/frontend/JSONUtils.hpp index add4a4a9..7f8e394d 100644 --- a/src/frontend/JSONUtils.hpp +++ b/src/frontend/JSONUtils.hpp @@ -21,20 +21,20 @@ static inline nlohmann::json JSONOpenOrCreate(const std::string& path) { json["audio"]["lock"] = true; json["cpu"]["type"] = "interpreter"; json["input"] = { - {"A", ""}, - {"B", ""}, - {"Z", ""}, - {"Start", ""}, - {"L", ""}, - {"R", ""}, + {"A", "X"}, + {"B", "C"}, + {"Z", "Z"}, + {"Start", "Enter"}, + {"L", "A"}, + {"R", "S"}, {"Dpad Up", ""}, {"Dpad Down", ""}, {"Dpad Left", ""}, {"Dpad Right", ""}, - {"C Up", ""}, - {"C Down", ""}, - {"C Left", ""}, - {"C Right", ""}, + {"C Up", "I"}, + {"C Down", "K"}, + {"C Left", "J"}, + {"C Right", "L"}, {"Analog Up", ""}, {"Analog Down", ""}, {"Analog Left", ""}, diff --git a/src/frontend/KaizenQt.cpp b/src/frontend/KaizenQt.cpp index dc563784..1fce29b8 100644 --- a/src/frontend/KaizenQt.cpp +++ b/src/frontend/KaizenQt.cpp @@ -3,6 +3,9 @@ #include #include #include +#include + +namespace fs = std::filesystem; KaizenQt::KaizenQt() noexcept : QWidget(nullptr) { mainWindow = new MainWindowController(); @@ -52,9 +55,13 @@ void KaizenQt::dropEvent(QDropEvent* event) { LoadROM(path); } -void KaizenQt::LoadROM(const QString& file_name) noexcept { +void KaizenQt::LoadROM(const QString& fileName) noexcept { emuThread->start(); - emuThread->core->LoadROM(file_name.toStdString()); + emuThread->core->LoadROM(fileName.toStdString()); +} + +void KaizenQt::LoadTAS(const QString& fileName) noexcept { + emuThread->core->LoadTAS(fileName.toStdString()); } void KaizenQt::keyPressEvent(QKeyEvent *e) { diff --git a/src/frontend/KaizenQt.hpp b/src/frontend/KaizenQt.hpp index bd478ebd..dfb213cc 100644 --- a/src/frontend/KaizenQt.hpp +++ b/src/frontend/KaizenQt.hpp @@ -27,6 +27,7 @@ class KaizenQt : public QWidget { Q_OBJECT public: KaizenQt() noexcept; + void LoadTAS(const QString& path) noexcept; void LoadROM(const QString& path) noexcept; void dropEvent(QDropEvent*) override; void dragEnterEvent(QDragEnterEvent*) override; diff --git a/src/frontend/main.cpp b/src/frontend/main.cpp index 95ed6b20..5e08594d 100644 --- a/src/frontend/main.cpp +++ b/src/frontend/main.cpp @@ -1,8 +1,6 @@ #include #include #include -#include -#include int main(int argc, char** argv) { QApplication app(argc, argv); @@ -20,7 +18,7 @@ int main(int argc, char** argv) { if (parser.positionalArguments().size() > 0) { kaizenQt.LoadROM(parser.positionalArguments().first()); if (parser.positionalArguments().size() > 1) { - LoadTAS(parser.positionalArguments()[1].toStdString().c_str()); + kaizenQt.LoadTAS(parser.positionalArguments()[1]); } }