Compare commits

..

No commits in common. "master" and "v2.0.0" have entirely different histories.

95 changed files with 1751 additions and 5650 deletions

3
.gitignore vendored
View File

@ -1,4 +1,3 @@
.*/
*~ *~
*.exe *.exe
*.o *.o
@ -12,5 +11,3 @@ build
*.pfs0 *.pfs0
*.nacp *.nacp
*.nro *.nro
test.*
switch

View File

@ -1,30 +1,18 @@
export APP_VERSION := 3.5.1 export APP_VERSION := v2.0.0
ifeq ($(RELEASE),)
export APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always)
endif
.PHONY: clean all nx pc dist-bin .PHONY: clean all nx pc dist-bin
all: nx pc all: nx pc
romfs: dist-bin:
@mkdir -p romfs
romfs/assets.zip : romfs assets
@rm -f romfs/assets.zip
@zip -rj romfs/assets.zip assets
dist-bin: romfs/assets.zip
$(MAKE) -f Makefile.nx dist-bin $(MAKE) -f Makefile.nx dist-bin
nx: romfs/assets.zip nx:
$(MAKE) -f Makefile.nx $(MAKE) -f Makefile.nx
pc: romfs/assets.zip pc:
$(MAKE) -f Makefile.pc $(MAKE) -f Makefile.pc
clean: clean:
@rm -Rf romfs
$(MAKE) -f Makefile.pc clean $(MAKE) -f Makefile.pc clean
$(MAKE) -f Makefile.nx clean $(MAKE) -f Makefile.nx clean

View File

@ -15,7 +15,7 @@ include $(DEVKITPRO)/libnx/switch_rules
# SOURCES is a list of directories containing source code # SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files # DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files # INCLUDES is a list of directories containing header files
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) # EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
# #
# NO_ICON: if set to anything, do not use icon. # NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated. # NO_NACP: if set to anything, no .nacp file is generated.
@ -28,25 +28,15 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg # - <Project name>.jpg
# - icon.jpg # - icon.jpg
# - <libnx folder>/default_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)) TARGET := $(notdir $(CURDIR))
BUILD := build BUILD := build
SOURCES := common/ nx_main/ nx_main/loaders/ SOURCES := common/ nx_main/ nx_main/loaders/
DATA := data DATA := data
INCLUDES := include INCLUDES := include
ROMFS := romfs EXEFS_SRC := exefs_src
DIST_PATH := $(TARGET)_v$(APP_VERSION) DIST_PATH := $(TARGET)_$(APP_VERSION)
APP_AUTHOR := switchbrew
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# options for code generation # options for code generation
@ -54,16 +44,16 @@ APP_AUTHOR := switchbrew
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \ CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES) `freetype-config --cflags` $(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__ -DVERSION=\"v$(APP_VERSION)\" CFLAGS += $(INCLUDE) -DSWITCH -DVERSION=\"$(APP_VERSION)\"
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS := -g $(ARCH) ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -ldeko3d -lphysfs `freetype-config --libs` -lconfig -lturbojpeg -lpng LIBS := -lnx -lm
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing # list of directories containing libraries, this must be the top level containing
@ -106,10 +96,8 @@ else
endif endif
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES)) export OFILES := $(addsuffix .o,$(BINFILES)) \
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
@ -117,18 +105,7 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(CONFIG_JSON)),) export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
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)),) ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg) icons := $(wildcard *.jpg)
@ -155,10 +132,6 @@ ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID) export NACPFLAGS += --titleid=$(APP_TITLEID)
endif endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all dist-bin .PHONY: $(BUILD) clean all dist-bin
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@ -171,11 +144,7 @@ $(BUILD):
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
clean: clean:
@echo clean ... @echo clean ...
ifeq ($(strip $(APP_JSON)),) @rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
@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 dist-bin: all
@ -192,9 +161,11 @@ DEPENDS := $(OFILES:.o=.d)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# main targets # main targets
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),) all : $(OUTPUT).pfs0 $(OUTPUT).nro
all : $(OUTPUT).nro $(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
ifeq ($(strip $(NO_NACP)),) ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -202,25 +173,17 @@ else
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nro : $(OUTPUT).elf
endif endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
menu.o : $(TOPDIR)/Makefile
$(OUTPUT).elf : $(OFILES) $(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data # you need a rule like this for each extension you use as binary data
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin %.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
%.nxfnt.o : %.nxfnt
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@echo $(notdir $<) @echo $(notdir $<)
@$(bin2o) @$(bin2o)

View File

@ -1,20 +1,93 @@
HOST_OS := $(shell uname -s) # canned command sequence for binary data
#---------------------------------------------------------------------------------
define bin2o
bin2s $< | $(AS) -o $(@)
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h
endef
ifeq ($(strip $(HOST_OS)),Darwin) test : pc_main/main.cpp pc_main/pc_launch.c \
BIN2S_FLAGS := --apple-llvm common/menu.c common/font.c common/language.c common/launch.c \
endif common/menu-entry.c common/menu-list.c common/text.c \
common/nanojpeg.c common/ui.c common/math.c common/theme.c \
build_pc/tahoma24.o build_pc/tahoma12.o build_pc/interuimedium20.o build_pc/interuimedium30.o \
build_pc/interuiregular14.o build_pc/interuiregular18.o \
build_pc/invalid_icon.bin.o build_pc/folder_icon.bin.o \
build_pc/button_a_light.bin.o build_pc/button_a_dark.bin.o build_pc/button_b_light.bin.o build_pc/button_b_dark.bin.o build_pc/hbmenu_logo_light.bin.o build_pc/hbmenu_logo_dark.bin.o
gcc -Wall -O2 -g0 -DVERSION=\"$(APP_VERSION)\" $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lm -I. -Ibuild_pc -o $@
build_pc/tahoma12.o : data/tahoma12.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/tahoma24.o : data/tahoma24.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/interuimedium20.o : data/interuimedium20.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/interuimedium30.o : data/interuimedium30.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/interuiregular14.o : data/interuiregular14.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/interuiregular18.o : data/interuiregular18.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/invalid_icon.bin.o : data/invalid_icon.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/folder_icon.bin.o : data/folder_icon.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/button_a_light.bin.o : data/button_a_light.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/button_a_dark.bin.o : data/button_a_dark.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/button_b_light.bin.o : data/button_b_light.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/button_b_dark.bin.o : data/button_b_dark.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/hbmenu_logo_light.bin.o : data/hbmenu_logo_light.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/hbmenu_logo_dark.bin.o : data/hbmenu_logo_dark.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
ifneq (,$(findstring MINGW,$(HOST_OS)))
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 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 -lphysfs -lz -lconfig -lturbojpeg -lpng $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@
clean: clean:
rm -rf build_pc/ test test.* rm -rf build_pc/ test

View File

@ -1,31 +1,13 @@
### Usage #### Usage
See [Homebrew_Applications](https://switchbrew.org/wiki/Homebrew_Applications) for SD layout and applications, etc. See [Switchbrew](https://switchbrew.org/wiki/Homebrew_Menu) for hbmenu docs. See [Switchbrew](http://switchbrew.org/index.php?title=Homebrew_Applications) for SD layout, applications, etc.
### Download #### Download
The latest release is available from the [releases](https://github.com/switchbrew/nx-hbmenu/releases/latest) page. The latest release is available from the [releases](https://github.com/switchbrew/nx-hbmenu/releases/latest) page.
### Building #### Building
Build for the Nintendo Switch with ```make nx``` and for the PC with ```make pc```. Build with ```make nx``` or just run ```make```.
Running ```make``` builds for both systems.
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`
The following libraries are required to build for PC:
- `libfreetype`
- `libconfig`
- `libjpeg-turbo`
- `libphysfs`
Building for Switch/PC requires `zip`.
Since C11 threads are used, building for the PC may fail if C11 threads are not available.
#### Credits #### Credits
* This uses code based on 3DS [new-hbmenu](https://github.com/fincs/new-hbmenu). * This uses code based on 3DS [new-hbmenu](https://github.com/fincs/new-hbmenu).
* `libjpeg-turbo` is used for handling JPEG icons. This library doesn't support lossless JPEG (likewise for official sw which uses `libjpeg-turbo`). * [nanojpeg](https://svn.emphy.de/nanojpeg/trunk/nanojpeg/nanojpeg.c) is used for handling JPEG icons. Due to this some JPEGs are not [supported](https://github.com/switchbrew/nx-hbmenu/issues/7).

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,277 +0,0 @@
#include "common.h"
#include <physfs.h>
#include <png.h>
#define GENASSET(_p, _mode, _w, _h) {{.path = _p, .imageMode = _mode, .imageSize = {_w, _h}}, {}}
static bool g_assetsInitialized = 0;
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) {
free(entry->buffer);
entry->size = 0;
entry->buffer = NULL;
memset(entry, 0, sizeof(*entry));
}
static void assetsSetPixelSize(assetsDataEntry *entry) {
switch (entry->imageMode) {
case IMAGE_MODE_RGB24:
entry->pixSize = 3;
break;
case IMAGE_MODE_RGBA32:
entry->pixSize = 4;
break;
}
}
Result assetsInit(void) {
bool ret=false;
int i, stopi;
assetsDataEntry *entry = NULL;
char tmp_path[PATH_MAX];
if (g_assetsInitialized) return 0;
#ifdef __SWITCH__
Result rc = romfsInit();
if (R_FAILED(rc)) return rc;
#endif
memset(tmp_path, 0, sizeof(tmp_path));
#ifdef __SWITCH__
strncpy(tmp_path, "romfs:/assets.zip", sizeof(tmp_path)-1);
#else
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/romfs/assets.zip", menuGetRootBasePath());
#endif
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);
}
#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;
}
bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term) {
bool ret=true;
*data_buf = NULL;
if (filesize) *filesize = 0;
PHYSFS_Stat tmpstat={0};
if (!(PHYSFS_stat(path, &tmpstat) && tmpstat.filesize!=-1)) ret = false;
if (ret) {
size_t bufsize = tmpstat.filesize;
if (nul_term) bufsize++;
*data_buf = (u8*)malloc(bufsize);
if (*data_buf) memset(*data_buf, 0, bufsize);
else ret = false;
}
if (ret) {
PHYSFS_File *f = PHYSFS_openRead(path);
if (f==NULL) ret = false;
else {
ret = PHYSFS_readBytes(f, *data_buf, tmpstat.filesize) == tmpstat.filesize;
PHYSFS_close(f);
}
}
if (ret) {
if (filesize) *filesize = tmpstat.filesize;
}
else {
free(*data_buf);
*data_buf = NULL;
}
return ret;
}
bool 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;
u32 pos = g_assetsDataList[id][1].initialized ? 1 : 0;
assetsDataEntry *entry = &g_assetsDataList[id][pos];
if (entry->initialized) *out = entry;
}
u8 *assetsGetDataBuffer(AssetId id) {
assetsDataEntry *entry = NULL;
assetsGetData(id, &entry);
return entry ? entry->buffer : NULL;
}

View File

@ -1,44 +0,0 @@
#pragma once
#include "common.h"
typedef enum {
AssetId_battery_icon,
AssetId_charging_icon,
AssetId_folder_icon,
AssetId_invalid_icon,
AssetId_hbmenu_logo_dark,
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;
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 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);

View File

@ -10,34 +10,16 @@
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <threads.h>
#ifndef __APPLE__
#include <malloc.h> #include <malloc.h>
#endif
#include <math.h> #include <math.h>
#ifdef __SWITCH__ #ifdef SWITCH
#include <switch.h> #include <switch.h>
#endif #endif
#include <stdint.h> #include <stdint.h>
typedef uint8_t u8; typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32; typedef uint32_t u32;
typedef uint64_t u64; typedef uint64_t u64;
typedef int8_t s8;
typedef int32_t s32;
typedef u32 Result;
typedef void (*workerThreadFunc)(void *);
#ifdef _WIN32
#define DIRECTORY_SEPARATOR_CHAR '\\'
static const char DIRECTORY_SEPARATOR[] = "\\";
#else
#define DIRECTORY_SEPARATOR_CHAR '/'
static const char DIRECTORY_SEPARATOR[] = "/";
#endif
#define M_TAU (2*M_PI) #define M_TAU (2*M_PI)
@ -48,76 +30,23 @@ typedef union {
}; };
} color_t; } color_t;
typedef enum
{
ThemeLayoutId_Logo,
ThemeLayoutId_HbmenuVersion,
ThemeLayoutId_LoaderInfo,
ThemeLayoutId_AttentionText,
ThemeLayoutId_LogInfo,
ThemeLayoutId_InfoMsg,
ThemeLayoutId_MenuPath,
ThemeLayoutId_MenuTypeMsg,
ThemeLayoutId_MsgBoxSeparator,
ThemeLayoutId_MsgBoxBottomText,
ThemeLayoutId_BackgroundImage,
ThemeLayoutId_BackWave,
ThemeLayoutId_MiddleWave,
ThemeLayoutId_FrontWave,
ThemeLayoutId_ButtonA,
ThemeLayoutId_ButtonAText,
ThemeLayoutId_ButtonB,
ThemeLayoutId_ButtonBText,
ThemeLayoutId_ButtonY,
ThemeLayoutId_ButtonYText,
ThemeLayoutId_ButtonM,
ThemeLayoutId_ButtonMText,
ThemeLayoutId_ButtonX,
ThemeLayoutId_ButtonXText,
ThemeLayoutId_NetworkIcon,
ThemeLayoutId_BatteryCharge,
ThemeLayoutId_BatteryIcon,
ThemeLayoutId_ChargingIcon,
ThemeLayoutId_Status,
ThemeLayoutId_Temperature,
ThemeLayoutId_MenuList,
ThemeLayoutId_MenuListTiles,
ThemeLayoutId_MenuListIcon,
ThemeLayoutId_MenuListName,
ThemeLayoutId_MenuActiveEntryIcon,
ThemeLayoutId_MenuActiveEntryName,
ThemeLayoutId_MenuActiveEntryAuthor,
ThemeLayoutId_MenuActiveEntryVersion,
ThemeLayoutId_Total,
} ThemeLayoutId;
// when building for pc we need to include these separately
#ifndef __SWITCH__
#include "switch/nro.h"
#include "switch/nacp.h"
#endif
#include "font.h" #include "font.h"
#include "nacp.h"
#include "menu.h" #include "menu.h"
#include "text.h" #include "text.h"
#include "ui.h" #include "ui.h"
#include "assets.h"
#include "launch.h" #include "launch.h"
#include "worker.h" #include "nanojpeg.h"
#include <turbojpeg.h>
#include "math.h" #include "math.h"
#include "theme.h" #include "theme.h"
#include "message-box.h"
#include "power.h"
#include "netloader.h"
#include "netstatus.h"
#include "thermalstatus.h"
#include "status.h"
void menuStartupPath(void); // when building for pc we need to include nro.h separately
void menuStartup(void); #ifndef SWITCH
void themeMenuStartup(void); #include "nro.h"
void menuLoop(void); #endif
void menuStartup();
void menuLoop();
static inline uint8_t BlendColor(uint32_t src, uint32_t dst, uint8_t alpha) static inline uint8_t BlendColor(uint32_t src, uint32_t dst, uint8_t alpha)
{ {
@ -135,14 +64,14 @@ static inline color_t MakeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
return clr; return clr;
} }
#ifdef __SWITCH__ #ifdef SWITCH
extern uint8_t* g_framebuf; extern uint8_t* g_framebuf;
extern u32 g_framebuf_width; extern u32 g_framebuf_width;
static inline void DrawPixel(uint32_t x, uint32_t y, color_t clr) static inline void DrawPixel(uint32_t x, uint32_t y, color_t clr)
{ {
if (x >= 1280 || y >= 720) if (x >= 1280 || y >= 720)
return; 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.r, clr.a); off++;
g_framebuf[off] = BlendColor(g_framebuf[off], clr.g, 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++; g_framebuf[off] = BlendColor(g_framebuf[off], clr.b, clr.a); off++;
@ -152,7 +81,7 @@ static inline void DrawPixelRaw(uint32_t x, uint32_t y, color_t clr)
{ {
if (x >= 1280 || y >= 720) if (x >= 1280 || y >= 720)
return; 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); *((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) static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr)
@ -162,12 +91,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); u32 color = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24);
u128 val = color | ((u128)color<<32) | ((u128)color<<64) | ((u128)color<<96); 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; *((u128*)&g_framebuf[off]) = val;
} }
static inline color_t FetchPixelColor(uint32_t x, uint32_t y) 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]); u32 val = *((u32*)&g_framebuf[off]);
u8 r = (u8)val; u8 r = (u8)val;
u8 g = (u8)(val>>8); u8 g = (u8)(val>>8);
@ -206,13 +135,5 @@ static inline color_t FetchPixelColor(uint32_t x, uint32_t y)
#endif #endif
void DrawPixel(uint32_t x, uint32_t y, color_t clr); void DrawPixel(uint32_t x, uint32_t y, color_t clr);
void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text); void DrawText(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text);
void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text); void DrawTextTruncate(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_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);
uint32_t GetTextYCoordinate(u32 font, uint32_t rY, const char* text, const char align);
bool fontInitialize(void);
void fontExit();

View File

@ -1,77 +1,6 @@
#include "common.h" #include "common.h"
#include <ft2build.h> static inline const ffnt_page_t* FontGetPage(const ffnt_header_t* font, uint32_t page_id)
#include FT_FREETYPE_H
#ifdef __SWITCH__
#define FONT_FACES_MAX PlSharedFontType_Total
#else
#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 s32 s_font_faces_total = 0;
static bool FontSetType(u32 font)
{
s32 i=0;
u32 scale=0;
FT_Error ret=0;
switch(font)
{
case interuiregular14:
scale = 4;
break;
case interuiregular18:
scale = 5;
break;
case interuimedium20:
scale = 6;
break;
case fontscale7:
scale = 7;
break;
case interuimedium30:
scale = 8;
break;
case largestar:
scale = 18;
break;
default:
return false;
break;
}
for (i=0; i<s_font_faces_total; i++) {
ret = FT_Set_Char_Size(
s_font_faces[i], /* handle to face object */
0, /* char_width in 1/64th of points */
scale*64, /* char_height in 1/64th of points */
300, /* horizontal device resolution */
300); /* vertical device resolution */
if (ret) return false;
}
return true;
}
/*static inline const ffnt_page_t* FontGetPage(const ffnt_header_t* font, uint32_t page_id)
{ {
//__builtin_printf("GetPage %u\n", (unsigned int)page_id); //__builtin_printf("GetPage %u\n", (unsigned int)page_id);
if (page_id >= font->npages) if (page_id >= font->npages)
@ -80,69 +9,27 @@ static bool FontSetType(u32 font)
if (ent->size == 0) if (ent->size == 0)
return NULL; return NULL;
return (const ffnt_page_t*)((const uint8_t*)font + ent->offset); return (const ffnt_page_t*)((const uint8_t*)font + ent->offset);
}*/ }
static inline bool FontLoadGlyph(glyph_t* glyph, u32 font, uint32_t codepoint) static inline bool FontLoadGlyph(glyph_t* glyph, const ffnt_header_t* font, uint32_t codepoint)
{ {
FT_Face face=0;
FT_Error ret=0;
FT_GlyphSlot slot;
FT_UInt glyph_index;
FT_Bitmap* bitmap;
s32 i=0;
//__builtin_printf("LoadGlyph %u\n", (unsigned int)codepoint); //__builtin_printf("LoadGlyph %u\n", (unsigned int)codepoint);
/*const ffnt_page_t* page = FontGetPage(font, codepoint >> 8); const ffnt_page_t* page = FontGetPage(font, codepoint >> 8);
if (!page) if (!page)
return false; return false;
codepoint &= 0xFF; codepoint &= 0xFF;
uint32_t off = page->hdr.pos[codepoint]; uint32_t off = page->hdr.pos[codepoint];
if (off == ~(uint32_t)0) if (off == ~(uint32_t)0)
return false;*/ return false;
if (s_font_faces_total==0) return false;
for (i=0; i<s_font_faces_total; i++) {
face = s_font_faces[i];
s_font_lastusedface = face;
glyph_index = FT_Get_Char_Index(face, codepoint);
if (glyph_index==0) continue;
ret = FT_Load_Glyph(
face, /* handle to face object */
glyph_index, /* glyph index */
FT_LOAD_DEFAULT);
if (ret==0)
{
ret = FT_Render_Glyph( face->glyph, /* glyph slot */
FT_RENDER_MODE_NORMAL); /* render mode */
}
if (ret) return false;
break;
}
slot = face->glyph;
bitmap = &slot->bitmap;
//__builtin_printf("%c %u\n", (char)codepoint, (unsigned int)off); //__builtin_printf("%c %u\n", (char)codepoint, (unsigned int)off);
/*glyph->width = page->hdr.widths[codepoint]; glyph->width = page->hdr.widths[codepoint];
glyph->height = page->hdr.heights[codepoint]; glyph->height = page->hdr.heights[codepoint];
glyph->advance = page->hdr.advances[codepoint]; glyph->advance = page->hdr.advances[codepoint];
glyph->posX = page->hdr.posX[codepoint]; glyph->posX = page->hdr.posX[codepoint];
glyph->posY = page->hdr.posY[codepoint]; glyph->posY = page->hdr.posY[codepoint];
glyph->data = &page->data[off];*/ glyph->data = &page->data[off];
glyph->width = bitmap->width;
glyph->height = bitmap->rows;
glyph->pitch = bitmap->pitch;
glyph->data = bitmap->buffer;
glyph->advance = slot->advance.x >> 6;
glyph->posX = slot->bitmap_left;
glyph->posY = slot->bitmap_top;
return true; return true;
} }
@ -151,17 +38,16 @@ static void DrawGlyph(uint32_t x, uint32_t y, color_t clr, const glyph_t* glyph)
uint32_t i, j; uint32_t i, j;
const uint8_t* data = glyph->data; const uint8_t* data = glyph->data;
x += glyph->posX; x += glyph->posX;
y -= glyph->posY; //y += glyph->posY; y += glyph->posY;
//__builtin_printf("DrawGlyph %u %u %08X\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr); //__builtin_printf("DrawGlyph %u %u %08X\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr);
for (j = 0; j < glyph->height; j ++) for (j = 0; j < glyph->height; j ++)
{ {
for (i = 0; i < glyph->width; i ++) for (i = 0; i < glyph->width; i ++)
{ {
clr.a = data[i]; clr.a = *data++;
if (!clr.a) continue; if (!clr.a) continue;
DrawPixel(x+i, y+j, clr); DrawPixel(x+i, y+j, clr);
} }
data+= glyph->pitch;
} }
} }
@ -232,15 +118,11 @@ static inline uint32_t DecodeUTF8(const char** ptr)
return 0xFFFD; return 0xFFFD;
} }
static void DrawText_(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text) static void DrawText_(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text)
{ {
//__builtin_printf("DrawText %u %u %08X %s\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr, text); //__builtin_printf("DrawText %u %u %08X %s\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr, text);
//y += font->baseline; y += font->baseline;
uint32_t origX = x; uint32_t origX = x;
if (s_font_faces_total==0) return;
if (!FontSetType(font)) return;
s_font_lastusedface = s_font_faces[0];
while (*text) while (*text)
{ {
if (max_width && x-origX >= max_width) { if (max_width && x-origX >= max_width) {
@ -260,7 +142,7 @@ static void DrawText_(u32 font, uint32_t x, uint32_t y, color_t clr, const char*
} }
x = origX; x = origX;
y += s_font_lastusedface->size->metrics.height / 64; y += font->height;
continue; continue;
} }
@ -275,200 +157,12 @@ static void DrawText_(u32 font, uint32_t x, uint32_t y, color_t clr, const char*
} }
} }
void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text) void DrawText(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text)
{ {
DrawText_(font, x, y, clr, text, 0, NULL); DrawText_(font, x, y, clr, text, 0, NULL);
} }
void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text) void DrawTextTruncate(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_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); DrawText_(font, x, y, clr, text, max_width, end_text);
} }
void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t* height_out)
{
uint32_t x = 0;
uint32_t width = 0, height = 0;
if (s_font_faces_total==0) return;
if (!FontSetType(font)) return;
s_font_lastusedface = s_font_faces[0];
while (*text)
{
glyph_t glyph;
uint32_t codepoint = DecodeUTF8(&text);
if (codepoint == '\n')
{
x = 0;
height += s_font_lastusedface->size->metrics.height / 64;
continue;
}
if (!FontLoadGlyph(&glyph, font, codepoint))
{
if (!FontLoadGlyph(&glyph, font, '?'))
continue;
}
x += glyph.advance;
if (x > width)
width = x;
}
if(width_out) *width_out = width;
if(height_out) *height_out = height;
}
bool fontInitialize(void)
{
FT_Error ret=0;
s32 i;
for (i=0; i<FONT_FACES_MAX; i++) s_font_facesret[i] = 1;
ret = FT_Init_FreeType(&s_font_library);
s_font_libret = ret;
if (s_font_libret) return false;
#ifdef __SWITCH__
PlFontData fonts[PlSharedFontType_Total];
Result rc=0;
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++) {
ret = FT_New_Memory_Face( s_font_library,
fonts[i].address, /* first byte in memory */
fonts[i].size, /* size in bytes */
0, /* face_index */
&s_font_faces[i]);
s_font_facesret[i] = ret;
if (ret) return false;
}
#endif
//These are loaded from "<original cwd>/fonts/<index>.ttf", these are user-supplied. Ideally these should be from plu SharedFont.
#ifndef __SWITCH__
char fontpath[PATH_MAX+1];
for (i=0; i<FONT_FACES_MAX; i++) {
memset(fontpath, 0, sizeof(fontpath));
snprintf(fontpath, sizeof(fontpath)-1, "%s%sfonts%s%u.ttf", menuGetRootBasePath(), DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, i);
ret = FT_New_Face( s_font_library,
fontpath,
0,
&s_font_faces[i]);
s_font_facesret[i] = ret;
if (ret) return false;
s_font_faces_total++;
}
#endif
return true;
}
void fontExit()
{
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
*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,
*you get the rest....
*/
uint32_t GetTextYCoordinate(u32 font, uint32_t rY, const char* text, const char align) {
uint32_t height_o,width;
GetTextDimensions(font,text,&width,&height_o);
uint32_t height = (uint32_t)height_o;
uint32_t fC = (rY-height);
switch(align){
case 't':
default:
return rY;
case 'c':
return (rY+(height>>1));//>>1 is a bitwise shift for dividing by 2
case 'b':
if(fC<=0) return 0;
else return fC;
}
}
/*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
*align='r','l','c' translates to (right,left,center)
*'r' aligned, rX location = end of string, you get the rest...
*/
uint32_t GetTextXCoordinate(u32 font, uint32_t rX, const char* text, const char align) {
uint32_t height,width_o;
GetTextDimensions(font,text,&width_o,&height);
uint32_t fC = (rX-width_o);
switch(align){
case 'r':
if(fC<0) return 0;
else return fC;
case 'c':
return (rX-(width_o>>1));//>>1 is a bitwise shift for dividing by 2
case 'l':
default:
return rX;
}
}

View File

@ -26,21 +26,19 @@ typedef struct {
typedef struct { typedef struct {
uint8_t width, height; uint8_t width, height;
int8_t posX, posY, advance, pitch; int8_t posX, posY, advance;
const uint8_t* data; const uint8_t* data;
} glyph_t; } glyph_t;
//extern const ffnt_header_t tahoma24_nxfnt;//These tahoma fonts aren't used anymore. extern const ffnt_header_t tahoma24_nxfnt;
//extern const ffnt_header_t tahoma12_nxfnt; extern const ffnt_header_t tahoma12_nxfnt;
/*extern const ffnt_header_t interuimedium20_nxfnt; extern const ffnt_header_t interuimedium20_nxfnt;
extern const ffnt_header_t interuimedium30_nxfnt; extern const ffnt_header_t interuimedium30_nxfnt;
extern const ffnt_header_t interuiregular14_nxfnt; extern const ffnt_header_t interuiregular14_nxfnt;
extern const ffnt_header_t interuiregular18_nxfnt;*/ extern const ffnt_header_t interuiregular18_nxfnt;
//#define tahoma24 &tahoma24_nxfnt #define tahoma24 &tahoma24_nxfnt
//#define tahoma12 &tahoma12_nxfnt #define tahoma12 &tahoma12_nxfnt
#define interuimedium20 2//&interuimedium20_nxfnt #define interuimedium20 &interuimedium20_nxfnt
#define interuimedium30 3//&interuimedium30_nxfnt #define interuimedium30 &interuimedium30_nxfnt
#define interuiregular14 0//&interuiregular14_nxfnt #define interuiregular14 &interuiregular14_nxfnt
#define interuiregular18 1//&interuiregular18_nxfnt #define interuiregular18 &interuiregular18_nxfnt
#define fontscale7 4
#define largestar 5

View File

@ -1,18 +1,18 @@
#include "language.h" #include "language.h"
#ifdef __SWITCH__ #ifdef SWITCH
#define STR_JP(_str) [SetLanguage_JA] = _str #define STR_JP(_str) [SetLanguage_JA] = _str
#define STR_EN(_str) [SetLanguage_ENUS] = _str, [SetLanguage_ENGB] = _str #define STR_EN(_str) [SetLanguage_ENUS] = _str, [SetLanguage_ENGB] = _str
#define STR_FR(_str) [SetLanguage_FR] = _str, [SetLanguage_FRCA] = _str #define STR_FR(_str) [SetLanguage_FR] = _str, [SetLanguage_FRCA] = _str
#define STR_DE(_str) [SetLanguage_DE] = _str #define STR_DE(_str) [SetLanguage_DE] = _str
#define STR_IT(_str) [SetLanguage_IT] = _str #define STR_IT(_str) [SetLanguage_IT] = _str
#define STR_ES(_str) [SetLanguage_ES] = _str, [SetLanguage_ES419] = _str #define STR_ES(_str) [SetLanguage_ES] = _str, [SetLanguage_ES419] = _str
#define STR_ZH_HANS(_str) [SetLanguage_ZHCN] = _str, [SetLanguage_ZHHANS] = _str #define STR_ZH(_str) [SetLanguage_ZHCN] = _str
#define STR_KO(_str) [SetLanguage_KO] = _str #define STR_KO(_str) [SetLanguage_KO] = _str
#define STR_NL(_str) [SetLanguage_NL] = _str #define STR_NL(_str) [SetLanguage_NL] = _str
#define STR_PT(_str) [SetLanguage_PT] = _str #define STR_PT(_str) [SetLanguage_PT] = _str
#define STR_RU(_str) [SetLanguage_RU] = _str #define STR_RU(_str) [SetLanguage_RU] = _str
#define STR_ZH_HANT(_str) [SetLanguage_ZHTW] = _str, [SetLanguage_ZHHANT] = _str #define STR_TW(_str) [SetLanguage_ZHTW] = _str
#else #else
#define STR_JP(_str) [0] = _str #define STR_JP(_str) [0] = _str
#define STR_EN(_str) [1] = _str #define STR_EN(_str) [1] = _str
@ -20,15 +20,15 @@
#define STR_DE(_str) [3] = _str #define STR_DE(_str) [3] = _str
#define STR_IT(_str) [4] = _str #define STR_IT(_str) [4] = _str
#define STR_ES(_str) [5] = _str #define STR_ES(_str) [5] = _str
#define STR_ZH_HANS(_str) [6] = _str #define STR_ZH(_str) [6] = _str
#define STR_KO(_str) [7] = _str #define STR_KO(_str) [7] = _str
#define STR_NL(_str) [8] = _str #define STR_NL(_str) [8] = _str
#define STR_PT(_str) [9] = _str #define STR_PT(_str) [9] = _str
#define STR_RU(_str) [10] = _str #define STR_RU(_str) [10] = _str
#define STR_ZH_HANT(_str) [11] = _str #define STR_TW(_str) [11] = _str
#endif #endif
const char* const g_strings[StrId_Max][17] = const char* const g_strings[StrId_Max][16] =
{ {
[StrId_Loading] = [StrId_Loading] =
{ {
@ -42,16 +42,8 @@ const char* const g_strings[StrId_Max][17] =
STR_NL("Laden…"), STR_NL("Laden…"),
STR_KO("로딩중…"), STR_KO("로딩중…"),
STR_RU("загрузка…"), STR_RU("загрузка…"),
STR_ZH_HANS("加载中…"), STR_ZH("加载中…"),
STR_ZH_HANT("載入中…"), STR_TW("加載中…"),
},
[StrId_AppletMode] =
{
STR_EN("● Applet Mode ●"),
STR_ES("● Modo Applet ●"),
STR_FR("● Mode Applet ●"),
STR_ZH_HANS("● 小程序模式 ●"),
}, },
[StrId_Directory] = [StrId_Directory] =
@ -66,8 +58,8 @@ const char* const g_strings[StrId_Max][17] =
STR_NL("Map"), STR_NL("Map"),
STR_KO("디렉토리"), STR_KO("디렉토리"),
STR_RU("каталог"), STR_RU("каталог"),
STR_ZH_HANS("目录"), STR_ZH("目录"),
STR_ZH_HANT("資料夾"), STR_TW("資料夾"),
}, },
/*[StrId_DefaultLongTitle] = /*[StrId_DefaultLongTitle] =
@ -82,8 +74,8 @@ const char* const g_strings[StrId_Max][17] =
STR_NL("Homebrew toepassing"), STR_NL("Homebrew toepassing"),
STR_KO("홈브류 애플리케이션"), STR_KO("홈브류 애플리케이션"),
STR_RU("приложение хомебреw"), STR_RU("приложение хомебреw"),
STR_ZH_HANS("自制应用程序"), STR_ZH("自制应用程序"),
STR_ZH_HANT("自製程式"), STR_TW("自製程式"),
},*/ },*/
[StrId_DefaultPublisher] = [StrId_DefaultPublisher] =
@ -93,29 +85,29 @@ const char* const g_strings[StrId_Max][17] =
STR_DE("Unbekannter Autor"), STR_DE("Unbekannter Autor"),
STR_FR("Auteur inconnu"), STR_FR("Auteur inconnu"),
STR_IT("Autore sconosciuto"), STR_IT("Autore sconosciuto"),
STR_JP("作者不明"), STR_JP("未知の作者"),
STR_PT("Autor Desconhecido"), STR_PT("Autor Desconhecido"),
STR_NL("Auteur onbekend"), STR_NL("Auteur onbekend"),
STR_KO("알 수 없는 개발자"), STR_KO("작자미상"),
STR_RU("неизвестный автор"), STR_RU("неизвестный автор"),
STR_ZH_HANS("未知作者"), STR_ZH("未知作者"),
STR_ZH_HANT("作者未知"), STR_TW("未知作者"),
}, },
[StrId_IOError] = [StrId_IOError] =
{ {
STR_EN("I/O Error"), STR_EN("I/O Error"),
STR_ES("Error de E/S"), STR_ES("Error de E/S"),
STR_DE("I/O-Fehler"), STR_DE("E/A-Fehler"),
STR_FR("Erreur d'E/S"), STR_FR("Erreur d'E/S"),
STR_IT("Errore I/O"), STR_IT("Errore di I/O"),
STR_JP("入出力エラー"), STR_JP("入出力エラー"),
STR_PT("Erro de E/S"), STR_PT("Erro de E/S"),
STR_NL("I/O Fout"), STR_NL("I/O Fout"),
STR_KO("입출력 오류"), STR_KO("I/O 에러"),
STR_RU("I/O-ошибка"), STR_RU("I/O-ошибка"),
STR_ZH_HANS("读写出错"), STR_ZH("读写出错"),
STR_ZH_HANT("取存錯誤"), STR_TW("讀寫錯誤"),
}, },
[StrId_CouldNotOpenFile] = [StrId_CouldNotOpenFile] =
@ -130,15 +122,8 @@ const char* const g_strings[StrId_Max][17] =
STR_NL("Kan bestand niet openen:\n%s"), STR_NL("Kan bestand niet openen:\n%s"),
STR_KO("파일을 열 수 없습니다:\n%s"), STR_KO("파일을 열 수 없습니다:\n%s"),
STR_RU("Не могу открыть файл:\n%s"), STR_RU("Не могу открыть файл:\n%s"),
STR_ZH_HANS("无法打开文件:\n%s"), STR_ZH("无法打开文件:\n%s"),
STR_ZH_HANT("無法開啟檔案:\n%s"), STR_TW("開啓檔案失敗:\n%s"),
},
[StrId_NroNotFound] =
{
STR_EN("Could not find executable: %s"),
STR_FR("Impossible trouver l'exécutable : %s"),
STR_ZH_HANS("找不到可执行文件"),
}, },
[StrId_NoAppsFound_Title] = [StrId_NoAppsFound_Title] =
@ -153,8 +138,8 @@ const char* const g_strings[StrId_Max][17] =
STR_NL("Geen toepassingen gevonden"), STR_NL("Geen toepassingen gevonden"),
STR_KO("애플리케이션을 찾을 수 없습니다"), STR_KO("애플리케이션을 찾을 수 없습니다"),
STR_RU("приложение не найдено"), STR_RU("приложение не найдено"),
STR_ZH_HANS("找不到可执行的自制程序"), STR_ZH("找不到可执行的自制程序"),
STR_ZH_HANT("沒有可執行的自製程式"), STR_TW("未能找到可執行的自製程式"),
}, },
[StrId_NoAppsFound_Msg] = [StrId_NoAppsFound_Msg] =
@ -172,7 +157,7 @@ const char* const g_strings[StrId_Max][17] =
STR_DE( STR_DE(
"Auf der SD-Karte wurden keine Anwendungen\n" "Auf der SD-Karte wurden keine Anwendungen\n"
"gefunden. Stelle sicher, dass ein Verzeichnis\n" "gefunden. Stelle sicher, dass ein Verzeichnis\n"
"namens /switch im Hauptverzeichnis der SD-Karte\n" "namens /switch im Wurzelverzeichnis der SD-Karte\n"
"existiert und Anwendungen enthält!" "existiert und Anwendungen enthält!"
), ),
STR_FR( STR_FR(
@ -189,7 +174,7 @@ const char* const g_strings[StrId_Max][17] =
), ),
STR_JP( STR_JP(
"SDカードにアプリケーションが見つかりませんでした。\n" "SDカードにアプリケーションが見つかりませんでした。\n"
"SDカードのルートに「switch」という名前のフォルダを\n" "SDカードのルートに「/switch」という名前のフォルダを\n"
"作成してください。" "作成してください。"
), ),
STR_PT( STR_PT(
@ -205,51 +190,27 @@ const char* const g_strings[StrId_Max][17] =
"en de toepassingen bevat." "en de toepassingen bevat."
), ),
STR_KO( STR_KO(
"SD 카드에서 애플리케이션을 찾을 수 없습니다.\n" "애플리케이션을 SD 카드에서 찾을 수 없습니다.\n"
"SD 카드 최상위에 /switch 폴더가 있고\n" "SD 카드 최상단에 /switch 라는 이름의 폴더가 있는지,\n"
"애플리케이션을 포함하는지 확인해 주십시오." "애플리케이션을 포함하고 있는지 확인해 주십시오."
), ),
STR_RU( STR_RU(
"На SD-карте нет приложений.\n" "На SD-карте нет приложений.\n"
"Убедитесь, что на карте SD есть каталог с\n" "Убедитесь, что на карте SD есть каталог с\n"
"названием switch и она содержит приложения." "названием switch и она содержит приложения."
), ),
STR_ZH_HANS( STR_ZH(
"找不到任何自制程序(nro)\n" "内存卡找不到任何可执行的应用程序\n"
"在SD卡根目录建立“switch”文件夹\n" "请在内存卡的根目录建立「switch」子目录\n"
"将自制程序(nro)放在其中" "存放自制应用软件至该目录"
), ),
STR_ZH_HANT( STR_TW(
"記憶卡內沒有可供執行的應用程式。\n" "記憶體找不到任何可執行的應用程式。\n"
"請在根目錄下建立「switch」資料夾\n" "請在記憶體建立「switch」資料夾\n"
"並將自製軟體複製到switch資料夾內" "然後儲存自製軟體到此處"
), ),
}, },
[StrId_LastLoadResult] =
{
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_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] = [StrId_AppInfo_Author] =
{ {
STR_EN("Author"), STR_EN("Author"),
@ -257,13 +218,13 @@ const char* const g_strings[StrId_Max][17] =
STR_DE("Autor"), STR_DE("Autor"),
STR_FR("Auteur"), STR_FR("Auteur"),
STR_IT("Autore"), STR_IT("Autore"),
STR_JP(""), STR_JP(""),
STR_PT("Autor"), STR_PT("Autor"),
STR_NL("Auteur"), STR_NL("Auteur"),
STR_KO("개발"), STR_KO(""),
STR_RU("автор"), STR_RU("автор"),
STR_ZH_HANS("作者"), STR_ZH("作者"),
STR_ZH_HANT("作者"), STR_TW("作者"),
}, },
[StrId_AppInfo_Version] = [StrId_AppInfo_Version] =
@ -276,10 +237,10 @@ const char* const g_strings[StrId_Max][17] =
STR_JP("バージョン"), STR_JP("バージョン"),
STR_PT("Versão"), STR_PT("Versão"),
STR_NL("Versie"), STR_NL("Versie"),
STR_KO("버전"), STR_KO("번역"),
STR_RU("Версия"), STR_RU("Версия"),
STR_ZH_HANS(""), STR_ZH(""),
STR_ZH_HANT(""), STR_TW(""),
}, },
[StrId_Actions_Launch] = [StrId_Actions_Launch] =
@ -287,128 +248,37 @@ const char* const g_strings[StrId_Max][17] =
STR_EN("Launch"), STR_EN("Launch"),
STR_ES("Lanzamiento"), STR_ES("Lanzamiento"),
STR_DE("Starten"), STR_DE("Starten"),
STR_FR("Lancer"), STR_FR("Lancement"),
STR_IT("Avvia"), STR_IT("Lanciare"),
STR_JP("起動"), STR_JP("打ち上げ"),
STR_PT("Lançamento"), STR_PT("Lançamento"),
STR_NL("Lancering"), STR_NL("Lancering"),
STR_KO("실행"), STR_KO("쏘다"),
STR_RU("запуск"), STR_RU("запуск"),
STR_ZH_HANS("发射"), STR_ZH("发射"),
STR_ZH_HANT("啟動"), STR_TW("发射"),
}, },
[StrId_Actions_Open] = [StrId_Actions_Open] =
{ {
STR_EN("Open"), STR_EN("Open"),
STR_ES("Abrir"), STR_ES("Abierto"),
STR_DE("Öffnen"), STR_DE("Öffnen"),
STR_FR("Ouvrir"), STR_FR("Ouvrir"),
STR_IT("Apri"), STR_IT("Aperto"),
STR_JP(""), STR_JP("いた"),
STR_PT("Abrir"), STR_PT("Aberto"),
STR_NL("Open"), STR_NL("Open"),
STR_KO(""), STR_KO(""),
STR_RU("открыто"), STR_RU("открыто"),
STR_ZH_HANS("打开"), STR_ZH("打开"),
STR_ZH_HANT("開啟"), STR_TW("打开"),
}, },
[StrId_Actions_Back] = [StrId_Actions_Back] =
{ {
STR_EN("Back"), STR_EN("Back"),
STR_ES("Volver"),
STR_DE("Zurück"), STR_DE("Zurück"),
STR_FR("Retour"),
STR_IT("Indietro"),
STR_JP("戻る"),
STR_PT("Regressar"),
STR_NL("Terug"),
STR_KO("뒤로 가기"),
STR_RU("возвращаться"),
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_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_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èmes"),
STR_DE("Theme Menü"),
STR_ES("Menú temático"),
STR_IT("Tema Menu"),
STR_JP("テーマメニュー"),
STR_KO("테마 메뉴"),
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_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] = /*[StrId_Reboot] =
@ -445,8 +315,8 @@ const char* const g_strings[StrId_Max][17] =
" \xEE\x80\x81 Annulla" " \xEE\x80\x81 Annulla"
), ),
STR_JP( STR_JP(
" \xEE\x81\xB3HOME に戻ることができませんでした\n" "\xEE\x81\xB3HOMEに戻ることはできません\n"
"今すぐ本体を再起動してください\n\n" "コンソールが今すぐ再起動する\n\n"
" \xEE\x80\x80 再起動\n" " \xEE\x80\x80 再起動\n"
" \xEE\x80\x81 キャンセル" " \xEE\x80\x81 キャンセル"
), ),
@ -463,9 +333,9 @@ const char* const g_strings[StrId_Max][17] =
" \xEE\x80\x81 Annuleren" " \xEE\x80\x81 Annuleren"
), ),
STR_KO( STR_KO(
"\xEE\x81\xB3HOME 으로 돌아갈 수 없습니다.\n" "\xEE\x81\xB3으로 돌아갈 수 없습니다.\n"
"콘솔을 재부팅할 것 입니다.\n\n" "당신의 기기를 리부팅 하려 합니다.\n\n"
" \xEE\x80\x80 부팅\n" " \xEE\x80\x80 부팅\n"
" \xEE\x80\x81 취소" " \xEE\x80\x81 취소"
), ),
STR_RU( STR_RU(
@ -474,13 +344,13 @@ const char* const g_strings[StrId_Max][17] =
" \xEE\x80\x80 Перезагрузите\n" " \xEE\x80\x80 Перезагрузите\n"
" \xEE\x80\x81 Отмена" " \xEE\x80\x81 Отмена"
), ),
STR_ZH_HANS( STR_ZH(
"无法返回至主机的 \xEE\x81\xB3HOME 菜单。\n" "无法返回至主机的 \xEE\x81\xB3HOME 菜单。\n"
"您需要重新启动您的 3DS 设备。\n\n" "您需要重新启动您的 3DS 设备。\n\n"
" \xEE\x80\x80 重启设备\n" " \xEE\x80\x80 重启设备\n"
" \xEE\x80\x81 取消操作" " \xEE\x80\x81 取消操作"
), ),
STR_ZH_HANT( STR_TW(
"無法返回至主機的 \xEE\x81\xB3HOME 選單。\n" "無法返回至主機的 \xEE\x81\xB3HOME 選單。\n"
"您需要重新啓動您的 3DS 設備。\n\n" "您需要重新啓動您的 3DS 設備。\n\n"
" \xEE\x80\x80 重啓設備\n" " \xEE\x80\x80 重啓設備\n"
@ -521,8 +391,8 @@ const char* const g_strings[StrId_Max][17] =
" \xEE\x80\x82 Riavvia" " \xEE\x80\x82 Riavvia"
), ),
STR_JP( STR_JP(
" \xEE\x81\xB3HOME に戻ろうとしています。\n\n" "あなたは今すぐ\xEE\x81\xB3HOMEに戻されます。\n\n"
" \xEE\x80\x80 了解\n" " \xEE\x80\x80 戻る\n"
" \xEE\x80\x81 キャンセル\n" " \xEE\x80\x81 キャンセル\n"
" \xEE\x80\x82 再起動" " \xEE\x80\x82 再起動"
), ),
@ -539,10 +409,10 @@ const char* const g_strings[StrId_Max][17] =
" \xEE\x80\x82 Herstarten" " \xEE\x80\x82 Herstarten"
), ),
STR_KO( STR_KO(
"\xEE\x81\xB3HOME 으로 돌아갈 것 입니다.\n\n" "\xEE\x81\xB3홈으로 돌아가려 합니다.\n\n"
" \xEE\x80\x80 돌아가기\n" " \xEE\x80\x80 이동\n"
" \xEE\x80\x81 취소\n" " \xEE\x80\x81 취소\n"
" \xEE\x80\x82 부팅" " \xEE\x80\x82 부팅"
), ),
STR_RU( STR_RU(
"Вы возвращаетесь в \xEE\x81\xB3HOME.\n\n" "Вы возвращаетесь в \xEE\x81\xB3HOME.\n\n"
@ -550,13 +420,13 @@ const char* const g_strings[StrId_Max][17] =
" \xEE\x80\x81 Отмена\n" " \xEE\x80\x81 Отмена\n"
" \xEE\x80\x82 Перезагрузите" " \xEE\x80\x82 Перезагрузите"
), ),
STR_ZH_HANS( STR_ZH(
"您即将返回到主機的 \xEE\x81\xB3HOME 菜单。\n\n" "您即将返回到主機的 \xEE\x81\xB3HOME 菜单。\n\n"
" \xEE\x80\x80 确认返回\n" " \xEE\x80\x80 确认返回\n"
" \xEE\x80\x81 取消操作\n" " \xEE\x80\x81 取消操作\n"
" \xEE\x80\x82 重启设备" " \xEE\x80\x82 重启设备"
), ),
STR_ZH_HANT( STR_TW(
"您即將返回到主機的 \xEE\x81\xB3HOME 選單。\n\n" "您即將返回到主機的 \xEE\x81\xB3HOME 選單。\n\n"
" \xEE\x80\x80 確認返回\n" " \xEE\x80\x80 確認返回\n"
" \xEE\x80\x81 取消操作\n" " \xEE\x80\x81 取消操作\n"
@ -568,7 +438,7 @@ const char* const g_strings[StrId_Max][17] =
{ {
STR_EN("Title selector"), STR_EN("Title selector"),
STR_ES("Selector de título"), STR_ES("Selector de título"),
STR_DE("Titel-Auswahl"), STR_DE("Titel-Selektor"),
STR_FR("Sélecteur de titre"), STR_FR("Sélecteur de titre"),
STR_IT("Selettore del titolo"), STR_IT("Selettore del titolo"),
STR_JP("タイトルセレクタ"), STR_JP("タイトルセレクタ"),
@ -576,18 +446,18 @@ const char* const g_strings[StrId_Max][17] =
STR_NL("Titel selector"), STR_NL("Titel selector"),
STR_KO("타이틀 선택기"), STR_KO("타이틀 선택기"),
STR_RU("Селектор заголовков"), STR_RU("Селектор заголовков"),
STR_ZH_HANS("应用启动器"), STR_ZH("应用启动器"),
STR_ZH_HANT("自製程式啓動器"), STR_TW("自製程式啓動器"),
}, },
[StrId_ErrorReadingTitleMetadata] = [StrId_ErrorReadingTitleMetadata] =
{ {
STR_EN("Error reading title metadata.\n%08lX%08lX@%d"), 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_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( STR_FR(
"Erreur lors de la lecture des métadonnées\n" "Erreur lors de la lecture des métadonnées\n"
"du titre.\n%08lX%08lX@%d" "de titre.\n%08lX%08lX@%d"
), ),
STR_IT("Errore nella lettura dei metadata dei titoli.\n%08lX%08lX@%d"), STR_IT("Errore nella lettura dei metadata dei titoli.\n%08lX%08lX@%d"),
STR_JP("タイトルメタデータを読み取ることができませんでした。\n%08lX%08lX@%d"), STR_JP("タイトルメタデータを読み取ることができませんでした。\n%08lX%08lX@%d"),
@ -595,8 +465,8 @@ const char* const g_strings[StrId_Max][17] =
STR_NL("Fout bij het lezen van titel metadata.\n%08lX%08lX@%d"), STR_NL("Fout bij het lezen van titel metadata.\n%08lX%08lX@%d"),
STR_KO("타이틀 메타데이터를 읽는데 실패하였습니다.\n%08lX%08lX@%d"), STR_KO("타이틀 메타데이터를 읽는데 실패하였습니다.\n%08lX%08lX@%d"),
STR_RU("Ошибка чтения метаданных заголовка\n.%08lX%08lX@%d"), STR_RU("Ошибка чтения метаданных заголовка\n.%08lX%08lX@%d"),
STR_ZH_HANS("读取软件相关信息时发生错误:\n%08lX%08lX@%d"), STR_ZH("读取软件相关信息时发生错误:\n%08lX%08lX@%d"),
STR_ZH_HANT("讀取軟體相關資訊時發生錯誤:\n%08lX%08lX@%d"), STR_TW("讀取軟體相關數據時發生錯誤:\n%08lX%08lX@%d"),
}, },
[StrId_NoTitlesFound] = [StrId_NoTitlesFound] =
@ -609,10 +479,10 @@ const char* const g_strings[StrId_Max][17] =
STR_JP("タイトルが見つかりませんでした。"), STR_JP("タイトルが見つかりませんでした。"),
STR_PT("Nenhum título foi encontrado."), STR_PT("Nenhum título foi encontrado."),
STR_NL("Geen titels gevonden."), STR_NL("Geen titels gevonden."),
STR_KO("타이틀을 찾을 수 없습니다."), STR_KO("타이틀을 찾지 못하였습니다."),
STR_RU("Заголовки не обнаружены"), STR_RU("Заголовки не обнаружены"),
STR_ZH_HANS("主机内找不到任何软件。"), STR_ZH("主机内找不到任何软件。"),
STR_ZH_HANT("主機内找不到任何軟體。"), STR_TW("主機内找不到任何軟體。"),
}, },
[StrId_SelectTitle] = [StrId_SelectTitle] =
@ -667,12 +537,12 @@ const char* const g_strings[StrId_Max][17] =
" \xEE\x80\x80 Выберите\n" " \xEE\x80\x80 Выберите\n"
" \xEE\x80\x81 Отмена" " \xEE\x80\x81 Отмена"
), ),
STR_ZH_HANS( STR_ZH(
"请选择一个目标软件。\n\n" "请选择一个目标软件。\n\n"
" \xEE\x80\x80 确认\n" " \xEE\x80\x80 确认\n"
" \xEE\x80\x81 取消" " \xEE\x80\x81 取消"
), ),
STR_ZH_HANT( STR_TW(
"請選擇一個目標軟體。\n\n" "請選擇一個目標軟體。\n\n"
" \xEE\x80\x80 確認\n" " \xEE\x80\x80 確認\n"
" \xEE\x80\x81 取消" " \xEE\x80\x81 取消"
@ -707,7 +577,7 @@ const char* const g_strings[StrId_Max][17] =
"Utilizza un exploit diverso." "Utilizza un exploit diverso."
), ),
STR_JP( STR_JP(
"この自作エクスプロイトでは、ターゲットタイトルの\n" "この自家製のエクスプロイトは、ターゲットタイトルの\n"
"下でアプリを起動することができません。\n" "下でアプリを起動することができません。\n"
"別のエクスプロイトを使用してください。" "別のエクスプロイトを使用してください。"
), ),
@ -722,8 +592,8 @@ const char* const g_strings[StrId_Max][17] =
"Gebruik een andere exploit." "Gebruik een andere exploit."
), ),
STR_KO( STR_KO(
"이 홈브류 익스플로잇은 해당 타이틀에서 플리케이션을\n" "이 홈브류 익스플로잇은 해당 타이틀에서 플리케이션을\n"
"실행는 것을 지원하지 않습니다.\n" "실행시키는 것을 지원하지 않습니다.\n"
"다른 익스플로잇을 사용해 주십시오." "다른 익스플로잇을 사용해 주십시오."
), ),
STR_RU( STR_RU(
@ -731,14 +601,14 @@ const char* const g_strings[StrId_Max][17] =
"приложений под целевыми заголовками.\n" "приложений под целевыми заголовками.\n"
"Пожалуйста, используйте другой эксплойт." "Пожалуйста, используйте другой эксплойт."
), ),
STR_ZH_HANS( STR_ZH(
"您所利用漏洞启动的「自制软件启动器」,\n" "您所利用漏洞启动的「自制软件启动器」,\n"
"无法在当前选中的软件中启动自制软件。\n" "无法在当前选中的软件中启动自制软件。\n"
"请使用其它的漏洞来启动「自制软件启动器」。" "请使用其它的漏洞来启动「自制软件启动器」。"
), ),
STR_ZH_HANT( STR_TW(
"您所利用漏洞開啓的「自製軟體啓動器」\n" "您所利用漏洞開啓的「自製軟體啓動器」\n"
"無法在當前選中的軟體啓動自製軟\n" "無法在當前選中的軟體啓動自製軟\n"
"請利用其它漏洞來啓動「自製軟體啓動器」。" "請利用其它漏洞來啓動「自製軟體啓動器」。"
), ),
}, },
@ -755,7 +625,7 @@ const char* const g_strings[StrId_Max][17] =
), ),
STR_DE( STR_DE(
"Die ausgewählte Anwendung benötigt einen\n" "Die ausgewählte Anwendung benötigt einen\n"
"Titel der nicht installiert ist." "Titel der nicht installiert ist"
), ),
STR_FR( STR_FR(
"L'application sélectionnée requiert un titre\n" "L'application sélectionnée requiert un titre\n"
@ -767,7 +637,7 @@ const char* const g_strings[StrId_Max][17] =
), ),
STR_JP( STR_JP(
"このアプリを実行するために\n" "このアプリを実行するために\n"
"必要なタイトルがインストールされていません。" "適切なタイトルがインストールされていません。"
), ),
STR_PT( STR_PT(
"A aplicação que acabou de tentar executar requer\n" "A aplicação que acabou de tentar executar requer\n"
@ -778,37 +648,37 @@ const char* const g_strings[StrId_Max][17] =
"vereist een titel die niet geinstalleerd is." "vereist een titel die niet geinstalleerd is."
), ),
STR_KO( STR_KO(
"해당 애플리케이션은 시스템에 설치되지 않은\n" "시도한 애플리케이션의 실행에 필요한 타이틀이\n"
"타이틀을 요구합니다." "시스템에 설치되어 있지 않습니다."
), ),
STR_RU( STR_RU(
"Для приложения требуется зависимость,\n" "Для приложения требуется зависимость,\n"
"которая не установлена." "которая не установлена."
), ),
STR_ZH_HANS( STR_ZH(
"主机找不到该应用程序\n" "主机找不到该应用程序\n"
"所需求的软件。" "所需求的软件。"
), ),
STR_ZH_HANT( STR_TW(
"主機找不到該應用程式\n" "主機找不到該應用程式\n"
"所需求的軟體。" "所需求的軟體。"
), ),
},*/ },*/
[StrId_NetLoader] = /*[StrId_NetLoader] =
{ {
STR_EN("NetLoader"), STR_EN("3dslink NetLoader"),
STR_ES("Cargador de programas"), STR_ES("Cargador de programas 3dslink"),
STR_DE("Netzwerk-Loader"), STR_DE("3dslink Netzwerk-Loader"),
STR_FR("NetLoader"), STR_FR("Chargeur de programme 3dslink"),
STR_IT("Caricamento programmi"), STR_IT("Caricamento programmi 3dslink"),
STR_JP("ネットローダ"), STR_JP("3dslinkネットローダ"),
STR_PT("Carregador de programas"), STR_PT("Carregador de programas 3dslink"),
STR_NL("netwerk lader"), STR_NL("3dslink netwerk lader"),
STR_KO("네트워크 로더"), STR_KO("3dslink 넷로더"),
STR_RU("Загрузчик"), STR_RU("Загрузчик 3dslink"),
STR_ZH_HANS("网络执行模块"), STR_ZH("3dslink 网络执行模块"),
STR_ZH_HANT("網路執行模組"), STR_TW("3dslink 網路執行模組"),
}, },
[StrId_NetLoaderUnavailable] = [StrId_NetLoaderUnavailable] =
@ -816,15 +686,15 @@ const char* const g_strings[StrId_Max][17] =
STR_EN("The NetLoader is currently unavailable."), STR_EN("The NetLoader is currently unavailable."),
STR_ES("El cargador de programas no está disponible."), STR_ES("El cargador de programas no está disponible."),
STR_DE("Der Netzwerk-Loader ist zur Zeit nicht verfügbar."), STR_DE("Der Netzwerk-Loader ist zur Zeit nicht verfügbar."),
STR_FR("Le programme nxlink est indisponible."), STR_FR("Le chargeur de programme 3dslink est indisponible."),
STR_IT("Il caricamento programmi nxlink non è disponibile."), STR_IT("Il caricamento programmi 3dslink non è disponibile."),
STR_JP("nxlinkネットローダは現在利用できません。"), STR_JP("3dslinkネットローダを利用できません。"),
STR_PT("O carregador de programas está de momento indisponível."), STR_PT("O carregador de programas está de momento indisponível."),
STR_NL("De netwerk lader is niet beschikbaar."), STR_NL("De netwerk lader is niet beschikbaar."),
STR_KO("현재 네트워크 로더는 사용이 불가합니다."), STR_KO("넷로더는 현재 사용이 불가능 합니다."),
STR_RU("Загрузчик в настоящее время недоступен."), STR_RU("Загрузчик в настоящее время недоступен."),
STR_ZH_HANS("无法启动 nxlink 网络执行模块。"), STR_ZH("无法启动 3dslink 网络执行模块。"),
STR_ZH_HANT("無法啓動 nxlink 網路執行模組。"), STR_TW("無法啓動 3dslink 網路執行模組。"),
}, },
[StrId_NetLoaderError] = [StrId_NetLoaderError] =
@ -837,74 +707,81 @@ const char* const g_strings[StrId_Max][17] =
STR_JP("エラーが発生しました。\n技術的な詳細:[%s:%d]"), STR_JP("エラーが発生しました。\n技術的な詳細:[%s:%d]"),
STR_PT("Ocorreu um erro.\nDetalhes técnicos: [%s:%d]"), STR_PT("Ocorreu um erro.\nDetalhes técnicos: [%s:%d]"),
STR_NL("Er is een fout opgetreden\nTechnische details: [%s:%d]"), STR_NL("Er is een fout opgetreden\nTechnische details: [%s:%d]"),
STR_KO("오류가 발생했습니다.\n기술적인 세부사항: [%s:%d]"), STR_KO("에러가 발생했습니다.\n상세: [%s:%d]"),
STR_RU("Произошла ошибка.\nТехнические подробности: [%s:%d]"), STR_RU("Произошла ошибка.\nТехнические подробности: [%s:%d]"),
STR_ZH_HANS("发生错误。\n详细错误信息:[%s:%d]"), STR_ZH("发生错误。\n详细错误信息:[%s:%d]"),
STR_ZH_HANT("發生錯誤。\n詳細錯誤資訊:[%s:%d]"), STR_TW("發生錯誤。\n詳細錯誤資訊:[%s:%d]"),
}, },
[StrId_NetLoaderOffline] = [StrId_NetLoaderOffline] =
{ {
STR_EN("Offline, waiting for network…"), STR_EN("Offline, waiting for network…\n\n\n \xEE\x80\x81 Cancel"),
STR_DE("Offline, warte auf Netzwerk…"), STR_ZH("无法连接网络,等待网络连接…\n\n\n \xEE\x80\x81 取消"),
STR_FR("Hors-ligne, en attente d'une connection..."), STR_TW("當前離線,等待網路連線…\n\n\n \xEE\x80\x81 取消"),
STR_IT("Disconnesso, in attesa della connessione…"), STR_IT("Disconnesso, in attesa della connessione…\n\n\n \xEE\x80\x81 Annullare"),
STR_ES("Desconectado, esperando a la red..."),
STR_JP("オフラインです。ネットワーク接続を待っています…"),
STR_KO("연결 끊김, 네트워크 기다리는 중…"),
STR_ZH_HANS("无法连接网络,等待网络连接…"),
STR_ZH_HANT("目前已離線,等待網路連線…"),
}, },
[StrId_NetLoaderActive] = [StrId_NetLoaderActive] =
{ {
STR_EN( STR_EN(
"Waiting for nxlink to connect…\n" "Waiting for 3dslink to connect…\n"
"IP Addr: %lu.%lu.%lu.%lu, Port: %d" "IP Addr: %lu.%lu.%lu.%lu, Port: %d\n\n"
" \xEE\x80\x81 Cancel"
), ),
STR_ES( STR_ES(
"Esperando a que se conecte nxlink…\n" "Esperando a que se conecte 3dslink…\n"
"Dir.IP: %lu.%lu.%lu.%lu, Puerto: %d" "Dir.IP: %lu.%lu.%lu.%lu, Puerto: %d\n\n"
" \xEE\x80\x81 Cancelar"
), ),
STR_DE( STR_DE(
"Warte auf Verbindung von nxlink…\n" "Warte auf Verbindung von 3dslink…\n"
"IP Addr: %lu.%lu.%lu.%lu, Port: %d" "IP Addr: %lu.%lu.%lu.%lu, Port: %d\n\n"
" \xEE\x80\x81 Abbrechen"
), ),
STR_FR( STR_FR(
"En attente de la connexion de nxlink…\n" "En attente de la connexion de 3dslink…\n"
"Adr. IP : %lu.%lu.%lu.%lu, Port : %d" "Adr. IP : %lu.%lu.%lu.%lu, Port : %d\n\n"
" \xEE\x80\x81 Annuler"
), ),
STR_IT( STR_IT(
"In attesa della connessione di nxlink…\n" "In attesa della connessione di 3dslink…\n"
"Ind. IP : %lu.%lu.%lu.%lu, Porta : %d" "Ind. IP : %lu.%lu.%lu.%lu, Porta : %d\n\n"
" \xEE\x80\x81 Annullare"
), ),
STR_JP( STR_JP(
"nxlinkが接続されるのを待っています…\n" "3dslinkが接続するのを待っている…\n"
"IPアドレス%lu.%lu.%lu.%lu, ポート番号:%d" "IPアドレス%lu.%lu.%lu.%lu, ポート番号:%d\n\n"
" \xEE\x80\x81 キャンセル"
), ),
STR_PT( STR_PT(
"A aguardar pela conexão do nxlink…\n" "A aguardar pela conexão do 3dslink…\n"
"End. IP: %lu.%lu.%lu.%lu, Porta: %d" "End. IP: %lu.%lu.%lu.%lu, Porta: %d\n\n"
" \xEE\x80\x81 Cancelar"
), ),
STR_NL( STR_NL(
"Wachten op nxlink verbinding…\n" "Wachten op 3dslink verbinding…\n"
"IP Addr: %lu.%lu.%lu.%lu, Poort: %d" "IP Addr: %lu.%lu.%lu.%lu, Poort: %d\n\n"
" \xEE\x80\x81 Annuleren"
), ),
STR_KO( STR_KO(
"nxlink의 연결을 대기중…\n" "3dslink 의 연결을 대기중…\n"
"IP 주소: %lu.%lu.%lu.%lu, 포트: %d" "IP 주소: %lu.%lu.%lu.%lu, 포트: %d\n\n"
" \xEE\x80\x81 취소"
), ),
STR_RU( STR_RU(
"Ожидание подключения nxlink…\n" "Ожидание подключения 3dslink…\n"
"айпи адрес: %lu.%lu.%lu.%lu, Порт: %d" "айпи адрес: %lu.%lu.%lu.%lu, Порт: %d\n\n"
" \xEE\x80\x81 Отмена"
), ),
STR_ZH_HANS( STR_ZH(
"等待 nxlink 连接…\n" "等待 3dslink 连接…\n"
"IP 地址:%lu.%lu.%lu.%lu端口%d" "IP 地址:%lu.%lu.%lu.%lu端口%d\n\n"
" \xEE\x80\x81 取消等待"
), ),
STR_ZH_HANT( STR_TW(
"等待 nxlink 連接…\n" "等待 3dslink 連接…\n"
"IP 位址:%lu.%lu.%lu.%lu連接埠%d" "IP 位址:%lu.%lu.%lu.%lu連接埠%d\n\n"
" \xEE\x80\x81 取消等待"
), ),
}, },
@ -931,8 +808,8 @@ const char* const g_strings[StrId_Max][17] =
"%zu di %zu KiB scritti" "%zu di %zu KiB scritti"
), ),
STR_JP( STR_JP(
"データを転送しています…\n" "データが転送されます…\n"
"%zu / %zu KiB 転送済み" "%zu / %zu KiB 書かれた"
), ),
STR_PT( STR_PT(
"A transferir…\n" "A transferir…\n"
@ -944,19 +821,20 @@ const char* const g_strings[StrId_Max][17] =
), ),
STR_KO( STR_KO(
"전송중…\n" "전송중…\n"
"%zu / %zu KiB 쓰여짐" "%zu / %zu KiB 전송"
), ),
STR_RU( STR_RU(
"Передача…\n" "Передача…\n"
"%zu из %zu КИБ написано" "%zu из %zu КИБ написано"
), ),
STR_ZH_HANS( STR_ZH(
"正在传输…\n" "正在传输…\n"
"已完成 %zu / %zu KiB" "已完成 %zu / %zu KiB"
), ),
STR_ZH_HANT( STR_TW(
"正在傳輸…\n" "正在傳輸…\n"
"已完成 %zu / %zu KiB" "已完成 %zu / %zu KiB"
), ),
}, },*/
}; };

View File

@ -1,34 +1,24 @@
#pragma once #pragma once
#ifdef __SWITCH__ #ifdef SWITCH
#include <switch.h> #include <switch.h>
#endif #endif
typedef enum typedef enum
{ {
StrId_Loading = 0, StrId_Loading = 0,
StrId_AppletMode,
StrId_Directory, StrId_Directory,
StrId_DefaultPublisher, StrId_DefaultPublisher,
StrId_IOError, StrId_IOError,
StrId_CouldNotOpenFile, StrId_CouldNotOpenFile,
StrId_NroNotFound,
StrId_NoAppsFound_Title, StrId_NoAppsFound_Title,
StrId_NoAppsFound_Msg, StrId_NoAppsFound_Msg,
StrId_LastLoadResult,
StrId_AppLaunchError,
StrId_AppInfo_Author, StrId_AppInfo_Author,
StrId_AppInfo_Version, StrId_AppInfo_Version,
StrId_Actions_Launch, StrId_Actions_Launch,
StrId_Actions_Open, StrId_Actions_Open,
StrId_Actions_Back, StrId_Actions_Back,
StrId_Actions_Apply,
StrId_Actions_Star,
StrId_Actions_Unstar,
StrId_MsgBox_OK,
StrId_Reboot, StrId_Reboot,
StrId_ReturnToHome, StrId_ReturnToHome,
@ -48,12 +38,8 @@ typedef enum
StrId_NetLoaderActive, StrId_NetLoaderActive,
StrId_NetLoaderTransferring, StrId_NetLoaderTransferring,
StrId_ThemeMenu,
StrId_ThemeNotApplied,
StrId_DefaultThemeName,
StrId_Max, StrId_Max,
} StrId; } StrId;
extern const char* const g_strings[StrId_Max][17]; extern const char* const g_strings[StrId_Max][16];

View File

@ -2,7 +2,7 @@
size_t launchAddArg(argData_s* ad, const char* arg) { size_t launchAddArg(argData_s* ad, const char* arg) {
size_t len = strlen(arg)+1; size_t len = strlen(arg)+1;
if ((ad->dst+len) >= (char*)(ad->buf + sizeof(ad->buf))) return len; // Overflow if ((ad->dst+len) >= (char*)(ad+1)) return len; // Overflow
ad->buf[0]++; ad->buf[0]++;
strcpy(ad->dst, arg); strcpy(ad->dst, arg);
ad->dst += len; ad->dst += len;

View File

@ -16,7 +16,7 @@ typedef struct
//void (* useTitle)(u64 tid, u8 mediatype); //void (* useTitle)(u64 tid, u8 mediatype);
} loaderFuncs_s; } loaderFuncs_s;
bool launchInit(void); void launchInit(void);
void launchExit(void); void launchExit(void);
const loaderFuncs_s* launchGetLoader(void); const loaderFuncs_s* launchGetLoader(void);
size_t launchAddArg(argData_s* ad, const char* arg); size_t launchAddArg(argData_s* ad, const char* arg);

View File

@ -4,6 +4,6 @@
float approxSin(float x); float approxSin(float x);
static inline float clamp(float x, float min, float max) { inline float clamp(float x, float min, float max) {
return fmin(fmax(x, min), max); return fmin(fmax(x, min), max);
} }

View File

@ -1,28 +1,25 @@
#include "common.h" #include "common.h"
#include <physfs.h>
void menuEntryInit(menuEntry_s* me, MenuEntryType type) { void menuEntryInit(menuEntry_s* me, MenuEntryType type) {
memset(me, 0, sizeof(*me)); memset(me, 0, sizeof(*me));
me->type = type; me->type = type;
} }
void menuEntryFree(menuEntry_s* me, bool skip_icongfx) { void menuEntryFree(menuEntry_s* me) {
me->icon_size = 0; me->icon_size = 0;
if (me->icon) { if (me->icon) {
free(me->icon); free(me->icon);
me->icon = NULL; me->icon = NULL;
} }
if (!skip_icongfx) { if (me->icon_gfx) {
if (me->icon_gfx) { free(me->icon_gfx);
free(me->icon_gfx); me->icon_gfx = NULL;
me->icon_gfx = NULL; }
}
if (me->icon_gfx_small) { if (me->icon_gfx_small) {
free(me->icon_gfx_small); free(me->icon_gfx_small);
me->icon_gfx_small = NULL; me->icon_gfx_small = NULL;
}
} }
if (me->nacp) { if (me->nacp) {
@ -36,11 +33,6 @@ bool fileExists(const char* path) {
return stat(path, &st)==0 && S_ISREG(st.st_mode); return stat(path, &st)==0 && S_ISREG(st.st_mode);
} }
bool fsobjExists(const char* path) {
struct stat st;
return stat(path, &st)==0;
}
static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) { static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) {
NroHeader header; NroHeader header;
NroAssetHeader asset_header; NroAssetHeader asset_header;
@ -81,57 +73,6 @@ static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) {
return ok; return ok;
} }
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");
if (!f) return false;
me->icon_size = st.st_size;
me->icon = (uint8_t*)malloc(me->icon_size);
if (me->icon == NULL) {
fclose(f);
return false;
}
memset(me->icon, 0, me->icon_size);
bool ok = fread(me->icon, me->icon_size, 1, f) == 1;
fclose(f);
return ok;
}
static bool menuEntryImportIconGfx(menuEntry_s* me, uint8_t* icon_gfx, uint8_t* icon_gfx_small) {
size_t tmpsize;
if (icon_gfx == NULL || icon_gfx_small == NULL) return false;
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 = 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);
if (me->icon_gfx_small == NULL) {
free(me->icon_gfx);
me->icon_gfx = NULL;
}
}
return me->icon_gfx && me->icon_gfx_small;
}
static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) { static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) {
NroHeader header; NroHeader header;
NroAssetHeader asset_header; NroAssetHeader asset_header;
@ -176,26 +117,6 @@ static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) {
return ok; 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) { /*static void fixSpaceNewLine(char* buf) {
char *outp = buf, *inp = buf; char *outp = buf, *inp = buf;
char lastc = 0; char lastc = 0;
@ -210,29 +131,20 @@ static bool menuEntryLoadExternalNacp(menuEntry_s* me, const char* path) {
} while (lastc); } while (lastc);
}*/ }*/
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists) { bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
int i=0, tmplen; static char tempbuf[PATH_MAX+1];
menu_s *menu_fileassoc = menuFileassocGetCurrent();
menuEntry_s* fileassoc_me = NULL;
char *strptr = NULL;
char tempbuf[PATH_MAX+16];
//bool isOldAppFolder = false; //bool isOldAppFolder = false;
if (check_exists && !fsobjExists(me->path)) return false;
tempbuf[PATH_MAX] = 0; tempbuf[PATH_MAX] = 0;
strncpy(me->name, name, sizeof(me->name)-1); strcpy(me->name, name);
if (me->type == ENTRY_TYPE_FOLDER) if (me->type == ENTRY_TYPE_FOLDER)
{ {
//Check for <dirpath>/<dirname>.nro //Check for <dirpath>/<dirname>.nro
snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s/%.*s.nro", (int)sizeof(tempbuf)/2, me->path, (int)sizeof(tempbuf)/2-7, name); snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s/%.*s.nro", (int)sizeof(tempbuf)/2, me->path, (int)sizeof(tempbuf)/2-7, name);
bool found = fileExists(tempbuf); bool found = fileExists(tempbuf);
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/". //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/".
tmplen = strlen(menuGetRootPath()); if (!found && strncmp(me->path, "sdmc:/switch/", 13)==0) {
if (!found && strncmp(me->path, menuGetRootPath(), tmplen)==0 && me->path[tmplen]=='/') {
DIR* dir; DIR* dir;
struct dirent* dp; struct dirent* dp;
u32 nro_count=0; u32 nro_count=0;
@ -257,44 +169,22 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_
} }
closedir(dir); closedir(dir);
} }
if (!found && menu_fileassoc->nEntries > 0) {
fileassoc_flag = 1;
dir = opendir(me->path);
if (dir) {
while ((dp = readdir(dir))) {
if (dp->d_name[0]=='.')//Check this here so that it's consistent with menuScan().
continue;
for (fileassoc_me = menu_fileassoc->firstEntry, i = 0; fileassoc_me; fileassoc_me = fileassoc_me->next, i++) {
if (!fileassoc_me->fileassoc_type) continue; //Only handle fileassoc entries for filenames, not file_extensions.
if (strcmp(dp->d_name, fileassoc_me->fileassoc_str)) continue;
snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s/%.*s", (int)sizeof(tempbuf)/2, me->path, (int)sizeof(tempbuf)/2-7, dp->d_name);
found = fileExists(tempbuf);
if (found) break;
}
if (found) break;
}
closedir(dir);
}
}
} }
if (found) if (found)
{ {
//isOldAppFolder = true; //isOldAppFolder = true;
shortcut = false; shortcut = false;
me->type = fileassoc_flag ? ENTRY_TYPE_FILE_OTHER : ENTRY_TYPE_FILE; me->type = ENTRY_TYPE_FILE;
strcpy(me->path, tempbuf); strcpy(me->path, tempbuf);
} }
} }
if (me->type == ENTRY_TYPE_FILE) if (me->type == ENTRY_TYPE_FILE)
{ {
strncpy(me->author, textGetString(StrId_DefaultPublisher), sizeof(me->author)-1); strcpy(me->name, name);
strncpy(me->version, "1.0.0", sizeof(me->version)-1); strcpy(me->author, textGetString(StrId_DefaultPublisher));
strcpy(me->version, "1.0.0");
//shortcut_s sc; //shortcut_s sc;
@ -331,7 +221,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_
char* ext = getExtension(tempbuf); char* ext = getExtension(tempbuf);
strcpy(ext, ".jpg"); strcpy(ext, ".jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false); iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
if (iconLoaded) break; if (iconLoaded) break;
if (isOldAppFolder) if (isOldAppFolder)
@ -339,7 +229,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_
char* slash = getSlash(tempbuf); char* slash = getSlash(tempbuf);
strcpy(slash, "/icon.jpg"); strcpy(slash, "/icon.jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false); iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
if (iconLoaded) break; if (iconLoaded) break;
}*/ }*/
@ -404,394 +294,67 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_
shortcutFree(&sc);*/ shortcutFree(&sc);*/
} }
if (me->type == ENTRY_TYPE_THEME) {
config_t cfg = {0};
config_init(&cfg);
config_setting_t *themeInfo;
const char *name,
*author = textGetString(StrId_DefaultPublisher),
*version = "1.0.0";
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))
strncpy(me->name, name, sizeof(me->name)-1);
config_setting_lookup_string(themeInfo, "author", &author);
config_setting_lookup_string(themeInfo, "version", &version);
}
}
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)
{
if (menu_fileassoc->nEntries == 0) return false;
for (fileassoc_me = menu_fileassoc->firstEntry, i = 0; fileassoc_me; fileassoc_me = fileassoc_me->next, i++) {
//For fileassoc_type==0 compare the extension, otherwise compare the filename.
if (!fileassoc_me->fileassoc_type) {
strptr = getExtension(me->path);
}
if (fileassoc_me->fileassoc_type) {
strptr = getSlash(me->path);
if (strptr[0] == '/') strptr++;
}
if (strcmp(strptr, fileassoc_me->fileassoc_str)) continue;
//At this point a match was found.
me->type = ENTRY_TYPE_FILE;
//Attempt to load the icon from {me->path filepath with extension .jpg}, then on failure use the icon data from fileassoc_me.
memset(tempbuf, 0, sizeof(tempbuf));
strncpy(tempbuf, me->path, sizeof(tempbuf));
tempbuf[sizeof(tempbuf)-1] = 0;
strptr = getExtension(tempbuf);
strncpy(strptr, ".jpg", sizeof(tempbuf)-1 - ((ptrdiff_t)strptr - (ptrdiff_t)tempbuf));
bool iconLoaded = false;
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) menuEntryParseIcon(me);
if (iconLoaded && !(me->icon_gfx && me->icon_gfx_small)) iconLoaded = 0;
if (!iconLoaded && fileassoc_me->icon_gfx && fileassoc_me->icon_gfx_small)
iconLoaded = menuEntryImportIconGfx(me, fileassoc_me->icon_gfx, fileassoc_me->icon_gfx_small);
//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));
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];
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;
return true;
}
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; return true;
} }
void menuEntryFileassocLoad(const char* filepath) {
bool success=0, iconLoaded=0;
menuEntry_s* me = NULL;
config_setting_t *fileassoc = NULL, *targets = NULL, *target = NULL, *app_args = NULL, *target_args = NULL;
config_t cfg = {0};
int targets_len=0, args_len=0, i;
const char *strptr = NULL;
char app_path[PATH_MAX+8];
char main_icon_path[PATH_MAX+1];
char target_icon_path[PATH_MAX+1];
char target_file_extension[PATH_MAX+1];
char target_filename[PATH_MAX+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;
config_init(&cfg);
memset(app_path, 0, sizeof(app_path));
memset(main_icon_path, 0, sizeof(main_icon_path));
memset(app_author, 0, sizeof(app_author));
memset(app_version, 0, sizeof(app_version));
if (!fileExists(filepath)) return;
if (config_read_file(&cfg, filepath)) {
fileassoc = config_lookup(&cfg, "fileassoc");
if (fileassoc != NULL) {
if (config_setting_lookup_string(fileassoc, "app_path", &strptr))
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);
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);
if (targets_len > 0) {
//Load the author/version and icon data with the NRO app path.
me = menuCreateEntry(ENTRY_TYPE_FILE);
success = 0;
if (me) {
strncpy(me->path, app_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0;
strptr = getSlash(app_path);
if(strptr[0] == '/') strptr++;
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));
app_version[sizeof(app_version)-1] = 0;
app_icon_gfx = me->icon_gfx;
app_icon_gfx_small = me->icon_gfx_small;
success = 1;
}
menuDeleteEntry(me, success);
me = NULL;
}
//Process the targets list.
if (success) {
for (i=0; i<targets_len; i++) {
target = config_setting_get_elem(targets, i);
if (target == NULL) continue;
memset(target_icon_path, 0, sizeof(target_icon_path));
memset(target_file_extension, 0, sizeof(target_file_extension));
memset(target_filename, 0, sizeof(target_filename));
if (config_setting_lookup_string(target, "icon_path", &strptr))
snprintf(target_icon_path, sizeof(target_icon_path)-1, "%s%s", menuGetRootBasePath(), strptr);
if (config_setting_lookup_string(target, "file_extension", &strptr))
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);
iconLoaded = 0;
if (me) {
strncpy(me->path, app_path, sizeof(me->path));
me->path[sizeof(me->path)-1] = 0;
strncpy(me->author, app_author, sizeof(me->author));
me->author[sizeof(me->author)-1] = 0;
strncpy(me->version, app_version, sizeof(me->version));
me->version[sizeof(me->version)-1] = 0;
if (target_file_extension[0]) {
me->fileassoc_type = 0;
strncpy(me->fileassoc_str, target_file_extension, sizeof(me->fileassoc_str));
} else if (target_filename[0]) {
me->fileassoc_type = 1;
strncpy(me->fileassoc_str, target_filename, sizeof(me->fileassoc_str));
}
me->fileassoc_str[sizeof(me->fileassoc_str)-1] = 0;
if (target_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, target_icon_path, false);
if (!iconLoaded && main_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, main_icon_path, false);
if (iconLoaded) {
menuEntryParseIcon(me);
} else {
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) menuFileassocAddEntry(me);
}
}
}
}
}
}
if (success) {
free(app_icon_gfx);
free(app_icon_gfx_small);
}
config_destroy(&cfg);
}
void menuEntryParseIcon(menuEntry_s* me) { void menuEntryParseIcon(menuEntry_s* me) {
uint8_t *imageptr = NULL;
size_t imagesize = 256*256*3;
if (me->icon_size==0 || me->icon==NULL) return; if (me->icon_size==0 || me->icon==NULL) return;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]; njInit();
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
size_t imagesize = layoutobj->imageSize[0]*layoutobj->imageSize[1]*3; if (njDecode(me->icon, me->icon_size) != NJ_OK) {
bool ret=true; njDone();
uint8_t *tmp_gfx = (uint8_t*)malloc(imagesize); return;
}
if (tmp_gfx == NULL) ret = false;
if (ret) ret = assetsLoadJpgFromMemory(me->icon, me->icon_size, tmp_gfx, IMAGE_MODE_RGB24, layoutobj->imageSize[0], layoutobj->imageSize[1]);
if (ret) me->icon_gfx = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], IMAGE_MODE_RGB24);
if (ret && me->icon_gfx==NULL) ret = false;
me->icon_size = 0; me->icon_size = 0;
free(me->icon); free(me->icon);
me->icon = NULL; me->icon = NULL;
if (ret) me->icon_gfx_small = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], IMAGE_MODE_RGB24); if ((njGetWidth() != 256 || njGetHeight() != 256 || (size_t)njGetImageSize() != imagesize) || njIsColor() != 1) {//The decoded image must be RGB and 256x256.
njDone();
if (!ret || me->icon_gfx_small == NULL) { return;
free(me->icon_gfx);
me->icon_gfx = NULL;
} }
free(tmp_gfx); imageptr = njGetImage();
if (imageptr == NULL) {
njDone();
return;
}
me->icon_gfx = (uint8_t*)malloc(imagesize);
if (me->icon_gfx == NULL) {
njDone();
return;
}
memcpy(me->icon_gfx, imageptr, imagesize);
njDone();
me->icon_gfx_small = downscaleIcon(me->icon_gfx);
} }
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode) { uint8_t *downscaleIcon(const uint8_t *image) {
uint8_t *out; uint8_t *out = (uint8_t*)malloc(140*140*3);
switch (mode) {
case IMAGE_MODE_RGBA32:
out = (uint8_t*)malloc(destWidth*destHeight*4);
break;
default:
out = (uint8_t*)malloc(destWidth*destHeight*3);
break;
}
if (out == NULL) { if (out == NULL) {
return NULL; return NULL;
} }
if (srcWidth == destWidth && srcHeight == destHeight) {
memcpy(out, image, destWidth*destHeight*(mode==IMAGE_MODE_RGBA32 ? 4 : 3));
return out;
}
int tmpx, tmpy; int tmpx, tmpy;
int pos; int pos;
float sourceX, sourceY; float sourceX, sourceY;
float xScale = (float)srcWidth / (float)destWidth; int destWidth = 140, destHeight = 140;
float yScale = (float)srcHeight / (float)destHeight; float xScale = 256.0 / (float)destWidth;
float yScale = 256.0 / (float)destHeight;
int pixelX, pixelY; int pixelX, pixelY;
uint8_t r1, r2, r3, r4; uint8_t r1, r2, r3, r4;
uint8_t g1, g2, g3, g4; uint8_t g1, g2, g3, g4;
uint8_t b1, b2, b3, b4; uint8_t b1, b2, b3, b4;
uint8_t a1, a2, a3, a4;
float fx, fy, fx1, fy1; float fx, fy, fx1, fy1;
int w1, w2, w3, w4; int w1, w2, w3, w4;
@ -803,57 +366,26 @@ uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int des
pixelY = (int)sourceY; pixelY = (int)sourceY;
// get colours from four surrounding pixels // get colours from four surrounding pixels
if (mode == IMAGE_MODE_RGBA32) pos = ((pixelY + 0) * 256 + pixelX + 0) * 3;
pos = ((pixelY + 0) * srcWidth + pixelX + 0) * 4;
else
pos = ((pixelY + 0) * srcWidth + pixelX + 0) * 3;
r1 = image[pos+0]; r1 = image[pos+0];
g1 = image[pos+1]; g1 = image[pos+1];
b1 = image[pos+2]; b1 = image[pos+2];
if (mode == IMAGE_MODE_RGBA32) pos = ((pixelY + 0) * 256 + pixelX + 1) * 3;
a1 = image[pos+3];
if (mode == IMAGE_MODE_RGBA32)
pos = ((pixelY + 0) * srcWidth + pixelX + 1) * 4;
else
pos = ((pixelY + 0) * srcWidth + pixelX + 1) * 3;
r2 = image[pos+0]; r2 = image[pos+0];
g2 = image[pos+1]; g2 = image[pos+1];
b2 = image[pos+2]; b2 = image[pos+2];
if (mode == IMAGE_MODE_RGBA32) pos = ((pixelY + 1) * 256 + pixelX + 0) * 3;
a2 = image[pos+3];
if (mode == IMAGE_MODE_RGBA32)
pos = ((pixelY + 1) * srcWidth + pixelX + 0) * 4;
else
pos = ((pixelY + 1) * srcWidth + pixelX + 0) * 3;
r3 = image[pos+0]; r3 = image[pos+0];
g3 = image[pos+1]; g3 = image[pos+1];
b3 = image[pos+2]; b3 = image[pos+2];
if (mode == IMAGE_MODE_RGBA32) pos = ((pixelY + 1) * 256 + pixelX + 1) * 3;
a3 = image[pos+3];
if (mode == IMAGE_MODE_RGBA32)
pos = ((pixelY + 1) * srcWidth + pixelX + 1) * 4;
else
pos = ((pixelY + 1) * srcWidth + pixelX + 1) * 3;
r4 = image[pos+0]; r4 = image[pos+0];
g4 = image[pos+1]; g4 = image[pos+1];
b4 = image[pos+2]; b4 = image[pos+2];
if (mode == IMAGE_MODE_RGBA32)
a4 = image[pos+3];
// determine weights // determine weights
fx = sourceX - pixelX; fx = sourceX - pixelX;
fy = sourceY - pixelY; fy = sourceY - pixelY;
@ -866,17 +398,10 @@ uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int des
w4 = (int)(fx*fy*256.0); w4 = (int)(fx*fy*256.0);
// set output pixels // set output pixels
if (mode == IMAGE_MODE_RGBA32) pos = ((tmpy*destWidth) + tmpx) * 3;
pos = ((tmpy*destWidth) + tmpx) * 4;
else
pos = ((tmpy*destWidth) + tmpx) * 3;
out[pos+0] = (uint8_t)((r1 * w1 + r2 * w2 + r3 * w3 + r4 * w4) >> 8); out[pos+0] = (uint8_t)((r1 * w1 + r2 * w2 + r3 * w3 + r4 * w4) >> 8);
out[pos+1] = (uint8_t)((g1 * w1 + g2 * w2 + g3 * w3 + g4 * w4) >> 8); out[pos+1] = (uint8_t)((g1 * w1 + g2 * w2 + g3 * w3 + g4 * w4) >> 8);
out[pos+2] = (uint8_t)((b1 * w1 + b2 * w2 + b3 * w3 + b4 * w4) >> 8); out[pos+2] = (uint8_t)((b1 * w1 + b2 * w2 + b3 * w3 + b4 * w4) >> 8);
if (mode == IMAGE_MODE_RGBA32)
out[pos+3] = (uint8_t)((a1 * w1 + a2 * w2 + a3 * w3 + a4 * w4) >> 8);
} }
} }
@ -884,24 +409,13 @@ uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int des
} }
void menuEntryParseNacp(menuEntry_s* me) { void menuEntryParseNacp(menuEntry_s* me) {
NacpLanguageEntry *langentry = NULL; int lang = 0;//TODO: Update this once libnx supports settings get-language.
if (me->nacp==NULL) return; if (me->nacp==NULL) return;
strncpy(me->version, me->nacp->display_version, sizeof(me->version)-1); strncpy(me->name, me->nacp->lang[lang].name, sizeof(me->name)-1);
strncpy(me->author, me->nacp->lang[lang].author, sizeof(me->author)-1);
#ifdef __SWITCH__ strncpy(me->version, me->nacp->version, sizeof(me->version)-1);
Result rc=0;
rc = nacpGetLanguageEntry(me->nacp, &langentry);
if (R_SUCCEEDED(rc) && langentry!=NULL) {
#else
langentry = &me->nacp->lang[0];
if (1) {
#endif
strncpy(me->name, langentry->name, sizeof(me->name)-1);
strncpy(me->author, langentry->author, sizeof(me->author)-1);
}
free(me->nacp); free(me->nacp);
me->nacp = NULL; me->nacp = NULL;

View File

@ -1,29 +1,25 @@
#include "common.h" #include "common.h"
static menu_s s_menu[2]; static menu_s s_menu[2];
static menu_s s_menuFileassoc[2]; static bool s_curMenu;
static bool s_curMenu, s_curMenuFileassoc;
menu_s* menuGetCurrent(void) { menu_s* menuGetCurrent(void) {
return &s_menu[s_curMenu]; return &s_menu[s_curMenu];
} }
menu_s* menuFileassocGetCurrent(void) { static menuEntry_s* menuCreateEntry(MenuEntryType type) {
return &s_menuFileassoc[s_curMenuFileassoc];
}
menuEntry_s* menuCreateEntry(MenuEntryType type) {
menuEntry_s* me = (menuEntry_s*)malloc(sizeof(menuEntry_s)); menuEntry_s* me = (menuEntry_s*)malloc(sizeof(menuEntry_s));
menuEntryInit(me, type); menuEntryInit(me, type);
return me; return me;
} }
void menuDeleteEntry(menuEntry_s* me, bool skip_icongfx) { static void menuDeleteEntry(menuEntry_s* me) {
menuEntryFree(me, skip_icongfx); menuEntryFree(me);
free(me); free(me);
} }
static void _menuAddEntry(menu_s *m, menuEntry_s* me) { static void menuAddEntry(menuEntry_s* me) {
menu_s* m = &s_menu[!s_curMenu];
me->menu = m; me->menu = m;
if (m->lastEntry) if (m->lastEntry)
{ {
@ -34,54 +30,20 @@ static void _menuAddEntry(menu_s *m, menuEntry_s* me) {
m->firstEntry = me; m->firstEntry = me;
m->lastEntry = me; m->lastEntry = me;
} }
m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++; m->nEntries ++;
} }
static void menuAddEntry(menuEntry_s* me) { static void menuClear(void) {
_menuAddEntry(&s_menu[!s_curMenu], me);
}
void menuFileassocAddEntry(menuEntry_s* me) {
_menuAddEntry(&s_menuFileassoc[!s_curMenuFileassoc], me);
}
static void menuAddEntryToFront(menuEntry_s* me) {
menu_s* m = &s_menu[!s_curMenu]; menu_s* m = &s_menu[!s_curMenu];
me->menu = m;
if (m->lastEntry)
{
me->next = m->firstEntry;
m->firstEntry = me;
} else
{
m->firstEntry = me;
m->lastEntry = me;
}
m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++;
}
static void _menuClear(menu_s* m) {
menuEntry_s *cur, *next; menuEntry_s *cur, *next;
for (cur = m->firstEntry; cur; cur = next) for (cur = m->firstEntry; cur; cur = next)
{ {
next = cur->next; next = cur->next;
menuDeleteEntry(cur, 0); menuDeleteEntry(cur);
} }
memset(m, 0, sizeof(*m)); memset(m, 0, sizeof(*m));
} }
static void menuClear(void) {
_menuClear(&s_menu[!s_curMenu]);
}
static void menuFileassocClear(void) {
_menuClear(&s_menuFileassoc[!s_curMenuFileassoc]);
}
static int menuEntryCmp(const void *p1, const void *p2) { static int menuEntryCmp(const void *p1, const void *p2) {
const menuEntry_s* lhs = *(menuEntry_s**)p1; const menuEntry_s* lhs = *(menuEntry_s**)p1;
const menuEntry_s* rhs = *(menuEntry_s**)p2; const menuEntry_s* rhs = *(menuEntry_s**)p2;
@ -98,34 +60,20 @@ static void menuSort(void) {
menu_s* m = &s_menu[!s_curMenu]; menu_s* m = &s_menu[!s_curMenu];
int nEntries = m->nEntries; int nEntries = m->nEntries;
if (nEntries==0) return; if (nEntries==0) return;
int nEntriesStar = 0, nEntriesNoStar = 0;
menuEntry_s** list = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*)); menuEntry_s** list = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*));
if(list == NULL) return; 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; menuEntry_s* p = m->firstEntry;
for(i = 0; i < nEntries; ++i) { for(i = 0; i < nEntries; ++i) {
if (p->starred) list[i] = p;
listStar[nEntriesStar++] = p;
else
list[nEntriesNoStar++] = p;
p = p->next; p = p->next;
} }
qsort(listStar, nEntriesStar, sizeof(menuEntry_s*), menuEntryCmp); qsort(list, nEntries, sizeof(menuEntry_s*), menuEntryCmp);
qsort(list, nEntriesNoStar, sizeof(menuEntry_s*), menuEntryCmp);
menuEntry_s** pp = &m->firstEntry; menuEntry_s** pp = &m->firstEntry;
for(i = 0; i < nEntriesStar; ++i) { for(i = 0; i < nEntries; ++i) {
*pp = listStar[i];
pp = &(*pp)->next;
}
for(i = 0; i < nEntriesNoStar; ++i) {
*pp = list[i]; *pp = list[i];
pp = &(*pp)->next; pp = &(*pp)->next;
} }
@ -133,33 +81,13 @@ static void menuSort(void) {
*pp = NULL; *pp = NULL;
free(list); free(list);
free(listStar);
}
void menuReorder (void) {
s_curMenu = !s_curMenu;
menuSort();
s_curMenu = !s_curMenu;
menuClear();
} }
int menuScan(const char* target) { int menuScan(const char* target) {
int pos;
char dirsep[8];
if (chdir(target) < 0) return 1; if (chdir(target) < 0) return 1;
if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL) if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL)
return 1; 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; DIR* dir;
struct dirent* dp; struct dirent* dp;
char tmp_path[PATH_MAX+1]; char tmp_path[PATH_MAX+1];
@ -176,12 +104,13 @@ int menuScan(const char* target) {
bool entrytype=0; bool entrytype=0;
memset(tmp_path, 0, sizeof(tmp_path)); memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s", s_menu[!s_curMenu].dirname, dirsep, dp->d_name); snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s", s_menu[!s_curMenu].dirname, dp->d_name);
#ifdef _DIRENT_HAVE_D_TYPE #ifdef SWITCH
if (dp->d_type == DT_UNKNOWN) fsdev_dir_t* dirSt = (fsdev_dir_t*)dir->dirData->dirStruct;
continue; FsDirectoryEntry* entry = &dirSt->entry_data[dirSt->index];
entrytype = dp->d_type != DT_REG;
entrytype = entry->type == ENTRYTYPE_DIR;
#else #else
struct stat tmpstat; struct stat tmpstat;
@ -198,21 +127,16 @@ int menuScan(const char* target) {
const char* ext = getExtension(dp->d_name); const char* ext = getExtension(dp->d_name);
if (strcasecmp(ext, ".nro")==0/* || (shortcut = strcasecmp(ext, ".xml")==0)*/) if (strcasecmp(ext, ".nro")==0/* || (shortcut = strcasecmp(ext, ".xml")==0)*/)
me = menuCreateEntry(ENTRY_TYPE_FILE); me = menuCreateEntry(ENTRY_TYPE_FILE);
if (!me)
me = menuCreateEntry(ENTRY_TYPE_FILE_OTHER);
} }
if (!me) if (!me)
continue; continue;
strncpy(me->path, tmp_path, sizeof(me->path)-1); strncpy(me->path, tmp_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0; if (menuEntryLoad(me, dp->d_name, shortcut))
if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me); menuAddEntry(me);
else else
menuDeleteEntry(me, 0); menuDeleteEntry(me);
} }
closedir(dir); closedir(dir);
@ -224,105 +148,3 @@ int menuScan(const char* target) {
return 0; return 0;
} }
int themeMenuScan(const char* target) {
menuClear();
if (chdir(target) < 0) return 1;
if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL)
return 1;
DIR* dir;
struct dirent* dp;
char tmp_path[PATH_MAX+1];
dir = opendir(s_menu[!s_curMenu].dirname);
if (!dir) return 2;
while ((dp = readdir(dir)))
{
menuEntry_s* me = NULL;
bool shortcut = false;
if (dp->d_name[0]=='.')
continue;
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 (entrytype || strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0 || strcasecmp(ext, ".zip")==0)
me = menuCreateEntry(ENTRY_TYPE_THEME);
if (!me)
continue;
strncpy(me->path, tmp_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0;
if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me);
else
menuDeleteEntry(me, 0);
}
closedir(dir);
menuSort();
menuEntry_s* me = menuCreateEntry(ENTRY_TYPE_THEME);
if(me) {
if(menuEntryLoad(me, textGetString(StrId_DefaultThemeName), false, false))//Create Default theme Menu Entry
menuAddEntryToFront(me);
else
menuDeleteEntry(me, 0);
}
// Swap the menu and clear the previous menu
s_curMenu = !s_curMenu;
menuClear();
return 0;
}
int menuFileassocScan(const char* target) {
menuFileassocClear();
if (chdir(target) < 0) return 1;
if (getcwd(s_menuFileassoc[!s_curMenuFileassoc].dirname, PATH_MAX+1) == NULL)
return 1;
DIR* dir;
struct dirent* dp;
char tmp_path[PATH_MAX+1];
dir = opendir(s_menuFileassoc[!s_curMenuFileassoc].dirname);
if (!dir) return 2;
while ((dp = readdir(dir)))
{
if (dp->d_name[0]=='.')
continue;
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menuFileassoc[!s_curMenuFileassoc].dirname, dp->d_name);
const char* ext = getExtension(dp->d_name);
if (strcasecmp(ext, ".cfg")!=0)
continue;
menuEntryFileassocLoad(tmp_path);
}
closedir(dir);
// Swap the menu and clear the previous menu
s_curMenuFileassoc = !s_curMenuFileassoc;
menuFileassocClear();
return 0;
}

View File

@ -1,189 +1,24 @@
#include <time.h> #include <time.h>
#include "common.h" #include "common.h"
#include "netloader.h"
#ifndef __SWITCH__ #include "invalid_icon_bin.h"
#include "switch/runtime/nxlink.h" #include "folder_icon_bin.h"
#endif
double menuTimer; void launchMenuEntryTask(menuEntry_s* arg)
{
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;
}
char *menuGetRootBasePath(void) {
return rootPathBase;
}
void launchMenuEntryTask(menuEntry_s* arg) {
menuEntry_s* me = arg; menuEntry_s* me = arg;
if (me->type == ENTRY_TYPE_FOLDER) if (me->type == ENTRY_TYPE_FOLDER)
menuScan(me->path); menuScan(me->path);
//changeDirTask(me->path); //changeDirTask(me->path);
else if(me->type == ENTRY_TYPE_THEME)
launchApplyThemeTask(me);
else else
launchMenuEntry(me); launchMenuEntry(me);
} }
void toggleStarState(menuEntry_s* arg) { typedef enum
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, IMAGE_MODE_RGB24,
HBMENU_NETLOADER_ACTIVE, IMAGE_MODE_RGBA32
HBMENU_NETLOADER_ERROR, } ImageMode;
HBMENU_NETLOADER_SUCCESS,
HBMENU_THEME_MENU,
} hbmenu_state = HBMENU_DEFAULT;
void launchMenuNetloaderTask() {
if(hbmenu_state == HBMENU_DEFAULT)
workerSchedule(netloaderTask, NULL);
}
void launchMenuBackTask() {
if(hbmenu_state == HBMENU_NETLOADER_ACTIVE) {
netloaderSignalExit();
}
else if(hbmenu_state == HBMENU_THEME_MENU) {
hbmenu_state = HBMENU_DEFAULT;
menuScan(rootPath);
}
else {
menuScan("..");
}
}
void menuHandleAButton(void) {
menu_s* menu = menuGetCurrent();
if (hbmenu_state != HBMENU_NETLOADER_ACTIVE && menuIsMsgBoxOpen()) {
menuCloseMsgBox();
}
else if (menu->nEntries > 0 && (hbmenu_state == HBMENU_DEFAULT || hbmenu_state == HBMENU_THEME_MENU))
{
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
launchMenuEntryTask(me);
//workerSchedule(launchMenuEntryTask, me);
}
}
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);
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) {
return hbmenu_state == HBMENU_NETLOADER_ACTIVE;
}
//Draws an RGB888 or RGBA8888 image. //Draws an RGB888 or RGBA8888 image.
static void drawImage(int x, int y, int width, int height, const uint8_t *image, ImageMode mode) { static void drawImage(int x, int y, int width, int height, const uint8_t *image, ImageMode mode) {
@ -210,38 +45,25 @@ static void drawImage(int x, int y, int width, int height, const uint8_t *image,
} }
} }
//Draws an RGBA8888 image masked by the passed color. uint8_t *folder_icon_small;
static void drawIcon(int x, int y, int width, int height, const uint8_t *image, color_t color) { uint8_t *invalid_icon_small;
int tmpx, tmpy; double timer;
int pos;
color_t current_color;
for (tmpy=0; tmpy<height; tmpy++) {
for (tmpx=0; tmpx<width; tmpx++) {
pos = ((tmpy*width) + tmpx) * 4;
current_color = MakeColor(color.r, color.g, color.b, image[pos+3]);
DrawPixel(x+tmpx, y+tmpy, current_color);
}
}
}
static void drawEntry(menuEntry_s* me, int off_x, int is_active) { static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
int x, y; int x, y;
int start_y = layoutobj->posStart[1];//*(n % 2); int start_y = 720 - 100 - 145;//*(n % 2);
int end_y = start_y + layoutobj->size[1]; int end_y = start_y + 140 + 32;
int start_x = off_x;//(n / 2); int start_x = off_x;//(n / 2);
int end_x = start_x + layoutobj->size[0]; int end_x = start_x + 140;
int j; int j;
const uint8_t *smallimg = NULL; const uint8_t *smallimg = NULL;
const uint8_t *largeimg = NULL; const uint8_t *largeimg = NULL;
char *strptr = NULL;
char tmpstr[1024]; char tmpstr[1024];
int border_start_x, border_end_x; int border_start_x, border_end_x;
int border_start_y, border_end_y; int border_start_y, border_end_y;
color_t border_color = themeCurrent.borderColor; color_t border_color = MakeColor(255, 255, 255, 255);
int shadow_start_y, shadow_y; int shadow_start_y, shadow_y;
int shadow_inset; int shadow_inset;
@ -251,8 +73,8 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
int shadow_size = 4; int shadow_size = 4;
if (is_active) { if (is_active) {
highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5); highlight_multiplier = fmax(0.0, fabs(fmod(timer, 1.0) - 0.5) / 0.5);
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_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_start_x = start_x-6; border_start_x = start_x-6;
border_end_x = end_x+6; border_end_x = end_x+6;
border_start_y = start_y-5; border_start_y = start_y-5;
@ -320,7 +142,7 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
for (y=start_y; y<end_y; y++) { for (y=start_y; y<end_y; y++) {
for (x=start_x; x<end_x; x+=4) { for (x=start_x; x<end_x; x+=4) {
Draw4PixelsRaw(x, y, themeCurrent.borderColor); Draw4PixelsRaw(x, y, MakeColor(255, 255, 255, 255));
} }
} }
@ -330,29 +152,23 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
} }
else if (me->type == ENTRY_TYPE_FOLDER) { else if (me->type == ENTRY_TYPE_FOLDER) {
smallimg = folder_icon_small; smallimg = folder_icon_small;
largeimg = folder_icon_large; largeimg = folder_icon_bin;
}
else if (me->type == ENTRY_TYPE_THEME){
smallimg = theme_icon_small;
largeimg = theme_icon_large;
} }
else { else {
smallimg = invalid_icon_small; smallimg = invalid_icon_small;
largeimg = invalid_icon_large; largeimg = invalid_icon_bin;
} }
if (smallimg) { if (smallimg) {
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon]; drawImage(start_x, start_y + 32, 140, 140, smallimg, IMAGE_MODE_RGB24);
drawImage(start_x + layoutobj->posStart[0], start_y + layoutobj->posStart[1], layoutobj->size[0], layoutobj->size[1], smallimg, IMAGE_MODE_RGB24);
} }
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]; if (is_active && largeimg) {
if (is_active && largeimg && layoutobj->visible) { drawImage(117, 100, 256, 256, largeimg, IMAGE_MODE_RGB24);
drawImage(layoutobj->posStart[0], layoutobj->posStart[1], layoutobj->size[0], layoutobj->size[1], largeimg, IMAGE_MODE_RGB24);
shadow_start_y = layoutobj->posStart[1]+layoutobj->size[1]; shadow_start_y = 100+256;
border_start_x = layoutobj->posStart[0]; border_start_x = 117;
border_end_x = layoutobj->posStart[0]+layoutobj->size[0]; border_end_x = 117+256;
for (shadow_y=shadow_start_y; shadow_y <shadow_start_y+shadow_size; shadow_y++) { for (shadow_y=shadow_start_y; shadow_y <shadow_start_y+shadow_size; shadow_y++) {
for (x=border_start_x; x<border_end_x; x++) { for (x=border_start_x; x<border_end_x; x++) {
@ -366,31 +182,21 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
} }
} }
if (me->type != ENTRY_TYPE_THEME) DrawTextTruncate(interuiregular14, start_x + 4, start_y + 4, MakeColor(64, 64, 64, 255), me->name, 140 - 32, "...");
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) { if (is_active) {
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryName]; start_x = 1280 - 790;
if (layoutobj->visible) DrawTextTruncate(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr, layoutobj->size[0], "..."); start_y = 135;
DrawTextTruncate(interuimedium30, start_x, start_y, themeCurrent.textColor, me->name, 1280 - start_x - 120 ,"...");
if (me->type != ENTRY_TYPE_FOLDER) { if (me->type != ENTRY_TYPE_FOLDER) {
memset(tmpstr, 0, sizeof(tmpstr)); memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Author), me->author); snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Author), me->author);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryAuthor]; DrawText(interuiregular14, start_x, start_y + 28 + 30, themeCurrent.textColor, tmpstr);
if (layoutobj->visible) DrawText(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
memset(tmpstr, 0, sizeof(tmpstr)); memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Version), me->version); snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Version), me->version);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion]; DrawText(interuiregular14, start_x, start_y + 28 + 30 + 18 + 6, themeCurrent.textColor, tmpstr);
if (layoutobj->visible) DrawText(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
} }
} }
} }
@ -403,8 +209,6 @@ void computeFrontGradient(color_t baseColor, int height) {
float dark_mult, dark_sub = 75; float dark_mult, dark_sub = 75;
color_t color; color_t color;
if (height < 0 || height > 720) return;
for (y=0; y<720; y++) { for (y=0; y<720; y++) {
alpha = y - (720 - height); alpha = y - (720 - height);
@ -419,69 +223,24 @@ void computeFrontGradient(color_t baseColor, int height) {
} }
} }
void menuStartupPath(void) { void menuStartup() {
char tmp_path[PATH_MAX+28]; const char *path;
#ifdef __SWITCH__ #ifdef SWITCH
strncpy(rootPathBase, "sdmc:", sizeof(rootPathBase)-1); path = "sdmc:/switch";
#else #else
getcwd(rootPathBase, sizeof(rootPathBase)); path = "switch";
#endif #endif
snprintf(rootPath, sizeof(rootPath)-1, "%s%s%s", rootPathBase, DIRECTORY_SEPARATOR, "switch");
struct stat st = {0}; menuScan(path);
if (stat(rootPath, &st) == -1) { folder_icon_small = downscaleIcon(folder_icon_bin);
mkdir(rootPath, 0755); invalid_icon_small = downscaleIcon(invalid_icon_bin);
} computeFrontGradient(themeCurrent.frontWaveColor, 280);
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/themes", rootPathBase);
if (stat(tmp_path, &st) == -1) {
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config", rootPathBase);
mkdir(tmp_path, 0755);
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu", rootPathBase);
mkdir(tmp_path, 0755);
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/themes", rootPathBase);
mkdir(tmp_path, 0755);
}
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/fileassoc", rootPathBase);
if (stat(tmp_path, &st) == -1) {
mkdir(tmp_path, 0755);
}
}
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);
menuStartupCommon();
}
void themeMenuStartup(void) {
if(hbmenu_state != HBMENU_DEFAULT) return;
hbmenu_state = HBMENU_THEME_MENU;
char tmp_path[PATH_MAX+25];
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) { color_t waveBlendAdd(color_t a, color_t b, float alpha) {
return MakeColor(a.r*(1.0f-alpha) + b.r*alpha, a.g*(1.0f-alpha) + b.g*alpha, a.b*(1.0f-alpha) + b.b*alpha, 255); return MakeColor(a.r+(b.r*alpha), a.g+b.g*alpha, a.b + b.b*alpha, 255);
} }
void drawWave(int id, float timer, color_t color, int height, float phase, float speed) { void drawWave(int id, float timer, color_t color, int height, float phase, float speed) {
@ -489,7 +248,6 @@ void drawWave(int id, float timer, color_t color, int height, float phase, float
float wave_top_y, alpha, one_minus_alpha; float wave_top_y, alpha, one_minus_alpha;
color_t existing_color, new_color; color_t existing_color, new_color;
if (height < 0 || height > 720) return;
height = 720 - height; height = 720 - height;
for (x=0; x<1280; x++) { for (x=0; x<1280; x++) {
@ -525,321 +283,107 @@ void drawWave(int id, float timer, color_t color, int height, float phase, float
} }
} }
void drawCharge() { void drawTime() {
char chargeString[5];
uint32_t batteryCharge;
bool isCharging;
bool validPower;
validPower = powerGetDetails(&batteryCharge, &isCharging); char timeString[9];
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); time_t unixTime = time(NULL);
struct tm* timeStruct = localtime((const time_t *)&unixTime); struct tm* timeStruct = gmtime((const time_t *)&unixTime);
int hours = timeStruct->tm_hour; int hours = timeStruct->tm_hour;
int minutes = timeStruct->tm_min; int minutes = timeStruct->tm_min;
int seconds = timeStruct->tm_sec; int seconds = timeStruct->tm_sec;
snprintf(tmpstr, sizeof(tmpstr)-1, "%02d:%02d:%02d", hours, minutes, seconds); sprintf(timeString, "%02d:%02d:%02d", hours, minutes, seconds);
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_Status]; DrawText(interuimedium20, 1280 - (9 * 16) - 30, 30, MakeColor(255, 255, 255, 255), 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 drawButtons(menu_s* menu, bool emptyDir, int *out_basePos) { void drawBackBtn(menu_s* menu, bool emptyDir) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA]; int x_image = 1280 - 252 - 30 - 32;
int basePos[2]={0}; int x_text = 1280 - 216 - 30 - 32;
basePos[0] = layoutobj->posStart[0]; if(emptyDir) {
basePos[1] = layoutobj->posStart[1]; x_image = 1280 - 126 - 30 - 32;
x_text = 1280 - 90 - 30 - 32;
}
#ifdef __SWITCH__ #ifdef SWITCH
if (strcmp( menu->dirname, "sdmc:/") != 0) if (strcmp( menu->dirname, "sdmc:/") != 0)
#else #else
if (strcmp( menu->dirname, "/") != 0) if (strcmp( menu->dirname, "/") != 0)
#endif #endif
{ {
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonBText]; drawImage(x_image, 720 - 48, 32, 32, themeCurrent.buttonBImage, IMAGE_MODE_RGBA32);
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonBText, basePos[0], basePos[1], !emptyDir ? layoutobj->posStart : layoutobj->posEnd, basePos, themeCurrent.textColor, textGetString(StrId_Actions_Back), 'l'); DrawText(interuiregular18, x_text, 720 - 47, themeCurrent.textColor, textGetString(StrId_Actions_Back));
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonB];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonB, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonBText, 'l');
} }
if(hbmenu_state == HBMENU_DEFAULT)
{
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) { void menuLoop() {
bool enable_progress = 0;
float progress = 0;
char netloader_displaytext[260];
char textbody[256];
memset(netloader_displaytext, 0, sizeof(netloader_displaytext));
memset(textbody, 0, sizeof(textbody));
u32 ip = gethostid();
if (ip == INADDR_LOOPBACK)
snprintf(textbody, sizeof(textbody)-1, "%s", textGetString(StrId_NetLoaderOffline));
else {
if (!netloader_state->sock_connected)
snprintf(textbody, sizeof(textbody)-1, textGetString(StrId_NetLoaderActive), ip&0xFF, (ip>>8)&0xFF, (ip>>16)&0xFF, (ip>>24)&0xFF, NXLINK_SERVER_PORT);
else {
enable_progress = 1;
progress = (float)netloader_state->filetotal / netloader_state->filelen;
snprintf(textbody, sizeof(textbody)-1, textGetString(StrId_NetLoaderTransferring), netloader_state->filetotal/1024, netloader_state->filelen/1024);
}
}
snprintf(netloader_displaytext, sizeof(netloader_displaytext)-1, "%s\n\n\n%s", textGetString(StrId_NetLoader), textbody);
menuMsgBoxSetNetloaderState(1, netloader_displaytext, enable_progress, progress);
}
void menuLoop(void) {
menuEntry_s* me; menuEntry_s* me;
menu_s* menu = NULL; menu_s* menu = menuGetCurrent();
int i; int i;
int x, y, endy = 720; int x, y;
int curPos[2]={0};
netloaderState netloader_state;
ThemeLayoutObject *layoutobj = NULL;
for (i=0; i<3; i++) { for (y=0; y<450; y++) {
if (i==2) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackWave];
if (i==1) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave];
if (i==0) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
if (layoutobj->visible && layoutobj->size[1] >= 0 && layoutobj->size[1] <= 720-10) {
endy = 720 - layoutobj->size[1] + 10;
break;
}
}
for (y=0; y<endy; y++) {
for (x=0; x<1280; x+=4) {// don't draw bottom pixels as they are covered by the waves for (x=0; x<1280; x+=4) {// don't draw bottom pixels as they are covered by the waves
Draw4PixelsRaw(x, y, themeCurrent.backgroundColor); Draw4PixelsRaw(x, y, themeCurrent.backgroundColor);
} }
} }
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage]; drawWave(0, timer, themeCurrent.backWaveColor, 295, 0.0, 3.0);
assetsDataEntry *data = NULL; drawWave(1, timer, themeCurrent.middleWaveColor, 290, 2.0, 3.5);
assetsGetData(AssetId_background_image, &data); drawWave(2, timer, themeCurrent.frontWaveColor, 280, 4.0, -2.5);
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); timer += 0.05;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackWave]; drawImage(40, 20, 140, 60, themeCurrent.hbmenuLogoImage, IMAGE_MODE_RGBA32);
if (layoutobj->visible) drawWave(0, menuTimer, themeCurrent.backWaveColor, layoutobj->size[1], 0.0, 3.0); DrawText(interuiregular14, 180, 46, themeCurrent.textColor, VERSION);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave]; DrawTextTruncate(interuiregular18, 40, 720 - 47, themeCurrent.textColor, menu->dirname, 918, "...");
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;
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. #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; extern u64 g_tickdiff_frame;
char tmpstr[64]; char tmpstr[64];
snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_vsync);
DrawText(interuiregular14, 180 + 256, 46, themeCurrent.textColor, tmpstr);
snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_frame); snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_frame);
DrawTextFromLayout(ThemeLayoutId_LogInfo, themeCurrent.textColor, tmpstr); DrawText(interuiregular14, 180 + 256, 46 + 16, themeCurrent.textColor, tmpstr);
#endif #endif
memset(&netloader_state, 0, sizeof(netloader_state)); //drawTime();
netloaderGetState(&netloader_state);
if(hbmenu_state == HBMENU_DEFAULT && netloader_state.activated) { if (menu->nEntries==0)
hbmenu_state = HBMENU_NETLOADER_ACTIVE;
menuCloseMsgBox();
menuCreateMsgBox(780,300, "");
} else if(hbmenu_state == HBMENU_NETLOADER_ACTIVE && !netloader_state.activated && !netloader_state.launch_app) {
hbmenu_state = HBMENU_DEFAULT;
menuScan(".");//Reload the menu since netloader may have deleted the NRO if the transfer aborted.
menuCloseMsgBox();
menuMsgBoxSetNetloaderState(0, NULL, 0, 0);
}
if (netloader_state.errormsg[0]) {
menuCloseMsgBox();
menuCreateMsgBox(780,300, netloader_state.errormsg);
}
if(hbmenu_state == HBMENU_NETLOADER_ACTIVE) {
menuUpdateNetloader(&netloader_state);
}
menu = menuGetCurrent();
if (menu->nEntries==0 || hbmenu_state == HBMENU_NETLOADER_ACTIVE)
{ {
if (hbmenu_state == HBMENU_NETLOADER_ACTIVE) { DrawText(interuiregular14, 64, 128, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg));
if (netloader_state.launch_app) { drawBackBtn(menu, true);
hbmenu_state = HBMENU_DEFAULT;
menuCloseMsgBox();
menuMsgBoxSetNetloaderState(0, NULL, 0, 0);
menuCreateMsgBox(240,240, textGetString(StrId_Loading));
launchMenuEntryTask(netloader_state.me);
}
} else {
DrawTextFromLayout(ThemeLayoutId_InfoMsg, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg));
}
drawButtons(menu, true, curPos);
} }
else else
{ {
static int x = 0;
static int v = 0; static int v = 0;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
// Gentle Realign only when not manually moving if (menu->nEntries > 7) {
if (menu->slideSpeed == 0) { int wanted_x = clamp(-menu->curEntry * (140 + 30), -(menu->nEntries - 7) * (140 + 30), 0);
if (menu->nEntries > entries_count) { x += v;
int wanted_x = clamp(-menu->curEntry * layoutobj->posEnd[0], -(menu->nEntries - entries_count) * layoutobj->posEnd[0], 0); v += (wanted_x - x) / 3;
menu->xPos += v; v /= 2;
v += (wanted_x - menu->xPos) / 3;
v /= 2;
}
else {
menu->xPos = v = 0;
}
} }
else { else {
menu->xPos += menu->slideSpeed; x = v = 0;
if (abs(menu->slideSpeed) > 2) {
// Slow down way faster when outside the normal bounds
if (menu->xPos > 0 || menu->xPos < -(menu->nEntries) * layoutobj->posEnd[0]) {
menu->slideSpeed *= .5f;
}
else {
menu->slideSpeed *= .9f;
}
}
else {
menu->slideSpeed = 0;
}
menu->curEntry = clamp(roundf(-((float) menu->xPos / layoutobj->posEnd[0])), 0, menu->nEntries);
} }
menuEntry_s *active_entry = NULL; menuEntry_s *active_entry = NULL;
// Draw menu entries // Draw menu entries
for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) { for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) {
int entry_start_x = layoutobj->posStart[0] + i * layoutobj->posEnd[0]; int entry_start_x = 29 + i * (140 + 30);
int entry_draw_x = entry_start_x + menu->xPos;
int screen_width = 1280; int screen_width = 1280;
if (entry_start_x >= (screen_width - menu->xPos)) if (entry_start_x >= (screen_width - x))
break; break;
int is_active = i==menu->curEntry; int is_active = i==menu->curEntry;
@ -847,61 +391,20 @@ void menuLoop(void) {
if (is_active) if (is_active)
active_entry = me; active_entry = me;
if (!is_active && entry_draw_x < -(layoutobj->posStart[0] + layoutobj->posEnd[0])) drawEntry(me, entry_start_x + x, is_active);
continue;
drawEntry(me, entry_draw_x, is_active);
}
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuTypeMsg];
int getX=0;
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 != NULL) {
const char *buttonstr = ""; if (active_entry->type != ENTRY_TYPE_FOLDER) {
drawImage(1280 - 126 - 30 - 32, 720 - 48, 32, 32, themeCurrent.buttonAImage, IMAGE_MODE_RGBA32);
if (active_entry->type == ENTRY_TYPE_THEME) DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47, themeCurrent.textColor, textGetString(StrId_Actions_Launch));
buttonstr = textGetString(StrId_Actions_Apply); }
else if (active_entry->type != ENTRY_TYPE_FOLDER) else {
buttonstr = textGetString(StrId_Actions_Launch); drawImage(1280 - 126 - 30 - 32, 720 - 48, 32, 32, themeCurrent.buttonAImage, IMAGE_MODE_RGBA32);
else DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47, themeCurrent.textColor, textGetString(StrId_Actions_Open));
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');
} }
drawBackBtn(menu, false);
} }
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();
} }

View File

@ -1,15 +1,5 @@
#pragma once #pragma once
#ifndef _WIN32
#include <arpa/inet.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#undef DrawText
#undef MessageBox
#endif
#define ENTRY_NAMELENGTH 0x200 #define ENTRY_NAMELENGTH 0x200
#define ENTRY_AUTHORLENGTH 0x100 #define ENTRY_AUTHORLENGTH 0x100
#define ENTRY_VERLENGTH 0x10 #define ENTRY_VERLENGTH 0x10
@ -19,9 +9,6 @@ typedef enum
{ {
ENTRY_TYPE_FILE, ENTRY_TYPE_FILE,
ENTRY_TYPE_FOLDER, ENTRY_TYPE_FOLDER,
ENTRY_TYPE_THEME,
ENTRY_TYPE_FILEASSOC,
ENTRY_TYPE_FILE_OTHER,
} MenuEntryType; } MenuEntryType;
typedef struct menuEntry_s_tag menuEntry_s; typedef struct menuEntry_s_tag menuEntry_s;
@ -32,8 +19,6 @@ struct menu_s_tag
menuEntry_s *firstEntry, *lastEntry; menuEntry_s *firstEntry, *lastEntry;
int nEntries; int nEntries;
int curEntry; int curEntry;
int xPos;
int slideSpeed;
char dirname[PATH_MAX+1]; char dirname[PATH_MAX+1];
}; };
@ -42,7 +27,6 @@ typedef struct
{ {
char* dst; char* dst;
uint32_t buf[ENTRY_ARGBUFSIZE/sizeof(uint32_t)]; uint32_t buf[ENTRY_ARGBUFSIZE/sizeof(uint32_t)];
struct in_addr nxlink_host;
} argData_s; } argData_s;
struct menuEntry_s_tag struct menuEntry_s_tag
@ -51,13 +35,9 @@ struct menuEntry_s_tag
menuEntry_s* next; menuEntry_s* next;
MenuEntryType type; MenuEntryType type;
char path[PATH_MAX+8]; char path[PATH_MAX+1];
char starpath[PATH_MAX+8];
argData_s args; argData_s args;
bool fileassoc_type;//0=file_extension, 1 = filename
char fileassoc_str[PATH_MAX+1];//file_extension/filename
char name[ENTRY_NAMELENGTH+1]; char name[ENTRY_NAMELENGTH+1];
char author[ENTRY_AUTHORLENGTH+1]; char author[ENTRY_AUTHORLENGTH+1];
char version[ENTRY_VERLENGTH+1]; char version[ENTRY_VERLENGTH+1];
@ -67,61 +47,19 @@ struct menuEntry_s_tag
uint8_t *icon_gfx; uint8_t *icon_gfx;
uint8_t *icon_gfx_small; uint8_t *icon_gfx_small;
bool starred;
NacpStruct *nacp; NacpStruct *nacp;
}; };
typedef enum
{
IMAGE_MODE_RGB24,
IMAGE_MODE_RGBA32
} ImageMode;
extern double menuTimer;
#ifdef __cplusplus
extern "C" {
#endif
void menuEntryInit(menuEntry_s* me, MenuEntryType type); void menuEntryInit(menuEntry_s* me, MenuEntryType type);
void menuEntryFree(menuEntry_s* me, bool skip_icongfx); void menuEntryFree(menuEntry_s* me);
bool fileExists(const char* path); bool fileExists(const char* path);
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists); bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut);
void menuEntryParseIcon(menuEntry_s* me); void menuEntryParseIcon(menuEntry_s* me);
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode); uint8_t *downscaleIcon(const uint8_t *image);
void menuEntryParseNacp(menuEntry_s* me); void menuEntryParseNacp(menuEntry_s* me);
void menuEntryFileassocLoad(const char* filepath);
menuEntry_s* menuCreateEntry(MenuEntryType type);
void menuFileassocAddEntry(menuEntry_s* me);
void menuDeleteEntry(menuEntry_s* me, bool skip_icongfx);
menu_s* menuGetCurrent(void); menu_s* menuGetCurrent(void);
menu_s* menuFileassocGetCurrent(void);
void menuReorder (void);
int menuScan(const char* target); 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();
char *menuGetRootPath(void);
char *menuGetRootBasePath(void);
void menuHandleAButton(void);
void menuHandleXButton(void);
bool menuIsNetloaderActive(void);
#ifdef __cplusplus
}
#endif
static inline char* getExtension(const char* str) static inline char* getExtension(const char* str)
{ {

View File

@ -1,219 +0,0 @@
#include "common.h"
#include "message-box.h"
MessageBox currMsgBox;
static bool msgboxNetloaderEnabled;
static char msgboxNetloaderText[256];
static bool msgboxNetloaderProgressEnabled;
static float msgboxNetloaderProgress;
void drawMsgBoxBgToBuff(color_t *buff, int width, int height) {
int x, y;
int off;
int circle_center_x, circle_center_y;
int corner_size = 0;
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++) {
if (corner_size > 0) {
if (x<corner_size && y<corner_size) { // top left corner
circle_center_x = corner_size-1;
circle_center_y = corner_size-1;
}
else if (x>width-corner_size && y<corner_size) { // top right corner
circle_center_x = width-corner_size;
circle_center_y = corner_size-1;
}
else if (x<corner_size && y>height-corner_size) { // bottom left corner
circle_center_x = corner_size-1;
circle_center_y = height-corner_size;
}
else if (x>width-corner_size && y>height-corner_size) { // bottom right corner
circle_center_x = width-corner_size;
circle_center_y = height-corner_size;
}
else {
circle_center_x = -1;
circle_center_y = -1;
}
if (circle_center_x == -1 && circle_center_y == -1) {
color = base_color;
}
else {
rad = sqrt(pow(circle_center_x - x, 2) + pow(circle_center_y - y, 2));
alpha = (float)corner_size - rad;
if (rad < corner_size) {
if (alpha < 1.0) {
color = MakeColor(base_color.r, base_color.g, base_color.b, base_color.a * alpha);
}
else
color = base_color;
}
else
color = MakeColor(0, 0, 0, 0);
}
}
else
color = base_color;
if (y == height + layoutobj->posStart[1]) {
color = themeCurrent.separatorColor;
}
off = (y * width + x);
*((uint32_t *)&buff[off]) = color.r | (color.g<<8) | (color.b<<16) | (color.a<<24);
}
}
}
void menuDrawMsgBox() {
if (!menuIsMsgBoxOpen())
return;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
int off;
int x, y;
int start_x = 1280 / 2 - currMsgBox.width / 2;
int start_y = 720 / 2 - currMsgBox.height / 2;
int end_x = start_x + currMsgBox.width;
uint32_t text_width, text_height;
color_t curr_color;
color_t border_color;
int sep_start_y = currMsgBox.height + layoutobj->posStart[1];
int border_thickness = 6;
int shadow_start_y, shadow_y;
int shadow_inset;
int shadow_size = 4;
float highlight_multiplier = highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5);
color_t shadow_color;
uint8_t shadow_alpha_base = 80;
const char* textptr = currMsgBox.text;
int progress_width = (int)(msgboxNetloaderProgress*currMsgBox.width);
char progress_text[32];
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);
// Darken the background
for (y=0; y<720; y++) {
for (x=0; x<1280; x++) {
DrawPixel(x, y, MakeColor(0, 0, 0, 100));
}
}
// Draw the message box background
for (y=0; y<currMsgBox.height; y++) {
for (x=0; x<currMsgBox.width; x++) {
off = (y * currMsgBox.width + x);
curr_color = currMsgBox.bg[off];
if (!msgboxNetloaderEnabled) {
if (((x<border_thickness || x>=currMsgBox.width-border_thickness) && y>sep_start_y) ||
(y>sep_start_y && y<=sep_start_y+border_thickness) || (y>=currMsgBox.height-border_thickness)) {
curr_color = border_color;
}
}
else if (msgboxNetloaderProgressEnabled && y > sep_start_y && x < progress_width) {
curr_color = themeCurrent.progressBarColor;
}
DrawPixel(start_x+x, start_y+y, curr_color);
}
}
if (msgboxNetloaderEnabled) textptr = msgboxNetloaderText;
GetTextDimensions(interuiregular18, textptr, &text_width, &text_height);
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 + (sep_start_y - text_height) / 2, themeCurrent.textColor, textptr);
}
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');
DrawText(interuimedium20, x, y, themeCurrent.textColor, textGetString(StrId_MsgBox_OK));
}
if (msgboxNetloaderEnabled && msgboxNetloaderProgressEnabled) {
memset(progress_text, 0, sizeof(progress_text));
snprintf(progress_text, sizeof(progress_text)-1, "%.02f%%", msgboxNetloaderProgress*100);
x = GetTextXCoordinate(interuiregular18, start_x + (currMsgBox.width / 2), progress_text, 'c');
DrawText(interuiregular18, x, y, themeCurrent.textColor, progress_text);
}
shadow_start_y = start_y + currMsgBox.height;
for (shadow_y=shadow_start_y; shadow_y <shadow_start_y+shadow_size; shadow_y++) {
for (x=start_x; x<end_x; x++) {
shadow_color = MakeColor(0, 0, 0, shadow_alpha_base * (1.0 - (float)(shadow_y - shadow_start_y) / ((float)shadow_size)));
shadow_inset =(shadow_y-shadow_start_y);
if (x >= start_x + shadow_inset && x <= end_x - shadow_inset) {
DrawPixel(x, shadow_y, shadow_color);
}
}
}
}
void menuCreateMsgBox(int width, int height, const char *text) {
if (menuIsMsgBoxOpen())
return;
char *new_text = strdup(text);
if (new_text==NULL)
return;
currMsgBox = (MessageBox) { width, height, NULL, new_text };
currMsgBox.bg = malloc(currMsgBox.width*currMsgBox.height*4);
if (currMsgBox.bg) {
drawMsgBoxBgToBuff(currMsgBox.bg, currMsgBox.width, currMsgBox.height);
}
}
bool menuIsMsgBoxOpen() {
return currMsgBox.width != 0 || currMsgBox.height != 0 || currMsgBox.bg || currMsgBox.text;
}
void menuCloseMsgBox() {
if (currMsgBox.bg) {
free(currMsgBox.bg);
currMsgBox.bg = NULL;
}
currMsgBox.width = currMsgBox.height = 0;
if (currMsgBox.text) {
free(currMsgBox.text);
currMsgBox.text = NULL;
}
}
MessageBox menuGetCurrentMsgBox() {
return currMsgBox;
}
void menuMsgBoxSetNetloaderState(bool enabled, const char *text, bool enable_progress, float progress) {
msgboxNetloaderEnabled = enabled;
memset(msgboxNetloaderText, 0, sizeof(msgboxNetloaderText));
if (text) strncpy(msgboxNetloaderText, text, sizeof(msgboxNetloaderText)-1);
msgboxNetloaderProgressEnabled = enable_progress;
msgboxNetloaderProgress = progress;
}

View File

@ -1,16 +0,0 @@
#pragma once
typedef struct
{
uint32_t width;
uint32_t height;
color_t *bg;
char *text;
} MessageBox;
void menuCreateMsgBox(int width, int height, const char *text);
void menuCloseMsgBox();
bool menuIsMsgBoxOpen();
void menuDrawMsgBox(void);
MessageBox menuGetCurrentMsgBox();
void menuMsgBoxSetNetloaderState(bool enabled, const char *text, bool enable_progress, float progress);

41
common/nacp.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
typedef struct {
char name[0x200];
char author[0x100];
} NacpLanguageEntry;
typedef struct {
NacpLanguageEntry lang[12];
NacpLanguageEntry lang_unk[4];//?
u8 x3000_unk[0x24];////Normally all-zero?
u32 x3024_unk;
u32 x3028_unk;
u32 x302C_unk;
u32 x3030_unk;
u32 x3034_unk;
u64 titleid0;
u8 x3040_unk[0x20];
char version[0x10];
u64 titleid_dlcbase;
u64 titleid1;
u32 x3080_unk;
u32 x3084_unk;
u32 x3088_unk;
u8 x308C_unk[0x24];//zeros?
u64 titleid2;
u64 titleids[7];//"Array of application titleIDs, normally the same as the above app-titleIDs. Only set for game-updates?"
u32 x30F0_unk;
u32 x30F4_unk;
u64 titleid3;//"Application titleID. Only set for game-updates?"
char bcat_passphrase[0x40];
u8 x3140_unk[0xEC0];//Normally all-zero?
} NacpStruct;

916
common/nanojpeg.c Normal file
View File

@ -0,0 +1,916 @@
// NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder
// version 1.3.5 (2016-11-14)
// Copyright (c) 2009-2016 Martin J. Fiedler <martin.fiedler@gmx.net>
// published under the terms of the MIT license
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
///////////////////////////////////////////////////////////////////////////////
// DOCUMENTATION SECTION //
// read this if you want to know what this is all about //
///////////////////////////////////////////////////////////////////////////////
// INTRODUCTION
// ============
//
// This is a minimal decoder for baseline JPEG images. It accepts memory dumps
// of JPEG files as input and generates either 8-bit grayscale or packed 24-bit
// RGB images as output. It does not parse JFIF or Exif headers; all JPEG files
// are assumed to be either grayscale or YCbCr. CMYK or other color spaces are
// not supported. All YCbCr subsampling schemes with power-of-two ratios are
// supported, as are restart intervals. Progressive or lossless JPEG is not
// supported.
// Summed up, NanoJPEG should be able to decode all images from digital cameras
// and most common forms of other non-progressive JPEG images.
// The decoder is not optimized for speed, it's optimized for simplicity and
// small code. Image quality should be at a reasonable level. A bicubic chroma
// upsampling filter ensures that subsampled YCbCr images are rendered in
// decent quality. The decoder is not meant to deal with broken JPEG files in
// a graceful manner; if anything is wrong with the bitstream, decoding will
// simply fail.
// The code should work with every modern C compiler without problems and
// should not emit any warnings. It uses only (at least) 32-bit integer
// arithmetic and is supposed to be endianness independent and 64-bit clean.
// However, it is not thread-safe.
// COMPILE-TIME CONFIGURATION
// ==========================
//
// The following aspects of NanoJPEG can be controlled with preprocessor
// defines:
//
// _NJ_EXAMPLE_PROGRAM = Compile a main() function with an example
// program.
// _NJ_INCLUDE_HEADER_ONLY = Don't compile anything, just act as a header
// file for NanoJPEG. Example:
// #define _NJ_INCLUDE_HEADER_ONLY
// #include "nanojpeg.c"
// int main(void) {
// njInit();
// // your code here
// njDone();
// }
// NJ_USE_LIBC=1 = Use the malloc(), free(), memset() and memcpy()
// functions from the standard C library (default).
// NJ_USE_LIBC=0 = Don't use the standard C library. In this mode,
// external functions njAlloc(), njFreeMem(),
// njFillMem() and njCopyMem() need to be defined
// and implemented somewhere.
// NJ_USE_WIN32=0 = Normal mode (default).
// NJ_USE_WIN32=1 = If compiling with MSVC for Win32 and
// NJ_USE_LIBC=0, NanoJPEG will use its own
// implementations of the required C library
// functions (default if compiling with MSVC and
// NJ_USE_LIBC=0).
// NJ_CHROMA_FILTER=1 = Use the bicubic chroma upsampling filter
// (default).
// NJ_CHROMA_FILTER=0 = Use simple pixel repetition for chroma upsampling
// (bad quality, but faster and less code).
// API
// ===
//
// For API documentation, read the "header section" below.
// EXAMPLE
// =======
//
// A few pages below, you can find an example program that uses NanoJPEG to
// convert JPEG files into PGM or PPM. To compile it, use something like
// gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c
// You may also add -std=c99 -Wall -Wextra -pedantic -Werror, if you want :)
// The only thing you might need is -Wno-shift-negative-value, because this
// code relies on the target machine using two's complement arithmetic, but
// the C standard does not, even though *any* practically useful machine
// nowadays uses two's complement.
///////////////////////////////////////////////////////////////////////////////
// HEADER SECTION //
// copy and pase this into nanojpeg.h if you want //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NANOJPEG_H
#define _NANOJPEG_H
// nj_result_t: Result codes for njDecode().
typedef enum _nj_result {
NJ_OK = 0, // no error, decoding successful
NJ_NO_JPEG, // not a JPEG file
NJ_UNSUPPORTED, // unsupported format
NJ_OUT_OF_MEM, // out of memory
NJ_INTERNAL_ERR, // internal error
NJ_SYNTAX_ERROR, // syntax error
__NJ_FINISHED, // used internally, will never be reported
} nj_result_t;
// njInit: Initialize NanoJPEG.
// For safety reasons, this should be called at least one time before using
// using any of the other NanoJPEG functions.
void njInit(void);
// njDecode: Decode a JPEG image.
// Decodes a memory dump of a JPEG file into internal buffers.
// Parameters:
// jpeg = The pointer to the memory dump.
// size = The size of the JPEG file.
// Return value: The error code in case of failure, or NJ_OK (zero) on success.
nj_result_t njDecode(const void* jpeg, const int size);
// njGetWidth: Return the width (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetWidth() is undefined.
int njGetWidth(void);
// njGetHeight: Return the height (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetHeight() is undefined.
int njGetHeight(void);
// njIsColor: Return 1 if the most recently decoded image is a color image
// (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result
// of njGetWidth() is undefined.
int njIsColor(void);
// njGetImage: Returns the decoded image data.
// Returns a pointer to the most recently image. The memory layout it byte-
// oriented, top-down, without any padding between lines. Pixels of color
// images will be stored as three consecutive bytes for the red, green and
// blue channels. This data format is thus compatible with the PGM or PPM
// file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8.
// If njDecode() failed, the result of njGetImage() is undefined.
unsigned char* njGetImage(void);
// njGetImageSize: Returns the size (in bytes) of the image data returned
// by njGetImage(). If njDecode() failed, the result of njGetImageSize() is
// undefined.
int njGetImageSize(void);
// njDone: Uninitialize NanoJPEG.
// Resets NanoJPEG's internal state and frees all memory that has been
// allocated at run-time by NanoJPEG. It is still possible to decode another
// image after a njDone() call.
void njDone(void);
#endif//_NANOJPEG_H
///////////////////////////////////////////////////////////////////////////////
// CONFIGURATION SECTION //
// adjust the default settings for the NJ_ defines here //
///////////////////////////////////////////////////////////////////////////////
#ifndef NJ_USE_LIBC
#define NJ_USE_LIBC 1
#endif
#ifndef NJ_USE_WIN32
#ifdef _MSC_VER
#define NJ_USE_WIN32 (!NJ_USE_LIBC)
#else
#define NJ_USE_WIN32 0
#endif
#endif
#ifndef NJ_CHROMA_FILTER
#define NJ_CHROMA_FILTER 1
#endif
///////////////////////////////////////////////////////////////////////////////
// EXAMPLE PROGRAM //
// just define _NJ_EXAMPLE_PROGRAM to compile this (requires NJ_USE_LIBC) //
///////////////////////////////////////////////////////////////////////////////
#ifdef _NJ_EXAMPLE_PROGRAM
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
int size;
char *buf;
FILE *f;
if (argc < 2) {
printf("Usage: %s <input.jpg> [<output.ppm>]\n", argv[0]);
return 2;
}
f = fopen(argv[1], "rb");
if (!f) {
printf("Error opening the input file.\n");
return 1;
}
fseek(f, 0, SEEK_END);
size = (int) ftell(f);
buf = (char*) malloc(size);
fseek(f, 0, SEEK_SET);
size = (int) fread(buf, 1, size, f);
fclose(f);
njInit();
if (njDecode(buf, size)) {
free((void*)buf);
printf("Error decoding the input file.\n");
return 1;
}
free((void*)buf);
f = fopen((argc > 2) ? argv[2] : (njIsColor() ? "nanojpeg_out.ppm" : "nanojpeg_out.pgm"), "wb");
if (!f) {
printf("Error opening the output file.\n");
return 1;
}
fprintf(f, "P%d\n%d %d\n255\n", njIsColor() ? 6 : 5, njGetWidth(), njGetHeight());
fwrite(njGetImage(), 1, njGetImageSize(), f);
fclose(f);
njDone();
return 0;
}
#endif
///////////////////////////////////////////////////////////////////////////////
// IMPLEMENTATION SECTION //
// you may stop reading here //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NJ_INCLUDE_HEADER_ONLY
#ifdef _MSC_VER
#define NJ_INLINE static __inline
#define NJ_FORCE_INLINE static __forceinline
#else
#define NJ_INLINE static inline
#define NJ_FORCE_INLINE static inline
#endif
#if NJ_USE_LIBC
#include <stdlib.h>
#include <string.h>
#define njAllocMem malloc
#define njFreeMem free
#define njFillMem memset
#define njCopyMem memcpy
#elif NJ_USE_WIN32
#include <windows.h>
#define njAllocMem(size) ((void*) LocalAlloc(LMEM_FIXED, (SIZE_T)(size)))
#define njFreeMem(block) ((void) LocalFree((HLOCAL) block))
NJ_INLINE void njFillMem(void* block, unsigned char value, int count) { __asm {
mov edi, block
mov al, value
mov ecx, count
rep stosb
} }
NJ_INLINE void njCopyMem(void* dest, const void* src, int count) { __asm {
mov edi, dest
mov esi, src
mov ecx, count
rep movsb
} }
#else
extern void* njAllocMem(int size);
extern void njFreeMem(void* block);
extern void njFillMem(void* block, unsigned char byte, int size);
extern void njCopyMem(void* dest, const void* src, int size);
#endif
typedef struct _nj_code {
unsigned char bits, code;
} nj_vlc_code_t;
typedef struct _nj_cmp {
int cid;
int ssx, ssy;
int width, height;
int stride;
int qtsel;
int actabsel, dctabsel;
int dcpred;
unsigned char *pixels;
} nj_component_t;
typedef struct _nj_ctx {
nj_result_t error;
const unsigned char *pos;
int size;
int length;
int width, height;
int mbwidth, mbheight;
int mbsizex, mbsizey;
int ncomp;
nj_component_t comp[3];
int qtused, qtavail;
unsigned char qtab[4][64];
nj_vlc_code_t vlctab[4][65536];
int buf, bufbits;
int block[64];
int rstinterval;
unsigned char *rgb;
} nj_context_t;
static nj_context_t nj;
static const char njZZ[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45,
38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 };
NJ_FORCE_INLINE unsigned char njClip(const int x) {
return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : (unsigned char) x);
}
#define W1 2841
#define W2 2676
#define W3 2408
#define W5 1609
#define W6 1108
#define W7 565
NJ_INLINE void njRowIDCT(int* blk) {
int x0, x1, x2, x3, x4, x5, x6, x7, x8;
if (!((x1 = blk[4] << 11)
| (x2 = blk[6])
| (x3 = blk[2])
| (x4 = blk[1])
| (x5 = blk[7])
| (x6 = blk[5])
| (x7 = blk[3])))
{
blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3;
return;
}
x0 = (blk[0] << 11) + 128;
x8 = W7 * (x4 + x5);
x4 = x8 + (W1 - W7) * x4;
x5 = x8 - (W1 + W7) * x5;
x8 = W3 * (x6 + x7);
x6 = x8 - (W3 - W5) * x6;
x7 = x8 - (W3 + W5) * x7;
x8 = x0 + x1;
x0 -= x1;
x1 = W6 * (x3 + x2);
x2 = x1 - (W2 + W6) * x2;
x3 = x1 + (W2 - W6) * x3;
x1 = x4 + x6;
x4 -= x6;
x6 = x5 + x7;
x5 -= x7;
x7 = x8 + x3;
x8 -= x3;
x3 = x0 + x2;
x0 -= x2;
x2 = (181 * (x4 + x5) + 128) >> 8;
x4 = (181 * (x4 - x5) + 128) >> 8;
blk[0] = (x7 + x1) >> 8;
blk[1] = (x3 + x2) >> 8;
blk[2] = (x0 + x4) >> 8;
blk[3] = (x8 + x6) >> 8;
blk[4] = (x8 - x6) >> 8;
blk[5] = (x0 - x4) >> 8;
blk[6] = (x3 - x2) >> 8;
blk[7] = (x7 - x1) >> 8;
}
NJ_INLINE void njColIDCT(const int* blk, unsigned char *out, int stride) {
int x0, x1, x2, x3, x4, x5, x6, x7, x8;
if (!((x1 = blk[8*4] << 8)
| (x2 = blk[8*6])
| (x3 = blk[8*2])
| (x4 = blk[8*1])
| (x5 = blk[8*7])
| (x6 = blk[8*5])
| (x7 = blk[8*3])))
{
x1 = njClip(((blk[0] + 32) >> 6) + 128);
for (x0 = 8; x0; --x0) {
*out = (unsigned char) x1;
out += stride;
}
return;
}
x0 = (blk[0] << 8) + 8192;
x8 = W7 * (x4 + x5) + 4;
x4 = (x8 + (W1 - W7) * x4) >> 3;
x5 = (x8 - (W1 + W7) * x5) >> 3;
x8 = W3 * (x6 + x7) + 4;
x6 = (x8 - (W3 - W5) * x6) >> 3;
x7 = (x8 - (W3 + W5) * x7) >> 3;
x8 = x0 + x1;
x0 -= x1;
x1 = W6 * (x3 + x2) + 4;
x2 = (x1 - (W2 + W6) * x2) >> 3;
x3 = (x1 + (W2 - W6) * x3) >> 3;
x1 = x4 + x6;
x4 -= x6;
x6 = x5 + x7;
x5 -= x7;
x7 = x8 + x3;
x8 -= x3;
x3 = x0 + x2;
x0 -= x2;
x2 = (181 * (x4 + x5) + 128) >> 8;
x4 = (181 * (x4 - x5) + 128) >> 8;
*out = njClip(((x7 + x1) >> 14) + 128); out += stride;
*out = njClip(((x3 + x2) >> 14) + 128); out += stride;
*out = njClip(((x0 + x4) >> 14) + 128); out += stride;
*out = njClip(((x8 + x6) >> 14) + 128); out += stride;
*out = njClip(((x8 - x6) >> 14) + 128); out += stride;
*out = njClip(((x0 - x4) >> 14) + 128); out += stride;
*out = njClip(((x3 - x2) >> 14) + 128); out += stride;
*out = njClip(((x7 - x1) >> 14) + 128);
}
#define njThrow(e) do { nj.error = e; return; } while (0)
#define njCheckError() do { if (nj.error) return; } while (0)
static int njShowBits(int bits) {
unsigned char newbyte;
if (!bits) return 0;
while (nj.bufbits < bits) {
if (nj.size <= 0) {
nj.buf = (nj.buf << 8) | 0xFF;
nj.bufbits += 8;
continue;
}
newbyte = *nj.pos++;
nj.size--;
nj.bufbits += 8;
nj.buf = (nj.buf << 8) | newbyte;
if (newbyte == 0xFF) {
if (nj.size) {
unsigned char marker = *nj.pos++;
nj.size--;
switch (marker) {
case 0x00:
case 0xFF:
break;
case 0xD9: nj.size = 0; break;
default:
if ((marker & 0xF8) != 0xD0)
nj.error = NJ_SYNTAX_ERROR;
else {
nj.buf = (nj.buf << 8) | marker;
nj.bufbits += 8;
}
}
} else
nj.error = NJ_SYNTAX_ERROR;
}
}
return (nj.buf >> (nj.bufbits - bits)) & ((1 << bits) - 1);
}
NJ_INLINE void njSkipBits(int bits) {
if (nj.bufbits < bits)
(void) njShowBits(bits);
nj.bufbits -= bits;
}
NJ_INLINE int njGetBits(int bits) {
int res = njShowBits(bits);
njSkipBits(bits);
return res;
}
NJ_INLINE void njByteAlign(void) {
nj.bufbits &= 0xF8;
}
static void njSkip(int count) {
nj.pos += count;
nj.size -= count;
nj.length -= count;
if (nj.size < 0) nj.error = NJ_SYNTAX_ERROR;
}
NJ_INLINE unsigned short njDecode16(const unsigned char *pos) {
return (pos[0] << 8) | pos[1];
}
static void njDecodeLength(void) {
if (nj.size < 2) njThrow(NJ_SYNTAX_ERROR);
nj.length = njDecode16(nj.pos);
if (nj.length > nj.size) njThrow(NJ_SYNTAX_ERROR);
njSkip(2);
}
NJ_INLINE void njSkipMarker(void) {
njDecodeLength();
njSkip(nj.length);
}
NJ_INLINE void njDecodeSOF(void) {
int i, ssxmax = 0, ssymax = 0;
nj_component_t* c;
njDecodeLength();
njCheckError();
if (nj.length < 9) njThrow(NJ_SYNTAX_ERROR);
if (nj.pos[0] != 8) njThrow(NJ_UNSUPPORTED);
nj.height = njDecode16(nj.pos+1);
nj.width = njDecode16(nj.pos+3);
if (!nj.width || !nj.height) njThrow(NJ_SYNTAX_ERROR);
nj.ncomp = nj.pos[5];
njSkip(6);
switch (nj.ncomp) {
case 1:
case 3:
break;
default:
njThrow(NJ_UNSUPPORTED);
}
if (nj.length < (nj.ncomp * 3)) njThrow(NJ_SYNTAX_ERROR);
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
c->cid = nj.pos[0];
if (!(c->ssx = nj.pos[1] >> 4)) njThrow(NJ_SYNTAX_ERROR);
if (c->ssx & (c->ssx - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two
if (!(c->ssy = nj.pos[1] & 15)) njThrow(NJ_SYNTAX_ERROR);
if (c->ssy & (c->ssy - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two
if ((c->qtsel = nj.pos[2]) & 0xFC) njThrow(NJ_SYNTAX_ERROR);
njSkip(3);
nj.qtused |= 1 << c->qtsel;
if (c->ssx > ssxmax) ssxmax = c->ssx;
if (c->ssy > ssymax) ssymax = c->ssy;
}
if (nj.ncomp == 1) {
c = nj.comp;
c->ssx = c->ssy = ssxmax = ssymax = 1;
}
nj.mbsizex = ssxmax << 3;
nj.mbsizey = ssymax << 3;
nj.mbwidth = (nj.width + nj.mbsizex - 1) / nj.mbsizex;
nj.mbheight = (nj.height + nj.mbsizey - 1) / nj.mbsizey;
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
c->width = (nj.width * c->ssx + ssxmax - 1) / ssxmax;
c->height = (nj.height * c->ssy + ssymax - 1) / ssymax;
c->stride = nj.mbwidth * c->ssx << 3;
if (((c->width < 3) && (c->ssx != ssxmax)) || ((c->height < 3) && (c->ssy != ssymax))) njThrow(NJ_UNSUPPORTED);
if (!(c->pixels = (unsigned char*) njAllocMem(c->stride * nj.mbheight * c->ssy << 3))) njThrow(NJ_OUT_OF_MEM);
}
if (nj.ncomp == 3) {
nj.rgb = (unsigned char*) njAllocMem(nj.width * nj.height * nj.ncomp);
if (!nj.rgb) njThrow(NJ_OUT_OF_MEM);
}
njSkip(nj.length);
}
NJ_INLINE void njDecodeDHT(void) {
int codelen, currcnt, remain, spread, i, j;
nj_vlc_code_t *vlc;
static unsigned char counts[16];
njDecodeLength();
njCheckError();
while (nj.length >= 17) {
i = nj.pos[0];
if (i & 0xEC) njThrow(NJ_SYNTAX_ERROR);
if (i & 0x02) njThrow(NJ_UNSUPPORTED);
i = (i | (i >> 3)) & 3; // combined DC/AC + tableid value
for (codelen = 1; codelen <= 16; ++codelen)
counts[codelen - 1] = nj.pos[codelen];
njSkip(17);
vlc = &nj.vlctab[i][0];
remain = spread = 65536;
for (codelen = 1; codelen <= 16; ++codelen) {
spread >>= 1;
currcnt = counts[codelen - 1];
if (!currcnt) continue;
if (nj.length < currcnt) njThrow(NJ_SYNTAX_ERROR);
remain -= currcnt << (16 - codelen);
if (remain < 0) njThrow(NJ_SYNTAX_ERROR);
for (i = 0; i < currcnt; ++i) {
register unsigned char code = nj.pos[i];
for (j = spread; j; --j) {
vlc->bits = (unsigned char) codelen;
vlc->code = code;
++vlc;
}
}
njSkip(currcnt);
}
while (remain--) {
vlc->bits = 0;
++vlc;
}
}
if (nj.length) njThrow(NJ_SYNTAX_ERROR);
}
NJ_INLINE void njDecodeDQT(void) {
int i;
unsigned char *t;
njDecodeLength();
njCheckError();
while (nj.length >= 65) {
i = nj.pos[0];
if (i & 0xFC) njThrow(NJ_SYNTAX_ERROR);
nj.qtavail |= 1 << i;
t = &nj.qtab[i][0];
for (i = 0; i < 64; ++i)
t[i] = nj.pos[i + 1];
njSkip(65);
}
if (nj.length) njThrow(NJ_SYNTAX_ERROR);
}
NJ_INLINE void njDecodeDRI(void) {
njDecodeLength();
njCheckError();
if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR);
nj.rstinterval = njDecode16(nj.pos);
njSkip(nj.length);
}
static int njGetVLC(nj_vlc_code_t* vlc, unsigned char* code) {
int value = njShowBits(16);
int bits = vlc[value].bits;
if (!bits) { nj.error = NJ_SYNTAX_ERROR; return 0; }
njSkipBits(bits);
value = vlc[value].code;
if (code) *code = (unsigned char) value;
bits = value & 15;
if (!bits) return 0;
value = njGetBits(bits);
if (value < (1 << (bits - 1)))
value += ((-1) << bits) + 1;
return value;
}
NJ_INLINE void njDecodeBlock(nj_component_t* c, unsigned char* out) {
unsigned char code = 0;
int value, coef = 0;
njFillMem(nj.block, 0, sizeof(nj.block));
c->dcpred += njGetVLC(&nj.vlctab[c->dctabsel][0], NULL);
nj.block[0] = (c->dcpred) * nj.qtab[c->qtsel][0];
do {
value = njGetVLC(&nj.vlctab[c->actabsel][0], &code);
if (!code) break; // EOB
if (!(code & 0x0F) && (code != 0xF0)) njThrow(NJ_SYNTAX_ERROR);
coef += (code >> 4) + 1;
if (coef > 63) njThrow(NJ_SYNTAX_ERROR);
nj.block[(int) njZZ[coef]] = value * nj.qtab[c->qtsel][coef];
} while (coef < 63);
for (coef = 0; coef < 64; coef += 8)
njRowIDCT(&nj.block[coef]);
for (coef = 0; coef < 8; ++coef)
njColIDCT(&nj.block[coef], &out[coef], c->stride);
}
NJ_INLINE void njDecodeScan(void) {
int i, mbx, mby, sbx, sby;
int rstcount = nj.rstinterval, nextrst = 0;
nj_component_t* c;
njDecodeLength();
njCheckError();
if (nj.length < (4 + 2 * nj.ncomp)) njThrow(NJ_SYNTAX_ERROR);
if (nj.pos[0] != nj.ncomp) njThrow(NJ_UNSUPPORTED);
njSkip(1);
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
if (nj.pos[0] != c->cid) njThrow(NJ_SYNTAX_ERROR);
if (nj.pos[1] & 0xEE) njThrow(NJ_SYNTAX_ERROR);
c->dctabsel = nj.pos[1] >> 4;
c->actabsel = (nj.pos[1] & 1) | 2;
njSkip(2);
}
if (nj.pos[0] || (nj.pos[1] != 63) || nj.pos[2]) njThrow(NJ_UNSUPPORTED);
njSkip(nj.length);
for (mbx = mby = 0;;) {
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c)
for (sby = 0; sby < c->ssy; ++sby)
for (sbx = 0; sbx < c->ssx; ++sbx) {
njDecodeBlock(c, &c->pixels[((mby * c->ssy + sby) * c->stride + mbx * c->ssx + sbx) << 3]);
njCheckError();
}
if (++mbx >= nj.mbwidth) {
mbx = 0;
if (++mby >= nj.mbheight) break;
}
if (nj.rstinterval && !(--rstcount)) {
njByteAlign();
i = njGetBits(16);
if (((i & 0xFFF8) != 0xFFD0) || ((i & 7) != nextrst)) njThrow(NJ_SYNTAX_ERROR);
nextrst = (nextrst + 1) & 7;
rstcount = nj.rstinterval;
for (i = 0; i < 3; ++i)
nj.comp[i].dcpred = 0;
}
}
nj.error = __NJ_FINISHED;
}
#if NJ_CHROMA_FILTER
#define CF4A (-9)
#define CF4B (111)
#define CF4C (29)
#define CF4D (-3)
#define CF3A (28)
#define CF3B (109)
#define CF3C (-9)
#define CF3X (104)
#define CF3Y (27)
#define CF3Z (-3)
#define CF2A (139)
#define CF2B (-11)
#define CF(x) njClip(((x) + 64) >> 7)
NJ_INLINE void njUpsampleH(nj_component_t* c) {
const int xmax = c->width - 3;
unsigned char *out, *lin, *lout;
int x, y;
out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
if (!out) njThrow(NJ_OUT_OF_MEM);
lin = c->pixels;
lout = out;
for (y = c->height; y; --y) {
lout[0] = CF(CF2A * lin[0] + CF2B * lin[1]);
lout[1] = CF(CF3X * lin[0] + CF3Y * lin[1] + CF3Z * lin[2]);
lout[2] = CF(CF3A * lin[0] + CF3B * lin[1] + CF3C * lin[2]);
for (x = 0; x < xmax; ++x) {
lout[(x << 1) + 3] = CF(CF4A * lin[x] + CF4B * lin[x + 1] + CF4C * lin[x + 2] + CF4D * lin[x + 3]);
lout[(x << 1) + 4] = CF(CF4D * lin[x] + CF4C * lin[x + 1] + CF4B * lin[x + 2] + CF4A * lin[x + 3]);
}
lin += c->stride;
lout += c->width << 1;
lout[-3] = CF(CF3A * lin[-1] + CF3B * lin[-2] + CF3C * lin[-3]);
lout[-2] = CF(CF3X * lin[-1] + CF3Y * lin[-2] + CF3Z * lin[-3]);
lout[-1] = CF(CF2A * lin[-1] + CF2B * lin[-2]);
}
c->width <<= 1;
c->stride = c->width;
njFreeMem((void*)c->pixels);
c->pixels = out;
}
NJ_INLINE void njUpsampleV(nj_component_t* c) {
const int w = c->width, s1 = c->stride, s2 = s1 + s1;
unsigned char *out, *cin, *cout;
int x, y;
out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
if (!out) njThrow(NJ_OUT_OF_MEM);
for (x = 0; x < w; ++x) {
cin = &c->pixels[x];
cout = &out[x];
*cout = CF(CF2A * cin[0] + CF2B * cin[s1]); cout += w;
*cout = CF(CF3X * cin[0] + CF3Y * cin[s1] + CF3Z * cin[s2]); cout += w;
*cout = CF(CF3A * cin[0] + CF3B * cin[s1] + CF3C * cin[s2]); cout += w;
cin += s1;
for (y = c->height - 3; y; --y) {
*cout = CF(CF4A * cin[-s1] + CF4B * cin[0] + CF4C * cin[s1] + CF4D * cin[s2]); cout += w;
*cout = CF(CF4D * cin[-s1] + CF4C * cin[0] + CF4B * cin[s1] + CF4A * cin[s2]); cout += w;
cin += s1;
}
cin += s1;
*cout = CF(CF3A * cin[0] + CF3B * cin[-s1] + CF3C * cin[-s2]); cout += w;
*cout = CF(CF3X * cin[0] + CF3Y * cin[-s1] + CF3Z * cin[-s2]); cout += w;
*cout = CF(CF2A * cin[0] + CF2B * cin[-s1]);
}
c->height <<= 1;
c->stride = c->width;
njFreeMem((void*) c->pixels);
c->pixels = out;
}
#else
NJ_INLINE void njUpsample(nj_component_t* c) {
int x, y, xshift = 0, yshift = 0;
unsigned char *out, *lin, *lout;
while (c->width < nj.width) { c->width <<= 1; ++xshift; }
while (c->height < nj.height) { c->height <<= 1; ++yshift; }
out = (unsigned char*) njAllocMem(c->width * c->height);
if (!out) njThrow(NJ_OUT_OF_MEM);
lin = c->pixels;
lout = out;
for (y = 0; y < c->height; ++y) {
lin = &c->pixels[(y >> yshift) * c->stride];
for (x = 0; x < c->width; ++x)
lout[x] = lin[x >> xshift];
lout += c->width;
}
c->stride = c->width;
njFreeMem((void*) c->pixels);
c->pixels = out;
}
#endif
NJ_INLINE void njConvert(void) {
int i;
nj_component_t* c;
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
#if NJ_CHROMA_FILTER
while ((c->width < nj.width) || (c->height < nj.height)) {
if (c->width < nj.width) njUpsampleH(c);
njCheckError();
if (c->height < nj.height) njUpsampleV(c);
njCheckError();
}
#else
if ((c->width < nj.width) || (c->height < nj.height))
njUpsample(c);
#endif
if ((c->width < nj.width) || (c->height < nj.height)) njThrow(NJ_INTERNAL_ERR);
}
if (nj.ncomp == 3) {
// convert to RGB
int x, yy;
unsigned char *prgb = nj.rgb;
const unsigned char *py = nj.comp[0].pixels;
const unsigned char *pcb = nj.comp[1].pixels;
const unsigned char *pcr = nj.comp[2].pixels;
for (yy = nj.height; yy; --yy) {
for (x = 0; x < nj.width; ++x) {
register int y = py[x] << 8;
register int cb = pcb[x] - 128;
register int cr = pcr[x] - 128;
*prgb++ = njClip((y + 359 * cr + 128) >> 8);
*prgb++ = njClip((y - 88 * cb - 183 * cr + 128) >> 8);
*prgb++ = njClip((y + 454 * cb + 128) >> 8);
}
py += nj.comp[0].stride;
pcb += nj.comp[1].stride;
pcr += nj.comp[2].stride;
}
} else if (nj.comp[0].width != nj.comp[0].stride) {
// grayscale -> only remove stride
unsigned char *pin = &nj.comp[0].pixels[nj.comp[0].stride];
unsigned char *pout = &nj.comp[0].pixels[nj.comp[0].width];
int y;
for (y = nj.comp[0].height - 1; y; --y) {
njCopyMem(pout, pin, nj.comp[0].width);
pin += nj.comp[0].stride;
pout += nj.comp[0].width;
}
nj.comp[0].stride = nj.comp[0].width;
}
}
void njInit(void) {
njFillMem(&nj, 0, sizeof(nj_context_t));
}
void njDone(void) {
int i;
for (i = 0; i < 3; ++i)
if (nj.comp[i].pixels) njFreeMem((void*) nj.comp[i].pixels);
if (nj.rgb) njFreeMem((void*) nj.rgb);
njInit();
}
nj_result_t njDecode(const void* jpeg, const int size) {
njDone();
nj.pos = (const unsigned char*) jpeg;
nj.size = size & 0x7FFFFFFF;
if (nj.size < 2) return NJ_NO_JPEG;
if ((nj.pos[0] ^ 0xFF) | (nj.pos[1] ^ 0xD8)) return NJ_NO_JPEG;
njSkip(2);
while (!nj.error) {
if ((nj.size < 2) || (nj.pos[0] != 0xFF)) return NJ_SYNTAX_ERROR;
njSkip(2);
switch (nj.pos[-1]) {
case 0xC0: njDecodeSOF(); break;
case 0xC4: njDecodeDHT(); break;
case 0xDB: njDecodeDQT(); break;
case 0xDD: njDecodeDRI(); break;
case 0xDA: njDecodeScan(); break;
case 0xFE: njSkipMarker(); break;
default:
if ((nj.pos[-1] & 0xF0) == 0xE0)
njSkipMarker();
else
return NJ_UNSUPPORTED;
}
}
if (nj.error != __NJ_FINISHED) return nj.error;
nj.error = NJ_OK;
njConvert();
return nj.error;
}
int njGetWidth(void) { return nj.width; }
int njGetHeight(void) { return nj.height; }
int njIsColor(void) { return (nj.ncomp != 1); }
unsigned char* njGetImage(void) { return (nj.ncomp == 1) ? nj.comp[0].pixels : nj.rgb; }
int njGetImageSize(void) { return nj.width * nj.height * nj.ncomp; }
#endif // _NJ_INCLUDE_HEADER_ONLY

66
common/nanojpeg.h Normal file
View File

@ -0,0 +1,66 @@
///////////////////////////////////////////////////////////////////////////////
// HEADER SECTION //
// copy and pase this into nanojpeg.h if you want //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NANOJPEG_H
#define _NANOJPEG_H
// nj_result_t: Result codes for njDecode().
typedef enum _nj_result {
NJ_OK = 0, // no error, decoding successful
NJ_NO_JPEG, // not a JPEG file
NJ_UNSUPPORTED, // unsupported format
NJ_OUT_OF_MEM, // out of memory
NJ_INTERNAL_ERR, // internal error
NJ_SYNTAX_ERROR, // syntax error
__NJ_FINISHED, // used internally, will never be reported
} nj_result_t;
// njInit: Initialize NanoJPEG.
// For safety reasons, this should be called at least one time before using
// using any of the other NanoJPEG functions.
void njInit(void);
// njDecode: Decode a JPEG image.
// Decodes a memory dump of a JPEG file into internal buffers.
// Parameters:
// jpeg = The pointer to the memory dump.
// size = The size of the JPEG file.
// Return value: The error code in case of failure, or NJ_OK (zero) on success.
nj_result_t njDecode(const void* jpeg, const int size);
// njGetWidth: Return the width (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetWidth() is undefined.
int njGetWidth(void);
// njGetHeight: Return the height (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetHeight() is undefined.
int njGetHeight(void);
// njIsColor: Return 1 if the most recently decoded image is a color image
// (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result
// of njGetWidth() is undefined.
int njIsColor(void);
// njGetImage: Returns the decoded image data.
// Returns a pointer to the most recently image. The memory layout it byte-
// oriented, top-down, without any padding between lines. Pixels of color
// images will be stored as three consecutive bytes for the red, green and
// blue channels. This data format is thus compatible with the PGM or PPM
// file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8.
// If njDecode() failed, the result of njGetImage() is undefined.
unsigned char* njGetImage(void);
// njGetImageSize: Returns the size (in bytes) of the image data returned
// by njGetImage(). If njDecode() failed, the result of njGetImageSize() is
// undefined.
int njGetImageSize(void);
// njDone: Uninitialize NanoJPEG.
// Resets NanoJPEG's internal state and frees all memory that has been
// allocated at run-time by NanoJPEG. It is still possible to decode another
// image after a njDone() call.
void njDone(void);
#endif//_NANOJPEG_H

View File

@ -1,813 +0,0 @@
#include "common.h"
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <zlib.h>
#include <sys/types.h>
#ifndef __WIN32__
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
typedef int socklen_t;
typedef uint32_t in_addr_t;
#undef DrawText
#endif
#include "netloader.h"
#define PING_ENABLED 1
#ifndef __SWITCH__
#include "switch/runtime/nxlink.h"
#endif
#define ZLIB_CHUNK (16 * 1024)
#define FILE_BUFFER_SIZE (128*1024)
static int netloader_listenfd = -1;
static int netloader_datafd = -1;
#if PING_ENABLED
static int netloader_udpfd = -1;
#endif
static unsigned char in[ZLIB_CHUNK];
static unsigned char out[ZLIB_CHUNK];
static mtx_t netloader_mtx;
static menuEntry_s netloader_me;
static volatile bool netloader_initialized = 0;
static volatile bool netloader_exitflag = 0;
static volatile bool netloader_activated = 0, netloader_launchapp = 0;
static volatile size_t netloader_filelen, netloader_filetotal;
static volatile char netloader_errortext[1024];
static bool netloaderGetExit(void);
//---------------------------------------------------------------------------------
static void netloader_error(const char *func, int err) {
//---------------------------------------------------------------------------------
if (!netloader_initialized || netloaderGetExit()) return;
mtx_lock(&netloader_mtx);
if (netloader_errortext[0] == 0) {
memset((char*)netloader_errortext, 0, sizeof(netloader_errortext));
snprintf((char*)netloader_errortext, sizeof(netloader_errortext)-1, "%s: err=%d\n %s\n", func, err, strerror(errno));
}
mtx_unlock(&netloader_mtx);
}
//---------------------------------------------------------------------------------
static void netloader_socket_error(const char *func) {
//---------------------------------------------------------------------------------
int errcode;
#ifdef __WIN32__
errcode = WSAGetLastError();
#else
errcode = errno;
#endif
netloader_error(func,errcode);
}
//---------------------------------------------------------------------------------
void shutdownSocket(int socket) {
//---------------------------------------------------------------------------------
#ifdef __WIN32__
shutdown (socket, SD_SEND);
closesocket (socket);
#else
close(socket);
#endif
}
static const char DIRECTORY_THIS[] = ".";
static const char DIRECTORY_PARENT[] = "..";
//---------------------------------------------------------------------------------
static bool isDirectorySeparator(int c) {
//---------------------------------------------------------------------------------
return c == DIRECTORY_SEPARATOR_CHAR;
}
//---------------------------------------------------------------------------------
static void sanitisePath(char *path) {
//---------------------------------------------------------------------------------
char *tmpPath = strdup(path);
tmpPath[0] = 0;
char *dirStart = path;
char *curPath = tmpPath;
#ifdef _WIN32
while(dirStart[0]) {
if (dirStart[0] == '/') dirStart[0] =DIRECTORY_SEPARATOR_CHAR;
dirStart++;
}
#endif
dirStart = path;
while(isDirectorySeparator(dirStart[0])) dirStart++;
do {
char *dirEnd = strchr(dirStart, DIRECTORY_SEPARATOR_CHAR);
if (dirEnd) {
dirEnd++;
if(!strncmp(DIRECTORY_PARENT,dirStart,strlen(DIRECTORY_PARENT))) {
/* move back one directory */
size_t pathlen = strlen(tmpPath);
if(tmpPath[pathlen-1] == DIRECTORY_SEPARATOR_CHAR) tmpPath[pathlen-1] = 0;
char *prev = strrchr(tmpPath,DIRECTORY_SEPARATOR_CHAR);
if (prev) {
curPath = prev + 1;
} else {
curPath = tmpPath;
}
dirStart = dirEnd;
} else if (!strncmp(DIRECTORY_THIS,dirStart,strlen(DIRECTORY_THIS))) {
/* strip this entry */
dirStart = dirEnd;
} else {
size_t dirSize = dirEnd - dirStart;
strncpy(curPath,dirStart,dirSize);
curPath[dirSize] = 0;
curPath += dirSize;
dirStart += dirSize;
}
} else {
strcpy(curPath,dirStart);
dirStart += strlen(dirStart);
}
} while(dirStart[0]);
strcpy(path, tmpPath);
free(tmpPath);
}
//---------------------------------------------------------------------------------
static int set_socket_nonblocking(int sock) {
//---------------------------------------------------------------------------------
#ifndef __WIN32__
int flags = fcntl(sock, F_GETFL);
if(flags == -1) return -1;
int rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
if(rc != 0) return -1;
#else
u_long opt = 1;
ioctlsocket(sock, FIONBIO, &opt);
#endif
return 0;
}
//---------------------------------------------------------------------------------
static int recvall(int sock, void *buffer, int size, int flags) {
//---------------------------------------------------------------------------------
int len, sizeleft = size;
bool blockflag=0;
while (sizeleft) {
len = recv(sock,buffer,sizeleft,flags);
if (len == 0) {
size = 0;
break;
};
if (len != -1) {
sizeleft -=len;
buffer +=len;
} else {
#ifdef _WIN32
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
netloader_error("win socket error",errcode);
break;
}
else {
blockflag = 1;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
netloader_socket_error("recv");
break;
}
else {
blockflag = 1;
}
#endif
if (blockflag && netloaderGetExit()) return 0;
}
}
return size;
}
//---------------------------------------------------------------------------------
static int sendall(int sock, void *buffer, int size, int flags) {
//---------------------------------------------------------------------------------
int len, sizeleft = size;
bool blockflag=0;
while (sizeleft) {
len = send(sock,buffer,sizeleft,flags);
if (len == 0) {
size = 0;
break;
};
if (len != -1) {
sizeleft -=len;
buffer +=len;
} else {
#ifdef _WIN32
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
netloader_error("win socket error",errcode);
break;
}
else {
blockflag = 1;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
netloader_socket_error("recv");
break;
}
else {
blockflag = 1;
}
#endif
if (blockflag && netloaderGetExit()) return 0;
}
}
return size;
}
//---------------------------------------------------------------------------------
static int decompress(int sock, FILE *fh, size_t filesize) {
//---------------------------------------------------------------------------------
int ret;
unsigned have;
z_stream strm;
uint32_t chunksize=0;
/* allocate inflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
if (ret != Z_OK) {
netloader_error("inflateInit failed.",ret);
return ret;
}
size_t total = 0;
/* decompress until deflate stream ends or end of file */
do {
if (netloaderGetExit()) {
ret = Z_DATA_ERROR;
break;
}
int len = recvall(sock, &chunksize, 4, 0);
if (len != 4) {
(void)inflateEnd(&strm);
netloader_error("Error getting chunk size",len);
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) {
(void)inflateEnd(&strm);
netloader_error("remote closed socket.",0);
return Z_DATA_ERROR;
}
strm.next_in = in;
/* run inflate() on input until output buffer not full */
do {
strm.avail_out = ZLIB_CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
case Z_STREAM_ERROR:
(void)inflateEnd(&strm);
netloader_error("inflate error",ret);
return ret;
}
have = ZLIB_CHUNK - strm.avail_out;
if (fwrite(out, 1, have, fh) != have || ferror(fh)) {
(void)inflateEnd(&strm);
netloader_error("file write error",0);
return Z_ERRNO;
}
total += have;
mtx_lock(&netloader_mtx);
netloader_filetotal = total;
mtx_unlock(&netloader_mtx);
//printf("%zu (%zd%%)",total, (100 * total) / filesize);
} while (strm.avail_out == 0);
/* done when inflate() says it's done */
} while (ret != Z_STREAM_END);
/* clean up and return */
(void)inflateEnd(&strm);
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
//---------------------------------------------------------------------------------
int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
//---------------------------------------------------------------------------------
int len, namelen, filelen;
char filepath[PATH_MAX+1];
len = recvall(sock, &namelen, 4, 0);
if (len != 4) {
netloader_error("Error getting name length", errno);
return -1;
}
if (namelen >= sizeof(filepath)-1) {
netloader_error("File-path length is too large",errno);
return -1;
}
len = recvall(sock, filepath, namelen, 0);
if (len != namelen) {
netloader_error("Error getting file-path", errno);
return -1;
}
filepath[namelen] = 0;
len = recvall(sock, &filelen, 4, 0);
if (len != 4) {
netloader_error("Error getting file length",errno);
return -1;
}
mtx_lock(&netloader_mtx);
netloader_filelen = filelen;
mtx_unlock(&netloader_mtx);
int response = 0;
sanitisePath(filepath);
snprintf(me->path, sizeof(me->path)-1, "%s%s%s", menuGetRootPath(), DIRECTORY_SEPARATOR, filepath);
// make sure it's terminated
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;
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
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);
}
close(fd);
}
}
#endif
FILE *file = NULL;
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);
}
}
if (response == 0 ) {
//printf("transferring %s\n%d bytes.\n", filepath, filelen);
if (decompress(sock,file,filelen)==Z_OK) {
int netloaded_cmdlen = 0;
len = sendall(sock,(char *)&response,sizeof(response),0);
if (len != sizeof(response)) {
netloader_error("Error sending response",errno);
response = -1;
}
//printf("\ntransferring command line\n");
if (response == 0 ) {
len = recvall(sock,(char*)&netloaded_cmdlen,4,0);
if (len != 4) {
netloader_error("Error getting netloaded_cmdlen",errno);
response = -1;
}
}
if (response == 0 ) {
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);
if (len != netloaded_cmdlen) {
netloader_error("Error getting args",errno);
response = -1;
}
}
if (response == 0 ) {
while(netloaded_cmdlen) {
size_t len = strlen(me->args.dst) + 1;
ad->dst += len;
ad->buf[0]++;
netloaded_cmdlen -= len;
}
}
} else {
response = -1;
}
}
if (file) {
fflush(file);
fclose(file);
}
if (response == -1) unlink(filepath);
free(writebuffer);
return response;
}
//---------------------------------------------------------------------------------
int netloader_activate(void) {
//---------------------------------------------------------------------------------
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(NXLINK_SERVER_PORT);
#if PING_ENABLED
// create udp socket for broadcast ping
netloader_udpfd = socket(AF_INET, SOCK_DGRAM, 0);
if (netloader_udpfd < 0)
{
netloader_socket_error("udp socket");
return -1;
}
if(bind(netloader_udpfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
netloader_socket_error("bind udp socket");
return -1;
}
if (set_socket_nonblocking(netloader_udpfd) == -1)
{
netloader_socket_error("listen fcntl");
return -1;
}
#endif
// create listening socket on all addresses on NXLINK_SERVER_PORT
netloader_listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(netloader_listenfd < 0)
{
netloader_socket_error("socket");
return -1;
}
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");
return -1;
}
if (set_socket_nonblocking(netloader_listenfd) == -1)
{
netloader_socket_error("listen fcntl");
return -1;
}
rc = listen(netloader_listenfd, 10);
if(rc != 0)
{
netloader_socket_error("listen");
return -1;
}
return 0;
}
//---------------------------------------------------------------------------------
int netloader_deactivate(void) {
//---------------------------------------------------------------------------------
// close all remaining sockets and allow mainloop to return to main menu
if(netloader_listenfd >= 0)
{
shutdownSocket(netloader_listenfd);
netloader_listenfd = -1;
}
if(netloader_datafd >= 0)
{
shutdownSocket(netloader_datafd);
netloader_datafd = -1;
}
#if PING_ENABLED
if(netloader_udpfd >= 0)
{
shutdownSocket(netloader_udpfd);
netloader_udpfd = -1;
}
#endif
return 0;
}
//---------------------------------------------------------------------------------
int netloader_loop(struct sockaddr_in *sa_remote) {
//---------------------------------------------------------------------------------
#if PING_ENABLED
char recvbuf[256];
socklen_t fromlen = sizeof(struct sockaddr_in);
int len = recvfrom(netloader_udpfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) sa_remote, &fromlen);
if (len!=-1) {
if (strncmp(recvbuf,"nxboot",strlen("nxboot")) == 0) {
sa_remote->sin_family=AF_INET;
sa_remote->sin_port=htons(NXLINK_CLIENT_PORT);
sendto(netloader_udpfd, "bootnx", strlen("bootnx"), 0, (struct sockaddr*) sa_remote,sizeof(struct sockaddr_in));
}
}
#endif
if(netloader_listenfd >= 0 && netloader_datafd < 0) {
socklen_t addrlen = sizeof(struct sockaddr_in);
netloader_datafd = accept(netloader_listenfd, (struct sockaddr*)sa_remote, &addrlen);
if(netloader_datafd < 0)
{
#ifdef _WIN32
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
netloader_error("accept", errcode);
return -1;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
netloader_error("accept", errno);
return -1;
}
#endif
}
else
{
if (set_socket_nonblocking(netloader_datafd) == -1)
{
netloader_socket_error("set_socket_nonblocking(netloader_datafd)");
return -1;
}
close(netloader_listenfd);
netloader_listenfd = -1;
return 1;
}
}
return 0;
}
void netloaderGetState(netloaderState *state) {
if(state==NULL)return;
mtx_lock(&netloader_mtx);
state->activated = netloader_activated;
state->launch_app = netloader_launchapp;
state->me = &netloader_me;
state->transferring = (netloader_datafd >= 0 && netloader_filelen);
state->sock_connected = netloader_datafd >= 0;
state->filelen = netloader_filelen;
state->filetotal = netloader_filetotal;
memset(state->errormsg, 0, sizeof(state->errormsg));
if(netloader_errortext[0]) {
strncpy(state->errormsg, (char*)netloader_errortext, sizeof(state->errormsg)-1);
memset((char*)netloader_errortext, 0, sizeof(netloader_errortext));
}
mtx_unlock(&netloader_mtx);
}
static bool netloaderGetExit(void) {
bool flag;
mtx_lock(&netloader_mtx);
flag = netloader_exitflag;
mtx_unlock(&netloader_mtx);
return flag;
}
void netloaderSignalExit(void) {
if (!netloader_initialized) return;
mtx_lock(&netloader_mtx);
netloader_exitflag = 1;
mtx_unlock(&netloader_mtx);
}
Result netloaderInit(void) {
Result rc=0;
if (netloader_initialized) return 0;
if (mtx_init(&netloader_mtx, mtx_plain) != thrd_success) return 1;
#ifdef __SWITCH__
rc = socketInitializeDefault();
if (R_SUCCEEDED(rc)) {
rc = nifmInitialize(NifmServiceType_User);
if (R_FAILED(rc)) socketExit();
}
#endif
#ifdef __WIN32__
WSADATA wsa_data;
if (WSAStartup (MAKEWORD(2,2), &wsa_data)) {
//netloader_error("WSAStartup failed\n",1);
rc = 2;
}
#endif
if (rc) {
mtx_destroy(&netloader_mtx);
return rc;
}
netloader_initialized = 1;
return 0;
}
void netloaderExit(void) {
if (!netloader_initialized) return;
netloader_initialized = 0;
mtx_destroy(&netloader_mtx);
#ifdef __SWITCH__
nifmExit();
socketExit();
#endif
#ifdef __WIN32__
WSACleanup ();
#endif
}
void netloaderTask(void* arg) {
int ret=0;
struct sockaddr_in sa_remote;
struct timespec duration = {.tv_nsec = 100000000};
menuEntryInit(&netloader_me,ENTRY_TYPE_FILE);
mtx_lock(&netloader_mtx);
netloader_exitflag = 0;
netloader_activated = 0;
netloader_launchapp = 0;
netloader_filelen = 0;
netloader_filetotal = 0;
mtx_unlock(&netloader_mtx);
if(netloader_activate() == 0) {
mtx_lock(&netloader_mtx);
netloader_activated = 1;
mtx_unlock(&netloader_mtx);
}
else {
netloader_deactivate();
return;
}
while((ret = netloader_loop(&sa_remote)) == 0 && !netloaderGetExit()) {
thrd_sleep(&duration, NULL);
}
if(ret == 1 && !netloaderGetExit()) {
int result = loadnro(&netloader_me, netloader_datafd,sa_remote.sin_addr);
if (result== 0) {
ret = 1;
} else {
ret = -1;
}
}
netloader_deactivate();
mtx_lock(&netloader_mtx);
if (ret==1 && !netloader_exitflag) netloader_launchapp = 1;//Access netloader_exitflag directly since the mutex is already locked.
netloader_exitflag = 0;
netloader_activated = 0;
mtx_unlock(&netloader_mtx);
}

View File

@ -1,23 +0,0 @@
#pragma once
typedef struct {
bool activated;
bool launch_app;
bool transferring;
bool sock_connected;
menuEntry_s *me;
size_t filelen, filetotal;
char errormsg[1025];
} netloaderState;
int netloader_activate(void);
int netloader_deactivate(void);
int netloader_loop(struct sockaddr_in *sa_remote);
Result netloaderInit(void);
void netloaderExit(void);
void netloaderTask(void* arg);
void netloaderGetState(netloaderState *state);
void netloaderSignalExit(void);

View File

@ -1,5 +0,0 @@
#pragma once
#include "common.h"
bool netstatusGetDetails(AssetId *id);

54
common/nro.h Normal file
View File

@ -0,0 +1,54 @@
/**
* @file nro.h
* @brief NRO headers.
* @copyright libnx Authors
*/
#pragma once
#define NROHEADER_MAGIC 0x304f524e
#define NROASSETHEADER_MAGIC 0x54455341
#define NROASSETHEADER_VERSION 0
/// Entry for each segment in the codebin.
typedef struct {
u32 file_off;
u32 size;
} NroSegment;
/// Offset 0x0 in the NRO.
typedef struct {
u32 unused;
u32 mod_offset;
u8 padding[8];
} NroStart;
/// This follows NroStart, the actual nro-header.
typedef struct {
u32 magic;
u32 unk1;
u32 size;
u32 unk2;
NroSegment segments[3];
u32 bss_size;
u32 unk3;
u8 build_id[0x20];
u8 padding[0x20];
} NroHeader;
/// Custom asset section.
typedef struct {
u64 offset;
u64 size;
} NroAssetSection;
/// Custom asset header.
typedef struct {
u32 magic;
u32 version;
NroAssetSection icon;
NroAssetSection nacp;
NroAssetSection romfs;
} NroAssetHeader;

View File

@ -1,8 +0,0 @@
#pragma once
#include "common.h"
void powerInit(void);
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging);
void powerExit(void);

View File

@ -1,130 +0,0 @@
#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);
}

View File

@ -1,7 +0,0 @@
#pragma once
#include "common.h"
bool statusInit(void);
void statusExit(void);
bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature);

View File

@ -1,30 +1,26 @@
#include "text.h" #include "text.h"
static u64 s_textLanguageCode = 0; #ifdef SWITCH
#ifdef __SWITCH__
static int s_textLang = SetLanguage_ENUS; static int s_textLang = SetLanguage_ENUS;
#else #else
static int s_textLang = 1; static int s_textLang = 1;
#endif #endif
Result textInit(void) { void textInit(void) {
#ifdef __SWITCH__ #ifdef SWITCH
SetLanguage Language=SetLanguage_ENUS; //u64 LanguageCode=0;
//s32 Language=0;
s_textLang = Language; s_textLang = SetLanguage_ENUS;
//TODO: Re-enable this once the font supports all used languages.
Result rc = setInitialize(); /*Result rc = setInitialize();
if (R_SUCCEEDED(rc)) rc = setGetSystemLanguage(&s_textLanguageCode); if (R_SUCCEEDED(rc)) rc = setGetSystemLanguage(&LanguageCode);
if (R_SUCCEEDED(rc)) rc = setMakeLanguage(s_textLanguageCode, &Language); if (R_SUCCEEDED(rc)) rc = setMakeLanguage(LanguageCode, &Language);
//if (R_SUCCEEDED(rc) && Language < 17) s_textLang = Language;//TODO: Re-enable this once language.c supports all used languages. if (R_SUCCEEDED(rc) && Language < 16) s_textLang = Language;
setExit(); setExit();*/
if (R_FAILED(rc)) return rc;
#else #else
s_textLang = 1; s_textLang = 1;
#endif #endif
return 0;
} }
int textGetLang(void) { int textGetLang(void) {
@ -33,14 +29,10 @@ int textGetLang(void) {
const char* textGetString(StrId id) { const char* textGetString(StrId id) {
const char* str = g_strings[id][s_textLang]; const char* str = g_strings[id][s_textLang];
#ifdef __SWITCH__ #ifdef SWITCH
if (!str) str = g_strings[id][SetLanguage_ENUS]; if (!str) str = g_strings[id][SetLanguage_ENUS];
#else #else
if (!str) str = g_strings[id][1]; if (!str) str = g_strings[id][1];
#endif #endif
return str; return str;
} }
u64 textGetLanguageCode(void) {
return s_textLanguageCode;
}

View File

@ -2,7 +2,6 @@
#include "common.h" #include "common.h"
#include "language.h" #include "language.h"
Result textInit(void); void textInit(void);
int textGetLang(void); int textGetLang(void);
const char* textGetString(StrId id); const char* textGetString(StrId id);
u64 textGetLanguageCode(void);

View File

@ -1,717 +1,41 @@
#include "theme.h" #include "theme.h"
#include <physfs.h> #include "button_a_light_bin.h"
#include "button_a_dark_bin.h"
theme_t themeCurrent; #include "button_b_light_bin.h"
ThemePreset themeGlobalPreset; #include "button_b_dark_bin.h"
#include "hbmenu_logo_light_bin.h"
bool colorFromSetting(config_setting_t *rgba, color_t *col) { #include "hbmenu_logo_dark_bin.h"
if(rgba == NULL)
return false;
*col = MakeColor(config_setting_get_int_elem(rgba, 0), config_setting_get_int_elem(rgba, 1), config_setting_get_int_elem(rgba, 2), config_setting_get_int_elem(rgba, 3));
return true;
}
bool intElemFromSetting(config_setting_t *setting, int *out, size_t count) {
if (!setting || config_setting_length(setting) < count)
return false;
for (size_t i=0; i<count; i++) {
out[i] = config_setting_get_int_elem(setting, i);
}
return true;
}
bool layoutObjectFromSetting(config_setting_t *layout_setting, ThemeLayoutObject *obj, bool ignore_cfg_visible) {
int tmp=0;
ThemeLayoutObject tmpobj={0};
if (!layout_setting)
return false;
memcpy(tmpobj.posStart, obj->posStart, sizeof(obj->posStart));
memcpy(tmpobj.posEnd, obj->posEnd, sizeof(obj->posEnd));
memcpy(tmpobj.size, obj->size, sizeof(obj->size));
if (config_setting_lookup_bool(layout_setting, "visible", &tmp)==CONFIG_TRUE)
tmpobj.visible = tmp;
else
tmpobj.visible = obj->visible;
if (config_setting_lookup_bool(layout_setting, "posType", &tmp)==CONFIG_TRUE)
tmpobj.posType = tmp;
else
tmpobj.posType = obj->posType;
intElemFromSetting(config_setting_lookup(layout_setting, "posStart"), tmpobj.posStart, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "posEnd"), tmpobj.posEnd, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "size"), tmpobj.size, 2);
if (!tmpobj.posType && (tmpobj.posStart[0] < 0 || tmpobj.posStart[1] < 0 || tmpobj.posEnd[0] < 0 || tmpobj.posEnd[1] < 0))
return false;
if (tmpobj.size[0] < 0 || tmpobj.size[1] < 0)
return false;
obj->posStart[0] = tmpobj.posStart[0];
obj->posStart[1] = tmpobj.posStart[1];
obj->posEnd[0] = tmpobj.posEnd[0];
obj->posEnd[1] = tmpobj.posEnd[1];
if (!ignore_cfg_visible) obj->visible = tmpobj.visible;
obj->posType = tmpobj.posType;
obj->size[0] = tmpobj.size[0];
obj->size[1] = tmpobj.size[1];
return true;
}
bool assetObjectFromSetting(config_setting_t *asset_setting, AssetId id, ThemeLayoutObject *layoutobj) {
int imageSize[2]={0};
const char *path = NULL;
char tmp_path[PATH_MAX];
if (!asset_setting)
return false;
if (config_setting_lookup_string(asset_setting, "path", &path)==CONFIG_FALSE)
return false;
if (!intElemFromSetting(config_setting_lookup(asset_setting, "imageSize"), imageSize, 2))
return false;
if (imageSize[0] <= 0 || imageSize[1] <= 0 || imageSize[0] > 1280 || imageSize[1] > 720)
return false;
if (layoutobj && (imageSize[0] != layoutobj->imageSize[0] || imageSize[1] != layoutobj->imageSize[1]))
return false;
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "theme/%s", path);
return assetsLoadData(id, tmp_path, imageSize);
}
void themeStartup(ThemePreset preset) { void themeStartup(ThemePreset preset) {
themeGlobalPreset = preset;
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),
.progressBarColor = MakeColor(0,224,0,255),
.enableWaveBlending = 0,
.buttonAText = "\uE0E0",
.buttonBText = "\uE0E1",
.buttonXText = "\uE0E2",
.buttonYText = "\uE0E3",
.buttonPText = "\uE0EF",
.buttonMText = "\uE0F0",
.labelStarOnText = "\u2605",
.labelStarOffText = "\u2606",
};
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),
.progressBarColor = MakeColor(0,224,0,255),
.enableWaveBlending = 0,
.buttonAText = "\uE0A0",
.buttonBText = "\uE0A1",
.buttonXText = "\uE0A2",
.buttonYText = "\uE0A3",
.buttonPText = "\uE0B3",
.buttonMText = "\uE0B4",
.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};
GetThemePathFromConfig(themePath, PATH_MAX);
theme_t *themeDefault;
config_t cfg = {0};
config_init(&cfg);
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, *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;
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) { switch (preset) {
case THEME_PRESET_LIGHT: case THEME_PRESET_LIGHT:
default: themeCurrent = (theme_t) {
themeDefault = &themeLight; textColor: MakeColor(0, 0, 0, 255),
if (good_cfg) frontWaveColor: MakeColor(100, 212, 250, 255),
theme = config_lookup(&cfg, "lightTheme"); middleWaveColor: MakeColor(100, 153, 255, 255),
backWaveColor: MakeColor(154, 171, 255, 255),
backgroundColor: MakeColor(233, 236, 241, 255),
highlightColor: MakeColor(91, 237, 224, 255),
enableWaveBlending: 0,
buttonAImage: button_a_light_bin,
buttonBImage: button_b_light_bin,
hbmenuLogoImage: hbmenu_logo_light_bin
};
break; break;
case THEME_PRESET_DARK: case THEME_PRESET_DARK:
themeDefault = &themeDark; themeCurrent = (theme_t) {
if (good_cfg) textColor: MakeColor(255, 255, 255, 255),
theme = config_lookup(&cfg, "darkTheme"); 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),
enableWaveBlending: 0,
buttonAImage: button_a_dark_bin,
buttonBImage: button_b_dark_bin,
hbmenuLogoImage: hbmenu_logo_dark_bin
};
break; break;
} }
if (good_cfg) {
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))
middleWave = themeDefault->middleWaveColor;
if (!colorFromSetting(config_setting_lookup(theme, "backWaveColor"), &backWave))
backWave = themeDefault->backWaveColor;
if (!colorFromSetting(config_setting_lookup(theme, "backgroundColor"), &background))
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))
borderColor = themeDefault->borderColor;
if (!colorFromSetting(config_setting_lookup(theme, "borderTextColor"), &borderTextColor))
borderTextColor = themeDefault->borderTextColor;
if (!colorFromSetting(config_setting_lookup(theme, "progressBarColor"), &progressBarColor))
progressBarColor = themeDefault->progressBarColor;
if (!config_setting_lookup_int(theme, "enableWaveBlending", &waveBlending))
waveBlending = themeDefault->enableWaveBlending;
if (!config_setting_lookup_string(theme, "buttonAText", &AText))
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;
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,
};
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) {
const char* tmpThemePath = "";
config_t cfg = {0};
config_setting_t *settings = NULL;
char tmp_path[PATH_MAX+1] = {0};
char tmp_path_theme[PATH_MAX+1] = {0};
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) {
if(config_setting_lookup_string(settings, "themePath", &tmpThemePath))
snprintf(themePath, size-1, "%s%s", tmp_path_theme, tmpThemePath);
}
}
config_destroy(&cfg);
}
void SetThemePathToConfig(const char* themePath) {
config_t cfg = {0};
config_init(&cfg);
char settingPath[PATH_MAX] = {0};
config_setting_t *root = NULL,
*group = NULL,
*settings = NULL;
themePath = getSlash(themePath);
if(themePath[0] == '/') themePath++;
#ifdef __SWITCH__
settingPath[0] = '/';
#endif
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)
settings = config_setting_lookup(group, "themePath");
if(settings != NULL)
config_setting_set_string(settings, themePath);
} else {
root = config_root_setting(&cfg);
if(root != NULL)
group = config_setting_add(root, "settings", CONFIG_TYPE_GROUP);
if(group != NULL)
settings = config_setting_add(group, "themePath", CONFIG_TYPE_STRING);
if(settings != NULL)
config_setting_set_string(settings, themePath);
}
if(!config_write_file(&cfg, settingPath)) {
menuCreateMsgBox(780, 300, textGetString(StrId_ThemeNotApplied));
}
config_destroy(&cfg);
} }

View File

@ -1,7 +1,20 @@
#pragma once #pragma once
#include "common.h" #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;
bool enableWaveBlending;
const uint8_t *buttonAImage;
const uint8_t *buttonBImage;
const uint8_t *hbmenuLogoImage;
} theme_t;
typedef enum typedef enum
{ {
@ -9,54 +22,6 @@ typedef enum
THEME_PRESET_DARK, THEME_PRESET_DARK,
} ThemePreset; } 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 themeStartup(ThemePreset preset);
void GetThemePathFromConfig(char* themePath, size_t size);
void SetThemePathToConfig(const char* themePath);
extern theme_t themeCurrent; theme_t themeCurrent;
extern ThemePreset themeGlobalPreset;

View File

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

View File

@ -1,82 +0,0 @@
#include "worker.h"
static bool s_workerInitialized = 0;
static thrd_t s_workerThread;
static cnd_t s_workerCdn;
static mtx_t s_workerMtx;
static volatile struct
{
workerThreadFunc func;
void* data;
bool exit;
} s_workerParam;
static int workerThreadProc(void* unused)
{
mtx_lock(&s_workerMtx);
for (;;)
{
cnd_wait(&s_workerCdn, &s_workerMtx);
if (s_workerParam.exit)
break;
s_workerParam.func(s_workerParam.data);
}
mtx_unlock(&s_workerMtx);
return 0;
}
bool workerInit(void)
{
if (s_workerInitialized) return 1;
if (cnd_init(&s_workerCdn) != thrd_success) return 0;
if (mtx_init(&s_workerMtx, mtx_plain) != thrd_success) {
cnd_destroy(&s_workerCdn);
return 0;
}
if (thrd_create(&s_workerThread, workerThreadProc, 0) != thrd_success) {
mtx_destroy(&s_workerMtx);
cnd_destroy(&s_workerCdn);
return 0;
}
s_workerInitialized = 1;
return 1;
}
void workerExit(void)
{
int res=0;
if (!s_workerInitialized) return;
s_workerInitialized = 0;
mtx_lock(&s_workerMtx);
s_workerParam.exit = true;
cnd_signal(&s_workerCdn);
mtx_unlock(&s_workerMtx);
thrd_join(s_workerThread, &res);
mtx_destroy(&s_workerMtx);
cnd_destroy(&s_workerCdn);
}
void workerSchedule(workerThreadFunc func, void* data)
{
if (!s_workerInitialized) return;
mtx_lock(&s_workerMtx);
s_workerParam.func = func;
s_workerParam.data = data;
cnd_signal(&s_workerCdn);
mtx_unlock(&s_workerMtx);
}

View File

@ -1,6 +0,0 @@
#pragma once
#include "common.h"
bool workerInit(void);
void workerExit(void);
void workerSchedule(workerThreadFunc func, void* data);

BIN
icon.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,10 +1,8 @@
#include <inttypes.h>
#include "../common/common.h" #include "../common/common.h"
static char argBuf[ENTRY_ARGBUFSIZE]; static char argBuf[ENTRY_ARGBUFSIZE];
static char *init_args(char *dst, size_t dst_maxsize, u32 *in_args, size_t size) static void init_args(char *dst, size_t dst_maxsize, u32 *in_args, size_t size)
{ {
size_t tmplen; size_t tmplen;
u32 argi; u32 argi;
@ -38,7 +36,6 @@ static char *init_args(char *dst, size_t dst_maxsize, u32 *in_args, size_t size)
dst_maxsize--; dst_maxsize--;
} }
} }
return dst;
} }
static bool init(void) static bool init(void)
@ -53,42 +50,13 @@ static void deinit(void)
static void launchFile(const char* path, argData_s* args) static void launchFile(const char* path, argData_s* args)
{ {
char msg[256];
/*if (strncmp(path, "sdmc:/",6) == 0) /*if (strncmp(path, "sdmc:/",6) == 0)
path += 5;*/ path += 5;*/
memset(argBuf, 0, sizeof(argBuf)); memset(argBuf, 0, sizeof(argBuf));
uint32_t remote = args->nxlink_host.s_addr;
if (remote) {
char nxlinked[17];
sprintf(nxlinked,"%08" PRIx32 "_NXLINK_",remote);
launchAddArg(args, nxlinked);
}
init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf)); init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf));
Result rc = envSetNextLoad(path, argBuf);
struct stat st; if(R_FAILED(rc)) fatalSimple(rc);//TODO: How should failing be handled?
uiExitLoop();
if (stat(path, &st) == -1) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, textGetString(StrId_NroNotFound), path);
menuCreateMsgBox(780, 300, msg);
menuScan(".");
}
else {
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();
}
}
} }
const loaderFuncs_s loader_builtin = const loaderFuncs_s loader_builtin =

View File

@ -1,256 +1,118 @@
#include <switch.h> #include <switch.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <physfs.h>
#include "../common/common.h" #include "../common/common.h"
#include "nx_graphics.h"
#include "nx_touch.h"
// Define the desired framebuffer resolution (here we set it to 720p).
#define FB_WIDTH 1280
#define FB_HEIGHT 720
uint8_t* g_framebuf; uint8_t* g_framebuf;
u32 g_framebuf_width; u32 g_framebuf_width;
PadState g_pad;
PadRepeater g_pad_repeater;
bool menuUpdateErrorScreen(void);
#ifdef PERF_LOG #ifdef PERF_LOG
u64 g_tickdiff_vsync=0;
u64 g_tickdiff_frame=0; u64 g_tickdiff_frame=0;
#endif #endif
#ifdef ENABLE_AUDIO
void audio_initialize(void);
void audio_exit(void);
#endif
extern u32 __nx_applet_exit_mode;
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
bool error_screen=0;
Result lastret=0;
Result rc=0;
char msg[256];
char errormsg[256];//Can't use StrId for these error messages since it would be unavailable if textInit fails.
#ifdef PERF_LOG #ifdef PERF_LOG
u64 start_tick=0; u64 start_tick=0;
#endif #endif
padConfigureInput(8, HidNpadStyleSet_NpadStandard); gfxInitDefault();
padInitializeAny(&g_pad);
padRepeaterInitialize(&g_pad_repeater, 20, 10);
hidSetNpadHandheldActivationMode(HidNpadHandheldActivationMode_Single);
touchInit();
memset(errormsg, 0, sizeof(errormsg)); appletSetScreenShotPermission(1);
appletLockExit(); ColorSetId theme;
appletSetScreenShotPermission(AppletScreenShotPermission_Enable); setsysInitialize();
setsysGetColorSetId(&theme);
themeStartup((ThemePreset)theme);
textInit();
menuStartup();
ColorSetId theme = ColorSetId_Light; launchInit();
rc = setsysInitialize();
if (R_SUCCEEDED(rc)) {
setsysGetColorSetId(&theme);
setsysExit();
}
if (R_SUCCEEDED(rc)) { #ifdef PERF_LOG
rc = textInit(); gfxWaitForVsync();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: textInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
if (R_SUCCEEDED(rc)) menuStartupPath(); start_tick = svcGetSystemTick();
gfxWaitForVsync();
if (R_SUCCEEDED(rc)) { g_tickdiff_vsync = svcGetSystemTick() - start_tick;
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: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
if (R_SUCCEEDED(rc)) themeStartup((ThemePreset)theme);
if (R_SUCCEEDED(rc)) powerInit();
if (R_SUCCEEDED(rc)) {
rc = netloaderInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
if (R_SUCCEEDED(rc) && !workerInit()) {
rc = 1;
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)) {
if (!launchInit()) {
rc = 2;
snprintf(errormsg, sizeof(errormsg)-1, "Error: launchInit() failed.");
}
}
if (R_SUCCEEDED(rc) && !fontInitialize()) {
rc = 3;
snprintf(errormsg, sizeof(errormsg)-1, "Error: fontInitialize() failed.");
}
#ifdef ENABLE_AUDIO
if (R_SUCCEEDED(rc)) audio_initialize();
#endif #endif
if (R_SUCCEEDED(rc)) {
lastret = envGetLastLoadResult();
if (R_FAILED(lastret)) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_LastLoadResult), R_MODULE(lastret), R_DESCRIPTION(lastret));
menuCreateMsgBox(780, 300, msg);
}
}
if (errormsg[0]) error_screen = 1;
if (!error_screen) {
graphicsInit(FB_WIDTH, FB_HEIGHT);
}
else {
consoleInit(NULL);
printf("%s\n", errormsg);
printf("Press the + button to exit.\n");
}
while (appletMainLoop()) while (appletMainLoop())
{ {
// Scan the gamepad. This should be done once for each frame #ifdef PERF_LOG
padUpdate(&g_pad); start_tick = svcGetSystemTick();
padRepeaterUpdate(&g_pad_repeater, padGetButtons(&g_pad) & ( #endif
HidNpadButton_AnyLeft | HidNpadButton_AnyUp | HidNpadButton_AnyRight | HidNpadButton_AnyDown
));
if (!error_screen) { //Scan all the inputs. This should be done once for each frame
if (!uiUpdate()) break; hidScanInput();
g_framebuf = graphicsFrameBegin(&g_framebuf_width);
#ifdef PERF_LOG
start_tick = armGetSystemTick();
#endif
memset(g_framebuf, 237, g_framebuf_width * FB_HEIGHT);
menuLoop();
}
else {
if (menuUpdateErrorScreen()) break;
}
if (!error_screen) { g_framebuf = gfxGetFramebuffer(&g_framebuf_width, NULL);
graphicsFrameEnd(); memset(g_framebuf, 237, gfxGetFramebufferSize());
if (!uiUpdate()) break;
menuLoop();
#ifdef PERF_LOG gfxFlushBuffers();
g_tickdiff_frame = armGetSystemTick() - start_tick;
#endif #ifdef PERF_LOG
} g_tickdiff_frame = svcGetSystemTick() - start_tick;
else { #endif
consoleUpdate(NULL);
} gfxSwapBuffers();
gfxWaitForVsync();
} }
if (!error_screen) {
graphicsExit();
}
else {
consoleExit(NULL);
__nx_applet_exit_mode = 1;
}
#ifdef ENABLE_AUDIO
audio_exit();
#endif
fontExit();
launchExit(); launchExit();
netloaderSignalExit(); setsysExit();
statusExit();
workerExit();
netloaderExit();
powerExit();
assetsExit();
PHYSFS_deinit();
appletUnlockExit();
gfxExit();
return 0; return 0;
} }
u64 menuGetKeysDown(void) { void launchMenuEntryTask(menuEntry_s* arg);
u64 keys = padGetButtonsDown(&g_pad);
keys |= padRepeaterGetButtons(&g_pad_repeater);
return keys;
}
//This is implemented here due to the hid code. //This is implemented here due to the hid code.
bool menuUpdate(void) { bool menuUpdate(void) {
bool exitflag = 0; bool exitflag = 0;
menu_s* menu = menuGetCurrent(); menu_s* menu = menuGetCurrent();
u64 down = menuGetKeysDown(); u32 down = hidKeysDown(CONTROLLER_P1_AUTO);
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
handleTouch(menu); if (down & KEY_A)
if (down & HidNpadButton_Y)
{ {
launchMenuNetloaderTask(); if (menu->nEntries > 0)
{
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
launchMenuEntryTask(me);
//workerSchedule(launchMenuEntryTask, me);
}
} }
else if (down & HidNpadButton_X) else if (down & KEY_B)
{ {
menuHandleXButton(); if (strcmp( menu->dirname, "sdmc:/") != 0)
{
//workerSchedule(changeDirTask, "..");
menuScan("..");
}
} }
else if (down & HidNpadButton_A) else if (down & KEY_PLUS)
{
menuHandleAButton();
}
else if (down & HidNpadButton_B)
{
launchMenuBackTask();
}
else if(down & HidNpadButton_Minus){
themeMenuStartup();
}
else if (down & HidNpadButton_Plus)
{ {
exitflag = 1; exitflag = 1;
} }
/*else if (down & KEY_Y)
{
workerSchedule(netloaderTask, NULL);
}*/
else if (menu->nEntries > 0) else if (menu->nEntries > 0)
{ {
int move = 0; int move = 0;
if (down & HidNpadButton_AnyLeft) move--; if (down & KEY_LEFT) move--;
if (down & HidNpadButton_AnyRight) move++; if (down & KEY_RIGHT) move++;
if (down & HidNpadButton_AnyDown) move-=entries_count; if (down & KEY_DOWN) move-=7;
if (down & HidNpadButton_AnyUp) move+=entries_count; if (down & KEY_UP) move+=7;
int newEntry = menu->curEntry + move; int newEntry = menu->curEntry + move;
if (newEntry < 0) newEntry = 0; if (newEntry < 0) newEntry = 0;
@ -260,15 +122,3 @@ bool menuUpdate(void) {
return exitflag; return exitflag;
} }
bool menuUpdateErrorScreen(void) {
bool exitflag = 0;
u64 down = menuGetKeysDown();
if (down & HidNpadButton_Plus)
{
exitflag = 1;
}
return exitflag;
}

View File

@ -1,166 +0,0 @@
#include <switch.h>
#include <string.h>
#include <stdio.h>
#include "../common/common.h"
#ifdef ENABLE_AUDIO
#error "Audio is not supported currently."
#define SAMPLERATE 48000
#define BYTESPERSAMPLE 2
#define CHANNELCOUNT 2
static u8* raw_data, *raw_data2;
static u8 *audio_data;
static size_t audio_data_size;
static size_t audio_data_loopoffset;
static AudioOutBuffer source_buffer[2];
static Thread audio_thread;
static bool audio_thread_exitflag = 0;
static bool audio_thread_started = 0;
static void audio_playback_thread(void* arg)
{
bool playing = 0;
bool data_ready=0;
int j, count;
int bufi=0;
u64 offset=0;
u64 tmpsize=0;
u64 totalsize = audio_data_size;
AudioOutBuffer *released_buffer = NULL;
AudioOutBuffer *src_buf = NULL;
u32 released_count=0;
while (!audio_thread_exitflag)
{
if (!playing)
{
count = 2;
if (data_ready && released_count<2) count = 1;
for (j=0; j<count; j++)
{
tmpsize = source_buffer[0].buffer_size;
if (tmpsize > totalsize - offset) tmpsize = totalsize - offset;
if (!data_ready || released_count==2) {
src_buf = &source_buffer[bufi];
}
else {
src_buf = released_buffer;
}
src_buf->data_size = tmpsize;
memcpy(src_buf->buffer, &audio_data[offset], tmpsize);
offset+= tmpsize;
if (offset >= totalsize) offset = audio_data_loopoffset;
audoutAppendAudioOutBuffer(src_buf);
bufi = 1-bufi;
}
if (!data_ready) data_ready = 1;
playing = 1;
}
if (R_SUCCEEDED(audoutWaitPlayFinish(&released_buffer, &released_count, UINT64_MAX)))
playing = 0;
}
}
void audio_initialize(void)
{
Result rc=0;
u8 *audio_intro, *audio_loop;
size_t audio_intro_size, audio_loop_size;
audio_intro = (u8*)audio_intro_bin;
audio_intro_size = audio_intro_bin_size;
audio_loop = (u8*)audio_loop_bin;
audio_loop_size = audio_loop_bin_size;
audio_data_loopoffset = audio_intro_size;
audio_data_size = audio_intro_size + audio_loop_size;
u32 SAMPLESPERBUF = SAMPLERATE/4;
u32 raw_data_size = (SAMPLESPERBUF * CHANNELCOUNT * BYTESPERSAMPLE);
u32 raw_data_size_aligned = (raw_data_size + 0xfff) & ~0xfff;
audio_data = (u8*)malloc(audio_data_size);
raw_data = (u8*)memalign(0x1000, raw_data_size_aligned);
raw_data2 = (u8*)memalign(0x1000, raw_data_size_aligned);
if (audio_data==NULL || raw_data == NULL || raw_data2==NULL) {
free(audio_data);//free() checks NULL.
free(raw_data);
free(raw_data2);
audio_data = NULL;
raw_data = NULL;
raw_data2 = NULL;
return;
}
memset(audio_data, 0, audio_data_size);
memset(raw_data, 0, raw_data_size_aligned);
memset(raw_data2, 0, raw_data_size_aligned);
memcpy(audio_data, audio_intro, audio_intro_size);
memcpy(&audio_data[audio_data_loopoffset], audio_loop, audio_loop_size);
source_buffer[0].next = 0;
source_buffer[0].buffer = raw_data;
source_buffer[0].buffer_size = raw_data_size;
source_buffer[0].data_size = raw_data_size;
source_buffer[0].data_offset = 0;
memcpy(&source_buffer[1], &source_buffer[0], sizeof(AudioOutBuffer));
source_buffer[1].buffer = raw_data2;
if (R_SUCCEEDED(rc)) rc = audoutInitialize();
if (R_SUCCEEDED(rc)) rc = audoutStartAudioOut();
audio_thread_started = 0;
if (R_SUCCEEDED(rc)) rc = threadCreate(&audio_thread, audio_playback_thread, 0, 0x4000, 28, -2);
if (R_SUCCEEDED(rc)) rc = threadStart(&audio_thread);
if (R_SUCCEEDED(rc)) audio_thread_started = 1;
}
void audio_exit(void)
{
if (audio_thread_started) {
audio_thread_exitflag = 1;
threadWaitForExit(&audio_thread);
threadClose(&audio_thread);
}
audoutStopAudioOut();
audoutExit();
free(audio_data);
free(raw_data);
free(raw_data2);
audio_data = NULL;
raw_data = NULL;
raw_data2 = NULL;
}
#endif

View File

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

View File

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

View File

@ -2,27 +2,25 @@
static const loaderFuncs_s* s_loader; static const loaderFuncs_s* s_loader;
bool launchInit(void) { void launchInit(void) {
#define ADD_LOADER(_name) do \ #define ADD_LOADER(_name) do \
{ \ { \
extern const loaderFuncs_s _name; \ extern const loaderFuncs_s _name; \
if (_name.init()) \ if (_name.init()) \
{ \ { \
s_loader = &_name; \ s_loader = &_name; \
return 1; \ return; \
} \ } \
} while(0) } while(0)
ADD_LOADER(loader_builtin); ADD_LOADER(loader_builtin);
// Shouldn't happen // Shouldn't happen
s_loader = NULL; fatalSimple(-1);//TODO: What value should be used for this?
return 0;
} }
void launchExit(void) { void launchExit(void) {
if (s_loader) s_loader->deinit(); s_loader->deinit();
s_loader = NULL;
} }
const loaderFuncs_s* launchGetLoader(void) { const loaderFuncs_s* launchGetLoader(void) {
@ -73,6 +71,5 @@ void launchMenuEntry(menuEntry_s* me) {
descriptorScanFile(&me->descriptor, me->path);*/ descriptorScanFile(&me->descriptor, me->path);*/
// Launch it // Launch it
if (s_loader == NULL) return;
s_loader->launchFile(me->path, &me->args); s_loader->launchFile(me->path, &me->args);
} }

View File

@ -1,36 +0,0 @@
#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;
}

View File

@ -1,74 +0,0 @@
#include <switch.h>
#include "../common/common.h"
static bool powerInitialized;
static bool powerCacheInitialized;
static uint32_t powerCacheCharge;
static bool powerCacheIsCharging;
static PsmSession powerSession;
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
PsmChargerType charger = PsmChargerType_Unconnected;
bool hwReadsSucceeded = false;
bool use_cache = false;
Result rc = 0;
*isCharging = false;
*batteryCharge = 0;
if (powerInitialized) {
if (powerCacheInitialized) {
rc = psmWaitStateChangeEvent(&powerSession, 0);
if (R_FAILED(rc)) use_cache = true;
}
rc = psmGetBatteryChargePercentage(batteryCharge);
hwReadsSucceeded = R_SUCCEEDED(rc);
if (use_cache) {
*isCharging = powerCacheIsCharging;
}
else {
rc = psmGetChargerType(&charger);
hwReadsSucceeded &= R_SUCCEEDED(rc);
*isCharging = (charger != PsmChargerType_Unconnected);
}
powerCacheCharge = *batteryCharge;
powerCacheIsCharging = *isCharging;
powerCacheInitialized = true;
}
return hwReadsSucceeded;
}
void powerInit(void) {
uint32_t charge=0;
bool isCharging=0;
powerCacheInitialized = false;
powerCacheCharge = 0;
powerCacheIsCharging = false;
if (!powerInitialized) {
Result rc = psmInitialize();
if (R_SUCCEEDED(rc)) {
rc = psmBindStateChangeEvent(&powerSession, 1, 1, 1);
if (R_FAILED(rc)) psmExit();
if (R_SUCCEEDED(rc)) {
powerInitialized = true;
powerGetDetails(&charge, &isCharging);//Init the cache.
}
}
}
}
void powerExit(void) {
if (powerInitialized) {
psmUnbindStateChangeEvent(&powerSession);
psmExit();
powerInitialized = false;
powerCacheInitialized = false;
}
}

View File

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

View File

@ -1,168 +0,0 @@
#include "nx_touch.h"
#define TAP_MOVEMENT_GAP 20
#define VERTICAL_SWIPE_HORIZONTAL_PLAY 250
#define VERTICAL_SWIPE_MINIMUM_DISTANCE 300
#define HORIZONTAL_SWIPE_VERTICAL_PLAY 250
#define HORIZONTAL_SWIPE_MINIMUM_DISTANCE 300
#define distance(x1, y1, x2, y2) (int) sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
struct touchInfo_s touchInfo;
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 = 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) + layoutobj->size[0]) {
launchMenuEntryTask(me);
break;
}
}
}
void handleTappingOnOpenLaunch(menu_s* menu) {
if (menu->nEntries > 0)
{
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
launchMenuEntryTask(me);
}
}
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) {
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 (touch.count == 1 && !touchInfo.gestureInProgress) {
touchInfo.gestureInProgress = true;
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 (touch.count >= 1 && touchInfo.gestureInProgress) {
touchInfo.lastSlideSpeed = ((int)(touch.touches[0].x - touchInfo.prevTouch.x));
touchInfo.prevTouch = touch.touches[0];
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.y > layoutobj->posStart[1] && touchInfo.firstTouch.y < layoutobj->posStart[1]+layoutobj->size[1] && !touchInfo.isTap && menu->nEntries > entries_count) {
if (!touchInfo.isTap) {
menu->slideSpeed = touchInfo.lastSlideSpeed;
}
}
}
// On touch end.
else if (touchInfo.gestureInProgress) {
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;
int end_x = start_x + currMsgBox.width;
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 > layoutobj->posStart[1] && y1 < layoutobj->posStart[1]+layoutobj->size[1]) {
handleTappingOnApp(menu, touchInfo.prevTouch.x);
}
// Bottom Buttons
else {
// Back Button
if (checkInsideTextLayoutObject(ThemeLayoutId_ButtonB, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonBText, x1, y1)) {
launchMenuBackTask();
}
// 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();
}
}
}
}
// Vertical Swipe
else if (abs(x1 - x2) < VERTICAL_SWIPE_HORIZONTAL_PLAY && distance(x1, y1, x2, y2) > VERTICAL_SWIPE_MINIMUM_DISTANCE) {
// Swipe up to go back
if (y1 - y2 > 0) {
launchMenuBackTask();
}
// Swipe down to go into netloader
else if (y1 - y2 < 0) {
launchMenuNetloaderTask();
}
}
// Horizontal Swipe
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();
}
}
}
touchInfo.gestureInProgress = false;
}
}

View File

@ -1,17 +0,0 @@
#pragma once
#include <switch.h>
#include "../common/common.h"
struct touchInfo_s {
bool gestureInProgress;
HidTouchState firstTouch;
HidTouchState prevTouch;
bool isTap;
int initMenuXPos;
int initMenuIndex;
int lastSlideSpeed;
};
void touchInit(void);
void handleTouch(menu_s* menu);

View File

@ -1,7 +1,6 @@
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <physfs.h>
extern "C" { extern "C" {
@ -11,20 +10,13 @@ extern "C" {
color_t pixels[720][1280]; color_t pixels[720][1280];
int main(int argc, char **argv) int main()
{ {
sf::RenderWindow window(sf::VideoMode(1280, 720), "Test"); sf::RenderWindow window(sf::VideoMode(1280, 720), "Test");
window.setFramerateLimit(60); window.setFramerateLimit(60);
menuStartupPath();
PHYSFS_init(argv[0]);
assetsInit();
themeStartup(THEME_PRESET_LIGHT); themeStartup(THEME_PRESET_LIGHT);
textInit(); textInit();
fontInitialize();
netloaderInit();
workerInit();
statusInit();
menuStartup(); menuStartup();
while (window.isOpen()) while (window.isOpen())
@ -58,17 +50,11 @@ int main(int argc, char **argv)
window.display(); window.display();
} }
netloaderSignalExit();
statusExit();
workerExit();
netloaderExit();
fontExit();
assetsExit();
PHYSFS_deinit();
return 0; return 0;
} }
extern "C" void launchMenuEntryTask(menuEntry_s* arg);
extern "C" bool menuUpdate(void) { extern "C" bool menuUpdate(void) {
//This is implemented here due to the hid code. //This is implemented here due to the hid code.
menu_s* menu = menuGetCurrent(); menu_s* menu = menuGetCurrent();
@ -77,33 +63,21 @@ extern "C" bool menuUpdate(void) {
int new_esc_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Escape); int new_esc_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Escape);
static int return_state = 0; static int return_state = 0;
int new_return_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Return); 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;
int new_t_state = sf::Keyboard::isKeyPressed(sf::Keyboard::T);
if(!new_y_state && y_state)
{
launchMenuNetloaderTask();
}
if(!new_x_state && x_state)
{
menuHandleXButton();
}
if (!new_esc_state && esc_state) if (!new_esc_state && esc_state)
{ {
launchMenuBackTask(); menuScan("..");
}
else if(!new_t_state && t_state){
themeMenuStartup();
} }
else if (!new_return_state && return_state) else if (!new_return_state && return_state)
{ {
menuHandleAButton(); if (menu->nEntries > 0)
{
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
launchMenuEntryTask(me);
//workerSchedule(launchMenuEntryTask, me);
}
} }
else if (menu->nEntries > 0) else if (menu->nEntries > 0)
{ {
@ -129,8 +103,6 @@ extern "C" bool menuUpdate(void) {
esc_state = new_esc_state; esc_state = new_esc_state;
return_state = new_return_state; return_state = new_return_state;
y_state = new_y_state;
t_state = new_t_state;
return 0; return 0;
} }

View File

@ -2,8 +2,8 @@
static const loaderFuncs_s* s_loader; static const loaderFuncs_s* s_loader;
bool launchInit(void) { void launchInit(void) {
return 1;
} }
void launchExit(void) { void launchExit(void) {

View File

@ -1,6 +0,0 @@
#include "../common/common.h"
bool netstatusGetDetails(AssetId *id) {
*id = AssetId_wifi3_icon;
return true;
}

View File

@ -1,15 +0,0 @@
#include "../common/common.h"
void powerInit(void) {
}
void powerExit(void) {
}
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
*isCharging = false;
*batteryCharge = 100;
return false;
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB