Merge branch 'main' of github.com:geode-sdk/geode into web-request-refactor

This commit is contained in:
matcool 2024-01-09 15:40:45 -03:00
commit 8081860055
39 changed files with 307 additions and 453 deletions

View file

@ -7,10 +7,9 @@ if (GEODE_BUILDING_DOCS)
set(GEODE_DISABLE_CLI_CALLS On)
set(CMAKE_EXPORT_COMPILE_COMMANDS On)
set(GEODE_DISABLE_PRECOMPILED_HEADERS On)
set(GEODE_DONT_BUILD_TEST_MODS On)
endif()
set(GEODE_GD_VERSION 2.200)
# Read version
file(READ VERSION GEODE_VERSION)
string(STRIP "${GEODE_VERSION}" GEODE_VERSION)
@ -77,8 +76,19 @@ include(cmake/Platform.cmake)
include(cmake/GeodeFile.cmake)
include(cmake/CPM.cmake)
if (NOT DEFINED GEODE_GD_VERSION)
if (${GEODE_TARGET_PLATFORM} STREQUAL "Win32")
set(GEODE_GD_VERSION 2.202)
else()
set(GEODE_GD_VERSION 2.200)
endif()
endif()
target_compile_definitions(${PROJECT_NAME} INTERFACE GEODE_GD_VERSION=${GEODE_GD_VERSION})
set(MAT_JSON_AS_INTERFACE ON)
CPMAddPackage("gh:geode-sdk/json#9f54cca")
CPMAddPackage("gh:geode-sdk/json#49bdff7")
CPMAddPackage("gh:fmtlib/fmt#10.1.1")
CPMAddPackage("gh:gulrak/filesystem#3e5b930")

View file

@ -1,22 +1,4 @@
if (NOT DEFINED GEODE_TARGET_PLATFORM)
if(APPLE)
if(IOS)
set(GEODE_TARGET_PLATFORM "iOS")
else()
set(GEODE_TARGET_PLATFORM "MacOS")
endif()
elseif(WIN32)
set(GEODE_TARGET_PLATFORM "Win32")
elseif(ANDROID)
if (ANDROID_ABI STREQUAL "arm64-v8a")
set(GEODE_TARGET_PLATFORM "Android64")
elseif(ANDROID_ABI STREQUAL "armeabi-v7a")
set(GEODE_TARGET_PLATFORM "Android32")
endif()
else()
message(FATAL_ERROR "Unable to detect platform, please set GEODE_TARGET_PLATFORM in the root CMake file.")
endif()
endif()
include(cmake/PlatformDetect.cmake)
if (NOT ${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME})
set(GEODE_TARGET_PLATFORM GEODE_TARGET_PLATFORM PARENT_SCOPE)

View file

@ -0,0 +1,19 @@
if (NOT DEFINED GEODE_TARGET_PLATFORM)
if(APPLE)
if(IOS)
set(GEODE_TARGET_PLATFORM "iOS")
else()
set(GEODE_TARGET_PLATFORM "MacOS")
endif()
elseif(WIN32)
set(GEODE_TARGET_PLATFORM "Win32")
elseif(ANDROID)
if (ANDROID_ABI STREQUAL "arm64-v8a")
set(GEODE_TARGET_PLATFORM "Android64")
elseif(ANDROID_ABI STREQUAL "armeabi-v7a")
set(GEODE_TARGET_PLATFORM "Android32")
endif()
else()
message(FATAL_ERROR "Unable to detect platform, please set GEODE_TARGET_PLATFORM in the root CMake file.")
endif()
endif()

View file

@ -11,7 +11,11 @@ icon = "loader/resources/logos/geode-circle.png"
[tutorials]
dir = "docs"
assets = ["docs/assets/*.png"]
assets = [
"docs/assets/*.png",
"docs/assets/handbook/vol1/*.png",
"docs/assets/handbook/vol2/*.png",
]
[[sources]]
name = "Geode"

View file

@ -26,6 +26,22 @@ execute_process(
ERROR_QUIET
)
if (NOT GEODE_COMMIT_HASH)
set(GEODE_COMMIT_HASH "(?)")
endif()
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY ${GEODE_BINDINGS_REPO_PATH}
OUTPUT_VARIABLE GEODE_BINDINGS_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if (NOT GEODE_BINDINGS_COMMIT_HASH)
set(GEODE_BINDINGS_COMMIT_HASH "(?)")
endif()
# Package info file for internal representation
set(GEODE_RESOURCES_PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources)
configure_file(resources/mod.json.in ${CMAKE_CURRENT_SOURCE_DIR}/resources/mod.json)
@ -53,8 +69,6 @@ file(GLOB SOURCES CONFIGURE_DEPENDS
src/ui/*.cpp
src/ui/nodes/*.cpp
src/ui/internal/*.cpp
src/ui/internal/credits/*.cpp
src/ui/internal/dev/*.cpp
src/ui/internal/info/*.cpp
src/ui/internal/list/*.cpp
src/ui/internal/settings/*.cpp
@ -277,6 +291,3 @@ elseif (WIN32)
endif()
add_subdirectory(test)
# Build index hashing algorithm test program
add_subdirectory(hash)

View file

@ -1,10 +0,0 @@
cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
project(GeodeChecksum VERSION 1.0)
add_executable(${PROJECT_NAME} main.cpp hash.cpp)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
target_link_libraries(${PROJECT_NAME} PUBLIC ghc_filesystem)
message(STATUS "Building Checksum Exe")

View file

@ -1,11 +0,0 @@
#include <iostream>
#include "hash.hpp"
int main(int argc, char** argv) {
if (argc < 2 || !ghc::filesystem::exists(argv[1])) {
std::cout << "Usage: \"checksum <file>\"\n";
return 1;
}
std::cout << calculateHash(argv[1]) << std::endl;
return 0;
}

View file

@ -42,7 +42,6 @@ enum class SearchType {
FavouriteLists = 103
};
// jesus fucking christ (painfully written by @hjfod)
enum class GameObjectType {
Solid = 0,
Hazard = 2,
@ -287,6 +286,11 @@ enum class SpecialRewardItem {
Orbs = 0x7,
Diamonds = 0x8,
CustomItem = 0x9,
EarthShard = 0xA,
BloodShard = 0xB,
MetalShard = 0xC,
LightShard = 0xD,
SoulShard = 0xE
};
enum class EditCommand {

View file

@ -417,7 +417,6 @@ public:
* @note For internal usage, we need to declare this member variable as public since it's used in UT_HASH.
*/
CCDictElement* m_pElements;
private:
/** The support type of dictionary, it's confirmed when setObject is invoked. */
enum CCDictType

View file

@ -90,12 +90,12 @@ namespace geode {
ModMetadata getMetadata() const;
ghc::filesystem::path getTempDir() const;
/**
* Get the path to the mod's platform binary (.dll on Windows, .dylib
* Get the path to the mod's platform binary (.dll on Windows, .dylib
* on Mac & iOS, .so on Android)
*/
ghc::filesystem::path getBinaryPath() const;
/**
* Get the path to the mod's runtime resources directory (contains all
* Get the path to the mod's runtime resources directory (contains all
* of its resources)
*/
ghc::filesystem::path getResourcesDir() const;
@ -119,25 +119,25 @@ namespace geode {
bool hasSettings() const;
std::vector<std::string> getSettingKeys() const;
bool hasSetting(std::string const& key) const;
std::optional<Setting> getSettingDefinition(std::string const& key) const;
SettingValue* getSetting(std::string const& key) const;
bool hasSetting(std::string_view const key) const;
std::optional<Setting> getSettingDefinition(std::string_view const key) const;
SettingValue* getSetting(std::string_view const key) const;
/**
* Register a custom setting's value class. See Mod::addCustomSetting
* for a convenience wrapper that creates the value in-place to avoid
* code duplication. Also see
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
* Register a custom setting's value class. See Mod::addCustomSetting
* for a convenience wrapper that creates the value in-place to avoid
* code duplication. Also see
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
* more information about custom settings
* @param key The setting's key
* @param value The SettingValue class that shall handle this setting
* @see addCustomSetting
*/
void registerCustomSetting(std::string const& key, std::unique_ptr<SettingValue> value);
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
/**
* Register a custom setting's value class. The new SettingValue class
* will be created in-place using `std::make_unique`. See
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
* Register a custom setting's value class. The new SettingValue class
* will be created in-place using `std::make_unique`. See
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
* more information about custom settings
* @param key The setting's key
* @param value The value of the custom setting
@ -147,14 +147,14 @@ namespace geode {
* }
*/
template <class T, class V>
void addCustomSetting(std::string const& key, V const& value) {
this->registerCustomSetting(key, std::make_unique<T>(key, this->getID(), value));
void addCustomSetting(std::string_view const key, V const& value) {
this->registerCustomSetting(key, std::make_unique<T>(std::string(key), this->getID(), value));
}
matjson::Value& getSaveContainer();
template <class T>
T getSettingValue(std::string const& key) const {
T getSettingValue(std::string_view const key) const {
if (auto sett = this->getSetting(key)) {
return SettingValueSetter<T>::get(sett);
}
@ -162,7 +162,7 @@ namespace geode {
}
template <class T>
T setSettingValue(std::string const& key, T const& value) {
T setSettingValue(std::string_view const key, T const& value) {
if (auto sett = this->getSetting(key)) {
auto old = this->getSettingValue<T>(key);
SettingValueSetter<T>::set(sett, value);
@ -171,10 +171,10 @@ namespace geode {
return T();
}
bool hasSavedValue(std::string const& key);
bool hasSavedValue(std::string_view const key);
template <class T>
T getSavedValue(std::string const& key) {
T getSavedValue(std::string_view const key) {
auto& saved = this->getSaveContainer();
if (saved.contains(key)) {
try {
@ -188,7 +188,7 @@ namespace geode {
}
template <class T>
T getSavedValue(std::string const& key, T const& defaultValue) {
T getSavedValue(std::string_view const key, T const& defaultValue) {
auto& saved = this->getSaveContainer();
if (saved.contains(key)) {
try {
@ -210,7 +210,7 @@ namespace geode {
* @returns The old value
*/
template <class T>
T setSavedValue(std::string const& key, T const& value) {
T setSavedValue(std::string_view const key, T const& value) {
auto& saved = this->getSaveContainer();
auto old = this->getSavedValue<T>(key);
saved[key] = value;
@ -335,7 +335,7 @@ namespace geode {
* Check whether or not this Mod
* depends on another mod
*/
bool depends(std::string const& id) const;
bool depends(std::string_view const id) const;
/**
* Check whether all the required

View file

@ -903,11 +903,33 @@ namespace geode::cocos {
}
};
template <typename _Type>
template <class T>
concept CocosObjectPtr = std::is_pointer_v<T> && std::is_convertible_v<T, cocos2d::CCObject const*>;
template <class K>
concept CocosDictionaryKey = std::same_as<K, int> || std::same_as<K, intptr_t> || std::same_as<K, gd::string> || std::same_as<K, std::string>;
/**
* A templated wrapper over CCArray, providing easy iteration and indexing.
* This will keep ownership of the given CCArray*.
*
* @tparam Type Pointer to a type that inherits CCObject.
*
* @example
* CCArrayExt<GameObject*> objects = PlayLayer::get()->m_objects;
* // Easy indexing, giving you the type you assigned
* GameObject* myObj = objects[2];
*
* // Easy iteration using C++ range-based for loops
* for (auto* obj : objects) {
* log::info("{}", obj->m_objectID);
* }
*/
template <CocosObjectPtr Type>
class CCArrayExt {
protected:
Ref<cocos2d::CCArray> m_arr;
using T = std::remove_pointer_t<_Type>;
using T = std::remove_pointer_t<Type>;
public:
using value_type = T;
@ -980,7 +1002,7 @@ namespace geode::cocos {
cocos2d::CCDictElement* m_ptr;
std::pair<K, T> operator*() {
if constexpr (std::is_same<K, std::string>::value) {
if constexpr (std::is_same_v<K, std::string> || std::is_same_v<K, gd::string>) {
return {m_ptr->getStrKey(), static_cast<T>(m_ptr->getObject())};
}
else {
@ -1027,42 +1049,41 @@ namespace geode::cocos {
}
};
template <typename K, typename T>
/**
* A templated wrapper over CCDictionary, providing easy iteration and indexing.
* This will keep ownership of the given CCDictionary*.
*
* @tparam Key Type of the key. MUST only be int or gd::string or std::string.
* @tparam ValuePtr Pointer to a type that inherits CCObject.
*
* @example
* CCDictionaryExt<std::string, GJGameLevel*> levels = getSomeDict();
* // Easy indexing, giving you the type you assigned
* GJGameLevel* myLvl = levels["Cube Adventures"];
*
* // Easy iteration using C++ range-based for loops
* for (auto [name, level] : levels) {
* log::info("{}: {}", name, level->m_levelID);
* }
*/
template <CocosDictionaryKey Key, CocosObjectPtr ValuePtr>
struct CCDictionaryExt {
protected:
cocos2d::CCDictionary* m_dict;
Ref<cocos2d::CCDictionary> m_dict;
public:
CCDictionaryExt() : m_dict(cocos2d::CCDictionary::create()) {
m_dict->retain();
}
CCDictionaryExt() : m_dict(cocos2d::CCDictionary::create()) {}
CCDictionaryExt(cocos2d::CCDictionary* dict) : m_dict(dict) {
m_dict->retain();
}
CCDictionaryExt(cocos2d::CCDictionary* dict) : m_dict(dict) {}
CCDictionaryExt(CCDictionaryExt const& d) : m_dict(d.m_dict) {
m_dict->retain();
}
CCDictionaryExt(CCDictionaryExt const& d) : m_dict(d.m_dict) {}
CCDictionaryExt(CCDictionaryExt&& d) : m_dict(d.m_dict) {
d.m_dict = nullptr;
}
~CCDictionaryExt() {
if (m_dict) m_dict->release();
}
CCDictionaryExt& operator=(cocos2d::CCDictionary* d) {
m_dict->release();
m_dict = d;
m_dict->retain();
return *this;
}
auto begin() {
return CCDictIterator<K, T*>(m_dict->m_pElements);
return CCDictIterator<Key, ValuePtr>(m_dict->m_pElements);
}
// do not use this
@ -1074,15 +1095,23 @@ namespace geode::cocos {
return m_dict->count();
}
auto operator[](K key) {
auto ret = static_cast<T*>(m_dict->objectForKey(key));
auto operator[](const Key& key) {
auto ret = static_cast<ValuePtr>(m_dict->objectForKey(key));
if (!ret) m_dict->setObject(cocos2d::CCNode::create(), key);
return CCDictEntry<K, T*>(key, m_dict);
return CCDictEntry<Key, ValuePtr>(key, m_dict);
}
size_t count(K key) {
return m_dict->allKeys(key)->count();
bool contains(const Key& key) {
return m_dict->objectForKey(key) != nullptr;
}
size_t count(const Key& key) {
return this->contains(key) ? 1 : 0;
}
cocos2d::CCDictionary* inner() {
return m_dict;
}
};
}

Binary file not shown.

View file

@ -52,7 +52,7 @@ bool CCNode::hasAncestor(CCNode* ancestor) {
CCArray* Layout::getNodesToPosition(CCNode* on) const {
auto arr = CCArray::create();
for (auto child : CCArrayExt<CCNode>(on->getChildren())) {
for (auto child : CCArrayExt<CCNode*>(on->getChildren())) {
if (!m_ignoreInvisibleChildren || child->isVisible()) {
arr->addObject(child);
}
@ -179,7 +179,7 @@ struct AxisLayout::Row : public CCObject {
void accountSpacers(Axis axis, float availableLength, float crossLength) {
std::vector<SpacerNode*> spacers;
for (auto& node : CCArrayExt<CCNode>(nodes)) {
for (auto& node : CCArrayExt<CCNode*>(nodes)) {
if (auto spacer = typeinfo_cast<SpacerNode*>(node)) {
spacers.push_back(spacer);
}
@ -259,7 +259,7 @@ bool AxisLayout::shouldAutoScale(AxisLayoutOptions const* opts) const {
float AxisLayout::minScaleForPrio(CCArray* nodes, int prio) const {
float min = AXISLAYOUT_DEFAULT_MIN_SCALE;
bool first = true;
for (auto node : CCArrayExt<CCNode>(nodes)) {
for (auto node : CCArrayExt<CCNode*>(nodes)) {
auto scale = optsMinScale(axisOpts(node));
if (first) {
min = scale;
@ -275,7 +275,7 @@ float AxisLayout::minScaleForPrio(CCArray* nodes, int prio) const {
float AxisLayout::maxScaleForPrio(CCArray* nodes, int prio) const {
float max = 1.f;
bool first = true;
for (auto node : CCArrayExt<CCNode>(nodes)) {
for (auto node : CCArrayExt<CCNode*>(nodes)) {
auto scale = optsMaxScale(axisOpts(node));
if (first) {
max = scale;
@ -490,7 +490,7 @@ void AxisLayout::tryFitLayout(
float crossSquishFactor = 0.f;
// make spacers have zero size so they don't affect spacing calculations
for (auto& node : CCArrayExt<CCNode>(nodes)) {
for (auto& node : CCArrayExt<CCNode*>(nodes)) {
if (auto spacer = typeinfo_cast<SpacerNode*>(node)) {
spacer->setContentSize(CCSizeZero);
}
@ -751,7 +751,7 @@ void AxisLayout::apply(CCNode* on) {
bool doAutoScale = false;
bool first = true;
for (auto node : CCArrayExt<CCNode>(nodes)) {
for (auto node : CCArrayExt<CCNode*>(nodes)) {
int prio = 0;
if (auto opts = axisOpts(node)) {
prio = opts->getScalePriority();

View file

@ -13,4 +13,4 @@ struct MyGameToolbox : Modify<MyGameToolbox, GameToolbox> {
CCEGLView::get()->setScissorInPoints(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
}
}
};
};

View file

@ -103,7 +103,7 @@ void CCNode::setID(std::string const& id) {
}
CCNode* CCNode::getChildByID(std::string const& id) {
for (auto child : CCArrayExt<CCNode>(m_pChildren)) {
for (auto child : CCArrayExt<CCNode*>(m_pChildren)) {
if (child->getID() == id) {
return child;
}
@ -115,7 +115,7 @@ CCNode* CCNode::getChildByIDRecursive(std::string const& id) {
if (auto child = this->getChildByID(id)) {
return child;
}
for (auto child : CCArrayExt<CCNode>(m_pChildren)) {
for (auto child : CCArrayExt<CCNode*>(m_pChildren)) {
if ((child = child->getChildByIDRecursive(id))) {
return child;
}
@ -131,7 +131,7 @@ void CCNode::removeChildByID(std::string const& id) {
void CCNode::setLayout(Layout* layout, bool apply, bool respectAnchor) {
if (respectAnchor && this->isIgnoreAnchorPointForPosition()) {
for (auto child : CCArrayExt<CCNode>(m_pChildren)) {
for (auto child : CCArrayExt<CCNode*>(m_pChildren)) {
child->setPosition(child->getPosition() + this->getScaledContentSize());
}
this->ignoreAnchorPointForPosition(false);

View file

@ -7,6 +7,7 @@ static constexpr int LOADER_VERSION_MAJOR = @PROJECT_VERSION_MAJOR@;
static constexpr int LOADER_VERSION_MINOR = @PROJECT_VERSION_MINOR@;
static constexpr int LOADER_VERSION_PATCH = @PROJECT_VERSION_PATCH@;
static constexpr const char* LOADER_COMMIT_HASH = "@GEODE_COMMIT_HASH@";
static constexpr const char* BINDINGS_COMMIT_HASH = "@GEODE_BINDINGS_COMMIT_HASH@";
static constexpr geode::VersionInfo LOADER_VERSION = {
@PROJECT_VERSION_MAJOR@,
@PROJECT_VERSION_MINOR@,

View file

@ -20,6 +20,7 @@ std::string crashlog::getDateString(bool filesafe) {
void crashlog::printGeodeInfo(std::stringstream& stream) {
stream << "Loader Version: " << Loader::get()->getVersion().toString() << "\n"
<< "Loader Commit: " << LOADER_COMMIT_HASH << "\n"
<< "Bindings Commit: " << BINDINGS_COMMIT_HASH << "\n"
<< "Installed mods: " << Loader::get()->getAllMods().size() << "\n"
<< "Problems: " << Loader::get()->getProblems().size() << "\n";
}

View file

@ -105,19 +105,19 @@ std::vector<std::string> Mod::getSettingKeys() const {
return m_impl->getSettingKeys();
}
bool Mod::hasSetting(std::string const& key) const {
bool Mod::hasSetting(std::string_view const key) const {
return m_impl->hasSetting(key);
}
std::optional<Setting> Mod::getSettingDefinition(std::string const& key) const {
std::optional<Setting> Mod::getSettingDefinition(std::string_view const key) const {
return m_impl->getSettingDefinition(key);
}
SettingValue* Mod::getSetting(std::string const& key) const {
SettingValue* Mod::getSetting(std::string_view const key) const {
return m_impl->getSetting(key);
}
void Mod::registerCustomSetting(std::string const& key, std::unique_ptr<SettingValue> value) {
void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value) {
return m_impl->registerCustomSetting(key, std::move(value));
}
@ -172,7 +172,7 @@ ModRequestedAction Mod::getRequestedAction() const {
return m_impl->getRequestedAction();
}
bool Mod::depends(std::string const& id) const {
bool Mod::depends(std::string_view const id) const {
return m_impl->depends(id);
}
@ -200,7 +200,7 @@ void Mod::setLoggingEnabled(bool enabled) {
m_impl->setLoggingEnabled(enabled);
}
bool Mod::hasSavedValue(std::string const& key) {
bool Mod::hasSavedValue(std::string_view const key) {
return this->getSaveContainer().contains(key);
}

View file

@ -258,13 +258,14 @@ void Mod::Impl::setupSettings() {
}
}
void Mod::Impl::registerCustomSetting(std::string const& key, std::unique_ptr<SettingValue> value) {
if (!m_settings.count(key)) {
void Mod::Impl::registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value) {
auto keystr = std::string(key);
if (!m_settings.count(keystr)) {
// load data
if (m_savedSettingsData.contains(key)) {
value->load(m_savedSettingsData[key]);
}
m_settings.emplace(key, std::move(value));
m_settings.emplace(keystr, std::move(value));
}
}
@ -280,7 +281,7 @@ std::vector<std::string> Mod::Impl::getSettingKeys() const {
return keys;
}
std::optional<Setting> Mod::Impl::getSettingDefinition(std::string const& key) const {
std::optional<Setting> Mod::Impl::getSettingDefinition(std::string_view const key) const {
for (auto& setting : m_metadata.getSettings()) {
if (setting.first == key) {
return setting.second;
@ -289,14 +290,16 @@ std::optional<Setting> Mod::Impl::getSettingDefinition(std::string const& key) c
return std::nullopt;
}
SettingValue* Mod::Impl::getSetting(std::string const& key) const {
if (m_settings.count(key)) {
return m_settings.at(key).get();
SettingValue* Mod::Impl::getSetting(std::string_view const key) const {
auto keystr = std::string(key);
if (m_settings.count(keystr)) {
return m_settings.at(keystr).get();
}
return nullptr;
}
bool Mod::Impl::hasSetting(std::string const& key) const {
bool Mod::Impl::hasSetting(std::string_view const key) const {
for (auto& setting : m_metadata.getSettings()) {
if (setting.first == key) {
return true;
@ -449,7 +452,7 @@ bool Mod::Impl::hasUnresolvedIncompatibilities() const {
return false;
}
bool Mod::Impl::depends(std::string const& id) const {
bool Mod::Impl::depends(std::string_view const id) const {
return utils::ranges::contains(m_metadata.getDependencies(), [id](ModMetadata::Dependency const& t) {
return t.id == id;
});
@ -585,13 +588,13 @@ Result<> Mod::Impl::unzipGeodeFile(ModMetadata metadata) {
if (ec) {
return Err("Unable to delete temp dir: " + ec.message());
}
(void)utils::file::createDirectoryAll(tempDir);
auto res = file::writeString(datePath, modifiedHash);
if (!res) {
log::warn("Failed to write modified date of geode zip: {}", res.unwrapErr());
}
GEODE_UNWRAP_INTO(auto unzip, file::Unzip::create(metadata.getPath()));
if (!unzip.hasEntry(metadata.getBinaryName())) {

View file

@ -109,10 +109,10 @@ namespace geode {
bool hasSettings() const;
std::vector<std::string> getSettingKeys() const;
bool hasSetting(std::string const& key) const;
std::optional<Setting> getSettingDefinition(std::string const& key) const;
SettingValue* getSetting(std::string const& key) const;
void registerCustomSetting(std::string const& key, std::unique_ptr<SettingValue> value);
bool hasSetting(std::string_view const key) const;
std::optional<Setting> getSettingDefinition(std::string_view const key) const;
SettingValue* getSetting(std::string_view const key) const;
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
std::vector<Hook*> getHooks() const;
Result<Hook*> addHook(Hook* hook);
@ -129,7 +129,7 @@ namespace geode {
// 1.3.0 additions
ModRequestedAction getRequestedAction() const;
bool depends(std::string const& id) const;
bool depends(std::string_view const id) const;
Result<> updateDependencies();
bool hasUnresolvedDependencies() const;
bool hasUnresolvedIncompatibilities() const;

View file

@ -44,20 +44,49 @@ CCPoint cocos::getMousePos() {
return CCPoint(0, 0);
}
namespace {
void clearJNIException() {
// this is a silly workaround to not crash when the method is not found.
// cocos figured this out half a year later...
auto vm = JniHelper::getJavaVM();
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_OK) {
env->ExceptionClear();
}
}
// jni breaks over multithreading, so the value is stored to avoid more jni calls
std::string s_savedBaseDir = "";
ghc::filesystem::path getBaseDir() {
std::string path = "/storage/emulated/0/Android/data/com.geode.launcher/files";
if (!s_savedBaseDir.empty()) {
return ghc::filesystem::path(s_savedBaseDir);
}
JniMethodInfo t;
if (JniHelper::getStaticMethodInfo(t, "com/geode/launcher/utils/GeodeUtils", "getBaseDirectory", "()Ljava/lang/String;")) {
jstring str = reinterpret_cast<jstring>(t.env->CallStaticObjectMethod(t.classID, t.methodID));
t.env->DeleteLocalRef(t.classID);
path = JniHelper::jstring2string(str);
t.env->DeleteLocalRef(str);
} else {
clearJNIException();
}
s_savedBaseDir = path;
return ghc::filesystem::path(path);
}
}
ghc::filesystem::path dirs::getGameDir() {
return ghc::filesystem::path(
"/storage/emulated/0/Android/data/com.geode.launcher/files/game"
// "/data/user/0/com.geode.launcher/files/"
/*CCFileUtils::sharedFileUtils()->getWritablePath().c_str()*/
);
return getBaseDir() / "game";
}
ghc::filesystem::path dirs::getSaveDir() {
return ghc::filesystem::path(
"/storage/emulated/0/Android/data/com.geode.launcher/files/save"
// "/data/user/0/com.geode.launcher/files/"
/*CCFileUtils::sharedFileUtils()->getWritablePath().c_str()*/
);
return getBaseDir() / "save";
}
ghc::filesystem::path dirs::getModRuntimeDir() {

View file

@ -0,0 +1,46 @@
#pragma once
#include <unordered_map>
#include <string_view>
#include <Windows.h>
// Original by absolute
// https://gist.github.com/absoIute/ebe5da42d118109a03632c9751d86e19
inline std::string_view detectGDVersion() {
static const std::unordered_map<uint32_t, std::string_view> buildMap = {
{ 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" },
};
auto hMod = GetModuleHandleA(NULL);
auto dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(hMod);
if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
auto ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<uintptr_t>(hMod) + dosHeader->e_lfanew);
if (ntHeader->Signature == IMAGE_NT_SIGNATURE) {
auto timeStamp = ntHeader->FileHeader.TimeDateStamp;
if (auto it = buildMap.find(timeStamp); it != buildMap.end())
return it->second;
}
}
return "";
}

View file

@ -4,6 +4,7 @@
#include "../load.hpp"
#include <Windows.h>
#include "VersionDetect.hpp"
#include "loader/LoaderImpl.hpp"
using namespace geode::prelude;
@ -41,8 +42,10 @@ int WINAPI gdMainHook(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmd
return reinterpret_cast<decltype(&wWinMain)>(mainTrampolineAddr)(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
bool loadGeode() {
// TODO: add version check or something
std::string loadGeode() {
if (detectGDVersion() != GEODE_STR(GEODE_GD_VERSION)) {
return "GD version mismatch, not loading geode.";
}
auto process = GetCurrentProcess();
@ -52,7 +55,7 @@ bool loadGeode() {
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE
);
static constexpr uintptr_t MAIN_OFFSET = 0x3ba7d0;
static constexpr uintptr_t MAIN_OFFSET = 0x3bdfd0;
auto patchAddr = geode::base::get() + MAIN_OFFSET;
constexpr size_t patchSize = 6;
@ -86,10 +89,10 @@ bool loadGeode() {
DWORD oldProtect;
if (!VirtualProtectEx(process, reinterpret_cast<void*>(patchAddr), patchSize, PAGE_EXECUTE_READWRITE, &oldProtect))
return false;
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);
return true;
return "";
}
DWORD WINAPI upgradeThread(void*) {
@ -156,9 +159,9 @@ BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID) {
}
else if (oldBootstrapperExists)
CreateThread(nullptr, 0, upgradeThread, nullptr, 0, nullptr);
else if (!loadGeode()) {
earlyError("There was an unknown error hooking the GD main function.");
return FALSE;
else if (auto error = loadGeode(); !error.empty()) {
earlyError(error);
return TRUE;
}
}
catch(...) {

View file

@ -75,8 +75,17 @@ std::string utils::clipboard::read() {
}
bool utils::file::openFolder(ghc::filesystem::path const& path) {
ShellExecuteA(NULL, "open", path.string().c_str(), NULL, NULL, SW_SHOWDEFAULT);
return true;
auto success = false;
if (CoInitializeEx(nullptr, COINIT_MULTITHREADED) == S_OK) {
if (auto id = ILCreateFromPathW(path.wstring().c_str())) {
if (SHOpenFolderAndSelectItems(id, 0, nullptr, 0) == S_OK) {
success = true;
}
ILFree(id);
}
CoUninitialize();
}
return success;
}
Result<ghc::filesystem::path> utils::file::pickFile(

View file

@ -1,27 +0,0 @@
#include "HookListLayer.hpp"
#include <Geode/binding/GJListLayer.hpp>
bool HookListLayer::init(Mod* mod) {
if (!GJDropDownLayer::init("Hooks", 220.f)) return false;
auto winSize = CCDirector::sharedDirector()->getWinSize();
auto hooks = CCArray::create();
for (auto const& hook : mod->getHooks()) {
hooks->addObject(new HookItem(hook));
}
m_listLayer->m_listView = HookListView::create(hooks, mod, 356.f, 220.f);
m_listLayer->addChild(m_listLayer->m_listView);
return true;
}
HookListLayer* HookListLayer::create(Mod* mod) {
auto ret = new HookListLayer;
if (ret && ret->init(mod)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}

View file

@ -1,13 +0,0 @@
#pragma once
#include "HookListView.hpp"
#include <Geode/binding/GJDropDownLayer.hpp>
class HookListLayer : public GJDropDownLayer {
protected:
bool init(Mod* mod);
public:
static HookListLayer* create(Mod* mod);
};

View file

@ -1,135 +0,0 @@
#include "HookListView.hpp"
#include <Geode/binding/StatsCell.hpp>
#include <Geode/binding/TableView.hpp>
#include <Geode/binding/FLAlertLayer.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
#include <Geode/utils/casts.hpp>
#include <Geode/loader/Mod.hpp>
HookCell::HookCell(char const* name, CCSize size) : TableViewCell(name, size.width, size.height) {}
void HookCell::draw() {
reinterpret_cast<StatsCell*>(this)->StatsCell::draw();
}
void HookCell::updateBGColor(int index) {
if (index & 1) m_backgroundLayer->setColor(ccc3(0xc2, 0x72, 0x3e));
else m_backgroundLayer->setColor(ccc3(0xa1, 0x58, 0x2c));
m_backgroundLayer->setOpacity(0xff);
}
void HookCell::onEnable(CCObject* pSender) {
auto toggle = as<CCMenuItemToggler*>(pSender);
if (!toggle->isToggled()) {
auto res = m_mod->enableHook(m_hook);
if (!res) {
FLAlertLayer::create(
nullptr, "Error Enabling Hook", std::string(res.unwrapErr()), "OK", nullptr, 280.f
)
->show();
}
}
else {
auto res = m_mod->disableHook(m_hook);
if (!res) {
FLAlertLayer::create(
nullptr, "Error Disabling Hook", std::string(res.unwrapErr()), "OK", nullptr, 280.f
)
->show();
}
}
toggle->toggle(!m_hook->isEnabled());
}
void HookCell::loadFromHook(Hook* hook, Mod* Mod) {
m_hook = hook;
m_mod = Mod;
m_mainLayer->setVisible(true);
m_backgroundLayer->setOpacity(255);
auto menu = CCMenu::create();
menu->setPosition(m_width - m_height, m_height / 2);
m_mainLayer->addChild(menu);
auto enableBtn =
CCMenuItemToggler::createWithStandardSprites(this, menu_selector(HookCell::onEnable), .6f);
enableBtn->setPosition(0, 0);
enableBtn->toggle(hook->isEnabled());
menu->addChild(enableBtn);
std::stringstream moduleName;
// #ifdef GEODE_IS_WINDOWS // add other platforms?
// HMODULE module;
// if (GetModuleHandleExA(
// GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
// GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
// as<LPCSTR>(hook->getAddress()),
// &module
// )) {
// addr -= as<uintptr_t>(module);
// char name[MAX_PATH];
// if (GetModuleFileNameA(
// module, name, sizeof name
// )) {
// auto fileName = std::filesystem::path(name).stem();
// moduleName << fileName.string() << " + ";
// }
// }
// #endif
if (hook->getDisplayName() != "") moduleName << hook->getDisplayName();
else moduleName << "0x" << std::hex << hook->getAddress();
auto label = CCLabelBMFont::create(moduleName.str().c_str(), "chatFont.fnt");
label->setPosition(m_height / 2, m_height / 2);
label->setScale(.7f);
label->setAnchorPoint({ .0f, .5f });
m_mainLayer->addChild(label);
}
HookCell* HookCell::create(char const* key, CCSize size) {
auto pRet = new HookCell(key, size);
if (pRet) {
return pRet;
}
CC_SAFE_DELETE(pRet);
return nullptr;
}
void HookListView::setupList(float) {
m_itemSeparation = 30.0f;
if (!m_entries->count()) return;
m_tableView->reloadData();
if (m_entries->count() == 1)
m_tableView->moveToTopWithOffset(m_itemSeparation);
m_tableView->moveToTop();
}
TableViewCell* HookListView::getListCell(char const* key) {
return HookCell::create(key, CCSize { m_width, m_itemSeparation });
}
void HookListView::loadCell(TableViewCell* cell, int index) {
as<HookCell*>(cell)->loadFromHook(
as<HookItem*>(m_entries->objectAtIndex(index))->m_hook, m_mod
);
as<HookCell*>(cell)->updateBGColor(index);
}
HookListView* HookListView::create(CCArray* hooks, Mod* Mod, float width, float height) {
auto pRet = new HookListView;
if (pRet) {
pRet->m_mod = Mod;
if (pRet->init(hooks, kBoomListType_Hooks, width, height)) {
pRet->autorelease();
return pRet;
}
}
CC_SAFE_DELETE(pRet);
return nullptr;
}

View file

@ -1,46 +0,0 @@
#pragma once
#include <Geode/binding/CustomListView.hpp>
#include <Geode/binding/TableViewCell.hpp>
using namespace geode::prelude;
static constexpr const BoomListType kBoomListType_Hooks = static_cast<BoomListType>(0x358);
struct HookItem : public CCObject {
Hook* m_hook;
HookItem(Hook* h) : m_hook(h) {
this->autorelease();
}
};
class HookCell : public TableViewCell {
protected:
Mod* m_mod;
Hook* m_hook;
HookCell(char const* name, CCSize size);
void draw() override;
void onEnable(CCObject*);
public:
void updateBGColor(int index);
void loadFromHook(Hook*, Mod*);
static HookCell* create(char const* key, CCSize size);
};
class HookListView : public CustomListView {
protected:
Mod* m_mod;
void setupList(float) override;
TableViewCell* getListCell(char const* key) override;
void loadCell(TableViewCell* cell, int index) override;
public:
static HookListView* create(CCArray* hooks, Mod* Mod, float width, float height);
};

View file

@ -1,45 +0,0 @@
#include "HotReloadLayer.hpp"
bool HotReloadLayer::init(std::string const& name) {
if (!CCLayer::init()) return false;
auto winSize = CCDirector::sharedDirector()->getWinSize();
auto bg = CCSprite::create("GJ_gradientBG.png");
auto bgSize = bg->getTextureRect().size;
bg->setAnchorPoint({ 0.0f, 0.0f });
bg->setScaleX((winSize.width + 10.0f) / bgSize.width);
bg->setScaleY((winSize.height + 10.0f) / bgSize.height);
bg->setPosition({ -5.0f, -5.0f });
bg->setColor({ 0, 102, 255 });
this->addChild(bg);
std::string text = "Reloading " + name + "...";
auto label = CCLabelBMFont::create(text.c_str(), "bigFont.fnt");
label->setPosition(this->getContentSize() / 2);
label->setZOrder(1);
label->setScale(.5f);
this->addChild(label);
return true;
}
HotReloadLayer* HotReloadLayer::create(std::string const& name) {
auto ret = new HotReloadLayer;
if (ret && ret->init(name)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
HotReloadLayer* HotReloadLayer::scene(std::string const& name) {
auto scene = CCScene::create();
auto layer = HotReloadLayer::create(name);
scene->addChild(layer);
CCDirector::sharedDirector()->replaceScene(scene);
return layer;
}

View file

@ -1,14 +0,0 @@
#pragma once
#include <cocos2d.h>
using namespace geode::prelude;
class HotReloadLayer : public CCLayer {
protected:
bool init(std::string const& name);
public:
static HotReloadLayer* create(std::string const& name);
static HotReloadLayer* scene(std::string const& name);
};

View file

@ -1,6 +1,5 @@
#include "ModInfoPopup.hpp"
#include "../dev/HookListLayer.hpp"
#include "../list/ModListLayer.hpp"
#include "../settings/ModSettingsPopup.hpp"
#include <Geode/loader/Dirs.hpp>
@ -523,11 +522,17 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
m_mainLayer->addChild(m_minorVersionLabel);
}
}
} else {
auto* label = CCLabelBMFont::create(LOADER_COMMIT_HASH, "chatFont.fnt");
}
if (mod == Mod::get()) {
// we're showing the internal geode mod :-)
auto* label = CCLabelBMFont::create(
fmt::format("Bindings: {}\nLoader: {}", BINDINGS_COMMIT_HASH, LOADER_COMMIT_HASH).c_str(),
"chatFont.fnt"
);
label->setAlignment(kCCTextAlignmentRight);
label->setAnchorPoint(ccp(1, 0));
label->setScale(0.775f);
label->setPosition(winSize.width - 1.f, 1.f);
label->setScale(0.6f);
label->setPosition(winSize.width - 3.f, 3.f);
label->setOpacity(89);
m_mainLayer->addChild(label);
}

View file

@ -578,7 +578,7 @@ void ModListLayer::reloadList(bool keepScroll, std::optional<ModListQuery> const
}
void ModListLayer::updateAllStates() {
for (auto cell : CCArrayExt<GenericListCell>(
for (auto cell : CCArrayExt<GenericListCell*>(
m_list->m_listView->m_tableView->m_cellArray
)) {
static_cast<ModListCell*>(cell->getChildByID("mod-list-cell"))->updateState();

View file

@ -58,7 +58,7 @@ public:
// so that's why based MDContentLayer expects itself
// to have a CCMenu :-)
if (m_content) {
for (auto child : CCArrayExt<CCNode>(m_content->getChildren())) {
for (auto child : CCArrayExt<CCNode*>(m_content->getChildren())) {
auto y = this->getPositionY() + child->getPositionY();
child->setVisible(
!((m_content->getContentSize().height < y) ||

View file

@ -35,7 +35,7 @@ void SceneManager::forget(CCNode* node) {
}
void SceneManager::willSwitchToScene(CCScene* scene) {
for (auto node : CCArrayExt<CCNode>(m_persistedNodes)) {
for (auto node : CCArrayExt<CCNode*>(m_persistedNodes)) {
// no cleanup in order to keep actions running
node->removeFromParentAndCleanup(false);
scene->addChild(node);

View file

@ -18,7 +18,7 @@ void GenericContentLayer::setPosition(CCPoint const& pos) {
// all be TableViewCells
CCLayerColor::setPosition(pos);
for (auto child : CCArrayExt<CCNode>(m_pChildren)) {
for (auto child : CCArrayExt<CCNode*>(m_pChildren)) {
auto y = this->getPositionY() + child->getPositionY();
child->setVisible(!((m_obContentSize.height < y) || (y < -child->getContentSize().height)));
}

View file

@ -246,7 +246,7 @@ CCNode* TextRenderer::end(
case TextAlignment::End: padY = std::max(m_size.height - renderedHeight, 0.f); break;
}
// adjust child positions
for (auto child : CCArrayExt<CCNode>(m_target->getChildren())) {
for (auto child : CCArrayExt<CCNode*>(m_target->getChildren())) {
child->setPosition(
child->getPositionX() + padX,
child->getPositionY() + m_target->getContentSize().height - coverage.size.height -

View file

@ -303,7 +303,7 @@ CCRect geode::cocos::calculateNodeCoverage(std::vector<CCNode*> const& nodes) {
CCRect geode::cocos::calculateNodeCoverage(CCArray* nodes) {
CCRect coverage;
for (auto child : CCArrayExt<CCNode>(nodes)) {
for (auto child : CCArrayExt<CCNode*>(nodes)) {
auto pos = child->getPosition() - child->getScaledContentSize() * child->getAnchorPoint();
auto csize = child->getPosition() +
child->getScaledContentSize() * (CCPoint{1.f, 1.f} - child->getAnchorPoint());

View file

@ -1,6 +1,6 @@
# if(NOT GEODE_DONT_BUILD_TEST_MODS)
# add_subdirectory(dependency)
# add_subdirectory(main)
# endif()
if(NOT GEODE_DONT_BUILD_TEST_MODS)
add_subdirectory(dependency)
add_subdirectory(main)
endif()
add_subdirectory(members)

View file

@ -1,6 +1,6 @@
#include "Common.hpp"
#ifdef GEODE_IS_WINDOWS
#if 0
using namespace cocos2d;