Compare commits

...

83 Commits

Author SHA1 Message Date
yellows8
2c53af56cc
Release 3.5.1 2022-06-14 18:20:43 -04:00
yellows8
7bdfce172c
Fix temperature display on [14.0.0+]. 2022-03-24 20:53:19 -04:00
imxyd
34f7f16cb1 Update language.c
Improve Chinese language
2022-01-22 19:34:54 +01:00
yellows8
73d7108a62
Release 3.5.0 2021-10-29 19:47:15 -04:00
yellows8
422039f727
Use physfs instead of minizip for asset loading, etc. 2021-10-22 13:18:30 -04:00
yellows8
3f73855170
Added theme support for dirs and .zip via physfs. Closes #121. 2021-10-21 15:26:30 -04:00
qazrfv1234
6e654ec2af Update language.c (typo fixed) 2021-08-27 20:06:28 +02:00
Dave Murphy
5c85dd784b 3.4.1 release 2021-07-16 14:53:06 +01:00
Dave Murphy
8c87b1c46f ensure build picks up version from Makefile 2021-07-16 14:52:34 +01:00
fincs
47fd18cabb
Update psm charger type 2021-07-15 21:59:49 +02:00
Chronoss
c1640f4b54
Update language.c (#130) 2021-06-09 08:21:37 +01:00
HamletDuFromage
aef37e10f7
stat(path) before trying to launch a nro (#129) 2021-06-09 08:16:37 +01:00
Mehdi Khelfi
95411fe5e9
Added some spanish and french strings (#132) 2021-04-18 14:51:03 +02:00
yellows8
65f23b0bde
Release 3.4.0 2020-12-19 12:11:00 -05:00
fincs
20c1f00972
Update for libnx hid-refactor 2020-12-02 15:01:40 +01:00
fincs
fcbc56acc4
Improve performance by using deko3d to do the framebuffer conversion on the GPU 2020-11-11 17:00:25 +01:00
fincs
244d058f1b
Minor fixes 2020-11-11 12:40:41 +01:00
Mehdi Khelfi
df09e9ed1b Added French strings
Added 2 missing french translations in language.c
2020-10-31 18:52:55 +01:00
Chris Bradel
45efcfcb98
added momentum to touch drags (#124)
* added momentum to touch drags
2020-06-26 11:35:11 -04:00
yellows8
16958ac4ba
Fixed warnings, menuTimer multi-definition, and updated for latest libnx. 2020-05-05 18:47:40 -04:00
HookedBehemoth
c69f4c56c5 update for libnx pl init change 2020-04-15 18:18:22 +02:00
yellows8
3e451a9c8b
Added resources/eth_none_icon.png which was missing. 2020-04-10 00:54:10 -04:00
yellows8
090054fc86
Release v3.3.0 2020-04-06 10:37:55 -04:00
yellows8
e2a16a83e0
Netloader now uses SO_REUSEADDR, this fixes netloader activation temporarily failing after a netloader error previously occurred. 2020-04-06 10:36:47 -04:00
yellows8
ba330cfd84
Actually display errors from netloader activation. 2020-04-06 10:35:43 -04:00
yellows8
f4067d64f3
Added app_args config setting for fileassoc. 2020-04-06 10:34:44 -04:00
yellows8
88fe495bd8
Don't delete the fileassoc entry when loading the icon failed. 2020-04-04 12:55:55 -04:00
fincs
7f1a9f4f29
U64_MAX -> UINT64_MAX 2020-03-16 16:23:36 +01:00
yellows8
ec24d595d6
Updated the default highlightGradientEdgeColor. 2020-01-08 17:03:49 -05:00
Ivan Mazzoli
5dfb9ec328 Updated italian translation and added new ones 2020-01-07 16:13:39 +01:00
yellows8
a4ad7b01b7
The icon sizes for theme-layout menuListIcon/menuActiveEntryIcon can now be controlled with the size field, with the internal imageSize field being used when size is invalid. When entering Theme Menu, the initially selected menu-entry is now the currently configured theme. Minor improvements. 2020-01-04 18:08:54 -05:00
yellows8
6ee5d36084
Removed hbmenuLogoImage from theme_t and just determine which AssetId to use for logo during rendering, imageSize from the asset is now used instead of from the layoutobj. Removed drawImageFromLayout since it's no longer used, and removed imageSize from the layout objects which don't need it anymore. 2020-01-03 17:49:08 -05:00
yellows8
981749cfb7
Added highlightGradientEdgeColor for theme config. 2020-01-03 11:47:49 -05:00
yellows8
9eda227d33
Added logoColor to the theme config. 2020-01-02 22:48:48 -05:00
yellows8
d11585589e
Added support for using themes with extension '.romfs', which contains '/theme.cfg', optionally '/icon.jpg' for the menu-entry in theme-menu, and any assets. Added 'assets' group to theme config, this is only used with .romfs themes. Added 'backgroundImage' to layout theme config, which requires assets.background_image to be setup. Various improvements. 2020-01-02 11:08:17 -05:00
yellows8
42a4ad9787
Added support for using fileassoc with netloader, an error will now be thrown if the filename/file-extension used with netloader is not recognized. Various improvements. 2019-12-30 11:15:38 -05:00
yellows8
b40d558458
Fixed a crash in touch handling for star-button when the current directory is empty (no menu entries). Implemented layouts which can optionally be loaded from theme config, closes #83. 2019-12-26 01:40:54 -05:00
yellows8
387850d301
Added APP_AUTHOR to Makefile.nx. 2019-12-14 12:44:43 -05:00
yellows8
38b48a8609
Don't use hidGetHandheldMode() to determine whether to use handheld or non-handheld. Just accept input from controllers 0-7 and handheld regardless, as long as the controller is connected. Closes #108. 2019-12-11 20:13:18 -05:00
yellows8
bd3466f1e1
Release v3.2.0 2019-12-10 20:24:41 -05:00
yellows8
e5d9851250
Actually fix the stringop warning. 2019-12-10 20:16:59 -05:00
yellows8
15baea1e44
Fixed bounds-check in netloader loadnro cmd-line args validation. 2019-12-10 12:14:23 -05:00
yellows8
c2743dcdd5
Fixed a string warning with gcc 9.x (and added the same change for other lines). 2019-12-08 17:00:17 -05:00
yellows8
233b765300
Improved hid handling. 2019-12-08 14:46:42 -05:00
octopuserectus
a747d92826 Update controller code to accept inputs from all players
not only 0 and 1 (CONTROLLER_P1_AUTO)
2019-12-07 18:58:26 -05:00
yellows8
24ce4ff924
Updated menuEntryParseNacp for latest libnx. 2019-12-07 18:08:51 -05:00
yellows8
4ecd7401b6
Use nifmInitialize/nifmExit in netloader.c, since netstatusGetDetails() uses nifm directly and latest libnx no longer uses nifm in socket init/exit. 2019-11-18 12:06:54 -05:00
yellows8
a150cbe167
Updated font.c for latest libnx + minor other changes. 2019-10-11 01:49:28 -04:00
yellows8
2feb085504
Updated language handling for latest libnx. Added support for SetLanguage_ZHHANS/SetLanguage_ZHHANT, and changed STR_ZH/STR_TW to STR_ZH_HANS/STR_ZH_HANT (system-language is still not actually used). 2019-10-04 18:16:42 -04:00
yellows8
97ab367379
Use dirent d_type when available instead of accessing FsDirectoryEntry. 2019-09-30 17:58:30 -04:00
yellows8
793b912efd
Thermal display (closes #99) (#102)
* Display the temperature status. Moved statusGet code from drawNetwork() into drawStatus(). Added types in common.h to fix building with latest libnx nacp.h, with the pc-build.
2019-09-17 11:47:58 -04:00
yellows8
42efd240de
Release v3.1.1 2019-09-14 13:53:09 -04:00
yellows8
7d01d059df
Updated README. 2019-09-09 10:34:12 -04:00
BullyWiiPlaza
6a75feeb46 Clarify build instructions 2019-09-09 10:25:23 -04:00
yellows8
d7c37c6861
Release v3.1.0 2019-08-08 20:49:30 -04:00
yellows8
fa3d93d649
Enabled using setvbuf with adjustments, improves netloader perf. Fixed issue with large-chunksize, chunksize is now uint32_t and initialized to 0. Minor other change. 2019-08-08 17:57:17 -04:00
yellows8
ea4db4ff02
Moved the gfx for the current active entry down by 10 pixels. 2019-08-05 16:49:59 -04:00
fincs
d9effc3143
Display hbmenu and hbloader version, side by side. Align "Applet Mode" text to the right. 2019-08-05 22:45:01 +02:00
yellows8
6c84575ef7
Fixed buffer overflow when the netloader chunksize is too large, during NRO transfer (this will not occur with an unmodified nxlink). 2019-08-05 12:00:45 -04:00
fincs
ce35f40f31 Display "Applet Mode" indicator when running under an applet 2019-07-29 17:36:59 +02:00
yellows8
78da39d0a2
Draw the star icons as part of the text, instead of seperately. Removed unstar icon usage. 2019-07-13 18:03:17 -04:00
yellows8
f2e085b6be
Use enum from latest libnx for appletSetScreenShotPermission. 2019-07-13 11:20:37 -04:00
octopuserectus
3ceb44fd78 Change font size for back button label 2019-07-10 16:27:31 -04:00
octopuserectus
a123712db8 Fix inconsistensies in button label placement
Also fixes text overlap with the new icons in "Theme Menu"
2019-07-10 16:27:21 -04:00
friedkeenan
48a4819e67 Fix stars with custom themes 2019-07-09 15:33:42 -04:00
hippydave
d6c780256f Add starred favourites at start of list (#91)
* Add starred favourites at start of list
2019-07-09 13:33:21 -04:00
Dave Murphy
dcad6f2afa fix assets.zip dependency. Closes #88 2019-07-01 16:14:41 +02:00
yellows8
753a97ef7b
Don't lock the mutex before calling netstatusGetDetails, lock it afterwards and use tmp fields. 2019-06-25 16:21:30 -04:00
yellows8
f0ef77b2f2
Added support for loading {filename}.nacp similar to {filename}.jpg, when processing files for fileassoc. 2019-06-24 17:05:24 -04:00
yellows8
9e257d7606
Use netstatusGetDetails from a dedicated thread, since nifmGetInternetConnectionStatus can block for a few seconds. 2019-06-24 15:41:35 -04:00
yellows8
7c029c18b0
Moved status gfx around (charging icon moved to the right of the battery icon). Previously there was gfx overlap when the battery charge was 100%. 2019-06-24 11:43:15 -04:00
yellows8
3ea2af46a5
Added netstatus files which were missing from e917589. Closes #86. 2019-06-22 17:47:31 -04:00
yellows8
e91758984c
Display a netstatus icon. Updated/added icons from #79. Closes #79. 2019-06-20 17:34:22 -04:00
yellows8
527ecb6ce3
Fixed regression caused by 8531833 where the gfx for the current active menu entry wasn't rendered in some cases. 2019-06-17 19:27:59 -04:00
yellows8
85318335fb
Fixed slowdown due to drawing too many menu entries. Closes #84. 2019-06-17 18:56:38 -04:00
fincs
6ec7388834 Update makefile 2019-03-26 23:12:39 +01:00
yellows8
1e3f057b23 Updated nx_power.c for latest libnx & changed nx_power psm prefix to 'power'. 2019-01-02 03:51:49 -05:00
fincs
29caa76884 Adapt to new libnx NWindow/Framebuffer API 2018-12-19 19:39:29 +01:00
lookye
d4af9cd2b9 Update language.c - german language strings
Added some german language strings, corrected some small grammatical mistakes.
2018-12-03 11:47:43 -05:00
yellows8
4739c8e730 Release v3.0.1 2018-11-28 17:55:18 -05:00
Dachuu
2a3564f53a Added french strings 2018-11-04 13:25:51 -05:00
yellows8
8e8e62ac33 In menuScan(), don't add an extra '/' to the path when the cwd path already has it at the end, which happens with rootdir. This fixes rootdir support on 1.0.0. Added check for '/' in menuEntryLoad() for the dirlisting code which checks menuGetRootPath(). 2018-11-01 21:41:23 -04:00
yellows8
d2bb1da2fa Disable touch horizontal swipe handling when it would conflict with the app-listing, fixes issue #73. 2018-10-30 12:22:51 -04:00
56 changed files with 2457 additions and 695 deletions

1
.gitignore vendored
View File

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

View File

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

View File

@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
@ -29,17 +28,26 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := common/ nx_main/ nx_main/loaders/
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
ROMFS := romfs
DIST_PATH := $(TARGET)_v$(APP_VERSION)
APP_AUTHOR := switchbrew
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
@ -55,7 +63,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lminizip `freetype-config --libs` -lconfig -lturbojpeg
LIBS := -ldeko3d -lphysfs `freetype-config --libs` -lconfig -lturbojpeg -lpng
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
@ -109,7 +117,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -152,7 +171,11 @@ $(BUILD):
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
dist-bin: all
@ -169,11 +192,9 @@ DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -181,6 +202,17 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
menu.o : $(TOPDIR)/Makefile
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
@ -193,11 +225,6 @@ $(OFILES_SRC) : $(HFILES_BIN)
@echo $(notdir $<)
@$(bin2o)
%.nxfnt.o : %.nxfnt
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------

View File

@ -9,12 +9,12 @@ EXTRA_CFLAGS="-D__USE_MINGW_ANSI_STDIO"
EXTRA_LDFLAGS="-lws2_32"
endif
test : pc_main/main.cpp pc_main/pc_launch.c pc_main/pc_power.c \
common/menu.c common/font.c common/language.c common/launch.c common/worker.c \
test : pc_main/main.cpp pc_main/pc_launch.c pc_main/pc_power.c pc_main/pc_netstatus.c pc_main/pc_thermalstatus.c \
common/menu.c common/font.c common/language.c common/launch.c common/worker.c common/status.c \
common/menu-entry.c common/menu-list.c common/message-box.c common/text.c \
common/ui.c common/assets.c common/math.c common/theme.c \
common/netloader.c
gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config freetype2 --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config freetype2 --libs` -lm -lminizip -lz -lconfig -lturbojpeg $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@
gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config freetype2 --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config freetype2 --libs` -lm -lphysfs -lz -lconfig -lturbojpeg -lpng $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@
clean:
rm -rf build_pc/ test test.*

View File

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

BIN
assets/airplane_icon.bin Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/eth_icon.bin Normal file

Binary file not shown.

BIN
assets/eth_none_icon.bin Normal file

Binary file not shown.

BIN
assets/wifi1_icon.bin Normal file

Binary file not shown.

BIN
assets/wifi2_icon.bin Normal file

Binary file not shown.

BIN
assets/wifi3_icon.bin Normal file

Binary file not shown.

BIN
assets/wifi_none_icon.bin Normal file

Binary file not shown.

View File

@ -1,25 +1,28 @@
#include "common.h"
#include <minizip/unzip.h>
#include <physfs.h>
#include <png.h>
typedef struct {
u8 *buffer;
size_t size;
const char *filename;
} assetsDataEntry;
#define GENASSET(x) {.filename = x}
#define GENASSET(_p, _mode, _w, _h) {{.path = _p, .imageMode = _mode, .imageSize = {_w, _h}}, {}}
static bool g_assetsInitialized = 0;
assetsDataEntry g_assetsDataList[AssetId_Max] = {
GENASSET("battery_icon.bin"),
GENASSET("charging_icon.bin"),
GENASSET("folder_icon.bin"),
GENASSET("invalid_icon.bin"),
GENASSET("hbmenu_logo_dark.bin"),
GENASSET("hbmenu_logo_light.bin"),
GENASSET("theme_icon_dark.bin"),
GENASSET("theme_icon_light.bin"),
assetsDataEntry g_assetsDataList[AssetId_Max][2] = {
GENASSET("battery_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("charging_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("folder_icon.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("invalid_icon.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("hbmenu_logo_dark.bin", IMAGE_MODE_RGBA32, 140, 60),
GENASSET("hbmenu_logo_light.bin", IMAGE_MODE_RGBA32, 140, 60),
GENASSET("theme_icon_dark.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("theme_icon_light.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("airplane_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi1_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi2_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi3_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("eth_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("eth_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("", IMAGE_MODE_RGB24, 1280, 720),
};
static void assetsClearEntry(assetsDataEntry *entry) {
@ -27,61 +30,27 @@ static void assetsClearEntry(assetsDataEntry *entry) {
entry->size = 0;
entry->buffer = NULL;
memset(entry, 0, sizeof(*entry));
}
static int assetsLoadFile(unzFile zipf, assetsDataEntry *entry) {
int ret;
int filesize=0;
unz_file_info file_info;
u8* buffer = NULL;
static void assetsSetPixelSize(assetsDataEntry *entry) {
switch (entry->imageMode) {
case IMAGE_MODE_RGB24:
entry->pixSize = 3;
break;
ret = unzLocateFile(zipf, entry->filename, 0);
if (ret==UNZ_OK) ret = unzOpenCurrentFile(zipf);
if (ret==UNZ_OK) {
ret = unzGetCurrentFileInfo(zipf, &file_info, NULL, 0, NULL, 0, NULL, 0);
filesize = file_info.uncompressed_size;
if (filesize == 0) ret = -10;
if (ret==UNZ_OK) {
buffer = (u8*)malloc(filesize);
if (buffer) {
memset(buffer, 0, filesize);
} else {
ret = -11;
case IMAGE_MODE_RGBA32:
entry->pixSize = 4;
break;
}
}
if (ret==UNZ_OK) {
ret = unzReadCurrentFile(zipf, buffer, filesize);
if(ret < filesize) {
ret = -12;
} else {
ret = UNZ_OK;
}
}
if (ret!=UNZ_OK && buffer!=NULL) free(buffer);
unzCloseCurrentFile(zipf);
}
if (ret==UNZ_OK) {
entry->buffer = buffer;
entry->size = filesize;
}
return ret;
}
Result assetsInit(void) {
int ret=0;
bool ret=false;
int i, stopi;
unzFile zipf;
assetsDataEntry *entry = NULL;
char tmp_path[PATH_MAX+1];
char tmp_path[PATH_MAX];
if (g_assetsInitialized) return 0;
@ -98,65 +67,211 @@ Result assetsInit(void) {
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/romfs/assets.zip", menuGetRootBasePath());
#endif
zipf = unzOpen(tmp_path);
if(zipf==NULL) {
#ifdef __SWITCH__
romfsExit();
#endif
return 0x80;
}
if (PHYSFS_mount(tmp_path, "", 0)) {
ret=true;
for (i=0; i<AssetId_Max; i++) {
stopi = i;
entry = &g_assetsDataList[i];
ret = assetsLoadFile(zipf, entry);
if (ret!=UNZ_OK) break;
entry = &g_assetsDataList[i][0];
if (entry->path[0]) {
ret = assetsLoadData(i, NULL, NULL);
if (!ret) break;
}
}
if (ret!=UNZ_OK) {
if (!ret) {
for (i=0; i<stopi; i++) {
assetsClearEntry(&g_assetsDataList[i]);
assetsClearEntry(&g_assetsDataList[i][0]);
}
}
if (ret==UNZ_OK) g_assetsInitialized = 1;
if (ret) g_assetsInitialized = 1;
unzClose(zipf);
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;
}
void assetsExit(void) {
int i;
bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term) {
bool ret=true;
*data_buf = NULL;
if (filesize) *filesize = 0;
if (!g_assetsInitialized) return;
g_assetsInitialized = 0;
PHYSFS_Stat tmpstat={0};
if (!(PHYSFS_stat(path, &tmpstat) && tmpstat.filesize!=-1)) ret = false;
for (i=0; i<AssetId_Max; i++) {
assetsClearEntry(&g_assetsDataList[i]);
if (ret) {
size_t bufsize = tmpstat.filesize;
if (nul_term) bufsize++;
*data_buf = (u8*)malloc(bufsize);
if (*data_buf) memset(*data_buf, 0, bufsize);
else ret = false;
}
if (ret) {
PHYSFS_File *f = PHYSFS_openRead(path);
if (f==NULL) ret = false;
else {
ret = PHYSFS_readBytes(f, *data_buf, tmpstat.filesize) == tmpstat.filesize;
PHYSFS_close(f);
}
}
void assetsGetData(AssetId id, u8 **buffer, size_t *size) {
if (buffer) *buffer = NULL;
if (size) *size = 0;
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;
assetsDataEntry *entry = &g_assetsDataList[id];
if (buffer) *buffer = entry->buffer;
if (size) *size = entry->size;
u32 pos = g_assetsDataList[id][1].initialized ? 1 : 0;
assetsDataEntry *entry = &g_assetsDataList[id][pos];
if (entry->initialized) *out = entry;
}
u8 *assetsGetDataBuffer(AssetId id) {
u8 *buffer = NULL;
assetsDataEntry *entry = NULL;
assetsGetData(id, &buffer, NULL);
return buffer;
assetsGetData(id, &entry);
return entry ? entry->buffer : NULL;
}

View File

@ -10,12 +10,35 @@ typedef enum {
AssetId_hbmenu_logo_light,
AssetId_theme_icon_dark,
AssetId_theme_icon_light,
AssetId_airplane_icon,
AssetId_wifi_none_icon,
AssetId_wifi1_icon,
AssetId_wifi2_icon,
AssetId_wifi3_icon,
AssetId_eth_icon,
AssetId_eth_none_icon,
AssetId_background_image,
AssetId_Max
AssetId_Max,
} AssetId;
typedef struct {
bool initialized;
u8 *buffer;
size_t size;
ImageMode imageMode;
size_t pixSize;
size_t imageSize[2];
char path[PATH_MAX];
} assetsDataEntry;
Result assetsInit(void);
void assetsExit(void);
void assetsGetData(AssetId id, u8 **buffer, size_t *size);
void assetsClearTheme(void);
bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term);
bool assetsLoadData(AssetId id, const char *path, int *imageSize);
void assetsGetData(AssetId id, assetsDataEntry **out);
u8 *assetsGetDataBuffer(AssetId id);
bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height);

View File

@ -21,8 +21,11 @@
#include <stdint.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int32_t s32;
typedef u32 Result;
typedef void (*workerThreadFunc)(void *);
@ -45,6 +48,49 @@ typedef union {
};
} color_t;
typedef enum
{
ThemeLayoutId_Logo,
ThemeLayoutId_HbmenuVersion,
ThemeLayoutId_LoaderInfo,
ThemeLayoutId_AttentionText,
ThemeLayoutId_LogInfo,
ThemeLayoutId_InfoMsg,
ThemeLayoutId_MenuPath,
ThemeLayoutId_MenuTypeMsg,
ThemeLayoutId_MsgBoxSeparator,
ThemeLayoutId_MsgBoxBottomText,
ThemeLayoutId_BackgroundImage,
ThemeLayoutId_BackWave,
ThemeLayoutId_MiddleWave,
ThemeLayoutId_FrontWave,
ThemeLayoutId_ButtonA,
ThemeLayoutId_ButtonAText,
ThemeLayoutId_ButtonB,
ThemeLayoutId_ButtonBText,
ThemeLayoutId_ButtonY,
ThemeLayoutId_ButtonYText,
ThemeLayoutId_ButtonM,
ThemeLayoutId_ButtonMText,
ThemeLayoutId_ButtonX,
ThemeLayoutId_ButtonXText,
ThemeLayoutId_NetworkIcon,
ThemeLayoutId_BatteryCharge,
ThemeLayoutId_BatteryIcon,
ThemeLayoutId_ChargingIcon,
ThemeLayoutId_Status,
ThemeLayoutId_Temperature,
ThemeLayoutId_MenuList,
ThemeLayoutId_MenuListTiles,
ThemeLayoutId_MenuListIcon,
ThemeLayoutId_MenuListName,
ThemeLayoutId_MenuActiveEntryIcon,
ThemeLayoutId_MenuActiveEntryName,
ThemeLayoutId_MenuActiveEntryAuthor,
ThemeLayoutId_MenuActiveEntryVersion,
ThemeLayoutId_Total,
} ThemeLayoutId;
// when building for pc we need to include these separately
#ifndef __SWITCH__
#include "switch/nro.h"
@ -64,6 +110,9 @@ typedef union {
#include "message-box.h"
#include "power.h"
#include "netloader.h"
#include "netstatus.h"
#include "thermalstatus.h"
#include "status.h"
void menuStartupPath(void);
void menuStartup(void);
@ -93,7 +142,7 @@ static inline void DrawPixel(uint32_t x, uint32_t y, color_t clr)
{
if (x >= 1280 || y >= 720)
return;
u32 off = (y * g_framebuf_width + x)*4;
u32 off = y*g_framebuf_width + x*4;
g_framebuf[off] = BlendColor(g_framebuf[off], clr.r, clr.a); off++;
g_framebuf[off] = BlendColor(g_framebuf[off], clr.g, clr.a); off++;
g_framebuf[off] = BlendColor(g_framebuf[off], clr.b, clr.a); off++;
@ -103,7 +152,7 @@ static inline void DrawPixelRaw(uint32_t x, uint32_t y, color_t clr)
{
if (x >= 1280 || y >= 720)
return;
u32 off = (y * g_framebuf_width + x)*4;
u32 off = y*g_framebuf_width + x*4;
*((u32*)&g_framebuf[off]) = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24);
}
static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr)
@ -113,12 +162,12 @@ static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr)
u32 color = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24);
u128 val = color | ((u128)color<<32) | ((u128)color<<64) | ((u128)color<<96);
u32 off = (y * g_framebuf_width + x)*4;
u32 off = y*g_framebuf_width + x*4;
*((u128*)&g_framebuf[off]) = val;
}
static inline color_t FetchPixelColor(uint32_t x, uint32_t y)
{
u32 off = (y * g_framebuf_width + x)*4;
u32 off = y*g_framebuf_width + x*4;
u32 val = *((u32*)&g_framebuf[off]);
u8 r = (u8)val;
u8 g = (u8)(val>>8);
@ -158,6 +207,8 @@ static inline color_t FetchPixelColor(uint32_t x, uint32_t y)
void DrawPixel(uint32_t x, uint32_t y, color_t clr);
void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text);
void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text);
void DrawTextFromLayoutRelative(ThemeLayoutId id, int base_x, int base_y, int *inPos, int *outPos, color_t clr, const char* text, const char align);
void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text);
void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t* height_out);
uint32_t GetTextXCoordinate(u32 font, uint32_t rX, const char* text, const char align);

View File

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

View File

@ -43,3 +43,4 @@ extern const ffnt_header_t interuiregular18_nxfnt;*/
#define interuiregular14 0//&interuiregular14_nxfnt
#define interuiregular18 1//&interuiregular18_nxfnt
#define fontscale7 4
#define largestar 5

View File

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

View File

@ -6,10 +6,12 @@
typedef enum
{
StrId_Loading = 0,
StrId_AppletMode,
StrId_Directory,
StrId_DefaultPublisher,
StrId_IOError,
StrId_CouldNotOpenFile,
StrId_NroNotFound,
StrId_NoAppsFound_Title,
StrId_NoAppsFound_Msg,
@ -23,6 +25,8 @@ typedef enum
StrId_Actions_Open,
StrId_Actions_Back,
StrId_Actions_Apply,
StrId_Actions_Star,
StrId_Actions_Unstar,
StrId_MsgBox_OK,
@ -51,5 +55,5 @@ typedef enum
StrId_Max,
} StrId;
extern const char* const g_strings[StrId_Max][16];
extern const char* const g_strings[StrId_Max][17];

View File

@ -1,4 +1,5 @@
#include "common.h"
#include <physfs.h>
void menuEntryInit(menuEntry_s* me, MenuEntryType type) {
memset(me, 0, sizeof(*me));
@ -80,9 +81,13 @@ static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) {
return ok;
}
static bool menuEntryLoadExternalIcon(menuEntry_s* me, const char* path) {
static bool menuEntryLoadExternalIcon(menuEntry_s* me, const char* path, bool data_source) {
struct stat st;
if (data_source) {
return assetsPhysfsReadFile(path, &me->icon, &me->icon_size, false);
}
if(stat(path, &st)==-1) return false;
FILE* f = fopen(path, "rb");
@ -106,12 +111,15 @@ static bool menuEntryImportIconGfx(menuEntry_s* me, uint8_t* icon_gfx, uint8_t*
if (icon_gfx == NULL || icon_gfx_small == NULL) return false;
tmpsize = 256*256*3;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
tmpsize = layoutobj->size[0]*layoutobj->size[1]*3;
me->icon_gfx = (uint8_t*)malloc(tmpsize);
if (me->icon_gfx) memcpy(me->icon_gfx, icon_gfx, tmpsize);
if (me->icon_gfx) {
tmpsize = 140*140*3;
tmpsize = layoutobj2->size[0]*layoutobj2->size[1]*3;
me->icon_gfx_small = (uint8_t*)malloc(tmpsize);
if (me->icon_gfx_small) memcpy(me->icon_gfx_small, icon_gfx_small, tmpsize);
@ -168,6 +176,26 @@ static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) {
return ok;
}
static bool menuEntryLoadExternalNacp(menuEntry_s* me, const char* path) {
struct stat st;
if(stat(path, &st)==-1) return false;
FILE* f = fopen(path, "rb");
if (!f) return false;
me->nacp = (NacpStruct*)malloc(sizeof(NacpStruct));
if (me->nacp == NULL) {
fclose(f);
return false;
}
memset(me->nacp, 0, sizeof(NacpStruct));
bool ok = fread(me->nacp, sizeof(NacpStruct), 1, f) == 1;
fclose(f);
return ok;
}
/*static void fixSpaceNewLine(char* buf) {
char *outp = buf, *inp = buf;
char lastc = 0;
@ -182,18 +210,18 @@ static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) {
} while (lastc);
}*/
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
int i=0;
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists) {
int i=0, tmplen;
menu_s *menu_fileassoc = menuFileassocGetCurrent();
menuEntry_s* fileassoc_me = NULL;
char *strptr = NULL;
static char tempbuf[PATH_MAX+1];
char tempbuf[PATH_MAX+16];
//bool isOldAppFolder = false;
if (!fsobjExists(me->path)) return false;
if (check_exists && !fsobjExists(me->path)) return false;
tempbuf[PATH_MAX] = 0;
strcpy(me->name, name);
strncpy(me->name, name, sizeof(me->name)-1);
if (me->type == ENTRY_TYPE_FOLDER)
{
@ -203,7 +231,8 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
bool fileassoc_flag = 0;
//Use the first .nro found in the directory, if there's only 1 NRO in the directory. Only used for paths starting with "sdmc:/switch/".
if (!found && strncmp(me->path, menuGetRootPath(), strlen(menuGetRootPath()))==0) {
tmplen = strlen(menuGetRootPath());
if (!found && strncmp(me->path, menuGetRootPath(), tmplen)==0 && me->path[tmplen]=='/') {
DIR* dir;
struct dirent* dp;
u32 nro_count=0;
@ -264,9 +293,8 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
if (me->type == ENTRY_TYPE_FILE)
{
//strcpy(me->name, name);//This is already done before both if statements
strcpy(me->author, textGetString(StrId_DefaultPublisher));
strcpy(me->version, "1.0.0");
strncpy(me->author, textGetString(StrId_DefaultPublisher), sizeof(me->author)-1);
strncpy(me->version, "1.0.0", sizeof(me->version)-1);
//shortcut_s sc;
@ -303,7 +331,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
char* ext = getExtension(tempbuf);
strcpy(ext, ".jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) break;
if (isOldAppFolder)
@ -311,7 +339,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
char* slash = getSlash(tempbuf);
strcpy(slash, "/icon.jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) break;
}*/
@ -384,7 +412,44 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
*author = textGetString(StrId_DefaultPublisher),
*version = "1.0.0";
if (config_read_file(&cfg, me->path)) {
const char* cfg_path = me->path;
const char* theme_archive_path = NULL;
const char* ext = getExtension(me->path);
bool good_cfg = false;
bool is_archive = false;
#ifdef __SWITCH__
bool is_romfs = false;
if (strcasecmp(ext, ".romfs")==0) {
if (R_FAILED(romfsMountFromFsdev(me->path, 0, "themetmp")))
return false;
is_romfs = true;
cfg_path = "themetmp:/theme.cfg";
theme_archive_path = "themetmp:/";
}
#endif
if (strcasecmp(ext, ".romfs")!=0 && strcasecmp(ext, ".cfg")!=0) {
theme_archive_path = me->path;
}
if (theme_archive_path) {
if (!PHYSFS_mount(theme_archive_path, "themetmp", 0)) cfg_path = NULL;
else {
is_archive = true;
cfg_path = "themetmp/theme.cfg";
}
}
if (cfg_path) {
if (!is_archive) good_cfg = config_read_file(&cfg, cfg_path);
else {
u8 *cfg_buf = NULL;
good_cfg = assetsPhysfsReadFile(cfg_path, &cfg_buf, NULL, true);
if (good_cfg) good_cfg = config_read_string(&cfg, (char*)cfg_buf);
free(cfg_buf);
}
}
if (good_cfg) {
themeInfo = config_lookup(&cfg, "themeInfo");
if (themeInfo != NULL) {
if(config_setting_lookup_string(themeInfo, "name", &name))
@ -397,6 +462,20 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
strncpy(me->author, author, sizeof(me->author)-1);
strncpy(me->version, version, sizeof(me->version)-1);
config_destroy(&cfg);
if (good_cfg && is_archive) {
bool iconLoaded = false;
iconLoaded = menuEntryLoadExternalIcon(me, "themetmp/icon.jpg", true);
if (iconLoaded) menuEntryParseIcon(me);
}
if (is_archive) PHYSFS_unmount(theme_archive_path);
#ifdef __SWITCH__
if (is_romfs) romfsUnmount("themetmp");
#endif
}
if (me->type == ENTRY_TYPE_FILE_OTHER)
@ -428,7 +507,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
bool iconLoaded = false;
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) menuEntryParseIcon(me);
@ -437,17 +516,48 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
if (!iconLoaded && fileassoc_me->icon_gfx && fileassoc_me->icon_gfx_small)
iconLoaded = menuEntryImportIconGfx(me, fileassoc_me->icon_gfx, fileassoc_me->icon_gfx_small);
//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];
launchAddArg(ad, fileassoc_me->path);
launchAddArg(ad, me->path);
for (u32 argi=0; argi<ad_assoc->buf[0]; argi++, arg_src+= strlen(arg_src)+1) {
if (argi) {
strptr = strchr(arg_src, '%');
if (strptr && strptr[0] && strptr[1] && (strptr == arg_src || strptr[-1] != '\\')) {
if (strptr[1] == 'f') {
memset(tempbuf, 0, sizeof(tempbuf));
snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s%s%s", (int)((uintptr_t)strptr-(uintptr_t)arg_src), arg_src, me->path, &strptr[2]);
launchAddArg(ad, tempbuf);
ftoken_found = 1;
continue;
}
}
}
launchAddArg(ad, arg_src);
}
if (!ftoken_found) launchAddArg(ad, me->path);
strncpy(me->path, fileassoc_me->path, sizeof(me->path));
me->path[sizeof(me->path)-1] = 0;
@ -457,16 +567,23 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
return false;
}
//check for .filename.star in same path
strptr = getSlash(me->path);
if (strptr[0] == '/') strptr++;
int strptrLen = strlen(strptr);
snprintf(me->starpath, sizeof(me->starpath)-1, "%.*s.%.*s.star", (int)(strlen(me->path) - strptrLen), me->path, (int)strptrLen, strptr);
me->starred = fileExists(me->starpath);
return true;
}
void menuEntryFileassocLoad(const char* filepath) {
bool success=0, success2=0;
bool success=0, iconLoaded=0;
menuEntry_s* me = NULL;
config_setting_t *fileassoc = NULL, *targets = NULL, *target = NULL;
config_setting_t *fileassoc = NULL, *targets = NULL, *target = NULL, *app_args = NULL, *target_args = NULL;
config_t cfg = {0};
int targets_len=0, i;
int targets_len=0, args_len=0, i;
const char *strptr = NULL;
char app_path[PATH_MAX+8];
@ -475,8 +592,8 @@ void menuEntryFileassocLoad(const char* filepath) {
char target_file_extension[PATH_MAX+1];
char target_filename[PATH_MAX+1];
char app_author[ENTRY_AUTHORLENGTH+1];
char app_version[ENTRY_VERLENGTH+1];
char app_author[ENTRY_AUTHORLENGTH+2];
char app_version[ENTRY_VERLENGTH+2];
uint8_t *app_icon_gfx = NULL;
uint8_t *app_icon_gfx_small = NULL;
@ -498,7 +615,8 @@ void menuEntryFileassocLoad(const char* filepath) {
snprintf(app_path, sizeof(app_path)-1, "%s%s", menuGetRootBasePath(), strptr);
if (config_setting_lookup_string(fileassoc, "icon_path", &strptr))
snprintf(main_icon_path, sizeof(main_icon_path)-1, "%s%s", menuGetRootBasePath(), strptr);
targets = config_setting_get_member(fileassoc, "targets");
app_args = config_setting_lookup(fileassoc, "app_args");
targets = config_setting_lookup(fileassoc, "targets");
if (app_path[0] && targets) {
targets_len = config_setting_length(targets);
@ -514,7 +632,7 @@ void menuEntryFileassocLoad(const char* filepath) {
strptr = getSlash(app_path);
if(strptr[0] == '/') strptr++;
if (menuEntryLoad(me, strptr, 0)) {
if (menuEntryLoad(me, strptr, 0, true)) {
strncpy(app_author, me->author, sizeof(app_author));
app_author[sizeof(app_author)-1] = 0;
strncpy(app_version, me->version, sizeof(app_version));
@ -544,12 +662,13 @@ void menuEntryFileassocLoad(const char* filepath) {
strncpy(target_file_extension, strptr, sizeof(target_file_extension)-1);
if (config_setting_lookup_string(target, "filename", &strptr))
strncpy(target_filename, strptr, sizeof(target_filename)-1);
target_args = config_setting_lookup(target, "app_args");
//string_is_set for target_file_extension and target_filename must differ: only 1 can be set, not both set or both not set.
if ((target_file_extension[0]!=0) == (target_filename[0]!=0)) continue;
me = menuCreateEntry(ENTRY_TYPE_FILEASSOC);
success2 = 0;
iconLoaded = 0;
if (me) {
strncpy(me->path, app_path, sizeof(me->path));
@ -568,22 +687,32 @@ void menuEntryFileassocLoad(const char* filepath) {
}
me->fileassoc_str[sizeof(me->fileassoc_str)-1] = 0;
if (target_icon_path[0]) success2 = menuEntryLoadExternalIcon(me, target_icon_path);
if (!success2 && main_icon_path[0]) success2 = menuEntryLoadExternalIcon(me, main_icon_path);
if (target_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, target_icon_path, false);
if (!iconLoaded && main_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, main_icon_path, false);
if (success2) {
if (iconLoaded) {
menuEntryParseIcon(me);
} else {
success2 = menuEntryImportIconGfx(me, app_icon_gfx, app_icon_gfx_small);
iconLoaded = menuEntryImportIconGfx(me, app_icon_gfx, app_icon_gfx_small);
}
argData_s* ad = &me->args;
ad->dst = (char*)&ad->buf[1];
launchAddArg(ad, me->path);
config_setting_t *config_args = target_args ? target_args : app_args;
if (config_args) {
args_len = config_setting_length(config_args);
for (int argi=0; argi<args_len; argi++) {
strptr = config_setting_get_string_elem(config_args, argi);
if (strptr==NULL) continue;
launchAddArg(ad, strptr);
}
}
}
if (me) {
if (success2)
menuFileassocAddEntry(me);
else
menuDeleteEntry(me, 0);
}
if (me) menuFileassocAddEntry(me);
}
}
}
@ -602,65 +731,33 @@ void menuEntryFileassocLoad(const char* filepath) {
void menuEntryParseIcon(menuEntry_s* me) {
if (me->icon_size==0 || me->icon==NULL) return;
int w,h,samp;
size_t imagesize = 256*256*3;
me->icon_gfx = (uint8_t*)malloc(imagesize);
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
if (me->icon_gfx == NULL) {
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
return;
}
size_t imagesize = layoutobj->imageSize[0]*layoutobj->imageSize[1]*3;
bool ret=true;
uint8_t *tmp_gfx = (uint8_t*)malloc(imagesize);
tjhandle _jpegDecompressor = tjInitDecompress();
if (tmp_gfx == NULL) ret = false;
if (_jpegDecompressor == NULL) {
free(me->icon_gfx);
me->icon_gfx = NULL;
if (ret) ret = assetsLoadJpgFromMemory(me->icon, me->icon_size, tmp_gfx, IMAGE_MODE_RGB24, layoutobj->imageSize[0], layoutobj->imageSize[1]);
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
return;
}
if (ret) me->icon_gfx = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], IMAGE_MODE_RGB24);
if (tjDecompressHeader2(_jpegDecompressor, me->icon, me->icon_size, &w, &h, &samp) == -1) {
free(me->icon_gfx);
me->icon_gfx = NULL;
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
tjDestroy(_jpegDecompressor);
return;
}
if (w != 256 || h != 256 ) return;
if (tjDecompress2(_jpegDecompressor, me->icon, me->icon_size, me->icon_gfx, w, 0, h, TJPF_RGB, TJFLAG_ACCURATEDCT) == -1) {
free(me->icon_gfx);
me->icon_gfx = NULL;
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
tjDestroy(_jpegDecompressor);
return;
}
if (ret && me->icon_gfx==NULL) ret = false;
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
tjDestroy(_jpegDecompressor);
if (ret) me->icon_gfx_small = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], IMAGE_MODE_RGB24);
me->icon_gfx_small = downscaleImg(me->icon_gfx, 256, 256, 140, 140, IMAGE_MODE_RGB24);
if (me->icon_gfx_small == NULL) {
if (!ret || me->icon_gfx_small == NULL) {
free(me->icon_gfx);
me->icon_gfx = NULL;
}
free(tmp_gfx);
}
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode) {
@ -680,6 +777,11 @@ uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int des
return NULL;
}
if (srcWidth == destWidth && srcHeight == destHeight) {
memcpy(out, image, destWidth*destHeight*(mode==IMAGE_MODE_RGBA32 ? 4 : 3));
return out;
}
int tmpx, tmpy;
int pos;
float sourceX, sourceY;
@ -786,7 +888,7 @@ void menuEntryParseNacp(menuEntry_s* me) {
if (me->nacp==NULL) return;
strncpy(me->version, me->nacp->version, sizeof(me->version)-1);
strncpy(me->version, me->nacp->display_version, sizeof(me->version)-1);
#ifdef __SWITCH__
Result rc=0;

View File

@ -35,6 +35,7 @@ static void _menuAddEntry(menu_s *m, menuEntry_s* me) {
m->lastEntry = me;
}
m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++;
}
@ -59,6 +60,7 @@ static void menuAddEntryToFront(menuEntry_s* me) {
m->lastEntry = me;
}
m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++;
}
@ -96,20 +98,34 @@ static void menuSort(void) {
menu_s* m = &s_menu[!s_curMenu];
int nEntries = m->nEntries;
if (nEntries==0) return;
int nEntriesStar = 0, nEntriesNoStar = 0;
menuEntry_s** list = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*));
if(list == NULL) return;
menuEntry_s** listStar = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*));
if(listStar == NULL) {
free(list);
return;
}
menuEntry_s* p = m->firstEntry;
for(i = 0; i < nEntries; ++i) {
list[i] = p;
if (p->starred)
listStar[nEntriesStar++] = p;
else
list[nEntriesNoStar++] = p;
p = p->next;
}
qsort(list, nEntries, sizeof(menuEntry_s*), menuEntryCmp);
qsort(listStar, nEntriesStar, sizeof(menuEntry_s*), menuEntryCmp);
qsort(list, nEntriesNoStar, sizeof(menuEntry_s*), menuEntryCmp);
menuEntry_s** pp = &m->firstEntry;
for(i = 0; i < nEntries; ++i) {
for(i = 0; i < nEntriesStar; ++i) {
*pp = listStar[i];
pp = &(*pp)->next;
}
for(i = 0; i < nEntriesNoStar; ++i) {
*pp = list[i];
pp = &(*pp)->next;
}
@ -117,13 +133,33 @@ static void menuSort(void) {
*pp = NULL;
free(list);
free(listStar);
}
void menuReorder (void) {
s_curMenu = !s_curMenu;
menuSort();
s_curMenu = !s_curMenu;
menuClear();
}
int menuScan(const char* target) {
int pos;
char dirsep[8];
if (chdir(target) < 0) return 1;
if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL)
return 1;
memset(dirsep, 0, sizeof(dirsep));
dirsep[0] = '/';
//While cwd will not have '/' at the end normally, it will have it when cwd is the root dir ("sdmc:/"). Don't add '/' to the path below when it's already present.
pos = strlen(s_menu[!s_curMenu].dirname);
if (pos > 0) {
if (s_menu[!s_curMenu].dirname[pos-1] == '/') dirsep[0] = 0;
}
DIR* dir;
struct dirent* dp;
char tmp_path[PATH_MAX+1];
@ -140,13 +176,12 @@ int menuScan(const char* target) {
bool entrytype=0;
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name);
snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s", s_menu[!s_curMenu].dirname, dirsep, dp->d_name);
#ifdef __SWITCH__
fsdev_dir_t* dirSt = (fsdev_dir_t*)dir->dirData->dirStruct;
FsDirectoryEntry* entry = &dirSt->entry_data[dirSt->index];
entrytype = entry->type == ENTRYTYPE_DIR;
#ifdef _DIRENT_HAVE_D_TYPE
if (dp->d_type == DT_UNKNOWN)
continue;
entrytype = dp->d_type != DT_REG;
#else
struct stat tmpstat;
@ -174,7 +209,7 @@ int menuScan(const char* target) {
strncpy(me->path, tmp_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0;
if (menuEntryLoad(me, dp->d_name, shortcut))
if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me);
else
menuDeleteEntry(me, 0);
@ -211,8 +246,23 @@ int themeMenuScan(const char* target) {
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name);
bool entrytype=0;
#ifdef _DIRENT_HAVE_D_TYPE
if (dp->d_type == DT_UNKNOWN)
continue;
entrytype = dp->d_type != DT_REG;
#else
struct stat tmpstat;
if(stat(tmp_path, &tmpstat)==-1)
continue;
entrytype = (tmpstat.st_mode & S_IFMT) != S_IFREG;
#endif
const char* ext = getExtension(dp->d_name);
if (strcasecmp(ext, ".cfg")==0)
if (entrytype || strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0 || strcasecmp(ext, ".zip")==0)
me = menuCreateEntry(ENTRY_TYPE_THEME);
if (!me)
@ -220,7 +270,7 @@ int themeMenuScan(const char* target) {
strncpy(me->path, tmp_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0;
if (menuEntryLoad(me, dp->d_name, shortcut))
if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me);
else
menuDeleteEntry(me, 0);
@ -232,7 +282,7 @@ int themeMenuScan(const char* target) {
menuEntry_s* me = menuCreateEntry(ENTRY_TYPE_THEME);
if(me) {
if(menuEntryLoad(me, textGetString(StrId_DefaultThemeName), false))//Create Default theme Menu Entry
if(menuEntryLoad(me, textGetString(StrId_DefaultThemeName), false, false))//Create Default theme Menu Entry
menuAddEntryToFront(me);
else
menuDeleteEntry(me, 0);

View File

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

View File

@ -33,6 +33,7 @@ struct menu_s_tag
int nEntries;
int curEntry;
int xPos;
int slideSpeed;
char dirname[PATH_MAX+1];
};
@ -51,6 +52,7 @@ struct menuEntry_s_tag
MenuEntryType type;
char path[PATH_MAX+8];
char starpath[PATH_MAX+8];
argData_s args;
bool fileassoc_type;//0=file_extension, 1 = filename
@ -65,6 +67,8 @@ struct menuEntry_s_tag
uint8_t *icon_gfx;
uint8_t *icon_gfx_small;
bool starred;
NacpStruct *nacp;
};
@ -74,7 +78,7 @@ typedef enum
IMAGE_MODE_RGBA32
} ImageMode;
double menuTimer;
extern double menuTimer;
#ifdef __cplusplus
extern "C" {
@ -83,7 +87,7 @@ extern "C" {
void menuEntryInit(menuEntry_s* me, MenuEntryType type);
void menuEntryFree(menuEntry_s* me, bool skip_icongfx);
bool fileExists(const char* path);
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut);
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists);
void menuEntryParseIcon(menuEntry_s* me);
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode);
void menuEntryParseNacp(menuEntry_s* me);
@ -97,11 +101,13 @@ void menuDeleteEntry(menuEntry_s* me, bool skip_icongfx);
menu_s* menuGetCurrent(void);
menu_s* menuFileassocGetCurrent(void);
void menuReorder (void);
int menuScan(const char* target);
int themeMenuScan(const char* target);
int menuFileassocScan(const char* target);
void launchMenuEntryTask(menuEntry_s* arg);
void toggleStarState(menuEntry_s* arg);
void launchApplyThemeTask(menuEntry_s* arg);
void launchMenuBackTask();
void launchMenuNetloaderTask();
@ -109,6 +115,7 @@ char *menuGetRootPath(void);
char *menuGetRootBasePath(void);
void menuHandleAButton(void);
void menuHandleXButton(void);
bool menuIsNetloaderActive(void);

View File

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

View File

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

5
common/netstatus.h Normal file
View File

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

130
common/status.c Normal file
View File

@ -0,0 +1,130 @@
#include "status.h"
static bool s_statusInitialized = 0;
static thrd_t s_statusThread;
static cnd_t s_statusCdn;
static mtx_t s_statusMtx;
static mtx_t s_statusAccessMtx;
static bool s_statusExit;
static bool s_statusReady;
static bool s_statusNetFlag;
static AssetId s_statusNetAssetId;
static bool s_statusTemperatureFlag;
static s32 s_statusTemperature;
// This uses netstatusGetDetails from a dedicated thread, since nifmGetInternetConnectionStatus can block for a few seconds.
static int statusThreadProc(void* unused)
{
mtx_lock(&s_statusMtx);
struct timespec timeout = {0};
bool tmpflag=0;
bool thermalflag=0;
bool thermal_initialized=0;
AssetId tmpid;
s32 temperature;
thermal_initialized = thermalstatusInit();
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec++;
for (;;)
{
cnd_timedwait(&s_statusCdn, &s_statusMtx, &timeout);
if (s_statusExit)
break;
tmpflag = netstatusGetDetails(&tmpid);
if (thermal_initialized) thermalflag = thermalstatusGetDetails(&temperature);
mtx_lock(&s_statusAccessMtx);
s_statusNetFlag = tmpflag;
s_statusNetAssetId = tmpid;
s_statusTemperatureFlag = thermalflag;
s_statusTemperature = temperature;
s_statusReady = 1;
mtx_unlock(&s_statusAccessMtx);
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec++;
}
mtx_unlock(&s_statusMtx);
if (thermal_initialized) thermalstatusExit();
return 0;
}
bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature) {
if (!s_statusReady) return 0;
mtx_lock(&s_statusAccessMtx);
*netstatusFlag = s_statusNetFlag;
*netstatusAssetId = s_statusNetAssetId;
*temperatureFlag = s_statusTemperatureFlag;
*temperature = s_statusTemperature;
mtx_unlock(&s_statusAccessMtx);
return 1;
}
bool statusInit(void)
{
if (s_statusInitialized) return 1;
if (cnd_init(&s_statusCdn) != thrd_success) return 0;
if (mtx_init(&s_statusMtx, mtx_plain) != thrd_success) {
cnd_destroy(&s_statusCdn);
return 0;
}
if (mtx_init(&s_statusAccessMtx, mtx_plain) != thrd_success) {
mtx_destroy(&s_statusMtx);
cnd_destroy(&s_statusCdn);
return 0;
}
if (thrd_create(&s_statusThread, statusThreadProc, 0) != thrd_success) {
mtx_destroy(&s_statusAccessMtx);
mtx_destroy(&s_statusMtx);
cnd_destroy(&s_statusCdn);
return 0;
}
s_statusInitialized = 1;
return 1;
}
void statusExit(void)
{
int res=0;
if (!s_statusInitialized) return;
s_statusInitialized = 0;
mtx_lock(&s_statusMtx);
s_statusExit = true;
cnd_signal(&s_statusCdn);
mtx_unlock(&s_statusMtx);
thrd_join(s_statusThread, &res);
s_statusReady = 0;
mtx_destroy(&s_statusAccessMtx);
mtx_destroy(&s_statusMtx);
cnd_destroy(&s_statusCdn);
}

7
common/status.h Normal file
View File

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

View File

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

View File

@ -1,4 +1,5 @@
#include "theme.h"
#include <physfs.h>
theme_t themeCurrent;
ThemePreset themeGlobalPreset;
@ -10,15 +11,94 @@ bool colorFromSetting(config_setting_t *rgba, color_t *col) {
return true;
}
bool intElemFromSetting(config_setting_t *setting, int *out, size_t count) {
if (!setting || config_setting_length(setting) < count)
return false;
for (size_t i=0; i<count; i++) {
out[i] = config_setting_get_int_elem(setting, i);
}
return true;
}
bool layoutObjectFromSetting(config_setting_t *layout_setting, ThemeLayoutObject *obj, bool ignore_cfg_visible) {
int tmp=0;
ThemeLayoutObject tmpobj={0};
if (!layout_setting)
return false;
memcpy(tmpobj.posStart, obj->posStart, sizeof(obj->posStart));
memcpy(tmpobj.posEnd, obj->posEnd, sizeof(obj->posEnd));
memcpy(tmpobj.size, obj->size, sizeof(obj->size));
if (config_setting_lookup_bool(layout_setting, "visible", &tmp)==CONFIG_TRUE)
tmpobj.visible = tmp;
else
tmpobj.visible = obj->visible;
if (config_setting_lookup_bool(layout_setting, "posType", &tmp)==CONFIG_TRUE)
tmpobj.posType = tmp;
else
tmpobj.posType = obj->posType;
intElemFromSetting(config_setting_lookup(layout_setting, "posStart"), tmpobj.posStart, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "posEnd"), tmpobj.posEnd, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "size"), tmpobj.size, 2);
if (!tmpobj.posType && (tmpobj.posStart[0] < 0 || tmpobj.posStart[1] < 0 || tmpobj.posEnd[0] < 0 || tmpobj.posEnd[1] < 0))
return false;
if (tmpobj.size[0] < 0 || tmpobj.size[1] < 0)
return false;
obj->posStart[0] = tmpobj.posStart[0];
obj->posStart[1] = tmpobj.posStart[1];
obj->posEnd[0] = tmpobj.posEnd[0];
obj->posEnd[1] = tmpobj.posEnd[1];
if (!ignore_cfg_visible) obj->visible = tmpobj.visible;
obj->posType = tmpobj.posType;
obj->size[0] = tmpobj.size[0];
obj->size[1] = tmpobj.size[1];
return true;
}
bool assetObjectFromSetting(config_setting_t *asset_setting, AssetId id, ThemeLayoutObject *layoutobj) {
int imageSize[2]={0};
const char *path = NULL;
char tmp_path[PATH_MAX];
if (!asset_setting)
return false;
if (config_setting_lookup_string(asset_setting, "path", &path)==CONFIG_FALSE)
return false;
if (!intElemFromSetting(config_setting_lookup(asset_setting, "imageSize"), imageSize, 2))
return false;
if (imageSize[0] <= 0 || imageSize[1] <= 0 || imageSize[0] > 1280 || imageSize[1] > 720)
return false;
if (layoutobj && (imageSize[0] != layoutobj->imageSize[0] || imageSize[1] != layoutobj->imageSize[1]))
return false;
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "theme/%s", path);
return assetsLoadData(id, tmp_path, imageSize);
}
void themeStartup(ThemePreset preset) {
themeGlobalPreset = preset;
theme_t themeLight = (theme_t) {
.textColor = MakeColor(0, 0, 0, 255),
.attentionTextColor = MakeColor(255, 0, 0, 255),
.frontWaveColor = MakeColor(100, 212, 250, 255),
.middleWaveColor = MakeColor(100, 153, 255, 255),
.backWaveColor = MakeColor(154, 171, 255, 255),
.backgroundColor = MakeColor(233, 236, 241, 255),
.highlightColor = MakeColor(91, 237, 224, 255),
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
.separatorColor = MakeColor(219, 218, 219, 255),
.borderColor = MakeColor(255,255,255,255),
.borderTextColor = MakeColor(64,64,64,255),
@ -26,19 +106,23 @@ void themeStartup(ThemePreset preset) {
.enableWaveBlending = 0,
.buttonAText = "\uE0E0",
.buttonBText = "\uE0E1",
.buttonXText = "\uE0E2",
.buttonYText = "\uE0E3",
.buttonPText = "\uE0EF",
.buttonMText = "\uE0F0",
.hbmenuLogoImage = assetsGetDataBuffer(AssetId_hbmenu_logo_light)
.labelStarOnText = "\u2605",
.labelStarOffText = "\u2606",
};
theme_t themeDark = (theme_t) {
.textColor = MakeColor(255, 255, 255, 255),
.attentionTextColor = MakeColor(255, 0, 0, 255),
.frontWaveColor = MakeColor(96, 204, 204, 255),
.middleWaveColor = MakeColor(66, 154, 159, 255),
.backWaveColor = MakeColor(73, 103, 169, 255),
.backgroundColor = MakeColor(45, 45, 50, 255),
.highlightColor = MakeColor(91, 237, 224, 255),
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
.separatorColor = MakeColor(219, 218, 219, 255),
.borderColor = MakeColor(255,255,255,255),
.borderTextColor = MakeColor(64,64,64,255),
@ -46,10 +130,282 @@ void themeStartup(ThemePreset preset) {
.enableWaveBlending = 0,
.buttonAText = "\uE0A0",
.buttonBText = "\uE0A1",
.buttonXText = "\uE0A2",
.buttonYText = "\uE0A3",
.buttonPText = "\uE0B3",
.buttonMText = "\uE0B4",
.hbmenuLogoImage = assetsGetDataBuffer(AssetId_hbmenu_logo_dark)
.labelStarOnText = "\u2605",
.labelStarOffText = "\u2606",
};
theme_t themeCommon = {
.layoutObjects = {
[ThemeLayoutId_Logo] = {
.visible = true,
.posType = false,
.posStart = {40, 20},
},
[ThemeLayoutId_HbmenuVersion] = {
.visible = true,
.posType = false,
.posStart = {184, 46 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_LoaderInfo] = {
.visible = true,
.posType = true,
.posStart = {43, 46 + 18 + 20},
.posEnd = {184},
.font = interuiregular14,
},
[ThemeLayoutId_AttentionText] = {
.visible = true,
.posType = true,
.posStart = {-32, 46 + 18},
.font = interuimedium30,
},
[ThemeLayoutId_LogInfo] = {
.visible = true,
.posType = false,
.posStart = {180 + 256, 46 + 16 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_InfoMsg] = {
.visible = true,
.posType = false,
.posStart = {64, 128 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_MenuPath] = {
.visible = true,
.posType = false,
.posStart = {40, 720 - 47 + 24},
.size = {380},
.font = interuiregular18,
},
[ThemeLayoutId_MenuTypeMsg] = {
.visible = true,
.posType = false,
.posStart = {1180, 30 + 26 + 32 + 20},
.font = interuiregular18,
},
[ThemeLayoutId_MsgBoxSeparator] = {
.visible = true,
.posType = true,
.posStart = {0, -80},
},
[ThemeLayoutId_MsgBoxBottomText] = {
.visible = true,
.posType = true,
.posStart = {0, -29},
},
// ThemeLayoutId_BackgroundImage is not set with the defaults.
[ThemeLayoutId_BackWave] = {
.visible = true,
.posType = true,
.size = {0, 295},
},
[ThemeLayoutId_MiddleWave] = {
.visible = true,
.posType = true,
.size = {0, 290},
},
[ThemeLayoutId_FrontWave] = {
.visible = true,
.posType = true,
.size = {0, 280},
},
[ThemeLayoutId_ButtonA] = {
.visible = true,
.posType = false,
.posStart = {1280 - 126 - 30 - 32, 720 - 47 + 24},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonAText] = {
.visible = true,
.posType = false,
.posStart = {1280 - 90 - 30 - 32, 720 - 47 + 24},
.touchSize = {0, 25},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonB] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.posEnd = {0},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonBText] = {
.visible = true,
.posType = true,
.posStart = {-90, 0},
.touchSize = {0, 32},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonY] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.font = fontscale7,
},
[ThemeLayoutId_ButtonYText] = {
.visible = true,
.posType = true,
.posStart = {-32, 0},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonM] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.font = fontscale7,
},
[ThemeLayoutId_ButtonMText] = {
.visible = true,
.posType = true,
.posStart = {-32, 0},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonX] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonXText] = {
.visible = true,
.posType = true,
.posStart = {-40 + 8, 0},
.touchSize = {0, 25},
.font = interuiregular18,
},
[ThemeLayoutId_NetworkIcon] = {
.visible = true,
.posType = true,
.posStart = {0, 0 + 47 + 10 + 3},
},
[ThemeLayoutId_BatteryCharge] = {
.visible = true,
.posType = false,
.posStart = {1180 - 10 - 24 - 8, 0 + 47 + 10 + 21 + 4},
.font = interuiregular14,
},
[ThemeLayoutId_BatteryIcon] = {
.visible = true,
.posType = false,
.posStart = {1180 - 8 - 24 - 8, 0 + 47 + 10 + 6},
},
[ThemeLayoutId_ChargingIcon] = {
.visible = true,
.posType = false,
.posStart = {1180 - 20, 0 + 47 + 10 + 6},
},
[ThemeLayoutId_Status] = {
.visible = true,
.posType = false,
.posStart = {1180, 0 + 47 + 10},
.font = interuimedium20,
},
[ThemeLayoutId_Temperature] = {
.visible = true,
.posType = false,
.posStart = {1180 + 4, 0 + 47 + 10 + + 21 + 6},
.font = interuiregular14,
},
[ThemeLayoutId_MenuList] = {
.visible = true,
.posType = false,
.posStart = {29, 720 - 100 - 145},
.posEnd = {140 + 30, 0},
.size = {140, 140 + 32},
},
[ThemeLayoutId_MenuListTiles] = {
.visible = true,
.posType = true,
.posEnd = {7, 0},
.size = {0, 0},
},
[ThemeLayoutId_MenuListIcon] = {
.visible = true,
.posType = true,
.posStart = {0, 32},
.size = {140, 140},
.imageSize = {256, 256},
},
[ThemeLayoutId_MenuListName] = {
.visible = true,
.posType = true,
.posStart = {4, 4 + 18},
.size = {140 - 32, 0},
.font = interuiregular14,
},
[ThemeLayoutId_MenuActiveEntryIcon] = {
.visible = true,
.posType = false,
.posStart = {117, 100+10},
.size = {256, 256},
.imageSize = {256, 256},
},
[ThemeLayoutId_MenuActiveEntryName] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 39},
.size = {790 - 120, 0},
.font = interuimedium30,
},
[ThemeLayoutId_MenuActiveEntryAuthor] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 28 + 30 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_MenuActiveEntryVersion] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 28 + 30 + 18 + 6 + 18},
.font = interuiregular14,
},
},
};
char themePath[PATH_MAX] = {0};
@ -58,14 +414,55 @@ void themeStartup(ThemePreset preset) {
theme_t *themeDefault;
config_t cfg = {0};
config_init(&cfg);
config_setting_t *theme = NULL;
color_t text, frontWave, middleWave, backWave, background, highlight, separator, borderColor, borderTextColor, progressBarColor;
config_setting_t *theme = NULL, *layout = NULL, *assets = NULL;
color_t text, logoColor={0}, attentionText, frontWave, middleWave, backWave, background, highlight, highlightGradientEdgeColor, separator, borderColor, borderTextColor, progressBarColor;
int waveBlending;
const char *AText, *BText, *YText, *PText, *MText;
const char *AText, *BText, *XText, *YText, *PText, *MText, *starOnText, *starOffText;
bool logoColor_set = false;
bool good_cfg = false;
#ifdef __SWITCH__
bool is_romfs = false;
#endif
bool is_archive = false;
const char* theme_archive_path = NULL;
if(themePath[0]!=0)
good_cfg = config_read_file(&cfg, themePath);
assetsClearTheme();
if(themePath[0]!=0) {
const char* cfg_path = themePath;
const char* ext = getExtension(themePath);
#ifdef __SWITCH__
if (strcasecmp(ext, ".romfs")==0) {
if (R_FAILED(romfsMountFromFsdev(themePath, 0, "theme")))
cfg_path = NULL;
else {
is_romfs = true;
cfg_path = "theme:/theme.cfg";
theme_archive_path = "theme:/";
}
}
#endif
if (strcasecmp(ext, ".romfs")!=0 && strcasecmp(ext, ".cfg")!=0) {
theme_archive_path = themePath;
}
if (theme_archive_path) {
if (!PHYSFS_mount(theme_archive_path, "theme", 0)) cfg_path = NULL;
else {
is_archive = true;
cfg_path = "theme/theme.cfg";
}
}
if (cfg_path) {
if (!is_archive) good_cfg = config_read_file(&cfg, cfg_path);
else {
u8 *cfg_buf = NULL;
good_cfg = assetsPhysfsReadFile(cfg_path, &cfg_buf, NULL, true);
if (good_cfg) good_cfg = config_read_string(&cfg, (char*)cfg_buf);
free(cfg_buf);
}
}
}
switch (preset) {
case THEME_PRESET_LIGHT:
@ -86,6 +483,10 @@ void themeStartup(ThemePreset preset) {
if (theme != NULL) {
if (!colorFromSetting(config_setting_lookup(theme, "textColor"), &text))
text = themeDefault->textColor;
if (colorFromSetting(config_setting_lookup(theme, "logoColor"), &logoColor))
logoColor_set = true;
if (!colorFromSetting(config_setting_lookup(theme, "attentionTextColor"), &attentionText))
attentionText = themeDefault->attentionTextColor;
if (!colorFromSetting(config_setting_lookup(theme, "frontWaveColor"), &frontWave))
frontWave = themeDefault->frontWaveColor;
if (!colorFromSetting(config_setting_lookup(theme, "middleWaveColor"), &middleWave))
@ -96,6 +497,8 @@ void themeStartup(ThemePreset preset) {
background = themeDefault->backgroundColor;
if (!colorFromSetting(config_setting_lookup(theme, "highlightColor"), &highlight))
highlight = themeDefault->highlightColor;
if (!colorFromSetting(config_setting_lookup(theme, "highlightGradientEdgeColor"), &highlightGradientEdgeColor))
highlightGradientEdgeColor = themeDefault->highlightGradientEdgeColor;
if (!colorFromSetting(config_setting_lookup(theme, "separatorColor"), &separator))
separator = themeDefault->separatorColor;
if (!colorFromSetting(config_setting_lookup(theme, "borderColor"), &borderColor))
@ -110,38 +513,143 @@ void themeStartup(ThemePreset preset) {
AText = themeDefault->buttonAText;
if (!config_setting_lookup_string(theme, "buttonBText", &BText))
BText = themeDefault->buttonBText;
if (!config_setting_lookup_string(theme, "buttonXText", &XText))
XText = themeDefault->buttonXText;
if (!config_setting_lookup_string(theme, "buttonYText", &YText))
YText = themeDefault->buttonYText;
if (!config_setting_lookup_string(theme, "buttonPText", &PText))
PText = themeDefault->buttonPText;
if (!config_setting_lookup_string(theme, "buttonMText", &MText))
MText = themeDefault->buttonMText;
if (!config_setting_lookup_string(theme, "labelStarOnText", &starOnText))
starOnText = themeDefault->labelStarOnText;
if (!config_setting_lookup_string(theme, "labelStarOffText", &starOffText))
starOffText = themeDefault->labelStarOffText;
themeCurrent = (theme_t) {
.textColor = text,
.logoColor = logoColor,
.attentionTextColor = attentionText,
.frontWaveColor = frontWave,
.middleWaveColor = middleWave,
.backWaveColor = backWave,
.backgroundColor = background,
.highlightColor = highlight,
.highlightGradientEdgeColor = highlightGradientEdgeColor,
.separatorColor = separator,
.borderColor = borderColor,
.borderTextColor = borderTextColor,
.progressBarColor = progressBarColor,
.logoColor_set = logoColor_set,
.enableWaveBlending = waveBlending,
.hbmenuLogoImage = themeDefault->hbmenuLogoImage
};
strncpy(themeCurrent.buttonAText, AText, sizeof(themeCurrent.buttonAText)-1);
strncpy(themeCurrent.buttonBText, BText, sizeof(themeCurrent.buttonBText)-1);
strncpy(themeCurrent.buttonYText, YText, sizeof(themeCurrent.buttonYText)-1);
strncpy(themeCurrent.buttonPText, PText, sizeof(themeCurrent.buttonPText)-1);
strncpy(themeCurrent.buttonMText, MText, sizeof(themeCurrent.buttonMText)-1);
strncpy(themeCurrent.buttonAText, AText, sizeof(themeCurrent.buttonAText));
themeCurrent.buttonAText[sizeof(themeCurrent.buttonAText)-1] = 0;
strncpy(themeCurrent.buttonBText, BText, sizeof(themeCurrent.buttonBText));
themeCurrent.buttonBText[sizeof(themeCurrent.buttonBText)-1] = 0;
strncpy(themeCurrent.buttonXText, XText, sizeof(themeCurrent.buttonXText));
themeCurrent.buttonXText[sizeof(themeCurrent.buttonXText)-1] = 0;
strncpy(themeCurrent.buttonYText, YText, sizeof(themeCurrent.buttonYText));
themeCurrent.buttonYText[sizeof(themeCurrent.buttonYText)-1] = 0;
strncpy(themeCurrent.buttonPText, PText, sizeof(themeCurrent.buttonPText));
themeCurrent.buttonPText[sizeof(themeCurrent.buttonPText)-1] = 0;
strncpy(themeCurrent.buttonMText, MText, sizeof(themeCurrent.buttonMText));
themeCurrent.buttonMText[sizeof(themeCurrent.buttonMText)-1] = 0;
strncpy(themeCurrent.labelStarOnText, starOnText, sizeof(themeCurrent.labelStarOnText));
themeCurrent.labelStarOnText[sizeof(themeCurrent.labelStarOnText)-1] = 0;
strncpy(themeCurrent.labelStarOffText, starOffText, sizeof(themeCurrent.labelStarOffText));
themeCurrent.labelStarOffText[sizeof(themeCurrent.labelStarOffText)-1] = 0;
} else {
themeCurrent = *themeDefault;
}
memcpy(themeCurrent.layoutObjects, themeCommon.layoutObjects, sizeof(themeCommon.layoutObjects));
layout = config_lookup(&cfg, "layout");
if (layout != NULL) {
layoutObjectFromSetting(config_setting_lookup(layout, "logo"), &themeCurrent.layoutObjects[ThemeLayoutId_Logo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "hbmenuVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_HbmenuVersion], true);
layoutObjectFromSetting(config_setting_lookup(layout, "loaderInfo"), &themeCurrent.layoutObjects[ThemeLayoutId_LoaderInfo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "attentionText"), &themeCurrent.layoutObjects[ThemeLayoutId_AttentionText], true);
layoutObjectFromSetting(config_setting_lookup(layout, "logInfo"), &themeCurrent.layoutObjects[ThemeLayoutId_LogInfo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "infoMsg"), &themeCurrent.layoutObjects[ThemeLayoutId_InfoMsg], true);
layoutObjectFromSetting(config_setting_lookup(layout, "menuPath"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuPath], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuTypeMsg"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuTypeMsg], false);
layoutObjectFromSetting(config_setting_lookup(layout, "msgBoxSeparator"), &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator], false);
layoutObjectFromSetting(config_setting_lookup(layout, "msgBoxBottomText"), &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxBottomText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "backgroundImage"), &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage], false);
layoutObjectFromSetting(config_setting_lookup(layout, "backWave"), &themeCurrent.layoutObjects[ThemeLayoutId_BackWave], false);
layoutObjectFromSetting(config_setting_lookup(layout, "middleWave"), &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave], false);
layoutObjectFromSetting(config_setting_lookup(layout, "frontWave"), &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonA"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonAText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonAText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonB"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonB], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonBText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonBText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonY"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonY], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonYText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonYText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonM"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonM], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonMText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonMText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonX"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonX], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonXText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonXText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "networkIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_NetworkIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "batteryCharge"), &themeCurrent.layoutObjects[ThemeLayoutId_BatteryCharge], false);
layoutObjectFromSetting(config_setting_lookup(layout, "batteryIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_BatteryIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "chargingIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_ChargingIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "status"), &themeCurrent.layoutObjects[ThemeLayoutId_Status], false);
layoutObjectFromSetting(config_setting_lookup(layout, "temperature"), &themeCurrent.layoutObjects[ThemeLayoutId_Temperature], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuList"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuList], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListTiles"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListName"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListName], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryName"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryName], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryAuthor"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryAuthor], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion], false);
}
if (is_archive) assets = config_lookup(&cfg, "assets");
if (is_archive && assets) {
assetObjectFromSetting(config_setting_lookup(assets, "battery_icon"), AssetId_battery_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "charging_icon"), AssetId_charging_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "folder_icon"), AssetId_folder_icon, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "invalid_icon"), AssetId_invalid_icon, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "theme_icon_dark"), AssetId_theme_icon_dark, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "theme_icon_light"), AssetId_theme_icon_light, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "airplane_icon"), AssetId_airplane_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi_none_icon"), AssetId_wifi_none_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi1_icon"), AssetId_wifi1_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi2_icon"), AssetId_wifi2_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi3_icon"), AssetId_wifi3_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "eth_icon"), AssetId_eth_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "eth_none_icon"), AssetId_eth_none_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "background_image"), AssetId_background_image, NULL);
}
} else {
themeCurrent = *themeDefault;
memcpy(themeCurrent.layoutObjects, themeCommon.layoutObjects, sizeof(themeCommon.layoutObjects));
}
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
if (layoutobj->posEnd[0] < 1) layoutobj->posEnd[0] = 1;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
if (layoutobj->size[0] <= 0 || layoutobj->size[1] <= 0 || layoutobj->size[0] > layoutobj->imageSize[0] || layoutobj->size[1] > layoutobj->imageSize[1]) {
layoutobj->size[0] = layoutobj->imageSize[0];
layoutobj->size[1] = layoutobj->imageSize[1];
}
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
if (layoutobj->size[0] <= 0 || layoutobj->size[1] <= 0 || layoutobj->size[0] > layoutobj->imageSize[0] || layoutobj->size[1] > layoutobj->imageSize[1]) {
layoutobj->size[0] = layoutobj->imageSize[0];
layoutobj->size[1] = layoutobj->imageSize[1];
}
config_destroy(&cfg);
if (is_archive) PHYSFS_unmount(theme_archive_path);
#ifdef __SWITCH__
if (is_romfs) romfsUnmount("theme");
#endif
}
void GetThemePathFromConfig(char* themePath, size_t size) {

View File

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

7
common/thermalstatus.h Normal file
View File

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

View File

@ -68,10 +68,20 @@ static void launchFile(const char* path, argData_s* args)
init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf));
struct stat st;
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\n0x%x", textGetString(StrId_AppLaunchError), rc);
snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_AppLaunchError), R_MODULE(rc), R_DESCRIPTION(rc));
menuCreateMsgBox(780, 300, msg);
}
@ -79,6 +89,7 @@ static void launchFile(const char* path, argData_s* args)
uiExitLoop();
}
}
}
const loaderFuncs_s loader_builtin =
{

View File

@ -1,17 +1,25 @@
#include <switch.h>
#include <string.h>
#include <stdio.h>
#include <physfs.h>
#include "../common/common.h"
#include "nx_graphics.h"
#include "nx_touch.h"
// Define the desired framebuffer resolution (here we set it to 720p).
#define FB_WIDTH 1280
#define FB_HEIGHT 720
uint8_t* g_framebuf;
u32 g_framebuf_width;
PadState g_pad;
PadRepeater g_pad_repeater;
bool menuUpdateErrorScreen(void);
#ifdef PERF_LOG
u64 g_tickdiff_vsync=0;
u64 g_tickdiff_frame=0;
#endif
@ -34,35 +42,44 @@ int main(int argc, char **argv)
u64 start_tick=0;
#endif
padConfigureInput(8, HidNpadStyleSet_NpadStandard);
padInitializeAny(&g_pad);
padRepeaterInitialize(&g_pad_repeater, 20, 10);
hidSetNpadHandheldActivationMode(HidNpadHandheldActivationMode_Single);
touchInit();
memset(errormsg, 0, sizeof(errormsg));
appletLockExit();
appletSetScreenShotPermission(1);
appletSetScreenShotPermission(AppletScreenShotPermission_Enable);
ColorSetId theme;
ColorSetId theme = ColorSetId_Light;
rc = setsysInitialize();
if (R_FAILED(rc)) snprintf(errormsg, sizeof(errormsg)-1, "Error: setsysInitialize() failed: 0x%x.", rc);
if (R_SUCCEEDED(rc)) setsysGetColorSetId(&theme);
if (R_SUCCEEDED(rc)) {
rc = plInitialize();
if (R_FAILED(rc)) snprintf(errormsg, sizeof(errormsg)-1, "Error: plInitialize() failed: 0x%x.", rc);
setsysGetColorSetId(&theme);
setsysExit();
}
if (R_SUCCEEDED(rc)) {
rc = textInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: textInit() failed: 0x%x.", rc);
snprintf(errormsg, sizeof(errormsg)-1, "Error: textInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
if (R_SUCCEEDED(rc)) menuStartupPath();
if (R_SUCCEEDED(rc)) {
if (!PHYSFS_init(argv[0])) {
rc = 1;
snprintf(errormsg, sizeof(errormsg)-1, "Error: PHYSFS_init() failed: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
}
}
if (R_SUCCEEDED(rc)) {
rc = assetsInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: assetsInit() failed: 0x%x.", rc);
snprintf(errormsg, sizeof(errormsg)-1, "Error: assetsInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
@ -73,7 +90,7 @@ int main(int argc, char **argv)
if (R_SUCCEEDED(rc)) {
rc = netloaderInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 0x%x.", rc);
snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
@ -82,6 +99,11 @@ int main(int argc, char **argv)
snprintf(errormsg, sizeof(errormsg)-1, "Error: workerInit() failed.");
}
if (R_SUCCEEDED(rc) && !statusInit()) {
rc = 1;
snprintf(errormsg, sizeof(errormsg)-1, "Error: statusInit() failed.");
}
if (R_SUCCEEDED(rc)) menuStartup();
if (R_SUCCEEDED(rc)) {
@ -105,7 +127,7 @@ int main(int argc, char **argv)
if (R_FAILED(lastret)) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, "%s\n0x%x", textGetString(StrId_LastLoadResult), lastret);
snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_LastLoadResult), R_MODULE(lastret), R_DESCRIPTION(lastret));
menuCreateMsgBox(780, 300, msg);
}
@ -114,7 +136,7 @@ int main(int argc, char **argv)
if (errormsg[0]) error_screen = 1;
if (!error_screen) {
gfxInitDefault();
graphicsInit(FB_WIDTH, FB_HEIGHT);
}
else {
consoleInit(NULL);
@ -122,29 +144,21 @@ int main(int argc, char **argv)
printf("Press the + button to exit.\n");
}
#ifdef PERF_LOG
if (!error_screen) {
gfxWaitForVsync();
start_tick = svcGetSystemTick();
gfxWaitForVsync();
g_tickdiff_vsync = svcGetSystemTick() - start_tick;
}
#endif
while (appletMainLoop())
{
#ifdef PERF_LOG
if (!error_screen) start_tick = svcGetSystemTick();
#endif
//Scan all the inputs. This should be done once for each frame
hidScanInput();
// Scan the gamepad. This should be done once for each frame
padUpdate(&g_pad);
padRepeaterUpdate(&g_pad_repeater, padGetButtons(&g_pad) & (
HidNpadButton_AnyLeft | HidNpadButton_AnyUp | HidNpadButton_AnyRight | HidNpadButton_AnyDown
));
if (!error_screen) {
g_framebuf = gfxGetFramebuffer(&g_framebuf_width, NULL);
memset(g_framebuf, 237, gfxGetFramebufferSize());
if (!uiUpdate()) break;
g_framebuf = graphicsFrameBegin(&g_framebuf_width);
#ifdef PERF_LOG
start_tick = armGetSystemTick();
#endif
memset(g_framebuf, 237, g_framebuf_width * FB_HEIGHT);
menuLoop();
}
else {
@ -152,13 +166,11 @@ int main(int argc, char **argv)
}
if (!error_screen) {
gfxFlushBuffers();
graphicsFrameEnd();
#ifdef PERF_LOG
g_tickdiff_frame = svcGetSystemTick() - start_tick;
g_tickdiff_frame = armGetSystemTick() - start_tick;
#endif
gfxSwapBuffers();
}
else {
consoleUpdate(NULL);
@ -166,7 +178,7 @@ int main(int argc, char **argv)
}
if (!error_screen) {
gfxExit();
graphicsExit();
}
else {
consoleExit(NULL);
@ -180,41 +192,54 @@ int main(int argc, char **argv)
fontExit();
launchExit();
netloaderSignalExit();
statusExit();
workerExit();
netloaderExit();
powerExit();
assetsExit();
plExit();
setsysExit();
PHYSFS_deinit();
appletUnlockExit();
return 0;
}
u64 menuGetKeysDown(void) {
u64 keys = padGetButtonsDown(&g_pad);
keys |= padRepeaterGetButtons(&g_pad_repeater);
return keys;
}
//This is implemented here due to the hid code.
bool menuUpdate(void) {
bool exitflag = 0;
menu_s* menu = menuGetCurrent();
u32 down = hidKeysDown(CONTROLLER_P1_AUTO);
u64 down = menuGetKeysDown();
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
handleTouch(menu);
if (down & KEY_Y)
if (down & HidNpadButton_Y)
{
launchMenuNetloaderTask();
}
else if (down & KEY_A)
else if (down & HidNpadButton_X)
{
menuHandleXButton();
}
else if (down & HidNpadButton_A)
{
menuHandleAButton();
}
else if (down & KEY_B)
else if (down & HidNpadButton_B)
{
launchMenuBackTask();
}
else if(down & KEY_MINUS){
else if(down & HidNpadButton_Minus){
themeMenuStartup();
}
else if (down & KEY_PLUS)
else if (down & HidNpadButton_Plus)
{
exitflag = 1;
}
@ -222,10 +247,10 @@ bool menuUpdate(void) {
{
int move = 0;
if (down & KEY_LEFT) move--;
if (down & KEY_RIGHT) move++;
if (down & KEY_DOWN) move-=7;
if (down & KEY_UP) move+=7;
if (down & HidNpadButton_AnyLeft) move--;
if (down & HidNpadButton_AnyRight) move++;
if (down & HidNpadButton_AnyDown) move-=entries_count;
if (down & HidNpadButton_AnyUp) move+=entries_count;
int newEntry = menu->curEntry + move;
if (newEntry < 0) newEntry = 0;
@ -238,9 +263,9 @@ bool menuUpdate(void) {
bool menuUpdateErrorScreen(void) {
bool exitflag = 0;
u32 down = hidKeysDown(CONTROLLER_P1_AUTO);
u64 down = menuGetKeysDown();
if (down & KEY_PLUS)
if (down & HidNpadButton_Plus)
{
exitflag = 1;
}

View File

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

141
nx_main/nx_graphics.c Normal file
View File

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

9
nx_main/nx_graphics.h Normal file
View File

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

36
nx_main/nx_netstatus.c Normal file
View File

@ -0,0 +1,36 @@
#include "../common/common.h"
bool netstatusGetDetails(AssetId *id) {
Result rc=0;
NifmInternetConnectionType contype;
u32 wifiStrength=0;
NifmInternetConnectionStatus connectionStatus;
rc = nifmGetInternetConnectionStatus(&contype, &wifiStrength, &connectionStatus);
if (R_FAILED(rc)) {
*id = AssetId_airplane_icon;
return true;
}
if (contype == NifmInternetConnectionType_Ethernet) {
if (connectionStatus != NifmInternetConnectionStatus_Connected)
*id = AssetId_eth_none_icon;
else
*id = AssetId_eth_icon;
return true;
}
if (wifiStrength==0) {
*id = AssetId_wifi_none_icon;
return true;
}
if (wifiStrength==3)
*id = AssetId_wifi3_icon;
if (wifiStrength==2)
*id = AssetId_wifi2_icon;
else
*id = AssetId_wifi1_icon;
return true;
}

View File

@ -1,13 +1,14 @@
#include <switch.h>
#include "../common/common.h"
static bool psmInitialized;
static bool psmCacheInitialized;
static uint32_t psmCacheCharge;
static bool psmCacheIsCharging;
static bool powerInitialized;
static bool powerCacheInitialized;
static uint32_t powerCacheCharge;
static bool powerCacheIsCharging;
static PsmSession powerSession;
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
ChargerType charger = ChargerType_None;
PsmChargerType charger = PsmChargerType_Unconnected;
bool hwReadsSucceeded = false;
bool use_cache = false;
Result rc = 0;
@ -15,9 +16,9 @@ bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
*isCharging = false;
*batteryCharge = 0;
if (psmInitialized) {
if (psmCacheInitialized) {
rc = psmWaitStateChangeEvent(0);
if (powerInitialized) {
if (powerCacheInitialized) {
rc = psmWaitStateChangeEvent(&powerSession, 0);
if (R_FAILED(rc)) use_cache = true;
}
@ -25,17 +26,17 @@ bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
rc = psmGetBatteryChargePercentage(batteryCharge);
hwReadsSucceeded = R_SUCCEEDED(rc);
if (use_cache) {
*isCharging = psmCacheIsCharging;
*isCharging = powerCacheIsCharging;
}
else {
rc = psmGetChargerType(&charger);
hwReadsSucceeded &= R_SUCCEEDED(rc);
*isCharging = (charger > ChargerType_None);
*isCharging = (charger != PsmChargerType_Unconnected);
}
psmCacheCharge = *batteryCharge;
psmCacheIsCharging = *isCharging;
psmCacheInitialized = true;
powerCacheCharge = *batteryCharge;
powerCacheIsCharging = *isCharging;
powerCacheInitialized = true;
}
return hwReadsSucceeded;
@ -45,18 +46,18 @@ void powerInit(void) {
uint32_t charge=0;
bool isCharging=0;
psmCacheInitialized = false;
psmCacheCharge = 0;
psmCacheIsCharging = false;
powerCacheInitialized = false;
powerCacheCharge = 0;
powerCacheIsCharging = false;
if (!psmInitialized) {
if (!powerInitialized) {
Result rc = psmInitialize();
if (R_SUCCEEDED(rc)) {
rc = psmBindStateChangeEvent(1, 1, 1);
rc = psmBindStateChangeEvent(&powerSession, 1, 1, 1);
if (R_FAILED(rc)) psmExit();
if (R_SUCCEEDED(rc)) {
psmInitialized = true;
powerInitialized = true;
powerGetDetails(&charge, &isCharging);//Init the cache.
}
}
@ -64,9 +65,10 @@ void powerInit(void) {
}
void powerExit(void) {
if (psmInitialized) {
if (powerInitialized) {
psmUnbindStateChangeEvent(&powerSession);
psmExit();
psmInitialized = false;
psmCacheInitialized = false;
powerInitialized = false;
powerCacheInitialized = false;
}
}

View File

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

View File

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

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

View File

@ -1,6 +1,7 @@
#include <SFML/Graphics.hpp>
#include <string.h>
#include <math.h>
#include <physfs.h>
extern "C" {
@ -10,18 +11,20 @@ extern "C" {
color_t pixels[720][1280];
int main()
int main(int argc, char **argv)
{
sf::RenderWindow window(sf::VideoMode(1280, 720), "Test");
window.setFramerateLimit(60);
menuStartupPath();
PHYSFS_init(argv[0]);
assetsInit();
themeStartup(THEME_PRESET_LIGHT);
textInit();
fontInitialize();
netloaderInit();
workerInit();
statusInit();
menuStartup();
while (window.isOpen())
@ -56,10 +59,12 @@ int main()
}
netloaderSignalExit();
statusExit();
workerExit();
netloaderExit();
fontExit();
assetsExit();
PHYSFS_deinit();
return 0;
}
@ -72,6 +77,8 @@ extern "C" bool menuUpdate(void) {
int new_esc_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Escape);
static int return_state = 0;
int new_return_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Return);
static int x_state = 0;
int new_x_state = sf::Keyboard::isKeyPressed(sf::Keyboard::X);
static int y_state = 0;
int new_y_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Y);
static int t_state = 0;
@ -80,7 +87,11 @@ extern "C" bool menuUpdate(void) {
if(!new_y_state && y_state)
{
launchMenuNetloaderTask();
}
if(!new_x_state && x_state)
{
menuHandleXButton();
}
if (!new_esc_state && esc_state)

6
pc_main/pc_netstatus.c Normal file
View File

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

View File

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

BIN
resources/airplane_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
resources/eth_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

BIN
resources/eth_none_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

BIN
resources/wifi1_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
resources/wifi2_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
resources/wifi3_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB