From f73d1042a3bc17b1e40e45dafd74265716ec1389 Mon Sep 17 00:00:00 2001 From: HJfod <60038575+HJfod@users.noreply.github.com> Date: Wed, 30 Nov 2022 19:19:00 +0200 Subject: [PATCH] move directories away from loader into new dirs namespace --- loader/include/Geode/Loader.hpp | 1 + loader/include/Geode/loader/Loader.hpp | 18 - loader/include/Geode/loader/Types.hpp | 15 +- loader/src/index/Index.cpp | 1365 +++++++++-------- loader/src/internal/InternalLoader.cpp | 9 +- loader/src/internal/InternalMod.cpp | 4 +- loader/src/internal/crashlog.hpp | 3 +- loader/src/loader/Loader.cpp | 75 +- loader/src/loader/Log.cpp | 8 +- loader/src/loader/Mod.cpp | 12 +- loader/src/platform/ios/InternalLoader.cpp | 3 +- loader/src/platform/ios/crashlog.cpp | 2 +- loader/src/platform/mac/crashlog.mm | 2 +- loader/src/platform/windows/crashlog.cpp | 15 +- loader/src/ui/internal/info/ModInfoLayer.cpp | 10 +- loader/src/ui/internal/list/ModListLayer.cpp | 3 +- .../ui/internal/settings/GeodeSettingNode.cpp | 3 +- 17 files changed, 739 insertions(+), 809 deletions(-) diff --git a/loader/include/Geode/Loader.hpp b/loader/include/Geode/Loader.hpp index 76053bda..89e17d30 100644 --- a/loader/include/Geode/Loader.hpp +++ b/loader/include/Geode/Loader.hpp @@ -6,5 +6,6 @@ #include "loader/Mod.hpp" #include "loader/Setting.hpp" #include "loader/SettingEvent.hpp" +#include "loader/Dirs.hpp" #include diff --git a/loader/include/Geode/loader/Loader.hpp b/loader/include/Geode/loader/Loader.hpp index 5ba0bd02..3139f3e2 100644 --- a/loader/include/Geode/loader/Loader.hpp +++ b/loader/include/Geode/loader/Loader.hpp @@ -83,23 +83,5 @@ namespace geode { static void closePlatfromConsole(); bool didLastLaunchCrash() const; - ghc::filesystem::path getCrashLogDirectory() const; - - /** - * Directory where Geometry Dash is - */ - ghc::filesystem::path getGameDirectory() const; - /** - * Directory where GD saves its files - */ - ghc::filesystem::path getSaveDirectory() const; - /** - * Directory where Geode is - */ - ghc::filesystem::path getGeodeDirectory() const; - /** - * Directory where Geode saves its files - */ - ghc::filesystem::path getGeodeSaveDirectory() const; }; } diff --git a/loader/include/Geode/loader/Types.hpp b/loader/include/Geode/loader/Types.hpp index 38656c33..aabf344b 100644 --- a/loader/include/Geode/loader/Types.hpp +++ b/loader/include/Geode/loader/Types.hpp @@ -129,9 +129,6 @@ namespace geode { } }; - class Mod; - class Setting; - /** * Represents if a mod has been loaded & * its dependencies resolved @@ -151,16 +148,10 @@ namespace geode { Disabled, }; - static constexpr std::string_view GEODE_DIRECTORY = "geode"; - static constexpr std::string_view GEODE_MOD_DIRECTORY = "mods"; - static constexpr std::string_view GEODE_LOG_DIRECTORY = "log"; - static constexpr std::string_view GEODE_RESOURCE_DIRECTORY = "resources"; - static constexpr std::string_view GEODE_CONFIG_DIRECTORY = "config"; - static constexpr std::string_view GEODE_TEMP_DIRECTORY = "temp"; - static constexpr std::string_view GEODE_MOD_EXTENSION = ".geode"; - static constexpr std::string_view GEODE_INDEX_DIRECTORY = "index"; - + constexpr std::string_view GEODE_MOD_EXTENSION = ".geode"; + class Mod; + class Setting; class Loader; class Hook; struct ModInfo; diff --git a/loader/src/index/Index.cpp b/loader/src/index/Index.cpp index 83bc3bf5..690259db 100644 --- a/loader/src/index/Index.cpp +++ b/loader/src/index/Index.cpp @@ -1,702 +1,703 @@ -#include "Index.hpp" +// #include "Index.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include -#define GITHUB_DONT_RATE_LIMIT_ME_PLS 0 +// #define GITHUB_DONT_RATE_LIMIT_ME_PLS 0 -template -static Result readJSON(ghc::filesystem::path const& path) { - GEODE_UNWRAP_INTO( - std::string indexJsonData, - utils::file::readString(path).expect("Unable to read {}", path.string()) - ); - try { - return Ok(Json::parse(indexJsonData)); - } - catch (std::exception& e) { - return Err("Error parsing JSON: " + std::string(e.what())); - } -} +// template +// static Result readJSON(ghc::filesystem::path const& path) { +// GEODE_UNWRAP_INTO( +// std::string indexJsonData, +// utils::file::readString(path).expect("Unable to read {}", path.string()) +// ); +// try { +// return Ok(Json::parse(indexJsonData)); +// } +// catch (std::exception& e) { +// return Err("Error parsing JSON: " + std::string(e.what())); +// } +// } -static PlatformID platformFromString(std::string const& str) { - switch (hash(utils::string::trim(utils::string::toLower(str)).c_str())) { - default: - case hash("unknown"): return PlatformID::Unknown; - case hash("windows"): return PlatformID::Windows; - case hash("macos"): return PlatformID::MacOS; - case hash("ios"): return PlatformID::iOS; - case hash("android"): return PlatformID::Android; - case hash("linux"): return PlatformID::Linux; - } -} +// static PlatformID platformFromString(std::string const& str) { +// switch (hash(utils::string::trim(utils::string::toLower(str)).c_str())) { +// default: +// case hash("unknown"): return PlatformID::Unknown; +// case hash("windows"): return PlatformID::Windows; +// case hash("macos"): return PlatformID::MacOS; +// case hash("ios"): return PlatformID::iOS; +// case hash("android"): return PlatformID::Android; +// case hash("linux"): return PlatformID::Linux; +// } +// } -Index* Index::get() { - static auto ret = new Index(); - return ret; -} +// Index* Index::get() { +// static auto ret = new Index(); +// return ret; +// } -bool Index::isIndexUpdated() const { - return m_upToDate; -} +// bool Index::isIndexUpdated() const { +// return m_upToDate; +// } -std::vector Index::getFeaturedItems() const { - std::vector items; - items.reserve(m_featured.size()); - std::transform( - m_featured.begin(), m_featured.end(), std::back_inserter(items), - [this](auto const& item) { - return this->getKnownItem(item); - } - ); - return items; -} +// std::vector Index::getFeaturedItems() const { +// std::vector items; +// items.reserve(m_featured.size()); +// std::transform( +// m_featured.begin(), m_featured.end(), std::back_inserter(items), +// [this](auto const& item) { +// return this->getKnownItem(item); +// } +// ); +// return items; +// } -bool Index::isFeaturedItem(std::string const& item) const { - return m_featured.count(item); -} +// bool Index::isFeaturedItem(std::string const& item) const { +// return m_featured.count(item); +// } -void Index::updateIndex(IndexUpdateCallback callback, bool force) { -#define RETURN_ERROR(str) \ - std::string err__ = (str); \ - if (callback) callback(UpdateStatus::Failed, err__, 0); \ - log::info("Index update failed: {}", err__); \ - return; +// void Index::updateIndex(IndexUpdateCallback callback, bool force) { +// #define RETURN_ERROR(str) \ +// std::string err__ = (str); \ +// if (callback) callback(UpdateStatus::Failed, err__, 0); \ +// log::info("Index update failed: {}", err__); \ +// return; - // if already updated and no force, let - // delegate know - if (!force && m_upToDate) { - if (callback) { - callback(UpdateStatus::Finished, "Index already updated", 100); - } - return; - } +// // if already updated and no force, let +// // delegate know +// if (!force && m_upToDate) { +// if (callback) { +// callback(UpdateStatus::Finished, "Index already updated", 100); +// } +// return; +// } - // create directory for the local clone of - // the index - auto indexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY; - ghc::filesystem::create_directories(indexDir); +// // create directory for the local clone of +// // the index +// auto indexDir = dirs::getIndexDir(); +// ghc::filesystem::create_directories(indexDir); -#if GITHUB_DONT_RATE_LIMIT_ME_PLS == 1 +// #if GITHUB_DONT_RATE_LIMIT_ME_PLS == 1 - auto err = this->updateIndexFromLocalCache(); - if (!err) { - RETURN_ERROR(err); - } +// auto err = this->updateIndexFromLocalCache(); +// if (!err) { +// RETURN_ERROR(err); +// } - m_upToDate = true; - m_updating = false; +// m_upToDate = true; +// m_updating = false; - if (callback) callback(UpdateStatus::Finished, "", 100); - return; +// if (callback) callback(UpdateStatus::Finished, "", 100); +// return; -#endif +// #endif - // read sha of currently installed commit - std::string currentCommitSHA = ""; - if (ghc::filesystem::exists(indexDir / "current")) { - auto data = utils::file::readString(indexDir / "current"); - if (data) { - currentCommitSHA = data.unwrap(); - } - } +// // read sha of currently installed commit +// std::string currentCommitSHA = ""; +// if (ghc::filesystem::exists(indexDir / "current")) { +// auto data = utils::file::readString(indexDir / "current"); +// if (data) { +// currentCommitSHA = data.unwrap(); +// } +// } - web::AsyncWebRequest() - .join("index-update") - .header(fmt::format("If-None-Match: \"{}\"", currentCommitSHA)) - .header("Accept: application/vnd.github.sha") - .fetch("https://api.github.com/repos/geode-sdk/mods/commits/main") - .text() - .then([this, force, callback, currentCommitSHA](std::string const& upcomingCommitSHA) { - auto indexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY; +// web::AsyncWebRequest() +// .join("index-update") +// .header(fmt::format("If-None-Match: \"{}\"", currentCommitSHA)) +// .header("Accept: application/vnd.github.sha") +// .fetch("https://api.github.com/repos/geode-sdk/mods/commits/main") +// .text() +// .then([this, force, callback, currentCommitSHA](std::string const& upcomingCommitSHA) { +// auto indexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY; - // gee i sure hope no one does 60 commits to the mod index an hour and download every - // single one of them - if (upcomingCommitSHA == "") { - auto err = this->updateIndexFromLocalCache(); - if (!err) { - RETURN_ERROR(err.unwrapErr()); - } +// // gee i sure hope no one does 60 commits to the mod index an hour and download every +// // single one of them +// if (upcomingCommitSHA == "") { +// auto err = this->updateIndexFromLocalCache(); +// if (!err) { +// RETURN_ERROR(err.unwrapErr()); +// } - m_upToDate = true; - m_updating = false; - - if (callback) callback(UpdateStatus::Finished, "", 100); - return; - } - - // update if forced or latest commit has - // different sha - if (force || currentCommitSHA != upcomingCommitSHA) { - // save new sha in file - (void)utils::file::writeString(indexDir / "current", upcomingCommitSHA); - - web::AsyncWebRequest() - .join("index-download") - .fetch("https://github.com/geode-sdk/mods/zipball/main") - .into(indexDir / "index.zip") - .then([this, indexDir, callback](auto) { - // delete old index - try { - if (ghc::filesystem::exists(indexDir / "index")) { - ghc::filesystem::remove_all(indexDir / "index"); - } - } - catch (std::exception& e) { - RETURN_ERROR("Unable to delete old index " + std::string(e.what())); - } - - // unzip new index - auto unzip = file::unzipTo(indexDir / "index.zip", indexDir); - if (!unzip) { - RETURN_ERROR(unzip.unwrapErr()); - } - - // update index - auto err = this->updateIndexFromLocalCache(); - if (!err) { - RETURN_ERROR(err.unwrapErr()); - } - - m_upToDate = true; - m_updating = false; - - if (callback) callback(UpdateStatus::Finished, "", 100); - }) - .expect([callback](std::string const& err) { - RETURN_ERROR(err); - }) - .progress([callback](web::SentAsyncWebRequest& req, double now, double total) { - if (callback) - callback( - UpdateStatus::Progress, "Downloading", - static_cast(now / total * 100.0) - ); - }); - } - else { - auto err = this->updateIndexFromLocalCache(); - if (!err) { - RETURN_ERROR(err.unwrapErr()); - } - - m_upToDate = true; - m_updating = false; - - if (callback) callback(UpdateStatus::Finished, "", 100); - } - }) - .expect([callback](std::string const& err) { - RETURN_ERROR(err); - }) - .progress([callback](web::SentAsyncWebRequest& req, double now, double total) { - if (callback) - callback( - UpdateStatus::Progress, "Downloading", static_cast(now / total * 100.0) - ); - }); -} - -void Index::addIndexItemFromFolder(ghc::filesystem::path const& dir) { - if (ghc::filesystem::exists(dir / "index.json")) { - auto readJson = readJSON(dir / "index.json"); - if (!readJson) { - log::warn("Error reading index.json: {}, skipping", readJson.unwrapErr()); - return; - } - auto json = readJson.unwrap(); - if (!json.is_object()) { - log::warn("[index.json] is not an object, skipping"); - return; - } - - auto infoRes = ModInfo::createFromFile(dir / "mod.json"); - if (!infoRes) { - log::warn("{}: {}, skipping", dir, infoRes.unwrapErr()); - return; - } - auto info = infoRes.unwrap(); - - // make sure only latest version is present in index - auto old = std::find_if(m_items.begin(), m_items.end(), [info](IndexItem const& item) { - return item.m_info.m_id == info.m_id; - }); - if (old != m_items.end()) { - // this one is newer - if (old->m_info.m_version < info.m_version) { - m_items.erase(old); - } else { - log::warn( - "Found older version of ({} < {}) of {}, skipping", - info.m_version, old->m_info.m_version, info.m_id - ); - return; - } - } - - IndexItem item; - - item.m_path = dir; - item.m_info = info; - - if (!json.contains("download") || !json["download"].is_object()) { - log::warn("[index.json].download is not an object, skipping"); - return; - } - -#define REQUIRE_DOWNLOAD_KEY(key, type) \ - if (!download.contains(key) || !download[key].is_##type()) { \ - log::warn("[index.json].download." key " is not a " #type ", skipping"); \ - return; \ - } - - try { - auto download = json["download"]; - - REQUIRE_DOWNLOAD_KEY("url", string); - REQUIRE_DOWNLOAD_KEY("name", string); - REQUIRE_DOWNLOAD_KEY("hash", string); - REQUIRE_DOWNLOAD_KEY("platforms", array); - - item.m_download.m_url = download["url"]; - item.m_download.m_filename = download["name"]; - item.m_download.m_hash = download["hash"]; - for (auto& platform : download["platforms"]) { - item.m_download.m_platforms.insert(platformFromString(platform)); - } - - if (json.contains("categories")) { - if (!json["categories"].is_array()) { - log::warn("[index.json].categories is not an array, skipping"); - return; - } - item.m_categories = json["categories"].template get>(); - m_categories.insert(item.m_categories.begin(), item.m_categories.end()); - } - } - catch (std::exception& e) { - log::warn("[index.json] parsing error: {}, skipping", e.what()); - return; - } - - m_items.push_back(item); - } - else { - log::warn("Index directory {} is missing index.json, skipping", dir); - } -} - -Result<> Index::updateIndexFromLocalCache() { - m_items.clear(); - auto baseIndexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY; - - // load geode.json (index settings) - if (auto baseIndexJson = readJSON(baseIndexDir / "geode.json")) { - auto json = baseIndexJson.unwrap(); - auto checker = JsonChecker(json); - checker.root("[index/geode.json]").obj().has("featured").into(m_featured); - } - - // load index mods - auto modsDir = baseIndexDir / "index"; - if (ghc::filesystem::exists(modsDir)) { - for (auto const& dir : ghc::filesystem::directory_iterator(modsDir)) { - if (ghc::filesystem::is_directory(dir)) { - this->addIndexItemFromFolder(dir); - } - } - log::info("Index updated"); - return Ok(); - } - else { - return Err( - "Index appears not to have been " - "downloaded, or is fully empty" - ); - } -} - -std::vector Index::getItems() const { - return m_items; -} - -std::unordered_set Index::getCategories() const { - return m_categories; -} - -bool Index::isKnownItem(std::string const& id) const { - for (auto& item : m_items) { - if (item.m_info.m_id == id) return true; - } - return false; -} - -IndexItem Index::getKnownItem(std::string const& id) const { - for (auto& item : m_items) { - if (item.m_info.m_id == id) { - return item; - } - } - return IndexItem(); -} - -struct UninstalledDependency { - std::string m_id; - bool m_isInIndex; -}; - -static void getUninstalledDependenciesRecursive( - ModInfo const& info, std::vector& deps -) { - for (auto& dep : info.m_dependencies) { - UninstalledDependency d; - d.m_isInIndex = Index::get()->isKnownItem(dep.m_id); - if (!Loader::get()->isModInstalled(dep.m_id)) { - d.m_id = dep.m_id; - deps.push_back(d); - } - if (d.m_isInIndex) { - getUninstalledDependenciesRecursive(Index::get()->getKnownItem(dep.m_id).m_info, deps); - } - } -} - -Result> Index::checkDependenciesForItem(IndexItem const& item) { - // todo: check versions - std::vector deps; - getUninstalledDependenciesRecursive(item.m_info, deps); - if (deps.size()) { - std::vector unknownDeps; - for (auto& dep : deps) { - if (!dep.m_isInIndex) { - unknownDeps.push_back(dep.m_id); - } - } - if (unknownDeps.size()) { - std::string list = ""; - for (auto& ud : unknownDeps) { - list += "" + ud + ", "; - } - list.pop_back(); - list.pop_back(); - return Err( - "This mod or its dependencies depends on the " - "following unknown mods: " + - list + - ". You will have " - "to manually install these mods before you can install " - "this one." - ); - } - std::vector list = {}; - for (auto& d : deps) { - list.push_back(d.m_id); - } - list.push_back(item.m_info.m_id); - return Ok(list); - } - else { - return Ok>({ item.m_info.m_id }); - } -} - -Result Index::installItems(std::vector const& items) { - std::vector ids {}; - for (auto& item : items) { - if (!item.m_download.m_platforms.count(GEODE_PLATFORM_TARGET)) { - return Err( - "This mod is not available on your " - "current platform \"" GEODE_PLATFORM_NAME "\" - Sorry! :(" - ); - } - if (!item.m_download.m_url.size()) { - return Err( - "Download URL not set! Report this bug to " - "the Geode developers - this should not happen, ever." - ); - } - if (!item.m_download.m_filename.size()) { - return Err( - "Download filename not set! Report this bug to " - "the Geode developers - this should not happen, ever." - ); - } - if (!item.m_download.m_hash.size()) { - return Err( - "Checksum not set! Report this bug to " - "the Geode developers - this should not happen, ever." - ); - } - GEODE_UNWRAP_INTO(auto list, checkDependenciesForItem(item)); - ranges::push(ids, list); - } - auto ret = std::make_shared(std::unordered_set(ids.begin(), ids.end())); - m_installations.insert(ret); - return Ok(ret); -} - -Result Index::installItem(IndexItem const& item) { - return this->installItems({ item }); -} - -bool Index::isUpdateAvailableForItem(std::string const& id) const { - if (!this->isKnownItem(id)) { - return false; - } - return this->isUpdateAvailableForItem(this->getKnownItem(id)); -} - -bool Index::isUpdateAvailableForItem(IndexItem const& item) const { - if (!Loader::get()->isModInstalled(item.m_info.m_id)) { - return false; - } - // has the mod been updated (but update not yet been applied by restarting game) - if (m_updated.count(item.m_info.m_id)) { - return false; - } - return item.m_info.m_version > Loader::get()->getInstalledMod(item.m_info.m_id)->getVersion(); -} - -bool Index::areUpdatesAvailable() const { - for (auto& item : m_items) { - if (this->isUpdateAvailableForItem(item.m_info.m_id)) { - return true; - } - } - return false; -} - -Result Index::installAllUpdates() { - // find items that need updating - std::vector itemsToUpdate {}; - for (auto& item : m_items) { - if (this->isUpdateAvailableForItem(item)) { - itemsToUpdate.push_back(item); - } - } - return this->installItems(itemsToUpdate); -} - -std::vector Index::getRunningInstallations() const { - return std::vector(m_installations.begin(), m_installations.end()); -} - -InstallHandle Index::isInstallingItem(std::string const& id) { - for (auto& inst : m_installations) { - if (inst->m_toInstall.count(id)) { - return inst; - } - } - return nullptr; -} - -std::unordered_set InstallItems::toInstall() const { - return m_toInstall; -} - -InstallItems::CallbackID InstallItems::join(ItemInstallCallback callback) { - // already finished? - if (m_started && this->finished()) { - callback(shared_from_this(), UpdateStatus::Finished, "", 100); - return 0; - } - // start at one because 0 means invalid callback - static CallbackID COUNTER = 1; - if (callback) { - auto id = COUNTER++; - m_callbacks.insert({ id, callback }); - return id; - } - return 0; -} - -void InstallItems::leave(InstallItems::CallbackID id) { - m_callbacks.erase(id); -} - -void InstallItems::post(UpdateStatus status, std::string const& info, uint8_t progress) { - for (auto& [_, cb] : m_callbacks) { - cb(shared_from_this(), status, info, progress); - } -} - -void InstallItems::progress(std::string const& info, uint8_t progress) { - this->post(UpdateStatus::Progress, info, progress); -} - -void InstallItems::error(std::string const& info) { - this->post(UpdateStatus::Failed, info, 0); -} - -void InstallItems::finish(bool replaceFiles) { - // move files from temp dir to geode directory - auto tempDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY / "temp"; - for (auto& file : ghc::filesystem::directory_iterator(tempDir)) { - try { - auto modDir = Loader::get()->getGeodeDirectory() / "mods"; - auto targetFile = modDir / file.path().filename(); - auto targetName = file.path().stem(); - - if (!replaceFiles) { - // find valid filename that doesn't exist yet - auto filename = ghc::filesystem::path(targetName).replace_extension("").string(); - - size_t number = 0; - while (ghc::filesystem::exists(targetFile)) { - targetFile = modDir / (filename + std::to_string(number) + ".geode"); - number++; - } - } - - // move file - ghc::filesystem::rename(file, targetFile); - } - catch (std::exception& e) { - try { - ghc::filesystem::remove_all(tempDir); - } - catch (...) { - } - return this->error( - "Unable to move downloaded file to mods directory: \"" + std::string(e.what()) + - " \" " - "(This might be due to insufficient permissions to " - "write files under SteamLibrary, try running GD as " - "administrator)" - ); - } - } - - // load mods - (void)Loader::get()->refreshModsList(); - - // finished - this->post(UpdateStatus::Finished, "", 100); - - // let index know these mods have been updated - for (auto& inst : m_toInstall) { - Index::get()->m_updated.insert(inst); - } - - // if no one is listening, show a popup anyway - if (!m_callbacks.size()) { - FLAlertLayer::create( - "Mods installed", - "The following mods have been installed: " + - ranges::join(m_toInstall, std::string(",")) + - "\n" - "Please restart the game to apply", - "OK" - ) - ->show(); - } - - // no longer need to ensure aliveness - Index::get()->m_installations.erase(shared_from_this()); -} - -InstallItems::CallbackID InstallItems::start(ItemInstallCallback callback, bool replaceFiles) { - auto id = this->join(callback); - - // check if started already, if so, behave like join - if (m_started) return id; - m_started = true; - - for (auto& inst : m_toInstall) { - // by virtue of running this function we know item must be valid - auto item = Index::get()->getKnownItem(inst); - - auto indexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY; - (void)file::createDirectoryAll(indexDir / "temp"); - auto tempFile = indexDir / "temp" / item.m_download.m_filename; - - m_downloaded.push_back(tempFile); - - auto handle = - web::AsyncWebRequest() - .join("install_mod_" + inst) - .fetch(item.m_download.m_url) - .into(tempFile) - .then([this, replaceFiles, item, inst, indexDir, tempFile](auto) { - // check for 404 - auto notFound = utils::file::readString(tempFile); - if (notFound && notFound.unwrap() == "Not Found") { - try { - ghc::filesystem::remove(tempFile); - } - catch (...) { - } - return this->error( - "Binary file download returned \"Not found\". Report " - "this to the Geode development team." - ); - } - - // verify checksum - this->progress("Verifying", 100); - if (::calculateHash(tempFile) != item.m_download.m_hash) { - try { - ghc::filesystem::remove(tempFile); - } - catch (...) { - } - return this->error( - "Checksum mismatch! (Downloaded file did not match what " - "was expected. Try again, and if the download fails another time, " - "report this to the Geode development team." - ); - } - - // finished() just checks if the web requests are done - if (this->finished()) { - this->finish(replaceFiles); - } - }) - .expect([this, inst](std::string const& error) { - this->error(error); - this->cancel(); - }) - .cancelled([this, item](auto&) { - this->cancel(); - }) - .progress([this, inst](web::SentAsyncWebRequest&, double now, double total) { - this->progress("Downloading binary", static_cast(now / total * 100.0)); - }) - .send(); - - m_handles.push_back(handle); - } - // manage installation in the index until it's finished so - // even if no one listens to it it doesn't get freed from - // memory - Index::get()->m_installations.insert(shared_from_this()); - - return id; -} - -bool InstallItems::finished() const { - for (auto& inst : m_handles) { - if (!inst->finished()) { - return false; - } - } - return true; -} - -void InstallItems::cancel() { - for (auto& inst : m_handles) { - inst->cancel(); - } -} +// m_upToDate = true; +// m_updating = false; + +// if (callback) callback(UpdateStatus::Finished, "", 100); +// return; +// } + +// // update if forced or latest commit has +// // different sha +// if (force || currentCommitSHA != upcomingCommitSHA) { +// // save new sha in file +// (void)utils::file::writeString(indexDir / "current", upcomingCommitSHA); + +// web::AsyncWebRequest() +// .join("index-download") +// .fetch("https://github.com/geode-sdk/mods/zipball/main") +// .into(indexDir / "index.zip") +// .then([this, indexDir, callback](auto) { +// // delete old index +// try { +// if (ghc::filesystem::exists(indexDir / "index")) { +// ghc::filesystem::remove_all(indexDir / "index"); +// } +// } +// catch (std::exception& e) { +// RETURN_ERROR("Unable to delete old index " + std::string(e.what())); +// } + +// // unzip new index +// auto unzip = file::unzipTo(indexDir / "index.zip", indexDir); +// if (!unzip) { +// RETURN_ERROR(unzip.unwrapErr()); +// } + +// // update index +// auto err = this->updateIndexFromLocalCache(); +// if (!err) { +// RETURN_ERROR(err.unwrapErr()); +// } + +// m_upToDate = true; +// m_updating = false; + +// if (callback) callback(UpdateStatus::Finished, "", 100); +// }) +// .expect([callback](std::string const& err) { +// RETURN_ERROR(err); +// }) +// .progress([callback](web::SentAsyncWebRequest& req, double now, double total) { +// if (callback) +// callback( +// UpdateStatus::Progress, "Downloading", +// static_cast(now / total * 100.0) +// ); +// }); +// } +// else { +// auto err = this->updateIndexFromLocalCache(); +// if (!err) { +// RETURN_ERROR(err.unwrapErr()); +// } + +// m_upToDate = true; +// m_updating = false; + +// if (callback) callback(UpdateStatus::Finished, "", 100); +// } +// }) +// .expect([callback](std::string const& err) { +// RETURN_ERROR(err); +// }) +// .progress([callback](web::SentAsyncWebRequest& req, double now, double total) { +// if (callback) +// callback( +// UpdateStatus::Progress, "Downloading", static_cast(now / total * 100.0) +// ); +// }); +// } + +// void Index::addIndexItemFromFolder(ghc::filesystem::path const& dir) { +// if (ghc::filesystem::exists(dir / "index.json")) { +// auto readJson = readJSON(dir / "index.json"); +// if (!readJson) { +// log::warn("Error reading index.json: {}, skipping", readJson.unwrapErr()); +// return; +// } +// auto json = readJson.unwrap(); +// if (!json.is_object()) { +// log::warn("[index.json] is not an object, skipping"); +// return; +// } + +// auto infoRes = ModInfo::createFromFile(dir / "mod.json"); +// if (!infoRes) { +// log::warn("{}: {}, skipping", dir, infoRes.unwrapErr()); +// return; +// } +// auto info = infoRes.unwrap(); + +// // make sure only latest version is present in index +// auto old = std::find_if(m_items.begin(), m_items.end(), [info](IndexItem const& item) { +// return item.m_info.m_id == info.m_id; +// }); +// if (old != m_items.end()) { +// // this one is newer +// if (old->m_info.m_version < info.m_version) { +// m_items.erase(old); +// } else { +// log::warn( +// "Found older version of ({} < {}) of {}, skipping", +// info.m_version, old->m_info.m_version, info.m_id +// ); +// return; +// } +// } + +// IndexItem item; + +// item.m_path = dir; +// item.m_info = info; + +// if (!json.contains("download") || !json["download"].is_object()) { +// log::warn("[index.json].download is not an object, skipping"); +// return; +// } + +// #define REQUIRE_DOWNLOAD_KEY(key, type) \ +// if (!download.contains(key) || !download[key].is_##type()) { \ +// log::warn("[index.json].download." key " is not a " #type ", skipping"); \ +// return; \ +// } + +// try { +// auto download = json["download"]; + +// REQUIRE_DOWNLOAD_KEY("url", string); +// REQUIRE_DOWNLOAD_KEY("name", string); +// REQUIRE_DOWNLOAD_KEY("hash", string); +// REQUIRE_DOWNLOAD_KEY("platforms", array); + +// item.m_download.m_url = download["url"]; +// item.m_download.m_filename = download["name"]; +// item.m_download.m_hash = download["hash"]; +// for (auto& platform : download["platforms"]) { +// item.m_download.m_platforms.insert(platformFromString(platform)); +// } + +// if (json.contains("categories")) { +// if (!json["categories"].is_array()) { +// log::warn("[index.json].categories is not an array, skipping"); +// return; +// } +// item.m_categories = json["categories"].template get>(); +// m_categories.insert(item.m_categories.begin(), item.m_categories.end()); +// } +// } +// catch (std::exception& e) { +// log::warn("[index.json] parsing error: {}, skipping", e.what()); +// return; +// } + +// m_items.push_back(item); +// } +// else { +// log::warn("Index directory {} is missing index.json, skipping", dir); +// } +// } + +// Result<> Index::updateIndexFromLocalCache() { +// m_items.clear(); +// auto baseIndexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY; + +// // load geode.json (index settings) +// if (auto baseIndexJson = readJSON(baseIndexDir / "geode.json")) { +// auto json = baseIndexJson.unwrap(); +// auto checker = JsonChecker(json); +// checker.root("[index/geode.json]").obj().has("featured").into(m_featured); +// } + +// // load index mods +// auto modsDir = baseIndexDir / "index"; +// if (ghc::filesystem::exists(modsDir)) { +// for (auto const& dir : ghc::filesystem::directory_iterator(modsDir)) { +// if (ghc::filesystem::is_directory(dir)) { +// this->addIndexItemFromFolder(dir); +// } +// } +// log::info("Index updated"); +// return Ok(); +// } +// else { +// return Err( +// "Index appears not to have been " +// "downloaded, or is fully empty" +// ); +// } +// } + +// std::vector Index::getItems() const { +// return m_items; +// } + +// std::unordered_set Index::getCategories() const { +// return m_categories; +// } + +// bool Index::isKnownItem(std::string const& id) const { +// for (auto& item : m_items) { +// if (item.m_info.m_id == id) return true; +// } +// return false; +// } + +// IndexItem Index::getKnownItem(std::string const& id) const { +// for (auto& item : m_items) { +// if (item.m_info.m_id == id) { +// return item; +// } +// } +// return IndexItem(); +// } + +// struct UninstalledDependency { +// std::string m_id; +// bool m_isInIndex; +// }; + +// static void getUninstalledDependenciesRecursive( +// ModInfo const& info, std::vector& deps +// ) { +// for (auto& dep : info.m_dependencies) { +// UninstalledDependency d; +// d.m_isInIndex = Index::get()->isKnownItem(dep.m_id); +// if (!Loader::get()->isModInstalled(dep.m_id)) { +// d.m_id = dep.m_id; +// deps.push_back(d); +// } +// if (d.m_isInIndex) { +// getUninstalledDependenciesRecursive(Index::get()->getKnownItem(dep.m_id).m_info, deps); +// } +// } +// } + +// Result> Index::checkDependenciesForItem(IndexItem const& item) { +// // todo: check versions +// std::vector deps; +// getUninstalledDependenciesRecursive(item.m_info, deps); +// if (deps.size()) { +// std::vector unknownDeps; +// for (auto& dep : deps) { +// if (!dep.m_isInIndex) { +// unknownDeps.push_back(dep.m_id); +// } +// } +// if (unknownDeps.size()) { +// std::string list = ""; +// for (auto& ud : unknownDeps) { +// list += "" + ud + ", "; +// } +// list.pop_back(); +// list.pop_back(); +// return Err( +// "This mod or its dependencies depends on the " +// "following unknown mods: " + +// list + +// ". You will have " +// "to manually install these mods before you can install " +// "this one." +// ); +// } +// std::vector list = {}; +// for (auto& d : deps) { +// list.push_back(d.m_id); +// } +// list.push_back(item.m_info.m_id); +// return Ok(list); +// } +// else { +// return Ok>({ item.m_info.m_id }); +// } +// } + +// Result Index::installItems(std::vector const& items) { +// std::vector ids {}; +// for (auto& item : items) { +// if (!item.m_download.m_platforms.count(GEODE_PLATFORM_TARGET)) { +// return Err( +// "This mod is not available on your " +// "current platform \"" GEODE_PLATFORM_NAME "\" - Sorry! :(" +// ); +// } +// if (!item.m_download.m_url.size()) { +// return Err( +// "Download URL not set! Report this bug to " +// "the Geode developers - this should not happen, ever." +// ); +// } +// if (!item.m_download.m_filename.size()) { +// return Err( +// "Download filename not set! Report this bug to " +// "the Geode developers - this should not happen, ever." +// ); +// } +// if (!item.m_download.m_hash.size()) { +// return Err( +// "Checksum not set! Report this bug to " +// "the Geode developers - this should not happen, ever." +// ); +// } +// GEODE_UNWRAP_INTO(auto list, checkDependenciesForItem(item)); +// ranges::push(ids, list); +// } +// auto ret = std::make_shared(std::unordered_set(ids.begin(), ids.end())); +// m_installations.insert(ret); +// return Ok(ret); +// } + +// Result Index::installItem(IndexItem const& item) { +// return this->installItems({ item }); +// } + +// bool Index::isUpdateAvailableForItem(std::string const& id) const { +// if (!this->isKnownItem(id)) { +// return false; +// } +// return this->isUpdateAvailableForItem(this->getKnownItem(id)); +// } + +// bool Index::isUpdateAvailableForItem(IndexItem const& item) const { +// if (!Loader::get()->isModInstalled(item.m_info.m_id)) { +// return false; +// } +// // has the mod been updated (but update not yet been applied by restarting game) +// if (m_updated.count(item.m_info.m_id)) { +// return false; +// } +// return item.m_info.m_version > Loader::get()->getInstalledMod(item.m_info.m_id)->getVersion(); +// } + +// bool Index::areUpdatesAvailable() const { +// for (auto& item : m_items) { +// if (this->isUpdateAvailableForItem(item.m_info.m_id)) { +// return true; +// } +// } +// return false; +// } + +// Result Index::installAllUpdates() { +// // find items that need updating +// std::vector itemsToUpdate {}; +// for (auto& item : m_items) { +// if (this->isUpdateAvailableForItem(item)) { +// itemsToUpdate.push_back(item); +// } +// } +// return this->installItems(itemsToUpdate); +// } + +// std::vector Index::getRunningInstallations() const { +// return std::vector(m_installations.begin(), m_installations.end()); +// } + +// InstallHandle Index::isInstallingItem(std::string const& id) { +// for (auto& inst : m_installations) { +// if (inst->m_toInstall.count(id)) { +// return inst; +// } +// } +// return nullptr; +// } + +// std::unordered_set InstallItems::toInstall() const { +// return m_toInstall; +// } + +// InstallItems::CallbackID InstallItems::join(ItemInstallCallback callback) { +// // already finished? +// if (m_started && this->finished()) { +// callback(shared_from_this(), UpdateStatus::Finished, "", 100); +// return 0; +// } +// // start at one because 0 means invalid callback +// static CallbackID COUNTER = 1; +// if (callback) { +// auto id = COUNTER++; +// m_callbacks.insert({ id, callback }); +// return id; +// } +// return 0; +// } + +// void InstallItems::leave(InstallItems::CallbackID id) { +// m_callbacks.erase(id); +// } + +// void InstallItems::post(UpdateStatus status, std::string const& info, uint8_t progress) { +// for (auto& [_, cb] : m_callbacks) { +// cb(shared_from_this(), status, info, progress); +// } +// } + +// void InstallItems::progress(std::string const& info, uint8_t progress) { +// this->post(UpdateStatus::Progress, info, progress); +// } + +// void InstallItems::error(std::string const& info) { +// this->post(UpdateStatus::Failed, info, 0); +// } + +// void InstallItems::finish(bool replaceFiles) { +// // move files from temp dir to geode directory +// auto tempDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY / "temp"; +// for (auto& file : ghc::filesystem::directory_iterator(tempDir)) { +// try { +// auto modDir = Loader::get()->getGeodeDirectory() / "mods"; +// auto targetFile = modDir / file.path().filename(); +// auto targetName = file.path().stem(); + +// if (!replaceFiles) { +// // find valid filename that doesn't exist yet +// auto filename = ghc::filesystem::path(targetName).replace_extension("").string(); + +// size_t number = 0; +// while (ghc::filesystem::exists(targetFile)) { +// targetFile = modDir / (filename + std::to_string(number) + ".geode"); +// number++; +// } +// } + +// // move file +// ghc::filesystem::rename(file, targetFile); +// } +// catch (std::exception& e) { +// try { +// ghc::filesystem::remove_all(tempDir); +// } +// catch (...) { +// } +// return this->error( +// "Unable to move downloaded file to mods directory: \"" + std::string(e.what()) + +// " \" " +// "(This might be due to insufficient permissions to " +// "write files under SteamLibrary, try running GD as " +// "administrator)" +// ); +// } +// } + +// // load mods +// (void)Loader::get()->refreshModsList(); + +// // finished +// this->post(UpdateStatus::Finished, "", 100); + +// // let index know these mods have been updated +// for (auto& inst : m_toInstall) { +// Index::get()->m_updated.insert(inst); +// } + +// // if no one is listening, show a popup anyway +// if (!m_callbacks.size()) { +// FLAlertLayer::create( +// "Mods installed", +// "The following mods have been installed: " + +// ranges::join(m_toInstall, std::string(",")) + +// "\n" +// "Please restart the game to apply", +// "OK" +// ) +// ->show(); +// } + +// // no longer need to ensure aliveness +// Index::get()->m_installations.erase(shared_from_this()); +// } + +// InstallItems::CallbackID InstallItems::start(ItemInstallCallback callback, bool replaceFiles) { +// auto id = this->join(callback); + +// // check if started already, if so, behave like join +// if (m_started) return id; +// m_started = true; + +// for (auto& inst : m_toInstall) { +// // by virtue of running this function we know item must be valid +// auto item = Index::get()->getKnownItem(inst); + +// auto indexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY; +// (void)file::createDirectoryAll(indexDir / "temp"); +// auto tempFile = indexDir / "temp" / item.m_download.m_filename; + +// m_downloaded.push_back(tempFile); + +// auto handle = +// web::AsyncWebRequest() +// .join("install_mod_" + inst) +// .fetch(item.m_download.m_url) +// .into(tempFile) +// .then([this, replaceFiles, item, inst, indexDir, tempFile](auto) { +// // check for 404 +// auto notFound = utils::file::readString(tempFile); +// if (notFound && notFound.unwrap() == "Not Found") { +// try { +// ghc::filesystem::remove(tempFile); +// } +// catch (...) { +// } +// return this->error( +// "Binary file download returned \"Not found\". Report " +// "this to the Geode development team." +// ); +// } + +// // verify checksum +// this->progress("Verifying", 100); +// if (::calculateHash(tempFile) != item.m_download.m_hash) { +// try { +// ghc::filesystem::remove(tempFile); +// } +// catch (...) { +// } +// return this->error( +// "Checksum mismatch! (Downloaded file did not match what " +// "was expected. Try again, and if the download fails another time, " +// "report this to the Geode development team." +// ); +// } + +// // finished() just checks if the web requests are done +// if (this->finished()) { +// this->finish(replaceFiles); +// } +// }) +// .expect([this, inst](std::string const& error) { +// this->error(error); +// this->cancel(); +// }) +// .cancelled([this, item](auto&) { +// this->cancel(); +// }) +// .progress([this, inst](web::SentAsyncWebRequest&, double now, double total) { +// this->progress("Downloading binary", static_cast(now / total * 100.0)); +// }) +// .send(); + +// m_handles.push_back(handle); +// } +// // manage installation in the index until it's finished so +// // even if no one listens to it it doesn't get freed from +// // memory +// Index::get()->m_installations.insert(shared_from_this()); + +// return id; +// } + +// bool InstallItems::finished() const { +// for (auto& inst : m_handles) { +// if (!inst->finished()) { +// return false; +// } +// } +// return true; +// } + +// void InstallItems::cancel() { +// for (auto& inst : m_handles) { +// inst->cancel(); +// } +// } diff --git a/loader/src/internal/InternalLoader.cpp b/loader/src/internal/InternalLoader.cpp index b9e7ef86..4a718741 100644 --- a/loader/src/internal/InternalLoader.cpp +++ b/loader/src/internal/InternalLoader.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -94,9 +95,8 @@ void InternalLoader::loadInfoAlerts(nlohmann::json& json) { void InternalLoader::downloadLoaderResources(IndexUpdateCallback callback) { auto version = this->getVersion().toString(); - auto tempResourcesZip = this->getGeodeDirectory() / GEODE_RESOURCE_DIRECTORY / "new.zip"; - auto resourcesDir = - this->getGeodeDirectory() / GEODE_RESOURCE_DIRECTORY / InternalMod::get()->getID(); + auto tempResourcesZip = dirs::getTempDir() / "new.zip"; + auto resourcesDir = dirs::getGeodeResourcesDir() / InternalMod::get()->getID(); web::AsyncWebRequest() .join("update-geode-loader-resources") @@ -142,8 +142,7 @@ bool InternalLoader::verifyLoaderResources(IndexUpdateCallback callback) { } // geode/resources/geode.loader - auto resourcesDir = - this->getGeodeDirectory() / GEODE_RESOURCE_DIRECTORY / InternalMod::get()->getID(); + auto resourcesDir = dirs::getGeodeResourcesDir() / InternalMod::get()->getID(); // if the resources dir doesn't exist, then it's probably incorrect if (!(ghc::filesystem::exists(resourcesDir) && ghc::filesystem::is_directory(resourcesDir))) { diff --git a/loader/src/internal/InternalMod.cpp b/loader/src/internal/InternalMod.cpp index bbb70a57..9575ebdb 100644 --- a/loader/src/internal/InternalMod.cpp +++ b/loader/src/internal/InternalMod.cpp @@ -1,5 +1,5 @@ #include "InternalMod.hpp" - +#include #include "InternalLoader.hpp" #include "about.hpp" @@ -41,7 +41,7 @@ static ModInfo getInternalModInfo() { } InternalMod::InternalMod() : Mod(getInternalModInfo()) { - m_saveDirPath = Loader::get()->getGeodeSaveDirectory() / GEODE_MOD_DIRECTORY / m_info.m_id; + m_saveDirPath = dirs::getModsSaveDir() / m_info.m_id; ghc::filesystem::create_directories(m_saveDirPath); diff --git a/loader/src/internal/crashlog.hpp b/loader/src/internal/crashlog.hpp index e91c74cd..770d6725 100644 --- a/loader/src/internal/crashlog.hpp +++ b/loader/src/internal/crashlog.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include /** @@ -22,5 +23,5 @@ namespace crashlog { * @returns Path to the directory, or an empty string if the platform does * not support crash logs */ - std::string GEODE_DLL getCrashLogDirectory(); + ghc::filesystem::path GEODE_DLL getCrashLogDirectory(); } diff --git a/loader/src/loader/Loader.cpp b/loader/src/loader/Loader.cpp index d5ff4bcf..90da5472 100644 --- a/loader/src/loader/Loader.cpp +++ b/loader/src/loader/Loader.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -20,9 +21,8 @@ Loader::~Loader() { } m_mods.clear(); log::Logs::clear(); - ghc::filesystem::remove_all( - this->getGeodeDirectory() / GEODE_TEMP_DIRECTORY - ); + ghc::filesystem::remove_all(dirs::getModRuntimeDir()); + ghc::filesystem::remove_all(dirs::getTempDir()); } VersionInfo Loader::getVersion() { @@ -49,24 +49,19 @@ bool Loader::isModVersionSupported(VersionInfo const& version) { } void Loader::createDirectories() { - auto modDir = this->getGeodeDirectory() / GEODE_MOD_DIRECTORY; - auto logDir = this->getGeodeDirectory() / GEODE_LOG_DIRECTORY; - auto resDir = this->getGeodeDirectory() / GEODE_RESOURCE_DIRECTORY; - auto tempDir = this->getGeodeDirectory() / GEODE_TEMP_DIRECTORY; - auto confDir = this->getGeodeDirectory() / GEODE_CONFIG_DIRECTORY; - #ifdef GEODE_IS_MACOS - ghc::filesystem::create_directory(this->getSaveDirectory()); + ghc::filesystem::create_directory(dirs::getSaveDir()); #endif - ghc::filesystem::create_directories(resDir); - ghc::filesystem::create_directory(confDir); - ghc::filesystem::create_directory(modDir); - ghc::filesystem::create_directory(logDir); - ghc::filesystem::create_directory(tempDir); + ghc::filesystem::create_directories(dirs::getGeodeResourcesDir()); + ghc::filesystem::create_directory(dirs::getModConfigDir()); + ghc::filesystem::create_directory(dirs::getModsDir()); + ghc::filesystem::create_directory(dirs::getGeodeLogDir()); + ghc::filesystem::create_directory(dirs::getTempDir()); + ghc::filesystem::create_directory(dirs::getModRuntimeDir()); - if (!ranges::contains(m_modSearchDirectories, modDir)) { - m_modSearchDirectories.push_back(modDir); + if (!ranges::contains(m_modSearchDirectories, dirs::getModsDir())) { + m_modSearchDirectories.push_back(dirs::getModsDir()); } } @@ -146,8 +141,7 @@ Result Loader::loadModFromInfo(ModInfo const& info) { // add mod resources this->queueInGDThread([this, mod]() { - auto searchPath = this->getGeodeDirectory() / - GEODE_TEMP_DIRECTORY / mod->getID() / "resources"; + auto searchPath = dirs::getModRuntimeDir() / mod->getID() / "resources"; CCFileUtils::get()->addSearchPath(searchPath.string().c_str()); this->updateModResources(mod); @@ -326,10 +320,6 @@ bool Loader::didLastLaunchCrash() const { return crashlog::didLastLaunchCrash(); } -ghc::filesystem::path Loader::getCrashLogDirectory() const { - return crashlog::getCrashLogDirectory(); -} - void Loader::openPlatformConsole() { InternalLoader::get()->openPlatformConsole(); } @@ -343,7 +333,7 @@ void Loader::updateModResources(Mod* mod) { return; } - auto searchPath = this->getGeodeDirectory() / GEODE_TEMP_DIRECTORY / mod->getID() / "resources"; + auto searchPath = dirs::getModRuntimeDir() / mod->getID() / "resources"; log::debug("Adding resources for {}", mod->getID()); @@ -369,12 +359,8 @@ void Loader::updateModResources(Mod* mod) { } void Loader::addSearchPaths() { - CCFileUtils::get()->addPriorityPath( - (this->getGeodeDirectory() / GEODE_RESOURCE_DIRECTORY).string().c_str() - ); - CCFileUtils::get()->addPriorityPath( - (this->getGeodeDirectory() / GEODE_TEMP_DIRECTORY).string().c_str() - ); + CCFileUtils::get()->addPriorityPath(dirs::getGeodeResourcesDir().string().c_str()); + CCFileUtils::get()->addPriorityPath(dirs::getModRuntimeDir().string().c_str()); } void Loader::updateResources() { @@ -388,32 +374,3 @@ void Loader::updateResources() { this->updateModResources(mod); } } - -ghc::filesystem::path Loader::getGameDirectory() const { - return ghc::filesystem::path(CCFileUtils::sharedFileUtils()->getWritablePath2().c_str()); -} - -ghc::filesystem::path Loader::getSaveDirectory() const { - #ifdef GEODE_IS_MACOS - // not using ~/Library/Caches - return ghc::filesystem::path("/Users/Shared/Geode"); - #elif defined(GEODE_IS_WINDOWS) - return ghc::filesystem::path( - ghc::filesystem::weakly_canonical( - CCFileUtils::sharedFileUtils()->getWritablePath().c_str() - ).string() - ); - #else - return ghc::filesystem::path( - CCFileUtils::sharedFileUtils()->getWritablePath().c_str() - ); - #endif -} - -ghc::filesystem::path Loader::getGeodeDirectory() const { - return this->getGameDirectory() / GEODE_DIRECTORY; -} - -ghc::filesystem::path Loader::getGeodeSaveDirectory() const { - return this->getSaveDirectory() / GEODE_DIRECTORY; -} diff --git a/loader/src/loader/Log.cpp b/loader/src/loader/Log.cpp index f4cda168..f3092882 100644 --- a/loader/src/loader/Log.cpp +++ b/loader/src/loader/Log.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -115,11 +115,7 @@ std::string Log::toString(bool logTime) const { } void Logs::setup() { - s_logStream = std::ofstream( - Loader::get()->getGeodeDirectory() / - GEODE_LOG_DIRECTORY / - log::generateLogName() - ); + s_logStream = std::ofstream(dirs::getGeodeLogDir() / log::generateLogName()); } void Logs::push(Log&& log) { diff --git a/loader/src/loader/Mod.cpp b/loader/src/loader/Mod.cpp index 7260b726..35090d12 100644 --- a/loader/src/loader/Mod.cpp +++ b/loader/src/loader/Mod.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -17,8 +18,7 @@ USE_GEODE_NAMESPACE(); Mod::Mod(ModInfo const& info) { m_info = info; - m_saveDirPath = Loader::get()->getGeodeSaveDirectory() / - GEODE_MOD_DIRECTORY / info.m_id; + m_saveDirPath = dirs::getModsSaveDir() / info.m_id; ghc::filesystem::create_directories(m_saveDirPath); } @@ -112,15 +112,15 @@ Result<> Mod::createTempDir() { } // Create geode/temp - auto tempDir = Loader::get()->getGeodeDirectory() / GEODE_TEMP_DIRECTORY; + auto tempDir = dirs::getModRuntimeDir(); if (!file::createDirectoryAll(tempDir)) { - return Err("Unable to create Geode temp directory"); + return Err("Unable to create mods' runtime directory"); } // Create geode/temp/mod.id auto tempPath = tempDir / m_info.m_id; if (!file::createDirectoryAll(tempPath)) { - return Err("Unable to create mod temp directory"); + return Err("Unable to create mod runtime directory"); } // Unzip .geode file into temp dir @@ -400,7 +400,7 @@ ghc::filesystem::path Mod::getPackagePath() const { } ghc::filesystem::path Mod::getConfigDir(bool create) const { - auto dir = Loader::get()->getGeodeDirectory() / GEODE_CONFIG_DIRECTORY / m_info.m_id; + auto dir = dirs::getModConfigDir() / m_info.m_id; if (create && !ghc::filesystem::exists(dir)) { ghc::filesystem::create_directories(dir); } diff --git a/loader/src/platform/ios/InternalLoader.cpp b/loader/src/platform/ios/InternalLoader.cpp index c94f458f..a8f592b7 100644 --- a/loader/src/platform/ios/InternalLoader.cpp +++ b/loader/src/platform/ios/InternalLoader.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -17,7 +18,7 @@ void InternalLoader::platformMessageBox(char const* title, std::string const& in void InternalLoader::openPlatformConsole() { ghc::filesystem::path(getpwuid(getuid())->pw_dir); freopen( - ghc::filesystem::path(Loader::get()->getGeodeDirectory() / "geode_log.txt").string().c_str(), "w", + ghc::filesystem::path(dirs::getGeodeDir() / "geode_log.txt").string().c_str(), "w", stdout ); InternalLoader::m_platformConsoleOpen = true; diff --git a/loader/src/platform/ios/crashlog.cpp b/loader/src/platform/ios/crashlog.cpp index ebcea479..b1c2d3d0 100644 --- a/loader/src/platform/ios/crashlog.cpp +++ b/loader/src/platform/ios/crashlog.cpp @@ -10,7 +10,7 @@ bool crashlog::didLastLaunchCrash() { return false; } -std::string crashlog::getCrashLogDirectory() { +ghc::filesystem::path crashlog::getCrashLogDirectory() { return ""; } diff --git a/loader/src/platform/mac/crashlog.mm b/loader/src/platform/mac/crashlog.mm index c6c5d413..a1016b42 100644 --- a/loader/src/platform/mac/crashlog.mm +++ b/loader/src/platform/mac/crashlog.mm @@ -14,7 +14,7 @@ bool crashlog::didLastLaunchCrash() { return false; } -std::string crashlog::getCrashLogDirectory() { +ghc::filesystem::path crashlog::getCrashLogDirectory() { std::array path; CFStringGetCString( (CFStringRef)NSHomeDirectory(), path.data(), path.size(), kCFStringEncodingUTF8 diff --git a/loader/src/platform/windows/crashlog.cpp b/loader/src/platform/windows/crashlog.cpp index 420e92b9..e8c345a3 100644 --- a/loader/src/platform/windows/crashlog.cpp +++ b/loader/src/platform/windows/crashlog.cpp @@ -5,7 +5,9 @@ #ifdef GEODE_IS_WINDOWS #include - +#include +#include +#include #include #include #include @@ -224,7 +226,7 @@ static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) { // add a file to let Geode know on next launch that it crashed previously // this could also be done by saving a loader setting or smth but eh. - (void)utils::file::writeBinary(crashlog::getCrashLogDirectory() + "/last-crashed", {}); + (void)utils::file::writeBinary(crashlog::getCrashLogDirectory() / "last-crashed", {}); SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); @@ -273,7 +275,7 @@ static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) { // save actual file std::ofstream actualFile; actualFile.open( - crashlog::getCrashLogDirectory() + "/" + getDateString(true) + ".log", std::ios::app + crashlog::getCrashLogDirectory() / (getDateString(true) + ".log"), std::ios::app ); actualFile << file.rdbuf() << std::flush; actualFile.close(); @@ -283,7 +285,7 @@ static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) { bool crashlog::setupPlatformHandler() { SetUnhandledExceptionFilter(exceptionHandler); - auto lastCrashedFile = crashlog::getCrashLogDirectory() + "/last-crashed"; + auto lastCrashedFile = crashlog::getCrashLogDirectory() / "last-crashed"; if (ghc::filesystem::exists(lastCrashedFile)) { g_lastLaunchCrashed = true; try { @@ -299,9 +301,8 @@ bool crashlog::didLastLaunchCrash() { return g_lastLaunchCrashed; } -std::string crashlog::getCrashLogDirectory() { - static auto dir = (Loader::get()->getGeodeDirectory() / "crashlogs").string(); - return dir; +ghc::filesystem::path crashlog::getCrashLogDirectory() { + return dirs::getGeodeDir() / "crashlogs"; } #endif diff --git a/loader/src/ui/internal/info/ModInfoLayer.cpp b/loader/src/ui/internal/info/ModInfoLayer.cpp index 153bfb80..fd9510de 100644 --- a/loader/src/ui/internal/info/ModInfoLayer.cpp +++ b/loader/src/ui/internal/info/ModInfoLayer.cpp @@ -5,6 +5,7 @@ #include "../settings/ModSettingsPopup.hpp" #include "../settings/AdvancedSettingsPopup.hpp" #include +#include #include #include @@ -264,9 +265,7 @@ bool ModInfoLayer::init(ModObject* obj, ModListView* list) { // Check if a config directory for the mod exists // Mod::getConfigDir auto-creates the directory for user convenience, so // have to do it manually - if (ghc::filesystem::exists( - Loader::get()->getGeodeDirectory() / GEODE_CONFIG_DIRECTORY / m_mod->getID() - )) { + if (ghc::filesystem::exists(m_mod->getConfigDir(false))) { auto configSpr = CircleButtonSprite::createWithSpriteFrameName( "pencil.png"_spr, 1.f, CircleBaseColor::Green, CircleBaseSize::Medium2 ); @@ -809,7 +808,7 @@ void ModInfoLayer::showIssueReportPopup(ModInfo const& info) { "\n\n" "If your issue relates to a game crash, please include the " "latest crash log(s) from `" + - Loader::get()->getCrashLogDirectory().string() + "`", + dirs::getCrashlogsDir().string() + "`", "OK", (info.m_issues.value().m_url ? "Open URL" : ""), [info](bool btn2) { if (btn2) { @@ -825,8 +824,7 @@ void ModInfoLayer::showIssueReportPopup(ModInfo const& info) { "[#support](https://discord.com/channels/911701438269386882/979352389985390603) " "channnel in the [Geode Discord Server](https://discord.gg/9e43WMKzhp)\n\n" "If your issue relates to a game crash, please include the " - "latest crash log(s) from `" + - Loader::get()->getCrashLogDirectory().string() + "`", + "latest crash log(s) from `" + dirs::getCrashlogsDir().string() + "`", "OK" ) ->show(); diff --git a/loader/src/ui/internal/list/ModListLayer.cpp b/loader/src/ui/internal/list/ModListLayer.cpp index d12b5c32..0b8cb3bd 100644 --- a/loader/src/ui/internal/list/ModListLayer.cpp +++ b/loader/src/ui/internal/list/ModListLayer.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include static ModListType g_tab = ModListType::Installed; @@ -382,7 +383,7 @@ void ModListLayer::onFilters(CCObject*) { } void ModListLayer::onOpenFolder(CCObject*) { - file::openFolder(ghc::filesystem::canonical(Loader::get()->getGeodeDirectory() / "mods")); + file::openFolder(ghc::filesystem::canonical(dirs::getModsDir())); } void ModListLayer::onResetSearch(CCObject*) { diff --git a/loader/src/ui/internal/settings/GeodeSettingNode.cpp b/loader/src/ui/internal/settings/GeodeSettingNode.cpp index c20c6ad7..491545a1 100644 --- a/loader/src/ui/internal/settings/GeodeSettingNode.cpp +++ b/loader/src/ui/internal/settings/GeodeSettingNode.cpp @@ -6,6 +6,7 @@ #include #include #include +#include // BoolSettingNode @@ -122,7 +123,7 @@ void FileSettingNode::onPickFile(CCObject*) { if (auto path = file::pickFile( file::PickMode::OpenFile, { - Loader::get()->getGameDirectory(), + dirs::getGameDir(), setting->getFileFilters().value_or(std::vector()) } )) {