Refactor Mupen Movies
This commit is contained in:
@@ -25,6 +25,10 @@ void Core::Stop() {
|
|||||||
cpu->mem.Reset();
|
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_) {
|
void Core::LoadROM(const std::string& rom_) {
|
||||||
pause = true;
|
pause = true;
|
||||||
rom = rom_;
|
rom = rom_;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ struct Core {
|
|||||||
Core();
|
Core();
|
||||||
void Stop();
|
void Stop();
|
||||||
void LoadROM(const std::string&);
|
void LoadROM(const std::string&);
|
||||||
|
bool LoadTAS(const fs::path&);
|
||||||
void Run(float volumeL, float volumeR);
|
void Run(float volumeL, float volumeR);
|
||||||
void Serialize();
|
void Serialize();
|
||||||
void Deserialize();
|
void Deserialize();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <mio/mmap.hpp>
|
#include <mio/mmap.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "MupenMovie.hpp"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
@@ -21,10 +22,10 @@ struct Controller {
|
|||||||
union {
|
union {
|
||||||
u8 byte1;
|
u8 byte1;
|
||||||
struct {
|
struct {
|
||||||
bool dp_right: 1;
|
bool dpRight: 1;
|
||||||
bool dp_left: 1;
|
bool dpLeft: 1;
|
||||||
bool dp_down: 1;
|
bool dpDown: 1;
|
||||||
bool dp_up: 1;
|
bool dpUp: 1;
|
||||||
bool start: 1;
|
bool start: 1;
|
||||||
bool z: 1;
|
bool z: 1;
|
||||||
bool b: 1;
|
bool b: 1;
|
||||||
@@ -34,19 +35,19 @@ struct Controller {
|
|||||||
union {
|
union {
|
||||||
u8 byte2;
|
u8 byte2;
|
||||||
struct {
|
struct {
|
||||||
bool c_right: 1;
|
bool cRight: 1;
|
||||||
bool c_left: 1;
|
bool cLeft: 1;
|
||||||
bool c_down: 1;
|
bool cDown: 1;
|
||||||
bool c_up: 1;
|
bool cUp: 1;
|
||||||
bool r: 1;
|
bool r: 1;
|
||||||
bool l: 1;
|
bool l: 1;
|
||||||
bool zero: 1;
|
bool zero: 1;
|
||||||
bool joy_reset: 1;
|
bool joyReset: 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
s8 joy_x;
|
s8 joyX;
|
||||||
s8 joy_y;
|
s8 joyY;
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 raw;
|
u32 raw;
|
||||||
@@ -54,8 +55,8 @@ struct Controller {
|
|||||||
Controller& operator=(const Controller& other) {
|
Controller& operator=(const Controller& other) {
|
||||||
byte1 = other.byte1;
|
byte1 = other.byte1;
|
||||||
byte2 = other.byte2;
|
byte2 = other.byte2;
|
||||||
joy_x = other.joy_x;
|
joyX = other.joyX;
|
||||||
joy_y = other.joy_y;
|
joyY = other.joyY;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -73,14 +74,14 @@ struct Controller {
|
|||||||
case B: b = state; break;
|
case B: b = state; break;
|
||||||
case Z: z = state; break;
|
case Z: z = state; break;
|
||||||
case Start: start = state; break;
|
case Start: start = state; break;
|
||||||
case DUp: dp_up = state; break;
|
case DUp: dpUp = state; break;
|
||||||
case DDown: dp_down = state; break;
|
case DDown: dpDown = state; break;
|
||||||
case DLeft: dp_left = state; break;
|
case DLeft: dpLeft = state; break;
|
||||||
case DRight: dp_right = state; break;
|
case DRight: dpRight = state; break;
|
||||||
case CUp: c_up = state; break;
|
case CUp: cUp = state; break;
|
||||||
case CDown: c_down = state; break;
|
case CDown: cDown = state; break;
|
||||||
case CLeft: c_left = state; break;
|
case CLeft: cLeft = state; break;
|
||||||
case CRight: c_right = state; break;
|
case CRight: cRight = state; break;
|
||||||
case LT: l = state; break;
|
case LT: l = state; break;
|
||||||
case RT: r = state; break;
|
case RT: r = state; break;
|
||||||
}
|
}
|
||||||
@@ -88,14 +89,14 @@ struct Controller {
|
|||||||
|
|
||||||
void UpdateAxis(Axis a, s8 state) {
|
void UpdateAxis(Axis a, s8 state) {
|
||||||
switch(a) {
|
switch(a) {
|
||||||
case X: joy_x = state; break;
|
case X: joyX = state; break;
|
||||||
case Y: joy_y = state; break;
|
case Y: joyY = state; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller& operator=(u32 v) {
|
Controller& operator=(u32 v) {
|
||||||
joy_y = v & 0xff;
|
joyY = v & 0xff;
|
||||||
joy_x = v >> 8;
|
joyX = v >> 8;
|
||||||
byte2 = v >> 16;
|
byte2 = v >> 16;
|
||||||
byte1 = v >> 24;
|
byte1 = v >> 24;
|
||||||
|
|
||||||
@@ -161,7 +162,7 @@ struct PIF {
|
|||||||
void CICChallenge();
|
void CICChallenge();
|
||||||
static void ExecutePIF(Mem& mem, Registers& regs);
|
static void ExecutePIF(Mem& mem, Registers& regs);
|
||||||
static void DoPIFHLE(Mem& mem, Registers& regs, bool pal, CICType cicType);
|
static void DoPIFHLE(Mem& mem, Registers& regs, bool pal, CICType cicType);
|
||||||
bool ReadButtons(u8*) const;
|
bool ReadButtons(u8*);
|
||||||
void ControllerID(u8*) const;
|
void ControllerID(u8*) const;
|
||||||
void MempakRead(const u8*, u8*);
|
void MempakRead(const u8*, u8*);
|
||||||
void MempakWrite(u8*, u8*);
|
void MempakWrite(u8*, u8*);
|
||||||
@@ -176,6 +177,7 @@ struct PIF {
|
|||||||
int channel = 0;
|
int channel = 0;
|
||||||
std::string mempakPath{}, eepromPath{};
|
std::string mempakPath{}, eepromPath{};
|
||||||
size_t eepromSize{};
|
size_t eepromSize{};
|
||||||
|
MupenMovie movie;
|
||||||
|
|
||||||
FORCE_INLINE u8 Read(u32 addr) {
|
FORCE_INLINE u8 Read(u32 addr) {
|
||||||
addr &= 0x7FF;
|
addr &= 0x7FF;
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ void PIF::ControllerID(u8 *res) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PIF::ReadButtons(u8* res) const {
|
bool PIF::ReadButtons(u8* res) {
|
||||||
if(channel >= 6) {
|
if(channel >= 6) {
|
||||||
res[0] = 0;
|
res[0] = 0;
|
||||||
res[1] = 0;
|
res[1] = 0;
|
||||||
@@ -95,17 +95,17 @@ bool PIF::ReadButtons(u8* res) const {
|
|||||||
case JOYBUS_4KB_EEPROM:
|
case JOYBUS_4KB_EEPROM:
|
||||||
case JOYBUS_16KB_EEPROM:
|
case JOYBUS_16KB_EEPROM:
|
||||||
case JOYBUS_CONTROLLER:
|
case JOYBUS_CONTROLLER:
|
||||||
if (TasMovieLoaded()) {
|
if (movie.IsLoaded()) {
|
||||||
Controller controller = TasNextInputs();
|
Controller controller = movie.NextInputs();
|
||||||
res[0] = controller.byte1;
|
res[0] = controller.byte1;
|
||||||
res[1] = controller.byte2;
|
res[1] = controller.byte2;
|
||||||
res[2] = controller.joy_x;
|
res[2] = controller.joyX;
|
||||||
res[3] = controller.joy_y;
|
res[3] = controller.joyY;
|
||||||
} else {
|
} else {
|
||||||
res[0] = joybusDevices[channel].controller.byte1;
|
res[0] = joybusDevices[channel].controller.byte1;
|
||||||
res[1] = joybusDevices[channel].controller.byte2;
|
res[1] = joybusDevices[channel].controller.byte2;
|
||||||
res[2] = joybusDevices[channel].controller.joy_x;
|
res[2] = joybusDevices[channel].controller.joyX;
|
||||||
res[3] = joybusDevices[channel].controller.joy_y;
|
res[3] = joybusDevices[channel].controller.joyY;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case JOYBUS_DANCEPAD:
|
case JOYBUS_DANCEPAD:
|
||||||
|
|||||||
@@ -1,178 +1,128 @@
|
|||||||
#include <PIF/MupenMovie.hpp>
|
#include <PIF/MupenMovie.hpp>
|
||||||
#include <log.hpp>
|
#include <log.hpp>
|
||||||
|
#include "File.hpp"
|
||||||
struct TASMovieHeader {
|
#include "PIF.hpp"
|
||||||
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);
|
|
||||||
|
|
||||||
union TASMovieControllerData {
|
union TASMovieControllerData {
|
||||||
struct {
|
struct {
|
||||||
bool dpad_right: 1;
|
unsigned dpadRight: 1;
|
||||||
bool dpad_left: 1;
|
unsigned dpadLeft: 1;
|
||||||
bool dpad_down: 1;
|
unsigned dpadDown: 1;
|
||||||
bool dpad_up: 1;
|
unsigned dpadUp: 1;
|
||||||
bool start: 1;
|
unsigned start: 1;
|
||||||
bool z: 1;
|
unsigned z: 1;
|
||||||
bool b: 1;
|
unsigned b: 1;
|
||||||
bool a: 1;
|
unsigned a: 1;
|
||||||
bool c_right: 1;
|
unsigned cRight: 1;
|
||||||
bool c_left: 1;
|
unsigned cLeft: 1;
|
||||||
bool c_down: 1;
|
unsigned cDown: 1;
|
||||||
bool c_up: 1;
|
unsigned cUp: 1;
|
||||||
bool r: 1;
|
unsigned r: 1;
|
||||||
bool l: 1;
|
unsigned l: 1;
|
||||||
u8: 2;
|
unsigned : 2;
|
||||||
s8 analog_x: 8;
|
signed analogX : 8;
|
||||||
s8 analog_y: 8;
|
signed analogY : 8;
|
||||||
};
|
};
|
||||||
u32 raw;
|
u32 raw;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
static_assert(sizeof(TASMovieControllerData) == 4);
|
static_assert(sizeof(TASMovieControllerData) == 4);
|
||||||
|
|
||||||
static u8* loaded_tas_movie = nullptr;
|
bool MupenMovie::Load(const fs::path &path) {
|
||||||
static size_t loaded_tas_movie_size = 0;
|
loadedTasMovie = Util::ReadFileBinary(path.string());
|
||||||
TASMovieHeader loaded_tas_movie_header;
|
if(!IsLoaded()) {
|
||||||
uint32_t loaded_tas_movie_index = 0;
|
Util::error("Error loading movie!");
|
||||||
|
return false;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
memcpy(&loadedTasMovieHeader, loadedTasMovie.data(), loadedTasMovie.size());
|
||||||
size_t size = ftell(fp);
|
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_SET);
|
if (loadedTasMovieHeader.signature[0] != 0x4D || loadedTasMovieHeader.signature[1] != 0x36 || loadedTasMovieHeader.signature[2] != 0x34 || loadedTasMovieHeader.signature[3] != 0x1A) {
|
||||||
u8 *buf = (u8*)malloc(size);
|
Util::error("Failed to load movie: incorrect signature. Are you sure this is a valid movie?");
|
||||||
fread(buf, size, 1, fp);
|
return false;
|
||||||
|
|
||||||
loaded_tas_movie = buf;
|
|
||||||
loaded_tas_movie_size = size;
|
|
||||||
|
|
||||||
if (!loaded_tas_movie) {
|
|
||||||
Util::panic("Error loading movie!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&loaded_tas_movie_header, buf, sizeof(TASMovieHeader));
|
if (loadedTasMovieHeader.version != 3) {
|
||||||
|
Util::error("This movie is version {}: only version 3 is supported.", loadedTasMovieHeader.version);
|
||||||
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) {
|
return false;
|
||||||
Util::panic("Failed to load movie: incorrect signature. Are you sure this is a valid movie?");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loaded_tas_movie_header.version != 3) {
|
if (loadedTasMovieHeader.startType != 2) {
|
||||||
Util::panic("This movie is version {}: only version 3 is supported.", loaded_tas_movie_header.version);
|
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::info("Loaded movie '{}' ", loadedTasMovieHeader.movie_description);
|
||||||
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("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
|
loadedTasMovieIndex = sizeof(TASMovieHeader) - 4; // skip header
|
||||||
|
return true;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TasMovieLoaded() {
|
MupenMovie::MupenMovie(const fs::path &path) {
|
||||||
return loaded_tas_movie != nullptr;
|
if(!Load(path)) {
|
||||||
|
Util::panic("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE void LogController(const n64::Controller& controller) {
|
FORCE_INLINE void LogController(const n64::Controller& controller) {
|
||||||
Util::debug("c_right: {}", controller.c_right);
|
Util::debug("c_right: {}", controller.cRight);
|
||||||
Util::debug("c_left: {}", controller.c_left);
|
Util::debug("c_left: {}", controller.cLeft);
|
||||||
Util::debug("c_down: {}", controller.c_down);
|
Util::debug("c_down: {}", controller.cDown);
|
||||||
Util::debug("c_up: {}", controller.c_up);
|
Util::debug("c_up: {}", controller.cUp);
|
||||||
Util::debug("r: {}", controller.r);
|
Util::debug("r: {}", controller.r);
|
||||||
Util::debug("l: {}", controller.l);
|
Util::debug("l: {}", controller.l);
|
||||||
Util::debug("dp_right: {}", controller.dp_right);
|
Util::debug("dp_right: {}", controller.dpRight);
|
||||||
Util::debug("dp_left: {}", controller.dp_left);
|
Util::debug("dp_left: {}", controller.dpLeft);
|
||||||
Util::debug("dp_down: {}", controller.dp_down);
|
Util::debug("dp_down: {}", controller.dpDown);
|
||||||
Util::debug("dp_up: {}", controller.dp_up);
|
Util::debug("dp_up: {}", controller.dpUp);
|
||||||
Util::debug("z: {}", controller.z);
|
Util::debug("z: {}", controller.z);
|
||||||
Util::debug("b: {}", controller.b);
|
Util::debug("b: {}", controller.b);
|
||||||
Util::debug("a: {}", controller.a);
|
Util::debug("a: {}", controller.a);
|
||||||
Util::debug("start: {}", controller.start);
|
Util::debug("start: {}", controller.start);
|
||||||
Util::debug("joy_x: {}", controller.joy_x);
|
Util::debug("joy_x: {}", controller.joyX);
|
||||||
Util::debug("joy_y: {}", controller.joy_y);
|
Util::debug("joy_y: {}", controller.joyY);
|
||||||
}
|
}
|
||||||
|
|
||||||
n64::Controller TasNextInputs() {
|
n64::Controller MupenMovie::NextInputs() {
|
||||||
if (loaded_tas_movie_index + sizeof(TASMovieControllerData) > loaded_tas_movie_size) {
|
if (loadedTasMovieIndex + sizeof(TASMovieControllerData) > loadedTasMovie.size()) {
|
||||||
loaded_tas_movie = nullptr;
|
loadedTasMovie.clear();
|
||||||
n64::Controller empty_controller{};
|
n64::Controller emptyController{};
|
||||||
memset(&empty_controller, 0, sizeof(n64::Controller));
|
return emptyController;
|
||||||
return empty_controller;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TASMovieControllerData movie_cdata{};
|
TASMovieControllerData movieCData{};
|
||||||
memcpy(&movie_cdata, loaded_tas_movie + loaded_tas_movie_index, sizeof(TASMovieControllerData));
|
memcpy(&movieCData, &loadedTasMovie[loadedTasMovieIndex], sizeof(TASMovieControllerData));
|
||||||
|
|
||||||
loaded_tas_movie_index += sizeof(TASMovieControllerData);
|
loadedTasMovieIndex += sizeof(TASMovieControllerData);
|
||||||
|
|
||||||
n64::Controller controller{};
|
n64::Controller controller{};
|
||||||
memset(&controller, 0, sizeof(controller));
|
|
||||||
|
|
||||||
controller.c_right = movie_cdata.c_right;
|
controller.cRight = movieCData.cRight;
|
||||||
controller.c_left = movie_cdata.c_left;
|
controller.cLeft = movieCData.cLeft;
|
||||||
controller.c_down = movie_cdata.c_down;
|
controller.cDown = movieCData.cDown;
|
||||||
controller.c_up = movie_cdata.c_up;
|
controller.cUp = movieCData.cUp;
|
||||||
controller.r = movie_cdata.r;
|
controller.r = movieCData.r;
|
||||||
controller.l = movie_cdata.l;
|
controller.l = movieCData.l;
|
||||||
|
|
||||||
controller.dp_right = movie_cdata.dpad_right;
|
controller.dpRight = movieCData.dpadRight;
|
||||||
controller.dp_left = movie_cdata.dpad_left;
|
controller.dpLeft = movieCData.dpadLeft;
|
||||||
controller.dp_down = movie_cdata.dpad_down;
|
controller.dpDown = movieCData.dpadDown;
|
||||||
controller.dp_up = movie_cdata.dpad_up;
|
controller.dpUp = movieCData.dpadUp;
|
||||||
|
|
||||||
controller.z = movie_cdata.z;
|
controller.z = movieCData.z;
|
||||||
controller.b = movie_cdata.b;
|
controller.b = movieCData.b;
|
||||||
controller.a = movie_cdata.a;
|
controller.a = movieCData.a;
|
||||||
controller.start = movie_cdata.start;
|
controller.start = movieCData.start;
|
||||||
|
|
||||||
controller.joy_x = movie_cdata.analog_x;
|
controller.joyX = movieCData.analogX;
|
||||||
controller.joy_y = movie_cdata.analog_y;
|
controller.joyY = movieCData.analogY;
|
||||||
|
|
||||||
LogController(controller);
|
LogController(controller);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,60 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <backend/core/mmio/PIF.hpp>
|
#include <common.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
void LoadTAS(const char* filename);
|
namespace fs = std::filesystem;
|
||||||
n64::Controller TasNextInputs();
|
|
||||||
bool TasMovieLoaded();
|
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<u8> loadedTasMovie = {};
|
||||||
|
TASMovieHeader loadedTasMovieHeader = {};
|
||||||
|
uint32_t loadedTasMovieIndex = 0;
|
||||||
|
};
|
||||||
@@ -254,7 +254,7 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) {
|
|||||||
bool old_exl = regs.cop0.status.exl;
|
bool old_exl = regs.cop0.status.exl;
|
||||||
|
|
||||||
if(!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;
|
pc -= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ public:
|
|||||||
SettingsWindow* settings;
|
SettingsWindow* settings;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
|
|
||||||
|
bool LoadTAS(const fs::path& path) {
|
||||||
|
return core->LoadTAS(path);
|
||||||
|
}
|
||||||
|
|
||||||
void TogglePause()
|
void TogglePause()
|
||||||
{
|
{
|
||||||
running = !running;
|
running = !running;
|
||||||
|
|||||||
@@ -21,20 +21,20 @@ static inline nlohmann::json JSONOpenOrCreate(const std::string& path) {
|
|||||||
json["audio"]["lock"] = true;
|
json["audio"]["lock"] = true;
|
||||||
json["cpu"]["type"] = "interpreter";
|
json["cpu"]["type"] = "interpreter";
|
||||||
json["input"] = {
|
json["input"] = {
|
||||||
{"A", ""},
|
{"A", "X"},
|
||||||
{"B", ""},
|
{"B", "C"},
|
||||||
{"Z", ""},
|
{"Z", "Z"},
|
||||||
{"Start", ""},
|
{"Start", "Enter"},
|
||||||
{"L", ""},
|
{"L", "A"},
|
||||||
{"R", ""},
|
{"R", "S"},
|
||||||
{"Dpad Up", ""},
|
{"Dpad Up", ""},
|
||||||
{"Dpad Down", ""},
|
{"Dpad Down", ""},
|
||||||
{"Dpad Left", ""},
|
{"Dpad Left", ""},
|
||||||
{"Dpad Right", ""},
|
{"Dpad Right", ""},
|
||||||
{"C Up", ""},
|
{"C Up", "I"},
|
||||||
{"C Down", ""},
|
{"C Down", "K"},
|
||||||
{"C Left", ""},
|
{"C Left", "J"},
|
||||||
{"C Right", ""},
|
{"C Right", "L"},
|
||||||
{"Analog Up", ""},
|
{"Analog Up", ""},
|
||||||
{"Analog Down", ""},
|
{"Analog Down", ""},
|
||||||
{"Analog Left", ""},
|
{"Analog Left", ""},
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDropEvent>
|
#include <QDropEvent>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
KaizenQt::KaizenQt() noexcept : QWidget(nullptr) {
|
KaizenQt::KaizenQt() noexcept : QWidget(nullptr) {
|
||||||
mainWindow = new MainWindowController();
|
mainWindow = new MainWindowController();
|
||||||
@@ -52,9 +55,13 @@ void KaizenQt::dropEvent(QDropEvent* event) {
|
|||||||
LoadROM(path);
|
LoadROM(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenQt::LoadROM(const QString& file_name) noexcept {
|
void KaizenQt::LoadROM(const QString& fileName) noexcept {
|
||||||
emuThread->start();
|
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) {
|
void KaizenQt::keyPressEvent(QKeyEvent *e) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class KaizenQt : public QWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
KaizenQt() noexcept;
|
KaizenQt() noexcept;
|
||||||
|
void LoadTAS(const QString& path) noexcept;
|
||||||
void LoadROM(const QString& path) noexcept;
|
void LoadROM(const QString& path) noexcept;
|
||||||
void dropEvent(QDropEvent*) override;
|
void dropEvent(QDropEvent*) override;
|
||||||
void dragEnterEvent(QDragEnterEvent*) override;
|
void dragEnterEvent(QDragEnterEvent*) override;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#include <KaizenQt.hpp>
|
#include <KaizenQt.hpp>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
#include <QCommandLineOption>
|
|
||||||
#include <MupenMovie.hpp>
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
@@ -20,7 +18,7 @@ int main(int argc, char** argv) {
|
|||||||
if (parser.positionalArguments().size() > 0) {
|
if (parser.positionalArguments().size() > 0) {
|
||||||
kaizenQt.LoadROM(parser.positionalArguments().first());
|
kaizenQt.LoadROM(parser.positionalArguments().first());
|
||||||
if (parser.positionalArguments().size() > 1) {
|
if (parser.positionalArguments().size() > 1) {
|
||||||
LoadTAS(parser.positionalArguments()[1].toStdString().c_str());
|
kaizenQt.LoadTAS(parser.positionalArguments()[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user