Skip to content

Commit 6e8f2dd

Browse files
committed
begin adding frame
1 parent 774c6b4 commit 6e8f2dd

7 files changed

Lines changed: 104 additions & 26 deletions

File tree

src/Replay.cpp

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,19 @@
33
#include "utils.hpp"
44

55
void Replay::remove_actions_after(float x) {
6-
auto check = [&](const Action& action) -> bool {
6+
const auto check = [&](const Action& action) -> bool {
77
return action.x >= x;
88
};
99
actions.erase(std::remove_if(actions.begin(), actions.end(), check), actions.end());
1010
}
1111

12+
void Replay::remove_actions_after(unsigned frame) {
13+
const auto check = [&](const Action& action) -> bool {
14+
return action.frame >= frame;
15+
};
16+
actions.erase(std::remove_if(actions.begin(), actions.end(), check), actions.end());
17+
}
18+
1219
// these two functions are so stupid
1320
// why cant c++ do it for me :(
1421

@@ -24,17 +31,33 @@ inline T bin_read(std::ifstream& stream) {
2431
return value;
2532
}
2633

27-
constexpr uint8_t format_ver = 1;
28-
constexpr const char format_magic[5] = "RPLY";
34+
/*
35+
* Format changelog
36+
*
37+
* - version 1
38+
* header | action[]
39+
* header: "RPLY" | version as u8 | fps as f32
40+
* action: x as f32 | state as u8
41+
*
42+
* - version 2
43+
* add `type as u8` between version and fps
44+
* change action x to be either float or an unsigned int
45+
*/
46+
47+
constexpr uint8_t format_ver = 2;
48+
constexpr const char format_magic[4] = {'R', 'P', 'L', 'Y'};
2949

3050
void Replay::save(const std::string& path) {
3151
std::ofstream file;
3252
file.open(path, std::ios::binary | std::ios::out);
33-
file << format_magic << format_ver;
53+
file << format_magic << format_ver << type;
3454
bin_write(file, fps);
3555
for (const auto& action : actions) {
3656
uint8_t state = action.hold | action.player2 << 1;
37-
bin_write(file, action.x);
57+
if (type == ReplayType::XPOS)
58+
bin_write(file, action.x);
59+
else if (type == ReplayType::FRAME)
60+
bin_write(file, action.frame);
3861
file << state;
3962
}
4063
file.close();
@@ -53,19 +76,25 @@ Replay Replay::load(const std::string& path) {
5376
file.read(magic, 4);
5477
if (memcmp(magic, format_magic, 4) == 0) {
5578
auto ver = bin_read<uint8_t>(file);
56-
if (ver == 1) {
79+
if (ver == 1 || ver == 2) {
80+
if (ver == 2) replay.type = ReplayType(bin_read<uint8_t>(file));
5781
replay.fps = bin_read<float>(file);
5882
size_t left = file_size - static_cast<size_t>(file.tellg());
83+
float x;
84+
unsigned frame;
5985
for (size_t _ = 0; _ < left / 5; ++_) {
60-
float x = bin_read<float>(file);
86+
if (replay.type == ReplayType::XPOS)
87+
x = bin_read<float>(file);
88+
else if (replay.type == ReplayType::FRAME)
89+
frame = bin_read<unsigned>(file);
6190
auto state = bin_read<uint8_t>(file);
6291
bool hold = state & 1;
6392
bool player2 = state & 2;
64-
replay.add_action({ x, hold, player2 });
93+
replay.add_action({ replay.type == ReplayType::XPOS ? x : frame, hold, player2 });
6594
}
6695
}
6796
} else {
68-
replay.fps = *reinterpret_cast<float*>(&magic);
97+
replay.fps = *cast<float*>(&magic);
6998
size_t left = file_size - static_cast<size_t>(file.tellg());
7099
for (size_t _ = 0; _ < left / 6; ++_) {
71100
float x = bin_read<float>(file);

src/dllmain.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
#include "hooks.hpp"
55

6-
#define _SHOW_CONSOLE_UWU 1
6+
#define _DEBUG
77

88
DWORD WINAPI thread_entry(void* module) {
9-
#if _SHOW_CONSOLE_UWU
9+
#ifdef _DEBUG
1010
AllocConsole();
1111
static std::ofstream conout("CONOUT$", std::ios::out);
1212
static std::ifstream conin("CONIN$", std::ios::in);
@@ -20,9 +20,8 @@ DWORD WINAPI thread_entry(void* module) {
2020

2121
MH_EnableHook(MH_ALL_HOOKS);
2222

23-
#if _SHOW_CONSOLE_UWU
24-
std::string dababy;
25-
std::getline(std::cin, dababy);
23+
#ifdef _DEBUG
24+
std::getline(std::cin, std::string());
2625
MH_Uninitialize();
2726
conout.close();
2827
conin.close();
@@ -35,7 +34,7 @@ DWORD WINAPI thread_entry(void* module) {
3534

3635
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
3736
if (reason == DLL_PROCESS_ATTACH) {
38-
auto handle = CreateThread(0, 0x100, thread_entry, module, 0, 0);
37+
auto handle = CreateThread(0, 0, thread_entry, module, 0, 0);
3938
if (handle) CloseHandle(handle);
4039
}
4140
return TRUE;

src/hooks.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void __fastcall Hooks::PlayLayer::pauseGame_H(gd::PlayLayer* self, int, bool idk
126126

127127
int __fastcall Hooks::PlayLayer::createCheckpoint_H(gd::PlayLayer* self, int) {
128128
auto& rs = ReplaySystem::get_instance();
129-
if (rs.is_recording()) rs.get_practice_fixes().add_checkpoint();
129+
if (rs.is_recording()) rs.get_practice_fixes().add_checkpoint(rs.get_frame());
130130
return createCheckpoint(self);
131131
}
132132

src/practice_fixes.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct Checkpoint {
1919
CheckpointData player1;
2020
CheckpointData player2;
2121
size_t activated_objects_size;
22+
unsigned frame;
2223
};
2324

2425
class PracticeFixes {
@@ -28,12 +29,13 @@ class PracticeFixes {
2829
public:
2930
PracticeFixes() {}
3031

31-
void add_checkpoint() {
32+
void add_checkpoint(unsigned frame = 0) {
3233
auto play_layer = gd::GameManager::sharedState()->getPlayLayer();
3334
checkpoints.push({
3435
CheckpointData::from_player(play_layer->m_player1),
3536
CheckpointData::from_player(play_layer->m_player2),
36-
activated_objects.size()
37+
activated_objects.size(),
38+
frame
3739
});
3840
}
3941

src/replay.hpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,36 @@
33
#include <string>
44
#include <iostream>
55

6+
enum ReplayType : std::uint8_t {
7+
XPOS,
8+
FRAME
9+
};
10+
611
struct Action {
7-
float x;
12+
union {
13+
float x;
14+
unsigned frame;
15+
};
816
bool hold;
917
bool player2;
1018
};
1119

1220
class Replay {
21+
protected:
1322
std::vector<Action> actions;
1423
float fps;
24+
ReplayType type;
1525
public:
16-
Replay(float fps) : fps(fps) {}
26+
Replay(float fps, ReplayType type = ReplayType::FRAME) : fps(fps), type(type) {}
1727

1828
float get_fps() { return fps; }
1929

2030
void add_action(const Action& action) { actions.push_back(action); }
2131
auto& get_actions() { return actions; }
2232
void remove_actions_after(float x);
33+
void remove_actions_after(unsigned frame);
34+
35+
auto get_type() { return type; }
2336

2437
void save(const std::string& path);
2538
static Replay load(const std::string& path);

src/replay_system.cpp

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ void ReplaySystem::record_action(bool hold, bool player1, bool flip) {
77
auto play_layer = gm->getPlayLayer();
88
auto is_two_player = play_layer->m_levelSettings->m_twoPlayerMode;
99
player1 ^= flip && gm->getGameVariable("0010");
10-
get_replay().add_action({ play_layer->m_player1->position.x, hold, is_two_player && !player1 });
10+
Action action;
11+
action.hold = hold;
12+
action.player2 = is_two_player && !player1;
13+
if (replay.get_type() == ReplayType::XPOS)
14+
action.x = play_layer->m_player1->position.x;
15+
else if (replay.get_type() == ReplayType::FRAME)
16+
action.frame = get_frame();
17+
replay.add_action(action);
1118
}
1219
}
1320

@@ -18,18 +25,27 @@ void ReplaySystem::play_action(const Action& action) {
1825
func(gm->getPlayLayer(), 0, !action.player2 ^ flip);
1926
}
2027

28+
unsigned ReplaySystem::get_frame() {
29+
auto play_layer = gd::GameManager::sharedState()->getPlayLayer();
30+
if (play_layer != nullptr) {
31+
return static_cast<unsigned>(play_layer->time * replay.get_fps()) + frame_offset;
32+
}
33+
return 0;
34+
}
35+
2136
void ReplaySystem::on_reset() {
2237
auto play_layer = gd::GameManager::sharedState()->getPlayLayer();
2338
if (is_playing()) {
2439
Hooks::PlayLayer::releaseButton(play_layer, 0, false);
2540
Hooks::PlayLayer::releaseButton(play_layer, 0, true);
2641
action_index = 0;
2742
} else if (is_recording()) {
28-
replay.remove_actions_after(play_layer->m_player1->position.x);
2943
auto& activated_objects = practice_fixes.activated_objects;
3044
if (practice_fixes.checkpoints.empty()) {
3145
activated_objects.clear();
46+
frame_offset = 0;
3247
} else {
48+
frame_offset = practice_fixes.checkpoints.top().frame;
3349
activated_objects.erase(
3450
activated_objects.begin() + practice_fixes.checkpoints.top().activated_objects_size,
3551
activated_objects.end()
@@ -38,6 +54,10 @@ void ReplaySystem::on_reset() {
3854
object->m_hasBeenActivated = true;
3955
}
4056
}
57+
if (replay.get_type() == ReplayType::XPOS)
58+
replay.remove_actions_after(play_layer->m_player1->position.x);
59+
else
60+
replay.remove_actions_after(get_frame());
4161
const auto& actions = replay.get_actions();
4262
bool holding = play_layer->m_player1->isActuallyHolding;
4363
if ((holding && actions.empty()) || (!actions.empty() && actions.back().hold != holding)) {
@@ -62,9 +82,16 @@ void ReplaySystem::handle_playing() {
6282
auto x = gd::GameManager::sharedState()->getPlayLayer()->m_player1->position.x;
6383
auto& actions = replay.get_actions();
6484
Action action;
65-
while (action_index < actions.size() && x >= (action = actions[action_index]).x) {
66-
play_action(action);
67-
++action_index;
85+
if (replay.get_type() == ReplayType::XPOS) {
86+
while (action_index < actions.size() && x >= (action = actions[action_index]).x) {
87+
play_action(action);
88+
++action_index;
89+
}
90+
} else {
91+
while (action_index < actions.size() && get_frame() >= (action = actions[action_index]).frame) {
92+
play_action(action);
93+
++action_index;
94+
}
6895
}
6996
if (action_index >= actions.size())
7097
toggle_playing();

src/replay_system.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@ class ReplaySystem {
1414

1515
Replay replay;
1616
RSState state = NOTHING;
17+
ReplayType replay_type;
1718

1819
size_t action_index = 0;
1920

2021
PracticeFixes practice_fixes;
2122

2223
bool frame_advance = false;
2324

24-
ReplaySystem() : default_fps(120.f), replay(default_fps) {}
25+
ReplaySystem() : default_fps(120.f), replay(default_fps), replay_type(replay.get_type()) {}
2526

2627
void _update_status_label();
28+
29+
unsigned frame_offset = 0;
2730
public:
2831
static auto& get_instance() {
2932
static ReplaySystem instance;
@@ -39,18 +42,21 @@ class ReplaySystem {
3942

4043
void toggle_playing() {
4144
state = is_playing() ? NOTHING : PLAYING;
45+
frame_offset = 0;
4246
_update_status_label();
4347
}
4448
void toggle_recording() {
4549
state = is_recording() ? NOTHING : RECORDING;
4650
if (!is_recording()) frame_advance = false;
4751
else replay = Replay(default_fps);
52+
frame_offset = 0;
4853
_update_status_label();
4954
}
5055

5156
void reset_state() {
5257
state = NOTHING;
5358
frame_advance = false;
59+
frame_offset = 0;
5460
_update_status_label();
5561
}
5662

@@ -65,4 +71,6 @@ class ReplaySystem {
6571

6672
inline bool get_frame_advance() { return frame_advance; }
6773
inline void set_frame_advance(bool b) { frame_advance = b; }
74+
75+
unsigned get_frame();
6876
};

0 commit comments

Comments
 (0)