optimise theme inherit, load default_music.bfstm if available.
This commit is contained in:
@@ -107,7 +107,7 @@ public:
|
|||||||
auto LoadElementColour(std::string_view value) -> ElementEntry;
|
auto LoadElementColour(std::string_view value) -> ElementEntry;
|
||||||
auto LoadElement(std::string_view data, ElementType type) -> ElementEntry;
|
auto LoadElement(std::string_view data, ElementType type) -> ElementEntry;
|
||||||
|
|
||||||
void LoadTheme(ThemeMeta meta, int inherit_level = 0);
|
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();
|
||||||
@@ -165,9 +165,6 @@ public:
|
|||||||
PLSR_BFSAR m_qlaunch_bfsar{};
|
PLSR_BFSAR m_qlaunch_bfsar{};
|
||||||
PLSR_PlayerSoundId m_sound_ids[SoundEffect_MAX]{};
|
PLSR_PlayerSoundId m_sound_ids[SoundEffect_MAX]{};
|
||||||
|
|
||||||
private:
|
|
||||||
void CloseMusic();
|
|
||||||
|
|
||||||
private: // from nanovg decko3d example by adubbz
|
private: // from nanovg decko3d example by adubbz
|
||||||
static constexpr unsigned NumFramebuffers = 2;
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
static constexpr unsigned StaticCmdSize = 0x1000;
|
static constexpr unsigned StaticCmdSize = 0x1000;
|
||||||
|
|||||||
@@ -33,6 +33,44 @@ extern "C" {
|
|||||||
namespace sphaira {
|
namespace sphaira {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
struct ThemeData {
|
||||||
|
fs::FsPath music_path{"/config/sphaira/themes/default_music.bfstm"};
|
||||||
|
std::string elements[ThemeEntryID_MAX]{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThemeIdPair {
|
||||||
|
const char* label;
|
||||||
|
ThemeEntryID id;
|
||||||
|
ElementType type{ElementType::None};
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr ThemeIdPair THEME_ENTRIES[] = {
|
||||||
|
{ "background", ThemeEntryID_BACKGROUND },
|
||||||
|
{ "grid", ThemeEntryID_GRID },
|
||||||
|
{ "text", ThemeEntryID_TEXT, ElementType::Colour },
|
||||||
|
{ "text_info", ThemeEntryID_TEXT_INFO, ElementType::Colour },
|
||||||
|
{ "text_selected", ThemeEntryID_TEXT_SELECTED, ElementType::Colour },
|
||||||
|
{ "selected_background", ThemeEntryID_SELECTED_BACKGROUND, ElementType::Colour },
|
||||||
|
{ "popup", ThemeEntryID_POPUP, ElementType::Colour },
|
||||||
|
{ "line", ThemeEntryID_LINE, ElementType::Colour },
|
||||||
|
{ "line_seperator", ThemeEntryID_LINE_SEPERATOR, ElementType::Colour },
|
||||||
|
{ "sidebar", ThemeEntryID_SIDEBAR, ElementType::Colour },
|
||||||
|
{ "scrollbar", ThemeEntryID_SCROLLBAR, ElementType::Colour },
|
||||||
|
{ "scrollbar_background", ThemeEntryID_SCROLLBAR_BACKGROUND, ElementType::Colour },
|
||||||
|
{ "progressbar", ThemeEntryID_PROGRESSBAR, ElementType::Colour },
|
||||||
|
{ "progressbar_background", ThemeEntryID_PROGRESSBAR_BACKGROUND, ElementType::Colour },
|
||||||
|
{ "highlight_1", ThemeEntryID_HIGHLIGHT_1, ElementType::Colour },
|
||||||
|
{ "highlight_2", ThemeEntryID_HIGHLIGHT_2, ElementType::Colour },
|
||||||
|
{ "icon_colour", ThemeEntryID_ICON_COLOUR, ElementType::Colour },
|
||||||
|
{ "icon_audio", ThemeEntryID_ICON_AUDIO, ElementType::Texture },
|
||||||
|
{ "icon_video", ThemeEntryID_ICON_VIDEO, ElementType::Texture },
|
||||||
|
{ "icon_image", ThemeEntryID_ICON_IMAGE, ElementType::Texture },
|
||||||
|
{ "icon_file", ThemeEntryID_ICON_FILE, ElementType::Texture },
|
||||||
|
{ "icon_folder", ThemeEntryID_ICON_FOLDER, ElementType::Texture },
|
||||||
|
{ "icon_zip", ThemeEntryID_ICON_ZIP, ElementType::Texture },
|
||||||
|
{ "icon_nro", ThemeEntryID_ICON_NRO, ElementType::Texture },
|
||||||
|
};
|
||||||
|
|
||||||
constinit App* g_app{};
|
constinit App* g_app{};
|
||||||
|
|
||||||
void deko3d_error_cb(void* userData, const char* context, DkResult result, const char* message) {
|
void deko3d_error_cb(void* userData, const char* context, DkResult result, const char* message) {
|
||||||
@@ -236,6 +274,62 @@ auto LoadThemeMeta(const fs::FsPath& path, ThemeMeta& meta) -> bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LoadThemeInternal(ThemeMeta meta, ThemeData& theme_data, int inherit_level = 0) {
|
||||||
|
constexpr auto inherit_level_max = 5;
|
||||||
|
|
||||||
|
// all themes will inherit from black theme by default.
|
||||||
|
if (meta.inherit.empty() && !inherit_level) {
|
||||||
|
meta.inherit = "romfs:/themes/base_black_theme.ini";
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the theme inherits from another, if so, load it.
|
||||||
|
// block inheriting from itself.
|
||||||
|
if (inherit_level < inherit_level_max && !meta.inherit.empty() && strcasecmp(meta.inherit, "none") && 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadThemeInternal(inherit_meta, theme_data, inherit_level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto cb = [](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int {
|
||||||
|
auto theme_data = static_cast<ThemeData*>(UserData);
|
||||||
|
|
||||||
|
if (!std::strcmp(Section, "theme")) {
|
||||||
|
if (!std::strcmp(Key, "music")) {
|
||||||
|
theme_data->music_path = Value;
|
||||||
|
} else {
|
||||||
|
for (auto& e : THEME_ENTRIES) {
|
||||||
|
if (!std::strcmp(Key, e.label)) {
|
||||||
|
theme_data->elements[e.id] = Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(romfsInit())) {
|
||||||
|
ON_SCOPE_EXIT(romfsExit());
|
||||||
|
|
||||||
|
if (!ini_browse(cb, &theme_data, meta.ini_path)) {
|
||||||
|
log_write("failed to open ini: %s\n", meta.ini_path.s);
|
||||||
|
} else {
|
||||||
|
log_write("opened ini: %s\n", meta.ini_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void haze_callback(const HazeCallbackData *data) {
|
void haze_callback(const HazeCallbackData *data) {
|
||||||
App::NotifyFlashLed();
|
App::NotifyFlashLed();
|
||||||
evman::push(*data, false);
|
evman::push(*data, false);
|
||||||
@@ -975,16 +1069,12 @@ auto App::LoadElement(std::string_view value, ElementType type) -> ElementEntry
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::CloseMusic() {
|
void App::CloseTheme() {
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void App::CloseTheme() {
|
|
||||||
CloseMusic();
|
|
||||||
|
|
||||||
for (auto& e : m_theme.elements) {
|
for (auto& e : m_theme.elements) {
|
||||||
if (e.type == ElementType::Texture) {
|
if (e.type == ElementType::Texture) {
|
||||||
@@ -995,100 +1085,29 @@ void App::CloseTheme() {
|
|||||||
m_theme = {};
|
m_theme = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::LoadTheme(ThemeMeta meta, int inherit_level) {
|
void App::LoadTheme(const ThemeMeta& meta) {
|
||||||
// reset theme
|
// reset theme
|
||||||
CloseTheme();
|
CloseTheme();
|
||||||
|
|
||||||
static constexpr struct ThemeIdPair{
|
ThemeData theme_data{};
|
||||||
const char* label;
|
LoadThemeInternal(meta, theme_data);
|
||||||
ThemeEntryID id;
|
|
||||||
ElementType type{ElementType::None};
|
|
||||||
} theme_entries[] = {
|
|
||||||
{ "background", ThemeEntryID_BACKGROUND },
|
|
||||||
{ "grid", ThemeEntryID_GRID },
|
|
||||||
{ "text", ThemeEntryID_TEXT, ElementType::Colour },
|
|
||||||
{ "text_info", ThemeEntryID_TEXT_INFO, ElementType::Colour },
|
|
||||||
{ "text_selected", ThemeEntryID_TEXT_SELECTED, ElementType::Colour },
|
|
||||||
{ "selected_background", ThemeEntryID_SELECTED_BACKGROUND, ElementType::Colour },
|
|
||||||
{ "popup", ThemeEntryID_POPUP, ElementType::Colour },
|
|
||||||
{ "line", ThemeEntryID_LINE, ElementType::Colour },
|
|
||||||
{ "line_seperator", ThemeEntryID_LINE_SEPERATOR, ElementType::Colour },
|
|
||||||
{ "sidebar", ThemeEntryID_SIDEBAR, ElementType::Colour },
|
|
||||||
{ "scrollbar", ThemeEntryID_SCROLLBAR, ElementType::Colour },
|
|
||||||
{ "scrollbar_background", ThemeEntryID_SCROLLBAR_BACKGROUND, ElementType::Colour },
|
|
||||||
{ "progressbar", ThemeEntryID_PROGRESSBAR, ElementType::Colour },
|
|
||||||
{ "progressbar_background", ThemeEntryID_PROGRESSBAR_BACKGROUND, ElementType::Colour },
|
|
||||||
{ "highlight_1", ThemeEntryID_HIGHLIGHT_1, ElementType::Colour },
|
|
||||||
{ "highlight_2", ThemeEntryID_HIGHLIGHT_2, ElementType::Colour },
|
|
||||||
{ "icon_colour", ThemeEntryID_ICON_COLOUR, ElementType::Colour },
|
|
||||||
{ "icon_audio", ThemeEntryID_ICON_AUDIO, ElementType::Texture },
|
|
||||||
{ "icon_video", ThemeEntryID_ICON_VIDEO, ElementType::Texture },
|
|
||||||
{ "icon_image", ThemeEntryID_ICON_IMAGE, ElementType::Texture },
|
|
||||||
{ "icon_file", ThemeEntryID_ICON_FILE, ElementType::Texture },
|
|
||||||
{ "icon_folder", ThemeEntryID_ICON_FOLDER, ElementType::Texture },
|
|
||||||
{ "icon_zip", ThemeEntryID_ICON_ZIP, ElementType::Texture },
|
|
||||||
{ "icon_nro", ThemeEntryID_ICON_NRO, ElementType::Texture },
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto inherit_level_max = 5;
|
|
||||||
|
|
||||||
// all themes will inherit from black theme by default.
|
|
||||||
if (meta.inherit.empty() && !inherit_level) {
|
|
||||||
meta.inherit = "romfs:/themes/base_black_theme.ini";
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the theme inherits from another, if so, load it.
|
|
||||||
// block inheriting from itself.
|
|
||||||
if (inherit_level < inherit_level_max && !meta.inherit.empty() && strcasecmp(meta.inherit, "none") && 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, inherit_level + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_theme.meta = 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);
|
|
||||||
auto& theme = app->m_theme;
|
|
||||||
|
|
||||||
if (!std::strcmp(Section, "theme")) {
|
|
||||||
if (!std::strcmp(Key, "music")) {
|
|
||||||
app->CloseMusic();
|
|
||||||
if (R_SUCCEEDED(plsrBFSTMOpen(Value, &theme.music))) {
|
|
||||||
if (R_SUCCEEDED(plsrPlayerLoadStream(&theme.music, &app->m_sound_ids[SoundEffect_Music]))) {
|
|
||||||
app->PlaySoundEffect(SoundEffect_Music);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (auto& e : theme_entries) {
|
|
||||||
if (!std::strcmp(Key, e.label)) {
|
|
||||||
// log_write("\tfound: %s value: %s\n", Key, Value);
|
|
||||||
theme.elements[e.id] = app->LoadElement(Value, e.type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(romfsInit())) {
|
if (R_SUCCEEDED(romfsInit())) {
|
||||||
ON_SCOPE_EXIT(romfsExit());
|
ON_SCOPE_EXIT(romfsExit());
|
||||||
if (!ini_browse(cb, this, meta.ini_path)) {
|
|
||||||
log_write("failed to open ini: %s\n", meta.ini_path.s);
|
// load all assets / colours.
|
||||||
} else {
|
for (auto& e : THEME_ENTRIES) {
|
||||||
log_write("opened ini: %s\n", meta.ini_path);
|
m_theme.elements[e.id] = LoadElement(theme_data.elements[e.id], e.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load music
|
||||||
|
if (!theme_data.music_path.empty()) {
|
||||||
|
if (R_SUCCEEDED(plsrBFSTMOpen(theme_data.music_path, &m_theme.music))) {
|
||||||
|
if (R_SUCCEEDED(plsrPlayerLoadStream(&m_theme.music, &m_sound_ids[SoundEffect_Music]))) {
|
||||||
|
PlaySoundEffect(SoundEffect_Music);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user