mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Debugger: Add replay execute/flush APIs.
This commit is contained in:
parent
85c64d3ec3
commit
ea59fa3562
3 changed files with 79 additions and 7 deletions
|
@ -16,6 +16,7 @@
|
|||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <cstdint>
|
||||
#include "Common/Data/Encoding/Base64.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Core/HLE/sceRtc.h"
|
||||
#include "Core/Replay.h"
|
||||
|
@ -54,28 +55,69 @@ void WebSocketReplayBegin(DebuggerRequest &req) {
|
|||
//
|
||||
// No parameters.
|
||||
//
|
||||
// Empty response.
|
||||
// Response (same event name) with no extra data.
|
||||
void WebSocketReplayAbort(DebuggerRequest &req) {
|
||||
ReplayAbort();
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
// Flush current recording data (replay.flush)
|
||||
//
|
||||
// Flushes event data and returns it. Note when combining, you must decode first.
|
||||
//
|
||||
// No parameters.
|
||||
//
|
||||
// Response (same event name):
|
||||
// - version: unsigned integer, version number of data.
|
||||
// - base64: base64 encode of binary data.
|
||||
void WebSocketReplayFlush(DebuggerRequest &req) {
|
||||
if (!PSP_IsInited())
|
||||
return req.Fail("Game not running");
|
||||
|
||||
// TODO
|
||||
std::vector<uint8_t> data;
|
||||
ReplayFlushBlob(&data);
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
json.writeInt("version", ReplayVersion());
|
||||
json.writeString("base64", Base64Encode(data.data(), data.size()));
|
||||
}
|
||||
|
||||
// Begin executing a replay (replay.execute)
|
||||
//
|
||||
// Parameters:
|
||||
// - version: unsigned integer, same version from replay.flush.
|
||||
// - base64: base64 encoded replay data.
|
||||
//
|
||||
// Response (same event name) with no extra data.
|
||||
void WebSocketReplayExecute(DebuggerRequest &req) {
|
||||
if (!PSP_IsInited())
|
||||
return req.Fail("Game not running");
|
||||
|
||||
// TODO
|
||||
uint32_t version = -1;
|
||||
if (!req.ParamU32("version", &version))
|
||||
return;
|
||||
std::string encoded;
|
||||
if (!req.ParamString("base64", &encoded))
|
||||
return;
|
||||
|
||||
std::vector<uint8_t> data = Base64Decode(encoded.data(), encoded.size());
|
||||
if (!ReplayExecuteBlob(version, data))
|
||||
return req.Fail("Invalid replay data or version");
|
||||
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
// Get replay status (replay.status)
|
||||
//
|
||||
// No parameters.
|
||||
//
|
||||
// Response (same event name):
|
||||
// - executing: boolean if a replay is being executed.
|
||||
// - saving: boolean if a replay is being recorded.
|
||||
void WebSocketReplayStatus(DebuggerRequest &req) {
|
||||
// TODO
|
||||
JsonWriter &json = req.Respond();
|
||||
json.writeBool("executing", ReplayIsExecuting());
|
||||
json.writeBool("saving", ReplayIsSaving());
|
||||
}
|
||||
|
||||
// Get the base RTC (real time clock) time for replay data (replay.time.get)
|
||||
|
|
|
@ -136,7 +136,16 @@ static uint8_t lastAnalog[2][2]{};
|
|||
static size_t replayDiskPos = 0;
|
||||
static bool diskFailed = false;
|
||||
|
||||
void ReplayExecuteBlob(const std::vector<uint8_t> &data) {
|
||||
bool ReplayExecuteBlob(int version, const std::vector<uint8_t> &data) {
|
||||
if (version < REPLAY_VERSION_MIN || version > REPLAY_VERSION_CURRENT) {
|
||||
ERROR_LOG(SYSTEM, "Bad replay data version: %d", version);
|
||||
return false;
|
||||
}
|
||||
if (data.size() == 0) {
|
||||
ERROR_LOG(SYSTEM, "Empty replay data");
|
||||
return false;
|
||||
}
|
||||
|
||||
ReplayAbort();
|
||||
|
||||
// Rough estimate.
|
||||
|
@ -167,6 +176,7 @@ void ReplayExecuteBlob(const std::vector<uint8_t> &data) {
|
|||
|
||||
replayState = ReplayState::EXECUTE;
|
||||
INFO_LOG(SYSTEM, "Executing replay with %lld items", (long long)replayItems.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReplayExecuteFile(const Path &filename) {
|
||||
|
@ -178,6 +188,7 @@ bool ReplayExecuteFile(const Path &filename) {
|
|||
return false;
|
||||
}
|
||||
|
||||
int version = -1;
|
||||
std::vector<uint8_t> data;
|
||||
auto loadData = [&]() {
|
||||
// TODO: Maybe stream instead.
|
||||
|
@ -207,6 +218,7 @@ bool ReplayExecuteFile(const Path &filename) {
|
|||
}
|
||||
|
||||
RtcSetBaseTime((int32_t)fh.rtcBaseSeconds, 0);
|
||||
version = fh.version;
|
||||
|
||||
data.resize(sz);
|
||||
|
||||
|
@ -220,7 +232,7 @@ bool ReplayExecuteFile(const Path &filename) {
|
|||
|
||||
if (loadData()) {
|
||||
fclose(fp);
|
||||
ReplayExecuteBlob(data);
|
||||
ReplayExecuteBlob(version, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -306,6 +318,10 @@ bool ReplayFlushFile(const Path &filename) {
|
|||
return success;
|
||||
}
|
||||
|
||||
int ReplayVersion() {
|
||||
return REPLAY_VERSION_CURRENT;
|
||||
}
|
||||
|
||||
void ReplayAbort() {
|
||||
replayItems.clear();
|
||||
replayExecPos = 0;
|
||||
|
@ -321,6 +337,14 @@ void ReplayAbort() {
|
|||
diskFailed = false;
|
||||
}
|
||||
|
||||
bool ReplayIsExecuting() {
|
||||
return replayState == ReplayState::EXECUTE;
|
||||
}
|
||||
|
||||
bool ReplayIsSaving() {
|
||||
return replayState == ReplayState::SAVE;
|
||||
}
|
||||
|
||||
static void ReplaySaveCtrl(uint32_t &buttons, uint8_t analog[2][2], uint64_t t) {
|
||||
if (lastButtons != buttons) {
|
||||
replayItems.push_back(ReplayItemHeader(ReplayAction::BUTTONS, t, buttons));
|
||||
|
|
|
@ -49,7 +49,7 @@ enum class ReplayAction : uint8_t {
|
|||
struct PSPFileInfo;
|
||||
|
||||
// Replay from data in memory. Does not manipulate base time / RNG state.
|
||||
void ReplayExecuteBlob(const std::vector<uint8_t> &data);
|
||||
bool ReplayExecuteBlob(int version, const std::vector<uint8_t> &data);
|
||||
// Replay from data in a file. Returns false if invalid.
|
||||
bool ReplayExecuteFile(const Path &filename);
|
||||
// Returns whether there are unexecuted events to replay.
|
||||
|
@ -63,10 +63,16 @@ void ReplayFlushBlob(std::vector<uint8_t> *data);
|
|||
// Flush buffered events to file. Continues recording (next call will receive new events only.)
|
||||
// Do not call with a different filename before ReplayAbort().
|
||||
bool ReplayFlushFile(const Path &filename);
|
||||
// Get current replay data version.
|
||||
int ReplayVersion();
|
||||
|
||||
// Abort any execute or record operation in progress.
|
||||
void ReplayAbort();
|
||||
|
||||
// Check if replay data is being executed or saved.
|
||||
bool ReplayIsExecuting();
|
||||
bool ReplayIsSaving();
|
||||
|
||||
void ReplayApplyCtrl(uint32_t &buttons, uint8_t analog[2][2], uint64_t t);
|
||||
uint32_t ReplayApplyDisk(ReplayAction action, uint32_t result, uint64_t t);
|
||||
uint64_t ReplayApplyDisk64(ReplayAction action, uint64_t result, uint64_t t);
|
||||
|
|
Loading…
Add table
Reference in a new issue