├── .gitignore ├── LICENSE.md ├── Makefile ├── Makefile.nx ├── Makefile.pc ├── README.md ├── assets ├── airplane_icon.bin ├── battery_icon.bin ├── charging_icon.bin ├── eth_icon.bin ├── eth_none_icon.bin ├── folder_icon.bin ├── hbmenu_logo_dark.bin ├── hbmenu_logo_light.bin ├── invalid_icon.bin ├── theme_icon_dark.bin ├── theme_icon_light.bin ├── wifi1_icon.bin ├── wifi2_icon.bin ├── wifi3_icon.bin └── wifi_none_icon.bin ├── assets_unused ├── button_a_dark.bin ├── button_a_light.bin ├── button_b_dark.bin ├── button_b_light.bin ├── interuimedium20.nxfnt ├── interuimedium28.nxfnt ├── interuimedium30.nxfnt ├── interuimedium42.nxfnt ├── interuiregular14.nxfnt ├── interuiregular18.nxfnt ├── interuiregular20.nxfnt ├── interuiregular24.nxfnt ├── tahoma12.nxfnt └── tahoma24.nxfnt ├── common ├── assets.c ├── assets.h ├── c11threads.h ├── common.h ├── font.c ├── font.h ├── language.c ├── language.h ├── launch.c ├── launch.h ├── main.h ├── math.c ├── math.h ├── menu-entry.c ├── menu-list.c ├── menu.c ├── menu.h ├── message-box.c ├── message-box.h ├── netloader.c ├── netloader.h ├── netstatus.h ├── power.h ├── status.c ├── status.h ├── text.c ├── text.h ├── theme.c ├── theme.h ├── thermalstatus.h ├── ui.c ├── ui.h ├── worker.c └── worker.h ├── icon.jpg ├── nx_main ├── loaders │ └── builtin.c ├── main.c ├── nx_audio.c ├── nx_graphics.c ├── nx_graphics.h ├── nx_launch.c ├── nx_netstatus.c ├── nx_power.c ├── nx_thermalstatus.c ├── nx_touch.c └── nx_touch.h ├── pc_main ├── main.cpp ├── pc_launch.c ├── pc_netstatus.c ├── pc_power.c └── pc_thermalstatus.c └── resources ├── airplane_icon.png ├── battery_icon.png ├── charging_icon.png ├── eth_icon.png ├── eth_none_icon.png ├── folder_icon.png ├── invalid_icon.png ├── theme_icon_dark.png ├── theme_icon_light.png ├── wifi1_icon.png ├── wifi2_icon.png ├── wifi3_icon.png └── wifi_none_icon.png /.gitignore: -------------------------------------------------------------------------------- 1 | .*/ 2 | *~ 3 | *.exe 4 | *.o 5 | test 6 | tahoma12.c 7 | tahoma24.c 8 | *.zip 9 | build 10 | *.elf 11 | *.nso 12 | *.pfs0 13 | *.nacp 14 | *.nro 15 | test.* 16 | switch 17 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017-2018 nx-hbmenu Authors 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export APP_VERSION := 3.6.0 2 | 3 | ifeq ($(RELEASE),) 4 | export APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always) 5 | endif 6 | 7 | .PHONY: clean all nx pc dist-bin 8 | 9 | all: nx pc 10 | 11 | romfs: 12 | @mkdir -p romfs 13 | 14 | romfs/assets.zip : romfs assets 15 | @rm -f romfs/assets.zip 16 | @zip -rj romfs/assets.zip assets 17 | 18 | dist-bin: romfs/assets.zip 19 | $(MAKE) -f Makefile.nx dist-bin 20 | 21 | nx: romfs/assets.zip 22 | $(MAKE) -f Makefile.nx 23 | 24 | pc: romfs/assets.zip 25 | $(MAKE) -f Makefile.pc 26 | 27 | clean: 28 | @rm -Rf romfs 29 | $(MAKE) -f Makefile.pc clean 30 | $(MAKE) -f Makefile.nx clean 31 | -------------------------------------------------------------------------------- /Makefile.nx: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITPRO)/libnx/switch_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | # ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) 19 | # 20 | # NO_ICON: if set to anything, do not use icon. 21 | # NO_NACP: if set to anything, no .nacp file is generated. 22 | # APP_TITLE is the name of the app stored in the .nacp file (Optional) 23 | # APP_AUTHOR is the author of the app stored in the .nacp file (Optional) 24 | # APP_VERSION is the version of the app stored in the .nacp file (Optional) 25 | # APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) 26 | # ICON is the filename of the icon (.jpg), relative to the project folder. 27 | # If not set, it attempts to use one of the following (in this order): 28 | # - .jpg 29 | # - icon.jpg 30 | # - /default_icon.jpg 31 | # 32 | # CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. 33 | # If not set, it attempts to use one of the following (in this order): 34 | # - .json 35 | # - config.json 36 | # If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead 37 | # of a homebrew executable (.nro). This is intended to be used for sysmodules. 38 | # NACP building is skipped as well. 39 | #--------------------------------------------------------------------------------- 40 | TARGET := $(notdir $(CURDIR)) 41 | BUILD := build 42 | SOURCES := common/ nx_main/ nx_main/loaders/ 43 | DATA := data 44 | INCLUDES := include 45 | ROMFS := romfs 46 | 47 | DIST_PATH := $(TARGET)_v$(APP_VERSION) 48 | 49 | APP_AUTHOR := switchbrew 50 | 51 | #--------------------------------------------------------------------------------- 52 | # options for code generation 53 | #--------------------------------------------------------------------------------- 54 | ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE 55 | 56 | CFLAGS := -g -Wall -O2 -ffunction-sections \ 57 | $(ARCH) $(DEFINES) `freetype-config --cflags` 58 | 59 | CFLAGS += $(INCLUDE) -D__SWITCH__ -DVERSION=\"v$(APP_VERSION)\" 60 | 61 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 62 | 63 | ASFLAGS := -g $(ARCH) 64 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 65 | 66 | LIBS := -ldeko3d -lphysfs `freetype-config --libs` -lconfig -lturbojpeg -lpng 67 | 68 | #--------------------------------------------------------------------------------- 69 | # list of directories containing libraries, this must be the top level containing 70 | # include and lib 71 | #--------------------------------------------------------------------------------- 72 | LIBDIRS := $(PORTLIBS) $(LIBNX) 73 | 74 | 75 | #--------------------------------------------------------------------------------- 76 | # no real need to edit anything past this point unless you need to add additional 77 | # rules for different file extensions 78 | #--------------------------------------------------------------------------------- 79 | ifneq ($(BUILD),$(notdir $(CURDIR))) 80 | #--------------------------------------------------------------------------------- 81 | 82 | export OUTPUT := $(CURDIR)/$(TARGET) 83 | export TOPDIR := $(CURDIR) 84 | 85 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 86 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 87 | 88 | export DEPSDIR := $(CURDIR)/$(BUILD) 89 | 90 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 91 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 92 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 93 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 94 | 95 | #--------------------------------------------------------------------------------- 96 | # use CXX for linking C++ projects, CC for standard C 97 | #--------------------------------------------------------------------------------- 98 | ifeq ($(strip $(CPPFILES)),) 99 | #--------------------------------------------------------------------------------- 100 | export LD := $(CC) 101 | #--------------------------------------------------------------------------------- 102 | else 103 | #--------------------------------------------------------------------------------- 104 | export LD := $(CXX) 105 | #--------------------------------------------------------------------------------- 106 | endif 107 | #--------------------------------------------------------------------------------- 108 | 109 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 110 | export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 111 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 112 | export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) 113 | 114 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 115 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 116 | -I$(CURDIR)/$(BUILD) 117 | 118 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 119 | 120 | ifeq ($(strip $(CONFIG_JSON)),) 121 | jsons := $(wildcard *.json) 122 | ifneq (,$(findstring $(TARGET).json,$(jsons))) 123 | export APP_JSON := $(TOPDIR)/$(TARGET).json 124 | else 125 | ifneq (,$(findstring config.json,$(jsons))) 126 | export APP_JSON := $(TOPDIR)/config.json 127 | endif 128 | endif 129 | else 130 | export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) 131 | endif 132 | 133 | ifeq ($(strip $(ICON)),) 134 | icons := $(wildcard *.jpg) 135 | ifneq (,$(findstring $(TARGET).jpg,$(icons))) 136 | export APP_ICON := $(TOPDIR)/$(TARGET).jpg 137 | else 138 | ifneq (,$(findstring icon.jpg,$(icons))) 139 | export APP_ICON := $(TOPDIR)/icon.jpg 140 | endif 141 | endif 142 | else 143 | export APP_ICON := $(TOPDIR)/$(ICON) 144 | endif 145 | 146 | ifeq ($(strip $(NO_ICON)),) 147 | export NROFLAGS += --icon=$(APP_ICON) 148 | endif 149 | 150 | ifeq ($(strip $(NO_NACP)),) 151 | export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp 152 | endif 153 | 154 | ifneq ($(APP_TITLEID),) 155 | export NACPFLAGS += --titleid=$(APP_TITLEID) 156 | endif 157 | 158 | ifneq ($(ROMFS),) 159 | export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) 160 | endif 161 | 162 | .PHONY: $(BUILD) clean all dist-bin 163 | 164 | #--------------------------------------------------------------------------------- 165 | all: $(BUILD) 166 | 167 | $(BUILD): 168 | @[ -d $@ ] || mkdir -p $@ 169 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.nx 170 | 171 | #--------------------------------------------------------------------------------- 172 | clean: 173 | @echo clean ... 174 | ifeq ($(strip $(APP_JSON)),) 175 | @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf 176 | else 177 | @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf 178 | endif 179 | 180 | #--------------------------------------------------------------------------------- 181 | dist-bin: all 182 | @mkdir -p $(DIST_PATH) 183 | @cp $(OUTPUT).nro $(DIST_PATH)/hbmenu.nro 184 | @zip -rj $(DIST_PATH).zip $(DIST_PATH) 185 | 186 | #--------------------------------------------------------------------------------- 187 | else 188 | .PHONY: all 189 | 190 | DEPENDS := $(OFILES:.o=.d) 191 | 192 | #--------------------------------------------------------------------------------- 193 | # main targets 194 | #--------------------------------------------------------------------------------- 195 | ifeq ($(strip $(APP_JSON)),) 196 | 197 | all : $(OUTPUT).nro 198 | 199 | ifeq ($(strip $(NO_NACP)),) 200 | $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp 201 | else 202 | $(OUTPUT).nro : $(OUTPUT).elf 203 | endif 204 | 205 | else 206 | 207 | all : $(OUTPUT).nsp 208 | 209 | $(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm 210 | 211 | $(OUTPUT).nso : $(OUTPUT).elf 212 | 213 | endif 214 | 215 | menu.o : $(TOPDIR)/Makefile 216 | $(OUTPUT).elf : $(OFILES) 217 | 218 | $(OFILES_SRC) : $(HFILES_BIN) 219 | 220 | #--------------------------------------------------------------------------------- 221 | # you need a rule like this for each extension you use as binary data 222 | #--------------------------------------------------------------------------------- 223 | %.bin.o %_bin.h : %.bin 224 | #--------------------------------------------------------------------------------- 225 | @echo $(notdir $<) 226 | @$(bin2o) 227 | 228 | -include $(DEPENDS) 229 | 230 | #--------------------------------------------------------------------------------------- 231 | endif 232 | #--------------------------------------------------------------------------------------- 233 | -------------------------------------------------------------------------------- /Makefile.pc: -------------------------------------------------------------------------------- 1 | HOST_OS := $(shell uname -s) 2 | 3 | ifeq ($(strip $(HOST_OS)),Darwin) 4 | BIN2S_FLAGS := --apple-llvm 5 | endif 6 | 7 | ifneq (,$(findstring MINGW,$(HOST_OS))) 8 | EXTRA_CFLAGS="-D__USE_MINGW_ANSI_STDIO" 9 | EXTRA_LDFLAGS="-lws2_32" 10 | endif 11 | 12 | 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 \ 13 | common/menu.c common/font.c common/language.c common/launch.c common/worker.c common/status.c \ 14 | common/menu-entry.c common/menu-list.c common/message-box.c common/text.c \ 15 | common/ui.c common/assets.c common/math.c common/theme.c \ 16 | common/netloader.c 17 | gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config --libs` -lm -lphysfs -lz -lconfig -lturbojpeg -lpng $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@ 18 | 19 | clean: 20 | rm -rf build_pc/ test test.* 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Usage 2 | 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. 3 | 4 | ### Download 5 | The latest release is available from the [releases](https://github.com/switchbrew/nx-hbmenu/releases/latest) page. 6 | 7 | ### Building 8 | Build for the Nintendo Switch with ```make nx``` and for the PC with ```make pc```. 9 | Running ```make``` builds for both systems. 10 | 11 | The following [pacman packages](https://devkitpro.org/wiki/devkitPro_pacman) are required to build for Switch: 12 | - `switch-dev` 13 | - `switch-freetype` 14 | - `switch-libconfig` 15 | - `switch-libjpeg-turbo` 16 | - `switch-physfs` 17 | 18 | The following libraries are required to build for PC: 19 | - `libfreetype` 20 | - `libconfig` 21 | - `libjpeg-turbo` 22 | - `libphysfs` 23 | - `sfml` 24 | 25 | Building for Switch/PC requires `zip`. 26 | 27 | Since C11 threads are used, building for the PC may fail if C11 threads are not available. 28 | 29 | #### Credits 30 | 31 | * This uses code based on 3DS [new-hbmenu](https://github.com/fincs/new-hbmenu). 32 | * `libjpeg-turbo` is used for handling JPEG icons. This library doesn't support lossless JPEG (likewise for official sw which uses `libjpeg-turbo`). 33 | * `c11threads` is used for implement C11 Threads on Windows and macOS. -------------------------------------------------------------------------------- /assets/airplane_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/airplane_icon.bin -------------------------------------------------------------------------------- /assets/battery_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/battery_icon.bin -------------------------------------------------------------------------------- /assets/charging_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/charging_icon.bin -------------------------------------------------------------------------------- /assets/eth_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/eth_icon.bin -------------------------------------------------------------------------------- /assets/eth_none_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/eth_none_icon.bin -------------------------------------------------------------------------------- /assets/folder_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/folder_icon.bin -------------------------------------------------------------------------------- /assets/hbmenu_logo_dark.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/hbmenu_logo_dark.bin -------------------------------------------------------------------------------- /assets/hbmenu_logo_light.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/hbmenu_logo_light.bin -------------------------------------------------------------------------------- /assets/invalid_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/invalid_icon.bin -------------------------------------------------------------------------------- /assets/theme_icon_dark.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/theme_icon_dark.bin -------------------------------------------------------------------------------- /assets/theme_icon_light.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/theme_icon_light.bin -------------------------------------------------------------------------------- /assets/wifi1_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/wifi1_icon.bin -------------------------------------------------------------------------------- /assets/wifi2_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/wifi2_icon.bin -------------------------------------------------------------------------------- /assets/wifi3_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/wifi3_icon.bin -------------------------------------------------------------------------------- /assets/wifi_none_icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets/wifi_none_icon.bin -------------------------------------------------------------------------------- /assets_unused/button_a_dark.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/button_a_dark.bin -------------------------------------------------------------------------------- /assets_unused/button_a_light.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/button_a_light.bin -------------------------------------------------------------------------------- /assets_unused/button_b_dark.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/button_b_dark.bin -------------------------------------------------------------------------------- /assets_unused/button_b_light.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/button_b_light.bin -------------------------------------------------------------------------------- /assets_unused/interuimedium20.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/interuimedium20.nxfnt -------------------------------------------------------------------------------- /assets_unused/interuimedium28.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/interuimedium28.nxfnt -------------------------------------------------------------------------------- /assets_unused/interuimedium30.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/interuimedium30.nxfnt -------------------------------------------------------------------------------- /assets_unused/interuimedium42.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/interuimedium42.nxfnt -------------------------------------------------------------------------------- /assets_unused/interuiregular14.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/interuiregular14.nxfnt -------------------------------------------------------------------------------- /assets_unused/interuiregular18.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/interuiregular18.nxfnt -------------------------------------------------------------------------------- /assets_unused/interuiregular20.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/interuiregular20.nxfnt -------------------------------------------------------------------------------- /assets_unused/interuiregular24.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/interuiregular24.nxfnt -------------------------------------------------------------------------------- /assets_unused/tahoma12.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/tahoma12.nxfnt -------------------------------------------------------------------------------- /assets_unused/tahoma24.nxfnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/assets_unused/tahoma24.nxfnt -------------------------------------------------------------------------------- /common/assets.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | #include 5 | 6 | #define GENASSET(_p, _mode, _w, _h) {{.path = _p, .imageMode = _mode, .imageSize = {_w, _h}}, {}} 7 | 8 | static bool g_assetsInitialized = 0; 9 | assetsDataEntry g_assetsDataList[AssetId_Max][2] = { 10 | GENASSET("battery_icon.bin", IMAGE_MODE_RGBA32, 24, 24), 11 | GENASSET("charging_icon.bin", IMAGE_MODE_RGBA32, 24, 24), 12 | GENASSET("folder_icon.bin", IMAGE_MODE_RGB24, 256, 256), 13 | GENASSET("invalid_icon.bin", IMAGE_MODE_RGB24, 256, 256), 14 | GENASSET("hbmenu_logo_dark.bin", IMAGE_MODE_RGBA32, 140, 60), 15 | GENASSET("hbmenu_logo_light.bin", IMAGE_MODE_RGBA32, 140, 60), 16 | GENASSET("theme_icon_dark.bin", IMAGE_MODE_RGB24, 256, 256), 17 | GENASSET("theme_icon_light.bin", IMAGE_MODE_RGB24, 256, 256), 18 | GENASSET("airplane_icon.bin", IMAGE_MODE_RGBA32, 24, 24), 19 | GENASSET("wifi_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24), 20 | GENASSET("wifi1_icon.bin", IMAGE_MODE_RGBA32, 24, 24), 21 | GENASSET("wifi2_icon.bin", IMAGE_MODE_RGBA32, 24, 24), 22 | GENASSET("wifi3_icon.bin", IMAGE_MODE_RGBA32, 24, 24), 23 | GENASSET("eth_icon.bin", IMAGE_MODE_RGBA32, 24, 24), 24 | GENASSET("eth_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24), 25 | GENASSET("", IMAGE_MODE_RGB24, 1280, 720), 26 | }; 27 | 28 | static void assetsClearEntry(assetsDataEntry *entry) { 29 | free(entry->buffer); 30 | 31 | entry->size = 0; 32 | entry->buffer = NULL; 33 | 34 | memset(entry, 0, sizeof(*entry)); 35 | } 36 | 37 | static void assetsSetPixelSize(assetsDataEntry *entry) { 38 | switch (entry->imageMode) { 39 | case IMAGE_MODE_RGB24: 40 | entry->pixSize = 3; 41 | break; 42 | 43 | case IMAGE_MODE_RGBA32: 44 | entry->pixSize = 4; 45 | break; 46 | } 47 | } 48 | 49 | Result assetsInit(void) { 50 | bool ret=false; 51 | int i, stopi; 52 | assetsDataEntry *entry = NULL; 53 | char tmp_path[PATH_MAX]; 54 | 55 | if (g_assetsInitialized) return 0; 56 | 57 | #ifdef __SWITCH__ 58 | Result rc = romfsInit(); 59 | if (R_FAILED(rc)) return rc; 60 | #endif 61 | 62 | memset(tmp_path, 0, sizeof(tmp_path)); 63 | 64 | #ifdef __SWITCH__ 65 | strncpy(tmp_path, "romfs:/assets.zip", sizeof(tmp_path)-1); 66 | #else 67 | snprintf(tmp_path, sizeof(tmp_path)-1, "%s/romfs/assets.zip", menuGetRootBasePath()); 68 | #endif 69 | 70 | if (PHYSFS_mount(tmp_path, "", 0)) { 71 | ret=true; 72 | for (i=0; ipath[0]) { 76 | ret = assetsLoadData(i, NULL, NULL); 77 | if (!ret) break; 78 | } 79 | } 80 | 81 | if (!ret) { 82 | for (i=0; i= AssetId_Max) return false; 208 | 209 | assetsDataEntry *entry = &g_assetsDataList[id][path ? 1 : 0]; 210 | if (entry->initialized) return false; 211 | 212 | if (path) memset(entry, 0, sizeof(*entry)); 213 | 214 | if (imageSize) { 215 | entry->imageSize[0] = imageSize[0]; 216 | entry->imageSize[1] = imageSize[1]; 217 | } 218 | 219 | if (path) entry->imageMode = g_assetsDataList[id][0].imageMode; 220 | assetsSetPixelSize(entry); 221 | entry->size = entry->imageSize[0] * entry->imageSize[1] * entry->pixSize; 222 | 223 | if (path) strncpy(entry->path, path, sizeof(entry->path)-1); 224 | 225 | const char* ext = getExtension(entry->path); 226 | bool ret=true; 227 | size_t filesize=0; 228 | if (ext==NULL) ret = false; 229 | 230 | u8 *data_buf = NULL; 231 | 232 | if (ret) { 233 | entry->buffer = (u8*)malloc(entry->size); 234 | if (entry->buffer) memset(entry->buffer, 0, entry->size); 235 | else ret = false; 236 | } 237 | 238 | if (ret) ret = assetsPhysfsReadFile(entry->path, &data_buf, &filesize, false); 239 | 240 | if (ret) { 241 | if (strcasecmp(ext, ".bin")==0) { 242 | if (filesize != entry->size) ret = false; 243 | 244 | if (ret) memcpy(entry->buffer, data_buf, entry->size); 245 | } 246 | else if (strcasecmp(ext, ".jpg")==0 || strcasecmp(ext, ".jpeg")==0) 247 | ret = assetsLoadJpgFromMemory(data_buf, filesize, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]); 248 | else if (strcasecmp(ext, ".png")==0) 249 | ret = assetsLoadPngFromMemory(data_buf, filesize, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]); 250 | else 251 | ret = false; // File extension not recognized. 252 | } 253 | 254 | if (ret) entry->initialized = true; 255 | else assetsClearEntry(entry); 256 | 257 | free(data_buf); 258 | 259 | return ret; 260 | } 261 | 262 | void assetsGetData(AssetId id, assetsDataEntry **out) { 263 | if (out) *out = NULL; 264 | if (id < 0 || id >= AssetId_Max) return; 265 | 266 | u32 pos = g_assetsDataList[id][1].initialized ? 1 : 0; 267 | assetsDataEntry *entry = &g_assetsDataList[id][pos]; 268 | if (entry->initialized) *out = entry; 269 | } 270 | 271 | u8 *assetsGetDataBuffer(AssetId id) { 272 | assetsDataEntry *entry = NULL; 273 | 274 | assetsGetData(id, &entry); 275 | return entry ? entry->buffer : NULL; 276 | } 277 | 278 | -------------------------------------------------------------------------------- /common/assets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | typedef enum { 5 | AssetId_battery_icon, 6 | AssetId_charging_icon, 7 | AssetId_folder_icon, 8 | AssetId_invalid_icon, 9 | AssetId_hbmenu_logo_dark, 10 | AssetId_hbmenu_logo_light, 11 | AssetId_theme_icon_dark, 12 | AssetId_theme_icon_light, 13 | AssetId_airplane_icon, 14 | AssetId_wifi_none_icon, 15 | AssetId_wifi1_icon, 16 | AssetId_wifi2_icon, 17 | AssetId_wifi3_icon, 18 | AssetId_eth_icon, 19 | AssetId_eth_none_icon, 20 | AssetId_background_image, 21 | 22 | AssetId_Max, 23 | } AssetId; 24 | 25 | typedef struct { 26 | bool initialized; 27 | u8 *buffer; 28 | size_t size; 29 | ImageMode imageMode; 30 | size_t pixSize; 31 | size_t imageSize[2]; 32 | char path[PATH_MAX]; 33 | } assetsDataEntry; 34 | 35 | Result assetsInit(void); 36 | void assetsExit(void); 37 | void assetsClearTheme(void); 38 | bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term); 39 | bool assetsLoadData(AssetId id, const char *path, int *imageSize); 40 | void assetsGetData(AssetId id, assetsDataEntry **out); 41 | u8 *assetsGetDataBuffer(AssetId id); 42 | 43 | bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height); 44 | 45 | -------------------------------------------------------------------------------- /common/c11threads.h: -------------------------------------------------------------------------------- 1 | /* 2 | c11threads 3 | 4 | Authors: 5 | John Tsiombikas - original POSIX threads wrapper 6 | Oliver Old - win32 implementation 7 | 8 | I place this piece of code in the public domain. Feel free to use as you see 9 | fit. I'd appreciate it if you keep my name at the top of the code somewhere, but 10 | whatever. 11 | 12 | Main project site: https://github.com/jtsiomb/c11threads 13 | */ 14 | #ifndef C11THREADS_H_ 15 | #define C11THREADS_H_ 16 | 17 | /* If you wish to use this with pthread-win32 (i.e. use the POSIX threads wrapper 18 | * instead of the native win32 API implementation of C11 threads), then just 19 | * define C11THREADS_PTHREAD_WIN32 before including this header file. 20 | */ 21 | #if defined(_WIN32) && !defined(C11THREADS_PTHREAD_WIN32) 22 | #define C11THREADS_WIN32 23 | #endif 24 | 25 | /* If your compiler does not support the inline keyword, or supports it with 26 | * some different variation of prefix or suffix underscores, you can define 27 | * C11THREADS_INLINE before including this header file. 28 | */ 29 | #ifndef C11THREADS_INLINE 30 | /* C99 compilers will have inline */ 31 | #if __STDC_VERSION__ >= 199901L 32 | #define C11THREADS_INLINE inline 33 | /* C++ has inline */ 34 | #elif defined(__cplusplus) 35 | #define C11THREADS_INLINE inline 36 | /* MSVC has inline from VS 2015 but supports __inline in older versions */ 37 | #elif defined(_MSC_VER) 38 | #if _MSC_VER >= 1900 39 | #define C11THREADS_INLINE inline 40 | #else 41 | #define C11THREADS_INLINE __inline 42 | #endif 43 | /* for every other case, just gamble on having __inline__, and let the user 44 | * define C11THREADS_INLINE if it breaks 45 | */ 46 | #else 47 | #define C11THREADS_INLINE __inline__ 48 | #endif 49 | #endif /* !defined C11THREADS_INLINE */ 50 | 51 | #include 52 | 53 | #ifndef TIME_UTC 54 | #define TIME_UTC 1 55 | #endif 56 | 57 | #ifdef __cplusplus 58 | extern "C" { 59 | #endif 60 | 61 | typedef int (*thrd_start_t)(void*); 62 | typedef void (*tss_dtor_t)(void*); 63 | 64 | enum { 65 | mtx_plain = 0, 66 | mtx_recursive = 1, 67 | mtx_timed = 2, 68 | }; 69 | 70 | enum { 71 | thrd_success, 72 | thrd_timedout, 73 | thrd_busy, 74 | thrd_error, 75 | thrd_nomem 76 | }; 77 | 78 | #ifndef C11THREADS_WIN32 79 | /* C11 threads over POSIX threads as thin static inline wrapper functions */ 80 | #include 81 | #include 82 | #include 83 | #include /* for sched_yield */ 84 | #include 85 | 86 | #ifndef thread_local 87 | #define thread_local _Thread_local 88 | #endif 89 | 90 | #define ONCE_FLAG_INIT PTHREAD_ONCE_INIT 91 | #define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS 92 | 93 | #ifdef __APPLE__ 94 | /* Darwin doesn't implement timed mutexes currently */ 95 | #define C11THREADS_NO_TIMED_MUTEX 96 | #include 97 | #ifndef __MAC_10_15 98 | #define C11THREADS_NO_TIMESPEC_GET 99 | #endif 100 | #elif __STDC_VERSION__ < 201112L 101 | #define C11THREADS_NO_TIMESPEC_GET 102 | #endif 103 | 104 | #ifdef C11THREADS_NO_TIMED_MUTEX 105 | #define C11THREADS_TIMEDLOCK_POLL_INTERVAL 5000000 /* 5 ms */ 106 | #endif 107 | 108 | /* types */ 109 | typedef pthread_t thrd_t; 110 | typedef pthread_mutex_t mtx_t; 111 | typedef pthread_cond_t cnd_t; 112 | typedef pthread_key_t tss_t; 113 | typedef pthread_once_t once_flag; 114 | 115 | /* ---- thread management ---- */ 116 | 117 | static C11THREADS_INLINE int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) 118 | { 119 | int res = pthread_create(thr, 0, (void*(*)(void*))func, arg); 120 | if(res == 0) { 121 | return thrd_success; 122 | } 123 | return res == ENOMEM ? thrd_nomem : thrd_error; 124 | } 125 | 126 | static C11THREADS_INLINE void thrd_exit(int res) 127 | { 128 | pthread_exit((void*)(intptr_t)res); 129 | } 130 | 131 | static C11THREADS_INLINE int thrd_join(thrd_t thr, int *res) 132 | { 133 | void *retval; 134 | 135 | if(pthread_join(thr, &retval) != 0) { 136 | return thrd_error; 137 | } 138 | if(res) { 139 | *res = (int)(intptr_t)retval; 140 | } 141 | return thrd_success; 142 | } 143 | 144 | static C11THREADS_INLINE int thrd_detach(thrd_t thr) 145 | { 146 | return pthread_detach(thr) == 0 ? thrd_success : thrd_error; 147 | } 148 | 149 | static C11THREADS_INLINE thrd_t thrd_current(void) 150 | { 151 | return pthread_self(); 152 | } 153 | 154 | static C11THREADS_INLINE int thrd_equal(thrd_t a, thrd_t b) 155 | { 156 | return pthread_equal(a, b); 157 | } 158 | 159 | static C11THREADS_INLINE int thrd_sleep(const struct timespec *ts_in, struct timespec *rem_out) 160 | { 161 | if(nanosleep(ts_in, rem_out) < 0) { 162 | if(errno == EINTR) return -1; 163 | return -2; 164 | } 165 | return 0; 166 | } 167 | 168 | static C11THREADS_INLINE void thrd_yield(void) 169 | { 170 | sched_yield(); 171 | } 172 | 173 | /* ---- mutexes ---- */ 174 | 175 | static C11THREADS_INLINE int mtx_init(mtx_t *mtx, int type) 176 | { 177 | int res; 178 | pthread_mutexattr_t attr; 179 | 180 | pthread_mutexattr_init(&attr); 181 | 182 | if(type & mtx_timed) { 183 | #ifdef PTHREAD_MUTEX_TIMED_NP 184 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_TIMED_NP); 185 | #else 186 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); 187 | #endif 188 | } 189 | if(type & mtx_recursive) { 190 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 191 | } 192 | 193 | res = pthread_mutex_init(mtx, &attr) == 0 ? thrd_success : thrd_error; 194 | pthread_mutexattr_destroy(&attr); 195 | return res; 196 | } 197 | 198 | static C11THREADS_INLINE void mtx_destroy(mtx_t *mtx) 199 | { 200 | pthread_mutex_destroy(mtx); 201 | } 202 | 203 | static C11THREADS_INLINE int mtx_lock(mtx_t *mtx) 204 | { 205 | int res = pthread_mutex_lock(mtx); 206 | return res == 0 ? thrd_success : thrd_error; 207 | } 208 | 209 | static C11THREADS_INLINE int mtx_trylock(mtx_t *mtx) 210 | { 211 | int res = pthread_mutex_trylock(mtx); 212 | if(res == EBUSY) { 213 | return thrd_busy; 214 | } 215 | return res == 0 ? thrd_success : thrd_error; 216 | } 217 | 218 | static C11THREADS_INLINE int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) 219 | { 220 | int res = 0; 221 | #ifdef C11THREADS_NO_TIMED_MUTEX 222 | /* fake a timedlock by polling trylock in a loop and waiting for a bit */ 223 | struct timeval now; 224 | struct timespec sleeptime; 225 | 226 | sleeptime.tv_sec = 0; 227 | sleeptime.tv_nsec = C11THREADS_TIMEDLOCK_POLL_INTERVAL; 228 | 229 | while((res = pthread_mutex_trylock(mtx)) == EBUSY) { 230 | gettimeofday(&now, NULL); 231 | 232 | if(now.tv_sec > ts->tv_sec || (now.tv_sec == ts->tv_sec && 233 | (now.tv_usec * 1000) >= ts->tv_nsec)) { 234 | return thrd_timedout; 235 | } 236 | 237 | nanosleep(&sleeptime, NULL); 238 | } 239 | #else 240 | if((res = pthread_mutex_timedlock(mtx, ts)) == ETIMEDOUT) { 241 | return thrd_timedout; 242 | } 243 | #endif 244 | return res == 0 ? thrd_success : thrd_error; 245 | } 246 | 247 | static C11THREADS_INLINE int mtx_unlock(mtx_t *mtx) 248 | { 249 | return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error; 250 | } 251 | 252 | /* ---- condition variables ---- */ 253 | 254 | static C11THREADS_INLINE int cnd_init(cnd_t *cond) 255 | { 256 | return pthread_cond_init(cond, 0) == 0 ? thrd_success : thrd_error; 257 | } 258 | 259 | static C11THREADS_INLINE void cnd_destroy(cnd_t *cond) 260 | { 261 | pthread_cond_destroy(cond); 262 | } 263 | 264 | static C11THREADS_INLINE int cnd_signal(cnd_t *cond) 265 | { 266 | return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error; 267 | } 268 | 269 | static C11THREADS_INLINE int cnd_broadcast(cnd_t *cond) 270 | { 271 | return pthread_cond_broadcast(cond) == 0 ? thrd_success : thrd_error; 272 | } 273 | 274 | static C11THREADS_INLINE int cnd_wait(cnd_t *cond, mtx_t *mtx) 275 | { 276 | return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error; 277 | } 278 | 279 | static C11THREADS_INLINE int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) 280 | { 281 | int res; 282 | 283 | if((res = pthread_cond_timedwait(cond, mtx, ts)) != 0) { 284 | return res == ETIMEDOUT ? thrd_timedout : thrd_error; 285 | } 286 | return thrd_success; 287 | } 288 | 289 | /* ---- thread-specific data ---- */ 290 | 291 | static C11THREADS_INLINE int tss_create(tss_t *key, tss_dtor_t dtor) 292 | { 293 | return pthread_key_create(key, dtor) == 0 ? thrd_success : thrd_error; 294 | } 295 | 296 | static C11THREADS_INLINE void tss_delete(tss_t key) 297 | { 298 | pthread_key_delete(key); 299 | } 300 | 301 | static C11THREADS_INLINE int tss_set(tss_t key, void *val) 302 | { 303 | return pthread_setspecific(key, val) == 0 ? thrd_success : thrd_error; 304 | } 305 | 306 | static C11THREADS_INLINE void *tss_get(tss_t key) 307 | { 308 | return pthread_getspecific(key); 309 | } 310 | 311 | /* ---- misc ---- */ 312 | 313 | static C11THREADS_INLINE void call_once(once_flag *flag, void (*func)(void)) 314 | { 315 | pthread_once(flag, func); 316 | } 317 | 318 | #ifdef C11THREADS_NO_TIMESPEC_GET 319 | static C11THREADS_INLINE int timespec_get(struct timespec *ts, int base) 320 | { 321 | struct timeval tv; 322 | 323 | if(base != TIME_UTC) { 324 | return 0; 325 | } 326 | 327 | if(gettimeofday(&tv, 0) == -1) { 328 | return 0; 329 | } 330 | 331 | ts->tv_sec = tv.tv_sec; 332 | ts->tv_nsec = tv.tv_usec * 1000; 333 | return base; 334 | } 335 | #endif 336 | 337 | 338 | #else /* C11THREADS_WIN32 */ 339 | 340 | /* C11 threads implementation using native Win32 API calls (see c11threads_win32.c) */ 341 | 342 | #ifndef thread_local 343 | #ifdef _MSC_VER 344 | #define thread_local __declspec(thread) 345 | #else 346 | #define thread_local _Thread_local 347 | #endif 348 | #endif 349 | 350 | #define ONCE_FLAG_INIT {0} 351 | #define TSS_DTOR_ITERATIONS 4 352 | 353 | #ifndef _UCRT 354 | #define C11THREADS_NO_TIMESPEC_GET 355 | #endif 356 | 357 | #ifdef _MSC_VER 358 | #define C11THREADS_MSVC_NORETURN __declspec(noreturn) 359 | #define C11THREADS_GNUC_NORETURN 360 | #elif defined(__GNUC__) 361 | #define C11THREADS_MSVC_NORETURN 362 | #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5) 363 | #define C11THREADS_GNUC_NORETURN __attribute__((noreturn)) 364 | #else 365 | #define C11THREADS_GNUC_NORETURN 366 | #endif 367 | #else 368 | #define C11THREADS_MSVC_NORETURN 369 | #define C11THREADS_GNUC_NORETURN 370 | #endif 371 | 372 | /* types */ 373 | typedef unsigned long thrd_t; 374 | typedef struct { 375 | void *debug_info; 376 | long lock_count; 377 | long recursion_count; 378 | void *owning_thread; 379 | void *lock_semaphore; 380 | void *spin_count; 381 | } mtx_t; 382 | typedef void *cnd_t; 383 | typedef unsigned long tss_t; 384 | typedef void *once_flag; 385 | struct _c11threads_win32_timespec32_t { 386 | long tv_sec; 387 | long tv_nsec; 388 | }; 389 | struct _c11threads_win32_timespec64_t { 390 | #ifdef _MSC_VER 391 | __int64 tv_sec; 392 | #else 393 | long long tv_sec; 394 | #endif 395 | long tv_nsec; 396 | }; 397 | #if !defined(_UCRT) && !defined(_TIMESPEC_DEFINED) 398 | #ifdef _USE_32BIT_TIME_T 399 | struct timespec { 400 | long tv_sec; 401 | long tv_nsec; 402 | }; 403 | #elif !defined(_USE_32BIT_TIME_T) 404 | struct timespec { 405 | __int64 tv_sec; 406 | long tv_nsec; 407 | }; 408 | #endif /* !defined(_USE_32BIT_TIME_T) */ 409 | #endif /* !defined(_UCRT) && !defined(_TIMESPEC_DEFINED) */ 410 | 411 | /* Thread functions. */ 412 | 413 | int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); 414 | /* Win32: Threads not created with thrd_create() need to call this to clean up TSS. */ 415 | C11THREADS_MSVC_NORETURN void thrd_exit(int res) C11THREADS_GNUC_NORETURN; 416 | int thrd_join(thrd_t thr, int *res); 417 | int thrd_detach(thrd_t thr); 418 | thrd_t thrd_current(void); 419 | int thrd_equal(thrd_t a, thrd_t b); 420 | static C11THREADS_INLINE int thrd_sleep(const struct timespec *ts_in, struct timespec *rem_out); 421 | void thrd_yield(void); 422 | 423 | /* Mutex functions. */ 424 | 425 | int mtx_init(mtx_t *mtx, int type); 426 | void mtx_destroy(mtx_t *mtx); 427 | int mtx_lock(mtx_t *mtx); 428 | int mtx_trylock(mtx_t *mtx); 429 | static C11THREADS_INLINE int mtx_timedlock(mtx_t *mtx, const struct timespec *ts); 430 | int mtx_unlock(mtx_t *mtx); 431 | 432 | /* Condition variable functions. */ 433 | 434 | int cnd_init(cnd_t *cond); 435 | void cnd_destroy(cnd_t *cond); 436 | int cnd_signal(cnd_t *cond); 437 | int cnd_broadcast(cnd_t *cond); 438 | int cnd_wait(cnd_t *cond, mtx_t *mtx); 439 | static C11THREADS_INLINE int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); 440 | 441 | /* Thread-specific storage functions. */ 442 | 443 | int tss_create(tss_t *key, tss_dtor_t dtor); 444 | void tss_delete(tss_t key); 445 | int tss_set(tss_t key, void *val); 446 | void *tss_get(tss_t key); 447 | 448 | /* One-time callable function. */ 449 | 450 | void call_once(once_flag *flag, void (*func)(void)); 451 | 452 | #ifdef C11THREADS_NO_TIMESPEC_GET 453 | static C11THREADS_INLINE int timespec_get(struct timespec *ts, int base); 454 | #endif 455 | 456 | /* Special Win32 functions. */ 457 | /* Win32: Free resources associated with this library. */ 458 | void c11threads_win32_destroy(void); 459 | /* Win32: Register current Win32 thread in c11threads to allow for proper thrd_join(). */ 460 | int c11threads_win32_thrd_self_register(void); 461 | /* Win32: Register Win32 thread by ID in c11threads to allow for proper thrd_join(). */ 462 | int c11threads_win32_thrd_register(unsigned long win32_thread_id); 463 | 464 | #ifdef _MSC_VER 465 | #pragma warning(push) 466 | #pragma warning(disable: 4127) /* Warning C4127: conditional expression is constant */ 467 | #endif 468 | 469 | /* ---- thread management ---- */ 470 | 471 | int _c11threads_win32_thrd_sleep32(const struct _c11threads_win32_timespec32_t *ts_in, struct _c11threads_win32_timespec32_t *rem_out); 472 | int _c11threads_win32_thrd_sleep64(const struct _c11threads_win32_timespec64_t *ts_in, struct _c11threads_win32_timespec64_t *rem_out); 473 | static C11THREADS_INLINE int thrd_sleep(const struct timespec *ts_in, struct timespec *rem_out) 474 | { 475 | if (sizeof(ts_in->tv_sec) == 4) { 476 | return _c11threads_win32_thrd_sleep32((const struct _c11threads_win32_timespec32_t*)ts_in, (struct _c11threads_win32_timespec32_t*)rem_out); 477 | } else { 478 | return _c11threads_win32_thrd_sleep64((const struct _c11threads_win32_timespec64_t*)ts_in, (struct _c11threads_win32_timespec64_t*)rem_out); 479 | } 480 | } 481 | 482 | /* ---- mutexes ---- */ 483 | 484 | int _c11threads_win32_mtx_timedlock32(mtx_t *mtx, const struct _c11threads_win32_timespec32_t *ts); 485 | int _c11threads_win32_mtx_timedlock64(mtx_t *mtx, const struct _c11threads_win32_timespec64_t *ts); 486 | static C11THREADS_INLINE int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) 487 | { 488 | if (sizeof(ts->tv_sec) == 4) { 489 | return _c11threads_win32_mtx_timedlock32(mtx, (const struct _c11threads_win32_timespec32_t*)ts); 490 | } else { 491 | return _c11threads_win32_mtx_timedlock64(mtx, (const struct _c11threads_win32_timespec64_t*)ts); 492 | } 493 | } 494 | 495 | /* ---- condition variables ---- */ 496 | 497 | int _c11threads_win32_cnd_timedwait32(cnd_t *cond, mtx_t *mtx, const struct _c11threads_win32_timespec32_t *ts); 498 | int _c11threads_win32_cnd_timedwait64(cnd_t *cond, mtx_t *mtx, const struct _c11threads_win32_timespec64_t *ts); 499 | static C11THREADS_INLINE int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) 500 | { 501 | if (sizeof(ts->tv_sec) == 4) { 502 | return _c11threads_win32_cnd_timedwait32(cond, mtx, (const struct _c11threads_win32_timespec32_t*)ts); 503 | } else { 504 | return _c11threads_win32_cnd_timedwait64(cond, mtx, (const struct _c11threads_win32_timespec64_t*)ts); 505 | } 506 | } 507 | 508 | /* ---- misc ---- */ 509 | 510 | #ifdef C11THREADS_NO_TIMESPEC_GET 511 | int _c11threads_win32_timespec32_get(struct _c11threads_win32_timespec32_t *ts, int base); 512 | int _c11threads_win32_timespec64_get(struct _c11threads_win32_timespec64_t *ts, int base); 513 | static C11THREADS_INLINE int timespec_get(struct timespec *ts, int base) 514 | { 515 | if (sizeof(ts->tv_sec) == 4) { 516 | return _c11threads_win32_timespec32_get((struct _c11threads_win32_timespec32_t*)ts, base); 517 | } else { 518 | return _c11threads_win32_timespec64_get((struct _c11threads_win32_timespec64_t*)ts, base); 519 | } 520 | } 521 | #endif 522 | 523 | #ifdef _MSC_VER 524 | #pragma warning(pop) 525 | #endif 526 | 527 | #endif /* C11THREADS_WIN32 */ 528 | 529 | #ifdef __cplusplus 530 | } 531 | #endif 532 | 533 | #endif /* C11THREADS_H_ */ 534 | -------------------------------------------------------------------------------- /common/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #if defined(__APPLE__) || defined(_WIN32) 14 | #include "c11threads.h" 15 | #else 16 | #include 17 | #endif 18 | #ifndef __APPLE__ 19 | #include 20 | #endif 21 | #include 22 | #ifdef __SWITCH__ 23 | #include 24 | #endif 25 | 26 | #include 27 | typedef uint8_t u8; 28 | typedef uint16_t u16; 29 | typedef uint32_t u32; 30 | typedef uint64_t u64; 31 | typedef int8_t s8; 32 | typedef int32_t s32; 33 | typedef u32 Result; 34 | 35 | typedef void (*workerThreadFunc)(void *); 36 | 37 | #ifdef _WIN32 38 | #define DIRECTORY_SEPARATOR_CHAR '\\' 39 | static const char DIRECTORY_SEPARATOR[] = "\\"; 40 | #else 41 | #define DIRECTORY_SEPARATOR_CHAR '/' 42 | static const char DIRECTORY_SEPARATOR[] = "/"; 43 | #endif 44 | 45 | 46 | #define M_TAU (2*M_PI) 47 | 48 | typedef union { 49 | uint32_t abgr; 50 | struct { 51 | uint8_t r,g,b,a; 52 | }; 53 | } color_t; 54 | 55 | typedef enum 56 | { 57 | ThemeLayoutId_Logo, 58 | ThemeLayoutId_HbmenuVersion, 59 | ThemeLayoutId_LoaderInfo, 60 | ThemeLayoutId_AttentionText, 61 | ThemeLayoutId_LogInfo, 62 | ThemeLayoutId_InfoMsg, 63 | ThemeLayoutId_MenuPath, 64 | ThemeLayoutId_MenuTypeMsg, 65 | ThemeLayoutId_MsgBoxSeparator, 66 | ThemeLayoutId_MsgBoxBottomText, 67 | ThemeLayoutId_BackgroundImage, 68 | ThemeLayoutId_BackWave, 69 | ThemeLayoutId_MiddleWave, 70 | ThemeLayoutId_FrontWave, 71 | ThemeLayoutId_ButtonA, 72 | ThemeLayoutId_ButtonAText, 73 | ThemeLayoutId_ButtonB, 74 | ThemeLayoutId_ButtonBText, 75 | ThemeLayoutId_ButtonY, 76 | ThemeLayoutId_ButtonYText, 77 | ThemeLayoutId_ButtonM, 78 | ThemeLayoutId_ButtonMText, 79 | ThemeLayoutId_ButtonX, 80 | ThemeLayoutId_ButtonXText, 81 | ThemeLayoutId_NetworkIcon, 82 | ThemeLayoutId_BatteryCharge, 83 | ThemeLayoutId_BatteryIcon, 84 | ThemeLayoutId_ChargingIcon, 85 | ThemeLayoutId_Status, 86 | ThemeLayoutId_Temperature, 87 | ThemeLayoutId_MenuList, 88 | ThemeLayoutId_MenuListTiles, 89 | ThemeLayoutId_MenuListIcon, 90 | ThemeLayoutId_MenuListName, 91 | ThemeLayoutId_MenuActiveEntryIcon, 92 | ThemeLayoutId_MenuActiveEntryName, 93 | ThemeLayoutId_MenuActiveEntryAuthor, 94 | ThemeLayoutId_MenuActiveEntryVersion, 95 | ThemeLayoutId_Total, 96 | } ThemeLayoutId; 97 | 98 | // when building for pc we need to include these separately 99 | #ifndef __SWITCH__ 100 | #include "switch/nro.h" 101 | #include "switch/nacp.h" 102 | #endif 103 | 104 | #include "font.h" 105 | #include "menu.h" 106 | #include "text.h" 107 | #include "ui.h" 108 | #include "assets.h" 109 | #include "launch.h" 110 | #include "worker.h" 111 | #include 112 | #include "math.h" 113 | #include "theme.h" 114 | #include "message-box.h" 115 | #include "power.h" 116 | #include "netloader.h" 117 | #include "netstatus.h" 118 | #include "thermalstatus.h" 119 | #include "status.h" 120 | 121 | void menuStartupPath(void); 122 | void menuStartup(void); 123 | void themeMenuStartup(void); 124 | void menuLoop(void); 125 | 126 | static inline uint8_t BlendColor(uint32_t src, uint32_t dst, uint8_t alpha) 127 | { 128 | uint8_t one_minus_alpha = (uint8_t)255 - alpha; 129 | return (dst*alpha + src*one_minus_alpha)/(uint8_t)255; 130 | } 131 | 132 | static inline color_t MakeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) 133 | { 134 | color_t clr; 135 | clr.r = r; 136 | clr.g = g; 137 | clr.b = b; 138 | clr.a = a; 139 | return clr; 140 | } 141 | 142 | #ifdef __SWITCH__ 143 | extern uint8_t* g_framebuf; 144 | extern u32 g_framebuf_width; 145 | static inline void DrawPixel(uint32_t x, uint32_t y, color_t clr) 146 | { 147 | if (x >= 1280 || y >= 720) 148 | return; 149 | u32 off = y*g_framebuf_width + x*4; 150 | g_framebuf[off] = BlendColor(g_framebuf[off], clr.r, clr.a); off++; 151 | g_framebuf[off] = BlendColor(g_framebuf[off], clr.g, clr.a); off++; 152 | g_framebuf[off] = BlendColor(g_framebuf[off], clr.b, clr.a); off++; 153 | g_framebuf[off] = 0xff; 154 | } 155 | static inline void DrawPixelRaw(uint32_t x, uint32_t y, color_t clr) 156 | { 157 | if (x >= 1280 || y >= 720) 158 | return; 159 | u32 off = y*g_framebuf_width + x*4; 160 | *((u32*)&g_framebuf[off]) = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24); 161 | } 162 | static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr) 163 | { 164 | if (x >= 1280 || y >= 720 || x > 1280-4) 165 | return; 166 | 167 | u32 color = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24); 168 | u128 val = color | ((u128)color<<32) | ((u128)color<<64) | ((u128)color<<96); 169 | u32 off = y*g_framebuf_width + x*4; 170 | *((u128*)&g_framebuf[off]) = val; 171 | } 172 | static inline color_t FetchPixelColor(uint32_t x, uint32_t y) 173 | { 174 | u32 off = y*g_framebuf_width + x*4; 175 | u32 val = *((u32*)&g_framebuf[off]); 176 | u8 r = (u8)val; 177 | u8 g = (u8)(val>>8); 178 | u8 b = (u8)(val>>16); 179 | return MakeColor(r, g, b, 255); 180 | } 181 | #else 182 | extern color_t pixels[720][1280]; 183 | static inline void DrawPixel(uint32_t x, uint32_t y, color_t clr) 184 | { 185 | if (x >= 1280 || y >= 720) 186 | return; 187 | pixels[y][x].r = BlendColor(pixels[y][x].r, clr.r, clr.a); 188 | pixels[y][x].g = BlendColor(pixels[y][x].g, clr.g, clr.a); 189 | pixels[y][x].b = BlendColor(pixels[y][x].b, clr.b, clr.a); 190 | pixels[y][x].a = 0xff; 191 | } 192 | static inline void DrawPixelRaw(uint32_t x, uint32_t y, color_t clr) 193 | { 194 | if (x >= 1280 || y >= 720) 195 | return; 196 | pixels[y][x].r = clr.r; 197 | pixels[y][x].g = clr.g; 198 | pixels[y][x].b = clr.b; 199 | pixels[y][x].a = 0xff; 200 | } 201 | static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr) 202 | { 203 | uint32_t pos; 204 | for (pos=0; pos<4; pos++) DrawPixelRaw(x+pos, y, clr); 205 | } 206 | static inline color_t FetchPixelColor(uint32_t x, uint32_t y) 207 | { 208 | return pixels[y][x]; 209 | } 210 | #endif 211 | 212 | void DrawPixel(uint32_t x, uint32_t y, color_t clr); 213 | void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text); 214 | void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text); 215 | void DrawTextFromLayoutRelative(ThemeLayoutId id, int base_x, int base_y, int *inPos, int *outPos, color_t clr, const char* text, const char align); 216 | void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text); 217 | void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t* height_out); 218 | uint32_t GetTextXCoordinate(u32 font, uint32_t rX, const char* text, const char align); 219 | uint32_t GetTextYCoordinate(u32 font, uint32_t rY, const char* text, const char align); 220 | 221 | bool fontInitialize(void); 222 | void fontExit(); -------------------------------------------------------------------------------- /common/font.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | #include FT_FREETYPE_H 5 | 6 | #ifdef __SWITCH__ 7 | #define FONT_FACES_MAX PlSharedFontType_Total 8 | #else 9 | #define FONT_FACES_MAX 2 10 | #endif 11 | 12 | #ifdef __SWITCH__ 13 | static bool s_plinited; 14 | #endif 15 | 16 | static FT_Error s_font_libret=1, s_font_facesret[FONT_FACES_MAX]; 17 | 18 | static FT_Library s_font_library; 19 | static FT_Face s_font_faces[FONT_FACES_MAX]; 20 | static FT_Face s_font_lastusedface; 21 | static s32 s_font_faces_total = 0; 22 | 23 | static bool FontSetType(u32 font) 24 | { 25 | s32 i=0; 26 | u32 scale=0; 27 | FT_Error ret=0; 28 | 29 | switch(font) 30 | { 31 | case interuiregular14: 32 | scale = 4; 33 | break; 34 | 35 | case interuiregular18: 36 | scale = 5; 37 | break; 38 | 39 | case interuimedium20: 40 | scale = 6; 41 | break; 42 | 43 | case fontscale7: 44 | scale = 7; 45 | break; 46 | 47 | case interuimedium30: 48 | scale = 8; 49 | break; 50 | 51 | case largestar: 52 | scale = 18; 53 | break; 54 | 55 | default: 56 | return false; 57 | break; 58 | } 59 | 60 | for (i=0; i= font->npages) 78 | return NULL; 79 | ffnt_pageentry_t* ent = &((ffnt_pageentry_t*)(font+1))[page_id]; 80 | if (ent->size == 0) 81 | return NULL; 82 | return (const ffnt_page_t*)((const uint8_t*)font + ent->offset); 83 | }*/ 84 | 85 | static inline bool FontLoadGlyph(glyph_t* glyph, u32 font, uint32_t codepoint) 86 | { 87 | FT_Face face=0; 88 | FT_Error ret=0; 89 | FT_GlyphSlot slot; 90 | FT_UInt glyph_index; 91 | FT_Bitmap* bitmap; 92 | s32 i=0; 93 | 94 | //__builtin_printf("LoadGlyph %u\n", (unsigned int)codepoint); 95 | /*const ffnt_page_t* page = FontGetPage(font, codepoint >> 8); 96 | if (!page) 97 | return false; 98 | 99 | codepoint &= 0xFF; 100 | uint32_t off = page->hdr.pos[codepoint]; 101 | if (off == ~(uint32_t)0) 102 | return false;*/ 103 | 104 | if (s_font_faces_total==0) return false; 105 | 106 | for (i=0; iglyph, /* glyph slot */ 120 | FT_RENDER_MODE_NORMAL); /* render mode */ 121 | } 122 | 123 | if (ret) return false; 124 | 125 | break; 126 | } 127 | 128 | slot = face->glyph; 129 | bitmap = &slot->bitmap; 130 | 131 | //__builtin_printf("%c %u\n", (char)codepoint, (unsigned int)off); 132 | /*glyph->width = page->hdr.widths[codepoint]; 133 | glyph->height = page->hdr.heights[codepoint]; 134 | glyph->advance = page->hdr.advances[codepoint]; 135 | glyph->posX = page->hdr.posX[codepoint]; 136 | glyph->posY = page->hdr.posY[codepoint]; 137 | glyph->data = &page->data[off];*/ 138 | 139 | glyph->width = bitmap->width; 140 | glyph->height = bitmap->rows; 141 | glyph->pitch = bitmap->pitch; 142 | glyph->data = bitmap->buffer; 143 | glyph->advance = slot->advance.x >> 6; 144 | glyph->posX = slot->bitmap_left; 145 | glyph->posY = slot->bitmap_top; 146 | return true; 147 | } 148 | 149 | static void DrawGlyph(uint32_t x, uint32_t y, color_t clr, const glyph_t* glyph) 150 | { 151 | uint32_t i, j; 152 | const uint8_t* data = glyph->data; 153 | x += glyph->posX; 154 | y -= glyph->posY; //y += glyph->posY; 155 | //__builtin_printf("DrawGlyph %u %u %08X\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr); 156 | for (j = 0; j < glyph->height; j ++) 157 | { 158 | for (i = 0; i < glyph->width; i ++) 159 | { 160 | clr.a = data[i]; 161 | if (!clr.a) continue; 162 | DrawPixel(x+i, y+j, clr); 163 | } 164 | data+= glyph->pitch; 165 | } 166 | } 167 | 168 | static inline uint8_t DecodeByte(const char** ptr) 169 | { 170 | uint8_t c = (uint8_t)**ptr; 171 | *ptr += 1; 172 | return c; 173 | } 174 | 175 | // UTF-8 code adapted from http://www.json.org/JSON_checker/utf8_decode.c 176 | 177 | static inline int8_t DecodeUTF8Cont(const char** ptr) 178 | { 179 | int c = DecodeByte(ptr); 180 | return ((c & 0xC0) == 0x80) ? (c & 0x3F) : -1; 181 | } 182 | 183 | static inline uint32_t DecodeUTF8(const char** ptr) 184 | { 185 | uint32_t r; 186 | uint8_t c; 187 | int8_t c1, c2, c3; 188 | 189 | c = DecodeByte(ptr); 190 | if ((c & 0x80) == 0) 191 | return c; 192 | if ((c & 0xE0) == 0xC0) 193 | { 194 | c1 = DecodeUTF8Cont(ptr); 195 | if (c1 >= 0) 196 | { 197 | r = ((c & 0x1F) << 6) | c1; 198 | if (r >= 0x80) 199 | return r; 200 | } 201 | } else if ((c & 0xF0) == 0xE0) 202 | { 203 | c1 = DecodeUTF8Cont(ptr); 204 | if (c1 >= 0) 205 | { 206 | c2 = DecodeUTF8Cont(ptr); 207 | if (c2 >= 0) 208 | { 209 | r = ((c & 0x0F) << 12) | (c1 << 6) | c2; 210 | if (r >= 0x800 && (r < 0xD800 || r >= 0xE000)) 211 | return r; 212 | } 213 | } 214 | } else if ((c & 0xF8) == 0xF0) 215 | { 216 | c1 = DecodeUTF8Cont(ptr); 217 | if (c1 >= 0) 218 | { 219 | c2 = DecodeUTF8Cont(ptr); 220 | if (c2 >= 0) 221 | { 222 | c3 = DecodeUTF8Cont(ptr); 223 | if (c3 >= 0) 224 | { 225 | r = ((c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3; 226 | if (r >= 0x10000 && r < 0x110000) 227 | return r; 228 | } 229 | } 230 | } 231 | } 232 | return 0xFFFD; 233 | } 234 | 235 | static void DrawText_(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text) 236 | { 237 | //__builtin_printf("DrawText %u %u %08X %s\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr, text); 238 | //y += font->baseline; 239 | uint32_t origX = x; 240 | if (s_font_faces_total==0) return; 241 | if (!FontSetType(font)) return; 242 | s_font_lastusedface = s_font_faces[0]; 243 | 244 | while (*text) 245 | { 246 | if (max_width && x-origX >= max_width) { 247 | text = end_text; 248 | max_width = 0; 249 | } 250 | 251 | glyph_t glyph; 252 | uint32_t codepoint = DecodeUTF8(&text); 253 | 254 | if (codepoint == '\n') 255 | { 256 | if (max_width) { 257 | text = end_text; 258 | max_width = 0; 259 | continue; 260 | } 261 | 262 | x = origX; 263 | y += s_font_lastusedface->size->metrics.height / 64; 264 | continue; 265 | } 266 | 267 | if (!FontLoadGlyph(&glyph, font, codepoint)) 268 | { 269 | if (!FontLoadGlyph(&glyph, font, '?')) 270 | continue; 271 | } 272 | 273 | DrawGlyph(x, y, clr, &glyph); 274 | x += glyph.advance; 275 | } 276 | } 277 | 278 | void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text) 279 | { 280 | DrawText_(font, x, y, clr, text, 0, NULL); 281 | } 282 | 283 | void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text) 284 | { 285 | ThemeLayoutObject *obj = &themeCurrent.layoutObjects[id]; 286 | if (!obj->visible) return; 287 | 288 | DrawText(obj->font, obj->posStart[0], obj->posStart[1], clr, text); 289 | } 290 | 291 | void DrawTextFromLayoutRelative(ThemeLayoutId id, int base_x, int base_y, int *inPos, int *outPos, color_t clr, const char* text, const char align) 292 | { 293 | ThemeLayoutObject *obj = &themeCurrent.layoutObjects[id]; 294 | 295 | base_x = obj->posType ? base_x + inPos[0] : inPos[0]; 296 | base_y = obj->posType ? base_y + inPos[1] : inPos[1]; 297 | 298 | base_x = GetTextXCoordinate(obj->font, base_x, text, align); 299 | 300 | if (outPos) { 301 | outPos[0] = base_x; 302 | outPos[1] = base_y; 303 | } 304 | 305 | obj->posFinal[0] = base_x; 306 | obj->posFinal[1] = base_y; 307 | 308 | if (!obj->visible) return; 309 | 310 | GetTextDimensions(obj->font, text, &obj->textSize[0], &obj->textSize[1]); 311 | 312 | DrawText(obj->font, base_x, base_y, clr, text); 313 | } 314 | 315 | void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text) 316 | { 317 | DrawText_(font, x, y, clr, text, max_width, end_text); 318 | } 319 | 320 | void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t* height_out) 321 | { 322 | uint32_t x = 0; 323 | uint32_t width = 0, height = 0; 324 | if (s_font_faces_total==0) return; 325 | if (!FontSetType(font)) return; 326 | s_font_lastusedface = s_font_faces[0]; 327 | 328 | while (*text) 329 | { 330 | glyph_t glyph; 331 | uint32_t codepoint = DecodeUTF8(&text); 332 | 333 | if (codepoint == '\n') 334 | { 335 | x = 0; 336 | height += s_font_lastusedface->size->metrics.height / 64; 337 | continue; 338 | } 339 | 340 | if (!FontLoadGlyph(&glyph, font, codepoint)) 341 | { 342 | if (!FontLoadGlyph(&glyph, font, '?')) 343 | continue; 344 | } 345 | 346 | x += glyph.advance; 347 | 348 | if (x > width) 349 | width = x; 350 | } 351 | 352 | if(width_out) *width_out = width; 353 | if(height_out) *height_out = height; 354 | } 355 | 356 | bool fontInitialize(void) 357 | { 358 | FT_Error ret=0; 359 | s32 i; 360 | 361 | for (i=0; i/fonts/.ttf", these are user-supplied. Ideally these should be from plu SharedFont. 391 | #ifndef __SWITCH__ 392 | char fontpath[PATH_MAX+1]; 393 | 394 | for (i=0; i>1));//>>1 is a bitwise shift for dividing by 2 446 | case 'b': 447 | if(fC<=0) return 0; 448 | else return fC; 449 | } 450 | } 451 | 452 | /*Automatically gives you the desired x-coordinate 453 | *based on the string length and desired alignment 454 | *rX=reference point... where to align around 455 | *text=string you want to display 456 | *align='r','l','c' translates to (right,left,center) 457 | *'r' aligned, rX location = end of string, you get the rest... 458 | */ 459 | uint32_t GetTextXCoordinate(u32 font, uint32_t rX, const char* text, const char align) { 460 | uint32_t height,width_o; 461 | GetTextDimensions(font,text,&width_o,&height); 462 | uint32_t fC = (rX-width_o); 463 | 464 | switch(align){ 465 | case 'r': 466 | if(fC<0) return 0; 467 | else return fC; 468 | case 'c': 469 | return (rX-(width_o>>1));//>>1 is a bitwise shift for dividing by 2 470 | case 'l': 471 | default: 472 | return rX; 473 | } 474 | } 475 | -------------------------------------------------------------------------------- /common/font.h: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | uint8_t magic[4]; // 'fFNT' 3 | int version; // 1 4 | uint16_t npages; 5 | uint8_t height; 6 | uint8_t baseline; 7 | } ffnt_header_t; 8 | 9 | typedef struct { 10 | uint32_t size, offset; 11 | } ffnt_pageentry_t; 12 | 13 | typedef struct { 14 | uint32_t pos[0x100]; 15 | uint8_t widths[0x100]; 16 | uint8_t heights[0x100]; 17 | int8_t advances[0x100]; 18 | int8_t posX[0x100]; 19 | int8_t posY[0x100]; 20 | } ffnt_pagehdr_t; 21 | 22 | typedef struct { 23 | ffnt_pagehdr_t hdr; 24 | uint8_t data[]; 25 | } ffnt_page_t; 26 | 27 | typedef struct { 28 | uint8_t width, height; 29 | int8_t posX, posY, advance, pitch; 30 | const uint8_t* data; 31 | } glyph_t; 32 | 33 | //extern const ffnt_header_t tahoma24_nxfnt;//These tahoma fonts aren't used anymore. 34 | //extern const ffnt_header_t tahoma12_nxfnt; 35 | /*extern const ffnt_header_t interuimedium20_nxfnt; 36 | extern const ffnt_header_t interuimedium30_nxfnt; 37 | extern const ffnt_header_t interuiregular14_nxfnt; 38 | extern const ffnt_header_t interuiregular18_nxfnt;*/ 39 | //#define tahoma24 &tahoma24_nxfnt 40 | //#define tahoma12 &tahoma12_nxfnt 41 | #define interuimedium20 2//&interuimedium20_nxfnt 42 | #define interuimedium30 3//&interuimedium30_nxfnt 43 | #define interuiregular14 0//&interuiregular14_nxfnt 44 | #define interuiregular18 1//&interuiregular18_nxfnt 45 | #define fontscale7 4 46 | #define largestar 5 47 | -------------------------------------------------------------------------------- /common/language.c: -------------------------------------------------------------------------------- 1 | #include "language.h" 2 | 3 | #ifdef __SWITCH__ 4 | #define STR_JP(_str) [SetLanguage_JA] = _str 5 | #define STR_EN(_str) [SetLanguage_ENUS] = _str, [SetLanguage_ENGB] = _str 6 | #define STR_FR(_str) [SetLanguage_FR] = _str, [SetLanguage_FRCA] = _str 7 | #define STR_DE(_str) [SetLanguage_DE] = _str 8 | #define STR_IT(_str) [SetLanguage_IT] = _str 9 | #define STR_ES(_str) [SetLanguage_ES] = _str, [SetLanguage_ES419] = _str 10 | #define STR_ZH_HANS(_str) [SetLanguage_ZHCN] = _str, [SetLanguage_ZHHANS] = _str 11 | #define STR_KO(_str) [SetLanguage_KO] = _str 12 | #define STR_NL(_str) [SetLanguage_NL] = _str 13 | #define STR_PT(_str) [SetLanguage_PT] = _str 14 | #define STR_RU(_str) [SetLanguage_RU] = _str 15 | #define STR_ZH_HANT(_str) [SetLanguage_ZHTW] = _str, [SetLanguage_ZHHANT] = _str 16 | #else 17 | #define STR_JP(_str) [0] = _str 18 | #define STR_EN(_str) [1] = _str 19 | #define STR_FR(_str) [2] = _str 20 | #define STR_DE(_str) [3] = _str 21 | #define STR_IT(_str) [4] = _str 22 | #define STR_ES(_str) [5] = _str 23 | #define STR_ZH_HANS(_str) [6] = _str 24 | #define STR_KO(_str) [7] = _str 25 | #define STR_NL(_str) [8] = _str 26 | #define STR_PT(_str) [9] = _str 27 | #define STR_RU(_str) [10] = _str 28 | #define STR_ZH_HANT(_str) [11] = _str 29 | #endif 30 | 31 | const char* const g_strings[StrId_Max][17] = 32 | { 33 | [StrId_Loading] = 34 | { 35 | STR_EN("Loading…"), 36 | STR_ES("Cargando…"), 37 | STR_DE("Lade…"), 38 | STR_FR("Chargement…"), 39 | STR_IT("Caricamento…"), 40 | STR_JP("ロード中…"), 41 | STR_PT("Carregando…"), 42 | STR_NL("Laden…"), 43 | STR_KO("로딩 중…"), 44 | STR_RU("Загрузка…"), 45 | STR_ZH_HANS("加载中…"), 46 | STR_ZH_HANT("載入中…"), 47 | }, 48 | 49 | [StrId_AppletMode] = 50 | { 51 | STR_EN("● Applet Mode ●"), 52 | STR_ES("● Modo Applet ●"), 53 | STR_FR("● Mode Applet ●"), 54 | STR_KO("● 애플릿 모드 ●"), 55 | STR_ZH_HANS("● 小程序模式 ●"), 56 | STR_RU("● Режим Applet ●"), 57 | }, 58 | 59 | [StrId_Directory] = 60 | { 61 | STR_EN("Directory"), 62 | STR_ES("Carpeta"), 63 | STR_DE("Verzeichnis"), 64 | STR_FR("Dossier"), 65 | STR_IT("Cartella"), 66 | STR_JP("フォルダ"), 67 | STR_PT("Directório"), 68 | STR_NL("Map"), 69 | STR_KO("디렉토리"), 70 | STR_RU("Директория"), 71 | STR_ZH_HANS("文件夹"), 72 | STR_ZH_HANT("資料夾"), 73 | }, 74 | 75 | /*[StrId_DefaultLongTitle] = 76 | { 77 | STR_EN("Homebrew application"), 78 | STR_ES("Aplicación homebrew"), 79 | STR_DE("Homebrew-Anwendung"), 80 | STR_FR("Application homebrew"), 81 | STR_IT("Applicazione homebrew"), 82 | STR_JP("自作アプリ"), 83 | STR_PT("Aplicação Homebrew"), 84 | STR_NL("Homebrew toepassing"), 85 | STR_KO("홈브류 애플리케이션"), 86 | STR_RU("приложение хомебреw"), 87 | STR_ZH_HANS("自制应用程序"), 88 | STR_ZH_HANT("自製程式"), 89 | },*/ 90 | 91 | [StrId_DefaultPublisher] = 92 | { 93 | STR_EN("Unknown author"), 94 | STR_ES("Autor desconocido"), 95 | STR_DE("Unbekannter Autor"), 96 | STR_FR("Auteur inconnu"), 97 | STR_IT("Autore sconosciuto"), 98 | STR_JP("作者不明"), 99 | STR_PT("Autor Desconhecido"), 100 | STR_NL("Auteur onbekend"), 101 | STR_KO("알 수 없는 개발자"), 102 | STR_RU("Неизвестный автор"), 103 | STR_ZH_HANS("未知作者"), 104 | STR_ZH_HANT("作者未知"), 105 | }, 106 | 107 | [StrId_IOError] = 108 | { 109 | STR_EN("I/O Error"), 110 | STR_ES("Error de E/S"), 111 | STR_DE("I/O-Fehler"), 112 | STR_FR("Erreur d'E/S"), 113 | STR_IT("Errore I/O"), 114 | STR_JP("入出力エラー"), 115 | STR_PT("Erro de E/S"), 116 | STR_NL("I/O Fout"), 117 | STR_KO("입출력 오류"), 118 | STR_RU("Ошибка ввода/вывода"), 119 | STR_ZH_HANS("读写出错"), 120 | STR_ZH_HANT("取存錯誤"), 121 | }, 122 | 123 | [StrId_CouldNotOpenFile] = 124 | { 125 | STR_EN("Could not open file:\n%s"), 126 | STR_ES("No se pudo abrir el archivo:\n%s"), 127 | STR_DE("Konnte Datei nicht öffnen:\n%s"), 128 | STR_FR("Impossible d'ouvrir le fichier :\n%s"), 129 | STR_IT("Impossibile aprire il file:\n%s"), 130 | STR_JP("ファイルを開くことができませんでした:\n%s"), 131 | STR_PT("Não foi possível abrir o ficheiro:\n%s"), 132 | STR_NL("Kan bestand niet openen:\n%s"), 133 | STR_KO("파일을 열 수 없습니다:\n%s"), 134 | STR_RU("Невозможно открыть файл:\n%s"), 135 | STR_ZH_HANS("无法打开文件:\n%s"), 136 | STR_ZH_HANT("無法開啟檔案:\n%s"), 137 | }, 138 | 139 | [StrId_NroNotFound] = 140 | { 141 | STR_EN("Could not find executable: %s"), 142 | STR_FR("Impossible trouver l'exécutable : %s"), 143 | STR_KO("실행 파일을 찾을 수 없습니다: %s"), 144 | STR_ZH_HANS("找不到可执行文件: %s"), 145 | STR_ZH_HANT("沒有可執行檔案: %s"), 146 | STR_RU("Не удалось найти исполняемый файл: %s"), 147 | }, 148 | 149 | [StrId_NoAppsFound_Title] = 150 | { 151 | STR_EN("No applications found"), 152 | STR_ES("No hay aplicaciones"), 153 | STR_DE("Keine Anwendungen gefunden"), 154 | STR_FR("Aucune application trouvée"), 155 | STR_IT("Nessun'applicazione trovata"), 156 | STR_JP("アプリが見つかりませんでした"), 157 | STR_PT("Não foram encontradas aplicações"), 158 | STR_NL("Geen toepassingen gevonden"), 159 | STR_KO("애플리케이션을 찾을 수 없습니다"), 160 | STR_RU("Приложения не найдены"), 161 | STR_ZH_HANS("找不到可执行的自制程序"), 162 | STR_ZH_HANT("沒有可執行的自製程式"), 163 | }, 164 | 165 | [StrId_NoAppsFound_Msg] = 166 | { 167 | STR_EN( 168 | "No applications could be found on the SD card.\n" 169 | "Make sure a folder named /switch exists in the\n" 170 | "root of the SD card and it contains applications.\n" 171 | ), 172 | STR_ES( 173 | "No se han podido encontrar aplicaciones en la\n" 174 | "tarjeta SD. Compruebe que haya una carpeta\n" 175 | "llamada /switch y que contenga aplicaciones.\n" 176 | ), 177 | STR_DE( 178 | "Auf der SD-Karte wurden keine Anwendungen\n" 179 | "gefunden. Stelle sicher, dass ein Verzeichnis\n" 180 | "namens /switch im Hauptverzeichnis der SD-Karte\n" 181 | "existiert und Anwendungen enthält!" 182 | ), 183 | STR_FR( 184 | "Aucune application n'a été trouvée sur la carte\n" 185 | "SD. Veillez à ce qu'un dossier intitulé /switch\n" 186 | "existe à la racine de la carte SD et à ce qu'il\n" 187 | "contienne des applications." 188 | ), 189 | STR_IT( 190 | "Nessun'applicazione è stata trovata sulla scheda\n" 191 | "SD. Assicurati che esista una cartella chiamata\n" 192 | "/switch nella root della scheda SD e che contenga\n" 193 | "delle applicazioni." 194 | ), 195 | STR_JP( 196 | "SDカードにアプリケーションが見つかりませんでした。\n" 197 | "SDカードのルートに「switch」という名前のフォルダを\n" 198 | "作成してください。" 199 | ), 200 | STR_KO( 201 | "SD 카드에서 애플리케이션을 찾을 수 없습니다.\n" 202 | "SD 카드 최상위에 /switch 폴더가 있고\n" 203 | "애플리케이션이 포함되어 있는지 확인하세요." 204 | ), 205 | STR_PT( 206 | "Nenhuma aplicação foi encontrada no cartão SD.\n" 207 | "Certifique-se que uma pasta com o nome /switch\n" 208 | "existe na raiz do cartão SD e que contêm\n" 209 | "aplicações." 210 | ), 211 | STR_NL( 212 | "Geen toepassingen gevonden op de SD kaart.\n" 213 | "Zorg ervoor dat een map genaamd /switch in de\n" 214 | "rootdirectory van de SD kaart aangemaakt is\n" 215 | "en de toepassingen bevat." 216 | ), 217 | STR_RU( 218 | "На SD-карте не удалось найти ни одного приложения.\n" 219 | "Убедитесь, что на SD-карте существует папка с именем switch в\n" 220 | "корневом каталоге и в ней содержатся приложения." 221 | ), 222 | STR_ZH_HANS( 223 | "找不到任何自制程序(nro)。\n" 224 | "请在SD卡根目录创建“switch”文件夹,\n" 225 | "并将自制程序(nro)放入其中。" 226 | ), 227 | STR_ZH_HANT( 228 | "記憶卡內沒有可供執行的應用程式。\n" 229 | "請在根目錄下建立「switch」資料夾,\n" 230 | "並將自製軟體複製到switch資料夾內。" 231 | ), 232 | }, 233 | 234 | [StrId_LastLoadResult] = 235 | { 236 | STR_EN("The last application returned an error:"), 237 | STR_ES("La última aplicación devolvió un error:"), 238 | STR_DE("Die letzte Anwendung erzeugte einen Fehler:"), 239 | STR_FR("La dernière application a retourné une erreur:"), 240 | STR_IT("L'ultima applicazione ha restituito un errore:"), 241 | STR_JP("直前に実行したアプリでエラーが発生しました:"), 242 | STR_KO("최근 애플리케이션에서 오류가 발생했습니다:"), 243 | STR_ZH_HANS("程序运行后出现错误:"), 244 | STR_ZH_HANT("程式執行後出現錯誤:"), 245 | STR_RU("Последнее приложение вернуло ошибку:"), 246 | }, 247 | 248 | [StrId_AppLaunchError] = 249 | { 250 | STR_EN("Failed to launch the application:"), 251 | STR_DE("Konnte die Anwendung nicht starten:"), 252 | STR_FR("Erreur au lancement de l'application:"), 253 | STR_IT("Errore nell'avvio dell'applicazione:"), 254 | STR_ES("No se ha podido iniciar la aplicación:"), 255 | STR_KO("애플리케이션을 시작하지 못했습니다:"), 256 | STR_ZH_HANS("运行程序时发生错误:"), 257 | STR_ZH_HANT("執行程式時發生錯誤:"), 258 | STR_RU("Не удалось запустить приложение:"), 259 | }, 260 | 261 | [StrId_AppInfo_Author] = 262 | { 263 | STR_EN("Author"), 264 | STR_ES("Autor"), 265 | STR_DE("Autor"), 266 | STR_FR("Auteur"), 267 | STR_IT("Autore"), 268 | STR_JP("作者"), 269 | STR_KO("개발자"), 270 | STR_PT("Autor"), 271 | STR_NL("Auteur"), 272 | STR_RU("Автор"), 273 | STR_ZH_HANS("作者"), 274 | STR_ZH_HANT("作者"), 275 | }, 276 | 277 | [StrId_AppInfo_Version] = 278 | { 279 | STR_EN("Version"), 280 | STR_ES("Versión"), 281 | STR_DE("Version"), 282 | STR_FR("Version"), 283 | STR_IT("Versione"), 284 | STR_JP("バージョン"), 285 | STR_KO("버전"), 286 | STR_PT("Versão"), 287 | STR_NL("Versie"), 288 | STR_RU("Версия"), 289 | STR_ZH_HANS("版本"), 290 | STR_ZH_HANT("版本"), 291 | }, 292 | 293 | [StrId_Actions_Launch] = 294 | { 295 | STR_EN("Launch"), 296 | STR_ES("Lanzamiento"), 297 | STR_DE("Starten"), 298 | STR_FR("Lancer"), 299 | STR_IT("Avvia"), 300 | STR_JP("起動"), 301 | STR_KO("실행"), 302 | STR_PT("Lançamento"), 303 | STR_NL("Lancering"), 304 | STR_RU("Запустить"), 305 | STR_ZH_HANS("启动"), 306 | STR_ZH_HANT("啟動"), 307 | }, 308 | 309 | [StrId_Actions_Open] = 310 | { 311 | STR_EN("Open"), 312 | STR_ES("Abrir"), 313 | STR_DE("Öffnen"), 314 | STR_FR("Ouvrir"), 315 | STR_IT("Apri"), 316 | STR_JP("開く"), 317 | STR_KO("열기"), 318 | STR_PT("Abrir"), 319 | STR_NL("Open"), 320 | STR_RU("Открыть"), 321 | STR_ZH_HANS("打开"), 322 | STR_ZH_HANT("開啟"), 323 | }, 324 | 325 | [StrId_Actions_Back] = 326 | { 327 | STR_EN("Back"), 328 | STR_ES("Volver"), 329 | STR_DE("Zurück"), 330 | STR_FR("Retour"), 331 | STR_IT("Indietro"), 332 | STR_JP("戻る"), 333 | STR_KO("뒤로가기"), 334 | STR_PT("Regressar"), 335 | STR_NL("Terug"), 336 | STR_RU("Назад"), 337 | STR_ZH_HANS("返回"), 338 | STR_ZH_HANT("返回"), 339 | }, 340 | 341 | [StrId_MsgBox_OK] = 342 | { 343 | STR_EN("OK"), 344 | STR_DE("OK"), 345 | STR_FR("OK"), 346 | STR_IT("OK"), 347 | STR_ES("Aceptar"), 348 | STR_JP("了解"), 349 | STR_KO("확인"), 350 | STR_ZH_HANS("确认"), 351 | STR_ZH_HANT("確認"), 352 | STR_RU("ОК"), 353 | }, 354 | 355 | [StrId_Actions_Apply] = 356 | { 357 | STR_EN("Apply"), 358 | STR_FR("Appliquer"), 359 | STR_DE("Anwenden"), 360 | STR_ES("Aplicar"), 361 | STR_IT("Applica"), 362 | STR_JP("適用"), 363 | STR_KO("적용"), 364 | STR_ZH_HANS("应用"), 365 | STR_ZH_HANT("套用"), 366 | STR_RU("Применить"), 367 | }, 368 | 369 | [StrId_Actions_Star] = 370 | { 371 | STR_EN("Star"), 372 | STR_ES("Agregar a favoritos"), 373 | STR_IT("Aggiungi ai preferiti"), 374 | STR_FR("Ajouter aux favoris"), 375 | STR_KO("즐겨찾기에 추가"), 376 | STR_ZH_HANS("收藏"), 377 | STR_RU("Добавить в избранное"), 378 | }, 379 | 380 | [StrId_Actions_Unstar] = 381 | { 382 | STR_EN("Unstar"), 383 | STR_ES("Borrar de favoritos"), 384 | STR_IT("Rimuovi dai preferiti"), 385 | STR_FR("Retirer des favoris"), 386 | STR_KO("즐겨찾기에 삭제"), 387 | STR_ZH_HANS("取消收藏"), 388 | STR_RU("Удалить из избранного") 389 | }, 390 | 391 | [StrId_ThemeMenu] = 392 | { 393 | STR_EN("Theme Menu"), 394 | STR_FR("Menu thèmes"), 395 | STR_DE("Theme Menü"), 396 | STR_ES("Menú temático"), 397 | STR_IT("Tema Menu"), 398 | STR_JP("テーマメニュー"), 399 | STR_KO("테마 메뉴"), 400 | STR_ZH_HANS("主题菜单"), 401 | STR_ZH_HANT("主題選單"), 402 | STR_RU("Меню тем") 403 | }, 404 | 405 | [StrId_ThemeNotApplied] = 406 | { 407 | STR_EN("Theme cannot be applied because an error occurred."), 408 | STR_DE("Das Theme konnte nicht geladen werden, da ein Fehler aufgetreten ist."), 409 | STR_FR("Le thème ne peut pas être appliqué car une erreur est survenue."), 410 | STR_ES("El tema no se pudo aplicar porque se ha producido un error."), 411 | STR_IT("Il tema non è stato applicato a causa di un errore."), 412 | STR_JP("エラーが発生したため、テーマを適用できませんでした。"), 413 | STR_KO("오류가 발생 했기 때문에 테마를 적용할 수 없습니다."), 414 | STR_ZH_HANS("由于发生错误, 无法应用主题。"), 415 | STR_ZH_HANT("出現錯誤,無法套用主題。"), 416 | STR_RU("Тема не может быть применена из-за возникшей ошибки."), 417 | }, 418 | 419 | [StrId_DefaultThemeName] = 420 | { 421 | STR_EN("Default Theme"), 422 | STR_FR("Thème par défaut"), 423 | STR_DE("Standard Theme"), 424 | STR_IT("Tema di default"), 425 | STR_ES("Tema por defecto"), 426 | STR_KO("기본 테마"), 427 | STR_ZH_HANS("默认主题"), 428 | STR_ZH_HANT("預設主題"), 429 | STR_RU("Тема по умолчанию"), 430 | }, 431 | 432 | /*[StrId_Reboot] = 433 | { 434 | STR_EN( 435 | "Returning to \xEE\x81\xB3HOME is not available.\n" 436 | "You're about to reboot your console.\n\n" 437 | " \xEE\x80\x80 Reboot\n" 438 | " \xEE\x80\x81 Cancel" 439 | ), 440 | STR_ES( 441 | "Volver a \xEE\x81\xB3HOME no está disponible.\n" 442 | "Está a punto de reiniciar su consola.\n\n" 443 | " \xEE\x80\x80 Reiniciar\n" 444 | " \xEE\x80\x81 Cancelar" 445 | ), 446 | STR_DE( 447 | "Rückkehr zu \xEE\x81\xB3HOME nicht verfügbar.\n" 448 | "Deine Konsole wird neu gestartet.\n\n" 449 | " \xEE\x80\x80 Neu starten\n" 450 | " \xEE\x80\x81 Abbrechen" 451 | ), 452 | STR_FR( 453 | "Retour au menu \xEE\x81\xB3HOME indisponible.\n" 454 | "Vous êtes sur le point de redémarrer\n" 455 | "votre console.\n\n" 456 | " \xEE\x80\x80 Redémarrer\n" 457 | " \xEE\x80\x81 Annuler" 458 | ), 459 | STR_IT( 460 | "Ritorno al menu \xEE\x81\xB3HOME non disponibile.\n" 461 | "Stai per riavviare la tua console.\n\n" 462 | " \xEE\x80\x80 Riavvia\n" 463 | " \xEE\x80\x81 Annulla" 464 | ), 465 | STR_JP( 466 | " \xEE\x81\xB3HOME に戻ることができませんでした。\n" 467 | "今すぐ本体を再起動してください。\n\n" 468 | " \xEE\x80\x80 再起動\n" 469 | " \xEE\x80\x81 キャンセル" 470 | ), 471 | STR_KO( 472 | "\xEE\x81\xB3HOME 으로 돌아갈 수 없습니다.\n" 473 | "콘솔을 재부팅할 것 입니다.\n\n" 474 | " \xEE\x80\x80 재부팅\n" 475 | " \xEE\x80\x81 취소" 476 | ), 477 | STR_PT( 478 | "Regressar para \xEE\x81\xB3HOME não está\n" 479 | "disponível. Está a reiniciar a sua consola.\n\n" 480 | " \xEE\x80\x80 Reiniciar\n" 481 | " \xEE\x80\x81 Cancelar" 482 | ), 483 | STR_NL( 484 | "Terugkeren naar \xEE\x81\xB3HOME is niet\n" 485 | "beschikbaar.Wil je de console herstarten?\n\n" 486 | " \xEE\x80\x80 Herstarten\n" 487 | " \xEE\x80\x81 Annuleren" 488 | ), 489 | STR_RU( 490 | "Возврат к \xEE\x81\xB3HOME недоступен.\n" 491 | "Вы собираетесь перезагрузить консоль.\n\n" 492 | " \xEE\x80\x80 Перезагрузите\n" 493 | " \xEE\x80\x81 Отмена" 494 | ), 495 | STR_ZH_HANS( 496 | "无法返回至主机的 \xEE\x81\xB3HOME 菜单。\n" 497 | "您需要重新启动您的 3DS 设备。\n\n" 498 | " \xEE\x80\x80 重启设备\n" 499 | " \xEE\x80\x81 取消操作" 500 | ), 501 | STR_ZH_HANT( 502 | "無法返回至主機的 \xEE\x81\xB3HOME 選單。\n" 503 | "您需要重新啓動您的 3DS 設備。\n\n" 504 | " \xEE\x80\x80 重啓設備\n" 505 | " \xEE\x80\x81 取消操作" 506 | ), 507 | },*/ 508 | 509 | /*[StrId_ReturnToHome] = 510 | { 511 | STR_EN( 512 | "You're about to return to \xEE\x81\xB3HOME.\n\n" 513 | " \xEE\x80\x80 Return\n" 514 | " \xEE\x80\x81 Cancel\n" 515 | " \xEE\x80\x82 Reboot" 516 | ), 517 | STR_ES( 518 | "Está a punto de volver a \xEE\x81\xB3HOME.\n\n" 519 | " \xEE\x80\x80 Volver\n" 520 | " \xEE\x80\x81 Cancelar\n" 521 | " \xEE\x80\x82 Reiniciar" 522 | ), 523 | STR_DE( 524 | "Rückkehr zum \xEE\x81\xB3HOME-Menü.\n\n" 525 | " \xEE\x80\x80 Fortfahren\n" 526 | " \xEE\x80\x81 Abbrechen\n" 527 | " \xEE\x80\x82 Konsole neustarten" 528 | ), 529 | STR_FR( 530 | "Retour au menu \xEE\x81\xB3HOME.\n\n" 531 | " \xEE\x80\x80 Continuer\n" 532 | " \xEE\x80\x81 Annuler\n" 533 | " \xEE\x80\x82 Redémarrer" 534 | ), 535 | STR_IT( 536 | "Ritorno al menu \xEE\x81\xB3HOME.\n\n" 537 | " \xEE\x80\x80 Continua\n" 538 | " \xEE\x80\x81 Annulla\n" 539 | " \xEE\x80\x82 Riavvia" 540 | ), 541 | STR_JP( 542 | " \xEE\x81\xB3HOME に戻ろうとしています。\n\n" 543 | " \xEE\x80\x80 了解\n" 544 | " \xEE\x80\x81 キャンセル\n" 545 | " \xEE\x80\x82 再起動" 546 | ), 547 | STR_KO( 548 | "\xEE\x81\xB3HOME으로 돌아가려고 합니다.\n\n" 549 | " \xEE\x80\x80 돌아가기\n" 550 | " \xEE\x80\x81 취소\n" 551 | " \xEE\x80\x82 재부팅" 552 | ), 553 | STR_PT( 554 | "Regressar ao menu \xEE\x81\xB3HOME.\n\n" 555 | " \xEE\x80\x80 Regressar\n" 556 | " \xEE\x80\x81 Cancelar\n" 557 | " \xEE\x80\x82 Reiniciar" 558 | ), 559 | STR_NL( 560 | "Je keert zo terug naar \xEE\x81\xB3HOME.\n\n" 561 | " \xEE\x80\x80 Doorgaan\n" 562 | " \xEE\x80\x81 Annuleren\n" 563 | " \xEE\x80\x82 Herstarten" 564 | ), 565 | STR_RU( 566 | "Вы возвращаетесь в \xEE\x81\xB3HOME.\n\n" 567 | " \xEE\x80\x80 Вернуть\n" 568 | " \xEE\x80\x81 Отмена\n" 569 | " \xEE\x80\x82 Перезагрузите" 570 | ), 571 | STR_ZH_HANS( 572 | "您即将返回到主機的 \xEE\x81\xB3HOME 菜单。\n\n" 573 | " \xEE\x80\x80 确认返回\n" 574 | " \xEE\x80\x81 取消操作\n" 575 | " \xEE\x80\x82 重启设备" 576 | ), 577 | STR_ZH_HANT( 578 | "您即將返回到主機的 \xEE\x81\xB3HOME 選單。\n\n" 579 | " \xEE\x80\x80 確認返回\n" 580 | " \xEE\x80\x81 取消操作\n" 581 | " \xEE\x80\x82 重啓設備" 582 | ), 583 | },*/ 584 | 585 | /*[StrId_TitleSelector] = 586 | { 587 | STR_EN("Title selector"), 588 | STR_ES("Selector de título"), 589 | STR_DE("Titel-Auswahl"), 590 | STR_FR("Sélecteur de titre"), 591 | STR_IT("Selettore del titolo"), 592 | STR_JP("タイトルセレクタ"), 593 | STR_KO("타이틀 선택기"), 594 | STR_PT("Selector de Títulos"), 595 | STR_NL("Titel selector"), 596 | STR_RU("Селектор заголовков"), 597 | STR_ZH_HANS("应用启动器"), 598 | STR_ZH_HANT("自製程式啓動器"), 599 | }, 600 | 601 | [StrId_ErrorReadingTitleMetadata] = 602 | { 603 | STR_EN("Error reading title metadata.\n%08lX%08lX@%d"), 604 | STR_ES("Error leyendo los metadatos de los títulos.\n%08lX%08lX@%d"), 605 | STR_DE("Fehler beim Lesen der Titel-Metadaten.\n%08lX%08lX@%d"), 606 | STR_FR( 607 | "Erreur lors de la lecture des métadonnées\n" 608 | "du titre.\n%08lX%08lX@%d" 609 | ), 610 | STR_IT("Errore nella lettura dei metadata dei titoli.\n%08lX%08lX@%d"), 611 | STR_JP("タイトルメタデータを読み取ることができませんでした。\n%08lX%08lX@%d"), 612 | STR_KO("타이틀 메타데이터를 읽는데 실패하였습니다.\n%08lX%08lX@%d"), 613 | STR_PT("Erro a ler os metadados do título.\n%08lX%08lX@%d"), 614 | STR_NL("Fout bij het lezen van titel metadata.\n%08lX%08lX@%d"), 615 | STR_RU("Ошибка чтения метаданных заголовка\n.%08lX%08lX@%d"), 616 | STR_ZH_HANS("读取软件相关信息时发生错误:\n%08lX%08lX@%d"), 617 | STR_ZH_HANT("讀取軟體相關資訊時發生錯誤:\n%08lX%08lX@%d"), 618 | }, 619 | 620 | [StrId_NoTitlesFound] = 621 | { 622 | STR_EN("No titles could be detected."), 623 | STR_ES("No se han podido detectar títulos."), 624 | STR_DE("Keine Titel gefunden."), 625 | STR_FR("Aucun titre trouvé."), 626 | STR_IT("Nessun titolo trovato."), 627 | STR_JP("タイトルが見つかりませんでした。"), 628 | STR_KO("타이틀을 찾을 수 없습니다."), 629 | STR_PT("Nenhum título foi encontrado."), 630 | STR_NL("Geen titels gevonden."), 631 | STR_RU("Заголовки не обнаружены"), 632 | STR_ZH_HANS("主机内找不到任何软件。"), 633 | STR_ZH_HANT("主機内找不到任何軟體。"), 634 | }, 635 | 636 | [StrId_SelectTitle] = 637 | { 638 | STR_EN( 639 | "Please select a target title.\n\n" 640 | " \xEE\x80\x80 Select\n" 641 | " \xEE\x80\x81 Cancel" 642 | ), 643 | STR_ES( 644 | "Elija el título de destino.\n\n" 645 | " \xEE\x80\x80 Seleccionar\n" 646 | " \xEE\x80\x81 Cancelar" 647 | ), 648 | STR_DE( 649 | "Bitte wähle den Ziel-Titel aus.\n\n" 650 | " \xEE\x80\x80 Auswählen\n" 651 | " \xEE\x80\x81 Abbrechen" 652 | ), 653 | STR_FR( 654 | "Veuillez sélectionner un titre de destination.\n\n" 655 | " \xEE\x80\x80 Sélectionner\n" 656 | " \xEE\x80\x81 Annuler" 657 | ), 658 | STR_IT( 659 | "Seleziona il titolo di destinazione.\n\n" 660 | " \xEE\x80\x80 Seleziona\n" 661 | " \xEE\x80\x81 Annulla" 662 | ), 663 | STR_JP( 664 | "ターゲットタイトルを選択してください。\n\n" 665 | " \xEE\x80\x80 選択\n" 666 | " \xEE\x80\x81 キャンセル" 667 | ), 668 | STR_KO( 669 | "대상 타이틀을 선택해 주세요.\n\n" 670 | " \xEE\x80\x80 선택\n" 671 | " \xEE\x80\x81 취소" 672 | ), 673 | STR_PT( 674 | "Por favor escolha um título alvo.\n\n" 675 | " \xEE\x80\x80 Escolher\n" 676 | " \xEE\x80\x81 Cancelar" 677 | ), 678 | STR_NL( 679 | "Selecteer een titel.\n\n" 680 | " \xEE\x80\x80 Selecteer\n" 681 | " \xEE\x80\x81 Annuleren" 682 | ), 683 | STR_RU( 684 | "Выберите целевой заголовок.\n\n" 685 | " \xEE\x80\x80 Выберите\n" 686 | " \xEE\x80\x81 Отмена" 687 | ), 688 | STR_ZH_HANS( 689 | "请选择一个目标软件。\n\n" 690 | " \xEE\x80\x80 确认\n" 691 | " \xEE\x80\x81 取消" 692 | ), 693 | STR_ZH_HANT( 694 | "請選擇一個目標軟體。\n\n" 695 | " \xEE\x80\x80 確認\n" 696 | " \xEE\x80\x81 取消" 697 | ), 698 | }, 699 | 700 | [StrId_NoTargetTitleSupport] = 701 | { 702 | STR_EN( 703 | "This homebrew exploit does not have support\n" 704 | "for launching applications under target titles.\n" 705 | "Please use a different exploit." 706 | ), 707 | STR_ES( 708 | "Este exploit de homebrew no tiene soporte para\n" 709 | "ejecutar aplicaciones bajo títulos de destino.\n" 710 | "Use otro exploit diferente." 711 | ), 712 | STR_DE( 713 | "Dieser Homebrew-Exploit unterstützt das Starten\n" 714 | "von Anwendungen unter Ziel-Titeln nicht.\n" 715 | "Bitte verwende einen anderen Exploit." 716 | ), 717 | STR_FR( 718 | "Cet exploit homebrew ne permet pas de lancer\n" 719 | "des applications sous des titres précis.\n" 720 | "Veuillez utiliser un exploit différent." 721 | ), 722 | STR_IT( 723 | "Questo exploit homebrew non permette di avviare\n" 724 | "applicazioni in titoli specifici.\n" 725 | "Utilizza un exploit diverso." 726 | ), 727 | STR_JP( 728 | "この自作エクスプロイトでは、ターゲットタイトルの\n" 729 | "下でアプリを起動することができません。\n" 730 | "別のエクスプロイトを使用してください。" 731 | ), 732 | STR_KO( 733 | "이 홈브류 익스플로잇은 해당 타이틀에서 애플리케이션을\n" 734 | "실행하는 것을 지원하지 않습니다.\n" 735 | "다른 익스플로잇을 사용해 주세요." 736 | ), 737 | STR_PT( 738 | "Este exploit homebrew não têm suporte\n" 739 | "para executar aplicações no título alvo.\n" 740 | "Por favor use um exploit diferente." 741 | ), 742 | STR_NL( 743 | "Deze homebrew exploit heeft geen ondersteuning\n" 744 | "voor het starten van toepassingen met de gekozen titlel.\n" 745 | "Gebruik een andere exploit." 746 | ), 747 | STR_RU( 748 | "Этот эксплойт homebrew не поддерживает запуск\n" 749 | "приложений под целевыми заголовками.\n" 750 | "Пожалуйста, используйте другой эксплойт." 751 | ), 752 | STR_ZH_HANS( 753 | "您所利用漏洞启动的「自制软件启动器」,\n" 754 | "无法在当前选中的软件中启动自制软件。\n" 755 | "请使用其它的漏洞来启动「自制软件启动器」。" 756 | ), 757 | STR_ZH_HANT( 758 | "您所利用漏洞開啓的「自製軟體啓動器」\n" 759 | "無法在當前選中的軟體啓動自製軟體。\n" 760 | "請利用其它漏洞來啓動「自製軟體啓動器」。" 761 | ), 762 | }, 763 | 764 | [StrId_MissingTargetTitle] = 765 | { 766 | STR_EN( 767 | "The application you attempted to run requires\n" 768 | "a title that is not installed in the system." 769 | ), 770 | STR_ES( 771 | "La aplicación seleccionada necesita un título\n" 772 | "que no está instalado en el sistema." 773 | ), 774 | STR_DE( 775 | "Die ausgewählte Anwendung benötigt einen\n" 776 | "Titel der nicht installiert ist." 777 | ), 778 | STR_FR( 779 | "L'application sélectionnée requiert un titre\n" 780 | "qui n'a pas été installé sur le système." 781 | ), 782 | STR_IT( 783 | "L'applicazione selezionata richiede un titolo\n" 784 | "che non è installato nel sistema." 785 | ), 786 | STR_JP( 787 | "このアプリを実行するために\n" 788 | "必要なタイトルがインストールされていません。" 789 | ), 790 | STR_KO( 791 | "해당 애플리케이션은 시스템에 설치되지 않은\n" 792 | "타이틀을 요구합니다." 793 | ), 794 | STR_PT( 795 | "A aplicação que acabou de tentar executar requer\n" 796 | "um título que não está instalado neste sistema." 797 | ), 798 | STR_NL( 799 | "De toepassing die je probeert te starten\n" 800 | "vereist een titel die niet geinstalleerd is." 801 | ), 802 | STR_RU( 803 | "Для приложения требуется зависимость,\n" 804 | "которая не установлена." 805 | ), 806 | STR_ZH_HANS( 807 | "主机找不到该应用程序\n" 808 | "所需求的软件。" 809 | ), 810 | STR_ZH_HANT( 811 | "主機找不到該應用程式\n" 812 | "所需求的軟體。" 813 | ), 814 | },*/ 815 | 816 | [StrId_NetLoader] = 817 | { 818 | STR_EN("NetLoader"), 819 | STR_ES("Cargador de programas"), 820 | STR_DE("Netzwerk-Loader"), 821 | STR_FR("NetLoader"), 822 | STR_IT("Caricamento programmi"), 823 | STR_JP("ネットローダ"), 824 | STR_KO("네트워크 로더"), 825 | STR_PT("Carregador de programas"), 826 | STR_NL("netwerk lader"), 827 | STR_RU("NetLoader"), 828 | STR_ZH_HANS("从网络加载程序"), 829 | STR_ZH_HANT("從網路載入程式"), 830 | }, 831 | 832 | [StrId_NetLoaderUnavailable] = 833 | { 834 | STR_EN("The NetLoader is currently unavailable."), 835 | STR_ES("El cargador de programas no está disponible."), 836 | STR_DE("Der Netzwerk-Loader ist zur Zeit nicht verfügbar."), 837 | STR_FR("Le programme nxlink est indisponible."), 838 | STR_IT("Il caricamento programmi nxlink non è disponibile."), 839 | STR_JP("nxlinkネットローダは現在利用できません。"), 840 | STR_KO("현재 네트워크 로더는 사용이 불가합니다."), 841 | STR_PT("O carregador de programas está de momento indisponível."), 842 | STR_NL("De netwerk lader is niet beschikbaar."), 843 | STR_RU("NetLoader в настоящее время недоступен."), 844 | STR_ZH_HANS("无法启动 nxlink 网络执行模块。"), 845 | STR_ZH_HANT("無法啓動 nxlink 網路執行模組。"), 846 | }, 847 | 848 | [StrId_NetLoaderError] = 849 | { 850 | STR_EN("An error occurred.\nTechnical details: [%s:%d]"), 851 | STR_ES("Ha ocurrido un error.\nDatos técnicos: [%s:%d]"), 852 | STR_DE("Ein Fehler ist aufgetreten\nTechnische Details: [%s:%d]"), 853 | STR_FR("Une erreur s'est produite.\nDétails techniques : [%s:%d]"), 854 | STR_IT("Si è verificato un errore.\nDettagli tecnici : [%s:%d]"), 855 | STR_JP("エラーが発生しました。\n技術的な詳細:[%s:%d]"), 856 | STR_KO("오류가 발생했습니다.\n기술적인 세부사항: [%s:%d]"), 857 | STR_PT("Ocorreu um erro.\nDetalhes técnicos: [%s:%d]"), 858 | STR_NL("Er is een fout opgetreden\nTechnische details: [%s:%d]"), 859 | STR_RU("Произошла ошибка.\nТехнические подробности: [%s:%d]"), 860 | STR_ZH_HANS("发生错误。\n详细错误信息:[%s:%d]"), 861 | STR_ZH_HANT("發生錯誤。\n詳細錯誤資訊:[%s:%d]"), 862 | }, 863 | 864 | [StrId_NetLoaderOffline] = 865 | { 866 | STR_EN("Offline, waiting for network…"), 867 | STR_DE("Offline, warte auf Netzwerk…"), 868 | STR_FR("Hors-ligne, en attente d'une connection..."), 869 | STR_IT("Disconnesso, in attesa della connessione…"), 870 | STR_ES("Desconectado, esperando a la red..."), 871 | STR_JP("オフラインです。ネットワーク接続を待っています…"), 872 | STR_KO("연결 끊김, 네트워크 기다리는 중…"), 873 | STR_ZH_HANS("无法连接网络,等待网络连接…"), 874 | STR_ZH_HANT("目前已離線,等待網路連線…"), 875 | STR_RU("Оффлайн, ожидание сети…"), 876 | }, 877 | 878 | [StrId_NetLoaderActive] = 879 | { 880 | STR_EN( 881 | "Waiting for nxlink to connect…\n" 882 | "IP Addr: %lu.%lu.%lu.%lu, Port: %d" 883 | ), 884 | STR_ES( 885 | "Esperando a que se conecte nxlink…\n" 886 | "Dir.IP: %lu.%lu.%lu.%lu, Puerto: %d" 887 | ), 888 | STR_DE( 889 | "Warte auf Verbindung von nxlink…\n" 890 | "IP Addr: %lu.%lu.%lu.%lu, Port: %d" 891 | ), 892 | STR_FR( 893 | "En attente de la connexion de nxlink…\n" 894 | "Adr. IP : %lu.%lu.%lu.%lu, Port : %d" 895 | ), 896 | STR_IT( 897 | "In attesa della connessione di nxlink…\n" 898 | "Ind. IP : %lu.%lu.%lu.%lu, Porta : %d" 899 | ), 900 | STR_JP( 901 | "nxlinkが接続されるのを待っています…\n" 902 | "IPアドレス:%lu.%lu.%lu.%lu, ポート番号:%d" 903 | ), 904 | STR_KO( 905 | "nxlink의 연결을 대기중…\n" 906 | "IP 주소: %lu.%lu.%lu.%lu, 포트: %d" 907 | ), 908 | STR_PT( 909 | "A aguardar pela conexão do nxlink…\n" 910 | "End. IP: %lu.%lu.%lu.%lu, Porta: %d" 911 | ), 912 | STR_NL( 913 | "Wachten op nxlink verbinding…\n" 914 | "IP Addr: %lu.%lu.%lu.%lu, Poort: %d" 915 | ), 916 | STR_RU( 917 | "Ожидание подключения nxlink…\n" 918 | "IP-адрес: %lu.%lu.%lu.%lu, Порт: %d" 919 | ), 920 | STR_ZH_HANS( 921 | "等待 nxlink 连接…\n" 922 | "IP 地址:%lu.%lu.%lu.%lu,端口:%d" 923 | ), 924 | STR_ZH_HANT( 925 | "等待 nxlink 連接…\n" 926 | "IP 位址:%lu.%lu.%lu.%lu,連接埠:%d" 927 | ), 928 | }, 929 | 930 | [StrId_NetLoaderTransferring] = 931 | { 932 | STR_EN( 933 | "Transferring…\n" 934 | "%zu out of %zu KiB written" 935 | ), 936 | STR_ES( 937 | "Transfiriendo…\n" 938 | "%zu de %zu KiB escritos" 939 | ), 940 | STR_DE( 941 | "Übertragen…\n" 942 | "%zu von %zu KiB geschrieben" 943 | ), 944 | STR_FR( 945 | "Transfert…\n" 946 | "%zu sur %zu Kio écrits" 947 | ), 948 | STR_IT( 949 | "Trasferimento…\n" 950 | "%zu di %zu KiB scritti" 951 | ), 952 | STR_JP( 953 | "データを転送しています…\n" 954 | "%zu / %zu KiB 転送済み" 955 | ), 956 | STR_KO( 957 | "전송 중…\n" 958 | "%zu / %zu KiB 쓰여짐" 959 | ), 960 | STR_PT( 961 | "A transferir…\n" 962 | "%zu de %zu KiB escritos" 963 | ), 964 | STR_NL( 965 | "Overbrengen…\n" 966 | "%zu van %zu KiB geschreven" 967 | ), 968 | STR_RU( 969 | "Передача…\n" 970 | "%zu из %zu КиБ получено" 971 | ), 972 | STR_ZH_HANS( 973 | "正在传输…\n" 974 | "已完成 %zu / %zu KiB" 975 | ), 976 | STR_ZH_HANT( 977 | "正在傳輸…\n" 978 | "已完成 %zu / %zu KiB" 979 | ), 980 | }, 981 | }; 982 | -------------------------------------------------------------------------------- /common/language.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef __SWITCH__ 3 | #include 4 | #endif 5 | 6 | typedef enum 7 | { 8 | StrId_Loading = 0, 9 | StrId_AppletMode, 10 | StrId_Directory, 11 | StrId_DefaultPublisher, 12 | StrId_IOError, 13 | StrId_CouldNotOpenFile, 14 | StrId_NroNotFound, 15 | 16 | StrId_NoAppsFound_Title, 17 | StrId_NoAppsFound_Msg, 18 | 19 | StrId_LastLoadResult, 20 | StrId_AppLaunchError, 21 | 22 | StrId_AppInfo_Author, 23 | StrId_AppInfo_Version, 24 | StrId_Actions_Launch, 25 | StrId_Actions_Open, 26 | StrId_Actions_Back, 27 | StrId_Actions_Apply, 28 | StrId_Actions_Star, 29 | StrId_Actions_Unstar, 30 | 31 | StrId_MsgBox_OK, 32 | 33 | StrId_Reboot, 34 | StrId_ReturnToHome, 35 | 36 | StrId_TitleSelector, 37 | StrId_ErrorReadingTitleMetadata, 38 | StrId_NoTitlesFound, 39 | StrId_SelectTitle, 40 | 41 | StrId_NoTargetTitleSupport, 42 | StrId_MissingTargetTitle, 43 | 44 | StrId_NetLoader, 45 | StrId_NetLoaderUnavailable, 46 | StrId_NetLoaderOffline, 47 | StrId_NetLoaderError, 48 | StrId_NetLoaderActive, 49 | StrId_NetLoaderTransferring, 50 | 51 | StrId_ThemeMenu, 52 | StrId_ThemeNotApplied, 53 | StrId_DefaultThemeName, 54 | 55 | StrId_Max, 56 | } StrId; 57 | 58 | extern const char* const g_strings[StrId_Max][17]; 59 | 60 | -------------------------------------------------------------------------------- /common/launch.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | size_t launchAddArg(argData_s* ad, const char* arg) { 4 | size_t len = strlen(arg)+1; 5 | if ((ad->dst+len) >= (char*)(ad->buf + sizeof(ad->buf))) return len; // Overflow 6 | ad->buf[0]++; 7 | strcpy(ad->dst, arg); 8 | ad->dst += len; 9 | return len; 10 | } 11 | 12 | void launchAddArgsFromString(argData_s* ad, char* arg) { 13 | char c, *pstr, *str=arg, *endarg = arg+strlen(arg); 14 | 15 | do 16 | { 17 | do 18 | { 19 | c = *str++; 20 | } while ((c == ' ' || c == '\t') && str < endarg); 21 | 22 | pstr = str-1; 23 | 24 | if (c == '\"') 25 | { 26 | pstr++; 27 | while(*str++ != '\"' && str < endarg); 28 | } 29 | else if (c == '\'') 30 | { 31 | pstr++; 32 | while(*str++ != '\'' && str < endarg); 33 | } 34 | else 35 | { 36 | do 37 | { 38 | c = *str++; 39 | } while (c != ' ' && c != '\t' && str < endarg); 40 | } 41 | 42 | str--; 43 | 44 | if (str == (endarg - 1)) 45 | { 46 | if(*str == '\"' || *str == '\'') 47 | *(str++) = 0; 48 | else 49 | str++; 50 | } 51 | else 52 | { 53 | *(str++) = '\0'; 54 | } 55 | 56 | launchAddArg(ad, pstr); 57 | 58 | } while(strmenu = m; 28 | if (m->lastEntry) 29 | { 30 | m->lastEntry->next = me; 31 | m->lastEntry = me; 32 | } else 33 | { 34 | m->firstEntry = me; 35 | m->lastEntry = me; 36 | } 37 | m->xPos = 0; 38 | m->slideSpeed = 0; 39 | m->nEntries ++; 40 | } 41 | 42 | static void menuAddEntry(menuEntry_s* me) { 43 | _menuAddEntry(&s_menu[!s_curMenu], me); 44 | } 45 | 46 | void menuFileassocAddEntry(menuEntry_s* me) { 47 | _menuAddEntry(&s_menuFileassoc[!s_curMenuFileassoc], me); 48 | } 49 | 50 | static void menuAddEntryToFront(menuEntry_s* me) { 51 | menu_s* m = &s_menu[!s_curMenu]; 52 | me->menu = m; 53 | if (m->lastEntry) 54 | { 55 | me->next = m->firstEntry; 56 | m->firstEntry = me; 57 | } else 58 | { 59 | m->firstEntry = me; 60 | m->lastEntry = me; 61 | } 62 | m->xPos = 0; 63 | m->slideSpeed = 0; 64 | m->nEntries ++; 65 | } 66 | 67 | static void _menuClear(menu_s* m) { 68 | menuEntry_s *cur, *next; 69 | for (cur = m->firstEntry; cur; cur = next) 70 | { 71 | next = cur->next; 72 | menuDeleteEntry(cur, 0); 73 | } 74 | memset(m, 0, sizeof(*m)); 75 | } 76 | 77 | static void menuClear(void) { 78 | _menuClear(&s_menu[!s_curMenu]); 79 | } 80 | 81 | static void menuFileassocClear(void) { 82 | _menuClear(&s_menuFileassoc[!s_curMenuFileassoc]); 83 | } 84 | 85 | static int menuEntryCmp(const void *p1, const void *p2) { 86 | const menuEntry_s* lhs = *(menuEntry_s**)p1; 87 | const menuEntry_s* rhs = *(menuEntry_s**)p2; 88 | 89 | if(lhs->type == rhs->type) 90 | return strcasecmp(lhs->name, rhs->name); 91 | if(lhs->type == ENTRY_TYPE_FOLDER) 92 | return -1; 93 | return 1; 94 | } 95 | 96 | static void menuSort(void) { 97 | int i; 98 | menu_s* m = &s_menu[!s_curMenu]; 99 | int nEntries = m->nEntries; 100 | if (nEntries==0) return; 101 | int nEntriesStar = 0, nEntriesNoStar = 0; 102 | 103 | menuEntry_s** list = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*)); 104 | if(list == NULL) return; 105 | menuEntry_s** listStar = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*)); 106 | if(listStar == NULL) { 107 | free(list); 108 | return; 109 | } 110 | 111 | menuEntry_s* p = m->firstEntry; 112 | for(i = 0; i < nEntries; ++i) { 113 | if (p->starred) 114 | listStar[nEntriesStar++] = p; 115 | else 116 | list[nEntriesNoStar++] = p; 117 | p = p->next; 118 | } 119 | 120 | qsort(listStar, nEntriesStar, sizeof(menuEntry_s*), menuEntryCmp); 121 | qsort(list, nEntriesNoStar, sizeof(menuEntry_s*), menuEntryCmp); 122 | 123 | menuEntry_s** pp = &m->firstEntry; 124 | for(i = 0; i < nEntriesStar; ++i) { 125 | *pp = listStar[i]; 126 | pp = &(*pp)->next; 127 | } 128 | for(i = 0; i < nEntriesNoStar; ++i) { 129 | *pp = list[i]; 130 | pp = &(*pp)->next; 131 | } 132 | m->lastEntry = list[nEntries-1]; 133 | *pp = NULL; 134 | 135 | free(list); 136 | free(listStar); 137 | } 138 | 139 | void menuReorder (void) { 140 | s_curMenu = !s_curMenu; 141 | menuSort(); 142 | s_curMenu = !s_curMenu; 143 | menuClear(); 144 | } 145 | 146 | int menuScan(const char* target) { 147 | int pos; 148 | char dirsep[8]; 149 | 150 | if (chdir(target) < 0) return 1; 151 | if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL) 152 | return 1; 153 | 154 | memset(dirsep, 0, sizeof(dirsep)); 155 | dirsep[0] = '/'; 156 | 157 | //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. 158 | pos = strlen(s_menu[!s_curMenu].dirname); 159 | if (pos > 0) { 160 | if (s_menu[!s_curMenu].dirname[pos-1] == '/') dirsep[0] = 0; 161 | } 162 | 163 | DIR* dir; 164 | struct dirent* dp; 165 | char tmp_path[PATH_MAX+1]; 166 | dir = opendir(s_menu[!s_curMenu].dirname); 167 | if (!dir) return 2; 168 | 169 | while ((dp = readdir(dir))) 170 | { 171 | menuEntry_s* me = NULL; 172 | bool shortcut = false; 173 | if (dp->d_name[0]=='.') 174 | continue; 175 | 176 | bool entrytype=0; 177 | 178 | memset(tmp_path, 0, sizeof(tmp_path)); 179 | snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s", s_menu[!s_curMenu].dirname, dirsep, dp->d_name); 180 | 181 | #ifdef _DIRENT_HAVE_D_TYPE 182 | if (dp->d_type == DT_UNKNOWN) 183 | continue; 184 | entrytype = dp->d_type != DT_REG; 185 | #else 186 | struct stat tmpstat; 187 | 188 | if(stat(tmp_path, &tmpstat)==-1) 189 | continue; 190 | 191 | entrytype = (tmpstat.st_mode & S_IFMT) != S_IFREG; 192 | #endif 193 | 194 | if (entrytype) 195 | me = menuCreateEntry(ENTRY_TYPE_FOLDER); 196 | else 197 | { 198 | const char* ext = getExtension(dp->d_name); 199 | if (strcasecmp(ext, ".nro")==0/* || (shortcut = strcasecmp(ext, ".xml")==0)*/) 200 | me = menuCreateEntry(ENTRY_TYPE_FILE); 201 | 202 | if (!me) 203 | me = menuCreateEntry(ENTRY_TYPE_FILE_OTHER); 204 | } 205 | 206 | if (!me) 207 | continue; 208 | 209 | strncpy(me->path, tmp_path, sizeof(me->path)-1); 210 | me->path[sizeof(me->path)-1] = 0; 211 | 212 | if (menuEntryLoad(me, dp->d_name, shortcut, true)) 213 | menuAddEntry(me); 214 | else 215 | menuDeleteEntry(me, 0); 216 | } 217 | 218 | closedir(dir); 219 | menuSort(); 220 | 221 | // Swap the menu and clear the previous menu 222 | s_curMenu = !s_curMenu; 223 | menuClear(); 224 | return 0; 225 | } 226 | 227 | int themeMenuScan(const char* target) { 228 | menuClear(); 229 | if (chdir(target) < 0) return 1; 230 | if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL) 231 | return 1; 232 | DIR* dir; 233 | struct dirent* dp; 234 | char tmp_path[PATH_MAX+1]; 235 | dir = opendir(s_menu[!s_curMenu].dirname); 236 | if (!dir) return 2; 237 | 238 | while ((dp = readdir(dir))) 239 | { 240 | menuEntry_s* me = NULL; 241 | 242 | bool shortcut = false; 243 | if (dp->d_name[0]=='.') 244 | continue; 245 | 246 | memset(tmp_path, 0, sizeof(tmp_path)); 247 | snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name); 248 | 249 | bool entrytype=0; 250 | 251 | #ifdef _DIRENT_HAVE_D_TYPE 252 | if (dp->d_type == DT_UNKNOWN) 253 | continue; 254 | entrytype = dp->d_type != DT_REG; 255 | #else 256 | struct stat tmpstat; 257 | 258 | if(stat(tmp_path, &tmpstat)==-1) 259 | continue; 260 | 261 | entrytype = (tmpstat.st_mode & S_IFMT) != S_IFREG; 262 | #endif 263 | 264 | const char* ext = getExtension(dp->d_name); 265 | if (entrytype || strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0 || strcasecmp(ext, ".zip")==0) 266 | me = menuCreateEntry(ENTRY_TYPE_THEME); 267 | 268 | if (!me) 269 | continue; 270 | 271 | strncpy(me->path, tmp_path, sizeof(me->path)-1); 272 | me->path[sizeof(me->path)-1] = 0; 273 | if (menuEntryLoad(me, dp->d_name, shortcut, true)) 274 | menuAddEntry(me); 275 | else 276 | menuDeleteEntry(me, 0); 277 | } 278 | 279 | closedir(dir); 280 | menuSort(); 281 | 282 | menuEntry_s* me = menuCreateEntry(ENTRY_TYPE_THEME); 283 | 284 | if(me) { 285 | if(menuEntryLoad(me, textGetString(StrId_DefaultThemeName), false, false))//Create Default theme Menu Entry 286 | menuAddEntryToFront(me); 287 | else 288 | menuDeleteEntry(me, 0); 289 | } 290 | // Swap the menu and clear the previous menu 291 | s_curMenu = !s_curMenu; 292 | menuClear(); 293 | return 0; 294 | } 295 | 296 | int menuFileassocScan(const char* target) { 297 | menuFileassocClear(); 298 | if (chdir(target) < 0) return 1; 299 | if (getcwd(s_menuFileassoc[!s_curMenuFileassoc].dirname, PATH_MAX+1) == NULL) 300 | return 1; 301 | DIR* dir; 302 | struct dirent* dp; 303 | char tmp_path[PATH_MAX+1]; 304 | dir = opendir(s_menuFileassoc[!s_curMenuFileassoc].dirname); 305 | if (!dir) return 2; 306 | 307 | while ((dp = readdir(dir))) 308 | { 309 | if (dp->d_name[0]=='.') 310 | continue; 311 | 312 | memset(tmp_path, 0, sizeof(tmp_path)); 313 | snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menuFileassoc[!s_curMenuFileassoc].dirname, dp->d_name); 314 | 315 | const char* ext = getExtension(dp->d_name); 316 | if (strcasecmp(ext, ".cfg")!=0) 317 | continue; 318 | 319 | menuEntryFileassocLoad(tmp_path); 320 | } 321 | 322 | closedir(dir); 323 | 324 | // Swap the menu and clear the previous menu 325 | s_curMenuFileassoc = !s_curMenuFileassoc; 326 | menuFileassocClear(); 327 | return 0; 328 | } 329 | -------------------------------------------------------------------------------- /common/menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef _WIN32 4 | #include 5 | #else 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | #include 9 | #undef DrawText 10 | #undef MessageBox 11 | #endif 12 | 13 | #define ENTRY_NAMELENGTH 0x200 14 | #define ENTRY_AUTHORLENGTH 0x100 15 | #define ENTRY_VERLENGTH 0x10 16 | #define ENTRY_ARGBUFSIZE 0x400 17 | 18 | typedef enum 19 | { 20 | ENTRY_TYPE_FILE, 21 | ENTRY_TYPE_FOLDER, 22 | ENTRY_TYPE_THEME, 23 | ENTRY_TYPE_FILEASSOC, 24 | ENTRY_TYPE_FILE_OTHER, 25 | } MenuEntryType; 26 | 27 | typedef struct menuEntry_s_tag menuEntry_s; 28 | typedef struct menu_s_tag menu_s; 29 | 30 | struct menu_s_tag 31 | { 32 | menuEntry_s *firstEntry, *lastEntry; 33 | int nEntries; 34 | int curEntry; 35 | int xPos; 36 | int slideSpeed; 37 | 38 | char dirname[PATH_MAX+1]; 39 | }; 40 | 41 | typedef struct 42 | { 43 | char* dst; 44 | uint32_t buf[ENTRY_ARGBUFSIZE/sizeof(uint32_t)]; 45 | struct in_addr nxlink_host; 46 | } argData_s; 47 | 48 | struct menuEntry_s_tag 49 | { 50 | menu_s* menu; 51 | menuEntry_s* next; 52 | MenuEntryType type; 53 | 54 | char path[PATH_MAX+8]; 55 | char starpath[PATH_MAX+8]; 56 | argData_s args; 57 | 58 | bool fileassoc_type;//0=file_extension, 1 = filename 59 | char fileassoc_str[PATH_MAX+1];//file_extension/filename 60 | 61 | char name[ENTRY_NAMELENGTH+1]; 62 | char author[ENTRY_AUTHORLENGTH+1]; 63 | char version[ENTRY_VERLENGTH+1]; 64 | 65 | uint8_t *icon; 66 | size_t icon_size; 67 | uint8_t *icon_gfx; 68 | uint8_t *icon_gfx_small; 69 | 70 | bool starred; 71 | 72 | NacpStruct *nacp; 73 | }; 74 | 75 | typedef enum 76 | { 77 | IMAGE_MODE_RGB24, 78 | IMAGE_MODE_RGBA32 79 | } ImageMode; 80 | 81 | extern double menuTimer; 82 | 83 | #ifdef __cplusplus 84 | extern "C" { 85 | #endif 86 | 87 | void menuEntryInit(menuEntry_s* me, MenuEntryType type); 88 | void menuEntryFree(menuEntry_s* me, bool skip_icongfx); 89 | bool fileExists(const char* path); 90 | bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists); 91 | void menuEntryParseIcon(menuEntry_s* me); 92 | uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode); 93 | void menuEntryParseNacp(menuEntry_s* me); 94 | 95 | void menuEntryFileassocLoad(const char* filepath); 96 | 97 | menuEntry_s* menuCreateEntry(MenuEntryType type); 98 | 99 | void menuFileassocAddEntry(menuEntry_s* me); 100 | void menuDeleteEntry(menuEntry_s* me, bool skip_icongfx); 101 | 102 | menu_s* menuGetCurrent(void); 103 | menu_s* menuFileassocGetCurrent(void); 104 | void menuReorder (void); 105 | int menuScan(const char* target); 106 | int themeMenuScan(const char* target); 107 | int menuFileassocScan(const char* target); 108 | 109 | void launchMenuEntryTask(menuEntry_s* arg); 110 | void toggleStarState(menuEntry_s* arg); 111 | void launchApplyThemeTask(menuEntry_s* arg); 112 | void launchMenuBackTask(); 113 | void launchMenuNetloaderTask(); 114 | char *menuGetRootPath(void); 115 | char *menuGetRootBasePath(void); 116 | 117 | void menuHandleAButton(void); 118 | void menuHandleXButton(void); 119 | 120 | bool menuIsNetloaderActive(void); 121 | 122 | #ifdef __cplusplus 123 | } 124 | #endif 125 | 126 | static inline char* getExtension(const char* str) 127 | { 128 | const char* p; 129 | for (p = str+strlen(str); p >= str && *p != '.'; p--); 130 | return (char*)p; 131 | } 132 | 133 | static inline char* getSlash(const char* str) 134 | { 135 | const char* p; 136 | for (p = str+strlen(str); p >= str && *p != '/'; p--); 137 | return (char*)p; 138 | } 139 | 140 | -------------------------------------------------------------------------------- /common/message-box.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "message-box.h" 3 | 4 | MessageBox currMsgBox; 5 | 6 | static bool msgboxNetloaderEnabled; 7 | static char msgboxNetloaderText[256]; 8 | static bool msgboxNetloaderProgressEnabled; 9 | static float msgboxNetloaderProgress; 10 | 11 | void drawMsgBoxBgToBuff(color_t *buff, int width, int height) { 12 | int x, y; 13 | int off; 14 | int circle_center_x, circle_center_y; 15 | int corner_size = 0; 16 | float rad, alpha; 17 | color_t base_color = themeCurrent.backgroundColor; 18 | color_t color; 19 | ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator]; 20 | 21 | for (y=0; y 0) { 24 | if (xwidth-corner_size && yheight-corner_size) { // bottom left corner 33 | circle_center_x = corner_size-1; 34 | circle_center_y = height-corner_size; 35 | } 36 | else if (x>width-corner_size && y>height-corner_size) { // bottom right corner 37 | circle_center_x = width-corner_size; 38 | circle_center_y = height-corner_size; 39 | } 40 | else { 41 | circle_center_x = -1; 42 | circle_center_y = -1; 43 | } 44 | 45 | if (circle_center_x == -1 && circle_center_y == -1) { 46 | color = base_color; 47 | } 48 | else { 49 | rad = sqrt(pow(circle_center_x - x, 2) + pow(circle_center_y - y, 2)); 50 | alpha = (float)corner_size - rad; 51 | 52 | if (rad < corner_size) { 53 | if (alpha < 1.0) { 54 | color = MakeColor(base_color.r, base_color.g, base_color.b, base_color.a * alpha); 55 | } 56 | else 57 | color = base_color; 58 | } 59 | else 60 | color = MakeColor(0, 0, 0, 0); 61 | } 62 | } 63 | else 64 | color = base_color; 65 | 66 | if (y == height + layoutobj->posStart[1]) { 67 | color = themeCurrent.separatorColor; 68 | } 69 | 70 | off = (y * width + x); 71 | *((uint32_t *)&buff[off]) = color.r | (color.g<<8) | (color.b<<16) | (color.a<<24); 72 | } 73 | } 74 | } 75 | 76 | void menuDrawMsgBox() { 77 | if (!menuIsMsgBoxOpen()) 78 | return; 79 | 80 | ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator]; 81 | int off; 82 | int x, y; 83 | int start_x = 1280 / 2 - currMsgBox.width / 2; 84 | int start_y = 720 / 2 - currMsgBox.height / 2; 85 | int end_x = start_x + currMsgBox.width; 86 | uint32_t text_width, text_height; 87 | color_t curr_color; 88 | 89 | color_t border_color; 90 | int sep_start_y = currMsgBox.height + layoutobj->posStart[1]; 91 | int border_thickness = 6; 92 | 93 | int shadow_start_y, shadow_y; 94 | int shadow_inset; 95 | int shadow_size = 4; 96 | float highlight_multiplier = highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5); 97 | color_t shadow_color; 98 | uint8_t shadow_alpha_base = 80; 99 | 100 | const char* textptr = currMsgBox.text; 101 | 102 | int progress_width = (int)(msgboxNetloaderProgress*currMsgBox.width); 103 | char progress_text[32]; 104 | 105 | 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); 106 | 107 | // Darken the background 108 | for (y=0; y<720; y++) { 109 | for (x=0; x<1280; x++) { 110 | DrawPixel(x, y, MakeColor(0, 0, 0, 100)); 111 | } 112 | } 113 | 114 | // Draw the message box background 115 | for (y=0; y=currMsgBox.width-border_thickness) && y>sep_start_y) || 122 | (y>sep_start_y && y<=sep_start_y+border_thickness) || (y>=currMsgBox.height-border_thickness)) { 123 | curr_color = border_color; 124 | } 125 | } 126 | else if (msgboxNetloaderProgressEnabled && y > sep_start_y && x < progress_width) { 127 | curr_color = themeCurrent.progressBarColor; 128 | } 129 | 130 | DrawPixel(start_x+x, start_y+y, curr_color); 131 | } 132 | } 133 | 134 | if (msgboxNetloaderEnabled) textptr = msgboxNetloaderText; 135 | 136 | GetTextDimensions(interuiregular18, textptr, &text_width, &text_height); 137 | x = GetTextXCoordinate(interuiregular18, start_x + (currMsgBox.width / 2), textptr, 'c'); 138 | 139 | if (text_width < currMsgBox.width && text_height < sep_start_y) { 140 | DrawText(interuiregular18, x, start_y + (sep_start_y - text_height) / 2, themeCurrent.textColor, textptr); 141 | } 142 | 143 | layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxBottomText]; 144 | y = start_y + currMsgBox.height + layoutobj->posStart[1]; 145 | 146 | if (!msgboxNetloaderEnabled) { 147 | x = GetTextXCoordinate(interuimedium20, start_x + (currMsgBox.width / 2), textGetString(StrId_MsgBox_OK), 'c'); 148 | DrawText(interuimedium20, x, y, themeCurrent.textColor, textGetString(StrId_MsgBox_OK)); 149 | } 150 | 151 | if (msgboxNetloaderEnabled && msgboxNetloaderProgressEnabled) { 152 | memset(progress_text, 0, sizeof(progress_text)); 153 | snprintf(progress_text, sizeof(progress_text)-1, "%.02f%%", msgboxNetloaderProgress*100); 154 | x = GetTextXCoordinate(interuiregular18, start_x + (currMsgBox.width / 2), progress_text, 'c'); 155 | DrawText(interuiregular18, x, y, themeCurrent.textColor, progress_text); 156 | } 157 | 158 | shadow_start_y = start_y + currMsgBox.height; 159 | 160 | for (shadow_y=shadow_start_y; shadow_y = start_x + shadow_inset && x <= end_x - shadow_inset) { 166 | DrawPixel(x, shadow_y, shadow_color); 167 | } 168 | } 169 | } 170 | } 171 | 172 | void menuCreateMsgBox(int width, int height, const char *text) { 173 | if (menuIsMsgBoxOpen()) 174 | return; 175 | 176 | char *new_text = strdup(text); 177 | if (new_text==NULL) 178 | return; 179 | 180 | currMsgBox = (MessageBox) { width, height, NULL, new_text }; 181 | 182 | currMsgBox.bg = malloc(currMsgBox.width*currMsgBox.height*4); 183 | 184 | if (currMsgBox.bg) { 185 | drawMsgBoxBgToBuff(currMsgBox.bg, currMsgBox.width, currMsgBox.height); 186 | } 187 | } 188 | 189 | bool menuIsMsgBoxOpen() { 190 | return currMsgBox.width != 0 || currMsgBox.height != 0 || currMsgBox.bg || currMsgBox.text; 191 | } 192 | 193 | void menuCloseMsgBox() { 194 | if (currMsgBox.bg) { 195 | free(currMsgBox.bg); 196 | currMsgBox.bg = NULL; 197 | } 198 | 199 | currMsgBox.width = currMsgBox.height = 0; 200 | 201 | if (currMsgBox.text) { 202 | free(currMsgBox.text); 203 | currMsgBox.text = NULL; 204 | } 205 | } 206 | 207 | MessageBox menuGetCurrentMsgBox() { 208 | return currMsgBox; 209 | } 210 | 211 | void menuMsgBoxSetNetloaderState(bool enabled, const char *text, bool enable_progress, float progress) { 212 | msgboxNetloaderEnabled = enabled; 213 | 214 | memset(msgboxNetloaderText, 0, sizeof(msgboxNetloaderText)); 215 | if (text) strncpy(msgboxNetloaderText, text, sizeof(msgboxNetloaderText)-1); 216 | 217 | msgboxNetloaderProgressEnabled = enable_progress; 218 | msgboxNetloaderProgress = progress; 219 | } 220 | -------------------------------------------------------------------------------- /common/message-box.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct 4 | { 5 | uint32_t width; 6 | uint32_t height; 7 | color_t *bg; 8 | char *text; 9 | } MessageBox; 10 | 11 | void menuCreateMsgBox(int width, int height, const char *text); 12 | void menuCloseMsgBox(); 13 | bool menuIsMsgBoxOpen(); 14 | void menuDrawMsgBox(void); 15 | MessageBox menuGetCurrentMsgBox(); 16 | void menuMsgBoxSetNetloaderState(bool enabled, const char *text, bool enable_progress, float progress); 17 | -------------------------------------------------------------------------------- /common/netloader.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifndef __WIN32__ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #else 15 | 16 | #define WIN32_LEAN_AND_MEAN 17 | #include 18 | #include 19 | typedef int socklen_t; 20 | typedef uint32_t in_addr_t; 21 | 22 | #undef DrawText 23 | 24 | #endif 25 | 26 | 27 | #include "netloader.h" 28 | 29 | #define PING_ENABLED 1 30 | 31 | #ifndef __SWITCH__ 32 | #include "switch/runtime/nxlink.h" 33 | #endif 34 | 35 | #define ZLIB_CHUNK (16 * 1024) 36 | #define FILE_BUFFER_SIZE (128*1024) 37 | 38 | static int netloader_listenfd = -1; 39 | static int netloader_datafd = -1; 40 | #if PING_ENABLED 41 | static int netloader_udpfd = -1; 42 | #endif 43 | static unsigned char in[ZLIB_CHUNK]; 44 | static unsigned char out[ZLIB_CHUNK]; 45 | 46 | static mtx_t netloader_mtx; 47 | 48 | static menuEntry_s netloader_me; 49 | static volatile bool netloader_initialized = 0; 50 | static volatile bool netloader_exitflag = 0; 51 | static volatile bool netloader_activated = 0, netloader_launchapp = 0; 52 | static volatile size_t netloader_filelen, netloader_filetotal; 53 | static volatile char netloader_errortext[1024]; 54 | 55 | static bool netloaderGetExit(void); 56 | 57 | //--------------------------------------------------------------------------------- 58 | static void netloader_error(const char *func, int err) { 59 | //--------------------------------------------------------------------------------- 60 | if (!netloader_initialized || netloaderGetExit()) return; 61 | 62 | mtx_lock(&netloader_mtx); 63 | if (netloader_errortext[0] == 0) { 64 | memset((char*)netloader_errortext, 0, sizeof(netloader_errortext)); 65 | snprintf((char*)netloader_errortext, sizeof(netloader_errortext)-1, "%s: err=%d\n %s\n", func, err, strerror(errno)); 66 | } 67 | mtx_unlock(&netloader_mtx); 68 | } 69 | 70 | //--------------------------------------------------------------------------------- 71 | static void netloader_socket_error(const char *func) { 72 | //--------------------------------------------------------------------------------- 73 | int errcode; 74 | #ifdef __WIN32__ 75 | errcode = WSAGetLastError(); 76 | #else 77 | errcode = errno; 78 | #endif 79 | netloader_error(func,errcode); 80 | } 81 | 82 | //--------------------------------------------------------------------------------- 83 | void shutdownSocket(int socket) { 84 | //--------------------------------------------------------------------------------- 85 | #ifdef __WIN32__ 86 | shutdown (socket, SD_SEND); 87 | closesocket (socket); 88 | #else 89 | close(socket); 90 | #endif 91 | } 92 | 93 | static const char DIRECTORY_THIS[] = "."; 94 | static const char DIRECTORY_PARENT[] = ".."; 95 | 96 | //--------------------------------------------------------------------------------- 97 | static bool isDirectorySeparator(int c) { 98 | //--------------------------------------------------------------------------------- 99 | return c == DIRECTORY_SEPARATOR_CHAR; 100 | } 101 | 102 | //--------------------------------------------------------------------------------- 103 | static void sanitisePath(char *path) { 104 | //--------------------------------------------------------------------------------- 105 | char *tmpPath = strdup(path); 106 | tmpPath[0] = 0; 107 | 108 | char *dirStart = path; 109 | char *curPath = tmpPath; 110 | 111 | #ifdef _WIN32 112 | while(dirStart[0]) { 113 | if (dirStart[0] == '/') dirStart[0] =DIRECTORY_SEPARATOR_CHAR; 114 | dirStart++; 115 | } 116 | #endif 117 | 118 | dirStart = path; 119 | 120 | while(isDirectorySeparator(dirStart[0])) dirStart++; 121 | 122 | 123 | do { 124 | char *dirEnd = strchr(dirStart, DIRECTORY_SEPARATOR_CHAR); 125 | if (dirEnd) { 126 | dirEnd++; 127 | if(!strncmp(DIRECTORY_PARENT,dirStart,strlen(DIRECTORY_PARENT))) { 128 | /* move back one directory */ 129 | size_t pathlen = strlen(tmpPath); 130 | if(tmpPath[pathlen-1] == DIRECTORY_SEPARATOR_CHAR) tmpPath[pathlen-1] = 0; 131 | char *prev = strrchr(tmpPath,DIRECTORY_SEPARATOR_CHAR); 132 | if (prev) { 133 | curPath = prev + 1; 134 | } else { 135 | curPath = tmpPath; 136 | } 137 | 138 | 139 | dirStart = dirEnd; 140 | } else if (!strncmp(DIRECTORY_THIS,dirStart,strlen(DIRECTORY_THIS))) { 141 | /* strip this entry */ 142 | dirStart = dirEnd; 143 | } else { 144 | size_t dirSize = dirEnd - dirStart; 145 | strncpy(curPath,dirStart,dirSize); 146 | curPath[dirSize] = 0; 147 | curPath += dirSize; 148 | dirStart += dirSize; 149 | } 150 | } else { 151 | strcpy(curPath,dirStart); 152 | dirStart += strlen(dirStart); 153 | } 154 | } while(dirStart[0]); 155 | 156 | strcpy(path, tmpPath); 157 | free(tmpPath); 158 | } 159 | 160 | //--------------------------------------------------------------------------------- 161 | static int set_socket_nonblocking(int sock) { 162 | //--------------------------------------------------------------------------------- 163 | 164 | #ifndef __WIN32__ 165 | int flags = fcntl(sock, F_GETFL); 166 | 167 | if(flags == -1) return -1; 168 | 169 | int rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK); 170 | 171 | if(rc != 0) return -1; 172 | 173 | #else 174 | u_long opt = 1; 175 | ioctlsocket(sock, FIONBIO, &opt); 176 | #endif 177 | 178 | 179 | return 0; 180 | } 181 | 182 | 183 | //--------------------------------------------------------------------------------- 184 | static int recvall(int sock, void *buffer, int size, int flags) { 185 | //--------------------------------------------------------------------------------- 186 | int len, sizeleft = size; 187 | bool blockflag=0; 188 | 189 | while (sizeleft) { 190 | 191 | len = recv(sock,buffer,sizeleft,flags); 192 | 193 | if (len == 0) { 194 | size = 0; 195 | break; 196 | }; 197 | 198 | if (len != -1) { 199 | sizeleft -=len; 200 | buffer +=len; 201 | } else { 202 | #ifdef _WIN32 203 | int errcode = WSAGetLastError(); 204 | if (errcode != WSAEWOULDBLOCK) { 205 | netloader_error("win socket error",errcode); 206 | break; 207 | } 208 | else { 209 | blockflag = 1; 210 | } 211 | #else 212 | if ( errno != EWOULDBLOCK && errno != EAGAIN) { 213 | netloader_socket_error("recv"); 214 | break; 215 | } 216 | else { 217 | blockflag = 1; 218 | } 219 | #endif 220 | 221 | if (blockflag && netloaderGetExit()) return 0; 222 | } 223 | } 224 | return size; 225 | } 226 | 227 | //--------------------------------------------------------------------------------- 228 | static int sendall(int sock, void *buffer, int size, int flags) { 229 | //--------------------------------------------------------------------------------- 230 | int len, sizeleft = size; 231 | bool blockflag=0; 232 | 233 | while (sizeleft) { 234 | 235 | len = send(sock,buffer,sizeleft,flags); 236 | 237 | if (len == 0) { 238 | size = 0; 239 | break; 240 | }; 241 | 242 | if (len != -1) { 243 | sizeleft -=len; 244 | buffer +=len; 245 | } else { 246 | #ifdef _WIN32 247 | int errcode = WSAGetLastError(); 248 | if (errcode != WSAEWOULDBLOCK) { 249 | netloader_error("win socket error",errcode); 250 | break; 251 | } 252 | else { 253 | blockflag = 1; 254 | } 255 | #else 256 | if ( errno != EWOULDBLOCK && errno != EAGAIN) { 257 | netloader_socket_error("recv"); 258 | break; 259 | } 260 | else { 261 | blockflag = 1; 262 | } 263 | #endif 264 | 265 | if (blockflag && netloaderGetExit()) return 0; 266 | } 267 | } 268 | return size; 269 | } 270 | 271 | //--------------------------------------------------------------------------------- 272 | static int decompress(int sock, FILE *fh, size_t filesize) { 273 | //--------------------------------------------------------------------------------- 274 | int ret; 275 | unsigned have; 276 | z_stream strm; 277 | uint32_t chunksize=0; 278 | 279 | /* allocate inflate state */ 280 | strm.zalloc = Z_NULL; 281 | strm.zfree = Z_NULL; 282 | strm.opaque = Z_NULL; 283 | strm.avail_in = 0; 284 | strm.next_in = Z_NULL; 285 | ret = inflateInit(&strm); 286 | if (ret != Z_OK) { 287 | netloader_error("inflateInit failed.",ret); 288 | return ret; 289 | } 290 | 291 | size_t total = 0; 292 | /* decompress until deflate stream ends or end of file */ 293 | do { 294 | if (netloaderGetExit()) { 295 | ret = Z_DATA_ERROR; 296 | break; 297 | } 298 | 299 | int len = recvall(sock, &chunksize, 4, 0); 300 | 301 | if (len != 4) { 302 | (void)inflateEnd(&strm); 303 | netloader_error("Error getting chunk size",len); 304 | return Z_DATA_ERROR; 305 | } 306 | 307 | if (chunksize > sizeof(in)) { 308 | (void)inflateEnd(&strm); 309 | netloader_error("Invalid chunk size",chunksize); 310 | return Z_DATA_ERROR; 311 | } 312 | 313 | strm.avail_in = recvall(sock,in,chunksize,0); 314 | 315 | if (strm.avail_in == 0) { 316 | (void)inflateEnd(&strm); 317 | netloader_error("remote closed socket.",0); 318 | return Z_DATA_ERROR; 319 | } 320 | 321 | strm.next_in = in; 322 | 323 | /* run inflate() on input until output buffer not full */ 324 | do { 325 | strm.avail_out = ZLIB_CHUNK; 326 | strm.next_out = out; 327 | ret = inflate(&strm, Z_NO_FLUSH); 328 | 329 | switch (ret) { 330 | 331 | case Z_NEED_DICT: 332 | ret = Z_DATA_ERROR; /* and fall through */ 333 | 334 | case Z_DATA_ERROR: 335 | case Z_MEM_ERROR: 336 | case Z_STREAM_ERROR: 337 | (void)inflateEnd(&strm); 338 | netloader_error("inflate error",ret); 339 | return ret; 340 | } 341 | 342 | have = ZLIB_CHUNK - strm.avail_out; 343 | 344 | if (fwrite(out, 1, have, fh) != have || ferror(fh)) { 345 | (void)inflateEnd(&strm); 346 | netloader_error("file write error",0); 347 | return Z_ERRNO; 348 | } 349 | 350 | total += have; 351 | mtx_lock(&netloader_mtx); 352 | netloader_filetotal = total; 353 | mtx_unlock(&netloader_mtx); 354 | //printf("%zu (%zd%%)",total, (100 * total) / filesize); 355 | } while (strm.avail_out == 0); 356 | 357 | /* done when inflate() says it's done */ 358 | } while (ret != Z_STREAM_END); 359 | 360 | /* clean up and return */ 361 | (void)inflateEnd(&strm); 362 | return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; 363 | } 364 | 365 | 366 | 367 | //--------------------------------------------------------------------------------- 368 | int loadnro(menuEntry_s *me, int sock, struct in_addr remote) { 369 | //--------------------------------------------------------------------------------- 370 | int len, namelen, filelen; 371 | char filepath[PATH_MAX+1]; 372 | len = recvall(sock, &namelen, 4, 0); 373 | 374 | if (len != 4) { 375 | netloader_error("Error getting name length", errno); 376 | return -1; 377 | } 378 | 379 | if (namelen >= sizeof(filepath)-1) { 380 | netloader_error("File-path length is too large",errno); 381 | return -1; 382 | } 383 | 384 | len = recvall(sock, filepath, namelen, 0); 385 | 386 | if (len != namelen) { 387 | netloader_error("Error getting file-path", errno); 388 | return -1; 389 | } 390 | 391 | filepath[namelen] = 0; 392 | 393 | len = recvall(sock, &filelen, 4, 0); 394 | 395 | if (len != 4) { 396 | netloader_error("Error getting file length",errno); 397 | return -1; 398 | } 399 | 400 | mtx_lock(&netloader_mtx); 401 | netloader_filelen = filelen; 402 | mtx_unlock(&netloader_mtx); 403 | 404 | int response = 0; 405 | 406 | sanitisePath(filepath); 407 | 408 | snprintf(me->path, sizeof(me->path)-1, "%s%s%s", menuGetRootPath(), DIRECTORY_SEPARATOR, filepath); 409 | // make sure it's terminated 410 | me->path[sizeof(me->path)-1] = 0; 411 | strncpy(filepath, me->path, sizeof(filepath)-1); // menuEntryLoad() below will overwrite me->path, so copy me->path to filepath and use that instead. 412 | filepath[sizeof(filepath)-1] = 0; 413 | 414 | argData_s* ad = &me->args; 415 | ad->dst = (char*)&ad->buf[1]; 416 | ad->nxlink_host = remote; 417 | 418 | const char* ext = getExtension(me->path); 419 | if (ext && strcasecmp(ext, ".nro")==0) 420 | launchAddArg(ad, me->path); 421 | else { 422 | me->type = ENTRY_TYPE_FILE_OTHER; // Handle fileassoc when extension isn't .nro. 423 | if (!menuEntryLoad(me, "", false, false)) { 424 | response = -3; 425 | errno = EINVAL; 426 | netloader_error("File-extension/filename not recognized",0); 427 | } 428 | menuEntryFree(me, false); // We don't need any of the buffers which may have been allocated. 429 | } 430 | 431 | #ifndef _WIN32 432 | if (response == 0) { 433 | int fd = open(filepath,O_CREAT|O_WRONLY, ACCESSPERMS); 434 | 435 | if (fd < 0) { 436 | response = -1; 437 | netloader_error("open", errno); 438 | } else { 439 | if (ftruncate(fd,filelen) == -1) { 440 | response = -2; 441 | netloader_error("ftruncate",errno); 442 | } 443 | close(fd); 444 | } 445 | } 446 | #endif 447 | 448 | FILE *file = NULL; 449 | 450 | if (response == 0) { 451 | file = fopen(filepath,"wb"); 452 | if(file == NULL) { 453 | perror("file"); 454 | response = -1; 455 | } 456 | } 457 | 458 | send(sock,(char *)&response,sizeof(response),0); 459 | 460 | char *writebuffer = NULL; 461 | if (response == 0 ) { 462 | writebuffer = malloc(FILE_BUFFER_SIZE); 463 | if (writebuffer==NULL) { 464 | netloader_error("Failed to allocate memory",ENOMEM); 465 | response = -1; 466 | } 467 | else { 468 | memset(writebuffer, 0, FILE_BUFFER_SIZE); 469 | setvbuf(file,writebuffer,_IOFBF, FILE_BUFFER_SIZE); 470 | } 471 | } 472 | 473 | if (response == 0 ) { 474 | //printf("transferring %s\n%d bytes.\n", filepath, filelen); 475 | 476 | if (decompress(sock,file,filelen)==Z_OK) { 477 | int netloaded_cmdlen = 0; 478 | len = sendall(sock,(char *)&response,sizeof(response),0); 479 | 480 | if (len != sizeof(response)) { 481 | netloader_error("Error sending response",errno); 482 | response = -1; 483 | } 484 | 485 | //printf("\ntransferring command line\n"); 486 | 487 | if (response == 0 ) { 488 | len = recvall(sock,(char*)&netloaded_cmdlen,4,0); 489 | 490 | if (len != 4) { 491 | netloader_error("Error getting netloaded_cmdlen",errno); 492 | response = -1; 493 | } 494 | } 495 | 496 | if (response == 0 ) { 497 | 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; 498 | 499 | len = recvall(sock,me->args.dst, netloaded_cmdlen,0); 500 | 501 | if (len != netloaded_cmdlen) { 502 | netloader_error("Error getting args",errno); 503 | response = -1; 504 | } 505 | } 506 | 507 | if (response == 0 ) { 508 | while(netloaded_cmdlen) { 509 | size_t len = strlen(me->args.dst) + 1; 510 | ad->dst += len; 511 | ad->buf[0]++; 512 | netloaded_cmdlen -= len; 513 | } 514 | } 515 | 516 | } else { 517 | response = -1; 518 | } 519 | } 520 | 521 | if (file) { 522 | fflush(file); 523 | fclose(file); 524 | } 525 | if (response == -1) unlink(filepath); 526 | free(writebuffer); 527 | 528 | return response; 529 | } 530 | 531 | //--------------------------------------------------------------------------------- 532 | int netloader_activate(void) { 533 | //--------------------------------------------------------------------------------- 534 | struct sockaddr_in serv_addr; 535 | 536 | memset(&serv_addr, 0, sizeof(serv_addr)); 537 | serv_addr.sin_family = AF_INET; 538 | serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 539 | serv_addr.sin_port = htons(NXLINK_SERVER_PORT); 540 | 541 | #if PING_ENABLED 542 | // create udp socket for broadcast ping 543 | netloader_udpfd = socket(AF_INET, SOCK_DGRAM, 0); 544 | if (netloader_udpfd < 0) 545 | { 546 | netloader_socket_error("udp socket"); 547 | return -1; 548 | } 549 | 550 | if(bind(netloader_udpfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) { 551 | netloader_socket_error("bind udp socket"); 552 | return -1; 553 | } 554 | 555 | if (set_socket_nonblocking(netloader_udpfd) == -1) 556 | { 557 | netloader_socket_error("listen fcntl"); 558 | return -1; 559 | } 560 | #endif 561 | // create listening socket on all addresses on NXLINK_SERVER_PORT 562 | 563 | netloader_listenfd = socket(AF_INET, SOCK_STREAM, 0); 564 | if(netloader_listenfd < 0) 565 | { 566 | netloader_socket_error("socket"); 567 | return -1; 568 | } 569 | 570 | uint32_t tmpval=1; 571 | int rc = setsockopt(netloader_listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&tmpval, sizeof(tmpval)); 572 | if(rc != 0) 573 | { 574 | netloader_socket_error("setsockopt"); 575 | return -1; 576 | } 577 | 578 | rc = bind(netloader_listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 579 | if(rc != 0) 580 | { 581 | netloader_socket_error("bind"); 582 | return -1; 583 | } 584 | 585 | if (set_socket_nonblocking(netloader_listenfd) == -1) 586 | { 587 | netloader_socket_error("listen fcntl"); 588 | return -1; 589 | } 590 | 591 | rc = listen(netloader_listenfd, 10); 592 | if(rc != 0) 593 | { 594 | netloader_socket_error("listen"); 595 | return -1; 596 | } 597 | 598 | return 0; 599 | } 600 | 601 | 602 | //--------------------------------------------------------------------------------- 603 | int netloader_deactivate(void) { 604 | //--------------------------------------------------------------------------------- 605 | // close all remaining sockets and allow mainloop to return to main menu 606 | if(netloader_listenfd >= 0) 607 | { 608 | shutdownSocket(netloader_listenfd); 609 | netloader_listenfd = -1; 610 | } 611 | 612 | if(netloader_datafd >= 0) 613 | { 614 | shutdownSocket(netloader_datafd); 615 | netloader_datafd = -1; 616 | } 617 | 618 | #if PING_ENABLED 619 | if(netloader_udpfd >= 0) 620 | { 621 | shutdownSocket(netloader_udpfd); 622 | netloader_udpfd = -1; 623 | } 624 | #endif 625 | 626 | return 0; 627 | } 628 | 629 | //--------------------------------------------------------------------------------- 630 | int netloader_loop(struct sockaddr_in *sa_remote) { 631 | //--------------------------------------------------------------------------------- 632 | 633 | #if PING_ENABLED 634 | char recvbuf[256]; 635 | socklen_t fromlen = sizeof(struct sockaddr_in); 636 | 637 | int len = recvfrom(netloader_udpfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) sa_remote, &fromlen); 638 | 639 | if (len!=-1) { 640 | if (strncmp(recvbuf,"nxboot",strlen("nxboot")) == 0) { 641 | sa_remote->sin_family=AF_INET; 642 | sa_remote->sin_port=htons(NXLINK_CLIENT_PORT); 643 | sendto(netloader_udpfd, "bootnx", strlen("bootnx"), 0, (struct sockaddr*) sa_remote,sizeof(struct sockaddr_in)); 644 | } 645 | } 646 | #endif 647 | if(netloader_listenfd >= 0 && netloader_datafd < 0) { 648 | socklen_t addrlen = sizeof(struct sockaddr_in); 649 | netloader_datafd = accept(netloader_listenfd, (struct sockaddr*)sa_remote, &addrlen); 650 | if(netloader_datafd < 0) 651 | { 652 | 653 | #ifdef _WIN32 654 | int errcode = WSAGetLastError(); 655 | if (errcode != WSAEWOULDBLOCK) { 656 | netloader_error("accept", errcode); 657 | return -1; 658 | } 659 | #else 660 | if ( errno != EWOULDBLOCK && errno != EAGAIN) { 661 | netloader_error("accept", errno); 662 | return -1; 663 | } 664 | #endif 665 | 666 | } 667 | else 668 | { 669 | if (set_socket_nonblocking(netloader_datafd) == -1) 670 | { 671 | netloader_socket_error("set_socket_nonblocking(netloader_datafd)"); 672 | return -1; 673 | } 674 | 675 | close(netloader_listenfd); 676 | netloader_listenfd = -1; 677 | return 1; 678 | } 679 | } 680 | 681 | return 0; 682 | } 683 | 684 | void netloaderGetState(netloaderState *state) { 685 | if(state==NULL)return; 686 | mtx_lock(&netloader_mtx); 687 | 688 | state->activated = netloader_activated; 689 | state->launch_app = netloader_launchapp; 690 | state->me = &netloader_me; 691 | 692 | state->transferring = (netloader_datafd >= 0 && netloader_filelen); 693 | state->sock_connected = netloader_datafd >= 0; 694 | state->filelen = netloader_filelen; 695 | state->filetotal = netloader_filetotal; 696 | 697 | memset(state->errormsg, 0, sizeof(state->errormsg)); 698 | if(netloader_errortext[0]) { 699 | strncpy(state->errormsg, (char*)netloader_errortext, sizeof(state->errormsg)-1); 700 | memset((char*)netloader_errortext, 0, sizeof(netloader_errortext)); 701 | } 702 | 703 | mtx_unlock(&netloader_mtx); 704 | } 705 | 706 | static bool netloaderGetExit(void) { 707 | bool flag; 708 | mtx_lock(&netloader_mtx); 709 | flag = netloader_exitflag; 710 | mtx_unlock(&netloader_mtx); 711 | return flag; 712 | } 713 | 714 | void netloaderSignalExit(void) { 715 | if (!netloader_initialized) return; 716 | 717 | mtx_lock(&netloader_mtx); 718 | netloader_exitflag = 1; 719 | mtx_unlock(&netloader_mtx); 720 | } 721 | 722 | Result netloaderInit(void) { 723 | Result rc=0; 724 | if (netloader_initialized) return 0; 725 | 726 | if (mtx_init(&netloader_mtx, mtx_plain) != thrd_success) return 1; 727 | 728 | #ifdef __SWITCH__ 729 | rc = socketInitializeDefault(); 730 | if (R_SUCCEEDED(rc)) { 731 | rc = nifmInitialize(NifmServiceType_User); 732 | if (R_FAILED(rc)) socketExit(); 733 | } 734 | #endif 735 | 736 | #ifdef __WIN32__ 737 | WSADATA wsa_data; 738 | if (WSAStartup (MAKEWORD(2,2), &wsa_data)) { 739 | //netloader_error("WSAStartup failed\n",1); 740 | rc = 2; 741 | } 742 | #endif 743 | 744 | if (rc) { 745 | mtx_destroy(&netloader_mtx); 746 | return rc; 747 | } 748 | 749 | netloader_initialized = 1; 750 | return 0; 751 | } 752 | 753 | void netloaderExit(void) { 754 | if (!netloader_initialized) return; 755 | netloader_initialized = 0; 756 | 757 | mtx_destroy(&netloader_mtx); 758 | 759 | #ifdef __SWITCH__ 760 | nifmExit(); 761 | socketExit(); 762 | #endif 763 | 764 | #ifdef __WIN32__ 765 | WSACleanup (); 766 | #endif 767 | } 768 | 769 | void netloaderTask(void* arg) { 770 | int ret=0; 771 | struct sockaddr_in sa_remote; 772 | struct timespec duration = {.tv_nsec = 100000000}; 773 | menuEntryInit(&netloader_me,ENTRY_TYPE_FILE); 774 | 775 | mtx_lock(&netloader_mtx); 776 | netloader_exitflag = 0; 777 | netloader_activated = 0; 778 | netloader_launchapp = 0; 779 | netloader_filelen = 0; 780 | netloader_filetotal = 0; 781 | mtx_unlock(&netloader_mtx); 782 | 783 | if(netloader_activate() == 0) { 784 | mtx_lock(&netloader_mtx); 785 | netloader_activated = 1; 786 | mtx_unlock(&netloader_mtx); 787 | } 788 | else { 789 | netloader_deactivate(); 790 | return; 791 | } 792 | 793 | while((ret = netloader_loop(&sa_remote)) == 0 && !netloaderGetExit()) { 794 | thrd_sleep(&duration, NULL); 795 | } 796 | 797 | if(ret == 1 && !netloaderGetExit()) { 798 | int result = loadnro(&netloader_me, netloader_datafd,sa_remote.sin_addr); 799 | if (result== 0) { 800 | ret = 1; 801 | } else { 802 | ret = -1; 803 | } 804 | } 805 | 806 | netloader_deactivate(); 807 | mtx_lock(&netloader_mtx); 808 | if (ret==1 && !netloader_exitflag) netloader_launchapp = 1;//Access netloader_exitflag directly since the mutex is already locked. 809 | netloader_exitflag = 0; 810 | netloader_activated = 0; 811 | mtx_unlock(&netloader_mtx); 812 | } 813 | 814 | -------------------------------------------------------------------------------- /common/netloader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct { 4 | bool activated; 5 | bool launch_app; 6 | bool transferring; 7 | bool sock_connected; 8 | menuEntry_s *me; 9 | size_t filelen, filetotal; 10 | char errormsg[1025]; 11 | } netloaderState; 12 | 13 | int netloader_activate(void); 14 | int netloader_deactivate(void); 15 | int netloader_loop(struct sockaddr_in *sa_remote); 16 | 17 | Result netloaderInit(void); 18 | void netloaderExit(void); 19 | 20 | void netloaderTask(void* arg); 21 | 22 | void netloaderGetState(netloaderState *state); 23 | void netloaderSignalExit(void); 24 | -------------------------------------------------------------------------------- /common/netstatus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | bool netstatusGetDetails(AssetId *id); 5 | 6 | -------------------------------------------------------------------------------- /common/power.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | void powerInit(void); 5 | 6 | bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging); 7 | 8 | void powerExit(void); -------------------------------------------------------------------------------- /common/status.c: -------------------------------------------------------------------------------- 1 | #include "status.h" 2 | 3 | static bool s_statusInitialized = 0; 4 | static thrd_t s_statusThread; 5 | static cnd_t s_statusCdn; 6 | static mtx_t s_statusMtx; 7 | static mtx_t s_statusAccessMtx; 8 | static bool s_statusExit; 9 | 10 | static bool s_statusReady; 11 | static bool s_statusNetFlag; 12 | static AssetId s_statusNetAssetId; 13 | static bool s_statusTemperatureFlag; 14 | static s32 s_statusTemperature; 15 | 16 | // This uses netstatusGetDetails from a dedicated thread, since nifmGetInternetConnectionStatus can block for a few seconds. 17 | 18 | static int statusThreadProc(void* unused) 19 | { 20 | mtx_lock(&s_statusMtx); 21 | 22 | struct timespec timeout = {0}; 23 | bool tmpflag=0; 24 | bool thermalflag=0; 25 | bool thermal_initialized=0; 26 | AssetId tmpid; 27 | s32 temperature; 28 | 29 | thermal_initialized = thermalstatusInit(); 30 | 31 | clock_gettime(CLOCK_MONOTONIC, &timeout); 32 | timeout.tv_sec++; 33 | 34 | for (;;) 35 | { 36 | cnd_timedwait(&s_statusCdn, &s_statusMtx, &timeout); 37 | 38 | if (s_statusExit) 39 | break; 40 | 41 | tmpflag = netstatusGetDetails(&tmpid); 42 | if (thermal_initialized) thermalflag = thermalstatusGetDetails(&temperature); 43 | 44 | mtx_lock(&s_statusAccessMtx); 45 | 46 | s_statusNetFlag = tmpflag; 47 | s_statusNetAssetId = tmpid; 48 | 49 | s_statusTemperatureFlag = thermalflag; 50 | s_statusTemperature = temperature; 51 | 52 | s_statusReady = 1; 53 | 54 | mtx_unlock(&s_statusAccessMtx); 55 | 56 | clock_gettime(CLOCK_MONOTONIC, &timeout); 57 | timeout.tv_sec++; 58 | } 59 | 60 | mtx_unlock(&s_statusMtx); 61 | 62 | if (thermal_initialized) thermalstatusExit(); 63 | 64 | return 0; 65 | } 66 | 67 | bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature) { 68 | if (!s_statusReady) return 0; 69 | 70 | mtx_lock(&s_statusAccessMtx); 71 | 72 | *netstatusFlag = s_statusNetFlag; 73 | *netstatusAssetId = s_statusNetAssetId; 74 | 75 | *temperatureFlag = s_statusTemperatureFlag; 76 | *temperature = s_statusTemperature; 77 | 78 | mtx_unlock(&s_statusAccessMtx); 79 | 80 | return 1; 81 | } 82 | 83 | bool statusInit(void) 84 | { 85 | if (s_statusInitialized) return 1; 86 | 87 | if (cnd_init(&s_statusCdn) != thrd_success) return 0; 88 | if (mtx_init(&s_statusMtx, mtx_plain) != thrd_success) { 89 | cnd_destroy(&s_statusCdn); 90 | return 0; 91 | } 92 | 93 | if (mtx_init(&s_statusAccessMtx, mtx_plain) != thrd_success) { 94 | mtx_destroy(&s_statusMtx); 95 | cnd_destroy(&s_statusCdn); 96 | return 0; 97 | } 98 | 99 | if (thrd_create(&s_statusThread, statusThreadProc, 0) != thrd_success) { 100 | mtx_destroy(&s_statusAccessMtx); 101 | mtx_destroy(&s_statusMtx); 102 | cnd_destroy(&s_statusCdn); 103 | return 0; 104 | } 105 | 106 | s_statusInitialized = 1; 107 | return 1; 108 | } 109 | 110 | void statusExit(void) 111 | { 112 | int res=0; 113 | 114 | if (!s_statusInitialized) return; 115 | s_statusInitialized = 0; 116 | 117 | mtx_lock(&s_statusMtx); 118 | s_statusExit = true; 119 | cnd_signal(&s_statusCdn); 120 | mtx_unlock(&s_statusMtx); 121 | 122 | thrd_join(s_statusThread, &res); 123 | 124 | s_statusReady = 0; 125 | 126 | mtx_destroy(&s_statusAccessMtx); 127 | mtx_destroy(&s_statusMtx); 128 | cnd_destroy(&s_statusCdn); 129 | } 130 | 131 | -------------------------------------------------------------------------------- /common/status.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | bool statusInit(void); 5 | void statusExit(void); 6 | bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature); 7 | 8 | -------------------------------------------------------------------------------- /common/text.c: -------------------------------------------------------------------------------- 1 | #include "text.h" 2 | 3 | static u64 s_textLanguageCode = 0; 4 | 5 | #ifdef __SWITCH__ 6 | static int s_textLang = SetLanguage_ENUS; 7 | #else 8 | static int s_textLang = 1; 9 | #endif 10 | 11 | Result textInit(void) { 12 | #ifdef __SWITCH__ 13 | SetLanguage Language=SetLanguage_ENUS; 14 | 15 | s_textLang = Language; 16 | 17 | Result rc = setInitialize(); 18 | if (R_SUCCEEDED(rc)) rc = setGetSystemLanguage(&s_textLanguageCode); 19 | if (R_SUCCEEDED(rc)) rc = setMakeLanguage(s_textLanguageCode, &Language); 20 | //if (R_SUCCEEDED(rc) && Language < 17) s_textLang = Language;//TODO: Re-enable this once language.c supports all used languages. 21 | setExit(); 22 | if (R_FAILED(rc)) return rc; 23 | #else 24 | s_textLang = 1; 25 | #endif 26 | 27 | return 0; 28 | } 29 | 30 | int textGetLang(void) { 31 | return s_textLang; 32 | } 33 | 34 | const char* textGetString(StrId id) { 35 | const char* str = g_strings[id][s_textLang]; 36 | #ifdef __SWITCH__ 37 | if (!str) str = g_strings[id][SetLanguage_ENUS]; 38 | #else 39 | if (!str) str = g_strings[id][1]; 40 | #endif 41 | return str; 42 | } 43 | 44 | u64 textGetLanguageCode(void) { 45 | return s_textLanguageCode; 46 | } 47 | -------------------------------------------------------------------------------- /common/text.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | #include "language.h" 4 | 5 | Result textInit(void); 6 | int textGetLang(void); 7 | const char* textGetString(StrId id); 8 | u64 textGetLanguageCode(void); 9 | -------------------------------------------------------------------------------- /common/theme.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include 5 | 6 | typedef enum 7 | { 8 | THEME_PRESET_LIGHT, 9 | THEME_PRESET_DARK, 10 | } ThemePreset; 11 | 12 | typedef struct 13 | { 14 | bool visible; 15 | bool posType; // false = absolute, true = relative 16 | int posStart[2]; 17 | int posEnd[2]; 18 | int size[2]; // width/height 19 | int imageSize[2]; // width/height for the actual image data 20 | int touchSize[2]; 21 | int posFinal[2]; 22 | uint32_t textSize[2]; 23 | u32 font; 24 | } ThemeLayoutObject; 25 | 26 | typedef struct 27 | { 28 | color_t textColor; 29 | color_t logoColor; 30 | color_t attentionTextColor; 31 | color_t frontWaveColor; 32 | color_t middleWaveColor; 33 | color_t backWaveColor; 34 | color_t backgroundColor; 35 | color_t highlightColor; 36 | color_t highlightGradientEdgeColor; 37 | color_t separatorColor; 38 | color_t borderColor; 39 | color_t borderTextColor; 40 | color_t progressBarColor; 41 | bool logoColor_set; 42 | bool enableWaveBlending; 43 | char buttonAText[32]; 44 | char buttonBText[32]; 45 | char buttonXText[32]; 46 | char buttonYText[32]; 47 | char buttonPText[32]; 48 | char buttonMText[32]; 49 | char labelStarOnText[32]; 50 | char labelStarOffText[32]; 51 | 52 | ThemeLayoutObject layoutObjects[ThemeLayoutId_Total]; 53 | } theme_t; 54 | 55 | bool colorFromSetting(config_setting_t *rgba, color_t *col); 56 | void themeStartup(ThemePreset preset); 57 | void GetThemePathFromConfig(char* themePath, size_t size); 58 | void SetThemePathToConfig(const char* themePath); 59 | 60 | extern theme_t themeCurrent; 61 | 62 | extern ThemePreset themeGlobalPreset; 63 | -------------------------------------------------------------------------------- /common/thermalstatus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | bool thermalstatusInit(void); 5 | void thermalstatusExit(void); 6 | bool thermalstatusGetDetails(s32 *temperature); 7 | 8 | -------------------------------------------------------------------------------- /common/ui.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | static bool s_shouldExit; 4 | 5 | void uiExitLoop(void) { 6 | s_shouldExit = true; 7 | } 8 | 9 | bool menuUpdate(void); 10 | 11 | bool uiUpdate(void) { 12 | bool exitflag=0; 13 | 14 | exitflag = !menuUpdate(); 15 | if (!exitflag) return exitflag; 16 | 17 | return !s_shouldExit; 18 | } 19 | -------------------------------------------------------------------------------- /common/ui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | void uiExitLoop(void); 5 | 6 | bool uiUpdate(void); 7 | -------------------------------------------------------------------------------- /common/worker.c: -------------------------------------------------------------------------------- 1 | #include "worker.h" 2 | 3 | static bool s_workerInitialized = 0; 4 | static thrd_t s_workerThread; 5 | static cnd_t s_workerCdn; 6 | static mtx_t s_workerMtx; 7 | 8 | static volatile struct 9 | { 10 | workerThreadFunc func; 11 | void* data; 12 | 13 | bool exit; 14 | } s_workerParam; 15 | 16 | static int workerThreadProc(void* unused) 17 | { 18 | mtx_lock(&s_workerMtx); 19 | 20 | for (;;) 21 | { 22 | cnd_wait(&s_workerCdn, &s_workerMtx); 23 | 24 | if (s_workerParam.exit) 25 | break; 26 | 27 | s_workerParam.func(s_workerParam.data); 28 | } 29 | 30 | mtx_unlock(&s_workerMtx); 31 | 32 | return 0; 33 | } 34 | 35 | bool workerInit(void) 36 | { 37 | if (s_workerInitialized) return 1; 38 | 39 | if (cnd_init(&s_workerCdn) != thrd_success) return 0; 40 | if (mtx_init(&s_workerMtx, mtx_plain) != thrd_success) { 41 | cnd_destroy(&s_workerCdn); 42 | return 0; 43 | } 44 | 45 | if (thrd_create(&s_workerThread, workerThreadProc, 0) != thrd_success) { 46 | mtx_destroy(&s_workerMtx); 47 | cnd_destroy(&s_workerCdn); 48 | return 0; 49 | } 50 | 51 | s_workerInitialized = 1; 52 | return 1; 53 | } 54 | 55 | void workerExit(void) 56 | { 57 | int res=0; 58 | 59 | if (!s_workerInitialized) return; 60 | s_workerInitialized = 0; 61 | 62 | mtx_lock(&s_workerMtx); 63 | s_workerParam.exit = true; 64 | cnd_signal(&s_workerCdn); 65 | mtx_unlock(&s_workerMtx); 66 | 67 | thrd_join(s_workerThread, &res); 68 | mtx_destroy(&s_workerMtx); 69 | cnd_destroy(&s_workerCdn); 70 | } 71 | 72 | void workerSchedule(workerThreadFunc func, void* data) 73 | { 74 | if (!s_workerInitialized) return; 75 | 76 | mtx_lock(&s_workerMtx); 77 | s_workerParam.func = func; 78 | s_workerParam.data = data; 79 | cnd_signal(&s_workerCdn); 80 | mtx_unlock(&s_workerMtx); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /common/worker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | bool workerInit(void); 5 | void workerExit(void); 6 | void workerSchedule(workerThreadFunc func, void* data); 7 | -------------------------------------------------------------------------------- /icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/icon.jpg -------------------------------------------------------------------------------- /nx_main/loaders/builtin.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../common/common.h" 4 | 5 | static char argBuf[ENTRY_ARGBUFSIZE]; 6 | 7 | static char *init_args(char *dst, size_t dst_maxsize, u32 *in_args, size_t size) 8 | { 9 | size_t tmplen; 10 | u32 argi; 11 | char *in_argdata = (char*)&in_args[1]; 12 | 13 | size-= sizeof(u32); 14 | 15 | for (argi=0; argi dst_maxsize) break; 21 | 22 | if (dst_maxsize < 3) break; 23 | 24 | *dst++ = '"'; 25 | dst_maxsize--; 26 | 27 | strncpy(dst, in_argdata, tmplen); 28 | in_argdata+= tmplen+1; 29 | size-= tmplen+1; 30 | dst+= tmplen; 31 | dst_maxsize-= tmplen; 32 | 33 | *dst++ = '"'; 34 | dst_maxsize--; 35 | 36 | if (argi+1 < in_args[0]) { 37 | *dst++ = ' '; 38 | dst_maxsize--; 39 | } 40 | } 41 | return dst; 42 | } 43 | 44 | static bool init(void) 45 | { 46 | return envHasNextLoad(); 47 | } 48 | 49 | static void deinit(void) 50 | { 51 | 52 | } 53 | 54 | static void launchFile(const char* path, argData_s* args) 55 | { 56 | char msg[256]; 57 | /*if (strncmp(path, "sdmc:/",6) == 0) 58 | path += 5;*/ 59 | memset(argBuf, 0, sizeof(argBuf)); 60 | 61 | uint32_t remote = args->nxlink_host.s_addr; 62 | 63 | if (remote) { 64 | char nxlinked[17]; 65 | sprintf(nxlinked,"%08" PRIx32 "_NXLINK_",remote); 66 | launchAddArg(args, nxlinked); 67 | } 68 | 69 | init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf)); 70 | 71 | struct stat st; 72 | 73 | if (stat(path, &st) == -1) { 74 | memset(msg, 0, sizeof(msg)); 75 | snprintf(msg, sizeof(msg)-1, textGetString(StrId_NroNotFound), path); 76 | 77 | menuCreateMsgBox(780, 300, msg); 78 | menuScan("."); 79 | } 80 | else { 81 | Result rc = envSetNextLoad(path, argBuf); 82 | if(R_FAILED(rc)) { 83 | memset(msg, 0, sizeof(msg)); 84 | snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_AppLaunchError), R_MODULE(rc), R_DESCRIPTION(rc)); 85 | 86 | menuCreateMsgBox(780, 300, msg); 87 | } 88 | else { 89 | uiExitLoop(); 90 | } 91 | } 92 | } 93 | 94 | const loaderFuncs_s loader_builtin = 95 | { 96 | .name = "builtin", 97 | .init = init, 98 | .deinit = deinit, 99 | .launchFile = launchFile, 100 | }; 101 | 102 | -------------------------------------------------------------------------------- /nx_main/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../common/common.h" 7 | #include "nx_graphics.h" 8 | #include "nx_touch.h" 9 | 10 | // Define the desired framebuffer resolution (here we set it to 720p). 11 | #define FB_WIDTH 1280 12 | #define FB_HEIGHT 720 13 | 14 | uint8_t* g_framebuf; 15 | u32 g_framebuf_width; 16 | 17 | PadState g_pad; 18 | PadRepeater g_pad_repeater; 19 | 20 | bool menuUpdateErrorScreen(void); 21 | 22 | #ifdef PERF_LOG 23 | u64 g_tickdiff_frame=0; 24 | #endif 25 | 26 | #ifdef ENABLE_AUDIO 27 | void audio_initialize(void); 28 | void audio_exit(void); 29 | #endif 30 | 31 | extern u32 __nx_applet_exit_mode; 32 | 33 | int main(int argc, char **argv) 34 | { 35 | bool error_screen=0; 36 | Result lastret=0; 37 | Result rc=0; 38 | char msg[256]; 39 | char errormsg[256];//Can't use StrId for these error messages since it would be unavailable if textInit fails. 40 | 41 | #ifdef PERF_LOG 42 | u64 start_tick=0; 43 | #endif 44 | 45 | padConfigureInput(8, HidNpadStyleSet_NpadStandard); 46 | padInitializeAny(&g_pad); 47 | padRepeaterInitialize(&g_pad_repeater, 20, 10); 48 | hidSetNpadHandheldActivationMode(HidNpadHandheldActivationMode_Single); 49 | touchInit(); 50 | 51 | memset(errormsg, 0, sizeof(errormsg)); 52 | 53 | appletLockExit(); 54 | appletSetScreenShotPermission(AppletScreenShotPermission_Enable); 55 | 56 | ColorSetId theme = ColorSetId_Light; 57 | rc = setsysInitialize(); 58 | if (R_SUCCEEDED(rc)) { 59 | setsysGetColorSetId(&theme); 60 | setsysExit(); 61 | } 62 | 63 | if (R_SUCCEEDED(rc)) { 64 | rc = textInit(); 65 | if (R_FAILED(rc)) { 66 | snprintf(errormsg, sizeof(errormsg)-1, "Error: textInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc)); 67 | } 68 | } 69 | 70 | if (R_SUCCEEDED(rc)) menuStartupPath(); 71 | 72 | if (R_SUCCEEDED(rc)) { 73 | if (!PHYSFS_init(argv[0])) { 74 | rc = 1; 75 | snprintf(errormsg, sizeof(errormsg)-1, "Error: PHYSFS_init() failed: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 76 | } 77 | } 78 | 79 | if (R_SUCCEEDED(rc)) { 80 | rc = assetsInit(); 81 | if (R_FAILED(rc)) { 82 | snprintf(errormsg, sizeof(errormsg)-1, "Error: assetsInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc)); 83 | } 84 | } 85 | 86 | if (R_SUCCEEDED(rc)) themeStartup((ThemePreset)theme); 87 | 88 | if (R_SUCCEEDED(rc)) powerInit(); 89 | 90 | if (R_SUCCEEDED(rc)) { 91 | rc = netloaderInit(); 92 | if (R_FAILED(rc)) { 93 | snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc)); 94 | } 95 | } 96 | 97 | if (R_SUCCEEDED(rc) && !workerInit()) { 98 | rc = 1; 99 | snprintf(errormsg, sizeof(errormsg)-1, "Error: workerInit() failed."); 100 | } 101 | 102 | if (R_SUCCEEDED(rc) && !statusInit()) { 103 | rc = 1; 104 | snprintf(errormsg, sizeof(errormsg)-1, "Error: statusInit() failed."); 105 | } 106 | 107 | if (R_SUCCEEDED(rc)) menuStartup(); 108 | 109 | if (R_SUCCEEDED(rc)) { 110 | if (!launchInit()) { 111 | rc = 2; 112 | snprintf(errormsg, sizeof(errormsg)-1, "Error: launchInit() failed."); 113 | } 114 | } 115 | 116 | if (R_SUCCEEDED(rc) && !fontInitialize()) { 117 | rc = 3; 118 | snprintf(errormsg, sizeof(errormsg)-1, "Error: fontInitialize() failed."); 119 | } 120 | 121 | #ifdef ENABLE_AUDIO 122 | if (R_SUCCEEDED(rc)) audio_initialize(); 123 | #endif 124 | 125 | if (R_SUCCEEDED(rc)) { 126 | lastret = envGetLastLoadResult(); 127 | 128 | if (R_FAILED(lastret)) { 129 | memset(msg, 0, sizeof(msg)); 130 | snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_LastLoadResult), R_MODULE(lastret), R_DESCRIPTION(lastret)); 131 | 132 | menuCreateMsgBox(780, 300, msg); 133 | } 134 | } 135 | 136 | if (errormsg[0]) error_screen = 1; 137 | 138 | if (!error_screen) { 139 | graphicsInit(FB_WIDTH, FB_HEIGHT); 140 | } 141 | else { 142 | consoleInit(NULL); 143 | printf("%s\n", errormsg); 144 | printf("Press the + button to exit.\n"); 145 | } 146 | 147 | while (appletMainLoop()) 148 | { 149 | // Scan the gamepad. This should be done once for each frame 150 | padUpdate(&g_pad); 151 | padRepeaterUpdate(&g_pad_repeater, padGetButtons(&g_pad) & ( 152 | HidNpadButton_AnyLeft | HidNpadButton_AnyUp | HidNpadButton_AnyRight | HidNpadButton_AnyDown | HidNpadButton_ZL | HidNpadButton_ZR 153 | )); 154 | 155 | if (!error_screen) { 156 | if (!uiUpdate()) break; 157 | g_framebuf = graphicsFrameBegin(&g_framebuf_width); 158 | #ifdef PERF_LOG 159 | start_tick = armGetSystemTick(); 160 | #endif 161 | memset(g_framebuf, 237, g_framebuf_width * FB_HEIGHT); 162 | menuLoop(); 163 | } 164 | else { 165 | if (menuUpdateErrorScreen()) break; 166 | } 167 | 168 | if (!error_screen) { 169 | graphicsFrameEnd(); 170 | 171 | #ifdef PERF_LOG 172 | g_tickdiff_frame = armGetSystemTick() - start_tick; 173 | #endif 174 | } 175 | else { 176 | consoleUpdate(NULL); 177 | } 178 | } 179 | 180 | if (!error_screen) { 181 | graphicsExit(); 182 | } 183 | else { 184 | consoleExit(NULL); 185 | __nx_applet_exit_mode = 1; 186 | } 187 | 188 | #ifdef ENABLE_AUDIO 189 | audio_exit(); 190 | #endif 191 | 192 | fontExit(); 193 | launchExit(); 194 | netloaderSignalExit(); 195 | statusExit(); 196 | workerExit(); 197 | netloaderExit(); 198 | powerExit(); 199 | assetsExit(); 200 | PHYSFS_deinit(); 201 | 202 | appletUnlockExit(); 203 | 204 | return 0; 205 | } 206 | 207 | u64 menuGetKeysDown(void) { 208 | u64 keys = padGetButtonsDown(&g_pad); 209 | keys |= padRepeaterGetButtons(&g_pad_repeater); 210 | return keys; 211 | } 212 | 213 | //This is implemented here due to the hid code. 214 | bool menuUpdate(void) { 215 | bool exitflag = 0; 216 | menu_s* menu = menuGetCurrent(); 217 | u64 down = menuGetKeysDown(); 218 | ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles]; 219 | int entries_count = layoutobj->posEnd[0]; 220 | 221 | handleTouch(menu); 222 | 223 | if (down & HidNpadButton_Y) 224 | { 225 | launchMenuNetloaderTask(); 226 | } 227 | else if (down & HidNpadButton_X) 228 | { 229 | menuHandleXButton(); 230 | } 231 | else if (down & HidNpadButton_A) 232 | { 233 | menuHandleAButton(); 234 | } 235 | else if (down & HidNpadButton_B) 236 | { 237 | launchMenuBackTask(); 238 | } 239 | else if(down & HidNpadButton_Minus){ 240 | themeMenuStartup(); 241 | } 242 | else if (down & HidNpadButton_Plus) 243 | { 244 | exitflag = 1; 245 | } 246 | else if (menu->nEntries > 0) 247 | { 248 | int move = 0; 249 | 250 | if (down & HidNpadButton_AnyLeft) move--; 251 | if (down & HidNpadButton_AnyRight) move++; 252 | if (down & (HidNpadButton_AnyDown | HidNpadButton_ZL)) move-=entries_count; 253 | if (down & (HidNpadButton_AnyUp | HidNpadButton_ZR)) move+=entries_count; 254 | 255 | int newEntry = menu->curEntry + move; 256 | if (newEntry < 0) newEntry = 0; 257 | if (newEntry >= menu->nEntries) newEntry = menu->nEntries-1; 258 | menu->curEntry = newEntry; 259 | } 260 | 261 | return exitflag; 262 | } 263 | 264 | bool menuUpdateErrorScreen(void) { 265 | bool exitflag = 0; 266 | u64 down = menuGetKeysDown(); 267 | 268 | if (down & HidNpadButton_Plus) 269 | { 270 | exitflag = 1; 271 | } 272 | 273 | return exitflag; 274 | } 275 | -------------------------------------------------------------------------------- /nx_main/nx_audio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../common/common.h" 6 | 7 | #ifdef ENABLE_AUDIO 8 | #error "Audio is not supported currently." 9 | 10 | #define SAMPLERATE 48000 11 | #define BYTESPERSAMPLE 2 12 | #define CHANNELCOUNT 2 13 | 14 | static u8* raw_data, *raw_data2; 15 | static u8 *audio_data; 16 | static size_t audio_data_size; 17 | static size_t audio_data_loopoffset; 18 | static AudioOutBuffer source_buffer[2]; 19 | 20 | static Thread audio_thread; 21 | static bool audio_thread_exitflag = 0; 22 | static bool audio_thread_started = 0; 23 | 24 | static void audio_playback_thread(void* arg) 25 | { 26 | bool playing = 0; 27 | bool data_ready=0; 28 | int j, count; 29 | int bufi=0; 30 | 31 | u64 offset=0; 32 | u64 tmpsize=0; 33 | u64 totalsize = audio_data_size; 34 | 35 | AudioOutBuffer *released_buffer = NULL; 36 | AudioOutBuffer *src_buf = NULL; 37 | u32 released_count=0; 38 | 39 | while (!audio_thread_exitflag) 40 | { 41 | if (!playing) 42 | { 43 | count = 2; 44 | if (data_ready && released_count<2) count = 1; 45 | 46 | for (j=0; j totalsize - offset) tmpsize = totalsize - offset; 50 | 51 | if (!data_ready || released_count==2) { 52 | src_buf = &source_buffer[bufi]; 53 | } 54 | else { 55 | src_buf = released_buffer; 56 | } 57 | 58 | src_buf->data_size = tmpsize; 59 | 60 | memcpy(src_buf->buffer, &audio_data[offset], tmpsize); 61 | 62 | offset+= tmpsize; 63 | if (offset >= totalsize) offset = audio_data_loopoffset; 64 | 65 | audoutAppendAudioOutBuffer(src_buf); 66 | 67 | bufi = 1-bufi; 68 | } 69 | 70 | if (!data_ready) data_ready = 1; 71 | 72 | playing = 1; 73 | } 74 | 75 | if (R_SUCCEEDED(audoutWaitPlayFinish(&released_buffer, &released_count, UINT64_MAX))) 76 | playing = 0; 77 | } 78 | } 79 | 80 | void audio_initialize(void) 81 | { 82 | Result rc=0; 83 | 84 | u8 *audio_intro, *audio_loop; 85 | size_t audio_intro_size, audio_loop_size; 86 | 87 | audio_intro = (u8*)audio_intro_bin; 88 | audio_intro_size = audio_intro_bin_size; 89 | 90 | audio_loop = (u8*)audio_loop_bin; 91 | audio_loop_size = audio_loop_bin_size; 92 | 93 | audio_data_loopoffset = audio_intro_size; 94 | audio_data_size = audio_intro_size + audio_loop_size; 95 | 96 | u32 SAMPLESPERBUF = SAMPLERATE/4; 97 | 98 | u32 raw_data_size = (SAMPLESPERBUF * CHANNELCOUNT * BYTESPERSAMPLE); 99 | u32 raw_data_size_aligned = (raw_data_size + 0xfff) & ~0xfff; 100 | 101 | audio_data = (u8*)malloc(audio_data_size); 102 | 103 | raw_data = (u8*)memalign(0x1000, raw_data_size_aligned); 104 | raw_data2 = (u8*)memalign(0x1000, raw_data_size_aligned); 105 | 106 | if (audio_data==NULL || raw_data == NULL || raw_data2==NULL) { 107 | free(audio_data);//free() checks NULL. 108 | free(raw_data); 109 | free(raw_data2); 110 | 111 | audio_data = NULL; 112 | raw_data = NULL; 113 | raw_data2 = NULL; 114 | 115 | return; 116 | } 117 | 118 | memset(audio_data, 0, audio_data_size); 119 | 120 | memset(raw_data, 0, raw_data_size_aligned); 121 | memset(raw_data2, 0, raw_data_size_aligned); 122 | 123 | memcpy(audio_data, audio_intro, audio_intro_size); 124 | memcpy(&audio_data[audio_data_loopoffset], audio_loop, audio_loop_size); 125 | 126 | source_buffer[0].next = 0; 127 | source_buffer[0].buffer = raw_data; 128 | source_buffer[0].buffer_size = raw_data_size; 129 | source_buffer[0].data_size = raw_data_size; 130 | source_buffer[0].data_offset = 0; 131 | 132 | memcpy(&source_buffer[1], &source_buffer[0], sizeof(AudioOutBuffer)); 133 | source_buffer[1].buffer = raw_data2; 134 | 135 | if (R_SUCCEEDED(rc)) rc = audoutInitialize(); 136 | if (R_SUCCEEDED(rc)) rc = audoutStartAudioOut(); 137 | 138 | audio_thread_started = 0; 139 | 140 | if (R_SUCCEEDED(rc)) rc = threadCreate(&audio_thread, audio_playback_thread, 0, 0x4000, 28, -2); 141 | 142 | if (R_SUCCEEDED(rc)) rc = threadStart(&audio_thread); 143 | 144 | if (R_SUCCEEDED(rc)) audio_thread_started = 1; 145 | } 146 | 147 | void audio_exit(void) 148 | { 149 | if (audio_thread_started) { 150 | audio_thread_exitflag = 1; 151 | threadWaitForExit(&audio_thread); 152 | threadClose(&audio_thread); 153 | } 154 | 155 | audoutStopAudioOut(); 156 | audoutExit(); 157 | 158 | free(audio_data); 159 | free(raw_data); 160 | free(raw_data2); 161 | audio_data = NULL; 162 | raw_data = NULL; 163 | raw_data2 = NULL; 164 | } 165 | #endif 166 | 167 | -------------------------------------------------------------------------------- /nx_main/nx_graphics.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "nx_graphics.h" 5 | 6 | #define FB_NUM 2 7 | #define CMDMEMSIZE 0x1000 8 | 9 | static u32 s_fbWidth, s_fbHeight; 10 | 11 | static DkDevice s_device; 12 | static DkMemBlock s_fbMemBlock, s_workMemBlock, s_cmdMemBlock; 13 | static DkSwapchain s_swapchain; 14 | static DkCmdBuf s_cmdBuf; 15 | static DkCmdList s_cmdLists[FB_NUM]; 16 | static DkFence s_fence; 17 | static DkQueue s_queue; 18 | 19 | void graphicsInit(u32 width, u32 height) 20 | { 21 | DkImageLayoutMaker imgLayoutMaker; 22 | DkMemBlockMaker memBlockMaker; 23 | 24 | // Create the device, which is the root object 25 | DkDeviceMaker deviceMaker; 26 | dkDeviceMakerDefaults(&deviceMaker); 27 | s_device = dkDeviceCreate(&deviceMaker); 28 | 29 | // Calculate layout for the framebuffers 30 | DkImageLayout fbLayout; 31 | dkImageLayoutMakerDefaults(&imgLayoutMaker, s_device); 32 | imgLayoutMaker.flags = DkImageFlags_UsagePresent; 33 | imgLayoutMaker.format = DkImageFormat_RGBA8_Unorm; 34 | imgLayoutMaker.dimensions[0] = s_fbWidth = width; 35 | imgLayoutMaker.dimensions[1] = s_fbHeight = height; 36 | dkImageLayoutInitialize(&fbLayout, &imgLayoutMaker); 37 | 38 | // Retrieve necessary size and alignment for the framebuffers 39 | uint32_t fbSize = dkImageLayoutGetSize(&fbLayout); 40 | uint32_t fbAlign = dkImageLayoutGetAlignment(&fbLayout); 41 | fbSize = (fbSize + fbAlign - 1) &~ (fbAlign - 1); 42 | 43 | // Create a memory block that will host the framebuffers 44 | dkMemBlockMakerDefaults(&memBlockMaker, s_device, FB_NUM*fbSize); 45 | memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image; 46 | s_fbMemBlock = dkMemBlockCreate(&memBlockMaker); 47 | 48 | // Initialize the framebuffers with the layout and backing memory we've just created 49 | DkImage fbImages[FB_NUM]; 50 | DkImage const* swapchainImages[FB_NUM]; 51 | for (unsigned i = 0; i < FB_NUM; i ++) 52 | { 53 | swapchainImages[i] = &fbImages[i]; 54 | dkImageInitialize(&fbImages[i], &fbLayout, s_fbMemBlock, i*fbSize); 55 | } 56 | 57 | // Create a swapchain out of the framebuffers we've just initialized 58 | DkSwapchainMaker swapchainMaker; 59 | dkSwapchainMakerDefaults(&swapchainMaker, s_device, nwindowGetDefault(), swapchainImages, FB_NUM); 60 | s_swapchain = dkSwapchainCreate(&swapchainMaker); 61 | 62 | // Create a memory block for the linear framebuffer 63 | dkMemBlockMakerDefaults(&memBlockMaker, s_device, width*height*4); 64 | memBlockMaker.flags = DkMemBlockFlags_CpuCached | DkMemBlockFlags_GpuUncached; 65 | s_workMemBlock = dkMemBlockCreate(&memBlockMaker); 66 | 67 | // Create a memory block for the command lists 68 | dkMemBlockMakerDefaults(&memBlockMaker, s_device, CMDMEMSIZE); 69 | memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached; 70 | s_cmdMemBlock = dkMemBlockCreate(&memBlockMaker); 71 | 72 | // Create a command buffer 73 | DkCmdBufMaker cmdBufMaker; 74 | dkCmdBufMakerDefaults(&cmdBufMaker, s_device); 75 | s_cmdBuf = dkCmdBufCreate(&cmdBufMaker); 76 | dkCmdBufAddMemory(s_cmdBuf, s_cmdMemBlock, 0, CMDMEMSIZE); 77 | 78 | // Define source for linear framebuffer copies 79 | const DkCopyBuf linearSrc = { 80 | .addr = dkMemBlockGetGpuAddr(s_workMemBlock), 81 | .rowLength = 0, 82 | .imageHeight = 0, 83 | }; 84 | 85 | // Define rectangle for the copies 86 | const DkImageRect copyRect = { 87 | .x = 0, .y = 0, .z = 0, 88 | .width = width, .height = height, .depth = 1, 89 | }; 90 | 91 | // Record command lists for the copies 92 | for (unsigned i = 0; i < FB_NUM; i ++) { 93 | DkImageView tiledDst; 94 | dkImageViewDefaults(&tiledDst, &fbImages[i]); 95 | dkCmdBufCopyBufferToImage(s_cmdBuf, &linearSrc, &tiledDst, ©Rect, 0); 96 | dkCmdBufSignalFence(s_cmdBuf, &s_fence, false); 97 | s_cmdLists[i] = dkCmdBufFinishList(s_cmdBuf); 98 | } 99 | 100 | // Create a queue, to which we will submit our command lists 101 | DkQueueMaker queueMaker; 102 | dkQueueMakerDefaults(&queueMaker, s_device); 103 | queueMaker.flags = 0; // we will only use this queue for transferring 104 | s_queue = dkQueueCreate(&queueMaker); 105 | } 106 | 107 | void graphicsExit(void) 108 | { 109 | // Make sure the queue is idle before destroying anything 110 | dkQueueWaitIdle(s_queue); 111 | 112 | // Destroy all the resources we've created 113 | dkQueueDestroy(s_queue); 114 | dkCmdBufDestroy(s_cmdBuf); 115 | dkMemBlockDestroy(s_cmdMemBlock); 116 | dkMemBlockDestroy(s_workMemBlock); 117 | dkSwapchainDestroy(s_swapchain); 118 | dkMemBlockDestroy(s_fbMemBlock); 119 | dkDeviceDestroy(s_device); 120 | } 121 | 122 | void* graphicsFrameBegin(u32* out_stride) 123 | { 124 | // Ensure the GPU is not reading from the framebuffer 125 | dkFenceWait(&s_fence, -1); 126 | 127 | // Return information 128 | if (out_stride) *out_stride = s_fbWidth*4; 129 | return dkMemBlockGetCpuAddr(s_workMemBlock); 130 | } 131 | 132 | void graphicsFrameEnd(void) 133 | { 134 | // Flush the linear framebuffer 135 | dkMemBlockFlushCpuCache(s_workMemBlock, 0, s_fbWidth*s_fbHeight*4); 136 | 137 | // Present a frame 138 | int slot = dkQueueAcquireImage(s_queue, s_swapchain); 139 | dkQueueSubmitCommands(s_queue, s_cmdLists[slot]); 140 | dkQueuePresentImage(s_queue, s_swapchain, slot); 141 | } 142 | -------------------------------------------------------------------------------- /nx_main/nx_graphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void graphicsInit(u32 width, u32 height); 6 | void graphicsExit(void); 7 | 8 | void* graphicsFrameBegin(u32* out_stride); 9 | void graphicsFrameEnd(void); 10 | -------------------------------------------------------------------------------- /nx_main/nx_launch.c: -------------------------------------------------------------------------------- 1 | #include "../common/common.h" 2 | 3 | static const loaderFuncs_s* s_loader; 4 | 5 | bool launchInit(void) { 6 | #define ADD_LOADER(_name) do \ 7 | { \ 8 | extern const loaderFuncs_s _name; \ 9 | if (_name.init()) \ 10 | { \ 11 | s_loader = &_name; \ 12 | return 1; \ 13 | } \ 14 | } while(0) 15 | 16 | ADD_LOADER(loader_builtin); 17 | 18 | // Shouldn't happen 19 | s_loader = NULL; 20 | return 0; 21 | } 22 | 23 | void launchExit(void) { 24 | if (s_loader) s_loader->deinit(); 25 | s_loader = NULL; 26 | } 27 | 28 | const loaderFuncs_s* launchGetLoader(void) { 29 | return s_loader; 30 | } 31 | 32 | void launchMenuEntry(menuEntry_s* me) { 33 | /*bool canUseTitles = loaderCanUseTitles(); 34 | if (me->descriptor.numTargetTitles && canUseTitles) 35 | { 36 | // Update the list of available titles 37 | titlesCheckUpdate(false, UI_STATE_NULL); 38 | 39 | int i; 40 | for (i = 0; i < me->descriptor.numTargetTitles; i ++) 41 | if (titlesExists(me->descriptor.targetTitles[i].tid, me->descriptor.targetTitles[i].mediatype)) 42 | break; 43 | 44 | if (i == me->descriptor.numTargetTitles) 45 | { 46 | errorScreen(s_loader->name, textGetString(StrId_MissingTargetTitle)); 47 | return; 48 | } 49 | 50 | // Use the title 51 | s_loader->useTitle(me->descriptor.targetTitles[i].tid, me->descriptor.targetTitles[i].mediatype); 52 | } else if (me->descriptor.selectTargetProcess) 53 | { 54 | if (!canUseTitles) 55 | { 56 | errorScreen(s_loader->name, textGetString(StrId_NoTargetTitleSupport)); 57 | return; 58 | } 59 | 60 | // Launch the title selector 61 | if (!me->titleSelected) 62 | { 63 | titleSelectInit(me); 64 | return; 65 | } 66 | 67 | // Use the title 68 | s_loader->useTitle(me->titleId, me->titleMediatype); 69 | }*/ 70 | 71 | // Scan the executable if needed 72 | /*if (loaderHasFlag(LOADER_NEED_SCAN)) 73 | descriptorScanFile(&me->descriptor, me->path);*/ 74 | 75 | // Launch it 76 | if (s_loader == NULL) return; 77 | s_loader->launchFile(me->path, &me->args); 78 | } 79 | -------------------------------------------------------------------------------- /nx_main/nx_netstatus.c: -------------------------------------------------------------------------------- 1 | #include "../common/common.h" 2 | 3 | bool netstatusGetDetails(AssetId *id) { 4 | Result rc=0; 5 | NifmInternetConnectionType contype; 6 | u32 wifiStrength=0; 7 | NifmInternetConnectionStatus connectionStatus; 8 | 9 | rc = nifmGetInternetConnectionStatus(&contype, &wifiStrength, &connectionStatus); 10 | if (R_FAILED(rc)) { 11 | *id = AssetId_airplane_icon; 12 | return true; 13 | } 14 | 15 | if (contype == NifmInternetConnectionType_Ethernet) { 16 | if (connectionStatus != NifmInternetConnectionStatus_Connected) 17 | *id = AssetId_eth_none_icon; 18 | else 19 | *id = AssetId_eth_icon; 20 | return true; 21 | } 22 | 23 | if (wifiStrength==0) { 24 | *id = AssetId_wifi_none_icon; 25 | return true; 26 | } 27 | 28 | if (wifiStrength==3) 29 | *id = AssetId_wifi3_icon; 30 | else if (wifiStrength==2) 31 | *id = AssetId_wifi2_icon; 32 | else 33 | *id = AssetId_wifi1_icon; 34 | 35 | return true; 36 | } 37 | -------------------------------------------------------------------------------- /nx_main/nx_power.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../common/common.h" 3 | 4 | static bool powerInitialized; 5 | static bool powerCacheInitialized; 6 | static uint32_t powerCacheCharge; 7 | static bool powerCacheIsCharging; 8 | static PsmSession powerSession; 9 | 10 | bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) { 11 | PsmChargerType charger = PsmChargerType_Unconnected; 12 | bool hwReadsSucceeded = false; 13 | bool use_cache = false; 14 | Result rc = 0; 15 | 16 | *isCharging = false; 17 | *batteryCharge = 0; 18 | 19 | if (powerInitialized) { 20 | if (powerCacheInitialized) { 21 | rc = psmWaitStateChangeEvent(&powerSession, 0); 22 | 23 | if (R_FAILED(rc)) use_cache = true; 24 | } 25 | 26 | rc = psmGetBatteryChargePercentage(batteryCharge); 27 | hwReadsSucceeded = R_SUCCEEDED(rc); 28 | if (use_cache) { 29 | *isCharging = powerCacheIsCharging; 30 | } 31 | else { 32 | rc = psmGetChargerType(&charger); 33 | hwReadsSucceeded &= R_SUCCEEDED(rc); 34 | *isCharging = (charger != PsmChargerType_Unconnected); 35 | } 36 | 37 | powerCacheCharge = *batteryCharge; 38 | powerCacheIsCharging = *isCharging; 39 | powerCacheInitialized = true; 40 | } 41 | 42 | return hwReadsSucceeded; 43 | } 44 | 45 | void powerInit(void) { 46 | uint32_t charge=0; 47 | bool isCharging=0; 48 | 49 | powerCacheInitialized = false; 50 | powerCacheCharge = 0; 51 | powerCacheIsCharging = false; 52 | 53 | if (!powerInitialized) { 54 | Result rc = psmInitialize(); 55 | if (R_SUCCEEDED(rc)) { 56 | rc = psmBindStateChangeEvent(&powerSession, 1, 1, 1); 57 | 58 | if (R_FAILED(rc)) psmExit(); 59 | if (R_SUCCEEDED(rc)) { 60 | powerInitialized = true; 61 | powerGetDetails(&charge, &isCharging);//Init the cache. 62 | } 63 | } 64 | } 65 | } 66 | 67 | void powerExit(void) { 68 | if (powerInitialized) { 69 | psmUnbindStateChangeEvent(&powerSession); 70 | psmExit(); 71 | powerInitialized = false; 72 | powerCacheInitialized = false; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /nx_main/nx_thermalstatus.c: -------------------------------------------------------------------------------- 1 | #include "../common/common.h" 2 | 3 | static TsSession g_tsInternalSession; 4 | 5 | bool thermalstatusInit(void) { 6 | if (R_FAILED(tsInitialize())) 7 | return false; 8 | 9 | if (hosversionAtLeast(17,0,0) && R_FAILED(tsOpenSession(&g_tsInternalSession, TsDeviceCode_LocationInternal))) 10 | return false; 11 | 12 | return true; 13 | } 14 | 15 | void thermalstatusExit(void) { 16 | if (hosversionAtLeast(17,0,0)) 17 | tsSessionClose(&g_tsInternalSession); 18 | tsExit(); 19 | } 20 | 21 | bool thermalstatusGetDetails(s32 *temperature) { 22 | if (hosversionAtLeast(17,0,0)) { 23 | float temp_float; 24 | if (R_SUCCEEDED(tsSessionGetTemperature(&g_tsInternalSession, &temp_float))) { 25 | *temperature = (int)temp_float; 26 | return true; 27 | } else { 28 | return false; 29 | } 30 | } else { 31 | return R_SUCCEEDED(tsGetTemperature(TsLocation_Internal, temperature)); 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /nx_main/nx_touch.c: -------------------------------------------------------------------------------- 1 | #include "nx_touch.h" 2 | 3 | #define TAP_MOVEMENT_GAP 20 4 | #define VERTICAL_SWIPE_HORIZONTAL_PLAY 250 5 | #define VERTICAL_SWIPE_MINIMUM_DISTANCE 300 6 | #define HORIZONTAL_SWIPE_VERTICAL_PLAY 250 7 | #define HORIZONTAL_SWIPE_MINIMUM_DISTANCE 300 8 | 9 | #define distance(x1, y1, x2, y2) (int) sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1))) 10 | 11 | struct touchInfo_s touchInfo; 12 | 13 | void touchInit(void) { 14 | touchInfo.gestureInProgress = false; 15 | touchInfo.isTap = true; 16 | touchInfo.initMenuXPos = 0; 17 | touchInfo.initMenuIndex = 0; 18 | touchInfo.lastSlideSpeed = 0; 19 | hidInitializeTouchScreen(); 20 | } 21 | 22 | void handleTappingOnApp(menu_s* menu, int px) { 23 | int i = 0; 24 | menuEntry_s *me = NULL; 25 | ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList]; 26 | 27 | for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) { 28 | int entry_start_x = layoutobj->posStart[0] + i * layoutobj->posEnd[0]; 29 | 30 | int screen_width = 1280; 31 | if (entry_start_x >= (screen_width - menu->xPos)) 32 | break; 33 | 34 | if (px >= (entry_start_x + menu->xPos) && px <= (entry_start_x + menu->xPos) + layoutobj->size[0]) { 35 | launchMenuEntryTask(me); 36 | break; 37 | } 38 | } 39 | } 40 | 41 | void handleTappingOnOpenLaunch(menu_s* menu) { 42 | if (menu->nEntries > 0) 43 | { 44 | int i; 45 | menuEntry_s* me; 46 | for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next); 47 | launchMenuEntryTask(me); 48 | } 49 | } 50 | 51 | static inline bool checkInsideTextLayoutObject(ThemeLayoutId id, int x, int y) { 52 | ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[id]; 53 | if (!layoutobj->visible) return false; 54 | 55 | 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]; 56 | } 57 | 58 | void handleTouch(menu_s* menu) { 59 | ThemeLayoutObject *layoutobj = NULL; 60 | HidTouchScreenState touch = {0}; 61 | hidGetTouchScreenStates(&touch, 1); 62 | 63 | layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles]; 64 | int entries_count = layoutobj->posEnd[0]; 65 | layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList]; 66 | 67 | // On touch start. 68 | if (touch.count == 1 && !touchInfo.gestureInProgress) { 69 | touchInfo.gestureInProgress = true; 70 | touchInfo.firstTouch = touch.touches[0]; 71 | touchInfo.prevTouch = touch.touches[0]; 72 | touchInfo.isTap = true; 73 | touchInfo.initMenuXPos = menu->xPos; 74 | touchInfo.initMenuIndex = menu->curEntry; 75 | touchInfo.lastSlideSpeed = 0; 76 | menu->slideSpeed = 0; 77 | } 78 | // On touch moving. 79 | else if (touch.count >= 1 && touchInfo.gestureInProgress) { 80 | touchInfo.lastSlideSpeed = ((int)(touch.touches[0].x - touchInfo.prevTouch.x)); 81 | 82 | touchInfo.prevTouch = touch.touches[0]; 83 | 84 | 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)) { 85 | touchInfo.isTap = false; 86 | } 87 | if (!menuIsMsgBoxOpen() && touchInfo.firstTouch.y > layoutobj->posStart[1] && touchInfo.firstTouch.y < layoutobj->posStart[1]+layoutobj->size[1] && !touchInfo.isTap && menu->nEntries > entries_count) { 88 | 89 | if (!touchInfo.isTap) { 90 | menu->slideSpeed = touchInfo.lastSlideSpeed; 91 | } 92 | } 93 | } 94 | // On touch end. 95 | else if (touchInfo.gestureInProgress) { 96 | int x1 = touchInfo.firstTouch.x; 97 | int y1 = touchInfo.firstTouch.y; 98 | int x2 = touchInfo.prevTouch.x; 99 | int y2 = touchInfo.prevTouch.y; 100 | 101 | if (!touchInfo.isTap) { 102 | menu->slideSpeed = touchInfo.lastSlideSpeed; 103 | } 104 | 105 | bool netloader_active = menuIsNetloaderActive(); 106 | 107 | if (menuIsMsgBoxOpen() && !netloader_active) { 108 | layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator]; 109 | MessageBox currMsgBox = menuGetCurrentMsgBox(); 110 | int start_x = 1280 / 2 - currMsgBox.width / 2; 111 | int start_y = (720 / 2 - currMsgBox.height / 2) + currMsgBox.height; 112 | int end_x = start_x + currMsgBox.width; 113 | int end_y = start_y; 114 | start_y+= layoutobj->posStart[1]; 115 | 116 | if (x1 > start_x && x1 < end_x && y1 > start_y && y1 < end_y && touchInfo.isTap) { 117 | menuCloseMsgBox(); 118 | } 119 | } else if (touchInfo.isTap && !netloader_active) { 120 | // App Icons 121 | if (y1 > layoutobj->posStart[1] && y1 < layoutobj->posStart[1]+layoutobj->size[1]) { 122 | handleTappingOnApp(menu, touchInfo.prevTouch.x); 123 | } 124 | // Bottom Buttons 125 | else { 126 | // Back Button 127 | if (checkInsideTextLayoutObject(ThemeLayoutId_ButtonB, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonBText, x1, y1)) { 128 | launchMenuBackTask(); 129 | } 130 | // Open/Launch Button 131 | else if (menu->nEntries != 0 && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonA, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonAText, x1, y1))) { 132 | handleTappingOnOpenLaunch(menu); 133 | } 134 | // Star Button 135 | else if (menu->nEntries != 0) { 136 | int i; 137 | menuEntry_s* me; 138 | for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next); 139 | if (me->type != ENTRY_TYPE_THEME && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonX, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonXText, x1, y1))) { 140 | menuHandleXButton(); 141 | } 142 | } 143 | } 144 | } 145 | // Vertical Swipe 146 | else if (abs(x1 - x2) < VERTICAL_SWIPE_HORIZONTAL_PLAY && distance(x1, y1, x2, y2) > VERTICAL_SWIPE_MINIMUM_DISTANCE) { 147 | // Swipe up to go back 148 | if (y1 - y2 > 0) { 149 | launchMenuBackTask(); 150 | } 151 | // Swipe down to go into netloader 152 | else if (y1 - y2 < 0) { 153 | launchMenuNetloaderTask(); 154 | } 155 | } 156 | // Horizontal Swipe 157 | else if (y1 < layoutobj->posStart[1] && y2 < layoutobj->posStart[1]) { 158 | if (abs(y1 - y2) < HORIZONTAL_SWIPE_VERTICAL_PLAY && distance(x1, y1, x2, y2) > HORIZONTAL_SWIPE_MINIMUM_DISTANCE) { 159 | // Swipe left to go into theme-menu 160 | if (x1 - x2 > 0) { 161 | themeMenuStartup(); 162 | } 163 | } 164 | } 165 | 166 | touchInfo.gestureInProgress = false; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /nx_main/nx_touch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../common/common.h" 5 | 6 | struct touchInfo_s { 7 | bool gestureInProgress; 8 | HidTouchState firstTouch; 9 | HidTouchState prevTouch; 10 | bool isTap; 11 | int initMenuXPos; 12 | int initMenuIndex; 13 | int lastSlideSpeed; 14 | }; 15 | 16 | void touchInit(void); 17 | void handleTouch(menu_s* menu); -------------------------------------------------------------------------------- /pc_main/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | extern "C" { 7 | 8 | #include "../common/common.h" 9 | 10 | } 11 | 12 | color_t pixels[720][1280]; 13 | 14 | int main(int argc, char **argv) 15 | { 16 | sf::RenderWindow window(sf::VideoMode(1280, 720), "Test"); 17 | window.setFramerateLimit(60); 18 | 19 | menuStartupPath(); 20 | PHYSFS_init(argv[0]); 21 | assetsInit(); 22 | themeStartup(THEME_PRESET_LIGHT); 23 | textInit(); 24 | fontInitialize(); 25 | netloaderInit(); 26 | workerInit(); 27 | statusInit(); 28 | menuStartup(); 29 | 30 | while (window.isOpen()) 31 | { 32 | sf::Event event; 33 | while (window.pollEvent(event)) 34 | { 35 | if (event.type == sf::Event::Closed) 36 | { 37 | window.close(); 38 | break; 39 | } 40 | } 41 | 42 | memset(pixels, 237, sizeof(pixels)); 43 | 44 | if (!uiUpdate()) break; 45 | menuLoop(); 46 | window.clear(); 47 | 48 | sf::Image image; 49 | image.create(1280, 720, (const unsigned char*)pixels); 50 | 51 | sf::Texture texture; 52 | texture.loadFromImage(image); 53 | 54 | sf::Sprite sprite; 55 | sprite.setTexture(texture); 56 | 57 | window.draw(sprite); 58 | window.display(); 59 | } 60 | 61 | netloaderSignalExit(); 62 | statusExit(); 63 | workerExit(); 64 | netloaderExit(); 65 | fontExit(); 66 | assetsExit(); 67 | PHYSFS_deinit(); 68 | 69 | return 0; 70 | } 71 | 72 | extern "C" bool menuUpdate(void) { 73 | //This is implemented here due to the hid code. 74 | menu_s* menu = menuGetCurrent(); 75 | 76 | static int esc_state = 0; 77 | int new_esc_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Escape); 78 | static int return_state = 0; 79 | int new_return_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Return); 80 | static int x_state = 0; 81 | int new_x_state = sf::Keyboard::isKeyPressed(sf::Keyboard::X); 82 | static int y_state = 0; 83 | int new_y_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Y); 84 | static int t_state = 0; 85 | int new_t_state = sf::Keyboard::isKeyPressed(sf::Keyboard::T); 86 | 87 | if(!new_y_state && y_state) 88 | { 89 | launchMenuNetloaderTask(); 90 | } 91 | 92 | if(!new_x_state && x_state) 93 | { 94 | menuHandleXButton(); 95 | } 96 | 97 | if (!new_esc_state && esc_state) 98 | { 99 | launchMenuBackTask(); 100 | } 101 | else if(!new_t_state && t_state){ 102 | themeMenuStartup(); 103 | } 104 | else if (!new_return_state && return_state) 105 | { 106 | menuHandleAButton(); 107 | } 108 | else if (menu->nEntries > 0) 109 | { 110 | int move = 0; 111 | 112 | static int left_state = 0; 113 | int new_left_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Left); 114 | if (!new_left_state && left_state) 115 | move--; 116 | left_state = new_left_state; 117 | 118 | static int right_state = 0; 119 | int new_right_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Right); 120 | if (!new_right_state && right_state) 121 | move++; 122 | right_state = new_right_state; 123 | 124 | int newEntry = menu->curEntry + move; 125 | if (newEntry < 0) newEntry = 0; 126 | if (newEntry >= menu->nEntries) newEntry = menu->nEntries-1; 127 | menu->curEntry = newEntry; 128 | } 129 | 130 | esc_state = new_esc_state; 131 | return_state = new_return_state; 132 | y_state = new_y_state; 133 | t_state = new_t_state; 134 | 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /pc_main/pc_launch.c: -------------------------------------------------------------------------------- 1 | #include "../common/common.h" 2 | 3 | static const loaderFuncs_s* s_loader; 4 | 5 | bool launchInit(void) { 6 | return 1; 7 | } 8 | 9 | void launchExit(void) { 10 | //s_loader->deinit(); 11 | } 12 | 13 | const loaderFuncs_s* launchGetLoader(void) { 14 | return s_loader; 15 | } 16 | 17 | void launchMenuEntry(menuEntry_s* me) { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /pc_main/pc_netstatus.c: -------------------------------------------------------------------------------- 1 | #include "../common/common.h" 2 | 3 | bool netstatusGetDetails(AssetId *id) { 4 | *id = AssetId_wifi3_icon; 5 | return true; 6 | } 7 | -------------------------------------------------------------------------------- /pc_main/pc_power.c: -------------------------------------------------------------------------------- 1 | #include "../common/common.h" 2 | 3 | void powerInit(void) { 4 | 5 | } 6 | 7 | void powerExit(void) { 8 | 9 | } 10 | 11 | bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) { 12 | *isCharging = false; 13 | *batteryCharge = 100; 14 | return false; 15 | } 16 | -------------------------------------------------------------------------------- /pc_main/pc_thermalstatus.c: -------------------------------------------------------------------------------- 1 | #include "../common/common.h" 2 | 3 | bool thermalstatusInit(void) { 4 | return false; 5 | } 6 | 7 | void thermalstatusExit(void) { 8 | 9 | } 10 | 11 | bool thermalstatusGetDetails(s32 *temperature) { 12 | *temperature = 0; 13 | return false; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /resources/airplane_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/airplane_icon.png -------------------------------------------------------------------------------- /resources/battery_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/battery_icon.png -------------------------------------------------------------------------------- /resources/charging_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/charging_icon.png -------------------------------------------------------------------------------- /resources/eth_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/eth_icon.png -------------------------------------------------------------------------------- /resources/eth_none_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/eth_none_icon.png -------------------------------------------------------------------------------- /resources/folder_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/folder_icon.png -------------------------------------------------------------------------------- /resources/invalid_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/invalid_icon.png -------------------------------------------------------------------------------- /resources/theme_icon_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/theme_icon_dark.png -------------------------------------------------------------------------------- /resources/theme_icon_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/theme_icon_light.png -------------------------------------------------------------------------------- /resources/wifi1_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/wifi1_icon.png -------------------------------------------------------------------------------- /resources/wifi2_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/wifi2_icon.png -------------------------------------------------------------------------------- /resources/wifi3_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/wifi3_icon.png -------------------------------------------------------------------------------- /resources/wifi_none_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchbrew/nx-hbmenu/b537333f3ed9e4d1f0edffb594352010cd214d26/resources/wifi_none_icon.png --------------------------------------------------------------------------------