rename utils namespaces to be consistent + add file picking function +

implement it on windows via NFD + settings are pretty much finished
This commit is contained in:
HJfod 2022-09-26 13:53:40 +03:00
parent 86f8d820d2
commit 6d2718ce09
36 changed files with 780 additions and 279 deletions

View file

@ -1,13 +1,14 @@
#pragma once
#include <Geode/DefaultInclude.hpp>
#include "../utils/container.hpp"
#include <optional>
#include <unordered_set>
#include "../utils/container.hpp"
#include "../utils/json.hpp"
#include "../utils/Result.hpp"
#include "../utils/JsonValidation.hpp"
#include "../utils/convert.hpp"
#include "../utils/platform.hpp"
#include <regex>
namespace geode {
@ -79,13 +80,14 @@ namespace geode {
class IMinMax;
template<class Class, class ValueType>
class IOneOf;
template<class Class>
template<class Class, class ValueType>
class IMatch;
class ICArrows;
template<class ValueType>
class ICSlider;
class ICInput;
class ICFileFilters;
template<class Class, class ValueType, SettingType Type>
class GeodeSetting : public Setting {
@ -110,7 +112,7 @@ namespace geode {
obj.has("description").intoAs<std::string>(res->m_description);
GEODE_INT_PARSE_SETTING_IMPL(obj, parseMinMax, IMinMax<ValueType>);
GEODE_INT_PARSE_SETTING_IMPL(obj, parseOneOf, IOneOf<Class, ValueType>);
GEODE_INT_PARSE_SETTING_IMPL(obj, parseMatch, IMatch<Class>);
GEODE_INT_PARSE_SETTING_IMPL(obj, parseMatch, IMatch<Class, ValueType>);
res->setValue(res->m_default);
if (auto controls = obj.has("control").obj()) {
@ -120,6 +122,7 @@ namespace geode {
GEODE_INT_PARSE_SETTING_IMPL(controls, parseArrows, ICArrows);
GEODE_INT_PARSE_SETTING_IMPL(controls, parseSlider, ICSlider<ValueType>);
GEODE_INT_PARSE_SETTING_IMPL(controls, parseInput, ICInput);
GEODE_INT_PARSE_SETTING_IMPL(controls, parseFileFilters, ICFileFilters);
}
return Ok(res);
@ -152,7 +155,7 @@ namespace geode {
if constexpr (std::is_base_of_v<IOneOf<Class, ValueType>, Class>) {
static_cast<Class*>(this)->constrainOneOf(m_value);
}
if constexpr (std::is_base_of_v<IMatch<Class>, Class>) {
if constexpr (std::is_base_of_v<IMatch<Class, ValueType>, Class>) {
static_cast<Class*>(this)->constrainMatch(m_value);
}
}
@ -160,7 +163,7 @@ namespace geode {
Result<> isValidValue(ValueType value) {
GEODE_INT_CONSTRAIN_SETTING_CAN_IMPL(constrainMinMax, IMinMax<ValueType>);
GEODE_INT_CONSTRAIN_SETTING_CAN_IMPL(constrainOneOf, IOneOf<Class, ValueType>);
GEODE_INT_CONSTRAIN_SETTING_CAN_IMPL(constrainMatch, IMatch<Class>);
GEODE_INT_CONSTRAIN_SETTING_CAN_IMPL(constrainMatch, IMatch<Class, ValueType>);
return Ok();
}
@ -238,7 +241,7 @@ namespace geode {
value = static_cast<Class*>(this)->getDefault();
return Err(
"Value must be one of " +
container_utils::join(m_oneOf.value(), ", ")
utils::container::join(m_oneOf.value(), ", ")
);
}
return Ok();
@ -260,13 +263,13 @@ namespace geode {
}
};
template<class Class>
template<class Class, class ValueType>
class IMatch {
protected:
std::optional<std::string> m_matchRegex = std::nullopt;
std::optional<ValueType> m_matchRegex = std::nullopt;
public:
Result<> constrainMatch(std::string& value) {
Result<> constrainMatch(ValueType& value) {
if (m_matchRegex) {
auto regex = std::regex(m_matchRegex.value());
if (!std::regex_match(value, regex)) {
@ -280,11 +283,11 @@ namespace geode {
}
Result<> parseMatch(JsonMaybeObject<ModJson>& obj) {
obj.has("match").intoAs<std::string>(m_matchRegex);
obj.has("match").intoAs<ValueType>(m_matchRegex);
return Ok();
}
std::optional<std::string> getMatch() const {
std::optional<ValueType> getMatch() const {
return m_matchRegex;
}
};
@ -354,6 +357,34 @@ namespace geode {
}
};
class ICFileFilters {
protected:
using Filter = utils::file::FilePickOptions::Filter;
std::optional<std::vector<Filter>> m_filters = std::nullopt;
public:
Result<> parseFileFilters(JsonMaybeObject<ModJson>& obj) {
std::vector<Filter> filters {};
for (auto& item : obj.has("filters").iterate()) {
if (auto iobj = item.obj()) {
Filter filter;
iobj.has("description").into(filter.description);
iobj.has("files").into(filter.files);
filters.push_back(filter);
}
}
if (filters.size()) {
m_filters = filters;
}
return Ok();
}
auto getFileFilters() const {
return m_filters;
}
};
GEODE_INT_DECL_SETTING_CONTROL(Input, hasInput, true, "input");
}
@ -390,7 +421,7 @@ namespace geode {
class GEODE_DLL StringSetting :
public GeodeSetting<StringSetting, std::string, SettingType::String>,
public IOneOf<StringSetting, std::string>,
public IMatch<StringSetting>,
public IMatch<StringSetting, std::string>,
public std::enable_shared_from_this<StringSetting>
{
public:
@ -399,6 +430,7 @@ namespace geode {
class GEODE_DLL FileSetting :
public GeodeSetting<FileSetting, ghc::filesystem::path, SettingType::File>,
public ICFileFilters,
public std::enable_shared_from_this<FileSetting>
{
public:

View file

@ -9,6 +9,20 @@ namespace geode {
template<class Json>
struct JsonChecker;
template <typename T, typename = void>
struct is_iterable : std::false_type {};
template <typename T>
struct is_iterable<T,
std::void_t<
decltype(std::begin(std::declval<T>())),
decltype(std::end(std::declval<T>()))
>
> : std::true_type {};
template <typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;
namespace {
using value_t = nlohmann::detail::value_t;
@ -43,11 +57,13 @@ namespace geode {
return value_t::number_integer;
}
else if constexpr (
std::is_same_v<T, std::string> ||
std::is_same_v<T, const char*>
std::is_constructible_v<T, std::string>
) {
return value_t::string;
}
else if constexpr (is_iterable_v<T>) {
return value_t::array;
}
return value_t::null;
}

View file

@ -4,7 +4,7 @@
#include <string>
#include <algorithm>
namespace geode::container_utils {
namespace geode::utils::container {
/**
* Check if a container contains an element by value.
* @param vec The vector to check.

View file

@ -6,7 +6,7 @@
#include "types.hpp"
#include "fs/filesystem.hpp"
namespace geode::file_utils {
namespace geode::utils::file {
GEODE_DLL Result<std::string> readString(std::string const& path);
GEODE_DLL Result<std::string> readString(std::wstring const& path);
GEODE_DLL Result<std::string> readString(ghc::filesystem::path const& path);

View file

@ -7,7 +7,7 @@
#include <functional>
#include <unordered_map>
namespace geode::map_utils {
namespace geode::utils::map {
/**
* Returns true if the map contains
* a value matching `containFunc`.

View file

@ -3,6 +3,7 @@
#include <Geode/DefaultInclude.hpp>
#include "Result.hpp"
#include <string>
#include <unordered_set>
#include <vector>
#include <functional>
#include "fs/filesystem.hpp"
@ -12,9 +13,34 @@ namespace geode::utils::clipboard {
GEODE_DLL std::string read();
}
namespace geode::utils::dirs {
namespace geode::utils::file {
GEODE_DLL ghc::filesystem::path geodeRoot();
GEODE_DLL bool openFolder(ghc::filesystem::path const& path);
enum class PickMode {
OpenFile,
SaveFile,
OpenFolder,
};
struct FilePickOptions {
struct Filter {
// Name of the filter
std::string description;
// Extensions (*.txt, *.doc, *.mp3, etc.)
std::unordered_set<std::string> files;
};
ghc::filesystem::path defaultPath;
std::vector<Filter> filters;
};
GEODE_DLL Result<ghc::filesystem::path> pickFile(
PickMode mode,
FilePickOptions const& options
);
GEODE_DLL Result<std::vector<ghc::filesystem::path>> pickFiles(
FilePickOptions const& options
);
}
namespace geode::utils::web {

View file

@ -5,117 +5,115 @@
#include <vector>
#include <functional>
namespace geode {
namespace string_utils {
/**
* Convert std::wstring to std::string (UTF-8)
* @param str String to convert
* @returns std::string
*/
GEODE_DLL std::string wideToUtf8(std::wstring const& str);
/**
* Convert std::string (UTF-8) to std::wstring
* @param str String to convert
* @returns std::wstring
*/
GEODE_DLL std::wstring utf8ToWide(std::string const& str);
namespace geode::utils::string {
/**
* Convert std::wstring to std::string (UTF-8)
* @param str String to convert
* @returns std::string
*/
GEODE_DLL std::string wideToUtf8(std::wstring const& str);
/**
* Convert std::string (UTF-8) to std::wstring
* @param str String to convert
* @returns std::wstring
*/
GEODE_DLL std::wstring utf8ToWide(std::string const& str);
GEODE_DLL std::string & toLowerIP(std::string & str);
GEODE_DLL std::wstring& toLowerIP(std::wstring& str);
GEODE_DLL std::string & toLowerIP(std::string & str);
GEODE_DLL std::wstring& toLowerIP(std::wstring& str);
GEODE_DLL std::string toLower(std::string const& str);
GEODE_DLL std::wstring toLower(std::wstring const& str);
GEODE_DLL std::string toLower(std::string const& str);
GEODE_DLL std::wstring toLower(std::wstring const& str);
GEODE_DLL std::string & toUpperIP(std::string & str);
GEODE_DLL std::wstring& toUpperIP(std::wstring& str);
GEODE_DLL std::string & toUpperIP(std::string & str);
GEODE_DLL std::wstring& toUpperIP(std::wstring& str);
GEODE_DLL std::string toUpper(std::string const& str);
GEODE_DLL std::wstring toUpper(std::wstring const& str);
GEODE_DLL std::string toUpper(std::string const& str);
GEODE_DLL std::wstring toUpper(std::wstring const& str);
GEODE_DLL std::string& replaceIP(
std::string & str,
std::string const& orig,
std::string const& repl
);
GEODE_DLL std::wstring& replaceIP(
std::wstring & str,
std::wstring const& orig,
std::wstring const& repl
);
GEODE_DLL std::string& replaceIP(
std::string & str,
std::string const& orig,
std::string const& repl
);
GEODE_DLL std::wstring& replaceIP(
std::wstring & str,
std::wstring const& orig,
std::wstring const& repl
);
GEODE_DLL std::string replace(
std::string const& str,
std::string const& orig,
std::string const& repl
);
GEODE_DLL std::wstring replace(
std::wstring const& str,
std::wstring const& orig,
std::wstring const& repl
);
GEODE_DLL std::string replace(
std::string const& str,
std::string const& orig,
std::string const& repl
);
GEODE_DLL std::wstring replace(
std::wstring const& str,
std::wstring const& orig,
std::wstring const& repl
);
GEODE_DLL std::vector<std::string> split(
std::string const& str,
std::string const& split
);
GEODE_DLL std::vector<std::wstring> split(
std::wstring const& str,
std::wstring const& split
);
GEODE_DLL std::vector<std::string> split(
std::string const& str,
std::string const& split
);
GEODE_DLL std::vector<std::wstring> split(
std::wstring const& str,
std::wstring const& split
);
GEODE_DLL std::vector<char> split(std::string const& str);
GEODE_DLL std::vector<wchar_t> split(std::wstring const& str);
GEODE_DLL std::vector<char> split(std::string const& str);
GEODE_DLL std::vector<wchar_t> split(std::wstring const& str);
GEODE_DLL bool contains(std::string const& str, std::string const& subs);
GEODE_DLL bool contains(std::wstring const& str, std::wstring const& subs);
GEODE_DLL bool contains(std::string const& str, std::string const& subs);
GEODE_DLL bool contains(std::wstring const& str, std::wstring const& subs);
GEODE_DLL bool contains(std::string const& str, char c);
GEODE_DLL bool contains(std::wstring const& str, wchar_t c);
GEODE_DLL bool contains(std::string const& str, char c);
GEODE_DLL bool contains(std::wstring const& str, wchar_t c);
GEODE_DLL bool containsAny(
std::string const& str,
std::vector<std::string> const& subs
);
GEODE_DLL bool containsAny(
std::wstring const& str,
std::vector<std::wstring> const& subs
);
GEODE_DLL bool containsAny(
std::string const& str,
std::vector<std::string> const& subs
);
GEODE_DLL bool containsAny(
std::wstring const& str,
std::vector<std::wstring> const& subs
);
GEODE_DLL bool containsAll(
std::string const& str,
std::vector<std::string> const& subs
);
GEODE_DLL bool containsAll(
std::wstring const& str,
std::vector<std::wstring> const& subs
);
GEODE_DLL bool containsAll(
std::string const& str,
std::vector<std::string> const& subs
);
GEODE_DLL bool containsAll(
std::wstring const& str,
std::vector<std::wstring> const& subs
);
GEODE_DLL size_t count(std::string const& str, char c);
GEODE_DLL size_t count(std::wstring const& str, wchar_t c);
GEODE_DLL size_t count(std::string const& str, char c);
GEODE_DLL size_t count(std::wstring const& str, wchar_t c);
GEODE_DLL std::string & trimLeftIP(std::string & str);
GEODE_DLL std::wstring& trimLeftIP(std::wstring& str);
GEODE_DLL std::string & trimRightIP(std::string & str);
GEODE_DLL std::wstring& trimRightIP(std::wstring& str);
GEODE_DLL std::string & trimIP(std::string & str);
GEODE_DLL std::wstring& trimIP(std::wstring& str);
GEODE_DLL std::string & trimLeftIP(std::string & str);
GEODE_DLL std::wstring& trimLeftIP(std::wstring& str);
GEODE_DLL std::string & trimRightIP(std::string & str);
GEODE_DLL std::wstring& trimRightIP(std::wstring& str);
GEODE_DLL std::string & trimIP(std::string & str);
GEODE_DLL std::wstring& trimIP(std::wstring& str);
GEODE_DLL std::string trimLeft(std::string const& str);
GEODE_DLL std::wstring trimLeft(std::wstring const& str);
GEODE_DLL std::string trimRight(std::string const& str);
GEODE_DLL std::wstring trimRight(std::wstring const& str);
GEODE_DLL std::string trim(std::string const& str);
GEODE_DLL std::wstring trim(std::wstring const& str);
GEODE_DLL std::string trimLeft(std::string const& str);
GEODE_DLL std::wstring trimLeft(std::wstring const& str);
GEODE_DLL std::string trimRight(std::string const& str);
GEODE_DLL std::wstring trimRight(std::wstring const& str);
GEODE_DLL std::string trim(std::string const& str);
GEODE_DLL std::wstring trim(std::wstring const& str);
GEODE_DLL std::string & normalizeIP(std::string & str);
GEODE_DLL std::wstring& normalizeIP(std::wstring & str);
GEODE_DLL std::string normalize(std::string const& str);
GEODE_DLL std::wstring normalize(std::wstring const& str);
GEODE_DLL std::string & normalizeIP(std::string & str);
GEODE_DLL std::wstring& normalizeIP(std::wstring & str);
GEODE_DLL std::string normalize(std::string const& str);
GEODE_DLL std::wstring normalize(std::wstring const& str);
GEODE_DLL bool startsWith(std::string const& str, std::string const& prefix);
GEODE_DLL bool startsWith(std::wstring const& str, std::wstring const& prefix);
GEODE_DLL bool endsWith(std::string const& str, std::string const& suffix);
GEODE_DLL bool endsWith(std::wstring const& str, std::wstring const& suffix);
}
GEODE_DLL bool startsWith(std::string const& str, std::string const& prefix);
GEODE_DLL bool startsWith(std::wstring const& str, std::wstring const& prefix);
GEODE_DLL bool endsWith(std::string const& str, std::string const& suffix);
GEODE_DLL bool endsWith(std::wstring const& str, std::wstring const& suffix);
}

View file

@ -6,7 +6,7 @@
#include <functional>
#include <algorithm>
namespace geode::vector_utils {
namespace geode::utils::vector {
/**
* Check if a vector contains an element by value.
* @param vec The vector to check.
@ -229,7 +229,7 @@ namespace geode::vector_utils {
*/
template<class T>
void insertBefore(std::vector<T> & vec, T const& item, T const& before) {
vec.insert(vector_utils::indexOf(vec, before), item);
vec.insert(utils::vector::indexOf(vec, before), item);
}
/**
@ -241,6 +241,6 @@ namespace geode::vector_utils {
*/
template<class T>
void insertAfter(std::vector<T> & vec, T const& item, T const& after) {
vec.insert(vector_utils::indexOf(vec, after) + 1, item);
vec.insert(utils::vector::indexOf(vec, after) + 1, item);
}
}

View file

@ -8,7 +8,7 @@
template<class Json = nlohmann::json>
static Result<Json> readJSON(ghc::filesystem::path const& path) {
auto indexJsonData = file_utils::readString(path);
auto indexJsonData = utils::file::readString(path);
if (!indexJsonData) {
return Err("Unable to read " + path.string());
}
@ -36,8 +36,8 @@ static Result<> unzipTo(
// getAllFiles returns the directories
// aswell now
if (
string_utils::endsWith(file, "\\") ||
string_utils::endsWith(file, "/")
utils::string::endsWith(file, "\\") ||
utils::string::endsWith(file, "/")
) continue;
auto zipPath = file;
@ -62,7 +62,7 @@ static Result<> unzipTo(
if (!data || !size) {
return Err("Unable to read \"" + std::string(zipPath) + "\"");
}
auto wrt = file_utils::writeBinary(
auto wrt = utils::file::writeBinary(
to / file,
byte_array(data, data + size)
);
@ -75,7 +75,7 @@ static Result<> unzipTo(
}
static PlatformID platformFromString(std::string const& str) {
switch (hash(string_utils::trim(string_utils::toLower(str)).c_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;
@ -165,7 +165,7 @@ void Index::updateIndexThread(bool force) {
// read sha of currently installed commit
std::string currentCommitSHA = "";
if (ghc::filesystem::exists(indexDir / "current")) {
auto data = file_utils::readString(indexDir / "current");
auto data = utils::file::readString(indexDir / "current");
if (data) {
currentCommitSHA = data.value();
}
@ -175,7 +175,7 @@ void Index::updateIndexThread(bool force) {
// different sha
if (force || currentCommitSHA != upcomingCommitSHA) {
// save new sha in file
file_utils::writeString(indexDir / "current", upcomingCommitSHA);
utils::file::writeString(indexDir / "current", upcomingCommitSHA);
// download latest commit (by downloading
// the repo as a zip)
@ -520,7 +520,7 @@ Result<InstallTicket*> Index::installItems(
if (!list) {
return Err(list.error());
}
vector_utils::push(ids, list.value());
utils::vector::push(ids, list.value());
}
return Ok(new InstallTicket(this, ids, progress));
}

View file

@ -88,7 +88,7 @@ void InstallTicket::install(std::string const& id) {
}
// check for 404
auto notFound = file_utils::readString(tempFile);
auto notFound = utils::file::readString(tempFile);
if (notFound && notFound.value() == "Not Found") {
try { ghc::filesystem::remove(tempFile); } catch(...) {}
return this->postProgress(

View file

@ -1,7 +1,7 @@
#include "fetch.hpp"
#include <curl/curl.h>
namespace fetch_utils {
namespace geode::utils::fetch {
static size_t writeData(char* data, size_t size, size_t nmemb, void* str) {
as<std::string*>(str)->append(data, size * nmemb);
return size * nmemb;
@ -35,12 +35,12 @@ Result<> fetchFile(
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fetch_utils::writeBinaryData);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, utils::fetch::writeBinaryData);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "github_api/1.0");
if (prog) {
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, fetch_utils::progress);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, utils::fetch::progress);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &prog);
}
auto res = curl_easy_perform(curl);
@ -68,7 +68,7 @@ Result<std::string> fetch(std::string const& url) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ret);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fetch_utils::writeData);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, utils::fetch::writeData);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "github_api/1.0");
auto res = curl_easy_perform(curl);
if (res != CURLE_OK) {

View file

@ -159,7 +159,7 @@ void InternalLoader::platformMessageBox(const char* title, const char* info) {
void InternalLoader::setupPlatformConsole() {
ghc::filesystem::path(getpwuid(getuid())->pw_dir);
freopen(ghc::filesystem::path(utils::dirs::geodeRoot() / "geode_log.txt").string().c_str(),"w",stdout);
freopen(ghc::filesystem::path(utils::file::geodeRoot() / "geode_log.txt").string().c_str(),"w",stdout);
InternalLoader::
m_platformConsoleReady = true;
}

View file

@ -256,11 +256,11 @@ static void printMods(std::ostream& stream) {
static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) {
// make sure crashlog directory exists
file_utils::createDirectoryAll(crashlog::getCrashLogDirectory());
utils::file::createDirectoryAll(crashlog::getCrashLogDirectory());
// 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.
file_utils::writeBinary(
utils::file::writeBinary(
crashlog::getCrashLogDirectory() + "/last-crashed", {}
);

View file

@ -6,12 +6,12 @@ USE_GEODE_NAMESPACE();
std::vector<BasicEventHandler*> Event::handlers = {};
void BasicEventHandler::listen() {
if (!vector_utils::contains(Event::handlers, this))
if (!utils::vector::contains(Event::handlers, this))
Event::handlers.push_back(this);
}
void BasicEventHandler::unlisten() {
vector_utils::erase(Event::handlers, this);
utils::vector::erase(Event::handlers, this);
}
Event::~Event() {}

View file

@ -58,7 +58,7 @@ Result<> Mod::disableHook(Hook* hook) {
Result<> Mod::removeHook(Hook* hook) {
auto res = this->disableHook(hook);
if (res) {
vector_utils::erase<Hook*>(this->m_hooks, hook);
utils::vector::erase<Hook*>(this->m_hooks, hook);
delete hook;
}
return res;

View file

@ -42,7 +42,7 @@ void Loader::createDirectories() {
ghc::filesystem::create_directory(logDir);
ghc::filesystem::create_directory(tempDir);
if (!vector_utils::contains(m_modDirectories, modDir)) {
if (!utils::vector::contains(m_modDirectories, modDir)) {
m_modDirectories.push_back(modDir);
}
@ -122,7 +122,7 @@ size_t Loader::loadModsFromDirectory(
}
// skip this entry if it's already loaded
if (map_utils::contains<std::string, Mod*>(
if (utils::map::contains<std::string, Mod*>(
m_mods,
[entry](Mod* p) -> bool {
return p->m_info.m_path == entry.path();
@ -195,7 +195,7 @@ Result<> Loader::saveSettings() {
json["succesfully-closed"] = true;
InternalLoader::get()->saveInfoAlerts(json);
auto path = this->getGeodeSaveDirectory() / "mods.json";
return file_utils::writeString(path, json.dump(4));
return utils::file::writeString(path, json.dump(4));
}
Result<> Loader::loadSettings() {
@ -204,7 +204,7 @@ Result<> Loader::loadSettings() {
return Ok();
}
auto read = file_utils::readString(path);
auto read = utils::file::readString(path);
if (!read) {
return read;
}
@ -283,7 +283,7 @@ Mod* Loader::getLoadedMod(std::string const& id) const {
}
std::vector<Mod*> Loader::getAllMods() const {
return map_utils::getValues(m_mods);
return utils::map::getValues(m_mods);
}
std::vector<Loader::FailedModInfo> Loader::getFailedMods() const {
@ -367,7 +367,7 @@ void Loader::pushLog(LogPtr* logptr) {
}
void Loader::popLog(LogPtr* log) {
vector_utils::erase(m_logs, log);
utils::vector::erase(m_logs, log);
delete log;
}
@ -385,7 +385,7 @@ std::vector<LogPtr*> Loader::getLogs(
std::vector<LogPtr*> logs;
for (auto const& log : m_logs) {
if (vector_utils::contains<Severity>(
if (utils::vector::contains<Severity>(
severityFilter, log->getSeverity()
)) {
logs.push_back(log);
@ -420,7 +420,7 @@ ghc::filesystem::path Loader::getSaveDirectory() const {
}
ghc::filesystem::path Loader::getGeodeDirectory() const {
return geode::utils::dirs::geodeRoot() / GEODE_DIRECTORY;
return geode::utils::file::geodeRoot() / GEODE_DIRECTORY;
}
ghc::filesystem::path Loader::getGeodeSaveDirectory() const {

View file

@ -57,7 +57,7 @@ Result<> Mod::loadSettings() {
// Check if settings exist
auto settPath = m_saveDirPath / "settings.json";
if (ghc::filesystem::exists(settPath)) {
auto settData = file_utils::readString(settPath);
auto settData = utils::file::readString(settPath);
if (!settData) return settData;
try {
// parse settings.json
@ -94,7 +94,7 @@ Result<> Mod::loadSettings() {
if (!ghc::filesystem::exists(dsPath)) {
m_dataStore = m_info.m_defaultDataStore;
} else {
auto dsData = file_utils::readString(dsPath);
auto dsData = utils::file::readString(dsPath);
if (!dsData) return dsData;
try {
m_dataStore = nlohmann::json::parse(dsData.value());
@ -114,14 +114,14 @@ Result<> Mod::saveSettings() {
return Err("Unable to save setting \"" + key + "\"");
}
}
auto sw = file_utils::writeString(settPath, json.dump(4));
auto sw = utils::file::writeString(settPath, json.dump(4));
if (!sw) {
return sw;
}
// datastore
auto dsPath = m_saveDirPath / "ds.json";
auto dw = file_utils::writeString(dsPath, m_dataStore.dump(4));
auto dw = utils::file::writeString(dsPath, m_dataStore.dump(4));
if (!dw) {
return dw;
}
@ -183,7 +183,7 @@ Result<> Mod::createTempDir() {
if (!data || !size) {
return Err<>("Unable to read \"" + std::string(file) + "\"");
}
auto wrt = file_utils::writeBinary(
auto wrt = utils::file::writeBinary(
tempPath / file,
byte_array(data, data + size)
);
@ -531,7 +531,7 @@ void Mod::logInfo(
}
bool Mod::depends(std::string const& id) const {
return vector_utils::contains<Dependency>(
return utils::vector::contains<Dependency>(
this->m_info.m_dependencies,
[id](Dependency t) -> bool { return t.m_id == id; }
);

View file

@ -8,7 +8,7 @@ USE_GEODE_NAMESPACE();
static std::string sanitizeDetailsData(unsigned char* start, unsigned char* end) {
// delete CRLF
return string_utils::replace(std::string(start, end), "\r", "");
return utils::string::replace(std::string(start, end), "\r", "");
}
Result<ModInfo> ModInfo::createFromSchemaV010(ModJson const& rawJson) {
@ -99,7 +99,7 @@ Result<ModInfo> ModInfo::createFromSchemaV010(ModJson const& rawJson) {
if (
root.has("binary") &&
autoEndBinaryName &&
!string_utils::endsWith(info.m_binaryName, GEODE_PLATFORM_EXTENSION)
!utils::string::endsWith(info.m_binaryName, GEODE_PLATFORM_EXTENSION)
) {
info.m_binaryName += GEODE_PLATFORM_EXTENSION;
}
@ -168,7 +168,7 @@ Result<ModInfo> ModInfo::create(ModJson const& json) {
Result<ModInfo> ModInfo::createFromFile(ghc::filesystem::path const& path) {
try {
auto read = file_utils::readString(path);
auto read = utils::file::readString(path);
if (!read) return Err(read.error());
try {
auto res = ModInfo::create(ModJson::parse(read.value()));

View file

@ -34,7 +34,7 @@ Result<Patch*> Mod::patch(void* address, byte_array data) {
Result<> Mod::unpatch(Patch* patch) {
if (patch->restore()) {
vector_utils::erase<Patch*>(m_patches, patch);
utils::vector::erase<Patch*>(m_patches, patch);
delete patch;
return Ok<>();
}

View file

@ -3,24 +3,24 @@
USE_GEODE_NAMESPACE();
std::string expandKnownLink(std::string const& link) {
switch (hash(string_utils::toLower(link).c_str())) {
switch (hash(utils::string::toLower(link).c_str())) {
case hash("github"):
if (!string_utils::contains(link, "/")) {
if (!utils::string::contains(link, "/")) {
return "https://github.com/" + link;
} break;
case hash("youtube"):
if (!string_utils::contains(link, "/")) {
if (!utils::string::contains(link, "/")) {
return "https://youtube.com/channel/" + link;
} break;
case hash("twitter"):
if (!string_utils::contains(link, "/")) {
if (!utils::string::contains(link, "/")) {
return "https://twitter.com/" + link;
} break;
case hash("newgrounds"):
if (!string_utils::contains(link, "/")) {
if (!utils::string::contains(link, "/")) {
return "https://" + link + ".newgrounds.com";
} break;
}

View file

@ -418,7 +418,7 @@ void ModInfoLayer::onInstallMod(CCObject*) {
this,
"Install",
"The following <cb>mods</c> will be installed: " +
vector_utils::join(m_ticket->getInstallList(), ",") + ".",
utils::vector::join(m_ticket->getInstallList(), ",") + ".",
"Cancel", "OK", 360.f
);
layer->setTag(TAG_CONFIRM_INSTALL);

View file

@ -412,7 +412,7 @@ void ModListLayer::onReload(CCObject*) {
}
void ModListLayer::onOpenFolder(CCObject*) {
dirs::openFolder(
file::openFolder(
ghc::filesystem::canonical(Loader::get()->getGeodeDirectory() / "mods")
);
}

View file

@ -443,9 +443,9 @@ bool ModListView::filter(ModInfo const& info, ModListQuery const& query) {
if (!query.m_searchFilter) return true;
auto check = [query](SearchFlags flag, std::string const& name) -> bool {
if (!(query.m_searchFlags & flag)) return false;
return string_utils::contains(
string_utils::toLower(name),
string_utils::toLower(query.m_searchFilter.value())
return utils::string::contains(
utils::string::toLower(name),
utils::string::toLower(query.m_searchFilter.value())
);
};
if (check(SearchFlag::Name, info.m_name)) return true;

View file

@ -1,4 +1,5 @@
#include "GeodeSettingNode.hpp"
#include <Geode/utils/platform.hpp>
// BoolSettingNode
@ -111,12 +112,34 @@ void FileSettingNode::valueChanged(bool updateText) {
}
bool FileSettingNode::setup(std::shared_ptr<FileSetting> setting, float width) {
m_input = InputNode::create(width / 2 - 10.f, "Path to File", "chatFont.fnt");
m_input->setPosition({ -(width / 2 - 70.f) / 2, .0f });
m_input = InputNode::create(width / 2 - 30.f, "Path to File", "chatFont.fnt");
m_input->setPosition({ -(width / 2 - 80.f) / 2 - 15.f, .0f });
m_input->setScale(.65f);
m_input->getInput()->setDelegate(this);
m_menu->addChild(m_input);
auto fileBtnSpr = CCSprite::createWithSpriteFrameName("gj_folderBtn_001.png");
fileBtnSpr->setScale(.5f);
auto fileBtn = CCMenuItemSpriteExtra::create(
fileBtnSpr, this, makeMenuSelector([this, setting](CCObject*) {
if (auto path = file::pickFile(
file::PickMode::OpenFile,
{
file::geodeRoot(),
setting->getFileFilters().value_or(
std::vector<file::FilePickOptions::Filter>()
)
}
)) {
m_uncommittedValue = path.value();
this->valueChanged(true);
}
})
);
fileBtn->setPosition(.0f, .0f);
m_menu->addChild(fileBtn);
return true;
}

View file

@ -66,9 +66,9 @@ public:
Result<ccColor3B> colorForIdentifier(std::string const& tag) {
if (string_utils::contains(tag, ' ')) {
auto hexStr = string_utils::split(
string_utils::normalize(tag), " "
if (utils::string::contains(tag, ' ')) {
auto hexStr = utils::string::split(
utils::string::normalize(tag), " "
).at(1);
try {
auto hex = std::stoi(hexStr, nullptr, 16);
@ -224,7 +224,7 @@ struct MDParser {
// rendering is done since the position of the
// rendered labels may change after alignments
// are adjusted
vector_utils::push(s_codeSpans, rendered);
utils::vector::push(s_codeSpans, rendered);
}
} break;
@ -243,7 +243,7 @@ struct MDParser {
auto rendered = renderer->renderStringInteractive(
text,
textarea,
string_utils::startsWith(s_lastLink, "user:") ?
utils::string::startsWith(s_lastLink, "user:") ?
menu_selector(MDTextArea::onGDProfile) :
menu_selector(MDTextArea::onLink)
);
@ -254,7 +254,7 @@ struct MDParser {
renderer->popColor();
} else if (s_lastImage.size()) {
bool isFrame = false;
if (string_utils::startsWith(s_lastImage, "frame:")) {
if (utils::string::startsWith(s_lastImage, "frame:")) {
s_lastImage = s_lastImage.substr(s_lastImage.find(":") + 1);
isFrame = true;
}
@ -277,7 +277,7 @@ struct MDParser {
case MD_TEXTTYPE::MD_TEXT_HTML: {
if (text.size() > 2) {
auto tag = string_utils::trim(text.substr(1, text.size() - 2));
auto tag = utils::string::trim(text.substr(1, text.size() - 2));
auto isClosing = tag.front() == '/';
if (isClosing) tag = tag.substr(1);
if (tag.front() != 'c') {

View file

@ -438,7 +438,7 @@ Notification* NotificationBuilder::show() {
bool NotificationManager::isInQueue(Notification* notification) {
auto location = notification->m_location;
if (m_notifications.count(location)) {
return vector_utils::contains(
return utils::vector::contains(
m_notifications.at(location), Ref(notification)
);
}
@ -458,7 +458,7 @@ void NotificationManager::push(Notification* notification) {
void NotificationManager::pop(Notification* notification) {
auto location = notification->m_location;
if (m_notifications.count(location)) {
vector_utils::erase(
utils::vector::erase(
m_notifications.at(location), Ref(notification)
);
if (!m_notifications.at(location).size()) {

View file

@ -431,12 +431,12 @@ std::vector<TextRenderer::Label> TextRenderer::renderStringEx(
if (!createLabel()) return {};
bool firstLine = true;
for (auto line : string_utils::split(str, "\n")) {
for (auto line : utils::string::split(str, "\n")) {
if (!firstLine && !nextLine()) {
return {};
}
firstLine = false;
for (auto word : string_utils::split(line, " ")) {
for (auto word : utils::string::split(line, " ")) {
// add extra space in front of word if not on
// new line
if (!newLine) word = " " + word;
@ -444,8 +444,8 @@ std::vector<TextRenderer::Label> TextRenderer::renderStringEx(
// update capitalization
switch (caps) {
case TextCapitalization::AllUpper: string_utils::toUpperIP(word); break;
case TextCapitalization::AllLower: string_utils::toLowerIP(word); break;
case TextCapitalization::AllUpper: utils::string::toUpperIP(word); break;
case TextCapitalization::AllLower: utils::string::toLowerIP(word); break;
default: break;
}
@ -455,7 +455,7 @@ std::vector<TextRenderer::Label> TextRenderer::renderStringEx(
// try to create a new line
if (!nextLine()) return {};
if (string_utils::startsWith(word, " ")) word = word.substr(1);
if (utils::string::startsWith(word, " ")) word = word.substr(1);
newLine = false;
// try to render on new line
@ -472,7 +472,7 @@ std::vector<TextRenderer::Label> TextRenderer::renderStringEx(
) {
if (!nextLine()) return {};
if (string_utils::startsWith(word, " ")) word = word.substr(1);
if (utils::string::startsWith(word, " ")) word = word.substr(1);
newLine = false;
}
}

View file

@ -3,7 +3,7 @@
USE_GEODE_NAMESPACE();
Result<std::string> file_utils::readString(std::string const& path) {
Result<std::string> utils::file::readString(std::string const& path) {
std::ifstream in(path, std::ios::in | std::ios::binary);
if (in) {
std::string contents;
@ -18,7 +18,7 @@ Result<std::string> file_utils::readString(std::string const& path) {
}
#if _WIN32
Result<std::string> file_utils::readString(std::wstring const& path) {
Result<std::string> utils::file::readString(std::wstring const& path) {
std::ifstream in(path, std::ios::in | std::ios::binary);
if (in) {
std::string contents;
@ -33,7 +33,7 @@ Result<std::string> file_utils::readString(std::wstring const& path) {
}
#endif
Result<std::string> file_utils::readString(ghc::filesystem::path const& path) {
Result<std::string> utils::file::readString(ghc::filesystem::path const& path) {
std::ifstream in(path.string(), std::ios::in | std::ios::binary);
if (in) {
std::string contents;
@ -47,7 +47,7 @@ Result<std::string> file_utils::readString(ghc::filesystem::path const& path) {
return Err("Unable to open file");
}
Result<byte_array> file_utils::readBinary(std::string const& path) {
Result<byte_array> utils::file::readBinary(std::string const& path) {
std::ifstream in(path, std::ios::in | std::ios::binary);
if (in) {
return Ok(byte_array (std::istreambuf_iterator<char>(in), {}));
@ -56,7 +56,7 @@ Result<byte_array> file_utils::readBinary(std::string const& path) {
}
#if _WIN32
Result<byte_array> file_utils::readBinary(std::wstring const& path) {
Result<byte_array> utils::file::readBinary(std::wstring const& path) {
std::ifstream in(path, std::ios::in | std::ios::binary);
if (in) {
return Ok(byte_array (std::istreambuf_iterator<char>(in), {}));
@ -65,7 +65,7 @@ Result<byte_array> file_utils::readBinary(std::wstring const& path) {
}
#endif
Result<byte_array> file_utils::readBinary(ghc::filesystem::path const& path) {
Result<byte_array> utils::file::readBinary(ghc::filesystem::path const& path) {
std::ifstream in(path.string(), std::ios::in | std::ios::binary);
if (in) {
return Ok(byte_array (std::istreambuf_iterator<char>(in), {}));
@ -73,7 +73,7 @@ Result<byte_array> file_utils::readBinary(ghc::filesystem::path const& path) {
return Err("Unable to open file");
}
Result<> file_utils::writeString(std::string const& path, std::string const& data) {
Result<> utils::file::writeString(std::string const& path, std::string const& data) {
std::ofstream file;
file.open(path);
if (file.is_open()) {
@ -87,7 +87,7 @@ Result<> file_utils::writeString(std::string const& path, std::string const& dat
}
#if _WIN32
Result<> file_utils::writeString(std::wstring const& path, std::string const& data) {
Result<> utils::file::writeString(std::wstring const& path, std::string const& data) {
std::ofstream file;
file.open(path);
if (file.is_open()) {
@ -101,7 +101,7 @@ Result<> file_utils::writeString(std::wstring const& path, std::string const& da
}
#endif
Result<> file_utils::writeString(ghc::filesystem::path const& path, std::string const& data) {
Result<> utils::file::writeString(ghc::filesystem::path const& path, std::string const& data) {
std::ofstream file;
file.open(path.string());
if (file.is_open()) {
@ -114,7 +114,7 @@ Result<> file_utils::writeString(ghc::filesystem::path const& path, std::string
return Err<>("Unable to open file");
}
Result<> file_utils::writeBinary(std::string const& path, byte_array const& data) {
Result<> utils::file::writeBinary(std::string const& path, byte_array const& data) {
std::ofstream file;
file.open(path, std::ios::out | std::ios::binary);
if (file.is_open()) {
@ -128,7 +128,7 @@ Result<> file_utils::writeBinary(std::string const& path, byte_array const& data
}
#if _WIN32
Result<> file_utils::writeBinary(std::wstring const& path, byte_array const& data) {
Result<> utils::file::writeBinary(std::wstring const& path, byte_array const& data) {
std::ofstream file;
file.open(path, std::ios::out | std::ios::binary);
if (file.is_open()) {
@ -142,7 +142,7 @@ Result<> file_utils::writeBinary(std::wstring const& path, byte_array const& dat
}
#endif
Result<> file_utils::writeBinary(ghc::filesystem::path const& path, byte_array const& data) {
Result<> utils::file::writeBinary(ghc::filesystem::path const& path, byte_array const& data) {
std::ofstream file;
file.open(path.string(), std::ios::out | std::ios::binary);
if (file.is_open()) {
@ -155,19 +155,19 @@ Result<> file_utils::writeBinary(ghc::filesystem::path const& path, byte_array c
return Err<>("Unable to open file");
}
Result<> file_utils::createDirectory(std::string const& path) {
Result<> utils::file::createDirectory(std::string const& path) {
if (ghc::filesystem::create_directory(path))
return Ok<>();
return Err<>("Unable to create directory");
}
Result<> file_utils::createDirectoryAll(std::string const& path) {
Result<> utils::file::createDirectoryAll(std::string const& path) {
if (ghc::filesystem::create_directories(path))
return Ok<>();
return Err<>("Unable to create directories");
}
Result<std::vector<std::string>> file_utils::listFiles(std::string const& path) {
Result<std::vector<std::string>> utils::file::listFiles(std::string const& path) {
if (!ghc::filesystem::exists(path))
return Err<>("Directory does not exist");
@ -178,7 +178,7 @@ Result<std::vector<std::string>> file_utils::listFiles(std::string const& path)
return Ok<>(res);
}
Result<std::vector<std::string>> file_utils::listFilesRecursively(std::string const& path) {
Result<std::vector<std::string>> utils::file::listFilesRecursively(std::string const& path) {
if (!ghc::filesystem::exists(path))
return Err<>("Directory does not exist");

View file

@ -17,7 +17,7 @@ std::string utils::clipboard::read() {
return std::string([[UIPasteboard generalPasteboard].string UTF8String]);
}
ghc::filesystem::path utils::dirs::geodeRoot() {
ghc::filesystem::path utils::file::geodeRoot() {
return ghc::filesystem::path([[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject].path.UTF8String);
}

View file

@ -21,14 +21,14 @@ std::string utils::clipboard::read() {
return std::string(clipboard);
}
ghc::filesystem::path utils::dirs::geodeRoot() {
ghc::filesystem::path utils::file::geodeRoot() {
char cwd[PATH_MAX];
getcwd(cwd, sizeof(cwd));
// utils::clipboard::write(cwd);
return ghc::filesystem::path(cwd);
}
bool utils::dirs::openFolder(ghc::filesystem::path const& path) {
bool utils::file::openFolder(ghc::filesystem::path const& path) {
NSURL* fileURL = [NSURL fileURLWithPath: [NSString stringWithUTF8String: path.string().c_str()]];
NSURL* folderURL = [fileURL URLByDeletingLastPathComponent];
[[NSWorkspace sharedWorkspace] openURL: folderURL];

View file

@ -9,14 +9,14 @@ USE_GEODE_NAMESPACE();
#include <Windows.h>
#include <stringapiset.h>
std::string string_utils::wideToUtf8(std::wstring const& wstr) {
std::string utils::string::wideToUtf8(std::wstring const& wstr) {
int count = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.length(), NULL, 0, NULL, NULL);
std::string str(count, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &str[0], count, NULL, NULL);
return str;
}
std::wstring string_utils::utf8ToWide(std::string const& str) {
std::wstring utils::string::utf8ToWide(std::string const& str) {
int count = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
std::wstring wstr(count, 0);
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &wstr[0], count);
@ -25,22 +25,22 @@ std::wstring string_utils::utf8ToWide(std::string const& str) {
#endif
bool string_utils::startsWith(std::string const& str, std::string const& prefix) {
bool utils::string::startsWith(std::string const& str, std::string const& prefix) {
return str.rfind(prefix, 0) == 0;
}
bool string_utils::startsWith(std::wstring const& str, std::wstring const& prefix) {
bool utils::string::startsWith(std::wstring const& str, std::wstring const& prefix) {
return str.rfind(prefix, 0) == 0;
}
bool string_utils::endsWith(std::string const& str, std::string const& suffix) {
bool utils::string::endsWith(std::string const& str, std::string const& suffix) {
if (suffix.size() > str.size()) return false;
return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
}
bool string_utils::endsWith(std::wstring const& str, std::wstring const& suffix) {
bool utils::string::endsWith(std::wstring const& str, std::wstring const& suffix) {
if (suffix.size() > str.size()) return false;
return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
}
std::string& string_utils::toLowerIP(std::string& str) {
std::string& utils::string::toLowerIP(std::string& str) {
std::transform(
str.begin(), str.end(),
str.begin(),
@ -51,7 +51,7 @@ std::string& string_utils::toLowerIP(std::string& str) {
return str;
}
std::wstring& string_utils::toLowerIP(std::wstring& str) {
std::wstring& utils::string::toLowerIP(std::wstring& str) {
std::transform(
str.begin(), str.end(),
str.begin(),
@ -62,17 +62,17 @@ std::wstring& string_utils::toLowerIP(std::wstring& str) {
return str;
}
std::string string_utils::toLower(std::string const& str) {
std::string utils::string::toLower(std::string const& str) {
std::string ret = str;
return string_utils::toLowerIP(ret);
return utils::string::toLowerIP(ret);
}
std::wstring string_utils::toLower(std::wstring const& str) {
std::wstring utils::string::toLower(std::wstring const& str) {
std::wstring ret = str;
return string_utils::toLowerIP(ret);
return utils::string::toLowerIP(ret);
}
std::string& string_utils::toUpperIP(std::string& str) {
std::string& utils::string::toUpperIP(std::string& str) {
std::transform(
str.begin(), str.end(),
str.begin(),
@ -83,7 +83,7 @@ std::string& string_utils::toUpperIP(std::string& str) {
return str;
}
std::wstring& string_utils::toUpperIP(std::wstring& str) {
std::wstring& utils::string::toUpperIP(std::wstring& str) {
std::transform(
str.begin(), str.end(),
str.begin(),
@ -94,17 +94,17 @@ std::wstring& string_utils::toUpperIP(std::wstring& str) {
return str;
}
std::string string_utils::toUpper(std::string const& str) {
std::string utils::string::toUpper(std::string const& str) {
std::string ret = str;
return string_utils::toUpperIP(ret);
return utils::string::toUpperIP(ret);
}
std::wstring string_utils::toUpper(std::wstring const& str) {
std::wstring utils::string::toUpper(std::wstring const& str) {
std::wstring ret = str;
return string_utils::toUpperIP(ret);
return utils::string::toUpperIP(ret);
}
std::string& string_utils::replaceIP(
std::string& utils::string::replaceIP(
std::string & str,
std::string const& orig,
std::string const& repl
@ -117,7 +117,7 @@ std::string& string_utils::replaceIP(
return str;
}
std::wstring& string_utils::replaceIP(
std::wstring& utils::string::replaceIP(
std::wstring & str,
std::wstring const& orig,
std::wstring const& repl
@ -130,25 +130,25 @@ std::wstring& string_utils::replaceIP(
return str;
}
std::string string_utils::replace(
std::string utils::string::replace(
std::string const& str,
std::string const& orig,
std::string const& repl
) {
auto ret = str;
return string_utils::replaceIP(ret, orig, repl);
return utils::string::replaceIP(ret, orig, repl);
}
std::wstring string_utils::replace(
std::wstring utils::string::replace(
std::wstring const& str,
std::wstring const& orig,
std::wstring const& repl
) {
auto ret = str;
return string_utils::replaceIP(ret, orig, repl);
return utils::string::replaceIP(ret, orig, repl);
}
std::vector<std::string> string_utils::split(
std::vector<std::string> utils::string::split(
std::string const& str,
std::string const& split
) {
@ -165,7 +165,7 @@ std::vector<std::string> string_utils::split(
return res;
}
std::vector<std::wstring> string_utils::split(
std::vector<std::wstring> utils::string::split(
std::wstring const& str,
std::wstring const& split
) {
@ -182,7 +182,7 @@ std::vector<std::wstring> string_utils::split(
return res;
}
std::vector<char> string_utils::split(std::string const& str) {
std::vector<char> utils::string::split(std::string const& str) {
std::vector<char> res;
for (auto const& s : str) {
res.push_back(s);
@ -190,7 +190,7 @@ std::vector<char> string_utils::split(std::string const& str) {
return res;
}
std::vector<wchar_t> string_utils::split(std::wstring const& str) {
std::vector<wchar_t> utils::string::split(std::wstring const& str) {
std::vector<wchar_t> res;
for (auto const& s : str) {
res.push_back(s);
@ -198,83 +198,83 @@ std::vector<wchar_t> string_utils::split(std::wstring const& str) {
return res;
}
bool string_utils::contains(std::string const& str, std::string const& subs) {
bool utils::string::contains(std::string const& str, std::string const& subs) {
return str.find(subs) != std::string::npos;
}
bool string_utils::contains(std::wstring const& str, std::wstring const& subs) {
bool utils::string::contains(std::wstring const& str, std::wstring const& subs) {
return str.find(subs) != std::wstring::npos;
}
bool string_utils::contains(std::string const& str, std::string::value_type c) {
bool utils::string::contains(std::string const& str, std::string::value_type c) {
return str.find(c) != std::string::npos;
}
bool string_utils::contains(std::wstring const& str, std::wstring::value_type c) {
bool utils::string::contains(std::wstring const& str, std::wstring::value_type c) {
return str.find(c) != std::wstring::npos;
}
bool string_utils::containsAny(
bool utils::string::containsAny(
std::string const& str,
std::vector<std::string> const& subs
) {
for (auto const& sub : subs) {
if (string_utils::contains(str, sub))
if (utils::string::contains(str, sub))
return true;
}
return false;
}
bool string_utils::containsAny(
bool utils::string::containsAny(
std::wstring const& str,
std::vector<std::wstring> const& subs
) {
for (auto const& sub : subs) {
if (string_utils::contains(str, sub))
if (utils::string::contains(str, sub))
return true;
}
return false;
}
bool string_utils::containsAll(
bool utils::string::containsAll(
std::string const& str,
std::vector<std::string> const& subs
) {
bool found = true;
for (auto const& sub : subs) {
if (!string_utils::contains(str, sub))
if (!utils::string::contains(str, sub))
found = false;
}
return found;
}
bool string_utils::containsAll(
bool utils::string::containsAll(
std::wstring const& str,
std::vector<std::wstring> const& subs
) {
bool found = true;
for (auto const& sub : subs) {
if (!string_utils::contains(str, sub))
if (!utils::string::contains(str, sub))
found = false;
}
return found;
}
size_t string_utils::count(std::string const& str, char countC) {
size_t utils::string::count(std::string const& str, char countC) {
size_t res = 0;
for (auto c : str)
if (c == countC) res++;
return res;
}
size_t string_utils::count(std::wstring const& str, wchar_t countC) {
size_t utils::string::count(std::wstring const& str, wchar_t countC) {
size_t res = 0;
for (auto c : str)
if (c == countC) res++;
return res;
}
std::string& string_utils::trimLeftIP(std::string & str) {
std::string& utils::string::trimLeftIP(std::string & str) {
str.erase(str.begin(),
std::find_if(str.begin(), str.end(), [](auto ch) {
return !std::isspace(ch);
@ -283,7 +283,7 @@ std::string& string_utils::trimLeftIP(std::string & str) {
return str;
}
std::wstring& string_utils::trimLeftIP(std::wstring & str) {
std::wstring& utils::string::trimLeftIP(std::wstring & str) {
str.erase(str.begin(),
std::find_if(str.begin(), str.end(), [](auto ch) {
return !std::isspace(ch);
@ -292,84 +292,84 @@ std::wstring& string_utils::trimLeftIP(std::wstring & str) {
return str;
}
std::string& string_utils::trimRightIP(std::string & str) {
std::string& utils::string::trimRightIP(std::string & str) {
str.erase(std::find_if(str.rbegin(), str.rend(), [](auto ch) {
return !std::isspace(ch);
}).base(), str.end());
return str;
}
std::wstring& string_utils::trimRightIP(std::wstring & str) {
std::wstring& utils::string::trimRightIP(std::wstring & str) {
str.erase(std::find_if(str.rbegin(), str.rend(), [](auto ch) {
return !std::isspace(ch);
}).base(), str.end());
return str;
}
std::string& string_utils::trimIP(std::string & str) {
std::string& utils::string::trimIP(std::string & str) {
return
string_utils::trimLeftIP(
string_utils::trimRightIP(
utils::string::trimLeftIP(
utils::string::trimRightIP(
str
));
}
std::wstring& string_utils::trimIP(std::wstring & str) {
std::wstring& utils::string::trimIP(std::wstring & str) {
return
string_utils::trimLeftIP(
string_utils::trimRightIP(
utils::string::trimLeftIP(
utils::string::trimRightIP(
str
));
}
std::string string_utils::trimLeft(std::string const& str) {
std::string utils::string::trimLeft(std::string const& str) {
auto s2 = str;
return string_utils::trimLeftIP(s2);
return utils::string::trimLeftIP(s2);
}
std::wstring string_utils::trimLeft(std::wstring const& str) {
std::wstring utils::string::trimLeft(std::wstring const& str) {
auto s2 = str;
return string_utils::trimLeftIP(s2);
return utils::string::trimLeftIP(s2);
}
std::string string_utils::trimRight(std::string const& str) {
std::string utils::string::trimRight(std::string const& str) {
auto ret = str;
return string_utils::trimRightIP(ret);
return utils::string::trimRightIP(ret);
}
std::wstring string_utils::trimRight(std::wstring const& str) {
std::wstring utils::string::trimRight(std::wstring const& str) {
auto ret = str;
return string_utils::trimRightIP(ret);
return utils::string::trimRightIP(ret);
}
std::string string_utils::trim(std::string const& str) {
std::string utils::string::trim(std::string const& str) {
auto ret = str;
return string_utils::trimIP(ret);
return utils::string::trimIP(ret);
}
std::wstring string_utils::trim(std::wstring const& str) {
std::wstring utils::string::trim(std::wstring const& str) {
auto ret = str;
return string_utils::trimIP(ret);
return utils::string::trimIP(ret);
}
std::string& string_utils::normalizeIP(std::string & str) {
while (string_utils::contains(str, " "))
string_utils::replaceIP(str, " ", " ");
std::string& utils::string::normalizeIP(std::string & str) {
while (utils::string::contains(str, " "))
utils::string::replaceIP(str, " ", " ");
return str;
}
std::wstring& string_utils::normalizeIP(std::wstring & str) {
while (string_utils::contains(str, L" "))
string_utils::replaceIP(str, L" ", L" ");
std::wstring& utils::string::normalizeIP(std::wstring & str) {
while (utils::string::contains(str, L" "))
utils::string::replaceIP(str, L" ", L" ");
return str;
}
std::string string_utils::normalize(std::string const& str) {
std::string utils::string::normalize(std::string const& str) {
auto ret = str;
return string_utils::normalizeIP(ret);
return utils::string::normalizeIP(ret);
}
std::wstring string_utils::normalize(std::wstring const& str) {
std::wstring utils::string::normalize(std::wstring const& str) {
auto ret = str;
return string_utils::normalizeIP(ret);
return utils::string::normalizeIP(ret);
}

View file

@ -0,0 +1,15 @@
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View file

@ -0,0 +1,281 @@
#include "nfdwin.hpp"
#include <Geode/utils/string.hpp>
#ifdef GEODE_IS_WINDOWS
using Path = ghc::filesystem::path;
using Paths = std::vector<ghc::filesystem::path>;
static BOOL COMIsInitialized(HRESULT coResult) {
if (coResult == RPC_E_CHANGED_MODE) {
// If COM was previously initialized with different init flags,
// NFD still needs to operate. Eat this warning.
return true;
}
return SUCCEEDED(coResult);
}
static HRESULT COMInit(void) {
return CoInitializeEx(
nullptr,
COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE
);
}
static void COMUninit(HRESULT coResult) {
// do not uninitialize if RPC_E_CHANGED_MODE occurred -- this
// case does not refcount COM.
if (SUCCEEDED(coResult)) {
CoUninitialize();
}
}
static std::pair<std::wstring, std::wstring> transformFilter(
file::FilePickOptions::Filter const& filter
) {
std::wstring extensions {};
bool first = true;
for (auto& ext : filter.files) {
if (first) {
first = false;
} else {
extensions += L";";
}
extensions += string::utf8ToWide(ext);
}
return {
string::utf8ToWide(filter.description),
extensions,
};
}
static bool addFiltersToDialog(
::IFileDialog *dialog,
std::vector<file::FilePickOptions::Filter> const& filters
) {
if (!filters.size()) {
return true;
}
std::vector<std::pair<std::wstring, std::wstring>> wfilters {};
wfilters.reserve(filters.size());
std::transform(
filters.begin(),
filters.end(),
std::back_inserter(wfilters),
transformFilter
);
wfilters.push_back({ L"All Files", L"*.*" });
std::vector<COMDLG_FILTERSPEC> specList {};
specList.reserve(filters.size() + 1);
for (auto& filter : wfilters) {
specList.emplace_back(
filter.first.c_str(),
filter.second.c_str()
);
}
return SUCCEEDED(
dialog->SetFileTypes(specList.size(), specList.data())
);
}
static Result<Paths> convShellItems(IShellItemArray* shellItems) {
DWORD shellItemCount;
if (!SUCCEEDED(shellItems->GetCount(&shellItemCount))) {
return Err("Unable to get shell item count");
}
std::vector<ghc::filesystem::path> paths;
for (DWORD i = 0; i < shellItemCount; i++) {
IShellItem* shellItem;
if (!SUCCEEDED(shellItems->GetItemAt(i, &shellItem))) {
return Err("Unable to get shell item");
}
SFGAOF attribs;
if (!SUCCEEDED(shellItem->GetAttributes(SFGAO_FILESYSTEM, &attribs))) {
return Err("Unable to get shell item attributes");
}
if (!(attribs & SFGAO_FILESYSTEM)) {
continue;
}
LPWSTR name;
shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name);
paths.push_back(name);
CoTaskMemFree(name);
}
return Ok(paths);
}
static bool setDefaultPath(
IFileDialog* dialog,
ghc::filesystem::path const& defaultPath
) {
IShellItem* folder;
if (!SUCCEEDED(SHCreateItemFromParsingName(
defaultPath.wstring().c_str(), nullptr,
IID_PPV_ARGS(&folder)
))) {
return false;
}
dialog->SetFolder(folder);
folder->Release();
return true;
}
template<class T>
struct Holder {
T m_deallocator;
Holder(T&& deallocator) : m_deallocator(deallocator) {}
~Holder() {
m_deallocator();
}
};
Result<> nfdPick(
NFDMode mode,
file::FilePickOptions const& options,
void* result
) {
auto coResult = COMInit();
if (!COMIsInitialized(coResult)) {
return Err("Could not initialize COM");
}
auto clsid = (
mode == NFDMode::SaveFile ?
CLSID_FileSaveDialog :
CLSID_FileOpenDialog
);
auto iid = (
mode == NFDMode::SaveFile ?
IID_IFileSaveDialog :
IID_IFileOpenDialog
);
IFileDialog* dialog = nullptr;
Holder _([&]() {
if (dialog) {
dialog->Release();
}
COMUninit(coResult);
});
if (!SUCCEEDED(CoCreateInstance(
clsid, nullptr, CLSCTX_ALL, iid,
reinterpret_cast<void**>(&dialog)
))) {
return Err("Could not create dialog");
}
if (!addFiltersToDialog(dialog, options.filters)) {
return Err("Unable to add filters to dialog");
}
if (!setDefaultPath(dialog, options.defaultPath)) {
return Err("Unable to set default path to dialog");
}
if (mode == NFDMode::OpenFiles) {
DWORD flags;
if (!SUCCEEDED(dialog->GetOptions(&flags))) {
return Err("Unable to get dialog options");
}
if (!SUCCEEDED(
dialog->SetOptions(flags | FOS_ALLOWMULTISELECT)
)) {
return Err("Unable to set dialog options");
}
}
else if (mode == NFDMode::OpenFolder) {
DWORD flags;
if (!SUCCEEDED(dialog->GetOptions(&flags))) {
return Err("Unable to get dialog options");
}
if (!SUCCEEDED(
dialog->SetOptions(flags | FOS_PICKFOLDERS)
)) {
return Err("Unable to set dialog options");
}
}
switch (dialog->Show(nullptr)) {
case S_OK: {
switch (mode) {
case NFDMode::OpenFile:
case NFDMode::SaveFile: {
IShellItem* shellItem = nullptr;
if (!SUCCEEDED(dialog->GetResult(&shellItem))) {
return Err("Could not get result from dialog");
}
Holder _([&]() {
shellItem->Release();
});
wchar_t* filePath = nullptr;
if (!SUCCEEDED(
shellItem->GetDisplayName(SIGDN_FILESYSPATH, &filePath)
)) {
return Err("Could not get path from result");
}
*reinterpret_cast<Path*>(result) = filePath;
CoTaskMemFree(filePath);
return Ok();
} break;
case NFDMode::OpenFiles: {
IShellItemArray *shellItems;
if (!SUCCEEDED(static_cast<IFileOpenDialog*>(
dialog
)->GetResults(&shellItems))) {
return Err("Could not get results from dialog");
}
Holder _([&]() {
shellItems->Release();
});
auto res = convShellItems(shellItems);
if (!res) {
return Err(res.error());
}
*reinterpret_cast<Paths*>(result) = res.value();
return Ok();
} break;
case NFDMode::OpenFolder: {
IShellItem* shellItem = nullptr;
if (!SUCCEEDED(dialog->GetResult(&shellItem))) {
return Err("Could not get result from dialog");
}
Holder _([&]() {
shellItem->Release();
});
wchar_t* filePath = nullptr;
if (!SUCCEEDED(
shellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &filePath)
)) {
return Err("Could not get path from result");
}
*reinterpret_cast<Path*>(result) = filePath;
CoTaskMemFree(filePath);
return Ok();
} break;
}
} break;
case HRESULT_FROM_WIN32(ERROR_CANCELLED): {
return Err("Dialog cancelled");
} break;
default: break;
}
return Err("Unknown error");
}
#endif

View file

@ -0,0 +1,62 @@
/*
Native File Dialog
http://www.frogtoss.com/labs
*/
/**
* Copied from https://github.com/mlabbe/nativefiledialog
* Modified to be modern Geode-fitting C++
*/
#include <Geode/utils/platform.hpp>
#ifdef GEODE_IS_WINDOWS
#ifdef __MINGW32__
// Explicitly setting NTDDI version, this is necessary for the MinGW compiler
#define NTDDI_VERSION NTDDI_VISTA
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#endif
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
/* only locally define UNICODE in this compilation unit */
#ifndef UNICODE
#define UNICODE
#endif
#include <wchar.h>
#include <stdio.h>
#include <assert.h>
#include <windows.h>
#include <shobjidl.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#define NFD_MAX_STRLEN 256
#define _NFD_UNUSED(x) ((void)x)
#define NFD_UTF8_BOM "\xEF\xBB\xBF"
#include <stddef.h>
USE_GEODE_NAMESPACE();
enum class NFDMode {
OpenFile,
OpenFiles,
SaveFile,
OpenFolder,
};
Result<> nfdPick(
NFDMode mode,
file::FilePickOptions const& options,
void* result
);
#endif

View file

@ -8,6 +8,9 @@ USE_GEODE_NAMESPACE();
#include <iostream>
#include <sstream>
#include <Windows.h>
#include <shobjidl.h>
#include <shlwapi.h>
#include "nfdwin.hpp"
bool utils::clipboard::write(std::string const& data) {
if (!OpenClipboard(nullptr))
@ -67,16 +70,49 @@ std::string utils::clipboard::read() {
return text;
}
ghc::filesystem::path utils::dirs::geodeRoot() {
ghc::filesystem::path utils::file::geodeRoot() {
return ghc::filesystem::path(CCFileUtils::sharedFileUtils()->getWritablePath2().c_str());
}
bool utils::dirs::openFolder(ghc::filesystem::path const& path) {
bool utils::file::openFolder(ghc::filesystem::path const& path) {
ShellExecuteA(NULL, "open", path.string().c_str(), NULL, NULL, SW_SHOWDEFAULT);
return true;
}
void geode::utils::web::openLinkInBrowser(std::string const& url) {
Result<ghc::filesystem::path> utils::file::pickFile(
file::PickMode mode,
file::FilePickOptions const& options
) {
#define TURN_INTO_NFDMODE(mode) \
case file::PickMode::mode: nfdMode = NFDMode::mode; break;
NFDMode nfdMode;
switch (mode) {
TURN_INTO_NFDMODE(OpenFile);
TURN_INTO_NFDMODE(SaveFile);
TURN_INTO_NFDMODE(OpenFolder);
default: return Err("Unknown open mode");
}
ghc::filesystem::path path;
auto r = nfdPick(nfdMode, options, &path);
if (!r) {
return Err(r.error());
}
return Ok(path);
}
Result<std::vector<ghc::filesystem::path>> utils::file::pickFiles(
file::FilePickOptions const& options
) {
std::vector<ghc::filesystem::path> paths;
auto r = nfdPick(NFDMode::OpenFolder, options, &paths);
if (!r) {
return Err(r.error());
}
return Ok(paths);
}
void utils::web::openLinkInBrowser(std::string const& url) {
ShellExecuteA(0, 0, url.c_str(), 0, 0, SW_SHOW);
}

View file

@ -55,16 +55,28 @@
},
"territory-battle": {
"type": "color",
"default": "#00f"
"default": "#ff006f"
},
"faithful-dog-hachi": {
"type": "rgba",
"default": [ 255, 150, 55, 180 ]
"default": [ 55, 224, 255, 255 ]
},
"im-getting-to-the-bus-to-the-other-world-see-ya": {
"type": "file",
"name": "Bus",
"default": ""
"default": "",
"control": {
"filters": [
{
"description": "Level Files",
"files": [ "*.gmd2", "*.gmd", "*.lvl" ]
},
{
"description": "GMD Files",
"files": [ "*.gmd" ]
}
]
}
}
}
}