add theme inheritance, fix broken theme when no longer installed
fixes #74
This commit is contained in:
@@ -2,8 +2,7 @@
|
|||||||
name=Abyss
|
name=Abyss
|
||||||
author=TotalJustice
|
author=TotalJustice
|
||||||
version=1.0.0
|
version=1.0.0
|
||||||
; unused currently
|
inherit=romfs:/themes/base_black_theme.ini
|
||||||
preview=romfs:/theme/preview.jpg
|
|
||||||
|
|
||||||
[theme]
|
[theme]
|
||||||
background=0x0f111aff
|
background=0x0f111aff
|
||||||
@@ -12,11 +11,3 @@ selected=0x0f115cff
|
|||||||
selected_overlay=0x529cffff
|
selected_overlay=0x529cffff
|
||||||
text=0xffbc41ff
|
text=0xffbc41ff
|
||||||
text_selected=0x529cffff
|
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
|
name=Black
|
||||||
author=TotalJustice
|
author=TotalJustice
|
||||||
version=1.0.0
|
version=1.0.0
|
||||||
preview=romfs:/theme/preview.jpg
|
inherit=romfs:/themes/base_black_theme.ini
|
||||||
|
|
||||||
[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
|
|
||||||
|
|||||||
@@ -2,22 +2,12 @@
|
|||||||
name=OLED Black
|
name=OLED Black
|
||||||
author=iTotalJustice/Sanras
|
author=iTotalJustice/Sanras
|
||||||
version=1.0.0
|
version=1.0.0
|
||||||
preview=romfs:/theme/preview.jpg
|
inherit=romfs:/themes/base_black_theme.ini
|
||||||
|
|
||||||
[theme]
|
[theme]
|
||||||
background=0x000000ff
|
background=0x000000ff
|
||||||
cursor=romfs:/theme/cursor.png
|
|
||||||
cursor_drag=romfs:/theme/cursor_drag.png
|
|
||||||
grid=0x46464640
|
grid=0x46464640
|
||||||
selected=0x323232ff
|
selected=0x323232ff
|
||||||
selected_overlay=0x00ffc8ff
|
selected_overlay=0x00ffc8ff
|
||||||
text=0xfbfbfbff
|
text=0xfbfbfbff
|
||||||
text_selected=0x00ffc8ff
|
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_Startup,
|
||||||
SoundEffect_Install,
|
SoundEffect_Install,
|
||||||
SoundEffect_Error,
|
SoundEffect_Error,
|
||||||
|
SoundEffect_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class LaunchType {
|
enum class LaunchType {
|
||||||
@@ -106,7 +107,7 @@ public:
|
|||||||
auto LoadElementColour(std::string_view value) -> ElementEntry;
|
auto LoadElementColour(std::string_view value) -> ElementEntry;
|
||||||
auto LoadElement(std::string_view data) -> ElementEntry;
|
auto LoadElement(std::string_view data) -> ElementEntry;
|
||||||
|
|
||||||
void LoadTheme(const fs::FsPath& path);
|
void LoadTheme(const ThemeMeta& meta);
|
||||||
void CloseTheme();
|
void CloseTheme();
|
||||||
void ScanThemes(const std::string& path);
|
void ScanThemes(const std::string& path);
|
||||||
void ScanThemeEntries();
|
void ScanThemeEntries();
|
||||||
@@ -162,7 +163,7 @@ public:
|
|||||||
option::OptionLong m_language{INI_SECTION, "language", 0}; // auto
|
option::OptionLong m_language{INI_SECTION, "language", 0}; // auto
|
||||||
|
|
||||||
PLSR_BFSAR m_qlaunch_bfsar{};
|
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
|
private: // from nanovg decko3d example by adubbz
|
||||||
static constexpr unsigned NumFramebuffers = 2;
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
|||||||
@@ -186,14 +186,12 @@ struct ThemeMeta {
|
|||||||
std::string name;
|
std::string name;
|
||||||
std::string author;
|
std::string author;
|
||||||
std::string version;
|
std::string version;
|
||||||
std::string ini_path;
|
fs::FsPath inherit;
|
||||||
|
fs::FsPath ini_path;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Theme {
|
struct Theme {
|
||||||
std::string name;
|
ThemeMeta meta;
|
||||||
std::string author;
|
|
||||||
std::string version;
|
|
||||||
fs::FsPath path;
|
|
||||||
PLSR_BFSTM music;
|
PLSR_BFSTM music;
|
||||||
ElementEntry elements[ThemeEntryID_MAX];
|
ElementEntry elements[ThemeEntryID_MAX];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -203,6 +203,39 @@ auto GetNroIcon(const std::vector<u8>& nro_icon) -> std::vector<u8> {
|
|||||||
return nro_icon;
|
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) {
|
void haze_callback(const HazeCallbackData *data) {
|
||||||
App::NotifyFlashLed();
|
App::NotifyFlashLed();
|
||||||
evman::push(*data, false);
|
evman::push(*data, false);
|
||||||
@@ -392,7 +425,7 @@ auto App::GetThemeMetaList() -> std::span<ThemeMeta> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void App::SetTheme(s64 theme_index) {
|
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;
|
g_app->m_theme_index = theme_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -873,15 +906,17 @@ auto App::LoadElement(std::string_view value) -> ElementEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void App::CloseTheme() {
|
void App::CloseTheme() {
|
||||||
m_theme.name.clear();
|
m_theme.meta.name.clear();
|
||||||
m_theme.author.clear();
|
m_theme.meta.author.clear();
|
||||||
m_theme.version.clear();
|
m_theme.meta.version.clear();
|
||||||
m_theme.path.clear();
|
m_theme.meta.ini_path.clear();
|
||||||
|
|
||||||
if (m_sound_ids[SoundEffect_Music]) {
|
if (m_sound_ids[SoundEffect_Music]) {
|
||||||
plsrPlayerFree(m_sound_ids[SoundEffect_Music]);
|
plsrPlayerFree(m_sound_ids[SoundEffect_Music]);
|
||||||
m_sound_ids[SoundEffect_Music] = nullptr;
|
m_sound_ids[SoundEffect_Music] = nullptr;
|
||||||
plsrBFSTMClose(&m_theme.music);
|
plsrBFSTMClose(&m_theme.music);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& e : m_theme.elements) {
|
for (auto& e : m_theme.elements) {
|
||||||
if (e.type == ElementType::Texture) {
|
if (e.type == ElementType::Texture) {
|
||||||
nvgDeleteImage(vg, e.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
|
// reset theme
|
||||||
CloseTheme();
|
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 {
|
const auto cb = [](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int {
|
||||||
auto app = static_cast<App*>(UserData);
|
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 key{Key};
|
||||||
std::string_view value{Value};
|
std::string_view value{Value};
|
||||||
|
|
||||||
if (section == "meta") {
|
if (section == "theme") {
|
||||||
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 (key == "background") {
|
if (key == "background") {
|
||||||
theme.elements[ThemeEntryID_BACKGROUND] = app->LoadElement(value);
|
theme.elements[ThemeEntryID_BACKGROUND] = app->LoadElement(value);
|
||||||
} else if (key == "music") {
|
} else if (key == "music") {
|
||||||
@@ -953,10 +999,10 @@ void App::LoadTheme(const fs::FsPath& path) {
|
|||||||
|
|
||||||
if (R_SUCCEEDED(romfsInit())) {
|
if (R_SUCCEEDED(romfsInit())) {
|
||||||
ON_SCOPE_EXIT(romfsExit());
|
ON_SCOPE_EXIT(romfsExit());
|
||||||
if (!ini_browse(cb, this, path)) {
|
if (!ini_browse(cb, this, meta.ini_path)) {
|
||||||
log_write("failed to open ini: %s\n", path);
|
log_write("failed to open ini: %s\n", meta.ini_path.s);
|
||||||
} else {
|
} 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;
|
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{};
|
ThemeMeta meta{};
|
||||||
|
if (LoadThemeMeta(full_path, meta)) {
|
||||||
char buf[FS_MAX_PATH]{};
|
m_theme_meta_entries.emplace_back(meta);
|
||||||
int len{};
|
|
||||||
len = ini_gets("meta", "name", "", buf, sizeof(buf) - 1, full_path.c_str());
|
|
||||||
if (len <= 1) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
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"));
|
ON_SCOPE_EXIT(romfsUnmount("qlaunch"));
|
||||||
plsrPlayerInit();
|
plsrPlayerInit();
|
||||||
plsrBFSAROpen("qlaunch:/sound/qlaunch.bfsar", &m_qlaunch_bfsar);
|
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, "SeGameIconFocus", &m_sound_ids[SoundEffect_Focus]);
|
||||||
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeGameIconScroll", &m_sound_ids[SoundEffect_Scroll]);
|
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeGameIconScroll", &m_sound_ids[SoundEffect_Scroll]);
|
||||||
@@ -1150,16 +1167,29 @@ App::App(const char* argv0) {
|
|||||||
ScanThemeEntries();
|
ScanThemeEntries();
|
||||||
|
|
||||||
fs::FsPath theme_path{};
|
fs::FsPath theme_path{};
|
||||||
|
constexpr fs::FsPath default_theme_path{"romfs:/themes/abyss_theme.ini"};
|
||||||
if (App::GetThemeShuffleEnable() && m_theme_meta_entries.size()) {
|
if (App::GetThemeShuffleEnable() && m_theme_meta_entries.size()) {
|
||||||
theme_path = m_theme_meta_entries[randomGet64() % m_theme_meta_entries.size()].ini_path;
|
theme_path = m_theme_meta_entries[randomGet64() % m_theme_meta_entries.size()].ini_path;
|
||||||
} else {
|
} 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
|
// find theme index using the path of the theme.ini
|
||||||
for (u64 i = 0; i < m_theme_meta_entries.size(); i++) {
|
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;
|
m_theme_index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1244,7 +1274,7 @@ App::~App() {
|
|||||||
|
|
||||||
appletUnhook(&m_appletHookCookie);
|
appletUnhook(&m_appletHookCookie);
|
||||||
|
|
||||||
ini_puts("config", "theme", m_theme.path, CONFIG_PATH);
|
ini_puts("config", "theme", m_theme.meta.ini_path, CONFIG_PATH);
|
||||||
|
|
||||||
CloseTheme();
|
CloseTheme();
|
||||||
|
|
||||||
|
|||||||
@@ -320,11 +320,6 @@ void Menu::Sort() {
|
|||||||
fs::FsPath star_path;
|
fs::FsPath star_path;
|
||||||
for (auto& p : m_entries) {
|
for (auto& p : m_entries) {
|
||||||
p.has_star = fs.FileExists(GenerateStarPath(p.path));
|
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