Compare commits

...

54 Commits

Author SHA1 Message Date
yellows8
2c53af56cc
Release 3.5.1 2022-06-14 18:20:43 -04:00
yellows8
7bdfce172c
Fix temperature display on [14.0.0+]. 2022-03-24 20:53:19 -04:00
imxyd
34f7f16cb1 Update language.c
Improve Chinese language
2022-01-22 19:34:54 +01:00
yellows8
73d7108a62
Release 3.5.0 2021-10-29 19:47:15 -04:00
yellows8
422039f727
Use physfs instead of minizip for asset loading, etc. 2021-10-22 13:18:30 -04:00
yellows8
3f73855170
Added theme support for dirs and .zip via physfs. Closes #121. 2021-10-21 15:26:30 -04:00
qazrfv1234
6e654ec2af Update language.c (typo fixed) 2021-08-27 20:06:28 +02:00
Dave Murphy
5c85dd784b 3.4.1 release 2021-07-16 14:53:06 +01:00
Dave Murphy
8c87b1c46f ensure build picks up version from Makefile 2021-07-16 14:52:34 +01:00
fincs
47fd18cabb
Update psm charger type 2021-07-15 21:59:49 +02:00
Chronoss
c1640f4b54
Update language.c (#130) 2021-06-09 08:21:37 +01:00
HamletDuFromage
aef37e10f7
stat(path) before trying to launch a nro (#129) 2021-06-09 08:16:37 +01:00
Mehdi Khelfi
95411fe5e9
Added some spanish and french strings (#132) 2021-04-18 14:51:03 +02:00
yellows8
65f23b0bde
Release 3.4.0 2020-12-19 12:11:00 -05:00
fincs
20c1f00972
Update for libnx hid-refactor 2020-12-02 15:01:40 +01:00
fincs
fcbc56acc4
Improve performance by using deko3d to do the framebuffer conversion on the GPU 2020-11-11 17:00:25 +01:00
fincs
244d058f1b
Minor fixes 2020-11-11 12:40:41 +01:00
Mehdi Khelfi
df09e9ed1b Added French strings
Added 2 missing french translations in language.c
2020-10-31 18:52:55 +01:00
Chris Bradel
45efcfcb98
added momentum to touch drags (#124)
* added momentum to touch drags
2020-06-26 11:35:11 -04:00
yellows8
16958ac4ba
Fixed warnings, menuTimer multi-definition, and updated for latest libnx. 2020-05-05 18:47:40 -04:00
HookedBehemoth
c69f4c56c5 update for libnx pl init change 2020-04-15 18:18:22 +02:00
yellows8
3e451a9c8b
Added resources/eth_none_icon.png which was missing. 2020-04-10 00:54:10 -04:00
yellows8
090054fc86
Release v3.3.0 2020-04-06 10:37:55 -04:00
yellows8
e2a16a83e0
Netloader now uses SO_REUSEADDR, this fixes netloader activation temporarily failing after a netloader error previously occurred. 2020-04-06 10:36:47 -04:00
yellows8
ba330cfd84
Actually display errors from netloader activation. 2020-04-06 10:35:43 -04:00
yellows8
f4067d64f3
Added app_args config setting for fileassoc. 2020-04-06 10:34:44 -04:00
yellows8
88fe495bd8
Don't delete the fileassoc entry when loading the icon failed. 2020-04-04 12:55:55 -04:00
fincs
7f1a9f4f29
U64_MAX -> UINT64_MAX 2020-03-16 16:23:36 +01:00
yellows8
ec24d595d6
Updated the default highlightGradientEdgeColor. 2020-01-08 17:03:49 -05:00
Ivan Mazzoli
5dfb9ec328 Updated italian translation and added new ones 2020-01-07 16:13:39 +01:00
yellows8
a4ad7b01b7
The icon sizes for theme-layout menuListIcon/menuActiveEntryIcon can now be controlled with the size field, with the internal imageSize field being used when size is invalid. When entering Theme Menu, the initially selected menu-entry is now the currently configured theme. Minor improvements. 2020-01-04 18:08:54 -05:00
yellows8
6ee5d36084
Removed hbmenuLogoImage from theme_t and just determine which AssetId to use for logo during rendering, imageSize from the asset is now used instead of from the layoutobj. Removed drawImageFromLayout since it's no longer used, and removed imageSize from the layout objects which don't need it anymore. 2020-01-03 17:49:08 -05:00
yellows8
981749cfb7
Added highlightGradientEdgeColor for theme config. 2020-01-03 11:47:49 -05:00
yellows8
9eda227d33
Added logoColor to the theme config. 2020-01-02 22:48:48 -05:00
yellows8
d11585589e
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. 2020-01-02 11:08:17 -05:00
yellows8
42a4ad9787
Added support for using fileassoc with netloader, an error will now be thrown if the filename/file-extension used with netloader is not recognized. Various improvements. 2019-12-30 11:15:38 -05:00
yellows8
b40d558458
Fixed a crash in touch handling for star-button when the current directory is empty (no menu entries). Implemented layouts which can optionally be loaded from theme config, closes #83. 2019-12-26 01:40:54 -05:00
yellows8
387850d301
Added APP_AUTHOR to Makefile.nx. 2019-12-14 12:44:43 -05:00
yellows8
38b48a8609
Don't use hidGetHandheldMode() to determine whether to use handheld or non-handheld. Just accept input from controllers 0-7 and handheld regardless, as long as the controller is connected. Closes #108. 2019-12-11 20:13:18 -05:00
yellows8
bd3466f1e1
Release v3.2.0 2019-12-10 20:24:41 -05:00
yellows8
e5d9851250
Actually fix the stringop warning. 2019-12-10 20:16:59 -05:00
yellows8
15baea1e44
Fixed bounds-check in netloader loadnro cmd-line args validation. 2019-12-10 12:14:23 -05:00
yellows8
c2743dcdd5
Fixed a string warning with gcc 9.x (and added the same change for other lines). 2019-12-08 17:00:17 -05:00
yellows8
233b765300
Improved hid handling. 2019-12-08 14:46:42 -05:00
octopuserectus
a747d92826 Update controller code to accept inputs from all players
not only 0 and 1 (CONTROLLER_P1_AUTO)
2019-12-07 18:58:26 -05:00
yellows8
24ce4ff924
Updated menuEntryParseNacp for latest libnx. 2019-12-07 18:08:51 -05:00
yellows8
4ecd7401b6
Use nifmInitialize/nifmExit in netloader.c, since netstatusGetDetails() uses nifm directly and latest libnx no longer uses nifm in socket init/exit. 2019-11-18 12:06:54 -05:00
yellows8
a150cbe167
Updated font.c for latest libnx + minor other changes. 2019-10-11 01:49:28 -04:00
yellows8
2feb085504
Updated language handling for latest libnx. Added support for SetLanguage_ZHHANS/SetLanguage_ZHHANT, and changed STR_ZH/STR_TW to STR_ZH_HANS/STR_ZH_HANT (system-language is still not actually used). 2019-10-04 18:16:42 -04:00
yellows8
97ab367379
Use dirent d_type when available instead of accessing FsDirectoryEntry. 2019-09-30 17:58:30 -04:00
yellows8
793b912efd
Thermal display (closes #99) (#102)
* Display the temperature status. Moved statusGet code from drawNetwork() into drawStatus(). Added types in common.h to fix building with latest libnx nacp.h, with the pc-build.
2019-09-17 11:47:58 -04:00
yellows8
42efd240de
Release v3.1.1 2019-09-14 13:53:09 -04:00
yellows8
7d01d059df
Updated README. 2019-09-09 10:34:12 -04:00
BullyWiiPlaza
6a75feeb46 Clarify build instructions 2019-09-09 10:25:23 -04:00
35 changed files with 1886 additions and 615 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.*/
*~ *~
*.exe *.exe
*.o *.o

View File

@ -1,4 +1,4 @@
export APP_VERSION := 3.1.0 export APP_VERSION := 3.5.1
ifeq ($(RELEASE),) ifeq ($(RELEASE),)
export APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always) export APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always)

View File

@ -46,6 +46,8 @@ ROMFS := romfs
DIST_PATH := $(TARGET)_v$(APP_VERSION) DIST_PATH := $(TARGET)_v$(APP_VERSION)
APP_AUTHOR := switchbrew
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# options for code generation # options for code generation
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@ -61,7 +63,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH) ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lminizip `freetype-config --libs` -lconfig -lturbojpeg LIBS := -ldeko3d -lphysfs `freetype-config --libs` -lconfig -lturbojpeg -lpng
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing # list of directories containing libraries, this must be the top level containing
@ -210,6 +212,7 @@ $(OUTPUT).nso : $(OUTPUT).elf
endif endif
menu.o : $(TOPDIR)/Makefile
$(OUTPUT).elf : $(OFILES) $(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN) $(OFILES_SRC) : $(HFILES_BIN)

View File

@ -9,12 +9,12 @@ EXTRA_CFLAGS="-D__USE_MINGW_ANSI_STDIO"
EXTRA_LDFLAGS="-lws2_32" EXTRA_LDFLAGS="-lws2_32"
endif endif
test : pc_main/main.cpp pc_main/pc_launch.c pc_main/pc_power.c pc_main/pc_netstatus.c \ test : pc_main/main.cpp pc_main/pc_launch.c pc_main/pc_power.c pc_main/pc_netstatus.c pc_main/pc_thermalstatus.c \
common/menu.c common/font.c common/language.c common/launch.c common/worker.c common/status.c \ common/menu.c common/font.c common/language.c common/launch.c common/worker.c common/status.c \
common/menu-entry.c common/menu-list.c common/message-box.c common/text.c \ 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/ui.c common/assets.c common/math.c common/theme.c \
common/netloader.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 -lphysfs -lz -lconfig -lturbojpeg -lpng $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@
clean: clean:
rm -rf build_pc/ test test.* rm -rf build_pc/ test test.*

View File

@ -1,17 +1,31 @@
#### Usage ### Usage
See [Homebrew_Applications](https://switchbrew.org/wiki/Homebrew_Applications) for SD layout and applications, etc. See [Switchbrew](https://switchbrew.org/wiki/Homebrew_Menu) for hbmenu docs. See [Homebrew_Applications](https://switchbrew.org/wiki/Homebrew_Applications) for SD layout and applications, etc. See [Switchbrew](https://switchbrew.org/wiki/Homebrew_Menu) for hbmenu docs.
#### Download ### Download
The latest release is available from the [releases](https://github.com/switchbrew/nx-hbmenu/releases/latest) page. The latest release is available from the [releases](https://github.com/switchbrew/nx-hbmenu/releases/latest) page.
#### Building ### Building
Build with ```make nx``` or just run ```make```. Build for the Nintendo Switch with ```make nx``` and for the PC with ```make pc```.
Running ```make``` builds for both systems.
The following is required to build: libfreetype (switch-freetype), libconfig (switch-libconfig), and libjpeg-turbo (switch-libjpeg-turbo). Where "({name})" is the pacman package. For the pc-build libminizip is required (for the Switch build, the switch-zlib package includes this). The following [pacman packages](https://devkitpro.org/wiki/devkitPro_pacman) are required to build for Switch:
- `switch-dev`
- `switch-freetype`
- `switch-libconfig`
- `switch-libjpeg-turbo`
- `switch-physfs`
C11-threads are used, hence building for the pc-build may fail if C11-threads are not available. The following libraries are required to build for PC:
- `libfreetype`
- `libconfig`
- `libjpeg-turbo`
- `libphysfs`
Building for Switch/PC requires `zip`.
Since C11 threads are used, building for the PC may fail if C11 threads are not available.
#### Credits #### Credits
* This uses code based on 3DS [new-hbmenu](https://github.com/fincs/new-hbmenu). * This uses code based on 3DS [new-hbmenu](https://github.com/fincs/new-hbmenu).
* libjpeg-turbo is used for handling JPEG icons. This library doesn't support lossless JPEG (likewise for official sw which uses libjpeg-turbo). * `libjpeg-turbo` is used for handling JPEG icons. This library doesn't support lossless JPEG (likewise for official sw which uses `libjpeg-turbo`).

View File

@ -1,32 +1,28 @@
#include "common.h" #include "common.h"
#include <minizip/unzip.h> #include <physfs.h>
#include <png.h>
typedef struct { #define GENASSET(_p, _mode, _w, _h) {{.path = _p, .imageMode = _mode, .imageSize = {_w, _h}}, {}}
u8 *buffer;
size_t size;
const char *filename;
} assetsDataEntry;
#define GENASSET(x) {.filename = x}
static bool g_assetsInitialized = 0; static bool g_assetsInitialized = 0;
assetsDataEntry g_assetsDataList[AssetId_Max] = { assetsDataEntry g_assetsDataList[AssetId_Max][2] = {
GENASSET("battery_icon.bin"), GENASSET("battery_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("charging_icon.bin"), GENASSET("charging_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("folder_icon.bin"), GENASSET("folder_icon.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("invalid_icon.bin"), GENASSET("invalid_icon.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("hbmenu_logo_dark.bin"), GENASSET("hbmenu_logo_dark.bin", IMAGE_MODE_RGBA32, 140, 60),
GENASSET("hbmenu_logo_light.bin"), GENASSET("hbmenu_logo_light.bin", IMAGE_MODE_RGBA32, 140, 60),
GENASSET("theme_icon_dark.bin"), GENASSET("theme_icon_dark.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("theme_icon_light.bin"), GENASSET("theme_icon_light.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("airplane_icon.bin"), GENASSET("airplane_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi_none_icon.bin"), GENASSET("wifi_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi1_icon.bin"), GENASSET("wifi1_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi2_icon.bin"), GENASSET("wifi2_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi3_icon.bin"), GENASSET("wifi3_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("eth_icon.bin"), GENASSET("eth_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("eth_none_icon.bin"), GENASSET("eth_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("", IMAGE_MODE_RGB24, 1280, 720),
}; };
static void assetsClearEntry(assetsDataEntry *entry) { static void assetsClearEntry(assetsDataEntry *entry) {
@ -34,61 +30,27 @@ static void assetsClearEntry(assetsDataEntry *entry) {
entry->size = 0; entry->size = 0;
entry->buffer = NULL; entry->buffer = NULL;
memset(entry, 0, sizeof(*entry));
} }
static int assetsLoadFile(unzFile zipf, assetsDataEntry *entry) { static void assetsSetPixelSize(assetsDataEntry *entry) {
int ret; switch (entry->imageMode) {
int filesize=0; case IMAGE_MODE_RGB24:
unz_file_info file_info; entry->pixSize = 3;
u8* buffer = NULL; break;
ret = unzLocateFile(zipf, entry->filename, 0); case IMAGE_MODE_RGBA32:
entry->pixSize = 4;
if (ret==UNZ_OK) ret = unzOpenCurrentFile(zipf); break;
if (ret==UNZ_OK) {
ret = unzGetCurrentFileInfo(zipf, &file_info, NULL, 0, NULL, 0, NULL, 0);
filesize = file_info.uncompressed_size;
if (filesize == 0) ret = -10;
if (ret==UNZ_OK) {
buffer = (u8*)malloc(filesize);
if (buffer) {
memset(buffer, 0, filesize);
} else {
ret = -11;
}
}
if (ret==UNZ_OK) {
ret = unzReadCurrentFile(zipf, buffer, filesize);
if(ret < filesize) {
ret = -12;
} else {
ret = UNZ_OK;
}
}
if (ret!=UNZ_OK && buffer!=NULL) free(buffer);
unzCloseCurrentFile(zipf);
} }
if (ret==UNZ_OK) {
entry->buffer = buffer;
entry->size = filesize;
}
return ret;
} }
Result assetsInit(void) { Result assetsInit(void) {
int ret=0; bool ret=false;
int i, stopi; int i, stopi;
unzFile zipf;
assetsDataEntry *entry = NULL; assetsDataEntry *entry = NULL;
char tmp_path[PATH_MAX+1]; char tmp_path[PATH_MAX];
if (g_assetsInitialized) return 0; if (g_assetsInitialized) return 0;
@ -105,65 +67,211 @@ Result assetsInit(void) {
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/romfs/assets.zip", menuGetRootBasePath()); snprintf(tmp_path, sizeof(tmp_path)-1, "%s/romfs/assets.zip", menuGetRootBasePath());
#endif #endif
zipf = unzOpen(tmp_path); if (PHYSFS_mount(tmp_path, "", 0)) {
if(zipf==NULL) { ret=true;
#ifdef __SWITCH__ for (i=0; i<AssetId_Max; i++) {
romfsExit(); stopi = i;
#endif entry = &g_assetsDataList[i][0];
if (entry->path[0]) {
return 0x80; ret = assetsLoadData(i, NULL, NULL);
} if (!ret) break;
}
for (i=0; i<AssetId_Max; i++) {
stopi = i;
entry = &g_assetsDataList[i];
ret = assetsLoadFile(zipf, entry);
if (ret!=UNZ_OK) break;
}
if (ret!=UNZ_OK) {
for (i=0; i<stopi; i++) {
assetsClearEntry(&g_assetsDataList[i]);
} }
if (!ret) {
for (i=0; i<stopi; i++) {
assetsClearEntry(&g_assetsDataList[i][0]);
}
}
if (ret) g_assetsInitialized = 1;
PHYSFS_unmount(tmp_path);
} }
if (ret==UNZ_OK) g_assetsInitialized = 1;
unzClose(zipf);
#ifdef __SWITCH__ #ifdef __SWITCH__
romfsExit(); romfsExit();
return ret ? 0 : MAKERESULT(Module_Libnx, LibnxError_IoError);
#else
return ret ? 0 : 1;
#endif #endif
}
void assetsExit(void) {
if (!g_assetsInitialized) return;
g_assetsInitialized = 0;
for (int i=0; i<AssetId_Max; i++) {
assetsClearEntry(&g_assetsDataList[i][0]);
}
assetsClearTheme();
}
void assetsClearTheme(void) {
for (int i=0; i<AssetId_Max; i++) {
assetsClearEntry(&g_assetsDataList[i][1]);
}
}
bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height) {
int w,h,samp;
tjhandle _jpegDecompressor = tjInitDecompress();
if (_jpegDecompressor == NULL) {
return false;
}
if (tjDecompressHeader2(_jpegDecompressor, indata, indata_size, &w, &h, &samp) == -1) {
tjDestroy(_jpegDecompressor);
return false;
}
if (w != width || h != height ) {
tjDestroy(_jpegDecompressor);
return false;
}
if (tjDecompress2(_jpegDecompressor, indata, indata_size, outdata, w, 0, h, imageMode == IMAGE_MODE_RGB24 ? TJPF_RGB : TJPF_RGBA, TJFLAG_ACCURATEDCT) == -1) {
tjDestroy(_jpegDecompressor);
return false;
}
tjDestroy(_jpegDecompressor);
return true;
}
bool assetsLoadPngFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height) {
png_image image;
bool ret=true;
memset(&image, 0, sizeof(image));
image.version = PNG_IMAGE_VERSION;
if (png_image_begin_read_from_memory(&image, indata, indata_size) != 0) {
if (image.width != width || image.height != height)
ret = false;
if (ret) image.format = imageMode == IMAGE_MODE_RGB24 ? PNG_FORMAT_RGB : PNG_FORMAT_RGBA;
if (ret && png_image_finish_read(&image, NULL, outdata, 0, NULL) == 0)
ret = false;
}
else
ret = false;
png_image_free(&image);
return ret; return ret;
} }
void assetsExit(void) { bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term) {
int i; bool ret=true;
*data_buf = NULL;
if (filesize) *filesize = 0;
if (!g_assetsInitialized) return; PHYSFS_Stat tmpstat={0};
g_assetsInitialized = 0; if (!(PHYSFS_stat(path, &tmpstat) && tmpstat.filesize!=-1)) ret = false;
for (i=0; i<AssetId_Max; i++) { if (ret) {
assetsClearEntry(&g_assetsDataList[i]); 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;
} }
void assetsGetData(AssetId id, u8 **buffer, size_t *size) { bool assetsLoadData(AssetId id, const char *path, int *imageSize) {
if (buffer) *buffer = NULL; if (id < 0 || id >= AssetId_Max) return false;
if (size) *size = 0;
assetsDataEntry *entry = &g_assetsDataList[id][path ? 1 : 0];
if (entry->initialized) return false;
if (path) memset(entry, 0, sizeof(*entry));
if (imageSize) {
entry->imageSize[0] = imageSize[0];
entry->imageSize[1] = imageSize[1];
}
if (path) entry->imageMode = g_assetsDataList[id][0].imageMode;
assetsSetPixelSize(entry);
entry->size = entry->imageSize[0] * entry->imageSize[1] * entry->pixSize;
if (path) strncpy(entry->path, path, sizeof(entry->path)-1);
const char* ext = getExtension(entry->path);
bool ret=true;
size_t filesize=0;
if (ext==NULL) ret = false;
u8 *data_buf = NULL;
if (ret) {
entry->buffer = (u8*)malloc(entry->size);
if (entry->buffer) memset(entry->buffer, 0, entry->size);
else ret = false;
}
if (ret) ret = assetsPhysfsReadFile(entry->path, &data_buf, &filesize, false);
if (ret) {
if (strcasecmp(ext, ".bin")==0) {
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, filesize, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]);
else if (strcasecmp(ext, ".png")==0)
ret = assetsLoadPngFromMemory(data_buf, filesize, 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; if (id < 0 || id >= AssetId_Max) return;
assetsDataEntry *entry = &g_assetsDataList[id]; u32 pos = g_assetsDataList[id][1].initialized ? 1 : 0;
assetsDataEntry *entry = &g_assetsDataList[id][pos];
if (buffer) *buffer = entry->buffer; if (entry->initialized) *out = entry;
if (size) *size = entry->size;
} }
u8 *assetsGetDataBuffer(AssetId id) { u8 *assetsGetDataBuffer(AssetId id) {
u8 *buffer = NULL; assetsDataEntry *entry = NULL;
assetsGetData(id, &buffer, NULL); assetsGetData(id, &entry);
return buffer; return entry ? entry->buffer : NULL;
} }

View File

@ -17,12 +17,28 @@ typedef enum {
AssetId_wifi3_icon, AssetId_wifi3_icon,
AssetId_eth_icon, AssetId_eth_icon,
AssetId_eth_none_icon, AssetId_eth_none_icon,
AssetId_background_image,
AssetId_Max AssetId_Max,
} AssetId; } 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); Result assetsInit(void);
void assetsExit(void); void assetsExit(void);
void assetsGetData(AssetId id, u8 **buffer, size_t *size); void assetsClearTheme(void);
bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term);
bool assetsLoadData(AssetId id, const char *path, int *imageSize);
void assetsGetData(AssetId id, assetsDataEntry **out);
u8 *assetsGetDataBuffer(AssetId id); u8 *assetsGetDataBuffer(AssetId id);
bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height);

View File

@ -21,8 +21,11 @@
#include <stdint.h> #include <stdint.h>
typedef uint8_t u8; typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32; typedef uint32_t u32;
typedef uint64_t u64; typedef uint64_t u64;
typedef int8_t s8;
typedef int32_t s32;
typedef u32 Result; typedef u32 Result;
typedef void (*workerThreadFunc)(void *); typedef void (*workerThreadFunc)(void *);
@ -45,6 +48,49 @@ typedef union {
}; };
} color_t; } color_t;
typedef enum
{
ThemeLayoutId_Logo,
ThemeLayoutId_HbmenuVersion,
ThemeLayoutId_LoaderInfo,
ThemeLayoutId_AttentionText,
ThemeLayoutId_LogInfo,
ThemeLayoutId_InfoMsg,
ThemeLayoutId_MenuPath,
ThemeLayoutId_MenuTypeMsg,
ThemeLayoutId_MsgBoxSeparator,
ThemeLayoutId_MsgBoxBottomText,
ThemeLayoutId_BackgroundImage,
ThemeLayoutId_BackWave,
ThemeLayoutId_MiddleWave,
ThemeLayoutId_FrontWave,
ThemeLayoutId_ButtonA,
ThemeLayoutId_ButtonAText,
ThemeLayoutId_ButtonB,
ThemeLayoutId_ButtonBText,
ThemeLayoutId_ButtonY,
ThemeLayoutId_ButtonYText,
ThemeLayoutId_ButtonM,
ThemeLayoutId_ButtonMText,
ThemeLayoutId_ButtonX,
ThemeLayoutId_ButtonXText,
ThemeLayoutId_NetworkIcon,
ThemeLayoutId_BatteryCharge,
ThemeLayoutId_BatteryIcon,
ThemeLayoutId_ChargingIcon,
ThemeLayoutId_Status,
ThemeLayoutId_Temperature,
ThemeLayoutId_MenuList,
ThemeLayoutId_MenuListTiles,
ThemeLayoutId_MenuListIcon,
ThemeLayoutId_MenuListName,
ThemeLayoutId_MenuActiveEntryIcon,
ThemeLayoutId_MenuActiveEntryName,
ThemeLayoutId_MenuActiveEntryAuthor,
ThemeLayoutId_MenuActiveEntryVersion,
ThemeLayoutId_Total,
} ThemeLayoutId;
// when building for pc we need to include these separately // when building for pc we need to include these separately
#ifndef __SWITCH__ #ifndef __SWITCH__
#include "switch/nro.h" #include "switch/nro.h"
@ -65,6 +111,7 @@ typedef union {
#include "power.h" #include "power.h"
#include "netloader.h" #include "netloader.h"
#include "netstatus.h" #include "netstatus.h"
#include "thermalstatus.h"
#include "status.h" #include "status.h"
void menuStartupPath(void); void menuStartupPath(void);
@ -160,6 +207,8 @@ static inline color_t FetchPixelColor(uint32_t x, uint32_t y)
void DrawPixel(uint32_t x, uint32_t y, color_t clr); void DrawPixel(uint32_t x, uint32_t y, color_t clr);
void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text); void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text);
void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text);
void DrawTextFromLayoutRelative(ThemeLayoutId id, int base_x, int base_y, int *inPos, int *outPos, color_t clr, const char* text, const char align);
void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text); void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text);
void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t* height_out); void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t* height_out);
uint32_t GetTextXCoordinate(u32 font, uint32_t rX, const char* text, const char align); uint32_t GetTextXCoordinate(u32 font, uint32_t rX, const char* text, const char align);

View File

@ -9,16 +9,20 @@
#define FONT_FACES_MAX 2 #define FONT_FACES_MAX 2
#endif #endif
#ifdef __SWITCH__
static bool s_plinited;
#endif
static FT_Error s_font_libret=1, s_font_facesret[FONT_FACES_MAX]; static FT_Error s_font_libret=1, s_font_facesret[FONT_FACES_MAX];
static FT_Library s_font_library; static FT_Library s_font_library;
static FT_Face s_font_faces[FONT_FACES_MAX]; static FT_Face s_font_faces[FONT_FACES_MAX];
static FT_Face s_font_lastusedface; static FT_Face s_font_lastusedface;
static size_t s_font_faces_total = 0; static s32 s_font_faces_total = 0;
static bool FontSetType(u32 font) static bool FontSetType(u32 font)
{ {
u32 i=0; s32 i=0;
u32 scale=0; u32 scale=0;
FT_Error ret=0; FT_Error ret=0;
@ -80,12 +84,12 @@ static bool FontSetType(u32 font)
static inline bool FontLoadGlyph(glyph_t* glyph, u32 font, uint32_t codepoint) static inline bool FontLoadGlyph(glyph_t* glyph, u32 font, uint32_t codepoint)
{ {
FT_Face face; FT_Face face=0;
FT_Error ret=0; FT_Error ret=0;
FT_GlyphSlot slot; FT_GlyphSlot slot;
FT_UInt glyph_index; FT_UInt glyph_index;
FT_Bitmap* bitmap; FT_Bitmap* bitmap;
u32 i=0; s32 i=0;
//__builtin_printf("LoadGlyph %u\n", (unsigned int)codepoint); //__builtin_printf("LoadGlyph %u\n", (unsigned int)codepoint);
/*const ffnt_page_t* page = FontGetPage(font, codepoint >> 8); /*const ffnt_page_t* page = FontGetPage(font, codepoint >> 8);
@ -276,6 +280,38 @@ void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text)
DrawText_(font, x, y, clr, text, 0, NULL); DrawText_(font, x, y, clr, text, 0, NULL);
} }
void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text)
{
ThemeLayoutObject *obj = &themeCurrent.layoutObjects[id];
if (!obj->visible) return;
DrawText(obj->font, obj->posStart[0], obj->posStart[1], clr, text);
}
void DrawTextFromLayoutRelative(ThemeLayoutId id, int base_x, int base_y, int *inPos, int *outPos, color_t clr, const char* text, const char align)
{
ThemeLayoutObject *obj = &themeCurrent.layoutObjects[id];
base_x = obj->posType ? base_x + inPos[0] : inPos[0];
base_y = obj->posType ? base_y + inPos[1] : inPos[1];
base_x = GetTextXCoordinate(obj->font, base_x, text, align);
if (outPos) {
outPos[0] = base_x;
outPos[1] = base_y;
}
obj->posFinal[0] = base_x;
obj->posFinal[1] = base_y;
if (!obj->visible) return;
GetTextDimensions(obj->font, text, &obj->textSize[0], &obj->textSize[1]);
DrawText(obj->font, base_x, base_y, clr, text);
}
void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text) void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text)
{ {
DrawText_(font, x, y, clr, text, max_width, end_text); DrawText_(font, x, y, clr, text, max_width, end_text);
@ -320,7 +356,7 @@ void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t
bool fontInitialize(void) bool fontInitialize(void)
{ {
FT_Error ret=0; FT_Error ret=0;
u32 i; s32 i;
for (i=0; i<FONT_FACES_MAX; i++) s_font_facesret[i] = 1; for (i=0; i<FONT_FACES_MAX; i++) s_font_facesret[i] = 1;
@ -332,7 +368,11 @@ bool fontInitialize(void)
PlFontData fonts[PlSharedFontType_Total]; PlFontData fonts[PlSharedFontType_Total];
Result rc=0; Result rc=0;
rc = plGetSharedFont(textGetLanguageCode(), fonts, FONT_FACES_MAX, &s_font_faces_total); rc = plInitialize(PlServiceType_User);
if (R_SUCCEEDED(rc)) {
s_plinited = true;
rc = plGetSharedFont(textGetLanguageCode(), fonts, FONT_FACES_MAX, &s_font_faces_total);
}
if (R_FAILED(rc)) return false; if (R_FAILED(rc)) return false;
for (i=0; i<s_font_faces_total; i++) { for (i=0; i<s_font_faces_total; i++) {
@ -372,12 +412,16 @@ bool fontInitialize(void)
void fontExit() void fontExit()
{ {
u32 i=0; s32 i=0;
for (i=0; i<s_font_faces_total; i++) for (i=0; i<s_font_faces_total; i++)
if (s_font_facesret[i]==0) FT_Done_Face(s_font_faces[i]); if (s_font_facesret[i]==0) FT_Done_Face(s_font_faces[i]);
if (s_font_libret==0) FT_Done_FreeType(s_font_library); if (s_font_libret==0) FT_Done_FreeType(s_font_library);
#ifdef __SWITCH__
if (s_plinited) plExit();
#endif
} }
/*Automatically gives you the desired x-coordinate /*Automatically gives you the desired x-coordinate

View File

@ -7,12 +7,12 @@
#define STR_DE(_str) [SetLanguage_DE] = _str #define STR_DE(_str) [SetLanguage_DE] = _str
#define STR_IT(_str) [SetLanguage_IT] = _str #define STR_IT(_str) [SetLanguage_IT] = _str
#define STR_ES(_str) [SetLanguage_ES] = _str, [SetLanguage_ES419] = _str #define STR_ES(_str) [SetLanguage_ES] = _str, [SetLanguage_ES419] = _str
#define STR_ZH(_str) [SetLanguage_ZHCN] = _str #define STR_ZH_HANS(_str) [SetLanguage_ZHCN] = _str, [SetLanguage_ZHHANS] = _str
#define STR_KO(_str) [SetLanguage_KO] = _str #define STR_KO(_str) [SetLanguage_KO] = _str
#define STR_NL(_str) [SetLanguage_NL] = _str #define STR_NL(_str) [SetLanguage_NL] = _str
#define STR_PT(_str) [SetLanguage_PT] = _str #define STR_PT(_str) [SetLanguage_PT] = _str
#define STR_RU(_str) [SetLanguage_RU] = _str #define STR_RU(_str) [SetLanguage_RU] = _str
#define STR_TW(_str) [SetLanguage_ZHTW] = _str #define STR_ZH_HANT(_str) [SetLanguage_ZHTW] = _str, [SetLanguage_ZHHANT] = _str
#else #else
#define STR_JP(_str) [0] = _str #define STR_JP(_str) [0] = _str
#define STR_EN(_str) [1] = _str #define STR_EN(_str) [1] = _str
@ -20,15 +20,15 @@
#define STR_DE(_str) [3] = _str #define STR_DE(_str) [3] = _str
#define STR_IT(_str) [4] = _str #define STR_IT(_str) [4] = _str
#define STR_ES(_str) [5] = _str #define STR_ES(_str) [5] = _str
#define STR_ZH(_str) [6] = _str #define STR_ZH_HANS(_str) [6] = _str
#define STR_KO(_str) [7] = _str #define STR_KO(_str) [7] = _str
#define STR_NL(_str) [8] = _str #define STR_NL(_str) [8] = _str
#define STR_PT(_str) [9] = _str #define STR_PT(_str) [9] = _str
#define STR_RU(_str) [10] = _str #define STR_RU(_str) [10] = _str
#define STR_TW(_str) [11] = _str #define STR_ZH_HANT(_str) [11] = _str
#endif #endif
const char* const g_strings[StrId_Max][16] = const char* const g_strings[StrId_Max][17] =
{ {
[StrId_Loading] = [StrId_Loading] =
{ {
@ -42,14 +42,16 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Laden…"), STR_NL("Laden…"),
STR_KO("로딩중…"), STR_KO("로딩중…"),
STR_RU("загрузка…"), STR_RU("загрузка…"),
STR_ZH("加载中…"), STR_ZH_HANS("加载中…"),
STR_TW("載中…"), STR_ZH_HANT("中…"),
}, },
[StrId_AppletMode] = [StrId_AppletMode] =
{ {
STR_EN("● Applet Mode ●"), STR_EN("● Applet Mode ●"),
STR_ES("● Modo Applet ●"), STR_ES("● Modo Applet ●"),
STR_FR("● Mode Applet ●"),
STR_ZH_HANS("● 小程序模式 ●"),
}, },
[StrId_Directory] = [StrId_Directory] =
@ -64,8 +66,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Map"), STR_NL("Map"),
STR_KO("디렉토리"), STR_KO("디렉토리"),
STR_RU("каталог"), STR_RU("каталог"),
STR_ZH("目录"), STR_ZH_HANS("目录"),
STR_TW("資料夾"), STR_ZH_HANT("資料夾"),
}, },
/*[StrId_DefaultLongTitle] = /*[StrId_DefaultLongTitle] =
@ -80,8 +82,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Homebrew toepassing"), STR_NL("Homebrew toepassing"),
STR_KO("홈브류 애플리케이션"), STR_KO("홈브류 애플리케이션"),
STR_RU("приложение хомебреw"), STR_RU("приложение хомебреw"),
STR_ZH("自制应用程序"), STR_ZH_HANS("自制应用程序"),
STR_TW("自製程式"), STR_ZH_HANT("自製程式"),
},*/ },*/
[StrId_DefaultPublisher] = [StrId_DefaultPublisher] =
@ -96,8 +98,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Auteur onbekend"), STR_NL("Auteur onbekend"),
STR_KO("알 수 없는 개발자"), STR_KO("알 수 없는 개발자"),
STR_RU("неизвестный автор"), STR_RU("неизвестный автор"),
STR_ZH("未知作者"), STR_ZH_HANS("未知作者"),
STR_TW("作者不詳"), STR_ZH_HANT("作者未知"),
}, },
[StrId_IOError] = [StrId_IOError] =
@ -106,14 +108,14 @@ const char* const g_strings[StrId_Max][16] =
STR_ES("Error de E/S"), STR_ES("Error de E/S"),
STR_DE("I/O-Fehler"), STR_DE("I/O-Fehler"),
STR_FR("Erreur d'E/S"), STR_FR("Erreur d'E/S"),
STR_IT("Errore di I/O"), STR_IT("Errore I/O"),
STR_JP("入出力エラー"), STR_JP("入出力エラー"),
STR_PT("Erro de E/S"), STR_PT("Erro de E/S"),
STR_NL("I/O Fout"), STR_NL("I/O Fout"),
STR_KO("입출력 오류"), STR_KO("입출력 오류"),
STR_RU("I/O-ошибка"), STR_RU("I/O-ошибка"),
STR_ZH("读写出错"), STR_ZH_HANS("读写出错"),
STR_TW("讀寫錯誤"), STR_ZH_HANT("取存錯誤"),
}, },
[StrId_CouldNotOpenFile] = [StrId_CouldNotOpenFile] =
@ -128,8 +130,15 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Kan bestand niet openen:\n%s"), STR_NL("Kan bestand niet openen:\n%s"),
STR_KO("파일을 열 수 없습니다:\n%s"), STR_KO("파일을 열 수 없습니다:\n%s"),
STR_RU("Не могу открыть файл:\n%s"), STR_RU("Не могу открыть файл:\n%s"),
STR_ZH("无法打开文件:\n%s"), STR_ZH_HANS("无法打开文件:\n%s"),
STR_TW("開啓檔案失敗:\n%s"), STR_ZH_HANT("無法開啟檔案:\n%s"),
},
[StrId_NroNotFound] =
{
STR_EN("Could not find executable: %s"),
STR_FR("Impossible trouver l'exécutable : %s"),
STR_ZH_HANS("找不到可执行文件"),
}, },
[StrId_NoAppsFound_Title] = [StrId_NoAppsFound_Title] =
@ -144,8 +153,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Geen toepassingen gevonden"), STR_NL("Geen toepassingen gevonden"),
STR_KO("애플리케이션을 찾을 수 없습니다"), STR_KO("애플리케이션을 찾을 수 없습니다"),
STR_RU("приложение не найдено"), STR_RU("приложение не найдено"),
STR_ZH("找不到可执行的自制程序"), STR_ZH_HANS("找不到可执行的自制程序"),
STR_TW("未能找到可執行的自製程式"), STR_ZH_HANT("沒有可執行的自製程式"),
}, },
[StrId_NoAppsFound_Msg] = [StrId_NoAppsFound_Msg] =
@ -205,15 +214,15 @@ const char* const g_strings[StrId_Max][16] =
"Убедитесь, что на карте SD есть каталог с\n" "Убедитесь, что на карте SD есть каталог с\n"
"названием switch и она содержит приложения." "названием switch и она содержит приложения."
), ),
STR_ZH( STR_ZH_HANS(
"内存卡找不到任何可执行的应用程序\n" "找不到任何自制程序(nro)\n"
"请在内存卡的根目录建立「switch」子目录\n" "在SD卡根目录建立“switch”文件夹\n"
"存放自制应用软件至该目录" "将自制程序(nro)放在其中"
), ),
STR_TW( STR_ZH_HANT(
"記憶體找不到任何可執行的應用程式。\n" "記憶卡內沒有可供執行的應用程式。\n"
"請在記憶體建立「switch」資料夾\n" "請在根目錄下建立「switch」資料夾\n"
"然後儲存自製軟體到此處" "並將自製軟體複製到switch資料夾內"
), ),
}, },
@ -223,16 +232,22 @@ const char* const g_strings[StrId_Max][16] =
STR_ES("La última aplicación devolvió un error:"), STR_ES("La última aplicación devolvió un error:"),
STR_DE("Die letzte Anwendung erzeugte einen Fehler:"), STR_DE("Die letzte Anwendung erzeugte einen Fehler:"),
STR_FR("La dernière application a retourné une erreur:"), STR_FR("La dernière application a retourné une erreur:"),
STR_IT("L'ultima applicazione ha restituito un errore:"),
STR_JP("直前に実行したアプリでエラーが発生しました:"), STR_JP("直前に実行したアプリでエラーが発生しました:"),
STR_KO("최근 애플리케이션에서 오류가 발생했습니다:"), STR_KO("최근 애플리케이션에서 오류가 발생했습니다:"),
STR_TW("程式執行時發生錯誤:"), STR_ZH_HANS("程序运行后出现错误:"),
STR_ZH_HANT("程式執行後出現錯誤:"),
}, },
[StrId_AppLaunchError] = [StrId_AppLaunchError] =
{ {
STR_EN("Failed to launch the application:"), STR_EN("Failed to launch the application:"),
STR_DE("Konnte die Anwendung nicht starten:"), STR_DE("Konnte die Anwendung nicht starten:"),
STR_FR("Erreur au lancement de l'application"), STR_FR("Erreur au lancement de l'application:"),
STR_IT("Errore nell'avvio dell'applicazione:"),
STR_ES("No se ha podido iniciar la aplicación:"),
STR_ZH_HANS("运行程序时发生错误:"),
STR_ZH_HANT("執行程式時發生錯誤:"),
}, },
[StrId_AppInfo_Author] = [StrId_AppInfo_Author] =
@ -247,8 +262,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Auteur"), STR_NL("Auteur"),
STR_KO("개발자"), STR_KO("개발자"),
STR_RU("автор"), STR_RU("автор"),
STR_ZH("作者"), STR_ZH_HANS("作者"),
STR_TW("作者"), STR_ZH_HANT("作者"),
}, },
[StrId_AppInfo_Version] = [StrId_AppInfo_Version] =
@ -263,8 +278,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Versie"), STR_NL("Versie"),
STR_KO("버전"), STR_KO("버전"),
STR_RU("Версия"), STR_RU("Версия"),
STR_ZH(""), STR_ZH_HANS(""),
STR_TW(""), STR_ZH_HANT(""),
}, },
[StrId_Actions_Launch] = [StrId_Actions_Launch] =
@ -272,15 +287,15 @@ const char* const g_strings[StrId_Max][16] =
STR_EN("Launch"), STR_EN("Launch"),
STR_ES("Lanzamiento"), STR_ES("Lanzamiento"),
STR_DE("Starten"), STR_DE("Starten"),
STR_FR("Lancement"), STR_FR("Lancer"),
STR_IT("Lanciare"), STR_IT("Avvia"),
STR_JP("起動"), STR_JP("起動"),
STR_PT("Lançamento"), STR_PT("Lançamento"),
STR_NL("Lancering"), STR_NL("Lancering"),
STR_KO("실행"), STR_KO("실행"),
STR_RU("запуск"), STR_RU("запуск"),
STR_ZH("发射"), STR_ZH_HANS("发射"),
STR_TW("啟動"), STR_ZH_HANT("啟動"),
}, },
[StrId_Actions_Open] = [StrId_Actions_Open] =
@ -289,14 +304,14 @@ const char* const g_strings[StrId_Max][16] =
STR_ES("Abrir"), STR_ES("Abrir"),
STR_DE("Öffnen"), STR_DE("Öffnen"),
STR_FR("Ouvrir"), STR_FR("Ouvrir"),
STR_IT("Aprire"), STR_IT("Apri"),
STR_JP("開く"), STR_JP("開く"),
STR_PT("Abrir"), STR_PT("Abrir"),
STR_NL("Open"), STR_NL("Open"),
STR_KO("열기"), STR_KO("열기"),
STR_RU("открыто"), STR_RU("открыто"),
STR_ZH("打开"), STR_ZH_HANS("打开"),
STR_TW("開啟"), STR_ZH_HANT("開啟"),
}, },
[StrId_Actions_Back] = [StrId_Actions_Back] =
@ -311,8 +326,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Terug"), STR_NL("Terug"),
STR_KO("뒤로 가기"), STR_KO("뒤로 가기"),
STR_RU("возвращаться"), STR_RU("возвращаться"),
STR_ZH(""), STR_ZH_HANS(""),
STR_TW(""), STR_ZH_HANT(""),
}, },
[StrId_MsgBox_OK] = [StrId_MsgBox_OK] =
@ -320,10 +335,12 @@ const char* const g_strings[StrId_Max][16] =
STR_EN("OK"), STR_EN("OK"),
STR_DE("OK"), STR_DE("OK"),
STR_FR("OK"), STR_FR("OK"),
STR_IT("OK"),
STR_ES("Aceptar"), STR_ES("Aceptar"),
STR_JP("了解"), STR_JP("了解"),
STR_KO("확인"), STR_KO("확인"),
STR_TW("確認"), STR_ZH_HANS("确认"),
STR_ZH_HANT("確認"),
}, },
[StrId_Actions_Apply] = [StrId_Actions_Apply] =
@ -332,43 +349,55 @@ const char* const g_strings[StrId_Max][16] =
STR_FR("Appliquer"), STR_FR("Appliquer"),
STR_DE("Anwenden"), STR_DE("Anwenden"),
STR_ES("Aplicar"), STR_ES("Aplicar"),
STR_IT("Applica"),
STR_JP("適用"), STR_JP("適用"),
STR_KO("적용"), STR_KO("적용"),
STR_TW("应用"), STR_ZH_HANS("应用"),
STR_ZH_HANT("套用"),
}, },
[StrId_Actions_Star] = [StrId_Actions_Star] =
{ {
STR_EN("Star"), STR_EN("Star"),
STR_ES("Agregar a favoritos"), STR_ES("Agregar a favoritos"),
STR_IT("Aggiungi ai preferiti"),
STR_FR("Ajouter aux favoris"),
STR_ZH_HANS("收藏"),
}, },
[StrId_Actions_Unstar] = [StrId_Actions_Unstar] =
{ {
STR_EN("Unstar"), STR_EN("Unstar"),
STR_ES("Borrar de favoritos"), STR_ES("Borrar de favoritos"),
STR_IT("Rimuovi dai preferiti"),
STR_FR("Retirer des favoris"),
STR_ZH_HANS("取消收藏"),
}, },
[StrId_ThemeMenu] = [StrId_ThemeMenu] =
{ {
STR_EN("Theme Menu"), STR_EN("Theme Menu"),
STR_FR("Menu Thème"), STR_FR("Menu thèmes"),
STR_DE("Theme Menü"), STR_DE("Theme Menü"),
STR_ES("Menú temático"), STR_ES("Menú temático"),
STR_IT("Tema Menu"),
STR_JP("テーマメニュー"), STR_JP("テーマメニュー"),
STR_KO("테마 메뉴"), STR_KO("테마 메뉴"),
STR_TW("主题菜单"), STR_ZH_HANS("主题菜单"),
STR_ZH_HANT("主題選單"),
}, },
[StrId_ThemeNotApplied] = [StrId_ThemeNotApplied] =
{ {
STR_EN("Theme cannot be applied because an error occurred."), STR_EN("Theme cannot be applied because an error occurred."),
STR_EN("Das Theme konnte nicht geladen werden, da ein Fehler aufgetreten ist."), STR_DE("Das Theme konnte nicht geladen werden, da ein Fehler aufgetreten ist."),
STR_FR("Le thème ne peut pas être appliqué car une erreur est survenue."), STR_FR("Le thème ne peut pas être appliqué car une erreur est survenue."),
STR_ES("El tema no se pudo aplicar porque se ha producido un error."), STR_ES("El tema no se pudo aplicar porque se ha producido un error."),
STR_IT("Il tema non è stato applicato a causa di un errore."),
STR_JP("エラーが発生したため、テーマを適用できませんでした。"), STR_JP("エラーが発生したため、テーマを適用できませんでした。"),
STR_KO("오류가 발생 했기 때문에 테마를 적용할 수 없습니다."), STR_KO("오류가 발생 했기 때문에 테마를 적용할 수 없습니다."),
STR_TW("由于发生错误, 无法应用主题。"), STR_ZH_HANS("由于发生错误, 无法应用主题。"),
STR_ZH_HANT("出現錯誤,無法套用主題。"),
}, },
[StrId_DefaultThemeName] = [StrId_DefaultThemeName] =
@ -376,6 +405,10 @@ const char* const g_strings[StrId_Max][16] =
STR_EN("Default Theme"), STR_EN("Default Theme"),
STR_FR("Thème par défaut"), STR_FR("Thème par défaut"),
STR_DE("Standard Theme"), STR_DE("Standard Theme"),
STR_IT("Tema di default"),
STR_ES("Tema por defecto"),
STR_ZH_HANS("默认主题"),
STR_ZH_HANT("預設主題"),
}, },
/*[StrId_Reboot] = /*[StrId_Reboot] =
@ -441,13 +474,13 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x80 Перезагрузите\n" " \xEE\x80\x80 Перезагрузите\n"
" \xEE\x80\x81 Отмена" " \xEE\x80\x81 Отмена"
), ),
STR_ZH( STR_ZH_HANS(
"无法返回至主机的 \xEE\x81\xB3HOME 菜单。\n" "无法返回至主机的 \xEE\x81\xB3HOME 菜单。\n"
"您需要重新启动您的 3DS 设备。\n\n" "您需要重新启动您的 3DS 设备。\n\n"
" \xEE\x80\x80 重启设备\n" " \xEE\x80\x80 重启设备\n"
" \xEE\x80\x81 取消操作" " \xEE\x80\x81 取消操作"
), ),
STR_TW( STR_ZH_HANT(
"無法返回至主機的 \xEE\x81\xB3HOME 選單。\n" "無法返回至主機的 \xEE\x81\xB3HOME 選單。\n"
"您需要重新啓動您的 3DS 設備。\n\n" "您需要重新啓動您的 3DS 設備。\n\n"
" \xEE\x80\x80 重啓設備\n" " \xEE\x80\x80 重啓設備\n"
@ -517,13 +550,13 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x81 Отмена\n" " \xEE\x80\x81 Отмена\n"
" \xEE\x80\x82 Перезагрузите" " \xEE\x80\x82 Перезагрузите"
), ),
STR_ZH( STR_ZH_HANS(
"您即将返回到主機的 \xEE\x81\xB3HOME 菜单。\n\n" "您即将返回到主機的 \xEE\x81\xB3HOME 菜单。\n\n"
" \xEE\x80\x80 确认返回\n" " \xEE\x80\x80 确认返回\n"
" \xEE\x80\x81 取消操作\n" " \xEE\x80\x81 取消操作\n"
" \xEE\x80\x82 重启设备" " \xEE\x80\x82 重启设备"
), ),
STR_TW( STR_ZH_HANT(
"您即將返回到主機的 \xEE\x81\xB3HOME 選單。\n\n" "您即將返回到主機的 \xEE\x81\xB3HOME 選單。\n\n"
" \xEE\x80\x80 確認返回\n" " \xEE\x80\x80 確認返回\n"
" \xEE\x80\x81 取消操作\n" " \xEE\x80\x81 取消操作\n"
@ -543,8 +576,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Titel selector"), STR_NL("Titel selector"),
STR_KO("타이틀 선택기"), STR_KO("타이틀 선택기"),
STR_RU("Селектор заголовков"), STR_RU("Селектор заголовков"),
STR_ZH("应用启动器"), STR_ZH_HANS("应用启动器"),
STR_TW("自製程式啓動器"), STR_ZH_HANT("自製程式啓動器"),
}, },
[StrId_ErrorReadingTitleMetadata] = [StrId_ErrorReadingTitleMetadata] =
@ -554,7 +587,7 @@ const char* const g_strings[StrId_Max][16] =
STR_DE("Fehler beim Lesen der Titel-Metadaten.\n%08lX%08lX@%d"), STR_DE("Fehler beim Lesen der Titel-Metadaten.\n%08lX%08lX@%d"),
STR_FR( STR_FR(
"Erreur lors de la lecture des métadonnées\n" "Erreur lors de la lecture des métadonnées\n"
"de titre.\n%08lX%08lX@%d" "du titre.\n%08lX%08lX@%d"
), ),
STR_IT("Errore nella lettura dei metadata dei titoli.\n%08lX%08lX@%d"), STR_IT("Errore nella lettura dei metadata dei titoli.\n%08lX%08lX@%d"),
STR_JP("タイトルメタデータを読み取ることができませんでした。\n%08lX%08lX@%d"), STR_JP("タイトルメタデータを読み取ることができませんでした。\n%08lX%08lX@%d"),
@ -562,8 +595,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Fout bij het lezen van titel metadata.\n%08lX%08lX@%d"), STR_NL("Fout bij het lezen van titel metadata.\n%08lX%08lX@%d"),
STR_KO("타이틀 메타데이터를 읽는데 실패하였습니다.\n%08lX%08lX@%d"), STR_KO("타이틀 메타데이터를 읽는데 실패하였습니다.\n%08lX%08lX@%d"),
STR_RU("Ошибка чтения метаданных заголовка\n.%08lX%08lX@%d"), STR_RU("Ошибка чтения метаданных заголовка\n.%08lX%08lX@%d"),
STR_ZH("读取软件相关信息时发生错误:\n%08lX%08lX@%d"), STR_ZH_HANS("读取软件相关信息时发生错误:\n%08lX%08lX@%d"),
STR_TW("讀取軟體相關數據時發生錯誤:\n%08lX%08lX@%d"), STR_ZH_HANT("讀取軟體相關資訊時發生錯誤:\n%08lX%08lX@%d"),
}, },
[StrId_NoTitlesFound] = [StrId_NoTitlesFound] =
@ -578,8 +611,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Geen titels gevonden."), STR_NL("Geen titels gevonden."),
STR_KO("타이틀을 찾을 수 없습니다."), STR_KO("타이틀을 찾을 수 없습니다."),
STR_RU("Заголовки не обнаружены"), STR_RU("Заголовки не обнаружены"),
STR_ZH("主机内找不到任何软件。"), STR_ZH_HANS("主机内找不到任何软件。"),
STR_TW("主機内找不到任何軟體。"), STR_ZH_HANT("主機内找不到任何軟體。"),
}, },
[StrId_SelectTitle] = [StrId_SelectTitle] =
@ -634,12 +667,12 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x80 Выберите\n" " \xEE\x80\x80 Выберите\n"
" \xEE\x80\x81 Отмена" " \xEE\x80\x81 Отмена"
), ),
STR_ZH( STR_ZH_HANS(
"请选择一个目标软件。\n\n" "请选择一个目标软件。\n\n"
" \xEE\x80\x80 确认\n" " \xEE\x80\x80 确认\n"
" \xEE\x80\x81 取消" " \xEE\x80\x81 取消"
), ),
STR_TW( STR_ZH_HANT(
"請選擇一個目標軟體。\n\n" "請選擇一個目標軟體。\n\n"
" \xEE\x80\x80 確認\n" " \xEE\x80\x80 確認\n"
" \xEE\x80\x81 取消" " \xEE\x80\x81 取消"
@ -698,14 +731,14 @@ const char* const g_strings[StrId_Max][16] =
"приложений под целевыми заголовками.\n" "приложений под целевыми заголовками.\n"
"Пожалуйста, используйте другой эксплойт." "Пожалуйста, используйте другой эксплойт."
), ),
STR_ZH( STR_ZH_HANS(
"您所利用漏洞启动的「自制软件启动器」,\n" "您所利用漏洞启动的「自制软件启动器」,\n"
"无法在当前选中的软件中启动自制软件。\n" "无法在当前选中的软件中启动自制软件。\n"
"请使用其它的漏洞来启动「自制软件启动器」。" "请使用其它的漏洞来启动「自制软件启动器」。"
), ),
STR_TW( STR_ZH_HANT(
"您所利用漏洞開啓的「自製軟體啓動器」\n" "您所利用漏洞開啓的「自製軟體啓動器」\n"
"無法在當前選中的軟體啓動自製軟\n" "無法在當前選中的軟體啓動自製軟\n"
"請利用其它漏洞來啓動「自製軟體啓動器」。" "請利用其它漏洞來啓動「自製軟體啓動器」。"
), ),
}, },
@ -752,11 +785,11 @@ const char* const g_strings[StrId_Max][16] =
"Для приложения требуется зависимость,\n" "Для приложения требуется зависимость,\n"
"которая не установлена." "которая не установлена."
), ),
STR_ZH( STR_ZH_HANS(
"主机找不到该应用程序\n" "主机找不到该应用程序\n"
"所需求的软件。" "所需求的软件。"
), ),
STR_TW( STR_ZH_HANT(
"主機找不到該應用程式\n" "主機找不到該應用程式\n"
"所需求的軟體。" "所需求的軟體。"
), ),
@ -767,15 +800,15 @@ const char* const g_strings[StrId_Max][16] =
STR_EN("NetLoader"), STR_EN("NetLoader"),
STR_ES("Cargador de programas"), STR_ES("Cargador de programas"),
STR_DE("Netzwerk-Loader"), STR_DE("Netzwerk-Loader"),
STR_FR("Chargeur de programme"), STR_FR("NetLoader"),
STR_IT("Caricamento programmi"), STR_IT("Caricamento programmi"),
STR_JP("ネットローダ"), STR_JP("ネットローダ"),
STR_PT("Carregador de programas"), STR_PT("Carregador de programas"),
STR_NL("netwerk lader"), STR_NL("netwerk lader"),
STR_KO("네트워크 로더"), STR_KO("네트워크 로더"),
STR_RU("Загрузчик"), STR_RU("Загрузчик"),
STR_ZH("网络执行模块"), STR_ZH_HANS("网络执行模块"),
STR_TW("網路執行模組"), STR_ZH_HANT("網路執行模組"),
}, },
[StrId_NetLoaderUnavailable] = [StrId_NetLoaderUnavailable] =
@ -783,15 +816,15 @@ const char* const g_strings[StrId_Max][16] =
STR_EN("The NetLoader is currently unavailable."), STR_EN("The NetLoader is currently unavailable."),
STR_ES("El cargador de programas no está disponible."), STR_ES("El cargador de programas no está disponible."),
STR_DE("Der Netzwerk-Loader ist zur Zeit nicht verfügbar."), STR_DE("Der Netzwerk-Loader ist zur Zeit nicht verfügbar."),
STR_FR("Le chargeur de programme nxlink est indisponible."), STR_FR("Le programme nxlink est indisponible."),
STR_IT("Il caricamento programmi nxlink non è disponibile."), STR_IT("Il caricamento programmi nxlink non è disponibile."),
STR_JP("nxlinkネットローダは現在利用できません。"), STR_JP("nxlinkネットローダは現在利用できません。"),
STR_PT("O carregador de programas está de momento indisponível."), STR_PT("O carregador de programas está de momento indisponível."),
STR_NL("De netwerk lader is niet beschikbaar."), STR_NL("De netwerk lader is niet beschikbaar."),
STR_KO("현재 네트워크 로더는 사용이 불가합니다."), STR_KO("현재 네트워크 로더는 사용이 불가합니다."),
STR_RU("Загрузчик в настоящее время недоступен."), STR_RU("Загрузчик в настоящее время недоступен."),
STR_ZH("无法启动 nxlink 网络执行模块。"), STR_ZH_HANS("无法启动 nxlink 网络执行模块。"),
STR_TW("無法啓動 nxlink 網路執行模組。"), STR_ZH_HANT("無法啓動 nxlink 網路執行模組。"),
}, },
[StrId_NetLoaderError] = [StrId_NetLoaderError] =
@ -806,8 +839,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Er is een fout opgetreden\nTechnische details: [%s:%d]"), STR_NL("Er is een fout opgetreden\nTechnische details: [%s:%d]"),
STR_KO("오류가 발생했습니다.\n기술적인 세부사항: [%s:%d]"), STR_KO("오류가 발생했습니다.\n기술적인 세부사항: [%s:%d]"),
STR_RU("Произошла ошибка.\nТехнические подробности: [%s:%d]"), STR_RU("Произошла ошибка.\nТехнические подробности: [%s:%d]"),
STR_ZH("发生错误。\n详细错误信息:[%s:%d]"), STR_ZH_HANS("发生错误。\n详细错误信息:[%s:%d]"),
STR_TW("發生錯誤。\n詳細錯誤資訊:[%s:%d]"), STR_ZH_HANT("發生錯誤。\n詳細錯誤資訊:[%s:%d]"),
}, },
[StrId_NetLoaderOffline] = [StrId_NetLoaderOffline] =
@ -816,10 +849,11 @@ const char* const g_strings[StrId_Max][16] =
STR_DE("Offline, warte auf Netzwerk…"), STR_DE("Offline, warte auf Netzwerk…"),
STR_FR("Hors-ligne, en attente d'une connection..."), STR_FR("Hors-ligne, en attente d'une connection..."),
STR_IT("Disconnesso, in attesa della connessione…"), STR_IT("Disconnesso, in attesa della connessione…"),
STR_ES("Desconectado, esperando a la red..."),
STR_JP("オフラインです。ネットワーク接続を待っています…"), STR_JP("オフラインです。ネットワーク接続を待っています…"),
STR_KO("연결 끊김, 네트워크 기다리는 중…"), STR_KO("연결 끊김, 네트워크 기다리는 중…"),
STR_ZH("无法连接网络,等待网络连接…"), STR_ZH_HANS("无法连接网络,等待网络连接…"),
STR_TW("當前離線,等待網路連線…"), STR_ZH_HANT("目前已離線,等待網路連線…"),
}, },
[StrId_NetLoaderActive] = [StrId_NetLoaderActive] =
@ -864,11 +898,11 @@ const char* const g_strings[StrId_Max][16] =
"Ожидание подключения nxlink…\n" "Ожидание подключения nxlink…\n"
"айпи адрес: %lu.%lu.%lu.%lu, Порт: %d" "айпи адрес: %lu.%lu.%lu.%lu, Порт: %d"
), ),
STR_ZH( STR_ZH_HANS(
"等待 nxlink 连接…\n" "等待 nxlink 连接…\n"
"IP 地址:%lu.%lu.%lu.%lu端口%d" "IP 地址:%lu.%lu.%lu.%lu端口%d"
), ),
STR_TW( STR_ZH_HANT(
"等待 nxlink 連接…\n" "等待 nxlink 連接…\n"
"IP 位址:%lu.%lu.%lu.%lu連接埠%d" "IP 位址:%lu.%lu.%lu.%lu連接埠%d"
), ),
@ -916,14 +950,13 @@ const char* const g_strings[StrId_Max][16] =
"Передача…\n" "Передача…\n"
"%zu из %zu КИБ написано" "%zu из %zu КИБ написано"
), ),
STR_ZH( STR_ZH_HANS(
"正在传输…\n" "正在传输…\n"
"已完成 %zu / %zu KiB" "已完成 %zu / %zu KiB"
), ),
STR_TW( STR_ZH_HANT(
"正在傳輸…\n" "正在傳輸…\n"
"已完成 %zu / %zu KiB" "已完成 %zu / %zu KiB"
), ),
}, },
}; };

View File

@ -11,6 +11,7 @@ typedef enum
StrId_DefaultPublisher, StrId_DefaultPublisher,
StrId_IOError, StrId_IOError,
StrId_CouldNotOpenFile, StrId_CouldNotOpenFile,
StrId_NroNotFound,
StrId_NoAppsFound_Title, StrId_NoAppsFound_Title,
StrId_NoAppsFound_Msg, StrId_NoAppsFound_Msg,
@ -54,5 +55,5 @@ typedef enum
StrId_Max, StrId_Max,
} StrId; } StrId;
extern const char* const g_strings[StrId_Max][16]; extern const char* const g_strings[StrId_Max][17];

View File

@ -1,4 +1,5 @@
#include "common.h" #include "common.h"
#include <physfs.h>
void menuEntryInit(menuEntry_s* me, MenuEntryType type) { void menuEntryInit(menuEntry_s* me, MenuEntryType type) {
memset(me, 0, sizeof(*me)); memset(me, 0, sizeof(*me));
@ -80,9 +81,13 @@ static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) {
return ok; 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; struct stat st;
if (data_source) {
return assetsPhysfsReadFile(path, &me->icon, &me->icon_size, false);
}
if(stat(path, &st)==-1) return false; if(stat(path, &st)==-1) return false;
FILE* f = fopen(path, "rb"); FILE* f = fopen(path, "rb");
@ -106,12 +111,15 @@ static bool menuEntryImportIconGfx(menuEntry_s* me, uint8_t* icon_gfx, uint8_t*
if (icon_gfx == NULL || icon_gfx_small == NULL) return false; 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->size[0]*layoutobj->size[1]*3;
me->icon_gfx = (uint8_t*)malloc(tmpsize); me->icon_gfx = (uint8_t*)malloc(tmpsize);
if (me->icon_gfx) memcpy(me->icon_gfx, icon_gfx, tmpsize); if (me->icon_gfx) memcpy(me->icon_gfx, icon_gfx, tmpsize);
if (me->icon_gfx) { if (me->icon_gfx) {
tmpsize = 140*140*3; tmpsize = layoutobj2->size[0]*layoutobj2->size[1]*3;
me->icon_gfx_small = (uint8_t*)malloc(tmpsize); me->icon_gfx_small = (uint8_t*)malloc(tmpsize);
if (me->icon_gfx_small) memcpy(me->icon_gfx_small, icon_gfx_small, tmpsize); if (me->icon_gfx_small) memcpy(me->icon_gfx_small, icon_gfx_small, tmpsize);
@ -202,19 +210,19 @@ static bool menuEntryLoadExternalNacp(menuEntry_s* me, const char* path) {
} while (lastc); } while (lastc);
}*/ }*/
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) { bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists) {
int i=0, tmplen; int i=0, tmplen;
menu_s *menu_fileassoc = menuFileassocGetCurrent(); menu_s *menu_fileassoc = menuFileassocGetCurrent();
menuEntry_s* fileassoc_me = NULL; menuEntry_s* fileassoc_me = NULL;
char *strptr = NULL; char *strptr = NULL;
static char tempbuf[PATH_MAX+1]; char tempbuf[PATH_MAX+16];
//bool isOldAppFolder = false; //bool isOldAppFolder = false;
if (!fsobjExists(me->path)) return false; if (check_exists && !fsobjExists(me->path)) return false;
tempbuf[PATH_MAX] = 0; tempbuf[PATH_MAX] = 0;
strcpy(me->name, name); strncpy(me->name, name, sizeof(me->name)-1);
if (me->type == ENTRY_TYPE_FOLDER) if (me->type == ENTRY_TYPE_FOLDER)
{ {
//Check for <dirpath>/<dirname>.nro //Check for <dirpath>/<dirname>.nro
@ -285,9 +293,8 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
if (me->type == ENTRY_TYPE_FILE) if (me->type == ENTRY_TYPE_FILE)
{ {
//strcpy(me->name, name);//This is already done before both if statements strncpy(me->author, textGetString(StrId_DefaultPublisher), sizeof(me->author)-1);
strcpy(me->author, textGetString(StrId_DefaultPublisher)); strncpy(me->version, "1.0.0", sizeof(me->version)-1);
strcpy(me->version, "1.0.0");
//shortcut_s sc; //shortcut_s sc;
@ -324,7 +331,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
char* ext = getExtension(tempbuf); char* ext = getExtension(tempbuf);
strcpy(ext, ".jpg"); strcpy(ext, ".jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf); iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) break; if (iconLoaded) break;
if (isOldAppFolder) if (isOldAppFolder)
@ -332,7 +339,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
char* slash = getSlash(tempbuf); char* slash = getSlash(tempbuf);
strcpy(slash, "/icon.jpg"); strcpy(slash, "/icon.jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf); iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) break; if (iconLoaded) break;
}*/ }*/
@ -404,8 +411,45 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
const char *name, const char *name,
*author = textGetString(StrId_DefaultPublisher), *author = textGetString(StrId_DefaultPublisher),
*version = "1.0.0"; *version = "1.0.0";
if (config_read_file(&cfg, me->path)) { const char* cfg_path = me->path;
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 (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"); themeInfo = config_lookup(&cfg, "themeInfo");
if (themeInfo != NULL) { if (themeInfo != NULL) {
if(config_setting_lookup_string(themeInfo, "name", &name)) if(config_setting_lookup_string(themeInfo, "name", &name))
@ -418,6 +462,20 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
strncpy(me->author, author, sizeof(me->author)-1); strncpy(me->author, author, sizeof(me->author)-1);
strncpy(me->version, version, sizeof(me->version)-1); strncpy(me->version, version, sizeof(me->version)-1);
config_destroy(&cfg); config_destroy(&cfg);
if (good_cfg && is_archive) {
bool iconLoaded = false;
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
} }
if (me->type == ENTRY_TYPE_FILE_OTHER) if (me->type == ENTRY_TYPE_FILE_OTHER)
@ -449,7 +507,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
bool iconLoaded = false; bool iconLoaded = false;
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf); iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) menuEntryParseIcon(me); if (iconLoaded) menuEntryParseIcon(me);
@ -478,9 +536,28 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
// Initialize the argument data // Initialize the argument data
argData_s* ad = &me->args; argData_s* ad = &me->args;
argData_s* ad_assoc = &fileassoc_me->args;
char *arg_src = (char*)&ad_assoc->buf[1];
bool ftoken_found=0;
ad->dst = (char*)&ad->buf[1]; ad->dst = (char*)&ad->buf[1];
launchAddArg(ad, fileassoc_me->path);
launchAddArg(ad, me->path); for (u32 argi=0; argi<ad_assoc->buf[0]; argi++, arg_src+= strlen(arg_src)+1) {
if (argi) {
strptr = strchr(arg_src, '%');
if (strptr && strptr[0] && strptr[1] && (strptr == arg_src || strptr[-1] != '\\')) {
if (strptr[1] == 'f') {
memset(tempbuf, 0, sizeof(tempbuf));
snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s%s%s", (int)((uintptr_t)strptr-(uintptr_t)arg_src), arg_src, me->path, &strptr[2]);
launchAddArg(ad, tempbuf);
ftoken_found = 1;
continue;
}
}
}
launchAddArg(ad, arg_src);
}
if (!ftoken_found) launchAddArg(ad, me->path);
strncpy(me->path, fileassoc_me->path, sizeof(me->path)); strncpy(me->path, fileassoc_me->path, sizeof(me->path));
me->path[sizeof(me->path)-1] = 0; me->path[sizeof(me->path)-1] = 0;
@ -501,12 +578,12 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
} }
void menuEntryFileassocLoad(const char* filepath) { void menuEntryFileassocLoad(const char* filepath) {
bool success=0, success2=0; bool success=0, iconLoaded=0;
menuEntry_s* me = NULL; menuEntry_s* me = NULL;
config_setting_t *fileassoc = NULL, *targets = NULL, *target = NULL; config_setting_t *fileassoc = NULL, *targets = NULL, *target = NULL, *app_args = NULL, *target_args = NULL;
config_t cfg = {0}; config_t cfg = {0};
int targets_len=0, i; int targets_len=0, args_len=0, i;
const char *strptr = NULL; const char *strptr = NULL;
char app_path[PATH_MAX+8]; char app_path[PATH_MAX+8];
@ -515,8 +592,8 @@ void menuEntryFileassocLoad(const char* filepath) {
char target_file_extension[PATH_MAX+1]; char target_file_extension[PATH_MAX+1];
char target_filename[PATH_MAX+1]; char target_filename[PATH_MAX+1];
char app_author[ENTRY_AUTHORLENGTH+1]; char app_author[ENTRY_AUTHORLENGTH+2];
char app_version[ENTRY_VERLENGTH+1]; char app_version[ENTRY_VERLENGTH+2];
uint8_t *app_icon_gfx = NULL; uint8_t *app_icon_gfx = NULL;
uint8_t *app_icon_gfx_small = NULL; uint8_t *app_icon_gfx_small = NULL;
@ -538,7 +615,8 @@ void menuEntryFileassocLoad(const char* filepath) {
snprintf(app_path, sizeof(app_path)-1, "%s%s", menuGetRootBasePath(), strptr); snprintf(app_path, sizeof(app_path)-1, "%s%s", menuGetRootBasePath(), strptr);
if (config_setting_lookup_string(fileassoc, "icon_path", &strptr)) if (config_setting_lookup_string(fileassoc, "icon_path", &strptr))
snprintf(main_icon_path, sizeof(main_icon_path)-1, "%s%s", menuGetRootBasePath(), strptr); snprintf(main_icon_path, sizeof(main_icon_path)-1, "%s%s", menuGetRootBasePath(), strptr);
targets = config_setting_get_member(fileassoc, "targets"); app_args = config_setting_lookup(fileassoc, "app_args");
targets = config_setting_lookup(fileassoc, "targets");
if (app_path[0] && targets) { if (app_path[0] && targets) {
targets_len = config_setting_length(targets); targets_len = config_setting_length(targets);
@ -554,7 +632,7 @@ void menuEntryFileassocLoad(const char* filepath) {
strptr = getSlash(app_path); strptr = getSlash(app_path);
if(strptr[0] == '/') strptr++; if(strptr[0] == '/') strptr++;
if (menuEntryLoad(me, strptr, 0)) { if (menuEntryLoad(me, strptr, 0, true)) {
strncpy(app_author, me->author, sizeof(app_author)); strncpy(app_author, me->author, sizeof(app_author));
app_author[sizeof(app_author)-1] = 0; app_author[sizeof(app_author)-1] = 0;
strncpy(app_version, me->version, sizeof(app_version)); strncpy(app_version, me->version, sizeof(app_version));
@ -584,12 +662,13 @@ void menuEntryFileassocLoad(const char* filepath) {
strncpy(target_file_extension, strptr, sizeof(target_file_extension)-1); strncpy(target_file_extension, strptr, sizeof(target_file_extension)-1);
if (config_setting_lookup_string(target, "filename", &strptr)) if (config_setting_lookup_string(target, "filename", &strptr))
strncpy(target_filename, strptr, sizeof(target_filename)-1); strncpy(target_filename, strptr, sizeof(target_filename)-1);
target_args = config_setting_lookup(target, "app_args");
//string_is_set for target_file_extension and target_filename must differ: only 1 can be set, not both set or both not set. //string_is_set for target_file_extension and target_filename must differ: only 1 can be set, not both set or both not set.
if ((target_file_extension[0]!=0) == (target_filename[0]!=0)) continue; if ((target_file_extension[0]!=0) == (target_filename[0]!=0)) continue;
me = menuCreateEntry(ENTRY_TYPE_FILEASSOC); me = menuCreateEntry(ENTRY_TYPE_FILEASSOC);
success2 = 0; iconLoaded = 0;
if (me) { if (me) {
strncpy(me->path, app_path, sizeof(me->path)); strncpy(me->path, app_path, sizeof(me->path));
@ -608,22 +687,32 @@ void menuEntryFileassocLoad(const char* filepath) {
} }
me->fileassoc_str[sizeof(me->fileassoc_str)-1] = 0; me->fileassoc_str[sizeof(me->fileassoc_str)-1] = 0;
if (target_icon_path[0]) success2 = menuEntryLoadExternalIcon(me, target_icon_path); if (target_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, target_icon_path, false);
if (!success2 && main_icon_path[0]) success2 = menuEntryLoadExternalIcon(me, main_icon_path); if (!iconLoaded && main_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, main_icon_path, false);
if (success2) { if (iconLoaded) {
menuEntryParseIcon(me); menuEntryParseIcon(me);
} else { } else {
success2 = menuEntryImportIconGfx(me, app_icon_gfx, app_icon_gfx_small); iconLoaded = menuEntryImportIconGfx(me, app_icon_gfx, app_icon_gfx_small);
}
argData_s* ad = &me->args;
ad->dst = (char*)&ad->buf[1];
launchAddArg(ad, me->path);
config_setting_t *config_args = target_args ? target_args : app_args;
if (config_args) {
args_len = config_setting_length(config_args);
for (int argi=0; argi<args_len; argi++) {
strptr = config_setting_get_string_elem(config_args, argi);
if (strptr==NULL) continue;
launchAddArg(ad, strptr);
}
} }
} }
if (me) { if (me) menuFileassocAddEntry(me);
if (success2)
menuFileassocAddEntry(me);
else
menuDeleteEntry(me, 0);
}
} }
} }
} }
@ -642,65 +731,33 @@ void menuEntryFileassocLoad(const char* filepath) {
void menuEntryParseIcon(menuEntry_s* me) { void menuEntryParseIcon(menuEntry_s* me) {
if (me->icon_size==0 || me->icon==NULL) return; if (me->icon_size==0 || me->icon==NULL) return;
int w,h,samp; ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
size_t imagesize = 256*256*3; ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
me->icon_gfx = (uint8_t*)malloc(imagesize);
if (me->icon_gfx == NULL) { size_t imagesize = layoutobj->imageSize[0]*layoutobj->imageSize[1]*3;
me->icon_size = 0; bool ret=true;
free(me->icon); uint8_t *tmp_gfx = (uint8_t*)malloc(imagesize);
me->icon = NULL;
return;
}
tjhandle _jpegDecompressor = tjInitDecompress(); if (tmp_gfx == NULL) ret = false;
if (_jpegDecompressor == NULL) { if (ret) ret = assetsLoadJpgFromMemory(me->icon, me->icon_size, tmp_gfx, IMAGE_MODE_RGB24, layoutobj->imageSize[0], layoutobj->imageSize[1]);
free(me->icon_gfx);
me->icon_gfx = NULL;
me->icon_size = 0; if (ret) me->icon_gfx = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], IMAGE_MODE_RGB24);
free(me->icon);
me->icon = NULL;
return;
}
if (tjDecompressHeader2(_jpegDecompressor, me->icon, me->icon_size, &w, &h, &samp) == -1) { if (ret && me->icon_gfx==NULL) ret = false;
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;
}
me->icon_size = 0; me->icon_size = 0;
free(me->icon); free(me->icon);
me->icon = NULL; me->icon = NULL;
tjDestroy(_jpegDecompressor); if (ret) me->icon_gfx_small = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], IMAGE_MODE_RGB24);
me->icon_gfx_small = downscaleImg(me->icon_gfx, 256, 256, 140, 140, IMAGE_MODE_RGB24); if (!ret || me->icon_gfx_small == NULL) {
if (me->icon_gfx_small == NULL) {
free(me->icon_gfx); free(me->icon_gfx);
me->icon_gfx = NULL; me->icon_gfx = NULL;
} }
free(tmp_gfx);
} }
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode) { uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode) {
@ -720,6 +777,11 @@ uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int des
return NULL; return NULL;
} }
if (srcWidth == destWidth && srcHeight == destHeight) {
memcpy(out, image, destWidth*destHeight*(mode==IMAGE_MODE_RGBA32 ? 4 : 3));
return out;
}
int tmpx, tmpy; int tmpx, tmpy;
int pos; int pos;
float sourceX, sourceY; float sourceX, sourceY;
@ -826,7 +888,7 @@ void menuEntryParseNacp(menuEntry_s* me) {
if (me->nacp==NULL) return; if (me->nacp==NULL) return;
strncpy(me->version, me->nacp->version, sizeof(me->version)-1); strncpy(me->version, me->nacp->display_version, sizeof(me->version)-1);
#ifdef __SWITCH__ #ifdef __SWITCH__
Result rc=0; Result rc=0;

View File

@ -35,6 +35,7 @@ static void _menuAddEntry(menu_s *m, menuEntry_s* me) {
m->lastEntry = me; m->lastEntry = me;
} }
m->xPos = 0; m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++; m->nEntries ++;
} }
@ -59,6 +60,7 @@ static void menuAddEntryToFront(menuEntry_s* me) {
m->lastEntry = me; m->lastEntry = me;
} }
m->xPos = 0; m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++; m->nEntries ++;
} }
@ -176,11 +178,10 @@ int menuScan(const char* target) {
memset(tmp_path, 0, sizeof(tmp_path)); memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s", s_menu[!s_curMenu].dirname, dirsep, dp->d_name); snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s", s_menu[!s_curMenu].dirname, dirsep, dp->d_name);
#ifdef __SWITCH__ #ifdef _DIRENT_HAVE_D_TYPE
fsdev_dir_t* dirSt = (fsdev_dir_t*)dir->dirData->dirStruct; if (dp->d_type == DT_UNKNOWN)
FsDirectoryEntry* entry = &dirSt->entry_data[dirSt->index]; continue;
entrytype = dp->d_type != DT_REG;
entrytype = entry->type == ENTRYTYPE_DIR;
#else #else
struct stat tmpstat; struct stat tmpstat;
@ -208,7 +209,7 @@ int menuScan(const char* target) {
strncpy(me->path, tmp_path, sizeof(me->path)-1); strncpy(me->path, tmp_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0; me->path[sizeof(me->path)-1] = 0;
if (menuEntryLoad(me, dp->d_name, shortcut)) if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me); menuAddEntry(me);
else else
menuDeleteEntry(me, 0); menuDeleteEntry(me, 0);
@ -245,8 +246,23 @@ int themeMenuScan(const char* target) {
memset(tmp_path, 0, sizeof(tmp_path)); memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name); 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); const char* ext = getExtension(dp->d_name);
if (strcasecmp(ext, ".cfg")==0) if (entrytype || strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0 || strcasecmp(ext, ".zip")==0)
me = menuCreateEntry(ENTRY_TYPE_THEME); me = menuCreateEntry(ENTRY_TYPE_THEME);
if (!me) if (!me)
@ -254,7 +270,7 @@ int themeMenuScan(const char* target) {
strncpy(me->path, tmp_path, sizeof(me->path)-1); strncpy(me->path, tmp_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0; me->path[sizeof(me->path)-1] = 0;
if (menuEntryLoad(me, dp->d_name, shortcut)) if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me); menuAddEntry(me);
else else
menuDeleteEntry(me, 0); menuDeleteEntry(me, 0);
@ -266,7 +282,7 @@ int themeMenuScan(const char* target) {
menuEntry_s* me = menuCreateEntry(ENTRY_TYPE_THEME); menuEntry_s* me = menuCreateEntry(ENTRY_TYPE_THEME);
if(me) { if(me) {
if(menuEntryLoad(me, textGetString(StrId_DefaultThemeName), false))//Create Default theme Menu Entry if(menuEntryLoad(me, textGetString(StrId_DefaultThemeName), false, false))//Create Default theme Menu Entry
menuAddEntryToFront(me); menuAddEntryToFront(me);
else else
menuDeleteEntry(me, 0); menuDeleteEntry(me, 0);

View File

@ -6,10 +6,19 @@
#include "switch/runtime/nxlink.h" #include "switch/runtime/nxlink.h"
#endif #endif
double menuTimer;
char rootPathBase[PATH_MAX]; char rootPathBase[PATH_MAX];
char rootPath[PATH_MAX+8]; char rootPath[PATH_MAX+8];
uint8_t *folder_icon_large, *folder_icon_small;
uint8_t *invalid_icon_large, *invalid_icon_small;
uint8_t *theme_icon_large, *theme_icon_small;
void computeFrontGradient(color_t baseColor, int height); void computeFrontGradient(color_t baseColor, int height);
void menuLoadFileassoc(void);
char *menuGetRootPath(void) { char *menuGetRootPath(void) {
return rootPath; return rootPath;
} }
@ -113,12 +122,63 @@ void menuHandleXButton(void) {
} }
} }
void menuStartupCommon(void) {
free(folder_icon_large);
free(folder_icon_small);
free(invalid_icon_large);
free(invalid_icon_small);
free(theme_icon_large);
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_large = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], data->imageMode);
folder_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], data->imageMode);
assetsGetData(AssetId_invalid_icon, &data);
invalid_icon_large = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], data->imageMode);
invalid_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], data->imageMode);
if(themeGlobalPreset == THEME_PRESET_DARK)
assetsGetData(AssetId_theme_icon_dark, &data);
else
assetsGetData(AssetId_theme_icon_light, &data);
theme_icon_large = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], data->imageMode);
theme_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], data->imageMode);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
computeFrontGradient(themeCurrent.frontWaveColor, layoutobj->size[1]);
}
void menuThemeSelectCurrentEntry(void) {
menu_s* menu = menuGetCurrent();
char themePath[PATH_MAX] = {0};
GetThemePathFromConfig(themePath, PATH_MAX);
if (themePath[0]==0) menu->curEntry = 0;
else {
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; me != NULL; i ++, me = me->next) {
if (strcmp(me->path, themePath)==0) {
menu->curEntry = i;
break;
}
}
}
}
void launchApplyThemeTask(menuEntry_s* arg) { void launchApplyThemeTask(menuEntry_s* arg) {
const char* themePath = arg->path; const char* themePath = arg->path;
menu_s* menu = menuGetCurrent();
SetThemePathToConfig(themePath); SetThemePathToConfig(themePath);
themeStartup(themeGlobalPreset); themeStartup(themeGlobalPreset);
computeFrontGradient(themeCurrent.frontWaveColor, 280); menuStartupCommon();
menuLoadFileassoc();
if (hbmenu_state == HBMENU_THEME_MENU) { // Normally this should never be used outside of theme-menu.
themeMenuScan(menu->dirname);
menuThemeSelectCurrentEntry();
} else menuScan(menu->dirname);
} }
bool menuIsNetloaderActive(void) { bool menuIsNetloaderActive(void) {
@ -165,16 +225,13 @@ 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) { static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
int x, y; int x, y;
int start_y = 720 - 100 - 145;//*(n % 2); int start_y = layoutobj->posStart[1];//*(n % 2);
int end_y = start_y + 140 + 32; int end_y = start_y + layoutobj->size[1];
int start_x = off_x;//(n / 2); int start_x = off_x;//(n / 2);
int end_x = start_x + 140; int end_x = start_x + layoutobj->size[0];
int j; int j;
const uint8_t *smallimg = NULL; const uint8_t *smallimg = NULL;
@ -195,7 +252,7 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
if (is_active) { if (is_active) {
highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5); highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5);
border_color = MakeColor(themeCurrent.highlightColor.r + (255 - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (255 - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (255 - themeCurrent.highlightColor.b) * highlight_multiplier, 255); border_color = MakeColor(themeCurrent.highlightColor.r + (themeCurrent.highlightGradientEdgeColor.r - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (themeCurrent.highlightGradientEdgeColor.g - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (themeCurrent.highlightGradientEdgeColor.b - themeCurrent.highlightColor.b) * highlight_multiplier, 255);
border_start_x = start_x-6; border_start_x = start_x-6;
border_end_x = end_x+6; border_end_x = end_x+6;
border_start_y = start_y-5; border_start_y = start_y-5;
@ -273,29 +330,29 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
} }
else if (me->type == ENTRY_TYPE_FOLDER) { else if (me->type == ENTRY_TYPE_FOLDER) {
smallimg = folder_icon_small; smallimg = folder_icon_small;
largeimg = assetsGetDataBuffer(AssetId_folder_icon); largeimg = folder_icon_large;
} }
else if (me->type == ENTRY_TYPE_THEME){ else if (me->type == ENTRY_TYPE_THEME){
smallimg = theme_icon_small; smallimg = theme_icon_small;
if(themeGlobalPreset == THEME_PRESET_DARK) largeimg = theme_icon_large;
largeimg = assetsGetDataBuffer(AssetId_theme_icon_dark);
else largeimg = assetsGetDataBuffer(AssetId_theme_icon_light);
} }
else { else {
smallimg = invalid_icon_small; smallimg = invalid_icon_small;
largeimg = assetsGetDataBuffer(AssetId_invalid_icon); largeimg = invalid_icon_large;
} }
if (smallimg) { if (smallimg) {
drawImage(start_x, start_y + 32, 140, 140, smallimg, IMAGE_MODE_RGB24); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
drawImage(start_x + layoutobj->posStart[0], start_y + layoutobj->posStart[1], layoutobj->size[0], layoutobj->size[1], smallimg, IMAGE_MODE_RGB24);
} }
if (is_active && largeimg) { layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
drawImage(117, 100+10, 256, 256, largeimg, IMAGE_MODE_RGB24); if (is_active && largeimg && layoutobj->visible) {
drawImage(layoutobj->posStart[0], layoutobj->posStart[1], layoutobj->size[0], layoutobj->size[1], largeimg, IMAGE_MODE_RGB24);
shadow_start_y = 100+10+256; shadow_start_y = layoutobj->posStart[1]+layoutobj->size[1];
border_start_x = 117; border_start_x = layoutobj->posStart[0];
border_end_x = 117+256; border_end_x = layoutobj->posStart[0]+layoutobj->size[0];
for (shadow_y=shadow_start_y; shadow_y <shadow_start_y+shadow_size; shadow_y++) { for (shadow_y=shadow_start_y; shadow_y <shadow_start_y+shadow_size; shadow_y++) {
for (x=border_start_x; x<border_end_x; x++) { for (x=border_start_x; x<border_end_x; x++) {
@ -317,21 +374,23 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
memset(tmpstr, 0, sizeof(tmpstr)); memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s%s", strptr, me->name); snprintf(tmpstr, sizeof(tmpstr)-1, "%s%s", strptr, me->name);
DrawTextTruncate(interuiregular14, start_x + 4, start_y + 4 + 18, themeCurrent.borderTextColor, tmpstr, 140 - 32, "..."); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListName];
DrawTextTruncate(layoutobj->font, start_x + layoutobj->posStart[0], start_y + layoutobj->posStart[1], themeCurrent.borderTextColor, tmpstr, layoutobj->size[0], "...");
if (is_active) { if (is_active) {
start_x = 1280 - 790; layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryName];
start_y = 135+10; if (layoutobj->visible) DrawTextTruncate(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr, layoutobj->size[0], "...");
DrawTextTruncate(interuimedium30, start_x, start_y + 39, themeCurrent.textColor, tmpstr, 1280 - start_x - 120 ,"...");
if (me->type != ENTRY_TYPE_FOLDER) { if (me->type != ENTRY_TYPE_FOLDER) {
memset(tmpstr, 0, sizeof(tmpstr)); memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Author), me->author); snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Author), me->author);
DrawText(interuiregular14, start_x, start_y + 28 + 30 + 18, themeCurrent.textColor, tmpstr); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryAuthor];
if (layoutobj->visible) DrawText(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
memset(tmpstr, 0, sizeof(tmpstr)); memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Version), me->version); snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Version), me->version);
DrawText(interuiregular14, start_x, start_y + 28 + 30 + 18 + 6 + 18, themeCurrent.textColor, tmpstr); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion];
if (layoutobj->visible) DrawText(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
} }
} }
} }
@ -344,6 +403,8 @@ void computeFrontGradient(color_t baseColor, int height) {
float dark_mult, dark_sub = 75; float dark_mult, dark_sub = 75;
color_t color; color_t color;
if (height < 0 || height > 720) return;
for (y=0; y<720; y++) { for (y=0; y<720; y++) {
alpha = y - (720 - height); alpha = y - (720 - height);
@ -392,22 +453,20 @@ void menuStartupPath(void) {
} }
} }
void menuStartup(void) { void menuLoadFileassoc(void) {
char tmp_path[PATH_MAX+28]; char tmp_path[PATH_MAX+28];
memset(tmp_path, 0, sizeof(tmp_path)-1);
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/fileassoc", rootPathBase); snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/fileassoc", rootPathBase);
menuFileassocScan(tmp_path); menuFileassocScan(tmp_path);
}
void menuStartup(void) {
menuLoadFileassoc();
menuScan(rootPath); menuScan(rootPath);
folder_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_folder_icon), 256, 256, 140, 140, IMAGE_MODE_RGB24); menuStartupCommon();
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");
} }
void themeMenuStartup(void) { void themeMenuStartup(void) {
@ -418,6 +477,7 @@ void themeMenuStartup(void) {
snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s%s%s%s%s", rootPathBase, DIRECTORY_SEPARATOR, "config", DIRECTORY_SEPARATOR, "nx-hbmenu" , DIRECTORY_SEPARATOR, "themes"); snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s%s%s%s%s", rootPathBase, DIRECTORY_SEPARATOR, "config", DIRECTORY_SEPARATOR, "nx-hbmenu" , DIRECTORY_SEPARATOR, "themes");
themeMenuScan(tmp_path); themeMenuScan(tmp_path);
menuThemeSelectCurrentEntry();
} }
color_t waveBlendAdd(color_t a, color_t b, float alpha) { color_t waveBlendAdd(color_t a, color_t b, float alpha) {
@ -429,6 +489,7 @@ void drawWave(int id, float timer, color_t color, int height, float phase, float
float wave_top_y, alpha, one_minus_alpha; float wave_top_y, alpha, one_minus_alpha;
color_t existing_color, new_color; color_t existing_color, new_color;
if (height < 0 || height > 720) return;
height = 720 - height; height = 720 - height;
for (x=0; x<1280; x++) { for (x=0; x<1280; x++) {
@ -478,27 +539,39 @@ void drawCharge() {
sprintf(chargeString, "%d%%", batteryCharge); sprintf(chargeString, "%d%%", batteryCharge);
int tmpX = GetTextXCoordinate(interuiregular14, 1180 - 10, chargeString, 'r'); ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BatteryCharge];
DrawText(interuiregular14, tmpX - 24 - 8, 0 + 47 + 10 + 21 + 4, themeCurrent.textColor, chargeString); if (layoutobj->visible) {
drawIcon(1180 - 8 - 24 - 8, 0 + 47 + 10 + 6, 24, 24, assetsGetDataBuffer(AssetId_battery_icon), themeCurrent.textColor); int tmpX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], chargeString, 'r');
if (isCharging) DrawText(layoutobj->font, tmpX, layoutobj->posStart[1], themeCurrent.textColor, chargeString);
drawIcon(1180 - 20, 0 + 47 + 10 + 6, 24, 24, assetsGetDataBuffer(AssetId_charging_icon), themeCurrent.textColor); }
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BatteryIcon];
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], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor);
} }
} }
void drawNetwork(int tmpX) { void drawNetwork(int tmpX, AssetId id) {
bool netstatusFlag=0; ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_NetworkIcon];
AssetId id; assetsDataEntry *data = NULL;
if (statusGet(&netstatusFlag, &id)) { assetsGetData(id, &data);
if (netstatusFlag) 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);
drawIcon(tmpX, 0 + 47 + 10 + 3, 24, 24, assetsGetDataBuffer(id), themeCurrent.textColor);
}
} }
u32 drawStatus() { u32 drawStatus() {
bool netstatusFlag=0;
bool temperatureFlag=0;
s32 temperature=0;
AssetId id;
char timeString[9]; char tmpstr[32];
time_t unixTime = time(NULL); time_t unixTime = time(NULL);
struct tm* timeStruct = localtime((const time_t *)&unixTime); struct tm* timeStruct = localtime((const time_t *)&unixTime);
@ -507,26 +580,33 @@ u32 drawStatus() {
int minutes = timeStruct->tm_min; int minutes = timeStruct->tm_min;
int seconds = timeStruct->tm_sec; int seconds = timeStruct->tm_sec;
sprintf(timeString, "%02d:%02d:%02d", hours, minutes, seconds); snprintf(tmpstr, sizeof(tmpstr)-1, "%02d:%02d:%02d", hours, minutes, seconds);
u32 tmpX = GetTextXCoordinate(interuimedium20, 1180, timeString, 'r'); ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_Status];
DrawText(interuimedium20, tmpX, 0 + 47 + 10, themeCurrent.textColor, timeString); u32 tmpX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], tmpstr, 'r');
if (layoutobj->visible) DrawText(layoutobj->font, tmpX, layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
drawCharge(); drawCharge();
drawNetwork(tmpX);
if (statusGet(&netstatusFlag, &id, &temperatureFlag, &temperature)) {
if (netstatusFlag) drawNetwork(tmpX, id);
if (temperatureFlag) {
snprintf(tmpstr, sizeof(tmpstr)-1, "%d°C", temperature);
DrawTextFromLayout(ThemeLayoutId_Temperature, themeCurrent.textColor, tmpstr);
}
}
return tmpX; return tmpX;
} }
void drawButtons(menu_s* menu, bool emptyDir, int *x_image_out) { void drawButtons(menu_s* menu, bool emptyDir, int *out_basePos) {
int x_image = 1280 - 252 - 30 - 32; ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA];
int x_text = 1280 - 216 - 30 - 32; int basePos[2]={0};
if(emptyDir) { basePos[0] = layoutobj->posStart[0];
x_image = 1280 - 126 - 30 - 32; basePos[1] = layoutobj->posStart[1];
x_text = 1280 - 90 - 30 - 32;
}
#ifdef __SWITCH__ #ifdef __SWITCH__
if (strcmp( menu->dirname, "sdmc:/") != 0) if (strcmp( menu->dirname, "sdmc:/") != 0)
@ -534,27 +614,26 @@ void drawButtons(menu_s* menu, bool emptyDir, int *x_image_out) {
if (strcmp( menu->dirname, "/") != 0) if (strcmp( menu->dirname, "/") != 0)
#endif #endif
{ {
//drawImage(x_image, 720 - 48, 32, 32, themeCurrent.buttonBImage, IMAGE_MODE_RGBA32); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonBText];
DrawText(fontscale7, x_image, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonBText);//Display the 'B' button from SharedFont. DrawTextFromLayoutRelative(ThemeLayoutId_ButtonBText, basePos[0], basePos[1], !emptyDir ? layoutobj->posStart : layoutobj->posEnd, basePos, themeCurrent.textColor, textGetString(StrId_Actions_Back), 'l');
DrawText(interuiregular18, x_text, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Back)); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonB];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonB, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonBText, 'l');
} }
if(hbmenu_state == HBMENU_DEFAULT) if(hbmenu_state == HBMENU_DEFAULT)
{ {
x_text = GetTextXCoordinate(interuiregular18, x_image - 32, textGetString(StrId_NetLoader), 'r'); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonYText];
x_image = x_text - 36; DrawTextFromLayoutRelative(ThemeLayoutId_ButtonYText, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, textGetString(StrId_NetLoader), 'r');
*x_image_out = x_image - 40; layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonY];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonY, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonYText, 'l');
DrawText(fontscale7, x_image, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonYText); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonMText];
DrawText(interuiregular18, x_text, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_NetLoader)); DrawTextFromLayoutRelative(ThemeLayoutId_ButtonMText, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, textGetString(StrId_ThemeMenu), 'r');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonM];
x_text = GetTextXCoordinate(interuiregular18, x_image - 32, textGetString(StrId_ThemeMenu), 'r'); DrawTextFromLayoutRelative(ThemeLayoutId_ButtonM, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonMText, 'l');
x_image = x_text - 36;
*x_image_out = x_image - 40;
DrawText(fontscale7, x_image, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonMText);
DrawText(interuiregular18, x_text, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_ThemeMenu));
} }
out_basePos[0] = basePos[0];
out_basePos[1] = basePos[1];
} }
void menuUpdateNetloader(netloaderState *netloader_state) { void menuUpdateNetloader(netloaderState *netloader_state) {
@ -589,44 +668,77 @@ void menuLoop(void) {
menuEntry_s* me; menuEntry_s* me;
menu_s* menu = NULL; menu_s* menu = NULL;
int i; int i;
int x, y; int x, y, endy = 720;
int menupath_x_endpos = 918 + 40; int curPos[2]={0};
netloaderState netloader_state; netloaderState netloader_state;
ThemeLayoutObject *layoutobj = NULL;
for (y=0; y<450; y++) { for (i=0; i<3; i++) {
if (i==2) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackWave];
if (i==1) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave];
if (i==0) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
if (layoutobj->visible && layoutobj->size[1] >= 0 && layoutobj->size[1] <= 720-10) {
endy = 720 - layoutobj->size[1] + 10;
break;
}
}
for (y=0; y<endy; y++) {
for (x=0; x<1280; x+=4) {// don't draw bottom pixels as they are covered by the waves for (x=0; x<1280; x+=4) {// don't draw bottom pixels as they are covered by the waves
Draw4PixelsRaw(x, y, themeCurrent.backgroundColor); Draw4PixelsRaw(x, y, themeCurrent.backgroundColor);
} }
} }
drawWave(0, menuTimer, themeCurrent.backWaveColor, 295, 0.0, 3.0); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage];
drawWave(1, menuTimer, themeCurrent.middleWaveColor, 290, 2.0, 3.5); assetsDataEntry *data = NULL;
drawWave(2, menuTimer, themeCurrent.frontWaveColor, 280, 4.0, -2.5); 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];
if (layoutobj->visible) drawWave(1, menuTimer, themeCurrent.middleWaveColor, layoutobj->size[1], 2.0, 3.5);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
if (layoutobj->visible) drawWave(2, menuTimer, themeCurrent.frontWaveColor, layoutobj->size[1], 4.0, -2.5);
menuTimer += 0.05; menuTimer += 0.05;
drawImage(40, 20, 140, 60, themeCurrent.hbmenuLogoImage, IMAGE_MODE_RGBA32); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_Logo];
DrawText(interuiregular14, 184, 46 + 18, themeCurrent.textColor, VERSION); if(themeGlobalPreset == THEME_PRESET_DARK)
assetsGetData(AssetId_hbmenu_logo_dark, &data);
else assetsGetData(AssetId_hbmenu_logo_light, &data);
if (layoutobj->visible) {
if (!themeCurrent.logoColor_set)
drawImage(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, data->imageMode);
else {
drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.logoColor);
}
}
DrawTextFromLayout(ThemeLayoutId_HbmenuVersion, themeCurrent.textColor, VERSION);
u32 statusXPos = drawStatus(); u32 statusXPos = drawStatus();
#ifdef __SWITCH__ #ifdef __SWITCH__
AppletType at = appletGetAppletType(); AppletType at = appletGetAppletType();
if (at != AppletType_Application && at != AppletType_SystemApplication) { layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_AttentionText];
if (at != AppletType_Application && at != AppletType_SystemApplication && layoutobj->visible) {
const char* appletMode = textGetString(StrId_AppletMode); const char* appletMode = textGetString(StrId_AppletMode);
u32 x_pos = GetTextXCoordinate(interuimedium30, statusXPos, appletMode, 'r'); u32 x_pos = GetTextXCoordinate(layoutobj->font, statusXPos, appletMode, 'r');
DrawText(interuimedium30, x_pos - 32, 46 + 18, themeCurrent.attentionTextColor, appletMode); DrawText(layoutobj->font, layoutobj->posType ? x_pos + layoutobj->posStart[0] : layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.attentionTextColor, appletMode);
} }
const char* loaderInfo = envGetLoaderInfo(); const char* loaderInfo = envGetLoaderInfo();
if (loaderInfo) { layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_LoaderInfo];
u32 x_pos = 43; if (loaderInfo && layoutobj->visible) {
u32 x_pos = layoutobj->posStart[0];
char* spacePos = strchr(loaderInfo, ' '); char* spacePos = strchr(loaderInfo, ' ');
if (spacePos) { if (spacePos) {
char tempbuf[64] = {0}; char tempbuf[64] = {0};
size_t tempsize = spacePos - loaderInfo + 1; size_t tempsize = spacePos - loaderInfo + 1;
if (tempsize > sizeof(tempbuf)-1) tempsize = sizeof(tempbuf)-1; if (tempsize > sizeof(tempbuf)-1) tempsize = sizeof(tempbuf)-1;
memcpy(tempbuf, loaderInfo, tempsize); memcpy(tempbuf, loaderInfo, tempsize);
x_pos = GetTextXCoordinate(interuiregular14, 184, tempbuf, 'r'); x_pos = GetTextXCoordinate(layoutobj->font, layoutobj->posEnd[0], tempbuf, 'r');
} }
DrawText(interuiregular14, x_pos, 46 + 18 + 20, themeCurrent.textColor, loaderInfo); DrawText(layoutobj->font, x_pos, layoutobj->posStart[1], themeCurrent.textColor, loaderInfo);
} }
#endif #endif
@ -636,7 +748,7 @@ void menuLoop(void) {
char tmpstr[64]; char tmpstr[64];
snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_frame); snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_frame);
DrawText(interuiregular14, 180 + 256, 46 + 16 + 18, themeCurrent.textColor, tmpstr); DrawTextFromLayout(ThemeLayoutId_LogInfo, themeCurrent.textColor, tmpstr);
#endif #endif
memset(&netloader_state, 0, sizeof(netloader_state)); memset(&netloader_state, 0, sizeof(netloader_state));
@ -653,8 +765,11 @@ void menuLoop(void) {
menuCloseMsgBox(); menuCloseMsgBox();
menuMsgBoxSetNetloaderState(0, NULL, 0, 0); menuMsgBoxSetNetloaderState(0, NULL, 0, 0);
}
if (netloader_state.errormsg[0]) menuCreateMsgBox(780,300, netloader_state.errormsg); if (netloader_state.errormsg[0]) {
menuCloseMsgBox();
menuCreateMsgBox(780,300, netloader_state.errormsg);
} }
if(hbmenu_state == HBMENU_NETLOADER_ACTIVE) { if(hbmenu_state == HBMENU_NETLOADER_ACTIVE) {
@ -674,29 +789,53 @@ void menuLoop(void) {
launchMenuEntryTask(netloader_state.me); launchMenuEntryTask(netloader_state.me);
} }
} else { } else {
DrawText(interuiregular14, 64, 128 + 18, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg)); DrawTextFromLayout(ThemeLayoutId_InfoMsg, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg));
} }
drawButtons(menu, true, &menupath_x_endpos); drawButtons(menu, true, curPos);
} }
else else
{ {
static int v = 0; static int v = 0;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
if (menu->nEntries > 7) { // Gentle Realign only when not manually moving
int wanted_x = clamp(-menu->curEntry * (140 + 30), -(menu->nEntries - 7) * (140 + 30), 0); if (menu->slideSpeed == 0) {
menu->xPos += v; if (menu->nEntries > entries_count) {
v += (wanted_x - menu->xPos) / 3; int wanted_x = clamp(-menu->curEntry * layoutobj->posEnd[0], -(menu->nEntries - entries_count) * layoutobj->posEnd[0], 0);
v /= 2; menu->xPos += v;
v += (wanted_x - menu->xPos) / 3;
v /= 2;
}
else {
menu->xPos = v = 0;
}
} }
else { else {
menu->xPos = v = 0; menu->xPos += menu->slideSpeed;
if (abs(menu->slideSpeed) > 2) {
// Slow down way faster when outside the normal bounds
if (menu->xPos > 0 || menu->xPos < -(menu->nEntries) * layoutobj->posEnd[0]) {
menu->slideSpeed *= .5f;
}
else {
menu->slideSpeed *= .9f;
}
}
else {
menu->slideSpeed = 0;
}
menu->curEntry = clamp(roundf(-((float) menu->xPos / layoutobj->posEnd[0])), 0, menu->nEntries);
} }
menuEntry_s *active_entry = NULL; menuEntry_s *active_entry = NULL;
// Draw menu entries // Draw menu entries
for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) { for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) {
int entry_start_x = 29 + i * (140 + 30); int entry_start_x = layoutobj->posStart[0] + i * layoutobj->posEnd[0];
int entry_draw_x = entry_start_x + menu->xPos; int entry_draw_x = entry_start_x + menu->xPos;
int screen_width = 1280; int screen_width = 1280;
@ -708,54 +847,61 @@ void menuLoop(void) {
if (is_active) if (is_active)
active_entry = me; active_entry = me;
if (!is_active && entry_draw_x < -(29 + 140 + 30)) if (!is_active && entry_draw_x < -(layoutobj->posStart[0] + layoutobj->posEnd[0]))
continue; continue;
drawEntry(me, entry_draw_x, is_active); drawEntry(me, entry_draw_x, is_active);
} }
int getX = GetTextXCoordinate(interuiregular18, 1180, textGetString(StrId_ThemeMenu), 'r'); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuTypeMsg];
int getX=0;
if(hbmenu_state == HBMENU_THEME_MENU) { if (layoutobj->visible) {
DrawText(interuiregular18, getX, 30 + 26 + 32 + 20, themeCurrent.textColor, textGetString(StrId_ThemeMenu)); getX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], textGetString(StrId_ThemeMenu), 'r');
} else {
//DrawText(interuiregular18, getX, 30 + 26 + 32 + 10, themeCurrent.textColor, textGetString(StrId_ThemeMenu)); if(hbmenu_state == HBMENU_THEME_MENU) {
//DrawText(fontscale7, getX - 40, 30 + 26 + 32 + 10, themeCurrent.textColor, themeCurrent.buttonMText); DrawText(layoutobj->font, getX, layoutobj->posStart[1], themeCurrent.textColor, textGetString(StrId_ThemeMenu));
} else {
//DrawText(interuiregular18, getX, 30 + 26 + 32 + 10, themeCurrent.textColor, textGetString(StrId_ThemeMenu));
//DrawText(fontscale7, getX - 40, 30 + 26 + 32 + 10, themeCurrent.textColor, themeCurrent.buttonMText);
}
} }
if(active_entry != NULL) { if(active_entry != NULL) {
if (active_entry->type == ENTRY_TYPE_THEME) { const char *buttonstr = "";
DrawText(fontscale7, 1280 - 126 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonAText);
DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Apply)); if (active_entry->type == ENTRY_TYPE_THEME)
} buttonstr = textGetString(StrId_Actions_Apply);
else if (active_entry->type != ENTRY_TYPE_FOLDER) { else if (active_entry->type != ENTRY_TYPE_FOLDER)
DrawText(fontscale7, 1280 - 126 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonAText);//Display the 'A' button from SharedFont. buttonstr = textGetString(StrId_Actions_Launch);
DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Launch)); else
} buttonstr = textGetString(StrId_Actions_Open);
else {
DrawText(fontscale7, 1280 - 126 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonAText); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonAText];
DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Open)); DrawTextFromLayoutRelative(ThemeLayoutId_ButtonAText, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, buttonstr, 'l');
} layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonA, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, themeCurrent.buttonAText, 'l');
} }
drawButtons(menu, false, &menupath_x_endpos); drawButtons(menu, false, curPos);
if (active_entry && active_entry->type != ENTRY_TYPE_THEME) { if (active_entry && active_entry->type != ENTRY_TYPE_THEME) {
if (active_entry->starred) { const char *buttonstr = "";
getX = GetTextXCoordinate(interuiregular18, menupath_x_endpos + 8, textGetString(StrId_Actions_Unstar), 'r'); if (active_entry->starred)
DrawText(fontscale7, getX - 36, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonXText); buttonstr = textGetString(StrId_Actions_Unstar);
DrawText(interuiregular18, getX, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Unstar)); else
} else { buttonstr = textGetString(StrId_Actions_Star);
getX = GetTextXCoordinate(interuiregular18, menupath_x_endpos + 8, textGetString(StrId_Actions_Star), 'r');
DrawText(fontscale7, getX - 36, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonXText); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonXText];
DrawText(interuiregular18, getX, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Star)); DrawTextFromLayoutRelative(ThemeLayoutId_ButtonXText, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, buttonstr, 'r');
} layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonX];
menupath_x_endpos = getX - 36 - 40; DrawTextFromLayoutRelative(ThemeLayoutId_ButtonX, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, themeCurrent.buttonXText, 'l');
} }
} }
DrawTextTruncate(interuiregular18, 40, 720 - 47 + 24, themeCurrent.textColor, menu->dirname, menupath_x_endpos - 40, "..."); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuPath];
if (layoutobj->visible) DrawTextTruncate(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, menu->dirname, layoutobj->size[0], "...");
menuDrawMsgBox(); menuDrawMsgBox();
} }

View File

@ -33,6 +33,7 @@ struct menu_s_tag
int nEntries; int nEntries;
int curEntry; int curEntry;
int xPos; int xPos;
int slideSpeed;
char dirname[PATH_MAX+1]; char dirname[PATH_MAX+1];
}; };
@ -77,7 +78,7 @@ typedef enum
IMAGE_MODE_RGBA32 IMAGE_MODE_RGBA32
} ImageMode; } ImageMode;
double menuTimer; extern double menuTimer;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -86,7 +87,7 @@ extern "C" {
void menuEntryInit(menuEntry_s* me, MenuEntryType type); void menuEntryInit(menuEntry_s* me, MenuEntryType type);
void menuEntryFree(menuEntry_s* me, bool skip_icongfx); void menuEntryFree(menuEntry_s* me, bool skip_icongfx);
bool fileExists(const char* path); bool fileExists(const char* path);
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut); bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists);
void menuEntryParseIcon(menuEntry_s* me); void menuEntryParseIcon(menuEntry_s* me);
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode); uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode);
void menuEntryParseNacp(menuEntry_s* me); void menuEntryParseNacp(menuEntry_s* me);

View File

@ -16,6 +16,7 @@ void drawMsgBoxBgToBuff(color_t *buff, int width, int height) {
float rad, alpha; float rad, alpha;
color_t base_color = themeCurrent.backgroundColor; color_t base_color = themeCurrent.backgroundColor;
color_t color; color_t color;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
for (y=0; y<height; y++) { for (y=0; y<height; y++) {
for (x=0; x<width; x++) { for (x=0; x<width; x++) {
@ -62,7 +63,7 @@ void drawMsgBoxBgToBuff(color_t *buff, int width, int height) {
else else
color = base_color; color = base_color;
if (y == height - 80) { if (y == height + layoutobj->posStart[1]) {
color = themeCurrent.separatorColor; color = themeCurrent.separatorColor;
} }
@ -76,6 +77,7 @@ void menuDrawMsgBox() {
if (!menuIsMsgBoxOpen()) if (!menuIsMsgBoxOpen())
return; return;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
int off; int off;
int x, y; int x, y;
int start_x = 1280 / 2 - currMsgBox.width / 2; int start_x = 1280 / 2 - currMsgBox.width / 2;
@ -85,7 +87,7 @@ void menuDrawMsgBox() {
color_t curr_color; color_t curr_color;
color_t border_color; color_t border_color;
int sep_start_y = currMsgBox.height - 80; int sep_start_y = currMsgBox.height + layoutobj->posStart[1];
int border_thickness = 6; int border_thickness = 6;
int shadow_start_y, shadow_y; int shadow_start_y, shadow_y;
@ -121,7 +123,7 @@ void menuDrawMsgBox() {
curr_color = border_color; curr_color = border_color;
} }
} }
else if (msgboxNetloaderProgressEnabled && y > currMsgBox.height - 80 && x < progress_width) { else if (msgboxNetloaderProgressEnabled && y > sep_start_y && x < progress_width) {
curr_color = themeCurrent.progressBarColor; curr_color = themeCurrent.progressBarColor;
} }
@ -135,10 +137,11 @@ void menuDrawMsgBox() {
x = GetTextXCoordinate(interuiregular18, start_x + (currMsgBox.width / 2), textptr, 'c'); x = GetTextXCoordinate(interuiregular18, start_x + (currMsgBox.width / 2), textptr, 'c');
if (text_width < currMsgBox.width && text_height < sep_start_y) { if (text_width < currMsgBox.width && text_height < sep_start_y) {
DrawText(interuiregular18, x, start_y + (currMsgBox.height - text_height - 80) / 2, themeCurrent.textColor, textptr); DrawText(interuiregular18, x, start_y + (sep_start_y - text_height) / 2, themeCurrent.textColor, textptr);
} }
y = start_y + 245 + 26; layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxBottomText];
y = start_y + currMsgBox.height + layoutobj->posStart[1];
if (!msgboxNetloaderEnabled) { if (!msgboxNetloaderEnabled) {
x = GetTextXCoordinate(interuimedium20, start_x + (currMsgBox.width / 2), textGetString(StrId_MsgBox_OK), 'c'); x = GetTextXCoordinate(interuimedium20, start_x + (currMsgBox.width / 2), textGetString(StrId_MsgBox_OK), 'c');

View File

@ -368,7 +368,7 @@ static int decompress(int sock, FILE *fh, size_t filesize) {
int loadnro(menuEntry_s *me, int sock, struct in_addr remote) { int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int len, namelen, filelen; int len, namelen, filelen;
char filename[PATH_MAX+1]; char filepath[PATH_MAX+1];
len = recvall(sock, &namelen, 4, 0); len = recvall(sock, &namelen, 4, 0);
if (len != 4) { if (len != 4) {
@ -376,19 +376,19 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
return -1; return -1;
} }
if (namelen >= sizeof(filename)-1) { if (namelen >= sizeof(filepath)-1) {
netloader_error("Filename length is too large",errno); netloader_error("File-path length is too large",errno);
return -1; return -1;
} }
len = recvall(sock, filename, namelen, 0); len = recvall(sock, filepath, namelen, 0);
if (len != namelen) { if (len != namelen) {
netloader_error("Error getting filename", errno); netloader_error("Error getting file-path", errno);
return -1; return -1;
} }
filename[namelen] = 0; filepath[namelen] = 0;
len = recvall(sock, &filelen, 4, 0); len = recvall(sock, &filelen, 4, 0);
@ -403,41 +403,56 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
int response = 0; int response = 0;
sanitisePath(filename); sanitisePath(filepath);
snprintf(me->path, sizeof(me->path)-1, "%s%s%s", menuGetRootPath(), DIRECTORY_SEPARATOR, filename); snprintf(me->path, sizeof(me->path)-1, "%s%s%s", menuGetRootPath(), DIRECTORY_SEPARATOR, filepath);
me->path[PATH_MAX] = 0;
// make sure it's terminated // make sure it's terminated
me->path[PATH_MAX] = 0; me->path[sizeof(me->path)-1] = 0;
strncpy(filepath, me->path, sizeof(filepath)-1); // menuEntryLoad() below will overwrite me->path, so copy me->path to filepath and use that instead.
filepath[sizeof(filepath)-1] = 0;
argData_s* ad = &me->args; argData_s* ad = &me->args;
ad->dst = (char*)&ad->buf[1]; ad->dst = (char*)&ad->buf[1];
ad->nxlink_host = remote; ad->nxlink_host = remote;
launchAddArg(ad, me->path); const char* ext = getExtension(me->path);
if (ext && strcasecmp(ext, ".nro")==0)
launchAddArg(ad, me->path);
else {
me->type = ENTRY_TYPE_FILE_OTHER; // Handle fileassoc when extension isn't .nro.
if (!menuEntryLoad(me, "", false, false)) {
response = -3;
errno = EINVAL;
netloader_error("File-extension/filename not recognized",0);
}
menuEntryFree(me, false); // We don't need any of the buffers which may have been allocated.
}
#ifndef _WIN32 #ifndef _WIN32
int fd = open(me->path,O_CREAT|O_WRONLY, ACCESSPERMS); if (response == 0) {
int fd = open(filepath,O_CREAT|O_WRONLY, ACCESSPERMS);
if (fd < 0) { if (fd < 0) {
response = -1; response = -1;
netloader_error("open", errno); netloader_error("open", errno);
} else { } else {
if (ftruncate(fd,filelen) == -1) { if (ftruncate(fd,filelen) == -1) {
response = -2; response = -2;
netloader_error("ftruncate",errno); netloader_error("ftruncate",errno);
}
close(fd);
} }
close(fd);
} }
#endif #endif
FILE *file = NULL; FILE *file = NULL;
if (response == 0) file = fopen(me->path,"wb"); if (response == 0) {
file = fopen(filepath,"wb");
if(NULL == file) { if(file == NULL) {
perror("file"); perror("file");
response = -1; response = -1;
}
} }
send(sock,(char *)&response,sizeof(response),0); send(sock,(char *)&response,sizeof(response),0);
@ -449,12 +464,14 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
netloader_error("Failed to allocate memory",ENOMEM); netloader_error("Failed to allocate memory",ENOMEM);
response = -1; response = -1;
} }
else else {
memset(writebuffer, 0, FILE_BUFFER_SIZE);
setvbuf(file,writebuffer,_IOFBF, FILE_BUFFER_SIZE); setvbuf(file,writebuffer,_IOFBF, FILE_BUFFER_SIZE);
}
} }
if (response == 0 ) { if (response == 0 ) {
//printf("transferring %s\n%d bytes.\n", filename, filelen); //printf("transferring %s\n%d bytes.\n", filepath, filelen);
if (decompress(sock,file,filelen)==Z_OK) { if (decompress(sock,file,filelen)==Z_OK) {
int netloaded_cmdlen = 0; int netloaded_cmdlen = 0;
@ -477,7 +494,7 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
} }
if (response == 0 ) { if (response == 0 ) {
if (netloaded_cmdlen > sizeof(me->args.buf)-1) netloaded_cmdlen = sizeof(me->args.buf)-1; if ((me->args.dst+netloaded_cmdlen) >= (char*)(me->args.buf + sizeof(me->args.buf))) netloaded_cmdlen = (uintptr_t)me->args.buf + sizeof(me->args.buf)-1 - (uintptr_t)me->args.dst;
len = recvall(sock,me->args.dst, netloaded_cmdlen,0); len = recvall(sock,me->args.dst, netloaded_cmdlen,0);
@ -499,13 +516,14 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
} else { } else {
response = -1; response = -1;
} }
}
if (file) {
fflush(file); fflush(file);
fclose(file); fclose(file);
free(writebuffer);
if (response == -1) unlink(me->path);
} }
if (response == -1) unlink(filepath);
free(writebuffer);
return response; return response;
} }
@ -549,7 +567,15 @@ int netloader_activate(void) {
return -1; return -1;
} }
int rc = bind(netloader_listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); uint32_t tmpval=1;
int rc = setsockopt(netloader_listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&tmpval, sizeof(tmpval));
if(rc != 0)
{
netloader_socket_error("setsockopt");
return -1;
}
rc = bind(netloader_listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if(rc != 0) if(rc != 0)
{ {
netloader_socket_error("bind"); netloader_socket_error("bind");
@ -701,6 +727,10 @@ Result netloaderInit(void) {
#ifdef __SWITCH__ #ifdef __SWITCH__
rc = socketInitializeDefault(); rc = socketInitializeDefault();
if (R_SUCCEEDED(rc)) {
rc = nifmInitialize(NifmServiceType_User);
if (R_FAILED(rc)) socketExit();
}
#endif #endif
#ifdef __WIN32__ #ifdef __WIN32__
@ -727,6 +757,7 @@ void netloaderExit(void) {
mtx_destroy(&netloader_mtx); mtx_destroy(&netloader_mtx);
#ifdef __SWITCH__ #ifdef __SWITCH__
nifmExit();
socketExit(); socketExit();
#endif #endif

View File

@ -10,6 +10,8 @@ static bool s_statusExit;
static bool s_statusReady; static bool s_statusReady;
static bool s_statusNetFlag; static bool s_statusNetFlag;
static AssetId s_statusNetAssetId; static AssetId s_statusNetAssetId;
static bool s_statusTemperatureFlag;
static s32 s_statusTemperature;
// This uses netstatusGetDetails from a dedicated thread, since nifmGetInternetConnectionStatus can block for a few seconds. // This uses netstatusGetDetails from a dedicated thread, since nifmGetInternetConnectionStatus can block for a few seconds.
@ -18,8 +20,13 @@ static int statusThreadProc(void* unused)
mtx_lock(&s_statusMtx); mtx_lock(&s_statusMtx);
struct timespec timeout = {0}; struct timespec timeout = {0};
bool tmpflag; bool tmpflag=0;
bool thermalflag=0;
bool thermal_initialized=0;
AssetId tmpid; AssetId tmpid;
s32 temperature;
thermal_initialized = thermalstatusInit();
clock_gettime(CLOCK_MONOTONIC, &timeout); clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec++; timeout.tv_sec++;
@ -32,12 +39,16 @@ static int statusThreadProc(void* unused)
break; break;
tmpflag = netstatusGetDetails(&tmpid); tmpflag = netstatusGetDetails(&tmpid);
if (thermal_initialized) thermalflag = thermalstatusGetDetails(&temperature);
mtx_lock(&s_statusAccessMtx); mtx_lock(&s_statusAccessMtx);
s_statusNetFlag = tmpflag; s_statusNetFlag = tmpflag;
s_statusNetAssetId = tmpid; s_statusNetAssetId = tmpid;
s_statusTemperatureFlag = thermalflag;
s_statusTemperature = temperature;
s_statusReady = 1; s_statusReady = 1;
mtx_unlock(&s_statusAccessMtx); mtx_unlock(&s_statusAccessMtx);
@ -48,10 +59,12 @@ static int statusThreadProc(void* unused)
mtx_unlock(&s_statusMtx); mtx_unlock(&s_statusMtx);
if (thermal_initialized) thermalstatusExit();
return 0; return 0;
} }
bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId) { bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature) {
if (!s_statusReady) return 0; if (!s_statusReady) return 0;
mtx_lock(&s_statusAccessMtx); mtx_lock(&s_statusAccessMtx);
@ -59,6 +72,9 @@ bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId) {
*netstatusFlag = s_statusNetFlag; *netstatusFlag = s_statusNetFlag;
*netstatusAssetId = s_statusNetAssetId; *netstatusAssetId = s_statusNetAssetId;
*temperatureFlag = s_statusTemperatureFlag;
*temperature = s_statusTemperature;
mtx_unlock(&s_statusAccessMtx); mtx_unlock(&s_statusAccessMtx);
return 1; return 1;

View File

@ -3,5 +3,5 @@
bool statusInit(void); bool statusInit(void);
void statusExit(void); void statusExit(void);
bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId); bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature);

View File

@ -10,14 +10,14 @@ static int s_textLang = 1;
Result textInit(void) { Result textInit(void) {
#ifdef __SWITCH__ #ifdef __SWITCH__
s32 Language=0; SetLanguage Language=SetLanguage_ENUS;
s_textLang = SetLanguage_ENUS; s_textLang = Language;
Result rc = setInitialize(); Result rc = setInitialize();
if (R_SUCCEEDED(rc)) rc = setGetSystemLanguage(&s_textLanguageCode); if (R_SUCCEEDED(rc)) rc = setGetSystemLanguage(&s_textLanguageCode);
if (R_SUCCEEDED(rc)) rc = setMakeLanguage(s_textLanguageCode, &Language); if (R_SUCCEEDED(rc)) rc = setMakeLanguage(s_textLanguageCode, &Language);
//if (R_SUCCEEDED(rc) && Language < 16) s_textLang = Language;//TODO: Re-enable this once language.c supports all used languages. //if (R_SUCCEEDED(rc) && Language < 17) s_textLang = Language;//TODO: Re-enable this once language.c supports all used languages.
setExit(); setExit();
if (R_FAILED(rc)) return rc; if (R_FAILED(rc)) return rc;
#else #else

View File

@ -1,4 +1,5 @@
#include "theme.h" #include "theme.h"
#include <physfs.h>
theme_t themeCurrent; theme_t themeCurrent;
ThemePreset themeGlobalPreset; ThemePreset themeGlobalPreset;
@ -10,6 +11,83 @@ bool colorFromSetting(config_setting_t *rgba, color_t *col) {
return true; return true;
} }
bool intElemFromSetting(config_setting_t *setting, int *out, size_t count) {
if (!setting || config_setting_length(setting) < count)
return false;
for (size_t i=0; i<count; i++) {
out[i] = config_setting_get_int_elem(setting, i);
}
return true;
}
bool layoutObjectFromSetting(config_setting_t *layout_setting, ThemeLayoutObject *obj, bool ignore_cfg_visible) {
int tmp=0;
ThemeLayoutObject tmpobj={0};
if (!layout_setting)
return false;
memcpy(tmpobj.posStart, obj->posStart, sizeof(obj->posStart));
memcpy(tmpobj.posEnd, obj->posEnd, sizeof(obj->posEnd));
memcpy(tmpobj.size, obj->size, sizeof(obj->size));
if (config_setting_lookup_bool(layout_setting, "visible", &tmp)==CONFIG_TRUE)
tmpobj.visible = tmp;
else
tmpobj.visible = obj->visible;
if (config_setting_lookup_bool(layout_setting, "posType", &tmp)==CONFIG_TRUE)
tmpobj.posType = tmp;
else
tmpobj.posType = obj->posType;
intElemFromSetting(config_setting_lookup(layout_setting, "posStart"), tmpobj.posStart, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "posEnd"), tmpobj.posEnd, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "size"), tmpobj.size, 2);
if (!tmpobj.posType && (tmpobj.posStart[0] < 0 || tmpobj.posStart[1] < 0 || tmpobj.posEnd[0] < 0 || tmpobj.posEnd[1] < 0))
return false;
if (tmpobj.size[0] < 0 || tmpobj.size[1] < 0)
return false;
obj->posStart[0] = tmpobj.posStart[0];
obj->posStart[1] = tmpobj.posStart[1];
obj->posEnd[0] = tmpobj.posEnd[0];
obj->posEnd[1] = tmpobj.posEnd[1];
if (!ignore_cfg_visible) obj->visible = tmpobj.visible;
obj->posType = tmpobj.posType;
obj->size[0] = tmpobj.size[0];
obj->size[1] = tmpobj.size[1];
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 assetsLoadData(id, tmp_path, imageSize);
}
void themeStartup(ThemePreset preset) { void themeStartup(ThemePreset preset) {
themeGlobalPreset = preset; themeGlobalPreset = preset;
theme_t themeLight = (theme_t) { theme_t themeLight = (theme_t) {
@ -20,6 +98,7 @@ void themeStartup(ThemePreset preset) {
.backWaveColor = MakeColor(154, 171, 255, 255), .backWaveColor = MakeColor(154, 171, 255, 255),
.backgroundColor = MakeColor(233, 236, 241, 255), .backgroundColor = MakeColor(233, 236, 241, 255),
.highlightColor = MakeColor(91, 237, 224, 255), .highlightColor = MakeColor(91, 237, 224, 255),
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
.separatorColor = MakeColor(219, 218, 219, 255), .separatorColor = MakeColor(219, 218, 219, 255),
.borderColor = MakeColor(255,255,255,255), .borderColor = MakeColor(255,255,255,255),
.borderTextColor = MakeColor(64,64,64,255), .borderTextColor = MakeColor(64,64,64,255),
@ -33,7 +112,6 @@ void themeStartup(ThemePreset preset) {
.buttonMText = "\uE0F0", .buttonMText = "\uE0F0",
.labelStarOnText = "\u2605", .labelStarOnText = "\u2605",
.labelStarOffText = "\u2606", .labelStarOffText = "\u2606",
.hbmenuLogoImage = assetsGetDataBuffer(AssetId_hbmenu_logo_light),
}; };
theme_t themeDark = (theme_t) { theme_t themeDark = (theme_t) {
@ -44,6 +122,7 @@ void themeStartup(ThemePreset preset) {
.backWaveColor = MakeColor(73, 103, 169, 255), .backWaveColor = MakeColor(73, 103, 169, 255),
.backgroundColor = MakeColor(45, 45, 50, 255), .backgroundColor = MakeColor(45, 45, 50, 255),
.highlightColor = MakeColor(91, 237, 224, 255), .highlightColor = MakeColor(91, 237, 224, 255),
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
.separatorColor = MakeColor(219, 218, 219, 255), .separatorColor = MakeColor(219, 218, 219, 255),
.borderColor = MakeColor(255,255,255,255), .borderColor = MakeColor(255,255,255,255),
.borderTextColor = MakeColor(64,64,64,255), .borderTextColor = MakeColor(64,64,64,255),
@ -57,7 +136,276 @@ void themeStartup(ThemePreset preset) {
.buttonMText = "\uE0B4", .buttonMText = "\uE0B4",
.labelStarOnText = "\u2605", .labelStarOnText = "\u2605",
.labelStarOffText = "\u2606", .labelStarOffText = "\u2606",
.hbmenuLogoImage = assetsGetDataBuffer(AssetId_hbmenu_logo_dark), };
theme_t themeCommon = {
.layoutObjects = {
[ThemeLayoutId_Logo] = {
.visible = true,
.posType = false,
.posStart = {40, 20},
},
[ThemeLayoutId_HbmenuVersion] = {
.visible = true,
.posType = false,
.posStart = {184, 46 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_LoaderInfo] = {
.visible = true,
.posType = true,
.posStart = {43, 46 + 18 + 20},
.posEnd = {184},
.font = interuiregular14,
},
[ThemeLayoutId_AttentionText] = {
.visible = true,
.posType = true,
.posStart = {-32, 46 + 18},
.font = interuimedium30,
},
[ThemeLayoutId_LogInfo] = {
.visible = true,
.posType = false,
.posStart = {180 + 256, 46 + 16 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_InfoMsg] = {
.visible = true,
.posType = false,
.posStart = {64, 128 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_MenuPath] = {
.visible = true,
.posType = false,
.posStart = {40, 720 - 47 + 24},
.size = {380},
.font = interuiregular18,
},
[ThemeLayoutId_MenuTypeMsg] = {
.visible = true,
.posType = false,
.posStart = {1180, 30 + 26 + 32 + 20},
.font = interuiregular18,
},
[ThemeLayoutId_MsgBoxSeparator] = {
.visible = true,
.posType = true,
.posStart = {0, -80},
},
[ThemeLayoutId_MsgBoxBottomText] = {
.visible = true,
.posType = true,
.posStart = {0, -29},
},
// ThemeLayoutId_BackgroundImage is not set with the defaults.
[ThemeLayoutId_BackWave] = {
.visible = true,
.posType = true,
.size = {0, 295},
},
[ThemeLayoutId_MiddleWave] = {
.visible = true,
.posType = true,
.size = {0, 290},
},
[ThemeLayoutId_FrontWave] = {
.visible = true,
.posType = true,
.size = {0, 280},
},
[ThemeLayoutId_ButtonA] = {
.visible = true,
.posType = false,
.posStart = {1280 - 126 - 30 - 32, 720 - 47 + 24},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonAText] = {
.visible = true,
.posType = false,
.posStart = {1280 - 90 - 30 - 32, 720 - 47 + 24},
.touchSize = {0, 25},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonB] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.posEnd = {0},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonBText] = {
.visible = true,
.posType = true,
.posStart = {-90, 0},
.touchSize = {0, 32},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonY] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.font = fontscale7,
},
[ThemeLayoutId_ButtonYText] = {
.visible = true,
.posType = true,
.posStart = {-32, 0},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonM] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.font = fontscale7,
},
[ThemeLayoutId_ButtonMText] = {
.visible = true,
.posType = true,
.posStart = {-32, 0},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonX] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonXText] = {
.visible = true,
.posType = true,
.posStart = {-40 + 8, 0},
.touchSize = {0, 25},
.font = interuiregular18,
},
[ThemeLayoutId_NetworkIcon] = {
.visible = true,
.posType = true,
.posStart = {0, 0 + 47 + 10 + 3},
},
[ThemeLayoutId_BatteryCharge] = {
.visible = true,
.posType = false,
.posStart = {1180 - 10 - 24 - 8, 0 + 47 + 10 + 21 + 4},
.font = interuiregular14,
},
[ThemeLayoutId_BatteryIcon] = {
.visible = true,
.posType = false,
.posStart = {1180 - 8 - 24 - 8, 0 + 47 + 10 + 6},
},
[ThemeLayoutId_ChargingIcon] = {
.visible = true,
.posType = false,
.posStart = {1180 - 20, 0 + 47 + 10 + 6},
},
[ThemeLayoutId_Status] = {
.visible = true,
.posType = false,
.posStart = {1180, 0 + 47 + 10},
.font = interuimedium20,
},
[ThemeLayoutId_Temperature] = {
.visible = true,
.posType = false,
.posStart = {1180 + 4, 0 + 47 + 10 + + 21 + 6},
.font = interuiregular14,
},
[ThemeLayoutId_MenuList] = {
.visible = true,
.posType = false,
.posStart = {29, 720 - 100 - 145},
.posEnd = {140 + 30, 0},
.size = {140, 140 + 32},
},
[ThemeLayoutId_MenuListTiles] = {
.visible = true,
.posType = true,
.posEnd = {7, 0},
.size = {0, 0},
},
[ThemeLayoutId_MenuListIcon] = {
.visible = true,
.posType = true,
.posStart = {0, 32},
.size = {140, 140},
.imageSize = {256, 256},
},
[ThemeLayoutId_MenuListName] = {
.visible = true,
.posType = true,
.posStart = {4, 4 + 18},
.size = {140 - 32, 0},
.font = interuiregular14,
},
[ThemeLayoutId_MenuActiveEntryIcon] = {
.visible = true,
.posType = false,
.posStart = {117, 100+10},
.size = {256, 256},
.imageSize = {256, 256},
},
[ThemeLayoutId_MenuActiveEntryName] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 39},
.size = {790 - 120, 0},
.font = interuimedium30,
},
[ThemeLayoutId_MenuActiveEntryAuthor] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 28 + 30 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_MenuActiveEntryVersion] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 28 + 30 + 18 + 6 + 18},
.font = interuiregular14,
},
},
}; };
char themePath[PATH_MAX] = {0}; char themePath[PATH_MAX] = {0};
@ -66,14 +414,55 @@ void themeStartup(ThemePreset preset) {
theme_t *themeDefault; theme_t *themeDefault;
config_t cfg = {0}; config_t cfg = {0};
config_init(&cfg); config_init(&cfg);
config_setting_t *theme = NULL; config_setting_t *theme = NULL, *layout = NULL, *assets = NULL;
color_t text, attentionText, frontWave, middleWave, backWave, background, highlight, separator, borderColor, borderTextColor, progressBarColor; color_t text, logoColor={0}, attentionText, frontWave, middleWave, backWave, background, highlight, highlightGradientEdgeColor, separator, borderColor, borderTextColor, progressBarColor;
int waveBlending; int waveBlending;
const char *AText, *BText, *XText, *YText, *PText, *MText, *starOnText, *starOffText; const char *AText, *BText, *XText, *YText, *PText, *MText, *starOnText, *starOffText;
bool logoColor_set = false;
bool good_cfg = false; bool good_cfg = false;
#ifdef __SWITCH__
bool is_romfs = false;
#endif
bool is_archive = false;
const char* theme_archive_path = NULL;
if(themePath[0]!=0) assetsClearTheme();
good_cfg = config_read_file(&cfg, themePath);
if(themePath[0]!=0) {
const char* cfg_path = themePath;
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) {
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) { switch (preset) {
case THEME_PRESET_LIGHT: case THEME_PRESET_LIGHT:
@ -94,6 +483,8 @@ void themeStartup(ThemePreset preset) {
if (theme != NULL) { if (theme != NULL) {
if (!colorFromSetting(config_setting_lookup(theme, "textColor"), &text)) if (!colorFromSetting(config_setting_lookup(theme, "textColor"), &text))
text = themeDefault->textColor; text = themeDefault->textColor;
if (colorFromSetting(config_setting_lookup(theme, "logoColor"), &logoColor))
logoColor_set = true;
if (!colorFromSetting(config_setting_lookup(theme, "attentionTextColor"), &attentionText)) if (!colorFromSetting(config_setting_lookup(theme, "attentionTextColor"), &attentionText))
attentionText = themeDefault->attentionTextColor; attentionText = themeDefault->attentionTextColor;
if (!colorFromSetting(config_setting_lookup(theme, "frontWaveColor"), &frontWave)) if (!colorFromSetting(config_setting_lookup(theme, "frontWaveColor"), &frontWave))
@ -106,6 +497,8 @@ void themeStartup(ThemePreset preset) {
background = themeDefault->backgroundColor; background = themeDefault->backgroundColor;
if (!colorFromSetting(config_setting_lookup(theme, "highlightColor"), &highlight)) if (!colorFromSetting(config_setting_lookup(theme, "highlightColor"), &highlight))
highlight = themeDefault->highlightColor; highlight = themeDefault->highlightColor;
if (!colorFromSetting(config_setting_lookup(theme, "highlightGradientEdgeColor"), &highlightGradientEdgeColor))
highlightGradientEdgeColor = themeDefault->highlightGradientEdgeColor;
if (!colorFromSetting(config_setting_lookup(theme, "separatorColor"), &separator)) if (!colorFromSetting(config_setting_lookup(theme, "separatorColor"), &separator))
separator = themeDefault->separatorColor; separator = themeDefault->separatorColor;
if (!colorFromSetting(config_setting_lookup(theme, "borderColor"), &borderColor)) if (!colorFromSetting(config_setting_lookup(theme, "borderColor"), &borderColor))
@ -134,34 +527,129 @@ void themeStartup(ThemePreset preset) {
starOffText = themeDefault->labelStarOffText; starOffText = themeDefault->labelStarOffText;
themeCurrent = (theme_t) { themeCurrent = (theme_t) {
.textColor = text, .textColor = text,
.logoColor = logoColor,
.attentionTextColor = attentionText, .attentionTextColor = attentionText,
.frontWaveColor = frontWave, .frontWaveColor = frontWave,
.middleWaveColor = middleWave, .middleWaveColor = middleWave,
.backWaveColor = backWave, .backWaveColor = backWave,
.backgroundColor = background, .backgroundColor = background,
.highlightColor = highlight, .highlightColor = highlight,
.highlightGradientEdgeColor = highlightGradientEdgeColor,
.separatorColor = separator, .separatorColor = separator,
.borderColor = borderColor, .borderColor = borderColor,
.borderTextColor = borderTextColor, .borderTextColor = borderTextColor,
.progressBarColor = progressBarColor, .progressBarColor = progressBarColor,
.logoColor_set = logoColor_set,
.enableWaveBlending = waveBlending, .enableWaveBlending = waveBlending,
.hbmenuLogoImage = themeDefault->hbmenuLogoImage
}; };
strncpy(themeCurrent.buttonAText, AText, sizeof(themeCurrent.buttonAText)-1); strncpy(themeCurrent.buttonAText, AText, sizeof(themeCurrent.buttonAText));
strncpy(themeCurrent.buttonBText, BText, sizeof(themeCurrent.buttonBText)-1); themeCurrent.buttonAText[sizeof(themeCurrent.buttonAText)-1] = 0;
strncpy(themeCurrent.buttonXText, XText, sizeof(themeCurrent.buttonXText)-1); strncpy(themeCurrent.buttonBText, BText, sizeof(themeCurrent.buttonBText));
strncpy(themeCurrent.buttonYText, YText, sizeof(themeCurrent.buttonYText)-1); themeCurrent.buttonBText[sizeof(themeCurrent.buttonBText)-1] = 0;
strncpy(themeCurrent.buttonPText, PText, sizeof(themeCurrent.buttonPText)-1); strncpy(themeCurrent.buttonXText, XText, sizeof(themeCurrent.buttonXText));
strncpy(themeCurrent.buttonMText, MText, sizeof(themeCurrent.buttonMText)-1); themeCurrent.buttonXText[sizeof(themeCurrent.buttonXText)-1] = 0;
strncpy(themeCurrent.labelStarOffText, starOffText, sizeof(themeCurrent.labelStarOffText)-1); strncpy(themeCurrent.buttonYText, YText, sizeof(themeCurrent.buttonYText));
strncpy(themeCurrent.labelStarOnText, starOnText, sizeof(themeCurrent.labelStarOnText)-1); themeCurrent.buttonYText[sizeof(themeCurrent.buttonYText)-1] = 0;
strncpy(themeCurrent.buttonPText, PText, sizeof(themeCurrent.buttonPText));
themeCurrent.buttonPText[sizeof(themeCurrent.buttonPText)-1] = 0;
strncpy(themeCurrent.buttonMText, MText, sizeof(themeCurrent.buttonMText));
themeCurrent.buttonMText[sizeof(themeCurrent.buttonMText)-1] = 0;
strncpy(themeCurrent.labelStarOnText, starOnText, sizeof(themeCurrent.labelStarOnText));
themeCurrent.labelStarOnText[sizeof(themeCurrent.labelStarOnText)-1] = 0;
strncpy(themeCurrent.labelStarOffText, starOffText, sizeof(themeCurrent.labelStarOffText));
themeCurrent.labelStarOffText[sizeof(themeCurrent.labelStarOffText)-1] = 0;
} else { } else {
themeCurrent = *themeDefault; themeCurrent = *themeDefault;
} }
memcpy(themeCurrent.layoutObjects, themeCommon.layoutObjects, sizeof(themeCommon.layoutObjects));
layout = config_lookup(&cfg, "layout");
if (layout != NULL) {
layoutObjectFromSetting(config_setting_lookup(layout, "logo"), &themeCurrent.layoutObjects[ThemeLayoutId_Logo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "hbmenuVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_HbmenuVersion], true);
layoutObjectFromSetting(config_setting_lookup(layout, "loaderInfo"), &themeCurrent.layoutObjects[ThemeLayoutId_LoaderInfo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "attentionText"), &themeCurrent.layoutObjects[ThemeLayoutId_AttentionText], true);
layoutObjectFromSetting(config_setting_lookup(layout, "logInfo"), &themeCurrent.layoutObjects[ThemeLayoutId_LogInfo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "infoMsg"), &themeCurrent.layoutObjects[ThemeLayoutId_InfoMsg], true);
layoutObjectFromSetting(config_setting_lookup(layout, "menuPath"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuPath], false);
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);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonA"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonAText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonAText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonB"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonB], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonBText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonBText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonY"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonY], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonYText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonYText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonM"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonM], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonMText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonMText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonX"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonX], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonXText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonXText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "networkIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_NetworkIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "batteryCharge"), &themeCurrent.layoutObjects[ThemeLayoutId_BatteryCharge], false);
layoutObjectFromSetting(config_setting_lookup(layout, "batteryIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_BatteryIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "chargingIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_ChargingIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "status"), &themeCurrent.layoutObjects[ThemeLayoutId_Status], false);
layoutObjectFromSetting(config_setting_lookup(layout, "temperature"), &themeCurrent.layoutObjects[ThemeLayoutId_Temperature], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuList"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuList], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListTiles"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListName"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListName], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryName"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryName], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryAuthor"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryAuthor], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion], false);
}
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]);
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 { } else {
themeCurrent = *themeDefault; themeCurrent = *themeDefault;
memcpy(themeCurrent.layoutObjects, themeCommon.layoutObjects, sizeof(themeCommon.layoutObjects));
} }
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
if (layoutobj->posEnd[0] < 1) layoutobj->posEnd[0] = 1;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
if (layoutobj->size[0] <= 0 || layoutobj->size[1] <= 0 || layoutobj->size[0] > layoutobj->imageSize[0] || layoutobj->size[1] > layoutobj->imageSize[1]) {
layoutobj->size[0] = layoutobj->imageSize[0];
layoutobj->size[1] = layoutobj->imageSize[1];
}
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
if (layoutobj->size[0] <= 0 || layoutobj->size[1] <= 0 || layoutobj->size[0] > layoutobj->imageSize[0] || layoutobj->size[1] > layoutobj->imageSize[1]) {
layoutobj->size[0] = layoutobj->imageSize[0];
layoutobj->size[1] = layoutobj->imageSize[1];
}
config_destroy(&cfg); config_destroy(&cfg);
if (is_archive) PHYSFS_unmount(theme_archive_path);
#ifdef __SWITCH__
if (is_romfs) romfsUnmount("theme");
#endif
} }
void GetThemePathFromConfig(char* themePath, size_t size) { void GetThemePathFromConfig(char* themePath, size_t size) {

View File

@ -3,19 +3,42 @@
#include "common.h" #include "common.h"
#include <libconfig.h> #include <libconfig.h>
typedef enum
{
THEME_PRESET_LIGHT,
THEME_PRESET_DARK,
} ThemePreset;
typedef struct
{
bool visible;
bool posType; // false = absolute, true = relative
int posStart[2];
int posEnd[2];
int size[2]; // width/height
int imageSize[2]; // width/height for the actual image data
int touchSize[2];
int posFinal[2];
uint32_t textSize[2];
u32 font;
} ThemeLayoutObject;
typedef struct typedef struct
{ {
color_t textColor; color_t textColor;
color_t logoColor;
color_t attentionTextColor; color_t attentionTextColor;
color_t frontWaveColor; color_t frontWaveColor;
color_t middleWaveColor; color_t middleWaveColor;
color_t backWaveColor; color_t backWaveColor;
color_t backgroundColor; color_t backgroundColor;
color_t highlightColor; color_t highlightColor;
color_t highlightGradientEdgeColor;
color_t separatorColor; color_t separatorColor;
color_t borderColor; color_t borderColor;
color_t borderTextColor; color_t borderTextColor;
color_t progressBarColor; color_t progressBarColor;
bool logoColor_set;
bool enableWaveBlending; bool enableWaveBlending;
char buttonAText[32]; char buttonAText[32];
char buttonBText[32]; char buttonBText[32];
@ -25,14 +48,9 @@ typedef struct
char buttonMText[32]; char buttonMText[32];
char labelStarOnText[32]; char labelStarOnText[32];
char labelStarOffText[32]; char labelStarOffText[32];
const uint8_t *hbmenuLogoImage;
} theme_t;
typedef enum ThemeLayoutObject layoutObjects[ThemeLayoutId_Total];
{ } theme_t;
THEME_PRESET_LIGHT,
THEME_PRESET_DARK,
} ThemePreset;
bool colorFromSetting(config_setting_t *rgba, color_t *col); bool colorFromSetting(config_setting_t *rgba, color_t *col);
void themeStartup(ThemePreset preset); void themeStartup(ThemePreset preset);

7
common/thermalstatus.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "common.h"
bool thermalstatusInit(void);
void thermalstatusExit(void);
bool thermalstatusGetDetails(s32 *temperature);

View File

@ -68,15 +68,26 @@ static void launchFile(const char* path, argData_s* args)
init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf)); init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf));
Result rc = envSetNextLoad(path, argBuf); struct stat st;
if(R_FAILED(rc)) {
if (stat(path, &st) == -1) {
memset(msg, 0, sizeof(msg)); memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, "%s\n0x%x", textGetString(StrId_AppLaunchError), rc); snprintf(msg, sizeof(msg)-1, textGetString(StrId_NroNotFound), path);
menuCreateMsgBox(780, 300, msg); menuCreateMsgBox(780, 300, msg);
menuScan(".");
} }
else { else {
uiExitLoop(); Result rc = envSetNextLoad(path, argBuf);
if(R_FAILED(rc)) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_AppLaunchError), R_MODULE(rc), R_DESCRIPTION(rc));
menuCreateMsgBox(780, 300, msg);
}
else {
uiExitLoop();
}
} }
} }

View File

@ -1,19 +1,22 @@
#include <switch.h> #include <switch.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <physfs.h>
#include "../common/common.h" #include "../common/common.h"
#include "nx_graphics.h"
#include "nx_touch.h" #include "nx_touch.h"
// Define the desired framebuffer resolution (here we set it to 720p). // Define the desired framebuffer resolution (here we set it to 720p).
#define FB_WIDTH 1280 #define FB_WIDTH 1280
#define FB_HEIGHT 720 #define FB_HEIGHT 720
Framebuffer g_framebufObj;
uint8_t* g_framebuf; uint8_t* g_framebuf;
u32 g_framebuf_width; u32 g_framebuf_width;
PadState g_pad;
PadRepeater g_pad_repeater;
bool menuUpdateErrorScreen(void); bool menuUpdateErrorScreen(void);
#ifdef PERF_LOG #ifdef PERF_LOG
@ -39,35 +42,44 @@ int main(int argc, char **argv)
u64 start_tick=0; u64 start_tick=0;
#endif #endif
padConfigureInput(8, HidNpadStyleSet_NpadStandard);
padInitializeAny(&g_pad);
padRepeaterInitialize(&g_pad_repeater, 20, 10);
hidSetNpadHandheldActivationMode(HidNpadHandheldActivationMode_Single);
touchInit();
memset(errormsg, 0, sizeof(errormsg)); memset(errormsg, 0, sizeof(errormsg));
appletLockExit(); appletLockExit();
appletSetScreenShotPermission(AppletScreenShotPermission_Enable); appletSetScreenShotPermission(AppletScreenShotPermission_Enable);
ColorSetId theme; ColorSetId theme = ColorSetId_Light;
rc = setsysInitialize(); rc = setsysInitialize();
if (R_FAILED(rc)) snprintf(errormsg, sizeof(errormsg)-1, "Error: setsysInitialize() failed: 0x%x.", rc);
if (R_SUCCEEDED(rc)) setsysGetColorSetId(&theme);
if (R_SUCCEEDED(rc)) { if (R_SUCCEEDED(rc)) {
rc = plInitialize(); setsysGetColorSetId(&theme);
if (R_FAILED(rc)) snprintf(errormsg, sizeof(errormsg)-1, "Error: plInitialize() failed: 0x%x.", rc); setsysExit();
} }
if (R_SUCCEEDED(rc)) { if (R_SUCCEEDED(rc)) {
rc = textInit(); rc = textInit();
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: textInit() failed: 0x%x.", rc); snprintf(errormsg, sizeof(errormsg)-1, "Error: textInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
} }
} }
if (R_SUCCEEDED(rc)) menuStartupPath(); 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)) { if (R_SUCCEEDED(rc)) {
rc = assetsInit(); rc = assetsInit();
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: assetsInit() failed: 0x%x.", rc); snprintf(errormsg, sizeof(errormsg)-1, "Error: assetsInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
} }
} }
@ -78,7 +90,7 @@ int main(int argc, char **argv)
if (R_SUCCEEDED(rc)) { if (R_SUCCEEDED(rc)) {
rc = netloaderInit(); rc = netloaderInit();
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 0x%x.", rc); snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
} }
} }
@ -115,7 +127,7 @@ int main(int argc, char **argv)
if (R_FAILED(lastret)) { if (R_FAILED(lastret)) {
memset(msg, 0, sizeof(msg)); memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, "%s\n0x%x", textGetString(StrId_LastLoadResult), lastret); snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_LastLoadResult), R_MODULE(lastret), R_DESCRIPTION(lastret));
menuCreateMsgBox(780, 300, msg); menuCreateMsgBox(780, 300, msg);
} }
@ -124,8 +136,7 @@ int main(int argc, char **argv)
if (errormsg[0]) error_screen = 1; if (errormsg[0]) error_screen = 1;
if (!error_screen) { if (!error_screen) {
framebufferCreate(&g_framebufObj, nwindowGetDefault(), FB_WIDTH, FB_HEIGHT, PIXEL_FORMAT_RGBA_8888, 2); graphicsInit(FB_WIDTH, FB_HEIGHT);
framebufferMakeLinear(&g_framebufObj);
} }
else { else {
consoleInit(NULL); consoleInit(NULL);
@ -135,16 +146,17 @@ int main(int argc, char **argv)
while (appletMainLoop()) while (appletMainLoop())
{ {
// Scan the gamepad. This should be done once for each frame
padUpdate(&g_pad);
//Scan all the inputs. This should be done once for each frame padRepeaterUpdate(&g_pad_repeater, padGetButtons(&g_pad) & (
hidScanInput(); HidNpadButton_AnyLeft | HidNpadButton_AnyUp | HidNpadButton_AnyRight | HidNpadButton_AnyDown
));
if (!error_screen) { if (!error_screen) {
if (!uiUpdate()) break; if (!uiUpdate()) break;
g_framebuf = framebufferBegin(&g_framebufObj, &g_framebuf_width); g_framebuf = graphicsFrameBegin(&g_framebuf_width);
#ifdef PERF_LOG #ifdef PERF_LOG
start_tick = svcGetSystemTick(); start_tick = armGetSystemTick();
#endif #endif
memset(g_framebuf, 237, g_framebuf_width * FB_HEIGHT); memset(g_framebuf, 237, g_framebuf_width * FB_HEIGHT);
menuLoop(); menuLoop();
@ -154,10 +166,10 @@ int main(int argc, char **argv)
} }
if (!error_screen) { if (!error_screen) {
framebufferEnd(&g_framebufObj); graphicsFrameEnd();
#ifdef PERF_LOG #ifdef PERF_LOG
g_tickdiff_frame = svcGetSystemTick() - start_tick; g_tickdiff_frame = armGetSystemTick() - start_tick;
#endif #endif
} }
else { else {
@ -166,7 +178,7 @@ int main(int argc, char **argv)
} }
if (!error_screen) { if (!error_screen) {
framebufferClose(&g_framebufObj); graphicsExit();
} }
else { else {
consoleExit(NULL); consoleExit(NULL);
@ -185,41 +197,49 @@ int main(int argc, char **argv)
netloaderExit(); netloaderExit();
powerExit(); powerExit();
assetsExit(); assetsExit();
plExit(); PHYSFS_deinit();
setsysExit();
appletUnlockExit(); appletUnlockExit();
return 0; return 0;
} }
u64 menuGetKeysDown(void) {
u64 keys = padGetButtonsDown(&g_pad);
keys |= padRepeaterGetButtons(&g_pad_repeater);
return keys;
}
//This is implemented here due to the hid code. //This is implemented here due to the hid code.
bool menuUpdate(void) { bool menuUpdate(void) {
bool exitflag = 0; bool exitflag = 0;
menu_s* menu = menuGetCurrent(); menu_s* menu = menuGetCurrent();
u32 down = hidKeysDown(CONTROLLER_P1_AUTO); u64 down = menuGetKeysDown();
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
handleTouch(menu); handleTouch(menu);
if (down & KEY_Y) if (down & HidNpadButton_Y)
{ {
launchMenuNetloaderTask(); launchMenuNetloaderTask();
} }
else if (down & KEY_X) else if (down & HidNpadButton_X)
{ {
menuHandleXButton(); menuHandleXButton();
} }
else if (down & KEY_A) else if (down & HidNpadButton_A)
{ {
menuHandleAButton(); menuHandleAButton();
} }
else if (down & KEY_B) else if (down & HidNpadButton_B)
{ {
launchMenuBackTask(); launchMenuBackTask();
} }
else if(down & KEY_MINUS){ else if(down & HidNpadButton_Minus){
themeMenuStartup(); themeMenuStartup();
} }
else if (down & KEY_PLUS) else if (down & HidNpadButton_Plus)
{ {
exitflag = 1; exitflag = 1;
} }
@ -227,10 +247,10 @@ bool menuUpdate(void) {
{ {
int move = 0; int move = 0;
if (down & KEY_LEFT) move--; if (down & HidNpadButton_AnyLeft) move--;
if (down & KEY_RIGHT) move++; if (down & HidNpadButton_AnyRight) move++;
if (down & KEY_DOWN) move-=7; if (down & HidNpadButton_AnyDown) move-=entries_count;
if (down & KEY_UP) move+=7; if (down & HidNpadButton_AnyUp) move+=entries_count;
int newEntry = menu->curEntry + move; int newEntry = menu->curEntry + move;
if (newEntry < 0) newEntry = 0; if (newEntry < 0) newEntry = 0;
@ -243,9 +263,9 @@ bool menuUpdate(void) {
bool menuUpdateErrorScreen(void) { bool menuUpdateErrorScreen(void) {
bool exitflag = 0; bool exitflag = 0;
u32 down = hidKeysDown(CONTROLLER_P1_AUTO); u64 down = menuGetKeysDown();
if (down & KEY_PLUS) if (down & HidNpadButton_Plus)
{ {
exitflag = 1; exitflag = 1;
} }

View File

@ -72,7 +72,7 @@ static void audio_playback_thread(void* arg)
playing = 1; playing = 1;
} }
if (R_SUCCEEDED(audoutWaitPlayFinish(&released_buffer, &released_count, U64_MAX))) if (R_SUCCEEDED(audoutWaitPlayFinish(&released_buffer, &released_count, UINT64_MAX)))
playing = 0; playing = 0;
} }
} }

141
nx_main/nx_graphics.c Normal file
View File

@ -0,0 +1,141 @@
#include <switch.h>
#include <deko3d.h>
#include "nx_graphics.h"
#define FB_NUM 2
#define CMDMEMSIZE 0x1000
static u32 s_fbWidth, s_fbHeight;
static DkDevice s_device;
static DkMemBlock s_fbMemBlock, s_workMemBlock, s_cmdMemBlock;
static DkSwapchain s_swapchain;
static DkCmdBuf s_cmdBuf;
static DkCmdList s_cmdLists[FB_NUM];
static DkFence s_fence;
static DkQueue s_queue;
void graphicsInit(u32 width, u32 height)
{
DkImageLayoutMaker imgLayoutMaker;
DkMemBlockMaker memBlockMaker;
// Create the device, which is the root object
DkDeviceMaker deviceMaker;
dkDeviceMakerDefaults(&deviceMaker);
s_device = dkDeviceCreate(&deviceMaker);
// Calculate layout for the framebuffers
DkImageLayout fbLayout;
dkImageLayoutMakerDefaults(&imgLayoutMaker, s_device);
imgLayoutMaker.flags = DkImageFlags_UsagePresent;
imgLayoutMaker.format = DkImageFormat_RGBA8_Unorm;
imgLayoutMaker.dimensions[0] = s_fbWidth = width;
imgLayoutMaker.dimensions[1] = s_fbHeight = height;
dkImageLayoutInitialize(&fbLayout, &imgLayoutMaker);
// Retrieve necessary size and alignment for the framebuffers
uint32_t fbSize = dkImageLayoutGetSize(&fbLayout);
uint32_t fbAlign = dkImageLayoutGetAlignment(&fbLayout);
fbSize = (fbSize + fbAlign - 1) &~ (fbAlign - 1);
// Create a memory block that will host the framebuffers
dkMemBlockMakerDefaults(&memBlockMaker, s_device, FB_NUM*fbSize);
memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image;
s_fbMemBlock = dkMemBlockCreate(&memBlockMaker);
// Initialize the framebuffers with the layout and backing memory we've just created
DkImage fbImages[FB_NUM];
DkImage const* swapchainImages[FB_NUM];
for (unsigned i = 0; i < FB_NUM; i ++)
{
swapchainImages[i] = &fbImages[i];
dkImageInitialize(&fbImages[i], &fbLayout, s_fbMemBlock, i*fbSize);
}
// Create a swapchain out of the framebuffers we've just initialized
DkSwapchainMaker swapchainMaker;
dkSwapchainMakerDefaults(&swapchainMaker, s_device, nwindowGetDefault(), swapchainImages, FB_NUM);
s_swapchain = dkSwapchainCreate(&swapchainMaker);
// Create a memory block for the linear framebuffer
dkMemBlockMakerDefaults(&memBlockMaker, s_device, width*height*4);
memBlockMaker.flags = DkMemBlockFlags_CpuCached | DkMemBlockFlags_GpuUncached;
s_workMemBlock = dkMemBlockCreate(&memBlockMaker);
// Create a memory block for the command lists
dkMemBlockMakerDefaults(&memBlockMaker, s_device, CMDMEMSIZE);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
s_cmdMemBlock = dkMemBlockCreate(&memBlockMaker);
// Create a command buffer
DkCmdBufMaker cmdBufMaker;
dkCmdBufMakerDefaults(&cmdBufMaker, s_device);
s_cmdBuf = dkCmdBufCreate(&cmdBufMaker);
dkCmdBufAddMemory(s_cmdBuf, s_cmdMemBlock, 0, CMDMEMSIZE);
// Define source for linear framebuffer copies
const DkCopyBuf linearSrc = {
.addr = dkMemBlockGetGpuAddr(s_workMemBlock),
.rowLength = 0,
.imageHeight = 0,
};
// Define rectangle for the copies
const DkImageRect copyRect = {
.x = 0, .y = 0, .z = 0,
.width = width, .height = height, .depth = 1,
};
// Record command lists for the copies
for (unsigned i = 0; i < FB_NUM; i ++) {
DkImageView tiledDst;
dkImageViewDefaults(&tiledDst, &fbImages[i]);
dkCmdBufCopyBufferToImage(s_cmdBuf, &linearSrc, &tiledDst, &copyRect, 0);
dkCmdBufSignalFence(s_cmdBuf, &s_fence, false);
s_cmdLists[i] = dkCmdBufFinishList(s_cmdBuf);
}
// Create a queue, to which we will submit our command lists
DkQueueMaker queueMaker;
dkQueueMakerDefaults(&queueMaker, s_device);
queueMaker.flags = 0; // we will only use this queue for transferring
s_queue = dkQueueCreate(&queueMaker);
}
void graphicsExit(void)
{
// Make sure the queue is idle before destroying anything
dkQueueWaitIdle(s_queue);
// Destroy all the resources we've created
dkQueueDestroy(s_queue);
dkCmdBufDestroy(s_cmdBuf);
dkMemBlockDestroy(s_cmdMemBlock);
dkMemBlockDestroy(s_workMemBlock);
dkSwapchainDestroy(s_swapchain);
dkMemBlockDestroy(s_fbMemBlock);
dkDeviceDestroy(s_device);
}
void* graphicsFrameBegin(u32* out_stride)
{
// Ensure the GPU is not reading from the framebuffer
dkFenceWait(&s_fence, -1);
// Return information
if (out_stride) *out_stride = s_fbWidth*4;
return dkMemBlockGetCpuAddr(s_workMemBlock);
}
void graphicsFrameEnd(void)
{
// Flush the linear framebuffer
dkMemBlockFlushCpuCache(s_workMemBlock, 0, s_fbWidth*s_fbHeight*4);
// Present a frame
int slot = dkQueueAcquireImage(s_queue, s_swapchain);
dkQueueSubmitCommands(s_queue, s_cmdLists[slot]);
dkQueuePresentImage(s_queue, s_swapchain, slot);
}

9
nx_main/nx_graphics.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <switch.h>
void graphicsInit(u32 width, u32 height);
void graphicsExit(void);
void* graphicsFrameBegin(u32* out_stride);
void graphicsFrameEnd(void);

View File

@ -8,7 +8,7 @@ static bool powerCacheIsCharging;
static PsmSession powerSession; static PsmSession powerSession;
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) { bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
ChargerType charger = ChargerType_None; PsmChargerType charger = PsmChargerType_Unconnected;
bool hwReadsSucceeded = false; bool hwReadsSucceeded = false;
bool use_cache = false; bool use_cache = false;
Result rc = 0; Result rc = 0;
@ -31,7 +31,7 @@ bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
else { else {
rc = psmGetChargerType(&charger); rc = psmGetChargerType(&charger);
hwReadsSucceeded &= R_SUCCEEDED(rc); hwReadsSucceeded &= R_SUCCEEDED(rc);
*isCharging = (charger > ChargerType_None); *isCharging = (charger != PsmChargerType_Unconnected);
} }
powerCacheCharge = *batteryCharge; powerCacheCharge = *batteryCharge;

View File

@ -0,0 +1,14 @@
#include "../common/common.h"
bool thermalstatusInit(void) {
return R_SUCCEEDED(tsInitialize());
}
void thermalstatusExit(void) {
tsExit();
}
bool thermalstatusGetDetails(s32 *temperature) {
return R_SUCCEEDED(tsGetTemperature(TsLocation_Internal, temperature));
}

View File

@ -5,43 +5,33 @@
#define VERTICAL_SWIPE_MINIMUM_DISTANCE 300 #define VERTICAL_SWIPE_MINIMUM_DISTANCE 300
#define HORIZONTAL_SWIPE_VERTICAL_PLAY 250 #define HORIZONTAL_SWIPE_VERTICAL_PLAY 250
#define HORIZONTAL_SWIPE_MINIMUM_DISTANCE 300 #define HORIZONTAL_SWIPE_MINIMUM_DISTANCE 300
#define LISTING_START_Y 475
#define LISTING_END_Y 647
#define BUTTON_START_Y 672
#define BUTTON_END_Y 704
#define BACK_BUTTON_START_X 966
#define BACK_BUTTON_END_X 1048
#define LAUNCH_BUTTON_START_X 1092
#define LAUNCH_BUTTON_END_X 1200
#define STAR_BUTTON_START_X 426
#define STAR_BUTTON_END_X 490
#define STAR_BUTTON_START_Y 100
#define STAR_BUTTON_END_Y 161
#define distance(x1, y1, x2, y2) (int) sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1))) #define distance(x1, y1, x2, y2) (int) sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
struct touchInfo_s touchInfo; struct touchInfo_s touchInfo;
void touchInit() { void touchInit(void) {
touchInfo.gestureInProgress = false; touchInfo.gestureInProgress = false;
touchInfo.isTap = true; touchInfo.isTap = true;
touchInfo.initMenuXPos = 0; touchInfo.initMenuXPos = 0;
touchInfo.initMenuIndex = 0; touchInfo.initMenuIndex = 0;
touchInfo.lastSlideSpeed = 0;
hidInitializeTouchScreen();
} }
void handleTappingOnApp(menu_s* menu, int px) { void handleTappingOnApp(menu_s* menu, int px) {
int i = 0; int i = 0;
menuEntry_s *me = NULL; menuEntry_s *me = NULL;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) { for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) {
int entry_start_x = 29 + i * (140 + 30); int entry_start_x = layoutobj->posStart[0] + i * layoutobj->posEnd[0];
int screen_width = 1280; int screen_width = 1280;
if (entry_start_x >= (screen_width - menu->xPos)) if (entry_start_x >= (screen_width - menu->xPos))
break; break;
if (px >= (entry_start_x + menu->xPos) && px <= (entry_start_x + menu->xPos) + 140 ) { if (px >= (entry_start_x + menu->xPos) && px <= (entry_start_x + menu->xPos) + layoutobj->size[0]) {
launchMenuEntryTask(me); launchMenuEntryTask(me);
break; break;
} }
@ -58,88 +48,97 @@ void handleTappingOnOpenLaunch(menu_s* menu) {
} }
} }
static inline bool checkInsideTextLayoutObject(ThemeLayoutId id, int x, int y) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[id];
if (!layoutobj->visible) return false;
return x > layoutobj->posFinal[0] && x < layoutobj->posFinal[0]+(layoutobj->touchSize[0] ? layoutobj->touchSize[0] : layoutobj->textSize[0]) && y > layoutobj->posFinal[1]-layoutobj->touchSize[1] && y < layoutobj->posFinal[1];
}
void handleTouch(menu_s* menu) { void handleTouch(menu_s* menu) {
touchPosition currentTouch; ThemeLayoutObject *layoutobj = NULL;
u32 touches = hidTouchCount(); HidTouchScreenState touch = {0};
hidGetTouchScreenStates(&touch, 1);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
// On touch start. // On touch start.
if (touches == 1 && !touchInfo.gestureInProgress) { if (touch.count == 1 && !touchInfo.gestureInProgress) {
hidTouchRead(&currentTouch, 0);
touchInfo.gestureInProgress = true; touchInfo.gestureInProgress = true;
touchInfo.firstTouch = currentTouch; touchInfo.firstTouch = touch.touches[0];
touchInfo.prevTouch = currentTouch; touchInfo.prevTouch = touch.touches[0];
touchInfo.isTap = true; touchInfo.isTap = true;
touchInfo.initMenuXPos = menu->xPos; touchInfo.initMenuXPos = menu->xPos;
touchInfo.initMenuIndex = menu->curEntry; touchInfo.initMenuIndex = menu->curEntry;
touchInfo.lastSlideSpeed = 0;
menu->slideSpeed = 0;
} }
// On touch moving. // On touch moving.
else if (touches >= 1 && touchInfo.gestureInProgress) { else if (touch.count >= 1 && touchInfo.gestureInProgress) {
hidTouchRead(&currentTouch, 0); touchInfo.lastSlideSpeed = ((int)(touch.touches[0].x - touchInfo.prevTouch.x));
touchInfo.prevTouch = currentTouch; touchInfo.prevTouch = touch.touches[0];
if (touchInfo.isTap && (abs(touchInfo.firstTouch.px - currentTouch.px) > TAP_MOVEMENT_GAP || abs(touchInfo.firstTouch.py - currentTouch.py) > TAP_MOVEMENT_GAP)) { if (touchInfo.isTap && (abs(touchInfo.firstTouch.x - touch.touches[0].x) > TAP_MOVEMENT_GAP || abs(touchInfo.firstTouch.y - touch.touches[0].y) > TAP_MOVEMENT_GAP)) {
touchInfo.isTap = false; touchInfo.isTap = false;
} }
if (!menuIsMsgBoxOpen() && touchInfo.firstTouch.py > LISTING_START_Y && touchInfo.firstTouch.py < LISTING_END_Y && !touchInfo.isTap && menu->nEntries > 7) { if (!menuIsMsgBoxOpen() && touchInfo.firstTouch.y > layoutobj->posStart[1] && touchInfo.firstTouch.y < layoutobj->posStart[1]+layoutobj->size[1] && !touchInfo.isTap && menu->nEntries > entries_count) {
menu->xPos = touchInfo.initMenuXPos + (currentTouch.px - touchInfo.firstTouch.px);
menu->curEntry = touchInfo.initMenuIndex + ((int) (touchInfo.firstTouch.px - currentTouch.px) / 170);
if (menu->curEntry < 0) if (!touchInfo.isTap) {
menu->curEntry = 0; menu->slideSpeed = touchInfo.lastSlideSpeed;
}
if (menu->curEntry >= menu->nEntries - 6)
menu->curEntry = menu->nEntries - 7;
} }
} }
// On touch end. // On touch end.
else if (touchInfo.gestureInProgress) { else if (touchInfo.gestureInProgress) {
int x1 = touchInfo.firstTouch.px; int x1 = touchInfo.firstTouch.x;
int y1 = touchInfo.firstTouch.py; int y1 = touchInfo.firstTouch.y;
int x2 = touchInfo.prevTouch.px; int x2 = touchInfo.prevTouch.x;
int y2 = touchInfo.prevTouch.py; int y2 = touchInfo.prevTouch.y;
if (!touchInfo.isTap) {
menu->slideSpeed = touchInfo.lastSlideSpeed;
}
bool netloader_active = menuIsNetloaderActive(); bool netloader_active = menuIsNetloaderActive();
if (menuIsMsgBoxOpen() && !netloader_active) { if (menuIsMsgBoxOpen() && !netloader_active) {
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
MessageBox currMsgBox = menuGetCurrentMsgBox(); MessageBox currMsgBox = menuGetCurrentMsgBox();
int start_x = 1280 / 2 - currMsgBox.width / 2; int start_x = 1280 / 2 - currMsgBox.width / 2;
int start_y = (720 / 2 - currMsgBox.height / 2) + (currMsgBox.height - 80); int start_y = (720 / 2 - currMsgBox.height / 2) + currMsgBox.height;
int end_x = start_x + currMsgBox.width; int end_x = start_x + currMsgBox.width;
int end_y = start_y + 80; int end_y = start_y;
start_y+= layoutobj->posStart[1];
if (x1 > start_x && x1 < end_x && y1 > start_y && y1 < end_y && touchInfo.isTap) { if (x1 > start_x && x1 < end_x && y1 > start_y && y1 < end_y && touchInfo.isTap) {
menuCloseMsgBox(); menuCloseMsgBox();
} }
} else if (touchInfo.isTap && !netloader_active) { } else if (touchInfo.isTap && !netloader_active) {
// App Icons // App Icons
if (y1 > LISTING_START_Y && y1 < LISTING_END_Y) { if (y1 > layoutobj->posStart[1] && y1 < layoutobj->posStart[1]+layoutobj->size[1]) {
handleTappingOnApp(menu, touchInfo.prevTouch.px); handleTappingOnApp(menu, touchInfo.prevTouch.x);
} }
// Bottom Buttons // Bottom Buttons
else if (y1 > BUTTON_START_Y && y1 < BUTTON_END_Y) { else {
// Back Button for non-empty directory // Back Button
if (menu->nEntries != 0 && x1 > BACK_BUTTON_START_X && x1 < BACK_BUTTON_END_X) { if (checkInsideTextLayoutObject(ThemeLayoutId_ButtonB, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonBText, x1, y1)) {
launchMenuBackTask(); launchMenuBackTask();
} }
// Open/Launch Button / Back Button for empty directories // Open/Launch Button
else if (x1 > LAUNCH_BUTTON_START_X && x1 < LAUNCH_BUTTON_END_X) { else if (menu->nEntries != 0 && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonA, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonAText, x1, y1))) {
if (menu->nEntries == 0) { handleTappingOnOpenLaunch(menu);
launchMenuBackTask();
} else {
handleTappingOnOpenLaunch(menu);
}
} }
} // Star Button
// Star else if (menu->nEntries != 0) {
else { int i;
int i; menuEntry_s* me;
menuEntry_s* me; for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next); if (me->type != ENTRY_TYPE_THEME && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonX, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonXText, x1, y1))) {
if (me->type != ENTRY_TYPE_THEME && x1 > STAR_BUTTON_START_X && x1 < STAR_BUTTON_END_X menuHandleXButton();
&& y1 > STAR_BUTTON_START_Y && y1 < STAR_BUTTON_END_Y) { }
menuHandleXButton();
} }
} }
} }
@ -155,7 +154,7 @@ void handleTouch(menu_s* menu) {
} }
} }
// Horizontal Swipe // Horizontal Swipe
else if (y1 < LISTING_START_Y && y2 < LISTING_START_Y) { else if (y1 < layoutobj->posStart[1] && y2 < layoutobj->posStart[1]) {
if (abs(y1 - y2) < HORIZONTAL_SWIPE_VERTICAL_PLAY && distance(x1, y1, x2, y2) > HORIZONTAL_SWIPE_MINIMUM_DISTANCE) { if (abs(y1 - y2) < HORIZONTAL_SWIPE_VERTICAL_PLAY && distance(x1, y1, x2, y2) > HORIZONTAL_SWIPE_MINIMUM_DISTANCE) {
// Swipe left to go into theme-menu // Swipe left to go into theme-menu
if (x1 - x2 > 0) { if (x1 - x2 > 0) {

View File

@ -5,11 +5,13 @@
struct touchInfo_s { struct touchInfo_s {
bool gestureInProgress; bool gestureInProgress;
touchPosition firstTouch; HidTouchState firstTouch;
touchPosition prevTouch; HidTouchState prevTouch;
bool isTap; bool isTap;
int initMenuXPos; int initMenuXPos;
int initMenuIndex; int initMenuIndex;
int lastSlideSpeed;
}; };
void touchInit(void);
void handleTouch(menu_s* menu); void handleTouch(menu_s* menu);

View File

@ -1,6 +1,7 @@
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <physfs.h>
extern "C" { extern "C" {
@ -10,12 +11,13 @@ extern "C" {
color_t pixels[720][1280]; color_t pixels[720][1280];
int main() int main(int argc, char **argv)
{ {
sf::RenderWindow window(sf::VideoMode(1280, 720), "Test"); sf::RenderWindow window(sf::VideoMode(1280, 720), "Test");
window.setFramerateLimit(60); window.setFramerateLimit(60);
menuStartupPath(); menuStartupPath();
PHYSFS_init(argv[0]);
assetsInit(); assetsInit();
themeStartup(THEME_PRESET_LIGHT); themeStartup(THEME_PRESET_LIGHT);
textInit(); textInit();
@ -62,6 +64,7 @@ int main()
netloaderExit(); netloaderExit();
fontExit(); fontExit();
assetsExit(); assetsExit();
PHYSFS_deinit();
return 0; return 0;
} }

View File

@ -0,0 +1,15 @@
#include "../common/common.h"
bool thermalstatusInit(void) {
return false;
}
void thermalstatusExit(void) {
}
bool thermalstatusGetDetails(s32 *temperature) {
*temperature = 0;
return false;
}

BIN
resources/eth_none_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B