forward compat mode

This commit is contained in:
ConfiG 2024-01-12 22:18:24 +03:00
parent 7ef57bbf67
commit 91bff5fbb2
No known key found for this signature in database
GPG key ID: 44DA1983F524C11B
17 changed files with 263 additions and 98 deletions

View file

@ -72,6 +72,8 @@ namespace geode {
Done
};
bool isForwardCompatMode();
// TODO: return void
Result<> saveData();
Result<> loadData();

View file

@ -47,73 +47,25 @@ DWORD WINAPI xinputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRen
#pragma comment(linker, "/export:XInputGetCapabilities=_xinputGetCapabilities@12")
#pragma comment(linker, "/export:XInputGetDSoundAudioDeviceGuids=_xinputGetDSoundAudioDeviceGuids@12")
unsigned int getExeTimestamp() {
HANDLE hMod = GetModuleHandleA(NULL);
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hMod;
if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((uintptr_t)(hMod) + dosHeader->e_lfanew);
if (ntHeader->Signature == IMAGE_NT_SIGNATURE) {
return ntHeader->FileHeader.TimeDateStamp;
}
}
return 0;
}
BOOL fileExists(char const* path) {
DWORD attrib = GetFileAttributesA(path);
return (attrib != INVALID_FILE_ATTRIBUTES && !(attrib & FILE_ATTRIBUTE_DIRECTORY));
}
// Table originally from this gist by absolute:
// https://gist.github.com/absoIute/ebe5da42d118109a03632c9751d86e19
#define TIMESTAMP_FOR_1_900 1419173053
#define TIMESTAMP_FOR_1_910 1419880840
#define TIMESTAMP_FOR_1_920 1421745341
#define TIMESTAMP_FOR_2_000 1440638199
#define TIMESTAMP_FOR_2_001 1440643927
#define TIMESTAMP_FOR_2_010 1443053232
#define TIMESTAMP_FOR_2_011 1443077847
#define TIMESTAMP_FOR_2_020 1443077847
#define TIMESTAMP_FOR_2_100 1484612867
#define TIMESTAMP_FOR_2_101 1484626658
#define TIMESTAMP_FOR_2_102 1484737207
#define TIMESTAMP_FOR_2_110 1510526914
#define TIMESTAMP_FOR_2_111 1510538091
#define TIMESTAMP_FOR_2_112 1510619253
#define TIMESTAMP_FOR_2_113 1511220108
#define TIMESTAMP_FOR_2_200 1702921605
#define TIMESTAMP_FOR_2_201 1704582672
#define TIMESTAMP_FOR_2_202 1704601266
#define TIMESTAMP_FOR_2_203 1704948277
#define GEODE_WRAPPER_CONCAT(x, y) x##y
#define GEODE_CONCAT(x, y) GEODE_WRAPPER_CONCAT(x, y)
// Hello future person. if this is erroring then you need to add
// the exe timestamp for whatever you set GEODE_GD_VERSION to. hope that helps!
#define TIMESTAMP GEODE_CONCAT(TIMESTAMP_FOR, GEODE_GD_VERSION_IDENTIFIER)
BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID _) {
if (reason != DLL_PROCESS_ATTACH)
return TRUE;
DisableThreadLibraryCalls(module);
if (fileExists("Geode.dll")) {
unsigned int timestamp = getExeTimestamp();
if (timestamp == TIMESTAMP) {
// somehow, this works fine inside of dllmain :-)
// yes, even on wine.
LoadLibraryA("Geode.dll");
} else {
char buffer[128];
sprintf_s(buffer, sizeof(buffer), "GD version mismatch, not loading Geode. (%" PRIu32 ")", timestamp);
MessageBoxA(NULL, buffer, "Unable to load Geode!", MB_OK | MB_ICONWARNING);
}
// somehow, this works fine inside of dllmain :-)
// yes, even on wine.
/* * * * * * * * * * * * * * * * * *\
* The Shadows Shall Smite You 01 *
* - ConfiG *
\* * * * * * * * * * * * * * * * * */
LoadLibraryA("Geode.dll");
}
return TRUE;
}
}

View file

@ -3,7 +3,16 @@
using namespace geode::prelude;
struct MyGameToolbox : Modify<MyGameToolbox, GameToolbox> {
static void preVisitWithClippingRect(CCNode* node, CCRect rect) {
static void onModify(const auto& self) {
if (!Loader::get()->isForwardCompatMode())
return;
log::warn("FixClippingRect disabled in forward compat");
for (const auto& [_, hook] : self.m_hooks) {
hook->setAutoEnable(false);
}
}
static void preVisitWithClippingRect(CCNode* node, CCRect rect) {
if (node->isVisible()) {
glEnable(0xc11);
if (node->getParent()) {

View file

@ -1,5 +1,6 @@
#include <Geode/modify/LoadingLayer.hpp>
#include <Geode/modify/CCLayer.hpp>
#include <Geode/utils/cocos.hpp>
#include <array>
#include <fmt/format.h>
@ -13,6 +14,15 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
int m_geodeLoadStep = 0;
int m_totalMods = 0;
static void onModify(const auto& self) {
if (!Loader::get()->isForwardCompatMode())
return;
log::warn("Switching to fallback custom loading layer in forward compat");
for (const auto& [_, hook] : self.m_hooks) {
hook->setAutoEnable(false);
}
}
void updateLoadedModsLabel() {
auto allMods = Loader::get()->getAllMods();
auto count = std::count_if(allMods.begin(), allMods.end(), [&](auto& item) {
@ -158,9 +168,9 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
}
return !m_fromRefresh;
}
// hook
void loadAssets() {
void loadAssets() {
switch (m_fields->m_geodeLoadStep) {
case 0:
if (this->skipOnRefresh()) this->setupLoadingMods();
@ -180,3 +190,38 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
this->updateLoadingBar();
}
};
struct FallbackCustomLoadingLayer : Modify<FallbackCustomLoadingLayer, CCLayer> {
static void onModify(const auto& self) {
if (Loader::get()->isForwardCompatMode())
return;
for (const auto& [_, hook] : self.m_hooks) {
hook->setAutoEnable(false);
}
}
bool init() {
if (!CCLayer::init())
return false;
if (!typeinfo_cast<LoadingLayer*>(this))
return true;
auto winSize = CCDirector::sharedDirector()->getWinSize();
auto label = CCLabelBMFont::create(
"Loading Geode without UI, see console for details.",
"goldFont.fnt"
);
label->setPosition(winSize.width / 2, 30.f);
label->setScale(.45f);
label->setZOrder(99);
label->setID("geode-small-label");
this->addChild(label);
// TODO: verify loader resources on fallback?
Loader::get()->updateResources(true);
return true;
}
};

View file

@ -49,6 +49,13 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
if (!self.setHookPriority("MenuLayer::init", geode::node_ids::GEODE_ID_PRIORITY)) {
log::warn("Failed to set MenuLayer::init hook priority, node IDs may not work properly");
}
if (!Loader::get()->isForwardCompatMode())
return;
log::warn("MenuLayer stuff disabled in forward compat");
for (const auto& [_, hook] : self.m_hooks) {
hook->setAutoEnable(false);
}
}
CCSprite* m_geodeButton;
@ -63,7 +70,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
auto winSize = CCDirector::sharedDirector()->getWinSize();
// add geode button
m_fields->m_geodeButton = CircleButtonSprite::createWithSpriteFrameName(
"geode-logo-outline-gold.png"_spr,
1.0f,

View file

@ -29,9 +29,10 @@ static void __cdecl fixedErrorHandler(int code, char const* description) {
}
$execute {
(void)Mod::get()->patch(
reinterpret_cast<void*>(geode::base::getCocos() + 0x19feec), toByteArray(&fixedErrorHandler)
);
// TODO: i'm pretty sure this patch only works on 2.113?
//(void)Mod::get()->patch(
// reinterpret_cast<void*>(geode::base::getCocos() + 0x19feec), toByteArray(&fixedErrorHandler)
//);
}
#endif

View file

@ -5,6 +5,15 @@ using namespace geode::prelude;
#include <Geode/modify/AchievementNotifier.hpp>
struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
static void onModify(const auto& self) {
if (!Loader::get()->isForwardCompatMode())
return;
log::warn("persist disabled in forward compat");
for (const auto& [_, hook] : self.m_hooks) {
hook->setAutoEnable(false);
}
}
void willSwitchToScene(CCScene* scene) {
AchievementNotifier::willSwitchToScene(scene);
SceneManager::get()->willSwitchToScene(scene);

View file

@ -5,6 +5,15 @@ using namespace geode::prelude;
#include <Geode/modify/AppDelegate.hpp>
struct SaveLoader : Modify<SaveLoader, AppDelegate> {
static void onModify(const auto& self) {
if (!Loader::get()->isForwardCompatMode())
return;
log::warn("save disabled in forward compat");
for (const auto& [_, hook] : self.m_hooks) {
hook->setAutoEnable(false);
}
}
void trySaveGame(bool p0) {
log::info("Saving mod data...");
log::pushNest();

View file

@ -58,10 +58,32 @@ $execute {
});
}
void tryLogForwardCompat() {
if (!LoaderImpl::get()->isForwardCompatMode()) return;
log::warn("+-----------------------------------------------------------------------------------------------+");
log::warn("| Geode is running in a newer version of GD than Geode targets. |");
log::warn("| UI is going to be disabled, platform console is forced on and crashes can be more common. |");
log::warn("| However, if your game crashes, it is probably caused by an outdated mod and not Geode itself. |");
log::warn("+-----------------------------------------------------------------------------------------------+");
}
int geodeEntry(void* platformData) {
log::Logger::get()->setup();
log::info("Running {} {}", Mod::get()->getName(), Mod::get()->getVersion());
std::string forwardCompatSuffix;
if (LoaderImpl::get()->isForwardCompatMode())
forwardCompatSuffix = " (forward compatibility mode)";
if (LoaderImpl::get()->getGameVersion().empty()) {
log::info("Running {} {}{}", Mod::get()->getName(), Mod::get()->getVersion(),
forwardCompatSuffix);
}
else {
log::info("Running {} {} in Geometry Dash v{}{}", Mod::get()->getName(),
Mod::get()->getVersion(), LoaderImpl::get()->getGameVersion(), forwardCompatSuffix);
}
tryLogForwardCompat();
auto begin = std::chrono::high_resolution_clock::now();
@ -81,7 +103,8 @@ int geodeEntry(void* platformData) {
}
// open console
if (Mod::get()->getSettingValue<bool>("show-platform-console")) {
if (LoaderImpl::get()->isForwardCompatMode() ||
Mod::get()->getSettingValue<bool>("show-platform-console")) {
log::debug("Opening console");
Loader::get()->openPlatformConsole();
}
@ -119,5 +142,8 @@ int geodeEntry(void* platformData) {
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
log::info("Entry took {}s", static_cast<float>(time) / 1000.f);
// also log after entry so that users are more likely to notice
tryLogForwardCompat();
return 0;
}

View file

@ -11,6 +11,10 @@ Loader* Loader::get() {
return g_geode;
}
bool Loader::isForwardCompatMode() {
return m_impl->isForwardCompatMode();
}
void Loader::createDirectories() {
return m_impl->createDirectories();
}

View file

@ -37,6 +37,15 @@ Loader::Impl::~Impl() = default;
// Initialization
bool Loader::Impl::isForwardCompatMode() {
if (!m_forwardCompatModeSet) {
m_forwardCompatMode = !this->getGameVersion().empty() &&
this->getGameVersion() != GEODE_STR(GEODE_GD_VERSION);
m_forwardCompatModeSet = true;
}
return m_forwardCompatMode;
}
void Loader::Impl::createDirectories() {
#ifdef GEODE_IS_MACOS
ghc::filesystem::create_directory(dirs::getSaveDir());

View file

@ -55,6 +55,10 @@ namespace geode {
public:
mutable std::mutex m_mutex;
bool m_forwardCompatModeSet = false;
std::string m_gdVersion;
bool m_forwardCompatMode = false;
std::vector<ghc::filesystem::path> m_modSearchDirectories;
std::vector<LoadProblem> m_problems;
std::unordered_map<std::string, Mod*> m_mods;
@ -91,6 +95,9 @@ namespace geode {
std::chrono::time_point<std::chrono::high_resolution_clock> m_timerBegin;
std::string getGameVersion();
bool isForwardCompatMode();
void provideNextMod(Mod* mod);
Mod* takeNextMod();
void releaseNextMod();

View file

@ -23,6 +23,13 @@ namespace {
}
}
std::string Loader::Impl::getGameVersion() {
if (m_gdVersion.empty()) {
// TODO: detect gd version
}
return m_gdVersion;
}
void Loader::Impl::platformMessageBox(char const* title, std::string const& info) {
cocos2d::CCMessageBox(info.c_str(), title);
}

View file

@ -14,6 +14,17 @@ using namespace geode::prelude;
static constexpr auto IPC_BUFFER_SIZE = 512;
#include "gdTimestampMap.hpp"
std::string Loader::Impl::getGameVersion() {
if (m_gdVersion.empty()) {
auto dosHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(geode::base::get());
auto ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(geode::base::get() + dosHeader->e_lfanew);
auto timestamp = ntHeader->FileHeader.TimeDateStamp;
m_gdVersion = timestampToVersion(timestamp);
}
return m_gdVersion;
}
void Loader::Impl::platformMessageBox(char const* title, std::string const& info) {
MessageBoxA(nullptr, info.c_str(), title, MB_ICONERROR);
}

View file

@ -0,0 +1,70 @@
#include <map>
// Table originally from this gist by absolute:
// https://gist.github.com/absoIute/ebe5da42d118109a03632c9751d86e19
namespace {
// don't add versions here until Geode actually supports them,
// this is used for toggling forward compat mode
std::map<uint32_t, std::string>* s_gdTimestampMap = nullptr;
std::map<std::string, uint32_t>* s_gdTimestampMapRev = nullptr;
void tryInitializeTimestamps() {
if (!s_gdTimestampMap) {
s_gdTimestampMap = new std::map<uint32_t, std::string>{
{ 1419173053, "1.900" },
{ 1419880840, "1.910" },
{ 1421745341, "1.920" },
{ 1440638199, "2.000" },
{ 1440643927, "2.001" },
{ 1443053232, "2.010" },
{ 1443077847, "2.011" },
{ 1443077847, "2.020" },
{ 1484612867, "2.100" },
{ 1484626658, "2.101" },
{ 1484737207, "2.102" },
{ 1510526914, "2.110" },
{ 1510538091, "2.111" },
{ 1510619253, "2.112" },
{ 1511220108, "2.113" },
{ 1702921605, "2.200" },
{ 1704582672, "2.201" },
{ 1704601266, "2.202" },
{ 1704948277, "2.203" }
};
}
if (!s_gdTimestampMapRev) {
s_gdTimestampMapRev = new std::map<std::string, uint32_t>{
{ "1.900", 1419173053 },
{ "1.910", 1419880840 },
{ "1.920", 1421745341 },
{ "2.000", 1440638199 },
{ "2.001", 1440643927 },
{ "2.010", 1443053232 },
{ "2.011", 1443077847 },
{ "2.020", 1443077847 },
{ "2.100", 1484612867 },
{ "2.101", 1484626658 },
{ "2.102", 1484737207 },
{ "2.110", 1510526914 },
{ "2.111", 1510538091 },
{ "2.112", 1510619253 },
{ "2.113", 1511220108 },
{ "2.200", 1702921605 },
{ "2.201", 1704582672 },
{ "2.202", 1704601266 },
{ "2.203", 1704948277 }
};
}
}
}
static std::string timestampToVersion(uint32_t timestamp) {
tryInitializeTimestamps();
return s_gdTimestampMap->contains(timestamp) ?
s_gdTimestampMap->at(timestamp) :
std::to_string(timestamp);
}
static uint32_t versionToTimestamp(const std::string& version) {
tryInitializeTimestamps();
return s_gdTimestampMapRev->contains(version) ? s_gdTimestampMapRev->at(version) : 0;
}

View file

@ -29,11 +29,28 @@ void updateGeode() {
void* mainTrampolineAddr;
#include "gdTimestampMap.hpp"
unsigned int gdTimestamp = 0;
int WINAPI gdMainHook(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) {
// MessageBoxA(NULL, "Hello from gdMainHook!", "Hi", 0);
updateGeode();
if (versionToTimestamp(GEODE_STR(GEODE_GD_VERSION)) > gdTimestamp) {
LoaderImpl::get()->platformMessageBox(
"Unable to Load Geode!",
fmt::format(
"This version of Geode is made for Geometry Dash {} "
"but you're trying to play with GD {}."
"Please, update your game or install an older version of Geode.",
GEODE_STR(GEODE_GD_VERSION),
LoaderImpl::get()->getGameVersion()
)
);
return 2;
}
int exitCode = geodeEntry(hInstance);
if (exitCode != 0)
return exitCode;
@ -43,6 +60,10 @@ int WINAPI gdMainHook(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmd
std::string loadGeode() {
auto process = GetCurrentProcess();
auto dosHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(geode::base::get());
auto ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(geode::base::get() + dosHeader->e_lfanew);
gdTimestamp = ntHeader->FileHeader.TimeDateStamp;
constexpr size_t trampolineSize = 12;
mainTrampolineAddr = VirtualAlloc(
@ -50,8 +71,6 @@ std::string loadGeode() {
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE
);
auto dosHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(geode::base::get());
auto ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(geode::base::get() + dosHeader->e_lfanew);
auto entryAddr = geode::base::get() + ntHeader->OptionalHeader.AddressOfEntryPoint;
// function that calls main
auto preWinMainAddr = entryAddr + 5 + *reinterpret_cast<uintptr_t*>(entryAddr + 6) + 5;
@ -126,37 +145,6 @@ void earlyError(std::string message) {
LoaderImpl::get()->platformMessageBox("Unable to Load Geode!", message);
}
DWORD WINAPI sus(void*) {
ShellExecuteA(nullptr, nullptr, "https://media.tenor.com/cW1jA2hYdfcAAAAC/among-us-funny.gif", nullptr, nullptr, SW_SHOW);
MessageBoxA(
nullptr,
"Red sus. Red suuuus. I\r\n"
"said red, sus,\r\n"
"hahahahahaha. Why\r\n"
"arent you laughing? I\r\n"
"just made a reference\r\n"
"to the popular game\r\n"
"\"Among Us\"! How can\r\n"
"you not laugh at it?\r\n"
"Emergency meeting!\r\n"
"Guys, this here guy\r\n"
"doesn't laugh at my\r\n"
"funny Among Us\r\n"
"memes! Let's beat him\r\n"
"to death! Dead body\r\n"
"reported! Skip! Skip!\r\n"
"Vote blue! Blue was\r\n"
"not an impostor.\r\n",
"AMONG US ACTIVATED REAL !!!!!!!!!",
MB_OK
);
return 0;
}
extern "C" __declspec(dllexport) void fake() {
for (int i = 0; i < 5; i++)
CreateThread(nullptr, 0, sus, nullptr, 0, nullptr);
}
BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID) {
if (reason != DLL_PROCESS_ATTACH)
return TRUE;

View file

@ -388,6 +388,15 @@ void geode::cocos::reloadTextures(CreateLayerFunc returnTo) {
}
struct LoadingFinished : Modify<LoadingFinished, LoadingLayer> {
static void onModify(const auto& self) {
if (!Loader::get()->isForwardCompatMode())
return;
log::warn("geode::cocos::reloadTextures disabled in forward compat");
for (const auto& [_, hook] : self.m_hooks) {
hook->setAutoEnable(false);
}
}
void loadAssets() {
// loadFinished is inlined on Macchew OS :sob: