add theme inheritance, fix broken theme when no longer installed
fixes #74
This commit is contained in:
@@ -2,8 +2,7 @@
|
||||
name=Abyss
|
||||
author=TotalJustice
|
||||
version=1.0.0
|
||||
; unused currently
|
||||
preview=romfs:/theme/preview.jpg
|
||||
inherit=romfs:/themes/base_black_theme.ini
|
||||
|
||||
[theme]
|
||||
background=0x0f111aff
|
||||
@@ -12,11 +11,3 @@ selected=0x0f115cff
|
||||
selected_overlay=0x529cffff
|
||||
text=0xffbc41ff
|
||||
text_selected=0x529cffff
|
||||
|
||||
icon_audio=romfs:/theme/icon_audio.png
|
||||
icon_video=romfs:/theme/icon_video.png
|
||||
icon_image=romfs:/theme/icon_image.png
|
||||
icon_file=romfs:/theme/icon_file.png
|
||||
icon_folder=romfs:/theme/icon_folder.png
|
||||
icon_zip=romfs:/theme/icon_zip.png
|
||||
icon_nro=romfs:/theme/icon_nro.png
|
||||
|
||||
15
assets/romfs/themes/base_black_theme.ini
Normal file
15
assets/romfs/themes/base_black_theme.ini
Normal file
@@ -0,0 +1,15 @@
|
||||
[theme]
|
||||
background=0x2d2d2dff
|
||||
grid=0x46464630
|
||||
selected=0x464646ff
|
||||
selected_overlay=0x00ffc8ff
|
||||
text=0xfbfbfbff
|
||||
text_selected=0x00ffc8ff
|
||||
|
||||
icon_audio=romfs:/theme/icon_audio.png
|
||||
icon_video=romfs:/theme/icon_video.png
|
||||
icon_image=romfs:/theme/icon_image.png
|
||||
icon_file=romfs:/theme/icon_file.png
|
||||
icon_folder=romfs:/theme/icon_folder.png
|
||||
icon_zip=romfs:/theme/icon_zip.png
|
||||
icon_nro=romfs:/theme/icon_nro.png
|
||||
@@ -2,22 +2,4 @@
|
||||
name=Black
|
||||
author=TotalJustice
|
||||
version=1.0.0
|
||||
preview=romfs:/theme/preview.jpg
|
||||
|
||||
[theme]
|
||||
background=0x2d2d2dff
|
||||
cursor=romfs:/theme/cursor.png
|
||||
cursor_drag=romfs:/theme/cursor_drag.png
|
||||
grid=0x46464630
|
||||
selected=0x464646ff
|
||||
selected_overlay=0x00ffc8ff
|
||||
text=0xfbfbfbff
|
||||
text_selected=0x00ffc8ff
|
||||
|
||||
icon_audio=romfs:/theme/icon_audio.png
|
||||
icon_video=romfs:/theme/icon_video.png
|
||||
icon_image=romfs:/theme/icon_image.png
|
||||
icon_file=romfs:/theme/icon_file.png
|
||||
icon_folder=romfs:/theme/icon_folder.png
|
||||
icon_zip=romfs:/theme/icon_zip.png
|
||||
icon_nro=romfs:/theme/icon_nro.png
|
||||
inherit=romfs:/themes/base_black_theme.ini
|
||||
|
||||
@@ -2,22 +2,12 @@
|
||||
name=OLED Black
|
||||
author=iTotalJustice/Sanras
|
||||
version=1.0.0
|
||||
preview=romfs:/theme/preview.jpg
|
||||
inherit=romfs:/themes/base_black_theme.ini
|
||||
|
||||
[theme]
|
||||
background=0x000000ff
|
||||
cursor=romfs:/theme/cursor.png
|
||||
cursor_drag=romfs:/theme/cursor_drag.png
|
||||
grid=0x46464640
|
||||
selected=0x323232ff
|
||||
selected_overlay=0x00ffc8ff
|
||||
text=0xfbfbfbff
|
||||
text_selected=0x00ffc8ff
|
||||
|
||||
icon_audio=romfs:/theme/icon_audio.png
|
||||
icon_video=romfs:/theme/icon_video.png
|
||||
icon_image=romfs:/theme/icon_image.png
|
||||
icon_file=romfs:/theme/icon_file.png
|
||||
icon_folder=romfs:/theme/icon_folder.png
|
||||
icon_zip=romfs:/theme/icon_zip.png
|
||||
icon_nro=romfs:/theme/icon_nro.png
|
||||
|
||||
@@ -25,6 +25,7 @@ enum SoundEffect {
|
||||
SoundEffect_Startup,
|
||||
SoundEffect_Install,
|
||||
SoundEffect_Error,
|
||||
SoundEffect_MAX,
|
||||
};
|
||||
|
||||
enum class LaunchType {
|
||||
@@ -106,7 +107,7 @@ public:
|
||||
auto LoadElementColour(std::string_view value) -> ElementEntry;
|
||||
auto LoadElement(std::string_view data) -> ElementEntry;
|
||||
|
||||
void LoadTheme(const fs::FsPath& path);
|
||||
void LoadTheme(const ThemeMeta& meta);
|
||||
void CloseTheme();
|
||||
void ScanThemes(const std::string& path);
|
||||
void ScanThemeEntries();
|
||||
@@ -162,7 +163,7 @@ public:
|
||||
option::OptionLong m_language{INI_SECTION, "language", 0}; // auto
|
||||
|
||||
PLSR_BFSAR m_qlaunch_bfsar{};
|
||||
PLSR_PlayerSoundId m_sound_ids[24]{};
|
||||
PLSR_PlayerSoundId m_sound_ids[SoundEffect_MAX]{};
|
||||
|
||||
private: // from nanovg decko3d example by adubbz
|
||||
static constexpr unsigned NumFramebuffers = 2;
|
||||
|
||||
@@ -186,14 +186,12 @@ struct ThemeMeta {
|
||||
std::string name;
|
||||
std::string author;
|
||||
std::string version;
|
||||
std::string ini_path;
|
||||
fs::FsPath inherit;
|
||||
fs::FsPath ini_path;
|
||||
};
|
||||
|
||||
struct Theme {
|
||||
std::string name;
|
||||
std::string author;
|
||||
std::string version;
|
||||
fs::FsPath path;
|
||||
ThemeMeta meta;
|
||||
PLSR_BFSTM music;
|
||||
ElementEntry elements[ThemeEntryID_MAX];
|
||||
};
|
||||
|
||||
@@ -203,6 +203,39 @@ auto GetNroIcon(const std::vector<u8>& nro_icon) -> std::vector<u8> {
|
||||
return nro_icon;
|
||||
}
|
||||
|
||||
auto LoadThemeMeta(const fs::FsPath& path, ThemeMeta& meta) -> bool {
|
||||
meta = {};
|
||||
|
||||
char buf[FS_MAX_PATH]{};
|
||||
int len{};
|
||||
len = ini_gets("meta", "name", "", buf, sizeof(buf) - 1, path);
|
||||
if (len <= 1) {
|
||||
return false;
|
||||
}
|
||||
meta.name = buf;
|
||||
|
||||
len = ini_gets("meta", "author", "", buf, sizeof(buf) - 1, path);
|
||||
if (len <= 1) {
|
||||
return false;
|
||||
}
|
||||
meta.author = buf;
|
||||
|
||||
len = ini_gets("meta", "version", "", buf, sizeof(buf) - 1, path);
|
||||
if (len <= 1) {
|
||||
return false;
|
||||
}
|
||||
meta.version = buf;
|
||||
|
||||
len = ini_gets("meta", "inherit", "", buf, sizeof(buf) - 1, path);
|
||||
if (len > 1) {
|
||||
meta.inherit = buf;
|
||||
}
|
||||
|
||||
log_write("loaded meta from: %s\n", path.s);
|
||||
meta.ini_path = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
void haze_callback(const HazeCallbackData *data) {
|
||||
App::NotifyFlashLed();
|
||||
evman::push(*data, false);
|
||||
@@ -392,7 +425,7 @@ auto App::GetThemeMetaList() -> std::span<ThemeMeta> {
|
||||
}
|
||||
|
||||
void App::SetTheme(s64 theme_index) {
|
||||
g_app->LoadTheme(g_app->m_theme_meta_entries[theme_index].ini_path.c_str());
|
||||
g_app->LoadTheme(g_app->m_theme_meta_entries[theme_index]);
|
||||
g_app->m_theme_index = theme_index;
|
||||
}
|
||||
|
||||
@@ -873,15 +906,17 @@ auto App::LoadElement(std::string_view value) -> ElementEntry {
|
||||
}
|
||||
|
||||
void App::CloseTheme() {
|
||||
m_theme.name.clear();
|
||||
m_theme.author.clear();
|
||||
m_theme.version.clear();
|
||||
m_theme.path.clear();
|
||||
m_theme.meta.name.clear();
|
||||
m_theme.meta.author.clear();
|
||||
m_theme.meta.version.clear();
|
||||
m_theme.meta.ini_path.clear();
|
||||
|
||||
if (m_sound_ids[SoundEffect_Music]) {
|
||||
plsrPlayerFree(m_sound_ids[SoundEffect_Music]);
|
||||
m_sound_ids[SoundEffect_Music] = nullptr;
|
||||
plsrBFSTMClose(&m_theme.music);
|
||||
}
|
||||
|
||||
for (auto& e : m_theme.elements) {
|
||||
if (e.type == ElementType::Texture) {
|
||||
nvgDeleteImage(vg, e.texture);
|
||||
@@ -890,10 +925,29 @@ void App::CloseTheme() {
|
||||
}
|
||||
}
|
||||
|
||||
void App::LoadTheme(const fs::FsPath& path) {
|
||||
void App::LoadTheme(const ThemeMeta& meta) {
|
||||
// reset theme
|
||||
CloseTheme();
|
||||
m_theme.path = path;
|
||||
|
||||
// check if the theme inherits from another, if so, load it.
|
||||
// block inheriting from itself.
|
||||
if (!meta.inherit.empty() && meta.inherit != meta.ini_path) {
|
||||
log_write("inherit is not empty: %s\n", meta.inherit.s);
|
||||
if (R_SUCCEEDED(romfsInit())) {
|
||||
ThemeMeta inherit_meta;
|
||||
const auto has_meta = LoadThemeMeta(meta.inherit, inherit_meta);
|
||||
romfsExit();
|
||||
|
||||
// base themes do not have a meta
|
||||
if (!has_meta) {
|
||||
inherit_meta.ini_path = meta.inherit;
|
||||
}
|
||||
|
||||
LoadTheme(inherit_meta);
|
||||
}
|
||||
}
|
||||
|
||||
m_theme.meta = meta;
|
||||
|
||||
const auto cb = [](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int {
|
||||
auto app = static_cast<App*>(UserData);
|
||||
@@ -902,15 +956,7 @@ void App::LoadTheme(const fs::FsPath& path) {
|
||||
std::string_view key{Key};
|
||||
std::string_view value{Value};
|
||||
|
||||
if (section == "meta") {
|
||||
if (key == "name") {
|
||||
theme.name = key;
|
||||
} else if (key == "author") {
|
||||
theme.author = key;
|
||||
} else if (key == "version") {
|
||||
theme.version = key;
|
||||
}
|
||||
} else if (section == "theme") {
|
||||
if (section == "theme") {
|
||||
if (key == "background") {
|
||||
theme.elements[ThemeEntryID_BACKGROUND] = app->LoadElement(value);
|
||||
} else if (key == "music") {
|
||||
@@ -953,10 +999,10 @@ void App::LoadTheme(const fs::FsPath& path) {
|
||||
|
||||
if (R_SUCCEEDED(romfsInit())) {
|
||||
ON_SCOPE_EXIT(romfsExit());
|
||||
if (!ini_browse(cb, this, path)) {
|
||||
log_write("failed to open ini: %s\n", path);
|
||||
if (!ini_browse(cb, this, meta.ini_path)) {
|
||||
log_write("failed to open ini: %s\n", meta.ini_path.s);
|
||||
} else {
|
||||
log_write("opened ini: %s\n", path);
|
||||
log_write("opened ini: %s\n", meta.ini_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -986,40 +1032,10 @@ void App::ScanThemes(const std::string& path) {
|
||||
|
||||
const auto full_path = path + name;
|
||||
|
||||
if (!ini_haskey("meta", "name", full_path.c_str())) {
|
||||
continue;
|
||||
}
|
||||
if (!ini_haskey("meta", "author", full_path.c_str())) {
|
||||
continue;
|
||||
}
|
||||
if (!ini_haskey("meta", "version", full_path.c_str())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ThemeMeta meta{};
|
||||
|
||||
char buf[FS_MAX_PATH]{};
|
||||
int len{};
|
||||
len = ini_gets("meta", "name", "", buf, sizeof(buf) - 1, full_path.c_str());
|
||||
if (len <= 1) {
|
||||
continue;
|
||||
if (LoadThemeMeta(full_path, meta)) {
|
||||
m_theme_meta_entries.emplace_back(meta);
|
||||
}
|
||||
meta.name = buf;
|
||||
|
||||
len = ini_gets("meta", "author", "", buf, sizeof(buf) - 1, full_path.c_str());
|
||||
if (len <= 1) {
|
||||
continue;
|
||||
}
|
||||
meta.author = buf;
|
||||
|
||||
len = ini_gets("meta", "version", "", buf, sizeof(buf) - 1, full_path.c_str());
|
||||
if (len <= 1) {
|
||||
continue;
|
||||
}
|
||||
meta.version = buf;
|
||||
|
||||
meta.ini_path = full_path;
|
||||
m_theme_meta_entries.emplace_back(meta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1132,6 +1148,7 @@ App::App(const char* argv0) {
|
||||
ON_SCOPE_EXIT(romfsUnmount("qlaunch"));
|
||||
plsrPlayerInit();
|
||||
plsrBFSAROpen("qlaunch:/sound/qlaunch.bfsar", &m_qlaunch_bfsar);
|
||||
ON_SCOPE_EXIT(plsrBFSARClose(&m_qlaunch_bfsar));
|
||||
|
||||
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeGameIconFocus", &m_sound_ids[SoundEffect_Focus]);
|
||||
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeGameIconScroll", &m_sound_ids[SoundEffect_Scroll]);
|
||||
@@ -1150,16 +1167,29 @@ App::App(const char* argv0) {
|
||||
ScanThemeEntries();
|
||||
|
||||
fs::FsPath theme_path{};
|
||||
constexpr fs::FsPath default_theme_path{"romfs:/themes/abyss_theme.ini"};
|
||||
if (App::GetThemeShuffleEnable() && m_theme_meta_entries.size()) {
|
||||
theme_path = m_theme_meta_entries[randomGet64() % m_theme_meta_entries.size()].ini_path;
|
||||
} else {
|
||||
ini_gets("config", "theme", "romfs:/themes/abyss_theme.ini", theme_path, sizeof(theme_path), CONFIG_PATH);
|
||||
ini_gets("config", "theme", default_theme_path, theme_path, sizeof(theme_path), CONFIG_PATH);
|
||||
}
|
||||
LoadTheme(theme_path);
|
||||
|
||||
// try and load previous theme, default to previous version otherwise.
|
||||
ThemeMeta theme_meta;
|
||||
if (R_SUCCEEDED(romfsInit())) {
|
||||
ON_SCOPE_EXIT(romfsExit());
|
||||
if (!LoadThemeMeta(theme_path, theme_meta)) {
|
||||
log_write("failed to load meta using default\n");
|
||||
theme_path = default_theme_path;
|
||||
LoadThemeMeta(theme_path, theme_meta);
|
||||
}
|
||||
}
|
||||
log_write("loading theme from: %s\n", theme_meta.ini_path.s);
|
||||
LoadTheme(theme_meta);
|
||||
|
||||
// find theme index using the path of the theme.ini
|
||||
for (u64 i = 0; i < m_theme_meta_entries.size(); i++) {
|
||||
if (m_theme.path == m_theme_meta_entries[i].ini_path) {
|
||||
if (m_theme.meta.ini_path == m_theme_meta_entries[i].ini_path) {
|
||||
m_theme_index = i;
|
||||
break;
|
||||
}
|
||||
@@ -1244,7 +1274,7 @@ App::~App() {
|
||||
|
||||
appletUnhook(&m_appletHookCookie);
|
||||
|
||||
ini_puts("config", "theme", m_theme.path, CONFIG_PATH);
|
||||
ini_puts("config", "theme", m_theme.meta.ini_path, CONFIG_PATH);
|
||||
|
||||
CloseTheme();
|
||||
|
||||
|
||||
@@ -320,11 +320,6 @@ void Menu::Sort() {
|
||||
fs::FsPath star_path;
|
||||
for (auto& p : m_entries) {
|
||||
p.has_star = fs.FileExists(GenerateStarPath(p.path));
|
||||
if (p.has_star == true) {
|
||||
log_write("found star: %s\n", p.path.s);
|
||||
} else {
|
||||
log_write("no star: %s\n", p.path.s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user