diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp index f8e3885b84..32be6e6532 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp +++ b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp @@ -148,8 +148,17 @@ CSIDevice_GCController::GetData(u32& _Hi, u32& _Low) } #endif - if(Frame::IsAutoFiring()) - Frame::ModifyController(&PadStatus); + if(Frame::IsPlayingInput()) + Frame::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber); + else + { + // Only pad 0 is allowed to autofire right now + if(Frame::IsAutoFiring() && ISIDevice::m_iDeviceNumber == 0) + Frame::ModifyController(&PadStatus, 0); + + if(Frame::IsRecordingInput()) + Frame::RecordInput(&PadStatus, ISIDevice::m_iDeviceNumber); + } // Thankfully changing mode does not change the high bits ;) _Hi = (u32)((u8)PadStatus.stickY); diff --git a/Source/Core/Core/Src/OnFrame.cpp b/Source/Core/Core/Src/OnFrame.cpp index b3515bf5ce..56928a38d9 100644 --- a/Source/Core/Core/Src/OnFrame.cpp +++ b/Source/Core/Core/Src/OnFrame.cpp @@ -20,6 +20,7 @@ #include "Core.h" #include "PluginManager.h" #include "Thread.h" +#include "FileUtil.h" Common::CriticalSection cs_frameSkip; @@ -29,11 +30,19 @@ bool g_bFrameStep = false; bool g_bAutoFire = false; u32 g_autoFirstKey = 0, g_autoSecondKey = 0; bool g_bFirstKey = true; +PlayMode g_playMode = MODE_NONE; int g_framesToSkip = 0, g_frameSkipCounter = 0; +int g_numPads = 0; +ControllerState *g_padStates; +FILE *g_recordfd = NULL; + +u64 g_frameCounter = 0; + void FrameUpdate() { + g_frameCounter++; if (g_bFrameStep) Core::SetState(Core::CORE_PAUSE); @@ -43,6 +52,14 @@ void FrameUpdate() { if (g_bAutoFire) g_bFirstKey = !g_bFirstKey; + if(IsRecordingInput()) { + + // Dump all controllers' states for this frame + fwrite(g_padStates, sizeof(ControllerState), g_numPads, g_recordfd); + + } else if(IsPlayingInput()) { + // TODO + } } void SetFrameSkipping(unsigned int framesToSkip) { @@ -87,8 +104,11 @@ void SetFrameStepping(bool bEnabled) { g_bFrameStep = bEnabled; } -void ModifyController(SPADStatus *PadStatus) +void ModifyController(SPADStatus *PadStatus, int controllerID) { + if(controllerID < 0) + return; + u32 keyToPress = (g_bFirstKey) ? g_autoFirstKey : g_autoSecondKey; if (!keyToPress) @@ -130,4 +150,117 @@ void FrameSkipping() cs_frameSkip.Leave(); } +bool IsRecordingInput() +{ + return (g_playMode == MODE_RECORDING); +} + +bool IsPlayingInput() +{ + return (g_playMode == MODE_PLAYING); +} + +// TODO: Add BeginRecordingFromSavestate +void BeginRecordingInput(const char *filename, int controllers) +{ + if(!filename || g_playMode != MODE_NONE || g_recordfd) + return; + + if(File::Exists(filename)) + File::Delete(filename); + + g_recordfd = fopen(filename, "wb+"); + if(!g_recordfd) { + PanicAlert("Error opening file %s for recording", filename); + return; + } + + // Write initial empty header + DTMHeader dummy; + fwrite(&dummy, sizeof(DTMHeader), 1, g_recordfd); + + g_numPads = controllers; + g_padStates = new ControllerState[controllers]; + + g_frameCounter = 0; + + g_playMode = MODE_RECORDING; +} + +void EndRecordingInput() +{ + rewind(g_recordfd); + + // Create the real header now and write it + DTMHeader header; + memset(&header, 0, sizeof(DTMHeader)); + + header.bWii = Core::g_CoreStartupParameter.bWii; + strncpy((char *)header.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str(), 6); + header.numControllers = g_numPads; + + header.bFromSaveState = false; // TODO: add the case where it's true + header.frameCount = g_frameCounter; + + // TODO + header.lagCount = 0; + header.uniqueID = 0; + header.numRerecords = 0; + header.author; + header.videoPlugin; + header.audioPlugin; + header.padPlugin; + + + fwrite(&header, sizeof(DTMHeader), 1, g_recordfd); + + fclose(g_recordfd); + g_recordfd = NULL; + + delete[] g_padStates; + + g_playMode = MODE_NONE; +} + +void RecordInput(SPADStatus *PadStatus, int controllerID) +{ + if(!IsRecordingInput() || controllerID >= g_numPads || controllerID < 0) + return; + + g_padStates[controllerID].A = ((PadStatus->button & PAD_BUTTON_A) != 0); + g_padStates[controllerID].B = ((PadStatus->button & PAD_BUTTON_B) != 0); + g_padStates[controllerID].X = ((PadStatus->button & PAD_BUTTON_X) != 0); + g_padStates[controllerID].Y = ((PadStatus->button & PAD_BUTTON_Y) != 0); + g_padStates[controllerID].Z = ((PadStatus->button & PAD_TRIGGER_Z) != 0); + g_padStates[controllerID].Start = ((PadStatus->button & PAD_BUTTON_START) != 0); + + g_padStates[controllerID].DPadUp = ((PadStatus->button & PAD_BUTTON_UP) != 0); + g_padStates[controllerID].DPadDown = ((PadStatus->button & PAD_BUTTON_DOWN) != 0); + g_padStates[controllerID].DPadLeft = ((PadStatus->button & PAD_BUTTON_LEFT) != 0); + g_padStates[controllerID].DPadRight = ((PadStatus->button & PAD_BUTTON_RIGHT) != 0); + + g_padStates[controllerID].L = PadStatus->triggerLeft; + g_padStates[controllerID].R = PadStatus->triggerRight; + + g_padStates[controllerID].AnalogStickX = PadStatus->stickX; + g_padStates[controllerID].AnalogStickY = PadStatus->stickY; + + g_padStates[controllerID].CStickX = PadStatus->substickX; + g_padStates[controllerID].CStickY = PadStatus->substickY; +} + +void PlayInput(const char *filename) +{ + // TODO: Implement + // TODO: Add the play from savestate case +} + +void PlayController(SPADStatus *PadStatus, int controllerID) +{ + // TODO: Implement + if(!IsPlayingInput() || controllerID >= g_numPads || controllerID < 0) + return; + +} + }; diff --git a/Source/Core/Core/Src/OnFrame.h b/Source/Core/Core/Src/OnFrame.h index 070ba27a0c..2c5af7db14 100644 --- a/Source/Core/Core/Src/OnFrame.h +++ b/Source/Core/Core/Src/OnFrame.h @@ -25,21 +25,72 @@ namespace Frame { + +enum PlayMode { + MODE_NONE = 0, + MODE_RECORDING, + MODE_PLAYING +}; + +// Gamecube Controller State +typedef struct { + bool Start, A, B, X, Y, Z; // Binary buttons, 6 bits + bool DPadUp, DPadDown, DPadLeft, DPadRight; // Binary D-Pad buttons, 4 bits + u8 L, R; // Triggers, 16 bits + u8 AnalogStickX, AnalogStickY; // Main Stick, 16 bits + u8 CStickX, CStickY; // Sub-Stick, 16 bits + + bool reserved1, reserved2; // Reserved bits, 2 bits +} ControllerState; // Total: 58 + 2 = 60 bits per frame + +typedef struct { + bool bWii; // Wii game + u8 gameID[6]; // The Game ID + + u8 numControllers; // The number of connected controllers (1-4) + + bool bFromSaveState; // false indicates that the recording started from bootup, true for savestate + u64 frameCount; // Number of frames in the recording + u64 lagCount; // Number of lag frames in the recording + u64 uniqueID; // A Unique ID comprised of: md5(time + Game ID) + u32 numRerecords; // Number of rerecords/'cuts' of this TAS + u8 author[32]; // Author's name (encoded in UTF-8) + + u8 videoPlugin[16]; // UTF-8 representation of the video plugin + u8 audioPlugin[16]; // UTF-8 representation of the audio plugin + u8 padPlugin[16]; // UTF-8 representation of the input plugin + + + bool padding[102]; // Padding to align the header to 1024 bits + + u8 reserved[128]; // Increasing size from 128 bytes to 256 bytes, just because we can +} DTMHeader; + + void FrameUpdate(); bool IsAutoFiring(); +bool IsRecordingInput(); +bool IsPlayingInput(); void SetAutoHold(bool bEnabled, u32 keyToHold = 0); void SetAutoFire(bool bEnabled, u32 keyOne = 0, u32 keyTwo = 0); void SetFrameStepping(bool bEnabled); -void ModifyController(SPADStatus *PadStatus); - void SetFrameSkipping(unsigned int framesToSkip); int FrameSkippingFactor(); void FrameSkipping(); +void ModifyController(SPADStatus *PadStatus, int controllerID); + +void BeginRecordingInput(const char *filename); +void RecordInput(SPADStatus *PadStatus, int controllerID); +void EndRecordingInput(); + +void PlayInput(const char *filename); +void PlayController(SPADStatus *PadStatus, int controllerID); + }; #endif // __FRAME_H