Citro2d switch (#175)

* Switch from pp2d to citro2d

* Fix various bugs by updating libarchive

* Begin work on translation infrastructure

* Switch to libpng for various improvements

* Fixed race condition in icon scrolling

* Use spritesheets to load sprites marginally faster and use less memory

* Various Readme fixes

* Huge improvements to QR reader speed and screen tearing

* Don't try to preview themes/splashes when there are none

* Prevent playing audio when ndspInit() fails

* Don't wait for audio to load before displaying preview

* Fix bug when shuffle themes didn't have BGM

* Fix bug caused by files not being zeroed out when created
This commit is contained in:
Alex Taber
2018-07-22 21:22:23 +03:00
committed by GitHub
parent aa3a00cedc
commit c40696e982
48 changed files with 1219 additions and 9887 deletions

1
.gitignore vendored
View File

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

161
Makefile
View File

@@ -9,6 +9,29 @@ endif
TOPDIR ?= $(CURDIR)
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.
APP_TITLE := Anemone3DS
APP_DESCRIPTION := A complete theming tool for the 3DS
@@ -18,9 +41,11 @@ APP_AUTHOR := Anemone3DS Team
TARGET := $(subst $e ,_,$(notdir $(APP_TITLE)))
OUTDIR := out
BUILD := build
SOURCES := source source/pp2d/pp2d source/quirc
SOURCES := source source/quirc
INCLUDES := include
ROMFS := romfs
GRAPHICS := assets
GFXBUILD := $(ROMFS)/gfx
# Path to the files
@@ -44,6 +69,17 @@ PRODUCT_CODE := CTR-P-ANEM
# Don't really need to change this
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
#---------------------------------------------------------------------------------
@@ -53,9 +89,7 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
-ffunction-sections \
$(ARCH)
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)\""
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\""
ifneq ($(strip $(CITRA_MODE)),)
CFLAGS += -DCITRA_MODE
endif
@@ -65,7 +99,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lvorbisidec -logg -larchive -ljansson -lcitro3d -lctrud -lm -lz
LIBS := -lpng -lvorbisidec -logg -larchive -ljansson -lcitro2d -lcitro3d -lctrud -lm -lz
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
@@ -85,6 +119,7 @@ export OUTPUT := $(CURDIR)/$(OUTDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
@@ -94,6 +129,7 @@ CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
@@ -110,9 +146,19 @@ else
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
export T3XFILES := $(GFXFILES:.t3s=.t3x)
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) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
$(if $(filter $(BUILD),$(GFXBUILD)),$(addsuffix .o,$(T3XFILES)))
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)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
@@ -120,6 +166,8 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh)
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.png)
ifneq (,$(findstring $(TARGET).png,$(icons)))
@@ -141,56 +189,24 @@ ifneq ($(ROMFS),)
export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS)
endif
.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
.PHONY: all clean
#---------------------------------------------------------------------------------
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)"
#ifneq ($(strip $(ROMFS)),)
# MAKEROM_ARGS += -romfs "$(BUILD)/romfs.bin"
#endif
ifeq ($(strip $(NOGIT)),)
MAKEROM_ARGS += -major $(VERSION_MAJOR) -minor $(VERSION_MINOR) -micro $(VERSION_BUILD)
endif
ifneq ($(strip $(LOGO)),)
MAKEROM_ARGS += -logo "$(LOGO)"
endif
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)
ifneq ($(strip $(ROMFS)),)
MAKEROM_ARGS += -DAPP_ROMFS="$(ROMFS)"
endif
BANNERTOOL ?= bannertool
ifeq ($(suffix $(BANNER_IMAGE)),.cgfx)
@@ -205,57 +221,78 @@ else
BANNER_AUDIO_ARG := -a
endif
$(BUILD)/banner.bnr : $(BANNER_IMAGE) $(BANNER_AUDIO)
$(BANNERTOOL) makebanner $(BANNER_IMAGE_ARG) "$(BANNER_IMAGE)" $(BANNER_AUDIO_ARG) "$(BANNER_AUDIO)" -o "$@"
$(BUILD)/icon.icn : $(APP_ICON)
$(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i "$(APP_ICON)" -f "$(ICON_FLAGS)" -o "$@"
#---------------------------------------------------------------------------------
all:
@mkdir -p $(BUILD) $(GFXBUILD) $(OUTDIR)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@$(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 "$(BUILD)/icon.icn"
$(MAKEROM) -f cia -o "$(OUTPUT).cia" -target t -exefslogo $(MAKEROM_ARGS)
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(GFXBUILD) $(OUTDIR)
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).3dsx : $(OUTPUT).elf $(_3DSXDEPS)
$(OFILES_SOURCES) : $(HFILES)
$(OUTPUT).elf : $(OFILES)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------
.PRECIOUS : %.t3x
%.t3x.o %_t3x.h : %.t3x
#---------------------------------------------------------------------------------
@$(bin2o)
#---------------------------------------------------------------------------------
# rules for assembling GPU shaders
#---------------------------------------------------------------------------------
define shader-as
$(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
picasso -o $(CURBIN) $1
bin2s $(CURBIN) | $(AS) -o $@
$(eval CURBIN := $*.shbin)
$(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d)
echo "$(CURBIN).o: $< $1" > $(DEPSFILE)
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 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
%.shbin.o : %.v.pica %.g.pica
%.shbin.o %_shbin.h : %.v.pica %.g.pica
@echo $(notdir $^)
@$(call shader-as,$^)
%.shbin.o : %.v.pica
%.shbin.o %_shbin.h : %.v.pica
@echo $(notdir $<)
@$(call shader-as,$<)
%.shbin.o : %.shlist
%.shbin.o %_shbin.h : %.shlist
@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

View File

@@ -5,7 +5,7 @@ To-do list here: https://trello.com/b/F1YSa1VK
# Dependencies
* devkitARM, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started).
* zlib, jansson, libvorbisidec, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702).
* jansson, libvorbisidec, libpng, 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 64-bit Windows binary of makerom is available [here](https://hm892.s-ul.eu/U0Irkqih).
@@ -13,7 +13,8 @@ To-do list here: https://trello.com/b/F1YSa1VK
First of all, make sure devkitARM is properly installed - `$DEVKITPRO` and `$DEVKITARM` should be set to `/opt/devkitpro` and `$DEVKITPRO/devkitARM`, respectively.
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).
To install zlib, jansson, libvorbisidec, 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`, `3ds-libvorbisidec`, and `3ds-libarchive` using `[sudo] pacman -S <package-name>`.
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>`.
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

BIN
assets/arrow_down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
assets/arrow_right.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 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

23
assets/sprites.t3s Normal file
View File

@@ -0,0 +1,23 @@
--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

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 494 B

View File

@@ -31,7 +31,8 @@
typedef struct {
u16 *camera_buffer;
u32 *texture_buffer;
C2D_Image image;
C3D_Tex *tex;
Handle mutex;
volatile bool finished;
volatile bool success;

View File

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

View File

@@ -28,6 +28,8 @@
#define COMMON_H
#include <3ds.h>
#include <citro3d.h>
#include <citro2d.h>
#include <stdio.h>
#include <stdlib.h>
@@ -40,7 +42,7 @@
POS(); \
DEBUG(__VA_ARGS__)
#define FASTSCROLL_WAIT 1.5e8
#define FASTSCROLL_WAIT 1e8
typedef enum {
MODE_THEMES = 0,
@@ -54,36 +56,6 @@ extern const int entries_per_screen_v[MODE_AMOUNT];
extern const int entries_per_screen_h[MODE_AMOUNT];
extern const int entry_size[MODE_AMOUNT];
extern bool quit;
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,
};
extern bool dspfirm;
#endif

View File

@@ -31,6 +31,8 @@
#include "loading.h"
#include "colors.h"
#define MAX_LINES 10
typedef enum {
INSTALL_LOADING_THEMES,
INSTALL_LOADING_SPLASHES,
@@ -56,6 +58,63 @@ typedef enum {
INSTALL_NONE,
} 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 {
ERROR_LEVEL_ERROR,
ERROR_LEVEL_WARNING,
@@ -76,25 +135,40 @@ enum {
BUTTONS_X_LEFT = 20,
BUTTONS_X_RIGHT = 200,
BUTTONS_X_MAX = 380,
} ButtonPos;
typedef struct {
const wchar_t * info_line;
Color info_line_color;
const wchar_t * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
const char * info_line;
const char * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
} 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 exit_screens(void);
void start_frame(void);
void end_frame(void);
void set_screen(C3D_RenderTarget * screen);
void throw_error(char* error, ErrorLevel level);
bool draw_confirm(const char* conf_msg, Entry_List_s* list);
void draw_preview(ssize_t previewID, int preview_offset);
void draw_preview(C2D_Image preview, int preview_offset);
void draw_install(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_grid_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,
.instructions = {
{
L"\uE000 Hold to install",
L"\uE001 Queue shuffle theme"
"\uE000 Hold to install",
"\uE001 Queue shuffle theme"
},
{
L"\uE002 Hold for more",
L"\uE003 Preview theme"
"\uE002 Hold for more",
"\uE003 Preview theme"
},
{
L"\uE004 Switch to splashes",
L"\uE005 Scan QR code"
"\uE004 Switch to splashes",
"\uE005 Scan QR code"
},
{
L"Exit",
L"Delete from SD"
"Exit",
"Delete from SD"
}
}
},
@@ -56,43 +56,42 @@ Instructions_s normal_instructions[MODE_AMOUNT] = {
.info_line = NULL,
.instructions = {
{
L"\uE000 Install splash",
L"\uE001 Delete installed splash"
"\uE000 Install splash",
"\uE001 Delete installed splash"
},
{
L"\uE002 Hold for more",
L"\uE003 Preview splash"
"\uE002 Hold for more",
"\uE003 Preview splash"
},
{
L"\uE004 Switch to themes",
L"\uE005 Scan QR code"
"\uE004 Switch to themes",
"\uE005 Scan QR code"
},
{
L"Exit",
L"Delete from SD"
"Exit",
"Delete from SD"
}
}
}
};
Instructions_s install_instructions = {
.info_line = L"Release \uE000 to cancel or hold \uE006 and release \uE000 to install",
.info_line_color = COLOR_WHITE,
.info_line = "Release \uE000 to cancel or hold \uE006 and release \uE000 to install",
.instructions = {
{
L"\uE079 Normal install",
L"\uE07A Shuffle install"
"\uE079 Normal install",
"\uE07A Shuffle install"
},
{
L"\uE07B BGM-only install",
L"\uE07C No-BGM install"
"\uE07B BGM-only install",
"\uE07C No-BGM install"
},
{
NULL,
NULL
},
{
L"Exit",
"Exit",
NULL
}
}
@@ -100,15 +99,14 @@ Instructions_s install_instructions = {
Instructions_s extra_instructions[3] = {
{
.info_line = L"Release \uE002 to cancel or hold \uE006 and release \uE002 to sort",
.info_line_color = COLOR_WHITE,
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to sort",
.instructions = {
{
L"\uE079 Sort by name",
L"\uE07A Sort by author"
"\uE079 Sort by name",
"\uE07A Sort by author"
},
{
L"\uE07B Sort by filename",
"\uE07B Sort by filename",
NULL
},
{
@@ -116,29 +114,28 @@ Instructions_s extra_instructions[3] = {
NULL
},
{
L"Exit",
"Exit",
NULL
}
}
},
{
.info_line = L"Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.info_line_color = COLOR_WHITE,
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.instructions = {
{
L"\uE079 Jump in the list",
L"\uE07A Reload broken icons"
"\uE079 Jump in the list",
"\uE07A Reload broken icons"
},
{
L"\uE07B Browse ThemePlaza",
"\uE07B Browse ThemePlaza",
NULL,
},
{
L"\uE004 Sorting menu",
"\uE004 Sorting menu",
NULL
},
{
L"Exit",
"Exit",
NULL
}
}

View File

@@ -81,8 +81,7 @@ typedef struct {
Entry_s * entries;
int entries_count;
ssize_t texture_id_offset;
ssize_t * icons_ids;
C2D_Image ** icons;
int previous_scroll;
int scroll;
@@ -110,13 +109,18 @@ typedef struct {
volatile bool run_thread;
} 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_author(Entry_List_s * list);
void sort_by_filename(Entry_List_s * list);
void delete_entry(Entry_s * entry, bool is_file);
Result load_entries(const char * loading_path, Entry_List_s * list);
bool load_preview(Entry_List_s list, int * preview_offset);
bool load_preview_from_buffer(void * buf, u32 size, C2D_Image * preview_image, 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 *);
void load_icons_first(Entry_List_s * current_list, bool silent);
void handle_scrolling(Entry_List_s * list);

View File

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

View File

@@ -27,7 +27,6 @@
#include "camera.h"
#include "quirc/quirc.h"
#include "pp2d/pp2d/pp2d.h"
#include "draw.h"
#include "fs.h"
@@ -37,13 +36,6 @@
#include <archive.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)
{
DEBUG("Exiting QR\n");
@@ -54,7 +46,7 @@ void exit_qr(qr_data *data)
data->capturing = false;
free(data->camera_buffer);
free(data->texture_buffer);
free(data->tex);
quirc_destroy(data->context);
}
@@ -133,6 +125,39 @@ void capture_cam_thread(void *arg)
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)
{
data->mutex = 0;
@@ -141,6 +166,11 @@ bool start_capture_cam(qr_data *data)
svcCreateMutex(&data->mutex, false);
if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL)
return false;
if(threadCreate(update_ui, data, 0x10000, 0x1A, 1, true) == NULL)
{
exit_qr(data);
return false;
}
return true;
}
@@ -166,31 +196,16 @@ void update_qr(qr_data *data)
exit_qr(data);
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 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 y = 0; y < h; y++) {
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);
}
}
svcReleaseMutex(data->mutex);
quirc_end(data->context);
if(quirc_count(data->context) > 0)
{
@@ -288,11 +303,17 @@ bool init_qr(void)
qr_data *data = calloc(1, sizeof(qr_data));
data->capturing = false;
data->finished = false;
data->context = quirc_new();
quirc_resize(data->context, 400, 240);
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);
bool success = data->success;

40
source/colors.c Normal file
View File

@@ -0,0 +1,40 @@
/*
* 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(55, 122, 168, 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(200, 0, 0, 255);
colors[COLOR_YELLOW] = C2D_Color32(239, 220, 11, 255);
}

View File

@@ -28,129 +28,282 @@
#include "unicode.h"
#include "colors.h"
#include "pp2d/pp2d/pp2d.h"
#include "sprites.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)
{
pp2d_init();
init_colors();
gfxInitDefault();
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
C2D_Init(C2D_DEFAULT_MAX_OBJECTS);
C2D_Prepare();
pp2d_set_screen_color(GFX_TOP, COLOR_BACKGROUND);
pp2d_set_screen_color(GFX_BOTTOM, COLOR_BACKGROUND);
top = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT);
bottom = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT);
pp2d_load_texture_png(TEXTURE_ARROW, "romfs:/arrow.png");
pp2d_load_texture_png(TEXTURE_ARROW_SIDE, "romfs:/arrow_side.png");
pp2d_load_texture_png(TEXTURE_SHUFFLE, "romfs:/shuffle.png");
pp2d_load_texture_png(TEXTURE_SHUFFLE_NO_BGM, "romfs:/shuffle_no_bgm.png");
pp2d_load_texture_png(TEXTURE_INSTALLED, "romfs:/installed.png");
pp2d_load_texture_png(TEXTURE_PREVIEW_ICON, "romfs:/preview.png");
pp2d_load_texture_png(TEXTURE_SORT, "romfs:/sort.png");
pp2d_load_texture_png(TEXTURE_DOWNLOAD, "romfs:/download.png");
pp2d_load_texture_png(TEXTURE_BROWSE, "romfs:/browse.png");
pp2d_load_texture_png(TEXTURE_LIST, "romfs:/list.png");
pp2d_load_texture_png(TEXTURE_EXIT, "romfs:/exit.png");
pp2d_load_texture_png(TEXTURE_BATTERY_0, "romfs:/battery0.png");
pp2d_load_texture_png(TEXTURE_BATTERY_1, "romfs:/battery1.png");
pp2d_load_texture_png(TEXTURE_BATTERY_2, "romfs:/battery2.png");
pp2d_load_texture_png(TEXTURE_BATTERY_3, "romfs:/battery3.png");
pp2d_load_texture_png(TEXTURE_BATTERY_4, "romfs:/battery4.png");
pp2d_load_texture_png(TEXTURE_BATTERY_5, "romfs:/battery5.png");
pp2d_load_texture_png(TEXTURE_BATTERY_CHARGE, "romfs:/charging.png");
pp2d_load_texture_png(TEXTURE_SELECT_BUTTON, "romfs:/select.png");
pp2d_load_texture_png(TEXTURE_START_BUTTON, "romfs:/start.png");
spritesheet = C2D_SpriteSheetLoad("romfs:/gfx/sprites.t3x");
C2D_SpriteFromSheet(&sprite_shuffle, spritesheet, sprites_shuffle_idx);
C2D_SpriteSetDepth(&sprite_shuffle, 0.6f);
C2D_SpriteFromSheet(&sprite_shuffle_no_bgm, spritesheet, sprites_shuffle_no_bgm_idx);
C2D_SpriteSetDepth(&sprite_shuffle_no_bgm, 0.6f);
C2D_SpriteFromSheet(&sprite_installed, spritesheet, sprites_installed_idx);
C2D_SpriteSetDepth(&sprite_installed, 0.6f);
C2D_SpriteFromSheet(&sprite_start, spritesheet, sprites_start_idx);
C2D_SpriteSetDepth(&sprite_start, 0.5f);
C2D_SpriteFromSheet(&sprite_select, spritesheet, sprites_select_idx);
C2D_SpriteSetDepth(&sprite_select, 0.5f);
staticBuf = C2D_TextBufNew(4096);
dynamicBuf = C2D_TextBufNew(4096);
widthBuf = C2D_TextBufNew(4096);
C2D_TextParse(&text[TEXT_VERSION], staticBuf, VERSION);
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, "Installing BGM-only theme...");
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)
{
pp2d_exit();
C2D_TextBufDelete(widthBuf);
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)
{
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
pp2d_draw_rectangle(0, 0, 400, 23, COLOR_ACCENT);
start_frame();
set_screen(top);
C2D_DrawRectSolid(0, 0, 0.5f, 400, 23, colors[COLOR_ACCENT]);
time_t t = time(NULL);
struct tm tm = *localtime(&t);
pp2d_draw_textf(7, 2, 0.6, 0.6, COLOR_WHITE, "%.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);
char string_hours[3] = {0};
sprintf(string_hours, "%.2i", tm.tm_hour);
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
u8 battery_charging = 0;
PTMU_GetBatteryChargeState(&battery_charging);
u8 battery_status = 0;
PTMU_GetBatteryLevel(&battery_status);
pp2d_draw_texture(TEXTURE_BATTERY_0 + battery_status, 357, 2);
draw_image(sprites_battery0_idx + battery_status, 357, 2);
if(battery_charging)
pp2d_draw_texture(TEXTURE_BATTERY_CHARGE, 357, 2);
draw_image(sprites_charging_idx, 357, 2);
#endif
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);
set_screen(bottom);
pp2d_draw_on(GFX_TOP, GFX_LEFT);
}
C2D_DrawRectSolid(0, 0, 0.5f, 320, 24, colors[COLOR_ACCENT]);
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]);
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);
set_screen(top);
}
void throw_error(char* error, ErrorLevel level)
{
Text bottom_text = TEXT_AMOUNT;
Color text_color = COLOR_WHITE;
switch(level)
{
case ERROR_LEVEL_ERROR:
while(aptMainLoop())
{
hidScanInput();
u32 kDown = hidKeysDown();
draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.6, 0.6, COLOR_RED, error);
pp2d_draw_wtext_center(GFX_TOP, 150, 0.6, 0.6, COLOR_WHITE, L"Press \uE000 to shut down.");
pp2d_end_draw();
if(kDown & KEY_A) break;
}
bottom_text = TEXT_ERROR_QUIT;
text_color = COLOR_RED;
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;
}
bottom_text = TEXT_ERROR_CONTINUE;
text_color = COLOR_YELLOW;
break;
default:
return;
}
while(aptMainLoop())
{
hidScanInput();
u32 kDown = hidKeysDown();
draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.5f, 0.6f, 0.6f, colors[text_color], error);
draw_c2d_text_center(GFX_TOP, 150, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[bottom_text]);
end_frame();
if(kDown & KEY_A) break;
}
}
@@ -160,10 +313,10 @@ bool draw_confirm(const char* conf_msg, Entry_List_s* list)
{
Instructions_s instructions = {0};
draw_interface(list, instructions);
pp2d_draw_on(GFX_TOP, GFX_LEFT);
draw_text_center(GFX_TOP, BUTTONS_Y_LINE_1, 0.7, 0.7, COLOR_YELLOW, conf_msg);
pp2d_draw_wtext_center(GFX_TOP, BUTTONS_Y_LINE_3, 0.6, 0.6, COLOR_WHITE, L"\uE000 Yes \uE001 No");
pp2d_end_draw();
set_screen(top);
draw_text_center(GFX_TOP, BUTTONS_Y_LINE_1, 0.5f, 0.7f, 0.7f, colors[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]);
end_frame();
hidScanInput();
u32 kDown = hidKeysDown();
@@ -174,68 +327,21 @@ bool draw_confirm(const char* conf_msg, Entry_List_s* list)
return false;
}
void draw_preview(ssize_t previewID, int preview_offset)
void draw_preview(C2D_Image preview, int preview_offset)
{
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
pp2d_draw_texture_part(previewID, 0, 0, preview_offset, 0, 400, 240);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_texture_part(previewID, 0, 0, 40 + preview_offset, 240, 320, 240);
start_frame();
set_screen(top);
C2D_DrawImageAt(preview, -preview_offset, 0, 0.5f, NULL, 1.0f, 1.0f);
set_screen(bottom);
C2D_DrawImageAt(preview, -(preview_offset+40), -240, 0.5f, NULL, 1.0f, 1.0f);
}
static void draw_install_handler(InstallType type)
{
switch(type)
if(type != INSTALL_NONE)
{
case INSTALL_LOADING_THEMES:
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;
C2D_Text * install_text = &text[type];
draw_c2d_text_center(GFX_TOP, 120.0f, 0.5f, 0.8f, 0.8f, colors[COLOR_WHITE], install_text);
}
}
@@ -243,28 +349,26 @@ void draw_install(InstallType type)
{
draw_base_interface();
draw_install_handler(type);
pp2d_end_draw();
end_frame();
}
void draw_loading_bar(u32 current, u32 max, InstallType type)
{
draw_base_interface();
draw_install_handler(type);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
set_screen(bottom);
double percent = 100*((double)current/(double)max);
u32 width = (u32)percent;
width *= 2;
pp2d_draw_rectangle(60-1, 110-1, 200+2, 20+2, COLOR_CURSOR);
pp2d_draw_rectangle(60, 110, width, 20, COLOR_ACCENT);
pp2d_end_draw();
C2D_DrawRectSolid(60-1, 110-1, 0.5f, 200+2, 20+2, colors[COLOR_CURSOR]);
C2D_DrawRectSolid(60, 110, 0.5f, width, 20, colors[COLOR_ACCENT]);
end_frame();
}
static void draw_instructions(Instructions_s instructions)
{
pp2d_draw_on(GFX_TOP, GFX_LEFT);
if(instructions.info_line != NULL)
pp2d_draw_wtext_center(GFX_TOP, BUTTONS_Y_INFO, 0.55, 0.55, instructions.info_line_color, instructions.info_line);
draw_text_center(GFX_TOP, BUTTONS_Y_INFO, 0.5, 0.55, 0.55, colors[COLOR_WHITE], instructions.info_line);
const int y_lines[BUTTONS_INFO_LINES-1] = {
BUTTONS_Y_LINE_1,
@@ -275,46 +379,127 @@ static void draw_instructions(Instructions_s instructions)
for(int i = 0; i < BUTTONS_INFO_LINES-1; i++)
{
if(instructions.instructions[i][0] != NULL)
pp2d_draw_wtext(BUTTONS_X_LEFT, y_lines[i], 0.6, 0.6, COLOR_WHITE, instructions.instructions[i][0]);
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);
if(instructions.instructions[i][1] != NULL)
pp2d_draw_wtext(BUTTONS_X_RIGHT, y_lines[i], 0.6, 0.6, COLOR_WHITE, instructions.instructions[i][1]);
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);
}
const wchar_t * start_line = instructions.instructions[BUTTONS_INFO_LINES-1][0];
C2D_ImageTint white_tint;
C2D_PlainImageTint(&white_tint, colors[COLOR_WHITE], 1.0f);
const char * start_line = instructions.instructions[BUTTONS_INFO_LINES-1][0];
if(start_line != NULL)
{
pp2d_draw_texture(TEXTURE_START_BUTTON, BUTTONS_X_LEFT-10, BUTTONS_Y_LINE_4 + 3);
pp2d_draw_wtext(BUTTONS_X_LEFT+26, BUTTONS_Y_LINE_4, 0.6, 0.6, COLOR_WHITE, start_line);
C2D_SpriteSetPos(&sprite_start, BUTTONS_X_LEFT-10, BUTTONS_Y_LINE_4 + 3);
C2D_DrawSpriteTinted(&sprite_start, &white_tint);
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 wchar_t * select_line = instructions.instructions[BUTTONS_INFO_LINES-1][1];
const char * select_line = instructions.instructions[BUTTONS_INFO_LINES-1][1];
if(select_line != NULL)
{
pp2d_draw_texture(TEXTURE_SELECT_BUTTON, BUTTONS_X_RIGHT-10, BUTTONS_Y_LINE_4 + 3);
pp2d_draw_wtext(BUTTONS_X_RIGHT+26, BUTTONS_Y_LINE_4, 0.6, 0.6, COLOR_WHITE, select_line);
C2D_SpriteSetPos(&sprite_select, BUTTONS_X_RIGHT-10, BUTTONS_Y_LINE_4 + 3);
C2D_DrawSpriteTinted(&sprite_select, &white_tint);
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)
{
float wrap = 363;
char author[0x41] = {0};
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);
wchar_t author[0x41] = {0};
utf16_to_utf32((u32*)author, entry->author, 0x40);
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 title[0x41] = {0};
utf16_to_utf8((u8*)title, entry->name, 0x40);
draw_text(20, 50, 0.5, 0.7, 0.7, colors[COLOR_WHITE], title);
wchar_t title[0x41] = {0};
utf16_to_utf32((u32*)title, entry->name, 0x40);
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);
char description[0x81] = {0};
utf16_to_utf8((u8*)description, entry->desc, 0x80);
draw_text_wrap(20, 70, 0.5, 0.5, 0.5, colors[COLOR_WHITE], description, 363);
}
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
@@ -322,12 +507,12 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
draw_base_interface();
EntryMode current_mode = list->mode;
const char* mode_string[MODE_AMOUNT] = {
"ThemePlaza Theme mode",
"ThemePlaza Splash mode",
C2D_Text* mode_string[MODE_AMOUNT] = {
&text[TEXT_THEMEPLAZA_THEME_MODE],
&text[TEXT_THEMEPLAZA_SPLASH_MODE],
};
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, mode_string[current_mode]);
draw_c2d_text_center(GFX_TOP, 4, 0.5f, 0.5f, 0.5f, colors[COLOR_WHITE], mode_string[current_mode]);
draw_instructions(instructions);
@@ -335,17 +520,18 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
Entry_s * current_entry = &list->entries[selected_entry];
draw_entry_info(current_entry);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
set_screen(bottom);
pp2d_draw_text(7, 3, 0.6, 0.6, COLOR_WHITE, "Search...");
draw_c2d_text(7, 3, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[TEXT_SEARCH]);
pp2d_draw_texture_blend(TEXTURE_LIST, 320-96, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_EXIT, 320-72, 0, COLOR_WHITE);
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_image(sprites_list_idx, 320-96, 0);
draw_image(sprites_exit_idx, 320-72, 0);
draw_image(sprites_preview_idx, 320-48, 0);
pp2d_draw_texture(TEXTURE_ARROW_SIDE, 3, 114);
pp2d_draw_texture_flip(TEXTURE_ARROW_SIDE, 308, 114, HORIZONTAL);
draw_text(320-24+2.5, -3, 0.6, 1.0f, 0.9f, colors[COLOR_WHITE], mode_switch_char[!current_mode]);
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++)
{
@@ -353,8 +539,8 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
current_entry = &list->entries[i];
wchar_t name[0x41] = {0};
utf16_to_utf32((u32*)name, current_entry->name, 0x40);
char name[0x41] = {0};
utf16_to_utf8((u8*)name, current_entry->name, 0x40);
int vertical_offset = 0;
int horizontal_offset = i - list->scroll;
@@ -368,34 +554,37 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
if(!current_entry->placeholder_color)
{
ssize_t id = list->icons_ids[i];
pp2d_draw_texture(id, horizontal_offset, vertical_offset);
C2D_Image * image = list->icons[i];
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
}
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);
if(i == selected_entry)
{
unsigned int border_width = 3;
pp2d_draw_rectangle(horizontal_offset, vertical_offset, border_width, list->entry_size, COLOR_CURSOR);
pp2d_draw_rectangle(horizontal_offset, vertical_offset, list->entry_size, border_width, COLOR_CURSOR);
pp2d_draw_rectangle(horizontal_offset, vertical_offset+list->entry_size-border_width, list->entry_size, border_width, COLOR_CURSOR);
pp2d_draw_rectangle(horizontal_offset+list->entry_size-border_width, vertical_offset, border_width, list->entry_size, COLOR_CURSOR);
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, border_width, list->entry_size, colors[COLOR_CURSOR]);
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, border_width, colors[COLOR_CURSOR]);
C2D_DrawRectSolid(horizontal_offset, vertical_offset+list->entry_size-border_width, 0.5f, list->entry_size, border_width, colors[COLOR_CURSOR]);
C2D_DrawRectSolid(horizontal_offset+list->entry_size-border_width, vertical_offset, 0.5f, border_width, list->entry_size, colors[COLOR_CURSOR]);
}
}
char entries_count_str[0x20] = {0};
sprintf(entries_count_str, "/%" JSON_INTEGER_FORMAT, list->tp_page_count);
float x = 316;
x -= pp2d_get_text_width(entries_count_str, 0.6, 0.6);
pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, entries_count_str);
float width = 0;
get_text_dimensions(entries_count_str, 0.6, 0.6, &width, NULL);
x -= width;
draw_text(x, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], entries_count_str);
char selected_entry_str[0x20] = {0};
sprintf(selected_entry_str, "%" JSON_INTEGER_FORMAT, list->tp_current_page);
x -= pp2d_get_text_width(selected_entry_str, 0.6, 0.6);
pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, selected_entry_str);
get_text_dimensions(selected_entry_str, 0.6, 0.6, &width, NULL);
x -= width;
draw_text(x, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], selected_entry_str);
pp2d_draw_text(176, 219, 0.6, 0.6, COLOR_WHITE, "Page:");
draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_PAGE]);
}
void draw_interface(Entry_List_s* list, Instructions_s instructions)
@@ -403,40 +592,47 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
draw_base_interface();
EntryMode current_mode = list->mode;
const char* mode_string[MODE_AMOUNT] = {
"Theme mode",
"Splash mode",
C2D_Text* mode_string[MODE_AMOUNT] = {
&text[TEXT_THEME_MODE],
&text[TEXT_SPLASH_MODE],
};
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, mode_string[current_mode]);
draw_c2d_text_center(GFX_TOP, 4, 0.5f, 0.5f, 0.5f, colors[COLOR_WHITE], mode_string[current_mode]);
if(list->entries == NULL)
{
const char* mode_found_string[MODE_AMOUNT] = {
"No themes found",
"No splashes found",
C2D_Text* mode_found_string[MODE_AMOUNT] = {
&text[TEXT_NO_THEME_FOUND],
&text[TEXT_NO_SPLASH_FOUND],
};
pp2d_draw_text_center(GFX_TOP, 80, 0.7, 0.7, 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");
const char* mode_switch_string[MODE_AMOUNT] = {
"Or \uE004 to switch to splashes",
"Or \uE004 to switch to themes",
draw_c2d_text_center(GFX_TOP, 80, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], mode_found_string[current_mode]);
draw_c2d_text_center(GFX_TOP, 110, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], &text[TEXT_DOWNLOAD_FROM_QR]);
C2D_Text* mode_switch_string[MODE_AMOUNT] = {
&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();
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
draw_c2d_text_center(GFX_TOP, 140, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], mode_switch_string[current_mode]);
draw_c2d_text_center(GFX_TOP, 170, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], &text[TEXT_OR_START_TO_QUIT]);
pp2d_draw_texture_blend(TEXTURE_SORT, 320-144, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_DOWNLOAD, 320-120, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_BROWSE, 320-96, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_EXIT, 320-72, 0, COLOR_WHITE);
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]);
C2D_ImageTint yellow_tint;
C2D_PlainImageTint(&yellow_tint, colors[COLOR_YELLOW], 1.0f);
C2D_SpriteSetPos(&sprite_start, 162, 173);
C2D_SpriteSetScale(&sprite_start, 1.25f, 1.4f);
C2D_DrawSpriteTinted(&sprite_start, &yellow_tint);
C2D_SpriteSetScale(&sprite_start, 1.0f, 1.0f);
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;
}
@@ -447,30 +643,30 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
Entry_s * current_entry = &list->entries[selected_entry];
draw_entry_info(current_entry);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
set_screen(bottom);
switch(current_mode)
if(current_mode == MODE_THEMES)
{
case MODE_THEMES:
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);
break;
default:
break;
char * shuffle_count_string = NULL;
asprintf(&shuffle_count_string, "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);
free(shuffle_count_string);
}
pp2d_draw_texture_blend(TEXTURE_SORT, 320-144, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_DOWNLOAD, 320-120, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_BROWSE, 320-96, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_EXIT, 320-72, 0, COLOR_WHITE);
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_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]);
// Show arrows if there are themes out of bounds
//----------------------------------------------------------------
if(list->scroll > 0)
pp2d_draw_texture(TEXTURE_ARROW, 152, 4);
draw_image(sprites_arrow_up_idx, 152, 4);
if(list->scroll + list->entries_loaded < list->entries_count)
pp2d_draw_texture_flip(TEXTURE_ARROW, 152, 220, VERTICAL);
draw_image(sprites_arrow_down_idx, 152, 220);
for(int i = list->scroll; i < (list->entries_loaded + list->scroll); i++)
{
@@ -478,8 +674,8 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
current_entry = &list->entries[i];
wchar_t name[0x41] = {0};
utf16_to_utf32((u32*)name, current_entry->name, 0x40);
char name[0x41] = {0};
utf16_to_utf8((u8*)name, current_entry->name, 0x40);
int vertical_offset = i - list->scroll;
int horizontal_offset = 0;
@@ -487,47 +683,67 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
vertical_offset *= list->entry_size;
vertical_offset += 24;
u32 font_color = COLOR_WHITE;
u32 font_color = colors[COLOR_WHITE];
if(i == selected_entry)
{
font_color = COLOR_BLACK;
pp2d_draw_rectangle(0, vertical_offset, 320, list->entry_size, COLOR_CURSOR);
font_color = colors[COLOR_BLACK];
C2D_DrawRectSolid(0, vertical_offset, 0.5f, 320, list->entry_size, colors[COLOR_CURSOR]);
}
pp2d_draw_wtext(list->entry_size+6, vertical_offset + 16, 0.55, 0.55, font_color, name);
draw_text(list->entry_size+6, vertical_offset + 16, 0.5f, 0.55, 0.55, font_color, name);
C2D_ImageTint tint;
C2D_PlainImageTint(&tint, font_color, 1.0f);
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)
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)
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)
{
ssize_t id = 0;
C2D_Image * image = NULL;
if(list->entries_count > list->entries_loaded*ICONS_OFFSET_AMOUNT)
id = list->icons_ids[ICONS_VISIBLE*list->entries_loaded + (i - list->scroll)];
image = list->icons[ICONS_VISIBLE*list->entries_loaded + (i - list->scroll)];
else
id = list->icons_ids[i];
pp2d_draw_texture(id, horizontal_offset, vertical_offset);
image = list->icons[i];
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
}
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};
sprintf(entries_count_str, "/%i", list->entries_count);
float x = 316;
x -= pp2d_get_text_width(entries_count_str, 0.6, 0.6);
pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, entries_count_str);
float width = 0;
get_text_dimensions(entries_count_str, 0.6, 0.6, &width, NULL);
x -= width;
draw_text(x, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], entries_count_str);
char selected_entry_str[0x20] = {0};
sprintf(selected_entry_str, "%i", selected_entry + 1);
x -= pp2d_get_text_width(selected_entry_str, 0.6, 0.6);
pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, selected_entry_str);
get_text_dimensions(selected_entry_str, 0.6, 0.6, &width, NULL);
x -= width;
draw_text(x, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], selected_entry_str);
pp2d_draw_text(176, 219, 0.6, 0.6, COLOR_WHITE, list->entries_count < 1000 ? "Selected:" : "Sel.:");
if(list->entries_count < 10000)
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,4 +200,7 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
FSUSER_DeleteFile(archive, path);
}
FSUSER_CreateFile(archive, path, 0, size);
char *buf = calloc(size, 1);
buf_to_file(size, path, archive, buf);
free(buf);
}

View File

@@ -25,12 +25,13 @@
*/
#include "loading.h"
#include "pp2d/pp2d/pp2d.h"
#include "fs.h"
#include "unicode.h"
#include "music.h"
#include "draw.h"
#include <png.h>
void delete_entry(Entry_s * entry, bool is_file)
{
if(is_file)
@@ -55,54 +56,71 @@ u32 load_data(char * filename, Entry_s entry, char ** buf)
}
}
static void parse_smdh(Entry_s * entry, const u16 * fallback_name)
// Function taken and adapted from https://github.com/BernardoGiordano/Checkpoint/blob/master/3ds/source/title.cpp
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;
u64 size = load_data("/info.smdh", *entry, &info_buffer);
Icon_s * smdh = (Icon_s *)info_buffer;
if(!size)
{
free(info_buffer);
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;
info_buffer = NULL;
}
memcpy(entry->name, smdh->name, 0x40*sizeof(u16));
memcpy(entry->desc, smdh->desc, 0x80*sizeof(u16));
memcpy(entry->author, smdh->author, 0x40*sizeof(u16));
}
static void load_smdh_icon(Entry_s entry, const ssize_t textureID)
{
pp2d_free_texture(textureID);
char *info_buffer = NULL;
u64 size = load_data("/info.smdh", entry, &info_buffer);
if(!size) return;
Icon_s * smdh = (Icon_s *)info_buffer;
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
parse_smdh(smdh, entry, fallback_name);
}
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));
static C2D_Image * load_entry_icon(Entry_s entry)
{
char *info_buffer = NULL;
u64 size = load_data("/info.smdh", entry, &info_buffer);
if(!size) return NULL;
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);
Icon_s * smdh = (Icon_s *)info_buffer;
return loadTextureIcon(smdh);
}
typedef int (*sort_comparator)(const void *, const void *);
@@ -192,7 +210,7 @@ Result load_entries(const char * loading_path, Entry_List_s * list)
strucat(current_entry->path, dir_entry.name);
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
parse_smdh(current_entry, dir_entry.name);
parse_entry_smdh(current_entry, dir_entry.name);
}
FSDIR_Close(dir_handle);
@@ -223,12 +241,11 @@ void load_icons_first(Entry_List_s * list, bool silent)
endi = starti + list->entries_loaded*ICONS_OFFSET_AMOUNT;
}
list->icons_ids = calloc(endi-starti, sizeof(ssize_t));
list->icons = calloc(endi-starti, sizeof(C2D_Image*));
ssize_t * icons_ids = list->icons_ids;
ssize_t id = list->texture_id_offset;
C2D_Image ** icons = list->icons;
for(int i = starti; i < endi; i++, id++)
for(int i = starti; i < endi; i++)
{
if(!silent)
draw_loading_bar(i - starti, endi-starti, INSTALL_LOADING_ICONS);
@@ -240,22 +257,20 @@ void load_icons_first(Entry_List_s * list, bool silent)
offset -= list->entries_count;
Entry_s current_entry = list->entries[offset];
load_smdh_icon(current_entry, id);
icons_ids[i-starti] = id;
icons[i-starti] = load_entry_icon(current_entry);
}
}
static void reverse(ssize_t a[], int sz) {
static void reverse(C2D_Image * a[], int sz) {
int i, j;
for (i = 0, j = sz; i < j; i++, j--) {
ssize_t tmp = a[i];
C2D_Image * tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
static void rotate(ssize_t array[], int size, int amt) {
static void rotate(C2D_Image * array[], int size, int amt) {
if (amt < 0)
amt = size + amt;
reverse(array, size-amt-1);
@@ -314,15 +329,15 @@ void handle_scrolling(Entry_List_s * list)
//----------------------------------------------------------------
}
static void load_icons(Entry_List_s * current_list)
static bool load_icons(Entry_List_s * current_list, Handle mutex)
{
if(current_list == NULL || current_list->entries == NULL)
return;
return false;
handle_scrolling(current_list);
if(current_list->entries_count <= current_list->entries_loaded*ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
return; // return if the list is one that doesnt need swapping, or if nothing changed
return false; // return if the list is one that doesnt need swapping, or if nothing changed
#define SIGN(x) (x > 0 ? 1 : ((x < 0) ? -1 : 0))
@@ -341,28 +356,26 @@ static void load_icons(Entry_List_s * current_list)
int ctr = 0;
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *));
ssize_t * ids = calloc(abs(delta), sizeof(ssize_t));
int * indexes = calloc(abs(delta), sizeof(int));
bool released = false;
#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;
C2D_Image ** icons = current_list->icons;
for(int i = starti; i != endi; i++, ctr++)
{
ssize_t id = 0;
int index = 0;
int offset = i;
rotate(icons_ids, ICONS_OFFSET_AMOUNT*current_list->entries_loaded, -1*SIGN(delta));
rotate(icons, ICONS_OFFSET_AMOUNT*current_list->entries_loaded, -1*SIGN(delta));
if(delta > 0)
{
id = LAST(icons_ids);
index = current_list->entries_loaded*ICONS_OFFSET_AMOUNT - delta + i - starti;
offset += current_list->entries_loaded*ICONS_UNDER - delta;
}
else
{
id = FIRST(icons_ids);
index = 0 - delta - 1 + i - starti;
offset -= current_list->entries_loaded*ICONS_VISIBLE;
i -= 2; //i-- twice to counter the i++, needed only for this case
}
@@ -373,43 +386,169 @@ static void load_icons(Entry_List_s * current_list)
offset -= current_list->entries_count;
entries[ctr] = &current_list->entries[offset];
ids[ctr] = id;
indexes[ctr] = index;
}
#undef FIRST
#undef LAST
#undef SIGN
svcSleepThread(1e6);
for(int i = 0; i < abs(delta); i++)
if(abs(delta) < 4)
{
Entry_s current_entry = *entries[i];
ssize_t id = ids[i];
load_smdh_icon(current_entry, id);
svcReleaseMutex(mutex);
released = true;
}
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(ids);
free(indexes);
current_list->previous_scroll = current_list->scroll;
return released;
}
void load_icons_thread(void * void_arg)
{
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
Handle update_request = *(Handle *)arg->thread_arg[1];
Handle mutex = *(Handle *)arg->thread_arg[1];
do
{
svcWaitSynchronization(update_request, U64_MAX);
svcClearEvent(update_request);
svcWaitSynchronization(mutex, U64_MAX);
volatile Entry_List_s * current_list = *(volatile Entry_List_s **)arg->thread_arg[0];
load_icons((Entry_List_s *)current_list);
bool released = load_icons((Entry_List_s *)current_list, mutex);
if(!released)
svcReleaseMutex(mutex);
}
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};
bool load_preview(Entry_List_s list, int * preview_offset)
bool load_preview(Entry_List_s list, C2D_Image * preview_image, int * preview_offset)
{
if(list.entries == NULL) return false;
@@ -427,66 +566,50 @@ bool load_preview(Entry_List_s list, int * preview_offset)
return false;
}
bool ret = false;
u8 * image = NULL;
unsigned int width = 0, height = 0;
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
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);
bool ret = load_preview_from_buffer(preview_buffer, size, preview_image, preview_offset);
free(preview_buffer);
if(ret)
{
// mark the new preview as loaded for optimisation
memcpy(&previous_path_preview, &entry.path, 0x106*sizeof(u16));
}
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
Result load_audio(Entry_s entry, audio_s *audio)
{
audio->filesize = load_data("/bgm.ogg", entry, &audio->filebuf);
if (audio->filesize == 0) {
free(audio);
DEBUG("File not found!\n");
DEBUG("<load_audio> File not found!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
}
audio->mix[0] = audio->mix[1] = 1.0f; // Determines volume for the 12 (?) different outputs. See http://smealum.github.io/ctrulib/channel_8h.html#a30eb26f1972cc3ec28370263796c0444
svcCreateEvent(&audio->finished, RESET_STICKY);
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetMix(0, audio->mix); // See mix comment above
FILE *file = fmemopen(audio->filebuf, audio->filesize, "rb");
DEBUG("Filesize: %lld\n", audio->filesize);
DEBUG("<load_audio> Filesize: %ld\n", audio->filesize);
if(file != NULL)
{
int e = ov_open(file, &audio->vf, NULL, 0);
if (e < 0)
{
DEBUG("Vorbis: %d\n", e);
DEBUG("<load_audio> Vorbis: %d\n", e);
free(audio->filebuf);
free(audio);
fclose(file);
@@ -496,10 +619,10 @@ Result load_audio(Entry_s entry, audio_s *audio)
vorbis_info *vi = ov_info(&audio->vf, -1);
ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file
if (vi->channels == 2) {
DEBUG("Using stereo\n");
DEBUG("<load_audio> Using stereo\n");
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); // 2 channels == Stereo
} else {
DEBUG("Invalid number of channels\n");
DEBUG("<load_audio> Invalid number of channels\n");
free(audio->filebuf);
free(audio);
fclose(file);
@@ -510,12 +633,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].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);
DEBUG("Success!\n");
DEBUG("<load_audio> Success!\n");
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
} else {
free(audio->filebuf);
free(audio);
DEBUG("fmemopen failed!\n");
DEBUG("<load_audio> fmemopen failed!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
}
}

View File

@@ -33,17 +33,18 @@
#include "music.h"
#include "remote.h"
#include "instructions.h"
#include "pp2d/pp2d/pp2d.h"
#include <time.h>
bool quit = false;
bool dspfirm = false;
audio_s * audio = NULL;
static bool homebrew = false;
static bool installed_themes = false;
static Thread iconLoadingThread = {0};
static Thread_Arg_s iconLoadingThread_arg = {0};
static Handle update_icons_handle;
static Handle update_icons_mutex;
static bool released = false;
static Thread installCheckThreads[MODE_AMOUNT] = {0};
static Thread_Arg_s installCheckThreads_arg[MODE_AMOUNT] = {0};
@@ -77,9 +78,10 @@ static void init_services(void)
cfguInit();
ptmuInit();
acInit();
ndspInit();
dspfirm = !ndspInit();
APT_GetAppCpuTimeLimit(&old_time_limit);
APT_SetAppCpuTimeLimit(30);
// aptSetHomeAllowed(false);
httpcInit(0);
archive_result = open_archives();
if(envIsHomebrew())
@@ -115,20 +117,36 @@ static void exit_thread(void)
{
DEBUG("exiting thread\n");
iconLoadingThread_arg.run_thread = false;
svcSignalEvent(update_icons_handle);
svcReleaseMutex(update_icons_mutex);
svcWaitSynchronization(update_icons_mutex, U64_MAX);
threadJoin(iconLoadingThread, U64_MAX);
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)
{
stop_install_check();
for(int i = 0; i < MODE_AMOUNT; i++)
{
Entry_List_s * current_list = &lists[i];
free_icons(current_list);
free(current_list->entries);
free(current_list->icons_ids);
memset(current_list, 0, sizeof(Entry_List_s));
}
exit_thread();
@@ -142,7 +160,7 @@ void exit_function(bool power_pressed)
svcWaitSynchronization(audio->finished, U64_MAX);
}
free_lists();
svcCloseHandle(update_icons_handle);
svcCloseHandle(update_icons_mutex);
exit_screens();
exit_services();
@@ -170,8 +188,6 @@ static void start_thread(void)
static void load_lists(Entry_List_s * lists)
{
ssize_t texture_id_offset = TEXTURE_ICON;
free_lists();
for(int i = 0; i < MODE_AMOUNT; i++)
{
@@ -182,7 +198,6 @@ static void load_lists(Entry_List_s * lists)
loading_screen = INSTALL_LOADING_SPLASHES;
draw_install(loading_screen);
draw_install(loading_screen);
Entry_List_s * current_list = &lists[i];
current_list->mode = i;
@@ -200,11 +215,8 @@ static void load_lists(Entry_List_s * lists)
DEBUG("total: %i\n", current_list->entries_count);
current_list->texture_id_offset = texture_id_offset;
load_icons_first(current_list, false);
texture_id_offset += current_list->entries_loaded*ICONS_OFFSET_AMOUNT;
void (*install_check_function)(void*) = NULL;
if(i == MODE_THEMES)
install_check_function = themes_check_installed;
@@ -311,18 +323,25 @@ 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)
{
srand(time(NULL));
init_services();
init_screens();
svcCreateEvent(&update_icons_handle, RESET_ONESHOT);
svcCreateMutex(&update_icons_mutex, true);
static Entry_List_s * current_list = NULL;
void * iconLoadingThread_args_void[] = {
&current_list,
&update_icons_handle,
&update_icons_mutex,
};
iconLoadingThread_arg.thread_arg = iconLoadingThread_args_void;
iconLoadingThread_arg.run_thread = false;
@@ -342,11 +361,13 @@ int main(void)
bool qr_mode = false;
bool install_mode = false;
bool extra_mode = false;
C2D_Image preview = {0};
while(true)
while(aptMainLoop())
{
if(quit)
{
free_preview(preview);
exit_function(false);
return 0;
}
@@ -388,7 +409,7 @@ int main(void)
if(qr_mode) take_picture();
else if(preview_mode)
{
draw_preview(TEXTURE_PREVIEW, preview_offset);
draw_preview(preview, preview_offset);
}
else {
if(!iconLoadingThread_arg.run_thread)
@@ -398,15 +419,21 @@ int main(void)
}
else
{
svcSignalEvent(update_icons_handle);
svcSleepThread(5e6);
if(!released)
{
svcReleaseMutex(update_icons_mutex);
released = true;
}
svcWaitSynchronization(update_icons_mutex, U64_MAX);
}
draw_interface(current_list, instructions);
svcSleepThread(1e7);
released = false;
}
pp2d_end_draw();
end_frame();
if(kDown & KEY_START) quit = true;
@@ -449,18 +476,24 @@ int main(void)
continue;
}
else if(!qr_mode && kDown & KEY_Y) //toggle preview mode
else if(!qr_mode && kDown & KEY_Y && current_list->entries != NULL) //toggle preview mode
{
toggle_preview:
if(!preview_mode)
{
preview_mode = load_preview(*current_list, &preview_offset);
if(current_mode == MODE_THEMES)
preview_mode = load_preview(*current_list, &preview, &preview_offset);
if(preview_mode)
{
audio = calloc(1, sizeof(audio_s));
Result r = load_audio(current_list->entries[current_list->selected_entry], audio);
if (R_SUCCEEDED(r)) play_audio(audio);
else audio = NULL;
end_frame();
draw_preview(preview, preview_offset);
end_frame();
if(current_mode == MODE_THEMES && dspfirm)
{
audio = calloc(1, sizeof(audio_s));
Result r = load_audio(current_list->entries[current_list->selected_entry], audio);
if (R_SUCCEEDED(r)) play_audio(audio);
else audio = NULL;
}
}
}
else
@@ -710,22 +743,22 @@ int main(void)
else if(kHeld & KEY_CPAD_UP)
{
change_selected(current_list, -1);
svcSleepThread(FASTSCROLL_WAIT);
wait_scroll();
}
else if(kHeld & KEY_CPAD_DOWN)
{
change_selected(current_list, 1);
svcSleepThread(FASTSCROLL_WAIT);
wait_scroll();
}
else if(kHeld & KEY_CPAD_LEFT)
{
change_selected(current_list, -current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
wait_scroll();
}
else if(kHeld & KEY_CPAD_RIGHT)
{
change_selected(current_list, current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
wait_scroll();
}
// Movement using the touchscreen
@@ -820,6 +853,9 @@ int main(void)
}
}
free_preview(preview);
// aptSetHomeAllowed(true);
exit_function(true);
return 0;
}

View File

@@ -30,14 +30,14 @@
// Play a given audio struct
Result update_audio(audio_s *audio)
{
long size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read;
DEBUG("Audio Size: %ld\n", size);
u32 size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read;
DEBUG("<update_audio> 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
{
DEBUG("Attempting ov_read\n");
DEBUG("<update_audio> Attempting ov_read\n");
int bitstream;
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("ov_read successful\n");
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
DEBUG("<update_audio> ov_read successful\n");
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
} else // Error :(
{
DEBUG("Vorbis play error: %d\n", read);
DEBUG("<update_audio> Vorbis play error: %ld\n", read);
ndspChnReset(0);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
}
@@ -71,8 +71,8 @@ void thread_audio(void* data) {
}
free(audio->filebuf);
ov_clear(&audio->vf);
linearFree(audio->wave_buf[0].data_vaddr);
linearFree(audio->wave_buf[1].data_vaddr);
linearFree((void*)audio->wave_buf[0].data_vaddr);
linearFree((void*)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);
svcSignalEvent(audio->finished);
svcSleepThread(1e8);
@@ -82,4 +82,4 @@ void thread_audio(void* data) {
void play_audio(audio_s *audio) {
threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, true);
}
}

View File

@@ -1,14 +0,0 @@
# [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)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,789 +0,0 @@
/* 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);
}

View File

@@ -1,472 +0,0 @@
/* 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

@@ -1,39 +0,0 @@
; 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

@@ -29,26 +29,25 @@
#include "fs.h"
#include "unicode.h"
#include "music.h"
#include "pp2d/pp2d/pp2d.h"
static Instructions_s browser_instructions[MODE_AMOUNT] = {
{
.info_line = NULL,
.instructions = {
{
L"\uE000 Download theme",
L"\uE001 Go back"
"\uE000 Download theme",
"\uE001 Go back"
},
{
L"\uE002 Hold for more",
L"\uE003 Preview theme"
"\uE002 Hold for more",
"\uE003 Preview theme"
},
{
L"\uE004 Previous page",
L"\uE005 Next page"
"\uE004 Previous page",
"\uE005 Next page"
},
{
L"Exit",
"Exit",
NULL
}
}
@@ -57,19 +56,19 @@ static Instructions_s browser_instructions[MODE_AMOUNT] = {
.info_line = NULL,
.instructions = {
{
L"\uE000 Download splash",
L"\uE001 Go back"
"\uE000 Download splash",
"\uE001 Go back"
},
{
L"\uE002 Hold for more",
L"\uE003 Preview splash"
"\uE002 Hold for more",
"\uE003 Preview splash"
},
{
L"\uE004 Previous page",
L"\uE005 Next page"
"\uE004 Previous page",
"\uE005 Next page"
},
{
L"Exit",
"Exit",
NULL
}
}
@@ -77,34 +76,49 @@ static Instructions_s browser_instructions[MODE_AMOUNT] = {
};
static Instructions_s extra_instructions = {
.info_line = L"Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.info_line_color = COLOR_WHITE,
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.instructions = {
{
L"\uE079 Jump to page",
L"\uE07A Search tags"
"\uE079 Jump to page",
"\uE07A Search tags"
},
{
L"\uE07B Toggle splash/theme",
L"\uE07C Reload without cache"
"\uE07B Toggle splash/theme",
"\uE07C Reload without cache"
},
{
NULL,
NULL
},
{
L"Exit",
"Exit",
NULL
}
}
};
static void load_remote_smdh(Entry_s * entry, size_t textureID, bool ignore_cache)
static void free_icons(Entry_List_s * list)
{
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;
char * smdh_buf = NULL;
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
@@ -116,40 +130,21 @@ static void load_remote_smdh(Entry_s * entry, size_t textureID, bool ignore_cach
asprintf(&api_url, THEMEPLAZA_SMDH_FORMAT, entry->tp_download_id);
smdh_size = http_get(api_url, NULL, &smdh_buf, INSTALL_NONE);
free(api_url);
smdh = (Icon_s *)smdh_buf;
}
if(!smdh_size)
{
free(smdh_buf);
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;
smdh_buf = NULL;
}
memcpy(entry->name, smdh->name, 0x40*sizeof(u16));
memcpy(entry->desc, smdh->desc, 0x80*sizeof(u16));
memcpy(entry->author, smdh->author, 0x40*sizeof(u16));
Icon_s * smdh = (Icon_s *)smdh_buf;
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
u16 fallback_name[0x81] = {0};
utf8_to_utf16(fallback_name, (u8*)"No name", 0x80);
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]);
}
}
pp2d_free_texture(textureID);
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
free(image);
parse_smdh(smdh, entry, fallback_name);
C2D_Image * image = loadTextureIcon(smdh);
if(not_cached)
{
@@ -161,15 +156,17 @@ static void load_remote_smdh(Entry_s * entry, size_t textureID, bool ignore_cach
buf_to_file(smdh_size, fsMakePath(PATH_UTF16, path), ArchiveSD, 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)
{
free_icons(list);
list->entries_count = json_array_size(ids_array);
free(list->entries);
list->entries = calloc(list->entries_count, sizeof(Entry_s));
free(list->icons_ids);
list->icons_ids = calloc(list->entries_count, sizeof(ssize_t));
list->icons = calloc(list->entries_count, sizeof(C2D_Image*));
list->entries_loaded = list->entries_count;
size_t i = 0;
@@ -177,18 +174,15 @@ static void load_remote_entries(Entry_List_s * list, json_t *ids_array, bool ign
json_array_foreach(ids_array, i, id)
{
draw_loading_bar(i, list->entries_count, type);
size_t offset = i;
Entry_s * current_entry = &list->entries[offset];
Entry_s * current_entry = &list->entries[i];
current_entry->tp_download_id = json_integer_value(id);
size_t textureID = list->texture_id_offset + i;
char * entry_path = NULL;
asprintf(&entry_path, CACHE_PATH_FORMAT, current_entry->tp_download_id);
utf8_to_utf16(current_entry->path, (u8*)entry_path, 0x106);
free(entry_path);
load_remote_smdh(current_entry, textureID, ignore_cache);
list->icons_ids[offset] = textureID;
list->icons[i] = load_remote_smdh(current_entry, ignore_cache);
}
}
@@ -214,7 +208,6 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
if(json_len)
{
list->texture_id_offset = TEXTURE_REMOTE_ICONS;
list->tp_current_page = page;
list->mode = mode;
list->entry_size = entry_size[mode];
@@ -249,7 +242,7 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
}
static u16 previous_path_preview[0x106] = {0};
static bool load_remote_preview(Entry_s * entry, int * preview_offset)
static bool load_remote_preview(Entry_s * entry, C2D_Image* preview_image, int * preview_offset)
{
bool not_cached = true;
@@ -280,39 +273,9 @@ static bool load_remote_preview(Entry_s * entry, int * preview_offset)
return false;
}
bool ret = false;
u8 * image = NULL;
unsigned int width = 0, height = 0;
bool ret = load_preview_from_buffer(preview_png, preview_size, preview_image, preview_offset);
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)
if(ret && not_cached) // only save the preview if it loaded correctly - isn't corrupted
{
u16 path[0x107] = {0};
strucat(path, entry->path);
@@ -320,6 +283,7 @@ static bool load_remote_preview(Entry_s * entry, int * preview_offset)
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, preview_size);
buf_to_file(preview_size, fsMakePath(PATH_UTF16, path), ArchiveSD, preview_png);
}
free(preview_png);
return ret;
@@ -417,7 +381,7 @@ static void jump_menu(Entry_List_s * list)
sprintf(numbuf, "Which page do you want to jump to?");
swkbdSetHintText(&swkbd, numbuf);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cance", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Jump", true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->tp_page_count);
@@ -442,7 +406,7 @@ static void search_menu(Entry_List_s * list)
swkbdInit(&swkbd, SWKBD_TYPE_WESTERN, 2, max_chars);
swkbdSetHintText(&swkbd, "Which tags do you want to search for?");
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cance", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Search", true);
swkbdSetValidation(&swkbd, SWKBD_NOTBLANK, 0, max_chars);
@@ -498,16 +462,19 @@ bool themeplaza_browser(EntryMode mode)
Entry_List_s * current_list = &list;
current_list->tp_search = strdup("");
load_remote_list(current_list, 1, mode, false);
C2D_Image preview = {0};
bool extra_mode = false;
while(true)
while(aptMainLoop())
{
if(current_list->entries == NULL)
break;
if(preview_mode)
draw_preview(TEXTURE_REMOTE_PREVIEW, preview_offset);
{
draw_preview(preview, preview_offset);
}
else
{
Instructions_s instructions = browser_instructions[mode];
@@ -515,7 +482,7 @@ bool themeplaza_browser(EntryMode mode)
instructions = extra_instructions;
draw_grid_interface(current_list, instructions);
}
pp2d_end_draw();
end_frame();
hidScanInput();
u32 kDown = hidKeysDown();
@@ -577,19 +544,19 @@ bool themeplaza_browser(EntryMode mode)
toggle_preview:
if(!preview_mode)
{
preview_mode = load_remote_preview(current_entry, &preview_offset);
if(mode == MODE_THEMES)
preview_mode = load_remote_preview(current_entry, &preview, &preview_offset);
if(mode == MODE_THEMES && dspfirm)
{
load_remote_bgm(current_entry);
audio = calloc(1, sizeof(audio_s));
load_audio(*current_entry, audio);
play_audio(audio);
if(R_FAILED(load_audio(*current_entry, audio))) audio = NULL;
if(audio != NULL) play_audio(audio);
}
}
else
{
preview_mode = false;
if(mode == MODE_THEMES && audio)
if(mode == MODE_THEMES && audio != NULL)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
@@ -602,7 +569,7 @@ bool themeplaza_browser(EntryMode mode)
if(preview_mode)
{
preview_mode = false;
if(mode == MODE_THEMES && audio)
if(mode == MODE_THEMES && audio != NULL)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
@@ -741,8 +708,10 @@ bool themeplaza_browser(EntryMode mode)
}
}
free_preview(preview);
free_icons(current_list);
free(current_list->entries);
free(current_list->icons_ids);
free(current_list->tp_search);
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);
padded = calloc(BGM_MAX_SIZE, sizeof(char));
if(!current_theme->no_bgm_shuffle)
if(!current_theme->no_bgm_shuffle && music_size)
{
memcpy(padded, music, music_size);
free(music);
@@ -191,6 +191,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
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);
free(music);