Merge branch 'main' of https://github.com/geode-sdk/geode into main

This commit is contained in:
camila314 2022-12-12 13:33:14 -06:00
commit c440aaa7cc
23 changed files with 322 additions and 169 deletions

3
.gitmodules vendored
View file

@ -4,6 +4,3 @@
[submodule "loader/minhook"]
path = loader/minhook
url = https://github.com/TsudaKageyu/minhook
[submodule "loader/include/Geode/external/scnlib"]
path = loader/include/Geode/external/scnlib
url = https://github.com/eliaskosunen/scnlib

View file

@ -83,7 +83,6 @@ add_definitions(-DFMT_CONSTEVAL=)
add_subdirectory(loader/include/Geode/external/filesystem)
add_subdirectory(loader/include/Geode/external/fmt)
add_subdirectory(loader/include/Geode/external/scnlib)
target_link_libraries(${PROJECT_NAME} INTERFACE filesystem fmt)

View file

@ -237,8 +237,8 @@ class cocos2d::CCIMEDispatcher {
static auto sharedDispatcher() = mac 0x2773f0, ios 0x12d170;
auto addDelegate(cocos2d::CCIMEDelegate*) = mac 0x277480, ios 0x12d204;
auto removeDelegate(cocos2d::CCIMEDelegate*) = mac 0x2775f0, ios 0x12d2c4;
void dispatchInsertText(const char* text, int len);
void dispatchDeleteBackward();
void dispatchInsertText(const char* text, int len) = mac 0x277ac0;
void dispatchDeleteBackward() = mac 0x277af0;
}
class cocos2d::CCImage {

View file

@ -129,7 +129,7 @@ target_link_libraries(${PROJECT_NAME} md4c)
# Lilac (hooking)
add_subdirectory(lilac)
target_link_libraries(${PROJECT_NAME} z lilac_hook geode-sdk scn::scn)
target_link_libraries(${PROJECT_NAME} z lilac_hook geode-sdk)
# Use precompiled headers for faster builds
target_precompile_headers(${PROJECT_NAME} PRIVATE

View file

@ -6,7 +6,7 @@
#include "ui/BasedButtonSprite.hpp"
#include "ui/IconButtonSprite.hpp"
#include "ui/InputNode.hpp"
#include "ui/LayerBG.hpp"
#include "ui/General.hpp"
#include "ui/ListView.hpp"
#include "ui/MDPopup.hpp"
#include "ui/MDTextArea.hpp"

@ -1 +0,0 @@
Subproject commit 72a4bab9e32f2b44593137fba40c54710e20a623

View file

@ -196,7 +196,7 @@ namespace geode::core::meta::x86 {
class Membercall<Ret, Class, Args...> {
protected:
using Sequences = Membercall<Ret*, Class, Ret*, Args...>::Sequences;
using Sequences = typename Membercall<Ret*, Class, Ret*, Args...>::Sequences;
// Where all the logic is actually implemented.
template <class Class, class>

View file

@ -27,6 +27,8 @@
#define GEODE_API extern "C" __declspec(dllexport)
#define GEODE_EXPORT __declspec(dllexport)
static_assert(sizeof(void*) == 4, "Geode must be compiled in 32-bit for Windows!");
#include "windows.hpp"
#elif defined(GEODE_IS_MACOS)

View file

@ -10,4 +10,13 @@ namespace geode {
* packs the ability to override this function.
*/
GEODE_DLL cocos2d::CCSprite* createLayerBG();
/**
* Add the rounded comment borders to a node
*/
GEODE_DLL void addListBorders(
cocos2d::CCNode* to,
cocos2d::CCPoint const& center,
cocos2d::CCSize const& size
);
}

View file

@ -4,6 +4,7 @@
#include <string_view>
#include "../external/json/json.hpp"
#include <tuple>
#include "../utils/Result.hpp"
namespace geode {
enum class VersionCompare {
@ -55,8 +56,8 @@ namespace geode {
m_patch = patch;
m_tag = tag;
}
VersionInfo(std::string const& versionString);
static bool validate(std::string const& string);
static Result<VersionInfo> parse(std::string const& string);
constexpr size_t getMajor() const {
return m_major;
@ -114,8 +115,8 @@ namespace geode {
VersionInfo const& version,
VersionCompare const& compare
) : m_version(version), m_compare(compare) {}
ComparableVersionInfo(std::string const& versionString);
static bool validate(std::string const& string);
static Result<ComparableVersionInfo> parse(std::string const& string);
constexpr bool compare(VersionInfo const& version) const {
switch (m_compare) {

View file

@ -32,4 +32,4 @@ BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID _) {
return FALSE;
}
return TRUE;
}
}

View file

@ -48,7 +48,7 @@ Result<ModInfo> ModInfo::createFromSchemaV010(ModJson const& rawJson) {
using nlohmann::detail::value_t;
root.needs("id").validate(&ModInfo::validateID).into(info.id);
root.needs("version").validate(&VersionInfo::validate).into(info.version);
root.needs("version").into(info.version);
root.needs("name").into(info.name);
root.needs("developer").into(info.developer);
root.has("description").into(info.description);
@ -62,9 +62,7 @@ Result<ModInfo> ModInfo::createFromSchemaV010(ModJson const& rawJson) {
auto depobj = Dependency {};
obj.needs("id").validate(&ModInfo::validateID).into(depobj.id);
obj.needs("version")
.validate(&ComparableVersionInfo::validate)
.into(depobj.version);
obj.needs("version").into(depobj.version);
obj.has("required").into(depobj.required);
obj.checkUnknownKeys();
@ -116,16 +114,10 @@ Result<ModInfo> ModInfo::create(ModJson const& json) {
// Check mod.json target version
auto schema = LOADER_VERSION;
if (json.contains("geode") && json["geode"].is_string()) {
auto ver = json["geode"];
if (VersionInfo::validate(ver)) {
schema = VersionInfo(ver);
}
else {
return Err(
"[mod.json] has no target loader version "
"specified, or it is invalidally formatted (required: \"[v]X.X.X\")!"
);
}
GEODE_UNWRAP_INTO(
schema, VersionInfo::parse(json["geode"])
.expect("[mod.json] has invalid target loader version: {error}")
);
}
else {
return Err(

View file

@ -0,0 +1,55 @@
#include "DevProfilePopup.hpp"
#include <Geode/ui/ListView.hpp>
#include <Geode/loader/Index.hpp>
#include <Geode/ui/General.hpp>
#include "../list/ModListCell.hpp"
#include "../list/ModListLayer.hpp"
bool DevProfilePopup::setup(std::string const& developer) {
m_noElasticity = true;
this->setTitle("Mods by " + developer);
auto winSize = CCDirector::get()->getWinSize();
auto items = CCArray::create();
// installed mods
for (auto& mod : Loader::get()->getAllMods()) {
if (mod->getDeveloper() == developer) {
items->addObject(ModCell::create(
mod, nullptr, ModListDisplay::Concise, { 358.f, 40.f }
));
}
}
// index mods
for (auto& item : Index::get()->getItemsByDeveloper(developer)) {
if (Loader::get()->isModInstalled(item->info.id)) {
continue;
}
items->addObject(IndexItemCell::create(
item, nullptr, ModListDisplay::Concise, { 358.f, 40.f }
));
}
// mods list
auto listSize = CCSize { 358.f, 160.f };
auto list = ListView::create(items, 40.f, listSize.width, listSize.height);
list->setPosition(winSize / 2 - listSize / 2);
m_mainLayer->addChild(list);
addListBorders(m_mainLayer, winSize / 2, listSize);
return true;
}
DevProfilePopup* DevProfilePopup::create(std::string const& developer) {
auto ret = new DevProfilePopup();
if (ret && ret->init(420.f, 260.f, developer)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}

View file

@ -0,0 +1,13 @@
#pragma once
#include <Geode/ui/Popup.hpp>
USE_GEODE_NAMESPACE();
class DevProfilePopup : public Popup<std::string const&> {
protected:
bool setup(std::string const& developer) override;
public:
static DevProfilePopup* create(std::string const& developer);
};

View file

@ -423,7 +423,9 @@ void LocalModInfoPopup::onEnableMod(CCObject* sender) {
"need to <cg>restart</c> the game to have it fully unloaded.",
"OK"
)->show();
if (m_layer) m_layer->updateAllStates(nullptr);
if (m_layer) {
m_layer->updateAllStates(nullptr);
}
return;
}
if (as<CCMenuItemToggler*>(sender)->isToggled()) {
@ -438,7 +440,9 @@ void LocalModInfoPopup::onEnableMod(CCObject* sender) {
FLAlertLayer::create(nullptr, "Error Disabling Mod", res.unwrapErr(), "OK", nullptr)->show();
}
}
if (m_layer) m_layer->updateAllStates(nullptr);
if (m_layer) {
m_layer->updateAllStates(nullptr);
}
as<CCMenuItemToggler*>(sender)->toggle(m_mod->isEnabled());
}
@ -477,7 +481,9 @@ void LocalModInfoPopup::FLAlert_Clicked(FLAlertLayer* layer, bool btn2) {
FLAlertLayer::create("Error", "Unable to delete mod's save directory!", "OK")->show();
}
}
if (m_layer) m_layer->reloadList();
if (m_layer) {
m_layer->reloadList();
}
this->onClose(nullptr);
} break;
}

View file

@ -1,7 +1,7 @@
#include "ModListCell.hpp"
#include "ModListLayer.hpp"
#include "../info/ModInfoPopup.hpp"
#include <Geode/binding/ButtonSprite.hpp>
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
@ -10,6 +10,7 @@
#include <Geode/ui/GeodeUI.hpp>
#include "../../../loader/LoaderImpl.hpp" // how should i include this src/loader/LoaderImpl.hpp
#include "../info/TagNode.hpp"
#include "../info/DevProfilePopup.hpp"
template <class T>
static bool tryOrAlert(Result<T> const& res, char const* title) {
@ -27,7 +28,11 @@ float ModListCell::getLogoSize() const {
return m_height / 1.5f;
}
void ModListCell::setupInfo(ModInfo const& info, bool spaceForTags) {
void ModListCell::setupInfo(
ModInfo const& info,
bool spaceForTags,
ModListDisplay display
) {
m_menu = CCMenu::create();
m_menu->setPosition(m_width - 40.f, m_height / 2);
this->addChild(m_menu);
@ -39,7 +44,7 @@ void ModListCell::setupInfo(ModInfo const& info, bool spaceForTags) {
this->addChild(logoSpr);
bool hasDesc =
m_layer->getDisplay() == ModListDisplay::Expanded &&
display == ModListDisplay::Expanded &&
info.description.has_value();
auto titleLabel = CCLabelBMFont::create(info.name.c_str(), "bigFont.fnt");
@ -135,8 +140,7 @@ void ModListCell::setupInfo(ModInfo const& info, bool spaceForTags) {
}
void ModListCell::onViewDev(CCObject*) {
m_layer->getQuery().developer = this->getDeveloper();
m_layer->reloadList();
DevProfilePopup::create(this->getDeveloper())->show();
}
bool ModListCell::init(ModListLayer* list, CCSize const& size) {
@ -153,10 +157,11 @@ bool ModListCell::init(ModListLayer* list, CCSize const& size) {
ModCell* ModCell::create(
Mod* mod,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
) {
auto ret = new ModCell();
if (ret && ret->init(mod, list, size)) {
if (ret && ret->init(mod, list, display, size)) {
return ret;
}
CC_SAFE_DELETE(ret);
@ -173,7 +178,9 @@ void ModCell::onEnable(CCObject* sender) {
"need to <cg>restart</c> the game to have it fully unloaded.",
"OK"
)->show();
m_layer->updateAllStates(this);
if (m_layer) {
m_layer->updateAllStates(this);
}
return;
}
if (!as<CCMenuItemToggler*>(sender)->isToggled()) {
@ -182,7 +189,9 @@ void ModCell::onEnable(CCObject* sender) {
else {
tryOrAlert(m_mod->disable(), "Error disabling mod");
}
m_layer->updateAllStates(this);
if (m_layer) {
m_layer->updateAllStates(this);
}
}
void ModCell::onUnresolvedInfo(CCObject*) {
@ -220,6 +229,7 @@ void ModCell::updateState() {
bool ModCell::init(
Mod* mod,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
) {
if (!ModListCell::init(list, size))
@ -227,7 +237,7 @@ bool ModCell::init(
m_mod = mod;
this->setupInfo(mod->getModInfo(), false);
this->setupInfo(mod->getModInfo(), false, display);
auto viewSpr = ButtonSprite::create("View", "bigFont.fnt", "GJ_button_01.png", .8f);
viewSpr->setScale(.65f);
@ -285,10 +295,11 @@ void IndexItemCell::onInfo(CCObject*) {
IndexItemCell* IndexItemCell::create(
IndexItemHandle item,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
) {
auto ret = new IndexItemCell();
if (ret && ret->init(item, list, size)) {
if (ret && ret->init(item, list, display, size)) {
return ret;
}
CC_SAFE_DELETE(ret);
@ -298,6 +309,7 @@ IndexItemCell* IndexItemCell::create(
bool IndexItemCell::init(
IndexItemHandle item,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
) {
if (!ModListCell::init(list, size))
@ -305,7 +317,7 @@ bool IndexItemCell::init(
m_item = item;
this->setupInfo(item->info, item->tags.size());
this->setupInfo(item->info, item->tags.size(), display);
auto viewSpr = ButtonSprite::create(
"View", "bigFont.fnt", "GJ_button_01.png", .8f
@ -384,13 +396,16 @@ void InvalidGeodeFileCell::FLAlert_Clicked(FLAlertLayer*, bool btn2) {
->show();
}
Loader::get()->refreshModsList();
m_layer->reloadList();
if (m_layer) {
m_layer->reloadList();
}
}
}
bool InvalidGeodeFileCell::init(
InvalidGeodeFile const& info,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
) {
if (!ModListCell::init(list, size))
@ -431,10 +446,11 @@ bool InvalidGeodeFileCell::init(
InvalidGeodeFileCell* InvalidGeodeFileCell::create(
InvalidGeodeFile const& file,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
) {
auto ret = new InvalidGeodeFileCell();
if (ret && ret->init(file, list, size)) {
if (ret && ret->init(file, list, display, size)) {
ret->autorelease();
return ret;
}

View file

@ -25,7 +25,7 @@ protected:
CCMenuItemSpriteExtra* m_unresolvedExMark;
bool init(ModListLayer* list, CCSize const& size);
void setupInfo(ModInfo const& info, bool spaceForTags);
void setupInfo(ModInfo const& info, bool spaceForTags, ModListDisplay display);
void draw() override;
float getLogoSize() const;
@ -47,6 +47,7 @@ protected:
bool init(
Mod* mod,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
);
@ -58,6 +59,7 @@ public:
static ModCell* create(
Mod* mod,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
);
@ -76,6 +78,7 @@ protected:
bool init(
IndexItemHandle item,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
);
@ -85,6 +88,7 @@ public:
static IndexItemCell* create(
IndexItemHandle item,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
);
@ -103,6 +107,7 @@ protected:
bool init(
InvalidGeodeFile const& file,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
);
@ -113,6 +118,7 @@ public:
static InvalidGeodeFileCell* create(
InvalidGeodeFile const& file,
ModListLayer* list,
ModListDisplay display,
CCSize const& size
);

View file

@ -73,12 +73,9 @@ static std::optional<int> queryMatchKeywords(
}
static std::optional<int> queryMatch(ModListQuery const& query, Mod* mod) {
// Only checking keywords and developer makes sense for mods since their
// Only checking keywords makes sense for mods since their
// platform always matches, they are always visible and they don't
// currently list their tags
if (query.developer && query.developer.value() != mod->getDeveloper()) {
return std::nullopt;
}
return queryMatchKeywords(query, mod->getModInfo());
}
@ -88,10 +85,6 @@ static std::optional<int> queryMatch(ModListQuery const& query, IndexItemHandle
if (!query.forceVisibility && Loader::get()->isModInstalled(item->info.id)) {
return std::nullopt;
}
// make sure developer matches
if (query.developer && query.developer.value() != item->info.developer) {
return std::nullopt;
}
// make sure all tags match
for (auto& tag : query.tags) {
if (!item->tags.count(tag)) {
@ -127,7 +120,6 @@ static std::optional<int> queryMatch(ModListQuery const& query, IndexItemHandle
static std::optional<int> queryMatch(ModListQuery const& query, InvalidGeodeFile const& info) {
// if any explicit filters were provided, no match
if (
query.developer.has_value() ||
query.tags.size() ||
query.keywords.has_value()
) {
@ -144,7 +136,9 @@ CCArray* ModListLayer::createModCells(ModListType type, ModListQuery const& quer
// failed mods first
for (auto const& mod : Loader::get()->getFailedMods()) {
if (!queryMatch(query, mod)) continue;
mods->addObject(InvalidGeodeFileCell::create(mod, this, this->getCellSize()));
mods->addObject(InvalidGeodeFileCell::create(
mod, this, m_display, this->getCellSize()
));
}
// sort the mods by match score
@ -173,7 +167,9 @@ CCArray* ModListLayer::createModCells(ModListType type, ModListQuery const& quer
// add the mods sorted
for (auto& [score, mod] : ranges::reverse(sorted)) {
mods->addObject(ModCell::create(mod, this, this->getCellSize()));
mods->addObject(ModCell::create(
mod, this, m_display, this->getCellSize()
));
}
} break;
@ -189,7 +185,9 @@ CCArray* ModListLayer::createModCells(ModListType type, ModListQuery const& quer
// add the mods sorted
for (auto& [score, item] : ranges::reverse(sorted)) {
mods->addObject(IndexItemCell::create(item, this, this->getCellSize()));
mods->addObject(IndexItemCell::create(
item, this, m_display, this->getCellSize()
));
}
} break;
@ -205,7 +203,9 @@ CCArray* ModListLayer::createModCells(ModListType type, ModListQuery const& quer
// add the mods sorted
for (auto& [score, item] : ranges::reverse(sorted)) {
mods->addObject(IndexItemCell::create(item, this, this->getCellSize()));
mods->addObject(IndexItemCell::create(
item, this, m_display, this->getCellSize()
));
}
} break;
}
@ -489,8 +489,7 @@ void ModListLayer::reloadList(std::optional<ModListQuery> const& query) {
// and show visual indicator if so
auto hasQuery =
(m_searchInput->getString() &&
strlen(m_searchInput->getString())) ||
m_query.developer;
strlen(m_searchInput->getString()));
m_searchBtn->setVisible(!hasQuery);
m_searchClearBtn->setVisible(hasQuery);
@ -606,8 +605,6 @@ void ModListLayer::onOpenFolder(CCObject*) {
}
void ModListLayer::onResetSearch(CCObject*) {
// todo: remove when implementing more reasonable developer view
m_query.developer = std::nullopt;
m_searchInput->setString("");
}

View file

@ -34,10 +34,6 @@ struct ModListQuery {
*/
std::unordered_set<PlatformID> platforms = { GEODE_PLATFORM_TARGET };
std::unordered_set<std::string> tags;
/**
* Used to filter by dev if you click their name
*/
std::optional<std::string> developer;
};
class ModListLayer : public CCLayer, public TextInputDelegate {

View file

@ -5,6 +5,7 @@
#include <Geode/loader/Setting.hpp>
#include <Geode/ui/ScrollLayer.hpp>
#include <Geode/utils/cocos.hpp>
#include <Geode/ui/General.hpp>
bool ModSettingsPopup::setup(Mod* mod) {
m_noElasticity = true;
@ -77,29 +78,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
// layer borders
auto layerTopSpr = CCSprite::createWithSpriteFrameName("GJ_commentTop_001.png");
layerTopSpr->setPosition({ winSize.width / 2, winSize.height / 2 + layerSize.height / 2 - 5.f }
);
m_mainLayer->addChild(layerTopSpr);
auto layerBottomSpr = CCSprite::createWithSpriteFrameName("GJ_commentTop_001.png");
layerBottomSpr->setFlipY(true);
layerBottomSpr->setPosition({ winSize.width / 2,
winSize.height / 2 - layerSize.height / 2 + 5.f });
m_mainLayer->addChild(layerBottomSpr);
auto layerLeftSpr = CCSprite::createWithSpriteFrameName("GJ_commentSide_001.png");
layerLeftSpr->setScaleY(6.3f);
layerLeftSpr->setPosition({ winSize.width / 2 - layerSize.width / 2 - .5f, winSize.height / 2 }
);
m_mainLayer->addChild(layerLeftSpr);
auto layerRightSpr = CCSprite::createWithSpriteFrameName("GJ_commentSide_001.png");
layerRightSpr->setScaleY(6.3f);
layerRightSpr->setFlipX(true);
layerRightSpr->setPosition({ winSize.width / 2 + layerSize.width / 2 + .5f, winSize.height / 2 }
);
m_mainLayer->addChild(layerRightSpr);
addListBorders(m_mainLayer, winSize / 2, layerSize);
// buttons

View file

@ -0,0 +1,91 @@
#include <Geode/ui/General.hpp>
#include <cocos-ext.h>
USE_GEODE_NAMESPACE();
CCSprite* geode::createLayerBG() {
auto winSize = CCDirector::get()->getWinSize();
auto bg = CCSprite::create("GJ_gradientBG.png");
auto bgSize = bg->getTextureRect().size;
bg->setAnchorPoint({ 0.0f, 0.0f });
bg->setScaleX((winSize.width + 10.0f) / bgSize.width);
bg->setScaleY((winSize.height + 10.0f) / bgSize.height);
bg->setPosition({ -5.0f, -5.0f });
bg->setColor({ 0, 102, 255 }); // todo: let mods customize this
return bg;
}
void geode::addListBorders(CCNode* to, CCPoint const& center, CCSize const& size) {
// if the size is 346.f, the top aligns perfectly by default :3
if (size.width == 346.f) {
auto layerTopSpr = CCSprite::createWithSpriteFrameName("GJ_commentTop_001.png");
layerTopSpr->setPosition({
center.x,
center.y + size.height / 2 - 5.f
});
to->addChild(layerTopSpr);
auto layerBottomSpr = CCSprite::createWithSpriteFrameName("GJ_commentTop_001.png");
layerBottomSpr->setFlipY(true);
layerBottomSpr->setPosition({
center.x,
center.y - size.height / 2 + 5.f
});
to->addChild(layerBottomSpr);
}
// otherwise stretch using CCScale9Sprite
else {
auto layerTopSpr = CCScale9Sprite::createWithSpriteFrameName(
"GJ_commentTop_001.png",
{ 0, 0, 240, 20 }
);
layerTopSpr->setContentSize({
size.width + 9.f,
layerTopSpr->getContentSize().height,
});
layerTopSpr->setPosition({
center.x,
center.y + size.height / 2 - 5.f
});
to->addChild(layerTopSpr);
auto layerBottomSpr = CCScale9Sprite::createWithSpriteFrameName(
"GJ_commentTop_001.png",
{ 0, 0, 240, 20 }
);
layerBottomSpr->setScaleY(-1);
layerBottomSpr->setContentSize({
size.width + 9.f,
layerBottomSpr->getContentSize().height,
});
layerBottomSpr->setPosition({
center.x,
center.y - size.height / 2 + 5.f
});
to->addChild(layerBottomSpr);
}
auto layerLeftSpr = CCSprite::createWithSpriteFrameName("GJ_commentSide_001.png");
layerLeftSpr->setScaleY(
(size.height - 30.f) / layerLeftSpr->getScaledContentSize().height
);
layerLeftSpr->setPosition({
center.x - size.width / 2 - .5f,
center.y
});
to->addChild(layerLeftSpr);
auto layerRightSpr = CCSprite::createWithSpriteFrameName("GJ_commentSide_001.png");
layerRightSpr->setScaleY(
(size.height - 30.f) / layerRightSpr->getScaledContentSize().height
);
layerRightSpr->setFlipX(true);
layerRightSpr->setPosition({
center.x + size.width / 2 + .5f,
center.y
});
to->addChild(layerRightSpr);
}

View file

@ -1,18 +0,0 @@
#include <Geode/ui/LayerBG.hpp>
USE_GEODE_NAMESPACE();
CCSprite* geode::createLayerBG() {
auto winSize = CCDirector::get()->getWinSize();
auto bg = CCSprite::create("GJ_gradientBG.png");
auto bgSize = bg->getTextureRect().size;
bg->setAnchorPoint({ 0.0f, 0.0f });
bg->setScaleX((winSize.width + 10.0f) / bgSize.width);
bg->setScaleY((winSize.height + 10.0f) / bgSize.height);
bg->setPosition({ -5.0f, -5.0f });
bg->setColor({ 0, 102, 255 }); // todo: let mods customize this
return bg;
}

View file

@ -4,16 +4,9 @@
#include <Geode/utils/VersionInfo.hpp>
#include <Geode/utils/general.hpp>
#include <Geode/external/scnlib/include/scn/scn.h>
USE_GEODE_NAMESPACE();
#ifdef GEODE_IS_WINDOWS
#define GEODE_SSCANF sscanf_s
#else
#define GEODE_SSCANF sscanf
#endif
// VersionTag
std::optional<VersionTag> geode::versionTagFromString(std::string const& str) {
@ -45,37 +38,57 @@ std::string geode::versionTagToString(VersionTag tag) {
// VersionInfo
bool VersionInfo::validate(std::string const& string) {
std::string copy = string;
if (copy.starts_with("v")) {
copy = string.substr(1);
}
int buf0, buf1, buf2;
std::string bufT;
if (scn::scan(copy, "{}.{}.{}-{}", buf0, buf1, buf2, bufT)) {
return versionTagFromString(bufT).has_value();
}
if (scn::scan(copy, "{}.{}.{}", buf0, buf1, buf2)) {
return true;
Result<VersionInfo> VersionInfo::parse(std::string const& string) {
std::stringstream str (string);
// allow leading v
if (str.peek() == 'v') {
str.get();
}
return false;
}
VersionInfo::VersionInfo(std::string const& string) {
std::string copy = string;
if (copy.starts_with("v")) {
copy = string.substr(1);
size_t major;
str >> major;
if (str.fail()) {
return Err("Unable to parse major");
}
std::string tag;
scn::scan(copy, "{}.{}.{}-{}", m_major, m_minor, m_patch, tag) ||
scn::scan(copy, "{}.{}.{}", m_major, m_minor, m_patch);
if (tag.size()) {
if (auto t = versionTagFromString(tag)) {
m_tag = t;
if (str.get() != '.') {
return Err("Minor version missing");
}
size_t minor;
str >> minor;
if (str.fail()) {
return Err("Unable to parse minor");
}
if (str.get() != '.') {
return Err("Patch version missing");
}
size_t patch;
str >> patch;
if (str.fail()) {
return Err("Unable to parse patch");
}
// tag
std::optional<VersionTag> tag;
if (str.peek() == '-') {
str.get();
std::string iden;
str >> iden;
if (str.fail()) {
return Err("Unable to parse tag");
}
if (auto t = versionTagFromString(iden)) {
tag = t;
}
else {
return Err("Invalid tag \"" + iden + "\"");
}
}
return Ok(VersionInfo(major, minor, patch, tag));
}
std::string VersionInfo::toString(bool includeTag) const {
@ -94,7 +107,13 @@ void geode::to_json(nlohmann::json& json, VersionInfo const& info) {
}
void geode::from_json(nlohmann::json const& json, VersionInfo& info) {
info = VersionInfo(json.template get<std::string>());
auto ver = VersionInfo::parse(json.template get<std::string>());
if (!ver) {
throw nlohmann::json::type_error::create(
0, "Invalid version format: " + ver.unwrapErr(), json
);
}
info = ver.unwrap();
}
std::ostream& geode::operator<<(std::ostream& stream, VersionInfo const& version) {
@ -103,35 +122,23 @@ std::ostream& geode::operator<<(std::ostream& stream, VersionInfo const& version
// ComparableVersionInfo
ComparableVersionInfo::ComparableVersionInfo(std::string const& rawStr) {
auto version = rawStr;
if (version.starts_with("<=")) {
m_compare = VersionCompare::LessEq;
version.erase(0, 2);
Result<ComparableVersionInfo> ComparableVersionInfo::parse(std::string const& rawStr) {
VersionCompare compare;
auto string = rawStr;
if (string.starts_with("<=")) {
compare = VersionCompare::LessEq;
string.erase(0, 2);
}
else if (version.starts_with(">=")) {
m_compare = VersionCompare::MoreEq;
version.erase(0, 2);
else if (string.starts_with(">=")) {
compare = VersionCompare::MoreEq;
string.erase(0, 2);
}
else if (version.starts_with("==")) {
m_compare = VersionCompare::Exact;
version.erase(0, 2);
else if (string.starts_with("==")) {
compare = VersionCompare::Exact;
string.erase(0, 2);
}
m_version = VersionInfo(version);
}
bool ComparableVersionInfo::validate(std::string const& rawStr) {
auto version = rawStr;
// remove prefix
if (
version.starts_with("<=") ||
version.starts_with(">=") ||
version.starts_with("==")
) {
version.erase(0, 2);
}
// otherwise there's no prefix or it's invalid
return VersionInfo::validate(version);
GEODE_UNWRAP_INTO(auto version, VersionInfo::parse(string));
return Ok(ComparableVersionInfo(version, compare));
}
std::string ComparableVersionInfo::toString() const {
@ -149,7 +156,13 @@ void geode::to_json(nlohmann::json& json, ComparableVersionInfo const& info) {
}
void geode::from_json(nlohmann::json const& json, ComparableVersionInfo& info) {
info = ComparableVersionInfo(json.template get<std::string>());
auto ver = ComparableVersionInfo::parse(json.template get<std::string>());
if (!ver) {
throw nlohmann::json::type_error::create(
0, "Invalid version format: " + ver.unwrapErr(), json
);
}
info = ver.unwrap();
}
std::ostream& geode::operator<<(std::ostream& stream, ComparableVersionInfo const& version) {