Compare commits
83 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2c53af56cc | ||
|
7bdfce172c | ||
|
34f7f16cb1 | ||
|
73d7108a62 | ||
|
422039f727 | ||
|
3f73855170 | ||
|
6e654ec2af | ||
|
5c85dd784b | ||
|
8c87b1c46f | ||
|
47fd18cabb | ||
|
c1640f4b54 | ||
|
aef37e10f7 | ||
|
95411fe5e9 | ||
|
65f23b0bde | ||
|
20c1f00972 | ||
|
fcbc56acc4 | ||
|
244d058f1b | ||
|
df09e9ed1b | ||
|
45efcfcb98 | ||
|
16958ac4ba | ||
|
c69f4c56c5 | ||
|
3e451a9c8b | ||
|
090054fc86 | ||
|
e2a16a83e0 | ||
|
ba330cfd84 | ||
|
f4067d64f3 | ||
|
88fe495bd8 | ||
|
7f1a9f4f29 | ||
|
ec24d595d6 | ||
|
5dfb9ec328 | ||
|
a4ad7b01b7 | ||
|
6ee5d36084 | ||
|
981749cfb7 | ||
|
9eda227d33 | ||
|
d11585589e | ||
|
42a4ad9787 | ||
|
b40d558458 | ||
|
387850d301 | ||
|
38b48a8609 | ||
|
bd3466f1e1 | ||
|
e5d9851250 | ||
|
15baea1e44 | ||
|
c2743dcdd5 | ||
|
233b765300 | ||
|
a747d92826 | ||
|
24ce4ff924 | ||
|
4ecd7401b6 | ||
|
a150cbe167 | ||
|
2feb085504 | ||
|
97ab367379 | ||
|
793b912efd | ||
|
42efd240de | ||
|
7d01d059df | ||
|
6a75feeb46 | ||
|
d7c37c6861 | ||
|
fa3d93d649 | ||
|
ea4db4ff02 | ||
|
d9effc3143 | ||
|
6c84575ef7 | ||
|
ce35f40f31 | ||
|
78da39d0a2 | ||
|
f2e085b6be | ||
|
3ceb44fd78 | ||
|
a123712db8 | ||
|
48a4819e67 | ||
|
d6c780256f | ||
|
dcad6f2afa | ||
|
753a97ef7b | ||
|
f0ef77b2f2 | ||
|
9e257d7606 | ||
|
7c029c18b0 | ||
|
3ea2af46a5 | ||
|
e91758984c | ||
|
527ecb6ce3 | ||
|
85318335fb | ||
|
6ec7388834 | ||
|
1e3f057b23 | ||
|
29caa76884 | ||
|
d4af9cd2b9 | ||
|
4739c8e730 | ||
|
2a3564f53a | ||
|
8e8e62ac33 | ||
|
d2bb1da2fa |
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.*/
|
||||
*~
|
||||
*.exe
|
||||
*.o
|
||||
|
12
Makefile
@ -1,4 +1,4 @@
|
||||
export APP_VERSION := 3.0.0
|
||||
export APP_VERSION := 3.5.1
|
||||
|
||||
ifeq ($(RELEASE),)
|
||||
export APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always)
|
||||
@ -8,18 +8,20 @@ endif
|
||||
|
||||
all: nx pc
|
||||
|
||||
romfs : assets
|
||||
romfs:
|
||||
@mkdir -p romfs
|
||||
|
||||
romfs/assets.zip : romfs assets
|
||||
@rm -f romfs/assets.zip
|
||||
@zip -rj romfs/assets.zip assets
|
||||
|
||||
dist-bin: romfs
|
||||
dist-bin: romfs/assets.zip
|
||||
$(MAKE) -f Makefile.nx dist-bin
|
||||
|
||||
nx: romfs
|
||||
nx: romfs/assets.zip
|
||||
$(MAKE) -f Makefile.nx
|
||||
|
||||
pc: romfs
|
||||
pc: romfs/assets.zip
|
||||
$(MAKE) -f Makefile.pc
|
||||
|
||||
clean:
|
||||
|
57
Makefile.nx
@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
|
||||
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||
#
|
||||
# NO_ICON: if set to anything, do not use icon.
|
||||
@ -29,17 +28,26 @@ include $(DEVKITPRO)/libnx/switch_rules
|
||||
# - <Project name>.jpg
|
||||
# - icon.jpg
|
||||
# - <libnx folder>/default_icon.jpg
|
||||
#
|
||||
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.json
|
||||
# - config.json
|
||||
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||
# NACP building is skipped as well.
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := common/ nx_main/ nx_main/loaders/
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
EXEFS_SRC := exefs_src
|
||||
ROMFS := romfs
|
||||
|
||||
DIST_PATH := $(TARGET)_v$(APP_VERSION)
|
||||
|
||||
APP_AUTHOR := switchbrew
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
@ -55,7 +63,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lminizip `freetype-config --libs` -lconfig -lturbojpeg
|
||||
LIBS := -ldeko3d -lphysfs `freetype-config --libs` -lconfig -lturbojpeg -lpng
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
@ -109,7 +117,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(ICON)),)
|
||||
icons := $(wildcard *.jpg)
|
||||
@ -152,7 +171,11 @@ $(BUILD):
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||
else
|
||||
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
dist-bin: all
|
||||
@ -169,11 +192,9 @@ DEPENDS := $(OFILES:.o=.d)
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).pfs0 $(OUTPUT).nro
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
|
||||
$(OUTPUT).pfs0 : $(OUTPUT).nso
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
all : $(OUTPUT).nro
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
|
||||
@ -181,6 +202,17 @@ else
|
||||
$(OUTPUT).nro : $(OUTPUT).elf
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
all : $(OUTPUT).nsp
|
||||
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
endif
|
||||
|
||||
menu.o : $(TOPDIR)/Makefile
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
@ -188,12 +220,7 @@ $(OFILES_SRC) : $(HFILES_BIN)
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h: %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
%.nxfnt.o : %.nxfnt
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
@ -9,12 +9,12 @@ EXTRA_CFLAGS="-D__USE_MINGW_ANSI_STDIO"
|
||||
EXTRA_LDFLAGS="-lws2_32"
|
||||
endif
|
||||
|
||||
test : pc_main/main.cpp pc_main/pc_launch.c pc_main/pc_power.c \
|
||||
common/menu.c common/font.c common/language.c common/launch.c common/worker.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-entry.c common/menu-list.c common/message-box.c common/text.c \
|
||||
common/ui.c common/assets.c common/math.c common/theme.c \
|
||||
common/netloader.c
|
||||
gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config freetype2 --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config freetype2 --libs` -lm -lminizip -lz -lconfig -lturbojpeg $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@
|
||||
gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config freetype2 --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config freetype2 --libs` -lm -lphysfs -lz -lconfig -lturbojpeg -lpng $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@
|
||||
|
||||
clean:
|
||||
rm -rf build_pc/ test test.*
|
||||
|
28
README.md
@ -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.
|
||||
|
||||
#### Download
|
||||
### Download
|
||||
The latest release is available from the [releases](https://github.com/switchbrew/nx-hbmenu/releases/latest) page.
|
||||
|
||||
#### Building
|
||||
Build with ```make nx``` or just run ```make```.
|
||||
### Building
|
||||
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
|
||||
|
||||
* 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`).
|
||||
|
BIN
assets/airplane_icon.bin
Normal file
BIN
assets/eth_icon.bin
Normal file
BIN
assets/eth_none_icon.bin
Normal file
BIN
assets/wifi1_icon.bin
Normal file
BIN
assets/wifi2_icon.bin
Normal file
BIN
assets/wifi3_icon.bin
Normal file
BIN
assets/wifi_none_icon.bin
Normal file
319
common/assets.c
@ -1,25 +1,28 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <minizip/unzip.h>
|
||||
#include <physfs.h>
|
||||
#include <png.h>
|
||||
|
||||
typedef struct {
|
||||
u8 *buffer;
|
||||
size_t size;
|
||||
const char *filename;
|
||||
} assetsDataEntry;
|
||||
|
||||
#define GENASSET(x) {.filename = x}
|
||||
#define GENASSET(_p, _mode, _w, _h) {{.path = _p, .imageMode = _mode, .imageSize = {_w, _h}}, {}}
|
||||
|
||||
static bool g_assetsInitialized = 0;
|
||||
assetsDataEntry g_assetsDataList[AssetId_Max] = {
|
||||
GENASSET("battery_icon.bin"),
|
||||
GENASSET("charging_icon.bin"),
|
||||
GENASSET("folder_icon.bin"),
|
||||
GENASSET("invalid_icon.bin"),
|
||||
GENASSET("hbmenu_logo_dark.bin"),
|
||||
GENASSET("hbmenu_logo_light.bin"),
|
||||
GENASSET("theme_icon_dark.bin"),
|
||||
GENASSET("theme_icon_light.bin"),
|
||||
assetsDataEntry g_assetsDataList[AssetId_Max][2] = {
|
||||
GENASSET("battery_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
|
||||
GENASSET("charging_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
|
||||
GENASSET("folder_icon.bin", IMAGE_MODE_RGB24, 256, 256),
|
||||
GENASSET("invalid_icon.bin", IMAGE_MODE_RGB24, 256, 256),
|
||||
GENASSET("hbmenu_logo_dark.bin", IMAGE_MODE_RGBA32, 140, 60),
|
||||
GENASSET("hbmenu_logo_light.bin", IMAGE_MODE_RGBA32, 140, 60),
|
||||
GENASSET("theme_icon_dark.bin", IMAGE_MODE_RGB24, 256, 256),
|
||||
GENASSET("theme_icon_light.bin", IMAGE_MODE_RGB24, 256, 256),
|
||||
GENASSET("airplane_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
|
||||
GENASSET("wifi_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
|
||||
GENASSET("wifi1_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
|
||||
GENASSET("wifi2_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
|
||||
GENASSET("wifi3_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
|
||||
GENASSET("eth_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
|
||||
GENASSET("eth_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
|
||||
GENASSET("", IMAGE_MODE_RGB24, 1280, 720),
|
||||
};
|
||||
|
||||
static void assetsClearEntry(assetsDataEntry *entry) {
|
||||
@ -27,61 +30,27 @@ static void assetsClearEntry(assetsDataEntry *entry) {
|
||||
|
||||
entry->size = 0;
|
||||
entry->buffer = NULL;
|
||||
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
}
|
||||
|
||||
static int assetsLoadFile(unzFile zipf, assetsDataEntry *entry) {
|
||||
int ret;
|
||||
int filesize=0;
|
||||
unz_file_info file_info;
|
||||
u8* buffer = NULL;
|
||||
static void assetsSetPixelSize(assetsDataEntry *entry) {
|
||||
switch (entry->imageMode) {
|
||||
case IMAGE_MODE_RGB24:
|
||||
entry->pixSize = 3;
|
||||
break;
|
||||
|
||||
ret = unzLocateFile(zipf, entry->filename, 0);
|
||||
|
||||
if (ret==UNZ_OK) ret = unzOpenCurrentFile(zipf);
|
||||
|
||||
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);
|
||||
case IMAGE_MODE_RGBA32:
|
||||
entry->pixSize = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret==UNZ_OK) {
|
||||
entry->buffer = buffer;
|
||||
entry->size = filesize;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result assetsInit(void) {
|
||||
int ret=0;
|
||||
bool ret=false;
|
||||
int i, stopi;
|
||||
unzFile zipf;
|
||||
assetsDataEntry *entry = NULL;
|
||||
char tmp_path[PATH_MAX+1];
|
||||
char tmp_path[PATH_MAX];
|
||||
|
||||
if (g_assetsInitialized) return 0;
|
||||
|
||||
@ -98,65 +67,211 @@ Result assetsInit(void) {
|
||||
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/romfs/assets.zip", menuGetRootBasePath());
|
||||
#endif
|
||||
|
||||
zipf = unzOpen(tmp_path);
|
||||
if(zipf==NULL) {
|
||||
#ifdef __SWITCH__
|
||||
romfsExit();
|
||||
#endif
|
||||
|
||||
return 0x80;
|
||||
}
|
||||
|
||||
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 (PHYSFS_mount(tmp_path, "", 0)) {
|
||||
ret=true;
|
||||
for (i=0; i<AssetId_Max; i++) {
|
||||
stopi = i;
|
||||
entry = &g_assetsDataList[i][0];
|
||||
if (entry->path[0]) {
|
||||
ret = assetsLoadData(i, NULL, NULL);
|
||||
if (!ret) break;
|
||||
}
|
||||
}
|
||||
|
||||
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__
|
||||
romfsExit();
|
||||
return ret ? 0 : MAKERESULT(Module_Libnx, LibnxError_IoError);
|
||||
#else
|
||||
return ret ? 0 : 1;
|
||||
#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;
|
||||
}
|
||||
|
||||
void assetsExit(void) {
|
||||
int i;
|
||||
bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term) {
|
||||
bool ret=true;
|
||||
*data_buf = NULL;
|
||||
if (filesize) *filesize = 0;
|
||||
|
||||
if (!g_assetsInitialized) return;
|
||||
g_assetsInitialized = 0;
|
||||
PHYSFS_Stat tmpstat={0};
|
||||
if (!(PHYSFS_stat(path, &tmpstat) && tmpstat.filesize!=-1)) ret = false;
|
||||
|
||||
for (i=0; i<AssetId_Max; i++) {
|
||||
assetsClearEntry(&g_assetsDataList[i]);
|
||||
if (ret) {
|
||||
size_t bufsize = tmpstat.filesize;
|
||||
if (nul_term) bufsize++;
|
||||
*data_buf = (u8*)malloc(bufsize);
|
||||
if (*data_buf) memset(*data_buf, 0, bufsize);
|
||||
else ret = false;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
PHYSFS_File *f = PHYSFS_openRead(path);
|
||||
if (f==NULL) ret = false;
|
||||
else {
|
||||
ret = PHYSFS_readBytes(f, *data_buf, tmpstat.filesize) == tmpstat.filesize;
|
||||
PHYSFS_close(f);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (filesize) *filesize = tmpstat.filesize;
|
||||
}
|
||||
else {
|
||||
free(*data_buf);
|
||||
*data_buf = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void assetsGetData(AssetId id, u8 **buffer, size_t *size) {
|
||||
if (buffer) *buffer = NULL;
|
||||
if (size) *size = 0;
|
||||
bool assetsLoadData(AssetId id, const char *path, int *imageSize) {
|
||||
if (id < 0 || id >= AssetId_Max) return false;
|
||||
|
||||
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;
|
||||
|
||||
assetsDataEntry *entry = &g_assetsDataList[id];
|
||||
|
||||
if (buffer) *buffer = entry->buffer;
|
||||
if (size) *size = entry->size;
|
||||
u32 pos = g_assetsDataList[id][1].initialized ? 1 : 0;
|
||||
assetsDataEntry *entry = &g_assetsDataList[id][pos];
|
||||
if (entry->initialized) *out = entry;
|
||||
}
|
||||
|
||||
u8 *assetsGetDataBuffer(AssetId id) {
|
||||
u8 *buffer = NULL;
|
||||
assetsDataEntry *entry = NULL;
|
||||
|
||||
assetsGetData(id, &buffer, NULL);
|
||||
return buffer;
|
||||
assetsGetData(id, &entry);
|
||||
return entry ? entry->buffer : NULL;
|
||||
}
|
||||
|
||||
|
@ -10,12 +10,35 @@ typedef enum {
|
||||
AssetId_hbmenu_logo_light,
|
||||
AssetId_theme_icon_dark,
|
||||
AssetId_theme_icon_light,
|
||||
AssetId_airplane_icon,
|
||||
AssetId_wifi_none_icon,
|
||||
AssetId_wifi1_icon,
|
||||
AssetId_wifi2_icon,
|
||||
AssetId_wifi3_icon,
|
||||
AssetId_eth_icon,
|
||||
AssetId_eth_none_icon,
|
||||
AssetId_background_image,
|
||||
|
||||
AssetId_Max
|
||||
AssetId_Max,
|
||||
} AssetId;
|
||||
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
u8 *buffer;
|
||||
size_t size;
|
||||
ImageMode imageMode;
|
||||
size_t pixSize;
|
||||
size_t imageSize[2];
|
||||
char path[PATH_MAX];
|
||||
} assetsDataEntry;
|
||||
|
||||
Result assetsInit(void);
|
||||
void assetsExit(void);
|
||||
void assetsGetData(AssetId id, u8 **buffer, size_t *size);
|
||||
void assetsClearTheme(void);
|
||||
bool 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);
|
||||
|
||||
bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height);
|
||||
|
||||
|
@ -21,8 +21,11 @@
|
||||
|
||||
#include <stdint.h>
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef int8_t s8;
|
||||
typedef int32_t s32;
|
||||
typedef u32 Result;
|
||||
|
||||
typedef void (*workerThreadFunc)(void *);
|
||||
@ -45,6 +48,49 @@ typedef union {
|
||||
};
|
||||
} 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
|
||||
#ifndef __SWITCH__
|
||||
#include "switch/nro.h"
|
||||
@ -64,6 +110,9 @@ typedef union {
|
||||
#include "message-box.h"
|
||||
#include "power.h"
|
||||
#include "netloader.h"
|
||||
#include "netstatus.h"
|
||||
#include "thermalstatus.h"
|
||||
#include "status.h"
|
||||
|
||||
void menuStartupPath(void);
|
||||
void menuStartup(void);
|
||||
@ -93,7 +142,7 @@ static inline void DrawPixel(uint32_t x, uint32_t y, color_t clr)
|
||||
{
|
||||
if (x >= 1280 || y >= 720)
|
||||
return;
|
||||
u32 off = (y * g_framebuf_width + x)*4;
|
||||
u32 off = y*g_framebuf_width + x*4;
|
||||
g_framebuf[off] = BlendColor(g_framebuf[off], clr.r, clr.a); off++;
|
||||
g_framebuf[off] = BlendColor(g_framebuf[off], clr.g, clr.a); off++;
|
||||
g_framebuf[off] = BlendColor(g_framebuf[off], clr.b, clr.a); off++;
|
||||
@ -103,7 +152,7 @@ static inline void DrawPixelRaw(uint32_t x, uint32_t y, color_t clr)
|
||||
{
|
||||
if (x >= 1280 || y >= 720)
|
||||
return;
|
||||
u32 off = (y * g_framebuf_width + x)*4;
|
||||
u32 off = y*g_framebuf_width + x*4;
|
||||
*((u32*)&g_framebuf[off]) = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24);
|
||||
}
|
||||
static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr)
|
||||
@ -113,12 +162,12 @@ static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr)
|
||||
|
||||
u32 color = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24);
|
||||
u128 val = color | ((u128)color<<32) | ((u128)color<<64) | ((u128)color<<96);
|
||||
u32 off = (y * g_framebuf_width + x)*4;
|
||||
u32 off = y*g_framebuf_width + x*4;
|
||||
*((u128*)&g_framebuf[off]) = val;
|
||||
}
|
||||
static inline color_t FetchPixelColor(uint32_t x, uint32_t y)
|
||||
{
|
||||
u32 off = (y * g_framebuf_width + x)*4;
|
||||
u32 off = y*g_framebuf_width + x*4;
|
||||
u32 val = *((u32*)&g_framebuf[off]);
|
||||
u8 r = (u8)val;
|
||||
u8 g = (u8)(val>>8);
|
||||
@ -158,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 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 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);
|
||||
|
@ -9,16 +9,20 @@
|
||||
#define FONT_FACES_MAX 2
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
static bool s_plinited;
|
||||
#endif
|
||||
|
||||
static FT_Error s_font_libret=1, s_font_facesret[FONT_FACES_MAX];
|
||||
|
||||
static FT_Library s_font_library;
|
||||
static FT_Face s_font_faces[FONT_FACES_MAX];
|
||||
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)
|
||||
{
|
||||
u32 i=0;
|
||||
s32 i=0;
|
||||
u32 scale=0;
|
||||
FT_Error ret=0;
|
||||
|
||||
@ -44,6 +48,10 @@ static bool FontSetType(u32 font)
|
||||
scale = 8;
|
||||
break;
|
||||
|
||||
case largestar:
|
||||
scale = 18;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
@ -76,12 +84,12 @@ static bool FontSetType(u32 font)
|
||||
|
||||
static inline bool FontLoadGlyph(glyph_t* glyph, u32 font, uint32_t codepoint)
|
||||
{
|
||||
FT_Face face;
|
||||
FT_Face face=0;
|
||||
FT_Error ret=0;
|
||||
FT_GlyphSlot slot;
|
||||
FT_UInt glyph_index;
|
||||
FT_Bitmap* bitmap;
|
||||
u32 i=0;
|
||||
s32 i=0;
|
||||
|
||||
//__builtin_printf("LoadGlyph %u\n", (unsigned int)codepoint);
|
||||
/*const ffnt_page_t* page = FontGetPage(font, codepoint >> 8);
|
||||
@ -272,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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
DrawText_(font, x, y, clr, text, max_width, end_text);
|
||||
@ -309,14 +349,14 @@ void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t
|
||||
width = x;
|
||||
}
|
||||
|
||||
*width_out = width;
|
||||
*height_out = height;
|
||||
if(width_out) *width_out = width;
|
||||
if(height_out) *height_out = height;
|
||||
}
|
||||
|
||||
bool fontInitialize(void)
|
||||
{
|
||||
FT_Error ret=0;
|
||||
u32 i;
|
||||
s32 i;
|
||||
|
||||
for (i=0; i<FONT_FACES_MAX; i++) s_font_facesret[i] = 1;
|
||||
|
||||
@ -328,7 +368,11 @@ bool fontInitialize(void)
|
||||
PlFontData fonts[PlSharedFontType_Total];
|
||||
|
||||
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;
|
||||
|
||||
for (i=0; i<s_font_faces_total; i++) {
|
||||
@ -368,19 +412,23 @@ bool fontInitialize(void)
|
||||
|
||||
void fontExit()
|
||||
{
|
||||
u32 i=0;
|
||||
s32 i=0;
|
||||
|
||||
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_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
|
||||
*based on the string length and desired alignment
|
||||
*rY=reference point... where to align around
|
||||
*align='t','b','c' translates to (top,bottom,center)
|
||||
*'t' aligned, top of text aligns with rY,
|
||||
*'t' aligned, top of text aligns with rY,
|
||||
*you get the rest....
|
||||
*/
|
||||
uint32_t GetTextYCoordinate(u32 font, uint32_t rY, const char* text, const char align) {
|
||||
@ -401,7 +449,7 @@ uint32_t GetTextYCoordinate(u32 font, uint32_t rY, const char* text, const char
|
||||
}
|
||||
}
|
||||
|
||||
/*Automatically gives you the desired x-coordinate
|
||||
/*Automatically gives you the desired x-coordinate
|
||||
*based on the string length and desired alignment
|
||||
*rX=reference point... where to align around
|
||||
*text=string you want to display
|
||||
|
@ -43,3 +43,4 @@ extern const ffnt_header_t interuiregular18_nxfnt;*/
|
||||
#define interuiregular14 0//&interuiregular14_nxfnt
|
||||
#define interuiregular18 1//&interuiregular18_nxfnt
|
||||
#define fontscale7 4
|
||||
#define largestar 5
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "language.h"
|
||||
#include "language.h"
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#define STR_JP(_str) [SetLanguage_JA] = _str
|
||||
@ -7,12 +7,12 @@
|
||||
#define STR_DE(_str) [SetLanguage_DE] = _str
|
||||
#define STR_IT(_str) [SetLanguage_IT] = _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_NL(_str) [SetLanguage_NL] = _str
|
||||
#define STR_PT(_str) [SetLanguage_PT] = _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
|
||||
#define STR_JP(_str) [0] = _str
|
||||
#define STR_EN(_str) [1] = _str
|
||||
@ -20,15 +20,15 @@
|
||||
#define STR_DE(_str) [3] = _str
|
||||
#define STR_IT(_str) [4] = _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_NL(_str) [8] = _str
|
||||
#define STR_PT(_str) [9] = _str
|
||||
#define STR_RU(_str) [10] = _str
|
||||
#define STR_TW(_str) [11] = _str
|
||||
#define STR_ZH_HANT(_str) [11] = _str
|
||||
#endif
|
||||
|
||||
const char* const g_strings[StrId_Max][16] =
|
||||
const char* const g_strings[StrId_Max][17] =
|
||||
{
|
||||
[StrId_Loading] =
|
||||
{
|
||||
@ -42,8 +42,16 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Laden…"),
|
||||
STR_KO("로딩중…"),
|
||||
STR_RU("загрузка…"),
|
||||
STR_ZH("加载中…"),
|
||||
STR_TW("加載中…"),
|
||||
STR_ZH_HANS("加载中…"),
|
||||
STR_ZH_HANT("載入中…"),
|
||||
},
|
||||
|
||||
[StrId_AppletMode] =
|
||||
{
|
||||
STR_EN("● Applet Mode ●"),
|
||||
STR_ES("● Modo Applet ●"),
|
||||
STR_FR("● Mode Applet ●"),
|
||||
STR_ZH_HANS("● 小程序模式 ●"),
|
||||
},
|
||||
|
||||
[StrId_Directory] =
|
||||
@ -58,8 +66,8 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Map"),
|
||||
STR_KO("디렉토리"),
|
||||
STR_RU("каталог"),
|
||||
STR_ZH("目录"),
|
||||
STR_TW("資料夾"),
|
||||
STR_ZH_HANS("目录"),
|
||||
STR_ZH_HANT("資料夾"),
|
||||
},
|
||||
|
||||
/*[StrId_DefaultLongTitle] =
|
||||
@ -74,8 +82,8 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Homebrew toepassing"),
|
||||
STR_KO("홈브류 애플리케이션"),
|
||||
STR_RU("приложение хомебреw"),
|
||||
STR_ZH("自制应用程序"),
|
||||
STR_TW("自製程式"),
|
||||
STR_ZH_HANS("自制应用程序"),
|
||||
STR_ZH_HANT("自製程式"),
|
||||
},*/
|
||||
|
||||
[StrId_DefaultPublisher] =
|
||||
@ -90,24 +98,24 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Auteur onbekend"),
|
||||
STR_KO("알 수 없는 개발자"),
|
||||
STR_RU("неизвестный автор"),
|
||||
STR_ZH("未知作者"),
|
||||
STR_TW("作者不詳"),
|
||||
STR_ZH_HANS("未知作者"),
|
||||
STR_ZH_HANT("作者未知"),
|
||||
},
|
||||
|
||||
[StrId_IOError] =
|
||||
{
|
||||
STR_EN("I/O Error"),
|
||||
STR_ES("Error de E/S"),
|
||||
STR_DE("E/A-Fehler"),
|
||||
STR_DE("I/O-Fehler"),
|
||||
STR_FR("Erreur d'E/S"),
|
||||
STR_IT("Errore di I/O"),
|
||||
STR_IT("Errore I/O"),
|
||||
STR_JP("入出力エラー"),
|
||||
STR_PT("Erro de E/S"),
|
||||
STR_NL("I/O Fout"),
|
||||
STR_KO("입출력 오류"),
|
||||
STR_RU("I/O-ошибка"),
|
||||
STR_ZH("读写出错"),
|
||||
STR_TW("讀寫錯誤"),
|
||||
STR_ZH_HANS("读写出错"),
|
||||
STR_ZH_HANT("取存錯誤"),
|
||||
},
|
||||
|
||||
[StrId_CouldNotOpenFile] =
|
||||
@ -122,8 +130,15 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Kan bestand niet openen:\n%s"),
|
||||
STR_KO("파일을 열 수 없습니다:\n%s"),
|
||||
STR_RU("Не могу открыть файл:\n%s"),
|
||||
STR_ZH("无法打开文件:\n%s"),
|
||||
STR_TW("開啓檔案失敗:\n%s"),
|
||||
STR_ZH_HANS("无法打开文件:\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] =
|
||||
@ -138,8 +153,8 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Geen toepassingen gevonden"),
|
||||
STR_KO("애플리케이션을 찾을 수 없습니다"),
|
||||
STR_RU("приложение не найдено"),
|
||||
STR_ZH("找不到可执行的自制程序"),
|
||||
STR_TW("未能找到可執行的自製程式"),
|
||||
STR_ZH_HANS("找不到可执行的自制程序"),
|
||||
STR_ZH_HANT("沒有可執行的自製程式"),
|
||||
},
|
||||
|
||||
[StrId_NoAppsFound_Msg] =
|
||||
@ -157,7 +172,7 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_DE(
|
||||
"Auf der SD-Karte wurden keine Anwendungen\n"
|
||||
"gefunden. Stelle sicher, dass ein Verzeichnis\n"
|
||||
"namens /switch im Wurzelverzeichnis der SD-Karte\n"
|
||||
"namens /switch im Hauptverzeichnis der SD-Karte\n"
|
||||
"existiert und Anwendungen enthält!"
|
||||
),
|
||||
STR_FR(
|
||||
@ -199,15 +214,15 @@ const char* const g_strings[StrId_Max][16] =
|
||||
"Убедитесь, что на карте SD есть каталог с\n"
|
||||
"названием switch и она содержит приложения."
|
||||
),
|
||||
STR_ZH(
|
||||
"内存卡找不到任何可执行的应用程序。\n"
|
||||
"请在内存卡的根目录建立「switch」子目录,\n"
|
||||
"并存放自制应用软件至该目录。"
|
||||
STR_ZH_HANS(
|
||||
"找不到任何自制程序(nro)。\n"
|
||||
"在SD卡根目录建立“switch”文件夹,\n"
|
||||
"并将自制程序(nro)放在其中。"
|
||||
),
|
||||
STR_TW(
|
||||
"記憶體找不到任何可執行的應用程式。\n"
|
||||
"請在記憶體建立「switch」資料夾,\n"
|
||||
"然後儲存自製軟體到此處。"
|
||||
STR_ZH_HANT(
|
||||
"記憶卡內沒有可供執行的應用程式。\n"
|
||||
"請在根目錄下建立「switch」資料夾,\n"
|
||||
"並將自製軟體複製到switch資料夾內。"
|
||||
),
|
||||
},
|
||||
|
||||
@ -215,15 +230,24 @@ const char* const g_strings[StrId_Max][16] =
|
||||
{
|
||||
STR_EN("The last application returned an error:"),
|
||||
STR_ES("La última aplicación devolvió un error:"),
|
||||
STR_DE("Die letzte Anwendung erzeugte einen Fehler:"),
|
||||
STR_FR("La dernière application a retourné une erreur:"),
|
||||
STR_IT("L'ultima applicazione ha restituito un errore:"),
|
||||
STR_JP("直前に実行したアプリでエラーが発生しました:"),
|
||||
STR_KO("최근 애플리케이션에서 오류가 발생했습니다:"),
|
||||
STR_TW("程式執行時發生錯誤:"),
|
||||
STR_ZH_HANS("程序运行后出现错误:"),
|
||||
STR_ZH_HANT("程式執行後出現錯誤:"),
|
||||
},
|
||||
|
||||
[StrId_AppLaunchError] =
|
||||
{
|
||||
STR_EN("Failed to launch the application:"),
|
||||
STR_DE("Konnte die Anwendung nicht starten:"),
|
||||
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] =
|
||||
@ -238,8 +262,8 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Auteur"),
|
||||
STR_KO("개발자"),
|
||||
STR_RU("автор"),
|
||||
STR_ZH("作者"),
|
||||
STR_TW("作者"),
|
||||
STR_ZH_HANS("作者"),
|
||||
STR_ZH_HANT("作者"),
|
||||
},
|
||||
|
||||
[StrId_AppInfo_Version] =
|
||||
@ -254,8 +278,8 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Versie"),
|
||||
STR_KO("버전"),
|
||||
STR_RU("Версия"),
|
||||
STR_ZH("版"),
|
||||
STR_TW("版"),
|
||||
STR_ZH_HANS("版本"),
|
||||
STR_ZH_HANT("版本"),
|
||||
},
|
||||
|
||||
[StrId_Actions_Launch] =
|
||||
@ -263,15 +287,15 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_EN("Launch"),
|
||||
STR_ES("Lanzamiento"),
|
||||
STR_DE("Starten"),
|
||||
STR_FR("Lancement"),
|
||||
STR_IT("Lanciare"),
|
||||
STR_FR("Lancer"),
|
||||
STR_IT("Avvia"),
|
||||
STR_JP("起動"),
|
||||
STR_PT("Lançamento"),
|
||||
STR_NL("Lancering"),
|
||||
STR_KO("실행"),
|
||||
STR_RU("запуск"),
|
||||
STR_ZH("发射"),
|
||||
STR_TW("啟動"),
|
||||
STR_ZH_HANS("发射"),
|
||||
STR_ZH_HANT("啟動"),
|
||||
},
|
||||
|
||||
[StrId_Actions_Open] =
|
||||
@ -280,16 +304,16 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_ES("Abrir"),
|
||||
STR_DE("Öffnen"),
|
||||
STR_FR("Ouvrir"),
|
||||
STR_IT("Aprire"),
|
||||
STR_IT("Apri"),
|
||||
STR_JP("開く"),
|
||||
STR_PT("Abrir"),
|
||||
STR_NL("Open"),
|
||||
STR_KO("열기"),
|
||||
STR_RU("открыто"),
|
||||
STR_ZH("打开"),
|
||||
STR_TW("開啟"),
|
||||
STR_ZH_HANS("打开"),
|
||||
STR_ZH_HANT("開啟"),
|
||||
},
|
||||
|
||||
|
||||
[StrId_Actions_Back] =
|
||||
{
|
||||
STR_EN("Back"),
|
||||
@ -302,54 +326,89 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Terug"),
|
||||
STR_KO("뒤로 가기"),
|
||||
STR_RU("возвращаться"),
|
||||
STR_ZH("回去"),
|
||||
STR_TW("回去"),
|
||||
STR_ZH_HANS("返回"),
|
||||
STR_ZH_HANT("返回"),
|
||||
},
|
||||
|
||||
[StrId_MsgBox_OK] =
|
||||
{
|
||||
STR_EN("OK"),
|
||||
STR_DE("OK"),
|
||||
STR_FR("OK"),
|
||||
STR_IT("OK"),
|
||||
STR_ES("Aceptar"),
|
||||
STR_JP("了解"),
|
||||
STR_KO("확인"),
|
||||
STR_TW("確認"),
|
||||
STR_ZH_HANS("确认"),
|
||||
STR_ZH_HANT("確認"),
|
||||
},
|
||||
|
||||
[StrId_Actions_Apply] =
|
||||
{
|
||||
STR_EN("Apply"),
|
||||
STR_FR("Appliquer"),
|
||||
STR_DE("Anwenden"),
|
||||
STR_ES("Aplicar"),
|
||||
STR_IT("Applica"),
|
||||
STR_JP("適用"),
|
||||
STR_KO("적용"),
|
||||
STR_TW("应用"),
|
||||
STR_ZH_HANS("应用"),
|
||||
STR_ZH_HANT("套用"),
|
||||
},
|
||||
|
||||
[StrId_Actions_Star] =
|
||||
{
|
||||
STR_EN("Star"),
|
||||
STR_ES("Agregar a favoritos"),
|
||||
STR_IT("Aggiungi ai preferiti"),
|
||||
STR_FR("Ajouter aux favoris"),
|
||||
STR_ZH_HANS("收藏"),
|
||||
},
|
||||
|
||||
[StrId_Actions_Unstar] =
|
||||
{
|
||||
STR_EN("Unstar"),
|
||||
STR_ES("Borrar de favoritos"),
|
||||
STR_IT("Rimuovi dai preferiti"),
|
||||
STR_FR("Retirer des favoris"),
|
||||
STR_ZH_HANS("取消收藏"),
|
||||
},
|
||||
|
||||
[StrId_ThemeMenu] =
|
||||
{
|
||||
STR_EN("Theme Menu"),
|
||||
STR_FR("Menu Thème"),
|
||||
STR_FR("Menu thèmes"),
|
||||
STR_DE("Theme Menü"),
|
||||
STR_ES("Menú temático"),
|
||||
STR_IT("Tema Menu"),
|
||||
STR_JP("テーマメニュー"),
|
||||
STR_KO("테마 메뉴"),
|
||||
STR_TW("主题菜单"),
|
||||
STR_ZH_HANS("主题菜单"),
|
||||
STR_ZH_HANT("主題選單"),
|
||||
},
|
||||
|
||||
[StrId_ThemeNotApplied] =
|
||||
{
|
||||
STR_EN("Theme cannot be applied because an error occurred."),
|
||||
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_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_KO("오류가 발생 했기 때문에 테마를 적용할 수 없습니다."),
|
||||
STR_TW("由于发生错误, 无法应用主题。"),
|
||||
STR_ZH_HANS("由于发生错误, 无法应用主题。"),
|
||||
STR_ZH_HANT("出現錯誤,無法套用主題。"),
|
||||
},
|
||||
|
||||
[StrId_DefaultThemeName] =
|
||||
{
|
||||
STR_EN("Default Theme"),
|
||||
STR_FR("Thème par défaut"),
|
||||
STR_DE("Standard Theme"),
|
||||
STR_IT("Tema di default"),
|
||||
STR_ES("Tema por defecto"),
|
||||
STR_ZH_HANS("默认主题"),
|
||||
STR_ZH_HANT("預設主題"),
|
||||
},
|
||||
|
||||
/*[StrId_Reboot] =
|
||||
@ -415,13 +474,13 @@ const char* const g_strings[StrId_Max][16] =
|
||||
" \xEE\x80\x80 Перезагрузите\n"
|
||||
" \xEE\x80\x81 Отмена"
|
||||
),
|
||||
STR_ZH(
|
||||
STR_ZH_HANS(
|
||||
"无法返回至主机的 \xEE\x81\xB3HOME 菜单。\n"
|
||||
"您需要重新启动您的 3DS 设备。\n\n"
|
||||
" \xEE\x80\x80 重启设备\n"
|
||||
" \xEE\x80\x81 取消操作"
|
||||
),
|
||||
STR_TW(
|
||||
STR_ZH_HANT(
|
||||
"無法返回至主機的 \xEE\x81\xB3HOME 選單。\n"
|
||||
"您需要重新啓動您的 3DS 設備。\n\n"
|
||||
" \xEE\x80\x80 重啓設備\n"
|
||||
@ -491,13 +550,13 @@ const char* const g_strings[StrId_Max][16] =
|
||||
" \xEE\x80\x81 Отмена\n"
|
||||
" \xEE\x80\x82 Перезагрузите"
|
||||
),
|
||||
STR_ZH(
|
||||
STR_ZH_HANS(
|
||||
"您即将返回到主機的 \xEE\x81\xB3HOME 菜单。\n\n"
|
||||
" \xEE\x80\x80 确认返回\n"
|
||||
" \xEE\x80\x81 取消操作\n"
|
||||
" \xEE\x80\x82 重启设备"
|
||||
),
|
||||
STR_TW(
|
||||
STR_ZH_HANT(
|
||||
"您即將返回到主機的 \xEE\x81\xB3HOME 選單。\n\n"
|
||||
" \xEE\x80\x80 確認返回\n"
|
||||
" \xEE\x80\x81 取消操作\n"
|
||||
@ -509,7 +568,7 @@ const char* const g_strings[StrId_Max][16] =
|
||||
{
|
||||
STR_EN("Title selector"),
|
||||
STR_ES("Selector de título"),
|
||||
STR_DE("Titel-Selektor"),
|
||||
STR_DE("Titel-Auswahl"),
|
||||
STR_FR("Sélecteur de titre"),
|
||||
STR_IT("Selettore del titolo"),
|
||||
STR_JP("タイトルセレクタ"),
|
||||
@ -517,18 +576,18 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Titel selector"),
|
||||
STR_KO("타이틀 선택기"),
|
||||
STR_RU("Селектор заголовков"),
|
||||
STR_ZH("应用启动器"),
|
||||
STR_TW("自製程式啓動器"),
|
||||
STR_ZH_HANS("应用启动器"),
|
||||
STR_ZH_HANT("自製程式啓動器"),
|
||||
},
|
||||
|
||||
[StrId_ErrorReadingTitleMetadata] =
|
||||
{
|
||||
STR_EN("Error reading title metadata.\n%08lX%08lX@%d"),
|
||||
STR_ES("Error leyendo los metadatos de los títulos.\n%08lX%08lX@%d"),
|
||||
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(
|
||||
"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_JP("タイトルメタデータを読み取ることができませんでした。\n%08lX%08lX@%d"),
|
||||
@ -536,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_KO("타이틀 메타데이터를 읽는데 실패하였습니다.\n%08lX%08lX@%d"),
|
||||
STR_RU("Ошибка чтения метаданных заголовка\n.%08lX%08lX@%d"),
|
||||
STR_ZH("读取软件相关信息时发生错误:\n%08lX%08lX@%d"),
|
||||
STR_TW("讀取軟體相關數據時發生錯誤:\n%08lX%08lX@%d"),
|
||||
STR_ZH_HANS("读取软件相关信息时发生错误:\n%08lX%08lX@%d"),
|
||||
STR_ZH_HANT("讀取軟體相關資訊時發生錯誤:\n%08lX%08lX@%d"),
|
||||
},
|
||||
|
||||
[StrId_NoTitlesFound] =
|
||||
@ -552,8 +611,8 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Geen titels gevonden."),
|
||||
STR_KO("타이틀을 찾을 수 없습니다."),
|
||||
STR_RU("Заголовки не обнаружены"),
|
||||
STR_ZH("主机内找不到任何软件。"),
|
||||
STR_TW("主機内找不到任何軟體。"),
|
||||
STR_ZH_HANS("主机内找不到任何软件。"),
|
||||
STR_ZH_HANT("主機内找不到任何軟體。"),
|
||||
},
|
||||
|
||||
[StrId_SelectTitle] =
|
||||
@ -608,12 +667,12 @@ const char* const g_strings[StrId_Max][16] =
|
||||
" \xEE\x80\x80 Выберите\n"
|
||||
" \xEE\x80\x81 Отмена"
|
||||
),
|
||||
STR_ZH(
|
||||
STR_ZH_HANS(
|
||||
"请选择一个目标软件。\n\n"
|
||||
" \xEE\x80\x80 确认\n"
|
||||
" \xEE\x80\x81 取消"
|
||||
),
|
||||
STR_TW(
|
||||
STR_ZH_HANT(
|
||||
"請選擇一個目標軟體。\n\n"
|
||||
" \xEE\x80\x80 確認\n"
|
||||
" \xEE\x80\x81 取消"
|
||||
@ -672,14 +731,14 @@ const char* const g_strings[StrId_Max][16] =
|
||||
"приложений под целевыми заголовками.\n"
|
||||
"Пожалуйста, используйте другой эксплойт."
|
||||
),
|
||||
STR_ZH(
|
||||
STR_ZH_HANS(
|
||||
"您所利用漏洞启动的「自制软件启动器」,\n"
|
||||
"无法在当前选中的软件中启动自制软件。\n"
|
||||
"请使用其它的漏洞来启动「自制软件启动器」。"
|
||||
),
|
||||
STR_TW(
|
||||
STR_ZH_HANT(
|
||||
"您所利用漏洞開啓的「自製軟體啓動器」\n"
|
||||
"無法在當前選中的軟體啓動自製軟件。\n"
|
||||
"無法在當前選中的軟體啓動自製軟體。\n"
|
||||
"請利用其它漏洞來啓動「自製軟體啓動器」。"
|
||||
),
|
||||
},
|
||||
@ -696,7 +755,7 @@ const char* const g_strings[StrId_Max][16] =
|
||||
),
|
||||
STR_DE(
|
||||
"Die ausgewählte Anwendung benötigt einen\n"
|
||||
"Titel der nicht installiert ist"
|
||||
"Titel der nicht installiert ist."
|
||||
),
|
||||
STR_FR(
|
||||
"L'application sélectionnée requiert un titre\n"
|
||||
@ -726,11 +785,11 @@ const char* const g_strings[StrId_Max][16] =
|
||||
"Для приложения требуется зависимость,\n"
|
||||
"которая не установлена."
|
||||
),
|
||||
STR_ZH(
|
||||
STR_ZH_HANS(
|
||||
"主机找不到该应用程序\n"
|
||||
"所需求的软件。"
|
||||
),
|
||||
STR_TW(
|
||||
STR_ZH_HANT(
|
||||
"主機找不到該應用程式\n"
|
||||
"所需求的軟體。"
|
||||
),
|
||||
@ -741,15 +800,15 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_EN("NetLoader"),
|
||||
STR_ES("Cargador de programas"),
|
||||
STR_DE("Netzwerk-Loader"),
|
||||
STR_FR("Chargeur de programme"),
|
||||
STR_FR("NetLoader"),
|
||||
STR_IT("Caricamento programmi"),
|
||||
STR_JP("ネットローダ"),
|
||||
STR_PT("Carregador de programas"),
|
||||
STR_NL("netwerk lader"),
|
||||
STR_KO("네트워크 로더"),
|
||||
STR_RU("Загрузчик"),
|
||||
STR_ZH("网络执行模块"),
|
||||
STR_TW("網路執行模組"),
|
||||
STR_ZH_HANS("网络执行模块"),
|
||||
STR_ZH_HANT("網路執行模組"),
|
||||
},
|
||||
|
||||
[StrId_NetLoaderUnavailable] =
|
||||
@ -757,15 +816,15 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_EN("The NetLoader is currently unavailable."),
|
||||
STR_ES("El cargador de programas no está disponible."),
|
||||
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_JP("nxlinkネットローダは現在利用できません。"),
|
||||
STR_PT("O carregador de programas está de momento indisponível."),
|
||||
STR_NL("De netwerk lader is niet beschikbaar."),
|
||||
STR_KO("현재 네트워크 로더는 사용이 불가합니다."),
|
||||
STR_RU("Загрузчик в настоящее время недоступен."),
|
||||
STR_ZH("无法启动 nxlink 网络执行模块。"),
|
||||
STR_TW("無法啓動 nxlink 網路執行模組。"),
|
||||
STR_ZH_HANS("无法启动 nxlink 网络执行模块。"),
|
||||
STR_ZH_HANT("無法啓動 nxlink 網路執行模組。"),
|
||||
},
|
||||
|
||||
[StrId_NetLoaderError] =
|
||||
@ -780,19 +839,21 @@ const char* const g_strings[StrId_Max][16] =
|
||||
STR_NL("Er is een fout opgetreden\nTechnische details: [%s:%d]"),
|
||||
STR_KO("오류가 발생했습니다.\n기술적인 세부사항: [%s:%d]"),
|
||||
STR_RU("Произошла ошибка.\nТехнические подробности: [%s:%d]"),
|
||||
STR_ZH("发生错误。\n详细错误信息:[%s:%d]"),
|
||||
STR_TW("發生錯誤。\n詳細錯誤資訊:[%s:%d]"),
|
||||
STR_ZH_HANS("发生错误。\n详细错误信息:[%s:%d]"),
|
||||
STR_ZH_HANT("發生錯誤。\n詳細錯誤資訊:[%s:%d]"),
|
||||
},
|
||||
|
||||
[StrId_NetLoaderOffline] =
|
||||
{
|
||||
STR_EN("Offline, waiting for network…"),
|
||||
STR_DE("Offline, warte auf Netzwerk…"),
|
||||
STR_FR("Hors-ligne, en attente d'une connection..."),
|
||||
STR_IT("Disconnesso, in attesa della connessione…"),
|
||||
STR_ES("Desconectado, esperando a la red..."),
|
||||
STR_JP("オフラインです。ネットワーク接続を待っています…"),
|
||||
STR_KO("연결 끊김, 네트워크 기다리는 중…"),
|
||||
STR_ZH("无法连接网络,等待网络连接…"),
|
||||
STR_TW("當前離線,等待網路連線…"),
|
||||
STR_ZH_HANS("无法连接网络,等待网络连接…"),
|
||||
STR_ZH_HANT("目前已離線,等待網路連線…"),
|
||||
},
|
||||
|
||||
[StrId_NetLoaderActive] =
|
||||
@ -806,7 +867,7 @@ const char* const g_strings[StrId_Max][16] =
|
||||
"Dir.IP: %lu.%lu.%lu.%lu, Puerto: %d"
|
||||
),
|
||||
STR_DE(
|
||||
"Warte auf Verbindung von 3dslink…\n"
|
||||
"Warte auf Verbindung von nxlink…\n"
|
||||
"IP Addr: %lu.%lu.%lu.%lu, Port: %d"
|
||||
),
|
||||
STR_FR(
|
||||
@ -837,11 +898,11 @@ const char* const g_strings[StrId_Max][16] =
|
||||
"Ожидание подключения nxlink…\n"
|
||||
"айпи адрес: %lu.%lu.%lu.%lu, Порт: %d"
|
||||
),
|
||||
STR_ZH(
|
||||
STR_ZH_HANS(
|
||||
"等待 nxlink 连接…\n"
|
||||
"IP 地址:%lu.%lu.%lu.%lu,端口:%d"
|
||||
),
|
||||
STR_TW(
|
||||
STR_ZH_HANT(
|
||||
"等待 nxlink 連接…\n"
|
||||
"IP 位址:%lu.%lu.%lu.%lu,連接埠:%d"
|
||||
),
|
||||
@ -889,14 +950,13 @@ const char* const g_strings[StrId_Max][16] =
|
||||
"Передача…\n"
|
||||
"%zu из %zu КИБ написано"
|
||||
),
|
||||
STR_ZH(
|
||||
STR_ZH_HANS(
|
||||
"正在传输…\n"
|
||||
"已完成 %zu / %zu KiB"
|
||||
),
|
||||
STR_TW(
|
||||
STR_ZH_HANT(
|
||||
"正在傳輸…\n"
|
||||
"已完成 %zu / %zu KiB"
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -6,10 +6,12 @@
|
||||
typedef enum
|
||||
{
|
||||
StrId_Loading = 0,
|
||||
StrId_AppletMode,
|
||||
StrId_Directory,
|
||||
StrId_DefaultPublisher,
|
||||
StrId_IOError,
|
||||
StrId_CouldNotOpenFile,
|
||||
StrId_NroNotFound,
|
||||
|
||||
StrId_NoAppsFound_Title,
|
||||
StrId_NoAppsFound_Msg,
|
||||
@ -23,6 +25,8 @@ typedef enum
|
||||
StrId_Actions_Open,
|
||||
StrId_Actions_Back,
|
||||
StrId_Actions_Apply,
|
||||
StrId_Actions_Star,
|
||||
StrId_Actions_Unstar,
|
||||
|
||||
StrId_MsgBox_OK,
|
||||
|
||||
@ -51,5 +55,5 @@ typedef enum
|
||||
StrId_Max,
|
||||
} StrId;
|
||||
|
||||
extern const char* const g_strings[StrId_Max][16];
|
||||
extern const char* const g_strings[StrId_Max][17];
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include <physfs.h>
|
||||
|
||||
void menuEntryInit(menuEntry_s* me, MenuEntryType type) {
|
||||
memset(me, 0, sizeof(*me));
|
||||
@ -80,9 +81,13 @@ static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool menuEntryLoadExternalIcon(menuEntry_s* me, const char* path) {
|
||||
static bool menuEntryLoadExternalIcon(menuEntry_s* me, const char* path, bool data_source) {
|
||||
struct stat st;
|
||||
|
||||
if (data_source) {
|
||||
return assetsPhysfsReadFile(path, &me->icon, &me->icon_size, false);
|
||||
}
|
||||
|
||||
if(stat(path, &st)==-1) return false;
|
||||
|
||||
FILE* f = fopen(path, "rb");
|
||||
@ -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;
|
||||
|
||||
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);
|
||||
if (me->icon_gfx) memcpy(me->icon_gfx, icon_gfx, tmpsize);
|
||||
|
||||
if (me->icon_gfx) {
|
||||
tmpsize = 140*140*3;
|
||||
tmpsize = layoutobj2->size[0]*layoutobj2->size[1]*3;
|
||||
me->icon_gfx_small = (uint8_t*)malloc(tmpsize);
|
||||
if (me->icon_gfx_small) memcpy(me->icon_gfx_small, icon_gfx_small, tmpsize);
|
||||
|
||||
@ -168,6 +176,26 @@ static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool menuEntryLoadExternalNacp(menuEntry_s* me, const char* path) {
|
||||
struct stat st;
|
||||
|
||||
if(stat(path, &st)==-1) return false;
|
||||
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (!f) return false;
|
||||
|
||||
me->nacp = (NacpStruct*)malloc(sizeof(NacpStruct));
|
||||
if (me->nacp == NULL) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
memset(me->nacp, 0, sizeof(NacpStruct));
|
||||
|
||||
bool ok = fread(me->nacp, sizeof(NacpStruct), 1, f) == 1;
|
||||
fclose(f);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*static void fixSpaceNewLine(char* buf) {
|
||||
char *outp = buf, *inp = buf;
|
||||
char lastc = 0;
|
||||
@ -182,19 +210,19 @@ static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) {
|
||||
} while (lastc);
|
||||
}*/
|
||||
|
||||
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
int i=0;
|
||||
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists) {
|
||||
int i=0, tmplen;
|
||||
menu_s *menu_fileassoc = menuFileassocGetCurrent();
|
||||
menuEntry_s* fileassoc_me = NULL;
|
||||
char *strptr = NULL;
|
||||
static char tempbuf[PATH_MAX+1];
|
||||
char tempbuf[PATH_MAX+16];
|
||||
//bool isOldAppFolder = false;
|
||||
|
||||
if (!fsobjExists(me->path)) return false;
|
||||
if (check_exists && !fsobjExists(me->path)) return false;
|
||||
|
||||
tempbuf[PATH_MAX] = 0;
|
||||
strcpy(me->name, name);
|
||||
|
||||
strncpy(me->name, name, sizeof(me->name)-1);
|
||||
|
||||
if (me->type == ENTRY_TYPE_FOLDER)
|
||||
{
|
||||
//Check for <dirpath>/<dirname>.nro
|
||||
@ -203,7 +231,8 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
bool fileassoc_flag = 0;
|
||||
|
||||
//Use the first .nro found in the directory, if there's only 1 NRO in the directory. Only used for paths starting with "sdmc:/switch/".
|
||||
if (!found && strncmp(me->path, menuGetRootPath(), strlen(menuGetRootPath()))==0) {
|
||||
tmplen = strlen(menuGetRootPath());
|
||||
if (!found && strncmp(me->path, menuGetRootPath(), tmplen)==0 && me->path[tmplen]=='/') {
|
||||
DIR* dir;
|
||||
struct dirent* dp;
|
||||
u32 nro_count=0;
|
||||
@ -264,9 +293,8 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
|
||||
if (me->type == ENTRY_TYPE_FILE)
|
||||
{
|
||||
//strcpy(me->name, name);//This is already done before both if statements
|
||||
strcpy(me->author, textGetString(StrId_DefaultPublisher));
|
||||
strcpy(me->version, "1.0.0");
|
||||
strncpy(me->author, textGetString(StrId_DefaultPublisher), sizeof(me->author)-1);
|
||||
strncpy(me->version, "1.0.0", sizeof(me->version)-1);
|
||||
|
||||
//shortcut_s sc;
|
||||
|
||||
@ -303,7 +331,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
char* ext = getExtension(tempbuf);
|
||||
|
||||
strcpy(ext, ".jpg");
|
||||
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
|
||||
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
|
||||
if (iconLoaded) break;
|
||||
|
||||
if (isOldAppFolder)
|
||||
@ -311,7 +339,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
char* slash = getSlash(tempbuf);
|
||||
|
||||
strcpy(slash, "/icon.jpg");
|
||||
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
|
||||
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
|
||||
if (iconLoaded) break;
|
||||
}*/
|
||||
|
||||
@ -383,8 +411,45 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
const char *name,
|
||||
*author = textGetString(StrId_DefaultPublisher),
|
||||
*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");
|
||||
if (themeInfo != NULL) {
|
||||
if(config_setting_lookup_string(themeInfo, "name", &name))
|
||||
@ -397,6 +462,20 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
strncpy(me->author, author, sizeof(me->author)-1);
|
||||
strncpy(me->version, version, sizeof(me->version)-1);
|
||||
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)
|
||||
@ -428,7 +507,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
|
||||
bool iconLoaded = false;
|
||||
|
||||
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
|
||||
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
|
||||
|
||||
if (iconLoaded) menuEntryParseIcon(me);
|
||||
|
||||
@ -437,17 +516,48 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
if (!iconLoaded && fileassoc_me->icon_gfx && fileassoc_me->icon_gfx_small)
|
||||
iconLoaded = menuEntryImportIconGfx(me, fileassoc_me->icon_gfx, fileassoc_me->icon_gfx_small);
|
||||
|
||||
strncpy(me->author, fileassoc_me->author, sizeof(me->author));
|
||||
me->author[sizeof(me->author)-1] = 0;
|
||||
//Attempt to load the nacp from {me->path filepath with extension .nacp}, then on failure use the config from fileassoc_me.
|
||||
memset(tempbuf, 0, sizeof(tempbuf));
|
||||
strncpy(tempbuf, me->path, sizeof(tempbuf));
|
||||
tempbuf[sizeof(tempbuf)-1] = 0;
|
||||
strptr = getExtension(tempbuf);
|
||||
strncpy(strptr, ".nacp", sizeof(tempbuf)-1 - ((ptrdiff_t)strptr - (ptrdiff_t)tempbuf));
|
||||
|
||||
strncpy(me->version, fileassoc_me->version, sizeof(me->version));
|
||||
me->version[sizeof(me->version)-1] = 0;
|
||||
bool nacpLoaded = menuEntryLoadExternalNacp(me, tempbuf);
|
||||
|
||||
if (nacpLoaded) menuEntryParseNacp(me);
|
||||
else {
|
||||
strncpy(me->author, fileassoc_me->author, sizeof(me->author));
|
||||
me->author[sizeof(me->author)-1] = 0;
|
||||
|
||||
strncpy(me->version, fileassoc_me->version, sizeof(me->version));
|
||||
me->version[sizeof(me->version)-1] = 0;
|
||||
}
|
||||
|
||||
// Initialize the argument data
|
||||
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];
|
||||
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));
|
||||
me->path[sizeof(me->path)-1] = 0;
|
||||
@ -457,16 +567,23 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//check for .filename.star in same path
|
||||
strptr = getSlash(me->path);
|
||||
if (strptr[0] == '/') strptr++;
|
||||
int strptrLen = strlen(strptr);
|
||||
snprintf(me->starpath, sizeof(me->starpath)-1, "%.*s.%.*s.star", (int)(strlen(me->path) - strptrLen), me->path, (int)strptrLen, strptr);
|
||||
me->starred = fileExists(me->starpath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void menuEntryFileassocLoad(const char* filepath) {
|
||||
bool success=0, success2=0;
|
||||
bool success=0, iconLoaded=0;
|
||||
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};
|
||||
int targets_len=0, i;
|
||||
int targets_len=0, args_len=0, i;
|
||||
const char *strptr = NULL;
|
||||
|
||||
char app_path[PATH_MAX+8];
|
||||
@ -475,8 +592,8 @@ void menuEntryFileassocLoad(const char* filepath) {
|
||||
char target_file_extension[PATH_MAX+1];
|
||||
char target_filename[PATH_MAX+1];
|
||||
|
||||
char app_author[ENTRY_AUTHORLENGTH+1];
|
||||
char app_version[ENTRY_VERLENGTH+1];
|
||||
char app_author[ENTRY_AUTHORLENGTH+2];
|
||||
char app_version[ENTRY_VERLENGTH+2];
|
||||
|
||||
uint8_t *app_icon_gfx = NULL;
|
||||
uint8_t *app_icon_gfx_small = NULL;
|
||||
@ -498,7 +615,8 @@ void menuEntryFileassocLoad(const char* filepath) {
|
||||
snprintf(app_path, sizeof(app_path)-1, "%s%s", menuGetRootBasePath(), strptr);
|
||||
if (config_setting_lookup_string(fileassoc, "icon_path", &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) {
|
||||
targets_len = config_setting_length(targets);
|
||||
@ -514,7 +632,7 @@ void menuEntryFileassocLoad(const char* filepath) {
|
||||
strptr = getSlash(app_path);
|
||||
if(strptr[0] == '/') strptr++;
|
||||
|
||||
if (menuEntryLoad(me, strptr, 0)) {
|
||||
if (menuEntryLoad(me, strptr, 0, true)) {
|
||||
strncpy(app_author, me->author, sizeof(app_author));
|
||||
app_author[sizeof(app_author)-1] = 0;
|
||||
strncpy(app_version, me->version, sizeof(app_version));
|
||||
@ -544,12 +662,13 @@ void menuEntryFileassocLoad(const char* filepath) {
|
||||
strncpy(target_file_extension, strptr, sizeof(target_file_extension)-1);
|
||||
if (config_setting_lookup_string(target, "filename", &strptr))
|
||||
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.
|
||||
if ((target_file_extension[0]!=0) == (target_filename[0]!=0)) continue;
|
||||
|
||||
me = menuCreateEntry(ENTRY_TYPE_FILEASSOC);
|
||||
success2 = 0;
|
||||
iconLoaded = 0;
|
||||
|
||||
if (me) {
|
||||
strncpy(me->path, app_path, sizeof(me->path));
|
||||
@ -568,22 +687,32 @@ void menuEntryFileassocLoad(const char* filepath) {
|
||||
}
|
||||
me->fileassoc_str[sizeof(me->fileassoc_str)-1] = 0;
|
||||
|
||||
if (target_icon_path[0]) success2 = menuEntryLoadExternalIcon(me, target_icon_path);
|
||||
if (!success2 && main_icon_path[0]) success2 = menuEntryLoadExternalIcon(me, main_icon_path);
|
||||
if (target_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, target_icon_path, false);
|
||||
if (!iconLoaded && main_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, main_icon_path, false);
|
||||
|
||||
if (success2) {
|
||||
if (iconLoaded) {
|
||||
menuEntryParseIcon(me);
|
||||
} 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 (success2)
|
||||
menuFileassocAddEntry(me);
|
||||
else
|
||||
menuDeleteEntry(me, 0);
|
||||
}
|
||||
if (me) menuFileassocAddEntry(me);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -602,65 +731,33 @@ void menuEntryFileassocLoad(const char* filepath) {
|
||||
void menuEntryParseIcon(menuEntry_s* me) {
|
||||
if (me->icon_size==0 || me->icon==NULL) return;
|
||||
|
||||
int w,h,samp;
|
||||
size_t imagesize = 256*256*3;
|
||||
me->icon_gfx = (uint8_t*)malloc(imagesize);
|
||||
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
|
||||
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
|
||||
|
||||
if (me->icon_gfx == NULL) {
|
||||
me->icon_size = 0;
|
||||
free(me->icon);
|
||||
me->icon = NULL;
|
||||
return;
|
||||
}
|
||||
size_t imagesize = layoutobj->imageSize[0]*layoutobj->imageSize[1]*3;
|
||||
bool ret=true;
|
||||
uint8_t *tmp_gfx = (uint8_t*)malloc(imagesize);
|
||||
|
||||
tjhandle _jpegDecompressor = tjInitDecompress();
|
||||
if (tmp_gfx == NULL) ret = false;
|
||||
|
||||
if (_jpegDecompressor == NULL) {
|
||||
free(me->icon_gfx);
|
||||
me->icon_gfx = NULL;
|
||||
if (ret) ret = assetsLoadJpgFromMemory(me->icon, me->icon_size, tmp_gfx, IMAGE_MODE_RGB24, layoutobj->imageSize[0], layoutobj->imageSize[1]);
|
||||
|
||||
me->icon_size = 0;
|
||||
free(me->icon);
|
||||
me->icon = NULL;
|
||||
return;
|
||||
}
|
||||
if (ret) me->icon_gfx = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], IMAGE_MODE_RGB24);
|
||||
|
||||
if (tjDecompressHeader2(_jpegDecompressor, me->icon, me->icon_size, &w, &h, &samp) == -1) {
|
||||
free(me->icon_gfx);
|
||||
me->icon_gfx = NULL;
|
||||
|
||||
me->icon_size = 0;
|
||||
free(me->icon);
|
||||
me->icon = NULL;
|
||||
tjDestroy(_jpegDecompressor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (w != 256 || h != 256 ) return;
|
||||
|
||||
if (tjDecompress2(_jpegDecompressor, me->icon, me->icon_size, me->icon_gfx, w, 0, h, TJPF_RGB, TJFLAG_ACCURATEDCT) == -1) {
|
||||
free(me->icon_gfx);
|
||||
me->icon_gfx = NULL;
|
||||
|
||||
me->icon_size = 0;
|
||||
free(me->icon);
|
||||
me->icon = NULL;
|
||||
tjDestroy(_jpegDecompressor);
|
||||
return;
|
||||
}
|
||||
if (ret && me->icon_gfx==NULL) ret = false;
|
||||
|
||||
me->icon_size = 0;
|
||||
free(me->icon);
|
||||
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 (me->icon_gfx_small == NULL) {
|
||||
if (!ret || me->icon_gfx_small == NULL) {
|
||||
free(me->icon_gfx);
|
||||
me->icon_gfx = NULL;
|
||||
}
|
||||
|
||||
free(tmp_gfx);
|
||||
}
|
||||
|
||||
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode) {
|
||||
@ -680,6 +777,11 @@ uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int des
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (srcWidth == destWidth && srcHeight == destHeight) {
|
||||
memcpy(out, image, destWidth*destHeight*(mode==IMAGE_MODE_RGBA32 ? 4 : 3));
|
||||
return out;
|
||||
}
|
||||
|
||||
int tmpx, tmpy;
|
||||
int pos;
|
||||
float sourceX, sourceY;
|
||||
@ -786,7 +888,7 @@ void menuEntryParseNacp(menuEntry_s* me) {
|
||||
|
||||
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__
|
||||
Result rc=0;
|
||||
|
@ -35,6 +35,7 @@ static void _menuAddEntry(menu_s *m, menuEntry_s* me) {
|
||||
m->lastEntry = me;
|
||||
}
|
||||
m->xPos = 0;
|
||||
m->slideSpeed = 0;
|
||||
m->nEntries ++;
|
||||
}
|
||||
|
||||
@ -59,6 +60,7 @@ static void menuAddEntryToFront(menuEntry_s* me) {
|
||||
m->lastEntry = me;
|
||||
}
|
||||
m->xPos = 0;
|
||||
m->slideSpeed = 0;
|
||||
m->nEntries ++;
|
||||
}
|
||||
|
||||
@ -96,20 +98,34 @@ static void menuSort(void) {
|
||||
menu_s* m = &s_menu[!s_curMenu];
|
||||
int nEntries = m->nEntries;
|
||||
if (nEntries==0) return;
|
||||
int nEntriesStar = 0, nEntriesNoStar = 0;
|
||||
|
||||
menuEntry_s** list = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*));
|
||||
if(list == NULL) return;
|
||||
menuEntry_s** listStar = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*));
|
||||
if(listStar == NULL) {
|
||||
free(list);
|
||||
return;
|
||||
}
|
||||
|
||||
menuEntry_s* p = m->firstEntry;
|
||||
for(i = 0; i < nEntries; ++i) {
|
||||
list[i] = p;
|
||||
if (p->starred)
|
||||
listStar[nEntriesStar++] = p;
|
||||
else
|
||||
list[nEntriesNoStar++] = p;
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
qsort(list, nEntries, sizeof(menuEntry_s*), menuEntryCmp);
|
||||
qsort(listStar, nEntriesStar, sizeof(menuEntry_s*), menuEntryCmp);
|
||||
qsort(list, nEntriesNoStar, sizeof(menuEntry_s*), menuEntryCmp);
|
||||
|
||||
menuEntry_s** pp = &m->firstEntry;
|
||||
for(i = 0; i < nEntries; ++i) {
|
||||
for(i = 0; i < nEntriesStar; ++i) {
|
||||
*pp = listStar[i];
|
||||
pp = &(*pp)->next;
|
||||
}
|
||||
for(i = 0; i < nEntriesNoStar; ++i) {
|
||||
*pp = list[i];
|
||||
pp = &(*pp)->next;
|
||||
}
|
||||
@ -117,13 +133,33 @@ static void menuSort(void) {
|
||||
*pp = NULL;
|
||||
|
||||
free(list);
|
||||
free(listStar);
|
||||
}
|
||||
|
||||
void menuReorder (void) {
|
||||
s_curMenu = !s_curMenu;
|
||||
menuSort();
|
||||
s_curMenu = !s_curMenu;
|
||||
menuClear();
|
||||
}
|
||||
|
||||
int menuScan(const char* target) {
|
||||
int pos;
|
||||
char dirsep[8];
|
||||
|
||||
if (chdir(target) < 0) return 1;
|
||||
if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL)
|
||||
return 1;
|
||||
|
||||
memset(dirsep, 0, sizeof(dirsep));
|
||||
dirsep[0] = '/';
|
||||
|
||||
//While cwd will not have '/' at the end normally, it will have it when cwd is the root dir ("sdmc:/"). Don't add '/' to the path below when it's already present.
|
||||
pos = strlen(s_menu[!s_curMenu].dirname);
|
||||
if (pos > 0) {
|
||||
if (s_menu[!s_curMenu].dirname[pos-1] == '/') dirsep[0] = 0;
|
||||
}
|
||||
|
||||
DIR* dir;
|
||||
struct dirent* dp;
|
||||
char tmp_path[PATH_MAX+1];
|
||||
@ -140,13 +176,12 @@ int menuScan(const char* target) {
|
||||
bool entrytype=0;
|
||||
|
||||
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", s_menu[!s_curMenu].dirname, dirsep, dp->d_name);
|
||||
|
||||
#ifdef __SWITCH__
|
||||
fsdev_dir_t* dirSt = (fsdev_dir_t*)dir->dirData->dirStruct;
|
||||
FsDirectoryEntry* entry = &dirSt->entry_data[dirSt->index];
|
||||
|
||||
entrytype = entry->type == ENTRYTYPE_DIR;
|
||||
#ifdef _DIRENT_HAVE_D_TYPE
|
||||
if (dp->d_type == DT_UNKNOWN)
|
||||
continue;
|
||||
entrytype = dp->d_type != DT_REG;
|
||||
#else
|
||||
struct stat tmpstat;
|
||||
|
||||
@ -174,7 +209,7 @@ int menuScan(const char* target) {
|
||||
strncpy(me->path, tmp_path, sizeof(me->path)-1);
|
||||
me->path[sizeof(me->path)-1] = 0;
|
||||
|
||||
if (menuEntryLoad(me, dp->d_name, shortcut))
|
||||
if (menuEntryLoad(me, dp->d_name, shortcut, true))
|
||||
menuAddEntry(me);
|
||||
else
|
||||
menuDeleteEntry(me, 0);
|
||||
@ -211,8 +246,23 @@ int themeMenuScan(const char* target) {
|
||||
memset(tmp_path, 0, sizeof(tmp_path));
|
||||
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name);
|
||||
|
||||
bool entrytype=0;
|
||||
|
||||
#ifdef _DIRENT_HAVE_D_TYPE
|
||||
if (dp->d_type == DT_UNKNOWN)
|
||||
continue;
|
||||
entrytype = dp->d_type != DT_REG;
|
||||
#else
|
||||
struct stat tmpstat;
|
||||
|
||||
if(stat(tmp_path, &tmpstat)==-1)
|
||||
continue;
|
||||
|
||||
entrytype = (tmpstat.st_mode & S_IFMT) != S_IFREG;
|
||||
#endif
|
||||
|
||||
const char* ext = getExtension(dp->d_name);
|
||||
if (strcasecmp(ext, ".cfg")==0)
|
||||
if (entrytype || strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0 || strcasecmp(ext, ".zip")==0)
|
||||
me = menuCreateEntry(ENTRY_TYPE_THEME);
|
||||
|
||||
if (!me)
|
||||
@ -220,7 +270,7 @@ int themeMenuScan(const char* target) {
|
||||
|
||||
strncpy(me->path, tmp_path, sizeof(me->path)-1);
|
||||
me->path[sizeof(me->path)-1] = 0;
|
||||
if (menuEntryLoad(me, dp->d_name, shortcut))
|
||||
if (menuEntryLoad(me, dp->d_name, shortcut, true))
|
||||
menuAddEntry(me);
|
||||
else
|
||||
menuDeleteEntry(me, 0);
|
||||
@ -232,7 +282,7 @@ int themeMenuScan(const char* target) {
|
||||
menuEntry_s* me = menuCreateEntry(ENTRY_TYPE_THEME);
|
||||
|
||||
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);
|
||||
else
|
||||
menuDeleteEntry(me, 0);
|
||||
|
513
common/menu.c
@ -6,10 +6,19 @@
|
||||
#include "switch/runtime/nxlink.h"
|
||||
#endif
|
||||
|
||||
double menuTimer;
|
||||
|
||||
char rootPathBase[PATH_MAX];
|
||||
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 menuLoadFileassoc(void);
|
||||
|
||||
char *menuGetRootPath(void) {
|
||||
return rootPath;
|
||||
}
|
||||
@ -29,6 +38,35 @@ void launchMenuEntryTask(menuEntry_s* arg) {
|
||||
launchMenuEntry(me);
|
||||
}
|
||||
|
||||
void toggleStarState(menuEntry_s* arg) {
|
||||
menuEntry_s* me = arg;
|
||||
if (me->starred) {
|
||||
if (fileExists(me->starpath))
|
||||
remove(me->starpath);
|
||||
} else {
|
||||
if (!fileExists(me->starpath)) {
|
||||
FILE* f = fopen(me->starpath, "w");
|
||||
if (f) fclose(f);
|
||||
}
|
||||
}
|
||||
me->starred = fileExists(me->starpath);
|
||||
//todo: error handling/message?
|
||||
|
||||
menuReorder();
|
||||
menu_s* menu = menuGetCurrent();
|
||||
menuEntry_s* meSearch = menu->firstEntry;
|
||||
menu->curEntry = -1;
|
||||
int i = 0;
|
||||
while (menu->curEntry < 0) {
|
||||
if (me == meSearch)
|
||||
menu->curEntry = i;
|
||||
else {
|
||||
meSearch = meSearch->next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum
|
||||
{
|
||||
HBMENU_DEFAULT,
|
||||
@ -73,11 +111,74 @@ void menuHandleAButton(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void menuHandleXButton(void) {
|
||||
menu_s* menu = menuGetCurrent();
|
||||
|
||||
if (menu->nEntries > 0 && hbmenu_state == HBMENU_DEFAULT) {
|
||||
int i;
|
||||
menuEntry_s* me;
|
||||
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
|
||||
toggleStarState(me);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
const char* themePath = arg->path;
|
||||
menu_s* menu = menuGetCurrent();
|
||||
SetThemePathToConfig(themePath);
|
||||
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) {
|
||||
@ -124,20 +225,18 @@ static void drawIcon(int x, int y, int width, int height, const uint8_t *image,
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *folder_icon_small;
|
||||
uint8_t *invalid_icon_small;
|
||||
uint8_t *theme_icon_small;
|
||||
|
||||
static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
|
||||
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
|
||||
int x, y;
|
||||
int start_y = 720 - 100 - 145;//*(n % 2);
|
||||
int end_y = start_y + 140 + 32;
|
||||
int start_y = layoutobj->posStart[1];//*(n % 2);
|
||||
int end_y = start_y + layoutobj->size[1];
|
||||
int start_x = off_x;//(n / 2);
|
||||
int end_x = start_x + 140;
|
||||
int end_x = start_x + layoutobj->size[0];
|
||||
int j;
|
||||
|
||||
const uint8_t *smallimg = NULL;
|
||||
const uint8_t *largeimg = NULL;
|
||||
char *strptr = NULL;
|
||||
char tmpstr[1024];
|
||||
|
||||
int border_start_x, border_end_x;
|
||||
@ -153,7 +252,7 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
|
||||
|
||||
if (is_active) {
|
||||
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_end_x = end_x+6;
|
||||
border_start_y = start_y-5;
|
||||
@ -231,29 +330,29 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
|
||||
}
|
||||
else if (me->type == ENTRY_TYPE_FOLDER) {
|
||||
smallimg = folder_icon_small;
|
||||
largeimg = assetsGetDataBuffer(AssetId_folder_icon);
|
||||
largeimg = folder_icon_large;
|
||||
}
|
||||
else if (me->type == ENTRY_TYPE_THEME){
|
||||
smallimg = theme_icon_small;
|
||||
if(themeGlobalPreset == THEME_PRESET_DARK)
|
||||
largeimg = assetsGetDataBuffer(AssetId_theme_icon_dark);
|
||||
else largeimg = assetsGetDataBuffer(AssetId_theme_icon_light);
|
||||
largeimg = theme_icon_large;
|
||||
}
|
||||
else {
|
||||
smallimg = invalid_icon_small;
|
||||
largeimg = assetsGetDataBuffer(AssetId_invalid_icon);
|
||||
largeimg = invalid_icon_large;
|
||||
}
|
||||
|
||||
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) {
|
||||
drawImage(117, 100, 256, 256, largeimg, IMAGE_MODE_RGB24);
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
|
||||
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+256;
|
||||
border_start_x = 117;
|
||||
border_end_x = 117+256;
|
||||
shadow_start_y = layoutobj->posStart[1]+layoutobj->size[1];
|
||||
border_start_x = layoutobj->posStart[0];
|
||||
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 (x=border_start_x; x<border_end_x; x++) {
|
||||
@ -267,21 +366,31 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
|
||||
}
|
||||
}
|
||||
|
||||
DrawTextTruncate(interuiregular14, start_x + 4, start_y + 4 + 18, themeCurrent.borderTextColor, me->name, 140 - 32, "...");
|
||||
if (me->type != ENTRY_TYPE_THEME)
|
||||
strptr = me->starred ? themeCurrent.labelStarOnText : "";
|
||||
else
|
||||
strptr = "";
|
||||
|
||||
memset(tmpstr, 0, sizeof(tmpstr));
|
||||
snprintf(tmpstr, sizeof(tmpstr)-1, "%s%s", strptr, me->name);
|
||||
|
||||
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) {
|
||||
start_x = 1280 - 790;
|
||||
start_y = 135;
|
||||
|
||||
DrawTextTruncate(interuimedium30, start_x, start_y + 39, themeCurrent.textColor, me->name, 1280 - start_x - 120 ,"...");
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryName];
|
||||
if (layoutobj->visible) DrawTextTruncate(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr, layoutobj->size[0], "...");
|
||||
|
||||
if (me->type != ENTRY_TYPE_FOLDER) {
|
||||
memset(tmpstr, 0, sizeof(tmpstr));
|
||||
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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -294,6 +403,8 @@ void computeFrontGradient(color_t baseColor, int height) {
|
||||
float dark_mult, dark_sub = 75;
|
||||
color_t color;
|
||||
|
||||
if (height < 0 || height > 720) return;
|
||||
|
||||
for (y=0; y<720; y++) {
|
||||
alpha = y - (720 - height);
|
||||
|
||||
@ -342,22 +453,20 @@ void menuStartupPath(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void menuStartup(void) {
|
||||
void menuLoadFileassoc(void) {
|
||||
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);
|
||||
menuFileassocScan(tmp_path);
|
||||
}
|
||||
|
||||
void menuStartup(void) {
|
||||
menuLoadFileassoc();
|
||||
|
||||
menuScan(rootPath);
|
||||
|
||||
folder_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_folder_icon), 256, 256, 140, 140, IMAGE_MODE_RGB24);
|
||||
invalid_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_invalid_icon), 256, 256, 140, 140, IMAGE_MODE_RGB24);
|
||||
if(themeGlobalPreset == THEME_PRESET_DARK)
|
||||
theme_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_theme_icon_dark), 256, 256, 140, 140, IMAGE_MODE_RGB24);
|
||||
else
|
||||
theme_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_theme_icon_light), 256, 256, 140, 140, IMAGE_MODE_RGB24);
|
||||
computeFrontGradient(themeCurrent.frontWaveColor, 280);
|
||||
//menuCreateMsgBox(780, 300, "This is a test");
|
||||
menuStartupCommon();
|
||||
}
|
||||
|
||||
void themeMenuStartup(void) {
|
||||
@ -368,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");
|
||||
|
||||
themeMenuScan(tmp_path);
|
||||
menuThemeSelectCurrentEntry();
|
||||
}
|
||||
|
||||
color_t waveBlendAdd(color_t a, color_t b, float alpha) {
|
||||
@ -379,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;
|
||||
color_t existing_color, new_color;
|
||||
|
||||
if (height < 0 || height > 720) return;
|
||||
height = 720 - height;
|
||||
|
||||
for (x=0; x<1280; x++) {
|
||||
@ -414,9 +525,53 @@ void drawWave(int id, float timer, color_t color, int height, float phase, float
|
||||
}
|
||||
}
|
||||
|
||||
void drawTime() {
|
||||
void drawCharge() {
|
||||
char chargeString[5];
|
||||
uint32_t batteryCharge;
|
||||
bool isCharging;
|
||||
bool validPower;
|
||||
|
||||
char timeString[9];
|
||||
validPower = powerGetDetails(&batteryCharge, &isCharging);
|
||||
|
||||
if (validPower)
|
||||
{
|
||||
batteryCharge = (batteryCharge > 100) ? 100 : batteryCharge;
|
||||
|
||||
sprintf(chargeString, "%d%%", batteryCharge);
|
||||
|
||||
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BatteryCharge];
|
||||
|
||||
if (layoutobj->visible) {
|
||||
int tmpX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], chargeString, 'r');
|
||||
DrawText(layoutobj->font, tmpX, layoutobj->posStart[1], themeCurrent.textColor, chargeString);
|
||||
}
|
||||
|
||||
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, AssetId id) {
|
||||
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_NetworkIcon];
|
||||
assetsDataEntry *data = NULL;
|
||||
assetsGetData(id, &data);
|
||||
if (layoutobj->visible) drawIcon(layoutobj->posType ? tmpX + layoutobj->posStart[0] : layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor);
|
||||
}
|
||||
|
||||
u32 drawStatus() {
|
||||
bool netstatusFlag=0;
|
||||
bool temperatureFlag=0;
|
||||
s32 temperature=0;
|
||||
AssetId id;
|
||||
|
||||
char tmpstr[32];
|
||||
|
||||
time_t unixTime = time(NULL);
|
||||
struct tm* timeStruct = localtime((const time_t *)&unixTime);
|
||||
@ -425,45 +580,33 @@ void drawTime() {
|
||||
int minutes = timeStruct->tm_min;
|
||||
int seconds = timeStruct->tm_sec;
|
||||
|
||||
sprintf(timeString, "%02d:%02d:%02d", hours, minutes, seconds);
|
||||
snprintf(tmpstr, sizeof(tmpstr)-1, "%02d:%02d:%02d", hours, minutes, seconds);
|
||||
|
||||
int 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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void drawCharge() {
|
||||
char chargeString[5];
|
||||
uint32_t batteryCharge;
|
||||
bool isCharging;
|
||||
bool validPower;
|
||||
void drawButtons(menu_s* menu, bool emptyDir, int *out_basePos) {
|
||||
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA];
|
||||
int basePos[2]={0};
|
||||
|
||||
validPower = powerGetDetails(&batteryCharge, &isCharging);
|
||||
|
||||
if (validPower)
|
||||
{
|
||||
batteryCharge = (batteryCharge > 100) ? 100 : batteryCharge;
|
||||
|
||||
sprintf(chargeString, "%d%%", batteryCharge);
|
||||
|
||||
int tmpX = GetTextXCoordinate(interuiregular14, 1180, chargeString, 'r');
|
||||
|
||||
DrawText(interuiregular14, tmpX - 15, 0 + 47 + 10 + 21, themeCurrent.textColor, chargeString);
|
||||
drawIcon(1180 - 11, 0 + 47 + 10 + 6, 10, 15, assetsGetDataBuffer(AssetId_battery_icon), themeCurrent.textColor);
|
||||
if (isCharging)
|
||||
drawIcon(tmpX - 32, 0 + 47 + 10 + 6, 9, 15, assetsGetDataBuffer(AssetId_charging_icon), themeCurrent.textColor);
|
||||
}
|
||||
}
|
||||
|
||||
void drawButtons(menu_s* menu, bool emptyDir, int *x_image_out) {
|
||||
int x_image = 1280 - 252 - 30 - 32;
|
||||
int x_text = 1280 - 216 - 30 - 32;
|
||||
|
||||
if(emptyDir) {
|
||||
x_image = 1280 - 126 - 30 - 32;
|
||||
x_text = 1280 - 90 - 30 - 32;
|
||||
}
|
||||
basePos[0] = layoutobj->posStart[0];
|
||||
basePos[1] = layoutobj->posStart[1];
|
||||
|
||||
#ifdef __SWITCH__
|
||||
if (strcmp( menu->dirname, "sdmc:/") != 0)
|
||||
@ -471,27 +614,26 @@ void drawButtons(menu_s* menu, bool emptyDir, int *x_image_out) {
|
||||
if (strcmp( menu->dirname, "/") != 0)
|
||||
#endif
|
||||
{
|
||||
//drawImage(x_image, 720 - 48, 32, 32, themeCurrent.buttonBImage, IMAGE_MODE_RGBA32);
|
||||
DrawText(fontscale7, x_image, 720 - 47 + 26, themeCurrent.textColor, themeCurrent.buttonBText);//Display the 'B' button from SharedFont.
|
||||
DrawText(interuimedium20, x_text, 720 - 47 + 26, themeCurrent.textColor, textGetString(StrId_Actions_Back));
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonBText];
|
||||
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonBText, basePos[0], basePos[1], !emptyDir ? layoutobj->posStart : layoutobj->posEnd, basePos, themeCurrent.textColor, textGetString(StrId_Actions_Back), 'l');
|
||||
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)
|
||||
{
|
||||
x_text = GetTextXCoordinate(interuiregular18, x_image - 32, textGetString(StrId_NetLoader), 'r');
|
||||
x_image = x_text - 36;
|
||||
*x_image_out = x_image - 40;
|
||||
|
||||
DrawText(fontscale7, x_image, 720 - 47 + 26, themeCurrent.textColor, themeCurrent.buttonYText);
|
||||
DrawText(interuiregular18, x_text, 720 - 47 + 26, themeCurrent.textColor, textGetString(StrId_NetLoader));
|
||||
|
||||
x_text = GetTextXCoordinate(interuiregular18, x_image - 32, textGetString(StrId_ThemeMenu), 'r');
|
||||
x_image = x_text - 36;
|
||||
*x_image_out = x_image - 40;
|
||||
|
||||
DrawText(fontscale7, x_image, 720 - 47 + 26, themeCurrent.textColor, themeCurrent.buttonMText);
|
||||
DrawText(interuiregular18, x_text, 720 - 47 + 26, themeCurrent.textColor, textGetString(StrId_ThemeMenu));
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonYText];
|
||||
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonYText, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, textGetString(StrId_NetLoader), 'r');
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonY];
|
||||
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonY, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonYText, 'l');
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonMText];
|
||||
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonMText, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, textGetString(StrId_ThemeMenu), 'r');
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonM];
|
||||
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonM, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonMText, 'l');
|
||||
}
|
||||
|
||||
out_basePos[0] = basePos[0];
|
||||
out_basePos[1] = basePos[1];
|
||||
}
|
||||
|
||||
void menuUpdateNetloader(netloaderState *netloader_state) {
|
||||
@ -526,40 +668,89 @@ void menuLoop(void) {
|
||||
menuEntry_s* me;
|
||||
menu_s* menu = NULL;
|
||||
int i;
|
||||
int x, y;
|
||||
int menupath_x_endpos = 918 + 40;
|
||||
int x, y, endy = 720;
|
||||
int curPos[2]={0};
|
||||
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
|
||||
Draw4PixelsRaw(x, y, themeCurrent.backgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
drawWave(0, menuTimer, themeCurrent.backWaveColor, 295, 0.0, 3.0);
|
||||
drawWave(1, menuTimer, themeCurrent.middleWaveColor, 290, 2.0, 3.5);
|
||||
drawWave(2, menuTimer, themeCurrent.frontWaveColor, 280, 4.0, -2.5);
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage];
|
||||
assetsDataEntry *data = NULL;
|
||||
assetsGetData(AssetId_background_image, &data);
|
||||
if (layoutobj->visible && data) drawImage(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], endy < data->imageSize[1] ? endy : data->imageSize[1], data->buffer, data->imageMode);
|
||||
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackWave];
|
||||
if (layoutobj->visible) drawWave(0, menuTimer, themeCurrent.backWaveColor, layoutobj->size[1], 0.0, 3.0);
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave];
|
||||
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;
|
||||
|
||||
drawImage(40, 20, 140, 60, themeCurrent.hbmenuLogoImage, IMAGE_MODE_RGBA32);
|
||||
DrawText(interuiregular14, 180, 46 + 18, themeCurrent.textColor, VERSION);
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_Logo];
|
||||
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();
|
||||
|
||||
#ifdef __SWITCH__
|
||||
AppletType at = appletGetAppletType();
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_AttentionText];
|
||||
if (at != AppletType_Application && at != AppletType_SystemApplication && layoutobj->visible) {
|
||||
const char* appletMode = textGetString(StrId_AppletMode);
|
||||
u32 x_pos = GetTextXCoordinate(layoutobj->font, statusXPos, appletMode, 'r');
|
||||
DrawText(layoutobj->font, layoutobj->posType ? x_pos + layoutobj->posStart[0] : layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.attentionTextColor, appletMode);
|
||||
}
|
||||
const char* loaderInfo = envGetLoaderInfo();
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_LoaderInfo];
|
||||
if (loaderInfo && layoutobj->visible) {
|
||||
u32 x_pos = layoutobj->posStart[0];
|
||||
char* spacePos = strchr(loaderInfo, ' ');
|
||||
if (spacePos) {
|
||||
char tempbuf[64] = {0};
|
||||
size_t tempsize = spacePos - loaderInfo + 1;
|
||||
if (tempsize > sizeof(tempbuf)-1) tempsize = sizeof(tempbuf)-1;
|
||||
memcpy(tempbuf, loaderInfo, tempsize);
|
||||
x_pos = GetTextXCoordinate(layoutobj->font, layoutobj->posEnd[0], tempbuf, 'r');
|
||||
}
|
||||
DrawText(layoutobj->font, x_pos, layoutobj->posStart[1], themeCurrent.textColor, loaderInfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PERF_LOG_DRAW//Seperate from the PERF_LOG define since this might affect perf.
|
||||
extern u64 g_tickdiff_vsync;
|
||||
extern u64 g_tickdiff_frame;
|
||||
|
||||
char tmpstr[64];
|
||||
|
||||
snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_vsync);
|
||||
DrawText(interuiregular14, 180 + 256, 46 + 18, themeCurrent.textColor, tmpstr);
|
||||
|
||||
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
|
||||
|
||||
drawTime();
|
||||
drawCharge();
|
||||
|
||||
memset(&netloader_state, 0, sizeof(netloader_state));
|
||||
netloaderGetState(&netloader_state);
|
||||
|
||||
@ -574,8 +765,11 @@ void menuLoop(void) {
|
||||
|
||||
menuCloseMsgBox();
|
||||
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) {
|
||||
@ -595,29 +789,54 @@ void menuLoop(void) {
|
||||
launchMenuEntryTask(netloader_state.me);
|
||||
}
|
||||
} 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
|
||||
{
|
||||
static int v = 0;
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
|
||||
int entries_count = layoutobj->posEnd[0];
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
|
||||
|
||||
if (menu->nEntries > 7) {
|
||||
int wanted_x = clamp(-menu->curEntry * (140 + 30), -(menu->nEntries - 7) * (140 + 30), 0);
|
||||
menu->xPos += v;
|
||||
v += (wanted_x - menu->xPos) / 3;
|
||||
v /= 2;
|
||||
// Gentle Realign only when not manually moving
|
||||
if (menu->slideSpeed == 0) {
|
||||
if (menu->nEntries > entries_count) {
|
||||
int wanted_x = clamp(-menu->curEntry * layoutobj->posEnd[0], -(menu->nEntries - entries_count) * layoutobj->posEnd[0], 0);
|
||||
menu->xPos += v;
|
||||
v += (wanted_x - menu->xPos) / 3;
|
||||
v /= 2;
|
||||
}
|
||||
else {
|
||||
menu->xPos = v = 0;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
// Draw menu entries
|
||||
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 screen_width = 1280;
|
||||
if (entry_start_x >= (screen_width - menu->xPos))
|
||||
@ -628,37 +847,61 @@ void menuLoop(void) {
|
||||
if (is_active)
|
||||
active_entry = me;
|
||||
|
||||
drawEntry(me, entry_start_x + menu->xPos, is_active);
|
||||
if (!is_active && entry_draw_x < -(layoutobj->posStart[0] + layoutobj->posEnd[0]))
|
||||
continue;
|
||||
|
||||
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) {
|
||||
DrawText(interuiregular18, getX, 30 + 26 + 32 + 10, 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 (layoutobj->visible) {
|
||||
getX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], textGetString(StrId_ThemeMenu), 'r');
|
||||
|
||||
if(hbmenu_state == HBMENU_THEME_MENU) {
|
||||
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->type == ENTRY_TYPE_THEME) {
|
||||
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));
|
||||
}
|
||||
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.
|
||||
DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Launch));
|
||||
}
|
||||
else {
|
||||
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_Open));
|
||||
}
|
||||
const char *buttonstr = "";
|
||||
|
||||
if (active_entry->type == ENTRY_TYPE_THEME)
|
||||
buttonstr = textGetString(StrId_Actions_Apply);
|
||||
else if (active_entry->type != ENTRY_TYPE_FOLDER)
|
||||
buttonstr = textGetString(StrId_Actions_Launch);
|
||||
else
|
||||
buttonstr = textGetString(StrId_Actions_Open);
|
||||
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonAText];
|
||||
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, curPos);
|
||||
|
||||
if (active_entry && active_entry->type != ENTRY_TYPE_THEME) {
|
||||
const char *buttonstr = "";
|
||||
if (active_entry->starred)
|
||||
buttonstr = textGetString(StrId_Actions_Unstar);
|
||||
else
|
||||
buttonstr = textGetString(StrId_Actions_Star);
|
||||
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonXText];
|
||||
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonXText, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, buttonstr, 'r');
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonX];
|
||||
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonX, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, themeCurrent.buttonXText, 'l');
|
||||
}
|
||||
|
||||
drawButtons(menu, false, &menupath_x_endpos);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ struct menu_s_tag
|
||||
int nEntries;
|
||||
int curEntry;
|
||||
int xPos;
|
||||
int slideSpeed;
|
||||
|
||||
char dirname[PATH_MAX+1];
|
||||
};
|
||||
@ -51,6 +52,7 @@ struct menuEntry_s_tag
|
||||
MenuEntryType type;
|
||||
|
||||
char path[PATH_MAX+8];
|
||||
char starpath[PATH_MAX+8];
|
||||
argData_s args;
|
||||
|
||||
bool fileassoc_type;//0=file_extension, 1 = filename
|
||||
@ -64,6 +66,8 @@ struct menuEntry_s_tag
|
||||
size_t icon_size;
|
||||
uint8_t *icon_gfx;
|
||||
uint8_t *icon_gfx_small;
|
||||
|
||||
bool starred;
|
||||
|
||||
NacpStruct *nacp;
|
||||
};
|
||||
@ -74,7 +78,7 @@ typedef enum
|
||||
IMAGE_MODE_RGBA32
|
||||
} ImageMode;
|
||||
|
||||
double menuTimer;
|
||||
extern double menuTimer;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -83,7 +87,7 @@ extern "C" {
|
||||
void menuEntryInit(menuEntry_s* me, MenuEntryType type);
|
||||
void menuEntryFree(menuEntry_s* me, bool skip_icongfx);
|
||||
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);
|
||||
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode);
|
||||
void menuEntryParseNacp(menuEntry_s* me);
|
||||
@ -97,11 +101,13 @@ void menuDeleteEntry(menuEntry_s* me, bool skip_icongfx);
|
||||
|
||||
menu_s* menuGetCurrent(void);
|
||||
menu_s* menuFileassocGetCurrent(void);
|
||||
void menuReorder (void);
|
||||
int menuScan(const char* target);
|
||||
int themeMenuScan(const char* target);
|
||||
int menuFileassocScan(const char* target);
|
||||
|
||||
void launchMenuEntryTask(menuEntry_s* arg);
|
||||
void toggleStarState(menuEntry_s* arg);
|
||||
void launchApplyThemeTask(menuEntry_s* arg);
|
||||
void launchMenuBackTask();
|
||||
void launchMenuNetloaderTask();
|
||||
@ -109,6 +115,7 @@ char *menuGetRootPath(void);
|
||||
char *menuGetRootBasePath(void);
|
||||
|
||||
void menuHandleAButton(void);
|
||||
void menuHandleXButton(void);
|
||||
|
||||
bool menuIsNetloaderActive(void);
|
||||
|
||||
|
@ -16,6 +16,7 @@ void drawMsgBoxBgToBuff(color_t *buff, int width, int height) {
|
||||
float rad, alpha;
|
||||
color_t base_color = themeCurrent.backgroundColor;
|
||||
color_t color;
|
||||
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
|
||||
|
||||
for (y=0; y<height; y++) {
|
||||
for (x=0; x<width; x++) {
|
||||
@ -62,7 +63,7 @@ void drawMsgBoxBgToBuff(color_t *buff, int width, int height) {
|
||||
else
|
||||
color = base_color;
|
||||
|
||||
if (y == height - 80) {
|
||||
if (y == height + layoutobj->posStart[1]) {
|
||||
color = themeCurrent.separatorColor;
|
||||
}
|
||||
|
||||
@ -76,6 +77,7 @@ void menuDrawMsgBox() {
|
||||
if (!menuIsMsgBoxOpen())
|
||||
return;
|
||||
|
||||
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
|
||||
int off;
|
||||
int x, y;
|
||||
int start_x = 1280 / 2 - currMsgBox.width / 2;
|
||||
@ -85,7 +87,7 @@ void menuDrawMsgBox() {
|
||||
color_t curr_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 shadow_start_y, shadow_y;
|
||||
@ -121,7 +123,7 @@ void menuDrawMsgBox() {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -135,10 +137,11 @@ void menuDrawMsgBox() {
|
||||
x = GetTextXCoordinate(interuiregular18, start_x + (currMsgBox.width / 2), textptr, 'c');
|
||||
|
||||
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) {
|
||||
x = GetTextXCoordinate(interuimedium20, start_x + (currMsgBox.width / 2), textGetString(StrId_MsgBox_OK), 'c');
|
||||
|
@ -274,7 +274,7 @@ static int decompress(int sock, FILE *fh, size_t filesize) {
|
||||
int ret;
|
||||
unsigned have;
|
||||
z_stream strm;
|
||||
size_t chunksize;
|
||||
uint32_t chunksize=0;
|
||||
|
||||
/* allocate inflate state */
|
||||
strm.zalloc = Z_NULL;
|
||||
@ -304,6 +304,12 @@ static int decompress(int sock, FILE *fh, size_t filesize) {
|
||||
return Z_DATA_ERROR;
|
||||
}
|
||||
|
||||
if (chunksize > sizeof(in)) {
|
||||
(void)inflateEnd(&strm);
|
||||
netloader_error("Invalid chunk size",chunksize);
|
||||
return Z_DATA_ERROR;
|
||||
}
|
||||
|
||||
strm.avail_in = recvall(sock,in,chunksize,0);
|
||||
|
||||
if (strm.avail_in == 0) {
|
||||
@ -362,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 len, namelen, filelen;
|
||||
char filename[PATH_MAX+1];
|
||||
char filepath[PATH_MAX+1];
|
||||
len = recvall(sock, &namelen, 4, 0);
|
||||
|
||||
if (len != 4) {
|
||||
@ -370,19 +376,19 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (namelen >= sizeof(filename)-1) {
|
||||
netloader_error("Filename length is too large",errno);
|
||||
if (namelen >= sizeof(filepath)-1) {
|
||||
netloader_error("File-path length is too large",errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = recvall(sock, filename, namelen, 0);
|
||||
len = recvall(sock, filepath, namelen, 0);
|
||||
|
||||
if (len != namelen) {
|
||||
netloader_error("Error getting filename", errno);
|
||||
netloader_error("Error getting file-path", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
filename[namelen] = 0;
|
||||
filepath[namelen] = 0;
|
||||
|
||||
len = recvall(sock, &filelen, 4, 0);
|
||||
|
||||
@ -397,51 +403,75 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
|
||||
|
||||
int response = 0;
|
||||
|
||||
sanitisePath(filename);
|
||||
sanitisePath(filepath);
|
||||
|
||||
snprintf(me->path, sizeof(me->path)-1, "%s%s%s", menuGetRootPath(), DIRECTORY_SEPARATOR, filename);
|
||||
me->path[PATH_MAX] = 0;
|
||||
snprintf(me->path, sizeof(me->path)-1, "%s%s%s", menuGetRootPath(), DIRECTORY_SEPARATOR, filepath);
|
||||
// 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;
|
||||
ad->dst = (char*)&ad->buf[1];
|
||||
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
|
||||
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) {
|
||||
response = -1;
|
||||
netloader_error("open", errno);
|
||||
} else {
|
||||
if (ftruncate(fd,filelen) == -1) {
|
||||
response = -2;
|
||||
netloader_error("ftruncate",errno);
|
||||
if (fd < 0) {
|
||||
response = -1;
|
||||
netloader_error("open", errno);
|
||||
} else {
|
||||
if (ftruncate(fd,filelen) == -1) {
|
||||
response = -2;
|
||||
netloader_error("ftruncate",errno);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
FILE *file = NULL;
|
||||
|
||||
if (response == 0) file = fopen(me->path,"wb");
|
||||
|
||||
if(NULL == file) {
|
||||
perror("file");
|
||||
response = -1;
|
||||
if (response == 0) {
|
||||
file = fopen(filepath,"wb");
|
||||
if(file == NULL) {
|
||||
perror("file");
|
||||
response = -1;
|
||||
}
|
||||
}
|
||||
|
||||
send(sock,(char *)&response,sizeof(response),0);
|
||||
|
||||
char *writebuffer = NULL;
|
||||
if (response == 0 ) {
|
||||
writebuffer = malloc(FILE_BUFFER_SIZE);
|
||||
if (writebuffer==NULL) {
|
||||
netloader_error("Failed to allocate memory",ENOMEM);
|
||||
response = -1;
|
||||
}
|
||||
else {
|
||||
memset(writebuffer, 0, FILE_BUFFER_SIZE);
|
||||
setvbuf(file,writebuffer,_IOFBF, FILE_BUFFER_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
//char *writebuffer=malloc(FILE_BUFFER_SIZE);
|
||||
//setvbuf(file,writebuffer,_IOFBF, FILE_BUFFER_SIZE);
|
||||
|
||||
//printf("transferring %s\n%d bytes.\n", filename, filelen);
|
||||
if (response == 0 ) {
|
||||
//printf("transferring %s\n%d bytes.\n", filepath, filelen);
|
||||
|
||||
if (decompress(sock,file,filelen)==Z_OK) {
|
||||
int netloaded_cmdlen = 0;
|
||||
@ -464,7 +494,7 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -486,13 +516,14 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
|
||||
} else {
|
||||
response = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//free(writebuffer);
|
||||
if (file) {
|
||||
fflush(file);
|
||||
fclose(file);
|
||||
|
||||
if (response == -1) unlink(me->path);
|
||||
}
|
||||
if (response == -1) unlink(filepath);
|
||||
free(writebuffer);
|
||||
|
||||
return response;
|
||||
}
|
||||
@ -536,7 +567,15 @@ int netloader_activate(void) {
|
||||
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)
|
||||
{
|
||||
netloader_socket_error("bind");
|
||||
@ -688,6 +727,10 @@ Result netloaderInit(void) {
|
||||
|
||||
#ifdef __SWITCH__
|
||||
rc = socketInitializeDefault();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = nifmInitialize(NifmServiceType_User);
|
||||
if (R_FAILED(rc)) socketExit();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __WIN32__
|
||||
@ -714,6 +757,7 @@ void netloaderExit(void) {
|
||||
mtx_destroy(&netloader_mtx);
|
||||
|
||||
#ifdef __SWITCH__
|
||||
nifmExit();
|
||||
socketExit();
|
||||
#endif
|
||||
|
||||
|
5
common/netstatus.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
bool netstatusGetDetails(AssetId *id);
|
||||
|
130
common/status.c
Normal file
@ -0,0 +1,130 @@
|
||||
#include "status.h"
|
||||
|
||||
static bool s_statusInitialized = 0;
|
||||
static thrd_t s_statusThread;
|
||||
static cnd_t s_statusCdn;
|
||||
static mtx_t s_statusMtx;
|
||||
static mtx_t s_statusAccessMtx;
|
||||
static bool s_statusExit;
|
||||
|
||||
static bool s_statusReady;
|
||||
static bool s_statusNetFlag;
|
||||
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.
|
||||
|
||||
static int statusThreadProc(void* unused)
|
||||
{
|
||||
mtx_lock(&s_statusMtx);
|
||||
|
||||
struct timespec timeout = {0};
|
||||
bool tmpflag=0;
|
||||
bool thermalflag=0;
|
||||
bool thermal_initialized=0;
|
||||
AssetId tmpid;
|
||||
s32 temperature;
|
||||
|
||||
thermal_initialized = thermalstatusInit();
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &timeout);
|
||||
timeout.tv_sec++;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
cnd_timedwait(&s_statusCdn, &s_statusMtx, &timeout);
|
||||
|
||||
if (s_statusExit)
|
||||
break;
|
||||
|
||||
tmpflag = netstatusGetDetails(&tmpid);
|
||||
if (thermal_initialized) thermalflag = thermalstatusGetDetails(&temperature);
|
||||
|
||||
mtx_lock(&s_statusAccessMtx);
|
||||
|
||||
s_statusNetFlag = tmpflag;
|
||||
s_statusNetAssetId = tmpid;
|
||||
|
||||
s_statusTemperatureFlag = thermalflag;
|
||||
s_statusTemperature = temperature;
|
||||
|
||||
s_statusReady = 1;
|
||||
|
||||
mtx_unlock(&s_statusAccessMtx);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &timeout);
|
||||
timeout.tv_sec++;
|
||||
}
|
||||
|
||||
mtx_unlock(&s_statusMtx);
|
||||
|
||||
if (thermal_initialized) thermalstatusExit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature) {
|
||||
if (!s_statusReady) return 0;
|
||||
|
||||
mtx_lock(&s_statusAccessMtx);
|
||||
|
||||
*netstatusFlag = s_statusNetFlag;
|
||||
*netstatusAssetId = s_statusNetAssetId;
|
||||
|
||||
*temperatureFlag = s_statusTemperatureFlag;
|
||||
*temperature = s_statusTemperature;
|
||||
|
||||
mtx_unlock(&s_statusAccessMtx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool statusInit(void)
|
||||
{
|
||||
if (s_statusInitialized) return 1;
|
||||
|
||||
if (cnd_init(&s_statusCdn) != thrd_success) return 0;
|
||||
if (mtx_init(&s_statusMtx, mtx_plain) != thrd_success) {
|
||||
cnd_destroy(&s_statusCdn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mtx_init(&s_statusAccessMtx, mtx_plain) != thrd_success) {
|
||||
mtx_destroy(&s_statusMtx);
|
||||
cnd_destroy(&s_statusCdn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (thrd_create(&s_statusThread, statusThreadProc, 0) != thrd_success) {
|
||||
mtx_destroy(&s_statusAccessMtx);
|
||||
mtx_destroy(&s_statusMtx);
|
||||
cnd_destroy(&s_statusCdn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s_statusInitialized = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void statusExit(void)
|
||||
{
|
||||
int res=0;
|
||||
|
||||
if (!s_statusInitialized) return;
|
||||
s_statusInitialized = 0;
|
||||
|
||||
mtx_lock(&s_statusMtx);
|
||||
s_statusExit = true;
|
||||
cnd_signal(&s_statusCdn);
|
||||
mtx_unlock(&s_statusMtx);
|
||||
|
||||
thrd_join(s_statusThread, &res);
|
||||
|
||||
s_statusReady = 0;
|
||||
|
||||
mtx_destroy(&s_statusAccessMtx);
|
||||
mtx_destroy(&s_statusMtx);
|
||||
cnd_destroy(&s_statusCdn);
|
||||
}
|
||||
|
7
common/status.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
bool statusInit(void);
|
||||
void statusExit(void);
|
||||
bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature);
|
||||
|
@ -10,14 +10,14 @@ static int s_textLang = 1;
|
||||
|
||||
Result textInit(void) {
|
||||
#ifdef __SWITCH__
|
||||
s32 Language=0;
|
||||
SetLanguage Language=SetLanguage_ENUS;
|
||||
|
||||
s_textLang = SetLanguage_ENUS;
|
||||
s_textLang = Language;
|
||||
|
||||
Result rc = setInitialize();
|
||||
if (R_SUCCEEDED(rc)) rc = setGetSystemLanguage(&s_textLanguageCode);
|
||||
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();
|
||||
if (R_FAILED(rc)) return rc;
|
||||
#else
|
||||
|
552
common/theme.c
@ -1,4 +1,5 @@
|
||||
#include "theme.h"
|
||||
#include <physfs.h>
|
||||
|
||||
theme_t themeCurrent;
|
||||
ThemePreset themeGlobalPreset;
|
||||
@ -10,15 +11,94 @@ bool colorFromSetting(config_setting_t *rgba, color_t *col) {
|
||||
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) {
|
||||
themeGlobalPreset = preset;
|
||||
theme_t themeLight = (theme_t) {
|
||||
theme_t themeLight = (theme_t) {
|
||||
.textColor = MakeColor(0, 0, 0, 255),
|
||||
.attentionTextColor = MakeColor(255, 0, 0, 255),
|
||||
.frontWaveColor = MakeColor(100, 212, 250, 255),
|
||||
.middleWaveColor = MakeColor(100, 153, 255, 255),
|
||||
.backWaveColor = MakeColor(154, 171, 255, 255),
|
||||
.backgroundColor = MakeColor(233, 236, 241, 255),
|
||||
.highlightColor = MakeColor(91, 237, 224, 255),
|
||||
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
|
||||
.separatorColor = MakeColor(219, 218, 219, 255),
|
||||
.borderColor = MakeColor(255,255,255,255),
|
||||
.borderTextColor = MakeColor(64,64,64,255),
|
||||
@ -26,19 +106,23 @@ void themeStartup(ThemePreset preset) {
|
||||
.enableWaveBlending = 0,
|
||||
.buttonAText = "\uE0E0",
|
||||
.buttonBText = "\uE0E1",
|
||||
.buttonXText = "\uE0E2",
|
||||
.buttonYText = "\uE0E3",
|
||||
.buttonPText = "\uE0EF",
|
||||
.buttonMText = "\uE0F0",
|
||||
.hbmenuLogoImage = assetsGetDataBuffer(AssetId_hbmenu_logo_light)
|
||||
.labelStarOnText = "\u2605",
|
||||
.labelStarOffText = "\u2606",
|
||||
};
|
||||
|
||||
theme_t themeDark = (theme_t) {
|
||||
|
||||
theme_t themeDark = (theme_t) {
|
||||
.textColor = MakeColor(255, 255, 255, 255),
|
||||
.attentionTextColor = MakeColor(255, 0, 0, 255),
|
||||
.frontWaveColor = MakeColor(96, 204, 204, 255),
|
||||
.middleWaveColor = MakeColor(66, 154, 159, 255),
|
||||
.backWaveColor = MakeColor(73, 103, 169, 255),
|
||||
.backgroundColor = MakeColor(45, 45, 50, 255),
|
||||
.highlightColor = MakeColor(91, 237, 224, 255),
|
||||
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
|
||||
.separatorColor = MakeColor(219, 218, 219, 255),
|
||||
.borderColor = MakeColor(255,255,255,255),
|
||||
.borderTextColor = MakeColor(64,64,64,255),
|
||||
@ -46,10 +130,282 @@ void themeStartup(ThemePreset preset) {
|
||||
.enableWaveBlending = 0,
|
||||
.buttonAText = "\uE0A0",
|
||||
.buttonBText = "\uE0A1",
|
||||
.buttonXText = "\uE0A2",
|
||||
.buttonYText = "\uE0A3",
|
||||
.buttonPText = "\uE0B3",
|
||||
.buttonMText = "\uE0B4",
|
||||
.hbmenuLogoImage = assetsGetDataBuffer(AssetId_hbmenu_logo_dark)
|
||||
.labelStarOnText = "\u2605",
|
||||
.labelStarOffText = "\u2606",
|
||||
};
|
||||
|
||||
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};
|
||||
@ -58,14 +414,55 @@ void themeStartup(ThemePreset preset) {
|
||||
theme_t *themeDefault;
|
||||
config_t cfg = {0};
|
||||
config_init(&cfg);
|
||||
config_setting_t *theme = NULL;
|
||||
color_t text, frontWave, middleWave, backWave, background, highlight, separator, borderColor, borderTextColor, progressBarColor;
|
||||
config_setting_t *theme = NULL, *layout = NULL, *assets = NULL;
|
||||
color_t text, logoColor={0}, attentionText, frontWave, middleWave, backWave, background, highlight, highlightGradientEdgeColor, separator, borderColor, borderTextColor, progressBarColor;
|
||||
int waveBlending;
|
||||
const char *AText, *BText, *YText, *PText, *MText;
|
||||
const char *AText, *BText, *XText, *YText, *PText, *MText, *starOnText, *starOffText;
|
||||
bool logoColor_set = 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)
|
||||
good_cfg = config_read_file(&cfg, themePath);
|
||||
assetsClearTheme();
|
||||
|
||||
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) {
|
||||
case THEME_PRESET_LIGHT:
|
||||
@ -86,6 +483,10 @@ void themeStartup(ThemePreset preset) {
|
||||
if (theme != NULL) {
|
||||
if (!colorFromSetting(config_setting_lookup(theme, "textColor"), &text))
|
||||
text = themeDefault->textColor;
|
||||
if (colorFromSetting(config_setting_lookup(theme, "logoColor"), &logoColor))
|
||||
logoColor_set = true;
|
||||
if (!colorFromSetting(config_setting_lookup(theme, "attentionTextColor"), &attentionText))
|
||||
attentionText = themeDefault->attentionTextColor;
|
||||
if (!colorFromSetting(config_setting_lookup(theme, "frontWaveColor"), &frontWave))
|
||||
frontWave = themeDefault->frontWaveColor;
|
||||
if (!colorFromSetting(config_setting_lookup(theme, "middleWaveColor"), &middleWave))
|
||||
@ -96,6 +497,8 @@ void themeStartup(ThemePreset preset) {
|
||||
background = themeDefault->backgroundColor;
|
||||
if (!colorFromSetting(config_setting_lookup(theme, "highlightColor"), &highlight))
|
||||
highlight = themeDefault->highlightColor;
|
||||
if (!colorFromSetting(config_setting_lookup(theme, "highlightGradientEdgeColor"), &highlightGradientEdgeColor))
|
||||
highlightGradientEdgeColor = themeDefault->highlightGradientEdgeColor;
|
||||
if (!colorFromSetting(config_setting_lookup(theme, "separatorColor"), &separator))
|
||||
separator = themeDefault->separatorColor;
|
||||
if (!colorFromSetting(config_setting_lookup(theme, "borderColor"), &borderColor))
|
||||
@ -110,38 +513,143 @@ void themeStartup(ThemePreset preset) {
|
||||
AText = themeDefault->buttonAText;
|
||||
if (!config_setting_lookup_string(theme, "buttonBText", &BText))
|
||||
BText = themeDefault->buttonBText;
|
||||
if (!config_setting_lookup_string(theme, "buttonXText", &XText))
|
||||
XText = themeDefault->buttonXText;
|
||||
if (!config_setting_lookup_string(theme, "buttonYText", &YText))
|
||||
YText = themeDefault->buttonYText;
|
||||
if (!config_setting_lookup_string(theme, "buttonPText", &PText))
|
||||
PText = themeDefault->buttonPText;
|
||||
if (!config_setting_lookup_string(theme, "buttonMText", &MText))
|
||||
MText = themeDefault->buttonMText;
|
||||
themeCurrent = (theme_t) {
|
||||
if (!config_setting_lookup_string(theme, "labelStarOnText", &starOnText))
|
||||
starOnText = themeDefault->labelStarOnText;
|
||||
if (!config_setting_lookup_string(theme, "labelStarOffText", &starOffText))
|
||||
starOffText = themeDefault->labelStarOffText;
|
||||
themeCurrent = (theme_t) {
|
||||
.textColor = text,
|
||||
.logoColor = logoColor,
|
||||
.attentionTextColor = attentionText,
|
||||
.frontWaveColor = frontWave,
|
||||
.middleWaveColor = middleWave,
|
||||
.backWaveColor = backWave,
|
||||
.backgroundColor = background,
|
||||
.highlightColor = highlight,
|
||||
.highlightGradientEdgeColor = highlightGradientEdgeColor,
|
||||
.separatorColor = separator,
|
||||
.borderColor = borderColor,
|
||||
.borderTextColor = borderTextColor,
|
||||
.progressBarColor = progressBarColor,
|
||||
.logoColor_set = logoColor_set,
|
||||
.enableWaveBlending = waveBlending,
|
||||
.hbmenuLogoImage = themeDefault->hbmenuLogoImage
|
||||
};
|
||||
strncpy(themeCurrent.buttonAText, AText, sizeof(themeCurrent.buttonAText)-1);
|
||||
strncpy(themeCurrent.buttonBText, BText, sizeof(themeCurrent.buttonBText)-1);
|
||||
strncpy(themeCurrent.buttonYText, YText, sizeof(themeCurrent.buttonYText)-1);
|
||||
strncpy(themeCurrent.buttonPText, PText, sizeof(themeCurrent.buttonPText)-1);
|
||||
strncpy(themeCurrent.buttonMText, MText, sizeof(themeCurrent.buttonMText)-1);
|
||||
strncpy(themeCurrent.buttonAText, AText, sizeof(themeCurrent.buttonAText));
|
||||
themeCurrent.buttonAText[sizeof(themeCurrent.buttonAText)-1] = 0;
|
||||
strncpy(themeCurrent.buttonBText, BText, sizeof(themeCurrent.buttonBText));
|
||||
themeCurrent.buttonBText[sizeof(themeCurrent.buttonBText)-1] = 0;
|
||||
strncpy(themeCurrent.buttonXText, XText, sizeof(themeCurrent.buttonXText));
|
||||
themeCurrent.buttonXText[sizeof(themeCurrent.buttonXText)-1] = 0;
|
||||
strncpy(themeCurrent.buttonYText, YText, sizeof(themeCurrent.buttonYText));
|
||||
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 {
|
||||
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 {
|
||||
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);
|
||||
|
||||
if (is_archive) PHYSFS_unmount(theme_archive_path);
|
||||
#ifdef __SWITCH__
|
||||
if (is_romfs) romfsUnmount("theme");
|
||||
#endif
|
||||
}
|
||||
|
||||
void GetThemePathFromConfig(char* themePath, size_t size) {
|
||||
@ -154,7 +662,7 @@ void GetThemePathFromConfig(char* themePath, size_t size) {
|
||||
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/settings.cfg", menuGetRootBasePath());
|
||||
snprintf(tmp_path_theme, sizeof(tmp_path_theme)-1, "%s/config/nx-hbmenu/themes/", menuGetRootBasePath());
|
||||
bool good_cfg = config_read_file(&cfg, tmp_path);
|
||||
|
||||
|
||||
if(good_cfg) {
|
||||
settings = config_lookup(&cfg, "settings");
|
||||
if(settings != NULL) {
|
||||
@ -172,7 +680,7 @@ void SetThemePathToConfig(const char* themePath) {
|
||||
|
||||
char settingPath[PATH_MAX] = {0};
|
||||
config_setting_t *root = NULL,
|
||||
*group = NULL,
|
||||
*group = NULL,
|
||||
*settings = NULL;
|
||||
|
||||
themePath = getSlash(themePath);
|
||||
@ -184,7 +692,7 @@ void SetThemePathToConfig(const char* themePath) {
|
||||
|
||||
snprintf(settingPath, sizeof(settingPath)-1, "%s/config/nx-hbmenu/settings.cfg", menuGetRootBasePath());
|
||||
bool good_cfg = config_read_file(&cfg, settingPath);
|
||||
|
||||
|
||||
if(good_cfg) {
|
||||
group = config_lookup(&cfg, "settings");
|
||||
if(group != NULL)
|
||||
@ -195,9 +703,9 @@ void SetThemePathToConfig(const char* themePath) {
|
||||
root = config_root_setting(&cfg);
|
||||
if(root != NULL)
|
||||
group = config_setting_add(root, "settings", CONFIG_TYPE_GROUP);
|
||||
if(group != NULL)
|
||||
if(group != NULL)
|
||||
settings = config_setting_add(group, "themePath", CONFIG_TYPE_STRING);
|
||||
if(settings != NULL)
|
||||
if(settings != NULL)
|
||||
config_setting_set_string(settings, themePath);
|
||||
}
|
||||
|
||||
|
@ -3,33 +3,55 @@
|
||||
#include "common.h"
|
||||
#include <libconfig.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
color_t textColor;
|
||||
color_t frontWaveColor;
|
||||
color_t middleWaveColor;
|
||||
color_t backWaveColor;
|
||||
color_t backgroundColor;
|
||||
color_t highlightColor;
|
||||
color_t separatorColor;
|
||||
color_t borderColor;
|
||||
color_t borderTextColor;
|
||||
color_t progressBarColor;
|
||||
bool enableWaveBlending;
|
||||
char buttonAText[32];
|
||||
char buttonBText[32];
|
||||
char buttonYText[32];
|
||||
char buttonPText[32];
|
||||
char buttonMText[32];
|
||||
const uint8_t *hbmenuLogoImage;
|
||||
} theme_t;
|
||||
|
||||
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
|
||||
{
|
||||
color_t textColor;
|
||||
color_t logoColor;
|
||||
color_t attentionTextColor;
|
||||
color_t frontWaveColor;
|
||||
color_t middleWaveColor;
|
||||
color_t backWaveColor;
|
||||
color_t backgroundColor;
|
||||
color_t highlightColor;
|
||||
color_t highlightGradientEdgeColor;
|
||||
color_t separatorColor;
|
||||
color_t borderColor;
|
||||
color_t borderTextColor;
|
||||
color_t progressBarColor;
|
||||
bool logoColor_set;
|
||||
bool enableWaveBlending;
|
||||
char buttonAText[32];
|
||||
char buttonBText[32];
|
||||
char buttonXText[32];
|
||||
char buttonYText[32];
|
||||
char buttonPText[32];
|
||||
char buttonMText[32];
|
||||
char labelStarOnText[32];
|
||||
char labelStarOffText[32];
|
||||
|
||||
ThemeLayoutObject layoutObjects[ThemeLayoutId_Total];
|
||||
} theme_t;
|
||||
|
||||
bool colorFromSetting(config_setting_t *rgba, color_t *col);
|
||||
void themeStartup(ThemePreset preset);
|
||||
void GetThemePathFromConfig(char* themePath, size_t size);
|
||||
|
7
common/thermalstatus.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
bool thermalstatusInit(void);
|
||||
void thermalstatusExit(void);
|
||||
bool thermalstatusGetDetails(s32 *temperature);
|
||||
|
@ -68,15 +68,26 @@ static void launchFile(const char* path, argData_s* args)
|
||||
|
||||
init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf));
|
||||
|
||||
Result rc = envSetNextLoad(path, argBuf);
|
||||
if(R_FAILED(rc)) {
|
||||
struct stat st;
|
||||
|
||||
if (stat(path, &st) == -1) {
|
||||
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);
|
||||
menuScan(".");
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
127
nx_main/main.c
@ -1,17 +1,25 @@
|
||||
#include <switch.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <physfs.h>
|
||||
|
||||
#include "../common/common.h"
|
||||
#include "nx_graphics.h"
|
||||
#include "nx_touch.h"
|
||||
|
||||
// Define the desired framebuffer resolution (here we set it to 720p).
|
||||
#define FB_WIDTH 1280
|
||||
#define FB_HEIGHT 720
|
||||
|
||||
uint8_t* g_framebuf;
|
||||
u32 g_framebuf_width;
|
||||
|
||||
PadState g_pad;
|
||||
PadRepeater g_pad_repeater;
|
||||
|
||||
bool menuUpdateErrorScreen(void);
|
||||
|
||||
#ifdef PERF_LOG
|
||||
u64 g_tickdiff_vsync=0;
|
||||
u64 g_tickdiff_frame=0;
|
||||
#endif
|
||||
|
||||
@ -34,35 +42,44 @@ int main(int argc, char **argv)
|
||||
u64 start_tick=0;
|
||||
#endif
|
||||
|
||||
padConfigureInput(8, HidNpadStyleSet_NpadStandard);
|
||||
padInitializeAny(&g_pad);
|
||||
padRepeaterInitialize(&g_pad_repeater, 20, 10);
|
||||
hidSetNpadHandheldActivationMode(HidNpadHandheldActivationMode_Single);
|
||||
touchInit();
|
||||
|
||||
memset(errormsg, 0, sizeof(errormsg));
|
||||
|
||||
appletLockExit();
|
||||
appletSetScreenShotPermission(1);
|
||||
appletSetScreenShotPermission(AppletScreenShotPermission_Enable);
|
||||
|
||||
ColorSetId theme;
|
||||
ColorSetId theme = ColorSetId_Light;
|
||||
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)) {
|
||||
rc = plInitialize();
|
||||
if (R_FAILED(rc)) snprintf(errormsg, sizeof(errormsg)-1, "Error: plInitialize() failed: 0x%x.", rc);
|
||||
setsysGetColorSetId(&theme);
|
||||
setsysExit();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = textInit();
|
||||
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)) {
|
||||
if (!PHYSFS_init(argv[0])) {
|
||||
rc = 1;
|
||||
snprintf(errormsg, sizeof(errormsg)-1, "Error: PHYSFS_init() failed: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
}
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = assetsInit();
|
||||
if (R_FAILED(rc)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +90,7 @@ int main(int argc, char **argv)
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = netloaderInit();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,6 +99,11 @@ int main(int argc, char **argv)
|
||||
snprintf(errormsg, sizeof(errormsg)-1, "Error: workerInit() failed.");
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc) && !statusInit()) {
|
||||
rc = 1;
|
||||
snprintf(errormsg, sizeof(errormsg)-1, "Error: statusInit() failed.");
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) menuStartup();
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
@ -105,7 +127,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (R_FAILED(lastret)) {
|
||||
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);
|
||||
}
|
||||
@ -114,7 +136,7 @@ int main(int argc, char **argv)
|
||||
if (errormsg[0]) error_screen = 1;
|
||||
|
||||
if (!error_screen) {
|
||||
gfxInitDefault();
|
||||
graphicsInit(FB_WIDTH, FB_HEIGHT);
|
||||
}
|
||||
else {
|
||||
consoleInit(NULL);
|
||||
@ -122,29 +144,21 @@ int main(int argc, char **argv)
|
||||
printf("Press the + button to exit.\n");
|
||||
}
|
||||
|
||||
#ifdef PERF_LOG
|
||||
if (!error_screen) {
|
||||
gfxWaitForVsync();
|
||||
|
||||
start_tick = svcGetSystemTick();
|
||||
gfxWaitForVsync();
|
||||
g_tickdiff_vsync = svcGetSystemTick() - start_tick;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (appletMainLoop())
|
||||
{
|
||||
#ifdef PERF_LOG
|
||||
if (!error_screen) start_tick = svcGetSystemTick();
|
||||
#endif
|
||||
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&g_pad);
|
||||
padRepeaterUpdate(&g_pad_repeater, padGetButtons(&g_pad) & (
|
||||
HidNpadButton_AnyLeft | HidNpadButton_AnyUp | HidNpadButton_AnyRight | HidNpadButton_AnyDown
|
||||
));
|
||||
|
||||
if (!error_screen) {
|
||||
g_framebuf = gfxGetFramebuffer(&g_framebuf_width, NULL);
|
||||
memset(g_framebuf, 237, gfxGetFramebufferSize());
|
||||
if (!uiUpdate()) break;
|
||||
g_framebuf = graphicsFrameBegin(&g_framebuf_width);
|
||||
#ifdef PERF_LOG
|
||||
start_tick = armGetSystemTick();
|
||||
#endif
|
||||
memset(g_framebuf, 237, g_framebuf_width * FB_HEIGHT);
|
||||
menuLoop();
|
||||
}
|
||||
else {
|
||||
@ -152,13 +166,11 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
if (!error_screen) {
|
||||
gfxFlushBuffers();
|
||||
graphicsFrameEnd();
|
||||
|
||||
#ifdef PERF_LOG
|
||||
g_tickdiff_frame = svcGetSystemTick() - start_tick;
|
||||
g_tickdiff_frame = armGetSystemTick() - start_tick;
|
||||
#endif
|
||||
|
||||
gfxSwapBuffers();
|
||||
}
|
||||
else {
|
||||
consoleUpdate(NULL);
|
||||
@ -166,7 +178,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
if (!error_screen) {
|
||||
gfxExit();
|
||||
graphicsExit();
|
||||
}
|
||||
else {
|
||||
consoleExit(NULL);
|
||||
@ -180,41 +192,54 @@ int main(int argc, char **argv)
|
||||
fontExit();
|
||||
launchExit();
|
||||
netloaderSignalExit();
|
||||
statusExit();
|
||||
workerExit();
|
||||
netloaderExit();
|
||||
powerExit();
|
||||
assetsExit();
|
||||
plExit();
|
||||
setsysExit();
|
||||
PHYSFS_deinit();
|
||||
|
||||
appletUnlockExit();
|
||||
|
||||
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.
|
||||
bool menuUpdate(void) {
|
||||
bool exitflag = 0;
|
||||
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);
|
||||
|
||||
if (down & KEY_Y)
|
||||
if (down & HidNpadButton_Y)
|
||||
{
|
||||
launchMenuNetloaderTask();
|
||||
}
|
||||
else if (down & KEY_A)
|
||||
else if (down & HidNpadButton_X)
|
||||
{
|
||||
menuHandleXButton();
|
||||
}
|
||||
else if (down & HidNpadButton_A)
|
||||
{
|
||||
menuHandleAButton();
|
||||
}
|
||||
else if (down & KEY_B)
|
||||
else if (down & HidNpadButton_B)
|
||||
{
|
||||
launchMenuBackTask();
|
||||
}
|
||||
else if(down & KEY_MINUS){
|
||||
else if(down & HidNpadButton_Minus){
|
||||
themeMenuStartup();
|
||||
}
|
||||
else if (down & KEY_PLUS)
|
||||
else if (down & HidNpadButton_Plus)
|
||||
{
|
||||
exitflag = 1;
|
||||
}
|
||||
@ -222,10 +247,10 @@ bool menuUpdate(void) {
|
||||
{
|
||||
int move = 0;
|
||||
|
||||
if (down & KEY_LEFT) move--;
|
||||
if (down & KEY_RIGHT) move++;
|
||||
if (down & KEY_DOWN) move-=7;
|
||||
if (down & KEY_UP) move+=7;
|
||||
if (down & HidNpadButton_AnyLeft) move--;
|
||||
if (down & HidNpadButton_AnyRight) move++;
|
||||
if (down & HidNpadButton_AnyDown) move-=entries_count;
|
||||
if (down & HidNpadButton_AnyUp) move+=entries_count;
|
||||
|
||||
int newEntry = menu->curEntry + move;
|
||||
if (newEntry < 0) newEntry = 0;
|
||||
@ -238,9 +263,9 @@ bool menuUpdate(void) {
|
||||
|
||||
bool menuUpdateErrorScreen(void) {
|
||||
bool exitflag = 0;
|
||||
u32 down = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
u64 down = menuGetKeysDown();
|
||||
|
||||
if (down & KEY_PLUS)
|
||||
if (down & HidNpadButton_Plus)
|
||||
{
|
||||
exitflag = 1;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ static void audio_playback_thread(void* arg)
|
||||
playing = 1;
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(audoutWaitPlayFinish(&released_buffer, &released_count, U64_MAX)))
|
||||
if (R_SUCCEEDED(audoutWaitPlayFinish(&released_buffer, &released_count, UINT64_MAX)))
|
||||
playing = 0;
|
||||
}
|
||||
}
|
||||
|
141
nx_main/nx_graphics.c
Normal 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, ©Rect, 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
@ -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);
|
36
nx_main/nx_netstatus.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "../common/common.h"
|
||||
|
||||
bool netstatusGetDetails(AssetId *id) {
|
||||
Result rc=0;
|
||||
NifmInternetConnectionType contype;
|
||||
u32 wifiStrength=0;
|
||||
NifmInternetConnectionStatus connectionStatus;
|
||||
|
||||
rc = nifmGetInternetConnectionStatus(&contype, &wifiStrength, &connectionStatus);
|
||||
if (R_FAILED(rc)) {
|
||||
*id = AssetId_airplane_icon;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (contype == NifmInternetConnectionType_Ethernet) {
|
||||
if (connectionStatus != NifmInternetConnectionStatus_Connected)
|
||||
*id = AssetId_eth_none_icon;
|
||||
else
|
||||
*id = AssetId_eth_icon;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wifiStrength==0) {
|
||||
*id = AssetId_wifi_none_icon;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wifiStrength==3)
|
||||
*id = AssetId_wifi3_icon;
|
||||
if (wifiStrength==2)
|
||||
*id = AssetId_wifi2_icon;
|
||||
else
|
||||
*id = AssetId_wifi1_icon;
|
||||
|
||||
return true;
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
#include <switch.h>
|
||||
#include "../common/common.h"
|
||||
|
||||
static bool psmInitialized;
|
||||
static bool psmCacheInitialized;
|
||||
static uint32_t psmCacheCharge;
|
||||
static bool psmCacheIsCharging;
|
||||
static bool powerInitialized;
|
||||
static bool powerCacheInitialized;
|
||||
static uint32_t powerCacheCharge;
|
||||
static bool powerCacheIsCharging;
|
||||
static PsmSession powerSession;
|
||||
|
||||
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
|
||||
ChargerType charger = ChargerType_None;
|
||||
PsmChargerType charger = PsmChargerType_Unconnected;
|
||||
bool hwReadsSucceeded = false;
|
||||
bool use_cache = false;
|
||||
Result rc = 0;
|
||||
@ -15,9 +16,9 @@ bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
|
||||
*isCharging = false;
|
||||
*batteryCharge = 0;
|
||||
|
||||
if (psmInitialized) {
|
||||
if (psmCacheInitialized) {
|
||||
rc = psmWaitStateChangeEvent(0);
|
||||
if (powerInitialized) {
|
||||
if (powerCacheInitialized) {
|
||||
rc = psmWaitStateChangeEvent(&powerSession, 0);
|
||||
|
||||
if (R_FAILED(rc)) use_cache = true;
|
||||
}
|
||||
@ -25,17 +26,17 @@ bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
|
||||
rc = psmGetBatteryChargePercentage(batteryCharge);
|
||||
hwReadsSucceeded = R_SUCCEEDED(rc);
|
||||
if (use_cache) {
|
||||
*isCharging = psmCacheIsCharging;
|
||||
*isCharging = powerCacheIsCharging;
|
||||
}
|
||||
else {
|
||||
rc = psmGetChargerType(&charger);
|
||||
hwReadsSucceeded &= R_SUCCEEDED(rc);
|
||||
*isCharging = (charger > ChargerType_None);
|
||||
*isCharging = (charger != PsmChargerType_Unconnected);
|
||||
}
|
||||
|
||||
psmCacheCharge = *batteryCharge;
|
||||
psmCacheIsCharging = *isCharging;
|
||||
psmCacheInitialized = true;
|
||||
powerCacheCharge = *batteryCharge;
|
||||
powerCacheIsCharging = *isCharging;
|
||||
powerCacheInitialized = true;
|
||||
}
|
||||
|
||||
return hwReadsSucceeded;
|
||||
@ -45,18 +46,18 @@ void powerInit(void) {
|
||||
uint32_t charge=0;
|
||||
bool isCharging=0;
|
||||
|
||||
psmCacheInitialized = false;
|
||||
psmCacheCharge = 0;
|
||||
psmCacheIsCharging = false;
|
||||
powerCacheInitialized = false;
|
||||
powerCacheCharge = 0;
|
||||
powerCacheIsCharging = false;
|
||||
|
||||
if (!psmInitialized) {
|
||||
if (!powerInitialized) {
|
||||
Result rc = psmInitialize();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = psmBindStateChangeEvent(1, 1, 1);
|
||||
rc = psmBindStateChangeEvent(&powerSession, 1, 1, 1);
|
||||
|
||||
if (R_FAILED(rc)) psmExit();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
psmInitialized = true;
|
||||
powerInitialized = true;
|
||||
powerGetDetails(&charge, &isCharging);//Init the cache.
|
||||
}
|
||||
}
|
||||
@ -64,9 +65,10 @@ void powerInit(void) {
|
||||
}
|
||||
|
||||
void powerExit(void) {
|
||||
if (psmInitialized) {
|
||||
if (powerInitialized) {
|
||||
psmUnbindStateChangeEvent(&powerSession);
|
||||
psmExit();
|
||||
psmInitialized = false;
|
||||
psmCacheInitialized = false;
|
||||
powerInitialized = false;
|
||||
powerCacheInitialized = false;
|
||||
}
|
||||
}
|
||||
|
14
nx_main/nx_thermalstatus.c
Normal 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));
|
||||
}
|
||||
|
@ -5,38 +5,33 @@
|
||||
#define VERTICAL_SWIPE_MINIMUM_DISTANCE 300
|
||||
#define HORIZONTAL_SWIPE_VERTICAL_PLAY 250
|
||||
#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 distance(x1, y1, x2, y2) (int) sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
|
||||
|
||||
struct touchInfo_s touchInfo;
|
||||
|
||||
void touchInit() {
|
||||
void touchInit(void) {
|
||||
touchInfo.gestureInProgress = false;
|
||||
touchInfo.isTap = true;
|
||||
touchInfo.initMenuXPos = 0;
|
||||
touchInfo.initMenuIndex = 0;
|
||||
touchInfo.lastSlideSpeed = 0;
|
||||
hidInitializeTouchScreen();
|
||||
}
|
||||
|
||||
void handleTappingOnApp(menu_s* menu, int px) {
|
||||
int i = 0;
|
||||
menuEntry_s *me = NULL;
|
||||
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
|
||||
|
||||
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;
|
||||
if (entry_start_x >= (screen_width - menu->xPos))
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@ -53,77 +48,96 @@ 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) {
|
||||
touchPosition currentTouch;
|
||||
u32 touches = hidTouchCount();
|
||||
ThemeLayoutObject *layoutobj = NULL;
|
||||
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.
|
||||
if (touches == 1 && !touchInfo.gestureInProgress) {
|
||||
hidTouchRead(¤tTouch, 0);
|
||||
|
||||
if (touch.count == 1 && !touchInfo.gestureInProgress) {
|
||||
touchInfo.gestureInProgress = true;
|
||||
touchInfo.firstTouch = currentTouch;
|
||||
touchInfo.prevTouch = currentTouch;
|
||||
touchInfo.firstTouch = touch.touches[0];
|
||||
touchInfo.prevTouch = touch.touches[0];
|
||||
touchInfo.isTap = true;
|
||||
touchInfo.initMenuXPos = menu->xPos;
|
||||
touchInfo.initMenuIndex = menu->curEntry;
|
||||
touchInfo.lastSlideSpeed = 0;
|
||||
menu->slideSpeed = 0;
|
||||
}
|
||||
// On touch moving.
|
||||
else if (touches >= 1 && touchInfo.gestureInProgress) {
|
||||
hidTouchRead(¤tTouch, 0);
|
||||
else if (touch.count >= 1 && touchInfo.gestureInProgress) {
|
||||
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;
|
||||
}
|
||||
if (!menuIsMsgBoxOpen() && touchInfo.firstTouch.py > LISTING_START_Y && touchInfo.firstTouch.py < LISTING_END_Y && !touchInfo.isTap && menu->nEntries > 7) {
|
||||
menu->xPos = touchInfo.initMenuXPos + (currentTouch.px - touchInfo.firstTouch.px);
|
||||
menu->curEntry = touchInfo.initMenuIndex + ((int) (touchInfo.firstTouch.px - currentTouch.px) / 170);
|
||||
if (!menuIsMsgBoxOpen() && touchInfo.firstTouch.y > layoutobj->posStart[1] && touchInfo.firstTouch.y < layoutobj->posStart[1]+layoutobj->size[1] && !touchInfo.isTap && menu->nEntries > entries_count) {
|
||||
|
||||
if (menu->curEntry < 0)
|
||||
menu->curEntry = 0;
|
||||
|
||||
if (menu->curEntry >= menu->nEntries - 6)
|
||||
menu->curEntry = menu->nEntries - 7;
|
||||
if (!touchInfo.isTap) {
|
||||
menu->slideSpeed = touchInfo.lastSlideSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
// On touch end.
|
||||
else if (touchInfo.gestureInProgress) {
|
||||
int x1 = touchInfo.firstTouch.px;
|
||||
int y1 = touchInfo.firstTouch.py;
|
||||
int x2 = touchInfo.prevTouch.px;
|
||||
int y2 = touchInfo.prevTouch.py;
|
||||
int x1 = touchInfo.firstTouch.x;
|
||||
int y1 = touchInfo.firstTouch.y;
|
||||
int x2 = touchInfo.prevTouch.x;
|
||||
int y2 = touchInfo.prevTouch.y;
|
||||
|
||||
if (!touchInfo.isTap) {
|
||||
menu->slideSpeed = touchInfo.lastSlideSpeed;
|
||||
}
|
||||
|
||||
bool netloader_active = menuIsNetloaderActive();
|
||||
|
||||
if (menuIsMsgBoxOpen() && !netloader_active) {
|
||||
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
|
||||
MessageBox currMsgBox = menuGetCurrentMsgBox();
|
||||
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_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) {
|
||||
menuCloseMsgBox();
|
||||
}
|
||||
} else if (touchInfo.isTap && !netloader_active) {
|
||||
// App Icons
|
||||
if (y1 > LISTING_START_Y && y1 < LISTING_END_Y) {
|
||||
handleTappingOnApp(menu, touchInfo.prevTouch.px);
|
||||
if (y1 > layoutobj->posStart[1] && y1 < layoutobj->posStart[1]+layoutobj->size[1]) {
|
||||
handleTappingOnApp(menu, touchInfo.prevTouch.x);
|
||||
}
|
||||
// Bottom Buttons
|
||||
else if (y1 > BUTTON_START_Y && y1 < BUTTON_END_Y) {
|
||||
// Back Button for non-empty directory
|
||||
if (menu->nEntries != 0 && x1 > BACK_BUTTON_START_X && x1 < BACK_BUTTON_END_X) {
|
||||
else {
|
||||
// Back Button
|
||||
if (checkInsideTextLayoutObject(ThemeLayoutId_ButtonB, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonBText, x1, y1)) {
|
||||
launchMenuBackTask();
|
||||
}
|
||||
// Open/Launch Button / Back Button for empty directories
|
||||
else if (x1 > LAUNCH_BUTTON_START_X && x1 < LAUNCH_BUTTON_END_X) {
|
||||
if (menu->nEntries == 0) {
|
||||
launchMenuBackTask();
|
||||
} else {
|
||||
handleTappingOnOpenLaunch(menu);
|
||||
// Open/Launch Button
|
||||
else if (menu->nEntries != 0 && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonA, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonAText, x1, y1))) {
|
||||
handleTappingOnOpenLaunch(menu);
|
||||
}
|
||||
// Star Button
|
||||
else if (menu->nEntries != 0) {
|
||||
int i;
|
||||
menuEntry_s* me;
|
||||
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))) {
|
||||
menuHandleXButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,10 +154,12 @@ void handleTouch(menu_s* menu) {
|
||||
}
|
||||
}
|
||||
// Horizontal Swipe
|
||||
else if (abs(y1 - y2) < HORIZONTAL_SWIPE_VERTICAL_PLAY && distance(x1, y1, x2, y2) > HORIZONTAL_SWIPE_MINIMUM_DISTANCE) {
|
||||
// Swipe left to go into theme-menu
|
||||
if (x1 - x2 > 0) {
|
||||
themeMenuStartup();
|
||||
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) {
|
||||
// Swipe left to go into theme-menu
|
||||
if (x1 - x2 > 0) {
|
||||
themeMenuStartup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,13 @@
|
||||
|
||||
struct touchInfo_s {
|
||||
bool gestureInProgress;
|
||||
touchPosition firstTouch;
|
||||
touchPosition prevTouch;
|
||||
HidTouchState firstTouch;
|
||||
HidTouchState prevTouch;
|
||||
bool isTap;
|
||||
int initMenuXPos;
|
||||
int initMenuIndex;
|
||||
int lastSlideSpeed;
|
||||
};
|
||||
|
||||
void touchInit(void);
|
||||
void handleTouch(menu_s* menu);
|
@ -1,6 +1,7 @@
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <physfs.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
@ -10,18 +11,20 @@ extern "C" {
|
||||
|
||||
color_t pixels[720][1280];
|
||||
|
||||
int main()
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
sf::RenderWindow window(sf::VideoMode(1280, 720), "Test");
|
||||
window.setFramerateLimit(60);
|
||||
|
||||
menuStartupPath();
|
||||
PHYSFS_init(argv[0]);
|
||||
assetsInit();
|
||||
themeStartup(THEME_PRESET_LIGHT);
|
||||
textInit();
|
||||
fontInitialize();
|
||||
netloaderInit();
|
||||
workerInit();
|
||||
statusInit();
|
||||
menuStartup();
|
||||
|
||||
while (window.isOpen())
|
||||
@ -56,10 +59,12 @@ int main()
|
||||
}
|
||||
|
||||
netloaderSignalExit();
|
||||
statusExit();
|
||||
workerExit();
|
||||
netloaderExit();
|
||||
fontExit();
|
||||
assetsExit();
|
||||
PHYSFS_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -72,6 +77,8 @@ extern "C" bool menuUpdate(void) {
|
||||
int new_esc_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Escape);
|
||||
static int return_state = 0;
|
||||
int new_return_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Return);
|
||||
static int x_state = 0;
|
||||
int new_x_state = sf::Keyboard::isKeyPressed(sf::Keyboard::X);
|
||||
static int y_state = 0;
|
||||
int new_y_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Y);
|
||||
static int t_state = 0;
|
||||
@ -80,7 +87,11 @@ extern "C" bool menuUpdate(void) {
|
||||
if(!new_y_state && y_state)
|
||||
{
|
||||
launchMenuNetloaderTask();
|
||||
}
|
||||
|
||||
if(!new_x_state && x_state)
|
||||
{
|
||||
menuHandleXButton();
|
||||
}
|
||||
|
||||
if (!new_esc_state && esc_state)
|
||||
|
6
pc_main/pc_netstatus.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include "../common/common.h"
|
||||
|
||||
bool netstatusGetDetails(AssetId *id) {
|
||||
*id = AssetId_wifi3_icon;
|
||||
return true;
|
||||
}
|
15
pc_main/pc_thermalstatus.c
Normal 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/airplane_icon.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 188 B After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 3.3 KiB |
BIN
resources/eth_icon.png
Normal file
After Width: | Height: | Size: 405 B |
BIN
resources/eth_none_icon.png
Normal file
After Width: | Height: | Size: 481 B |
BIN
resources/wifi1_icon.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
resources/wifi2_icon.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
resources/wifi3_icon.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
resources/wifi_none_icon.png
Normal file
After Width: | Height: | Size: 4.5 KiB |