This commit is contained in:
camila314 2022-10-03 00:06:48 -05:00
commit 02023afe0c
19 changed files with 185 additions and 95 deletions

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
project(geode-sdk VERSION 0.2.0 LANGUAGES CXX C)
project(geode-sdk VERSION 0.2.1 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.0 LANGUAGES C CXX)
project(geode-loader VERSION 0.2.1 LANGUAGES C CXX)
set(PROJECT_VERSION_TYPE Alpha)
# Package info file for internal representation

View file

@ -77,7 +77,7 @@ namespace geode {
* and has downloaded a mod from the
* future.
*/
static constexpr VersionInfo s_supportedVersionMax { 0, 2, 0 };
static constexpr VersionInfo s_supportedVersionMax { 0, 2, 1 };
Result<std::string> createTempDirectoryForMod(ModInfo const& info);
Result<Mod*> loadModFromFile(std::string const& file);

View file

@ -101,27 +101,27 @@ namespace geode {
* Short & concise description of the
* mod.
*/
std::string m_description = "";
std::optional<std::string> m_description;
/**
* Detailed description of the mod, writtenin Markdown (see
* <Geode/ui/MDTextArea.hpp>) for more info
*/
std::string m_details = "";
std::optional<std::string> m_details;
/**
* Changelog for the mod, written in Markdown (see
* <Geode/ui/MDTextArea.hpp>) for more info
*/
std::string m_changelog = "";
std::optional<std::string> m_changelog;
/**
* Support info for the mod; this means anything to show ways to
* support the mod's development, like donations. Written in Markdown
* (see <Geode/ui/MDTextArea.hpp>) for more info
*/
std::string m_supportInfo = "";
std::optional<std::string> m_supportInfo;
/**
* Git Repository of the mod
*/
std::string m_repository = "";
std::optional<std::string> m_repository;
/**
* Info about where users should report issues and request help
*/
@ -321,8 +321,8 @@ namespace geode {
std::string getID() const;
std::string getName() const;
std::string getDeveloper() const;
std::string getDescription() const;
std::string getDetails() const;
std::optional<std::string> getDescription() const;
std::optional<std::string> getDetails() const;
std::string getPath() const;
VersionInfo getVersion() const;
bool isEnabled() const;

View file

@ -27,6 +27,8 @@ namespace geode {
bool vertical
);
bool ccTouchBegan(cocos2d::CCTouch*, cocos2d::CCEvent*) override;
public:
static ScrollLayer* create(
cocos2d::CCRect const& rect,

View file

@ -214,6 +214,11 @@ namespace geode {
return this->intoAs<T, T>(target);
}
template<class T>
JsonMaybeValue<Json> into(std::optional<T>& target) {
return this->intoAs<T, std::optional<T>>(target);
}
template<class A, class T>
JsonMaybeValue<Json> intoAs(T& target) {
this->inferType<A>();

View file

@ -63,6 +63,22 @@ namespace geode::cocos {
result = node;
return *this;
}
template<class... Args>
SafeCreate<T>& make(Args... args) {
result = T::create(args...);
return *this;
}
// convenience for CCSprite
template<class... Args>
SafeCreate<T>& makeWithFrame(Args... args) {
result = T::createWithSpriteFrameName(args...);
return *this;
}
template<class... Args>
SafeCreate<T>& makeUsing(T*(*func)(Args...), Args... args) {
result = func(args...);
return *this;
}
template<class O = T, class... Args>
T* orMakeUsing(O*(*func)(Args...), Args... args) {
if (result) return result;
@ -73,6 +89,11 @@ namespace geode::cocos {
if (result) return result;
return O::create(args...);
}
template<class O = T, class... Args>
T* orMakeWithFrame(Args... args) {
if (result) return result;
return O::createWithSpriteFrameName(args...);
}
};
/**

Binary file not shown.

After

(image error) Size: 1 KiB

View file

@ -182,7 +182,9 @@ class $modify(CustomMenuLayer, MenuLayer) {
}
// show crash info
if (Loader::get()->didLastLaunchCrash()) {
static bool shownLastCrash = false;
if (Loader::get()->didLastLaunchCrash() && !shownLastCrash) {
shownLastCrash = true;
auto popup = createQuickPopup(
"Crashed",
"It appears that the last session crashed. Would you like to "

View file

@ -89,7 +89,8 @@ void InternalLoader::platformMessageBox(const char* title, std::string const& in
void InternalLoader::openPlatformConsole() {
if (m_platformConsoleOpen) return;
if (AllocConsole() == 0) return;
if (AllocConsole() == 0) return;
SetConsoleCP(CP_UTF8);
// redirect console output
freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);
freopen_s(reinterpret_cast<FILE**>(stdin), "CONIN$", "r", stdin);

View file

@ -44,7 +44,7 @@ DataStore::~DataStore() {
}
Mod::Mod(ModInfo const& info) {
this->m_info = info;
m_info = info;
}
Mod::~Mod() {
@ -142,16 +142,16 @@ void Mod::postDSUpdate() {
}
Result<> Mod::createTempDir() {
ZipFile unzip(this->m_info.m_path.string());
ZipFile unzip(m_info.m_path.string());
if (!unzip.isLoaded()) {
return Err<>("Unable to unzip " + this->m_info.m_path.string());
return Err<>("Unable to unzip " + m_info.m_path.string());
}
if (!unzip.fileExists(this->m_info.m_binaryName)) {
if (!unzip.fileExists(m_info.m_binaryName)) {
return Err<>(
"Unable to find platform binary under the name \"" +
this->m_info.m_binaryName + "\""
m_info.m_binaryName + "\""
);
}
@ -162,7 +162,7 @@ Result<> Mod::createTempDir() {
}
}
auto tempPath = ghc::filesystem::path(tempDir) / this->m_info.m_id;
auto tempPath = ghc::filesystem::path(tempDir) / m_info.m_id;
if (!ghc::filesystem::exists(tempPath) && !ghc::filesystem::create_directories(tempPath)) {
return Err<>("Unable to create temp directory");
}
@ -190,20 +190,20 @@ Result<> Mod::createTempDir() {
if (!wrt) return Err<>("Unable to write \"" + file + "\": " + wrt.error());
}
this->m_addResourcesToSearchPath = true;
m_addResourcesToSearchPath = true;
return Ok<>(tempPath);
}
Result<> Mod::load() {
if (this->m_loaded) {
if (m_loaded) {
return Ok<>();
}
#define RETURN_LOAD_ERR(str) \
{m_loadErrorInfo = str; \
return Err<>(m_loadErrorInfo);}
if (!this->m_tempDirName.string().size()) {
if (!m_tempDirName.string().size()) {
auto err = this->createTempDir();
if (!err) RETURN_LOAD_ERR("Unable to create temp directory: " + err.error());
}
@ -213,23 +213,23 @@ Result<> Mod::load() {
}
auto err = this->loadPlatformBinary();
if (!err) RETURN_LOAD_ERR(err.error());
if (this->m_implicitLoadFunc) {
auto r = this->m_implicitLoadFunc(this);
if (m_implicitLoadFunc) {
auto r = m_implicitLoadFunc(this);
if (!r) {
this->unloadPlatformBinary();
RETURN_LOAD_ERR("Implicit mod entry point returned an error");
}
}
if (this->m_loadFunc) {
auto r = this->m_loadFunc(this);
if (m_loadFunc) {
auto r = m_loadFunc(this);
if (!r) {
this->unloadPlatformBinary();
RETURN_LOAD_ERR("Mod entry point returned an error");
}
}
this->m_loaded = true;
if (this->m_loadDataFunc) {
if (!this->m_loadDataFunc(this->m_saveDirPath.string().c_str())) {
m_loaded = true;
if (m_loadDataFunc) {
if (!m_loadDataFunc(m_saveDirPath.string().c_str())) {
this->logInfo("Mod load data function returned false", Severity::Error);
}
}
@ -239,7 +239,7 @@ Result<> Mod::load() {
}
Result<> Mod::unload() {
if (!this->m_loaded) {
if (!m_loaded) {
return Ok<>();
}
@ -247,63 +247,63 @@ Result<> Mod::unload() {
return Err<>("Mod does not support unloading");
}
if (this->m_saveDataFunc) {
if (!this->m_saveDataFunc(this->m_saveDirPath.string().c_str())) {
if (m_saveDataFunc) {
if (!m_saveDataFunc(m_saveDirPath.string().c_str())) {
this->logInfo("Mod save data function returned false", Severity::Error);
}
}
if (this->m_unloadFunc) {
this->m_unloadFunc();
if (m_unloadFunc) {
m_unloadFunc();
}
for (auto const& hook : this->m_hooks) {
for (auto const& hook : m_hooks) {
auto d = this->disableHook(hook);
if (!d) return d;
delete hook;
}
this->m_hooks.clear();
m_hooks.clear();
for (auto const& patch : this->m_patches) {
for (auto const& patch : m_patches) {
if (!patch->restore()) {
return Err<>("Unable to restore patch at " + std::to_string(patch->getAddress()));
}
delete patch;
}
this->m_patches.clear();
m_patches.clear();
auto res = this->unloadPlatformBinary();
if (!res) {
return res;
}
this->m_loaded = false;
m_loaded = false;
Loader::get()->updateAllDependencies();
return Ok<>();
}
Result<> Mod::enable() {
if (!this->m_loaded) {
if (!m_loaded) {
return Err<>("Mod is not loaded");
}
if (this->m_enableFunc) {
if (!this->m_enableFunc()) {
if (m_enableFunc) {
if (!m_enableFunc()) {
return Err<>("Mod enable function returned false");
}
}
for (auto const& hook : this->m_hooks) {
for (auto const& hook : m_hooks) {
auto d = this->enableHook(hook);
if (!d) return d;
}
for (auto const& patch : this->m_patches) {
for (auto const& patch : m_patches) {
if (!patch->apply()) {
return Err<>("Unable to apply patch at " + std::to_string(patch->getAddress()));
}
}
this->m_enabled = true;
m_enabled = true;
return Ok<>();
}
@ -313,24 +313,24 @@ Result<> Mod::disable() {
return Err<>("Mod does not support disabling");
}
if (this->m_disableFunc) {
if (!this->m_disableFunc()) {
if (m_disableFunc) {
if (!m_disableFunc()) {
return Err<>("Mod disable function returned false");
}
}
for (auto const& hook : this->m_hooks) {
for (auto const& hook : m_hooks) {
auto d = this->disableHook(hook);
if (!d) return d;
}
for (auto const& patch : this->m_patches) {
for (auto const& patch : m_patches) {
if (!patch->restore()) {
return Err<>("Unable to restore patch at " + std::to_string(patch->getAddress()));
}
}
this->m_enabled = false;
m_enabled = false;
return Ok<>();
}
@ -359,15 +359,15 @@ bool Mod::isUninstalled() const {
}
bool Dependency::isUnresolved() const {
return this->m_required &&
(this->m_state == ModResolveState::Unloaded ||
this->m_state == ModResolveState::Unresolved ||
this->m_state == ModResolveState::Disabled);
return m_required &&
(m_state == ModResolveState::Unloaded ||
m_state == ModResolveState::Unresolved ||
m_state == ModResolveState::Disabled);
}
bool Mod::updateDependencyStates() {
bool hasUnresolved = false;
for (auto & dep : this->m_info.m_dependencies) {
for (auto & dep : m_info.m_dependencies) {
if (!dep.m_mod) {
dep.m_mod = Loader::get()->getLoadedMod(dep.m_id);
}
@ -404,15 +404,15 @@ bool Mod::updateDependencyStates() {
dep.m_state = ModResolveState::Unloaded;
}
if (dep.isUnresolved()) {
this->m_resolved = false;
m_resolved = false;
this->unload();
hasUnresolved = true;
}
}
if (!hasUnresolved && !this->m_resolved) {
if (!hasUnresolved && !m_resolved) {
Log::get() << Severity::Debug << "All dependencies for " << m_info.m_id << " found";
this->m_resolved = true;
if (this->m_enabled) {
m_resolved = true;
if (m_enabled) {
Log::get() << Severity::Debug << "Resolved & loading " << m_info.m_id;
auto r = this->load();
if (!r) {
@ -432,7 +432,7 @@ bool Mod::updateDependencyStates() {
}
bool Mod::hasUnresolvedDependencies() const {
for (auto const& dep : this->m_info.m_dependencies) {
for (auto const& dep : m_info.m_dependencies) {
if (dep.isUnresolved()) {
return true;
}
@ -442,7 +442,7 @@ bool Mod::hasUnresolvedDependencies() const {
std::vector<Dependency> Mod::getUnresolvedDependencies() {
std::vector<Dependency> res;
for (auto const& dep : this->m_info.m_dependencies) {
for (auto const& dep : m_info.m_dependencies) {
if (dep.isUnresolved()) {
res.push_back(dep);
}
@ -451,27 +451,27 @@ std::vector<Dependency> Mod::getUnresolvedDependencies() {
}
ghc::filesystem::path Mod::getSaveDir() const {
return this->m_saveDirPath;
return m_saveDirPath;
}
decltype(ModInfo::m_id) Mod::getID() const {
return this->m_info.m_id;
return m_info.m_id;
}
decltype(ModInfo::m_name) Mod::getName() const {
return this->m_info.m_name;
return m_info.m_name;
}
decltype(ModInfo::m_developer) Mod::getDeveloper() const {
return this->m_info.m_developer;
return m_info.m_developer;
}
decltype(ModInfo::m_description) Mod::getDescription() const {
return this->m_info.m_description;
return m_info.m_description;
}
decltype(ModInfo::m_details) Mod::getDetails() const {
return this->m_info.m_details;
return m_info.m_details;
}
ModInfo Mod::getModInfo() const {
@ -487,27 +487,27 @@ ghc::filesystem::path Mod::getBinaryPath() const {
}
std::string Mod::getPath() const {
return this->m_info.m_path.string();
return m_info.m_path.string();
}
VersionInfo Mod::getVersion() const {
return this->m_info.m_version;
return m_info.m_version;
}
bool Mod::isEnabled() const {
return this->m_enabled;
return m_enabled;
}
bool Mod::isLoaded() const {
return this->m_loaded;
return m_loaded;
}
bool Mod::supportsDisabling() const {
return this->m_info.m_supportsDisabling;
return m_info.m_supportsDisabling;
}
bool Mod::supportsUnloading() const {
return this->m_info.m_supportsUnloading;
return m_info.m_supportsUnloading;
}
bool Mod::wasSuccesfullyLoaded() const {
@ -515,7 +515,7 @@ bool Mod::wasSuccesfullyLoaded() const {
}
std::vector<Hook*> Mod::getHooks() const {
return this->m_hooks;
return m_hooks;
}
Log Mod::log() {
@ -532,7 +532,7 @@ void Mod::logInfo(
bool Mod::depends(std::string const& id) const {
return utils::vector::contains<Dependency>(
this->m_info.m_dependencies,
m_info.m_dependencies,
[id](Dependency t) -> bool { return t.m_id == id; }
);
}
@ -542,8 +542,8 @@ const char* Mod::expandSpriteName(const char* name) {
if (expanded.count(name)) {
return expanded[name];
}
auto exp = new char[strlen(name) + 2 + this->m_info.m_id.size()];
auto exps = this->m_info.m_id + "/" + name;
auto exp = new char[strlen(name) + 2 + m_info.m_id.size()];
auto exps = m_info.m_id + "/" + name;
memcpy(exp, exps.c_str(), exps.size() + 1);
expanded[name] = exp;
return exp;

View file

@ -161,7 +161,7 @@ Result<ModInfo> ModInfo::create(ModJson const& json) {
}
// Handle mod.json data based on target
if (schema <= VersionInfo(0, 2, 0)) {
if (schema <= VersionInfo(0, 2, 1)) {
return ModInfo::createFromSchemaV010(json);
}
@ -233,7 +233,7 @@ Result<ModInfo> ModInfo::createFromGeodeFile(ghc::filesystem::path const& path)
info.m_path = path;
// unzip known MD files
using God = std::initializer_list<std::pair<std::string, std::string*>>;
using God = std::initializer_list<std::pair<std::string, std::optional<std::string>*>>;
for (auto [file, target] : God {
{ "about.md", &info.m_details },
{ "changelog.md", &info.m_changelog },

View file

@ -164,8 +164,8 @@ bool ModInfoLayer::init(ModObject* obj, ModListView* list) {
this->registerWithTouchDispatcher();
auto details = MDTextArea::create(
m_info.m_details.size() ?
m_info.m_details :
m_info.m_details ?
m_info.m_details.value() :
"### No description provided.",
{ 350.f, 137.5f }
);
@ -182,6 +182,59 @@ bool ModInfoLayer::init(ModObject* obj, ModListView* list) {
);
m_mainLayer->addChild(detailsBar);
// changelog
if (m_info.m_changelog) {
auto changelog = MDTextArea::create(
m_info.m_changelog.value(),
{ 350.f, 137.5f }
);
changelog->setPosition(
-5000.f,
winSize.height / 2 - changelog->getScaledContentSize().height / 2 - 20.f
);
changelog->setVisible(false);
m_mainLayer->addChild(changelog);
auto changelogBtnOffSpr = ButtonSprite::create(
CCSprite::createWithSpriteFrameName("changelog.png"_spr),
0x20, true, 32.f, "GJ_button_01.png", 1.f
);
changelogBtnOffSpr->setScale(.65f);
auto changelogBtnOnSpr = ButtonSprite::create(
CCSprite::createWithSpriteFrameName("changelog.png"_spr),
0x20, true, 32.f, "GJ_button_02.png", 1.f
);
changelogBtnOnSpr->setScale(.65f);
auto changelogBtn = CCMenuItemToggler::create(
changelogBtnOffSpr,
changelogBtnOnSpr,
this,
makeMenuSelector([
this, winSize, details, detailsBar, changelog
](CCMenuItemToggler* toggle) {
details->setVisible(toggle->isToggled());
// as it turns out, cocos2d is stupid and still passes touch
// events to invisible nodes
details->setPositionX(toggle->isToggled() ?
winSize.width / 2 - details->getScaledContentSize().width / 2 :
-5000.f
);
changelog->setVisible(!toggle->isToggled());
// as it turns out, cocos2d is stupid and still passes touch
// events to invisible nodes
changelog->setPositionX(!toggle->isToggled() ?
winSize.width / 2 - changelog->getScaledContentSize().width / 2 :
-5000.f
);
})
);
changelogBtn->setPosition(-size.width / 2 + 21.5f, .0f);
m_buttonMenu->addChild(changelogBtn);
}
auto infoSpr = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png");
infoSpr->setScale(.85f);
@ -231,11 +284,11 @@ bool ModInfoLayer::init(ModObject* obj, ModListView* list) {
);
}
if (m_mod->getModInfo().m_repository.size()) {
if (m_mod->getModInfo().m_repository) {
auto repoBtn = CCMenuItemSpriteExtra::create(
CCSprite::createWithSpriteFrameName("github.png"_spr),
this, makeMenuSelector([this](CCObject*) {
web::openLinkInBrowser(m_mod->getModInfo().m_repository);
web::openLinkInBrowser(m_mod->getModInfo().m_repository.value());
})
);
repoBtn->setPosition(
@ -245,13 +298,13 @@ bool ModInfoLayer::init(ModObject* obj, ModListView* list) {
m_buttonMenu->addChild(repoBtn);
}
if (m_mod->getModInfo().m_supportInfo.size()) {
if (m_mod->getModInfo().m_supportInfo) {
auto supportBtn = CCMenuItemSpriteExtra::create(
CCSprite::createWithSpriteFrameName("gift.png"_spr),
this, makeMenuSelector([this](CCObject*) {
MDPopup::create(
"Support " + m_mod->getName(),
m_mod->getModInfo().m_supportInfo,
m_mod->getModInfo().m_supportInfo.value(),
"OK"
)->show();
})

View file

@ -193,7 +193,7 @@ void ModCell::loadFromObject(ModObject* modobj) {
default: return;
}
bool hasDesc = m_expanded && info.m_description.size();
bool hasDesc = m_expanded && info.m_description.has_value();
auto titleLabel = CCLabelBMFont::create(info.m_name.c_str(), "bigFont.fnt");
titleLabel->setAnchorPoint({ .0f, .5f });
@ -254,7 +254,7 @@ void ModCell::loadFromObject(ModObject* modobj) {
m_mainLayer->addChild(descBG);
auto descText = CCLabelBMFont::create(
info.m_description.c_str(),
info.m_description.value().c_str(),
"chatFont.fnt"
);
descText->setAnchorPoint({ .0f, .5f });
@ -451,8 +451,8 @@ bool ModListView::filter(ModInfo const& info, ModListQuery const& query) {
if (check(SearchFlag::Name, info.m_name)) return true;
if (check(SearchFlag::ID, info.m_id)) return true;
if (check(SearchFlag::Developer, info.m_developer)) return true;
if (check(SearchFlag::Description, info.m_description)) return true;
if (check(SearchFlag::Details, info.m_details)) return true;
if (check(SearchFlag::Description, info.m_description.value_or(""))) return true;
if (check(SearchFlag::Details, info.m_details.value_or(""))) return true;
return false;
}

View file

@ -38,6 +38,13 @@ void ScrollLayer::enableScrollWheel(bool enable) {
m_scrollWheelEnabled = enable;
}
bool ScrollLayer::ccTouchBegan(CCTouch* touch, CCEvent* event) {
if (this->isVisible()) {
return CCScrollLayerExt::ccTouchBegan(touch, event);
}
return false;
}
ScrollLayer::ScrollLayer(
CCRect const& rect,
bool scrollWheelEnabled,

View file

@ -34,7 +34,7 @@ Result<std::string> utils::file::readString(std::wstring const& path) {
#endif
Result<std::string> utils::file::readString(ghc::filesystem::path const& path) {
std::ifstream in(path.string(), std::ios::in | std::ios::binary);
std::ifstream in(path.wstring(), std::ios::in | std::ios::binary);
if (in) {
std::string contents;
in.seekg(0, std::ios::end);
@ -66,7 +66,7 @@ Result<byte_array> utils::file::readBinary(std::wstring const& path) {
#endif
Result<byte_array> utils::file::readBinary(ghc::filesystem::path const& path) {
std::ifstream in(path.string(), std::ios::in | std::ios::binary);
std::ifstream in(path.wstring(), std::ios::in | std::ios::binary);
if (in) {
return Ok(byte_array (std::istreambuf_iterator<char>(in), {}));
}
@ -103,7 +103,7 @@ Result<> utils::file::writeString(std::wstring const& path, std::string const& d
Result<> utils::file::writeString(ghc::filesystem::path const& path, std::string const& data) {
std::ofstream file;
file.open(path.string());
file.open(path.wstring());
if (file.is_open()) {
file << data;
file.close();
@ -144,7 +144,7 @@ Result<> utils::file::writeBinary(std::wstring const& path, byte_array const& da
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);
file.open(path.wstring(), std::ios::out | std::ios::binary);
if (file.is_open()) {
file.write(reinterpret_cast<const char*>(data.data()), data.size());
file.close();

View file

@ -219,7 +219,6 @@ Result<> nfdPick(
)) {
return Err("Could not get path from result");
}
*reinterpret_cast<Path*>(result) = filePath;
CoTaskMemFree(filePath);

View file

@ -1,5 +1,5 @@
{
"geode": "v0.2.0",
"geode": "v0.2.1",
"version": "v1.0.0",
"id": "geode.testdep",
"name": "Geode Test Dependency",

View file

@ -1,5 +1,5 @@
{
"geode": "v0.2.0",
"geode": "v0.2.1",
"version": "v1.0.0",
"id": "geode.test",
"name": "Geode Test",