├── .gitignore ├── Makefile ├── README.md ├── assets ├── App Bubble.png ├── Battery-0.png ├── Battery-1.png ├── Battery-2.png ├── Battery-3.png ├── Battery-4.png ├── Battery-CHARGE.png ├── Battery.psd ├── Bubble 1.png ├── Bubble 2.png ├── Bubble 3.png ├── Bubble 4.png ├── Bubble 5.png ├── Bubble 6.png ├── Bubble 7.png ├── Bubble 8.png ├── Bubble 9.png ├── Documentation - App Bubble.png ├── Documentation - Colour Palette.png ├── Logo.png ├── Main.png ├── Main.psd ├── Scrolling Bar.png ├── Scrolling Node.png ├── Settings Button.png ├── Wifi-0.png ├── Wifi-1.png ├── Wifi-2.png ├── Wifi-3.png ├── Wifi-NULL.png ├── folderIcon.png └── wifi icon.psd ├── data ├── app_bubble.bin ├── battery_charging.bin ├── battery_full.bin ├── battery_low.bin ├── battery_lowest.bin ├── battery_mid_high.bin ├── battery_mid_low.bin ├── bubble.bin ├── folderIcon.bin ├── font.bin ├── installerIcon.bin ├── logo.bin ├── regionfree.bin ├── top_bar.bin ├── wifi_full.bin └── wifi_none.bin ├── font.bmfc ├── font.fnt ├── font.py ├── fontSmall.bmfc ├── put3ds.bat └── source ├── background.c ├── background.h ├── boot.c ├── boot.h ├── costable.c ├── costable.h ├── cppsupport.cpp ├── descriptor.cpp ├── descriptor.h ├── error.c ├── error.h ├── filesystem.c ├── filesystem.h ├── font.c ├── font.h ├── font1.c ├── font2.c ├── gfx.c ├── gfx.h ├── main.c ├── menu.c ├── menu.h ├── mmap.cpp ├── mmap.h ├── netloader.c ├── netloader.h ├── regionfree.c ├── regionfree.h ├── scanner.c ├── scanner.h ├── shortcut.cpp ├── shortcut.h ├── smdh.c ├── smdh.h ├── statusbar.c ├── statusbar.h ├── text.c ├── text.h ├── tinyxml2.cpp ├── tinyxml2.h ├── titles.c ├── titles.h ├── utils.h ├── water.c └── water.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.3dsx 3 | *.exe 4 | *.elf 5 | *~ 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITARM)/3ds_rules 11 | 12 | export VER_MAJOR := 1 13 | export VER_MINOR := 1 14 | export VER_PATCH := 1 15 | 16 | export VERSTRING := $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH) 17 | #--------------------------------------------------------------------------------- 18 | # TARGET is the name of the output 19 | # BUILD is the directory where object files & intermediate files will be placed 20 | # SOURCES is a list of directories containing source code 21 | # DATA is a list of directories containing data files 22 | # INCLUDES is a list of directories containing header files 23 | # 24 | # NO_SMDH: if set to anything, no SMDH file is generated. 25 | # APP_TITLE is the name of the app stored in the SMDH file (Optional) 26 | # APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) 27 | # APP_AUTHOR is the author of the app stored in the SMDH file (Optional) 28 | # ICON is the filename of the icon (.png), relative to the project folder. 29 | # If not set, it attempts to use one of the following (in this order): 30 | # - .png 31 | # - icon.png 32 | # - /default_icon.png 33 | #--------------------------------------------------------------------------------- 34 | TARGET := boot 35 | BUILD := build 36 | SOURCES := source 37 | DATA := data 38 | INCLUDES := include 39 | NO_SMDH := 1 40 | 41 | #--------------------------------------------------------------------------------- 42 | # options for code generation 43 | #--------------------------------------------------------------------------------- 44 | ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard 45 | 46 | CFLAGS := -g -Wall -O2 -mword-relocations -ffunction-sections \ 47 | -fomit-frame-pointer -ffast-math \ 48 | $(ARCH) 49 | 50 | CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DVERSION=\"$(VERSTRING)\" 51 | 52 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 53 | 54 | ASFLAGS := -g $(ARCH) 55 | LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -Wl,--gc-sections 56 | 57 | LIBS := -lctru -lm -lz 58 | 59 | #--------------------------------------------------------------------------------- 60 | # list of directories containing libraries, this must be the top level containing 61 | # include and lib 62 | #--------------------------------------------------------------------------------- 63 | LIBDIRS := $(PORTLIBS) $(CTRULIB) 64 | 65 | 66 | #--------------------------------------------------------------------------------- 67 | # no real need to edit anything past this point unless you need to add additional 68 | # rules for different file extensions 69 | #--------------------------------------------------------------------------------- 70 | ifneq ($(BUILD),$(notdir $(CURDIR))) 71 | #--------------------------------------------------------------------------------- 72 | 73 | export OUTPUT := $(CURDIR)/$(TARGET) 74 | export TOPDIR := $(CURDIR) 75 | 76 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 77 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 78 | 79 | export DEPSDIR := $(CURDIR)/$(BUILD) 80 | 81 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 82 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 83 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 84 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 85 | 86 | #--------------------------------------------------------------------------------- 87 | # use CXX for linking C++ projects, CC for standard C 88 | #--------------------------------------------------------------------------------- 89 | ifeq ($(strip $(CPPFILES)),) 90 | #--------------------------------------------------------------------------------- 91 | export LD := $(CC) 92 | #--------------------------------------------------------------------------------- 93 | else 94 | #--------------------------------------------------------------------------------- 95 | export LD := $(CXX) 96 | #--------------------------------------------------------------------------------- 97 | endif 98 | #--------------------------------------------------------------------------------- 99 | 100 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 101 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 102 | 103 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 104 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 105 | -I$(CURDIR)/$(BUILD) 106 | 107 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 108 | 109 | ifeq ($(strip $(ICON)),) 110 | icons := $(wildcard *.png) 111 | ifneq (,$(findstring $(TARGET).png,$(icons))) 112 | export APP_ICON := $(TOPDIR)/$(TARGET).png 113 | else 114 | ifneq (,$(findstring icon.png,$(icons))) 115 | export APP_ICON := $(TOPDIR)/icon.png 116 | endif 117 | endif 118 | else 119 | export APP_ICON := $(TOPDIR)/$(ICON) 120 | endif 121 | 122 | .PHONY: $(BUILD) clean all 123 | 124 | #--------------------------------------------------------------------------------- 125 | all: $(BUILD) 126 | 127 | $(BUILD): 128 | @[ -d $@ ] || mkdir -p $@ 129 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 130 | 131 | #--------------------------------------------------------------------------------- 132 | clean: 133 | @echo clean ... 134 | @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf 135 | 136 | 137 | #--------------------------------------------------------------------------------- 138 | dist: $(BUILD) 139 | @tar -cvjf 3ds_hb_menu-$(VERSTRING).tar.bz2 boot.3dsx README.md 140 | #--------------------------------------------------------------------------------- 141 | else 142 | 143 | DEPENDS := $(OFILES:.o=.d) 144 | 145 | 146 | #--------------------------------------------------------------------------------- 147 | # main targets 148 | #--------------------------------------------------------------------------------- 149 | ifeq ($(strip $(NO_SMDH)),) 150 | .PHONY: all 151 | all : $(OUTPUT).3dsx $(OUTPUT).smdh 152 | endif 153 | $(OUTPUT).3dsx : $(OUTPUT).elf 154 | $(OUTPUT).elf : $(OFILES) 155 | 156 | background.o: $(TOPDIR)/Makefile 157 | 158 | #--------------------------------------------------------------------------------- 159 | # you need a rule like this for each extension you use as binary data 160 | #--------------------------------------------------------------------------------- 161 | %.bin.o : %.bin 162 | #--------------------------------------------------------------------------------- 163 | @echo $(notdir $<) 164 | @$(bin2o) 165 | 166 | # WARNING: This is not the right way to do this! TODO: Do it right! 167 | #--------------------------------------------------------------------------------- 168 | %.vsh.o : %.vsh 169 | #--------------------------------------------------------------------------------- 170 | @echo $(notdir $<) 171 | @python $(AEMSTRO)/aemstro_as.py $< ../$(notdir $<).shbin 172 | @bin2s ../$(notdir $<).shbin | $(PREFIX)as -o $@ 173 | @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(notdir $<).shbin | tr . _)`.h 174 | @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h 175 | @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h 176 | @rm ../$(notdir $<).shbin 177 | 178 | -include $(DEPENDS) 179 | 180 | #--------------------------------------------------------------------------------------- 181 | endif 182 | #--------------------------------------------------------------------------------------- 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### This is the hbmenu 1.x codebase, which is not maintained anymore and is kept for archival/historic purposes. 2 | 3 | See [new-hbmenu](https://github.com/fincs/new-hbmenu) for the latest version of the Homebrew Launcher. 4 | 5 | # The Homebrew Launcher 6 | 7 | #### Presentation 8 | 9 | The Homebrew Launcher (hbmenu for short) is the default menu for ninjhax, a 3DS homebrew-enabling exploit released on November 20th 2014. It is a fairly simple (and beautiful) menu that lists homebrew applications and lets you run them. 10 | 11 | #### Usage 12 | 13 | To use hbmenu as your ninjhax menu, simply rename the 3dsx executable to boot.3dsx and place it at the root of your SD card. 14 | 15 | Press START in hbmenu to reboot your console into home menu. Use the D-PAD, CIRCLE-PAD or the touchscreen to select an application, and press A or touch it again to start it. 16 | 17 | hbmenu starts in the sdmc:/3ds/ directory for applications; it will recognise folders containing "boot.3dsx" or "\[folder name\].3dsx" as "application bundles", other folders can be opened and browsed as you'd expect. You can have an icon file named "icon.bin", "icon.smdh", "icon.icn", "\[folder name\].smdh" or "\[folder name\].icn". hbmenu will also recognize stray 3dsx executables located in the sdmc:/3ds/ directory. 18 | 19 | Here is an example directory structure that hbmenu will have no trouble recognizing : 20 | 21 | - sdmc:/ 22 | - 3ds/ 23 | - 3dscraft/ 24 | - 3dscraft.3dsx 25 | - icon.bin 26 | - blargsnes/ 27 | - boot.3dsx 28 | - blargsnes.smdh 29 | - gameyob/ 30 | - gameyob.3dsx 31 | - gameyob.icn 32 | - cubedemo/ 33 | - boot.3dsx 34 | - icon.icn 35 | - 3dnes.3dsx 36 | - ftpony.3dsx 37 | 38 | If hbmenu does not find an icon file to associate with a given 3dsx, it will display a default icon and the path to the executable instead of the actual metadata for that executable. 39 | 40 | Hbmenu also allows you to create "shortcuts" which are xml files containing a path to a 3dsx file and optional arguments to pass to the .3dsx. This file can also include a path to icon data as well as name, description and author text using tags as follows: 41 | 42 | 43 | The path to the 3dsx file goes here. 44 | path to smdh icon data 45 | Place arguments to be passed to 3dsx here. 46 | Name to display 47 | Description of homebrew app 48 | Name of the author 49 | 50 | 51 | Arguments are space or tab separated but can use single or double quotes to contain whitespace. 52 | 53 | Name, description and author will be read from the .3dsx if it has embedded smdh data or from the supplied icon path. The fields in the xml file will then override their respective entries. 54 | 55 | Note that while you can hotswap the SD card while hbmenu is running and it *should* work fine, in practice this feature has proven to be unstable, so use at your own risk. It is recommended that you instead use a file transfer homebrew application such as ftpony to transfer files without rebooting. 56 | 57 | #### Technical notes 58 | 59 | Currently, everything in hbmenu is rendered in software. The reason for that is of course that homebrew GPU support is still unstable and very much a work in progress. Eventually, one of our goals should be to make hbmenu use the GPU for rendering. 60 | 61 | hbmenu uses some funky service commands to launch 3dsx files. If you're interested in launching 3dsx files from your own application, you should look here. 62 | 63 | #### Netloader 64 | 65 | The netloader has now been replaced with 3dslink. Press Y to activate as usual then run 3dslink <3dsxfile> if your network can cope with UDP broadcast messages. 66 | If 3dslink says 3DS not found then you can use -a to tell it where to send the file. 67 | 68 | All the other arguments you give 3dslink will be passed as arguments to the launched 3dsx file. You can also specify argv[0] with -0 which is useful for 69 | setting the current working directory if you already have data files in a particular place i.e. 3dslink myfile.3dsx -0 sdmc:/3ds/mydata/ 70 | 71 | 3dslink is provided with devkitARM or you can download binaries from http://davejmurphy.com/3dslink/ 72 | 73 | #### Building 74 | 75 | 3dslink uses zlib for compression so you'll need to compile and install zlib for 3DS. You can do this from a bash shell (use the devkitPro provided msys bash on windows) 76 | 77 | export PORTLIBS := $(DEVKITPRO)/portlibs/armv6k 78 | export PATH := $(DEVKITARM)/bin:$(PATH) 79 | export PKG_CONFIG_PATH := $(PORTLIBS)/lib/pkgconfig 80 | export CFLAGS := -march=armv6k -mtune=mpcore -mfloat-abi=hard -O3 -mword-relocations 81 | export CPPFLAGS := -I$(PORTLIBS)/include 82 | export LDFLAGS := -L$(PORTLIBS)/lib 83 | 84 | CHOST=arm-none-eabi ./configure --static --prefix=$(PORTLIBS) 85 | make && make install 86 | 87 | Binaries of 3ds_hb_menu can be downloaded from https://github.com/smealum/3ds_hb_menu/releases 88 | 89 | #### Contributing 90 | 91 | hbmenu is looking for contributors ! We're making this repository public so that you, the community, can make hbmenu into the menu of your dreams. Or show you how to make your own, better menu ! Of course we'd rather you improved hbmenu rather than went off and started fragmenting the userbase, but any contributions to the homebrew scene are welcome. Feel free to use code from hbmenu for your own projects, so long as you give credit to its original authors. 92 | 93 | #### Credits 94 | 95 | - smea : code 96 | - GEMISIS : code 97 | - fincs : code 98 | - mtheall : code 99 | - Fluto : graphics 100 | - Arkhandar : graphics 101 | - dotjasp : graphics (regionfree icon) 102 | -------------------------------------------------------------------------------- /assets/App Bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/App Bubble.png -------------------------------------------------------------------------------- /assets/Battery-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Battery-0.png -------------------------------------------------------------------------------- /assets/Battery-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Battery-1.png -------------------------------------------------------------------------------- /assets/Battery-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Battery-2.png -------------------------------------------------------------------------------- /assets/Battery-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Battery-3.png -------------------------------------------------------------------------------- /assets/Battery-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Battery-4.png -------------------------------------------------------------------------------- /assets/Battery-CHARGE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Battery-CHARGE.png -------------------------------------------------------------------------------- /assets/Battery.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Battery.psd -------------------------------------------------------------------------------- /assets/Bubble 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Bubble 1.png -------------------------------------------------------------------------------- /assets/Bubble 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Bubble 2.png -------------------------------------------------------------------------------- /assets/Bubble 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Bubble 3.png -------------------------------------------------------------------------------- /assets/Bubble 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Bubble 4.png -------------------------------------------------------------------------------- /assets/Bubble 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Bubble 5.png -------------------------------------------------------------------------------- /assets/Bubble 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Bubble 6.png -------------------------------------------------------------------------------- /assets/Bubble 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Bubble 7.png -------------------------------------------------------------------------------- /assets/Bubble 8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Bubble 8.png -------------------------------------------------------------------------------- /assets/Bubble 9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Bubble 9.png -------------------------------------------------------------------------------- /assets/Documentation - App Bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Documentation - App Bubble.png -------------------------------------------------------------------------------- /assets/Documentation - Colour Palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Documentation - Colour Palette.png -------------------------------------------------------------------------------- /assets/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Logo.png -------------------------------------------------------------------------------- /assets/Main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Main.png -------------------------------------------------------------------------------- /assets/Main.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Main.psd -------------------------------------------------------------------------------- /assets/Scrolling Bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Scrolling Bar.png -------------------------------------------------------------------------------- /assets/Scrolling Node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Scrolling Node.png -------------------------------------------------------------------------------- /assets/Settings Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Settings Button.png -------------------------------------------------------------------------------- /assets/Wifi-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Wifi-0.png -------------------------------------------------------------------------------- /assets/Wifi-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Wifi-1.png -------------------------------------------------------------------------------- /assets/Wifi-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Wifi-2.png -------------------------------------------------------------------------------- /assets/Wifi-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Wifi-3.png -------------------------------------------------------------------------------- /assets/Wifi-NULL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/Wifi-NULL.png -------------------------------------------------------------------------------- /assets/folderIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/folderIcon.png -------------------------------------------------------------------------------- /assets/wifi icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/assets/wifi icon.psd -------------------------------------------------------------------------------- /data/app_bubble.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/app_bubble.bin -------------------------------------------------------------------------------- /data/battery_charging.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/battery_charging.bin -------------------------------------------------------------------------------- /data/battery_full.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/battery_full.bin -------------------------------------------------------------------------------- /data/battery_low.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/battery_low.bin -------------------------------------------------------------------------------- /data/battery_lowest.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/battery_lowest.bin -------------------------------------------------------------------------------- /data/battery_mid_high.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/battery_mid_high.bin -------------------------------------------------------------------------------- /data/battery_mid_low.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/battery_mid_low.bin -------------------------------------------------------------------------------- /data/bubble.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/bubble.bin -------------------------------------------------------------------------------- /data/folderIcon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/folderIcon.bin -------------------------------------------------------------------------------- /data/font.bin: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /data/installerIcon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/installerIcon.bin -------------------------------------------------------------------------------- /data/logo.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/logo.bin -------------------------------------------------------------------------------- /data/regionfree.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/regionfree.bin -------------------------------------------------------------------------------- /data/top_bar.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/top_bar.bin -------------------------------------------------------------------------------- /data/wifi_full.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/wifi_full.bin -------------------------------------------------------------------------------- /data/wifi_none.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/data/wifi_none.bin -------------------------------------------------------------------------------- /font.bmfc: -------------------------------------------------------------------------------- 1 | # AngelCode Bitmap Font Generator configuration file 2 | fileVersion=1 3 | 4 | # font settings 5 | fontName=Segoe UI Semilight 6 | fontFile= 7 | charSet=0 8 | fontSize=16 9 | aa=1 10 | scaleH=100 11 | useSmoothing=1 12 | isBold=1 13 | isItalic=0 14 | useUnicode=1 15 | disableBoxChars=1 16 | outputInvalidCharGlyph=0 17 | dontIncludeKerningPairs=0 18 | useHinting=1 19 | renderFromOutline=0 20 | useClearType=1 21 | 22 | # character alignment 23 | paddingDown=0 24 | paddingUp=0 25 | paddingRight=0 26 | paddingLeft=0 27 | spacingHoriz=1 28 | spacingVert=1 29 | useFixedHeight=0 30 | forceZero=0 31 | 32 | # output file 33 | outWidth=256 34 | outHeight=64 35 | outBitDepth=8 36 | fontDescFormat=0 37 | fourChnlPacked=0 38 | textureFormat=png 39 | textureCompression=0 40 | alphaChnl=0 41 | redChnl=4 42 | greenChnl=4 43 | blueChnl=4 44 | invA=0 45 | invR=0 46 | invG=0 47 | invB=0 48 | 49 | # outline 50 | outlineThickness=0 51 | 52 | # selected chars 53 | chars=32-126,160-255 54 | 55 | # imported icon images 56 | -------------------------------------------------------------------------------- /font.fnt: -------------------------------------------------------------------------------- 1 | info face="Segoe UI Semilight" size=16 bold=1 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 2 | common lineHeight=15 base=12 scaleW=256 scaleH=64 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4 3 | page id=0 file="font_0.png" 4 | chars count=191 5 | char id=32 x=201 y=57 width=3 height=1 xoffset=-1 yoffset=14 xadvance=4 page=0 chnl=15 6 | char id=33 x=100 y=44 width=4 height=9 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=15 7 | char id=34 x=118 y=59 width=5 height=3 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 8 | char id=35 x=136 y=43 width=8 height=8 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 9 | char id=36 x=10 y=13 width=8 height=11 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15 10 | char id=37 x=175 y=13 width=11 height=9 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=15 11 | char id=38 x=33 y=25 width=10 height=9 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=15 12 | char id=39 x=252 y=42 width=3 height=3 xoffset=1 yoffset=3 xadvance=4 page=0 chnl=15 13 | char id=40 x=53 y=13 width=5 height=11 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 14 | char id=41 x=65 y=13 width=4 height=11 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 15 | char id=42 x=225 y=50 width=6 height=4 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 16 | char id=43 x=168 y=52 width=7 height=5 xoffset=1 yoffset=6 xadvance=9 page=0 chnl=15 17 | char id=44 x=129 y=59 width=3 height=3 xoffset=0 yoffset=11 xadvance=4 page=0 chnl=15 18 | char id=45 x=184 y=58 width=6 height=1 xoffset=0 yoffset=8 xadvance=6 page=0 chnl=15 19 | char id=46 x=196 y=57 width=4 height=1 xoffset=0 yoffset=11 xadvance=4 page=0 chnl=15 20 | char id=47 x=109 y=13 width=7 height=10 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=15 21 | char id=48 x=209 y=23 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 22 | char id=49 x=68 y=45 width=6 height=9 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 23 | char id=50 x=218 y=23 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 24 | char id=51 x=24 y=45 width=7 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 25 | char id=52 x=227 y=22 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 26 | char id=53 x=236 y=22 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 27 | char id=54 x=245 y=22 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 28 | char id=55 x=126 y=34 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 29 | char id=56 x=0 y=35 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 30 | char id=57 x=9 y=35 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 31 | char id=58 x=113 y=54 width=4 height=6 xoffset=0 yoffset=6 xadvance=4 page=0 chnl=15 32 | char id=59 x=199 y=43 width=4 height=8 xoffset=0 yoffset=6 xadvance=4 page=0 chnl=15 33 | char id=60 x=136 y=52 width=7 height=5 xoffset=1 yoffset=6 xadvance=9 page=0 chnl=15 34 | char id=61 x=232 y=50 width=7 height=3 xoffset=1 yoffset=7 xadvance=9 page=0 chnl=15 35 | char id=62 x=144 y=52 width=7 height=5 xoffset=1 yoffset=6 xadvance=9 page=0 chnl=15 36 | char id=63 x=249 y=32 width=6 height=9 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 37 | char id=64 x=75 y=13 width=12 height=10 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=15 38 | char id=65 x=144 y=23 width=9 height=9 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=15 39 | char id=66 x=18 y=35 width=8 height=9 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15 40 | char id=67 x=54 y=25 width=9 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 41 | char id=68 x=0 y=25 width=10 height=9 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=15 42 | char id=69 x=40 y=45 width=6 height=9 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15 43 | char id=70 x=47 y=45 width=6 height=9 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15 44 | char id=71 x=235 y=12 width=10 height=9 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=15 45 | char id=72 x=246 y=12 width=9 height=9 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=15 46 | char id=73 x=113 y=44 width=3 height=9 xoffset=1 yoffset=3 xadvance=4 page=0 chnl=15 47 | char id=74 x=61 y=45 width=6 height=9 xoffset=-1 yoffset=3 xadvance=5 page=0 chnl=15 48 | char id=75 x=16 y=45 width=7 height=9 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15 49 | char id=76 x=54 y=45 width=6 height=9 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15 50 | char id=77 x=223 y=12 width=11 height=9 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=15 51 | char id=78 x=84 y=24 width=9 height=9 xoffset=1 yoffset=3 xadvance=10 page=0 chnl=15 52 | char id=79 x=163 y=13 width=11 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 53 | char id=80 x=27 y=35 width=8 height=9 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15 54 | char id=81 x=88 y=13 width=11 height=10 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 55 | char id=82 x=36 y=35 width=8 height=9 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15 56 | char id=83 x=45 y=35 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 57 | char id=84 x=54 y=35 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 58 | char id=85 x=74 y=25 width=9 height=9 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=15 59 | char id=86 x=154 y=23 width=9 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 60 | char id=87 x=138 y=13 width=12 height=9 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=15 61 | char id=88 x=63 y=35 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 62 | char id=89 x=72 y=35 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 63 | char id=90 x=81 y=35 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 64 | char id=91 x=59 y=13 width=5 height=11 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 65 | char id=92 x=117 y=13 width=7 height=10 xoffset=-1 yoffset=3 xadvance=5 page=0 chnl=15 66 | char id=93 x=70 y=13 width=4 height=11 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 67 | char id=94 x=160 y=52 width=7 height=5 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=15 68 | char id=95 x=152 y=58 width=8 height=1 xoffset=-1 yoffset=13 xadvance=6 page=0 chnl=15 69 | char id=96 x=147 y=58 width=4 height=2 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=15 70 | char id=97 x=56 y=55 width=8 height=6 xoffset=0 yoffset=6 xadvance=7 page=0 chnl=15 71 | char id=98 x=124 y=24 width=9 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 72 | char id=99 x=82 y=55 width=7 height=6 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=15 73 | char id=100 x=90 y=34 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 74 | char id=101 x=29 y=55 width=8 height=6 xoffset=0 yoffset=6 xadvance=7 page=0 chnl=15 75 | char id=102 x=233 y=32 width=7 height=9 xoffset=-1 yoffset=3 xadvance=5 page=0 chnl=15 76 | char id=103 x=99 y=34 width=8 height=9 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 77 | char id=104 x=108 y=34 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 78 | char id=105 x=109 y=44 width=3 height=9 xoffset=1 yoffset=3 xadvance=4 page=0 chnl=15 79 | char id=106 x=179 y=0 width=6 height=12 xoffset=-2 yoffset=3 xadvance=4 page=0 chnl=15 80 | char id=107 x=75 y=45 width=6 height=9 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15 81 | char id=108 x=105 y=44 width=3 height=9 xoffset=1 yoffset=3 xadvance=4 page=0 chnl=15 82 | char id=109 x=215 y=43 width=12 height=6 xoffset=0 yoffset=6 xadvance=11 page=0 chnl=15 83 | char id=110 x=47 y=55 width=8 height=6 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 84 | char id=111 x=10 y=55 width=9 height=6 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 85 | char id=112 x=64 y=25 width=9 height=9 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 86 | char id=113 x=117 y=34 width=8 height=9 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 87 | char id=114 x=106 y=54 width=6 height=6 xoffset=0 yoffset=6 xadvance=5 page=0 chnl=15 88 | char id=115 x=98 y=54 width=7 height=6 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=15 89 | char id=116 x=187 y=43 width=6 height=8 xoffset=-1 yoffset=4 xadvance=5 page=0 chnl=15 90 | char id=117 x=38 y=55 width=8 height=6 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 91 | char id=118 x=90 y=55 width=7 height=6 xoffset=0 yoffset=6 xadvance=7 page=0 chnl=15 92 | char id=119 x=241 y=42 width=10 height=6 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=15 93 | char id=120 x=74 y=55 width=7 height=6 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=15 94 | char id=121 x=32 y=45 width=7 height=9 xoffset=0 yoffset=6 xadvance=7 page=0 chnl=15 95 | char id=122 x=65 y=55 width=8 height=6 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=15 96 | char id=123 x=47 y=13 width=5 height=11 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 97 | char id=124 x=217 y=0 width=4 height=12 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=15 98 | char id=125 x=41 y=13 width=5 height=11 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 99 | char id=126 x=133 y=59 width=7 height=2 xoffset=1 yoffset=7 xadvance=9 page=0 chnl=15 100 | char id=160 x=205 y=57 width=3 height=1 xoffset=-1 yoffset=14 xadvance=4 page=0 chnl=15 101 | char id=161 x=194 y=43 width=4 height=8 xoffset=0 yoffset=6 xadvance=4 page=0 chnl=15 102 | char id=162 x=172 y=43 width=7 height=8 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15 103 | char id=163 x=135 y=33 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 104 | char id=164 x=118 y=53 width=8 height=5 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=15 105 | char id=165 x=144 y=33 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 106 | char id=166 x=212 y=0 width=4 height=12 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=15 107 | char id=167 x=0 y=45 width=7 height=9 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 108 | char id=168 x=170 y=58 width=6 height=1 xoffset=0 yoffset=4 xadvance=6 page=0 chnl=15 109 | char id=169 x=199 y=13 width=11 height=9 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=15 110 | char id=170 x=211 y=51 width=6 height=4 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 111 | char id=171 x=203 y=52 width=7 height=4 xoffset=0 yoffset=7 xadvance=7 page=0 chnl=15 112 | char id=172 x=240 y=50 width=7 height=3 xoffset=1 yoffset=8 xadvance=9 page=0 chnl=15 113 | char id=173 x=177 y=58 width=6 height=1 xoffset=0 yoffset=8 xadvance=6 page=0 chnl=15 114 | char id=174 x=204 y=43 width=10 height=7 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 115 | char id=175 x=161 y=58 width=8 height=1 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=15 116 | char id=176 x=248 y=49 width=6 height=3 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 117 | char id=177 x=20 y=55 width=8 height=6 xoffset=1 yoffset=6 xadvance=9 page=0 chnl=15 118 | char id=178 x=189 y=52 width=5 height=5 xoffset=0 yoffset=1 xadvance=5 page=0 chnl=15 119 | char id=179 x=183 y=52 width=5 height=5 xoffset=0 yoffset=1 xadvance=5 page=0 chnl=15 120 | char id=180 x=141 y=58 width=5 height=2 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=15 121 | char id=181 x=127 y=44 width=8 height=8 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 122 | char id=182 x=8 y=45 width=7 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 123 | char id=183 x=191 y=58 width=4 height=1 xoffset=0 yoffset=7 xadvance=4 page=0 chnl=15 124 | char id=184 x=124 y=59 width=4 height=3 xoffset=0 yoffset=12 xadvance=3 page=0 chnl=15 125 | char id=185 x=176 y=52 width=6 height=5 xoffset=0 yoffset=1 xadvance=5 page=0 chnl=15 126 | char id=186 x=218 y=50 width=6 height=4 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 127 | char id=187 x=195 y=52 width=7 height=4 xoffset=0 yoffset=7 xadvance=7 page=0 chnl=15 128 | char id=188 x=22 y=25 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 129 | char id=189 x=187 y=13 width=11 height=9 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=15 130 | char id=190 x=11 y=25 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 131 | char id=191 x=225 y=33 width=7 height=9 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=15 132 | char id=192 x=48 y=0 width=9 height=12 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 133 | char id=193 x=108 y=0 width=9 height=12 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 134 | char id=194 x=88 y=0 width=9 height=12 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 135 | char id=195 x=68 y=0 width=9 height=12 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 136 | char id=196 x=244 y=0 width=9 height=11 xoffset=0 yoffset=1 xadvance=9 page=0 chnl=15 137 | char id=197 x=234 y=0 width=9 height=11 xoffset=0 yoffset=1 xadvance=9 page=0 chnl=15 138 | char id=198 x=125 y=13 width=12 height=9 xoffset=-1 yoffset=3 xadvance=11 page=0 chnl=15 139 | char id=199 x=98 y=0 width=9 height=12 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 140 | char id=200 x=165 y=0 width=6 height=12 xoffset=1 yoffset=0 xadvance=7 page=0 chnl=15 141 | char id=201 x=186 y=0 width=6 height=12 xoffset=1 yoffset=0 xadvance=7 page=0 chnl=15 142 | char id=202 x=172 y=0 width=6 height=12 xoffset=1 yoffset=0 xadvance=7 page=0 chnl=15 143 | char id=203 x=34 y=13 width=6 height=11 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15 144 | char id=204 x=206 y=0 width=5 height=12 xoffset=-1 yoffset=0 xadvance=4 page=0 chnl=15 145 | char id=205 x=200 y=0 width=5 height=12 xoffset=0 yoffset=0 xadvance=4 page=0 chnl=15 146 | char id=206 x=193 y=0 width=6 height=12 xoffset=-1 yoffset=0 xadvance=4 page=0 chnl=15 147 | char id=207 x=27 y=13 width=6 height=11 xoffset=-1 yoffset=1 xadvance=4 page=0 chnl=15 148 | char id=208 x=211 y=13 width=11 height=9 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 149 | char id=209 x=78 y=0 width=9 height=12 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 150 | char id=210 x=24 y=0 width=11 height=12 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 151 | char id=211 x=12 y=0 width=11 height=12 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 152 | char id=212 x=36 y=0 width=11 height=12 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 153 | char id=213 x=0 y=0 width=11 height=12 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 154 | char id=214 x=222 y=0 width=11 height=11 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 155 | char id=215 x=152 y=52 width=7 height=5 xoffset=1 yoffset=6 xadvance=9 page=0 chnl=15 156 | char id=216 x=151 y=13 width=11 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 157 | char id=217 x=58 y=0 width=9 height=12 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 158 | char id=218 x=138 y=0 width=9 height=12 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 159 | char id=219 x=128 y=0 width=9 height=12 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 160 | char id=220 x=0 y=13 width=9 height=11 xoffset=0 yoffset=1 xadvance=9 page=0 chnl=15 161 | char id=221 x=148 y=0 width=8 height=12 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 162 | char id=222 x=153 y=33 width=8 height=9 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15 163 | char id=223 x=162 y=33 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 164 | char id=224 x=171 y=33 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 165 | char id=225 x=180 y=33 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 166 | char id=226 x=189 y=33 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 167 | char id=227 x=198 y=33 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 168 | char id=228 x=163 y=43 width=8 height=8 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15 169 | char id=229 x=100 y=13 width=8 height=10 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15 170 | char id=230 x=228 y=43 width=12 height=6 xoffset=0 yoffset=6 xadvance=11 page=0 chnl=15 171 | char id=231 x=241 y=32 width=7 height=9 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=15 172 | char id=232 x=200 y=23 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 173 | char id=233 x=207 y=33 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 174 | char id=234 x=216 y=33 width=8 height=9 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 175 | char id=235 x=154 y=43 width=8 height=8 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15 176 | char id=236 x=88 y=45 width=5 height=9 xoffset=-1 yoffset=3 xadvance=4 page=0 chnl=15 177 | char id=237 x=82 y=45 width=5 height=9 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=15 178 | char id=238 x=94 y=44 width=5 height=9 xoffset=-1 yoffset=3 xadvance=4 page=0 chnl=15 179 | char id=239 x=180 y=43 width=6 height=8 xoffset=-1 yoffset=4 xadvance=4 page=0 chnl=15 180 | char id=240 x=44 y=25 width=9 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 181 | char id=241 x=164 y=23 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 182 | char id=242 x=134 y=23 width=9 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 183 | char id=243 x=94 y=24 width=9 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 184 | char id=244 x=104 y=24 width=9 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 185 | char id=245 x=114 y=24 width=9 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 186 | char id=246 x=117 y=44 width=9 height=8 xoffset=0 yoffset=4 xadvance=8 page=0 chnl=15 187 | char id=247 x=127 y=53 width=8 height=5 xoffset=1 yoffset=6 xadvance=9 page=0 chnl=15 188 | char id=248 x=0 y=55 width=9 height=6 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 189 | char id=249 x=173 y=23 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 190 | char id=250 x=182 y=23 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 191 | char id=251 x=191 y=23 width=8 height=9 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 192 | char id=252 x=145 y=43 width=8 height=8 xoffset=0 yoffset=4 xadvance=8 page=0 chnl=15 193 | char id=253 x=157 y=0 width=7 height=12 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 194 | char id=254 x=118 y=0 width=9 height=12 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 195 | char id=255 x=19 y=13 width=7 height=11 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15 196 | kernings count=355 197 | kerning first=222 second=193 amount=-1 198 | kerning first=222 second=194 amount=-1 199 | kerning first=222 second=195 amount=-1 200 | kerning first=222 second=192 amount=-1 201 | kerning first=222 second=198 amount=-2 202 | kerning first=222 second=197 amount=-1 203 | kerning first=222 second=196 amount=-1 204 | kerning first=222 second=74 amount=-1 205 | kerning first=222 second=65 amount=-1 206 | kerning first=222 second=46 amount=-2 207 | kerning first=222 second=44 amount=-2 208 | kerning first=253 second=46 amount=-1 209 | kerning first=253 second=44 amount=-1 210 | kerning first=221 second=193 amount=-1 211 | kerning first=221 second=194 amount=-1 212 | kerning first=221 second=195 amount=-1 213 | kerning first=221 second=192 amount=-1 214 | kerning first=221 second=191 amount=-1 215 | kerning first=221 second=230 amount=-1 216 | kerning first=221 second=198 amount=-1 217 | kerning first=221 second=252 amount=-1 218 | kerning first=221 second=251 amount=-1 219 | kerning first=221 second=249 amount=-1 220 | kerning first=221 second=250 amount=-1 221 | kerning first=221 second=245 amount=-1 222 | kerning first=221 second=246 amount=-1 223 | kerning first=42 second=65 amount=-1 224 | kerning first=42 second=74 amount=-1 225 | kerning first=42 second=99 amount=-1 226 | kerning first=42 second=100 amount=-1 227 | kerning first=42 second=101 amount=-1 228 | kerning first=42 second=103 amount=-1 229 | kerning first=42 second=111 amount=-1 230 | kerning first=42 second=113 amount=-1 231 | kerning first=221 second=244 amount=-1 232 | kerning first=221 second=242 amount=-1 233 | kerning first=221 second=243 amount=-1 234 | kerning first=221 second=241 amount=-1 235 | kerning first=221 second=239 amount=1 236 | kerning first=221 second=235 amount=-1 237 | kerning first=221 second=234 amount=-1 238 | kerning first=221 second=232 amount=-1 239 | kerning first=221 second=233 amount=-1 240 | kerning first=221 second=231 amount=-1 241 | kerning first=221 second=229 amount=-1 242 | kerning first=221 second=227 amount=-1 243 | kerning first=221 second=228 amount=-1 244 | kerning first=221 second=226 amount=-1 245 | kerning first=221 second=224 amount=-1 246 | kerning first=221 second=225 amount=-1 247 | kerning first=221 second=197 amount=-1 248 | kerning first=221 second=196 amount=-1 249 | kerning first=221 second=117 amount=-1 250 | kerning first=221 second=115 amount=-1 251 | kerning first=221 second=114 amount=-1 252 | kerning first=221 second=113 amount=-1 253 | kerning first=221 second=112 amount=-1 254 | kerning first=221 second=111 amount=-1 255 | kerning first=221 second=110 amount=-1 256 | kerning first=221 second=109 amount=-1 257 | kerning first=221 second=103 amount=-1 258 | kerning first=221 second=101 amount=-1 259 | kerning first=221 second=100 amount=-1 260 | kerning first=221 second=99 amount=-1 261 | kerning first=221 second=97 amount=-1 262 | kerning first=221 second=65 amount=-1 263 | kerning first=221 second=46 amount=-1 264 | kerning first=221 second=44 amount=-1 265 | kerning first=208 second=84 amount=-1 266 | kerning first=208 second=46 amount=-1 267 | kerning first=208 second=44 amount=-1 268 | kerning first=210 second=84 amount=-1 269 | kerning first=212 second=84 amount=-1 270 | kerning first=211 second=84 amount=-1 271 | kerning first=65 second=42 amount=-1 272 | kerning first=193 second=221 amount=-1 273 | kerning first=193 second=89 amount=-1 274 | kerning first=193 second=86 amount=-1 275 | kerning first=193 second=84 amount=-1 276 | kerning first=65 second=74 amount=1 277 | kerning first=193 second=74 amount=1 278 | kerning first=65 second=84 amount=-1 279 | kerning first=194 second=221 amount=-1 280 | kerning first=65 second=86 amount=-1 281 | kerning first=194 second=89 amount=-1 282 | kerning first=65 second=89 amount=-1 283 | kerning first=194 second=86 amount=-1 284 | kerning first=194 second=84 amount=-1 285 | kerning first=194 second=74 amount=1 286 | kerning first=213 second=84 amount=-1 287 | kerning first=195 second=221 amount=-1 288 | kerning first=195 second=89 amount=-1 289 | kerning first=195 second=86 amount=-1 290 | kerning first=195 second=84 amount=-1 291 | kerning first=195 second=74 amount=1 292 | kerning first=192 second=221 amount=-1 293 | kerning first=192 second=89 amount=-1 294 | kerning first=192 second=86 amount=-1 295 | kerning first=192 second=84 amount=-1 296 | kerning first=65 second=221 amount=-1 297 | kerning first=192 second=74 amount=1 298 | kerning first=187 second=221 amount=-1 299 | kerning first=187 second=89 amount=-1 300 | kerning first=161 second=106 amount=1 301 | kerning first=191 second=221 amount=-1 302 | kerning first=191 second=106 amount=1 303 | kerning first=191 second=89 amount=-1 304 | kerning first=216 second=84 amount=-1 305 | kerning first=214 second=84 amount=-1 306 | kerning first=197 second=221 amount=-1 307 | kerning first=197 second=89 amount=-1 308 | kerning first=197 second=86 amount=-1 309 | kerning first=197 second=84 amount=-1 310 | kerning first=197 second=74 amount=1 311 | kerning first=196 second=221 amount=-1 312 | kerning first=196 second=89 amount=-1 313 | kerning first=196 second=86 amount=-1 314 | kerning first=196 second=84 amount=-1 315 | kerning first=196 second=74 amount=1 316 | kerning first=123 second=106 amount=1 317 | kerning first=121 second=46 amount=-1 318 | kerning first=121 second=44 amount=-1 319 | kerning first=119 second=46 amount=-1 320 | kerning first=119 second=44 amount=-1 321 | kerning first=118 second=46 amount=-1 322 | kerning first=118 second=44 amount=-1 323 | kerning first=114 second=46 amount=-1 324 | kerning first=114 second=44 amount=-1 325 | kerning first=113 second=106 amount=1 326 | kerning first=111 second=39 amount=-1 327 | kerning first=111 second=34 amount=-1 328 | kerning first=110 second=39 amount=-1 329 | kerning first=110 second=34 amount=-1 330 | kerning first=102 second=239 amount=1 331 | kerning first=102 second=93 amount=1 332 | kerning first=102 second=46 amount=-1 333 | kerning first=102 second=44 amount=-1 334 | kerning first=101 second=39 amount=-1 335 | kerning first=101 second=34 amount=-1 336 | kerning first=66 second=84 amount=-1 337 | kerning first=99 second=84 amount=-1 338 | kerning first=91 second=106 amount=1 339 | kerning first=89 second=193 amount=-1 340 | kerning first=89 second=194 amount=-1 341 | kerning first=89 second=195 amount=-1 342 | kerning first=89 second=192 amount=-1 343 | kerning first=89 second=191 amount=-1 344 | kerning first=89 second=230 amount=-1 345 | kerning first=89 second=198 amount=-1 346 | kerning first=89 second=252 amount=-1 347 | kerning first=89 second=251 amount=-1 348 | kerning first=89 second=249 amount=-1 349 | kerning first=89 second=250 amount=-1 350 | kerning first=89 second=245 amount=-1 351 | kerning first=89 second=246 amount=-1 352 | kerning first=89 second=244 amount=-1 353 | kerning first=89 second=242 amount=-1 354 | kerning first=89 second=243 amount=-1 355 | kerning first=89 second=241 amount=-1 356 | kerning first=89 second=239 amount=1 357 | kerning first=89 second=235 amount=-1 358 | kerning first=89 second=234 amount=-1 359 | kerning first=89 second=232 amount=-1 360 | kerning first=89 second=233 amount=-1 361 | kerning first=89 second=231 amount=-1 362 | kerning first=89 second=229 amount=-1 363 | kerning first=89 second=227 amount=-1 364 | kerning first=89 second=228 amount=-1 365 | kerning first=89 second=226 amount=-1 366 | kerning first=89 second=224 amount=-1 367 | kerning first=89 second=225 amount=-1 368 | kerning first=89 second=197 amount=-1 369 | kerning first=89 second=196 amount=-1 370 | kerning first=89 second=117 amount=-1 371 | kerning first=89 second=115 amount=-1 372 | kerning first=89 second=114 amount=-1 373 | kerning first=89 second=113 amount=-1 374 | kerning first=89 second=112 amount=-1 375 | kerning first=89 second=111 amount=-1 376 | kerning first=89 second=110 amount=-1 377 | kerning first=68 second=44 amount=-1 378 | kerning first=68 second=46 amount=-1 379 | kerning first=89 second=109 amount=-1 380 | kerning first=68 second=84 amount=-1 381 | kerning first=89 second=103 amount=-1 382 | kerning first=89 second=101 amount=-1 383 | kerning first=89 second=100 amount=-1 384 | kerning first=89 second=99 amount=-1 385 | kerning first=89 second=97 amount=-1 386 | kerning first=89 second=65 amount=-1 387 | kerning first=89 second=46 amount=-1 388 | kerning first=89 second=44 amount=-1 389 | kerning first=88 second=74 amount=1 390 | kerning first=87 second=191 amount=-1 391 | kerning first=87 second=198 amount=-1 392 | kerning first=87 second=239 amount=1 393 | kerning first=87 second=46 amount=-1 394 | kerning first=87 second=44 amount=-1 395 | kerning first=86 second=193 amount=-1 396 | kerning first=86 second=194 amount=-1 397 | kerning first=86 second=195 amount=-1 398 | kerning first=86 second=192 amount=-1 399 | kerning first=86 second=191 amount=-1 400 | kerning first=86 second=230 amount=-1 401 | kerning first=86 second=198 amount=-1 402 | kerning first=86 second=245 amount=-1 403 | kerning first=86 second=246 amount=-1 404 | kerning first=86 second=244 amount=-1 405 | kerning first=86 second=242 amount=-1 406 | kerning first=86 second=243 amount=-1 407 | kerning first=86 second=239 amount=1 408 | kerning first=86 second=235 amount=-1 409 | kerning first=86 second=234 amount=-1 410 | kerning first=86 second=232 amount=-1 411 | kerning first=86 second=233 amount=-1 412 | kerning first=86 second=231 amount=-1 413 | kerning first=86 second=229 amount=-1 414 | kerning first=86 second=227 amount=-1 415 | kerning first=86 second=228 amount=-1 416 | kerning first=86 second=226 amount=-1 417 | kerning first=86 second=224 amount=-1 418 | kerning first=86 second=225 amount=-1 419 | kerning first=86 second=197 amount=-1 420 | kerning first=86 second=196 amount=-1 421 | kerning first=86 second=113 amount=-1 422 | kerning first=86 second=111 amount=-1 423 | kerning first=86 second=103 amount=-1 424 | kerning first=86 second=101 amount=-1 425 | kerning first=86 second=100 amount=-1 426 | kerning first=86 second=99 amount=-1 427 | kerning first=86 second=97 amount=-1 428 | kerning first=86 second=65 amount=-1 429 | kerning first=86 second=46 amount=-1 430 | kerning first=86 second=44 amount=-1 431 | kerning first=85 second=198 amount=-1 432 | kerning first=84 second=253 amount=-1 433 | kerning first=70 second=44 amount=-1 434 | kerning first=70 second=46 amount=-1 435 | kerning first=70 second=65 amount=-1 436 | kerning first=84 second=210 amount=-1 437 | kerning first=84 second=212 amount=-1 438 | kerning first=84 second=211 amount=-1 439 | kerning first=84 second=193 amount=-1 440 | kerning first=84 second=194 amount=-1 441 | kerning first=70 second=196 amount=-1 442 | kerning first=70 second=197 amount=-1 443 | kerning first=84 second=255 amount=-1 444 | kerning first=70 second=198 amount=-1 445 | kerning first=84 second=213 amount=-1 446 | kerning first=70 second=192 amount=-1 447 | kerning first=70 second=195 amount=-1 448 | kerning first=84 second=195 amount=-1 449 | kerning first=84 second=192 amount=-1 450 | kerning first=70 second=194 amount=-1 451 | kerning first=70 second=193 amount=-1 452 | kerning first=84 second=171 amount=-1 453 | kerning first=84 second=230 amount=-1 454 | kerning first=84 second=216 amount=-1 455 | kerning first=84 second=198 amount=-1 456 | kerning first=84 second=252 amount=-1 457 | kerning first=84 second=251 amount=-1 458 | kerning first=84 second=249 amount=-1 459 | kerning first=84 second=250 amount=-1 460 | kerning first=84 second=245 amount=-1 461 | kerning first=84 second=246 amount=-1 462 | kerning first=84 second=244 amount=-1 463 | kerning first=84 second=242 amount=-1 464 | kerning first=84 second=243 amount=-1 465 | kerning first=84 second=241 amount=-1 466 | kerning first=84 second=239 amount=1 467 | kerning first=84 second=235 amount=-1 468 | kerning first=84 second=234 amount=-1 469 | kerning first=84 second=232 amount=-1 470 | kerning first=84 second=233 amount=-1 471 | kerning first=84 second=231 amount=-1 472 | kerning first=84 second=229 amount=-1 473 | kerning first=84 second=227 amount=-1 474 | kerning first=84 second=228 amount=-1 475 | kerning first=84 second=226 amount=-1 476 | kerning first=84 second=224 amount=-1 477 | kerning first=84 second=225 amount=-1 478 | kerning first=84 second=214 amount=-1 479 | kerning first=84 second=199 amount=-1 480 | kerning first=84 second=197 amount=-1 481 | kerning first=74 second=44 amount=-1 482 | kerning first=74 second=46 amount=-1 483 | kerning first=84 second=196 amount=-1 484 | kerning first=84 second=122 amount=-1 485 | kerning first=84 second=121 amount=-1 486 | kerning first=84 second=120 amount=-1 487 | kerning first=84 second=119 amount=-1 488 | kerning first=84 second=118 amount=-1 489 | kerning first=84 second=117 amount=-1 490 | kerning first=84 second=115 amount=-1 491 | kerning first=84 second=114 amount=-1 492 | kerning first=84 second=113 amount=-1 493 | kerning first=84 second=112 amount=-1 494 | kerning first=74 second=198 amount=-1 495 | kerning first=84 second=111 amount=-1 496 | kerning first=84 second=110 amount=-1 497 | kerning first=84 second=109 amount=-1 498 | kerning first=84 second=103 amount=-1 499 | kerning first=84 second=102 amount=-1 500 | kerning first=84 second=101 amount=-1 501 | kerning first=84 second=100 amount=-1 502 | kerning first=84 second=99 amount=-1 503 | kerning first=84 second=97 amount=-1 504 | kerning first=84 second=81 amount=-1 505 | kerning first=84 second=79 amount=-1 506 | kerning first=84 second=74 amount=-1 507 | kerning first=84 second=71 amount=-1 508 | kerning first=84 second=67 amount=-1 509 | kerning first=84 second=65 amount=-1 510 | kerning first=84 second=46 amount=-1 511 | kerning first=84 second=44 amount=-1 512 | kerning first=81 second=84 amount=-1 513 | kerning first=81 second=46 amount=-1 514 | kerning first=81 second=44 amount=-1 515 | kerning first=80 second=193 amount=-1 516 | kerning first=75 second=67 amount=-1 517 | kerning first=75 second=71 amount=-1 518 | kerning first=75 second=74 amount=1 519 | kerning first=75 second=79 amount=-1 520 | kerning first=75 second=81 amount=-1 521 | kerning first=80 second=194 amount=-1 522 | kerning first=80 second=195 amount=-1 523 | kerning first=80 second=192 amount=-1 524 | kerning first=80 second=198 amount=-2 525 | kerning first=80 second=197 amount=-1 526 | kerning first=80 second=196 amount=-1 527 | kerning first=80 second=74 amount=-1 528 | kerning first=80 second=65 amount=-1 529 | kerning first=80 second=46 amount=-2 530 | kerning first=80 second=44 amount=-2 531 | kerning first=79 second=84 amount=-1 532 | kerning first=75 second=121 amount=-1 533 | kerning first=75 second=199 amount=-1 534 | kerning first=75 second=214 amount=-1 535 | kerning first=79 second=46 amount=-1 536 | kerning first=79 second=44 amount=-1 537 | kerning first=76 second=221 amount=-1 538 | kerning first=76 second=118 amount=-1 539 | kerning first=76 second=89 amount=-1 540 | kerning first=76 second=86 amount=-1 541 | kerning first=76 second=84 amount=-1 542 | kerning first=76 second=74 amount=1 543 | kerning first=76 second=63 amount=-1 544 | kerning first=76 second=42 amount=-1 545 | kerning first=75 second=253 amount=-1 546 | kerning first=75 second=210 amount=-1 547 | kerning first=75 second=216 amount=-1 548 | kerning first=75 second=213 amount=-1 549 | kerning first=75 second=212 amount=-1 550 | kerning first=75 second=211 amount=-1 551 | kerning first=75 second=255 amount=-1 552 | -------------------------------------------------------------------------------- /font.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import math 3 | import os 4 | import sys 5 | from PIL import Image 6 | 7 | fntfn = sys.argv[1] 8 | fontName = sys.argv[2] 9 | 10 | def readLine(l): 11 | l=l.split() 12 | carac={} 13 | if len(l)>0 and l[0]!="info": 14 | for w in l[1:]: 15 | v = w.split("=") 16 | carac[v[0]]=v[1].replace("\"","") 17 | return (l[0], carac) 18 | 19 | fontData=[] 20 | fontDesc={} 21 | 22 | def outputChar(p, c): 23 | global fontData, fontDesc, fontName 24 | im = p[c["page"]][1] 25 | x, y = int(c["x"]), int(c["y"]) 26 | w, h = int(c["width"]), int(c["height"]) 27 | xo, yo = int(c["xoffset"]), int(c["yoffset"]) 28 | id, xa = int(c["id"]), int(c["xadvance"]) 29 | if id<164: 30 | c = chr(id) 31 | c = c if c!="'" else "\\'" 32 | c = c if c!="\\" else "\\\\" 33 | c = "\'"+c+"\'" 34 | else: 35 | c = str(id) 36 | data = [] 37 | for i in range(w): 38 | data.extend([im.getpixel((x+i,y+h-1-j)) for j in range(h)]) 39 | fontDesc[id] = (" (charDesc_s){%s, %d, %d, %d, %d, %d, %d, %d, &%sData[%d]},"%(c,x,y,w,h,xo,yo,xa,fontName,len(fontData))) 40 | fontData.extend(data) 41 | 42 | def outputFontDesc(): 43 | global fontDesc 44 | for i in range(256): 45 | if i in fontDesc: 46 | print(fontDesc[i]) 47 | else: 48 | print(" (charDesc_s){0, 0, 0, 0, 0, 0, 0, 0, NULL},") 49 | 50 | def outputFontData(): 51 | global fontData, fontName 52 | print("u8 "+fontName+"Data[] = {"+"".join([hex(v)+", " for v in fontData])+"0x00};") 53 | 54 | f = open(fntfn, "r") 55 | pages = {} 56 | for l in f: 57 | l=readLine(l) 58 | if len(l)>0: 59 | if l[0]=="page": 60 | pages[l[1]["id"]]=(l[1]["file"], Image.open(l[1]["file"])) 61 | elif l[0]=="char": 62 | outputChar(pages, l[1]) 63 | 64 | 65 | print("#include <3ds.h>") 66 | print("#include \"font.h\"") 67 | # print("extern u8 fontData[];") 68 | # print("typedef struct {char c; int x, y, w, h, xo, yo, xa; u8* data;}charDesc_s;") 69 | print("charDesc_s "+fontName+"Desc[] = {") 70 | outputFontDesc() 71 | print("};") 72 | outputFontData() 73 | -------------------------------------------------------------------------------- /fontSmall.bmfc: -------------------------------------------------------------------------------- 1 | # AngelCode Bitmap Font Generator configuration file 2 | fileVersion=1 3 | 4 | # font settings 5 | fontName=Segoe UI 6 | fontFile= 7 | charSet=0 8 | fontSize=13 9 | aa=2 10 | scaleH=100 11 | useSmoothing=1 12 | isBold=0 13 | isItalic=0 14 | useUnicode=1 15 | disableBoxChars=1 16 | outputInvalidCharGlyph=0 17 | dontIncludeKerningPairs=0 18 | useHinting=1 19 | renderFromOutline=0 20 | useClearType=1 21 | 22 | # character alignment 23 | paddingDown=0 24 | paddingUp=0 25 | paddingRight=0 26 | paddingLeft=0 27 | spacingHoriz=1 28 | spacingVert=1 29 | useFixedHeight=0 30 | forceZero=0 31 | 32 | # output file 33 | outWidth=256 34 | outHeight=64 35 | outBitDepth=8 36 | fontDescFormat=0 37 | fourChnlPacked=0 38 | textureFormat=png 39 | textureCompression=0 40 | alphaChnl=0 41 | redChnl=4 42 | greenChnl=4 43 | blueChnl=4 44 | invA=0 45 | invR=0 46 | invG=0 47 | invB=0 48 | 49 | # outline 50 | outlineThickness=0 51 | 52 | # selected chars 53 | chars=32-126,160-255 54 | 55 | # imported icon images 56 | -------------------------------------------------------------------------------- /put3ds.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | WinSCP.com /command "option batch abort" "option confirm off" "open 3ds" "put %~dp0%1.3dsx /boot.3dsx" "exit" 3 | -------------------------------------------------------------------------------- /source/background.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "background.h" 5 | #include "water.h" 6 | #include "gfx.h" 7 | 8 | #include "logo_bin.h" 9 | #include "bubble_bin.h" 10 | 11 | //#define xstr(a) str(a) 12 | //#define str(a) #a 13 | 14 | #define BG_WATER_CONTROLPOINTS (100) 15 | #define BG_WATER_NEIGHBORHOODS (3) 16 | #define BG_WATER_DAMPFACTOR (0.7f) 17 | #define BG_WATER_SPRINGFACTOR (0.85f) 18 | #define BG_WATER_WIDTH (550) 19 | #define BG_WATER_OFFSET (-50) 20 | 21 | static bubble_t bubbles[BUBBLE_COUNT]; 22 | static waterEffect_s waterEffect; 23 | static int backgroundCnt; 24 | 25 | void initBackground(void) 26 | { 27 | int i = 0; 28 | for(i = 0;i < BUBBLE_COUNT;i += 1) 29 | { 30 | bubbles[i].x = rand() % 400; 31 | bubbles[i].y = rand() % 240; 32 | bubbles[i].fade = 15; 33 | } 34 | 35 | initWaterEffect(&waterEffect, BG_WATER_CONTROLPOINTS, BG_WATER_NEIGHBORHOODS, BG_WATER_DAMPFACTOR, BG_WATER_SPRINGFACTOR, BG_WATER_WIDTH, BG_WATER_OFFSET); 36 | backgroundCnt = 0; 37 | } 38 | 39 | void updateBubble(bubble_t* bubble) 40 | { 41 | // Float up the screen. 42 | bubble->y += 2; 43 | 44 | // Check if faded away, then reset if gone. 45 | if(bubble->fade < 10) 46 | { 47 | bubble->x = rand() % 400; 48 | bubble->y = rand() % 10; 49 | bubble->fade = 15; 50 | } 51 | // Check if too far up screen and start fizzling away. 52 | else if(bubble->y >= 240 && bubble->y % 240 > 100) 53 | { 54 | bubble->fade -= 10; 55 | } 56 | // Otherwise make sure the bubble is visible. 57 | else if(bubble->fade < 255) 58 | { 59 | bubble->fade += 10; 60 | } 61 | } 62 | 63 | void drawBubbles(void) 64 | { 65 | int i; 66 | //BUBBLES!! 67 | for(i = 0;i < BUBBLE_COUNT;i += 1) 68 | { 69 | // Then draw (no point in separating more because then we go through them all twice). 70 | gfxDrawSpriteAlphaBlendFade((bubbles[i].y >= 240) ? (GFX_TOP) : (GFX_BOTTOM), GFX_LEFT, (u8*)bubble_bin, 32, 32, 71 | ((bubbles[i].y >= 240) ? -64 : 0) + bubbles[i].y % 240, 72 | ((bubbles[i].y >= 240) ? 0 : -40) + bubbles[i].x, bubbles[i].fade); 73 | } 74 | } 75 | 76 | float randomFloat() 77 | { 78 | return (float)rand()/(float)(RAND_MAX); 79 | } 80 | 81 | void updateBackground(void) 82 | { 83 | int i; 84 | for(i = 0;i < BUBBLE_COUNT;i += 1) 85 | { 86 | // Update first 87 | updateBubble(&bubbles[i]); 88 | } 89 | 90 | exciteWater(&waterEffect, sin(backgroundCnt*0.1f)*2.0f, 0, true); 91 | 92 | //TODO : improve 93 | if((hidKeysDown()&KEY_UP) || hidKeysDown()&KEY_DOWN) 94 | { 95 | exciteWater(&waterEffect, 0.2f+randomFloat()*2.0f, rand()%BG_WATER_CONTROLPOINTS, false); 96 | }else if((hidKeysDown()&KEY_LEFT) || hidKeysDown()&KEY_RIGHT) 97 | { 98 | float v=3.0f+randomFloat()*1.0f; 99 | if(rand()%2)v=-v; 100 | int l=rand()%BG_WATER_CONTROLPOINTS; 101 | int i; for(i=0;i<5;i++)exciteWater(&waterEffect, v, l-2+i, false); 102 | } 103 | 104 | updateWaterEffect(&waterEffect); 105 | 106 | backgroundCnt++; 107 | } 108 | 109 | 110 | 111 | void drawBackground(u8 bgColor[3], u8 waterBorderColor[3], u8 waterColor[3]) 112 | { 113 | //top screen stuff 114 | //gfxFillColorGradient(GFX_TOP, GFX_LEFT, waterBorderColor, waterColor); 115 | gfxFillColor(GFX_TOP, GFX_LEFT, bgColor); 116 | gfxDrawWave(GFX_TOP, GFX_LEFT, waterBorderColor, waterColor, 135, 20, 5, (gfxWaveCallback)&evaluateWater, &waterEffect); 117 | gfxDrawWave(GFX_TOP, GFX_LEFT, waterColor, waterBorderColor, 130, 20, 0, (gfxWaveCallback)&evaluateWater, &waterEffect); 118 | 119 | //sub screen stuff 120 | gfxFillColorGradient(GFX_BOTTOM, GFX_LEFT, waterColor, waterBorderColor); 121 | 122 | // Bubbles belong on both screens so they should be drawn second to last. 123 | drawBubbles(); 124 | 125 | // Finally draw the logo. 126 | gfxDrawSpriteAlphaBlend(GFX_TOP, GFX_LEFT, (u8*)logo_bin, 113, 271, 64, 80); 127 | 128 | // draw version number 129 | gfxDrawText(GFX_TOP, GFX_LEFT, NULL, VERSION, 240 - 200, 300); 130 | 131 | } 132 | -------------------------------------------------------------------------------- /source/background.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include <3ds.h> 3 | 4 | #define WATERBORDERCOLOR (u8[]){66, 224, 255} 5 | #define WATERCOLOR (u8[]){66, 163, 255} 6 | 7 | #define BEERBORDERCOLOR (u8[]){240, 240, 240} 8 | #define BEERCOLOR (u8[]){188, 157, 75} 9 | 10 | #define BGCOLOR (u8[]){0, 132, 255} 11 | 12 | #define BUBBLE_COUNT 15 13 | 14 | typedef struct 15 | { 16 | s32 x, y; 17 | u8 fade; 18 | }bubble_t; 19 | 20 | void initBackground(void); 21 | void updateBackground(void); 22 | void drawBackground(u8 bgColor[3], u8 waterBorderColor[3], u8 waterColor[3]); 23 | -------------------------------------------------------------------------------- /source/boot.c: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mmap.h" 8 | #include "boot.h" 9 | #include "netloader.h" 10 | #include "filesystem.h" 11 | 12 | extern void (*__system_retAddr)(void); 13 | 14 | static Handle hbFileHandle; 15 | static u32 argbuffer[0x100]; 16 | static u32 mmap[0x40]; 17 | static u32 argbuffer_length = 0; 18 | 19 | // ninjhax 1.x 20 | void (*callBootloader_1x)(Handle hb, Handle file); 21 | void (*setArgs_1x)(u32* src, u32 length); 22 | 23 | static void launchFile_1x(void) 24 | { 25 | // jump to bootloader 26 | callBootloader_1x(0x00000000, hbFileHandle); 27 | } 28 | 29 | // ninjhax 2.0+ 30 | typedef struct 31 | { 32 | int processId; 33 | bool capabilities[0x10]; 34 | }processEntry_s; 35 | 36 | void (*callBootloader_2x)(Handle file, u32* argbuf, u32 arglength) = (void*)0x00100000; 37 | void (*callBootloaderNewProcess_2x)(int processId, u32* argbuf, u32 arglength) = (void*)0x00100008; 38 | void (*callBootloaderRunTitle_2x)(u8 mediatype, u32* argbuf, u32 argbuflength, u32 tid_low, u32 tid_high) = (void*)0x00100010; 39 | void (*callBootloaderRunTitleCustom_2x)(u8 mediatype, u32* argbuf, u32 argbuflength, u32 tid_low, u32 tid_high, memorymap_t* mmap) = (void*)0x00100014; 40 | void (*getBestProcess_2x)(u32 sectionSizes[3], bool* requirements, int num_requirements, processEntry_s* out, int out_size, int* out_len) = (void*)0x0010000C; 41 | 42 | int targetProcessId = -1; 43 | titleInfo_s target_title; 44 | bool custom_map = false; 45 | 46 | memorymap_t test_map = 47 | { 48 | { 49 | 3, 50 | 0x160000, 51 | 0x6ed000, 52 | 0x0, 53 | 0x500000, // processLinearOffset 54 | 0x1037e0, // processHookAddress 55 | 0x105000, // processAppCodeAddress 56 | 0x00182200, 0x00040000, 0x1, 57 | {false, false, false, false, false, 58 | false, false, false, false, false, false, false, false, false, false, false}, 59 | }, 60 | { 61 | {0x100000, 0x8000, 0x4f8000}, 62 | {0x5f8000, - 0xf0000, 0xf0000}, 63 | {0x6e8000, - 0xf5000, 0x5000}, 64 | } 65 | }; 66 | 67 | 68 | static void launchFile_2x(void) 69 | { 70 | // jump to bootloader 71 | if(targetProcessId == -1) callBootloader_2x(hbFileHandle, argbuffer, argbuffer_length); 72 | else if(targetProcessId == -2) 73 | { 74 | if(custom_map) callBootloaderRunTitleCustom_2x(target_title.mediatype, argbuffer, argbuffer_length, target_title.title_id & 0xffffffff, (target_title.title_id >> 32) & 0xffffffff, (memorymap_t*)&mmap); 75 | else callBootloaderRunTitle_2x(target_title.mediatype, argbuffer, argbuffer_length, target_title.title_id & 0xffffffff, (target_title.title_id >> 32) & 0xffffffff); 76 | }else callBootloaderNewProcess_2x(targetProcessId, argbuffer, argbuffer_length); 77 | } 78 | 79 | bool isNinjhax2(void) 80 | { 81 | Result ret = hbInit(); 82 | if(!ret) 83 | { 84 | hbExit(); 85 | return false; 86 | }else return true; 87 | } 88 | 89 | void bootSetTargetTitle(titleInfo_s info) 90 | { 91 | target_title = info; 92 | targetProcessId = -2; 93 | custom_map = false; 94 | 95 | static char path[256]; 96 | snprintf(path, 255, "sdmc:/mmap/%08X%08X.xml", (unsigned int)((target_title.title_id >> 32) & 0xffffffff), (unsigned int)(target_title.title_id & 0xffffffff)); 97 | memorymap_t* _mmap = loadMemoryMap(path); 98 | if(_mmap) 99 | { 100 | _mmap->header.processHookTidLow = target_title.title_id & 0xffffffff; 101 | _mmap->header.processHookTidHigh = (target_title.title_id >> 32) & 0xffffffff; 102 | _mmap->header.mediatype = target_title.mediatype; 103 | memcpy(mmap, _mmap, size_memmap(*_mmap)); 104 | free(_mmap); 105 | custom_map = true; 106 | } 107 | } 108 | 109 | int bootApp(char* executablePath, executableMetadata_s* em, char* arg) 110 | { 111 | // set argv/argc 112 | argbuffer[0] = 0; 113 | argbuffer_length = sizeof(argbuffer); 114 | 115 | if (!netloader_boot) 116 | { 117 | argbuffer[0] = 1; 118 | snprintf((char*)&argbuffer[1], sizeof(argbuffer) - 4, "sdmc:%s", executablePath); 119 | } 120 | 121 | { 122 | char *ptr = netloaded_commandline; 123 | char *dst = (char*)&argbuffer[1]; 124 | if (!netloader_boot) 125 | dst += strlen(dst) + 1; // skip first argument 126 | 127 | if(arg && *arg) 128 | { 129 | 130 | char c, *pstr, *str=arg, *endarg = arg+strlen(arg); 131 | 132 | do 133 | { 134 | do 135 | { 136 | c = *str++; 137 | } while ((c == ' ' || c == '\t') && str < endarg); 138 | 139 | pstr = str-1; 140 | 141 | if (c == '\"') 142 | { 143 | pstr++; 144 | while(*str++ != '\"' && str < endarg); 145 | } 146 | else 147 | if (c == '\'') 148 | { 149 | pstr++; 150 | while(*str++ != '\'' && str < endarg); 151 | } 152 | else 153 | { 154 | do 155 | { 156 | c = *str++; 157 | } while (c != ' ' && c != '\t' && str < endarg); 158 | } 159 | 160 | str--; 161 | 162 | if (str == (endarg - 1)) 163 | { 164 | if(*str == '\"' || *str == '\'') 165 | { 166 | *(str++) = 0; 167 | } 168 | else 169 | { 170 | str++; 171 | } 172 | } 173 | else 174 | { 175 | *(str++) = '\0'; 176 | } 177 | 178 | strcpy(dst, pstr); 179 | dst += strlen(dst) + 1; 180 | argbuffer[0]++; 181 | 182 | } while(strscanned && targetProcessId == -1) 227 | { 228 | // this is a really shitty implementation of what we should be doing 229 | // i'm really too lazy to do any better right now, but a good solution will come 230 | // (some day) 231 | processEntry_s out[4]; 232 | int out_len = 0; 233 | getBestProcess_2x(em->sectionSizes, (bool*)em->servicesThatMatter, NUM_SERVICESTHATMATTER, out, 4, &out_len); 234 | 235 | // temp : check if we got all the services we want 236 | if(em->servicesThatMatter[0] <= out[0].capabilities[0] && em->servicesThatMatter[1] <= out[0].capabilities[1] && em->servicesThatMatter[2] <= out[0].capabilities[2] && em->servicesThatMatter[3] <= out[0].capabilities[3] && em->servicesThatMatter[4] <= out[0].capabilities[4]) 237 | { 238 | targetProcessId = out[0].processId; 239 | }else{ 240 | // temp : if we didn't get everything we wanted, we search for the candidate that has as many highest-priority services as possible 241 | int i, j; 242 | int best_id = 0; 243 | int best_sum = 0; 244 | for(i=0; iservicesThatMatter[j] == 1) && out[i].capabilities[j]; 250 | } 251 | 252 | if(sum > best_sum) 253 | { 254 | best_id = i; 255 | best_sum = sum; 256 | } 257 | } 258 | targetProcessId = out[best_id].processId; 259 | } 260 | 261 | }else if(targetProcessId != -1) targetProcessId = -2; 262 | } 263 | } 264 | 265 | return 0; 266 | } 267 | -------------------------------------------------------------------------------- /source/boot.h: -------------------------------------------------------------------------------- 1 | #ifndef BOOT_H 2 | #define BOOT_H 3 | 4 | #include <3ds.h> 5 | #include "scanner.h" 6 | #include "titles.h" 7 | 8 | extern int targetProcessId; 9 | 10 | bool isNinjhax2(void); 11 | void bootSetTargetTitle(titleInfo_s info); 12 | int bootApp(char* executablePath, executableMetadata_s* em, char* arg); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /source/costable.c: -------------------------------------------------------------------------------- 1 | #include "costable.h" 2 | 3 | const s32 costable[] = { 4 | 4096, 4095, 4094, 4093, 4091, 4088, 4084, 4080, 5 | 4076, 4071, 4065, 4058, 4051, 4044, 4035, 4026, 6 | 4017, 4007, 3996, 3985, 3973, 3960, 3947, 3934, 7 | 3919, 3904, 3889, 3873, 3856, 3839, 3821, 3803, 8 | 3784, 3765, 3744, 3724, 3703, 3681, 3659, 3636, 9 | 3612, 3588, 3564, 3539, 3513, 3487, 3461, 3434, 10 | 3406, 3378, 3349, 3320, 3290, 3260, 3229, 3198, 11 | 3167, 3135, 3102, 3069, 3035, 3002, 2967, 2932, 12 | 2897, 2861, 2825, 2788, 2751, 2714, 2676, 2638, 13 | 2599, 2560, 2521, 2481, 2441, 2401, 2360, 2318, 14 | 2277, 2235, 2193, 2150, 2107, 2064, 2020, 1976, 15 | 1932, 1888, 1843, 1798, 1753, 1707, 1662, 1616, 16 | 1569, 1523, 1476, 1429, 1382, 1334, 1287, 1239, 17 | 1191, 1143, 1095, 1046, 997, 949, 900, 851, 18 | 801, 752, 703, 653, 603, 554, 504, 454, 19 | 404, 354, 304, 254, 204, 153, 103, 53, 20 | 3, -46, -97, -147, -197, -247, -297, -347, 21 | -398, -448, -497, -547, -597, -647, -696, -746, 22 | -795, -844, -893, -942, -991, -1040, -1088, -1137, 23 | -1185, -1233, -1281, -1328, -1376, -1423, -1470, -1517, 24 | -1563, -1610, -1656, -1701, -1747, -1792, -1837, -1882, 25 | -1927, -1971, -2015, -2058, -2102, -2144, -2187, -2229, 26 | -2271, -2313, -2354, -2395, -2436, -2476, -2516, -2555, 27 | -2594, -2633, -2671, -2709, -2747, -2784, -2820, -2857, 28 | -2892, -2928, -2963, -2997, -3031, -3065, -3098, -3130, 29 | -3163, -3194, -3225, -3256, -3286, -3316, -3345, -3374, 30 | -3402, -3430, -3457, -3484, -3510, -3536, -3561, -3585, 31 | -3609, -3633, -3656, -3678, -3700, -3721, -3742, -3762, 32 | -3782, -3801, -3819, -3837, -3854, -3871, -3887, -3902, 33 | -3917, -3932, -3946, -3959, -3971, -3983, -3995, -4005, 34 | -4016, -4025, -4034, -4042, -4050, -4057, -4064, -4070, 35 | -4075, -4080, -4084, -4087, -4090, -4092, -4094, -4095, 36 | -4095, -4095, -4094, -4093, -4091, -4088, -4085, -4081, 37 | -4076, -4071, -4066, -4059, -4052, -4045, -4036, -4028, 38 | -4018, -4008, -3997, -3986, -3974, -3962, -3949, -3935, 39 | -3921, -3906, -3891, -3875, -3858, -3841, -3824, -3805, 40 | -3787, -3767, -3747, -3727, -3705, -3684, -3662, -3639, 41 | -3615, -3592, -3567, -3542, -3517, -3491, -3464, -3437, 42 | -3409, -3381, -3353, -3324, -3294, -3264, -3233, -3202, 43 | -3171, -3139, -3106, -3073, -3040, -3006, -2972, -2937, 44 | -2902, -2866, -2830, -2793, -2756, -2719, -2681, -2643, 45 | -2604, -2565, -2526, -2486, -2446, -2406, -2365, -2324, 46 | -2282, -2240, -2198, -2156, -2113, -2069, -2026, -1982, 47 | -1938, -1894, -1849, -1804, -1759, -1713, -1668, -1622, 48 | -1575, -1529, -1482, -1435, -1388, -1341, -1293, -1245, 49 | -1197, -1149, -1101, -1052, -1004, -955, -906, -857, 50 | -808, -758, -709, -660, -610, -560, -510, -460, 51 | -411, -360, -310, -260, -210, -160, -110, -60, 52 | -9, 40, 90, 140, 191, 241, 291, 341, 53 | 391, 441, 491, 541, 591, 640, 690, 739, 54 | 789, 838, 887, 936, 985, 1033, 1082, 1130, 55 | 1179, 1227, 1274, 1322, 1370, 1417, 1464, 1511, 56 | 1557, 1604, 1650, 1695, 1741, 1786, 1831, 1876, 57 | 1921, 1965, 2009, 2053, 2096, 2139, 2182, 2224, 58 | 2266, 2308, 2349, 2390, 2431, 2471, 2511, 2550, 59 | 2589, 2628, 2666, 2704, 2742, 2779, 2816, 2852, 60 | 2888, 2923, 2958, 2993, 3027, 3060, 3093, 3126, 61 | 3158, 3190, 3221, 3252, 3282, 3312, 3342, 3370, 62 | 3399, 3426, 3454, 3480, 3507, 3532, 3557, 3582, 63 | 3606, 3630, 3653, 3675, 3697, 3718, 3739, 3759, 64 | 3779, 3798, 3817, 3835, 3852, 3869, 3885, 3900, 65 | 3915, 3930, 3944, 3957, 3970, 3982, 3993, 4004, 66 | 4014, 4024, 4033, 4041, 4049, 4056, 4063, 4069, 67 | 4074, 4079, 4083, 4087, 4090, 4092, 4094, 4095 68 | }; 69 | -------------------------------------------------------------------------------- /source/costable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include <3ds/types.h> 4 | 5 | extern const s32 costable[]; 6 | 7 | static inline s32 pcCos(u16 v) 8 | { 9 | return costable[v&0x1FF]; 10 | } 11 | -------------------------------------------------------------------------------- /source/cppsupport.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" void __cxa_pure_virtual() 4 | { 5 | } 6 | 7 | void* operator new(size_t size) { return malloc(size); } 8 | void operator delete(void *p) { free(p); } 9 | void* operator new[](size_t size) { return malloc(size); } 10 | void operator delete[](void *p) { free(p); } 11 | -------------------------------------------------------------------------------- /source/descriptor.cpp: -------------------------------------------------------------------------------- 1 | #include "descriptor.h" 2 | #include "tinyxml2.h" 3 | 4 | using namespace tinyxml2; 5 | 6 | void initDescriptor(descriptor_s* d) 7 | { 8 | if(!d)return; 9 | 10 | d->targetTitles = NULL; 11 | d->numTargetTitles = 0; 12 | 13 | d->requestedServices = NULL; 14 | d->numRequestedServices = 0; 15 | 16 | d->selectTargetProcess = false; 17 | d->autodetectServices = true; 18 | 19 | initMetadata(&d->executableMetadata); 20 | } 21 | 22 | // TODO : error checking 23 | void loadDescriptor(descriptor_s* d, char* path) 24 | { 25 | if(!d || !path)return; 26 | 27 | XMLDocument doc; 28 | if(doc.LoadFile(path))return; 29 | 30 | XMLElement* targets = doc.FirstChildElement("targets"); 31 | if(targets) 32 | { 33 | // grab selectable target flag (default to false) 34 | { 35 | if(targets->QueryBoolAttribute("selectable", &d->selectTargetProcess)) d->selectTargetProcess = false; 36 | } 37 | 38 | // grab preferred target titles 39 | { 40 | d->numTargetTitles = 0; 41 | for (tinyxml2::XMLElement* child = targets->FirstChildElement(); child != NULL; child = child->NextSiblingElement()) 42 | { 43 | if(!strcmp(child->Name(), "title")) 44 | { 45 | d->numTargetTitles++; 46 | } 47 | } 48 | 49 | d->targetTitles = (targetTitle_s*)malloc(sizeof(targetTitle_s) * d->numTargetTitles); 50 | d->numTargetTitles = 0; 51 | 52 | for (tinyxml2::XMLElement* child = targets->FirstChildElement(); child != NULL; child = child->NextSiblingElement()) 53 | { 54 | if(!strcmp(child->Name(), "title")) 55 | { 56 | // SD is default mediatype 57 | int mediatype; 58 | if(child->QueryIntAttribute("mediatype", &mediatype))mediatype = 1; 59 | 60 | d->targetTitles[d->numTargetTitles].tid = strtoull(child->GetText(), NULL, 16); 61 | d->targetTitles[d->numTargetTitles].mediatype = mediatype; 62 | 63 | d->numTargetTitles++; 64 | } 65 | } 66 | } 67 | } 68 | 69 | XMLElement* services = doc.FirstChildElement("services"); 70 | if(services) 71 | { 72 | // grab "autodetect services" flag (default to true) 73 | { 74 | if(services->QueryBoolAttribute("autodetect", &d->autodetectServices)) d->autodetectServices = true; 75 | } 76 | 77 | // grab requested services 78 | { 79 | d->numRequestedServices = 0; 80 | for (tinyxml2::XMLElement* child = services->FirstChildElement(); child != NULL; child = child->NextSiblingElement()) 81 | { 82 | if(!strcmp(child->Name(), "request")) 83 | { 84 | d->numRequestedServices++; 85 | } 86 | } 87 | 88 | d->requestedServices = (serviceRequest_s*)malloc(sizeof(serviceRequest_s) * d->numRequestedServices); 89 | d->numRequestedServices = 0; 90 | 91 | for (tinyxml2::XMLElement* child = services->FirstChildElement(); child != NULL; child = child->NextSiblingElement()) 92 | { 93 | if(!strcmp(child->Name(), "request")) 94 | { 95 | // 1 (highest) is default priority 96 | if(child->QueryIntAttribute("priority", &d->requestedServices[d->numRequestedServices].priority))d->requestedServices[d->numRequestedServices].priority = 1; 97 | 98 | strncpy(d->requestedServices[d->numRequestedServices].name, child->GetText(), 9); 99 | 100 | d->numRequestedServices++; 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | void freeDescriptor(descriptor_s* d) 108 | { 109 | if(!d)return; 110 | 111 | if(d->targetTitles) 112 | { 113 | free(d->targetTitles); 114 | d->targetTitles = NULL; 115 | } 116 | 117 | if(d->requestedServices) 118 | { 119 | free(d->requestedServices); 120 | d->requestedServices = NULL; 121 | } 122 | 123 | initDescriptor(d); 124 | } 125 | -------------------------------------------------------------------------------- /source/descriptor.h: -------------------------------------------------------------------------------- 1 | #ifndef DESCRIPTOR_H 2 | #define DESCRIPTOR_H 3 | 4 | #include <3ds.h> 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #include "scanner.h" 11 | 12 | typedef struct 13 | { 14 | u64 tid; 15 | u8 mediatype; 16 | }targetTitle_s; 17 | 18 | typedef struct 19 | { 20 | char name[9]; 21 | int priority; 22 | }serviceRequest_s; 23 | 24 | typedef struct 25 | { 26 | targetTitle_s* targetTitles; 27 | u32 numTargetTitles; 28 | 29 | serviceRequest_s *requestedServices; 30 | u32 numRequestedServices; 31 | 32 | bool selectTargetProcess; 33 | bool autodetectServices; 34 | 35 | executableMetadata_s executableMetadata; 36 | }descriptor_s; 37 | 38 | void initDescriptor(descriptor_s* d); 39 | void freeDescriptor(descriptor_s* d); 40 | void loadDescriptor(descriptor_s* d, char* path); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /source/error.c: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | 3 | #include "error.h" 4 | #include "menu.h" 5 | #include "text.h" 6 | 7 | u8 roundLutError[]={8, 5, 4, 3, 2, 1, 1, 1, 0}; 8 | 9 | int countLines(char* str) 10 | { 11 | if(!str)return 0; 12 | int cnt; for(cnt=1;*str=='\n'?++cnt:*str;str++); 13 | return cnt; 14 | } 15 | 16 | void initErrors() 17 | { 18 | //placeholder 19 | } 20 | 21 | void drawError(gfxScreen_t screen, char* title, char* body, int offset) 22 | { 23 | int i; 24 | 25 | int numLines=countLines(body); 26 | 27 | int width=numLines*8+32; 28 | int height=300; 29 | int x=240-width-12+offset, y=4; 30 | 31 | //main frame 32 | for(i=0; i<9; i++)gfxDrawRectangle(screen, GFX_LEFT, ENTRY_BGCOLOR, x+roundLutError[i], y+i, width-roundLutError[i]*2, 1); 33 | gfxDrawRectangle(screen, GFX_LEFT, ENTRY_BGCOLOR, x, y+9, width, height-9*2); 34 | for(i=0; i<9; i++)gfxDrawRectangle(screen, GFX_LEFT, ENTRY_BGCOLOR, x+roundLutError[i], y+height-1-i, width-roundLutError[i]*2, 1); 35 | 36 | //content 37 | gfxDrawText(screen, GFX_LEFT, &fontTitle, title, x+width-6-16, y+6); 38 | gfxDrawText(screen, GFX_LEFT, &fontDescription, body, x+width-5-16-13, y+8); 39 | } 40 | -------------------------------------------------------------------------------- /source/error.h: -------------------------------------------------------------------------------- 1 | #ifndef ERROR_H 2 | #define ERROR_H 3 | 4 | #include <3ds.h> 5 | 6 | void initErrors(); 7 | void drawError(gfxScreen_t screen, char* title, char* body, int offset); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /source/filesystem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include <3ds.h> 4 | 5 | #include "installerIcon_bin.h" 6 | #include "folderIcon_bin.h" 7 | 8 | #include "filesystem.h" 9 | #include "smdh.h" 10 | #include "utils.h" 11 | 12 | #define DEFAULT_DIRECTORY "/3ds/" 13 | 14 | static char cwd[1024] = DEFAULT_DIRECTORY; 15 | 16 | FS_Archive sdmcArchive; 17 | 18 | // File header 19 | #define _3DSX_MAGIC 0x58534433 // '3DSX' 20 | typedef struct 21 | { 22 | u32 magic; 23 | u16 headerSize, relocHdrSize; 24 | u32 formatVer; 25 | u32 flags; 26 | 27 | // Sizes of the code, rodata and data segments + 28 | // size of the BSS section (uninitialized latter half of the data segment) 29 | u32 codeSegSize, rodataSegSize, dataSegSize, bssSize; 30 | // offset and size of smdh 31 | u32 smdhOffset, smdhSize; 32 | // offset to filesystem 33 | u32 fsOffset; 34 | } _3DSX_Header; 35 | 36 | void initFilesystem(void) 37 | { 38 | fsInit(); 39 | sdmcInit(); 40 | } 41 | 42 | void exitFilesystem(void) 43 | { 44 | sdmcExit(); 45 | fsExit(); 46 | } 47 | 48 | void openSDArchive() 49 | { 50 | FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, (FS_Path){PATH_EMPTY, 1, (u8*)""}); 51 | } 52 | 53 | void closeSDArchive() 54 | { 55 | FSUSER_CloseArchive(sdmcArchive); 56 | } 57 | 58 | int loadFile(char* path, void* dst, FS_Archive* archive, u64 maxSize) 59 | { 60 | if(!path || !dst || !archive)return -1; 61 | 62 | u64 size; 63 | u32 bytesRead; 64 | Result ret; 65 | Handle fileHandle; 66 | 67 | ret=FSUSER_OpenFile(&fileHandle, *archive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0); 68 | if(ret!=0)return ret; 69 | 70 | ret=FSFILE_GetSize(fileHandle, &size); 71 | if(ret!=0)goto loadFileExit; 72 | if(size>maxSize){ret=-2; goto loadFileExit;} 73 | 74 | ret=FSFILE_Read(fileHandle, &bytesRead, 0x0, dst, size); 75 | if(ret!=0)goto loadFileExit; 76 | if(bytesRead smdhPath; --p) { 93 | if(*p == '.') { 94 | /* this should always be true */ 95 | if(strcmp(p, ".3dsx") == 0) { 96 | static smdh_s smdh; 97 | 98 | u32 bytesRead; 99 | Result ret; 100 | Handle fileHandle; 101 | bool gotsmdh = false; 102 | 103 | _3DSX_Header header; 104 | 105 | // first check for embedded smdh 106 | ret = FSUSER_OpenFile(&fileHandle, sdmcArchive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0); 107 | if (ret == 0) 108 | { 109 | ret=FSFILE_Read(fileHandle, &bytesRead, 0x0, &header, sizeof(header)); 110 | if (ret == 0 && bytesRead == sizeof(header)) 111 | { 112 | if (header.headerSize >= 44 ) 113 | { 114 | ret=FSFILE_Read(fileHandle, &bytesRead, header.smdhOffset, &smdh, sizeof(smdh)); 115 | if (ret == 0 && bytesRead == sizeof(smdh)) gotsmdh = true; 116 | } 117 | } 118 | FSFILE_Close(fileHandle); 119 | } 120 | 121 | if (!gotsmdh) { 122 | strcpy(p, ".smdh"); 123 | if(fileExists(smdhPath, &sdmcArchive)) { 124 | if(!loadFile(smdhPath, &smdh, &sdmcArchive, sizeof(smdh))) gotsmdh = true; 125 | } 126 | } 127 | if (!gotsmdh) { 128 | strcpy(p, ".icn"); 129 | if(fileExists(smdhPath, &sdmcArchive)) { 130 | if(!loadFile(smdhPath, &smdh, &sdmcArchive, sizeof(smdh))) gotsmdh = true; 131 | } 132 | } 133 | if (!gotsmdh && fnamep && isAppFolder) { 134 | strcpy(fnamep, "icon.smdh"); 135 | if(fileExists(smdhPath, &sdmcArchive)) { 136 | if(!loadFile(smdhPath, &smdh, &sdmcArchive, sizeof(smdh))) gotsmdh = true; 137 | } 138 | } 139 | if (!gotsmdh && fnamep && isAppFolder) { 140 | strcpy(fnamep, "icon.icn"); 141 | if(fileExists(smdhPath, &sdmcArchive)) { 142 | if(!loadFile(smdhPath, &smdh, &sdmcArchive, sizeof(smdh))) gotsmdh = true; 143 | } 144 | } 145 | 146 | if (gotsmdh) extractSmdhData(&smdh, entry->name, entry->description, entry->author, entry->iconData); 147 | 148 | } 149 | } else if(*p == '/') { 150 | fnamep = p+1; 151 | } 152 | } 153 | } 154 | 155 | bool fileExists(char* path, FS_Archive* archive) 156 | { 157 | if(!path || !archive)return false; 158 | 159 | Result ret; 160 | Handle fileHandle; 161 | 162 | ret=FSUSER_OpenFile(&fileHandle, *archive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0); 163 | if(ret!=0)return false; 164 | 165 | ret=FSFILE_Close(fileHandle); 166 | if(ret!=0)return false; 167 | 168 | return true; 169 | } 170 | 171 | extern int debugValues[100]; 172 | 173 | void addExecutableToMenu(menu_s* m, char* execPath) 174 | { 175 | if(!m || !execPath)return; 176 | 177 | static menuEntry_s tmpEntry; 178 | 179 | if(!fileExists(execPath, &sdmcArchive))return; 180 | 181 | int i, l=-1; for(i=0; execPath[i]; i++) if(execPath[i]=='/')l=i; 182 | 183 | initMenuEntry(&tmpEntry, execPath, &execPath[l+1], execPath, "Unknown publisher", (u8*)installerIcon_bin, MENU_ENTRY_FILE); 184 | 185 | loadSmdh(&tmpEntry, execPath, false); 186 | 187 | static char xmlPath[128]; 188 | snprintf(xmlPath, 128, "%s", execPath); 189 | l = strlen(xmlPath); 190 | xmlPath[l-1] = 0; 191 | xmlPath[l-2] = 'l'; 192 | xmlPath[l-3] = 'm'; 193 | xmlPath[l-4] = 'x'; 194 | if(fileExists(xmlPath, &sdmcArchive)) loadDescriptor(&tmpEntry.descriptor, xmlPath); 195 | 196 | addMenuEntryCopy(m, &tmpEntry); 197 | } 198 | 199 | void addDirectoryToMenu(menu_s* m, char* path) 200 | { 201 | if(!m || !path)return; 202 | 203 | static menuEntry_s tmpEntry; 204 | static char execPath[128]; 205 | static char xmlPath[128]; 206 | 207 | int i, l=-1; for(i=0; path[i]; i++) if(path[i]=='/') l=i; 208 | 209 | // Check for old-style application folder 210 | bool exists = false; 211 | if (strcmp(cwd, DEFAULT_DIRECTORY)==0) 212 | { 213 | snprintf(execPath, 128, "%s/boot.3dsx", path); 214 | exists = fileExists(execPath, &sdmcArchive); 215 | if (!exists) 216 | { 217 | snprintf(execPath, 128, "%s/%s.3dsx", path, &path[l+1]); 218 | exists = fileExists(execPath, &sdmcArchive); 219 | } 220 | } 221 | if (exists) 222 | { 223 | // Add the application 224 | initMenuEntry(&tmpEntry, execPath, &path[l+1], execPath, "Unknown publisher", (u8*)installerIcon_bin, MENU_ENTRY_FILE); 225 | 226 | // Load the icon 227 | loadSmdh(&tmpEntry, execPath, true); 228 | 229 | // Load the descriptor 230 | snprintf(xmlPath, 128, "%s/descriptor.xml", path); 231 | if(!fileExists(xmlPath, &sdmcArchive))snprintf(xmlPath, 128, "%s/%s.xml", path, &path[l+1]); 232 | loadDescriptor(&tmpEntry.descriptor, xmlPath); 233 | } else 234 | { 235 | // This is a normal folder 236 | initMenuEntry(&tmpEntry, path, &path[l+1], path, "", (u8*)folderIcon_bin, MENU_ENTRY_FOLDER); 237 | } 238 | 239 | addMenuEntryCopy(m, &tmpEntry); 240 | } 241 | 242 | // should go in menu.c ? 243 | void createMenuEntryShortcut(menu_s* m, shortcut_s* s) 244 | { 245 | if(!m || !s)return; 246 | 247 | static menuEntry_s tmpEntry; 248 | static smdh_s tmpSmdh; 249 | 250 | char* execPath = s->executable; 251 | 252 | if(!fileExists(execPath, &sdmcArchive))return; 253 | 254 | int i, l=-1; for(i=0; execPath[i]; i++) if(execPath[i]=='/') l=i; 255 | 256 | char* iconPath = s->icon; 257 | int ret = loadFile(iconPath, &tmpSmdh, &sdmcArchive, sizeof(smdh_s)); 258 | 259 | if(!ret) 260 | { 261 | initEmptyMenuEntry(&tmpEntry); 262 | ret = extractSmdhData(&tmpSmdh, tmpEntry.name, tmpEntry.description, tmpEntry.author, tmpEntry.iconData); 263 | strncpy(tmpEntry.executablePath, execPath, ENTRY_PATHLENGTH); 264 | } 265 | else 266 | { 267 | initMenuEntry(&tmpEntry, execPath, &execPath[l+1], execPath, "Unknown publisher", (u8*)installerIcon_bin, MENU_ENTRY_FILE); 268 | loadSmdh(&tmpEntry, execPath, false); 269 | } 270 | 271 | if(s->name) strncpy(tmpEntry.name, s->name, ENTRY_NAMELENGTH); 272 | if(s->description) strncpy(tmpEntry.description, s->description, ENTRY_DESCLENGTH); 273 | if(s->author) strncpy(tmpEntry.author, s->author, ENTRY_AUTHORLENGTH); 274 | 275 | if(s->arg) 276 | { 277 | strncpy(tmpEntry.arg, s->arg, ENTRY_ARGLENGTH); 278 | } 279 | 280 | if(fileExists(s->descriptor, &sdmcArchive)) loadDescriptor(&tmpEntry.descriptor, s->descriptor); 281 | 282 | addMenuEntryCopyAt(m, &tmpEntry, 1); 283 | } 284 | 285 | void addShortcutToMenu(menu_s* m, char* shortcutPath) 286 | { 287 | if(!m || !shortcutPath)return; 288 | 289 | static shortcut_s tmpShortcut; 290 | 291 | Result ret = createShortcut(&tmpShortcut, shortcutPath); 292 | if(!ret) createMenuEntryShortcut(m, &tmpShortcut); 293 | 294 | freeShortcut(&tmpShortcut); 295 | } 296 | 297 | void scanHomebrewDirectory(menu_s* m) 298 | { 299 | Handle dirHandle; 300 | FS_Path dirPath=fsMakePath(PATH_ASCII, cwd); 301 | FSUSER_OpenDirectory(&dirHandle, sdmcArchive, dirPath); 302 | 303 | static char fullPath[1024]; 304 | u32 entriesRead; 305 | do 306 | { 307 | static FS_DirectoryEntry entry; 308 | memset(&entry,0,sizeof(FS_DirectoryEntry)); 309 | entriesRead=0; 310 | FSDIR_Read(dirHandle, &entriesRead, 1, &entry); 311 | if(entriesRead) 312 | { 313 | strncpy(fullPath, cwd, 1024); 314 | int n=strlen(fullPath); 315 | unicodeToChar(&fullPath[n], entry.name, 1024-n); 316 | if(entry.attributes & FS_ATTRIBUTE_DIRECTORY) //directories 317 | { 318 | addDirectoryToMenu(m, fullPath); 319 | }else{ //stray executables and shortcuts 320 | n=strlen(fullPath); 321 | if(n>5 && !strcmp(".3dsx", &fullPath[n-5]))addExecutableToMenu(m, fullPath); 322 | if(n>4 && !strcmp(".xml", &fullPath[n-4]))addShortcutToMenu(m, fullPath); 323 | } 324 | } 325 | }while(entriesRead); 326 | 327 | FSDIR_Close(dirHandle); 328 | 329 | sortMenu(m); 330 | } 331 | 332 | void changeDirectory(const char* path) 333 | { 334 | if(strcmp(path, "..") == 0) { 335 | char *p = cwd + strlen(cwd)-2; 336 | while(p > cwd && *p != '/') *p-- = 0; 337 | } 338 | else { 339 | strncpy(cwd, path, sizeof(cwd)); 340 | strcat(cwd, "/"); 341 | } 342 | } 343 | 344 | void printDirectory(void) 345 | { 346 | gfxDrawText(GFX_TOP, GFX_LEFT, NULL, cwd, 10, 10); 347 | } 348 | -------------------------------------------------------------------------------- /source/filesystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include <3ds.h> 3 | #include "menu.h" 4 | #include "shortcut.h" 5 | 6 | extern FS_Archive sdmcArchive; 7 | 8 | //system stuff 9 | void initFilesystem(void); 10 | void exitFilesystem(void); 11 | 12 | void openSDArchive(); 13 | void closeSDArchive(); 14 | 15 | //general fs stuff 16 | int loadFile(char* path, void* dst, FS_Archive* archive, u64 maxSize); 17 | bool fileExists(char* path, FS_Archive* archive); 18 | 19 | //menu fs stuff 20 | void addDirectoryToMenu(menu_s* m, char* path); 21 | void scanHomebrewDirectory(menu_s* m); 22 | void changeDirectory(const char* path); 23 | void printDirectory(void); 24 | 25 | //shortcut menu stuff 26 | void createMenuEntryShortcut(menu_s* m, shortcut_s* s); 27 | -------------------------------------------------------------------------------- /source/font.c: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | #include "font.h" 3 | 4 | font_s fontDefault = 5 | { 6 | font1Data, 7 | font1Desc, 8 | 16, 9 | (u8[]){0xFF,0xFF,0xFF} 10 | }; 11 | 12 | font_s fontTitle = 13 | { 14 | font1Data, 15 | font1Desc, 16 | 16, 17 | (u8[]){0x76,0x76,0x76} 18 | }; 19 | 20 | font_s fontDescription = 21 | { 22 | font2Data, 23 | font2Desc, 24 | 13, 25 | (u8[]){0x76,0x76,0x76} 26 | }; 27 | -------------------------------------------------------------------------------- /source/font.h: -------------------------------------------------------------------------------- 1 | #ifndef FONT_H 2 | #define FONT_H 3 | 4 | typedef struct {char c; int x, y, w, h, xo, yo, xa; u8* data;}charDesc_s; 5 | typedef struct 6 | { 7 | u8* data; 8 | charDesc_s* desc; 9 | u8 height; 10 | u8 color[3]; 11 | }font_s; 12 | 13 | extern u8 font1Data[]; 14 | extern charDesc_s font1Desc[]; 15 | 16 | extern u8 font2Data[]; 17 | extern charDesc_s font2Desc[]; 18 | 19 | extern font_s fontDefault; 20 | extern font_s fontTitle; 21 | extern font_s fontDescription; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /source/font1.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/source/font1.c -------------------------------------------------------------------------------- /source/font2.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smealum/3ds_hb_menu/c72b1f2ae11bfcefaffabef4be57fa2d22456a48/source/font2.c -------------------------------------------------------------------------------- /source/gfx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include <3ds.h> 5 | 6 | #include "gfx.h" 7 | #include "font.h" 8 | #include "text.h" 9 | #include "costable.h" 10 | 11 | void gfxDrawText(gfxScreen_t screen, gfx3dSide_t side, font_s* f, char* str, s16 x, s16 y) 12 | { 13 | if(!str)return; 14 | if(!f)f=&fontDefault; 15 | 16 | u16 fbWidth, fbHeight; 17 | u8* fbAdr=gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); 18 | 19 | drawString(fbAdr, f, str, y, x, fbHeight, fbWidth); 20 | } 21 | 22 | void gfxDrawTextN(gfxScreen_t screen, gfx3dSide_t side, font_s* f, char* str, u16 length, s16 x, s16 y) 23 | { 24 | if(!str)return; 25 | if(!f)f=&fontDefault; 26 | 27 | u16 fbWidth, fbHeight; 28 | u8* fbAdr=gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); 29 | 30 | drawStringN(fbAdr, f, str, length, y, x, fbHeight, fbWidth); 31 | } 32 | 33 | void gfxDrawSprite(gfxScreen_t screen, gfx3dSide_t side, u8* spriteData, u16 width, u16 height, s16 x, s16 y) 34 | { 35 | if(!spriteData)return; 36 | 37 | u16 fbWidth, fbHeight; 38 | u8* fbAdr=gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); 39 | 40 | if(x+width<0 || x>=fbWidth)return; 41 | if(y+height<0 || y>=fbHeight)return; 42 | 43 | u16 xOffset=0, yOffset=0; 44 | u16 widthDrawn=width, heightDrawn=height; 45 | 46 | if(x<0)xOffset=-x; 47 | if(y<0)yOffset=-y; 48 | if(x+width>=fbWidth)widthDrawn=fbWidth-x; 49 | if(y+height>=fbHeight)heightDrawn=fbHeight-y; 50 | widthDrawn-=xOffset; 51 | heightDrawn-=yOffset; 52 | 53 | int j; 54 | for(j=yOffset; j=fbWidth)return; 76 | if(y+height<0 || y>=fbHeight)return; 77 | 78 | u16 xOffset=0, yOffset=0; 79 | u16 widthDrawn=width, heightDrawn=height; 80 | 81 | if(x<0)xOffset=-x; 82 | if(y<0)yOffset=-y; 83 | if(x+width>=fbWidth)widthDrawn=fbWidth-x; 84 | if(y+height>=fbHeight)heightDrawn=fbHeight-y; 85 | widthDrawn-=xOffset; 86 | heightDrawn-=yOffset; 87 | 88 | //TODO : optimize 89 | fbAdr+=(y+yOffset)*fbWidth*3; 90 | spriteData+=yOffset*width*4; 91 | int j, i; 92 | for(j=yOffset; j=fbWidth)return; 119 | if(y+height<0 || y>=fbHeight)return; 120 | 121 | u16 xOffset=0, yOffset=0; 122 | u16 widthDrawn=width, heightDrawn=height; 123 | 124 | if(x<0)xOffset=-x; 125 | if(y<0)yOffset=-y; 126 | if(x+width>=fbWidth)widthDrawn=fbWidth-x; 127 | if(y+height>=fbHeight)heightDrawn=fbHeight-y; 128 | widthDrawn-=xOffset; 129 | heightDrawn-=yOffset; 130 | 131 | //TODO : optimize 132 | fbAdr+=(y+yOffset)*fbWidth*3; 133 | spriteData+=yOffset*width*4; 134 | int j, i; 135 | for(j=yOffset; j=fbWidth)return; 163 | if(y+height<0 || y>=fbHeight)return; 164 | 165 | u16 xOffset=0, yOffset=0; 166 | u16 widthDrawn=width, heightDrawn=height; 167 | 168 | if(x<0)xOffset=-x; 169 | if(y<0)yOffset=-y; 170 | if(x+width>=fbWidth)widthDrawn=fbWidth-x; 171 | if(y+height>=fbHeight)heightDrawn=fbHeight-y; 172 | widthDrawn-=xOffset; 173 | heightDrawn-=yOffset; 174 | 175 | //TODO : optimize 176 | fbAdr+=(y+yOffset)*fbWidth*3; 177 | spriteData+=yOffset*width*4; 178 | int j, i; 179 | for(j=yOffset; j=fbWidth)return; 247 | if(y+height<0 || y>=fbHeight)return; 248 | 249 | if(x<0){width+=x; x=0;} 250 | if(y<0){height+=y; y=0;} 251 | if(x+width>=fbWidth)width=fbWidth-x; 252 | if(y+height>=fbHeight)height=fbHeight-y; 253 | 254 | u8 colorLine[width*3]; 255 | 256 | int j; 257 | for(j=0; j>8;fbAdr++; 280 | *fbAdr=(*fbAdr*f)>>8;fbAdr++; 281 | *fbAdr=(*fbAdr*f)>>8;fbAdr++; 282 | *fbAdr=(*fbAdr*f)>>8;fbAdr++; 283 | *fbAdr=(*fbAdr*f)>>8;fbAdr++; 284 | *fbAdr=(*fbAdr*f)>>8;fbAdr++; 285 | } 286 | } 287 | 288 | void gfxDrawWave(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColorStart[3], u8 rgbColorEnd[3], u16 level, u16 amplitude, u16 width, gfxWaveCallback cb, void* p) 289 | { 290 | u16 fbWidth, fbHeight; 291 | u8* fbAdr=gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); 292 | 293 | u8 colorLine[fbWidth*3]; 294 | 295 | int j; 296 | 297 | if(width) 298 | { 299 | for(j=0; j 3 | #include "font.h" 4 | 5 | typedef float (*gfxWaveCallback)(void* p, u16 x); 6 | 7 | //rendering stuff 8 | void gfxFadeScreen(gfxScreen_t screen, gfx3dSide_t side, u32 f); 9 | void gfxDrawSprite(gfxScreen_t screen, gfx3dSide_t side, u8* spriteData, u16 width, u16 height, s16 x, s16 y); 10 | void gfxDrawDualSprite(u8* spriteData, u16 width, u16 height, s16 x, s16 y); 11 | void gfxDrawSpriteAlpha(gfxScreen_t screen, gfx3dSide_t side, u8* spriteData, u16 width, u16 height, s16 x, s16 y); 12 | void gfxDrawSpriteAlphaBlend(gfxScreen_t screen, gfx3dSide_t side, u8* spriteData, u16 width, u16 height, s16 x, s16 y); 13 | void gfxDrawSpriteAlphaBlendFade(gfxScreen_t screen, gfx3dSide_t side, u8* spriteData, u16 width, u16 height, s16 x, s16 y, u8 fadeValue); 14 | void gfxDrawText(gfxScreen_t screen, gfx3dSide_t side, font_s* f, char* str, s16 x, s16 y); 15 | void gfxDrawTextN(gfxScreen_t screen, gfx3dSide_t side, font_s* f, char* str, u16 length, s16 x, s16 y); 16 | void gfxFillColor(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[3]); 17 | void gfxFillColorGradient(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColorStart[3], u8 rgbColorEnd[3]); 18 | void gfxDrawRectangle(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[3], s16 x, s16 y, u16 width, u16 height); 19 | void gfxDrawWave(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColorStart[3], u8 rgbColorEnd[3], u16 level, u16 amplitude, u16 width, gfxWaveCallback cb, void* p); 20 | -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include <3ds.h> 5 | 6 | #include "gfx.h" 7 | #include "menu.h" 8 | #include "background.h" 9 | #include "statusbar.h" 10 | #include "filesystem.h" 11 | #include "error.h" 12 | #include "netloader.h" 13 | #include "regionfree.h" 14 | #include "boot.h" 15 | #include "titles.h" 16 | #include "mmap.h" 17 | 18 | bool brewMode; 19 | bool sdmcCurrent; 20 | u64 nextSdCheck; 21 | 22 | menu_s menu; 23 | u32 wifiStatus; 24 | u8 batteryLevel = 5; 25 | u8 charging; 26 | int rebootCounter; 27 | titleBrowser_s titleBrowser; 28 | 29 | u32 menuret_enabled = 0; 30 | 31 | static enum 32 | { 33 | HBMENU_DEFAULT, 34 | HBMENU_REGIONFREE, 35 | HBMENU_TITLESELECT, 36 | HBMENU_TITLETARGET_ERROR, 37 | HBMENU_NETLOADER_ACTIVE, 38 | HBMENU_NETLOADER_UNAVAILABLE_NINJHAX2, 39 | HBMENU_NETLOADER_ERROR, 40 | } hbmenu_state = HBMENU_DEFAULT; 41 | 42 | int debugValues[100]; 43 | 44 | // typedef struct 45 | // { 46 | // int processId; 47 | // bool capabilities[0x10]; 48 | // }processEntry_s; 49 | 50 | // void (*_getBestProcess_2x)(u32 sectionSizes[3], bool* requirements, int num_requirements, processEntry_s* out, int out_size, int* out_len) = (void*)0x0010000C; 51 | 52 | // void drawDebug() 53 | // { 54 | // char str[256]; 55 | // sprintf(str, "hello3 %08X %d %d %d %d %d %d %d\n\n%08X %08X %08X %08X\n\n%08X %08X %08X %08X\n\n%08X %08X %08X %08X\n\n", debugValues[50], debugValues[51], debugValues[52], debugValues[53], debugValues[54], debugValues[55], debugValues[56], debugValues[57], debugValues[58], debugValues[59], debugValues[60], debugValues[61], debugValues[62], debugValues[63], debugValues[64], debugValues[65], debugValues[66], debugValues[67], debugValues[68], debugValues[69]); 56 | // // menuEntry_s* me = getMenuEntry(&menu, menu.selectedEntry); 57 | // // if(me && me->descriptor.numRequestedServices) 58 | // // { 59 | // // scanMenuEntry(me); 60 | // // executableMetadata_s* em = &me->descriptor.executableMetadata; 61 | // // processEntry_s out[4]; 62 | // // int out_len = 0; 63 | // // _getBestProcess_2x(em->sectionSizes, (bool*)em->servicesThatMatter, NUM_SERVICESTHATMATTER, out, 4, &out_len); 64 | // // sprintf(str, "hello3 %s %d %d %d %d\n", me->descriptor.requestedServices[0].name, out_len, out[0].processId, out[0].capabilities[4], em->servicesThatMatter[4]); 65 | // // } 66 | // gfxDrawText(GFX_TOP, GFX_LEFT, NULL, str, 48, 100); 67 | // } 68 | 69 | void renderFrame(u8 bgColor[3], u8 waterBorderColor[3], u8 waterColor[3]) 70 | { 71 | // background stuff 72 | drawBackground(bgColor, waterBorderColor, waterColor); 73 | 74 | // status bar 75 | drawStatusBar(wifiStatus, charging, batteryLevel); 76 | 77 | // current directory 78 | printDirectory(); 79 | 80 | // debug text 81 | // drawDebug(); 82 | 83 | //menu stuff 84 | if(rebootCounter<257) 85 | { 86 | //about to reboot 87 | 88 | if(!menuret_enabled) 89 | { 90 | drawError(GFX_BOTTOM, 91 | "Reboot", 92 | " You're about to reboot your console into Home Menu.\n\n" 93 | " A : Proceed\n" 94 | " B : Cancel\n", 95 | 0); 96 | } 97 | else 98 | { 99 | drawError(GFX_BOTTOM, 100 | "Reboot", 101 | " You're about to reboot your console into Home Menu.\n\n" 102 | " A : Proceed\n" 103 | " B : Cancel\n" 104 | " X : Return to Home Menu (no reboot)\n", 105 | 0); 106 | } 107 | }else if(!sdmcCurrent) 108 | { 109 | //no SD 110 | drawError(GFX_BOTTOM, 111 | "No SD detected", 112 | " It looks like your 3DS doesn't have an SD inserted into it.\n" 113 | " Please insert an SD card for optimal homebrew launcher performance !\n", 114 | 0); 115 | }else if(sdmcCurrent<0) 116 | { 117 | //SD error 118 | drawError(GFX_BOTTOM, 119 | "SD Error", 120 | " Something unexpected happened when trying to mount your SD card.\n" 121 | " Try taking it out and putting it back in. If that doesn't work,\n" 122 | "please try again with another SD card.", 123 | 0); 124 | }else if(hbmenu_state == HBMENU_NETLOADER_ACTIVE){ 125 | char bof[256]; 126 | u32 ip = gethostid(); 127 | sprintf(bof, 128 | " NetLoader Active - waiting for 3dslink connection\n" 129 | " IP: %lu.%lu.%lu.%lu, Port: %d\n\n" 130 | " B : Cancel\n", 131 | ip & 0xFF, (ip>>8)&0xFF, (ip>>16)&0xFF, (ip>>24)&0xFF, NETLOADER_PORT); 132 | 133 | drawError(GFX_BOTTOM, 134 | "NetLoader", 135 | bof, 136 | 0); 137 | }else if(hbmenu_state == HBMENU_NETLOADER_UNAVAILABLE_NINJHAX2){ 138 | drawError(GFX_BOTTOM, 139 | "NetLoader", 140 | " The NetLoader is currently unavailable. :(\n" 141 | " This might be normal and fixable. Try and enable it ?\n\n" 142 | " A : Yes\n" 143 | " B : No\n", 144 | 0); 145 | }else if(hbmenu_state == HBMENU_REGIONFREE){ 146 | if(regionFreeGamecardIn) 147 | { 148 | drawError(GFX_BOTTOM, 149 | "Region free launcher", 150 | " The region free launcher is ready to run your out-of-region gamecard !\n\n" 151 | " A : Play\n" 152 | " B : Cancel\n", 153 | 10-drawMenuEntry(&gamecardMenuEntry, GFX_BOTTOM, 240, 9, true)); 154 | }else{ 155 | drawError(GFX_BOTTOM, 156 | "Region free launcher", 157 | " The region free loader cannot detect a gamecard in the slot.\n" 158 | " Please insert a gamecard in your console before continuing.\n\n" 159 | " B : Cancel\n", 160 | 0); 161 | } 162 | }else if(hbmenu_state == HBMENU_TITLESELECT){ 163 | drawTitleBrowser(&titleBrowser); 164 | }else if(hbmenu_state == HBMENU_TITLETARGET_ERROR){ 165 | drawError(GFX_BOTTOM, 166 | "Missing target title", 167 | " The application you are trying to run requested a specific target title.\n" 168 | " Please make sure you have that title !\n\n" 169 | " B : Back\n", 170 | 0); 171 | }else if(hbmenu_state == HBMENU_NETLOADER_ERROR){ 172 | netloader_draw_error(); 173 | }else{ 174 | //got SD 175 | drawMenu(&menu); 176 | } 177 | } 178 | 179 | bool secretCode(void) 180 | { 181 | static const u32 secret_code[] = 182 | { 183 | KEY_UP, 184 | KEY_UP, 185 | KEY_DOWN, 186 | KEY_DOWN, 187 | KEY_LEFT, 188 | KEY_RIGHT, 189 | KEY_LEFT, 190 | KEY_RIGHT, 191 | KEY_B, 192 | KEY_A, 193 | }; 194 | 195 | static u32 state = 0; 196 | static u32 timeout = 30; 197 | u32 down = hidKeysDown(); 198 | 199 | if(down & secret_code[state]) 200 | { 201 | ++state; 202 | timeout = 30; 203 | 204 | if(state == sizeof(secret_code)/sizeof(secret_code[0])) 205 | { 206 | state = 0; 207 | return true; 208 | } 209 | } 210 | 211 | if(timeout > 0 && --timeout == 0) 212 | state = 0; 213 | 214 | return false; 215 | } 216 | 217 | // handled in main 218 | // doing it in main is preferred because execution ends in launching another 3dsx 219 | void __appInit() 220 | { 221 | srvInit(); 222 | } 223 | 224 | // same 225 | void __appExit() 226 | { 227 | srvExit(); 228 | } 229 | 230 | int main() 231 | { 232 | u32 menuret = 0; 233 | Handle kill=0; 234 | 235 | aptInit(); 236 | gfxInitDefault(); 237 | initFilesystem(); 238 | openSDArchive(); 239 | hidInit(); 240 | acInit(); 241 | ptmuInit(); 242 | titlesInit(); 243 | regionFreeInit(); 244 | netloader_init(); 245 | 246 | osSetSpeedupEnable(true); 247 | 248 | // offset potential issues caused by homebrew that just ran 249 | APT_SetAppCpuTimeLimit(0); 250 | 251 | initBackground(); 252 | initErrors(); 253 | initMenu(&menu); 254 | initTitleBrowser(&titleBrowser, NULL); 255 | 256 | bool sdmcPrevious = false; 257 | FSUSER_IsSdmcDetected(&sdmcCurrent); 258 | if(sdmcCurrent == 1) 259 | { 260 | scanHomebrewDirectory(&menu); 261 | } 262 | sdmcPrevious = sdmcCurrent; 263 | nextSdCheck = osGetTime()+250; 264 | 265 | srand(svcGetSystemTick()); 266 | 267 | if(srvGetServiceHandle(&kill, "hb:kill")==0) menuret_enabled = 1; 268 | 269 | rebootCounter=257; 270 | 271 | while(aptMainLoop()) 272 | { 273 | if (nextSdCheck < osGetTime()) 274 | { 275 | regionFreeUpdate(); 276 | 277 | FSUSER_IsSdmcDetected(&sdmcCurrent); 278 | 279 | if(sdmcCurrent == 1 && (sdmcPrevious == 0 || sdmcPrevious < 0)) 280 | { 281 | closeSDArchive(); 282 | openSDArchive(); 283 | scanHomebrewDirectory(&menu); 284 | } 285 | else if(sdmcCurrent < 1 && sdmcPrevious == 1) 286 | { 287 | clearMenuEntries(&menu); 288 | } 289 | sdmcPrevious = sdmcCurrent; 290 | nextSdCheck = osGetTime()+250; 291 | } 292 | 293 | ACU_GetWifiStatus(&wifiStatus); 294 | PTMU_GetBatteryLevel(&batteryLevel); 295 | PTMU_GetBatteryChargeState(&charging); 296 | hidScanInput(); 297 | 298 | updateBackground(); 299 | 300 | // menuEntry_s* me = getMenuEntry(&menu, menu.selectedEntry); 301 | // debugValues[50] = me->descriptor.numTargetTitles; 302 | // debugValues[51] = me->descriptor.selectTargetProcess; 303 | // if(me->descriptor.numTargetTitles) 304 | // { 305 | // debugValues[58] = (me->descriptor.targetTitles[0].tid >> 32) & 0xFFFFFFFF; 306 | // debugValues[59] = me->descriptor.targetTitles[0].tid & 0xFFFFFFFF; 307 | // } 308 | 309 | if(hbmenu_state == HBMENU_NETLOADER_ACTIVE){ 310 | if(hidKeysDown()&KEY_B){ 311 | netloader_deactivate(); 312 | hbmenu_state = HBMENU_DEFAULT; 313 | }else{ 314 | int rc = netloader_loop(); 315 | if(rc > 0) 316 | { 317 | netloader_boot = true; 318 | break; 319 | }else if(rc < 0){ 320 | hbmenu_state = HBMENU_NETLOADER_ERROR; 321 | } 322 | } 323 | }else if(hbmenu_state == HBMENU_NETLOADER_UNAVAILABLE_NINJHAX2){ 324 | if(hidKeysDown()&KEY_B){ 325 | hbmenu_state = HBMENU_DEFAULT; 326 | }else if(hidKeysDown()&KEY_A){ 327 | if(isNinjhax2()) 328 | { 329 | // basically just relaunch boot.3dsx w/ scanning in hopes of getting netloader capabilities 330 | static char hbmenuPath[] = "/boot.3dsx"; 331 | netloadedPath = hbmenuPath; // fine since it's static 332 | netloader_boot = true; 333 | break; 334 | } 335 | } 336 | }else if(hbmenu_state == HBMENU_REGIONFREE){ 337 | if(hidKeysDown()&KEY_B){ 338 | hbmenu_state = HBMENU_DEFAULT; 339 | }else if(hidKeysDown()&KEY_A && regionFreeGamecardIn) 340 | { 341 | // region free menu entry is selected so we can just break out like updateMenu() normally would 342 | break; 343 | } 344 | }else if(hbmenu_state == HBMENU_TITLETARGET_ERROR){ 345 | if(hidKeysDown()&KEY_B){ 346 | hbmenu_state = HBMENU_DEFAULT; 347 | } 348 | }else if(hbmenu_state == HBMENU_TITLESELECT){ 349 | if(hidKeysDown()&KEY_A && titleBrowser.selected) 350 | { 351 | bootSetTargetTitle(*titleBrowser.selected); 352 | break; 353 | } 354 | else if(hidKeysDown()&KEY_B)hbmenu_state = HBMENU_DEFAULT; 355 | else updateTitleBrowser(&titleBrowser); 356 | }else if(hbmenu_state == HBMENU_NETLOADER_ERROR){ 357 | if(hidKeysDown()&KEY_B) 358 | hbmenu_state = HBMENU_DEFAULT; 359 | }else if(rebootCounter==256){ 360 | if(hidKeysDown()&KEY_A) 361 | { 362 | //reboot 363 | APT_HardwareResetAsync(); 364 | rebootCounter--; 365 | }else if(hidKeysDown()&KEY_X) 366 | { 367 | if(menuret_enabled) 368 | { 369 | menuret = 1; 370 | break; 371 | } 372 | }else if(hidKeysDown()&KEY_B) 373 | { 374 | rebootCounter++; 375 | } 376 | }else if(rebootCounter==257){ 377 | if(hidKeysDown()&KEY_START)rebootCounter--; 378 | if(hidKeysDown()&KEY_Y) 379 | { 380 | if(netloader_activate() == 0) hbmenu_state = HBMENU_NETLOADER_ACTIVE; 381 | else if(isNinjhax2()) hbmenu_state = HBMENU_NETLOADER_UNAVAILABLE_NINJHAX2; 382 | } 383 | if(secretCode())brewMode = true; 384 | else if(hidKeysDown()&KEY_B) { 385 | changeDirectory(".."); 386 | clearMenuEntries(&menu); 387 | initMenu(&menu); 388 | scanHomebrewDirectory(&menu); 389 | } 390 | else if(updateMenu(&menu)) 391 | { 392 | menuEntry_s* me = getMenuEntry(&menu, menu.selectedEntry); 393 | if(me && me->type == MENU_ENTRY_FOLDER) { 394 | changeDirectory(me->executablePath); 395 | clearMenuEntries(&menu); 396 | initMenu(&menu); 397 | scanHomebrewDirectory(&menu); 398 | } else 399 | if(me && !strcmp(me->executablePath, REGIONFREE_PATH) && regionFreeAvailable && !netloader_boot) 400 | { 401 | hbmenu_state = HBMENU_REGIONFREE; 402 | regionFreeUpdate(); 403 | }else 404 | { 405 | // if appropriate, look for specified titles in list 406 | if(me->descriptor.numTargetTitles) 407 | { 408 | // first refresh list (for sd/gamecard) 409 | updateTitleBrowser(&titleBrowser); 410 | 411 | // go through target title list in order so that first ones on list have priority 412 | int i; 413 | titleInfo_s* ret = NULL; 414 | for(i=0; idescriptor.numTargetTitles; i++) 415 | { 416 | ret = findTitleBrowser(&titleBrowser, me->descriptor.targetTitles[i].mediatype, me->descriptor.targetTitles[i].tid); 417 | if(ret)break; 418 | } 419 | 420 | if(ret) 421 | { 422 | bootSetTargetTitle(*ret); 423 | break; 424 | } 425 | 426 | // if we get here, we aint found shit 427 | // if appropriate, let user select target title 428 | if(me->descriptor.selectTargetProcess) hbmenu_state = HBMENU_TITLESELECT; 429 | else hbmenu_state = HBMENU_TITLETARGET_ERROR; 430 | }else 431 | { 432 | if(me->descriptor.selectTargetProcess) hbmenu_state = HBMENU_TITLESELECT; 433 | else break; 434 | } 435 | 436 | 437 | } 438 | } 439 | } 440 | 441 | if(brewMode)renderFrame(BGCOLOR, BEERBORDERCOLOR, BEERCOLOR); 442 | else renderFrame(BGCOLOR, WATERBORDERCOLOR, WATERCOLOR); 443 | 444 | if(rebootCounter<256) 445 | { 446 | if(rebootCounter<0)rebootCounter=0; 447 | gfxFadeScreen(GFX_TOP, GFX_LEFT, rebootCounter); 448 | gfxFadeScreen(GFX_BOTTOM, GFX_BOTTOM, rebootCounter); 449 | if(rebootCounter>0)rebootCounter-=6; 450 | } 451 | 452 | gfxFlushBuffers(); 453 | gfxSwapBuffers(); 454 | 455 | gspWaitForVBlank(); 456 | } 457 | 458 | menuEntry_s* me = getMenuEntry(&menu, menu.selectedEntry); 459 | 460 | if(netloader_boot) 461 | { 462 | me = malloc(sizeof(menuEntry_s)); 463 | initMenuEntry(me, netloadedPath, "netloaded app", "", "", NULL, MENU_ENTRY_FILE); 464 | } 465 | 466 | scanMenuEntry(me); 467 | 468 | // cleanup whatever we have to cleanup 469 | netloader_exit(); 470 | titlesExit(); 471 | ptmuExit(); 472 | acExit(); 473 | hidExit(); 474 | gfxExit(); 475 | closeSDArchive(); 476 | exitFilesystem(); 477 | aptExit(); 478 | 479 | if(menuret) 480 | { 481 | srvExit(); 482 | 483 | svcSignalEvent(kill); 484 | svcExitProcess(); 485 | } 486 | 487 | if (!strcmp(me->executablePath, REGIONFREE_PATH) && regionFreeAvailable && !netloader_boot)return regionFreeRun(); 488 | 489 | regionFreeExit(); 490 | 491 | return bootApp(me->executablePath, &me->descriptor.executableMetadata, me->arg); 492 | } 493 | -------------------------------------------------------------------------------- /source/menu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include <3ds.h> 4 | 5 | #include "text.h" 6 | #include "menu.h" 7 | #include "error.h" 8 | #include "smdh.h" 9 | #include "regionfree.h" 10 | #include "regionfree_bin.h" 11 | 12 | u8 roundLut[]={8, 5, 4, 3, 2, 1, 1, 1, 0}; 13 | u8 roundLut2[]={4, 3, 2, 1, 0}; 14 | u8 roundLut3[]={0, 1, 2}; 15 | 16 | #define SCROLLING_SPEED (16) //lower is faster 17 | 18 | extern int debugValues[100]; //TEMP 19 | 20 | menuEntry_s regionfreeEntry; 21 | 22 | void initMenu(menu_s* m) 23 | { 24 | if(!m)return; 25 | 26 | m->entries=NULL; 27 | m->numEntries=0; 28 | m->selectedEntry=0; 29 | m->scrollLocation=0; 30 | m->scrollVelocity=0; 31 | m->scrollBarSize=0; 32 | m->scrollBarPos=0; 33 | m->scrollTarget=0; 34 | m->atEquilibrium=false; 35 | 36 | if(regionFreeAvailable) 37 | { 38 | extractSmdhData((smdh_s*)regionfree_bin, regionfreeEntry.name, regionfreeEntry.description, regionfreeEntry.author, regionfreeEntry.iconData); 39 | strcpy(regionfreeEntry.executablePath, REGIONFREE_PATH); 40 | addMenuEntryCopy(m, ®ionfreeEntry); 41 | } 42 | } 43 | 44 | static inline s16 getEntryLocationPx(menu_s* m, int px) 45 | { 46 | return 240-px+fptToInt(m->scrollLocation); 47 | } 48 | 49 | static inline s16 getEntryLocation(menu_s* m, int n) 50 | { 51 | return getEntryLocationPx(m, (n+1)*ENTRY_WIDTH); 52 | } 53 | 54 | void drawScrollBar(menu_s* m) 55 | { 56 | if(!m)return; 57 | 58 | int scrollBarTotalSize=m->scrollBarSize+3*2; 59 | 60 | int i; 61 | // background scrollbar thing 62 | //35,308 - 200,7 63 | u8 colorBg[]={132, 224, 255}; 64 | for(i=0; i<3; i++)gfxDrawRectangle(GFX_BOTTOM, GFX_LEFT, colorBg, 38-i, 308+roundLut3[i], 1, 7-2*roundLut3[i]); 65 | gfxDrawRectangle(GFX_BOTTOM, GFX_LEFT, colorBg, 38, 308, 194, 7); 66 | for(i=0; i<3; i++)gfxDrawRectangle(GFX_BOTTOM, GFX_LEFT, colorBg, 232+i, 308+roundLut3[i], 1, 7-2*roundLut3[i]); 67 | // actual scrollbar 68 | u8 color[]={255, 255, 255}; 69 | for(i=0; i<3; i++)gfxDrawRectangle(GFX_BOTTOM, GFX_LEFT, color, 200-scrollBarTotalSize-m->scrollBarPos+38-i, 308+roundLut3[i], 1, 7-2*roundLut3[i]); 70 | gfxDrawRectangle(GFX_BOTTOM, GFX_LEFT, color, 200-scrollBarTotalSize-m->scrollBarPos+38, 308, m->currentScrollBarSize, 7); 71 | for(i=0; i<3; i++)gfxDrawRectangle(GFX_BOTTOM, GFX_LEFT, color, 200-scrollBarTotalSize-m->scrollBarPos+38+m->currentScrollBarSize+i, 308+roundLut3[i], 1, 7-2*roundLut3[i]); 72 | } 73 | 74 | void drawMenu(menu_s* m) 75 | { 76 | if(!m)return; 77 | 78 | if(!m->numEntries) 79 | { 80 | drawError(GFX_BOTTOM, 81 | "Error", 82 | " It seems you don't have any homebrew applications installed on your\n" 83 | "SD card.\n" 84 | " Please take out your SD card, create a folder named \"3ds\" at the root of\n" 85 | "your card and place homebrew there.\n" 86 | " Then, simply insert your SD card back into your 3DS !\n" 87 | " The homebrew launcher will take it from there.", 88 | 0); 89 | }else{ 90 | menuEntry_s* me=m->entries; 91 | int i=0; 92 | int h=0; 93 | while(me) 94 | { 95 | h+=drawMenuEntry(me, GFX_BOTTOM, getEntryLocationPx(m,h), 9, i==m->selectedEntry); 96 | me=me->next; 97 | i++; 98 | } 99 | } 100 | 101 | drawScrollBar(m); 102 | } 103 | 104 | void addMenuEntryAt(menu_s* m, menuEntry_s* me, int offset) 105 | { 106 | if(!m || !me)return; 107 | if(offset == 0) offset++; 108 | 109 | // add to the end of the list 110 | menuEntry_s* l = m->entries; 111 | menuEntry_s** lp = &m->entries; 112 | int i = 0; 113 | while(l && *lp && i != offset) 114 | { 115 | lp = &l->next; 116 | l = l->next; 117 | i++; 118 | } 119 | *lp = me; 120 | me->next = l; 121 | m->numEntries++; 122 | } 123 | 124 | void addMenuEntry(menu_s* m, menuEntry_s* me) 125 | { 126 | addMenuEntryAt(m, me, -1); 127 | } 128 | 129 | void addMenuEntryCopyAt(menu_s* m, menuEntry_s* me, int offset) 130 | { 131 | if(!m || !me)return; 132 | 133 | menuEntry_s* me2=malloc(sizeof(menuEntry_s)); 134 | if(!me2)return; 135 | 136 | memcpy(me2, me, sizeof(menuEntry_s)); 137 | 138 | addMenuEntryAt(m, me2, offset); 139 | } 140 | 141 | void addMenuEntryCopy(menu_s* m, menuEntry_s* me) 142 | { 143 | addMenuEntryCopyAt(m, me, -1); 144 | } 145 | 146 | void freeMenuEntry(menuEntry_s* me) 147 | { 148 | if(!me)return; 149 | 150 | freeDescriptor(&me->descriptor); 151 | } 152 | 153 | void clearMenuEntries(menu_s* m) 154 | { 155 | if(!m)return; 156 | 157 | m->selectedEntry=0; 158 | drawScrollBar(m); 159 | 160 | menuEntry_s* me = m->entries; 161 | menuEntry_s* temp = NULL; 162 | while(me) 163 | { 164 | temp=me->next; 165 | me->next = NULL; 166 | freeMenuEntry(me); 167 | free(me); 168 | me = temp; 169 | } 170 | 171 | m->numEntries = 0; 172 | m->entries = NULL; 173 | 174 | if(regionFreeAvailable) 175 | { 176 | // should always be available 177 | addMenuEntryCopy(m, ®ionfreeEntry); 178 | } 179 | } 180 | 181 | void createMenuEntry(menu_s* m, char* execPath, char* name, char* description, char* author, u8* iconData, menuEntryType_s type) 182 | { 183 | if(!m || !name || !description || !iconData)return; 184 | 185 | menuEntry_s* me=malloc(sizeof(menuEntry_s)); 186 | if(!me)return; 187 | 188 | initMenuEntry(me, execPath, name, description, author, iconData, type); 189 | 190 | addMenuEntry(m, me); 191 | } 192 | 193 | menuEntry_s* getMenuEntry(menu_s* m, u16 n) 194 | { 195 | if(!m || n>=m->numEntries)return NULL; 196 | menuEntry_s* me=m->entries; 197 | while(n && me){me=me->next; n--;} 198 | return me; 199 | } 200 | 201 | //return true when we're ready to boot something 202 | //(TEMP ?) 203 | bool updateMenu(menu_s* m) 204 | { 205 | if(!m)return false; 206 | if(!m->numEntries)return false; 207 | 208 | //controls 209 | s8 move=0; 210 | circlePosition cstick; 211 | touchPosition touch; 212 | hidCstickRead(&cstick); 213 | hidTouchRead(&touch); 214 | 215 | cstick.dy=(abs(cstick.dy)<5)?0:cstick.dy; 216 | 217 | if(hidKeysDown()&KEY_DOWN)move++; 218 | if(hidKeysDown()&KEY_UP)move--; 219 | if(hidKeysDown()&KEY_RIGHT)move+=4; 220 | if(hidKeysDown()&KEY_LEFT)move-=4; 221 | 222 | u16 oldEntry=m->selectedEntry; 223 | 224 | if(hidKeysDown()&KEY_TOUCH) 225 | { 226 | m->touchTimer=0; 227 | m->firstTouch=touch; 228 | }else if((hidKeysUp()&KEY_TOUCH) && m->touchTimer<30 && abs(m->firstTouch.px-m->previousTouch.px)+abs(m->firstTouch.py-m->previousTouch.py)<12){ 229 | menuEntry_s* me=m->entries; 230 | int i=0; 231 | int p=0; 232 | while(me) 233 | { 234 | int h=(i==m->selectedEntry)?ENTRY_WIDTH_SELECTED:ENTRY_WIDTH; 235 | if((240-m->previousTouch.py)>=getEntryLocationPx(m,p)-h && (240-m->previousTouch.py)next; 238 | i++; 239 | } 240 | if(m->selectedEntry==i)return true; 241 | else m->selectedEntry=i; 242 | }else if(hidKeysHeld()&KEY_TOUCH){ 243 | //condition to make sure previousTouch is valid 244 | cstick.dy+=(touch.py-m->previousTouch.py)*16; 245 | m->touchTimer++; 246 | } 247 | if(move+m->selectedEntry<0)m->selectedEntry=0; 248 | else if(move+m->selectedEntry>=m->numEntries)m->selectedEntry=m->numEntries-1; 249 | else m->selectedEntry+=move; 250 | 251 | if(m->selectedEntry!=oldEntry)m->atEquilibrium=false; 252 | 253 | if(hidKeysDown()&KEY_A)return true; 254 | 255 | m->previousTouch=touch; 256 | 257 | //scrolling code 258 | const int maxScroll=240-(m->numEntries)*ENTRY_WIDTH; //cf getEntryLocation 259 | 260 | if(!m->atEquilibrium) 261 | { 262 | m->scrollTarget=intToFpt(getEntryLocation(m, m->selectedEntry)); 263 | if(m->scrollTarget>intToFpt(240-ENTRY_WIDTH) || (m->selectedEntry==0 && m->numEntries>3)) 264 | m->scrollVelocity+=(intToFpt(240-ENTRY_WIDTH)-m->scrollTarget)/SCROLLING_SPEED; 265 | if(m->scrollTarget<0 || (m->selectedEntry==m->numEntries-1 && m->numEntries>3)) 266 | m->scrollVelocity+=(intToFpt(0)-m->scrollTarget)/SCROLLING_SPEED; 267 | }else if(m->numEntries>3){ 268 | s32 val=-cstick.dy*16; // TODO : make it inversely proportional to numEntries ? 269 | if(m->scrollLocation>intToFpt(-maxScroll)) 270 | { 271 | m->scrollVelocity+=(intToFpt(-maxScroll)-m->scrollLocation)/SCROLLING_SPEED; 272 | if(val<0)m->scrollVelocity+=val; 273 | }else if(m->scrollLocationscrollVelocity-=m->scrollLocation/SCROLLING_SPEED; 275 | if(val>0)m->scrollVelocity+=val; 276 | }else m->scrollVelocity+=val; 277 | } 278 | 279 | m->scrollLocation+=m->scrollVelocity; 280 | m->scrollVelocity=(m->scrollVelocity*3)/4; 281 | 282 | m->scrollBarSize=40; //TEMP : make it adaptive to number of menu entries ? 283 | m->scrollBarPos=-fptToInt(m->scrollLocation*(200-m->scrollBarSize))/maxScroll; 284 | if(m->scrollBarPos<0) 285 | { 286 | m->currentScrollBarSize=m->scrollBarSize+m->scrollBarPos; 287 | if(m->currentScrollBarSize<10)m->currentScrollBarSize=10; 288 | m->scrollBarPos=m->currentScrollBarSize-m->scrollBarSize; 289 | }else if(m->scrollBarPos>=200-m->scrollBarSize) 290 | { 291 | m->currentScrollBarSize=-(m->scrollBarPos-200); 292 | if(m->currentScrollBarSize<10)m->currentScrollBarSize=10; 293 | debugValues[3]=m->scrollBarPos-200; 294 | m->scrollBarPos=200-m->scrollBarSize; 295 | }else m->currentScrollBarSize=m->scrollBarSize; 296 | 297 | if(!m->scrollVelocity)m->atEquilibrium=true; 298 | 299 | // debugValues[0]=m->scrollLocation; 300 | // debugValues[1]=m->scrollTarget; 301 | // debugValues[1]=fptToInt(m->scrollLocation); 302 | // debugValues[2]=intToFpt(maxScroll); 303 | // debugValues[3]=maxScroll; 304 | 305 | return false; 306 | } 307 | 308 | void initEmptyMenuEntry(menuEntry_s* me) 309 | { 310 | if(!me)return; 311 | 312 | me->name[0]=0x00; 313 | me->description[0]=0x00; 314 | me->executablePath[0]=0x00; 315 | me->author[0]=0x00; 316 | me->arg[0]=0x00; 317 | 318 | initDescriptor(&me->descriptor); 319 | 320 | me->next=NULL; 321 | } 322 | 323 | void initMenuEntry(menuEntry_s* me, char* execPath, char* name, char* description, char* author, u8* iconData, menuEntryType_s type) 324 | { 325 | if(!me)return; 326 | 327 | initEmptyMenuEntry(me); 328 | 329 | strncpy(me->executablePath, execPath, ENTRY_PATHLENGTH); 330 | strncpy(me->name, name, ENTRY_NAMELENGTH); 331 | strncpy(me->description, description, ENTRY_DESCLENGTH); 332 | strncpy(me->author, author, ENTRY_AUTHORLENGTH); 333 | me->type = type; 334 | if(iconData)memcpy(me->iconData, iconData, ENTRY_ICONSIZE); 335 | 336 | initDescriptor(&me->descriptor); 337 | } 338 | 339 | int drawMenuEntry(menuEntry_s* me, gfxScreen_t screen, u16 x, u16 y, bool selected) 340 | { 341 | if(!me)return 0; 342 | int i; 343 | 344 | //TODO : proper template sort of thing ? 345 | //this is all hardcoded and horrible 346 | 347 | const int totalWidth=selected?ENTRY_WIDTH_SELECTED:ENTRY_WIDTH; 348 | const int actualWidth=(selected?ENTRY_FWIDTH_SELECTED:ENTRY_FWIDTH); 349 | const int widthOffset=actualWidth-ENTRY_FWIDTH; 350 | const int actualHeight=selected?ENTRY_HEIGHT_SELECTED:ENTRY_HEIGHT; 351 | if(selected)y-=ENTRY_HEIGHT_SELECTED-ENTRY_HEIGHT; 352 | x-=ENTRY_WIDTH+widthOffset; 353 | 354 | //drop shadow 355 | if(selected) 356 | { 357 | const int sw=4; 358 | const int sx=x-sw; 359 | for(i=0; i<9; i++)gfxDrawRectangle(screen, GFX_LEFT, ENTRY_BGCOLOR_SHADOW, sx+roundLut[i], y+i, sw, 1); 360 | gfxDrawRectangle(screen, GFX_LEFT, ENTRY_BGCOLOR_SHADOW, sx, y+9, sw, actualHeight-9*2); 361 | for(i=0; i<9; i++)gfxDrawRectangle(screen, GFX_LEFT, ENTRY_BGCOLOR_SHADOW, sx+roundLut[i], y+actualHeight-1-i, sw, 1); 362 | } 363 | 364 | //main frame 365 | for(i=0; i<9; i++)gfxDrawRectangle(screen, GFX_LEFT, ENTRY_BGCOLOR, x-widthOffset+roundLut[i], y+i, actualWidth-roundLut[i]*2, 1); 366 | gfxDrawRectangle(screen, GFX_LEFT, ENTRY_BGCOLOR, x-widthOffset, y+9, actualWidth, actualHeight-9*2); 367 | for(i=0; i<9; i++)gfxDrawRectangle(screen, GFX_LEFT, ENTRY_BGCOLOR, x-widthOffset+roundLut[i], y+actualHeight-1-i, actualWidth-roundLut[i]*2, 1); 368 | 369 | //icon frame 370 | u8 colorIcon[]={225, 225, 225}; 371 | for(i=0; i<5; i++)gfxDrawRectangle(screen, GFX_LEFT, colorIcon, x+3+roundLut2[i], y+4+i, 56-roundLut2[i]*2, 1); 372 | gfxDrawRectangle(screen, GFX_LEFT, colorIcon, x+3, y+9, 56, 46); 373 | for(i=0; i<5; i++)gfxDrawRectangle(screen, GFX_LEFT, colorIcon, x+3+roundLut2[i], y+4+56-1-i, 56-roundLut2[i]*2, 1); 374 | 375 | //app specific stuff 376 | gfxDrawSprite(screen, GFX_LEFT, me->iconData, ENTRY_ICON_WIDTH, ENTRY_ICON_HEIGHT, x+7, y+8); 377 | gfxDrawTextN(screen, GFX_LEFT, &fontTitle, me->name, ENTRY_NAMELENGTH, x+38, y+66); 378 | gfxDrawTextN(screen, GFX_LEFT, &fontDescription, me->description, 56, x+26, y+70); 379 | if(strlen(me->description) > 56 * 1) 380 | { 381 | gfxDrawTextN(screen, GFX_LEFT, &fontDescription, me->description + 56, 56, x+18, y+70); 382 | } 383 | else if(strlen(me->description) > 56 * 2) 384 | { 385 | gfxDrawTextN(screen, GFX_LEFT, &fontDescription, me->description + 56 * 1, 56, x+18, y+70); 386 | gfxDrawTextN(screen, GFX_LEFT, &fontDescription, me->description + 56 * 2, 56, x+10, y+70); 387 | } 388 | gfxDrawTextN(screen, GFX_LEFT, &fontDescription, me->author, ENTRY_AUTHORLENGTH, x+4, y+ENTRY_HEIGHT-getStringLength(&fontDescription, me->author)-10); 389 | 390 | return totalWidth; 391 | } 392 | 393 | static int menuEntryCmp(const void *p1, const void *p2) 394 | { 395 | const menuEntry_s* lhs = *(menuEntry_s**)p1; 396 | const menuEntry_s* rhs = *(menuEntry_s**)p2; 397 | 398 | if(lhs->type == rhs->type) 399 | return strcasecmp(lhs->name, rhs->name); 400 | if(lhs->type == MENU_ENTRY_FOLDER) 401 | return -1; 402 | return 1; 403 | } 404 | 405 | void sortMenu(menu_s* m) 406 | { 407 | u16 i; 408 | if(m->numEntries == 0) return; 409 | 410 | menuEntry_s** list = (menuEntry_s**)calloc(m->numEntries, sizeof(menuEntry_s*)); 411 | if(list == NULL) return; 412 | 413 | menuEntry_s* p = m->entries; 414 | for(i = 0; i < m->numEntries; ++i) { 415 | list[i] = p; 416 | p = p->next; 417 | } 418 | 419 | qsort(list, m->numEntries, sizeof(menuEntry_s*), menuEntryCmp); 420 | 421 | menuEntry_s** pp = &m->entries; 422 | for(i = 0; i < m->numEntries; ++i) { 423 | *pp = list[i]; 424 | pp = &(*pp)->next; 425 | } 426 | *pp = NULL; 427 | 428 | free(list); 429 | } 430 | -------------------------------------------------------------------------------- /source/menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gfx.h" 4 | #include "scanner.h" 5 | #include "descriptor.h" 6 | 7 | #define ENTRY_PATHLENGTH (128) 8 | #define ENTRY_NAMELENGTH (64) 9 | #define ENTRY_DESCLENGTH (128) 10 | #define ENTRY_AUTHORLENGTH (64) 11 | #define ENTRY_ARGLENGTH (256) 12 | #define ENTRY_ICON_WIDTH (48) 13 | #define ENTRY_ICON_HEIGHT (48) 14 | #define ENTRY_ICONSIZE (ENTRY_ICON_WIDTH*ENTRY_ICON_HEIGHT*3) 15 | #define ENTRY_WIDTH (66) 16 | #define ENTRY_WIDTH_SELECTED (72) 17 | #define ENTRY_FWIDTH (63) 18 | #define ENTRY_FWIDTH_SELECTED (63) 19 | #define ENTRY_HEIGHT (294) 20 | #define ENTRY_HEIGHT_SELECTED (298) 21 | 22 | #define ENTRY_BGCOLOR (u8[]){246, 252, 255} 23 | #define ENTRY_BGCOLOR_SHADOW (u8[]){34, 153, 183} 24 | 25 | #define fptToInt(v) ((v)>>10) 26 | #define intToFpt(v) ((v)<<10) 27 | 28 | typedef enum menuEntryType_s 29 | { 30 | MENU_ENTRY_FILE, 31 | MENU_ENTRY_FOLDER, 32 | }menuEntryType_s; 33 | 34 | typedef struct menuEntry_s 35 | { 36 | char executablePath[ENTRY_PATHLENGTH+1]; 37 | char name[ENTRY_NAMELENGTH+1]; 38 | char description[ENTRY_DESCLENGTH+1]; 39 | char author[ENTRY_AUTHORLENGTH+1]; 40 | char arg[ENTRY_ARGLENGTH+1]; 41 | u8 iconData[ENTRY_ICONSIZE]; 42 | menuEntryType_s type; 43 | descriptor_s descriptor; 44 | struct menuEntry_s* next; 45 | }menuEntry_s; 46 | 47 | typedef struct 48 | { 49 | menuEntry_s* entries; 50 | u16 numEntries; 51 | u16 selectedEntry; 52 | s32 scrollTarget; //10 bit fixed point 53 | s32 scrollLocation; //10 bit fixed point 54 | s32 scrollVelocity; //10 bit fixed point 55 | s32 scrollBarSize; 56 | s32 currentScrollBarSize; 57 | s32 scrollBarPos; 58 | touchPosition previousTouch, firstTouch; 59 | u16 touchTimer; 60 | bool atEquilibrium; 61 | }menu_s; 62 | 63 | extern menuEntry_s regionfreeEntry; 64 | 65 | //menu stuff 66 | void initMenu(menu_s* m); 67 | void drawMenu(menu_s* m); 68 | bool updateMenu(menu_s* m); 69 | void addMenuEntry(menu_s* m, menuEntry_s* me); 70 | void addMenuEntryCopy(menu_s* m, menuEntry_s* me); 71 | void sortMenu(menu_s* m); 72 | void addMenuEntryAt(menu_s* m, menuEntry_s* me, int offset); 73 | void addMenuEntryCopyAt(menu_s* m, menuEntry_s* me, int offset); 74 | void clearMenuEntries(menu_s* m); 75 | void createMenuEntry(menu_s* m, char* execPath, char* name, char* description, char* author, u8* iconData, menuEntryType_s type); 76 | menuEntry_s* getMenuEntry(menu_s* m, u16 n); 77 | 78 | //menu entry stuff 79 | void initEmptyMenuEntry(menuEntry_s* me); 80 | void initMenuEntry(menuEntry_s* me, char* execPath, char* name, char* description, char* author, u8* iconData, menuEntryType_s type); 81 | int drawMenuEntry(menuEntry_s* me, gfxScreen_t screen, u16 x, u16 y, bool selected); 82 | 83 | void scanMenuEntry(menuEntry_s* me); 84 | -------------------------------------------------------------------------------- /source/mmap.cpp: -------------------------------------------------------------------------------- 1 | #include "mmap.h" 2 | #include "tinyxml2.h" 3 | 4 | using namespace tinyxml2; 5 | 6 | u32 getXmlUnsignedInt(XMLElement* el) 7 | { 8 | if(!el) return 0; 9 | 10 | const char* str = el->GetText(); 11 | if(!str) return 0; 12 | 13 | return strtoul(str, NULL, 0); 14 | } 15 | 16 | u32 getXmlInt(XMLElement* el) 17 | { 18 | if(!el) return 0; 19 | 20 | const char* str = el->GetText(); 21 | if(!str) return 0; 22 | 23 | return strtol(str, NULL, 0); 24 | } 25 | 26 | // TODO : error checking 27 | memorymap_t* loadMemoryMap(char* path) 28 | { 29 | if(!path)return NULL; 30 | 31 | XMLDocument doc; 32 | if(doc.LoadFile(path))return NULL; 33 | 34 | memorymap_header_t header; 35 | XMLElement* header_element = doc.FirstChildElement("header"); 36 | if(header_element) 37 | { 38 | header.num = getXmlUnsignedInt(header_element->FirstChildElement("num")); 39 | header.text_end = getXmlUnsignedInt(header_element->FirstChildElement("text_end")); 40 | header.data_address = getXmlUnsignedInt(header_element->FirstChildElement("data_address")); 41 | header.data_size = getXmlUnsignedInt(header_element->FirstChildElement("data_size")); 42 | header.processLinearOffset = getXmlUnsignedInt(header_element->FirstChildElement("processLinearOffset")); 43 | header.processHookAddress = getXmlUnsignedInt(header_element->FirstChildElement("processHookAddress")); 44 | header.processAppCodeAddress = getXmlUnsignedInt(header_element->FirstChildElement("processAppCodeAddress")); 45 | header.processHookTidLow = getXmlUnsignedInt(header_element->FirstChildElement("processHookTidLow")); 46 | header.processHookTidHigh = getXmlUnsignedInt(header_element->FirstChildElement("processHookTidHigh")); 47 | header.mediatype = getXmlUnsignedInt(header_element->FirstChildElement("mediatype")); 48 | }else return NULL; 49 | 50 | memorymap_t* ret = (memorymap_t*) malloc(sizeof(memorymap_header_t) + header.num * sizeof(memorymap_entry_t)); 51 | if(!ret) return NULL; 52 | 53 | ret->header = header; 54 | 55 | XMLElement* map = doc.FirstChildElement("map"); 56 | if(map) 57 | { 58 | u32 i = 0; 59 | 60 | for (tinyxml2::XMLElement* child = map->FirstChildElement(); child != NULL && i < header.num; child = child->NextSiblingElement()) 61 | { 62 | if(!strcmp(child->Name(), "entry")) 63 | { 64 | ret->map[i].src = getXmlInt(child->FirstChildElement("src")); 65 | ret->map[i].dst = getXmlInt(child->FirstChildElement("dst")); 66 | ret->map[i].size = getXmlInt(child->FirstChildElement("size")); 67 | 68 | i++; 69 | } 70 | } 71 | 72 | if(i == header.num) return ret; 73 | } 74 | 75 | free(ret); 76 | 77 | return NULL; 78 | } 79 | -------------------------------------------------------------------------------- /source/mmap.h: -------------------------------------------------------------------------------- 1 | #ifndef MMAP_H 2 | #define MMAP_H 3 | 4 | #include <3ds.h> 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #define size_memmap(m) (sizeof(memorymap_header_t) + sizeof(memorymap_entry_t) * (m).header.num) 11 | 12 | typedef struct 13 | { 14 | u32 num; 15 | u32 text_end; 16 | u32 data_address; 17 | u32 data_size; 18 | u32 processLinearOffset; 19 | u32 processHookAddress; 20 | u32 processAppCodeAddress; 21 | u32 processHookTidLow, processHookTidHigh; 22 | u32 mediatype; 23 | bool capabilities[0x10]; // {socuAccess, csndAccess, qtmAccess, nfcAccess, httpcAccess, reserved...} 24 | } memorymap_header_t; 25 | 26 | typedef struct 27 | { 28 | u32 src, dst, size; 29 | } memorymap_entry_t; 30 | 31 | typedef struct { 32 | memorymap_header_t header; 33 | memorymap_entry_t map[]; 34 | } memorymap_t; 35 | 36 | memorymap_t* loadMemoryMap(char* path); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /source/netloader.c: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #define ZLIB_CHUNK (16 * 1024) 14 | 15 | #include "error.h" 16 | #include "filesystem.h" 17 | #include "netloader.h" 18 | 19 | char *netloadedPath = NULL; 20 | char *netloaded_commandline = NULL; 21 | bool netloader_boot = false; 22 | int netloaded_cmdlen = 0; 23 | 24 | static char errbuf[1024]; 25 | 26 | static int netloader_listenfd = -1; 27 | static int netloader_datafd = -1; 28 | static int netloader_udpfd = -1; 29 | 30 | 31 | static void *SOC_buffer = NULL; 32 | 33 | 34 | unsigned char in[ZLIB_CHUNK]; 35 | unsigned char out[ZLIB_CHUNK]; 36 | 37 | static void netloader_socket_error(const char *func, int err) { 38 | siprintf(errbuf, " %s: err=%d", func, err); 39 | netloader_deactivate(); 40 | } 41 | 42 | static char progress[256]; 43 | 44 | static int netloader_draw_progress(void) { 45 | char info[1024]; 46 | sprintf(info, "Transferring: %s\n\n%s",netloadedPath,progress); 47 | drawError(GFX_BOTTOM, "NetLoader", info, 0); 48 | gfxFlushBuffers(); 49 | gfxSwapBuffers(); 50 | 51 | gspWaitForVBlank(); 52 | 53 | return 0; 54 | } 55 | 56 | //--------------------------------------------------------------------------------- 57 | static int recvall(int sock, void *buffer, int size, int flags) { 58 | //--------------------------------------------------------------------------------- 59 | int len, sizeleft = size; 60 | 61 | while (sizeleft) { 62 | 63 | len = recv(sock,buffer,sizeleft,flags); 64 | 65 | if (len == 0) { 66 | size = 0; 67 | break; 68 | }; 69 | 70 | if (len == -1) { 71 | 72 | if (errno != EAGAIN && errno != EWOULDBLOCK) { 73 | netloader_socket_error("recv", errno); 74 | break; 75 | } 76 | } else { 77 | sizeleft -=len; 78 | buffer +=len; 79 | } 80 | } 81 | return size; 82 | } 83 | 84 | 85 | //--------------------------------------------------------------------------------- 86 | static int decompress(int sock, FILE *fh, size_t filesize) { 87 | //--------------------------------------------------------------------------------- 88 | int ret; 89 | unsigned have; 90 | z_stream strm; 91 | size_t chunksize; 92 | 93 | /* allocate inflate state */ 94 | strm.zalloc = Z_NULL; 95 | strm.zfree = Z_NULL; 96 | strm.opaque = Z_NULL; 97 | strm.avail_in = 0; 98 | strm.next_in = Z_NULL; 99 | ret = inflateInit(&strm); 100 | if (ret != Z_OK) { 101 | netloader_socket_error("inflateInit failed.",ret); 102 | return ret; 103 | } 104 | 105 | size_t total = 0; 106 | /* decompress until deflate stream ends or end of file */ 107 | do { 108 | 109 | int len = recvall(sock, &chunksize, 4, 0); 110 | 111 | if (len != 4) { 112 | (void)inflateEnd(&strm); 113 | netloader_socket_error("Error getting chunk size",len); 114 | return Z_DATA_ERROR; 115 | } 116 | 117 | strm.avail_in = recvall(sock,in,chunksize,0); 118 | 119 | if (strm.avail_in == 0) { 120 | (void)inflateEnd(&strm); 121 | netloader_socket_error("remote closed socket.",0); 122 | return Z_DATA_ERROR; 123 | } 124 | 125 | strm.next_in = in; 126 | 127 | /* run inflate() on input until output buffer not full */ 128 | do { 129 | strm.avail_out = ZLIB_CHUNK; 130 | strm.next_out = out; 131 | ret = inflate(&strm, Z_NO_FLUSH); 132 | 133 | switch (ret) { 134 | 135 | case Z_NEED_DICT: 136 | ret = Z_DATA_ERROR; /* and fall through */ 137 | 138 | case Z_DATA_ERROR: 139 | case Z_MEM_ERROR: 140 | case Z_STREAM_ERROR: 141 | (void)inflateEnd(&strm); 142 | netloader_socket_error("inflate error",ret); 143 | return ret; 144 | } 145 | 146 | have = ZLIB_CHUNK - strm.avail_out; 147 | 148 | if (fwrite(out, 1, have, fh) != have || ferror(fh)) { 149 | (void)inflateEnd(&strm); 150 | netloader_socket_error("file write error",0); 151 | return Z_ERRNO; 152 | } 153 | 154 | total += have; 155 | sprintf(progress,"%zu (%d%%)",total, (100 * total) / filesize); 156 | netloader_draw_progress(); 157 | } while (strm.avail_out == 0); 158 | 159 | /* done when inflate() says it's done */ 160 | } while (ret != Z_STREAM_END); 161 | 162 | /* clean up and return */ 163 | (void)inflateEnd(&strm); 164 | return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; 165 | } 166 | 167 | 168 | int netloader_draw_error(void) { 169 | drawError(GFX_BOTTOM, "Failure", errbuf, 0); 170 | return 0; 171 | } 172 | 173 | int netloader_init(void) { 174 | SOC_buffer = memalign(0x1000, 0x100000); 175 | if(SOC_buffer == NULL) 176 | return -1; 177 | 178 | Result ret = socInit(SOC_buffer, 0x100000); 179 | if(ret != 0) 180 | { 181 | // need to free the shared memory block if something goes wrong 182 | socExit(); 183 | free(SOC_buffer); 184 | SOC_buffer = NULL; 185 | return -1; 186 | } 187 | return 0; 188 | } 189 | 190 | static int set_socket_nonblocking(int sock) { 191 | 192 | int flags = fcntl(sock, F_GETFL); 193 | 194 | if(flags == -1) return -1; 195 | 196 | int rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK); 197 | 198 | if(rc != 0) return -1; 199 | 200 | return 0; 201 | } 202 | 203 | int netloader_activate(void) { 204 | struct sockaddr_in serv_addr; 205 | // create udp socket for broadcast ping 206 | netloader_udpfd = socket(AF_INET, SOCK_DGRAM, 0); 207 | if (netloader_udpfd < 0) 208 | { 209 | netloader_socket_error("udp socket", errno ); 210 | return -1; 211 | } 212 | 213 | memset(&serv_addr, '0', sizeof(serv_addr)); 214 | serv_addr.sin_family = AF_INET; 215 | serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 216 | serv_addr.sin_port = htons(NETLOADER_PORT); 217 | 218 | if(bind(netloader_udpfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) { 219 | netloader_socket_error("bind udp socket", errno ); 220 | return -1; 221 | } 222 | 223 | if (set_socket_nonblocking(netloader_udpfd) == -1) 224 | { 225 | netloader_socket_error("listen fcntl", errno); 226 | return -1; 227 | } 228 | 229 | // create listening socket on all addresses on NETLOADER_PORT 230 | 231 | netloader_listenfd = socket(AF_INET, SOCK_STREAM, 0); 232 | if(netloader_listenfd < 0) 233 | { 234 | netloader_socket_error("socket", errno ); 235 | return -1; 236 | } 237 | 238 | int rc = bind(netloader_listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 239 | if(rc != 0) 240 | { 241 | netloader_socket_error("bind", errno); 242 | return -1; 243 | } 244 | 245 | if (set_socket_nonblocking(netloader_listenfd) == -1) 246 | { 247 | netloader_socket_error("listen fcntl", errno); 248 | return -1; 249 | } 250 | 251 | rc = listen(netloader_listenfd, 10); 252 | if(rc != 0) 253 | { 254 | netloader_socket_error("listen", errno); 255 | return -1; 256 | } 257 | 258 | return 0; 259 | } 260 | 261 | int netloader_deactivate(void) { 262 | // close all remaining sockets and allow mainloop to return to main menu 263 | if(netloader_listenfd >= 0) 264 | { 265 | closesocket(netloader_listenfd); 266 | netloader_listenfd = -1; 267 | } 268 | 269 | if(netloader_datafd >= 0) 270 | { 271 | closesocket(netloader_datafd); 272 | netloader_datafd = -1; 273 | } 274 | 275 | if(netloader_udpfd >= 0) 276 | { 277 | closesocket(netloader_udpfd); 278 | netloader_udpfd = -1; 279 | } 280 | 281 | return 0; 282 | } 283 | 284 | //--------------------------------------------------------------------------------- 285 | int load3DSX(int sock, u32 remote) { 286 | //--------------------------------------------------------------------------------- 287 | int len, namelen, filelen; 288 | char filename[256]; 289 | len = recvall(sock, &namelen, 4, 0); 290 | 291 | if (len != 4) { 292 | netloader_socket_error("Error getting name length", errno); 293 | return -1; 294 | } 295 | 296 | if (namelen >= sizeof(filename)-1) { 297 | netloader_socket_error("Filename length is too large",errno); 298 | return -1; 299 | } 300 | 301 | len = recvall(sock, filename, namelen, 0); 302 | 303 | if (len != namelen) { 304 | netloader_socket_error("Error getting filename", errno); 305 | return -1; 306 | } 307 | 308 | filename[namelen] = 0; 309 | 310 | len = recvall(sock, &filelen, 4, 0); 311 | 312 | if (len != 4) { 313 | netloader_socket_error("Error getting file length",errno); 314 | return -1; 315 | } 316 | 317 | int response = 0; 318 | 319 | chdir("sdmc:/3ds/"); 320 | 321 | int fd = open(filename,O_CREAT|O_WRONLY,ACCESSPERMS); 322 | 323 | if (fd < 0) { 324 | response = -1; 325 | } else { 326 | if (ftruncate(fd,filelen) == -1) { 327 | response = -2; 328 | netloader_socket_error("ftruncate",errno); 329 | } 330 | } 331 | 332 | send(sock,(int *)&response,sizeof(response),0); 333 | close(fd); 334 | 335 | netloadedPath=getcwd(NULL,0); 336 | strcat(netloadedPath,filename); 337 | 338 | FILE *file = fopen(filename,"wb"); 339 | char *writebuffer=malloc(65536); 340 | setvbuf(file,writebuffer,_IOFBF, 65536); 341 | 342 | if (response == 0) { 343 | //printf("transferring %s\n%d bytes.\n", filename, filelen); 344 | 345 | if (decompress(sock,file,filelen)==Z_OK) { 346 | send(sock,(int *)&response,sizeof(response),0); 347 | //printf("\ntransferring command line\n"); 348 | len = recvall(sock,(char*)&netloaded_cmdlen,4,0); 349 | if (netloaded_cmdlen) { 350 | netloaded_commandline = malloc(netloaded_cmdlen); 351 | len = recvall(sock, netloaded_commandline, netloaded_cmdlen,0); 352 | } 353 | } else { 354 | response = 1; 355 | } 356 | } 357 | 358 | free(netloadedPath); 359 | free(writebuffer); 360 | ftruncate(fileno(file), ftell(file)); 361 | fclose(file); 362 | 363 | if (response == 0) { 364 | netloadedPath=getcwd(NULL,0); 365 | strcat(netloadedPath,filename); 366 | netloadedPath = strchr(netloadedPath,'/'); 367 | } 368 | return response; 369 | } 370 | 371 | int netloader_loop(void) { 372 | 373 | struct sockaddr_in sa_udp_remote; 374 | char recvbuf[256]; 375 | socklen_t fromlen = sizeof(sa_udp_remote); 376 | 377 | int len = recvfrom(netloader_udpfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) &sa_udp_remote, &fromlen); 378 | 379 | if (len!=-1) { 380 | if (strncmp(recvbuf,"3dsboot",strlen("3dsboot")) == 0) { 381 | sa_udp_remote.sin_family=AF_INET; 382 | sa_udp_remote.sin_port=htons(17491); 383 | sendto(netloader_udpfd, "boot3ds", strlen("boot3ds"), 0, (struct sockaddr*) &sa_udp_remote,sizeof(sa_udp_remote)); 384 | } 385 | } 386 | 387 | if(netloader_listenfd >= 0 && netloader_datafd < 0) { 388 | netloader_datafd = accept(netloader_listenfd, (struct sockaddr*)NULL, NULL); 389 | if(netloader_datafd < 0) 390 | { 391 | if(errno != -EWOULDBLOCK && errno != EWOULDBLOCK) 392 | { 393 | netloader_socket_error("accept", errno); 394 | return -1; 395 | } 396 | } 397 | else 398 | { 399 | closesocket(netloader_listenfd); 400 | netloader_listenfd = -1; 401 | } 402 | } 403 | 404 | if(netloader_datafd >= 0) 405 | { 406 | int result = load3DSX(netloader_datafd,0); 407 | netloader_deactivate(); 408 | if (result== 0) return 1; 409 | } 410 | 411 | return 0; 412 | } 413 | 414 | int netloader_exit(void) { 415 | Result ret = socExit(); 416 | if(ret != 0) 417 | return -1; 418 | return 0; 419 | } 420 | -------------------------------------------------------------------------------- /source/netloader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NETLOADER_PORT 17491 4 | 5 | extern char *netloadedPath; 6 | extern char *netloaded_commandline; 7 | extern int netloaded_cmdlen; 8 | extern bool netloader_boot; 9 | 10 | int netloader_activate(void); 11 | int netloader_deactivate(void); 12 | int netloader_init(void); 13 | int netloader_loop(void); 14 | int netloader_exit(void); 15 | int netloader_draw_error(void); 16 | -------------------------------------------------------------------------------- /source/regionfree.c: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | 3 | #include "regionfree.h" 4 | 5 | bool regionFreeAvailable = false; 6 | bool regionFreeGamecardIn = false; 7 | Handle nssHandle = 0; 8 | 9 | smdh_s gamecardSmdh; 10 | menuEntry_s gamecardMenuEntry; 11 | 12 | Result regionFreeInit() 13 | { 14 | Result ret = srvGetServiceHandle(&nssHandle, "ns:s"); 15 | 16 | if(!ret)regionFreeAvailable = true; 17 | regionFreeGamecardIn = false; 18 | 19 | return ret; 20 | } 21 | 22 | Result regionFreeExit() 23 | { 24 | return svcCloseHandle(nssHandle); 25 | } 26 | 27 | void regionFreeUpdate() 28 | { 29 | if(!regionFreeAvailable)return; 30 | 31 | Result ret = loadGamecardIcon(&gamecardSmdh); 32 | 33 | regionFreeGamecardIn = (ret == 0); 34 | 35 | if(regionFreeGamecardIn)extractSmdhData(&gamecardSmdh, gamecardMenuEntry.name, gamecardMenuEntry.description, gamecardMenuEntry.author, gamecardMenuEntry.iconData); 36 | } 37 | 38 | Result loadGamecardIcon(smdh_s* out) 39 | { 40 | if(!out)return -1; 41 | 42 | Handle fileHandle; 43 | static const u32 archivePath[] = {0x00000000, 0x00000000, 0x00000002, 0x00000000}; 44 | static const u32 filePath[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000}; 45 | Result ret = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path){PATH_BINARY, 0x10, (u8*)archivePath}, (FS_Path){PATH_BINARY, 0x14, (u8*)filePath}, FS_OPEN_READ, 0); 46 | if(ret)return ret; 47 | 48 | u32 bytesRead; 49 | ret = FSFILE_Read(fileHandle, &bytesRead, 0x0, out, sizeof(smdh_s)); 50 | 51 | FSFILE_Close(fileHandle); 52 | 53 | return ret; 54 | } 55 | 56 | Result NSS_Reboot(u32 pid_low, u32 pid_high, u8 mediatype, u8 flag) 57 | { 58 | Result ret = 0; 59 | u32 *cmdbuf = getThreadCommandBuffer(); 60 | 61 | cmdbuf[0] = 0x00100180; 62 | cmdbuf[1] = flag; 63 | cmdbuf[2] = pid_low; 64 | cmdbuf[3] = pid_high; 65 | cmdbuf[4] = mediatype; 66 | cmdbuf[5] = 0x00000000; 67 | cmdbuf[6] = 0x00000000; 68 | 69 | if((ret = svcSendSyncRequest(nssHandle))!=0) return ret; 70 | 71 | return (Result)cmdbuf[1]; 72 | } 73 | 74 | Result regionFreeRun() 75 | { 76 | Result ret = NSS_Reboot(0x00000000, 0x00000000, 0x2, 0x1); 77 | 78 | regionFreeExit(); 79 | 80 | return ret; 81 | } 82 | -------------------------------------------------------------------------------- /source/regionfree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include <3ds.h> 3 | 4 | #include "menu.h" 5 | #include "smdh.h" 6 | 7 | #define REGIONFREE_PATH "regionfree:/" 8 | 9 | extern bool regionFreeAvailable; 10 | extern bool regionFreeGamecardIn; 11 | 12 | extern menuEntry_s gamecardMenuEntry; 13 | 14 | Result regionFreeInit(); 15 | Result regionFreeExit(); 16 | Result regionFreeRun(); 17 | 18 | void regionFreeUpdate(); 19 | 20 | Result loadGamecardIcon(smdh_s* out); 21 | -------------------------------------------------------------------------------- /source/scanner.c: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | #include 3 | #include 4 | #include "scanner.h" 5 | #include "menu.h" 6 | 7 | #define _3DSX_MAGIC 0x58534433 // '3DSX' 8 | 9 | typedef struct 10 | { 11 | u32 magic; 12 | u16 headerSize, relocHdrSize; 13 | u32 formatVer; 14 | u32 flags; 15 | 16 | // Sizes of the code, rodata and data segments + 17 | // size of the BSS section (uninitialized latter half of the data segment) 18 | u32 codeSegSize, rodataSegSize, dataSegSize, bssSize; 19 | } _3DSX_Header; 20 | 21 | const char* servicesThatMatter[] = 22 | { 23 | "soc:U", 24 | "csnd:SND", 25 | "qtm:s", 26 | "nfc:u", 27 | "http:C" 28 | }; 29 | 30 | void initMetadata(executableMetadata_s* em) 31 | { 32 | if(!em)return; 33 | 34 | em->scanned = false; 35 | 36 | em->sectionSizes[0] = 0; 37 | em->sectionSizes[1] = 0; 38 | em->sectionSizes[2] = 0; 39 | 40 | memset(em->servicesThatMatter, 0x00, sizeof(em->servicesThatMatter)); 41 | } 42 | 43 | Result scan3dsx(char* path, char** patterns, int num_patterns, u32* sectionSizes, bool* patternsFound) 44 | { 45 | if(!path)return -1; 46 | 47 | FILE* f = fopen(path, "rb"); 48 | if(!f)return -2; 49 | 50 | Result ret = 0; 51 | 52 | _3DSX_Header hdr; 53 | fread(&hdr, sizeof(_3DSX_Header), 1, f); 54 | 55 | if(hdr.magic != _3DSX_MAGIC) 56 | { 57 | ret = -3; 58 | goto end; 59 | } 60 | 61 | if(sectionSizes) 62 | { 63 | sectionSizes[0] = hdr.codeSegSize; 64 | sectionSizes[1] = hdr.rodataSegSize; 65 | sectionSizes[2] = hdr.dataSegSize + hdr.bssSize; 66 | } 67 | 68 | if(patterns && num_patterns && patternsFound) 69 | { 70 | const int buffer_size = 0x1000; 71 | const int max_pattern_size = 0x10; 72 | 73 | static u8 buffer[0x1000 + 0x10]; 74 | 75 | int j; 76 | for(j=0; jscanned)return; 128 | 129 | Result ret = scan3dsx(path, (char**)servicesThatMatter, NUM_SERVICESTHATMATTER, em->sectionSizes, (bool*)em->servicesThatMatter); 130 | 131 | if(!ret)em->scanned = true; 132 | else em->scanned = false; 133 | } 134 | 135 | void scanMenuEntry(menuEntry_s* me) 136 | { 137 | if(!me)return; 138 | 139 | executableMetadata_s* em = &me->descriptor.executableMetadata; 140 | 141 | static char tmp[0x200]; 142 | snprintf(tmp, 0x200, "sdmc:%s", me->executablePath); 143 | 144 | if(me->descriptor.autodetectServices) 145 | { 146 | // if autodetection is enabled (default), we just scan the 3dsx for service names (not ideal but whatchagonnado) 147 | scanExecutable(em, tmp); 148 | }else{ 149 | // if it's disabled, then we just populate the metadata structure with section sizes and requested services from descriptor 150 | int i, j; 151 | scan3dsx(tmp, NULL, 0, em->sectionSizes, NULL); 152 | 153 | for(i=0; idescriptor.numRequestedServices; i++) 154 | { 155 | for(j=0; jdescriptor.requestedServices[i].name, servicesThatMatter[j])) 158 | { 159 | em->servicesThatMatter[j] = me->descriptor.requestedServices[i].priority; 160 | break; 161 | } 162 | } 163 | } 164 | em->scanned = true; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /source/scanner.h: -------------------------------------------------------------------------------- 1 | #ifndef SCANNER_H 2 | #define SCANNER_H 3 | 4 | #define NUM_SERVICESTHATMATTER 5 5 | 6 | typedef struct 7 | { 8 | bool scanned; 9 | u32 sectionSizes[3]; 10 | u8 servicesThatMatter[NUM_SERVICESTHATMATTER]; 11 | }executableMetadata_s; 12 | 13 | void initMetadata(executableMetadata_s* em); 14 | 15 | Result scan3dsx(char* path, char** patterns, int num_patterns, u32* sectionSizes, bool* patternsFound); 16 | void scanExecutable(executableMetadata_s* em, char* path); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /source/shortcut.cpp: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | 3 | #include "shortcut.h" 4 | #include "tinyxml2.h" 5 | 6 | using namespace tinyxml2; 7 | 8 | void initShortcut(shortcut_s* s) 9 | { 10 | if(!s)return; 11 | 12 | s->executable = NULL; 13 | s->descriptor = NULL; 14 | s->icon = NULL; 15 | s->arg = NULL; 16 | s->name = NULL; 17 | s->description = NULL; 18 | s->author = NULL; 19 | } 20 | 21 | Result createShortcut(shortcut_s* s, char* path) 22 | { 23 | if(!s || !path)return -1; 24 | 25 | initShortcut(s); 26 | 27 | return loadShortcut(s, path); 28 | } 29 | 30 | void loadXmlString(char** out, XMLElement* in, const char* key) 31 | { 32 | if(!out || !in || !key)return; 33 | 34 | XMLElement* node = in->FirstChildElement(key); 35 | if(node) 36 | { 37 | const char* str = node->GetText(); 38 | if(str) 39 | { 40 | *out = (char*)malloc(strlen(str) + 1); 41 | if(*out) strcpy(*out, str); 42 | } 43 | } 44 | } 45 | 46 | // TODO : error checking 47 | Result loadShortcut(shortcut_s* s, char* path) 48 | { 49 | if(!s || !path)return -1; 50 | 51 | XMLDocument doc; 52 | if(doc.LoadFile(path))return -2; 53 | 54 | XMLElement* shortcut = doc.FirstChildElement("shortcut"); 55 | if(shortcut) 56 | { 57 | XMLElement* executable = shortcut->FirstChildElement("executable"); 58 | if(executable) 59 | { 60 | const char* str = executable->GetText(); 61 | if(str) 62 | { 63 | s->executable = (char*)malloc(strlen(str) + 1); 64 | if(s->executable) strcpy(s->executable, str); 65 | } 66 | } 67 | if(!s->executable) return -3; 68 | 69 | XMLElement* descriptor = shortcut->FirstChildElement("descriptor"); 70 | const char* descriptor_path = path; 71 | if(descriptor) descriptor_path = descriptor->GetText(); 72 | if(descriptor_path) 73 | { 74 | s->descriptor = (char*)malloc(strlen(descriptor_path) + 1); 75 | if(s->descriptor) strcpy(s->descriptor, descriptor_path); 76 | } 77 | 78 | loadXmlString(&s->icon, shortcut, "icon"); 79 | loadXmlString(&s->arg, shortcut, "arg"); 80 | loadXmlString(&s->name, shortcut, "name"); 81 | loadXmlString(&s->description, shortcut, "description"); 82 | loadXmlString(&s->author, shortcut, "author"); 83 | }else return -4; 84 | 85 | return 0; 86 | } 87 | 88 | void freeShortcut(shortcut_s* s) 89 | { 90 | if(!s)return; 91 | 92 | free(s->executable); 93 | s->executable = NULL; 94 | 95 | free(s->descriptor); 96 | s->descriptor = NULL; 97 | 98 | free(s->icon); 99 | s->icon = NULL; 100 | 101 | free(s->arg); 102 | s->arg = NULL; 103 | } 104 | -------------------------------------------------------------------------------- /source/shortcut.h: -------------------------------------------------------------------------------- 1 | #ifndef SHORTCUT_H 2 | #define SHORTCUT_H 3 | 4 | #include <3ds.h> 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef struct 11 | { 12 | char* executable; 13 | char* descriptor; 14 | char* icon; 15 | char* arg; 16 | char* name; 17 | char* description; 18 | char* author; 19 | }shortcut_s; 20 | 21 | void initShortcut(shortcut_s* d); 22 | void freeShortcut(shortcut_s* d); 23 | Result loadShortcut(shortcut_s* d, char* path); 24 | Result createShortcut(shortcut_s* d, char* path); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /source/smdh.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include <3ds.h> 3 | 4 | #include "smdh.h" 5 | #include "utils.h" 6 | 7 | //shamelessly stolen from bch2obj.py 8 | u8 tileOrder[]={0,1,8,9,2,3,10,11,16,17,24,25,18,19,26,27,4,5,12,13,6,7,14,15,20,21,28,29,22,23,30,31,32,33,40,41,34,35,42,43,48,49,56,57,50,51,58,59,36,37,44,45,38,39,46,47,52,53,60,61,54,55,62,63}; 9 | 10 | static inline void putPixel565(u8* dst, u8 x, u8 y, u16 v) 11 | { 12 | dst[((47-y)+x*48)*3+0]=(v&0x1F)<<3; 13 | dst[((47-y)+x*48)*3+1]=((v>>5)&0x3F)<<2; 14 | dst[((47-y)+x*48)*3+2]=((v>>11)&0x1F)<<3; 15 | } 16 | 17 | int extractSmdhData(smdh_s* s, char* name, char* desc, char* auth, u8* iconData) 18 | { 19 | if(!s)return -1; 20 | if(s->header.magic!=0x48444D53)return -2; 21 | 22 | if(name)unicodeToChar(name, s->applicationTitles[1].shortDescription, 0x40); 23 | if(desc)unicodeToChar(desc, s->applicationTitles[1].longDescription, 0x80); 24 | if(auth)unicodeToChar(auth, s->applicationTitles[1].publisher, 0x40); 25 | if(iconData) 26 | { 27 | u16* data=s->bigIconData; 28 | //convert RGB565 to RGB24 29 | int i, j, k; 30 | for(j=0; j<48; j+=8) 31 | { 32 | for(i=0; i<48; i+=8) 33 | { 34 | //parse tiling... 35 | for(k=0; k<8*8; k++) 36 | { 37 | u8 x=tileOrder[k]&0x7; 38 | u8 y=tileOrder[k]>>3; 39 | putPixel565(iconData, i+x, j+y, *data++); 40 | } 41 | } 42 | } 43 | } 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /source/smdh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include <3ds.h> 3 | 4 | typedef struct 5 | { 6 | u32 magic; 7 | u16 version; 8 | u16 reserved; 9 | }smdhHeader_s; 10 | 11 | typedef struct 12 | { 13 | u16 shortDescription[0x40]; 14 | u16 longDescription[0x80]; 15 | u16 publisher[0x40]; 16 | }smdhTitle_s; 17 | 18 | typedef struct 19 | { 20 | u8 gameRatings[0x10]; 21 | u32 regionLock; 22 | u8 matchMakerId[0xC]; 23 | u32 flags; 24 | u16 eulaVersion; 25 | u16 reserved; 26 | u32 defaultFrame; 27 | u32 cecId; 28 | }smdhSettings_s; 29 | 30 | typedef struct 31 | { 32 | smdhHeader_s header; 33 | smdhTitle_s applicationTitles[16]; 34 | smdhSettings_s settings; 35 | u8 reserved[0x8]; 36 | u8 smallIconData[0x480]; 37 | u16 bigIconData[0x900]; 38 | }smdh_s; 39 | 40 | int extractSmdhData(smdh_s* s, char* name, char* desc, char* auth, u8* iconData); 41 | -------------------------------------------------------------------------------- /source/statusbar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "statusbar.h" 5 | #include "gfx.h" 6 | 7 | #include "wifi_full_bin.h" 8 | #include "wifi_none_bin.h" 9 | #include "battery_full_bin.h" 10 | #include "battery_mid_high_bin.h" 11 | #include "battery_mid_low_bin.h" 12 | #include "battery_low_bin.h" 13 | #include "battery_lowest_bin.h" 14 | #include "battery_charging_bin.h" 15 | 16 | u8* batteryLevels[] = { 17 | (u8*)battery_lowest_bin, 18 | (u8*)battery_lowest_bin, 19 | (u8*)battery_low_bin, 20 | (u8*)battery_mid_low_bin, 21 | (u8*)battery_mid_high_bin, 22 | (u8*)battery_full_bin, 23 | }; 24 | 25 | #define SECONDS_IN_DAY 86400 26 | #define SECONDS_IN_HOUR 3600 27 | #define SECONDS_IN_MINUTE 60 28 | 29 | void drawStatusBar(bool wifiStatus, bool charging, int batteryLevel) 30 | { 31 | u64 timeInSeconds = osGetTime() / 1000; 32 | u64 dayTime = timeInSeconds % SECONDS_IN_DAY; 33 | u8 hour = dayTime / SECONDS_IN_HOUR; 34 | u8 min = (dayTime % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE; 35 | u8 seconds = dayTime % SECONDS_IN_MINUTE; 36 | 37 | char timeString[9]; 38 | sprintf(timeString, "%02d:%02d:%02d", hour, min, seconds); 39 | gfxDrawText(GFX_TOP, GFX_LEFT, NULL, timeString, 240 - 18, 400 / 2 - 16); 40 | 41 | if(wifiStatus) 42 | { 43 | gfxDrawSpriteAlphaBlend(GFX_TOP, GFX_LEFT, (u8*)wifi_full_bin, 18, 20, 240 - 18, 0); 44 | } 45 | else 46 | { 47 | gfxDrawSpriteAlphaBlend(GFX_TOP, GFX_LEFT, (u8*)wifi_none_bin, 18, 20, 240 - 18, 0); 48 | } 49 | 50 | if(charging) 51 | { 52 | gfxDrawSpriteAlphaBlend(GFX_TOP, GFX_LEFT, (u8*)battery_charging_bin, 18, 27, 240 - 18, 400 - 27); 53 | } 54 | else 55 | { 56 | gfxDrawSpriteAlphaBlend(GFX_TOP, GFX_LEFT, batteryLevels[batteryLevel], 18, 27, 240 - 18, 400 - 27); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /source/statusbar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include <3ds.h> 3 | 4 | void drawStatusBar(bool wifiStatus, bool charging, int batteryLevel); -------------------------------------------------------------------------------- /source/text.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include <3ds.h> 5 | #include "text.h" 6 | #include "font_bin.h" 7 | 8 | #include "font.h" 9 | 10 | const u8* font=font_bin; 11 | 12 | //this code is not meant to be readable 13 | int drawCharacter(u8* fb, font_s* f, char c, s16 x, s16 y, u16 w, u16 h) 14 | { 15 | charDesc_s* cd=&f->desc[(int)c]; 16 | if(!cd->data)return 0; 17 | x+=cd->xo; y+=f->height-cd->yo-cd->h; 18 | if(x<0 || x+cd->w>=w || y<-cd->h || y>=h+cd->h)return cd->xa; 19 | u8* charData=cd->data; 20 | int i, j; 21 | s16 cy=y, ch=cd->h, cyo=0; 22 | if(y<0){cy=0;cyo=-y;ch=cd->h-cyo;} 23 | else if(y+ch>h)ch=h-y; 24 | fb+=(x*h+cy)*3; 25 | const u8 r=f->color[0], g=f->color[1], b=f->color[2]; 26 | for(i=0;iw;i++) 27 | { 28 | charData+=cyo; 29 | for(j=0;j>8; 35 | fb[1]=(fb[1]*(0xFF-v)+(g*v))>>8; 36 | fb[2]=(fb[2]*(0xFF-v)+(r*v))>>8; 37 | } 38 | fb+=3; 39 | } 40 | charData+=(cd->h-(cyo+ch)); 41 | fb+=(h-ch)*3; 42 | } 43 | return cd->xa; 44 | } 45 | 46 | int getStringLength(font_s* f, char* str) 47 | { 48 | if(!f)f=&fontDefault; 49 | if(!str)return 0; 50 | int ret; for(ret=0;*str;ret+=f->desc[(int)*str++].xa); 51 | return ret; 52 | } 53 | 54 | void drawString(u8* fb, font_s* f, char* str, s16 x, s16 y, u16 w, u16 h) 55 | { 56 | drawStringN(fb, f, str, strlen(str), x, y, w, h); 57 | } 58 | 59 | void drawStringN(u8* fb, font_s* f, char* str, u16 length, s16 x, s16 y, u16 w, u16 h) 60 | { 61 | if(!f || !fb || !str)return; 62 | int k; int dx=0, dy=0; 63 | k=strlen(str); if(k 2 | #include 3 | #include 4 | 5 | #include <3ds.h> 6 | 7 | #include "titles.h" 8 | #include "error.h" 9 | 10 | extern int debugValues[100]; 11 | 12 | void titlesInit() 13 | { 14 | amInit(); 15 | } 16 | 17 | void titlesExit() 18 | { 19 | amExit(); 20 | } 21 | 22 | void initTitleInfo(titleInfo_s* ti, u8 mediatype, u64 title_id) 23 | { 24 | if(!ti)return; 25 | 26 | ti->mediatype = mediatype; 27 | ti->title_id = title_id; 28 | ti->icon = NULL; 29 | } 30 | 31 | void freeTitleInfo(titleInfo_s* ti) 32 | { 33 | if(!ti)return; 34 | 35 | ti->title_id = 0x0; 36 | if(ti->icon)free(ti->icon); 37 | ti->icon = NULL; 38 | } 39 | 40 | Result loadTitleInfoIcon(titleInfo_s* ti) 41 | { 42 | if(!ti || ti->icon)return -1; 43 | 44 | ti->icon = malloc(sizeof(smdh_s)); 45 | if(!ti->icon)return -1; 46 | 47 | Handle fileHandle; 48 | u32 archivePath[] = {ti->title_id & 0xFFFFFFFF, (ti->title_id >> 32) & 0xFFFFFFFF, ti->mediatype, 0x00000000}; 49 | static const u32 filePath[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000}; 50 | Result ret = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path){PATH_BINARY, 0x10, (u8*)archivePath}, (FS_Path){PATH_BINARY, 0x14, (u8*)filePath}, FS_OPEN_READ, 0); 51 | 52 | if(ret) 53 | { 54 | free(ti->icon); 55 | ti->icon = NULL; 56 | return ret; 57 | } 58 | 59 | u32 bytesRead; 60 | ret = FSFILE_Read(fileHandle, &bytesRead, 0x0, ti->icon, sizeof(smdh_s)); 61 | 62 | if(ret) 63 | { 64 | free(ti->icon); 65 | ti->icon = NULL; 66 | } 67 | 68 | FSFILE_Close(fileHandle); 69 | 70 | return ret; 71 | } 72 | 73 | bool application_filter(u64 tid) 74 | { 75 | u32 tid_high = tid >> 32; 76 | return (tid_high == 0x00040010 || tid_high == 0x00040000 || tid_high == 0x00040002); 77 | } 78 | 79 | void initTitleList(titleList_s* tl, titleFilter_callback filter, u8 mediatype) 80 | { 81 | if(!tl)return; 82 | 83 | tl->mediatype = mediatype; 84 | 85 | if(tl->filter) tl->filter = filter; 86 | else tl->filter = &application_filter; 87 | 88 | tl->num = 0; 89 | tl->titles = NULL; 90 | 91 | populateTitleList(tl); 92 | } 93 | 94 | void freeTitleList(titleList_s* tl) 95 | { 96 | if(!tl)return; 97 | 98 | int i; 99 | for(i=0; inum; i++)freeTitleInfo(&tl->titles[i]); 100 | 101 | tl->num = 0; 102 | if(tl->titles)free(tl->titles); 103 | tl->titles = NULL; 104 | } 105 | 106 | int populateTitleList(titleList_s* tl) 107 | { 108 | if(!tl)return 0; 109 | Result ret; 110 | 111 | u32 old_num = tl->num; 112 | 113 | u32 num; 114 | ret = AM_GetTitleCount(tl->mediatype, &num); 115 | 116 | if(ret) 117 | { 118 | freeTitleList(tl); 119 | }else if(num){ 120 | // temp buffer is not ideal, but i like modularity 121 | u64* tmp = (u64*)malloc(sizeof(u64) * num); 122 | 123 | if(!tmp) 124 | { 125 | tl->num = 0; 126 | return 1; 127 | } 128 | 129 | ret = AM_GetTitleList(&num, tl->mediatype, num, tmp); 130 | 131 | if(!ret) 132 | { 133 | int i; 134 | 135 | // apply tid filter 136 | for(i=0; ifilter(tmp[i])) 139 | { 140 | num--; 141 | tmp[i] = tmp[num]; 142 | i--; 143 | } 144 | } 145 | 146 | if(tl->num != num || tl->mediatype == 2) 147 | { 148 | freeTitleList(tl); 149 | 150 | tl->num = num; 151 | 152 | if(tl->num) tl->titles = malloc(sizeof(titleInfo_s) * tl->num); 153 | 154 | if(tl->titles) 155 | { 156 | for(i=0; inum; i++) 157 | { 158 | initTitleInfo(&tl->titles[i], tl->mediatype, tmp[i]); 159 | } 160 | } 161 | } 162 | }else tl->num = 0; 163 | 164 | free(tmp); 165 | } 166 | 167 | return old_num != tl->num; 168 | } 169 | 170 | titleInfo_s* findTitleList(titleList_s* tl, u64 tid) 171 | { 172 | if(!tl)return NULL; 173 | 174 | // special case : gamecard mediatype with 0 tid 175 | if(!tid && tl->mediatype == 2 && tl->num)return &tl->titles[0]; 176 | 177 | int i; 178 | for(i=0; inum; i++) 179 | { 180 | if(tl->titles[i].title_id == tid)return &tl->titles[i]; 181 | } 182 | 183 | return NULL; 184 | } 185 | 186 | titleInfo_s* findTitleBrowser(titleBrowser_s* tb, u8 mediatype, u64 tid) 187 | { 188 | if(!tb || mediatype > 2)return NULL; 189 | 190 | return findTitleList(&tb->lists[2-mediatype], tid); 191 | } 192 | 193 | void initTitleBrowser(titleBrowser_s* tb, titleFilter_callback filter) 194 | { 195 | if(!tb)return; 196 | 197 | int i; 198 | for(i=0; i<3; i++) 199 | { 200 | initTitleList(&tb->lists[i], filter, (u8)2-i); 201 | } 202 | 203 | tb->total = 0; 204 | tb->nextCheck = 0; 205 | tb->selectedId = 0; 206 | tb->selected = NULL; 207 | } 208 | 209 | void updateTitleBrowser(titleBrowser_s* tb) 210 | { 211 | if(!tb)return; 212 | 213 | int i; 214 | 215 | if (osGetTime() > tb->nextCheck) 216 | { 217 | bool updated = false; 218 | 219 | tb->total = 0; 220 | 221 | for(i=0; i<3; i++) 222 | { 223 | updated = populateTitleList(&tb->lists[i]) || updated; 224 | tb->total += tb->lists[i].num; 225 | } 226 | 227 | if(updated) 228 | { 229 | tb->selectedId = 0; 230 | } 231 | 232 | tb->nextCheck = osGetTime() + 250; 233 | } 234 | 235 | tb->selected = NULL; 236 | 237 | if(!tb->total)return; 238 | 239 | u32 padDown = hidKeysDown(); 240 | 241 | int move = 0; 242 | if(padDown & KEY_LEFT)move--; 243 | if(padDown & KEY_RIGHT)move++; 244 | 245 | tb->selectedId += move; 246 | 247 | while(tb->selectedId < 0) tb->selectedId += tb->total; 248 | while(tb->selectedId >= tb->total) tb->selectedId -= tb->total; 249 | 250 | int id = tb->selectedId; 251 | for(i=0; i<3; i++) 252 | { 253 | const titleList_s* tl = &tb->lists[i]; 254 | if(id >= 0 && id < tl->num) 255 | { 256 | tb->selected = &tl->titles[id]; 257 | break; 258 | }else id -= tl->num; 259 | } 260 | 261 | if(tb->selected) 262 | { 263 | if(!tb->selected->icon)loadTitleInfoIcon(tb->selected); 264 | if(tb->selected->icon)extractSmdhData(tb->selected->icon, tb->selectedEntry.name, tb->selectedEntry.description, tb->selectedEntry.author, tb->selectedEntry.iconData); 265 | else 266 | { 267 | tb->selected = NULL; 268 | if(!move)tb->selectedId++; 269 | else tb->selectedId += move; 270 | } 271 | } 272 | } 273 | 274 | void drawTitleBrowser(titleBrowser_s* tb) 275 | { 276 | if(!tb)return; 277 | 278 | if(tb->selected && tb->selected->icon) 279 | { 280 | drawError(GFX_BOTTOM, 281 | "Target title selector", 282 | " The application you're trying to run requires that you select a target.\n\n" 283 | " A : Select target\n" 284 | " B : Cancel\n", 285 | 10-drawMenuEntry(&tb->selectedEntry, GFX_BOTTOM, 240, 9, true)); 286 | }else{ 287 | drawError(GFX_BOTTOM, 288 | "Target title selector", 289 | " The application you're trying to run requires that you select a target.\n" 290 | " No adequate target title could be found. :(\n\n" 291 | " B : Cancel\n", 292 | 0); 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /source/titles.h: -------------------------------------------------------------------------------- 1 | #ifndef TITLES_H 2 | #define TITLES_H 3 | 4 | #include <3ds.h> 5 | 6 | #include "smdh.h" 7 | #include "menu.h" 8 | 9 | typedef struct 10 | { 11 | u8 mediatype; 12 | u64 title_id; 13 | smdh_s* icon; 14 | }titleInfo_s; 15 | 16 | typedef bool (*titleFilter_callback)(u64 tid); 17 | 18 | typedef struct 19 | { 20 | u8 mediatype; 21 | u32 num; 22 | titleInfo_s* titles; 23 | titleFilter_callback filter; 24 | }titleList_s; 25 | 26 | typedef struct 27 | { 28 | titleList_s lists[3]; 29 | u32 total; 30 | u64 nextCheck; 31 | int selectedId; 32 | titleInfo_s* selected; 33 | menuEntry_s selectedEntry; 34 | }titleBrowser_s; 35 | 36 | void titlesInit(); 37 | void titlesExit(); 38 | 39 | void initTitleInfo(titleInfo_s* ti, u8 mediatype, u64 title_id); 40 | void freeTitleInfo(titleInfo_s* ti); 41 | 42 | void initTitleList(titleList_s* tl, titleFilter_callback filter, u8 mediatype); 43 | void freeTitleList(titleList_s* tl); 44 | int populateTitleList(titleList_s* tl); 45 | 46 | void initTitleBrowser(titleBrowser_s* tb, titleFilter_callback filter); 47 | void updateTitleBrowser(titleBrowser_s* tb); 48 | void drawTitleBrowser(titleBrowser_s* tb); 49 | titleInfo_s* findTitleBrowser(titleBrowser_s* tb, u8 mediatype, u64 tid); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /source/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | static inline void unicodeToChar(char* dst, u16* src, int max) 4 | { 5 | if(!src || !dst)return; 6 | int n=0; 7 | while(*src && n 2 | #include 3 | #include 4 | #include "water.h" 5 | 6 | void initWaterEffect(waterEffect_s* we, u16 n, u16 s, float d, float sf, u16 w, s16 offset) 7 | { 8 | if(!we)return; 9 | 10 | we->numControlPoints=n; 11 | we->neighborhoodSize=s; 12 | we->dampFactor=d; 13 | we->springFactor=sf; 14 | we->width=w; 15 | we->offset=offset; 16 | we->controlPoints=calloc(n, sizeof(float)); 17 | we->controlPointSpeeds=calloc(n, sizeof(float)); 18 | } 19 | 20 | //dst shouldn't have been initialized 21 | void copyWaterEffect(waterEffect_s* dst, waterEffect_s* src) 22 | { 23 | if(!dst || !src)return; 24 | 25 | initWaterEffect(dst, src->numControlPoints, src->neighborhoodSize, src->dampFactor, src->springFactor, src->width, src->offset); 26 | memcpy(dst->controlPoints, src->controlPoints, sizeof(float)*src->numControlPoints); 27 | memcpy(dst->controlPointSpeeds, src->controlPointSpeeds, sizeof(float)*src->numControlPoints); 28 | } 29 | 30 | void killWaterEffect(waterEffect_s* we) 31 | { 32 | if(!we)return; 33 | 34 | free(we->controlPoints); 35 | free(we->controlPointSpeeds); 36 | } 37 | 38 | float getNeighborAverage(waterEffect_s* we, int k) 39 | { 40 | if(!we || k<0 || k>=we->numControlPoints)return 0.0f; 41 | 42 | float sum=0.0f; 43 | float factors=0.0f; 44 | 45 | int i; 46 | for(i=k-we->neighborhoodSize; ineighborhoodSize; i++) 47 | { 48 | if(i==k)continue; 49 | const int d=i-k; 50 | const float f=fabs(1.0f/d); // TODO : better function (gauss ?) 51 | float v=0.0f; 52 | if(i>=0 && inumControlPoints)v=we->controlPoints[i]; 53 | sum+=f*v; 54 | factors+=f; 55 | } 56 | 57 | return sum/factors; 58 | } 59 | 60 | float evaluateWater(waterEffect_s* we, u16 x) 61 | { 62 | if(!we || x>=we->width)return 0.0f; 63 | 64 | const float vx=((float)((x-we->offset)*we->numControlPoints))/we->width; 65 | const int k=(int)vx; 66 | const float f=vx-(float)k; 67 | 68 | return we->controlPoints[k]*(1.0f-f)+we->controlPoints[k+1]*f; 69 | } 70 | 71 | void exciteWater(waterEffect_s* we, float v, u16 k, bool absolute) 72 | { 73 | if(!we || k>=we->numControlPoints)return; 74 | 75 | if(absolute) 76 | { 77 | we->controlPoints[k]=v; 78 | we->controlPointSpeeds[k]=0.0f; 79 | }else we->controlPoints[k]+=v; 80 | } 81 | 82 | void updateWaterEffect(waterEffect_s* we) 83 | { 84 | if(!we)return; 85 | 86 | waterEffect_s tmpwe; 87 | copyWaterEffect(&tmpwe, we); 88 | 89 | int k; 90 | for(k=0; knumControlPoints; k++) 91 | { 92 | float rest=getNeighborAverage(&tmpwe, k); 93 | we->controlPointSpeeds[k]*=we->dampFactor; 94 | we->controlPointSpeeds[k]+=(rest-we->controlPoints[k])*we->springFactor; 95 | we->controlPoints[k]+=we->controlPointSpeeds[k]; 96 | } 97 | 98 | killWaterEffect(&tmpwe); 99 | } 100 | -------------------------------------------------------------------------------- /source/water.h: -------------------------------------------------------------------------------- 1 | #ifndef WATER_H 2 | #define WATER_H 3 | 4 | #include <3ds/types.h> 5 | 6 | typedef struct 7 | { 8 | u16 numControlPoints; 9 | u16 neighborhoodSize; 10 | u16 width; 11 | s16 offset; 12 | float springFactor; 13 | float dampFactor; 14 | float* controlPoints; 15 | float* controlPointSpeeds; 16 | }waterEffect_s; 17 | 18 | void initWaterEffect(waterEffect_s* we, u16 n, u16 s, float d, float sf, u16 w, s16 offset); 19 | void killWaterEffect(waterEffect_s* we); 20 | 21 | float evaluateWater(waterEffect_s* we, u16 x); 22 | 23 | void exciteWater(waterEffect_s* we, float v, u16 k, bool absolute); 24 | void updateWaterEffect(waterEffect_s* we); 25 | 26 | #endif 27 | --------------------------------------------------------------------------------