From d11585589e473e091634e750ab17b37582e55076 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Thu, 2 Jan 2020 11:07:43 -0500 Subject: [PATCH] Added support for using themes with extension '.romfs', which contains '/theme.cfg', optionally '/icon.jpg' for the menu-entry in theme-menu, and any assets. Added 'assets' group to theme config, this is only used with .romfs themes. Added 'backgroundImage' to layout theme config, which requires assets.background_image to be setup. Various improvements. --- Makefile.nx | 2 +- Makefile.pc | 2 +- common/assets.c | 229 +++++++++++++++++++++++++++++++++++--------- common/assets.h | 19 +++- common/common.h | 1 + common/menu-entry.c | 91 ++++++++---------- common/menu-list.c | 2 +- common/menu.c | 61 ++++++++---- common/theme.c | 73 +++++++++++++- 9 files changed, 361 insertions(+), 119 deletions(-) diff --git a/Makefile.nx b/Makefile.nx index bc014f5..fc7005e 100644 --- a/Makefile.nx +++ b/Makefile.nx @@ -63,7 +63,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions ASFLAGS := -g $(ARCH) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lminizip `freetype-config --libs` -lconfig -lturbojpeg +LIBS := -lminizip `freetype-config --libs` -lconfig -lturbojpeg -lpng #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/Makefile.pc b/Makefile.pc index 134b71f..996d547 100644 --- a/Makefile.pc +++ b/Makefile.pc @@ -14,7 +14,7 @@ test : pc_main/main.cpp pc_main/pc_launch.c pc_main/pc_power.c pc_main/pc_netsta common/menu-entry.c common/menu-list.c common/message-box.c common/text.c \ common/ui.c common/assets.c common/math.c common/theme.c \ common/netloader.c - gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config freetype2 --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config freetype2 --libs` -lm -lminizip -lz -lconfig -lturbojpeg $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@ + gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config freetype2 --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config freetype2 --libs` -lm -lminizip -lz -lconfig -lturbojpeg -lpng $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@ clean: rm -rf build_pc/ test test.* diff --git a/common/assets.c b/common/assets.c index e3cc541..07645cb 100644 --- a/common/assets.c +++ b/common/assets.c @@ -1,32 +1,28 @@ #include "common.h" #include +#include -typedef struct { - u8 *buffer; - size_t size; - const char *filename; -} assetsDataEntry; - -#define GENASSET(x) {.filename = x} +#define GENASSET(_p, _mode, _w, _h) {{.path = _p, .imageMode = _mode, .imageSize = {_w, _h}}, {}} static bool g_assetsInitialized = 0; -assetsDataEntry g_assetsDataList[AssetId_Max] = { - GENASSET("battery_icon.bin"), - GENASSET("charging_icon.bin"), - GENASSET("folder_icon.bin"), - GENASSET("invalid_icon.bin"), - GENASSET("hbmenu_logo_dark.bin"), - GENASSET("hbmenu_logo_light.bin"), - GENASSET("theme_icon_dark.bin"), - GENASSET("theme_icon_light.bin"), - GENASSET("airplane_icon.bin"), - GENASSET("wifi_none_icon.bin"), - GENASSET("wifi1_icon.bin"), - GENASSET("wifi2_icon.bin"), - GENASSET("wifi3_icon.bin"), - GENASSET("eth_icon.bin"), - GENASSET("eth_none_icon.bin"), +assetsDataEntry g_assetsDataList[AssetId_Max][2] = { + GENASSET("battery_icon.bin", IMAGE_MODE_RGBA32, 24, 24), + GENASSET("charging_icon.bin", IMAGE_MODE_RGBA32, 24, 24), + GENASSET("folder_icon.bin", IMAGE_MODE_RGB24, 256, 256), + GENASSET("invalid_icon.bin", IMAGE_MODE_RGB24, 256, 256), + GENASSET("hbmenu_logo_dark.bin", IMAGE_MODE_RGBA32, 140, 60), + GENASSET("hbmenu_logo_light.bin", IMAGE_MODE_RGBA32, 140, 60), + GENASSET("theme_icon_dark.bin", IMAGE_MODE_RGB24, 256, 256), + GENASSET("theme_icon_light.bin", IMAGE_MODE_RGB24, 256, 256), + GENASSET("airplane_icon.bin", IMAGE_MODE_RGBA32, 24, 24), + GENASSET("wifi_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24), + GENASSET("wifi1_icon.bin", IMAGE_MODE_RGBA32, 24, 24), + GENASSET("wifi2_icon.bin", IMAGE_MODE_RGBA32, 24, 24), + GENASSET("wifi3_icon.bin", IMAGE_MODE_RGBA32, 24, 24), + GENASSET("eth_icon.bin", IMAGE_MODE_RGBA32, 24, 24), + GENASSET("eth_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24), + GENASSET("", IMAGE_MODE_RGB24, 1280, 720), }; static void assetsClearEntry(assetsDataEntry *entry) { @@ -34,6 +30,20 @@ static void assetsClearEntry(assetsDataEntry *entry) { entry->size = 0; entry->buffer = NULL; + + memset(entry, 0, sizeof(*entry)); +} + +static void assetsSetPixelSize(assetsDataEntry *entry) { + switch (entry->imageMode) { + case IMAGE_MODE_RGB24: + entry->pixSize = 3; + break; + + case IMAGE_MODE_RGBA32: + entry->pixSize = 4; + break; + } } static int assetsLoadFile(unzFile zipf, assetsDataEntry *entry) { @@ -42,7 +52,9 @@ static int assetsLoadFile(unzFile zipf, assetsDataEntry *entry) { unz_file_info file_info; u8* buffer = NULL; - ret = unzLocateFile(zipf, entry->filename, 0); + assetsSetPixelSize(entry); + + ret = unzLocateFile(zipf, entry->path, 0); if (ret==UNZ_OK) ret = unzOpenCurrentFile(zipf); @@ -50,7 +62,7 @@ static int assetsLoadFile(unzFile zipf, assetsDataEntry *entry) { ret = unzGetCurrentFileInfo(zipf, &file_info, NULL, 0, NULL, 0, NULL, 0); filesize = file_info.uncompressed_size; - if (filesize == 0) ret = -10; + if (filesize != entry->imageSize[0] * entry->imageSize[1] * entry->pixSize) ret = -10; if (ret==UNZ_OK) { buffer = (u8*)malloc(filesize); @@ -88,7 +100,7 @@ Result assetsInit(void) { int i, stopi; unzFile zipf; assetsDataEntry *entry = NULL; - char tmp_path[PATH_MAX+1]; + char tmp_path[PATH_MAX]; if (g_assetsInitialized) return 0; @@ -116,14 +128,17 @@ Result assetsInit(void) { for (i=0; ipath[0]) { + ret = assetsLoadFile(zipf, entry); + if (ret!=UNZ_OK) break; + entry->initialized = true; + } } if (ret!=UNZ_OK) { for (i=0; i= AssetId_Max) return false; + + assetsDataEntry *entry = &g_assetsDataList[id][1]; + if (entry->initialized) return false; + + memset(entry, 0, sizeof(*entry)); + + entry->imageSize[0] = imageSize[0]; + entry->imageSize[1] = imageSize[1]; + + entry->imageMode = g_assetsDataList[id][0].imageMode; + assetsSetPixelSize(entry); + entry->size = entry->imageSize[0] * entry->imageSize[1] * entry->pixSize; + + strncpy(entry->path, path, sizeof(entry->path)-1); + + const char* ext = getExtension(entry->path); + bool ret=true; + if (ext==NULL) ret = false; + + u8 *data_buf = NULL; + struct stat st; + + if (ret && stat(path, &st)==-1) ret = false; + + if (ret) { + entry->buffer = (u8*)malloc(entry->size); + if (entry->buffer) memset(entry->buffer, 0, entry->size); + else ret = false; + } + + if (ret) { + data_buf = (u8*)malloc(st.st_size); + if (data_buf) memset(data_buf, 0, st.st_size); + else ret = false; + } + + if (ret) { + FILE *f = fopen(entry->path, "rb"); + if (f==NULL) ret = false; + else { + ret = fread(data_buf, st.st_size, 1, f) == 1; + fclose(f); + } + } + + if (ret) { + if (strcasecmp(ext, ".bin")==0) { + if (st.st_size != entry->size) ret = false; + + if (ret) memcpy(entry->buffer, data_buf, entry->size); + } + else if (strcasecmp(ext, ".jpg")==0 || strcasecmp(ext, ".jpeg")==0) + ret = assetsLoadJpgFromMemory(data_buf, st.st_size, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]); + else if (strcasecmp(ext, ".png")==0) + ret = assetsLoadPngFromMemory(data_buf, st.st_size, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]); + else + ret = false; // File extension not recognized. + } + + if (ret) entry->initialized = true; + else assetsClearEntry(entry); + + free(data_buf); + + return ret; +} + +void assetsGetData(AssetId id, assetsDataEntry **out) { + if (out) *out = NULL; if (id < 0 || id >= AssetId_Max) return; - assetsDataEntry *entry = &g_assetsDataList[id]; - - if (buffer) *buffer = entry->buffer; - if (size) *size = entry->size; + u32 pos = g_assetsDataList[id][1].initialized ? 1 : 0; + assetsDataEntry *entry = &g_assetsDataList[id][pos]; + if (entry->initialized) *out = entry; } u8 *assetsGetDataBuffer(AssetId id) { - u8 *buffer = NULL; + assetsDataEntry *entry = NULL; - assetsGetData(id, &buffer, NULL); - return buffer; + assetsGetData(id, &entry); + return entry ? entry->buffer : NULL; } diff --git a/common/assets.h b/common/assets.h index 6eb23e2..ed70310 100644 --- a/common/assets.h +++ b/common/assets.h @@ -17,12 +17,27 @@ typedef enum { AssetId_wifi3_icon, AssetId_eth_icon, AssetId_eth_none_icon, + AssetId_background_image, - AssetId_Max + AssetId_Max, } AssetId; +typedef struct { + bool initialized; + u8 *buffer; + size_t size; + ImageMode imageMode; + size_t pixSize; + size_t imageSize[2]; + char path[PATH_MAX]; +} assetsDataEntry; + Result assetsInit(void); void assetsExit(void); -void assetsGetData(AssetId id, u8 **buffer, size_t *size); +void assetsClearTheme(void); +bool assetsLoadFromTheme(AssetId id, const char *path, int *imageSize); +void assetsGetData(AssetId id, assetsDataEntry **out); u8 *assetsGetDataBuffer(AssetId id); +bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height); + diff --git a/common/common.h b/common/common.h index 5d14a73..c373932 100644 --- a/common/common.h +++ b/common/common.h @@ -59,6 +59,7 @@ typedef enum ThemeLayoutId_MenuTypeMsg, ThemeLayoutId_MsgBoxSeparator, ThemeLayoutId_MsgBoxBottomText, + ThemeLayoutId_BackgroundImage, ThemeLayoutId_BackWave, ThemeLayoutId_MiddleWave, ThemeLayoutId_FrontWave, diff --git a/common/menu-entry.c b/common/menu-entry.c index b2ed75f..8ac7844 100644 --- a/common/menu-entry.c +++ b/common/menu-entry.c @@ -106,12 +106,15 @@ static bool menuEntryImportIconGfx(menuEntry_s* me, uint8_t* icon_gfx, uint8_t* if (icon_gfx == NULL || icon_gfx_small == NULL) return false; - tmpsize = 256*256*3; + ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]; + ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon]; + + tmpsize = layoutobj->imageSize[0]*layoutobj->imageSize[1]*3; me->icon_gfx = (uint8_t*)malloc(tmpsize); if (me->icon_gfx) memcpy(me->icon_gfx, icon_gfx, tmpsize); if (me->icon_gfx) { - tmpsize = 140*140*3; + tmpsize = layoutobj2->imageSize[0]*layoutobj2->imageSize[1]*3; me->icon_gfx_small = (uint8_t*)malloc(tmpsize); if (me->icon_gfx_small) memcpy(me->icon_gfx_small, icon_gfx_small, tmpsize); @@ -403,8 +406,20 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_ const char *name, *author = textGetString(StrId_DefaultPublisher), *version = "1.0.0"; - - if (config_read_file(&cfg, me->path)) { + + const char* cfg_path = me->path; + #ifdef __SWITCH__ + const char* ext = getExtension(me->path); + bool is_romfs = false; + if (strcasecmp(ext, ".romfs")==0) { + if (R_FAILED(romfsMountFromFsdev(me->path, 0, "themetmp"))) + return false; + is_romfs = true; + cfg_path = "themetmp:/theme.cfg"; + } + #endif + + if (config_read_file(&cfg, cfg_path)) { themeInfo = config_lookup(&cfg, "themeInfo"); if (themeInfo != NULL) { if(config_setting_lookup_string(themeInfo, "name", &name)) @@ -417,6 +432,18 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_ strncpy(me->author, author, sizeof(me->author)-1); strncpy(me->version, version, sizeof(me->version)-1); config_destroy(&cfg); + + #ifdef __SWITCH__ + if (is_romfs) { + bool iconLoaded = false; + + iconLoaded = menuEntryLoadExternalIcon(me, "themetmp:/icon.jpg"); + + if (iconLoaded) menuEntryParseIcon(me); + } + + if (is_romfs) romfsUnmount("themetmp"); + #endif } if (me->type == ENTRY_TYPE_FILE_OTHER) @@ -641,62 +668,24 @@ void menuEntryFileassocLoad(const char* filepath) { void menuEntryParseIcon(menuEntry_s* me) { if (me->icon_size==0 || me->icon==NULL) return; - int w,h,samp; - size_t imagesize = 256*256*3; + ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]; + ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon]; + + size_t imagesize = layoutobj->imageSize[0]*layoutobj->imageSize[1]*3; + bool ret=true; me->icon_gfx = (uint8_t*)malloc(imagesize); - if (me->icon_gfx == NULL) { - me->icon_size = 0; - free(me->icon); - me->icon = NULL; - return; - } + if (me->icon_gfx == NULL) ret = false; - tjhandle _jpegDecompressor = tjInitDecompress(); - - if (_jpegDecompressor == NULL) { - free(me->icon_gfx); - me->icon_gfx = NULL; - - me->icon_size = 0; - free(me->icon); - me->icon = NULL; - return; - } - - if (tjDecompressHeader2(_jpegDecompressor, me->icon, me->icon_size, &w, &h, &samp) == -1) { - free(me->icon_gfx); - me->icon_gfx = NULL; - - me->icon_size = 0; - free(me->icon); - me->icon = NULL; - tjDestroy(_jpegDecompressor); - return; - } - - if (w != 256 || h != 256 ) return; - - if (tjDecompress2(_jpegDecompressor, me->icon, me->icon_size, me->icon_gfx, w, 0, h, TJPF_RGB, TJFLAG_ACCURATEDCT) == -1) { - free(me->icon_gfx); - me->icon_gfx = NULL; - - me->icon_size = 0; - free(me->icon); - me->icon = NULL; - tjDestroy(_jpegDecompressor); - return; - } + if (ret) ret = assetsLoadJpgFromMemory(me->icon, me->icon_size, me->icon_gfx, IMAGE_MODE_RGB24, layoutobj->imageSize[0], layoutobj->imageSize[1]); me->icon_size = 0; free(me->icon); me->icon = NULL; - tjDestroy(_jpegDecompressor); + if (ret) me->icon_gfx_small = downscaleImg(me->icon_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->imageSize[0], layoutobj2->imageSize[1], IMAGE_MODE_RGB24); - me->icon_gfx_small = downscaleImg(me->icon_gfx, 256, 256, 140, 140, IMAGE_MODE_RGB24); - - if (me->icon_gfx_small == NULL) { + if (!ret || me->icon_gfx_small == NULL) { free(me->icon_gfx); me->icon_gfx = NULL; } diff --git a/common/menu-list.c b/common/menu-list.c index f22c66a..64fd6e3 100644 --- a/common/menu-list.c +++ b/common/menu-list.c @@ -245,7 +245,7 @@ int themeMenuScan(const char* target) { snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name); const char* ext = getExtension(dp->d_name); - if (strcasecmp(ext, ".cfg")==0) + if (strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0) me = menuCreateEntry(ENTRY_TYPE_THEME); if (!me) diff --git a/common/menu.c b/common/menu.c index 1a96164..44d8ca1 100644 --- a/common/menu.c +++ b/common/menu.c @@ -8,6 +8,11 @@ char rootPathBase[PATH_MAX]; char rootPath[PATH_MAX+8]; + +uint8_t *folder_icon_small; +uint8_t *invalid_icon_small; +uint8_t *theme_icon_small; + void computeFrontGradient(color_t baseColor, int height); char *menuGetRootPath(void) { @@ -113,12 +118,37 @@ void menuHandleXButton(void) { } } +void menuStartupCommon(void) { + free(folder_icon_small); + free(invalid_icon_small); + free(theme_icon_small); + + ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]; + ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon]; + assetsDataEntry *data = NULL; + + assetsGetData(AssetId_folder_icon, &data); + folder_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->imageSize[0], layoutobj2->imageSize[1], data->imageMode); + assetsGetData(AssetId_invalid_icon, &data); + invalid_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->imageSize[0], layoutobj2->imageSize[1], data->imageMode); + if(themeGlobalPreset == THEME_PRESET_DARK) { + assetsGetData(AssetId_theme_icon_dark, &data); + theme_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->imageSize[0], layoutobj2->imageSize[1], data->imageMode); + } + else { + assetsGetData(AssetId_theme_icon_light, &data); + theme_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->imageSize[0], layoutobj2->imageSize[1], data->imageMode); + } + + layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave]; + computeFrontGradient(themeCurrent.frontWaveColor, layoutobj->size[1]); +} void launchApplyThemeTask(menuEntry_s* arg) { const char* themePath = arg->path; SetThemePathToConfig(themePath); themeStartup(themeGlobalPreset); - computeFrontGradient(themeCurrent.frontWaveColor, 280); + menuStartupCommon(); } bool menuIsNetloaderActive(void) { @@ -171,10 +201,6 @@ static void drawIcon(int x, int y, int width, int height, const uint8_t *image, } } -uint8_t *folder_icon_small; -uint8_t *invalid_icon_small; -uint8_t *theme_icon_small; - static void drawEntry(menuEntry_s* me, int off_x, int is_active) { ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList]; int x, y; @@ -413,14 +439,7 @@ void menuStartup(void) { menuScan(rootPath); - folder_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_folder_icon), 256, 256, 140, 140, IMAGE_MODE_RGB24); - invalid_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_invalid_icon), 256, 256, 140, 140, IMAGE_MODE_RGB24); - if(themeGlobalPreset == THEME_PRESET_DARK) - theme_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_theme_icon_dark), 256, 256, 140, 140, IMAGE_MODE_RGB24); - else - theme_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_theme_icon_light), 256, 256, 140, 140, IMAGE_MODE_RGB24); - computeFrontGradient(themeCurrent.frontWaveColor, 280); - //menuCreateMsgBox(780, 300, "This is a test"); + menuStartupCommon(); } void themeMenuStartup(void) { @@ -500,17 +519,22 @@ void drawCharge() { } layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BatteryIcon]; - if (layoutobj->visible) drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], layoutobj->imageSize[0], layoutobj->imageSize[1], assetsGetDataBuffer(AssetId_battery_icon), themeCurrent.textColor); + assetsDataEntry *data = NULL; + assetsGetData(AssetId_battery_icon, &data); + if (layoutobj->visible) drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ChargingIcon]; + assetsGetData(AssetId_charging_icon, &data); if (isCharging && layoutobj->visible) - drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], layoutobj->imageSize[0], layoutobj->imageSize[1], assetsGetDataBuffer(AssetId_charging_icon), themeCurrent.textColor); + drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor); } } void drawNetwork(int tmpX, AssetId id) { ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_NetworkIcon]; - if (layoutobj->visible) drawIcon(layoutobj->posType ? tmpX + layoutobj->posStart[0] : layoutobj->posStart[0], layoutobj->posStart[1], layoutobj->imageSize[0], layoutobj->imageSize[1], assetsGetDataBuffer(id), themeCurrent.textColor); + assetsDataEntry *data = NULL; + assetsGetData(id, &data); + if (layoutobj->visible) drawIcon(layoutobj->posType ? tmpX + layoutobj->posStart[0] : layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor); } u32 drawStatus() { @@ -637,6 +661,11 @@ void menuLoop(void) { } } + layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage]; + assetsDataEntry *data = NULL; + assetsGetData(AssetId_background_image, &data); + if (layoutobj->visible && data) drawImage(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], endy < data->imageSize[1] ? endy : data->imageSize[1], data->buffer, data->imageMode); + layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackWave]; if (layoutobj->visible) drawWave(0, menuTimer, themeCurrent.backWaveColor, layoutobj->size[1], 0.0, 3.0); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave]; diff --git a/common/theme.c b/common/theme.c index 8538fdd..8720825 100644 --- a/common/theme.c +++ b/common/theme.c @@ -62,6 +62,31 @@ bool layoutObjectFromSetting(config_setting_t *layout_setting, ThemeLayoutObject return true; } +bool assetObjectFromSetting(config_setting_t *asset_setting, AssetId id, ThemeLayoutObject *layoutobj) { + int imageSize[2]={0}; + const char *path = NULL; + char tmp_path[PATH_MAX]; + if (!asset_setting) + return false; + + if (config_setting_lookup_string(asset_setting, "path", &path)==CONFIG_FALSE) + return false; + + if (!intElemFromSetting(config_setting_lookup(asset_setting, "imageSize"), imageSize, 2)) + return false; + + if (imageSize[0] <= 0 || imageSize[1] <= 0 || imageSize[0] > 1280 || imageSize[1] > 720) + return false; + + if (layoutobj && (imageSize[0] != layoutobj->imageSize[0] || imageSize[1] != layoutobj->imageSize[1])) + return false; + + memset(tmp_path, 0, sizeof(tmp_path)); + snprintf(tmp_path, sizeof(tmp_path)-1, "theme:/%s", path); + + return assetsLoadFromTheme(id, tmp_path, imageSize); +} + void themeStartup(ThemePreset preset) { themeGlobalPreset = preset; theme_t themeLight = (theme_t) { @@ -184,6 +209,8 @@ void themeStartup(ThemePreset preset) { .posStart = {0, -29}, }, + // ThemeLayoutId_BackgroundImage is not set with the defaults. + [ThemeLayoutId_BackWave] = { .visible = true, .posType = true, @@ -388,14 +415,31 @@ void themeStartup(ThemePreset preset) { theme_t *themeDefault; config_t cfg = {0}; config_init(&cfg); - config_setting_t *theme = NULL, *layout = NULL; + config_setting_t *theme = NULL, *layout = NULL, *assets = NULL; color_t text, attentionText, frontWave, middleWave, backWave, background, highlight, separator, borderColor, borderTextColor, progressBarColor; int waveBlending; const char *AText, *BText, *XText, *YText, *PText, *MText, *starOnText, *starOffText; bool good_cfg = false; + bool is_romfs = false; - if(themePath[0]!=0) - good_cfg = config_read_file(&cfg, themePath); + assetsClearTheme(); + + if(themePath[0]!=0) { + const char* cfg_path = themePath; + #ifdef __SWITCH__ + const char* ext = getExtension(themePath); + if (strcasecmp(ext, ".romfs")==0) { + if (R_FAILED(romfsMountFromFsdev(themePath, 0, "theme"))) + cfg_path = NULL; + else { + is_romfs = true; + cfg_path = "theme:/theme.cfg"; + } + } + #endif + + if (cfg_path) good_cfg = config_read_file(&cfg, cfg_path); + } switch (preset) { case THEME_PRESET_LIGHT: @@ -504,6 +548,7 @@ void themeStartup(ThemePreset preset) { layoutObjectFromSetting(config_setting_lookup(layout, "menuTypeMsg"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuTypeMsg], false); layoutObjectFromSetting(config_setting_lookup(layout, "msgBoxSeparator"), &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator], false); layoutObjectFromSetting(config_setting_lookup(layout, "msgBoxBottomText"), &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxBottomText], false); + layoutObjectFromSetting(config_setting_lookup(layout, "backgroundImage"), &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage], false); layoutObjectFromSetting(config_setting_lookup(layout, "backWave"), &themeCurrent.layoutObjects[ThemeLayoutId_BackWave], false); layoutObjectFromSetting(config_setting_lookup(layout, "middleWave"), &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave], false); layoutObjectFromSetting(config_setting_lookup(layout, "frontWave"), &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave], false); @@ -532,6 +577,24 @@ void themeStartup(ThemePreset preset) { layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryAuthor"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryAuthor], false); layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion], false); } + + if (is_romfs) assets = config_lookup(&cfg, "assets"); + if (is_romfs && assets) { + assetObjectFromSetting(config_setting_lookup(assets, "battery_icon"), AssetId_battery_icon, NULL); + assetObjectFromSetting(config_setting_lookup(assets, "charging_icon"), AssetId_charging_icon, NULL); + assetObjectFromSetting(config_setting_lookup(assets, "folder_icon"), AssetId_folder_icon, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]); + assetObjectFromSetting(config_setting_lookup(assets, "invalid_icon"), AssetId_invalid_icon, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]); + assetObjectFromSetting(config_setting_lookup(assets, "theme_icon_dark"), AssetId_theme_icon_dark, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]); + assetObjectFromSetting(config_setting_lookup(assets, "theme_icon_light"), AssetId_theme_icon_light, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]); + assetObjectFromSetting(config_setting_lookup(assets, "airplane_icon"), AssetId_airplane_icon, NULL); + assetObjectFromSetting(config_setting_lookup(assets, "wifi_none_icon"), AssetId_wifi_none_icon, NULL); + assetObjectFromSetting(config_setting_lookup(assets, "wifi1_icon"), AssetId_wifi1_icon, NULL); + assetObjectFromSetting(config_setting_lookup(assets, "wifi2_icon"), AssetId_wifi2_icon, NULL); + assetObjectFromSetting(config_setting_lookup(assets, "wifi3_icon"), AssetId_wifi3_icon, NULL); + assetObjectFromSetting(config_setting_lookup(assets, "eth_icon"), AssetId_eth_icon, NULL); + assetObjectFromSetting(config_setting_lookup(assets, "eth_none_icon"), AssetId_eth_none_icon, NULL); + assetObjectFromSetting(config_setting_lookup(assets, "background_image"), AssetId_background_image, NULL); + } } else { themeCurrent = *themeDefault; memcpy(themeCurrent.layoutObjects, themeCommon.layoutObjects, sizeof(themeCommon.layoutObjects)); @@ -541,6 +604,10 @@ void themeStartup(ThemePreset preset) { if (layoutobj->posEnd[0] < 1) layoutobj->posEnd[0] = 1; config_destroy(&cfg); + + #ifdef __SWITCH__ + if (is_romfs) romfsUnmount("theme"); + #endif } void GetThemePathFromConfig(char* themePath, size_t size) {