Merge branch 'main' into 1.4.0-dev

This commit is contained in:
mat 2023-12-19 10:02:19 -03:00 committed by GitHub
commit ebcc23e7a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 156 additions and 17 deletions

View file

@ -2,6 +2,7 @@ name: Test Offsets
on:
workflow_dispatch:
pull_request:
push:
paths:
- 'bindings/**' # only when adjusting bindings

View file

@ -27,6 +27,26 @@
* Check modified date when unzipping `.geode` files (5c765c6)
* Only hash markdown files on resource checking (f563c46)
## v1.3.10
* Panic if decompressString2 fails, to prevent data loss (0787b8f4)
## v1.3.9
* Fix followThunkFunction (4b766301)
## v1.3.8
* Implement a save file fix for Windows (391f63ed)
* Recursively follow jumps in followThunkFunction (44a018cd)
* Remove need for GEODE_DEBUG for crashlogs (e8a326f7)
* Some bindings (f18335fa)
## v1.3.7
* Fix web download deadlock (16418562)
* Set max time for updating index notification (f7962246)
* Log geode version on startup (c5550a67)
* Fix logic error in addHook (5cf0f3c2)
* Improve logging + minor refactors (5083017b)
* Some bindings changes
## v1.3.6
* Allow error responses in our WebRequest classes (237128bf)
* Display unhandled C++ exceptions in crash log (fdd78aca, 0d091626, 52421d8c, 0472075f)

View file

@ -1156,6 +1156,7 @@ class cocos2d::CCTransitionFade {
class cocos2d::ZipUtils {
static auto compressString(gd::string, bool, int) = mac 0xe9a50;
static auto decompressString(gd::string, bool, int) = mac 0xea380;
static auto decompressString2(unsigned char* data, bool decrypt, int size, int decryptionKey);
static int ccDeflateMemory(unsigned char*, unsigned int, unsigned char**) = mac 0xe9cf0;
}

View file

@ -3031,6 +3031,7 @@ class GameLevelManager : cocos2d::CCNode {
void uploadAccountComment(gd::string text) = win 0xb3250;
void uploadLevelComment(int levelID, gd::string text, int unk) = win 0xb31c0;
void uploadComment(gd::string text, CommentType type, int levelID, int unk) = win 0xb32e0;
void deleteComment(int commentID, CommentType type, int levelID) = win 0xb41a0;
void downloadLevel(int id, bool downloadData) = win 0xaa730;
bool hasDownloadedLevel(int id) = win 0xab830;
GJGameLevel* getSavedLevel(int id) = win 0xa2ee0;
@ -6286,9 +6287,14 @@ class LevelTools {
static bool verifyLevelIntegrity(gd::string, int) = mac 0x294360, win 0x18b180;
static float xPosForTime(float, cocos2d::CCArray*, int) = mac 0x293d90, win 0x18acd0;
static float timeForXPos(float, cocos2d::CCArray*, int) = mac 0x293eb0, win 0x18ae70;
static gd::string getAudioFileName(int) = mac 0x292840;
static gd::string getAudioTitle(int) = mac 0x2922f0;
static gd::string artistForAudio(int) = mac 0x292d90;
static gd::string urlForAudio(int) = mac 0x292f10;
static gd::string getAudioFileName(int) = mac 0x292840, win 0x189fa0;
static gd::string getAudioTitle(int) = mac 0x2922f0, win 0x189c60;
static int artistForAudio(int) = mac 0x292d90, win 0x18A2D0;
static gd::string urlForAudio(int) = mac 0x292f10, win 0x18a4a0;
static gd::string nameForArtist(int) = win 0x18A3A0;
static gd::string ngURLForArtist(int) = win 0x18A7C0;
static gd::string ytURLForArtist(int) = win 0x18A8C0;
static gd::string fbURLForArtist(int) = win 0x18A9B0;
static gd::string getAudioString(int) = win 0x18AAA0;
}
// clang-format on

View file

@ -16,5 +16,5 @@ namespace geode {
namespace {
// to make sure the instance is set into the sharedMod<> in load time
static auto mod = geode::getMod();
static auto mod = geode::getMod();
}

View file

@ -174,6 +174,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
INDEX_UPDATE_NOTIF = Notification::create(
"Updating Index", NotificationIcon::Loading, 0
);
INDEX_UPDATE_NOTIF->setTime(NOTIFICATION_LONG_TIME);
INDEX_UPDATE_NOTIF->show();
Index::get()->update();
}

View file

@ -0,0 +1,78 @@
#include <Geode/loader/Loader.hpp>
#if defined(GEODE_IS_WINDOWS) || defined(GEODE_IS_ANDROID)
using namespace geode::prelude;
#include <Geode/cocos/support/base64.h>
#include "../loader/LoaderImpl.hpp"
void panic(std::string reason) {
LoaderImpl::get()->platformMessageBox("Critical", fmt::format(
"Your save file failed to load (reason: {})\n"
"As to not lose all of your data, the game will now abort.\n"
"Please backup your save files and try opening the game again, it might work.\n"
"Please contact the Geode Team about this", reason
));
std::abort();
}
// This function is well known for crashing on certain save files,
// causing the game to crash at startup, known as the infamous save file bug.
//
// Rob ends up relying on strlen for knowing the size of `data`, instead of just using the passed in `size`.
// Its a miracle this works most of the time, considering `data` is just binary data
//
// To fix this, we just rewrite the function.
gd::string decompressString2(unsigned char* data, bool decrypt, int size, int decryptionKey) {
log::debug("decompressString2 data={} size={}", reinterpret_cast<const void*>(data), size);
if (data == nullptr || size == 0) {
return {};
}
std::vector<unsigned char> copiedData(data, data + size);
if (decrypt) {
for (int i = 0; i < size; i++) {
copiedData[i] ^= decryptionKey;
}
}
// TODO: maybe not use cocos's base64 and inflateMemory..
unsigned char* out = nullptr;
auto const decodedSize = cocos2d::base64Decode(copiedData.data(), size, &out);
std::unique_ptr<unsigned char> b64decoded { out };
if (decodedSize <= 0) {
panic(fmt::format("base64 (size={}) (data={} size={})", decodedSize, reinterpret_cast<const void*>(data), size));
return {};
}
out = nullptr;
auto const inflatedSize = cocos2d::ZipUtils::ccInflateMemory(b64decoded.get(), decodedSize, &out);
std::unique_ptr<unsigned char> inflated { out };
if (inflatedSize <= 0) {
panic(fmt::format("inflate (size={}) (data={} size={})", inflatedSize, reinterpret_cast<const void*>(data), size));
return {};
}
return std::string(reinterpret_cast<char*>(inflated.get()), inflatedSize);
}
// Modify doesnt want to work for some reason!
$execute {
(void) Mod::get()->addHook(
reinterpret_cast<void*>(
geode::addresser::getNonVirtual(
&cocos2d::ZipUtils::decompressString2
)
),
&decompressString2,
"cocos2d::ZipUtils::decompressString2",
tulip::hook::TulipConvention::Cdecl
);
}
#endif

View file

@ -9,9 +9,13 @@ using namespace geode::prelude;
$register_ids(GJGarageLayer) {
// the lock does not exist for not logged in users
auto loggedInOffset = GJAccountManager::get()->m_accountID == GJAccountManager::get()->m_playerID ? -1 : 0;
if (loggedInOffset == -1 && !GameManager::get()->m_clickedName) {
// adjusts for the sprite asking for your name
loggedInOffset++;
}
setIDSafe(this, 2, "username-label");
setIDSafe(this, 6 + loggedInOffset, "player-icon");
setIDSafe<CCTextInputNode>(this, 0, "username-label");
setIDSafe<SimplePlayer>(this, 0, "player-icon");
auto winSize = CCDirector::get()->getWinSize();

View file

@ -60,7 +60,7 @@ $execute {
int geodeEntry(void* platformData) {
log::Logger::setup();
log::info("Entry");
log::info("Running {} {}", Mod::get()->getName(), Mod::get()->getVersion());
auto begin = std::chrono::high_resolution_clock::now();

View file

@ -673,7 +673,10 @@ Result<IndexInstallList> Index::getInstallList(IndexItemHandle item) const {
}
// recursively add dependencies
GEODE_UNWRAP_INTO(auto deps, this->getInstallList(depItem));
ranges::push(list.list, deps.list);
for (auto& dep : deps.list) {
if (ranges::contains(list.list, dep)) continue;
list.list.push_back(dep);
}
}
// otherwise user must get this dependency manually from somewhere
else {
@ -745,6 +748,7 @@ void Index::Impl::installNext(size_t index, IndexInstallList const& list) {
auto item = list.list.at(index);
auto tempFile = dirs::getTempDir() / (item->getMetadata().getID() + ".index");
log::debug("Installing {}", item->getMetadata().getID());
m_runningInstallations[list.target] = web::AsyncWebRequest()
.join("install_item_" + item->getMetadata().getID())
.fetch(item->getDownloadURL())
@ -780,6 +784,8 @@ void Index::Impl::installNext(size_t index, IndexInstallList const& list) {
item->setIsInstalled(true);
log::debug("Installed {}", item->getMetadata().getID());
// Install next item in queue
this->installNext(index + 1, list);
})

View file

@ -690,6 +690,11 @@ Mod* Loader::Impl::getInternalMod() {
auto& mod = Mod::sharedMod<>;
if (mod)
return mod;
if (m_mods.contains("geode.loader")) {
log::warn("Something went wrong and Mod::sharedMod<> got unset after the internal mod was created! Setting sharedMod back...");
mod = m_mods["geode.loader"];
return mod;
}
auto infoRes = getModImplInfo();
if (!infoRes) {
LoaderImpl::get()->platformMessageBox(
@ -706,7 +711,6 @@ Mod* Loader::Impl::getInternalMod() {
}
mod->m_impl->m_enabled = true;
m_mods.insert({ mod->getID(), mod });
log::debug("Created internal mod {}", mod->getName());
return mod;
}

View file

@ -264,10 +264,7 @@ static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) {
auto text = crashlog::writeCrashlog(faultyMod, getInfo(info, faultyMod), getStacktrace(info->ContextRecord), getRegisters(info->ContextRecord));
// show message box on debug mode
#ifdef GEODE_DEBUG
MessageBoxA(nullptr, text.c_str(), "Geode Crashed", MB_ICONERROR);
#endif
return EXCEPTION_CONTINUE_SEARCH;
}

View file

@ -68,6 +68,19 @@ Addresser::MultipleInheritance* Addresser::instance() {
intptr_t Addresser::followThunkFunction(intptr_t address) {
#ifdef GEODE_IS_WINDOWS
// if theres a jmp at the start
if (*reinterpret_cast<uint8_t*>(address) == 0xE9) {
auto relative = *reinterpret_cast<uint32_t*>(address + 1);
auto newAddress = address + relative + 5;
// and if that jmp leads to a jmp dword ptr, only then follow it,
// because otherwise its just a hook.
// For some reason this [jmp -> jmp dword ptr] chain happens with a few cocos functions,
// but not all. For example: cocos2d::ZipUtils::decompressString2
if (*reinterpret_cast<uint8_t*>(newAddress) == 0xFF && *reinterpret_cast<uint8_t*>(newAddress + 1) == 0x25) {
address = newAddress;
}
}
// check if first instruction is a jmp dword ptr [....], i.e. if the func is a thunk
if (*reinterpret_cast<uint8_t*>(address) == 0xFF && *reinterpret_cast<uint8_t*>(address + 1) == 0x25) {
// read where the jmp reads from

View file

@ -298,9 +298,11 @@ SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const
}
Loader::get()->queueInMainThread([self = data->self, now, total]() {
std::lock_guard _(self->m_mutex);
std::unique_lock<std::mutex> l(self->m_mutex);
for (auto& prog : self->m_progresses) {
l.unlock();
prog(*self->m_self, now, total);
l.lock();
}
});
return 0;
@ -328,9 +330,11 @@ SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const
m_finished = true;
Loader::get()->queueInMainThread([this, ret]() {
std::lock_guard _(m_mutex);
std::unique_lock<std::mutex> l(m_mutex);
for (auto& then : m_thens) {
l.unlock();
then(*m_self, ret);
l.lock();
}
std::lock_guard __(RUNNING_REQUESTS_MUTEX);
RUNNING_REQUESTS.erase(m_id);
@ -355,9 +359,11 @@ void SentAsyncWebRequest::Impl::doCancel() {
}
Loader::get()->queueInMainThread([this]() {
std::lock_guard _(m_mutex);
std::unique_lock<std::mutex> l(m_mutex);
for (auto& canc : m_cancelleds) {
l.unlock();
canc(*m_self);
l.lock();
}
});
@ -393,9 +399,11 @@ void SentAsyncWebRequest::Impl::error(std::string const& error, int code) {
});
Loader::get()->queueInMainThread([this, error, code]() {
{
std::lock_guard _(m_mutex);
std::unique_lock<std::mutex> l(m_mutex);
for (auto& expect : m_expects) {
l.unlock();
expect(error, code);
l.lock();
}
}
std::lock_guard _(RUNNING_REQUESTS_MUTEX);