Skip to content

Commit 2d58949

Browse files
committed
fix crashes and remove the shit
1 parent 5cdc77b commit 2d58949

2,008 files changed

Lines changed: 2510 additions & 402462 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,3 @@ build/
5555

5656
Thumbs.db
5757
.DS_Store
58-
59-
.claude/

CMakeLists.txt

Lines changed: 261 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ set(CMAKE_CXX_VISIBILITY_PRESET hidden)
66

77
project(ToastyReplay VERSION 1.3.1 LANGUAGES CXX)
88

9-
file(GLOB SOURCES
9+
set(TOASTYREPLAY_SOURCES
1010
src/ttr_format.cpp
1111
src/core/checkpoint_handler.cpp
1212
src/core/replay_engine.cpp
@@ -28,17 +28,263 @@ file(GLOB SOURCES
2828
src/render/watermark.cpp
2929
src/audio/clicksounds.cpp
3030
src/audio/playsound.cpp
31+
src/i18n/localization.cpp
3132
src/online/online_client.cpp
3233
)
3334

34-
add_library(${PROJECT_NAME} SHARED ${SOURCES})
35+
function(toastyreplay_replace_once_or_verify content_var search replacement)
36+
string(FIND "${${content_var}}" "${replacement}" replacement_index)
37+
if (replacement_index GREATER -1)
38+
set(${content_var} "${${content_var}}" PARENT_SCOPE)
39+
return()
40+
endif()
41+
42+
string(FIND "${${content_var}}" "${search}" search_index)
43+
if (search_index EQUAL -1)
44+
message(FATAL_ERROR "Unable to patch gd-imgui-cocos backend. Missing expected snippet: ${search}")
45+
endif()
46+
47+
string(REPLACE "${search}" "${replacement}" updated_content "${${content_var}}")
48+
set(${content_var} "${updated_content}" PARENT_SCOPE)
49+
endfunction()
50+
51+
function(patch_gd_imgui_cocos_backend backend_file)
52+
file(READ "${backend_file}" backend_content)
53+
54+
toastyreplay_replace_once_or_verify(
55+
backend_content
56+
"#include <Geode/Geode.hpp>"
57+
"#include <Geode/Geode.hpp>\n#include <Geode/utils/string.hpp>"
58+
)
59+
toastyreplay_replace_once_or_verify(
60+
backend_content
61+
"static const auto iniPath = (Mod::get()->getSaveDir() / \"imgui.ini\").string();"
62+
"static const auto iniPath = new std::string(geode::utils::string::pathToString(Mod::get()->getSaveDir() / \"imgui.ini\"));"
63+
)
64+
toastyreplay_replace_once_or_verify(
65+
backend_content
66+
"static const auto iniPath = new std::string((Mod::get()->getSaveDir() / \"imgui.ini\").string());"
67+
"static const auto iniPath = new std::string(geode::utils::string::pathToString(Mod::get()->getSaveDir() / \"imgui.ini\"));"
68+
)
69+
toastyreplay_replace_once_or_verify(
70+
backend_content
71+
"io.IniFilename = iniPath.c_str();"
72+
"io.IniFilename = iniPath->c_str();"
73+
)
74+
toastyreplay_replace_once_or_verify(
75+
backend_content
76+
"#ifdef GEODE_IS_MOBILE\n\nclass ImGuiIMEDelegate"
77+
"#ifdef GEODE_IS_WINDOWS\nstatic std::string readClipboardText() {\n\tif (!OpenClipboard(nullptr)) return {};\n\n\tHANDLE hData = GetClipboardData(CF_UNICODETEXT);\n\tif (hData == nullptr) {\n\t\tCloseClipboard();\n\t\treturn {};\n\t}\n\n\tauto pszText = static_cast<wchar_t*>(GlobalLock(hData));\n\tif (pszText == nullptr) {\n\t\tCloseClipboard();\n\t\treturn {};\n\t}\n\n\tint size = WideCharToMultiByte(CP_UTF8, 0, pszText, -1, nullptr, 0, nullptr, nullptr);\n\tstd::string text;\n\tif (size > 0) {\n\t\ttext.resize(static_cast<size_t>(size));\n\t\tWideCharToMultiByte(CP_UTF8, 0, pszText, -1, text.data(), size, nullptr, nullptr);\n\t\tif (!text.empty() && text.back() == '\\0') {\n\t\t\ttext.pop_back();\n\t\t}\n\t}\n\n\tGlobalUnlock(hData);\n\tCloseClipboard();\n\treturn text;\n}\n#endif\n\n#ifdef GEODE_IS_MOBILE\n\nclass ImGuiIMEDelegate"
78+
)
79+
toastyreplay_replace_once_or_verify(
80+
backend_content
81+
"auto static read = geode::utils::clipboard::read();"
82+
"auto static read = new std::string();"
83+
)
84+
toastyreplay_replace_once_or_verify(
85+
backend_content
86+
"read = geode::utils::clipboard::read();"
87+
"*read = readClipboardText();"
88+
)
89+
toastyreplay_replace_once_or_verify(
90+
backend_content
91+
"return read.c_str();"
92+
"return read->c_str();"
93+
)
94+
95+
file(WRITE "${backend_file}" "${backend_content}")
96+
endfunction()
97+
98+
function(patch_gd_replay_format_header header_file)
99+
file(READ "${header_file}" replay_content)
100+
101+
string(FIND "${replay_content}" "static std::optional<Self> tryImportData(std::vector<uint8_t> const& data, bool importInputs = true)" already_patched_index)
102+
if (already_patched_index GREATER -1)
103+
return()
104+
endif()
105+
106+
toastyreplay_replace_once_or_verify(
107+
replay_content
108+
[=[ static Self importData(std::vector<uint8_t> const& data, bool importInputs = true) {
109+
Self replay;
110+
json replayJson;
111+
112+
try {
113+
replayJson = json::from_msgpack(data);
114+
} catch(std::exception& e) {
115+
replayJson = json::parse(data);
116+
}
117+
118+
replay.gameVersion = replayJson["gameVersion"];
119+
replay.description = replayJson["description"];
120+
replay.version = replayJson["version"];
121+
replay.duration = replayJson["duration"];
122+
replay.botInfo.name = replayJson["bot"]["name"];
123+
replay.botInfo.version = replayJson["bot"]["version"];
124+
replay.levelInfo.id = replayJson["level"]["id"];
125+
replay.levelInfo.name = replayJson["level"]["name"];
126+
replay.author = replayJson["author"];
127+
replay.seed = replayJson["seed"];
128+
replay.coins = replayJson["coins"];
129+
replay.ldm = replayJson["ldm"];
130+
131+
if(replayJson.contains("framerate"))
132+
replay.framerate = replayJson["framerate"];
133+
replay.parseExtension(replayJson.get<json::object_t>());
134+
135+
if(!importInputs)
136+
return replay;
137+
138+
for (json const& inputJson : replayJson["inputs"]) {
139+
InputType input;
140+
input.frame = inputJson["frame"];
141+
input.button = inputJson["btn"];
142+
input.player2 = inputJson["2p"];
143+
input.down = inputJson["down"];
144+
input.parseExtension(inputJson.get<json::object_t>());
145+
146+
replay.inputs.push_back(input);
147+
}
148+
149+
return replay;
150+
}
151+
]=]
152+
[=[ static std::optional<Self> tryImportData(std::vector<uint8_t> const& data, bool importInputs = true) {
153+
Self replay;
154+
json replayJson = json::from_msgpack(data, true, false);
155+
if (replayJson.is_discarded()) {
156+
replayJson = json::parse(data, nullptr, false);
157+
}
158+
if (!replayJson.is_object()) {
159+
return std::nullopt;
160+
}
161+
162+
auto const* root = replayJson.get_ptr<const json::object_t*>();
163+
if (!root) {
164+
return std::nullopt;
165+
}
166+
167+
auto getValue = [](json::object_t const& object, char const* key) -> json const* {
168+
auto it = object.find(key);
169+
return it == object.end() ? nullptr : &it->second;
170+
};
171+
172+
auto getString = [&](json::object_t const& object, char const* key, std::string& out) -> bool {
173+
auto const* value = getValue(object, key);
174+
if (!value) return false;
175+
auto const* stringValue = value->template get_ptr<const std::string*>();
176+
if (!stringValue) return false;
177+
out = *stringValue;
178+
return true;
179+
};
180+
181+
auto getBool = [&](json::object_t const& object, char const* key, bool& out) -> bool {
182+
auto const* value = getValue(object, key);
183+
if (!value) return false;
184+
auto const* boolValue = value->template get_ptr<const bool*>();
185+
if (!boolValue) return false;
186+
out = *boolValue;
187+
return true;
188+
};
189+
190+
auto getNumber = [&](json::object_t const& object, char const* key, auto& out) -> bool {
191+
auto const* value = getValue(object, key);
192+
if (!value) return false;
193+
194+
if (auto const* floatValue = value->template get_ptr<const json::number_float_t*>()) {
195+
out = static_cast<std::decay_t<decltype(out)>>(*floatValue);
196+
return true;
197+
}
198+
if (auto const* signedValue = value->template get_ptr<const json::number_integer_t*>()) {
199+
out = static_cast<std::decay_t<decltype(out)>>(*signedValue);
200+
return true;
201+
}
202+
if (auto const* unsignedValue = value->template get_ptr<const json::number_unsigned_t*>()) {
203+
out = static_cast<std::decay_t<decltype(out)>>(*unsignedValue);
204+
return true;
205+
}
206+
207+
return false;
208+
};
209+
210+
auto const* botJson = getValue(*root, "bot");
211+
auto const* levelJson = getValue(*root, "level");
212+
auto const* inputsJson = getValue(*root, "inputs");
213+
if (!botJson || !botJson->is_object() || !levelJson || !levelJson->is_object()) {
214+
return std::nullopt;
215+
}
216+
217+
auto const* botObject = botJson->template get_ptr<const json::object_t*>();
218+
auto const* levelObject = levelJson->template get_ptr<const json::object_t*>();
219+
if (!botObject || !levelObject) {
220+
return std::nullopt;
221+
}
222+
223+
if (!getNumber(*root, "gameVersion", replay.gameVersion)
224+
!getString(*root, "description", replay.description)
225+
!getNumber(*root, "version", replay.version)
226+
!getNumber(*root, "duration", replay.duration)
227+
!getString(*botObject, "name", replay.botInfo.name)
228+
!getString(*botObject, "version", replay.botInfo.version)
229+
!getNumber(*levelObject, "id", replay.levelInfo.id)
230+
!getString(*levelObject, "name", replay.levelInfo.name)
231+
!getString(*root, "author", replay.author)
232+
!getNumber(*root, "seed", replay.seed)
233+
!getNumber(*root, "coins", replay.coins)
234+
!getBool(*root, "ldm", replay.ldm)) {
235+
return std::nullopt;
236+
}
237+
238+
getNumber(*root, "framerate", replay.framerate);
239+
replay.parseExtension(*root);
240+
241+
if(!importInputs)
242+
return replay;
243+
244+
if (!inputsJson || !inputsJson->is_array()) {
245+
return std::nullopt;
246+
}
247+
248+
for (json const& inputJson : *inputsJson) {
249+
auto const* inputObject = inputJson.template get_ptr<const json::object_t*>();
250+
if (!inputObject) {
251+
return std::nullopt;
252+
}
253+
254+
InputType input;
255+
if (!getNumber(*inputObject, "frame", input.frame)
256+
!getNumber(*inputObject, "btn", input.button)
257+
!getBool(*inputObject, "2p", input.player2)
258+
!getBool(*inputObject, "down", input.down)) {
259+
return std::nullopt;
260+
}
261+
input.parseExtension(*inputObject);
262+
263+
replay.inputs.push_back(input);
264+
}
265+
266+
return replay;
267+
}
268+
269+
static Self importData(std::vector<uint8_t> const& data, bool importInputs = true) {
270+
auto replay = tryImportData(data, importInputs);
271+
return replay.value_or(Self{});
272+
}
273+
]=]
274+
)
275+
276+
file(WRITE "${header_file}" "${replay_content}")
277+
endfunction()
278+
279+
add_library(${PROJECT_NAME} SHARED ${TOASTYREPLAY_SOURCES})
35280

36281
target_include_directories(${PROJECT_NAME} PRIVATE
37282
src
38-
${gd-imgui-cocos_SOURCE_DIR}/include
39283
)
40284

41285
if (MSVC)
286+
target_compile_options(${PROJECT_NAME} PRIVATE /FS)
287+
42288
set_source_files_properties(
43289
src/audio/clicksounds.cpp
44290
src/audio/playsound.cpp
@@ -62,66 +308,31 @@ endif()
62308
add_subdirectory($ENV{GEODE_SDK} $ENV{GEODE_SDK}/build)
63309

64310
CPMAddPackage("gh:maxnut/GDReplayFormat#9c32b5529067901418ea6e4dfbd36437dc8712bc")
311+
patch_gd_replay_format_header("${GDReplayFormat_SOURCE_DIR}/include/gdr/gdr.hpp")
65312
set(IMGUI_VERSION "v1.91.4" CACHE STRING "Pin imgui version" FORCE)
66313
CPMAddPackage("gh:matcool/gd-imgui-cocos#97643336209b760a3d59d83b770546b1cfef4e7d")
67-
set(GD_IMGUI_COCOS_BACKEND_FILE "${gd-imgui-cocos_SOURCE_DIR}/src/backend.cpp")
68-
file(READ "${GD_IMGUI_COCOS_BACKEND_FILE}" GD_IMGUI_COCOS_BACKEND_CONTENT)
69-
string(REPLACE
70-
"static const auto iniPath = (Mod::get()->getSaveDir() / \"imgui.ini\").string();"
71-
"static const auto iniPath = new std::string((Mod::get()->getSaveDir() / \"imgui.ini\").string());"
72-
GD_IMGUI_COCOS_BACKEND_CONTENT
73-
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
74-
)
75-
string(REPLACE
76-
"io.IniFilename = iniPath.c_str();"
77-
"io.IniFilename = iniPath->c_str();"
78-
GD_IMGUI_COCOS_BACKEND_CONTENT
79-
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
80-
)
81-
if (NOT GD_IMGUI_COCOS_BACKEND_CONTENT MATCHES "readClipboardText")
82-
string(REPLACE
83-
"#ifdef GEODE_IS_MOBILE\n\nclass ImGuiIMEDelegate"
84-
"#ifdef GEODE_IS_WINDOWS\nstatic std::string readClipboardText() {\n\tif (!OpenClipboard(nullptr)) return {};\n\n\tHANDLE hData = GetClipboardData(CF_UNICODETEXT);\n\tif (hData == nullptr) {\n\t\tCloseClipboard();\n\t\treturn {};\n\t}\n\n\tauto pszText = static_cast<wchar_t*>(GlobalLock(hData));\n\tif (pszText == nullptr) {\n\t\tCloseClipboard();\n\t\treturn {};\n\t}\n\n\tint size = WideCharToMultiByte(CP_UTF8, 0, pszText, -1, nullptr, 0, nullptr, nullptr);\n\tstd::string text;\n\tif (size > 0) {\n\t\ttext.resize(static_cast<size_t>(size));\n\t\tWideCharToMultiByte(CP_UTF8, 0, pszText, -1, text.data(), size, nullptr, nullptr);\n\t\tif (!text.empty() && text.back() == '\\0') {\n\t\t\ttext.pop_back();\n\t\t}\n\t}\n\n\tGlobalUnlock(hData);\n\tCloseClipboard();\n\treturn text;\n}\n#endif\n\n#ifdef GEODE_IS_MOBILE\n\nclass ImGuiIMEDelegate"
85-
GD_IMGUI_COCOS_BACKEND_CONTENT
86-
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
87-
)
88-
endif()
89-
string(REPLACE
90-
"auto static read = geode::utils::clipboard::read();"
91-
"auto static read = new std::string();"
92-
GD_IMGUI_COCOS_BACKEND_CONTENT
93-
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
94-
)
95-
string(REPLACE
96-
"read = geode::utils::clipboard::read();"
97-
"*read = readClipboardText();"
98-
GD_IMGUI_COCOS_BACKEND_CONTENT
99-
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
100-
)
101-
string(REPLACE
102-
"return read.c_str();"
103-
"return read->c_str();"
104-
GD_IMGUI_COCOS_BACKEND_CONTENT
105-
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
106-
)
107-
file(WRITE "${GD_IMGUI_COCOS_BACKEND_FILE}" "${GD_IMGUI_COCOS_BACKEND_CONTENT}")
314+
target_include_directories(${PROJECT_NAME} PRIVATE ${gd-imgui-cocos_SOURCE_DIR}/include)
315+
patch_gd_imgui_cocos_backend("${gd-imgui-cocos_SOURCE_DIR}/src/backend.cpp")
316+
108317
CPMAddPackage(
109318
NAME ZLIB
110319
GITHUB_REPOSITORY madler/zlib
111320
VERSION 1.3.1
112321
OPTIONS "ZLIB_BUILD_EXAMPLES OFF"
113322
)
114-
if (ZLIB_ADDED)
115-
set(ZLIB_INCLUDE_DIR ${ZLIB_SOURCE_DIR} ${ZLIB_BINARY_DIR})
116-
set(ZLIB_LIBRARY zlibstatic)
117-
target_include_directories(${PROJECT_NAME} PRIVATE ${ZLIB_INCLUDE_DIR})
323+
324+
set(TOASTYREPLAY_LINK_LIBRARIES imgui-cocos libGDR)
325+
if (TARGET zlibstatic)
326+
target_include_directories(${PROJECT_NAME} PRIVATE ${ZLIB_SOURCE_DIR} ${ZLIB_BINARY_DIR})
327+
list(APPEND TOASTYREPLAY_LINK_LIBRARIES zlibstatic)
118328
endif()
329+
119330
target_compile_definitions(${PROJECT_NAME} PRIVATE MOD_VERSION="${CMAKE_PROJECT_VERSION}")
120331

121332
if (WIN32)
122-
target_link_libraries(${PROJECT_NAME} imgui-cocos libGDR zlibstatic)
333+
target_link_libraries(${PROJECT_NAME} ${TOASTYREPLAY_LINK_LIBRARIES})
123334
elseif (APPLE)
124-
target_link_libraries(${PROJECT_NAME} imgui-cocos libGDR zlibstatic "-framework IOKit")
335+
target_link_libraries(${PROJECT_NAME} ${TOASTYREPLAY_LINK_LIBRARIES} "-framework IOKit")
125336
endif()
126337

127338

bindings/.git-blame-ignore-revs

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)