Implemented frame counter and TAS GC recording. What's left: A lot of GUI, actually playing the TASes, and begin recording from savestate.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4025 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
92d4c55e14
commit
7ed4b06e48
|
@ -148,8 +148,17 @@ CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(Frame::IsAutoFiring())
|
if(Frame::IsPlayingInput())
|
||||||
Frame::ModifyController(&PadStatus);
|
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 ;)
|
// Thankfully changing mode does not change the high bits ;)
|
||||||
_Hi = (u32)((u8)PadStatus.stickY);
|
_Hi = (u32)((u8)PadStatus.stickY);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "Core.h"
|
#include "Core.h"
|
||||||
#include "PluginManager.h"
|
#include "PluginManager.h"
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
|
#include "FileUtil.h"
|
||||||
|
|
||||||
Common::CriticalSection cs_frameSkip;
|
Common::CriticalSection cs_frameSkip;
|
||||||
|
|
||||||
|
@ -29,11 +30,19 @@ bool g_bFrameStep = false;
|
||||||
bool g_bAutoFire = false;
|
bool g_bAutoFire = false;
|
||||||
u32 g_autoFirstKey = 0, g_autoSecondKey = 0;
|
u32 g_autoFirstKey = 0, g_autoSecondKey = 0;
|
||||||
bool g_bFirstKey = true;
|
bool g_bFirstKey = true;
|
||||||
|
PlayMode g_playMode = MODE_NONE;
|
||||||
|
|
||||||
int g_framesToSkip = 0, g_frameSkipCounter = 0;
|
int g_framesToSkip = 0, g_frameSkipCounter = 0;
|
||||||
|
|
||||||
|
int g_numPads = 0;
|
||||||
|
ControllerState *g_padStates;
|
||||||
|
FILE *g_recordfd = NULL;
|
||||||
|
|
||||||
|
u64 g_frameCounter = 0;
|
||||||
|
|
||||||
void FrameUpdate() {
|
void FrameUpdate() {
|
||||||
|
|
||||||
|
g_frameCounter++;
|
||||||
|
|
||||||
if (g_bFrameStep)
|
if (g_bFrameStep)
|
||||||
Core::SetState(Core::CORE_PAUSE);
|
Core::SetState(Core::CORE_PAUSE);
|
||||||
|
@ -43,6 +52,14 @@ void FrameUpdate() {
|
||||||
if (g_bAutoFire)
|
if (g_bAutoFire)
|
||||||
g_bFirstKey = !g_bFirstKey;
|
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) {
|
void SetFrameSkipping(unsigned int framesToSkip) {
|
||||||
|
@ -87,8 +104,11 @@ void SetFrameStepping(bool bEnabled) {
|
||||||
g_bFrameStep = 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;
|
u32 keyToPress = (g_bFirstKey) ? g_autoFirstKey : g_autoSecondKey;
|
||||||
|
|
||||||
if (!keyToPress)
|
if (!keyToPress)
|
||||||
|
@ -130,4 +150,117 @@ void FrameSkipping()
|
||||||
cs_frameSkip.Leave();
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,21 +25,72 @@
|
||||||
|
|
||||||
namespace Frame {
|
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();
|
void FrameUpdate();
|
||||||
|
|
||||||
bool IsAutoFiring();
|
bool IsAutoFiring();
|
||||||
|
bool IsRecordingInput();
|
||||||
|
bool IsPlayingInput();
|
||||||
|
|
||||||
void SetAutoHold(bool bEnabled, u32 keyToHold = 0);
|
void SetAutoHold(bool bEnabled, u32 keyToHold = 0);
|
||||||
void SetAutoFire(bool bEnabled, u32 keyOne = 0, u32 keyTwo = 0);
|
void SetAutoFire(bool bEnabled, u32 keyOne = 0, u32 keyTwo = 0);
|
||||||
|
|
||||||
void SetFrameStepping(bool bEnabled);
|
void SetFrameStepping(bool bEnabled);
|
||||||
|
|
||||||
void ModifyController(SPADStatus *PadStatus);
|
|
||||||
|
|
||||||
void SetFrameSkipping(unsigned int framesToSkip);
|
void SetFrameSkipping(unsigned int framesToSkip);
|
||||||
int FrameSkippingFactor();
|
int FrameSkippingFactor();
|
||||||
void FrameSkipping();
|
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
|
#endif // __FRAME_H
|
||||||
|
|
Loading…
Reference in New Issue