From 3f7385517029886b8794a5904a26d2f267529483 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Thu, 21 Oct 2021 15:26:30 -0400 Subject: [PATCH] Added theme support for dirs and .zip via physfs. Closes #121. --- Makefile.nx | 2 +- Makefile.pc | 2 +- common/assets.c | 62 ++++++++++++++++++++++++++++++--------------- common/assets.h | 1 + common/menu-entry.c | 54 +++++++++++++++++++++++++++++++-------- common/menu-list.c | 17 ++++++++++++- common/theme.c | 33 ++++++++++++++++++++---- nx_main/main.c | 9 +++++++ pc_main/main.cpp | 5 +++- 9 files changed, 145 insertions(+), 40 deletions(-) diff --git a/Makefile.nx b/Makefile.nx index 5789eb1..71b05ae 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 := -ldeko3d -lminizip `freetype-config --libs` -lconfig -lturbojpeg -lpng +LIBS := -ldeko3d -lminizip -lphysfs `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 996d547..a58074d 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 -lpng $(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 -lphysfs -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 07645cb..941b099 100644 --- a/common/assets.c +++ b/common/assets.c @@ -1,6 +1,7 @@ #include "common.h" #include +#include #include #define GENASSET(_p, _mode, _w, _h) {{.path = _p, .imageMode = _mode, .imageSize = {_w, _h}}, {}} @@ -223,6 +224,42 @@ bool assetsLoadPngFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageM return ret; } +bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term) { + bool ret=true; + *data_buf = NULL; + if (filesize) *filesize = 0; + + PHYSFS_Stat tmpstat={0}; + if (!(PHYSFS_stat(path, &tmpstat) && tmpstat.filesize!=-1)) ret = false; + + if (ret) { + size_t bufsize = tmpstat.filesize; + if (nul_term) bufsize++; + *data_buf = (u8*)malloc(bufsize); + if (*data_buf) memset(*data_buf, 0, bufsize); + else ret = false; + } + + if (ret) { + PHYSFS_File *f = PHYSFS_openRead(path); + if (f==NULL) ret = false; + else { + ret = PHYSFS_readBytes(f, *data_buf, tmpstat.filesize) == tmpstat.filesize; + PHYSFS_close(f); + } + } + + if (ret) { + if (filesize) *filesize = tmpstat.filesize; + } + else { + free(*data_buf); + *data_buf = NULL; + } + + return ret; +} + bool assetsLoadFromTheme(AssetId id, const char *path, int *imageSize) { if (id < 0 || id >= AssetId_Max) return false; @@ -242,12 +279,10 @@ bool assetsLoadFromTheme(AssetId id, const char *path, int *imageSize) { const char* ext = getExtension(entry->path); bool ret=true; + size_t filesize=0; 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); @@ -255,31 +290,18 @@ bool assetsLoadFromTheme(AssetId id, const char *path, int *imageSize) { 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) ret = assetsPhysfsReadFile(entry->path, &data_buf, &filesize, false); if (ret) { if (strcasecmp(ext, ".bin")==0) { - if (st.st_size != entry->size) ret = false; + if (filesize != 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]); + ret = assetsLoadJpgFromMemory(data_buf, filesize, 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]); + ret = assetsLoadPngFromMemory(data_buf, filesize, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]); else ret = false; // File extension not recognized. } diff --git a/common/assets.h b/common/assets.h index ed70310..fdf4b9c 100644 --- a/common/assets.h +++ b/common/assets.h @@ -35,6 +35,7 @@ typedef struct { Result assetsInit(void); void assetsExit(void); void assetsClearTheme(void); +bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term); bool assetsLoadFromTheme(AssetId id, const char *path, int *imageSize); void assetsGetData(AssetId id, assetsDataEntry **out); u8 *assetsGetDataBuffer(AssetId id); diff --git a/common/menu-entry.c b/common/menu-entry.c index b852609..9a1e9fb 100644 --- a/common/menu-entry.c +++ b/common/menu-entry.c @@ -1,4 +1,5 @@ #include "common.h" +#include void menuEntryInit(menuEntry_s* me, MenuEntryType type) { memset(me, 0, sizeof(*me)); @@ -80,9 +81,13 @@ static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) { return ok; } -static bool menuEntryLoadExternalIcon(menuEntry_s* me, const char* path) { +static bool menuEntryLoadExternalIcon(menuEntry_s* me, const char* path, bool data_source) { struct stat st; + if (data_source) { + return assetsPhysfsReadFile(path, &me->icon, &me->icon_size, false); + } + if(stat(path, &st)==-1) return false; FILE* f = fopen(path, "rb"); @@ -326,7 +331,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_ char* ext = getExtension(tempbuf); strcpy(ext, ".jpg"); - iconLoaded = menuEntryLoadExternalIcon(me, tempbuf); + iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false); if (iconLoaded) break; if (isOldAppFolder) @@ -334,7 +339,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_ char* slash = getSlash(tempbuf); strcpy(slash, "/icon.jpg"); - iconLoaded = menuEntryLoadExternalIcon(me, tempbuf); + iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false); if (iconLoaded) break; }*/ @@ -408,18 +413,43 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_ *version = "1.0.0"; const char* cfg_path = me->path; - #ifdef __SWITCH__ + const char* theme_archive_path = NULL; const char* ext = getExtension(me->path); + bool good_cfg = false; + bool is_archive = false; + #ifdef __SWITCH__ 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"; + theme_archive_path = "themetmp:/"; } #endif - if (config_read_file(&cfg, cfg_path)) { + if (strcasecmp(ext, ".romfs")!=0 && strcasecmp(ext, ".cfg")!=0) { + theme_archive_path = me->path; + } + if (theme_archive_path) { + if (!PHYSFS_mount(theme_archive_path, "themetmp", 0)) cfg_path = NULL; + else { + is_archive = true; + cfg_path = "themetmp/theme.cfg"; + } + } + + if (cfg_path) { + if (!is_archive) good_cfg = config_read_file(&cfg, cfg_path); + else { + u8 *cfg_buf = NULL; + good_cfg = assetsPhysfsReadFile(cfg_path, &cfg_buf, NULL, true); + if (good_cfg) good_cfg = config_read_string(&cfg, (char*)cfg_buf); + free(cfg_buf); + } + } + + if (good_cfg) { themeInfo = config_lookup(&cfg, "themeInfo"); if (themeInfo != NULL) { if(config_setting_lookup_string(themeInfo, "name", &name)) @@ -433,15 +463,17 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_ strncpy(me->version, version, sizeof(me->version)-1); config_destroy(&cfg); - #ifdef __SWITCH__ - if (is_romfs) { + if (good_cfg && is_archive) { bool iconLoaded = false; - iconLoaded = menuEntryLoadExternalIcon(me, "themetmp:/icon.jpg"); + iconLoaded = menuEntryLoadExternalIcon(me, "themetmp/icon.jpg", true); if (iconLoaded) menuEntryParseIcon(me); } + if (is_archive) PHYSFS_unmount(theme_archive_path); + + #ifdef __SWITCH__ if (is_romfs) romfsUnmount("themetmp"); #endif } @@ -475,7 +507,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_ bool iconLoaded = false; - iconLoaded = menuEntryLoadExternalIcon(me, tempbuf); + iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false); if (iconLoaded) menuEntryParseIcon(me); @@ -655,8 +687,8 @@ void menuEntryFileassocLoad(const char* filepath) { } me->fileassoc_str[sizeof(me->fileassoc_str)-1] = 0; - if (target_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, target_icon_path); - if (!iconLoaded && main_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, main_icon_path); + if (target_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, target_icon_path, false); + if (!iconLoaded && main_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, main_icon_path, false); if (iconLoaded) { menuEntryParseIcon(me); diff --git a/common/menu-list.c b/common/menu-list.c index dd8cb1a..1709617 100644 --- a/common/menu-list.c +++ b/common/menu-list.c @@ -246,8 +246,23 @@ int themeMenuScan(const char* target) { memset(tmp_path, 0, sizeof(tmp_path)); snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name); + bool entrytype=0; + + #ifdef _DIRENT_HAVE_D_TYPE + if (dp->d_type == DT_UNKNOWN) + continue; + entrytype = dp->d_type != DT_REG; + #else + struct stat tmpstat; + + if(stat(tmp_path, &tmpstat)==-1) + continue; + + entrytype = (tmpstat.st_mode & S_IFMT) != S_IFREG; + #endif + const char* ext = getExtension(dp->d_name); - if (strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0) + if (entrytype || strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0 || strcasecmp(ext, ".zip")==0) me = menuCreateEntry(ENTRY_TYPE_THEME); if (!me) diff --git a/common/theme.c b/common/theme.c index c37dacf..a86a1c7 100644 --- a/common/theme.c +++ b/common/theme.c @@ -1,4 +1,5 @@ #include "theme.h" +#include theme_t themeCurrent; ThemePreset themeGlobalPreset; @@ -82,7 +83,7 @@ bool assetObjectFromSetting(config_setting_t *asset_setting, AssetId id, ThemeLa return false; memset(tmp_path, 0, sizeof(tmp_path)); - snprintf(tmp_path, sizeof(tmp_path)-1, "theme:/%s", path); + snprintf(tmp_path, sizeof(tmp_path)-1, "theme/%s", path); return assetsLoadFromTheme(id, tmp_path, imageSize); } @@ -420,24 +421,45 @@ void themeStartup(ThemePreset preset) { bool logoColor_set = false; bool good_cfg = false; bool is_romfs = false; + bool is_archive = false; + const char* theme_archive_path = NULL; assetsClearTheme(); if(themePath[0]!=0) { const char* cfg_path = themePath; - #ifdef __SWITCH__ const char* ext = getExtension(themePath); + #ifdef __SWITCH__ if (strcasecmp(ext, ".romfs")==0) { if (R_FAILED(romfsMountFromFsdev(themePath, 0, "theme"))) cfg_path = NULL; else { is_romfs = true; cfg_path = "theme:/theme.cfg"; + theme_archive_path = "theme:/"; } } #endif + if (strcasecmp(ext, ".romfs")!=0 && strcasecmp(ext, ".cfg")!=0) { + theme_archive_path = themePath; + } + if (theme_archive_path) { + if (!PHYSFS_mount(theme_archive_path, "theme", 0)) cfg_path = NULL; + else { + is_archive = true; + cfg_path = "theme/theme.cfg"; + } + } - if (cfg_path) good_cfg = config_read_file(&cfg, cfg_path); + if (cfg_path) { + if (!is_archive) good_cfg = config_read_file(&cfg, cfg_path); + else { + u8 *cfg_buf = NULL; + good_cfg = assetsPhysfsReadFile(cfg_path, &cfg_buf, NULL, true); + if (good_cfg) good_cfg = config_read_string(&cfg, (char*)cfg_buf); + free(cfg_buf); + } + } } switch (preset) { @@ -583,8 +605,8 @@ void themeStartup(ThemePreset preset) { layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion], false); } - if (is_romfs) assets = config_lookup(&cfg, "assets"); - if (is_romfs && assets) { + if (is_archive) assets = config_lookup(&cfg, "assets"); + if (is_archive && 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]); @@ -622,6 +644,7 @@ void themeStartup(ThemePreset preset) { config_destroy(&cfg); + if (is_archive) PHYSFS_unmount(theme_archive_path); #ifdef __SWITCH__ if (is_romfs) romfsUnmount("theme"); #endif diff --git a/nx_main/main.c b/nx_main/main.c index b3a176e..b4b94b6 100644 --- a/nx_main/main.c +++ b/nx_main/main.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "../common/common.h" #include "nx_graphics.h" @@ -68,6 +69,13 @@ int main(int argc, char **argv) if (R_SUCCEEDED(rc)) menuStartupPath(); + if (R_SUCCEEDED(rc)) { + if (!PHYSFS_init(argv[0])) { + rc = 1; + snprintf(errormsg, sizeof(errormsg)-1, "Error: PHYSFS_init() failed: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + } + } + if (R_SUCCEEDED(rc)) { rc = assetsInit(); if (R_FAILED(rc)) { @@ -189,6 +197,7 @@ int main(int argc, char **argv) netloaderExit(); powerExit(); assetsExit(); + PHYSFS_deinit(); appletUnlockExit(); diff --git a/pc_main/main.cpp b/pc_main/main.cpp index e8adbe0..10e0dd3 100644 --- a/pc_main/main.cpp +++ b/pc_main/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include extern "C" { @@ -10,12 +11,13 @@ extern "C" { color_t pixels[720][1280]; -int main() +int main(int argc, char **argv) { sf::RenderWindow window(sf::VideoMode(1280, 720), "Test"); window.setFramerateLimit(60); menuStartupPath(); + PHYSFS_init(argv[0]); assetsInit(); themeStartup(THEME_PRESET_LIGHT); textInit(); @@ -62,6 +64,7 @@ int main() netloaderExit(); fontExit(); assetsExit(); + PHYSFS_deinit(); return 0; }