move fetch from Index to exported utils + add close button as a member

to Popup and move setup to be last in init + add
GEODE_PLATFORM_SHORT_IDENTIFIER macro for the platform's identifier in
GitHub release zips + add GEODE_VERSION to cmake + move unzipTo from
Index to be an exported util in file namespace + add mod resources
directories in accordance with new CLI
This commit is contained in:
HJfod 2022-10-05 15:41:05 +03:00
parent 34878e97b7
commit b1776d1d26
23 changed files with 232 additions and 144 deletions

View file

@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
project(geode-sdk VERSION 0.3.0 LANGUAGES CXX C)
set(GEODE_VERSION 0.3.0)
project(geode-sdk VERSION ${GEODE_VERSION} LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
project(geode-loader VERSION 0.2.1 LANGUAGES C CXX)
project(geode-loader VERSION ${GEODE_VERSION} LANGUAGES C CXX)
set(PROJECT_VERSION_TYPE Alpha)
# Package info file for internal representation
@ -28,6 +28,7 @@ file(GLOB CORE_SOURCES
src/utils/zip/*.cpp
src/index/*.cpp
src/ui/nodes/*.cpp
src/ui/internal/*.cpp
src/ui/internal/credits/*.cpp
src/ui/internal/dev/*.cpp
src/ui/internal/info/*.cpp

View file

@ -60,25 +60,6 @@ namespace geode {
bool m_isSetup = false;
static bool s_unloading;
/**
* Lowest supported mod version.
* Any mod targeting a geode version
* lower than this will not be loaded,
* as they will be considered out-of-date.
*/
static constexpr VersionInfo s_supportedVersionMin { 0, 1, 0 };
/**
* Highest support mod version.
* Any mod targeting a geode version
* higher than this will not be loaded,
* as a higher version means that
* the user's geode is out-of-date,
* or that the user is a time traveller
* and has downloaded a mod from the
* future.
*/
static constexpr VersionInfo s_supportedVersionMax { 0, 2, 1 };
Result<std::string> createTempDirectoryForMod(ModInfo const& info);
Result<Mod*> loadModFromFile(std::string const& file);
size_t loadModsFromDirectory(
@ -107,8 +88,8 @@ namespace geode {
*/
static Loader* get();
VersionInfo getVersion() const;
std::string getVersionType() const;
static VersionInfo getVersion();
static std::string getVersionType();
Result<> saveSettings();
Result<> loadSettings();
@ -133,10 +114,18 @@ namespace geode {
*/
ghc::filesystem::path getGeodeSaveDirectory() const;
/**
* Minimum supported mod version
*/
static VersionInfo minModVersion();
/**
* Maximum supported mod version
*/
static VersionInfo maxModVersion();
/**
* Check if a mod's version is within the supported range
*/
bool supportedModVersion(VersionInfo const& version);
static bool supportedModVersion(VersionInfo const& version);
/**
* Whether mod specified with ID is enabled

View file

@ -332,7 +332,7 @@ namespace geode {
bool m_hasArrows = true;
bool m_hasBigArrows = false;
size_t m_arrowStep = 1;
size_t m_bigArrowStep = 1;
size_t m_bigArrowStep = 5;
public:
Result<> parseArrows(JsonMaybeObject<ModJson>& obj) {

View file

@ -11,6 +11,7 @@
#define GEODE_PLATFORM_TARGET PlatformID::Windows
#define GEODE_CALL __stdcall
#define GEODE_PLATFORM_EXTENSION ".dll"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "win"
#else
#define GEODE_WINDOWS(...)
#endif
@ -26,6 +27,7 @@
#define GEODE_PLATFORM_NAME "iOS"
#define GEODE_PLATFORM_TARGET PlatformID::iOS
#define GEODE_PLATFORM_EXTENSION ".ios.dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "ios"
#else
#define GEODE_IOS(...)
#define GEODE_MACOS(...) __VA_ARGS__
@ -34,6 +36,7 @@
#define GEODE_PLATFORM_NAME "MacOS"
#define GEODE_PLATFORM_TARGET PlatformID::MacOS
#define GEODE_PLATFORM_EXTENSION ".dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "mac"
#endif
#define GEODE_CALL
#else
@ -50,6 +53,7 @@
#define GEODE_PLATFORM_TARGET PlatformID::Android
#define GEODE_CALL
#define GEODE_PLATFORM_EXTENSION ".so"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "android"
#else
#define GEODE_ANDROID(...)
#endif

View file

@ -20,6 +20,7 @@
#define GEODE_PLATFORM_NAME "Windows"
#define GEODE_CALL __stdcall
#define GEODE_PLATFORM_EXTENSION ".dll"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "win"
#ifdef GEODE_EXPORTING
#undef GEODE_C_DLL
@ -43,6 +44,7 @@
#define GEODE_IS_MOBILE
#define GEODE_PLATFORM_NAME "iOS"
#define GEODE_PLATFORM_EXTENSION ".ios.dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "ios"
#else
#define GEODE_IOS(...)
#define GEODE_MACOS(...) __VA_ARGS__
@ -50,6 +52,7 @@
#define GEODE_IS_DESKTOP
#define GEODE_PLATFORM_NAME "MacOS"
#define GEODE_PLATFORM_EXTENSION ".dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "mac"
#endif
#define GEODE_CALL
#else
@ -65,6 +68,7 @@
#define GEODE_PLATFORM_NAME "Android"
#define GEODE_CALL
#define GEODE_PLATFORM_EXTENSION ".so"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "android"
#else
#define GEODE_ANDROID(...)
#endif

View file

@ -77,6 +77,7 @@ namespace std {
#define GEODE_VIRTUAL_CONSTEXPR
#define GEODE_NOINLINE __declspec(noinline)
#define GEODE_PLATFORM_EXTENSION ".dll"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "win"
#ifdef GEODE_EXPORTING
#define GEODE_DLL __declspec(dllexport)
@ -111,6 +112,7 @@ namespace std {
#define GEODE_VIRTUAL_CONSTEXPR constexpr
#define GEODE_NOINLINE __attribute__((noinline))
#define GEODE_PLATFORM_EXTENSION ".ios.dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "ios"
#ifdef GEODE_EXPORTING
#define GEODE_DLL __attribute__((visibility("default")))
@ -137,6 +139,7 @@ namespace std {
#define GEODE_VIRTUAL_CONSTEXPR constexpr
#define GEODE_NOINLINE __attribute__((noinline))
#define GEODE_PLATFORM_EXTENSION ".dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "mac"
#ifdef GEODE_EXPORTING
#define GEODE_DLL __attribute__((visibility("default")))
@ -169,6 +172,7 @@ namespace std {
#define GEODE_VIRTUAL_CONSTEXPR constexpr
#define GEODE_NOINLINE __attribute__((noinline))
#define GEODE_PLATFORM_EXTENSION ".so"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "android"
#ifdef GEODE_EXPORTING
#define GEODE_DLL __attribute__((visibility("default")))

View file

@ -9,6 +9,7 @@ namespace geode {
cocos2d::CCSize m_size;
cocos2d::extension::CCScale9Sprite* m_bgSprite;
cocos2d::CCLabelBMFont* m_title = nullptr;
CCMenuItemSpriteExtra* m_closeBtn;
bool init(
float width,
@ -36,21 +37,21 @@ namespace geode {
cocos2d::CCDirector::sharedDirector()->getTouchDispatcher()->incrementForcePrio(2);
this->registerWithTouchDispatcher();
if (!setup(std::forward<InitArgs>(args)...)) {
return false;
}
auto closeSpr = cocos2d::CCSprite::createWithSpriteFrameName("GJ_closeBtn_001.png");
closeSpr->setScale(.8f);
auto closeBtn = CCMenuItemSpriteExtra::create(
m_closeBtn = CCMenuItemSpriteExtra::create(
closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose)
);
closeBtn->setPosition(
m_closeBtn->setPosition(
-m_size.width / 2 + 3.f,
m_size.height / 2 - 3.f
);
m_buttonMenu->addChild(closeBtn);
m_buttonMenu->addChild(m_closeBtn);
if (!setup(std::forward<InitArgs>(args)...)) {
return false;
}
this->setKeypadEnabled(true);
this->setTouchEnabled(true);

View file

@ -0,0 +1,50 @@
#pragma once
#include "../DefaultInclude.hpp"
#include <fs/filesystem.hpp>
#include "Result.hpp"
#include "json.hpp"
namespace geode::utils::web {
using FileProgressCallback = std::function<bool(double, double)>;
/**
* Synchronously fetch data from the internet
* @param url URL to fetch
* @returns Returned data as string, or error on error
*/
GEODE_DLL Result<std::string> fetch(std::string const& url);
/**
* Syncronously download a file from the internet
* @param url URL to fetch
* @param into Path to download file into
* @param prog Progress function; first parameter is bytes downloaded so
* far, and second is total bytes to download. Return true to continue
* downloading, and false to interrupt. Note that interrupting does not
* automatically remove the file that was being downloaded
* @returns Returned data as JSON, or error on error
*/
GEODE_DLL Result<> fetchFile(
std::string const& url,
ghc::filesystem::path const& into,
FileProgressCallback prog = nullptr
);
/**
* Synchronously fetch data from the internet and parse it as JSON
* @param url URL to fetch
* @returns Returned data as JSON, or error on error
*/
template<class Json = nlohmann::json>
Result<Json> fetchJSON(std::string const& url) {
auto res = fetch(url);
if (!res) return Err(res.error());
try {
return Ok(Json::parse(res.value()));
} catch(std::exception& e) {
return Err(e.what());
}
}
}

View file

@ -4,7 +4,7 @@
#include "Result.hpp"
#include <string>
#include "types.hpp"
#include "fs/filesystem.hpp"
#include <fs/filesystem.hpp>
namespace geode::utils::file {
GEODE_DLL Result<std::string> readString(std::string const& path);
@ -25,4 +25,15 @@ namespace geode::utils::file {
GEODE_DLL Result<> createDirectoryAll(std::string const& path);
GEODE_DLL Result<std::vector<std::string>> listFiles(std::string const& path);
GEODE_DLL Result<std::vector<std::string>> listFilesRecursively(std::string const& path);
/**
* Unzip file to directory
* @param from File to unzip
* @param to Directory to unzip to
* @returns Ok on success, Error on error
*/
GEODE_DLL Result<> unzipTo(
ghc::filesystem::path const& from,
ghc::filesystem::path const& to
);
}

View file

@ -11,6 +11,7 @@
#define GEODE_PLATFORM_TARGET PlatformID::Windows
#define GEODE_CALL __stdcall
#define GEODE_PLATFORM_EXTENSION ".dll"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "win"
#else
#define GEODE_WINDOWS(...)
#endif
@ -26,6 +27,7 @@
#define GEODE_PLATFORM_NAME "iOS"
#define GEODE_PLATFORM_TARGET PlatformID::iOS
#define GEODE_PLATFORM_EXTENSION ".ios.dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "ios"
#else
#define GEODE_IOS(...)
#define GEODE_MACOS(...) __VA_ARGS__
@ -34,6 +36,7 @@
#define GEODE_PLATFORM_NAME "MacOS"
#define GEODE_PLATFORM_TARGET PlatformID::MacOS
#define GEODE_PLATFORM_EXTENSION ".dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "mac"
#endif
#define GEODE_CALL
#else
@ -50,6 +53,7 @@
#define GEODE_PLATFORM_TARGET PlatformID::Android
#define GEODE_CALL
#define GEODE_PLATFORM_EXTENSION ".so"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "android"
#else
#define GEODE_ANDROID(...)
#endif

View file

@ -52,7 +52,13 @@
"type": "bool",
"default": false,
"name": "Show Platform Console",
"description": "Show the native console (if one exists). <cr>This setting is meant for developers</c>."
"description": "Show the native console (if one exists). <cr>This setting is meant for developers</c>"
},
"auto-check-updates": {
"type": "bool",
"default": true,
"name": "Check For Updates",
"description": "Automatically check for <cy>updates</c> to Geode on startup"
}
},
"issues": {

View file

@ -7,6 +7,7 @@
#include <Geode/ui/MDPopup.hpp>
#include <InternalMod.hpp>
#include "../ui/internal/info/ModInfoLayer.hpp"
#include <InternalLoader.hpp>
USE_GEODE_NAMESPACE();

View file

@ -2,7 +2,7 @@
#include <thread>
#include <Geode/utils/json.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include "fetch.hpp"
#include <Geode/utils/fetch.hpp>
#define GITHUB_DONT_RATE_LIMIT_ME_PLS 0
@ -19,61 +19,6 @@ static Result<Json> readJSON(ghc::filesystem::path const& path) {
}
}
static Result<> unzipTo(
ghc::filesystem::path const& from,
ghc::filesystem::path const& to
) {
// unzip downloaded
auto unzip = ZipFile(from.string());
if (!unzip.isLoaded()) {
return Err("Unable to unzip index.zip");
}
for (auto file : unzip.getAllFiles()) {
// this is a very bad check for seeing
// if file is a directory. it seems to
// work on windows at least. idk why
// getAllFiles returns the directories
// aswell now
if (
utils::string::endsWith(file, "\\") ||
utils::string::endsWith(file, "/")
) continue;
auto zipPath = file;
// dont include the github repo folder
file = file.substr(file.find_first_of("/") + 1);
auto path = ghc::filesystem::path(file);
if (path.has_parent_path()) {
if (
!ghc::filesystem::exists(to / path.parent_path()) &&
!ghc::filesystem::create_directories(to / path.parent_path())
) {
return Err(
"Unable to create directories \"" +
path.parent_path().string() + "\""
);
}
}
unsigned long size;
auto data = unzip.getFileData(zipPath, &size);
if (!data || !size) {
return Err("Unable to read \"" + std::string(zipPath) + "\"");
}
auto wrt = utils::file::writeBinary(
to / file,
byte_array(data, data + size)
);
if (!wrt) {
return Err("Unable to write \"" + file + "\": " + wrt.error());
}
}
return Ok();
}
static PlatformID platformFromString(std::string const& str) {
switch (hash(utils::string::trim(utils::string::toLower(str)).c_str())) {
default:
@ -126,7 +71,7 @@ void Index::updateIndexThread(bool force) {
);
// get all commits in index repo
auto commit = fetchJSON(
auto commit = web::fetchJSON(
"https://api.github.com/repos/geode-sdk/mods/commits"
);
if (!commit) {
@ -202,7 +147,7 @@ void Index::updateIndexThread(bool force) {
"Downloading index",
50
);
auto gotZip = fetchFile(
auto gotZip = web::fetchFile(
"https://github.com/geode-sdk/mods/zipball/main",
indexDir / "index.zip"
);
@ -218,7 +163,7 @@ void Index::updateIndexThread(bool force) {
ghc::filesystem::remove_all(indexDir / "index");
}
auto unzip = unzipTo(indexDir / "index.zip", indexDir);
auto unzip = file::unzipTo(indexDir / "index.zip", indexDir);
if (!unzip) {
return indexUpdateProgress(
UpdateStatus::Failed, unzip.error()

View file

@ -2,7 +2,7 @@
#include <thread>
#include <Geode/utils/json.hpp>
#include <hash.hpp>
#include "fetch.hpp"
#include <Geode/utils/fetch.hpp>
void InstallTicket::postProgress(
UpdateStatus status,
@ -46,7 +46,7 @@ void InstallTicket::install(std::string const& id) {
auto tempFile = indexDir / item.m_download.m_filename;
this->postProgress(UpdateStatus::Progress, "Fetching binary", 0);
auto res = fetchFile(
auto res = web::fetchFile(
item.m_download.m_url,
tempFile,
[this, tempFile](double now, double total) -> int {
@ -54,7 +54,7 @@ void InstallTicket::install(std::string const& id) {
std::lock_guard cancelLock(m_cancelMutex);
if (m_cancelling) {
try { ghc::filesystem::remove(tempFile); } catch(...) {}
return 1;
return false;
}
// no need to scope the lock guard more as this
@ -65,7 +65,7 @@ void InstallTicket::install(std::string const& id) {
"Downloading binary",
static_cast<uint8_t>(now / total * 100.0)
);
return 0;
return true;
}
);
if (!res) {

View file

@ -1,25 +0,0 @@
#pragma once
#include <Geode/Geode.hpp>
USE_GEODE_NAMESPACE();
Result<std::string> fetch(std::string const& url);
Result<> fetchFile(
std::string const& url,
ghc::filesystem::path const& into,
std::function<int(double, double)> prog = nullptr
);
template<class Json = nlohmann::json>
Result<Json> fetchJSON(std::string const& url) {
auto res = fetch(url);
if (!res) return Err(res.error());
try {
return Ok(Json::parse(res.value()));
} catch(std::exception& e) {
return Err(e.what());
}
}

View file

@ -7,6 +7,7 @@
#include <Geode/loader/Log.hpp>
#include <Geode/loader/Loader.hpp>
#include <Geode/Geode.hpp>
#include <Geode/utils/fetch.hpp>
#include <thread>
InternalLoader::InternalLoader() : Loader() {}

View file

@ -8,6 +8,8 @@
#include <Geode/loader/Log.hpp>
#include <Geode/utils/Result.hpp>
#include <unordered_set>
#include <Geode/utils/json.hpp>
#include <optional>
USE_GEODE_NAMESPACE();
@ -52,6 +54,6 @@ public:
void openPlatformConsole();
void closePlatformConsole();
static void platformMessageBox(const char* title, std::string const& info);
friend int geodeEntry(void* platformData);
};

View file

@ -19,11 +19,11 @@ USE_GEODE_NAMESPACE();
bool Loader::s_unloading = false;
std::mutex g_unloadMutex;
VersionInfo Loader::getVersion() const {
VersionInfo Loader::getVersion() {
return LOADER_VERSION;
}
std::string Loader::getVersionType() const {
std::string Loader::getVersionType() {
return LOADER_VERSION_TYPE;
}
@ -51,14 +51,23 @@ void Loader::createDirectories() {
}
void Loader::updateResourcePaths() {
// add own resources directory
// add own geode/resources directory
CCFileUtils::sharedFileUtils()->addSearchPath(
(this->getGeodeDirectory() / GEODE_RESOURCE_DIRECTORY).string().c_str()
);
// add mods directory
CCFileUtils::sharedFileUtils()->addSearchPath(
(this->getGeodeDirectory() / GEODE_TEMP_DIRECTORY).string().c_str()
);
// add geode/temp for accessing root resources in mods
auto tempDir = this->getGeodeDirectory() / GEODE_TEMP_DIRECTORY;
CCFileUtils::sharedFileUtils()->addSearchPath(tempDir.string().c_str());
// add geode/temp/mod.id/resources for accessing additional resources in mods
for (auto& [_, mod] : m_mods) {
if (mod->m_addResourcesToSearchPath) {
CCFileUtils::sharedFileUtils()->addSearchPath(
(tempDir / mod->getID() / "resources").string().c_str()
);
}
}
}
void Loader::updateModResources(Mod* mod) {
@ -447,10 +456,18 @@ size_t Loader::getFieldIndexForClass(size_t hash) {
return nextIndex[hash]++;
}
VersionInfo Loader::minModVersion() {
return { 0, 1, 0 };
}
VersionInfo Loader::maxModVersion() {
return Loader::getVersion();
}
bool Loader::supportedModVersion(VersionInfo const& version) {
return
version >= s_supportedVersionMin &&
version <= s_supportedVersionMax;
version >= Loader::minModVersion() &&
version <= Loader::maxModVersion();
}
void Loader::openPlatformConsole() {

View file

@ -148,28 +148,28 @@ Result<ModInfo> ModInfo::create(ModJson const& json) {
"specified, or it is invalidally formatted (required: \"[v]X.X.X\")!"
);
}
if (schema < Loader::s_supportedVersionMin) {
if (schema < Loader::minModVersion()) {
return Err(
"[mod.json] is built for an older version (" +
schema.toString() + ") of Geode (current: " +
Loader::s_supportedVersionMin.toString() +
Loader::minModVersion().toString() +
"). Please update the mod to the latest version, "
"and if the problem persists, contact the developer "
"to update it."
);
}
if (schema > Loader::s_supportedVersionMax) {
if (schema > Loader::maxModVersion()) {
return Err(
"[mod.json] is built for a newer version (" +
schema.toString() + ") of Geode (current: " +
Loader::s_supportedVersionMax.toString() +
Loader::maxModVersion().toString() +
"). You need to update Geode in order to use "
"this mod."
);
}
// Handle mod.json data based on target
if (schema <= VersionInfo(0, 2, 1)) {
if (schema >= VersionInfo(0, 1, 0)) {
return ModInfo::createFromSchemaV010(json);
}

View file

@ -121,7 +121,7 @@ int geodeEntry(void* platformData) {
if (InternalMod::get()->getSettingValue<bool>("show-platform-console")) {
Loader::get()->openPlatformConsole();
}
InternalMod::get()->log()
<< Severity::Debug
<< "Entry done.";

View file

@ -1,5 +1,8 @@
#include "fetch.hpp"
#include <Geode/utils/fetch.hpp>
#include <curl/curl.h>
#include <Geode/utils/casts.hpp>
USE_GEODE_NAMESPACE();
namespace geode::utils::fetch {
static size_t writeData(char* data, size_t size, size_t nmemb, void* str) {
@ -13,14 +16,14 @@ namespace geode::utils::fetch {
}
static int progress(void* ptr, double total, double now, double, double) {
return (*as<std::function<int(double, double)>*>(ptr))(now, total);
return (*as<web::FileProgressCallback*>(ptr))(now, total) != true;
}
}
Result<> fetchFile(
Result<> web::fetchFile(
std::string const& url,
ghc::filesystem::path const& into,
std::function<int(double, double)> prog
FileProgressCallback prog
) {
auto curl = curl_easy_init();
@ -46,7 +49,7 @@ Result<> fetchFile(
auto res = curl_easy_perform(curl);
if (res != CURLE_OK) {
curl_easy_cleanup(curl);
return Err("Fetch failed");
return Err("Fetch failed: " + std::string(curl_easy_strerror(res)));
}
char* ct;
@ -59,7 +62,7 @@ Result<> fetchFile(
return Err("Error getting info: " + std::string(curl_easy_strerror(res)));
}
Result<std::string> fetch(std::string const& url) {
Result<std::string> web::fetch(std::string const& url) {
auto curl = curl_easy_init();
if (!curl) return Err("Curl not initialized!");

View file

@ -1,5 +1,7 @@
#include <Geode/utils/file.hpp>
#include <Geode/utils/string.hpp>
#include <fstream>
#include <Geode/Bindings.hpp>
USE_GEODE_NAMESPACE();
@ -188,3 +190,69 @@ Result<std::vector<std::string>> utils::file::listFilesRecursively(std::string c
}
return Ok<>(res);
}
Result<> utils::file::unzipTo(
ghc::filesystem::path const& from,
ghc::filesystem::path const& to
) {
// unzip downloaded
auto unzip = ZipFile(from.string());
if (!unzip.isLoaded()) {
return Err("Unable to unzip index.zip");
}
if (
!ghc::filesystem::exists(to) &&
!ghc::filesystem::create_directories(to)
) {
return Err(
"Unable to create directories \"" +
to.string() + "\""
);
}
for (auto file : unzip.getAllFiles()) {
// this is a very bad check for seeing
// if file is a directory. it seems to
// work on windows at least. idk why
// getAllFiles returns the directories
// aswell now
if (
utils::string::endsWith(file, "\\") ||
utils::string::endsWith(file, "/")
) continue;
auto zipPath = file;
// dont include the github repo folder
file = file.substr(file.find_first_of("/") + 1);
auto path = ghc::filesystem::path(file);
if (path.has_parent_path()) {
auto dir = to / path.parent_path();
if (
!ghc::filesystem::exists(dir) &&
!ghc::filesystem::create_directories(dir)
) {
return Err(
"Unable to create directories \"" +
dir.string() + "\""
);
}
}
unsigned long size;
auto data = unzip.getFileData(zipPath, &size);
if (!data || !size) {
return Err("Unable to read \"" + std::string(zipPath) + "\"");
}
auto wrt = utils::file::writeBinary(
to / file,
byte_array(data, data + size)
);
if (!wrt) {
return Err("Unable to write \"" + (to / file).string() + "\": " + wrt.error());
}
}
return Ok();
}