separate mod and impl

This commit is contained in:
altalk23 2022-12-14 14:11:19 +03:00
parent 950db7e474
commit 5666c8f356
33 changed files with 1245 additions and 1024 deletions

View file

@ -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();
}

View file

@ -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"

View file

@ -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

View file

@ -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();
};

View file

@ -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;
};
/**

View file

@ -6,8 +6,6 @@
#include <string>
class InternalMod;
namespace geode {
/**
* Describes the severity of the log

View file

@ -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

View file

@ -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;

View file

@ -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());

View file

@ -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"

View file

@ -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();

View file

@ -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);
}
};

View file

@ -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;
}

View file

@ -1,12 +0,0 @@
#pragma once
class InternalMod;
#include <Geode/loader/Mod.hpp>
USE_GEODE_NAMESPACE();
class InternalMod : public Mod {
public:
static Mod* get();
};

View file

@ -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;
}

View 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();
};
}

View 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;
}

View 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);
};
}

View file

@ -1,4 +1,6 @@
#include "LoaderImpl.hpp"
#include "InternalLoader.hpp"
USE_GEODE_NAMESPACE();
Loader::Loader() : m_impl(new Impl) {}

View file

@ -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();
};
}

View file

@ -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));

View file

@ -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>
#include "InternalMod.hpp"
USE_GEODE_NAMESPACE();
Mod::Mod(ModInfo const& info) {
m_info = info;
m_saveDirPath = dirs::getModsSaveDir() / info.id;
ghc::filesystem::create_directories(m_saveDirPath);
}
Mod::Mod(ModInfo const& info) : m_impl(std::make_unique<Impl>(this, info)) {}
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;
}
Mod::~Mod() {}
std::string Mod::getID() const {
return m_info.id;
return m_impl->getID();
}
std::string Mod::getName() const {
return m_info.name;
return m_impl->getName();
}
std::string Mod::getDeveloper() const {
return m_info.developer;
return m_impl->getDeveloper();
}
std::optional<std::string> Mod::getDescription() const {
return m_info.description;
return m_impl->getDescription();
}
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;
return m_impl->getDetails();
}
ghc::filesystem::path Mod::getPackagePath() const {
return m_info.path;
return m_impl->getPackagePath();
}
VersionInfo Mod::getVersion() const {
return m_info.version;
return m_impl->getVersion();
}
nlohmann::json& Mod::getSaveContainer() {
return m_impl->getSaveContainer();
}
bool Mod::isEnabled() const {
return m_enabled;
return m_impl->isEnabled();
}
bool Mod::isLoaded() const {
return m_binaryLoaded;
return m_impl->isLoaded();
}
bool Mod::supportsDisabling() const {
return m_info.supportsDisabling;
return m_impl->supportsDisabling();
}
bool Mod::supportsUnloading() const {
return m_info.supportsUnloading;
return m_impl->supportsUnloading();
}
bool Mod::wasSuccesfullyLoaded() const {
return !this->isEnabled() || this->isLoaded();
return m_impl->wasSuccesfullyLoaded();
}
std::vector<Hook*> Mod::getHooks() const {
return m_hooks;
ModInfo Mod::getModInfo() const {
return m_impl->getModInfo();
}
// Settings and saved values
ghc::filesystem::path Mod::getTempDir() const {
return m_impl->getTempDir();
}
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();
ghc::filesystem::path Mod::getBinaryPath() const {
return m_impl->getBinaryPath();
}
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();
return m_impl->saveData();
}
void Mod::setupSettings() {
for (auto& [key, sett] : m_info.settings) {
if (auto value = sett.createDefaultValue()) {
m_settings.emplace(key, std::move(value));
}
}
Result<> Mod::loadData() {
return m_impl->loadData();
}
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::getSaveDir() const {
return m_impl->getSaveDir();
}
ghc::filesystem::path Mod::getConfigDir(bool create) const {
auto dir = dirs::getModConfigDir() / m_info.id;
if (create) {
(void)file::createDirectoryAll(dir);
}
return dir;
return m_impl->getConfigDir(create);
}
const char* Mod::expandSpriteName(const char* name) {
static std::unordered_map<std::string, const char*> expanded = {};
if (expanded.count(name)) return expanded[name];
bool Mod::hasSettings() const {
return m_impl->hasSettings();
}
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);
std::vector<std::string> Mod::getSettingKeys() const {
return m_impl->getSettingKeys();
}
expanded[name] = exp;
bool Mod::hasSetting(std::string const& key) const {
return m_impl->hasSetting(key);
}
return exp;
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 {
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;
return m_impl->getRuntimeInfo();
}

View file

@ -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;
}

View file

@ -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>

View file

@ -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 {

View file

@ -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());
}

View file

@ -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 {

View file

@ -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);

View file

@ -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 {

View file

@ -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;

View file

@ -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"

View file

@ -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) {

View file

@ -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()));
}