diff --git a/assets/romfs/themes/abyss_theme.ini b/assets/romfs/themes/abyss_theme.ini index 34d19c4..4d94280 100644 --- a/assets/romfs/themes/abyss_theme.ini +++ b/assets/romfs/themes/abyss_theme.ini @@ -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 diff --git a/assets/romfs/themes/base_black_theme.ini b/assets/romfs/themes/base_black_theme.ini new file mode 100644 index 0000000..674f454 --- /dev/null +++ b/assets/romfs/themes/base_black_theme.ini @@ -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 diff --git a/assets/romfs/themes/black_theme.ini b/assets/romfs/themes/black_theme.ini index cabd402..f31fa27 100644 --- a/assets/romfs/themes/black_theme.ini +++ b/assets/romfs/themes/black_theme.ini @@ -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 diff --git a/assets/romfs/themes/oled_black_theme.ini b/assets/romfs/themes/oled_black_theme.ini index 4893d87..9d2c26a 100644 --- a/assets/romfs/themes/oled_black_theme.ini +++ b/assets/romfs/themes/oled_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 diff --git a/sphaira/include/app.hpp b/sphaira/include/app.hpp index 1edd6a3..aa0810d 100644 --- a/sphaira/include/app.hpp +++ b/sphaira/include/app.hpp @@ -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; diff --git a/sphaira/include/ui/types.hpp b/sphaira/include/ui/types.hpp index 1d2d16f..616fd32 100644 --- a/sphaira/include/ui/types.hpp +++ b/sphaira/include/ui/types.hpp @@ -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]; }; diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index e8317fd..11dd017 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -203,6 +203,39 @@ auto GetNroIcon(const std::vector& nro_icon) -> std::vector { 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 { } 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(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(); diff --git a/sphaira/source/ui/menus/homebrew.cpp b/sphaira/source/ui/menus/homebrew.cpp index 97d1e5f..2ed0907 100644 --- a/sphaira/source/ui/menus/homebrew.cpp +++ b/sphaira/source/ui/menus/homebrew.cpp @@ -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); - } } }