1 Commits

Author SHA1 Message Date
730a0cd3d7 Add libvorbisdec to deps (closes #162) 2018-05-13 09:21:41 -04:00
52 changed files with 9914 additions and 1237 deletions

1
.gitignore vendored
View File

@@ -58,7 +58,6 @@ dkms.conf
# Build files # Build files
build/ build/
out/ out/
romfs/
# Sublime stuff # Sublime stuff
.clang_complete .clang_complete

163
Makefile
View File

@@ -9,29 +9,6 @@ endif
TOPDIR ?= $(CURDIR) TOPDIR ?= $(CURDIR)
include $(DEVKITARM)/3ds_rules include $(DEVKITARM)/3ds_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# GRAPHICS is a list of directories containing graphics files
# GFXBUILD is the directory where converted graphics files will be placed
# If set to $(BUILD), it will statically link in the converted
# files as if they were data files.
#
# NO_SMDH: if set to anything, no SMDH file is generated.
# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional)
# APP_TITLE is the name of the app stored in the SMDH file (Optional)
# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional)
# APP_AUTHOR is the author of the app stored in the SMDH file (Optional)
# ICON is the filename of the icon (.png), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.png
# - icon.png
# - <libctru folder>/default_icon.png
#---------------------------------------------------------------------------------
# Your values. # Your values.
APP_TITLE := Anemone3DS APP_TITLE := Anemone3DS
APP_DESCRIPTION := A complete theming tool for the 3DS APP_DESCRIPTION := A complete theming tool for the 3DS
@@ -41,11 +18,9 @@ APP_AUTHOR := Anemone3DS Team
TARGET := $(subst $e ,_,$(notdir $(APP_TITLE))) TARGET := $(subst $e ,_,$(notdir $(APP_TITLE)))
OUTDIR := out OUTDIR := out
BUILD := build BUILD := build
SOURCES := source source/quirc SOURCES := source source/pp2d/pp2d source/quirc
INCLUDES := include INCLUDES := include
ROMFS := romfs ROMFS := romfs
GRAPHICS := assets
GFXBUILD := $(ROMFS)/gfx
# Path to the files # Path to the files
@@ -63,23 +38,12 @@ LOGO := meta/logo.bin
# If left blank, makerom will use default values (0xff3ff and CTR-P-CTAP, respectively) # If left blank, makerom will use default values (0xff3ff and CTR-P-CTAP, respectively)
# Be careful if UNIQUE_ID is the same as other apps: it will overwrite the previously installed one # Be careful if UNIQUE_ID is the same as other apps: it will overwrite the previously installed one
UNIQUE_ID := 0xBAFE0 UNIQUE_ID := 0xAFEN
PRODUCT_CODE := CTR-P-ANEM PRODUCT_CODE := CTR-P-ANEM
# Don't really need to change this # Don't really need to change this
ICON_FLAGS := nosavebackups,visible ICON_FLAGS := nosavebackups,visible
ifeq ($(strip $(NOGIT)),)
VERSION := $(shell git describe --tags --match v[0-9]* --abbrev=7 | sed 's/-[0-9]*-g/-/')
VERSION_MAJOR := $(shell echo $(VERSION) | cut -c2- | cut -f1 -d- | cut -f1 -d.)
VERSION_MINOR := $(shell echo $(VERSION) | cut -c2- | cut -f1 -d- | cut -f2 -d.)
VERSION_BUILD := $(shell echo $(VERSION) | cut -c2- | cut -f1 -d- | cut -f3 -d.)
ifeq ($(strip $(VERSION_BUILD)),)
VERSION_BUILD := 0
endif
endif
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# options for code generation # options for code generation
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@@ -89,7 +53,9 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
-ffunction-sections \ -ffunction-sections \
$(ARCH) $(ARCH)
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\"" revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/')
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(revision)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(revision)\"" -DAPP_TITLE="\"$(APP_TITLE)\""
ifneq ($(strip $(CITRA_MODE)),) ifneq ($(strip $(CITRA_MODE)),)
CFLAGS += -DCITRA_MODE CFLAGS += -DCITRA_MODE
endif endif
@@ -99,7 +65,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS := -g $(ARCH) ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lpng -lvorbisidec -logg -larchive -ljansson -lcitro2d -lcitro3d -lctrud -lm -lz LIBS := -lvorbisidec -logg -larchive -ljansson -lcitro3d -lctrud -lm -lz
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing # list of directories containing libraries, this must be the top level containing
@@ -119,7 +85,6 @@ export OUTPUT := $(CURDIR)/$(OUTDIR)/$(TARGET)
export TOPDIR := $(CURDIR) export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) $(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD) export DEPSDIR := $(CURDIR)/$(BUILD)
@@ -129,7 +94,6 @@ CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@@ -146,19 +110,9 @@ else
endif endif
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
export T3XFILES := $(GFXFILES:.t3s=.t3x) export OFILES := $(addsuffix .o,$(BINFILES)) \
export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \
$(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \
$(if $(filter $(BUILD),$(GFXBUILD)),$(addsuffix .o,$(T3XFILES))) $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SOURCES)
export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \
$(addsuffix .h,$(subst .,_,$(BINFILES))) \
$(GFXFILES:.t3s=.h)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
@@ -166,8 +120,6 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh)
ifeq ($(strip $(ICON)),) ifeq ($(strip $(ICON)),)
icons := $(wildcard *.png) icons := $(wildcard *.png)
ifneq (,$(findstring $(TARGET).png,$(icons))) ifneq (,$(findstring $(TARGET).png,$(icons)))
@@ -189,24 +141,56 @@ ifneq ($(ROMFS),)
export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS)
endif endif
.PHONY: all clean .PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: 3dsx cia
3dsx: $(BUILD) $(OUTPUT).3dsx
cia : $(BUILD) $(OUTPUT).cia
citra: export CITRA_MODE = 1
citra: 3dsx
#---------------------------------------------------------------------------------
$(BUILD):
@mkdir -p $(OUTDIR)
@[ -d "$@" ] || mkdir -p "$@"
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTDIR)
#---------------------------------------------------------------------------------
ifeq ($(strip $(NO_SMDH)),)
$(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh
else
$(OUTPUT).3dsx : $(OUTPUT).elf
endif
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
MAKEROM ?= makerom MAKEROM ?= makerom
MAKEROM_ARGS := -elf "$(OUTPUT).elf" -rsf "$(RSF_PATH)" -banner "$(BUILD)/banner.bnr" -icon "$(BUILD)/icon.icn" -DAPP_TITLE="$(APP_TITLE)" -DAPP_PRODUCT_CODE="$(PRODUCT_CODE)" -DAPP_UNIQUE_ID="$(UNIQUE_ID)" MAKEROM_ARGS := -elf "$(OUTPUT).elf" -rsf "$(RSF_PATH)" -banner "$(BUILD)/banner.bnr" -icon "$(BUILD)/icon.icn" -DAPP_TITLE="$(APP_TITLE)" -DAPP_PRODUCT_CODE="$(PRODUCT_CODE)" -DAPP_UNIQUE_ID="$(UNIQUE_ID)"
ifeq ($(strip $(NOGIT)),) #ifneq ($(strip $(ROMFS)),)
MAKEROM_ARGS += -major $(VERSION_MAJOR) -minor $(VERSION_MINOR) -micro $(VERSION_BUILD) # MAKEROM_ARGS += -romfs "$(BUILD)/romfs.bin"
endif #endif
ifneq ($(strip $(LOGO)),) ifneq ($(strip $(LOGO)),)
MAKEROM_ARGS += -logo "$(LOGO)" MAKEROM_ARGS += -logo "$(LOGO)"
endif endif
ifneq ($(strip $(ROMFS)),)
MAKEROM_ARGS += -DAPP_ROMFS="$(ROMFS)" ifeq ($(strip $(ROMFS)),)
$(OUTPUT).cia: $(OUTPUT).elf $(BUILD)/banner.bnr $(BUILD)/icon.icn
$(MAKEROM) -f cia -o "$@" -target t -exefslogo $(MAKEROM_ARGS)
else
$(OUTPUT).cia: $(OUTPUT).elf $(BUILD)/banner.bnr $(BUILD)/icon.icn
$(MAKEROM) -f cia -o "$@" -target t -exefslogo $(MAKEROM_ARGS)
endif endif
BANNERTOOL ?= bannertool BANNERTOOL ?= bannertool
ifeq ($(suffix $(BANNER_IMAGE)),.cgfx) ifeq ($(suffix $(BANNER_IMAGE)),.cgfx)
@@ -221,78 +205,57 @@ else
BANNER_AUDIO_ARG := -a BANNER_AUDIO_ARG := -a
endif endif
#--------------------------------------------------------------------------------- $(BUILD)/banner.bnr : $(BANNER_IMAGE) $(BANNER_AUDIO)
all: $(BANNERTOOL) makebanner $(BANNER_IMAGE_ARG) "$(BANNER_IMAGE)" $(BANNER_AUDIO_ARG) "$(BANNER_AUDIO)" -o "$@"
@mkdir -p $(BUILD) $(GFXBUILD) $(OUTDIR)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile $(BUILD)/icon.icn : $(APP_ICON)
@$(BANNERTOOL) makebanner $(BANNER_IMAGE_ARG) "$(BANNER_IMAGE)" $(BANNER_AUDIO_ARG) "$(BANNER_AUDIO)" -o "$(BUILD)/banner.bnr" $(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i "$(APP_ICON)" -f "$(ICON_FLAGS)" -o "$@"
@$(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i "$(APP_ICON)" -f "$(ICON_FLAGS)" -o "$(BUILD)/icon.icn"
$(MAKEROM) -f cia -o "$(OUTPUT).cia" -target t -exefslogo $(MAKEROM_ARGS)
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(GFXBUILD) $(OUTDIR)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
else else
DEPENDS := $(OFILES:.o=.d)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# main targets # main targets
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
$(OUTPUT).3dsx : $(OUTPUT).elf $(_3DSXDEPS)
$(OFILES_SOURCES) : $(HFILES)
$(OUTPUT).elf : $(OFILES) $(OUTPUT).elf : $(OFILES)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data # you need a rule like this for each extension you use as binary data
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin %.bin.o : %.bin
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@echo $(notdir $<) @echo $(notdir $<)
@$(bin2o) @$(bin2o)
#---------------------------------------------------------------------------------
.PRECIOUS : %.t3x
%.t3x.o %_t3x.h : %.t3x
#---------------------------------------------------------------------------------
@$(bin2o)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# rules for assembling GPU shaders # rules for assembling GPU shaders
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
define shader-as define shader-as
$(eval CURBIN := $*.shbin) $(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
$(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) picasso -o $(CURBIN) $1
echo "$(CURBIN).o: $< $1" > $(DEPSFILE) bin2s $(CURBIN) | $(AS) -o $@
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h
picasso -o $(CURBIN) $1
bin2s $(CURBIN) | $(AS) -o $*.shbin.o
endef endef
%.shbin.o %_shbin.h : %.v.pica %.g.pica %.shbin.o : %.v.pica %.g.pica
@echo $(notdir $^) @echo $(notdir $^)
@$(call shader-as,$^) @$(call shader-as,$^)
%.shbin.o %_shbin.h : %.v.pica %.shbin.o : %.v.pica
@echo $(notdir $<) @echo $(notdir $<)
@$(call shader-as,$<) @$(call shader-as,$<)
%.shbin.o %_shbin.h : %.shlist %.shbin.o : %.shlist
@echo $(notdir $<) @echo $(notdir $<)
@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
#--------------------------------------------------------------------------------- -include $(DEPENDS)
%.t3x %.h : %.t3s
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@tex3ds -i $< -H $*.h -d $*.d -o $(TOPDIR)/$(GFXBUILD)/$*.t3x
-include $(DEPSDIR)/*.d
#--------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------
endif endif

View File

@@ -4,18 +4,20 @@ A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.\
To-do list here: https://trello.com/b/F1YSa1VK To-do list here: https://trello.com/b/F1YSa1VK
# Dependencies # Dependencies
* devkitARM, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started). * devkitPro, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started).
* jansson, libvorbisidec, libpng, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702). * zlib, jansson, libvorbisidec, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702).
* A recent build of [makerom](https://github.com/profi200/Project_CTR) and the latest release of [bannertool](https://github.com/Steveice10/bannertool). These must be added to your PATH. * A recent build of [makerom](https://github.com/profi200/Project_CTR) and the latest release of [bannertool](https://github.com/Steveice10/bannertool). These must be added to your PATH.
A 64-bit Windows binary of makerom is available [here](https://hm892.s-ul.eu/U0Irkqih). A 64-bit Windows binary of makerom is available [here](https://hm892.s-ul.eu/U0Irkqih).
* ~~[pp2d](https://github.com/BernardoGiordano/pp2d), which is included in the repo if you do a git clone --recursive.~~
Due to circumstances surrounding the privacy settings on the pp2d repo, the source files are now included directly within the repo.
* Git needs to be on your PATH (this is usually only manual under Windows).
# Building # Building
First of all, make sure devkitARM is properly installed - `$DEVKITPRO` and `$DEVKITARM` should be set to `/opt/devkitpro` and `$DEVKITPRO/devkitARM`, respectively. First of all, make sure devkitPro is properly installed and added to your PATH.
After that, open the directory you want to clone the repo into, and execute After that, open the directory you want to clone the repo into, and execute
`git clone https://github.com/astronautlevel2/Anemone3DS` (or any other cloning method). `git clone https://github.com/astronautlevel2/Anemone3DS` (or any other cloning method).
To install the prerequisite libraries, begin by ensuring devkitPro pacman (and the base install group, `3ds-dev`) is installed, and then install the dkP packages `3ds-jansson`, `3ds-libvorbisidec`, `3ds-libpng`, and `3ds-libarchive` using `[sudo] [dkp-]pacman -S <package-name>`. To install zlib, jansson and libarchive, begin by following the instructions found above ([here](https://devkitpro.org/viewtopic.php?f=13&t=8702)) on the devkitPro forums, and then install the dkP packages `3ds-zlib`, `3ds-jansson` and `3ds-libarchive`.
After also adding [makerom](https://github.com/profi200/Project_CTR) and [bannertool](https://github.com/Steveice10/buildtools) to your PATH, just enter your directory and run `make`. All built files will be in `/out/`.
After adding [makerom](https://github.com/profi200/Project_CTR) and [bannertool](https://github.com/Steveice10/buildtools) to your PATH, just enter your directory and run `make`. All built binaries will be in `/out/`.
# License # License
This project is licensed under the GNU GPLv3. See LICENSE.md for details. Additional terms 7b and 7c apply to this project. This project is licensed under the GNU GPLv3. See LICENSE.md for details. Additional terms 7b and 7c apply to this project.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,23 +0,0 @@
--atlas -f rgba8888 -z auto
arrow_up.png
arrow_down.png
arrow_left.png
arrow_right.png
battery0.png
battery1.png
battery2.png
battery3.png
battery4.png
battery5.png
browse.png
charging.png
download.png
exit.png
installed.png
list.png
preview.png
select.png
shuffle.png
shuffle_no_bgm.png
sort.png
start.png

View File

@@ -31,13 +31,11 @@
typedef struct { typedef struct {
u16 *camera_buffer; u16 *camera_buffer;
C2D_Image image; u32 *texture_buffer;
C3D_Tex *tex;
Handle mutex; Handle mutex;
volatile bool finished; volatile bool finished;
volatile bool success; volatile bool success;
Handle cancel; Handle cancel;
Handle started;
bool capturing; bool capturing;
struct quirc* context; struct quirc* context;

View File

@@ -27,23 +27,17 @@
#ifndef COLORS_H #ifndef COLORS_H
#define COLORS_H #define COLORS_H
#include "common.h" #define ABGR8(a, b, g, r) ((((a)&0xFF)<<0) | (((b)&0xFF)<<8) | (((g)&0xFF)<<16) | (((r)&0xFF)<<24))
#define RGBA8(r, g, b, a) ((((r)&0xFF)<<0) | (((g)&0xFF)<<8) | (((b)&0xFF)<<16) | (((a)&0xFF)<<24))
typedef u32 Color; typedef enum {
COLOR_BACKGROUND = ABGR8(255, 32, 28, 35), //silver-y black
enum { COLOR_ACCENT = RGBA8(55, 122, 168, 255),
COLOR_BACKGROUND, //silver-y black COLOR_WHITE = RGBA8(255, 255, 255, 255),
COLOR_ACCENT, COLOR_CURSOR = RGBA8(200, 200, 200, 255),
COLOR_WHITE, COLOR_BLACK = RGBA8(0, 0, 0, 255),
COLOR_CURSOR, COLOR_RED = RGBA8(200, 0, 0, 255),
COLOR_BLACK, COLOR_YELLOW = RGBA8(239, 220, 11, 255),
COLOR_RED, } Color;
COLOR_YELLOW,
COLOR_AMOUNT,
} Colors_e;
extern Color colors[COLOR_AMOUNT];
void init_colors(void);
#endif #endif

View File

@@ -28,8 +28,6 @@
#define COMMON_H #define COMMON_H
#include <3ds.h> #include <3ds.h>
#include <citro3d.h>
#include <citro2d.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -42,7 +40,7 @@
POS(); \ POS(); \
DEBUG(__VA_ARGS__) DEBUG(__VA_ARGS__)
#define FASTSCROLL_WAIT 1e8 #define FASTSCROLL_WAIT 1.5e8
typedef enum { typedef enum {
MODE_THEMES = 0, MODE_THEMES = 0,
@@ -56,6 +54,36 @@ extern const int entries_per_screen_v[MODE_AMOUNT];
extern const int entries_per_screen_h[MODE_AMOUNT]; extern const int entries_per_screen_h[MODE_AMOUNT];
extern const int entry_size[MODE_AMOUNT]; extern const int entry_size[MODE_AMOUNT];
extern bool quit; extern bool quit;
extern bool dspfirm;
enum TextureID {
TEXTURE_FONT_RESERVED = 0, // used by pp2d for the font
TEXTURE_ARROW,
TEXTURE_ARROW_SIDE,
TEXTURE_SHUFFLE,
TEXTURE_SHUFFLE_NO_BGM,
TEXTURE_INSTALLED,
TEXTURE_PREVIEW_ICON,
TEXTURE_SORT,
TEXTURE_DOWNLOAD,
TEXTURE_BROWSE,
TEXTURE_LIST,
TEXTURE_EXIT,
TEXTURE_BATTERY_0,
TEXTURE_BATTERY_1,
TEXTURE_BATTERY_2,
TEXTURE_BATTERY_3,
TEXTURE_BATTERY_4,
TEXTURE_BATTERY_5,
TEXTURE_BATTERY_CHARGE,
TEXTURE_QR,
TEXTURE_PREVIEW,
TEXTURE_REMOTE_PREVIEW,
TEXTURE_SELECT_BUTTON,
TEXTURE_START_BUTTON,
// always the last
TEXTURE_REMOTE_ICONS,
TEXTURE_ICON = TEXTURE_REMOTE_ICONS + 24,
};
#endif #endif

View File

@@ -31,8 +31,6 @@
#include "loading.h" #include "loading.h"
#include "colors.h" #include "colors.h"
#define MAX_LINES 10
typedef enum { typedef enum {
INSTALL_LOADING_THEMES, INSTALL_LOADING_THEMES,
INSTALL_LOADING_SPLASHES, INSTALL_LOADING_SPLASHES,
@@ -58,63 +56,6 @@ typedef enum {
INSTALL_NONE, INSTALL_NONE,
} InstallType; } InstallType;
typedef enum {
// InstallType text
TEXT_INSTALL_LOADING_THEMES,
TEXT_INSTALL_LOADING_SPLASHES,
TEXT_INSTALL_LOADING_ICONS,
TEXT_INSTALL_SPLASH,
TEXT_INSTALL_SPLASH_DELETE,
TEXT_INSTALL_SINGLE,
TEXT_INSTALL_SHUFFLE,
TEXT_INSTALL_BGM,
TEXT_INSTALL_NO_BGM,
TEXT_INSTALL_DOWNLOAD,
TEXT_INSTALL_CHECKING_DOWNLOAD,
TEXT_INSTALL_ENTRY_DELETE,
TEXT_INSTALL_LOADING_REMOTE_THEMES,
TEXT_INSTALL_LOADING_REMOTE_SPLASHES,
TEXT_INSTALL_LOADING_REMOTE_PREVIEW,
TEXT_INSTALL_LOADING_REMOTE_BGM,
// Other text
TEXT_VERSION,
TEXT_THEME_MODE,
TEXT_SPLASH_MODE,
TEXT_NO_THEME_FOUND,
TEXT_NO_SPLASH_FOUND,
TEXT_DOWNLOAD_FROM_QR,
TEXT_SWITCH_TO_SPLASHES,
TEXT_SWITCH_TO_THEMES,
TEXT_OR_START_TO_QUIT,
TEXT_BY_AUTHOR,
TEXT_SELECTED,
TEXT_SELECTED_SHORT,
TEXT_THEMEPLAZA_THEME_MODE,
TEXT_THEMEPLAZA_SPLASH_MODE,
TEXT_SEARCH,
TEXT_PAGE,
TEXT_ERROR_QUIT,
TEXT_ERROR_CONTINUE,
TEXT_CONFIRM_YES_NO,
TEXT_AMOUNT
} Text;
typedef enum { typedef enum {
ERROR_LEVEL_ERROR, ERROR_LEVEL_ERROR,
ERROR_LEVEL_WARNING, ERROR_LEVEL_WARNING,
@@ -135,40 +76,25 @@ enum {
BUTTONS_X_LEFT = 20, BUTTONS_X_LEFT = 20,
BUTTONS_X_RIGHT = 200, BUTTONS_X_RIGHT = 200,
BUTTONS_X_MAX = 380,
} ButtonPos; } ButtonPos;
typedef struct { typedef struct {
const char * info_line; const wchar_t * info_line;
const char * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS]; Color info_line_color;
const wchar_t * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
} Instructions_s; } Instructions_s;
extern C3D_RenderTarget* top;
extern C3D_RenderTarget* bottom;
extern C2D_TextBuf staticBuf, dynamicBuf;
extern C2D_Text text[TEXT_AMOUNT];
void init_screens(void); void init_screens(void);
void exit_screens(void); void exit_screens(void);
void start_frame(void);
void end_frame(void);
void set_screen(C3D_RenderTarget * screen);
void throw_error(char* error, ErrorLevel level); void throw_error(char* error, ErrorLevel level);
bool draw_confirm(const char* conf_msg, Entry_List_s* list); bool draw_confirm(const char* conf_msg, Entry_List_s* list);
void draw_preview(C2D_Image preview, int preview_offset); void draw_preview(ssize_t previewID, int preview_offset);
void draw_install(InstallType type); void draw_install(InstallType type);
void draw_loading_bar(u32 current, u32 max, InstallType type); void draw_loading_bar(u32 current, u32 max, InstallType type);
void draw_text(float x, float y, float z, float scaleX, float scaleY, Color color, const char * text);
void draw_text_wrap(float x, float y, float z, float scaleX, float scaleY, Color color, const char * text, float max_width);
void draw_text_wrap_scaled(float x, float y, float z, Color color, const char * text, float max_scale, float min_scale, float max_width);
void draw_text_center(gfxScreen_t target, float y, float z, float scaleX, float scaleY, Color color, const char * text);
void draw_base_interface(void); void draw_base_interface(void);
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions); void draw_grid_interface(Entry_List_s* list, Instructions_s instructions);
void draw_interface(Entry_List_s* list, Instructions_s instructions); void draw_interface(Entry_List_s* list, Instructions_s instructions);

View File

@@ -35,20 +35,20 @@ Instructions_s normal_instructions[MODE_AMOUNT] = {
.info_line = NULL, .info_line = NULL,
.instructions = { .instructions = {
{ {
"\uE000 Hold to install", L"\uE000 Hold to install",
"\uE001 Queue shuffle theme" L"\uE001 Queue shuffle theme"
}, },
{ {
"\uE002 Hold for more", L"\uE002 Hold for more",
"\uE003 Preview theme" L"\uE003 Preview theme"
}, },
{ {
"\uE004 Switch to splashes", L"\uE004 Switch to splashes",
"\uE005 Scan QR code" L"\uE005 Scan QR code"
}, },
{ {
"Exit", L"Exit",
"Delete from SD" L"Delete from SD"
} }
} }
}, },
@@ -56,42 +56,43 @@ Instructions_s normal_instructions[MODE_AMOUNT] = {
.info_line = NULL, .info_line = NULL,
.instructions = { .instructions = {
{ {
"\uE000 Install splash", L"\uE000 Install splash",
"\uE001 Delete installed splash" L"\uE001 Delete installed splash"
}, },
{ {
"\uE002 Hold for more", L"\uE002 Hold for more",
"\uE003 Preview splash" L"\uE003 Preview splash"
}, },
{ {
"\uE004 Switch to themes", L"\uE004 Switch to themes",
"\uE005 Scan QR code" L"\uE005 Scan QR code"
}, },
{ {
"Exit", L"Exit",
"Delete from SD" L"Delete from SD"
} }
} }
} }
}; };
Instructions_s install_instructions = { Instructions_s install_instructions = {
.info_line = "Release \uE000 to cancel or hold \uE006 and release \uE000 to install", .info_line = L"Release \uE000 to cancel or hold \uE006 and release \uE000 to install",
.info_line_color = COLOR_WHITE,
.instructions = { .instructions = {
{ {
"\uE079 Normal install", L"\uE079 Normal install",
"\uE07A Shuffle install" L"\uE07A Shuffle install"
}, },
{ {
"\uE07B BGM-only install", L"\uE07B BGM-only install",
"\uE07C No-BGM install" L"\uE07C No-BGM install"
}, },
{ {
NULL, NULL,
NULL NULL
}, },
{ {
"Exit", L"Exit",
NULL NULL
} }
} }
@@ -99,14 +100,15 @@ Instructions_s install_instructions = {
Instructions_s extra_instructions[3] = { Instructions_s extra_instructions[3] = {
{ {
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to sort", .info_line = L"Release \uE002 to cancel or hold \uE006 and release \uE002 to sort",
.info_line_color = COLOR_WHITE,
.instructions = { .instructions = {
{ {
"\uE079 Sort by name", L"\uE079 Sort by name",
"\uE07A Sort by author" L"\uE07A Sort by author"
}, },
{ {
"\uE07B Sort by filename", L"\uE07B Sort by filename",
NULL NULL
}, },
{ {
@@ -114,28 +116,29 @@ Instructions_s extra_instructions[3] = {
NULL NULL
}, },
{ {
"Exit", L"Exit",
NULL NULL
} }
} }
}, },
{ {
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff", .info_line = L"Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.info_line_color = COLOR_WHITE,
.instructions = { .instructions = {
{ {
"\uE079 Jump in the list", L"\uE079 Jump in the list",
"\uE07A Reload broken icons" L"\uE07A Reload broken icons"
}, },
{ {
"\uE07B Browse ThemePlaza", L"\uE07B Browse ThemePlaza",
NULL, NULL,
}, },
{ {
"\uE004 Sorting menu", L"\uE004 Sorting menu",
NULL NULL
}, },
{ {
"Exit", L"Exit",
NULL NULL
} }
} }

View File

@@ -81,7 +81,8 @@ typedef struct {
Entry_s * entries; Entry_s * entries;
int entries_count; int entries_count;
C2D_Image ** icons; ssize_t texture_id_offset;
ssize_t * icons_ids;
int previous_scroll; int previous_scroll;
int scroll; int scroll;
@@ -109,18 +110,13 @@ typedef struct {
volatile bool run_thread; volatile bool run_thread;
} Thread_Arg_s; } Thread_Arg_s;
C2D_Image * loadTextureIcon(Icon_s *icon);
void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name);
void sort_by_name(Entry_List_s * list); void sort_by_name(Entry_List_s * list);
void sort_by_author(Entry_List_s * list); void sort_by_author(Entry_List_s * list);
void sort_by_filename(Entry_List_s * list); void sort_by_filename(Entry_List_s * list);
void delete_entry(Entry_s * entry, bool is_file); void delete_entry(Entry_s * entry, bool is_file);
Result load_entries(const char * loading_path, Entry_List_s * list); Result load_entries(const char * loading_path, Entry_List_s * list);
bool load_preview_from_buffer(void * buf, u32 size, C2D_Image * preview_image, int * preview_offset); bool load_preview(Entry_List_s list, int * preview_offset);
bool load_preview(Entry_List_s list, C2D_Image * preview_image, int * preview_offset);
void free_preview(C2D_Image preview_image);
Result load_audio(Entry_s, audio_s *); Result load_audio(Entry_s, audio_s *);
void load_icons_first(Entry_List_s * current_list, bool silent); void load_icons_first(Entry_List_s * current_list, bool silent);
void handle_scrolling(Entry_List_s * list); void handle_scrolling(Entry_List_s * list);

View File

@@ -1,13 +1,12 @@
BasicInfo: BasicInfo:
Title : $(APP_TITLE) Title : "Anemone3DS"
CompanyCode : "00" CompanyCode : "00"
ProductCode : $(APP_PRODUCT_CODE) ProductCode : "CTR-P-ANEM"
ContentType : Application ContentType : Application
Logo : Homebrew # Nintendo / Licensed / Distributed / iQue / iQueForSystem Logo : Homebrew # Nintendo / Licensed / Distributed / iQue / iQueForSystem
TitleInfo: TitleInfo:
UniqueId : $(APP_UNIQUE_ID) UniqueId : 0xAFEN
Category : Application Category : Application
CardInfo: CardInfo:
@@ -35,7 +34,7 @@ SystemControlInfo:
StackSize: 0x40000 StackSize: 0x40000
RomFs: RomFs:
RootPath : $(APP_ROMFS) RootPath : romfs
# DO NOT EDIT BELOW HERE OR PROGRAMS WILL NOT LAUNCH (most likely) # DO NOT EDIT BELOW HERE OR PROGRAMS WILL NOT LAUNCH (most likely)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 498 B

View File

Before

Width:  |  Height:  |  Size: 303 B

After

Width:  |  Height:  |  Size: 303 B

View File

Before

Width:  |  Height:  |  Size: 319 B

After

Width:  |  Height:  |  Size: 319 B

View File

Before

Width:  |  Height:  |  Size: 323 B

After

Width:  |  Height:  |  Size: 323 B

View File

Before

Width:  |  Height:  |  Size: 323 B

After

Width:  |  Height:  |  Size: 323 B

View File

Before

Width:  |  Height:  |  Size: 323 B

After

Width:  |  Height:  |  Size: 323 B

View File

Before

Width:  |  Height:  |  Size: 654 B

After

Width:  |  Height:  |  Size: 654 B

View File

Before

Width:  |  Height:  |  Size: 806 B

After

Width:  |  Height:  |  Size: 806 B

View File

Before

Width:  |  Height:  |  Size: 404 B

After

Width:  |  Height:  |  Size: 404 B

View File

Before

Width:  |  Height:  |  Size: 390 B

After

Width:  |  Height:  |  Size: 390 B

View File

Before

Width:  |  Height:  |  Size: 579 B

After

Width:  |  Height:  |  Size: 579 B

View File

Before

Width:  |  Height:  |  Size: 179 B

After

Width:  |  Height:  |  Size: 179 B

View File

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 307 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 658 B

After

Width:  |  Height:  |  Size: 658 B

View File

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 511 B

View File

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 291 B

View File

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 494 B

View File

@@ -27,6 +27,7 @@
#include "camera.h" #include "camera.h"
#include "quirc/quirc.h" #include "quirc/quirc.h"
#include "pp2d/pp2d/pp2d.h"
#include "draw.h" #include "draw.h"
#include "fs.h" #include "fs.h"
@@ -36,6 +37,13 @@
#include <archive.h> #include <archive.h>
#include <archive_entry.h> #include <archive_entry.h>
/*
static u32 transfer_size;
static Handle event;
static struct quirc* context;
static u16 * camera_buf = NULL;
*/
void exit_qr(qr_data *data) void exit_qr(qr_data *data)
{ {
DEBUG("Exiting QR\n"); DEBUG("Exiting QR\n");
@@ -46,7 +54,7 @@ void exit_qr(qr_data *data)
data->capturing = false; data->capturing = false;
free(data->camera_buffer); free(data->camera_buffer);
free(data->tex); free(data->texture_buffer);
quirc_destroy(data->context); quirc_destroy(data->context);
} }
@@ -58,6 +66,7 @@ void capture_cam_thread(void *arg)
u32 transferUnit; u32 transferUnit;
u16 *buffer = calloc(1, 400 * 240 * sizeof(u16)); u16 *buffer = calloc(1, 400 * 240 * sizeof(u16));
camInit();
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A); CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A);
CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A); CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A);
CAMU_SetFrameRate(SELECT_OUT1, FRAME_RATE_30); CAMU_SetFrameRate(SELECT_OUT1, FRAME_RATE_30);
@@ -72,7 +81,6 @@ void capture_cam_thread(void *arg)
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), (s16) transferUnit); CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), (s16) transferUnit);
CAMU_StartCapture(PORT_CAM1); CAMU_StartCapture(PORT_CAM1);
svcSignalEvent(data->started);
bool cancel = false; bool cancel = false;
while (!cancel) while (!cancel)
{ {
@@ -125,40 +133,6 @@ void capture_cam_thread(void *arg)
data->finished = true; data->finished = true;
} }
void update_ui(void *arg)
{
qr_data* data = (qr_data*) arg;
while (!data->finished)
{
draw_base_interface();
// Untiled texture loading code adapted from FBI
svcWaitSynchronization(data->mutex, U64_MAX);
for(u32 x = 0; x < 400 && !data->finished; x++) {
for(u32 y = 0; y < 256 && !data->finished; y++) {
u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) * sizeof(u16);
u32 srcPos = (y * 400 + x) * sizeof(u16);
memcpy(&((u8*) data->image.tex->data)[dstPos], &((u8*) data->camera_buffer)[srcPos], sizeof(u16));
}
}
svcReleaseMutex(data->mutex);
if (data->finished)
{
end_frame();
}
C2D_DrawImageAt(data->image, 0.0f, 0.0f, 0.4f, NULL, 1.0f, 1.0f);
set_screen(bottom);
draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, 0.5, colors[COLOR_WHITE], "Press \uE005 To Quit");
end_frame();
}
}
bool start_capture_cam(qr_data *data) bool start_capture_cam(qr_data *data)
{ {
data->mutex = 0; data->mutex = 0;
@@ -167,12 +141,6 @@ bool start_capture_cam(qr_data *data)
svcCreateMutex(&data->mutex, false); svcCreateMutex(&data->mutex, false);
if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL) if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL)
return false; return false;
svcWaitSynchronization(data->started, U64_MAX);
if(threadCreate(update_ui, data, 0x10000, 0x1A, 1, true) == NULL)
{
exit_qr(data);
return false;
}
return true; return true;
} }
@@ -198,16 +166,31 @@ void update_qr(qr_data *data)
exit_qr(data); exit_qr(data);
return; return;
} }
for (int i = 0; i < 240 * 400; i++)
{
data->texture_buffer[i] = RGB565_TO_ABGR8(data->camera_buffer[i]);
}
draw_base_interface();
pp2d_free_texture(TEXTURE_QR);
pp2d_load_texture_memory(TEXTURE_QR, data->texture_buffer, 400, 240);
pp2d_draw_texture(TEXTURE_QR, 0, 0);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, RGBA8(255, 255, 255, 255), "Press \uE005 To Quit");
pp2d_end_draw();
int w; int w;
int h; int h;
u8 *image = (u8*) quirc_begin(data->context, &w, &h); u8 *image = (u8*) quirc_begin(data->context, &w, &h);
svcWaitSynchronization(data->mutex, U64_MAX);
for (ssize_t x = 0; x < w; x++) { for (ssize_t x = 0; x < w; x++) {
for (ssize_t y = 0; y < h; y++) { for (ssize_t y = 0; y < h; y++) {
u16 px = data->camera_buffer[y * 400 + x]; u16 px = data->camera_buffer[y * 400 + x];
image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3); image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
} }
} }
svcReleaseMutex(data->mutex);
quirc_end(data->context); quirc_end(data->context);
if(quirc_count(data->context) > 0) if(quirc_count(data->context) > 0)
{ {
@@ -305,18 +288,11 @@ bool init_qr(void)
qr_data *data = calloc(1, sizeof(qr_data)); qr_data *data = calloc(1, sizeof(qr_data));
data->capturing = false; data->capturing = false;
data->finished = false; data->finished = false;
svcCreateEvent(&data->started, RESET_STICKY);
data->context = quirc_new(); data->context = quirc_new();
quirc_resize(data->context, 400, 240); quirc_resize(data->context, 400, 240);
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16)); data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
data->texture_buffer = calloc(1, 400 * 240 * sizeof(u32));
data->tex = (C3D_Tex*)malloc(sizeof(C3D_Tex));
static const Tex3DS_SubTexture subt3x = { 512, 256, 0.0f, 1.0f, 1.0f, 0.0f };
data->image = (C2D_Image){ data->tex, &subt3x };
C3D_TexInit(data->image.tex, 512, 256, GPU_RGB565);
C3D_TexSetFilter(data->image.tex, GPU_LINEAR, GPU_LINEAR);
while (!data->finished) update_qr(data); while (!data->finished) update_qr(data);
bool success = data->success; bool success = data->success;

View File

@@ -1,40 +0,0 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "colors.h"
Color colors[COLOR_AMOUNT] = {0};
void init_colors(void)
{
colors[COLOR_BACKGROUND] = C2D_Color32(35, 28, 32, 255); //silver-y black
colors[COLOR_ACCENT] = C2D_Color32(12, 58, 111, 255);
colors[COLOR_WHITE] = C2D_Color32(255, 255, 255, 255);
colors[COLOR_CURSOR] = C2D_Color32(200, 200, 200, 255);
colors[COLOR_BLACK] = C2D_Color32(0, 0, 0, 255);
colors[COLOR_RED] = C2D_Color32(229, 66, 66, 255);
colors[COLOR_YELLOW] = C2D_Color32(239, 220, 11, 255);
}

View File

@@ -28,283 +28,130 @@
#include "unicode.h" #include "unicode.h"
#include "colors.h" #include "colors.h"
#include "sprites.h" #include "pp2d/pp2d/pp2d.h"
#include <time.h> #include <time.h>
C3D_RenderTarget* top;
C3D_RenderTarget* bottom;
C2D_TextBuf staticBuf, dynamicBuf;
static C2D_TextBuf widthBuf;
static C2D_SpriteSheet spritesheet;
static C2D_Sprite sprite_shuffle, sprite_shuffle_no_bgm, sprite_installed, sprite_start, sprite_select;
C2D_Text text[TEXT_AMOUNT];
static const char * mode_switch_char[MODE_AMOUNT] = {
"S",
"T",
};
void init_screens(void) void init_screens(void)
{ {
init_colors(); pp2d_init();
gfxInitDefault();
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
C2D_Init(C2D_DEFAULT_MAX_OBJECTS);
C2D_Prepare();
top = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT); pp2d_set_screen_color(GFX_TOP, COLOR_BACKGROUND);
bottom = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT); pp2d_set_screen_color(GFX_BOTTOM, COLOR_BACKGROUND);
spritesheet = C2D_SpriteSheetLoad("romfs:/gfx/sprites.t3x"); pp2d_load_texture_png(TEXTURE_ARROW, "romfs:/arrow.png");
C2D_SpriteFromSheet(&sprite_shuffle, spritesheet, sprites_shuffle_idx); pp2d_load_texture_png(TEXTURE_ARROW_SIDE, "romfs:/arrow_side.png");
C2D_SpriteSetDepth(&sprite_shuffle, 0.6f); pp2d_load_texture_png(TEXTURE_SHUFFLE, "romfs:/shuffle.png");
C2D_SpriteFromSheet(&sprite_shuffle_no_bgm, spritesheet, sprites_shuffle_no_bgm_idx); pp2d_load_texture_png(TEXTURE_SHUFFLE_NO_BGM, "romfs:/shuffle_no_bgm.png");
C2D_SpriteSetDepth(&sprite_shuffle_no_bgm, 0.6f); pp2d_load_texture_png(TEXTURE_INSTALLED, "romfs:/installed.png");
pp2d_load_texture_png(TEXTURE_PREVIEW_ICON, "romfs:/preview.png");
C2D_SpriteFromSheet(&sprite_installed, spritesheet, sprites_installed_idx); pp2d_load_texture_png(TEXTURE_SORT, "romfs:/sort.png");
C2D_SpriteSetDepth(&sprite_installed, 0.6f); pp2d_load_texture_png(TEXTURE_DOWNLOAD, "romfs:/download.png");
pp2d_load_texture_png(TEXTURE_BROWSE, "romfs:/browse.png");
C2D_SpriteFromSheet(&sprite_start, spritesheet, sprites_start_idx); pp2d_load_texture_png(TEXTURE_LIST, "romfs:/list.png");
C2D_SpriteSetDepth(&sprite_start, 0.5f); pp2d_load_texture_png(TEXTURE_EXIT, "romfs:/exit.png");
C2D_SpriteFromSheet(&sprite_select, spritesheet, sprites_select_idx); pp2d_load_texture_png(TEXTURE_BATTERY_0, "romfs:/battery0.png");
C2D_SpriteSetDepth(&sprite_select, 0.5f); pp2d_load_texture_png(TEXTURE_BATTERY_1, "romfs:/battery1.png");
pp2d_load_texture_png(TEXTURE_BATTERY_2, "romfs:/battery2.png");
staticBuf = C2D_TextBufNew(4096); pp2d_load_texture_png(TEXTURE_BATTERY_3, "romfs:/battery3.png");
dynamicBuf = C2D_TextBufNew(4096); pp2d_load_texture_png(TEXTURE_BATTERY_4, "romfs:/battery4.png");
widthBuf = C2D_TextBufNew(4096); pp2d_load_texture_png(TEXTURE_BATTERY_5, "romfs:/battery5.png");
pp2d_load_texture_png(TEXTURE_BATTERY_CHARGE, "romfs:/charging.png");
C2D_TextParse(&text[TEXT_VERSION], staticBuf, VERSION); pp2d_load_texture_png(TEXTURE_SELECT_BUTTON, "romfs:/select.png");
pp2d_load_texture_png(TEXTURE_START_BUTTON, "romfs:/start.png");
C2D_TextParse(&text[TEXT_THEME_MODE], staticBuf, "Theme mode");
C2D_TextParse(&text[TEXT_SPLASH_MODE], staticBuf, "Splash mode");
C2D_TextParse(&text[TEXT_NO_THEME_FOUND], staticBuf, "No theme found");
C2D_TextParse(&text[TEXT_NO_SPLASH_FOUND], staticBuf, "No splash found");
C2D_TextParse(&text[TEXT_DOWNLOAD_FROM_QR], staticBuf, "Press \uE005 to download from QR");
C2D_TextParse(&text[TEXT_SWITCH_TO_SPLASHES], staticBuf, "Or \uE004 to switch to splashes");
C2D_TextParse(&text[TEXT_SWITCH_TO_THEMES], staticBuf, "Or \uE004 to switch to themes");
C2D_TextParse(&text[TEXT_OR_START_TO_QUIT], staticBuf, "Or to quit");
C2D_TextParse(&text[TEXT_BY_AUTHOR], staticBuf, "By ");
C2D_TextParse(&text[TEXT_SELECTED], staticBuf, "Selected:");
C2D_TextParse(&text[TEXT_SELECTED_SHORT], staticBuf, "Sel.:");
C2D_TextParse(&text[TEXT_THEMEPLAZA_THEME_MODE], staticBuf, "ThemePlaza Theme mode");
C2D_TextParse(&text[TEXT_THEMEPLAZA_SPLASH_MODE], staticBuf, "ThemePlaza Splash mode");
C2D_TextParse(&text[TEXT_SEARCH], staticBuf, "Search...");
C2D_TextParse(&text[TEXT_PAGE], staticBuf, "Page:");
C2D_TextParse(&text[TEXT_ERROR_QUIT], staticBuf, "Press \uE000 to quit.");
C2D_TextParse(&text[TEXT_ERROR_CONTINUE], staticBuf, "Press \uE000 to continue.");
C2D_TextParse(&text[TEXT_CONFIRM_YES_NO], staticBuf, "\uE000 Yes \uE001 No");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_THEMES], staticBuf, "Loading themes, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_SPLASHES], staticBuf, "Loading splashes, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_ICONS], staticBuf, "Loading icons, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_SPLASH], staticBuf, "Installing a splash...");
C2D_TextParse(&text[TEXT_INSTALL_SPLASH_DELETE], staticBuf, "Deleting installed splash...");
C2D_TextParse(&text[TEXT_INSTALL_SINGLE], staticBuf, "Installing a single theme...");
C2D_TextParse(&text[TEXT_INSTALL_SHUFFLE], staticBuf, "Installing shuffle themes...");
C2D_TextParse(&text[TEXT_INSTALL_BGM], staticBuf, "Installing BGM-only theme...");
C2D_TextParse(&text[TEXT_INSTALL_NO_BGM], staticBuf, "Installing theme without BGM...");
C2D_TextParse(&text[TEXT_INSTALL_DOWNLOAD], staticBuf, "Downloading...");
C2D_TextParse(&text[TEXT_INSTALL_CHECKING_DOWNLOAD], staticBuf, "Checking downloaded file...");
C2D_TextParse(&text[TEXT_INSTALL_ENTRY_DELETE], staticBuf, "Deleting from SD...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_THEMES], staticBuf, "Downloading theme list, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_SPLASHES], staticBuf, "Downloading splash list, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_PREVIEW], staticBuf, "Downloading preview, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, "Downloading BGM, please wait...");
for(int i = 0; i < TEXT_AMOUNT; i++)
C2D_TextOptimize(&text[i]);
} }
void exit_screens(void) void exit_screens(void)
{ {
C2D_TextBufDelete(widthBuf); pp2d_exit();
C2D_TextBufDelete(dynamicBuf);
C2D_TextBufDelete(staticBuf);
C2D_Fini();
C3D_Fini();
gfxExit();
}
void set_screen(C3D_RenderTarget * screen)
{
C2D_SceneBegin(screen);
}
void start_frame(void)
{
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
C2D_TargetClear(top, colors[COLOR_BACKGROUND]);
C2D_TargetClear(bottom, colors[COLOR_BACKGROUND]);
}
void end_frame(void)
{
C2D_TextBufClear(dynamicBuf);
C2D_TextBufClear(widthBuf);
C3D_FrameEnd(0);
}
static void draw_image(int image_id, float x, float y)
{
C2D_DrawImageAt(C2D_SpriteSheetGetImage(spritesheet, image_id), x, y, 0.6f, NULL, 1.0f, 1.0f);
}
static void get_text_dimensions(const char * text, float scaleX, float scaleY, float * width, float * height)
{
C2D_Text c2d_text;
C2D_TextParse(&c2d_text, widthBuf, text);
C2D_TextGetDimensions(&c2d_text, scaleX, scaleY, width, height);
}
static void draw_c2d_text(float x, float y, float z, float scaleX, float scaleY, Color color, C2D_Text * text)
{
C2D_DrawText(text, C2D_WithColor, x, y, z, scaleX, scaleY, color);
}
void draw_text(float x, float y, float z, float scaleX, float scaleY, Color color, const char * text)
{
C2D_Text c2d_text;
C2D_TextParse(&c2d_text, dynamicBuf, text);
C2D_TextOptimize(&c2d_text);
C2D_DrawText(&c2d_text, C2D_WithColor, x, y, z, scaleX, scaleY, color);
}
static void draw_c2d_text_center(gfxScreen_t target, float y, float z, float scaleX, float scaleY, Color color, C2D_Text * text)
{
float width = 0;
C2D_TextGetDimensions(text, scaleX, scaleY, &width, NULL);
float offset = (target == GFX_TOP ? 400 : 320)/2 - width/2;
C2D_DrawText(text, C2D_WithColor, offset, y, z, scaleX, scaleY, color);
}
void draw_text_center(gfxScreen_t target, float y, float z, float scaleX, float scaleY, Color color, const char * text)
{
C2D_Text text_arr[MAX_LINES];
float offsets_arr[MAX_LINES];
int actual_lines = 0;
const char * end = text - 1;
do {
end = C2D_TextParseLine(&text_arr[actual_lines], dynamicBuf, end + 1, actual_lines);
actual_lines++;
} while(*end == '\n');
for(int i = 0; i < actual_lines; i++)
{
C2D_TextOptimize(&text_arr[i]);
float width = 0;
C2D_TextGetDimensions(&text_arr[i], scaleX, scaleY, &width, NULL);
offsets_arr[i] = (target == GFX_TOP ? 400 : 320)/2 - width/2;
}
for(int i = 0; i < actual_lines; i++)
{
C2D_DrawText(&text_arr[i], C2D_WithColor, offsets_arr[i], y, z, scaleX, scaleY, color);
}
} }
void draw_base_interface(void) void draw_base_interface(void)
{ {
start_frame(); pp2d_begin_draw(GFX_TOP, GFX_LEFT);
set_screen(top); pp2d_draw_rectangle(0, 0, 400, 23, COLOR_ACCENT);
C2D_DrawRectSolid(0, 0, 0.5f, 400, 23, colors[COLOR_ACCENT]);
time_t t = time(NULL); time_t t = time(NULL);
struct tm tm = *localtime(&t); struct tm tm = *localtime(&t);
char string_hours[3] = {0}; pp2d_draw_textf(7, 2, 0.6, 0.6, COLOR_WHITE, "%.2i", tm.tm_hour);
sprintf(string_hours, "%.2i", tm.tm_hour); pp2d_draw_text(28, 1, 0.6, 0.6, COLOR_WHITE, (tm.tm_sec % 2 == 1) ? ":" : " ");
pp2d_draw_textf(34, 2, 0.6, 0.6, COLOR_WHITE, "%.2i", tm.tm_min);
C2D_Text hours, separator, minutes;
C2D_TextParse(&hours, dynamicBuf, string_hours);
C2D_TextOptimize(&hours);
if(tm.tm_sec % 2 == 1)
{
C2D_TextParse(&separator, dynamicBuf, ":");
C2D_TextOptimize(&separator);
}
char string_minutes[3] = {0};
sprintf(string_minutes, "%.2i", tm.tm_min);
C2D_TextParse(&minutes, dynamicBuf, string_minutes);
C2D_TextOptimize(&minutes);
C2D_DrawText(&hours, C2D_WithColor, 7, 2, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE]);
if(tm.tm_sec % 2 == 1)
C2D_DrawText(&separator, C2D_WithColor, 28, 1, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE]);
C2D_DrawText(&minutes, C2D_WithColor, 34, 2, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE]);
#ifndef CITRA_MODE #ifndef CITRA_MODE
u8 battery_charging = 0; u8 battery_charging = 0;
PTMU_GetBatteryChargeState(&battery_charging); PTMU_GetBatteryChargeState(&battery_charging);
u8 battery_status = 0; u8 battery_status = 0;
PTMU_GetBatteryLevel(&battery_status); PTMU_GetBatteryLevel(&battery_status);
draw_image(sprites_battery0_idx + battery_status, 357, 2); pp2d_draw_texture(TEXTURE_BATTERY_0 + battery_status, 357, 2);
if(battery_charging) if(battery_charging)
draw_image(sprites_charging_idx, 357, 2); pp2d_draw_texture(TEXTURE_BATTERY_CHARGE, 357, 2);
#endif #endif
set_screen(bottom); pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_rectangle(0, 0, 320, 24, COLOR_ACCENT);
pp2d_draw_rectangle(0, 216, 320, 24, COLOR_ACCENT);
pp2d_draw_text(7, 219, 0.6, 0.6, COLOR_WHITE, VERSION);
C2D_DrawRectSolid(0, 0, 0.5f, 320, 24, colors[COLOR_ACCENT]); pp2d_draw_on(GFX_TOP, GFX_LEFT);
C2D_DrawRectSolid(0, 216, 0.5f, 320, 24, colors[COLOR_ACCENT]); }
C2D_DrawText(&text[TEXT_VERSION], C2D_WithColor, 7, 219, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE]);
set_screen(top); static void draw_text_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const char* text)
{
char * _text = strdup(text);
float prevY = y;
int offset = 0;
while(true)
{
char *nline = strchr(_text+offset, '\n');
int nlinepos = 0;
if(nline != NULL)
{
nlinepos = nline-_text;
_text[nlinepos] = '\0';
}
pp2d_draw_text_center(target, prevY, scaleX, scaleY, color, _text+offset);
if(nline == NULL) break;
else
{
prevY += pp2d_get_text_height(_text+offset, scaleX, scaleY);
_text[nlinepos] = '\n';
offset = nlinepos+1;
}
}
free(_text);
} }
void throw_error(char* error, ErrorLevel level) void throw_error(char* error, ErrorLevel level)
{ {
Text bottom_text = TEXT_AMOUNT;
Color text_color = COLOR_WHITE;
switch(level) switch(level)
{ {
case ERROR_LEVEL_ERROR: case ERROR_LEVEL_ERROR:
bottom_text = TEXT_ERROR_QUIT;
text_color = COLOR_RED;
break;
case ERROR_LEVEL_WARNING:
bottom_text = TEXT_ERROR_CONTINUE;
text_color = COLOR_YELLOW;
break;
default:
return;
}
while(aptMainLoop()) while(aptMainLoop())
{ {
hidScanInput(); hidScanInput();
u32 kDown = hidKeysDown(); u32 kDown = hidKeysDown();
draw_base_interface(); draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.5f, 0.6f, 0.6f, colors[text_color], error); draw_text_center(GFX_TOP, 100, 0.6, 0.6, COLOR_RED, error);
draw_c2d_text_center(GFX_TOP, 150, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[bottom_text]); pp2d_draw_wtext_center(GFX_TOP, 150, 0.6, 0.6, COLOR_WHITE, L"Press \uE000 to shut down.");
end_frame(); pp2d_end_draw();
if(kDown & KEY_A) break; if(kDown & KEY_A) break;
} }
break;
case ERROR_LEVEL_WARNING:
while(aptMainLoop())
{
hidScanInput();
u32 kDown = hidKeysDown();
draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.6, 0.6, COLOR_YELLOW, error);
pp2d_draw_wtext_center(GFX_TOP, 150, 0.6, 0.6, COLOR_WHITE, L"Press \uE000 to continue.");
pp2d_end_draw();
if(kDown & KEY_A) break;
}
break;
}
} }
bool draw_confirm(const char* conf_msg, Entry_List_s* list) bool draw_confirm(const char* conf_msg, Entry_List_s* list)
@@ -313,10 +160,10 @@ bool draw_confirm(const char* conf_msg, Entry_List_s* list)
{ {
Instructions_s instructions = {0}; Instructions_s instructions = {0};
draw_interface(list, instructions); draw_interface(list, instructions);
set_screen(top); pp2d_draw_on(GFX_TOP, GFX_LEFT);
draw_text_center(GFX_TOP, BUTTONS_Y_LINE_1, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], conf_msg); draw_text_center(GFX_TOP, BUTTONS_Y_LINE_1, 0.7, 0.7, COLOR_YELLOW, conf_msg);
draw_c2d_text_center(GFX_TOP, BUTTONS_Y_LINE_3, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[TEXT_CONFIRM_YES_NO]); pp2d_draw_wtext_center(GFX_TOP, BUTTONS_Y_LINE_3, 0.6, 0.6, COLOR_WHITE, L"\uE000 Yes \uE001 No");
end_frame(); pp2d_end_draw();
hidScanInput(); hidScanInput();
u32 kDown = hidKeysDown(); u32 kDown = hidKeysDown();
@@ -327,21 +174,68 @@ bool draw_confirm(const char* conf_msg, Entry_List_s* list)
return false; return false;
} }
void draw_preview(C2D_Image preview, int preview_offset) void draw_preview(ssize_t previewID, int preview_offset)
{ {
start_frame(); pp2d_begin_draw(GFX_TOP, GFX_LEFT);
set_screen(top); pp2d_draw_texture_part(previewID, 0, 0, preview_offset, 0, 400, 240);
C2D_DrawImageAt(preview, -preview_offset, 0, 0.5f, NULL, 1.0f, 1.0f); pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
set_screen(bottom); pp2d_draw_texture_part(previewID, 0, 0, 40 + preview_offset, 240, 320, 240);
C2D_DrawImageAt(preview, -(preview_offset+40), -240, 0.5f, NULL, 1.0f, 1.0f);
} }
static void draw_install_handler(InstallType type) static void draw_install_handler(InstallType type)
{ {
if(type != INSTALL_NONE) switch(type)
{ {
C2D_Text * install_text = &text[type]; case INSTALL_LOADING_THEMES:
draw_c2d_text_center(GFX_TOP, 120.0f, 0.5f, 0.8f, 0.8f, colors[COLOR_WHITE], install_text); pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Loading themes, please wait...");
break;
case INSTALL_LOADING_SPLASHES:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Loading splashes, please wait...");
break;
case INSTALL_LOADING_ICONS:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Loading icons, please wait...");
break;
case INSTALL_LOADING_REMOTE_THEMES:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading theme list, please wait...");
break;
case INSTALL_LOADING_REMOTE_SPLASHES:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading splash list, please wait...");
break;
case INSTALL_LOADING_REMOTE_PREVIEW:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading preview, please wait...");
break;
case INSTALL_LOADING_REMOTE_BGM:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading BGM, please wait...");
break;
case INSTALL_SINGLE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing a single theme...");
break;
case INSTALL_SHUFFLE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing shuffle themes...");
break;
case INSTALL_BGM:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing BGM-only theme...");
break;
case INSTALL_DOWNLOAD:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading...");
break;
case INSTALL_CHECKING_DOWNLOAD:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Checking downloaded file...");
break;
case INSTALL_SPLASH:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing a splash...");
break;
case INSTALL_SPLASH_DELETE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Deleting installed splash...");
break;
case INSTALL_ENTRY_DELETE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Deleting from SD...");
break;
case INSTALL_NO_BGM:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing theme without BGM...");
break;
default:
break;
} }
} }
@@ -349,26 +243,28 @@ void draw_install(InstallType type)
{ {
draw_base_interface(); draw_base_interface();
draw_install_handler(type); draw_install_handler(type);
end_frame(); pp2d_end_draw();
} }
void draw_loading_bar(u32 current, u32 max, InstallType type) void draw_loading_bar(u32 current, u32 max, InstallType type)
{ {
draw_base_interface(); draw_base_interface();
draw_install_handler(type); draw_install_handler(type);
set_screen(bottom); pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
double percent = 100*((double)current/(double)max); double percent = 100*((double)current/(double)max);
u32 width = (u32)percent; u32 width = (u32)percent;
width *= 2; width *= 2;
C2D_DrawRectSolid(60-1, 110-1, 0.5f, 200+2, 20+2, colors[COLOR_CURSOR]); pp2d_draw_rectangle(60-1, 110-1, 200+2, 20+2, COLOR_CURSOR);
C2D_DrawRectSolid(60, 110, 0.5f, width, 20, colors[COLOR_ACCENT]); pp2d_draw_rectangle(60, 110, width, 20, COLOR_ACCENT);
end_frame(); pp2d_end_draw();
} }
static void draw_instructions(Instructions_s instructions) static void draw_instructions(Instructions_s instructions)
{ {
pp2d_draw_on(GFX_TOP, GFX_LEFT);
if(instructions.info_line != NULL) if(instructions.info_line != NULL)
draw_text_center(GFX_TOP, BUTTONS_Y_INFO, 0.5, 0.55, 0.55, colors[COLOR_WHITE], instructions.info_line); pp2d_draw_wtext_center(GFX_TOP, BUTTONS_Y_INFO, 0.55, 0.55, instructions.info_line_color, instructions.info_line);
const int y_lines[BUTTONS_INFO_LINES-1] = { const int y_lines[BUTTONS_INFO_LINES-1] = {
BUTTONS_Y_LINE_1, BUTTONS_Y_LINE_1,
@@ -379,127 +275,46 @@ static void draw_instructions(Instructions_s instructions)
for(int i = 0; i < BUTTONS_INFO_LINES-1; i++) for(int i = 0; i < BUTTONS_INFO_LINES-1; i++)
{ {
if(instructions.instructions[i][0] != NULL) if(instructions.instructions[i][0] != NULL)
draw_text_wrap_scaled(BUTTONS_X_LEFT, y_lines[i], 0.5, colors[COLOR_WHITE], instructions.instructions[i][0], 0.6, 0, BUTTONS_X_RIGHT-2); pp2d_draw_wtext(BUTTONS_X_LEFT, y_lines[i], 0.6, 0.6, COLOR_WHITE, instructions.instructions[i][0]);
if(instructions.instructions[i][1] != NULL) if(instructions.instructions[i][1] != NULL)
draw_text_wrap_scaled(BUTTONS_X_RIGHT, y_lines[i], 0.5, colors[COLOR_WHITE], instructions.instructions[i][1], 0.6, 0, BUTTONS_X_MAX-2); pp2d_draw_wtext(BUTTONS_X_RIGHT, y_lines[i], 0.6, 0.6, COLOR_WHITE, instructions.instructions[i][1]);
} }
C2D_ImageTint white_tint; const wchar_t * start_line = instructions.instructions[BUTTONS_INFO_LINES-1][0];
C2D_PlainImageTint(&white_tint, colors[COLOR_WHITE], 1.0f);
const char * start_line = instructions.instructions[BUTTONS_INFO_LINES-1][0];
if(start_line != NULL) if(start_line != NULL)
{ {
C2D_SpriteSetPos(&sprite_start, BUTTONS_X_LEFT-10, BUTTONS_Y_LINE_4 + 3); pp2d_draw_texture(TEXTURE_START_BUTTON, BUTTONS_X_LEFT-10, BUTTONS_Y_LINE_4 + 3);
C2D_DrawSpriteTinted(&sprite_start, &white_tint); pp2d_draw_wtext(BUTTONS_X_LEFT+26, BUTTONS_Y_LINE_4, 0.6, 0.6, COLOR_WHITE, start_line);
draw_text_wrap_scaled(BUTTONS_X_LEFT+26, BUTTONS_Y_LINE_4, 0.5, colors[COLOR_WHITE], start_line, 0.6, 0, BUTTONS_X_RIGHT-2);
} }
const char * select_line = instructions.instructions[BUTTONS_INFO_LINES-1][1]; const wchar_t * select_line = instructions.instructions[BUTTONS_INFO_LINES-1][1];
if(select_line != NULL) if(select_line != NULL)
{ {
C2D_SpriteSetPos(&sprite_select, BUTTONS_X_RIGHT-10, BUTTONS_Y_LINE_4 + 3); pp2d_draw_texture(TEXTURE_SELECT_BUTTON, BUTTONS_X_RIGHT-10, BUTTONS_Y_LINE_4 + 3);
C2D_DrawSpriteTinted(&sprite_select, &white_tint); pp2d_draw_wtext(BUTTONS_X_RIGHT+26, BUTTONS_Y_LINE_4, 0.6, 0.6, COLOR_WHITE, select_line);
draw_text_wrap_scaled(BUTTONS_X_RIGHT+26, BUTTONS_Y_LINE_4, 0.5, colors[COLOR_WHITE], select_line, 0.6, 0, BUTTONS_X_MAX-2);
}
}
void draw_text_wrap(float x, float y, float z, float scaleX, float scaleY, Color color, const char * text, float max_width)
{
// sanity check
if(max_width <= 0)
return;
int length = strlen(text) + 1;
char result[length]; // of note is that, if `text` has no spaces in it and needs to be wrapped, this can and will overflow (!!)
memset(result, 0, length);
int idx = 0;
float current_width = 0;
while(*text)
{
ssize_t consumed;
u32 codepoint;
if(*text == '\n')
current_width = 0;
if(*text == '\r')
{
text++;
continue;
}
if((consumed = decode_utf8(&codepoint, (unsigned char*)text)) == -1)
break;
float character_width = scaleX * (fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(codepoint))->charWidth);
if((current_width += character_width) > max_width)
{
char* last_space = NULL;
for(int i = idx; i >= 0; i--)
{
if(result[i] == ' ')
{
last_space = &result[i];
break;
}
}
if(last_space != NULL)
*last_space = '\n';
else
result[idx++] = '\n';
current_width = 0;
}
memcpy(&result[idx], text, consumed);
idx += consumed;
text += consumed;
}
draw_text(x, y, z, scaleX, scaleY, color, result);
}
void draw_text_wrap_scaled(float x, float y, float z, Color color, const char * text, float max_scale, float min_scale, float max_width)
{
// sanity check
if(max_scale < 0 || min_scale < 0)
return;
float width = 0;
get_text_dimensions(text, max_scale, max_scale, &width, NULL);
float scale = 0;
if(width < max_width)
{
draw_text(x, y, z, max_scale, max_scale, color, text);
}
else if((scale = max_width / width) >= min_scale)
{
draw_text(x, y, z, scale, scale, color, text);
}
else
{
draw_text_wrap(x, y, z, min_scale, min_scale, color, text, max_width);
} }
} }
static void draw_entry_info(Entry_s * entry) static void draw_entry_info(Entry_s * entry)
{ {
char author[0x41] = {0}; float wrap = 363;
utf16_to_utf8((u8*)author, entry->author, 0x40);
draw_c2d_text(20, 35, 0.5, 0.5, 0.5, colors[COLOR_WHITE], &text[TEXT_BY_AUTHOR]);
float width = 0;
C2D_TextGetDimensions(&text[TEXT_BY_AUTHOR], 0.5, 0.5, &width, NULL);
draw_text(20+width, 35, 0.5, 0.5, 0.5, colors[COLOR_WHITE], author);
char title[0x41] = {0}; wchar_t author[0x41] = {0};
utf16_to_utf8((u8*)title, entry->name, 0x40); utf16_to_utf32((u32*)author, entry->author, 0x40);
draw_text(20, 50, 0.5, 0.7, 0.7, colors[COLOR_WHITE], title); pp2d_draw_text(20, 35, 0.5, 0.5, COLOR_WHITE, "By ");
pp2d_draw_wtext_wrap(40, 35, 0.5, 0.5, COLOR_WHITE, wrap, author);
char description[0x81] = {0}; wchar_t title[0x41] = {0};
utf16_to_utf8((u8*)description, entry->desc, 0x80); utf16_to_utf32((u32*)title, entry->name, 0x40);
draw_text_wrap(20, 70, 0.5, 0.5, 0.5, colors[COLOR_WHITE], description, 363); pp2d_draw_wtext_wrap(20, 50, 0.7, 0.7, COLOR_WHITE, wrap, title);
int width = (int)pp2d_get_wtext_width(title, 0.7, 0.7);
int height = (int)pp2d_get_wtext_height(title, 0.7, 0.7);
int count = ((width - (width % (int)wrap))/wrap) + 1;
wchar_t description[0x81] = {0};
utf16_to_utf32((u32*)description, entry->desc, 0x80);
pp2d_draw_wtext_wrap(20, 50+count*height, 0.5, 0.5, COLOR_WHITE, wrap, description);
} }
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions) void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
@@ -507,12 +322,12 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
draw_base_interface(); draw_base_interface();
EntryMode current_mode = list->mode; EntryMode current_mode = list->mode;
C2D_Text* mode_string[MODE_AMOUNT] = { const char* mode_string[MODE_AMOUNT] = {
&text[TEXT_THEMEPLAZA_THEME_MODE], "ThemePlaza Theme mode",
&text[TEXT_THEMEPLAZA_SPLASH_MODE], "ThemePlaza Splash mode",
}; };
draw_c2d_text_center(GFX_TOP, 4, 0.5f, 0.5f, 0.5f, colors[COLOR_WHITE], mode_string[current_mode]); pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, mode_string[current_mode]);
draw_instructions(instructions); draw_instructions(instructions);
@@ -520,18 +335,17 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
Entry_s * current_entry = &list->entries[selected_entry]; Entry_s * current_entry = &list->entries[selected_entry];
draw_entry_info(current_entry); draw_entry_info(current_entry);
set_screen(bottom); pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
draw_c2d_text(7, 3, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[TEXT_SEARCH]); pp2d_draw_text(7, 3, 0.6, 0.6, COLOR_WHITE, "Search...");
draw_image(sprites_list_idx, 320-96, 0); pp2d_draw_texture_blend(TEXTURE_LIST, 320-96, 0, COLOR_WHITE);
draw_image(sprites_exit_idx, 320-72, 0); pp2d_draw_texture_blend(TEXTURE_EXIT, 320-72, 0, COLOR_WHITE);
draw_image(sprites_preview_idx, 320-48, 0); pp2d_draw_texture_blend(TEXTURE_PREVIEW_ICON, 320-48, 0, COLOR_WHITE);
pp2d_draw_textf(320-24+2.5, -3, 1, 1, COLOR_WHITE, "%c", mode_string[!list->mode][11]);
draw_text(320-24+2.5, -3, 0.6, 1.0f, 0.9f, colors[COLOR_WHITE], mode_switch_char[!current_mode]); pp2d_draw_texture(TEXTURE_ARROW_SIDE, 3, 114);
pp2d_draw_texture_flip(TEXTURE_ARROW_SIDE, 308, 114, HORIZONTAL);
draw_image(sprites_arrow_left_idx, 3, 114);
draw_image(sprites_arrow_right_idx, 308, 114);
for(int i = list->scroll; i < (list->entries_loaded + list->scroll); i++) for(int i = list->scroll; i < (list->entries_loaded + list->scroll); i++)
{ {
@@ -539,8 +353,8 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
current_entry = &list->entries[i]; current_entry = &list->entries[i];
char name[0x41] = {0}; wchar_t name[0x41] = {0};
utf16_to_utf8((u8*)name, current_entry->name, 0x40); utf16_to_utf32((u32*)name, current_entry->name, 0x40);
int vertical_offset = 0; int vertical_offset = 0;
int horizontal_offset = i - list->scroll; int horizontal_offset = i - list->scroll;
@@ -554,37 +368,34 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
if(!current_entry->placeholder_color) if(!current_entry->placeholder_color)
{ {
C2D_Image * image = list->icons[i]; ssize_t id = list->icons_ids[i];
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f); pp2d_draw_texture(id, horizontal_offset, vertical_offset);
} }
else else
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, list->entry_size, current_entry->placeholder_color); pp2d_draw_rectangle(horizontal_offset, vertical_offset, list->entry_size, list->entry_size, current_entry->placeholder_color);
if(i == selected_entry) if(i == selected_entry)
{ {
unsigned int border_width = 3; unsigned int border_width = 3;
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, border_width, list->entry_size, colors[COLOR_CURSOR]); pp2d_draw_rectangle(horizontal_offset, vertical_offset, border_width, list->entry_size, COLOR_CURSOR);
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, border_width, colors[COLOR_CURSOR]); pp2d_draw_rectangle(horizontal_offset, vertical_offset, list->entry_size, border_width, COLOR_CURSOR);
C2D_DrawRectSolid(horizontal_offset, vertical_offset+list->entry_size-border_width, 0.5f, list->entry_size, border_width, colors[COLOR_CURSOR]); pp2d_draw_rectangle(horizontal_offset, vertical_offset+list->entry_size-border_width, list->entry_size, border_width, COLOR_CURSOR);
C2D_DrawRectSolid(horizontal_offset+list->entry_size-border_width, vertical_offset, 0.5f, border_width, list->entry_size, colors[COLOR_CURSOR]); pp2d_draw_rectangle(horizontal_offset+list->entry_size-border_width, vertical_offset, border_width, list->entry_size, COLOR_CURSOR);
} }
} }
char entries_count_str[0x20] = {0}; char entries_count_str[0x20] = {0};
sprintf(entries_count_str, "/%" JSON_INTEGER_FORMAT, list->tp_page_count); sprintf(entries_count_str, "/%" JSON_INTEGER_FORMAT, list->tp_page_count);
float x = 316; float x = 316;
float width = 0; x -= pp2d_get_text_width(entries_count_str, 0.6, 0.6);
get_text_dimensions(entries_count_str, 0.6, 0.6, &width, NULL); pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, entries_count_str);
x -= width;
draw_text(x, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], entries_count_str);
char selected_entry_str[0x20] = {0}; char selected_entry_str[0x20] = {0};
sprintf(selected_entry_str, "%" JSON_INTEGER_FORMAT, list->tp_current_page); sprintf(selected_entry_str, "%" JSON_INTEGER_FORMAT, list->tp_current_page);
get_text_dimensions(selected_entry_str, 0.6, 0.6, &width, NULL); x -= pp2d_get_text_width(selected_entry_str, 0.6, 0.6);
x -= width; pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, selected_entry_str);
draw_text(x, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], selected_entry_str);
draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_PAGE]); pp2d_draw_text(176, 219, 0.6, 0.6, COLOR_WHITE, "Page:");
} }
void draw_interface(Entry_List_s* list, Instructions_s instructions) void draw_interface(Entry_List_s* list, Instructions_s instructions)
@@ -592,47 +403,40 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
draw_base_interface(); draw_base_interface();
EntryMode current_mode = list->mode; EntryMode current_mode = list->mode;
C2D_Text* mode_string[MODE_AMOUNT] = { const char* mode_string[MODE_AMOUNT] = {
&text[TEXT_THEME_MODE], "Theme mode",
&text[TEXT_SPLASH_MODE], "Splash mode",
}; };
draw_c2d_text_center(GFX_TOP, 4, 0.5f, 0.5f, 0.5f, colors[COLOR_WHITE], mode_string[current_mode]); pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, mode_string[current_mode]);
if(list->entries == NULL) if(list->entries == NULL)
{ {
C2D_Text* mode_found_string[MODE_AMOUNT] = { const char* mode_found_string[MODE_AMOUNT] = {
&text[TEXT_NO_THEME_FOUND], "No themes found",
&text[TEXT_NO_SPLASH_FOUND], "No splashes found",
}; };
pp2d_draw_text_center(GFX_TOP, 80, 0.7, 0.7, COLOR_YELLOW, mode_found_string[current_mode]);
draw_c2d_text_center(GFX_TOP, 80, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], mode_found_string[current_mode]); pp2d_draw_text_center(GFX_TOP, 110, 0.7, 0.7, COLOR_YELLOW, "Press \uE005 to download from QR");
draw_c2d_text_center(GFX_TOP, 110, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], &text[TEXT_DOWNLOAD_FROM_QR]); const char* mode_switch_string[MODE_AMOUNT] = {
"Or \uE004 to switch to splashes",
C2D_Text* mode_switch_string[MODE_AMOUNT] = { "Or \uE004 to switch to themes",
&text[TEXT_SWITCH_TO_SPLASHES],
&text[TEXT_SWITCH_TO_THEMES],
}; };
pp2d_draw_text_center(GFX_TOP, 140, 0.7, 0.7, COLOR_YELLOW, mode_switch_string[current_mode]);
pp2d_draw_text_center(GFX_TOP, 170, 0.7, 0.7, COLOR_YELLOW, "Or to quit");
pp2d_texture_select(TEXTURE_START_BUTTON, 162, 173);
pp2d_texture_blend(COLOR_YELLOW);
pp2d_texture_scale(1.25, 1.4);
pp2d_texture_draw();
draw_c2d_text_center(GFX_TOP, 140, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], mode_switch_string[current_mode]); pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
draw_c2d_text_center(GFX_TOP, 170, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], &text[TEXT_OR_START_TO_QUIT]);
C2D_ImageTint yellow_tint; pp2d_draw_texture_blend(TEXTURE_SORT, 320-144, 0, COLOR_WHITE);
C2D_PlainImageTint(&yellow_tint, colors[COLOR_YELLOW], 1.0f); pp2d_draw_texture_blend(TEXTURE_DOWNLOAD, 320-120, 0, COLOR_WHITE);
C2D_SpriteSetPos(&sprite_start, 162, 173); pp2d_draw_texture_blend(TEXTURE_BROWSE, 320-96, 0, COLOR_WHITE);
C2D_SpriteSetScale(&sprite_start, 1.25f, 1.4f); pp2d_draw_texture_blend(TEXTURE_EXIT, 320-72, 0, COLOR_WHITE);
C2D_DrawSpriteTinted(&sprite_start, &yellow_tint); pp2d_draw_texture_blend(TEXTURE_PREVIEW_ICON, 320-48, 0, COLOR_WHITE);
C2D_SpriteSetScale(&sprite_start, 1.0f, 1.0f); pp2d_draw_textf(320-24+2.5, -3, 1, 1, COLOR_WHITE, "%c", mode_string[!list->mode][0]);
set_screen(bottom);
draw_image(sprites_sort_idx, 320-144, 0);
draw_image(sprites_download_idx, 320-120, 0);
draw_image(sprites_browse_idx, 320-96, 0);
draw_image(sprites_exit_idx, 320-72, 0);
draw_image(sprites_preview_idx, 320-48, 0);
draw_text(320-24+2.5, -3, 0.6, 1.0f, 0.9f, colors[COLOR_WHITE], mode_switch_char[!current_mode]);
return; return;
} }
@@ -643,30 +447,30 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
Entry_s * current_entry = &list->entries[selected_entry]; Entry_s * current_entry = &list->entries[selected_entry];
draw_entry_info(current_entry); draw_entry_info(current_entry);
set_screen(bottom); pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
if(current_mode == MODE_THEMES) switch(current_mode)
{ {
char * shuffle_count_string = NULL; case MODE_THEMES:
asprintf(&shuffle_count_string, "Shuffle: %i/10", list->shuffle_count); pp2d_draw_textf(7, 3, 0.6, 0.6, list->shuffle_count <= 10 && list->shuffle_count >= 2 ? COLOR_WHITE : COLOR_RED, "Shuffle: %i/10", list->shuffle_count);
draw_text(7, 3, 0.6, 0.6, 0.6f, list->shuffle_count <= 10 && list->shuffle_count >= 2 ? colors[COLOR_WHITE] : colors[COLOR_RED], shuffle_count_string); break;
free(shuffle_count_string); default:
break;
} }
draw_image(sprites_sort_idx, 320-144, 0); pp2d_draw_texture_blend(TEXTURE_SORT, 320-144, 0, COLOR_WHITE);
draw_image(sprites_download_idx, 320-120, 0); pp2d_draw_texture_blend(TEXTURE_DOWNLOAD, 320-120, 0, COLOR_WHITE);
draw_image(sprites_browse_idx, 320-96, 0); pp2d_draw_texture_blend(TEXTURE_BROWSE, 320-96, 0, COLOR_WHITE);
draw_image(sprites_exit_idx, 320-72, 0); pp2d_draw_texture_blend(TEXTURE_EXIT, 320-72, 0, COLOR_WHITE);
draw_image(sprites_preview_idx, 320-48, 0); pp2d_draw_texture_blend(TEXTURE_PREVIEW_ICON, 320-48, 0, COLOR_WHITE);
pp2d_draw_textf(320-24+2.5, -3, 1, 1, COLOR_WHITE, "%c", mode_string[!list->mode][0]);
draw_text(320-24+2.5, -3, 0.6, 1.0f, 0.9f, colors[COLOR_WHITE], mode_switch_char[!current_mode]);
// Show arrows if there are themes out of bounds // Show arrows if there are themes out of bounds
//---------------------------------------------------------------- //----------------------------------------------------------------
if(list->scroll > 0) if(list->scroll > 0)
draw_image(sprites_arrow_up_idx, 152, 4); pp2d_draw_texture(TEXTURE_ARROW, 152, 4);
if(list->scroll + list->entries_loaded < list->entries_count) if(list->scroll + list->entries_loaded < list->entries_count)
draw_image(sprites_arrow_down_idx, 152, 220); pp2d_draw_texture_flip(TEXTURE_ARROW, 152, 220, VERTICAL);
for(int i = list->scroll; i < (list->entries_loaded + list->scroll); i++) for(int i = list->scroll; i < (list->entries_loaded + list->scroll); i++)
{ {
@@ -674,8 +478,8 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
current_entry = &list->entries[i]; current_entry = &list->entries[i];
char name[0x41] = {0}; wchar_t name[0x41] = {0};
utf16_to_utf8((u8*)name, current_entry->name, 0x40); utf16_to_utf32((u32*)name, current_entry->name, 0x40);
int vertical_offset = i - list->scroll; int vertical_offset = i - list->scroll;
int horizontal_offset = 0; int horizontal_offset = 0;
@@ -683,67 +487,47 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
vertical_offset *= list->entry_size; vertical_offset *= list->entry_size;
vertical_offset += 24; vertical_offset += 24;
u32 font_color = colors[COLOR_WHITE]; u32 font_color = COLOR_WHITE;
if(i == selected_entry) if(i == selected_entry)
{ {
font_color = colors[COLOR_BLACK]; font_color = COLOR_BLACK;
C2D_DrawRectSolid(0, vertical_offset, 0.5f, 320, list->entry_size, colors[COLOR_CURSOR]); pp2d_draw_rectangle(0, vertical_offset, 320, list->entry_size, COLOR_CURSOR);
} }
draw_text(list->entry_size+6, vertical_offset + 16, 0.5f, 0.55, 0.55, font_color, name); pp2d_draw_wtext(list->entry_size+6, vertical_offset + 16, 0.55, 0.55, font_color, name);
C2D_ImageTint tint;
C2D_PlainImageTint(&tint, font_color, 1.0f);
if(current_entry->no_bgm_shuffle) if(current_entry->no_bgm_shuffle)
{ pp2d_draw_texture_blend(TEXTURE_SHUFFLE_NO_BGM, 320-24-4, vertical_offset, font_color);
C2D_SpriteSetPos(&sprite_shuffle_no_bgm, 320-24-4, vertical_offset);
C2D_DrawSpriteTinted(&sprite_shuffle_no_bgm, &tint);
}
else if(current_entry->in_shuffle) else if(current_entry->in_shuffle)
{ pp2d_draw_texture_blend(TEXTURE_SHUFFLE, 320-24-4, vertical_offset, font_color);
C2D_SpriteSetPos(&sprite_shuffle, 320-24-4, vertical_offset);
C2D_DrawSpriteTinted(&sprite_shuffle, &tint);
}
if(current_entry->installed) if(current_entry->installed)
{ pp2d_draw_texture_blend(TEXTURE_INSTALLED, 320-24-4, vertical_offset + 22, font_color);
C2D_SpriteSetPos(&sprite_installed, 320-24-4, vertical_offset + 22);
C2D_DrawSpriteTinted(&sprite_installed, &tint);
}
if(!current_entry->placeholder_color) if(!current_entry->placeholder_color)
{ {
C2D_Image * image = NULL; ssize_t id = 0;
if(list->entries_count > list->entries_loaded*ICONS_OFFSET_AMOUNT) if(list->entries_count > list->entries_loaded*ICONS_OFFSET_AMOUNT)
image = list->icons[ICONS_VISIBLE*list->entries_loaded + (i - list->scroll)]; id = list->icons_ids[ICONS_VISIBLE*list->entries_loaded + (i - list->scroll)];
else else
image = list->icons[i]; id = list->icons_ids[i];
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f); pp2d_draw_texture(id, horizontal_offset, vertical_offset);
} }
else else
{ pp2d_draw_rectangle(horizontal_offset, vertical_offset, list->entry_size, list->entry_size, current_entry->placeholder_color);
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, list->entry_size, current_entry->placeholder_color);
}
} }
char entries_count_str[0x20] = {0}; char entries_count_str[0x20] = {0};
sprintf(entries_count_str, "/%i", list->entries_count); sprintf(entries_count_str, "/%i", list->entries_count);
float x = 316; float x = 316;
float width = 0; x -= pp2d_get_text_width(entries_count_str, 0.6, 0.6);
get_text_dimensions(entries_count_str, 0.6, 0.6, &width, NULL); pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, entries_count_str);
x -= width;
draw_text(x, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], entries_count_str);
char selected_entry_str[0x20] = {0}; char selected_entry_str[0x20] = {0};
sprintf(selected_entry_str, "%i", selected_entry + 1); sprintf(selected_entry_str, "%i", selected_entry + 1);
get_text_dimensions(selected_entry_str, 0.6, 0.6, &width, NULL); x -= pp2d_get_text_width(selected_entry_str, 0.6, 0.6);
x -= width; pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, selected_entry_str);
draw_text(x, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], selected_entry_str);
if(list->entries_count < 10000) pp2d_draw_text(176, 219, 0.6, 0.6, COLOR_WHITE, list->entries_count < 1000 ? "Selected:" : "Sel.:");
draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_SELECTED]);
else
draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_SELECTED_SHORT]);
} }

View File

@@ -200,7 +200,4 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
FSUSER_DeleteFile(archive, path); FSUSER_DeleteFile(archive, path);
} }
FSUSER_CreateFile(archive, path, 0, size); FSUSER_CreateFile(archive, path, 0, size);
char *buf = calloc(size, 1);
buf_to_file(size, path, archive, buf);
free(buf);
} }

View File

@@ -25,13 +25,12 @@
*/ */
#include "loading.h" #include "loading.h"
#include "pp2d/pp2d/pp2d.h"
#include "fs.h" #include "fs.h"
#include "unicode.h" #include "unicode.h"
#include "music.h" #include "music.h"
#include "draw.h" #include "draw.h"
#include <png.h>
void delete_entry(Entry_s * entry, bool is_file) void delete_entry(Entry_s * entry, bool is_file)
{ {
if(is_file) if(is_file)
@@ -56,71 +55,54 @@ u32 load_data(char * filename, Entry_s entry, char ** buf)
} }
} }
// Function taken and adapted from https://github.com/BernardoGiordano/Checkpoint/blob/master/3ds/source/title.cpp static void parse_smdh(Entry_s * entry, const u16 * fallback_name)
C2D_Image * loadTextureIcon(Icon_s *icon)
{
if(icon == NULL)
return NULL;
C2D_Image * image = calloc(1, sizeof(C2D_Image));
C3D_Tex* tex = malloc(sizeof(C3D_Tex));
static const Tex3DS_SubTexture subt3x = { 48, 48, 0.0f, 48/64.0f, 48/64.0f, 0.0f };
image->tex = tex;
image->subtex = &subt3x;
C3D_TexInit(image->tex, 64, 64, GPU_RGB565);
u16* dest = (u16*)image->tex->data + (64-48)*64;
u16* src = icon->big_icon;
for (int j = 0; j < 48; j += 8)
{
memcpy(dest, src, 48*8*sizeof(u16));
src += 48*8;
dest += 64*8;
}
return image;
}
void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name)
{
if(icon == NULL)
{
memcpy(entry->name, fallback_name, 0x80);
utf8_to_utf16(entry->desc, (u8*)"No description", 0x100);
utf8_to_utf16(entry->author, (u8*)"Unknown author", 0x80);
entry->placeholder_color = C2D_Color32(rand() % 255, rand() % 255, rand() % 255, 255);
return;
}
memcpy(entry->name, icon->name, 0x40*sizeof(u16));
memcpy(entry->desc, icon->desc, 0x80*sizeof(u16));
memcpy(entry->author, icon->author, 0x40*sizeof(u16));
}
static void parse_entry_smdh(Entry_s * entry, const u16 * fallback_name)
{ {
char *info_buffer = NULL; char *info_buffer = NULL;
u64 size = load_data("/info.smdh", *entry, &info_buffer); u64 size = load_data("/info.smdh", *entry, &info_buffer);
Icon_s * smdh = (Icon_s *)info_buffer;
if(!size) if(!size)
{ {
free(info_buffer); free(info_buffer);
info_buffer = NULL; memcpy(entry->name, fallback_name, 0x80);
utf8_to_utf16(entry->desc, (u8*)"No description", 0x100);
utf8_to_utf16(entry->author, (u8*)"Unknown author", 0x80);
entry->placeholder_color = RGBA8(rand() % 255, rand() % 255, rand() % 255, 255);
return;
} }
Icon_s * smdh = (Icon_s *)info_buffer; memcpy(entry->name, smdh->name, 0x40*sizeof(u16));
memcpy(entry->desc, smdh->desc, 0x80*sizeof(u16));
parse_smdh(smdh, entry, fallback_name); memcpy(entry->author, smdh->author, 0x40*sizeof(u16));
} }
static C2D_Image * load_entry_icon(Entry_s entry) static void load_smdh_icon(Entry_s entry, const ssize_t textureID)
{ {
pp2d_free_texture(textureID);
char *info_buffer = NULL; char *info_buffer = NULL;
u64 size = load_data("/info.smdh", entry, &info_buffer); u64 size = load_data("/info.smdh", entry, &info_buffer);
if(!size) return NULL; if(!size) return;
Icon_s * smdh = (Icon_s *)info_buffer; Icon_s * smdh = (Icon_s *)info_buffer;
return loadTextureIcon(smdh);
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
for(u32 x = 0; x < width; x++)
{
for(u32 y = 0; y < height; y++)
{
unsigned int dest_pixel = (x + y*width);
unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
image[dest_pixel] = RGB565_TO_ABGR8(smdh->big_icon[source_pixel]);
}
}
free(info_buffer);
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
free(image);
} }
typedef int (*sort_comparator)(const void *, const void *); typedef int (*sort_comparator)(const void *, const void *);
@@ -210,7 +192,7 @@ Result load_entries(const char * loading_path, Entry_List_s * list)
strucat(current_entry->path, dir_entry.name); strucat(current_entry->path, dir_entry.name);
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP"); current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
parse_entry_smdh(current_entry, dir_entry.name); parse_smdh(current_entry, dir_entry.name);
} }
FSDIR_Close(dir_handle); FSDIR_Close(dir_handle);
@@ -241,11 +223,12 @@ void load_icons_first(Entry_List_s * list, bool silent)
endi = starti + list->entries_loaded*ICONS_OFFSET_AMOUNT; endi = starti + list->entries_loaded*ICONS_OFFSET_AMOUNT;
} }
list->icons = calloc(endi-starti, sizeof(C2D_Image*)); list->icons_ids = calloc(endi-starti, sizeof(ssize_t));
C2D_Image ** icons = list->icons; ssize_t * icons_ids = list->icons_ids;
ssize_t id = list->texture_id_offset;
for(int i = starti; i < endi; i++) for(int i = starti; i < endi; i++, id++)
{ {
if(!silent) if(!silent)
draw_loading_bar(i - starti, endi-starti, INSTALL_LOADING_ICONS); draw_loading_bar(i - starti, endi-starti, INSTALL_LOADING_ICONS);
@@ -257,20 +240,22 @@ void load_icons_first(Entry_List_s * list, bool silent)
offset -= list->entries_count; offset -= list->entries_count;
Entry_s current_entry = list->entries[offset]; Entry_s current_entry = list->entries[offset];
icons[i-starti] = load_entry_icon(current_entry); load_smdh_icon(current_entry, id);
icons_ids[i-starti] = id;
} }
} }
static void reverse(C2D_Image * a[], int sz) { static void reverse(ssize_t a[], int sz) {
int i, j; int i, j;
for (i = 0, j = sz; i < j; i++, j--) { for (i = 0, j = sz; i < j; i++, j--) {
C2D_Image * tmp = a[i]; ssize_t tmp = a[i];
a[i] = a[j]; a[i] = a[j];
a[j] = tmp; a[j] = tmp;
} }
} }
static void rotate(C2D_Image * array[], int size, int amt) { static void rotate(ssize_t array[], int size, int amt) {
if (amt < 0) if (amt < 0)
amt = size + amt; amt = size + amt;
reverse(array, size-amt-1); reverse(array, size-amt-1);
@@ -329,15 +314,15 @@ void handle_scrolling(Entry_List_s * list)
//---------------------------------------------------------------- //----------------------------------------------------------------
} }
static bool load_icons(Entry_List_s * current_list, Handle mutex) static void load_icons(Entry_List_s * current_list)
{ {
if(current_list == NULL || current_list->entries == NULL) if(current_list == NULL || current_list->entries == NULL)
return false; return;
handle_scrolling(current_list); handle_scrolling(current_list);
if(current_list->entries_count <= current_list->entries_loaded*ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll) if(current_list->entries_count <= current_list->entries_loaded*ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
return false; // return if the list is one that doesnt need swapping, or if nothing changed return; // return if the list is one that doesnt need swapping, or if nothing changed
#define SIGN(x) (x > 0 ? 1 : ((x < 0) ? -1 : 0)) #define SIGN(x) (x > 0 ? 1 : ((x < 0) ? -1 : 0))
@@ -356,26 +341,28 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
int ctr = 0; int ctr = 0;
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *)); Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *));
int * indexes = calloc(abs(delta), sizeof(int)); ssize_t * ids = calloc(abs(delta), sizeof(ssize_t));
bool released = false;
C2D_Image ** icons = current_list->icons; #define FIRST(arr) arr[0]
#define LAST(arr) arr[current_list->entries_loaded*ICONS_OFFSET_AMOUNT - 1]
ssize_t * icons_ids = current_list->icons_ids;
for(int i = starti; i != endi; i++, ctr++) for(int i = starti; i != endi; i++, ctr++)
{ {
int index = 0; ssize_t id = 0;
int offset = i; int offset = i;
rotate(icons, ICONS_OFFSET_AMOUNT*current_list->entries_loaded, -1*SIGN(delta)); rotate(icons_ids, ICONS_OFFSET_AMOUNT*current_list->entries_loaded, -1*SIGN(delta));
if(delta > 0) if(delta > 0)
{ {
index = current_list->entries_loaded*ICONS_OFFSET_AMOUNT - delta + i - starti; id = LAST(icons_ids);
offset += current_list->entries_loaded*ICONS_UNDER - delta; offset += current_list->entries_loaded*ICONS_UNDER - delta;
} }
else else
{ {
index = 0 - delta - 1 + i - starti; id = FIRST(icons_ids);
offset -= current_list->entries_loaded*ICONS_VISIBLE; offset -= current_list->entries_loaded*ICONS_VISIBLE;
i -= 2; //i-- twice to counter the i++, needed only for this case i -= 2; //i-- twice to counter the i++, needed only for this case
} }
@@ -386,169 +373,43 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
offset -= current_list->entries_count; offset -= current_list->entries_count;
entries[ctr] = &current_list->entries[offset]; entries[ctr] = &current_list->entries[offset];
indexes[ctr] = index; ids[ctr] = id;
} }
#undef FIRST
#undef LAST
#undef SIGN #undef SIGN
if(abs(delta) < 4) svcSleepThread(1e6);
for(int i = 0; i < abs(delta); i++)
{ {
svcReleaseMutex(mutex); Entry_s current_entry = *entries[i];
released = true; ssize_t id = ids[i];
} load_smdh_icon(current_entry, id);
svcSleepThread(1e7);
starti = 0;
endi = abs(delta);
for(int i = starti; i < endi; i++)
{
Entry_s * current_entry = entries[i];
int index = indexes[i];
C2D_Image * image = icons[index];
C3D_TexDelete(image->tex);
free(image->tex);
free(image);
icons[index] = load_entry_icon(*current_entry);
if(!released && i > endi/2)
{
svcReleaseMutex(mutex);
released = true;
}
} }
free(entries); free(entries);
free(indexes); free(ids);
current_list->previous_scroll = current_list->scroll; current_list->previous_scroll = current_list->scroll;
return released;
} }
void load_icons_thread(void * void_arg) void load_icons_thread(void * void_arg)
{ {
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg; Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
Handle mutex = *(Handle *)arg->thread_arg[1]; Handle update_request = *(Handle *)arg->thread_arg[1];
do do
{ {
svcWaitSynchronization(mutex, U64_MAX); svcWaitSynchronization(update_request, U64_MAX);
svcClearEvent(update_request);
volatile Entry_List_s * current_list = *(volatile Entry_List_s **)arg->thread_arg[0]; volatile Entry_List_s * current_list = *(volatile Entry_List_s **)arg->thread_arg[0];
bool released = load_icons((Entry_List_s *)current_list, mutex); load_icons((Entry_List_s *)current_list);
if(!released)
svcReleaseMutex(mutex);
} }
while(arg->run_thread); while(arg->run_thread);
} }
bool load_preview_from_buffer(void * buf, u32 size, C2D_Image * preview_image, int * preview_offset)
{
if(size < 8 || png_sig_cmp(buf, 0, 8))
{
throw_error("Invalid preview.png", ERROR_LEVEL_WARNING);
return false;
}
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
if(setjmp(png_jmpbuf(png)))
{
png_destroy_read_struct(&png, &info, NULL);
return false;
}
FILE * fp = fmemopen(buf, size, "rb");
png_init_io(png, fp);
png_read_info(png, info);
int width = png_get_image_width(png, info);
int height = png_get_image_height(png, info);
png_byte color_type = png_get_color_type(png, info);
png_byte bit_depth = png_get_bit_depth(png, info);
// Read any color_type into 8bit depth, ABGR format.
// See http://www.libpng.org/pub/png/libpng-manual.txt
if(bit_depth == 16)
png_set_strip_16(png);
if(color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.
if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png);
if(png_get_valid(png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png);
// These color_type don't have an alpha channel then fill it with 0xff.
if(color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if(color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
//output ABGR
png_set_bgr(png);
png_set_swap_alpha(png);
png_read_update_info(png, info);
png_bytep * row_pointers = malloc(sizeof(png_bytep) * height);
for(int y = 0; y < height; y++) {
row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png,info));
}
png_read_image(png, row_pointers);
fclose(fp);
png_destroy_read_struct(&png, &info, NULL);
free_preview(*preview_image);
C3D_Tex* tex = malloc(sizeof(C3D_Tex));
preview_image->tex = tex;
Tex3DS_SubTexture * subt3x = malloc(sizeof(Tex3DS_SubTexture));
subt3x->width = width;
subt3x->height = height;
subt3x->left = 0.0f;
subt3x->top = 1.0f;
subt3x->right = width/512.0f;
subt3x->bottom = 1.0-(height/512.0f);
preview_image->subtex = subt3x;
C3D_TexInit(preview_image->tex, 512, 512, GPU_RGBA8);
memset(preview_image->tex->data, 0, preview_image->tex->size);
for(int j = 0; j < height; j++) {
png_bytep row = row_pointers[j];
for(int i = 0; i < width; i++) {
png_bytep px = &(row[i * 4]);
u32 dst = ((((j >> 3) * (512 >> 3) + (i >> 3)) << 6) + ((i & 1) | ((j & 1) << 1) | ((i & 2) << 1) | ((j & 2) << 2) | ((i & 4) << 2) | ((j & 4) << 3))) * 4;
memcpy(preview_image->tex->data + dst, px, sizeof(u32));
}
}
*preview_offset = (width-400)/2;
return true;
}
static u16 previous_path_preview[0x106] = {0}; static u16 previous_path_preview[0x106] = {0};
bool load_preview(Entry_List_s list, C2D_Image * preview_image, int * preview_offset) bool load_preview(Entry_List_s list, int * preview_offset)
{ {
if(list.entries == NULL) return false; if(list.entries == NULL) return false;
@@ -566,33 +427,49 @@ bool load_preview(Entry_List_s list, C2D_Image * preview_image, int * preview_of
return false; return false;
} }
bool ret = load_preview_from_buffer(preview_buffer, size, preview_image, preview_offset); bool ret = false;
free(preview_buffer); u8 * image = NULL;
unsigned int width = 0, height = 0;
if(ret) if((lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size)) == 0) // no error
{ {
for(u32 i = 0; i < width; i++)
{
for(u32 j = 0; j < height; j++)
{
u32* pixel = (u32*)(image + (i + j*width) * 4);
*pixel = __builtin_bswap32(*pixel); //swap from RGBA to ABGR, needed for pp2d
}
}
// mark the new preview as loaded for optimisation // mark the new preview as loaded for optimisation
memcpy(&previous_path_preview, &entry.path, 0x106*sizeof(u16)); memcpy(&previous_path_preview, &entry.path, 0x106*sizeof(u16));
// free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_PREVIEW);
pp2d_load_texture_memory(TEXTURE_PREVIEW, image, (u32)width, (u32)height);
*preview_offset = (width-400)/2;
ret = true;
} }
else
{
throw_error("Corrupted/invalid preview.png", ERROR_LEVEL_WARNING);
}
free(image);
free(preview_buffer);
return ret; return ret;
} }
void free_preview(C2D_Image preview)
{
if(preview.tex)
C3D_TexDelete(preview.tex);
free(preview.tex);
free((Tex3DS_SubTexture*)preview.subtex);
}
// Initialize the audio struct // Initialize the audio struct
Result load_audio(Entry_s entry, audio_s *audio) Result load_audio(Entry_s entry, audio_s *audio)
{ {
audio->filesize = load_data("/bgm.ogg", entry, &audio->filebuf); audio->filesize = load_data("/bgm.ogg", entry, &audio->filebuf);
if (audio->filesize == 0) { if (audio->filesize == 0) {
free(audio); free(audio);
DEBUG("<load_audio> File not found!\n"); DEBUG("File not found!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND); return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
} }
@@ -603,13 +480,13 @@ Result load_audio(Entry_s entry, audio_s *audio)
ndspChnSetMix(0, audio->mix); // See mix comment above ndspChnSetMix(0, audio->mix); // See mix comment above
FILE *file = fmemopen(audio->filebuf, audio->filesize, "rb"); FILE *file = fmemopen(audio->filebuf, audio->filesize, "rb");
DEBUG("<load_audio> Filesize: %ld\n", audio->filesize); DEBUG("Filesize: %lld\n", audio->filesize);
if(file != NULL) if(file != NULL)
{ {
int e = ov_open(file, &audio->vf, NULL, 0); int e = ov_open(file, &audio->vf, NULL, 0);
if (e < 0) if (e < 0)
{ {
DEBUG("<load_audio> Vorbis: %d\n", e); DEBUG("Vorbis: %d\n", e);
free(audio->filebuf); free(audio->filebuf);
free(audio); free(audio);
fclose(file); fclose(file);
@@ -619,10 +496,10 @@ Result load_audio(Entry_s entry, audio_s *audio)
vorbis_info *vi = ov_info(&audio->vf, -1); vorbis_info *vi = ov_info(&audio->vf, -1);
ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file
if (vi->channels == 2) { if (vi->channels == 2) {
DEBUG("<load_audio> Using stereo\n"); DEBUG("Using stereo\n");
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); // 2 channels == Stereo ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); // 2 channels == Stereo
} else { } else {
DEBUG("<load_audio> Invalid number of channels\n"); DEBUG("Invalid number of channels\n");
free(audio->filebuf); free(audio->filebuf);
free(audio); free(audio);
fclose(file); fclose(file);
@@ -633,12 +510,12 @@ Result load_audio(Entry_s entry, audio_s *audio)
audio->wave_buf[0].status = audio->wave_buf[1].status = NDSP_WBUF_DONE; // Used in play to stop from writing to current buffer audio->wave_buf[0].status = audio->wave_buf[1].status = NDSP_WBUF_DONE; // Used in play to stop from writing to current buffer
audio->wave_buf[0].data_vaddr = linearAlloc(BUF_TO_READ); // Most vorbis packets should only be 4 KB at most (?) Possibly dangerous assumption audio->wave_buf[0].data_vaddr = linearAlloc(BUF_TO_READ); // Most vorbis packets should only be 4 KB at most (?) Possibly dangerous assumption
audio->wave_buf[1].data_vaddr = linearAlloc(BUF_TO_READ); audio->wave_buf[1].data_vaddr = linearAlloc(BUF_TO_READ);
DEBUG("<load_audio> Success!\n"); DEBUG("Success!\n");
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS); return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
} else { } else {
free(audio->filebuf); free(audio->filebuf);
free(audio); free(audio);
DEBUG("<load_audio> fmemopen failed!\n"); DEBUG("fmemopen failed!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND); return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
} }
} }

View File

@@ -33,18 +33,17 @@
#include "music.h" #include "music.h"
#include "remote.h" #include "remote.h"
#include "instructions.h" #include "instructions.h"
#include "pp2d/pp2d/pp2d.h"
#include <time.h> #include <time.h>
bool quit = false; bool quit = false;
bool dspfirm = false;
audio_s * audio = NULL; audio_s * audio = NULL;
static bool homebrew = false; static bool homebrew = false;
static bool installed_themes = false; static bool installed_themes = false;
static Thread iconLoadingThread = {0}; static Thread iconLoadingThread = {0};
static Thread_Arg_s iconLoadingThread_arg = {0}; static Thread_Arg_s iconLoadingThread_arg = {0};
static Handle update_icons_mutex; static Handle update_icons_handle;
static bool released = false;
static Thread installCheckThreads[MODE_AMOUNT] = {0}; static Thread installCheckThreads[MODE_AMOUNT] = {0};
static Thread_Arg_s installCheckThreads_arg[MODE_AMOUNT] = {0}; static Thread_Arg_s installCheckThreads_arg[MODE_AMOUNT] = {0};
@@ -78,10 +77,9 @@ static void init_services(void)
cfguInit(); cfguInit();
ptmuInit(); ptmuInit();
acInit(); acInit();
dspfirm = !ndspInit(); ndspInit();
APT_GetAppCpuTimeLimit(&old_time_limit); APT_GetAppCpuTimeLimit(&old_time_limit);
APT_SetAppCpuTimeLimit(30); APT_SetAppCpuTimeLimit(30);
// aptSetHomeAllowed(false);
httpcInit(0); httpcInit(0);
archive_result = open_archives(); archive_result = open_archives();
if(envIsHomebrew()) if(envIsHomebrew())
@@ -117,36 +115,20 @@ static void exit_thread(void)
{ {
DEBUG("exiting thread\n"); DEBUG("exiting thread\n");
iconLoadingThread_arg.run_thread = false; iconLoadingThread_arg.run_thread = false;
svcReleaseMutex(update_icons_mutex); svcSignalEvent(update_icons_handle);
svcWaitSynchronization(update_icons_mutex, U64_MAX);
threadJoin(iconLoadingThread, U64_MAX); threadJoin(iconLoadingThread, U64_MAX);
threadFree(iconLoadingThread); threadFree(iconLoadingThread);
} }
} }
static void free_icons(Entry_List_s * list)
{
int amount = list->entries_count;
if(list->entries_count > list->entries_loaded*ICONS_OFFSET_AMOUNT)
amount = list->entries_loaded*ICONS_OFFSET_AMOUNT;
for(int i = 0; i < amount; i++)
{
C3D_TexDelete(list->icons[i]->tex);
free(list->icons[i]->tex);
free(list->icons[i]);
}
free(list->icons);
}
void free_lists(void) void free_lists(void)
{ {
stop_install_check(); stop_install_check();
for(int i = 0; i < MODE_AMOUNT; i++) for(int i = 0; i < MODE_AMOUNT; i++)
{ {
Entry_List_s * current_list = &lists[i]; Entry_List_s * current_list = &lists[i];
free_icons(current_list);
free(current_list->entries); free(current_list->entries);
free(current_list->icons_ids);
memset(current_list, 0, sizeof(Entry_List_s)); memset(current_list, 0, sizeof(Entry_List_s));
} }
exit_thread(); exit_thread();
@@ -160,7 +142,7 @@ void exit_function(bool power_pressed)
svcWaitSynchronization(audio->finished, U64_MAX); svcWaitSynchronization(audio->finished, U64_MAX);
} }
free_lists(); free_lists();
svcCloseHandle(update_icons_mutex); svcCloseHandle(update_icons_handle);
exit_screens(); exit_screens();
exit_services(); exit_services();
@@ -188,6 +170,8 @@ static void start_thread(void)
static void load_lists(Entry_List_s * lists) static void load_lists(Entry_List_s * lists)
{ {
ssize_t texture_id_offset = TEXTURE_ICON;
free_lists(); free_lists();
for(int i = 0; i < MODE_AMOUNT; i++) for(int i = 0; i < MODE_AMOUNT; i++)
{ {
@@ -198,6 +182,7 @@ static void load_lists(Entry_List_s * lists)
loading_screen = INSTALL_LOADING_SPLASHES; loading_screen = INSTALL_LOADING_SPLASHES;
draw_install(loading_screen); draw_install(loading_screen);
draw_install(loading_screen);
Entry_List_s * current_list = &lists[i]; Entry_List_s * current_list = &lists[i];
current_list->mode = i; current_list->mode = i;
@@ -215,8 +200,11 @@ static void load_lists(Entry_List_s * lists)
DEBUG("total: %i\n", current_list->entries_count); DEBUG("total: %i\n", current_list->entries_count);
current_list->texture_id_offset = texture_id_offset;
load_icons_first(current_list, false); load_icons_first(current_list, false);
texture_id_offset += current_list->entries_loaded*ICONS_OFFSET_AMOUNT;
void (*install_check_function)(void*) = NULL; void (*install_check_function)(void*) = NULL;
if(i == MODE_THEMES) if(i == MODE_THEMES)
install_check_function = themes_check_installed; install_check_function = themes_check_installed;
@@ -323,25 +311,18 @@ static void toggle_shuffle(Entry_List_s * list)
} }
} }
static inline void wait_scroll(void)
{
released = true;
svcReleaseMutex(update_icons_mutex);
svcSleepThread(FASTSCROLL_WAIT);
}
int main(void) int main(void)
{ {
srand(time(NULL)); srand(time(NULL));
init_services(); init_services();
init_screens(); init_screens();
svcCreateMutex(&update_icons_mutex, true); svcCreateEvent(&update_icons_handle, RESET_ONESHOT);
static Entry_List_s * current_list = NULL; static Entry_List_s * current_list = NULL;
void * iconLoadingThread_args_void[] = { void * iconLoadingThread_args_void[] = {
&current_list, &current_list,
&update_icons_mutex, &update_icons_handle,
}; };
iconLoadingThread_arg.thread_arg = iconLoadingThread_args_void; iconLoadingThread_arg.thread_arg = iconLoadingThread_args_void;
iconLoadingThread_arg.run_thread = false; iconLoadingThread_arg.run_thread = false;
@@ -361,13 +342,11 @@ int main(void)
bool qr_mode = false; bool qr_mode = false;
bool install_mode = false; bool install_mode = false;
bool extra_mode = false; bool extra_mode = false;
C2D_Image preview = {0};
while(aptMainLoop()) while(true)
{ {
if(quit) if(quit)
{ {
free_preview(preview);
exit_function(false); exit_function(false);
return 0; return 0;
} }
@@ -409,7 +388,7 @@ int main(void)
if(qr_mode) take_picture(); if(qr_mode) take_picture();
else if(preview_mode) else if(preview_mode)
{ {
draw_preview(preview, preview_offset); draw_preview(TEXTURE_PREVIEW, preview_offset);
} }
else { else {
if(!iconLoadingThread_arg.run_thread) if(!iconLoadingThread_arg.run_thread)
@@ -419,21 +398,15 @@ int main(void)
} }
else else
{ {
if(!released) svcSignalEvent(update_icons_handle);
{ svcSleepThread(5e6);
svcReleaseMutex(update_icons_mutex);
released = true;
}
svcWaitSynchronization(update_icons_mutex, U64_MAX);
} }
draw_interface(current_list, instructions); draw_interface(current_list, instructions);
svcSleepThread(1e7); svcSleepThread(1e7);
released = false;
} }
end_frame(); pp2d_end_draw();
if(kDown & KEY_START) quit = true; if(kDown & KEY_START) quit = true;
@@ -449,11 +422,9 @@ int main(void)
else if(!qr_mode && !preview_mode && kDown & KEY_R) //toggle QR mode else if(!qr_mode && !preview_mode && kDown & KEY_R) //toggle QR mode
{ {
enable_qr: enable_qr:
draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], "Loading QR Scanner...");
end_frame();
if(R_SUCCEEDED(camInit())) if(R_SUCCEEDED(camInit()))
{ {
camExit();
u32 out; u32 out;
ACU_GetWifiStatus(&out); ACU_GetWifiStatus(&out);
if(out) if(out)
@@ -478,18 +449,13 @@ int main(void)
continue; continue;
} }
else if(!qr_mode && kDown & KEY_Y && current_list->entries != NULL) //toggle preview mode else if(!qr_mode && kDown & KEY_Y) //toggle preview mode
{ {
toggle_preview: toggle_preview:
if(!preview_mode) if(!preview_mode)
{ {
preview_mode = load_preview(*current_list, &preview, &preview_offset); preview_mode = load_preview(*current_list, &preview_offset);
if(preview_mode) if(current_mode == MODE_THEMES)
{
end_frame();
draw_preview(preview, preview_offset);
end_frame();
if(current_mode == MODE_THEMES && dspfirm)
{ {
audio = calloc(1, sizeof(audio_s)); audio = calloc(1, sizeof(audio_s));
Result r = load_audio(current_list->entries[current_list->selected_entry], audio); Result r = load_audio(current_list->entries[current_list->selected_entry], audio);
@@ -497,7 +463,6 @@ int main(void)
else audio = NULL; else audio = NULL;
} }
} }
}
else else
{ {
preview_mode = false; preview_mode = false;
@@ -685,14 +650,6 @@ int main(void)
case MODE_SPLASHES: case MODE_SPLASHES:
draw_install(INSTALL_SPLASH); draw_install(INSTALL_SPLASH);
splash_install(*current_entry); splash_install(*current_entry);
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * splash = &current_list->entries[i];
if(splash == current_entry)
splash->installed = true;
else
splash->installed = false;
}
break; break;
default: default:
break; break;
@@ -753,22 +710,22 @@ int main(void)
else if(kHeld & KEY_CPAD_UP) else if(kHeld & KEY_CPAD_UP)
{ {
change_selected(current_list, -1); change_selected(current_list, -1);
wait_scroll(); svcSleepThread(FASTSCROLL_WAIT);
} }
else if(kHeld & KEY_CPAD_DOWN) else if(kHeld & KEY_CPAD_DOWN)
{ {
change_selected(current_list, 1); change_selected(current_list, 1);
wait_scroll(); svcSleepThread(FASTSCROLL_WAIT);
} }
else if(kHeld & KEY_CPAD_LEFT) else if(kHeld & KEY_CPAD_LEFT)
{ {
change_selected(current_list, -current_list->entries_per_screen_v); change_selected(current_list, -current_list->entries_per_screen_v);
wait_scroll(); svcSleepThread(FASTSCROLL_WAIT);
} }
else if(kHeld & KEY_CPAD_RIGHT) else if(kHeld & KEY_CPAD_RIGHT)
{ {
change_selected(current_list, current_list->entries_per_screen_v); change_selected(current_list, current_list->entries_per_screen_v);
wait_scroll(); svcSleepThread(FASTSCROLL_WAIT);
} }
// Movement using the touchscreen // Movement using the touchscreen
@@ -863,9 +820,6 @@ int main(void)
} }
} }
free_preview(preview);
// aptSetHomeAllowed(true);
exit_function(true); exit_function(true);
return 0; return 0;
} }

View File

@@ -30,14 +30,14 @@
// Play a given audio struct // Play a given audio struct
Result update_audio(audio_s *audio) Result update_audio(audio_s *audio)
{ {
u32 size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read; long size = BUF_TO_READ - audio->data_read;
DEBUG("<update_audio> Audio Size: %ld\n", size); DEBUG("Audio Size: %ld\n", size);
if (audio->wave_buf[audio->buf_pos].status == NDSP_WBUF_DONE) // only run if the current selected buffer has already finished playing if (audio->wave_buf[audio->buf_pos].status == NDSP_WBUF_DONE) // only run if the current selected buffer has already finished playing
{ {
DEBUG("<update_audio> Attempting ov_read\n"); DEBUG("Attempting ov_read\n");
int bitstream; int bitstream;
u32 read = ov_read(&audio->vf, (char*)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, &bitstream); // read 1 vorbis packet into wave buffer size_t read = ov_read(&audio->vf, (char*)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, &bitstream); // read 1 vorbis packet into wave buffer
DEBUG("<update_audio> ov_read successful\n"); DEBUG("ov_read successful\n");
if (read <= 0) // EoF or error if (read <= 0) // EoF or error
{ {
@@ -47,7 +47,7 @@ Result update_audio(audio_s *audio)
ov_open(fmemopen(audio->filebuf, audio->filesize, "rb"), &audio->vf, NULL, 0); // Reopen file. Don't need to reinit channel stuff since it's all the same as before ov_open(fmemopen(audio->filebuf, audio->filesize, "rb"), &audio->vf, NULL, 0); // Reopen file. Don't need to reinit channel stuff since it's all the same as before
} else // Error :( } else // Error :(
{ {
DEBUG("<update_audio> Vorbis play error: %ld\n", read); DEBUG("Vorbis play error: %d\n", read);
ndspChnReset(0); ndspChnReset(0);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA); return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
} }
@@ -71,8 +71,8 @@ void thread_audio(void* data) {
} }
free(audio->filebuf); free(audio->filebuf);
ov_clear(&audio->vf); ov_clear(&audio->vf);
linearFree((void*)audio->wave_buf[0].data_vaddr); linearFree(audio->wave_buf[0].data_vaddr);
linearFree((void*)audio->wave_buf[1].data_vaddr); linearFree(audio->wave_buf[1].data_vaddr);
while (audio->wave_buf[0].status != NDSP_WBUF_DONE || audio->wave_buf[1].status != NDSP_WBUF_DONE) svcSleepThread(1e7); while (audio->wave_buf[0].status != NDSP_WBUF_DONE || audio->wave_buf[1].status != NDSP_WBUF_DONE) svcSleepThread(1e7);
svcSignalEvent(audio->finished); svcSignalEvent(audio->finished);
svcSleepThread(1e8); svcSleepThread(1e8);

14
source/pp2d/README.md Normal file
View File

@@ -0,0 +1,14 @@
# [pp2d](https://discord.gg/zqXWgsH)
Plug & Play 2D (unofficial) wrapper for Citro3D.
## License
pp2d is licensed under the MIT License.
## Changes
* depth parameter (Ryuzaki-MrL)
* standalone pp2d_free_texture (LiquidFenrir)
* 3d support (Robz8)
* load png from memory (ErmanSayin)

6167
source/pp2d/pp2d/lodepng.c Normal file

File diff suppressed because it is too large Load Diff

1713
source/pp2d/pp2d/lodepng.h Normal file

File diff suppressed because it is too large Load Diff

789
source/pp2d/pp2d/pp2d.c Normal file
View File

@@ -0,0 +1,789 @@
/* MIT License
*
* Copyright (c) 2017 Bernardo Giordano
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* https://discord.gg/zqXWgsH
*/
/**
* Plug & Play 2D
* @file pp2d.c
* @author Bernardo Giordano
* @date 26 October 2017
* @brief pp2d implementation
*/
#include "pp2d.h"
static DVLB_s* vshader_dvlb;
static shaderProgram_s program;
static int uLoc_projection;
static C3D_Mtx projectionTopLeft;
static C3D_Mtx projectionTopRight;
static C3D_Mtx projectionBot;
static C3D_Tex* glyphSheets;
static textVertex_s* textVtxArray;
static int textVtxArrayPos;
static C3D_RenderTarget* topLeft;
static C3D_RenderTarget* topRight;
static C3D_RenderTarget* bot;
static struct {
GPU_TEXTURE_FILTER_PARAM magFilter;
GPU_TEXTURE_FILTER_PARAM minFilter;
} textureFilters;
static struct {
size_t id;
int x;
int y;
int xbegin;
int ybegin;
int width;
int height;
u32 color;
flipType fliptype;
float scaleX;
float scaleY;
float angle;
float depth;
bool initialized;
} textureData;
static struct {
C3D_Tex tex;
u32 width;
u32 height;
bool allocated;
} textures[MAX_TEXTURES];
static void pp2d_add_text_vertex(float vx, float vy, float vz, float tx, float ty);
static bool pp2d_fast_draw_vbo(int x, int y, int height, int width, float left, float right, float top, float bottom, float depth);
static u32 pp2d_get_next_pow2(u32 n);
static void pp2d_get_text_size_internal(float* width, float* height, float scaleX, float scaleY, int wrapX, const char* text);
static void pp2d_set_text_color(u32 color);
static void pp2d_add_text_vertex(float vx, float vy, float vz, float tx, float ty)
{
textVertex_s* vtx = &textVtxArray[textVtxArrayPos++];
vtx->position[0] = vx;
vtx->position[1] = vy;
vtx->position[2] = vz;
vtx->texcoord[0] = tx;
vtx->texcoord[1] = ty;
}
void pp2d_begin_draw(gfxScreen_t target, gfx3dSide_t side)
{
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
textVtxArrayPos = 0;
pp2d_draw_on(target, side);
}
void pp2d_draw_on(gfxScreen_t target, gfx3dSide_t side)
{
if(target == GFX_TOP) {
C3D_FrameDrawOn(side == GFX_LEFT ? topLeft : topRight);
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, side == GFX_LEFT ? &projectionTopLeft : &projectionTopRight);
} else {
C3D_FrameDrawOn(bot);
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projectionBot);
}
}
void pp2d_draw_rectangle(int x, int y, int width, int height, u32 color)
{
C3D_TexEnv* env = C3D_GetTexEnv(0);
C3D_TexEnvSrc(env, C3D_Both, GPU_CONSTANT, GPU_CONSTANT, 0);
C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
C3D_TexEnvColor(env, color);
if (pp2d_fast_draw_vbo(x, y, height, width, 0, 0, 0, 0, DEFAULT_DEPTH))
{
C3D_DrawArrays(GPU_TRIANGLE_STRIP, textVtxArrayPos - 4, 4);
}
}
void pp2d_draw_text(float x, float y, float scaleX, float scaleY, u32 color, const char* text)
{
pp2d_draw_text_wrap(x, y, scaleX, scaleY, color, -1, text);
}
void pp2d_draw_text_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const char* text)
{
float width = pp2d_get_text_width(text, scaleX, scaleY);
float x = ((target == GFX_TOP ? TOP_WIDTH : BOTTOM_WIDTH) - width) / 2;
pp2d_draw_text(x, y, scaleX, scaleY, color, text);
}
void pp2d_draw_textf(float x, float y, float scaleX, float scaleY, u32 color, const char* text, ...)
{
char buffer[256];
va_list args;
va_start(args, text);
vsnprintf(buffer, 256, text, args);
pp2d_draw_text(x, y, scaleX, scaleY, color, buffer);
va_end(args);
}
void pp2d_draw_text_wrap(float x, float y, float scaleX, float scaleY, u32 color, float wrapX, const char* text)
{
if (text == NULL)
return;
pp2d_set_text_color(color);
ssize_t units;
uint32_t code;
const uint8_t* p = (const uint8_t*)text;
float firstX = x;
int lastSheet = -1;
do
{
if (!*p) break;
units = decode_utf8(&code, p);
if (units == -1)
break;
p += units;
if (code == '\n' || (wrapX != -1 && x + scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth >= firstX + wrapX))
{
x = firstX;
y += scaleY*fontGetInfo()->lineFeed;
p -= code == '\n' ? 0 : 1;
}
else if (code > 0)
{
int glyphIdx = fontGlyphIndexFromCodePoint(code);
fontGlyphPos_s data;
fontCalcGlyphPos(&data, glyphIdx, GLYPH_POS_CALC_VTXCOORD, scaleX, scaleY);
if (data.sheetIndex != lastSheet)
{
lastSheet = data.sheetIndex;
C3D_TexBind(0, &glyphSheets[lastSheet]);
}
if ((textVtxArrayPos+4) >= TEXT_VTX_ARRAY_COUNT)
break;
pp2d_add_text_vertex(x+data.vtxcoord.left, y+data.vtxcoord.bottom, DEFAULT_DEPTH, data.texcoord.left, data.texcoord.bottom);
pp2d_add_text_vertex(x+data.vtxcoord.right, y+data.vtxcoord.bottom, DEFAULT_DEPTH, data.texcoord.right, data.texcoord.bottom);
pp2d_add_text_vertex(x+data.vtxcoord.left, y+data.vtxcoord.top, DEFAULT_DEPTH, data.texcoord.left, data.texcoord.top);
pp2d_add_text_vertex(x+data.vtxcoord.right, y+data.vtxcoord.top, DEFAULT_DEPTH, data.texcoord.right, data.texcoord.top);
C3D_DrawArrays(GPU_TRIANGLE_STRIP, textVtxArrayPos - 4, 4);
x += data.xAdvance;
}
} while (code > 0);
}
void pp2d_draw_texture(size_t id, int x, int y)
{
pp2d_texture_select(id, x, y);
pp2d_texture_draw();
}
void pp2d_draw_texture_blend(size_t id, int x, int y, u32 color)
{
pp2d_texture_select(id, x, y);
pp2d_texture_blend(color);
pp2d_texture_draw();
}
void pp2d_draw_texture_flip(size_t id, int x, int y, flipType fliptype)
{
pp2d_texture_select(id, x, y);
pp2d_texture_flip(fliptype);
pp2d_texture_draw();
}
void pp2d_draw_texture_rotate(size_t id, int x, int y, float angle)
{
pp2d_texture_select(id, x, y);
pp2d_texture_rotate(angle);
pp2d_texture_draw();
}
void pp2d_draw_texture_scale(size_t id, int x, int y, float scaleX, float scaleY)
{
pp2d_texture_select(id, x, y);
pp2d_texture_scale(scaleX, scaleY);
pp2d_texture_draw();
}
void pp2d_draw_texture_part(size_t id, int x, int y, int xbegin, int ybegin, int width, int height)
{
pp2d_texture_select_part(id, x, y, xbegin, ybegin, width, height);
pp2d_texture_draw();
}
void pp2d_draw_wtext(float x, float y, float scaleX, float scaleY, u32 color, const wchar_t* text)
{
pp2d_draw_wtext_wrap(x, y, scaleX, scaleY, color, -1, text);
}
void pp2d_draw_wtext_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const wchar_t* text)
{
float width = pp2d_get_wtext_width(text, scaleX, scaleY);
float x = ((target == GFX_TOP ? TOP_WIDTH : BOTTOM_WIDTH) - width) / 2;
pp2d_draw_wtext(x, y, scaleX, scaleY, color, text);
}
void pp2d_draw_wtext_wrap(float x, float y, float scaleX, float scaleY, u32 color, float wrapX, const wchar_t* text)
{
if (text == NULL)
return;
u32 size = wcslen(text) * sizeof(wchar_t);
char buf[size];
memset(buf, 0, size);
utf32_to_utf8((uint8_t*)buf, (uint32_t*)text, size);
buf[size - 1] = '\0';
pp2d_draw_text_wrap(x, y, scaleX, scaleY, color, wrapX, buf);
}
void pp2d_draw_wtextf(float x, float y, float scaleX, float scaleY, u32 color, const wchar_t* text, ...)
{
wchar_t buffer[256];
va_list args;
va_start(args, text);
vswprintf(buffer, 256, text, args);
pp2d_draw_wtext(x, y, scaleX, scaleY, color, buffer);
va_end(args);
}
void pp2d_end_draw(void)
{
C3D_FrameEnd(0);
}
void pp2d_exit(void)
{
for (size_t id = 0; id < MAX_TEXTURES; id++)
{
pp2d_free_texture(id);
}
linearFree(textVtxArray);
free(glyphSheets);
shaderProgramFree(&program);
DVLB_Free(vshader_dvlb);
C3D_Fini();
gfxExit();
}
static bool pp2d_fast_draw_vbo(int x, int y, int height, int width, float left, float right, float top, float bottom, float depth)
{
if ((textVtxArrayPos+4) >= TEXT_VTX_ARRAY_COUNT)
return false;
pp2d_add_text_vertex( x, y + height, depth, left, bottom);
pp2d_add_text_vertex(x + width, y + height, depth, right, bottom);
pp2d_add_text_vertex( x, y, depth, left, top);
pp2d_add_text_vertex(x + width, y, depth, right, top);
return true;
}
void pp2d_free_texture(size_t id)
{
if (id >= MAX_TEXTURES)
return;
if (!textures[id].allocated)
return;
C3D_TexDelete(&textures[id].tex);
textures[id].width = 0;
textures[id].height = 0;
textures[id].allocated = false;
}
Result pp2d_init(void)
{
Result res = 0;
gfxInitDefault();
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
topLeft = C3D_RenderTargetCreate(SCREEN_HEIGHT, TOP_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
C3D_RenderTargetSetClear(topLeft, C3D_CLEAR_ALL, BACKGROUND_COLOR, 0);
C3D_RenderTargetSetOutput(topLeft, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS);
topRight = C3D_RenderTargetCreate(SCREEN_HEIGHT, TOP_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
C3D_RenderTargetSetClear(topRight, C3D_CLEAR_ALL, BACKGROUND_COLOR, 0);
C3D_RenderTargetSetOutput(topRight, GFX_TOP, GFX_RIGHT, DISPLAY_TRANSFER_FLAGS);
bot = C3D_RenderTargetCreate(SCREEN_HEIGHT, BOTTOM_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
C3D_RenderTargetSetClear(bot, C3D_CLEAR_ALL, BACKGROUND_COLOR, 0);
C3D_RenderTargetSetOutput(bot, GFX_BOTTOM, GFX_LEFT, DISPLAY_TRANSFER_FLAGS);
res = fontEnsureMapped();
if (R_FAILED(res))
return res;
pp2d_set_texture_filter(GPU_NEAREST, GPU_NEAREST);
#ifdef BUILDTOOLS
vshader_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_len);
#else
vshader_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_size);
#endif
shaderProgramInit(&program);
shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]);
C3D_BindProgram(&program);
uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection");
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
AttrInfo_Init(attrInfo);
AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3);
AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2);
Mtx_OrthoTilt(&projectionTopLeft, 0, TOP_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, true);
Mtx_OrthoTilt(&projectionTopRight, 0, TOP_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, true);
Mtx_OrthoTilt(&projectionBot, 0, BOTTOM_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, true);
C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL);
int i;
TGLP_s* glyphInfo = fontGetGlyphInfo();
glyphSheets = malloc(sizeof(C3D_Tex)*glyphInfo->nSheets);
for (i = 0; i < glyphInfo->nSheets; i ++)
{
C3D_Tex* tex = &glyphSheets[i];
tex->data = fontGetGlyphSheetTex(i);
tex->fmt = glyphInfo->sheetFmt;
tex->size = glyphInfo->sheetSize;
tex->width = glyphInfo->sheetWidth;
tex->height = glyphInfo->sheetHeight;
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)
| GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE);
tex->border = 0;
tex->lodParam = 0;
}
textVtxArray = (textVertex_s*)linearAlloc(sizeof(textVertex_s)*TEXT_VTX_ARRAY_COUNT);
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
BufInfo_Init(bufInfo);
BufInfo_Add(bufInfo, textVtxArray, sizeof(textVertex_s), 2, 0x10);
return 0;
}
static u32 pp2d_get_next_pow2(u32 v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v >= 64 ? v : 64;
}
float pp2d_get_text_height(const char* text, float scaleX, float scaleY)
{
float height;
pp2d_get_text_size_internal(NULL, &height, scaleX, scaleY, -1, text);
return height;
}
float pp2d_get_text_height_wrap(const char* text, float scaleX, float scaleY, int wrapX)
{
float height;
pp2d_get_text_size_internal(NULL, &height, scaleX, scaleY, wrapX, text);
return height;
}
void pp2d_get_text_size(float* width, float* height, float scaleX, float scaleY, const char* text)
{
pp2d_get_text_size_internal(width, height, scaleX, scaleY, -1, text);
}
static void pp2d_get_text_size_internal(float* width, float* height, float scaleX, float scaleY, int wrapX, const char* text)
{
float w = 0.0f;
float h = 0.0f;
ssize_t units;
uint32_t code;
float x = 0;
float firstX = x;
const uint8_t* p = (const uint8_t*)text;
do
{
if (!*p) break;
units = decode_utf8(&code, p);
if (units == -1)
break;
p += units;
if (code == '\n' || (wrapX != -1 && x + scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth >= firstX + wrapX))
{
x = firstX;
h += scaleY*fontGetInfo()->lineFeed;
p -= code == '\n' ? 0 : 1;
}
else if (code > 0)
{
float len = (scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth);
w += len;
x += len;
}
} while (code > 0);
if (width)
{
*width = w;
}
if (height)
{
h += scaleY*fontGetInfo()->lineFeed;
*height = h;
}
}
float pp2d_get_text_width(const char* text, float scaleX, float scaleY)
{
float width;
pp2d_get_text_size_internal(&width, NULL, scaleX, scaleY, -1, text);
return width;
}
float pp2d_get_wtext_height(const wchar_t* text, float scaleX, float scaleY)
{
u32 size = wcslen(text) * sizeof(wchar_t);
char buf[size];
memset(buf, 0, size);
utf32_to_utf8((uint8_t*)buf, (uint32_t*)text, size);
buf[size - 1] = '\0';
float height;
pp2d_get_text_size_internal(NULL, &height, scaleX, scaleY, -1, buf);
return height;
}
float pp2d_get_wtext_width(const wchar_t* text, float scaleX, float scaleY)
{
u32 size = wcslen(text) * sizeof(wchar_t);
char buf[size];
memset(buf, 0, size);
utf32_to_utf8((uint8_t*)buf, (uint32_t*)text, size);
buf[size - 1] = '\0';
float width;
pp2d_get_text_size_internal(&width, NULL, scaleX, scaleY, -1, buf);
return width;
}
void pp2d_load_texture_memory(size_t id, void* buf, u32 width, u32 height)
{
u32 w_pow2 = pp2d_get_next_pow2(width);
u32 h_pow2 = pp2d_get_next_pow2(height);
C3D_TexInit(&textures[id].tex, (u16)w_pow2, (u16)h_pow2, GPU_RGBA8);
C3D_TexSetFilter(&textures[id].tex, textureFilters.magFilter, textureFilters.minFilter);
textures[id].allocated = true;
textures[id].width = width;
textures[id].height = height;
memset(textures[id].tex.data, 0, textures[id].tex.size);
for (u32 i = 0; i < width; i++)
{
for (u32 j = 0; j < height; j++)
{
u32 dst = ((((j >> 3) * (w_pow2 >> 3) + (i >> 3)) << 6) + ((i & 1) | ((j & 1) << 1) | ((i & 2) << 1) | ((j & 2) << 2) | ((i & 4) << 2) | ((j & 4) << 3))) * 4;
u32 src = (j * width + i) * 4;
memcpy(textures[id].tex.data + dst, buf + src, 4);
}
}
C3D_TexFlush(&textures[id].tex);
}
void pp2d_load_texture_png(size_t id, const char* path)
{
if (id >= MAX_TEXTURES)
return;
u8* image;
unsigned width;
unsigned height;
lodepng_decode32_file(&image, &width, &height, path);
for (u32 i = 0; i < width; i++)
{
for (u32 j = 0; j < height; j++)
{
u32 p = (i + j*width) * 4;
u8 r = *(u8*)(image + p);
u8 g = *(u8*)(image + p + 1);
u8 b = *(u8*)(image + p + 2);
u8 a = *(u8*)(image + p + 3);
*(image + p) = a;
*(image + p + 1) = b;
*(image + p + 2) = g;
*(image + p + 3) = r;
}
}
pp2d_load_texture_memory(id, image, width, height);
free(image);
}
void pp2d_load_texture_png_memory(size_t id, void* buf, size_t buf_size)
{
if (id >= MAX_TEXTURES)
return;
u8* image;
unsigned width;
unsigned height;
lodepng_decode32(&image, &width, &height, buf, buf_size);
for (u32 i = 0; i < width; i++)
{
for (u32 j = 0; j < height; j++)
{
u32 p = (i + j*width) * 4;
u8 r = *(u8*)(image + p);
u8 g = *(u8*)(image + p + 1);
u8 b = *(u8*)(image + p + 2);
u8 a = *(u8*)(image + p + 3);
*(image + p) = a;
*(image + p + 1) = b;
*(image + p + 2) = g;
*(image + p + 3) = r;
}
}
pp2d_load_texture_memory(id, image, width, height);
free(image);
}
void pp2d_set_3D(int enable)
{
gfxSet3D(enable);
}
void pp2d_set_screen_color(gfxScreen_t target, u32 color)
{
if (target == GFX_TOP)
{
C3D_RenderTargetSetClear(topLeft, C3D_CLEAR_ALL, color, 0);
C3D_RenderTargetSetClear(topRight, C3D_CLEAR_ALL, color, 0);
}
else
{
C3D_RenderTargetSetClear(bot, C3D_CLEAR_ALL, color, 0);
}
}
void pp2d_set_texture_filter(GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter)
{
textureFilters.magFilter = magFilter;
textureFilters.minFilter = minFilter;
}
static void pp2d_set_text_color(u32 color)
{
C3D_TexEnv* env = C3D_GetTexEnv(0);
C3D_TexEnvSrc(env, C3D_RGB, GPU_CONSTANT, 0, 0);
C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_CONSTANT, 0);
C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
C3D_TexEnvColor(env, color);
}
void pp2d_texture_select(size_t id, int x, int y)
{
if (id >= MAX_TEXTURES)
{
textureData.initialized = false;
return;
}
textureData.id = id;
textureData.x = x;
textureData.y = y;
textureData.xbegin = 0;
textureData.ybegin = 0;
textureData.width = textures[id].width;
textureData.height = textures[id].height;
textureData.color = PP2D_NEUTRAL;
textureData.fliptype = NONE;
textureData.scaleX = 1;
textureData.scaleY = 1;
textureData.angle = 0;
textureData.depth = DEFAULT_DEPTH;
textureData.initialized = true;
}
void pp2d_texture_select_part(size_t id, int x, int y, int xbegin, int ybegin, int width, int height)
{
if (id >= MAX_TEXTURES)
{
textureData.initialized = false;
return;
}
textureData.id = id;
textureData.x = x;
textureData.y = y;
textureData.xbegin = xbegin;
textureData.ybegin = ybegin;
textureData.width = width;
textureData.height = height;
textureData.color = PP2D_NEUTRAL;
textureData.fliptype = NONE;
textureData.scaleX = 1;
textureData.scaleY = 1;
textureData.angle = 0;
textureData.depth = DEFAULT_DEPTH;
textureData.initialized = true;
}
void pp2d_texture_blend(u32 color)
{
textureData.color = color;
}
void pp2d_texture_scale(float scaleX, float scaleY)
{
textureData.scaleX = scaleX;
textureData.scaleY = scaleY;
}
void pp2d_texture_flip(flipType fliptype)
{
textureData.fliptype = fliptype;
}
void pp2d_texture_rotate(float angle)
{
textureData.angle = angle;
}
void pp2d_texture_depth(float depth)
{
textureData.depth = depth;
}
void pp2d_texture_draw(void)
{
if (!textureData.initialized)
return;
if ((textVtxArrayPos+4) >= TEXT_VTX_ARRAY_COUNT)
return;
size_t id = textureData.id;
float left = (float)textureData.xbegin / (float)textures[id].tex.width;
float right = (float)(textureData.xbegin + textureData.width) / (float)textures[id].tex.width;
float top = (float)(textures[id].tex.height - textureData.ybegin) / (float)textures[id].tex.height;
float bottom = (float)(textures[id].tex.height - textureData.ybegin - textureData.height) / (float)textures[id].tex.height;
// scaling
textureData.height *= textureData.scaleY;
textureData.width *= textureData.scaleX;
float vert[4][2] = {
{ textureData.x, textureData.height + textureData.y},
{textureData.width + textureData.x, textureData.height + textureData.y},
{ textureData.x, textureData.y},
{textureData.width + textureData.x, textureData.y},
};
// flipping
if (textureData.fliptype == BOTH || textureData.fliptype == HORIZONTAL)
{
float tmp = left;
left = right;
right = tmp;
}
if (textureData.fliptype == BOTH || textureData.fliptype == VERTICAL)
{
float tmp = top;
top = bottom;
bottom = tmp;
}
// rotating
textureData.angle = fmod(textureData.angle, 360);
if (textureData.angle != 0)
{
const float rad = textureData.angle/(180/M_PI);
const float c = cosf(rad);
const float s = sinf(rad);
const float xcenter = textureData.x + textureData.width/2.0f;
const float ycenter = textureData.y + textureData.height/2.0f;
for (int i = 0; i < 4; i++)
{
float oldx = vert[i][0];
float oldy = vert[i][1];
float newx = c * (oldx - xcenter) - s * (oldy - ycenter) + xcenter;
float newy = s * (oldx - xcenter) + c * (oldy - ycenter) + ycenter;
vert[i][0] = newx;
vert[i][1] = newy;
}
}
// blending
C3D_TexBind(0, &textures[id].tex);
C3D_TexEnv* env = C3D_GetTexEnv(0);
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_CONSTANT, 0);
C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
C3D_TexEnvColor(env, textureData.color);
// rendering
pp2d_add_text_vertex(vert[0][0], vert[0][1], textureData.depth, left, bottom);
pp2d_add_text_vertex(vert[1][0], vert[1][1], textureData.depth, right, bottom);
pp2d_add_text_vertex(vert[2][0], vert[2][1], textureData.depth, left, top);
pp2d_add_text_vertex(vert[3][0], vert[3][1], textureData.depth, right, top);
C3D_DrawArrays(GPU_TRIANGLE_STRIP, textVtxArrayPos - 4, 4);
}

472
source/pp2d/pp2d/pp2d.h Normal file
View File

@@ -0,0 +1,472 @@
/* MIT License
*
* Copyright (c) 2017 Bernardo Giordano
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* https://discord.gg/zqXWgsH
*/
/**
* Plug & Play 2D
* @file pp2d.h
* @author Bernardo Giordano
* @date 26 October 2017
* @brief pp2d header
*/
#ifndef PP2D_H
#define PP2D_H
#include "lodepng.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <3ds.h>
#include <citro3d.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "vshader_shbin.h"
#define TOP_WIDTH 400
#define BOTTOM_WIDTH 320
#define SCREEN_HEIGHT 240
/**
* @brief Used to transfer the final rendered display to the framebuffer
*/
#define DISPLAY_TRANSFER_FLAGS \
(GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \
GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
/**
* @brief Creates a 8 byte RGBA color
* @param r red component of the color
* @param g green component of the color
* @param b blue component of the color
* @param a alpha component of the color
*/
#define RGBA8(r, g, b, a) ((((r)&0xFF)<<0) | (((g)&0xFF)<<8) | (((b)&0xFF)<<16) | (((a)&0xFF)<<24))
/**
* @brief Creates a 8 byte ABGR color
* @param a alpha component of the color
* @param b blue component of the color
* @param g green component of the color
* @param r red component of the color
*/
#define ABGR8(a, b, g, r) ((((a)&0xFF)<<0) | (((b)&0xFF)<<8) | (((g)&0xFF)<<16) | (((r)&0xFF)<<24))
/**
* @brief Converts a RGB565 color to RGBA8 color (adds maximum alpha)
* @param rgb 565 to be converted
*/
#define RGB565_TO_RGBA8(rgb) \
(RGBA8(((rgb>>11)&0x1F)*0xFF/0x1F, ((rgb>>5)&0x3F)*0xFF/0x3F, (rgb&0x1F)*0xFF/0x1F, 255))
/**
* @brief Converts a RGB565 color to ABGR8 color (adds maximum alpha)
* @param rgb 565 to be converted
*/
#define RGB565_TO_ABGR8(rgb) \
(RGBA8(255, (rgb&0x1F)*0xFF/0x1F, ((rgb>>5)&0x3F)*0xFF/0x3F, ((rgb>>11)&0x1F)*0xFF/0x1F))
#define BACKGROUND_COLOR ABGR8(255, 0, 0, 0)
#define PP2D_NEUTRAL RGBA8(255, 255, 255, 255)
#define DEFAULT_DEPTH 0.5f
#define MAX_TEXTURES 1024
#define TEXT_VTX_ARRAY_COUNT (4*1024)
typedef enum {
NONE,
HORIZONTAL,
VERTICAL,
BOTH
} flipType;
typedef struct {
float position[3];
float texcoord[2];
} textVertex_s;
/**
* @brief Starts a new frame on the specified screen
* @param target GFX_TOP or GFX_BOTTOM
*/
void pp2d_begin_draw(gfxScreen_t target, gfx3dSide_t side);
/**
* @brief Changes target screen to the specified target
* @param target GFX_TOP or GFX_BOTTOM
*/
void pp2d_draw_on(gfxScreen_t target, gfx3dSide_t side);
/**
* @brief Draw a rectangle
* @param x of the top left corner
* @param y of the top left corner
* @param width on the rectangle
* @param height of the rectangle
* @param color RGBA8 to fill the rectangle
*/
void pp2d_draw_rectangle(int x, int y, int width, int height, u32 color);
/**
* @brief Prints a char pointer
* @param x position to start drawing
* @param y position to start drawing
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param color RGBA8 the text will be drawn
* @param text to be printed on the screen
*/
void pp2d_draw_text(float x, float y, float scaleX, float scaleY, u32 color, const char* text);
/**
* @brief Prints a char pointer in the middle of the target screen
* @param target screen to draw the text to
* @param y position to start drawing
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param color RGBA8 the text will be drawn
* @param text to be printed on the screen
*/
void pp2d_draw_text_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const char* text);
/**
* @brief Prints a char pointer in the middle of the target screen
* @param x position to start drawing
* @param y position to start drawing
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param color RGBA8 the text will be drawn
* @param wrapX wrap width
* @param text to be printed on the screen
*/
void pp2d_draw_text_wrap(float x, float y, float scaleX, float scaleY, u32 color, float wrapX, const char* text);
/**
* @brief Prints a char pointer with arguments
* @param x position to start drawing
* @param y position to start drawing
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param color RGBA8 the text will be drawn
* @param text to be printed on the screen
* @param ... arguments
*/
void pp2d_draw_textf(float x, float y, float scaleX, float scaleY, u32 color, const char* text, ...);
/**
* @brief Prints a texture
* @param id of the texture
* @param x position on the screen to draw the texture
* @param y position on the screen to draw the texture
*/
void pp2d_draw_texture(size_t id, int x, int y);
/**
* @brief Prints a texture with color modulation
* @param id of the texture
* @param x position on the screen to draw the texture
* @param y position on the screen to draw the texture
* @param color RGBA8 to modulate the texture with
*/
void pp2d_draw_texture_blend(size_t id, int x, int y, u32 color);
/**
* @brief Prints a flipped texture
* @param id of the texture
* @param x position on the screen to draw the texture
* @param y position on the screen to draw the texture
* @param fliptype HORIZONTAL, VERTICAL or BOTH
*/
void pp2d_draw_texture_flip(size_t id, int x, int y, flipType fliptype);
/**
* @brief Prints a rotated texture
* @param id of the texture
* @param x position on the screen to draw the texture
* @param y position on the screen to draw the texture
* @param angle in degrees to rotate the texture
*/
void pp2d_draw_texture_rotate(size_t id, int x, int y, float angle);
/**
* @brief Prints a texture with a scale factor
* @param id of the texture
* @param x position on the screen to draw the texture
* @param y position on the screen to draw the texture
* @param scaleX width scale factor
* @param scaleY height scale factor
*/
void pp2d_draw_texture_scale(size_t id, int x, int y, float scaleX, float scaleY);
/**
* @brief Prints a portion of a texture
* @param id of the texture
* @param x position on the screen to draw the texture
* @param y position on the screen to draw the texture
* @param xbegin position to start drawing
* @param ybegin position to start drawing
* @param width to draw from the xbegin position
* @param height to draw from the ybegin position
*/
void pp2d_draw_texture_part(size_t id, int x, int y, int xbegin, int ybegin, int width, int height);
/**
* @brief Prints a wchar_t pointer
* @param x position to start drawing
* @param y position to start drawing
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param color RGBA8 the text will be drawn
* @param text to be printed on the screen
*/
void pp2d_draw_wtext(float x, float y, float scaleX, float scaleY, u32 color, const wchar_t* text);
/**
* @brief Prints a wchar_t pointer in the middle of the target screen
* @param target screen to draw the text to
* @param y position to start drawing
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param color RGBA8 the text will be drawn
* @param text to be printed on the screen
*/
void pp2d_draw_wtext_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const wchar_t* text);
/**
* @brief Prints a wchar_t pointer
* @param x position to start drawing
* @param y position to start drawing
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param color RGBA8 the text will be drawn
* @param wrapX wrap width
* @param text to be printed on the screen
*/
void pp2d_draw_wtext_wrap(float x, float y, float scaleX, float scaleY, u32 color, float wrapX, const wchar_t* text);
/**
* @brief Prints a wchar_t pointer with arguments
* @param x position to start drawing
* @param y position to start drawing
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param color RGBA8 the text will be drawn
* @param text to be printed on the screen
* @param ... arguments
*/
void pp2d_draw_wtextf(float x, float y, float scaleX, float scaleY, u32 color, const wchar_t* text, ...);
/**
* @brief Ends a frame
*/
void pp2d_end_draw(void);
/**
* @brief Frees the pp2d environment
*/
void pp2d_exit(void);
/**
* @brief Inits the pp2d environment
* @return 0 if everything went correctly, otherwise returns Result code
* @note This will trigger gfxInitDefault by default
*/
Result pp2d_init(void);
/**
* @brief Calculates a char pointer height
* @param text char pointer to calculate the height of
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @return height the text will have if rendered in the supplied conditions
*/
float pp2d_get_text_height(const char* text, float scaleX, float scaleY);
/**
* @brief Calculates a char pointer height
* @param text char pointer to calculate the height of
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param wrapX wrap width
* @return height the text will have if rendered in the supplied conditions
*/
float pp2d_get_text_height_wrap(const char* text, float scaleX, float scaleY, int wrapX);
/**
* @brief Calculates width and height for a char pointer
* @param width pointer to the width to return
* @param height pointer to the height to return
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @param text to calculate dimensions of
*/
void pp2d_get_text_size(float* width, float* height, float scaleX, float scaleY, const char* text);
/**
* @brief Calculates a char pointer width
* @param text char pointer to calculate the width of
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @return width the text will have if rendered in the supplied conditions
*/
float pp2d_get_text_width(const char* text, float scaleX, float scaleY);
/**
* @brief Calculates a wchar_t pointer height
* @param text wchar_t pointer to calculate the height of
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @return height the text will have if rendered in the supplied conditions
*/
float pp2d_get_wtext_height(const wchar_t* text, float scaleX, float scaleY);
/**
* @brief Calculates a wchar_t pointer width
* @param text wchar_t pointer to calculate the width of
* @param scaleX multiplier for the text width
* @param scaleY multiplier for the text height
* @return width the text will have if rendered in the supplied conditions
*/
float pp2d_get_wtext_width(const wchar_t* text, float scaleX, float scaleY);
/**
* @brief Frees a texture
* @param id of the texture to free
*/
void pp2d_free_texture(size_t id);
/**
* @brief Loads a texture from a a buffer in memory
* @param id of the texture
* @param buf buffer where the texture is stored
* @param width of the texture
* @param height of the texture
*/
void pp2d_load_texture_memory(size_t id, void* buf, u32 width, u32 height);
/**
* @brief Loads a texture from a png file
* @param id of the texture
* @param path where the png file is located
*/
void pp2d_load_texture_png(size_t id, const char* path);
/**
* @brief Loads a texture from a buffer in memory
* @param id of the texture
* @param buf buffer where the png is stored
* @param buf_size size of buffer
*/
void pp2d_load_texture_png_memory(size_t id, void* buf, size_t buf_size);
/**
* @brief Enables 3D service
* @param enable integer
*/
void pp2d_set_3D(int enable);
/**
* @brief Sets a background color for the specified screen
* @param target GFX_TOP or GFX_BOTTOM
* @param color ABGR8 which will be the background one
*/
void pp2d_set_screen_color(gfxScreen_t target, u32 color);
/**
* @brief Sets filters to load texture with
* @param magFilter GPU_NEAREST or GPU_LINEAR
* @param minFilter GPU_NEAREST or GPU_LINEAR
*/
void pp2d_set_texture_filter(GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter);
/**
* @brief Inits a texture to be drawn
* @param id of the texture
* @param x to draw the texture at
* @param y to draw the texture at
*/
void pp2d_texture_select(size_t id, int x, int y);
/**
* @brief Inits a portion of a texture to be drawn
* @param id of the texture
* @param x position on the screen to draw the texture
* @param y position on the screen to draw the texture
* @param xbegin position to start drawing
* @param ybegin position to start drawing
* @param width to draw from the xbegin position
* @param height to draw from the ybegin position
*/
void pp2d_texture_select_part(size_t id, int x, int y, int xbegin, int ybegin, int width, int height);
/**
* @brief Modulates a texture with a color
* @param color to modulate the texture
*/
void pp2d_texture_blend(u32 color);
/**
* @brief Scales a texture
* @param scaleX width scale factor
* @param scaleY height scale factor
*/
void pp2d_texture_scale(float scaleX, float scaleY);
/**
* @brief Flips a texture
* @param fliptype HORIZONTAL, VERTICAL or BOTH
*/
void pp2d_texture_flip(flipType fliptype);
/**
* @brief Rotates a texture
* @param angle in degrees to rotate the texture
*/
void pp2d_texture_rotate(float angle);
/**
* @brief Sets the depth of a texture
* @param depth factor of the texture
*/
void pp2d_texture_depth(float depth);
/**
* @brief Renders a texture
*/
void pp2d_texture_draw(void);
#ifdef __cplusplus
}
#endif
#endif /*PP2D_H*/

View File

@@ -0,0 +1,39 @@
; Uniforms
.fvec projection[4]
; Constants
.constf myconst(0.0, 1.0, -1.0, 0.1)
.constf RGBA_TO_FLOAT4(0.00392156862, 0, 0, 0)
.alias zeros myconst.xxxx ; Vector full of zeros
.alias ones myconst.yyyy ; Vector full of ones
; Outputs
.out outpos position
.out outclr color
.out outtc0 texcoord0
; Inputs (defined as aliases for convenience)
.alias inpos v0
.alias intex v1
.bool test
.proc main
; Force the w component of inpos to be 1.0
mov r0.xyz, inpos
mov r0.w, ones
; outpos = projectionMatrix * inpos
dp4 outpos.x, projection[0], r0
dp4 outpos.y, projection[1], r0
dp4 outpos.z, projection[2], r0
dp4 outpos.w, projection[3], r0
;outtc0 = intexcoord
mov outtc0, intex
;outclr
mul outclr, RGBA_TO_FLOAT4.xxxx, intex
end
.end

View File

@@ -117,7 +117,7 @@ static const uint8_t gf256_log[256] = {
0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
}; };
static const struct galois_field gf256 = { const static struct galois_field gf256 = {
.p = 255, .p = 255,
.log = gf256_log, .log = gf256_log,
.exp = gf256_exp .exp = gf256_exp
@@ -873,7 +873,7 @@ static quirc_decode_error_t decode_payload(struct quirc_data *data,
done: done:
/* Add nul terminator to all payloads */ /* Add nul terminator to all payloads */
if ((unsigned int)data->payload_len >= sizeof(data->payload)) if (data->payload_len >= sizeof(data->payload))
data->payload_len--; data->payload_len--;
data->payload[data->payload_len] = 0; data->payload[data->payload_len] = 0;

View File

@@ -348,7 +348,6 @@ static void threshold(struct quirc *q)
static void area_count(void *user_data, int y, int left, int right) static void area_count(void *user_data, int y, int left, int right)
{ {
(void)y;
((struct quirc_region *)user_data)->count += right - left + 1; ((struct quirc_region *)user_data)->count += right - left + 1;
} }

View File

@@ -84,8 +84,7 @@ static const char *const error_table[] = {
const char *quirc_strerror(quirc_decode_error_t err) const char *quirc_strerror(quirc_decode_error_t err)
{ {
// note from Anemone3DS dev - L88 used to compare err >= 0, but err is always positive if (err >= 0 && err < sizeof(error_table) / sizeof(error_table[0]))
if (err < sizeof(error_table) / sizeof(error_table[0]))
return error_table[err]; return error_table[err];
return "Unknown error"; return "Unknown error";

View File

@@ -29,25 +29,26 @@
#include "fs.h" #include "fs.h"
#include "unicode.h" #include "unicode.h"
#include "music.h" #include "music.h"
#include "pp2d/pp2d/pp2d.h"
static Instructions_s browser_instructions[MODE_AMOUNT] = { static Instructions_s browser_instructions[MODE_AMOUNT] = {
{ {
.info_line = NULL, .info_line = NULL,
.instructions = { .instructions = {
{ {
"\uE000 Download theme", L"\uE000 Download theme",
"\uE001 Go back" L"\uE001 Go back"
}, },
{ {
"\uE002 Hold for more", L"\uE002 Hold for more",
"\uE003 Preview theme" L"\uE003 Preview theme"
}, },
{ {
"\uE004 Previous page", L"\uE004 Previous page",
"\uE005 Next page" L"\uE005 Next page"
}, },
{ {
"Exit", L"Exit",
NULL NULL
} }
} }
@@ -56,19 +57,19 @@ static Instructions_s browser_instructions[MODE_AMOUNT] = {
.info_line = NULL, .info_line = NULL,
.instructions = { .instructions = {
{ {
"\uE000 Download splash", L"\uE000 Download splash",
"\uE001 Go back" L"\uE001 Go back"
}, },
{ {
"\uE002 Hold for more", L"\uE002 Hold for more",
"\uE003 Preview splash" L"\uE003 Preview splash"
}, },
{ {
"\uE004 Previous page", L"\uE004 Previous page",
"\uE005 Next page" L"\uE005 Next page"
}, },
{ {
"Exit", L"Exit",
NULL NULL
} }
} }
@@ -76,49 +77,34 @@ static Instructions_s browser_instructions[MODE_AMOUNT] = {
}; };
static Instructions_s extra_instructions = { static Instructions_s extra_instructions = {
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff", .info_line = L"Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.info_line_color = COLOR_WHITE,
.instructions = { .instructions = {
{ {
"\uE079 Jump to page", L"\uE079 Jump to page",
"\uE07A Search tags" L"\uE07A Search tags"
}, },
{ {
"\uE07B Toggle splash/theme", L"\uE07B Toggle splash/theme",
"\uE07C Reload without cache" L"\uE07C Reload without cache"
}, },
{ {
NULL, NULL,
NULL NULL
}, },
{ {
"Exit", L"Exit",
NULL NULL
} }
} }
}; };
static void free_icons(Entry_List_s * list) static void load_remote_smdh(Entry_s * entry, size_t textureID, bool ignore_cache)
{
if(list != NULL)
{
if(list->icons != NULL)
{
for(int i = 0; i < list->entries_count; i++)
{
C3D_TexDelete(list->icons[i]->tex);
free(list->icons[i]->tex);
free(list->icons[i]);
}
free(list->icons);
}
}
}
static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
{ {
bool not_cached = true; bool not_cached = true;
char * smdh_buf = NULL; char * smdh_buf = NULL;
u32 smdh_size = load_data("/info.smdh", *entry, &smdh_buf); u32 smdh_size = load_data("/info.smdh", *entry, &smdh_buf);
Icon_s * smdh = (Icon_s *)smdh_buf;
not_cached = !smdh_size || ignore_cache; // if the size is 0, the file wasn't there not_cached = !smdh_size || ignore_cache; // if the size is 0, the file wasn't there
@@ -130,21 +116,40 @@ static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
asprintf(&api_url, THEMEPLAZA_SMDH_FORMAT, entry->tp_download_id); asprintf(&api_url, THEMEPLAZA_SMDH_FORMAT, entry->tp_download_id);
smdh_size = http_get(api_url, NULL, &smdh_buf, INSTALL_NONE); smdh_size = http_get(api_url, NULL, &smdh_buf, INSTALL_NONE);
free(api_url); free(api_url);
smdh = (Icon_s *)smdh_buf;
} }
if(!smdh_size) if(!smdh_size)
{ {
free(smdh_buf); free(smdh_buf);
smdh_buf = NULL; utf8_to_utf16(entry->name, (u8*)"No name", 0x80);
utf8_to_utf16(entry->desc, (u8*)"No description", 0x100);
utf8_to_utf16(entry->author, (u8*)"Unknown author", 0x80);
entry->placeholder_color = RGBA8(rand() % 255, rand() % 255, rand() % 255, 255);
return;
} }
Icon_s * smdh = (Icon_s *)smdh_buf; memcpy(entry->name, smdh->name, 0x40*sizeof(u16));
memcpy(entry->desc, smdh->desc, 0x80*sizeof(u16));
memcpy(entry->author, smdh->author, 0x40*sizeof(u16));
u16 fallback_name[0x81] = {0}; const u32 width = 48, height = 48;
utf8_to_utf16(fallback_name, (u8*)"No name", 0x80); u32 *image = malloc(width*height*sizeof(u32));
parse_smdh(smdh, entry, fallback_name); for(u32 x = 0; x < width; x++)
C2D_Image * image = loadTextureIcon(smdh); {
for(u32 y = 0; y < height; y++)
{
unsigned int dest_pixel = (x + y*width);
unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
image[dest_pixel] = RGB565_TO_ABGR8(smdh->big_icon[source_pixel]);
}
}
pp2d_free_texture(textureID);
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
free(image);
if(not_cached) if(not_cached)
{ {
@@ -156,17 +161,15 @@ static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
buf_to_file(smdh_size, fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_buf); buf_to_file(smdh_size, fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_buf);
} }
free(smdh_buf); free(smdh_buf);
return image;
} }
static void load_remote_entries(Entry_List_s * list, json_t *ids_array, bool ignore_cache, InstallType type) static void load_remote_entries(Entry_List_s * list, json_t *ids_array, bool ignore_cache, InstallType type)
{ {
free_icons(list);
list->entries_count = json_array_size(ids_array); list->entries_count = json_array_size(ids_array);
free(list->entries); free(list->entries);
list->entries = calloc(list->entries_count, sizeof(Entry_s)); list->entries = calloc(list->entries_count, sizeof(Entry_s));
list->icons = calloc(list->entries_count, sizeof(C2D_Image*)); free(list->icons_ids);
list->icons_ids = calloc(list->entries_count, sizeof(ssize_t));
list->entries_loaded = list->entries_count; list->entries_loaded = list->entries_count;
size_t i = 0; size_t i = 0;
@@ -174,15 +177,18 @@ static void load_remote_entries(Entry_List_s * list, json_t *ids_array, bool ign
json_array_foreach(ids_array, i, id) json_array_foreach(ids_array, i, id)
{ {
draw_loading_bar(i, list->entries_count, type); draw_loading_bar(i, list->entries_count, type);
Entry_s * current_entry = &list->entries[i]; size_t offset = i;
Entry_s * current_entry = &list->entries[offset];
current_entry->tp_download_id = json_integer_value(id); current_entry->tp_download_id = json_integer_value(id);
size_t textureID = list->texture_id_offset + i;
char * entry_path = NULL; char * entry_path = NULL;
asprintf(&entry_path, CACHE_PATH_FORMAT, current_entry->tp_download_id); asprintf(&entry_path, CACHE_PATH_FORMAT, current_entry->tp_download_id);
utf8_to_utf16(current_entry->path, (u8*)entry_path, 0x106); utf8_to_utf16(current_entry->path, (u8*)entry_path, 0x106);
free(entry_path); free(entry_path);
list->icons[i] = load_remote_smdh(current_entry, ignore_cache); load_remote_smdh(current_entry, textureID, ignore_cache);
list->icons_ids[offset] = textureID;
} }
} }
@@ -208,6 +214,7 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
if(json_len) if(json_len)
{ {
list->texture_id_offset = TEXTURE_REMOTE_ICONS;
list->tp_current_page = page; list->tp_current_page = page;
list->mode = mode; list->mode = mode;
list->entry_size = entry_size[mode]; list->entry_size = entry_size[mode];
@@ -242,7 +249,7 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
} }
static u16 previous_path_preview[0x106] = {0}; static u16 previous_path_preview[0x106] = {0};
static bool load_remote_preview(Entry_s * entry, C2D_Image* preview_image, int * preview_offset) static bool load_remote_preview(Entry_s * entry, int * preview_offset)
{ {
bool not_cached = true; bool not_cached = true;
@@ -273,9 +280,39 @@ static bool load_remote_preview(Entry_s * entry, C2D_Image* preview_image, int *
return false; return false;
} }
bool ret = load_preview_from_buffer(preview_png, preview_size, preview_image, preview_offset); bool ret = false;
u8 * image = NULL;
unsigned int width = 0, height = 0;
if(ret && not_cached) // only save the preview if it loaded correctly - isn't corrupted if((lodepng_decode32(&image, &width, &height, (u8*)preview_png, preview_size)) == 0) // no error
{
for(u32 i = 0; i < width; i++)
{
for(u32 j = 0; j < height; j++)
{
u32* pixel = (u32*)(image + (i + j*width) * 4);
*pixel = __builtin_bswap32(*pixel); //swap from RGBA to ABGR, needed for pp2d
}
}
// mark the new preview as loaded for optimisation
memcpy(&previous_path_preview, entry->path, 0x106*sizeof(u16));
// free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_REMOTE_PREVIEW);
pp2d_load_texture_memory(TEXTURE_REMOTE_PREVIEW, image, (u32)width, (u32)height);
*preview_offset = (width-400)/2;
ret = true;
}
else
{
throw_error("Corrupted/invalid preview.png", ERROR_LEVEL_WARNING);
}
free(image);
if(not_cached)
{ {
u16 path[0x107] = {0}; u16 path[0x107] = {0};
strucat(path, entry->path); strucat(path, entry->path);
@@ -283,7 +320,6 @@ static bool load_remote_preview(Entry_s * entry, C2D_Image* preview_image, int *
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, preview_size); remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, preview_size);
buf_to_file(preview_size, fsMakePath(PATH_UTF16, path), ArchiveSD, preview_png); buf_to_file(preview_size, fsMakePath(PATH_UTF16, path), ArchiveSD, preview_png);
} }
free(preview_png); free(preview_png);
return ret; return ret;
@@ -381,7 +417,7 @@ static void jump_menu(Entry_List_s * list)
sprintf(numbuf, "Which page do you want to jump to?"); sprintf(numbuf, "Which page do you want to jump to?");
swkbdSetHintText(&swkbd, numbuf); swkbdSetHintText(&swkbd, numbuf);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cance", false); swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Jump", true); swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Jump", true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars); swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->tp_page_count); swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->tp_page_count);
@@ -406,7 +442,7 @@ static void search_menu(Entry_List_s * list)
swkbdInit(&swkbd, SWKBD_TYPE_WESTERN, 2, max_chars); swkbdInit(&swkbd, SWKBD_TYPE_WESTERN, 2, max_chars);
swkbdSetHintText(&swkbd, "Which tags do you want to search for?"); swkbdSetHintText(&swkbd, "Which tags do you want to search for?");
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cance", false); swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Search", true); swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Search", true);
swkbdSetValidation(&swkbd, SWKBD_NOTBLANK, 0, max_chars); swkbdSetValidation(&swkbd, SWKBD_NOTBLANK, 0, max_chars);
@@ -462,7 +498,6 @@ bool themeplaza_browser(EntryMode mode)
Entry_List_s * current_list = &list; Entry_List_s * current_list = &list;
current_list->tp_search = strdup(""); current_list->tp_search = strdup("");
load_remote_list(current_list, 1, mode, false); load_remote_list(current_list, 1, mode, false);
C2D_Image preview = {0};
bool extra_mode = false; bool extra_mode = false;
@@ -472,9 +507,7 @@ bool themeplaza_browser(EntryMode mode)
break; break;
if(preview_mode) if(preview_mode)
{ draw_preview(TEXTURE_REMOTE_PREVIEW, preview_offset);
draw_preview(preview, preview_offset);
}
else else
{ {
Instructions_s instructions = browser_instructions[mode]; Instructions_s instructions = browser_instructions[mode];
@@ -482,7 +515,7 @@ bool themeplaza_browser(EntryMode mode)
instructions = extra_instructions; instructions = extra_instructions;
draw_grid_interface(current_list, instructions); draw_grid_interface(current_list, instructions);
} }
end_frame(); pp2d_end_draw();
hidScanInput(); hidScanInput();
u32 kDown = hidKeysDown(); u32 kDown = hidKeysDown();
@@ -544,19 +577,19 @@ bool themeplaza_browser(EntryMode mode)
toggle_preview: toggle_preview:
if(!preview_mode) if(!preview_mode)
{ {
preview_mode = load_remote_preview(current_entry, &preview, &preview_offset); preview_mode = load_remote_preview(current_entry, &preview_offset);
if(mode == MODE_THEMES && dspfirm) if(mode == MODE_THEMES)
{ {
load_remote_bgm(current_entry); load_remote_bgm(current_entry);
audio = calloc(1, sizeof(audio_s)); audio = calloc(1, sizeof(audio_s));
if(R_FAILED(load_audio(*current_entry, audio))) audio = NULL; load_audio(*current_entry, audio);
if(audio != NULL) play_audio(audio); play_audio(audio);
} }
} }
else else
{ {
preview_mode = false; preview_mode = false;
if(mode == MODE_THEMES && audio != NULL) if(mode == MODE_THEMES && audio)
{ {
audio->stop = true; audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX); svcWaitSynchronization(audio->finished, U64_MAX);
@@ -569,7 +602,7 @@ bool themeplaza_browser(EntryMode mode)
if(preview_mode) if(preview_mode)
{ {
preview_mode = false; preview_mode = false;
if(mode == MODE_THEMES && audio != NULL) if(mode == MODE_THEMES && audio)
{ {
audio->stop = true; audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX); svcWaitSynchronization(audio->finished, U64_MAX);
@@ -620,6 +653,28 @@ bool themeplaza_browser(EntryMode mode)
change_selected(current_list, 1); change_selected(current_list, 1);
} }
// Fast scroll using circle pad
else if(kHeld & KEY_CPAD_UP)
{
change_selected(current_list, -1);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_DOWN)
{
change_selected(current_list, 1);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_LEFT)
{
change_selected(current_list, -current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_RIGHT)
{
change_selected(current_list, current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
}
touch: touch:
if((kDown | kHeld) & KEY_TOUCH) if((kDown | kHeld) & KEY_TOUCH)
{ {
@@ -708,10 +763,8 @@ bool themeplaza_browser(EntryMode mode)
} }
} }
free_preview(preview);
free_icons(current_list);
free(current_list->entries); free(current_list->entries);
free(current_list->icons_ids);
free(current_list->tp_search); free(current_list->tp_search);
return downloaded; return downloaded;

View File

@@ -125,7 +125,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
FSUSER_OpenFile(&bgm_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, bgm_cache_path), FS_OPEN_WRITE, 0); FSUSER_OpenFile(&bgm_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, bgm_cache_path), FS_OPEN_WRITE, 0);
padded = calloc(BGM_MAX_SIZE, sizeof(char)); padded = calloc(BGM_MAX_SIZE, sizeof(char));
if(!current_theme->no_bgm_shuffle && music_size) if(!current_theme->no_bgm_shuffle)
{ {
memcpy(padded, music, music_size); memcpy(padded, music, music_size);
free(music); free(music);
@@ -191,7 +191,6 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE); return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
} }
remake_file(fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, BGM_MAX_SIZE);
res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music); res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
free(music); free(music);