Merge branch 'main' into new-index-but-better

This commit is contained in:
dankmeme01 2024-06-01 22:39:58 +02:00
commit f8f85ad2b0
16 changed files with 418 additions and 70 deletions

View file

@ -243,7 +243,7 @@ if (DEFINED GEODE_TULIPHOOK_REPO_PATH)
message(STATUS "Using ${GEODE_TULIPHOOK_REPO_PATH} for TulipHook")
add_subdirectory(${GEODE_TULIPHOOK_REPO_PATH} ${GEODE_TULIPHOOK_REPO_PATH}/build)
else()
CPMAddPackage("gh:geode-sdk/TulipHook#58dc814")
CPMAddPackage("gh:geode-sdk/TulipHook#fd1e02e")
endif()
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)

View file

@ -9,18 +9,30 @@
// Set dllexport/dllimport to geode classes & functions
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)
#define GEODE_WINDOWS(...) __VA_ARGS__
#define GEODE_IS_WINDOWS
#define GEODE_IS_DESKTOP
#define GEODE_PLATFORM_NAME "Windows"
#define GEODE_CALL __stdcall
#define GEODE_CDECL_CALL __cdecl
#define GEODE_PLATFORM_EXTENSION ".dll"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "win"
#define CC_TARGET_OS_WIN32
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)
#define GEODE_IS_WINDOWS64
#define GEODE_WINDOWS64(...) __VA_ARGS__
#define GEODE_WINDOWS32(...)
#define GEODE_CALL
#else
#define GEODE_IS_WINDOWS32
#define GEODE_WINDOWS32(...) __VA_ARGS__
#define GEODE_WINDOWS64(...)
#define GEODE_CALL __stdcall
#endif
#else
#define GEODE_WINDOWS(...)
#define GEODE_WINDOWS32(...)
#define GEODE_WINDOWS64(...)
#endif
#if defined(__APPLE__)
@ -45,7 +57,6 @@
#define CC_TARGET_OS_MAC
#endif
#define GEODE_CALL
#define GEODE_CDECL_CALL
#else
#define GEODE_MACOS(...)
#define GEODE_IOS(...)
@ -57,7 +68,6 @@
#define GEODE_IS_ANDROID
#define GEODE_IS_MOBILE
#define GEODE_CALL
#define GEODE_CDECL_CALL
#define CC_TARGET_OS_ANDROID
#if defined(__arm__)

View file

@ -27,7 +27,13 @@
#define GEODE_API extern "C" __declspec(dllexport)
#define GEODE_EXPORT __declspec(dllexport)
static_assert(sizeof(void*) == 4, "Geode must be compiled in 32-bit for Windows!");
#if defined(GEODE_IS_WINDOWS64)
#define GEODE_IS_X64
#define GEODE_CDECL_CALL
#else
#define GEODE_IS_X86
#define GEODE_CDECL_CALL __cdecl
#endif
#include "windows.hpp"
@ -47,6 +53,9 @@
#define GEODE_API extern "C" __attribute__((visibility("default")))
#define GEODE_EXPORT __attribute__((visibility("default")))
#define GEODE_IS_X64
#define GEODE_CDECL_CALL
#include "macos.hpp"
#elif defined(GEODE_IS_IOS)
@ -65,6 +74,9 @@
#define GEODE_API extern "C" __attribute__((visibility("default")))
#define GEODE_EXPORT __attribute__((visibility("default")))
#define GEODE_IS_X64
#define GEODE_CDECL_CALL
#include "ios.hpp"
#elif defined(GEODE_IS_ANDROID)
@ -83,6 +95,13 @@
#define GEODE_API extern "C" __attribute__((visibility("default")))
#define GEODE_EXPORT __attribute__((visibility("default")))
#if defined(GEODE_IS_ANDROID64)
#define GEODE_IS_X64
#else
#define GEODE_IS_X86
#endif
#define GEODE_CDECL_CALL
#include "android.hpp"
#else

View file

@ -27,36 +27,53 @@ namespace geode::base {
}
}
namespace geode::cast {
template <class Type>
struct ShrunkPointer {
uint32_t m_ptrOffset;
Type* into(uintptr_t base) {
return reinterpret_cast<Type*>(base + m_ptrOffset);
}
};
struct TypeDescriptorType {
void* m_typeinfoTable;
int32_t m_spare;
intptr_t m_spare;
char m_typeDescriptorName[0x100];
};
struct ClassDescriptorType;
struct BaseClassDescriptorType {
TypeDescriptorType* m_typeDescriptor;
ShrunkPointer<TypeDescriptorType> m_typeDescriptor;
int32_t m_numContainedBases;
int32_t m_memberDisplacement[3];
int32_t m_attributes;
ClassDescriptorType* m_classDescriptor;
ShrunkPointer<ClassDescriptorType> m_classDescriptor;
};
struct BaseClassArrayType {
ShrunkPointer<BaseClassDescriptorType> m_descriptorEntries[0x100];
};
struct ClassDescriptorType {
int32_t m_signature;
int32_t m_attributes;
int32_t m_numBaseClasses;
BaseClassDescriptorType** m_baseClassArray;
ShrunkPointer<BaseClassArrayType> m_baseClassArray;
};
struct CompleteLocatorType {
int32_t m_signature;
int32_t m_offset;
int32_t m_cdOffset;
TypeDescriptorType* m_typeDescriptor;
ClassDescriptorType* m_classDescriptor;
ShrunkPointer<TypeDescriptorType> m_typeDescriptor;
ShrunkPointer<ClassDescriptorType> m_classDescriptor;
#ifdef GEODE_IS_X64
int32_t m_locatorOffset;
#endif
};
struct MetaPointerType {
@ -89,12 +106,20 @@ namespace geode::cast {
auto afterIdent = static_cast<char const*>(afterDesc->m_typeDescriptorName);
auto classDesc = metaPtr->m_completeLocator->m_classDescriptor;
#ifdef GEODE_IS_X64
auto locatorOffset = metaPtr->m_completeLocator->m_locatorOffset;
auto base = reinterpret_cast<uintptr_t>(metaPtr->m_completeLocator) - locatorOffset;
#else
auto base = 0;
#endif
auto classDesc = metaPtr->m_completeLocator->m_classDescriptor.into(base);
for (int32_t i = 0; i < classDesc->m_numBaseClasses; ++i) {
auto entry = classDesc->m_baseClassArray.into(base)->m_descriptorEntries[i].into(base);
auto optionIdent = static_cast<char const*>(
classDesc->m_baseClassArray[i]->m_typeDescriptor->m_typeDescriptorName
entry->m_typeDescriptor.into(base)->m_typeDescriptorName
);
auto optionOffset = classDesc->m_baseClassArray[i]->m_memberDisplacement[0];
auto optionOffset = entry->m_memberDisplacement[0];
if (std::strcmp(afterIdent, optionIdent) == 0) {
auto afterPtr = reinterpret_cast<std::byte*>(basePtr) + optionOffset;

View file

@ -2,6 +2,7 @@
#include <Geode/DefaultInclude.hpp>
#include <cocos2d.h>
#include <cocos-ext.h>
namespace geode {
/**

View file

@ -66,7 +66,6 @@ namespace geode {
float m_lineHeight = 0.f;
float m_linePadding = 0.f;
SimpleTextArea();
cocos2d::CCLabelBMFont* createLabel(const std::string& text, const float top);
float calculateOffset(cocos2d::CCLabelBMFont* label);
void charIteration(const std::function<cocos2d::CCLabelBMFont*(cocos2d::CCLabelBMFont* line, const char c, const float top)>& overflowHandling);
@ -74,6 +73,5 @@ namespace geode {
void updateLinesWordWrap();
void updateLinesCutoffWrap();
void updateContainer();
virtual void draw() override;
};
}
}

View file

@ -8,6 +8,8 @@
#include <functional>
#include <type_traits>
#include "../loader/Event.hpp"
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
#include "MiniFunction.hpp"
// support converting ccColor3B / ccColor4B to / from json
@ -1204,6 +1206,158 @@ namespace geode::cocos {
}
};
struct CCMenuItemExt {
private:
template <class Node>
class LambdaCallback : public cocos2d::CCObject {
public:
utils::MiniFunction<void(Node*)> m_callback;
static LambdaCallback* create(utils::MiniFunction<void(Node*)>&& callback) {
auto ret = new (std::nothrow) LambdaCallback();
if (ret && ret->init(std::forward<std::remove_reference_t<decltype(callback)>>(callback))) {
ret->autorelease();
return ret;
}
delete ret;
return nullptr;
}
bool init(utils::MiniFunction<void(Node*)>&& callback) {
m_callback = std::forward<std::remove_reference_t<decltype(callback)>>(callback);
return true;
}
void execute(cocos2d::CCNode* node) {
m_callback(static_cast<Node*>(node));
}
};
public:
static cocos2d::CCMenuItem* create(
utils::MiniFunction<void(cocos2d::CCMenuItem*)>&& callback
) {
auto item = cocos2d::CCMenuItem::create();
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
return item;
}
static cocos2d::CCMenuItemSprite* createSprite(
cocos2d::CCNode* normalSprite,
cocos2d::CCNode* selectedSprite,
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback
) {
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite);
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
return item;
}
static cocos2d::CCMenuItemSprite* createSprite(
cocos2d::CCNode* normalSprite,
cocos2d::CCNode* selectedSprite,
cocos2d::CCNode* disabledSprite,
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback
) {
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite, disabledSprite);
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
return item;
}
static CCMenuItemSpriteExtra* createSpriteExtra(
cocos2d::CCNode* normalSprite,
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
) {
auto item = CCMenuItemSpriteExtra::create(normalSprite, nullptr, nullptr);
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
return item;
}
static CCMenuItemSpriteExtra* createSpriteExtraWithFilename(
std::string_view normalSpriteName,
float scale,
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
) {
auto sprite = cocos2d::CCSprite::create(normalSpriteName.data());
sprite->setScale(scale);
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
}
static CCMenuItemSpriteExtra* createSpriteExtraWithFrameName(
std::string_view normalSpriteName,
float scale,
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
) {
auto sprite = cocos2d::CCSprite::createWithSpriteFrameName(normalSpriteName.data());
sprite->setScale(scale);
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
}
static CCMenuItemToggler* createToggler(
cocos2d::CCNode* onSprite,
cocos2d::CCNode* offSprite,
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
) {
auto item = CCMenuItemToggler::create(onSprite, offSprite, nullptr, nullptr);
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
return item;
}
static CCMenuItemToggler* createTogglerWithStandardSprites(
float scale,
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
) {
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png");
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOn_001.png");
offSprite->setScale(scale);
onSprite->setScale(scale);
return createToggler(offSprite, onSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
}
static CCMenuItemToggler* createTogglerWithFilename(
std::string_view onSpriteName,
std::string_view offSpriteName,
float scale,
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
) {
auto offSprite = cocos2d::CCSprite::create(offSpriteName.data());
auto onSprite = cocos2d::CCSprite::create(onSpriteName.data());
offSprite->setScale(scale);
onSprite->setScale(scale);
return createToggler(offSprite, onSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
}
static CCMenuItemToggler* createTogglerWithFrameName(
std::string_view onSpriteName,
std::string_view offSpriteName,
float scale,
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
) {
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName(offSpriteName.data());
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName(onSpriteName.data());
offSprite->setScale(scale);
onSprite->setScale(scale);
return createToggler(offSprite, onSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
}
template <class Node>
static void assignCallback(
cocos2d::CCMenuItem* item,
utils::MiniFunction<void(Node*)>&& callback
) {
auto lambda = LambdaCallback<Node>::create(std::forward<std::remove_reference_t<decltype(callback)>>(callback));
item->setTarget(lambda, menu_selector(LambdaCallback<Node>::execute));
item->setUserObject("lambda-callback", lambda);
}
};
void GEODE_DLL handleTouchPriorityWith(cocos2d::CCNode* node, int priority, bool force = false);
void GEODE_DLL handleTouchPriority(cocos2d::CCNode* node, bool force = false);
}

View file

@ -42,10 +42,19 @@ DWORD WINAPI xinputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRen
return getDSoundAudioDeviceGuids(dwUserIndex, pDSoundRenderGuid, pDSoundCaptureGuid);
}
#pragma comment(linker, "/export:XInputGetState=_xinputGetState@8")
#pragma comment(linker, "/export:XInputSetState=_xinputSetState@8")
#pragma comment(linker, "/export:XInputGetCapabilities=_xinputGetCapabilities@12")
#pragma comment(linker, "/export:XInputGetDSoundAudioDeviceGuids=_xinputGetDSoundAudioDeviceGuids@12")
// https://github.com/mrexodia/perfect-dll-proxy
#if defined(_WIN64)
#define PROXY_PATH(export) \
"/export:" #export "=\\\\.\\GLOBALROOT\\SystemRoot\\System32\\XInput9_1_0.dll." #export
#else
#define PROXY_PATH(export) \
"/export:" #export "=\\\\.\\GLOBALROOT\\SystemRoot\\SysWOW64\\XInput9_1_0.dll." #export
#endif
#pragma comment(linker, PROXY_PATH(XInputGetState))
#pragma comment(linker, PROXY_PATH(XInputSetState))
#pragma comment(linker, PROXY_PATH(XInputGetCapabilities))
#pragma comment(linker, PROXY_PATH(XInputGetDSoundAudioDeviceGuids))
BOOL fileExists(char const* path) {
DWORD attrib = GetFileAttributesA(path);

View file

@ -383,7 +383,7 @@ int ZipUtils::ccInflateCCZFile(char const* path, unsigned char** out) {
}
unsigned long destlen = len;
unsigned long source = (unsigned long)compressed + sizeof(*header);
uintptr_t source = (uintptr_t)compressed + sizeof(*header);
int ret = uncompress(*out, &destlen, (Bytef*)source, fileLen - sizeof(*header));
delete[] compressed;

View file

@ -28,14 +28,14 @@ $execute {
reinterpret_cast<void*>(geode::addresser::getNonVirtual(&FMOD::System::init)),
&FMOD_System_init_hook,
"FMOD::System::init"
GEODE_WINDOWS(, tulip::hook::TulipConvention::Stdcall)
GEODE_WINDOWS32(, tulip::hook::TulipConvention::Stdcall)
);
(void)geode::Mod::get()->hook(
reinterpret_cast<void*>(geode::addresser::getNonVirtual(&FMOD::ChannelControl::setVolume)),
&FMOD_ChannelControl_setVolume_hook,
"FMOD::ChannelControl::setVolume"
GEODE_WINDOWS(, tulip::hook::TulipConvention::Stdcall)
GEODE_WINDOWS32(, tulip::hook::TulipConvention::Stdcall)
);
}

View file

@ -38,6 +38,7 @@ $execute {
// geode::base::getCocos() + 0x1225DC = MessageBoxW in .idata
if (LoaderImpl::get()->isForwardCompatMode()) return;
#if GEODE_COMP_GD_VERSION == 22040
const uint32_t importedMessageBoxA = geode::base::getCocos() + 0x122600;
ByteVector p = {
@ -48,6 +49,9 @@ $execute {
(void)Mod::get()->patch(reinterpret_cast<void*>(geode::base::getCocos() + 0xC75F9), p);
(void)Mod::get()->patch(reinterpret_cast<void*>(geode::base::getCocos() + 0xc7651), p);
#else
#pragma message("Unsupported GD version!")
#endif
}
#endif

View file

@ -38,11 +38,15 @@ $execute {
if (LoaderImpl::get()->isForwardCompatMode()) return;
// BitmapDC::~BitmapDC
#if GEODE_COMP_GD_VERSION == 22040
patchCall(0xC9A56, (uintptr_t)&RemoveFontResourceWHook);
// BitmapDC::setFont
patchCall(0xCB5BC, (uintptr_t)&RemoveFontResourceWHook);
patchCall(0xCB642, (uintptr_t)&AddFontResourceWHook);
#else
#pragma message("Unsupported GD version!")
#endif
};
#endif

View file

@ -154,11 +154,18 @@ static std::string getStacktrace(PCONTEXT context) {
auto process = GetCurrentProcess();
auto thread = GetCurrentThread();
#ifdef GEODE_IS_X86
stack.AddrPC.Offset = context->Eip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = context->Esp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = context->Ebp;
#else
stack.AddrPC.Offset = context->Rip;
stack.AddrStack.Offset = context->Rsp;
stack.AddrFrame.Offset = context->Rbp;
#endif
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Mode = AddrModeFlat;
// size_t frame = 0;
@ -177,6 +184,7 @@ static std::string getStacktrace(PCONTEXT context) {
}
static std::string getRegisters(PCONTEXT context) {
#ifdef GEODE_IS_X86
return fmt::format(
"EAX: {:08x}\n"
"EBX: {:08x}\n"
@ -197,6 +205,44 @@ static std::string getRegisters(PCONTEXT context) {
context->Esi,
context->Eip
);
#else
return fmt::format(
"RAX: {:016x}\n"
"RBX: {:016x}\n"
"RCX: {:016x}\n"
"RDX: {:016x}\n"
"RBP: {:016x}\n"
"RSP: {:016x}\n"
"RDI: {:016x}\n"
"RSI: {:016x}\n"
"RIP: {:016x}\n"
"R8: {:016x}\n"
"R9: {:016x}\n"
"R10: {:016x}\n"
"R11: {:016x}\n"
"R12: {:016x}\n"
"R13: {:016x}\n"
"R14: {:016x}\n"
"R15: {:016x}\n",
context->Rax,
context->Rbx,
context->Rcx,
context->Rdx,
context->Rbp,
context->Rsp,
context->Rdi,
context->Rsi,
context->Rip,
context->R8,
context->R9,
context->R10,
context->R11,
context->R12,
context->R13,
context->R14,
context->R15
);
#endif
}
template <typename T, typename U>

View file

@ -65,6 +65,8 @@ std::string loadGeode() {
gdTimestamp = ntHeader->FileHeader.TimeDateStamp;
#ifdef GEODE_IS_WINDOWS32
constexpr size_t trampolineSize = 12;
mainTrampolineAddr = VirtualAlloc(
nullptr, trampolineSize,
@ -128,6 +130,9 @@ std::string loadGeode() {
return "Geode could not hook the main function, not loading Geode.";
std::memcpy(reinterpret_cast<void*>(patchAddr), patchBytes, patchSize);
VirtualProtectEx(process, reinterpret_cast<void*>(patchAddr), patchSize, oldProtect, &oldProtect);
#else
#pragma message("64-bit entry is not implemented yet.")
#endif
return "";
}

View file

@ -24,8 +24,6 @@ SimpleTextArea* SimpleTextArea::create(const std::string& font, const std::strin
}
}
SimpleTextArea::SimpleTextArea() {}
bool SimpleTextArea::init(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth) {
m_font = font;
m_text = text;
@ -37,9 +35,7 @@ bool SimpleTextArea::init(const std::string& font, const std::string& text, cons
m_container->setPosition({ 0, 0 });
m_container->setAnchorPoint({ 0, 1 });
m_container->setContentSize({ width, 0 });
this->addChild(m_container);
this->updateContainer();
return true;
@ -142,14 +138,23 @@ float SimpleTextArea::getLineHeight() {
}
CCLabelBMFont* SimpleTextArea::createLabel(const std::string& text, const float top) {
CCLabelBMFont* label = CCLabelBMFont::create(text.c_str(), m_font.c_str());
if (m_maxLines && m_lines.size() >= m_maxLines) {
CCLabelBMFont* last = m_lines.at(m_maxLines - 1);
const std::string& text = last->getString();
label->setScale(m_scale);
label->setPosition({ 0, top });
label->setColor({ m_color.r, m_color.g, m_color.b });
label->setOpacity(m_color.a);
last->setString(text.substr(0, text.size() - 3).append("...").c_str());
return label;
return nullptr;
} else {
CCLabelBMFont* label = CCLabelBMFont::create(text.c_str(), m_font.c_str());
label->setScale(m_scale);
label->setPosition({ 0, top });
label->setColor({ m_color.r, m_color.g, m_color.b });
label->setOpacity(m_color.a);
return label;
}
}
float SimpleTextArea::calculateOffset(CCLabelBMFont* label) {
@ -158,22 +163,27 @@ float SimpleTextArea::calculateOffset(CCLabelBMFont* label) {
void SimpleTextArea::charIteration(const std::function<CCLabelBMFont*(CCLabelBMFont* line, const char c, const float top)>& overflowHandling) {
float top = 0;
m_lines.clear();
CCLabelBMFont* line = this->createLabel("", top);
m_lines = { line };
for (const char c : m_text) {
if (m_maxLines && m_lines.size() > m_maxLines) {
CCLabelBMFont* last = m_lines.at(m_maxLines - 1);
const std::string text = last->getString();
if (c == '\n') {
line = this->createLabel("", top -= this->calculateOffset(line));
m_lines.pop_back();
last->setString(text.substr(0, text.size() - 3).append("...").c_str());
if (line == nullptr) {
break;
} else {
m_lines.push_back(line);
}
} else if (m_artificialWidth && line->getContentWidth() * m_scale >= this->getWidth()) {
line = overflowHandling(line, c, top -= this->calculateOffset(line));
break;
} else if (c == '\n') {
m_lines.push_back(line = this->createLabel("", top -= this->calculateOffset(line)));
} else if (m_artificialWidth && line->getContentSize().width * m_scale >= this->getWidth()) {
m_lines.push_back(line = overflowHandling(line, c, top -= this->calculateOffset(line)));
if (line == nullptr) {
break;
} else {
m_lines.push_back(line);
}
} else {
line->setString((std::string(line->getString()) + c).c_str());
}
@ -184,18 +194,14 @@ void SimpleTextArea::updateLinesNoWrap() {
std::stringstream stream(m_text);
std::string part;
float top = 0;
m_lines.clear();
while (std::getline(stream, part)) {
if (m_maxLines && m_lines.size() >= m_maxLines) {
CCLabelBMFont* last = m_lines.at(m_maxLines - 1);
const std::string text = last->getString();
last->setString(text.substr(0, text.size() - 3).append("...").c_str());
CCLabelBMFont* line = this->createLabel(part, top);
if (line == nullptr) {
break;
} else {
CCLabelBMFont* line = this->createLabel(part, 0);
top -= this->calculateOffset(line);
m_lines.push_back(line);
@ -205,15 +211,18 @@ void SimpleTextArea::updateLinesNoWrap() {
void SimpleTextArea::updateLinesWordWrap() {
this->charIteration([this](CCLabelBMFont* line, const char c, const float top) {
static std::string delimiters(" `~!@#$%^&*()-_=+[{}];:'\",<.>/?\\|");
static const std::string delimiters(" `~!@#$%^&*()-_=+[{}];:'\",<.>/?\\|");
if (delimiters.find(c) == std::string_view::npos) {
const std::string text = line->getString();
const std::string& text = line->getString();
const size_t position = text.find_last_of(delimiters) + 1;
CCLabelBMFont* newLine = this->createLabel(text.substr(position) + c, top);
line->setString(text.substr(0, position).c_str());
if (newLine != nullptr) {
line->setString(text.substr(0, position).c_str());
}
return this->createLabel(text.substr(position) + c, top);
return newLine;
} else {
return this->createLabel(std::string(c, c != ' '), top);
}
@ -222,12 +231,12 @@ void SimpleTextArea::updateLinesWordWrap() {
void SimpleTextArea::updateLinesCutoffWrap() {
this->charIteration([this](CCLabelBMFont* line, const char c, const float top) {
const std::string text = line->getString();
const std::string& text = line->getString();
const char back = text.back();
const bool lastIsSpace = back == ' ';
CCLabelBMFont* newLine = this->createLabel(std::string(!lastIsSpace, back).append(std::string(c != ' ', c)), top);
if (!lastIsSpace) {
if (newLine == nullptr && !lastIsSpace) {
if (text[text.size() - 2] == ' ') {
line->setString(text.substr(0, text.size() - 1).c_str());
} else {
@ -261,7 +270,7 @@ void SimpleTextArea::updateContainer() {
m_lineHeight = 0;
}
float height = m_lineHeight * lineCount + m_linePadding * (lineCount - 1);
const float height = m_lineHeight * lineCount + m_linePadding * (lineCount - 1);
this->setContentSize({ width, height });
m_container->setContentSize(this->getContentSize());
@ -288,7 +297,3 @@ void SimpleTextArea::updateContainer() {
m_container->addChild(line);
}
}
void SimpleTextArea::draw() {
CCNode::draw();
}

View file

@ -69,12 +69,26 @@ Addresser::MultipleInheritance* Addresser::instance() {
#ifdef GEODE_IS_WINDOWS
#include <delayimp.h>
extern "C" FARPROC WINAPI __delayLoadHelper2(PCImgDelayDescr pidd, FARPROC* ppfnIATEntry); // NOLINT(*-reserved-identifier)
FARPROC WINAPI delayLoadHook(unsigned dliNotify, PDelayLoadInfo pdli) {
switch (dliNotify) {
case dliFailLoadLib:
case dliFailGetProc:
// incase the delayload helper fails at all (missing symbol, or library entirely),
// return -1, so we can more easily handle it below
return (FARPROC)(-1);
default:
return NULL;
}
}
extern "C" const PfnDliHook __pfnDliFailureHook2 = delayLoadHook;
#endif
intptr_t Addresser::followThunkFunction(intptr_t address) {
#ifdef GEODE_IS_WINDOWS
#ifdef GEODE_IS_WINDOWS32
// if theres a jmp at the start
if (*reinterpret_cast<uint8_t*>(address) == 0xE9) {
if (address && *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,
@ -87,7 +101,7 @@ intptr_t Addresser::followThunkFunction(intptr_t address) {
}
// 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) {
if (address && *reinterpret_cast<uint8_t*>(address) == 0xFF && *reinterpret_cast<uint8_t*>(address + 1) == 0x25) {
// read where the jmp reads from
address = *reinterpret_cast<uint32_t*>(address + 2);
// that then contains the actual address of the func
@ -95,7 +109,7 @@ intptr_t Addresser::followThunkFunction(intptr_t address) {
}
// if it starts with mov eax,..., it's a delay loaded func
if (*reinterpret_cast<uint8_t*>(address) == 0xB8) {
if (address && *reinterpret_cast<uint8_t*>(address) == 0xB8) {
// follow the jmp to the tailMerge func and grab the ImgDelayDescr pointer from there
// do it this way instead of grabbing it from the NT header ourselves because
// we don't know the dll name
@ -110,6 +124,60 @@ intptr_t Addresser::followThunkFunction(intptr_t address) {
// get the address of the function, loading the library if needed
address = reinterpret_cast<intptr_t>(__delayLoadHelper2(idd, imp));
// if the helper failed, it will return -1, so we can handle it here
if (address == -1) {
address = 0;
}
}
#endif
#ifdef GEODE_IS_WINDOWS64
static constexpr auto checkByteSequence = [](uintptr_t address, const std::initializer_list<uint8_t>& bytes) {
for (auto byte : bytes) {
if (*reinterpret_cast<uint8_t*>(address++) != byte) {
return false;
}
}
return true;
};
// check if first instruction is a jmp qword ptr [rip + ...], i.e. if the func is a thunk
// FF 25 xxxxxxxx
if (address && checkByteSequence(address, {0xFF, 0x25})) {
const auto offset = *reinterpret_cast<int32_t*>(address + 2);
// rip is at address + 6 (size of the instruction)
address = *reinterpret_cast<uintptr_t*>(address + 6 + offset);
}
// if it starts with lea eax,..., it's a delay loaded func
// 48 8D 05 xxxxxxxx
if (address && checkByteSequence(address, {0x48, 0x8d, 0x05})) {
// follow the jmp to the tailMerge func and grab the ImgDelayDescr pointer from there
// do it this way instead of grabbing it from the NT header ourselves because
// we don't know the dll name
auto leaAddress = address + 7 + *reinterpret_cast<int32_t*>(address + 3);
auto jmpOffset = *reinterpret_cast<int32_t*>(address + 7 + 1);
auto tailMergeAddr = address + 7 + jmpOffset + 5;
// this is quite a scary offset, but lets hope it works
auto leaAddr = tailMergeAddr + 51;
// make sure leaAddr is pointing to a lea rcx, [rip + ...]
if (leaAddr && checkByteSequence(leaAddr, {0x48, 0x8d, 0x0d})) {
auto offset = *reinterpret_cast<int32_t*>(leaAddr + 3);
auto did = reinterpret_cast<PCImgDelayDescr>(leaAddr + 7 + offset);
address = reinterpret_cast<intptr_t>(__delayLoadHelper2(did, reinterpret_cast<FARPROC*>(leaAddress)));
if (address == -1) {
address = 0;
}
}
}
// if theres a jmp at the start
if (address && *reinterpret_cast<uint8_t*>(address) == 0xE9) {
auto relative = *reinterpret_cast<uint32_t*>(address + 1);
auto newAddress = address + relative + 5;
address = newAddress;
}
#endif
return address;