diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a8da0e..94fa0b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ add_executable(${PROJECT_NAME} Sources/sntpClient.cpp Sources/subAppRouter.cpp Sources/subsystems.cpp + Sources/theme.cpp Sources/timing.cpp Sources/timeMenu.cpp Sources/videoMenu.cpp diff --git a/Includes/config.hpp b/Includes/config.hpp index c1b4cab..79d5b42 100644 --- a/Includes/config.hpp +++ b/Includes/config.hpp @@ -133,7 +133,7 @@ void from_json(nlohmann::json const& j, homescreenConfig& o); class Settings { public: - Settings() = default; + std::string activeThemeDirectory{ "default" }; #ifdef NXDK netConfig net; sntpConfig sntp; @@ -154,6 +154,7 @@ class Config { void setChanged(); void storeToDisk(); + Settings settings; nlohmann::json menu; }; diff --git a/Includes/renderer.hpp b/Includes/renderer.hpp index 5912162..0bac81c 100644 --- a/Includes/renderer.hpp +++ b/Includes/renderer.hpp @@ -2,6 +2,7 @@ #define RENDERER_H #include +#include #include int min(int lhs, int rhs); @@ -13,7 +14,7 @@ class Renderer { ~Renderer(); int init(); - int init(const char* bg); + int init(std::string const& backgroundImagePath); int clear(); void flip(); diff --git a/Includes/theme.hpp b/Includes/theme.hpp new file mode 100644 index 0000000..106876f --- /dev/null +++ b/Includes/theme.hpp @@ -0,0 +1,58 @@ +#ifndef NEVOLUTIONX_THEME_H +#define NEVOLUTIONX_THEME_H + +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type" +#include "../3rdparty/json.hpp" +#pragma clang diagnostic pop + + +class Theme { +public: + struct MenuTheme { + std::string font; + // TODO: Actually support this in Font. + unsigned int fontColor; + }; + + struct ImageSet { + std::string image480p; + std::string image720p; + }; + + explicit Theme(std::string themeDirectory); + + void setTitle(std::string const& val) { title = val; } + std::string const& getTitle() const { return title; } + + void setBackground(ImageSet const& val) { background = val; } + ImageSet const& getBackground() const { return background; } + + void setMenu(MenuTheme const& val) { menu = val; } + MenuTheme const& getMenu() const { return menu; } + + std::string getAbsolutePath(std::string const& subpath) const { + return rootPath + subpath; + } + +private: + void load(); + + std::string rootPath; + std::string title{ "??MISSING??" }; + ImageSet background; + MenuTheme menu; +}; + +void to_json(nlohmann::json& j, Theme const& o); +void from_json(nlohmann::json const& j, Theme& o); + +void to_json(nlohmann::json& j, Theme::MenuTheme const& o); +void from_json(nlohmann::json const& j, Theme::MenuTheme& o); + +void to_json(nlohmann::json& j, Theme::ImageSet const& o); +void from_json(nlohmann::json const& j, Theme::ImageSet& o); + +#endif // NEVOLUTIONX_THEME_H diff --git a/Makefile b/Makefile index fa6c22f..ccb734a 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ SRCS += \ $(SRCDIR)/sntpClient.cpp \ $(SRCDIR)/subAppRouter.cpp \ $(SRCDIR)/subsystems.cpp \ + $(SRCDIR)/theme.cpp \ $(SRCDIR)/timeMenu.cpp \ $(SRCDIR)/timing.cpp \ $(SRCDIR)/videoMenu.cpp \ @@ -46,12 +47,17 @@ CFLAGS += -O2 CXXFLAGS += -O2 endif -new_all: copy_resources all - include $(NXDK_DIR)/Makefile -copy_resources: $(OUTPUT_DIR)/config.json - @cp $(RESOURCEDIR)/480.png $(RESOURCEDIR)/720.png $(RESOURCEDIR)/vegur.ttf $(OUTPUT_DIR) +RESOURCES = \ + $(OUTPUT_DIR)/config.json \ + $(patsubst $(CURDIR)/Resources/%,$(OUTPUT_DIR)/%,$(wildcard $(CURDIR)/Resources/NeXThemes/*)) +TARGET += $(RESOURCES) +$(GEN_XISO): $(RESOURCES) + +$(OUTPUT_DIR)/NeXThemes/%: $(CURDIR)/Resources/NeXThemes/% + $(VE)mkdir -p '$(dir $@)' + $(VE)cp -r '$<' '$@' $(OUTPUT_DIR)/config.json: $(CURDIR)/sampleconfig.json @mkdir -p $(OUTPUT_DIR) diff --git a/Resources/480.png b/Resources/NeXThemes/default/480.png similarity index 100% rename from Resources/480.png rename to Resources/NeXThemes/default/480.png diff --git a/Resources/720.png b/Resources/NeXThemes/default/720.png similarity index 100% rename from Resources/720.png rename to Resources/NeXThemes/default/720.png diff --git a/Resources/NeXThemes/default/theme.json b/Resources/NeXThemes/default/theme.json new file mode 100644 index 0000000..06d3a61 --- /dev/null +++ b/Resources/NeXThemes/default/theme.json @@ -0,0 +1,11 @@ +{ + "title": "Default", + "background": { + "480": "480.png", + "720": "720.png" + }, + "menu": { + "font": "vegur.ttf", + "font_color": "FFFFFF" + } +} diff --git a/Resources/vegur.ttf b/Resources/NeXThemes/default/vegur.ttf similarity index 100% rename from Resources/vegur.ttf rename to Resources/NeXThemes/default/vegur.ttf diff --git a/Sources/config.cpp b/Sources/config.cpp index b399f86..972ece6 100644 --- a/Sources/config.cpp +++ b/Sources/config.cpp @@ -212,16 +212,20 @@ void from_json(nlohmann::json const& j, homescreenConfig& o) { } void to_json(nlohmann::json& j, Settings const& o) { - j = nlohmann::json{ { "ftp", nlohmann::json(o.ftp) }, + j = nlohmann::json{ { "active_theme_directory", o.activeThemeDirectory }, + { "ftp", nlohmann::json(o.ftp) }, { "mount", nlohmann::json(o.mount) }, #ifdef NXDK { "network", nlohmann::json(o.net) }, #endif { "logging", nlohmann::json(o.logging) }, - { "homescreenConfig", nlohmann::json(o.homescreen) } }; + { "homescreen", nlohmann::json(o.homescreen) } }; } void from_json(nlohmann::json const& j, Settings& o) { + if (j.contains("active_theme_directory")) { + o.activeThemeDirectory = j["active_theme_directory"]; + } if (j.contains("ftp")) { o.ftp = j["ftp"].get(); } diff --git a/Sources/renderer.cpp b/Sources/renderer.cpp index 07003cc..5d1ef23 100644 --- a/Sources/renderer.cpp +++ b/Sources/renderer.cpp @@ -59,15 +59,12 @@ int Renderer::init() { return 0; } -int Renderer::init(const char* bgpath) { +int Renderer::init(std::string const& backgroundImagePath) { int ret = init(); if (ret != 0) { return ret; } - char* bgname = (char*)malloc(strlen(bgpath) + 10); - sprintf(bgname, "%s%d.png", bgpath, height); - SDL_Surface* bgsurf = IMG_Load(bgname); - free(bgname); + SDL_Surface* bgsurf = IMG_Load(backgroundImagePath.c_str()); if (bgsurf == nullptr) { InfoLog::outputLine(InfoLog::ERROR, "Creating background surface failed.\n"); return 3; diff --git a/Sources/theme.cpp b/Sources/theme.cpp new file mode 100644 index 0000000..0e65107 --- /dev/null +++ b/Sources/theme.cpp @@ -0,0 +1,74 @@ +#include "theme.hpp" +#include +#include + +#define THEME_JSON_FILE_NAME "theme.json" + +Theme::Theme(std::string themeDirectory) : rootPath(std::move(themeDirectory)) { + if (rootPath.back() != '\\') { + rootPath += "\\"; + } + load(); +} + +void Theme::load() { + std::string themeFilePath = rootPath + THEME_JSON_FILE_NAME; + + std::ifstream themeFile(themeFilePath); + nlohmann::json json; + // FIXME: Once nxdk supports C++ Exceptions, this needs to be put in a try-catch block! + themeFile >> json; + from_json(json, *this); + + themeFile.close(); +} + +void to_json(nlohmann::json& j, Theme::MenuTheme const& o) { + char fontColor[16] = { 0 }; + snprintf(fontColor, 15, "%X", o.fontColor); + + j = nlohmann::json{ { "font", nlohmann::json(o.font) }, + { "font_color", nlohmann::json(fontColor) } }; +} + +void from_json(nlohmann::json const& j, Theme::MenuTheme& o) { + if (j.contains("font")) { + o.font = j["font"]; + } + if (j.contains("font_color")) { + std::string const& colorString = j["font_color"]; + sscanf(colorString.c_str(), "%X", &o.fontColor); + } +} + +void to_json(nlohmann::json& j, Theme::ImageSet const& o) { + j = nlohmann::json{ { "480", nlohmann::json(o.image480p) }, + { "720", nlohmann::json(o.image720p) } }; +} + +void from_json(nlohmann::json const& j, Theme::ImageSet& o) { + if (j.contains("480")) { + o.image480p = j["480"]; + } + if (j.contains("720")) { + o.image720p = j["720"]; + } +} + +void to_json(nlohmann::json& j, Theme const& o) { + j = nlohmann::json{ { "title", o.getTitle() }, + { "menu", nlohmann::json(o.getMenu()) }, + { "background", nlohmann::json(o.getBackground()) } }; +} + +void from_json(nlohmann::json const& j, Theme& o) { + if (j.contains("title")) { + o.setTitle(j["title"]); + } + if (j.contains("menu")) { + o.setMenu(j["menu"].get()); + } + if (j.contains("background")) { + o.setBackground(j["background"].get()); + } +} diff --git a/main.cpp b/main.cpp index 945bd42..fcf4dae 100644 --- a/main.cpp +++ b/main.cpp @@ -12,6 +12,7 @@ #include "sntpClient.hpp" #include "subAppRouter.hpp" #include "subsystems.hpp" +#include "theme.hpp" #include "timeMenu.hpp" #include "timing.hpp" @@ -29,6 +30,12 @@ #define HOME "." SEPARATOR #endif +static void initWithBackground(Renderer& r, Theme const& theme) { + auto const& imageSet = theme.getBackground(); + std::string const& path = (r.getHeight() >= 720) ? imageSet.image720p + : imageSet.image480p; + r.init(theme.getAbsolutePath(path)); +} int main(void) { #ifdef NXDK @@ -45,6 +52,10 @@ int main(void) { return init; } + std::string themePath = "A:\\NeXThemes\\" + config.settings.activeThemeDirectory; + InfoLog::outputLine(InfoLog::INFO, "Loading theme from %s", themePath.c_str()); + Theme activeTheme(themePath); + NetworkManager networkManager(config); #ifdef NXDK if (config.settings.net.getEnabled()) { @@ -73,11 +84,10 @@ int main(void) { // Create render system Renderer r; - r.init(HOME); + initWithBackground(r, activeTheme); // Load font - // FIXME: Font path should be read from theme - Font f(r, HOME "vegur.ttf"); + Font f(r, activeTheme.getAbsolutePath(activeTheme.getMenu().font).c_str()); SubAppRouter& router = *SubAppRouter::getInstance(); diff --git a/sampleconfig.json b/sampleconfig.json index bbcb15a..99bd9f1 100644 --- a/sampleconfig.json +++ b/sampleconfig.json @@ -1,6 +1,7 @@ { "settings": { + "active_theme_directory": "default", "network": { "enable": true,