mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-14 19:15:05 -05:00
separate mod and impl
This commit is contained in:
parent
950db7e474
commit
5666c8f356
33 changed files with 1245 additions and 1024 deletions
11
entry.cpp
11
entry.cpp
|
@ -1,8 +1,7 @@
|
|||
// included by default in every geode project
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
||||
GEODE_API void GEODE_CALL geode_implicit_load(geode::Mod* m) {
|
||||
// geode::Mod::setSharedMod(m);
|
||||
// geode::Loader::get()->dispatchScheduledFunctions(m);
|
||||
}
|
||||
namespace {
|
||||
// to make sure the instance is set into the sharedMod<> in load time
|
||||
static auto mod = geode::getMod();
|
||||
}
|
|
@ -25,6 +25,7 @@ THE SOFTWARE.
|
|||
#define __SUPPORT_ZIPUTILS_H__
|
||||
|
||||
#include <string>
|
||||
#include <ghc/filesystem.hpp>
|
||||
#include "../../platform/CCPlatformDefine.h"
|
||||
#include "../../platform/CCPlatformConfig.h"
|
||||
#include "../../include/ccMacros.h"
|
||||
|
|
|
@ -97,8 +97,8 @@ namespace geode {
|
|||
protected:
|
||||
Mod* m_owner;
|
||||
void* m_address;
|
||||
byte_array m_original;
|
||||
byte_array m_patch;
|
||||
ByteVector m_original;
|
||||
ByteVector m_patch;
|
||||
bool m_applied;
|
||||
|
||||
// Only allow friend classes to create
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace geode {
|
|||
std::string reason;
|
||||
};
|
||||
|
||||
class LoaderImpl;
|
||||
class InternalLoader;
|
||||
|
||||
class GEODE_DLL Loader {
|
||||
private:
|
||||
|
@ -79,7 +79,7 @@ namespace geode {
|
|||
|
||||
bool didLastLaunchCrash() const;
|
||||
|
||||
friend class LoaderImpl;
|
||||
friend class InternalLoader;
|
||||
|
||||
friend Mod* takeNextLoaderMod();
|
||||
};
|
||||
|
|
|
@ -33,87 +33,17 @@ namespace geode {
|
|||
|
||||
GEODE_HIDDEN Mod* takeNextLoaderMod();
|
||||
|
||||
class InternalMod;
|
||||
|
||||
/**
|
||||
* @class Mod
|
||||
* Represents a Mod ingame.
|
||||
* @abstract
|
||||
*/
|
||||
class GEODE_DLL Mod {
|
||||
protected:
|
||||
/**
|
||||
* Mod info
|
||||
*/
|
||||
ModInfo m_info;
|
||||
/**
|
||||
* Platform-specific info
|
||||
*/
|
||||
PlatformInfo* m_platformInfo = nullptr;
|
||||
/**
|
||||
* Hooks owned by this mod
|
||||
*/
|
||||
std::vector<Hook*> m_hooks;
|
||||
/**
|
||||
* Patches owned by this mod
|
||||
*/
|
||||
std::vector<Patch*> m_patches;
|
||||
/**
|
||||
* Whether the mod is enabled or not
|
||||
*/
|
||||
bool m_enabled = false;
|
||||
/**
|
||||
* Whether the mod binary is loaded or not
|
||||
*/
|
||||
bool m_binaryLoaded = false;
|
||||
/**
|
||||
* Mod temp directory name
|
||||
*/
|
||||
ghc::filesystem::path m_tempDirName;
|
||||
/**
|
||||
* Mod save directory name
|
||||
*/
|
||||
ghc::filesystem::path m_saveDirPath;
|
||||
/**
|
||||
* Pointers to mods that depend on
|
||||
* this Mod. Makes it possible to
|
||||
* enable / disable them automatically,
|
||||
* when their dependency is disabled.
|
||||
*/
|
||||
std::vector<Mod*> m_parentDependencies;
|
||||
decltype(geode_implicit_load)* m_implicitLoadFunc;
|
||||
/**
|
||||
* Saved values
|
||||
*/
|
||||
nlohmann::json m_saved;
|
||||
/**
|
||||
* Setting values
|
||||
*/
|
||||
std::unordered_map<std::string, std::unique_ptr<SettingValue>> m_settings;
|
||||
/**
|
||||
* Settings save data. Stored for efficient loading of custom settings
|
||||
*/
|
||||
nlohmann::json m_savedSettingsData;
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
/**
|
||||
* Load the platform binary
|
||||
*/
|
||||
Result<> loadPlatformBinary();
|
||||
Result<> unloadPlatformBinary();
|
||||
Result<> createTempDir();
|
||||
|
||||
void setupSettings();
|
||||
|
||||
// no copying
|
||||
Mod(Mod const&) = delete;
|
||||
Mod operator=(Mod const&) = delete;
|
||||
|
||||
/**
|
||||
* Protected constructor/destructor
|
||||
*/
|
||||
Mod() = delete;
|
||||
Mod(ModInfo const& info);
|
||||
virtual ~Mod();
|
||||
|
||||
friend class ::InternalMod;
|
||||
friend class Loader;
|
||||
friend struct ModInfo;
|
||||
|
||||
|
@ -129,8 +59,15 @@ namespace geode {
|
|||
friend void GEODE_CALL ::geode_implicit_load(Mod*);
|
||||
|
||||
public:
|
||||
// TODO: impl pimpl
|
||||
Result<> setup();
|
||||
// no copying
|
||||
Mod(Mod const&) = delete;
|
||||
Mod operator=(Mod const&) = delete;
|
||||
|
||||
// Protected constructor/destructor
|
||||
Mod() = delete;
|
||||
Mod(ModInfo const& info);
|
||||
~Mod();
|
||||
|
||||
|
||||
std::string getID() const;
|
||||
std::string getName() const;
|
||||
|
@ -170,6 +107,8 @@ namespace geode {
|
|||
std::unique_ptr<SettingValue> value
|
||||
);
|
||||
|
||||
nlohmann::json& getSaveContainer();
|
||||
|
||||
template <class T>
|
||||
T getSettingValue(std::string const& key) const {
|
||||
if (auto sett = this->getSetting(key)) {
|
||||
|
@ -190,10 +129,11 @@ namespace geode {
|
|||
|
||||
template <class T>
|
||||
T getSavedValue(std::string const& key) {
|
||||
if (m_saved.count(key)) {
|
||||
auto& saved = this->getSaveContainer();
|
||||
if (saved.count(key)) {
|
||||
try {
|
||||
// json -> T may fail
|
||||
return m_saved.at(key);
|
||||
return saved.at(key);
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
|
@ -203,15 +143,16 @@ namespace geode {
|
|||
|
||||
template <class T>
|
||||
T getSavedValue(std::string const& key, T const& defaultValue) {
|
||||
if (m_saved.count(key)) {
|
||||
auto& saved = this->getSaveContainer();
|
||||
if (saved.count(key)) {
|
||||
try {
|
||||
// json -> T may fail
|
||||
return m_saved.at(key);
|
||||
return saved.at(key);
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
}
|
||||
m_saved[key] = defaultValue;
|
||||
saved[key] = defaultValue;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
@ -224,8 +165,9 @@ namespace geode {
|
|||
*/
|
||||
template<class T>
|
||||
T setSavedValue(std::string const& key, T const& value) {
|
||||
auto& saved = this->getSaveContainer();
|
||||
auto old = this->getSavedValue<T>(key);
|
||||
m_saved[key] = value;
|
||||
saved[key] = value;
|
||||
return old;
|
||||
}
|
||||
|
||||
|
@ -313,7 +255,7 @@ namespace geode {
|
|||
* @returns Successful result on success,
|
||||
* errorful result with info on error
|
||||
*/
|
||||
Result<Patch*> patch(void* address, byte_array data);
|
||||
Result<Patch*> patch(void* address, ByteVector const& data);
|
||||
|
||||
/**
|
||||
* Remove a patch owned by this Mod
|
||||
|
@ -401,6 +343,8 @@ namespace geode {
|
|||
* @note For IPC
|
||||
*/
|
||||
ModJson getRuntimeInfo() const;
|
||||
|
||||
friend class InternalMod;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
class InternalMod;
|
||||
|
||||
namespace geode {
|
||||
/**
|
||||
* Describes the severity of the log
|
||||
|
|
|
@ -18,10 +18,10 @@ namespace ghc::filesystem {
|
|||
namespace geode::utils::file {
|
||||
GEODE_DLL Result<std::string> readString(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<nlohmann::json> readJson(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<byte_array> readBinary(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<ByteVector> readBinary(ghc::filesystem::path const& path);
|
||||
|
||||
GEODE_DLL Result<> writeString(ghc::filesystem::path const& path, std::string const& data);
|
||||
GEODE_DLL Result<> writeBinary(ghc::filesystem::path const& path, byte_array const& data);
|
||||
GEODE_DLL Result<> writeBinary(ghc::filesystem::path const& path, ByteVector const& data);
|
||||
|
||||
GEODE_DLL Result<> createDirectory(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<> createDirectoryAll(ghc::filesystem::path const& path);
|
||||
|
@ -62,7 +62,7 @@ namespace geode::utils::file {
|
|||
/**
|
||||
* Add an entry to the zip with data
|
||||
*/
|
||||
Result<> add(Path const& entry, byte_array const& data);
|
||||
Result<> add(Path const& entry, ByteVector const& data);
|
||||
/**
|
||||
* Add an entry to the zip with string data
|
||||
*/
|
||||
|
@ -128,7 +128,7 @@ namespace geode::utils::file {
|
|||
* Extract entry to memory
|
||||
* @param name Entry path in zip
|
||||
*/
|
||||
Result<byte_array> extract(Path const& name);
|
||||
Result<ByteVector> extract(Path const& name);
|
||||
/**
|
||||
* Extract entry to file
|
||||
* @param name Entry path in zip
|
||||
|
|
|
@ -20,11 +20,11 @@ struct std::hash<ghc::filesystem::path> {
|
|||
};
|
||||
|
||||
namespace geode {
|
||||
using byte_array = std::vector<uint8_t>;
|
||||
using ByteVector = std::vector<uint8_t>;
|
||||
|
||||
template <typename T>
|
||||
byte_array to_byte_array(T const& a) {
|
||||
byte_array out;
|
||||
ByteVector to_byte_array(T const& a) {
|
||||
ByteVector out;
|
||||
out.resize(sizeof(T));
|
||||
std::memcpy(out.data(), &a, sizeof(T));
|
||||
return out;
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace geode::utils::web {
|
|||
* @param url URL to fetch
|
||||
* @returns Returned data as bytes, or error on error
|
||||
*/
|
||||
GEODE_DLL Result<byte_array> fetchBytes(std::string const& url);
|
||||
GEODE_DLL Result<ByteVector> fetchBytes(std::string const& url);
|
||||
|
||||
/**
|
||||
* Synchronously fetch data from the internet
|
||||
|
@ -66,7 +66,7 @@ namespace geode::utils::web {
|
|||
|
||||
using AsyncProgress = std::function<void(SentAsyncWebRequest&, double, double)>;
|
||||
using AsyncExpect = std::function<void(std::string const&)>;
|
||||
using AsyncThen = std::function<void(SentAsyncWebRequest&, byte_array const&)>;
|
||||
using AsyncThen = std::function<void(SentAsyncWebRequest&, ByteVector const&)>;
|
||||
using AsyncCancelled = std::function<void(SentAsyncWebRequest&)>;
|
||||
|
||||
/**
|
||||
|
@ -110,7 +110,7 @@ namespace geode::utils::web {
|
|||
using SentAsyncWebRequestHandle = std::shared_ptr<SentAsyncWebRequest>;
|
||||
|
||||
template <class T>
|
||||
using DataConverter = Result<T> (*)(byte_array const&);
|
||||
using DataConverter = Result<T> (*)(ByteVector const&);
|
||||
|
||||
/**
|
||||
* An asynchronous, thread-safe web request. Downloads data from the
|
||||
|
@ -272,7 +272,7 @@ namespace geode::utils::web {
|
|||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
* after the download is finished
|
||||
*/
|
||||
AsyncWebResult<byte_array> bytes();
|
||||
AsyncWebResult<ByteVector> bytes();
|
||||
/**
|
||||
* Download into memory as JSON
|
||||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
|
@ -298,7 +298,7 @@ namespace geode::utils::web {
|
|||
template <class T>
|
||||
AsyncWebRequest& AsyncWebResult<T>::then(std::function<void(T)> handle) {
|
||||
m_request.m_then = [converter = m_converter,
|
||||
handle](SentAsyncWebRequest& req, byte_array const& arr) {
|
||||
handle](SentAsyncWebRequest& req, ByteVector const& arr) {
|
||||
auto conv = converter(arr);
|
||||
if (conv) {
|
||||
handle(conv.unwrap());
|
||||
|
@ -313,7 +313,7 @@ namespace geode::utils::web {
|
|||
template <class T>
|
||||
AsyncWebRequest& AsyncWebResult<T>::then(std::function<void(SentAsyncWebRequest&, T)> handle) {
|
||||
m_request.m_then = [converter = m_converter,
|
||||
handle](SentAsyncWebRequest& req, byte_array const& arr) {
|
||||
handle](SentAsyncWebRequest& req, ByteVector const& arr) {
|
||||
auto conv = converter(arr);
|
||||
if (conv) {
|
||||
handle(req, conv.value());
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <Geode/utils/cocos.hpp>
|
||||
#include <array>
|
||||
#include <fmt/format.h>
|
||||
#include <loader/LoaderImpl.hpp>
|
||||
#include <loader/InternalLoader.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
|
@ -35,7 +35,7 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
|||
));
|
||||
|
||||
// verify loader resources
|
||||
if (!LoaderImpl::get()->verifyLoaderResources()) {
|
||||
if (!InternalLoader::get()->verifyLoaderResources()) {
|
||||
m_fields->m_updatingResources = true;
|
||||
this->setUpdateText("Downloading Resources");
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
|||
this->loadAssets();
|
||||
},
|
||||
[&](UpdateFailed const& error) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
InternalLoader::get()->platformMessageBox(
|
||||
"Error updating resources",
|
||||
"Unable to update Geode resources: " +
|
||||
error + ".\n"
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <Geode/ui/Notification.hpp>
|
||||
#include <Geode/ui/Popup.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <InternalMod.hpp>
|
||||
#include <loader/InternalMod.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "../loader/LoaderImpl.hpp"
|
||||
#include <loader/InternalLoader.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
|
@ -6,7 +6,7 @@ USE_GEODE_NAMESPACE();
|
|||
|
||||
struct FunctionQueue : Modify<FunctionQueue, CCScheduler> {
|
||||
void update(float dt) {
|
||||
LoaderImpl::get()->executeGDThreadQueue();
|
||||
InternalLoader::get()->executeGDThreadQueue();
|
||||
return CCScheduler::update(dt);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
#include "InternalMod.hpp"
|
||||
|
||||
#include "about.hpp"
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <LoaderImpl.hpp>
|
||||
|
||||
static constexpr char const* SUPPORT_INFO = R"MD(
|
||||
**Geode** is funded through your gracious <cy>**donations**</c>!
|
||||
You can support our work by sending <cp>**catgirl pictures**</c> to [HJfod](user:104257) :))
|
||||
)MD";
|
||||
|
||||
static ModInfo getInternalModInfo() {
|
||||
try {
|
||||
auto json = ModJson::parse(LOADER_MOD_JSON);
|
||||
auto infoRes = ModInfo::create(json);
|
||||
if (infoRes.isErr()) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
"Fatal Internal Error",
|
||||
"Unable to parse loader mod.json: \"" + infoRes.unwrapErr() +
|
||||
"\"\n"
|
||||
"This is a fatal internal error in the loader, please "
|
||||
"contact Geode developers immediately!"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
auto info = infoRes.unwrap();
|
||||
info.details = LOADER_ABOUT_MD;
|
||||
info.supportInfo = SUPPORT_INFO;
|
||||
info.supportsDisabling = false;
|
||||
return info;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
"Fatal Internal Error",
|
||||
"Unable to parse loader mod.json: \"" + std::string(e.what()) +
|
||||
"\"\n"
|
||||
"This is a fatal internal error in the loader, please "
|
||||
"contact Geode developers immediately!"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Mod* InternalMod::get() {
|
||||
auto& mod = Mod::sharedMod<>;
|
||||
if (mod) return mod;
|
||||
|
||||
mod = new Mod(getInternalModInfo());
|
||||
|
||||
auto setupRes = mod->setup();
|
||||
if (!setupRes) {
|
||||
log::error("Failed to setup internal mod! ({})", setupRes.unwrapErr());
|
||||
return mod;
|
||||
}
|
||||
return mod;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
class InternalMod;
|
||||
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
class InternalMod : public Mod {
|
||||
public:
|
||||
static Mod* get();
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#include "LoaderImpl.hpp"
|
||||
#include "InternalLoader.hpp"
|
||||
#include <cocos2d.h>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/IPC.hpp>
|
||||
|
@ -10,7 +10,7 @@
|
|||
#include <Geode/utils/map.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <Geode/utils/web.hpp>
|
||||
#include <InternalMod.hpp>
|
||||
#include "InternalMod.hpp"
|
||||
#include <about.hpp>
|
||||
#include <crashlog.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
Loader::Impl* LoaderImpl::get() {
|
||||
Loader::Impl* InternalLoader::get() {
|
||||
return Loader::get()->m_impl.get();
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ Result<Mod*> Loader::Impl::loadModFromInfo(ModInfo const& info) {
|
|||
|
||||
// create Mod instance
|
||||
auto mod = new Mod(info);
|
||||
auto setupRes = mod->setup();
|
||||
auto setupRes = mod->m_impl->setup();
|
||||
if (!setupRes) {
|
||||
return Err(fmt::format(
|
||||
"Unable to setup mod '{}': {}",
|
||||
|
@ -199,7 +199,7 @@ Result<Mod*> Loader::Impl::loadModFromInfo(ModInfo const& info) {
|
|||
}
|
||||
|
||||
m_mods.insert({ info.id, mod });
|
||||
mod->m_enabled = InternalMod::get()->getSavedValue<bool>(
|
||||
mod->m_impl->m_enabled = InternalMod::get()->getSavedValue<bool>(
|
||||
"should-load-" + info.id, true
|
||||
);
|
||||
|
||||
|
@ -255,7 +255,7 @@ Mod* Loader::Impl::getLoadedMod(std::string const& id) const {
|
|||
}
|
||||
|
||||
void Loader::Impl::updateModResources(Mod* mod) {
|
||||
if (!mod->m_info.spritesheets.size()) {
|
||||
if (!mod->m_impl->m_info.spritesheets.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -264,7 +264,7 @@ void Loader::Impl::updateModResources(Mod* mod) {
|
|||
log::debug("Adding resources for {}", mod->getID());
|
||||
|
||||
// add spritesheets
|
||||
for (auto const& sheet : mod->m_info.spritesheets) {
|
||||
for (auto const& sheet : mod->m_impl->m_info.spritesheets) {
|
||||
log::debug("Adding sheet {}", sheet);
|
||||
auto png = sheet + ".png";
|
||||
auto plist = sheet + ".plist";
|
||||
|
@ -274,7 +274,7 @@ void Loader::Impl::updateModResources(Mod* mod) {
|
|||
plist == std::string(ccfu->fullPathForFilename(plist.c_str(), false))) {
|
||||
log::warn(
|
||||
"The resource dir of \"{}\" is missing \"{}\" png and/or plist files",
|
||||
mod->m_info.id, sheet
|
||||
mod->m_impl->m_info.id, sheet
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
@ -309,7 +309,7 @@ void Loader::Impl::loadModsFromDirectory(
|
|||
}
|
||||
// skip this entry if it's already loaded
|
||||
if (map::contains<std::string, Mod*>(m_mods, [entry](Mod* p) -> bool {
|
||||
return p->m_info.path == entry.path();
|
||||
return p->m_impl->m_info.path == entry.path();
|
||||
})) {
|
||||
continue;
|
||||
}
|
134
loader/src/loader/InternalLoader.hpp
Normal file
134
loader/src/loader/InternalLoader.hpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
#include "FileWatcher.hpp"
|
||||
|
||||
#include <Geode/external/json/json.hpp>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Index.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/utils/Result.hpp>
|
||||
#include <Geode/utils/map.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include "InternalMod.hpp"
|
||||
#include <about.hpp>
|
||||
#include <crashlog.hpp>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
// TODO: Find a file convention for impl headers
|
||||
namespace geode {
|
||||
struct ResourceDownloadEvent : public Event {
|
||||
const UpdateStatus status;
|
||||
ResourceDownloadEvent(UpdateStatus const& status);
|
||||
};
|
||||
|
||||
class GEODE_DLL ResourceDownloadFilter : public EventFilter<ResourceDownloadEvent> {
|
||||
public:
|
||||
using Callback = void(ResourceDownloadEvent*);
|
||||
|
||||
ListenerResult handle(std::function<Callback> fn, ResourceDownloadEvent* event);
|
||||
ResourceDownloadFilter();
|
||||
};
|
||||
|
||||
class Loader::Impl {
|
||||
public:
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
std::vector<ghc::filesystem::path> m_modSearchDirectories;
|
||||
std::vector<ModInfo> m_modsToLoad;
|
||||
std::vector<InvalidGeodeFile> m_invalidMods;
|
||||
std::unordered_map<std::string, Mod*> m_mods;
|
||||
std::vector<ghc::filesystem::path> m_texturePaths;
|
||||
bool m_isSetup = false;
|
||||
|
||||
std::condition_variable m_earlyLoadFinishedCV;
|
||||
std::mutex m_earlyLoadFinishedMutex;
|
||||
std::atomic_bool m_earlyLoadFinished = false;
|
||||
std::vector<std::function<void(void)>> m_gdThreadQueue;
|
||||
mutable std::mutex m_gdThreadMutex;
|
||||
bool m_platformConsoleOpen = false;
|
||||
std::vector<std::pair<Hook*, Mod*>> m_internalHooks;
|
||||
bool m_readyToHook = false;
|
||||
|
||||
std::mutex m_nextModMutex;
|
||||
std::unique_lock<std::mutex> m_nextModLock = std::unique_lock<std::mutex>(m_nextModMutex, std::defer_lock);
|
||||
std::condition_variable m_nextModCV;
|
||||
std::mutex m_nextModAccessMutex;
|
||||
Mod* m_nextMod = nullptr;
|
||||
|
||||
void provideNextMod(Mod* mod);
|
||||
Mod* takeNextMod();
|
||||
void releaseNextMod();
|
||||
|
||||
void downloadLoaderResources();
|
||||
|
||||
bool loadHooks();
|
||||
void setupIPC();
|
||||
|
||||
Impl();
|
||||
~Impl();
|
||||
|
||||
void createDirectories();
|
||||
|
||||
void updateModResources(Mod* mod);
|
||||
void addSearchPaths();
|
||||
|
||||
friend void GEODE_CALL ::geode_implicit_load(Mod*);
|
||||
|
||||
Result<Mod*> loadModFromInfo(ModInfo const& info);
|
||||
|
||||
Result<> setup();
|
||||
void reset();
|
||||
|
||||
Result<> saveData();
|
||||
Result<> loadData();
|
||||
|
||||
VersionInfo getVersion();
|
||||
VersionInfo minModVersion();
|
||||
VersionInfo maxModVersion();
|
||||
bool isModVersionSupported(VersionInfo const& version);
|
||||
|
||||
Result<Mod*> loadModFromFile(ghc::filesystem::path const& file);
|
||||
void loadModsFromDirectory(ghc::filesystem::path const& dir, bool recursive = true);
|
||||
void refreshModsList();
|
||||
bool isModInstalled(std::string const& id) const;
|
||||
Mod* getInstalledMod(std::string const& id) const;
|
||||
bool isModLoaded(std::string const& id) const;
|
||||
Mod* getLoadedMod(std::string const& id) const;
|
||||
std::vector<Mod*> getAllMods();
|
||||
Mod* getInternalMod();
|
||||
void updateAllDependencies();
|
||||
std::vector<InvalidGeodeFile> getFailedMods() const;
|
||||
|
||||
void updateResources();
|
||||
|
||||
void waitForModsToBeLoaded();
|
||||
|
||||
bool didLastLaunchCrash() const;
|
||||
|
||||
nlohmann::json processRawIPC(void* rawHandle, std::string const& buffer);
|
||||
|
||||
void queueInGDThread(ScheduledFunction func);
|
||||
void executeGDThreadQueue();
|
||||
|
||||
void logConsoleMessage(std::string const& msg);
|
||||
bool platformConsoleOpen() const;
|
||||
void openPlatformConsole();
|
||||
void closePlatformConsole();
|
||||
void platformMessageBox(char const* title, std::string const& info);
|
||||
|
||||
bool verifyLoaderResources();
|
||||
|
||||
bool isReadyToHook() const;
|
||||
void addInternalHook(Hook* hook, Mod* mod);
|
||||
};
|
||||
|
||||
class InternalLoader {
|
||||
public:
|
||||
static Loader::Impl* get();
|
||||
};
|
||||
}
|
677
loader/src/loader/InternalMod.cpp
Normal file
677
loader/src/loader/InternalMod.cpp
Normal file
|
@ -0,0 +1,677 @@
|
|||
#include "InternalMod.hpp"
|
||||
#include "InternalLoader.hpp"
|
||||
#include "about.hpp"
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Hook.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/utils/file.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
Mod::Impl* InternalMod::getImpl(Mod* mod) {
|
||||
return mod->m_impl.get();
|
||||
}
|
||||
|
||||
Mod::Impl::Impl(Mod* self, ModInfo const& info) : m_self(self), m_info(info) {
|
||||
m_saveDirPath = dirs::getModsSaveDir() / info.id;
|
||||
ghc::filesystem::create_directories(m_saveDirPath);
|
||||
}
|
||||
|
||||
Mod::Impl::~Impl() {
|
||||
(void)this->unloadBinary();
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::setup() {
|
||||
this->setupSettings();
|
||||
auto loadRes = this->loadData();
|
||||
if (!loadRes) {
|
||||
log::warn("Unable to load data for \"{}\": {}", m_info.id, loadRes.unwrapErr());
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
ghc::filesystem::path Mod::Impl::getSaveDir() const {
|
||||
return m_saveDirPath;
|
||||
}
|
||||
|
||||
std::string Mod::Impl::getID() const {
|
||||
return m_info.id;
|
||||
}
|
||||
|
||||
std::string Mod::Impl::getName() const {
|
||||
return m_info.name;
|
||||
}
|
||||
|
||||
std::string Mod::Impl::getDeveloper() const {
|
||||
return m_info.developer;
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::Impl::getDescription() const {
|
||||
return m_info.description;
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::Impl::getDetails() const {
|
||||
return m_info.details;
|
||||
}
|
||||
|
||||
ModInfo Mod::Impl::getModInfo() const {
|
||||
return m_info;
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::Impl::getTempDir() const {
|
||||
return m_tempDirName;
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::Impl::getBinaryPath() const {
|
||||
return m_tempDirName / m_info.binaryName;
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::Impl::getPackagePath() const {
|
||||
return m_info.path;
|
||||
}
|
||||
|
||||
VersionInfo Mod::Impl::getVersion() const {
|
||||
return m_info.version;
|
||||
}
|
||||
|
||||
nlohmann::json& Mod::Impl::getSaveContainer() {
|
||||
return m_saved;
|
||||
}
|
||||
|
||||
bool Mod::Impl::isEnabled() const {
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
bool Mod::Impl::isLoaded() const {
|
||||
return m_binaryLoaded;
|
||||
}
|
||||
|
||||
bool Mod::Impl::supportsDisabling() const {
|
||||
return m_info.supportsDisabling;
|
||||
}
|
||||
|
||||
bool Mod::Impl::supportsUnloading() const {
|
||||
return m_info.supportsUnloading;
|
||||
}
|
||||
|
||||
bool Mod::Impl::wasSuccesfullyLoaded() const {
|
||||
return !this->isEnabled() || this->isLoaded();
|
||||
}
|
||||
|
||||
std::vector<Hook*> Mod::Impl::getHooks() const {
|
||||
return m_hooks;
|
||||
}
|
||||
|
||||
// Settings and saved values
|
||||
|
||||
Result<> Mod::Impl::loadData() {
|
||||
ModStateEvent(this->m_self, ModEventType::DataLoaded).post();
|
||||
|
||||
// Settings
|
||||
// Check if settings exist
|
||||
auto settingPath = m_saveDirPath / "settings.json";
|
||||
if (ghc::filesystem::exists(settingPath)) {
|
||||
GEODE_UNWRAP_INTO(auto settingData, utils::file::readString(settingPath));
|
||||
try {
|
||||
// parse settings.json
|
||||
auto json = nlohmann::json::parse(settingData);
|
||||
JsonChecker checker(json);
|
||||
auto root = checker.root("[settings.json]");
|
||||
|
||||
m_savedSettingsData = json;
|
||||
|
||||
for (auto& [key, value] : root.items()) {
|
||||
// check if this is a known setting
|
||||
if (auto setting = this->getSetting(key)) {
|
||||
// load its value
|
||||
if (!setting->load(value.json())) {
|
||||
log::internalLog(
|
||||
Severity::Error,
|
||||
this->m_self,
|
||||
"{}: Unable to load value for setting \"{}\"",
|
||||
m_info.id,
|
||||
key
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
log::internalLog(
|
||||
Severity::Warning,
|
||||
this->m_self,
|
||||
"Encountered unknown setting \"{}\" while loading "
|
||||
"settings",
|
||||
key
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
return Err(std::string("Unable to parse settings: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// Saved values
|
||||
auto savedPath = m_saveDirPath / "saved.json";
|
||||
if (ghc::filesystem::exists(savedPath)) {
|
||||
GEODE_UNWRAP_INTO(auto data, utils::file::readString(savedPath));
|
||||
try {
|
||||
m_saved = nlohmann::json::parse(data);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
return Err(std::string("Unable to parse saved values: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::saveData() {
|
||||
ModStateEvent(this->m_self, ModEventType::DataSaved).post();
|
||||
|
||||
// Data saving should be fully fail-safe
|
||||
|
||||
std::unordered_set<std::string> coveredSettings;
|
||||
|
||||
// Settings
|
||||
auto json = nlohmann::json::object();
|
||||
for (auto& [key, value] : m_settings) {
|
||||
coveredSettings.insert(key);
|
||||
if (!value->save(json[key])) {
|
||||
log::error("Unable to save setting \"" + key + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
// if some settings weren't provided a custom settings handler (for example,
|
||||
// the mod was not loaded) then make sure to save their previous state in
|
||||
// order to not lose data
|
||||
try {
|
||||
log::debug("Check covered");
|
||||
for (auto& [key, value] : m_savedSettingsData.items()) {
|
||||
log::debug("Check if {} is saved", key);
|
||||
if (!coveredSettings.count(key)) {
|
||||
json[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
|
||||
auto res = utils::file::writeString(m_saveDirPath / "settings.json", json.dump(4));
|
||||
if (!res) {
|
||||
log::error("Unable to save settings: {}", res.unwrapErr());
|
||||
}
|
||||
|
||||
auto res2 = utils::file::writeString(m_saveDirPath / "saved.json", m_saved.dump(4));
|
||||
if (!res2) {
|
||||
log::error("Unable to save values: {}", res2.unwrapErr());
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
void Mod::Impl::setupSettings() {
|
||||
for (auto& [key, sett] : m_info.settings) {
|
||||
if (auto value = sett.createDefaultValue()) {
|
||||
m_settings.emplace(key, std::move(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mod::Impl::registerCustomSetting(std::string const& key, std::unique_ptr<SettingValue> value) {
|
||||
if (!m_settings.count(key)) {
|
||||
// load data
|
||||
if (m_savedSettingsData.count(key)) {
|
||||
value->load(m_savedSettingsData.at(key));
|
||||
}
|
||||
m_settings.emplace(key, std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
bool Mod::Impl::hasSettings() const {
|
||||
return m_info.settings.size();
|
||||
}
|
||||
|
||||
std::vector<std::string> Mod::Impl::getSettingKeys() const {
|
||||
std::vector<std::string> keys;
|
||||
for (auto& [key, _] : m_info.settings) {
|
||||
keys.push_back(key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::optional<Setting> Mod::Impl::getSettingDefinition(std::string const& key) const {
|
||||
for (auto& setting : m_info.settings) {
|
||||
if (setting.first == key) {
|
||||
return setting.second;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
SettingValue* Mod::Impl::getSetting(std::string const& key) const {
|
||||
if (m_settings.count(key)) {
|
||||
return m_settings.at(key).get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Mod::Impl::hasSetting(std::string const& key) const {
|
||||
for (auto& setting : m_info.settings) {
|
||||
if (setting.first == key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Loading, Toggling, Installing
|
||||
|
||||
Result<> Mod::Impl::loadBinary() {
|
||||
if (m_binaryLoaded) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
GEODE_UNWRAP(this->createTempDir());
|
||||
|
||||
if (this->hasUnresolvedDependencies()) {
|
||||
return Err("Mod has unresolved dependencies");
|
||||
}
|
||||
|
||||
InternalLoader::get()->provideNextMod(this->m_self);
|
||||
|
||||
GEODE_UNWRAP(this->loadPlatformBinary());
|
||||
m_binaryLoaded = true;
|
||||
|
||||
InternalLoader::get()->releaseNextMod();
|
||||
|
||||
ModStateEvent(this->m_self, ModEventType::Loaded).post();
|
||||
|
||||
Loader::get()->updateAllDependencies();
|
||||
|
||||
GEODE_UNWRAP(this->enable());
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::unloadBinary() {
|
||||
if (!m_binaryLoaded) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
if (!m_info.supportsUnloading) {
|
||||
return Err("Mod does not support unloading");
|
||||
}
|
||||
|
||||
GEODE_UNWRAP(this->saveData());
|
||||
|
||||
GEODE_UNWRAP(this->disable());
|
||||
ModStateEvent(this->m_self, ModEventType::Unloaded).post();
|
||||
|
||||
// Disabling unhooks and unpatches already
|
||||
for (auto const& hook : m_hooks) {
|
||||
delete hook;
|
||||
}
|
||||
m_hooks.clear();
|
||||
|
||||
for (auto const& patch : m_patches) {
|
||||
delete patch;
|
||||
}
|
||||
m_patches.clear();
|
||||
|
||||
GEODE_UNWRAP(this->unloadPlatformBinary());
|
||||
m_binaryLoaded = false;
|
||||
|
||||
Loader::get()->updateAllDependencies();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::enable() {
|
||||
if (!m_binaryLoaded) {
|
||||
return this->loadBinary();
|
||||
}
|
||||
|
||||
for (auto const& hook : m_hooks) {
|
||||
GEODE_UNWRAP(this->enableHook(hook));
|
||||
}
|
||||
|
||||
for (auto const& patch : m_patches) {
|
||||
if (!patch->apply()) {
|
||||
return Err("Unable to apply patch at " + std::to_string(patch->getAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
ModStateEvent(this->m_self, ModEventType::Enabled).post();
|
||||
m_enabled = true;
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::disable() {
|
||||
if (!m_enabled) {
|
||||
return Ok();
|
||||
}
|
||||
if (!m_info.supportsDisabling) {
|
||||
return Err("Mod does not support disabling");
|
||||
}
|
||||
|
||||
ModStateEvent(this->m_self, ModEventType::Disabled).post();
|
||||
|
||||
for (auto const& hook : m_hooks) {
|
||||
GEODE_UNWRAP(this->disableHook(hook));
|
||||
}
|
||||
for (auto const& patch : m_patches) {
|
||||
if (!patch->restore()) {
|
||||
return Err("Unable to restore patch at " + std::to_string(patch->getAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
m_enabled = false;
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::uninstall() {
|
||||
if (m_info.supportsDisabling) {
|
||||
GEODE_UNWRAP(this->disable());
|
||||
if (m_info.supportsUnloading) {
|
||||
GEODE_UNWRAP(this->unloadBinary());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ghc::filesystem::remove(m_info.path);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
return Err(
|
||||
"Unable to delete mod's .geode file! "
|
||||
"This might be due to insufficient permissions - "
|
||||
"try running GD as administrator."
|
||||
);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
bool Mod::Impl::isUninstalled() const {
|
||||
return this->m_self != InternalMod::get() && !ghc::filesystem::exists(m_info.path);
|
||||
}
|
||||
|
||||
// Dependencies
|
||||
|
||||
Result<> Mod::Impl::updateDependencies() {
|
||||
bool hasUnresolved = false;
|
||||
for (auto& dep : m_info.dependencies) {
|
||||
// set the dependency's loaded mod if such exists
|
||||
if (!dep.mod) {
|
||||
dep.mod = Loader::get()->getLoadedMod(dep.id);
|
||||
// verify loaded dependency version
|
||||
if (dep.mod && !dep.version.compare(dep.mod->getVersion())) {
|
||||
dep.mod = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// check if the dependency is loaded
|
||||
if (dep.mod) {
|
||||
// update the dependency recursively
|
||||
GEODE_UNWRAP(dep.mod->updateDependencies());
|
||||
|
||||
// enable mod if it's resolved & enabled
|
||||
if (!dep.mod->hasUnresolvedDependencies()) {
|
||||
if (dep.mod->isEnabled()) {
|
||||
GEODE_UNWRAP(dep.mod->loadBinary().expect("Unable to load dependency: {error}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if the dependency is resolved now
|
||||
if (!dep.isResolved()) {
|
||||
GEODE_UNWRAP(this->unloadBinary().expect("Unable to unload mod: {error}"));
|
||||
hasUnresolved = true;
|
||||
}
|
||||
}
|
||||
// load if there weren't any unresolved dependencies
|
||||
if (!hasUnresolved) {
|
||||
log::debug("All dependencies for {} found", m_info.id);
|
||||
if (m_enabled) {
|
||||
log::debug("Resolved & loading {}", m_info.id);
|
||||
GEODE_UNWRAP(this->loadBinary());
|
||||
}
|
||||
else {
|
||||
log::debug("Resolved {}, however not loading it as it is disabled", m_info.id);
|
||||
}
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
bool Mod::Impl::hasUnresolvedDependencies() const {
|
||||
for (auto const& dep : m_info.dependencies) {
|
||||
if (!dep.isResolved()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Dependency> Mod::Impl::getUnresolvedDependencies() {
|
||||
std::vector<Dependency> unresolved;
|
||||
for (auto const& dep : m_info.dependencies) {
|
||||
if (!dep.isResolved()) {
|
||||
unresolved.push_back(dep);
|
||||
}
|
||||
}
|
||||
return unresolved;
|
||||
}
|
||||
|
||||
bool Mod::Impl::depends(std::string const& id) const {
|
||||
return utils::ranges::contains(m_info.dependencies, [id](Dependency const& t) {
|
||||
return t.id == id;
|
||||
});
|
||||
}
|
||||
|
||||
// Hooks
|
||||
|
||||
Result<> Mod::Impl::enableHook(Hook* hook) {
|
||||
auto res = hook->enable();
|
||||
if (res) m_hooks.push_back(hook);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::disableHook(Hook* hook) {
|
||||
return hook->disable();
|
||||
}
|
||||
|
||||
Result<Hook*> Mod::Impl::addHook(Hook* hook) {
|
||||
if (InternalLoader::get()->isReadyToHook()) {
|
||||
auto res = this->enableHook(hook);
|
||||
if (!res) {
|
||||
delete hook;
|
||||
return Err("Can't create hook");
|
||||
}
|
||||
}
|
||||
else {
|
||||
InternalLoader::get()->addInternalHook(hook, this->m_self);
|
||||
}
|
||||
|
||||
return Ok(hook);
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::removeHook(Hook* hook) {
|
||||
auto res = this->disableHook(hook);
|
||||
if (res) {
|
||||
ranges::remove(m_hooks, hook);
|
||||
delete hook;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Patches
|
||||
|
||||
// TODO: replace this with a safe one
|
||||
static ByteVector readMemory(void* address, size_t amount) {
|
||||
ByteVector ret;
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
ret.push_back(*reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(address) + i));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result<Patch*> Mod::Impl::patch(void* address, ByteVector const& data) {
|
||||
auto p = new Patch;
|
||||
p->m_address = address;
|
||||
p->m_original = readMemory(address, data.size());
|
||||
p->m_owner = this->m_self;
|
||||
p->m_patch = data;
|
||||
if (!p->apply()) {
|
||||
delete p;
|
||||
return Err("Unable to enable patch at " + std::to_string(p->getAddress()));
|
||||
}
|
||||
m_patches.push_back(p);
|
||||
return Ok(p);
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::unpatch(Patch* patch) {
|
||||
if (patch->restore()) {
|
||||
ranges::remove(m_patches, patch);
|
||||
delete patch;
|
||||
return Ok();
|
||||
}
|
||||
return Err("Unable to restore patch!");
|
||||
}
|
||||
|
||||
// Misc.
|
||||
|
||||
Result<> Mod::Impl::createTempDir() {
|
||||
// Check if temp dir already exists
|
||||
if (!m_tempDirName.string().empty()) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// Create geode/temp
|
||||
auto tempDir = dirs::getModRuntimeDir();
|
||||
if (!file::createDirectoryAll(tempDir)) {
|
||||
return Err("Unable to create mods' runtime directory");
|
||||
}
|
||||
|
||||
// Create geode/temp/mod.id
|
||||
auto tempPath = tempDir / m_info.id;
|
||||
if (!file::createDirectoryAll(tempPath)) {
|
||||
return Err("Unable to create mod runtime directory");
|
||||
}
|
||||
|
||||
// Unzip .geode file into temp dir
|
||||
GEODE_UNWRAP_INTO(auto unzip, file::Unzip::create(m_info.path));
|
||||
if (!unzip.hasEntry(m_info.binaryName)) {
|
||||
return Err(
|
||||
fmt::format("Unable to find platform binary under the name \"{}\"", m_info.binaryName)
|
||||
);
|
||||
}
|
||||
GEODE_UNWRAP(unzip.extractAllTo(tempPath));
|
||||
|
||||
// Mark temp dir creation as succesful
|
||||
m_tempDirName = tempPath;
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::Impl::getConfigDir(bool create) const {
|
||||
auto dir = dirs::getModConfigDir() / m_info.id;
|
||||
if (create) {
|
||||
(void)file::createDirectoryAll(dir);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
char const* Mod::Impl::expandSpriteName(char const* name) {
|
||||
static std::unordered_map<std::string, char const*> expanded = {};
|
||||
if (expanded.count(name)) return expanded[name];
|
||||
|
||||
auto exp = new char[strlen(name) + 2 + m_info.id.size()];
|
||||
auto exps = m_info.id + "/" + name;
|
||||
memcpy(exp, exps.c_str(), exps.size() + 1);
|
||||
|
||||
expanded[name] = exp;
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
ModJson Mod::Impl::getRuntimeInfo() const {
|
||||
auto json = m_info.toJSON();
|
||||
|
||||
auto obj = ModJson::object();
|
||||
obj["hooks"] = ModJson::array();
|
||||
for (auto hook : m_hooks) {
|
||||
obj["hooks"].push_back(ModJson(hook->getRuntimeInfo()));
|
||||
}
|
||||
obj["patches"] = ModJson::array();
|
||||
for (auto patch : m_patches) {
|
||||
obj["patches"].push_back(ModJson(patch->getRuntimeInfo()));
|
||||
}
|
||||
obj["enabled"] = m_enabled;
|
||||
obj["loaded"] = m_binaryLoaded;
|
||||
obj["temp-dir"] = this->getTempDir();
|
||||
obj["save-dir"] = this->getSaveDir();
|
||||
obj["config-dir"] = this->getConfigDir(false);
|
||||
json["runtime"] = obj;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
static constexpr char const* SUPPORT_INFO = R"MD(
|
||||
**Geode** is funded through your gracious <cy>**donations**</c>!
|
||||
You can support our work by sending <cp>**catgirl pictures**</c> to [HJfod](https://youtu.be/LOHSF9MmBDw) :))
|
||||
)MD";
|
||||
|
||||
static ModInfo getInternalModInfo() {
|
||||
try {
|
||||
auto json = ModJson::parse(LOADER_MOD_JSON);
|
||||
auto infoRes = ModInfo::create(json);
|
||||
if (infoRes.isErr()) {
|
||||
InternalLoader::get()->platformMessageBox(
|
||||
"Fatal Internal Error",
|
||||
"Unable to parse loader mod.json: \"" + infoRes.unwrapErr() +
|
||||
"\"\n"
|
||||
"This is a fatal internal error in the loader, please "
|
||||
"contact Geode developers immediately!"
|
||||
);
|
||||
return ModInfo();
|
||||
}
|
||||
auto info = infoRes.unwrap();
|
||||
info.details = LOADER_ABOUT_MD;
|
||||
info.supportInfo = SUPPORT_INFO;
|
||||
info.supportsDisabling = false;
|
||||
return info;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
InternalLoader::get()->platformMessageBox(
|
||||
"Fatal Internal Error",
|
||||
"Unable to parse loader mod.json: \"" + std::string(e.what()) +
|
||||
"\"\n"
|
||||
"This is a fatal internal error in the loader, please "
|
||||
"contact Geode developers immediately!"
|
||||
);
|
||||
return ModInfo();
|
||||
}
|
||||
}
|
||||
|
||||
Mod* InternalMod::get() {
|
||||
auto& mod = Mod::sharedMod<>;
|
||||
if (mod) return mod;
|
||||
|
||||
mod = new Mod(getInternalModInfo());
|
||||
|
||||
auto setupRes = mod->m_impl->setup();
|
||||
if (!setupRes) {
|
||||
log::error("Failed to setup internal mod! ({})", setupRes.unwrapErr());
|
||||
return mod;
|
||||
}
|
||||
return mod;
|
||||
}
|
129
loader/src/loader/InternalMod.hpp
Normal file
129
loader/src/loader/InternalMod.hpp
Normal file
|
@ -0,0 +1,129 @@
|
|||
#pragma once
|
||||
|
||||
namespace geode {
|
||||
class Mod::Impl {
|
||||
public:
|
||||
Mod* m_self;
|
||||
/**
|
||||
* Mod info
|
||||
*/
|
||||
ModInfo m_info;
|
||||
/**
|
||||
* Platform-specific info
|
||||
*/
|
||||
PlatformInfo* m_platformInfo = nullptr;
|
||||
/**
|
||||
* Hooks owned by this mod
|
||||
*/
|
||||
std::vector<Hook*> m_hooks;
|
||||
/**
|
||||
* Patches owned by this mod
|
||||
*/
|
||||
std::vector<Patch*> m_patches;
|
||||
/**
|
||||
* Whether the mod is enabled or not
|
||||
*/
|
||||
bool m_enabled = false;
|
||||
/**
|
||||
* Whether the mod binary is loaded or not
|
||||
*/
|
||||
bool m_binaryLoaded = false;
|
||||
/**
|
||||
* Mod temp directory name
|
||||
*/
|
||||
ghc::filesystem::path m_tempDirName;
|
||||
/**
|
||||
* Mod save directory name
|
||||
*/
|
||||
ghc::filesystem::path m_saveDirPath;
|
||||
/**
|
||||
* Pointers to mods that depend on
|
||||
* this Mod. Makes it possible to
|
||||
* enable / disable them automatically,
|
||||
* when their dependency is disabled.
|
||||
*/
|
||||
std::vector<Mod*> m_parentDependencies;
|
||||
/**
|
||||
* Saved values
|
||||
*/
|
||||
nlohmann::json m_saved;
|
||||
/**
|
||||
* Setting values
|
||||
*/
|
||||
std::unordered_map<std::string, std::unique_ptr<SettingValue>> m_settings;
|
||||
/**
|
||||
* Settings save data. Stored for efficient loading of custom settings
|
||||
*/
|
||||
nlohmann::json m_savedSettingsData;
|
||||
|
||||
Impl(Mod* self, ModInfo const& info);
|
||||
~Impl();
|
||||
|
||||
Result<> setup();
|
||||
|
||||
Result<> loadPlatformBinary();
|
||||
Result<> unloadPlatformBinary();
|
||||
Result<> createTempDir();
|
||||
|
||||
void setupSettings();
|
||||
|
||||
std::string getID() const;
|
||||
std::string getName() const;
|
||||
std::string getDeveloper() const;
|
||||
std::optional<std::string> getDescription() const;
|
||||
std::optional<std::string> getDetails() const;
|
||||
ghc::filesystem::path getPackagePath() const;
|
||||
VersionInfo getVersion() const;
|
||||
bool isEnabled() const;
|
||||
bool isLoaded() const;
|
||||
bool supportsDisabling() const;
|
||||
bool supportsUnloading() const;
|
||||
bool wasSuccesfullyLoaded() const;
|
||||
ModInfo getModInfo() const;
|
||||
ghc::filesystem::path getTempDir() const;
|
||||
ghc::filesystem::path getBinaryPath() const;
|
||||
|
||||
nlohmann::json& getSaveContainer();
|
||||
|
||||
Result<> saveData();
|
||||
Result<> loadData();
|
||||
|
||||
ghc::filesystem::path getSaveDir() const;
|
||||
ghc::filesystem::path getConfigDir(bool create = true) const;
|
||||
|
||||
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);
|
||||
|
||||
std::vector<Hook*> getHooks() const;
|
||||
Result<Hook*> addHook(Hook* hook);
|
||||
Result<> enableHook(Hook* hook);
|
||||
Result<> disableHook(Hook* hook);
|
||||
Result<> removeHook(Hook* hook);
|
||||
Result<Patch*> patch(void* address, ByteVector const& data);
|
||||
Result<> unpatch(Patch* patch);
|
||||
Result<> loadBinary();
|
||||
Result<> unloadBinary();
|
||||
Result<> enable();
|
||||
Result<> disable();
|
||||
Result<> uninstall();
|
||||
bool isUninstalled() const;
|
||||
bool depends(std::string const& id) const;
|
||||
bool hasUnresolvedDependencies() const;
|
||||
Result<> updateDependencies();
|
||||
std::vector<Dependency> getUnresolvedDependencies();
|
||||
|
||||
char const* expandSpriteName(char const* name);
|
||||
ModJson getRuntimeInfo() const;
|
||||
};
|
||||
|
||||
class InternalMod : public Mod {
|
||||
public:
|
||||
static Mod* get();
|
||||
|
||||
static Mod::Impl* getImpl(Mod* mod);
|
||||
};
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
#include "LoaderImpl.hpp"
|
||||
#include "InternalLoader.hpp"
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
Loader::Loader() : m_impl(new Impl) {}
|
||||
|
||||
|
|
|
@ -1,138 +0,0 @@
|
|||
#include "FileWatcher.hpp"
|
||||
|
||||
#include <Geode/external/json/json.hpp>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Index.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/utils/Result.hpp>
|
||||
#include <Geode/utils/map.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <InternalMod.hpp>
|
||||
#include <about.hpp>
|
||||
#include <crashlog.hpp>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
struct ResourceDownloadEvent : public Event {
|
||||
const UpdateStatus status;
|
||||
ResourceDownloadEvent(UpdateStatus const& status);
|
||||
};
|
||||
|
||||
class GEODE_DLL ResourceDownloadFilter : public EventFilter<ResourceDownloadEvent> {
|
||||
public:
|
||||
using Callback = void(ResourceDownloadEvent*);
|
||||
|
||||
ListenerResult handle(std::function<Callback> fn, ResourceDownloadEvent* event);
|
||||
ResourceDownloadFilter();
|
||||
};
|
||||
|
||||
// TODO: Find a file convention for impl headers
|
||||
namespace geode {
|
||||
class LoaderImpl;
|
||||
}
|
||||
|
||||
class Loader::Impl {
|
||||
public:
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
std::vector<ghc::filesystem::path> m_modSearchDirectories;
|
||||
std::vector<ModInfo> m_modsToLoad;
|
||||
std::vector<InvalidGeodeFile> m_invalidMods;
|
||||
std::unordered_map<std::string, Mod*> m_mods;
|
||||
std::vector<ghc::filesystem::path> m_texturePaths;
|
||||
bool m_isSetup = false;
|
||||
|
||||
std::condition_variable m_earlyLoadFinishedCV;
|
||||
std::mutex m_earlyLoadFinishedMutex;
|
||||
std::atomic_bool m_earlyLoadFinished = false;
|
||||
std::vector<std::function<void(void)>> m_gdThreadQueue;
|
||||
mutable std::mutex m_gdThreadMutex;
|
||||
bool m_platformConsoleOpen = false;
|
||||
std::vector<std::pair<Hook*, Mod*>> m_internalHooks;
|
||||
bool m_readyToHook = false;
|
||||
|
||||
std::mutex m_nextModMutex;
|
||||
std::unique_lock<std::mutex> m_nextModLock = std::unique_lock<std::mutex>(m_nextModMutex, std::defer_lock);
|
||||
std::condition_variable m_nextModCV;
|
||||
std::mutex m_nextModAccessMutex;
|
||||
Mod* m_nextMod = nullptr;
|
||||
|
||||
void provideNextMod(Mod* mod);
|
||||
Mod* takeNextMod();
|
||||
void releaseNextMod();
|
||||
|
||||
void downloadLoaderResources();
|
||||
|
||||
bool loadHooks();
|
||||
void setupIPC();
|
||||
|
||||
Impl();
|
||||
~Impl();
|
||||
|
||||
void createDirectories();
|
||||
|
||||
void updateModResources(Mod* mod);
|
||||
void addSearchPaths();
|
||||
|
||||
friend void GEODE_CALL ::geode_implicit_load(Mod*);
|
||||
|
||||
Result<Mod*> loadModFromInfo(ModInfo const& info);
|
||||
|
||||
Result<> setup();
|
||||
void reset();
|
||||
|
||||
Result<> saveData();
|
||||
Result<> loadData();
|
||||
|
||||
VersionInfo getVersion();
|
||||
VersionInfo minModVersion();
|
||||
VersionInfo maxModVersion();
|
||||
bool isModVersionSupported(VersionInfo const& version);
|
||||
|
||||
Result<Mod*> loadModFromFile(ghc::filesystem::path const& file);
|
||||
void loadModsFromDirectory(ghc::filesystem::path const& dir, bool recursive = true);
|
||||
void refreshModsList();
|
||||
bool isModInstalled(std::string const& id) const;
|
||||
Mod* getInstalledMod(std::string const& id) const;
|
||||
bool isModLoaded(std::string const& id) const;
|
||||
Mod* getLoadedMod(std::string const& id) const;
|
||||
std::vector<Mod*> getAllMods();
|
||||
Mod* getInternalMod();
|
||||
void updateAllDependencies();
|
||||
std::vector<InvalidGeodeFile> getFailedMods() const;
|
||||
|
||||
void updateResources();
|
||||
|
||||
void waitForModsToBeLoaded();
|
||||
|
||||
bool didLastLaunchCrash() const;
|
||||
|
||||
nlohmann::json processRawIPC(void* rawHandle, std::string const& buffer);
|
||||
|
||||
void queueInGDThread(ScheduledFunction func);
|
||||
void executeGDThreadQueue();
|
||||
|
||||
void logConsoleMessage(std::string const& msg);
|
||||
bool platformConsoleOpen() const;
|
||||
void openPlatformConsole();
|
||||
void closePlatformConsole();
|
||||
void platformMessageBox(char const* title, std::string const& info);
|
||||
|
||||
bool verifyLoaderResources();
|
||||
|
||||
bool isReadyToHook() const;
|
||||
void addInternalHook(Hook* hook, Mod* mod);
|
||||
};
|
||||
|
||||
namespace geode {
|
||||
class LoaderImpl {
|
||||
public:
|
||||
static Loader::Impl* get();
|
||||
};
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#include "LoaderImpl.hpp"
|
||||
#include "InternalLoader.hpp"
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
|
@ -190,7 +190,7 @@ void Logger::setup() {
|
|||
void Logger::_push(Log&& log) {
|
||||
std::string logStr = log.toString(true);
|
||||
|
||||
LoaderImpl::get()->logConsoleMessage(logStr);
|
||||
InternalLoader::get()->logConsoleMessage(logStr);
|
||||
s_logStream << logStr << std::endl;
|
||||
|
||||
s_logs.emplace_back(std::forward<Log>(log));
|
||||
|
|
|
@ -1,629 +1,188 @@
|
|||
#include "LoaderImpl.hpp"
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Hook.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/utils/file.hpp>
|
||||
#include <InternalMod.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
Mod::Mod(ModInfo const& info) {
|
||||
m_info = info;
|
||||
m_saveDirPath = dirs::getModsSaveDir() / info.id;
|
||||
ghc::filesystem::create_directories(m_saveDirPath);
|
||||
}
|
||||
|
||||
Result<> Mod::setup() {
|
||||
this->setupSettings();
|
||||
auto loadRes = this->loadData();
|
||||
if (!loadRes) {
|
||||
log::warn(
|
||||
"Unable to load data for \"{}\": {}",
|
||||
m_info.id, loadRes.unwrapErr()
|
||||
);
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Mod::~Mod() {
|
||||
(void)this->unloadBinary();
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
ghc::filesystem::path Mod::getSaveDir() const {
|
||||
return m_saveDirPath;
|
||||
}
|
||||
|
||||
std::string Mod::getID() const {
|
||||
return m_info.id;
|
||||
}
|
||||
|
||||
std::string Mod::getName() const {
|
||||
return m_info.name;
|
||||
}
|
||||
|
||||
std::string Mod::getDeveloper() const {
|
||||
return m_info.developer;
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::getDescription() const {
|
||||
return m_info.description;
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::getDetails() const {
|
||||
return m_info.details;
|
||||
}
|
||||
|
||||
ModInfo Mod::getModInfo() const {
|
||||
return m_info;
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::getTempDir() const {
|
||||
return m_tempDirName;
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::getBinaryPath() const {
|
||||
return m_tempDirName / m_info.binaryName;
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::getPackagePath() const {
|
||||
return m_info.path;
|
||||
}
|
||||
|
||||
VersionInfo Mod::getVersion() const {
|
||||
return m_info.version;
|
||||
}
|
||||
|
||||
bool Mod::isEnabled() const {
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
bool Mod::isLoaded() const {
|
||||
return m_binaryLoaded;
|
||||
}
|
||||
|
||||
bool Mod::supportsDisabling() const {
|
||||
return m_info.supportsDisabling;
|
||||
}
|
||||
|
||||
bool Mod::supportsUnloading() const {
|
||||
return m_info.supportsUnloading;
|
||||
}
|
||||
|
||||
bool Mod::wasSuccesfullyLoaded() const {
|
||||
return !this->isEnabled() || this->isLoaded();
|
||||
}
|
||||
|
||||
std::vector<Hook*> Mod::getHooks() const {
|
||||
return m_hooks;
|
||||
}
|
||||
|
||||
// Settings and saved values
|
||||
|
||||
Result<> Mod::loadData() {
|
||||
ModStateEvent(this, ModEventType::DataLoaded).post();
|
||||
|
||||
// Settings
|
||||
// Check if settings exist
|
||||
auto settingPath = m_saveDirPath / "settings.json";
|
||||
if (ghc::filesystem::exists(settingPath)) {
|
||||
GEODE_UNWRAP_INTO(auto settingData, utils::file::readString(settingPath));
|
||||
try {
|
||||
// parse settings.json
|
||||
auto json = nlohmann::json::parse(settingData);
|
||||
JsonChecker checker(json);
|
||||
auto root = checker.root("[settings.json]");
|
||||
|
||||
m_savedSettingsData = json;
|
||||
|
||||
for (auto& [key, value] : root.items()) {
|
||||
// check if this is a known setting
|
||||
if (auto setting = this->getSetting(key)) {
|
||||
// load its value
|
||||
if (!setting->load(value.json())) {
|
||||
log::internalLog(
|
||||
Severity::Error,
|
||||
this,
|
||||
"{}: Unable to load value for setting \"{}\"",
|
||||
m_info.id, key
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
log::internalLog(
|
||||
Severity::Warning,
|
||||
this,
|
||||
"Encountered unknown setting \"{}\" while loading "
|
||||
"settings",
|
||||
key
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
return Err(std::string("Unable to parse settings: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// Saved values
|
||||
auto savedPath = m_saveDirPath / "saved.json";
|
||||
if (ghc::filesystem::exists(savedPath)) {
|
||||
GEODE_UNWRAP_INTO(auto data, utils::file::readString(savedPath));
|
||||
try {
|
||||
m_saved = nlohmann::json::parse(data);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
return Err(std::string("Unable to parse saved values: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::saveData() {
|
||||
ModStateEvent(this, ModEventType::DataSaved).post();
|
||||
|
||||
// Data saving should be fully fail-safe
|
||||
|
||||
std::unordered_set<std::string> coveredSettings;
|
||||
|
||||
// Settings
|
||||
auto json = nlohmann::json::object();
|
||||
for (auto& [key, value] : m_settings) {
|
||||
coveredSettings.insert(key);
|
||||
if (!value->save(json[key])) {
|
||||
log::error("Unable to save setting \"" + key + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
// if some settings weren't provided a custom settings handler (for example,
|
||||
// the mod was not loaded) then make sure to save their previous state in
|
||||
// order to not lose data
|
||||
try {
|
||||
log::debug("Check covered");
|
||||
for (auto& [key, value] : m_savedSettingsData.items()) {
|
||||
log::debug("Check if {} is saved", key);
|
||||
if (!coveredSettings.count(key)) {
|
||||
json[key] = value;
|
||||
}
|
||||
}
|
||||
} catch(...) {}
|
||||
|
||||
auto res = utils::file::writeString(m_saveDirPath / "settings.json", json.dump(4));
|
||||
if (!res) {
|
||||
log::error("Unable to save settings: {}", res.unwrapErr());
|
||||
}
|
||||
|
||||
auto res2 = utils::file::writeString(m_saveDirPath / "saved.json", m_saved.dump(4));
|
||||
if (!res2) {
|
||||
log::error("Unable to save values: {}", res2.unwrapErr());
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
void Mod::setupSettings() {
|
||||
for (auto& [key, sett] : m_info.settings) {
|
||||
if (auto value = sett.createDefaultValue()) {
|
||||
m_settings.emplace(key, std::move(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mod::registerCustomSetting(
|
||||
std::string const& key,
|
||||
std::unique_ptr<SettingValue> value
|
||||
) {
|
||||
if (!m_settings.count(key)) {
|
||||
// load data
|
||||
if (m_savedSettingsData.count(key)) {
|
||||
value->load(m_savedSettingsData.at(key));
|
||||
}
|
||||
m_settings.emplace(key, std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
bool Mod::hasSettings() const {
|
||||
return m_info.settings.size();
|
||||
}
|
||||
|
||||
std::vector<std::string> Mod::getSettingKeys() const {
|
||||
std::vector<std::string> keys;
|
||||
for (auto& [key, _] : m_info.settings) {
|
||||
keys.push_back(key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::optional<Setting> Mod::getSettingDefinition(std::string const& key) const {
|
||||
for (auto& setting : m_info.settings) {
|
||||
if (setting.first == key) {
|
||||
return setting.second;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
SettingValue* Mod::getSetting(std::string const& key) const {
|
||||
if (m_settings.count(key)) {
|
||||
return m_settings.at(key).get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Mod::hasSetting(std::string const& key) const {
|
||||
for (auto& setting : m_info.settings) {
|
||||
if (setting.first == key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Loading, Toggling, Installing
|
||||
|
||||
Result<> Mod::loadBinary() {
|
||||
if (m_binaryLoaded) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
GEODE_UNWRAP(this->createTempDir());
|
||||
|
||||
if (this->hasUnresolvedDependencies()) {
|
||||
return Err("Mod has unresolved dependencies");
|
||||
}
|
||||
|
||||
LoaderImpl::get()->provideNextMod(this);
|
||||
|
||||
GEODE_UNWRAP(this->loadPlatformBinary());
|
||||
m_binaryLoaded = true;
|
||||
|
||||
LoaderImpl::get()->releaseNextMod();
|
||||
|
||||
// Call implicit entry point to place hooks etc.
|
||||
m_implicitLoadFunc(this);
|
||||
|
||||
ModStateEvent(this, ModEventType::Loaded).post();
|
||||
|
||||
Loader::get()->updateAllDependencies();
|
||||
|
||||
GEODE_UNWRAP(this->enable());
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::unloadBinary() {
|
||||
if (!m_binaryLoaded) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
if (!m_info.supportsUnloading) {
|
||||
return Err("Mod does not support unloading");
|
||||
}
|
||||
|
||||
GEODE_UNWRAP(this->saveData());
|
||||
|
||||
GEODE_UNWRAP(this->disable());
|
||||
ModStateEvent(this, ModEventType::Unloaded).post();
|
||||
|
||||
// Disabling unhooks and unpatches already
|
||||
for (auto const& hook : m_hooks) {
|
||||
delete hook;
|
||||
}
|
||||
m_hooks.clear();
|
||||
|
||||
for (auto const& patch : m_patches) {
|
||||
delete patch;
|
||||
}
|
||||
m_patches.clear();
|
||||
|
||||
GEODE_UNWRAP(this->unloadPlatformBinary());
|
||||
m_binaryLoaded = false;
|
||||
|
||||
Loader::get()->updateAllDependencies();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::enable() {
|
||||
if (!m_binaryLoaded) {
|
||||
return this->loadBinary();
|
||||
}
|
||||
|
||||
for (auto const& hook : m_hooks) {
|
||||
GEODE_UNWRAP(this->enableHook(hook));
|
||||
}
|
||||
|
||||
for (auto const& patch : m_patches) {
|
||||
if (!patch->apply()) {
|
||||
return Err("Unable to apply patch at " + std::to_string(patch->getAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
ModStateEvent(this, ModEventType::Enabled).post();
|
||||
m_enabled = true;
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::disable() {
|
||||
if (!m_enabled) {
|
||||
return Ok();
|
||||
}
|
||||
if (!m_info.supportsDisabling) {
|
||||
return Err("Mod does not support disabling");
|
||||
}
|
||||
|
||||
ModStateEvent(this, ModEventType::Disabled).post();
|
||||
|
||||
for (auto const& hook : m_hooks) {
|
||||
GEODE_UNWRAP(this->disableHook(hook));
|
||||
}
|
||||
for (auto const& patch : m_patches) {
|
||||
if (!patch->restore()) {
|
||||
return Err("Unable to restore patch at " + std::to_string(patch->getAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
m_enabled = false;
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::uninstall() {
|
||||
if (m_info.supportsDisabling) {
|
||||
GEODE_UNWRAP(this->disable());
|
||||
if (m_info.supportsUnloading) {
|
||||
GEODE_UNWRAP(this->unloadBinary());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ghc::filesystem::remove(m_info.path);
|
||||
} catch(std::exception& e) {
|
||||
return Err(
|
||||
"Unable to delete mod's .geode file! "
|
||||
"This might be due to insufficient permissions - "
|
||||
"try running GD as administrator."
|
||||
);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
bool Mod::isUninstalled() const {
|
||||
return this != InternalMod::get() && !ghc::filesystem::exists(m_info.path);
|
||||
}
|
||||
|
||||
// Dependencies
|
||||
|
||||
Result<> Mod::updateDependencies() {
|
||||
bool hasUnresolved = false;
|
||||
for (auto& dep : m_info.dependencies) {
|
||||
// set the dependency's loaded mod if such exists
|
||||
if (!dep.mod) {
|
||||
dep.mod = Loader::get()->getLoadedMod(dep.id);
|
||||
// verify loaded dependency version
|
||||
if (dep.mod && !dep.version.compare(dep.mod->getVersion())) {
|
||||
dep.mod = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// check if the dependency is loaded
|
||||
if (dep.mod) {
|
||||
// update the dependency recursively
|
||||
GEODE_UNWRAP(dep.mod->updateDependencies());
|
||||
|
||||
// enable mod if it's resolved & enabled
|
||||
if (!dep.mod->hasUnresolvedDependencies()) {
|
||||
if (dep.mod->isEnabled()) {
|
||||
GEODE_UNWRAP(dep.mod->loadBinary()
|
||||
.expect("Unable to load dependency: {error}")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if the dependency is resolved now
|
||||
if (!dep.isResolved()) {
|
||||
GEODE_UNWRAP(this->unloadBinary()
|
||||
.expect("Unable to unload mod: {error}")
|
||||
);
|
||||
hasUnresolved = true;
|
||||
}
|
||||
}
|
||||
// load if there weren't any unresolved dependencies
|
||||
if (!hasUnresolved) {
|
||||
log::debug("All dependencies for {} found", m_info.id);
|
||||
if (m_enabled) {
|
||||
log::debug("Resolved & loading {}", m_info.id);
|
||||
GEODE_UNWRAP(this->loadBinary());
|
||||
}
|
||||
else {
|
||||
log::debug("Resolved {}, however not loading it as it is disabled", m_info.id);
|
||||
}
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
bool Mod::hasUnresolvedDependencies() const {
|
||||
for (auto const& dep : m_info.dependencies) {
|
||||
if (!dep.isResolved()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Dependency> Mod::getUnresolvedDependencies() {
|
||||
std::vector<Dependency> unresolved;
|
||||
for (auto const& dep : m_info.dependencies) {
|
||||
if (!dep.isResolved()) {
|
||||
unresolved.push_back(dep);
|
||||
}
|
||||
}
|
||||
return unresolved;
|
||||
}
|
||||
|
||||
bool Mod::depends(std::string const& id) const {
|
||||
return utils::ranges::contains(
|
||||
m_info.dependencies,
|
||||
[id](Dependency const& t) {
|
||||
return t.id == id;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Hooks
|
||||
|
||||
Result<> Mod::enableHook(Hook* hook) {
|
||||
auto res = hook->enable();
|
||||
if (res) m_hooks.push_back(hook);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Result<> Mod::disableHook(Hook* hook) {
|
||||
return hook->disable();
|
||||
}
|
||||
|
||||
Result<Hook*> Mod::addHook(Hook* hook) {
|
||||
if (LoaderImpl::get()->isReadyToHook()) {
|
||||
auto res = this->enableHook(hook);
|
||||
if (!res) {
|
||||
delete hook;
|
||||
return Err("Can't create hook");
|
||||
}
|
||||
}
|
||||
else {
|
||||
LoaderImpl::get()->addInternalHook(hook, this);
|
||||
}
|
||||
|
||||
return Ok(hook);
|
||||
}
|
||||
|
||||
Result<> Mod::removeHook(Hook* hook) {
|
||||
auto res = this->disableHook(hook);
|
||||
if (res) {
|
||||
ranges::remove(m_hooks, hook);
|
||||
delete hook;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Patches
|
||||
|
||||
byte_array readMemory(void* address, size_t amount) {
|
||||
byte_array ret;
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
ret.push_back(*reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(address) + i));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result<Patch*> Mod::patch(void* address, byte_array data) {
|
||||
auto p = new Patch;
|
||||
p->m_address = address;
|
||||
p->m_original = readMemory(address, data.size());
|
||||
p->m_owner = this;
|
||||
p->m_patch = data;
|
||||
if (!p->apply()) {
|
||||
delete p;
|
||||
return Err("Unable to enable patch at " + std::to_string(p->getAddress()));
|
||||
}
|
||||
m_patches.push_back(p);
|
||||
return Ok(p);
|
||||
}
|
||||
|
||||
Result<> Mod::unpatch(Patch* patch) {
|
||||
if (patch->restore()) {
|
||||
ranges::remove(m_patches, patch);
|
||||
delete patch;
|
||||
return Ok();
|
||||
}
|
||||
return Err("Unable to restore patch!");
|
||||
}
|
||||
|
||||
// Misc.
|
||||
|
||||
Result<> Mod::createTempDir() {
|
||||
// Check if temp dir already exists
|
||||
if (!m_tempDirName.string().empty()) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// Create geode/temp
|
||||
auto tempDir = dirs::getModRuntimeDir();
|
||||
if (!file::createDirectoryAll(tempDir)) {
|
||||
return Err("Unable to create mods' runtime directory");
|
||||
}
|
||||
|
||||
// Create geode/temp/mod.id
|
||||
auto tempPath = tempDir / m_info.id;
|
||||
if (!file::createDirectoryAll(tempPath)) {
|
||||
return Err("Unable to create mod runtime directory");
|
||||
}
|
||||
|
||||
// Unzip .geode file into temp dir
|
||||
GEODE_UNWRAP_INTO(auto unzip, file::Unzip::create(m_info.path));
|
||||
if (!unzip.hasEntry(m_info.binaryName)) {
|
||||
return Err(fmt::format(
|
||||
"Unable to find platform binary under the name \"{}\"", m_info.binaryName
|
||||
));
|
||||
}
|
||||
GEODE_UNWRAP(unzip.extractAllTo(tempPath));
|
||||
|
||||
// Mark temp dir creation as succesful
|
||||
m_tempDirName = tempPath;
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::getConfigDir(bool create) const {
|
||||
auto dir = dirs::getModConfigDir() / m_info.id;
|
||||
if (create) {
|
||||
(void)file::createDirectoryAll(dir);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
const char* Mod::expandSpriteName(const char* name) {
|
||||
static std::unordered_map<std::string, const char*> expanded = {};
|
||||
if (expanded.count(name)) return expanded[name];
|
||||
|
||||
auto exp = new char[strlen(name) + 2 + m_info.id.size()];
|
||||
auto exps = m_info.id + "/" + name;
|
||||
memcpy(exp, exps.c_str(), exps.size() + 1);
|
||||
|
||||
expanded[name] = exp;
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
ModJson Mod::getRuntimeInfo() const {
|
||||
auto json = m_info.toJSON();
|
||||
|
||||
auto obj = ModJson::object();
|
||||
obj["hooks"] = ModJson::array();
|
||||
for (auto hook : m_hooks) {
|
||||
obj["hooks"].push_back(ModJson(hook->getRuntimeInfo()));
|
||||
}
|
||||
obj["patches"] = ModJson::array();
|
||||
for (auto patch : m_patches) {
|
||||
obj["patches"].push_back(ModJson(patch->getRuntimeInfo()));
|
||||
}
|
||||
obj["enabled"] = m_enabled;
|
||||
obj["loaded"] = m_binaryLoaded;
|
||||
obj["temp-dir"] = this->getTempDir();
|
||||
obj["save-dir"] = this->getSaveDir();
|
||||
obj["config-dir"] = this->getConfigDir(false);
|
||||
json["runtime"] = obj;
|
||||
|
||||
return json;
|
||||
}
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include "InternalMod.hpp"
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
Mod::Mod(ModInfo const& info) : m_impl(std::make_unique<Impl>(this, info)) {}
|
||||
|
||||
Mod::~Mod() {}
|
||||
|
||||
std::string Mod::getID() const {
|
||||
return m_impl->getID();
|
||||
}
|
||||
|
||||
std::string Mod::getName() const {
|
||||
return m_impl->getName();
|
||||
}
|
||||
|
||||
std::string Mod::getDeveloper() const {
|
||||
return m_impl->getDeveloper();
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::getDescription() const {
|
||||
return m_impl->getDescription();
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::getDetails() const {
|
||||
return m_impl->getDetails();
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::getPackagePath() const {
|
||||
return m_impl->getPackagePath();
|
||||
}
|
||||
|
||||
VersionInfo Mod::getVersion() const {
|
||||
return m_impl->getVersion();
|
||||
}
|
||||
|
||||
nlohmann::json& Mod::getSaveContainer() {
|
||||
return m_impl->getSaveContainer();
|
||||
}
|
||||
|
||||
bool Mod::isEnabled() const {
|
||||
return m_impl->isEnabled();
|
||||
}
|
||||
|
||||
bool Mod::isLoaded() const {
|
||||
return m_impl->isLoaded();
|
||||
}
|
||||
|
||||
bool Mod::supportsDisabling() const {
|
||||
return m_impl->supportsDisabling();
|
||||
}
|
||||
|
||||
bool Mod::supportsUnloading() const {
|
||||
return m_impl->supportsUnloading();
|
||||
}
|
||||
|
||||
bool Mod::wasSuccesfullyLoaded() const {
|
||||
return m_impl->wasSuccesfullyLoaded();
|
||||
}
|
||||
|
||||
ModInfo Mod::getModInfo() const {
|
||||
return m_impl->getModInfo();
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::getTempDir() const {
|
||||
return m_impl->getTempDir();
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::getBinaryPath() const {
|
||||
return m_impl->getBinaryPath();
|
||||
}
|
||||
|
||||
Result<> Mod::saveData() {
|
||||
return m_impl->saveData();
|
||||
}
|
||||
|
||||
Result<> Mod::loadData() {
|
||||
return m_impl->loadData();
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::getSaveDir() const {
|
||||
return m_impl->getSaveDir();
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::getConfigDir(bool create) const {
|
||||
return m_impl->getConfigDir(create);
|
||||
}
|
||||
|
||||
bool Mod::hasSettings() const {
|
||||
return m_impl->hasSettings();
|
||||
}
|
||||
|
||||
std::vector<std::string> Mod::getSettingKeys() const {
|
||||
return m_impl->getSettingKeys();
|
||||
}
|
||||
|
||||
bool Mod::hasSetting(std::string const& key) const {
|
||||
return m_impl->hasSetting(key);
|
||||
}
|
||||
|
||||
std::optional<Setting> Mod::getSettingDefinition(std::string const& key) const {
|
||||
return m_impl->getSettingDefinition(key);
|
||||
}
|
||||
|
||||
SettingValue* Mod::getSetting(std::string const& key) const {
|
||||
return m_impl->getSetting(key);
|
||||
}
|
||||
|
||||
void Mod::registerCustomSetting(std::string const& key, std::unique_ptr<SettingValue> value) {
|
||||
return m_impl->registerCustomSetting(key, std::move(value));
|
||||
}
|
||||
|
||||
std::vector<Hook*> Mod::getHooks() const {
|
||||
return m_impl->getHooks();
|
||||
}
|
||||
|
||||
Result<Hook*> Mod::addHook(Hook* hook) {
|
||||
return m_impl->addHook(hook);
|
||||
}
|
||||
|
||||
Result<> Mod::enableHook(Hook* hook) {
|
||||
return m_impl->enableHook(hook);
|
||||
}
|
||||
|
||||
Result<> Mod::disableHook(Hook* hook) {
|
||||
return m_impl->disableHook(hook);
|
||||
}
|
||||
|
||||
Result<> Mod::removeHook(Hook* hook) {
|
||||
return m_impl->removeHook(hook);
|
||||
}
|
||||
|
||||
Result<Patch*> Mod::patch(void* address, ByteVector const& data) {
|
||||
return m_impl->patch(address, data);
|
||||
}
|
||||
|
||||
Result<> Mod::unpatch(Patch* patch) {
|
||||
return m_impl->unpatch(patch);
|
||||
}
|
||||
|
||||
Result<> Mod::loadBinary() {
|
||||
return m_impl->loadBinary();
|
||||
}
|
||||
|
||||
Result<> Mod::unloadBinary() {
|
||||
return m_impl->unloadBinary();
|
||||
}
|
||||
|
||||
Result<> Mod::enable() {
|
||||
return m_impl->enable();
|
||||
}
|
||||
|
||||
Result<> Mod::disable() {
|
||||
return m_impl->disable();
|
||||
}
|
||||
|
||||
Result<> Mod::uninstall() {
|
||||
return m_impl->uninstall();
|
||||
}
|
||||
|
||||
bool Mod::isUninstalled() const {
|
||||
return m_impl->isUninstalled();
|
||||
}
|
||||
|
||||
bool Mod::depends(std::string const& id) const {
|
||||
return m_impl->depends(id);
|
||||
}
|
||||
|
||||
bool Mod::hasUnresolvedDependencies() const {
|
||||
return m_impl->hasUnresolvedDependencies();
|
||||
}
|
||||
|
||||
Result<> Mod::updateDependencies() {
|
||||
return m_impl->updateDependencies();
|
||||
}
|
||||
|
||||
std::vector<Dependency> Mod::getUnresolvedDependencies() {
|
||||
return m_impl->getUnresolvedDependencies();
|
||||
}
|
||||
|
||||
char const* Mod::expandSpriteName(char const* name) {
|
||||
return m_impl->expandSpriteName(name);
|
||||
}
|
||||
|
||||
ModJson Mod::getRuntimeInfo() const {
|
||||
return m_impl->getRuntimeInfo();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#include "../core/Core.hpp"
|
||||
#include "loader/LoaderImpl.hpp"
|
||||
#include "loader/InternalLoader.hpp"
|
||||
|
||||
#include <Geode/loader/IPC.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
@ -7,7 +7,7 @@
|
|||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/Setting.hpp>
|
||||
#include <Geode/loader/SettingEvent.hpp>
|
||||
#include <InternalMod.hpp>
|
||||
#include <loader/InternalMod.hpp>
|
||||
#include <array>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
@ -146,7 +146,7 @@ int geodeEntry(void* platformData) {
|
|||
// setup internals
|
||||
|
||||
if (!geode::core::hook::initialize()) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
InternalLoader::get()->platformMessageBox(
|
||||
"Unable to load Geode!",
|
||||
"There was an unknown fatal error setting up "
|
||||
"internal tools and Geode can not be loaded. "
|
||||
|
@ -156,13 +156,13 @@ int geodeEntry(void* platformData) {
|
|||
}
|
||||
|
||||
// set up loader, load mods, etc.
|
||||
if (!LoaderImpl::get()->setup()) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
if (!InternalLoader::get()->setup()) {
|
||||
InternalLoader::get()->platformMessageBox(
|
||||
"Unable to Load Geode!",
|
||||
"There was an unknown fatal error setting up "
|
||||
"the loader and Geode can not be loaded."
|
||||
);
|
||||
LoaderImpl::get()->reset();
|
||||
InternalLoader::get()->reset();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include <loader/LoaderImpl.hpp>
|
||||
#include <loader/InternalLoader.hpp>
|
||||
|
||||
#ifdef GEODE_IS_IOS
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <InternalMod.hpp>
|
||||
#include <loader/InternalMod.hpp>
|
||||
#include <iostream>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
|
@ -1,7 +1,9 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_IOS
|
||||
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <loader/InternalMod.hpp>
|
||||
#include <dlfcn.h>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
@ -15,16 +17,10 @@ T findSymbolOrMangled(void* dylib, char const* name, char const* mangled) {
|
|||
return res;
|
||||
}
|
||||
|
||||
Result<> Mod::loadPlatformBinary() {
|
||||
Result<> Mod::Impl::loadPlatformBinary() {
|
||||
auto dylib =
|
||||
dlopen((this->m_tempDirName / this->m_info.binaryName).string().c_str(), RTLD_LAZY);
|
||||
if (dylib) {
|
||||
this->m_implicitLoadFunc =
|
||||
findSymbolOrMangled<geode_load>(dylib, "geode_implicit_load", "_geode_implicit_load");
|
||||
|
||||
if (!this->m_implicitLoadFunc) {
|
||||
return Err("Unable to find mod entry point");
|
||||
}
|
||||
if (this->m_platformInfo) {
|
||||
delete this->m_platformInfo;
|
||||
}
|
||||
|
@ -36,12 +32,11 @@ Result<> Mod::loadPlatformBinary() {
|
|||
return Err("Unable to load the DYLIB: dlerror returned (" + err + ")");
|
||||
}
|
||||
|
||||
Result<> Mod::unloadPlatformBinary() {
|
||||
Result<> Mod::Impl::unloadPlatformBinary() {
|
||||
auto dylib = this->m_platformInfo->m_dylib;
|
||||
delete this->m_platformInfo;
|
||||
this->m_platformInfo = nullptr;
|
||||
if (dlclose(dylib) == 0) {
|
||||
this->m_implicitLoadFunc = nullptr;
|
||||
return Ok();
|
||||
}
|
||||
else {
|
|
@ -1,13 +1,15 @@
|
|||
#include <Geode/loader/IPC.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <InternalMod.hpp>
|
||||
#include <iostream>
|
||||
#include <loader/LoaderImpl.hpp>
|
||||
#include <loader/InternalLoader.hpp>
|
||||
#include <loader/InternalMod.hpp>
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
void Loader::Impl::platformMessageBox(char const* title, std::string const& info) {
|
||||
CFStringRef cfTitle = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
|
||||
CFStringRef cfMessage = CFStringCreateWithCString(NULL, info.c_str(), kCFStringEncodingUTF8);
|
||||
|
@ -34,7 +36,7 @@ CFDataRef msgPortCallback(CFMessagePortRef port, SInt32 messageID, CFDataRef dat
|
|||
|
||||
std::string cdata(reinterpret_cast<char const*>(CFDataGetBytePtr(data)), CFDataGetLength(data));
|
||||
|
||||
std::string reply = LoaderImpl::get()->processRawIPC(port, cdata);
|
||||
std::string reply = InternalLoader::get()->processRawIPC(port, cdata);
|
||||
return CFDataCreate(NULL, (UInt8 const*)reply.data(), reply.size());
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
#ifdef GEODE_IS_MACOS
|
||||
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <loader/InternalMod.hpp>
|
||||
#include <dlfcn.h>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
@ -16,17 +17,10 @@ T findSymbolOrMangled(void* dylib, char const* name, char const* mangled) {
|
|||
return res;
|
||||
}
|
||||
|
||||
Result<> Mod::loadPlatformBinary() {
|
||||
Result<> Mod::Impl::loadPlatformBinary() {
|
||||
auto dylib =
|
||||
dlopen((this->m_tempDirName / this->m_info.binaryName).string().c_str(), RTLD_LAZY);
|
||||
if (dylib) {
|
||||
this->m_implicitLoadFunc =
|
||||
findSymbolOrMangled<decltype(geode_implicit_load)*>(dylib, "geode_implicit_load", "_geode_implicit_load");
|
||||
|
||||
if (!this->m_implicitLoadFunc) {
|
||||
return Err("Unable to find mod entry point");
|
||||
}
|
||||
|
||||
if (this->m_platformInfo) {
|
||||
delete this->m_platformInfo;
|
||||
}
|
||||
|
@ -38,12 +32,11 @@ Result<> Mod::loadPlatformBinary() {
|
|||
return Err("Unable to load the DYLIB: dlerror returned (" + err + ")");
|
||||
}
|
||||
|
||||
Result<> Mod::unloadPlatformBinary() {
|
||||
Result<> Mod::Impl::unloadPlatformBinary() {
|
||||
auto dylib = this->m_platformInfo->m_dylib;
|
||||
delete this->m_platformInfo;
|
||||
this->m_platformInfo = nullptr;
|
||||
if (dlclose(dylib) == 0) {
|
||||
this->m_implicitLoadFunc = nullptr;
|
||||
return Ok();
|
||||
}
|
||||
else {
|
|
@ -1,8 +1,8 @@
|
|||
#include <Geode/loader/IPC.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <InternalMod.hpp>
|
||||
#include <loader/InternalMod.hpp>
|
||||
#include <iostream>
|
||||
#include <loader/LoaderImpl.hpp>
|
||||
#include <loader/InternalLoader.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
|
@ -49,7 +49,7 @@ void ipcPipeThread(HANDLE pipe) {
|
|||
if (ReadFile(pipe, buffer, sizeof(buffer) - 1, &read, nullptr)) {
|
||||
buffer[read] = '\0';
|
||||
|
||||
std::string reply = LoaderImpl::get()->processRawIPC((void*)pipe, buffer);
|
||||
std::string reply = InternalLoader::get()->processRawIPC((void*)pipe, buffer);
|
||||
|
||||
DWORD written;
|
||||
WriteFile(pipe, reply.c_str(), reply.size(), &written, nullptr);
|
|
@ -3,6 +3,7 @@
|
|||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <loader/InternalMod.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
|
@ -71,14 +72,9 @@ std::string getLastWinError() {
|
|||
return msg + " (" + std::to_string(err) + ")";
|
||||
}
|
||||
|
||||
Result<> Mod::loadPlatformBinary() {
|
||||
Result<> Mod::Impl::loadPlatformBinary() {
|
||||
auto load = LoadLibraryW((m_tempDirName / m_info.binaryName).wstring().c_str());
|
||||
if (load) {
|
||||
if (!(m_implicitLoadFunc = findSymbolOrMangled<decltype(geode_implicit_load)*>(
|
||||
load, "geode_implicit_load", "_geode_implicit_load@4"
|
||||
))) {
|
||||
return Err("Unable to find mod entry point");
|
||||
}
|
||||
if (m_platformInfo) {
|
||||
delete m_platformInfo;
|
||||
}
|
||||
|
@ -88,11 +84,10 @@ Result<> Mod::loadPlatformBinary() {
|
|||
return Err("Unable to load the DLL: " + getLastWinError());
|
||||
}
|
||||
|
||||
Result<> Mod::unloadPlatformBinary() {
|
||||
Result<> Mod::Impl::unloadPlatformBinary() {
|
||||
auto hmod = m_platformInfo->m_hmod;
|
||||
delete m_platformInfo;
|
||||
if (FreeLibrary(hmod)) {
|
||||
m_implicitLoadFunc = nullptr;
|
||||
return Ok();
|
||||
}
|
||||
else {
|
|
@ -21,7 +21,7 @@
|
|||
#include <Geode/utils/casts.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <Geode/utils/web.hpp>
|
||||
#include <loader/LoaderImpl.hpp>
|
||||
#include <loader/InternalLoader.hpp>
|
||||
|
||||
static constexpr int const TAG_CONFIRM_UNINSTALL = 5;
|
||||
static constexpr int const TAG_DELETE_SAVEDATA = 6;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <Geode/binding/FLAlertLayer.hpp>
|
||||
#include <Geode/binding/StatsCell.hpp>
|
||||
#include <Geode/ui/GeodeUI.hpp>
|
||||
#include "../../../loader/LoaderImpl.hpp" // how should i include this src/loader/LoaderImpl.hpp
|
||||
#include <loader/InternalLoader.hpp>
|
||||
#include "../info/TagNode.hpp"
|
||||
#include "../info/DevProfilePopup.hpp"
|
||||
|
||||
|
|
|
@ -53,14 +53,14 @@ Result<nlohmann::json> utils::file::readJson(ghc::filesystem::path const& path)
|
|||
return Err("Unable to open file");
|
||||
}
|
||||
|
||||
Result<byte_array> utils::file::readBinary(ghc::filesystem::path const& path) {
|
||||
Result<ByteVector> utils::file::readBinary(ghc::filesystem::path const& path) {
|
||||
#if _WIN32
|
||||
std::ifstream in(path.wstring(), std::ios::in | std::ios::binary);
|
||||
#else
|
||||
std::ifstream in(path.string(), std::ios::in | std::ios::binary);
|
||||
#endif
|
||||
if (in) {
|
||||
return Ok(byte_array(std::istreambuf_iterator<char>(in), {}));
|
||||
return Ok(ByteVector(std::istreambuf_iterator<char>(in), {}));
|
||||
}
|
||||
return Err("Unable to open file");
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ Result<> utils::file::writeString(ghc::filesystem::path const& path, std::string
|
|||
return Err("Unable to open file");
|
||||
}
|
||||
|
||||
Result<> utils::file::writeBinary(ghc::filesystem::path const& path, byte_array const& data) {
|
||||
Result<> utils::file::writeBinary(ghc::filesystem::path const& path, ByteVector const& data) {
|
||||
std::ofstream file;
|
||||
#if _WIN32
|
||||
file.open(path.wstring(), std::ios::out | std::ios::binary);
|
||||
|
@ -200,7 +200,7 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
Result<byte_array> extract(Path const& name) {
|
||||
Result<ByteVector> extract(Path const& name) {
|
||||
if (!m_entries.count(name)) {
|
||||
return Err("Entry not found");
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ public:
|
|||
if (unzOpenCurrentFile(m_zip) != UNZ_OK) {
|
||||
return Err("Unable to open entry");
|
||||
}
|
||||
byte_array res;
|
||||
ByteVector res;
|
||||
res.resize(entry.uncompressedSize);
|
||||
auto size = unzReadCurrentFile(m_zip, res.data(), entry.uncompressedSize);
|
||||
if (size < 0 || size != entry.uncompressedSize) {
|
||||
|
@ -279,7 +279,7 @@ bool Unzip::hasEntry(Path const& name) {
|
|||
return m_impl->entries().count(name);
|
||||
}
|
||||
|
||||
Result<byte_array> Unzip::extract(Path const& name) {
|
||||
Result<ByteVector> Unzip::extract(Path const& name) {
|
||||
return m_impl->extract(name);
|
||||
}
|
||||
|
||||
|
@ -364,7 +364,7 @@ public:
|
|||
return Ok();
|
||||
}
|
||||
|
||||
Result<> add(Path const& path, byte_array const& data) {
|
||||
Result<> add(Path const& path, ByteVector const& data) {
|
||||
// open entry
|
||||
zip_fileinfo info = { 0 };
|
||||
if (zipOpenNewFileInZip(
|
||||
|
@ -418,12 +418,12 @@ Zip::Path Zip::getPath() const {
|
|||
return m_impl->path();
|
||||
}
|
||||
|
||||
Result<> Zip::add(Path const& path, byte_array const& data) {
|
||||
Result<> Zip::add(Path const& path, ByteVector const& data) {
|
||||
return m_impl->add(path, data);
|
||||
}
|
||||
|
||||
Result<> Zip::add(Path const& path, std::string const& data) {
|
||||
return this->add(path, byte_array(data.begin(), data.end()));
|
||||
return this->add(path, ByteVector(data.begin(), data.end()));
|
||||
}
|
||||
|
||||
Result<> Zip::addFrom(Path const& file, Path const& entryDir) {
|
||||
|
|
|
@ -9,7 +9,7 @@ using namespace web;
|
|||
|
||||
namespace geode::utils::fetch {
|
||||
static size_t writeBytes(char* data, size_t size, size_t nmemb, void* str) {
|
||||
as<byte_array*>(str)->insert(as<byte_array*>(str)->end(), data, data + size * nmemb);
|
||||
as<ByteVector*>(str)->insert(as<ByteVector*>(str)->end(), data, data + size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
|
@ -69,12 +69,12 @@ Result<> web::fetchFile(
|
|||
return Err("Error getting info: " + std::string(curl_easy_strerror(res)));
|
||||
}
|
||||
|
||||
Result<byte_array> web::fetchBytes(std::string const& url) {
|
||||
Result<ByteVector> web::fetchBytes(std::string const& url) {
|
||||
auto curl = curl_easy_init();
|
||||
|
||||
if (!curl) return Err("Curl not initialized!");
|
||||
|
||||
byte_array ret;
|
||||
ByteVector ret;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
@ -204,7 +204,7 @@ SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const
|
|||
}
|
||||
|
||||
// resulting byte array
|
||||
byte_array ret;
|
||||
ByteVector ret;
|
||||
// output file if downloading to file. unique_ptr because not always
|
||||
// initialized but don't wanna manually managed memory
|
||||
std::unique_ptr<std::ofstream> file = nullptr;
|
||||
|
@ -461,32 +461,32 @@ AsyncWebRequest::~AsyncWebRequest() {
|
|||
|
||||
AsyncWebResult<std::monostate> AsyncWebResponse::into(std::ostream& stream) {
|
||||
m_request.m_target = &stream;
|
||||
return this->as(+[](byte_array const&) -> Result<std::monostate> {
|
||||
return this->as(+[](ByteVector const&) -> Result<std::monostate> {
|
||||
return Ok(std::monostate());
|
||||
});
|
||||
}
|
||||
|
||||
AsyncWebResult<std::monostate> AsyncWebResponse::into(ghc::filesystem::path const& path) {
|
||||
m_request.m_target = path;
|
||||
return this->as(+[](byte_array const&) -> Result<std::monostate> {
|
||||
return this->as(+[](ByteVector const&) -> Result<std::monostate> {
|
||||
return Ok(std::monostate());
|
||||
});
|
||||
}
|
||||
|
||||
AsyncWebResult<std::string> AsyncWebResponse::text() {
|
||||
return this->as(+[](byte_array const& bytes) -> Result<std::string> {
|
||||
return this->as(+[](ByteVector const& bytes) -> Result<std::string> {
|
||||
return Ok(std::string(bytes.begin(), bytes.end()));
|
||||
});
|
||||
}
|
||||
|
||||
AsyncWebResult<byte_array> AsyncWebResponse::bytes() {
|
||||
return this->as(+[](byte_array const& bytes) -> Result<byte_array> {
|
||||
AsyncWebResult<ByteVector> AsyncWebResponse::bytes() {
|
||||
return this->as(+[](ByteVector const& bytes) -> Result<ByteVector> {
|
||||
return Ok(bytes);
|
||||
});
|
||||
}
|
||||
|
||||
AsyncWebResult<nlohmann::json> AsyncWebResponse::json() {
|
||||
return this->as(+[](byte_array const& bytes) -> Result<nlohmann::json> {
|
||||
return this->as(+[](ByteVector const& bytes) -> Result<nlohmann::json> {
|
||||
try {
|
||||
return Ok(nlohmann::json::parse(bytes.begin(), bytes.end()));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue