44 Commits

Author SHA1 Message Date
LiquidFenrir
477d8c784b attempt 1
- get rid of all the calls to abs(delta), though that would probably already be optimized by gcc
- only not allow drawing at the same time as swapping if the swapping happens on visible icons
2018-10-19 19:18:40 +02:00
9e378b5059 Fix camera initialization on CTR cameras 2018-08-20 18:38:04 -04:00
312fbd5fbf Fixed QR download text 2018-08-19 20:46:55 -04:00
d3e1b746cd Various improvements to QR scanner
* Improve QR scanner initialization time
* Fix race condition with QR scanner and button spamming
* Don't show black screen before QR scanner has loaded
* Show loading message for QR scanner
2018-07-26 22:02:54 +03:00
TurdPooCharger
09de17729b Sharpen banner edges (#187)
Pixels along the parameter had their tranparency removed and colors solidified with the blue backdrop. Nearly invisible buffer edges of 1-2px were added for rendering compensation.

12x fold size increase due to *.png saved as uncompressed. Testing revealed color blurring with compressed *.png.

Information originally posted in issue #183.
2018-07-24 17:49:40 +03:00
9dc3f4c533 Change blue back to older blue color, add more contrast to red 2018-07-23 15:32:06 +03:00
0d1ab5ed95 Properly update installed splashes 2018-07-23 15:09:16 +03:00
Alex Taber
c40696e982 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
2018-07-22 21:22:23 +03:00
Helloman892
aa3a00cedc Patched quirc to finally rid ourselves of those compiler warnings 2018-05-16 19:20:27 +01:00
Helloman892
89c8a4fae2 amended README 2018-05-16 01:22:16 +01:00
5fd86bf7af Change TitleID to not conflict with games (Closes #160) 2018-05-15 17:12:53 -04:00
90d316bd06 Revert "Remove deprecated C3D functions. (#164)"
This reverts commit 71ecdd9aa5.
2018-05-14 13:06:08 -04:00
LiquidFenrir
6d65770840 fix #167
shouldnt have copy pasted blindly
2018-05-14 18:53:08 +02:00
Joel
71ecdd9aa5 Remove deprecated C3D functions. (#164)
* Fix home button being usable in the browser (closes 163)

* Replace deprecated C3D functions

This fix is just a placeholder until C2D arrives.
2018-05-13 13:54:43 -04:00
LiquidFenrir
db8f9e81e8 Fix home button being usable in the browser (closes #163) 2018-05-13 19:45:42 +02:00
83887bcb8e Add libvorbisdec to deps (closes #162) 2018-05-13 10:11:10 -04:00
fa37bcec99 Don't try and play mono ogg files 2018-05-13 09:03:16 -04:00
1c3e8809f6 Fix UB caused by using a free'd struct (actually fix QR code reader) 2018-05-11 18:01:35 -04:00
LiquidFenrir
3dad4e2a67 fix DEBUG calls 2018-05-11 15:59:17 +02:00
LiquidFenrir
403453c3fc add a loading bar (#159)
- when loading icons
- when downloading via QR
- when loading list in the browser
- when loading preview/BGM in the browser
- when downloading from the browser
2018-05-11 09:53:38 -04:00
fe58e2d938 Fixed QR code bug 2018-05-11 09:45:21 -04:00
LiquidFenrir
c28b794349 update remote.c to fix bgm preview there as well 2018-05-11 14:04:31 +02:00
d2b83659ea Fix race condition with music not being done playing 2018-05-11 07:42:05 -04:00
8189264908 Fix race condition in QR reader - slightly broken (doesn't run more than once) 2018-05-10 22:08:24 -04:00
5bb98a7fe2 Free everything when exiting theme preview 2018-05-10 21:30:41 -04:00
305c55e6b2 Disable home button 2018-05-10 17:17:54 -04:00
cb6f90adc7 Fix preview bug 2018-05-10 15:43:07 -04:00
7beb9b72db Change no-bgm install to make BGM silent 2018-05-10 13:09:27 -04:00
LiquidFenrir
8e43bb1a74 Shuffle without bgm (#157)
* add ability to shuffle without bgm
press B twice

* add license thingy for icons8, and thanks for their amazing icons
2018-05-10 12:52:14 -04:00
4dbc71489f Fix bug when bgm.ogg doesn't exist 2018-05-10 12:33:55 -04:00
LiquidFenrir
8022ed1682 add bgm preview to the themeplaza browser
and prevent some errors
2018-05-10 13:53:51 +02:00
5520ec2b2f Move load_audio into loading.c for consistency 2018-05-10 07:40:28 -04:00
f2aa7420df Add support for zip BGM preview 2018-05-10 06:49:03 -04:00
05b56b1aa1 Add BGM Preview Functionality
Some code adapted from Themely. Thanks to ErmanSayin
2018-05-09 22:26:39 -04:00
LiquidFenrir
cac2450fde fix copy-paste error 2018-05-09 00:22:34 +02:00
Alex Taber
1c24d08eef restore shuffle install speed back to roughly 30 seconds (#156) 2018-05-08 18:17:14 -04:00
Alex Taber
24b12023ca always 0-pad stuff (#155) 2018-05-08 16:39:22 -04:00
FrozenChen
9ccc705046 Make uncompressed logo smaller because of home menu bug (#150)
Closes #146
2018-04-15 21:17:31 -04:00
95d8808a73 Copyright Update
* Replaced contributors section in README with CONTRIBUTORS.md
* Updated copyright notice to include all contributors
* Updated SMDH
2018-04-10 16:09:10 -04:00
LiquidFenrir
6c2f09147f Themeplaza caching (#147)
* change fs to use a FS_Path argument for remake_file and buf_to_file

* more debug information when downloading

* browser caching v1: poc, works and is fast but not ideal

* add preview to cache

* this wasnt meant to be added now
2018-04-10 15:12:37 -04:00
LiquidFenrir
7b1d6f9860 implement L and R as extra menus instead of extra keys for X (#148)
L is now the sorting menu
R is unused
2018-04-09 19:28:37 -04:00
LiquidFenrir
6f7c2489d5 Different sorting modes (#143) (#145) 2018-04-04 22:04:45 +02:00
LiquidFenrir
38e70df17b Forgot to update the bottom screen when there's no entry (fix #144) 2018-04-04 11:40:38 +02:00
Helloman892
ad6117404f updated readme to fit: dkP updates, buildtools updates, library changes, lexigraphical errors 2018-04-03 23:48:47 +01:00
62 changed files with 1949 additions and 10019 deletions

1
.gitignore vendored
View File

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

18
CONTRIBUTORS.md Normal file
View File

@@ -0,0 +1,18 @@
# Main Contributors
* Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2))
* [@LiquidFenrir](https://github.com/LiquidFenrir)
* Dawid Eckert ([@daedreth](https://github.com/daedreth))
* Dylan G. ([@helloman892](https://github.com/helloman892))
* Nils P. ([@ZetaDesigns](https://github.com/ZetaDesigns))
* Matt Kenny ([@mattkenster](https://github.com/mattkenster))
# Minor Contributors
* Nic ([@Wizzrobes](https://github.com/Wizzrobes))
* [@saibotu](https://github.com/saibotu)
* Jeremy Postelnek ([@TurtleP](https://github.com/TurtleP))
* [@FrozenChen](https://github.com/FrozenChen)
* Luís Marques ([@luigoalma](https://github.com/luigoalma))
* [@uyuiyu](https://github.com/uyuiyu)
* Guillaume Gérard ([@GreatWizard](https://github.com/GreatWizard))
* Joel ([@joel16](https://github.com/joel16))
* [@thedax](https://github.com/thedax)

165
Makefile
View File

@@ -9,18 +9,43 @@ 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
APP_AUTHOR := astronautlevel and daedreth
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
@@ -38,12 +63,23 @@ LOGO := meta/logo.bin
# If left blank, makerom will use default values (0xff3ff and CTR-P-CTAP, respectively)
# Be careful if UNIQUE_ID is the same as other apps: it will overwrite the previously installed one
UNIQUE_ID := 0xAFEN
UNIQUE_ID := 0xBAFE0
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)\""
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 := -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

@@ -4,31 +4,34 @@ A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.\
To-do list here: https://trello.com/b/F1YSa1VK
# Dependencies
* zlib, jansson, and libarchive, which can be retrieved from the [3ds_portlibs](https://github.com/devkitPro/3ds_portlibs).
* devkitARM, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started).
* jansson, libvorbisidec, libpng, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702).
* 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).
* [makerom](https://github.com/profi200/Project_CTR) and [bannertool](https://github.com/Steveice10/buildtools), which can be retrieved from [SteveIce10's](https://github.com/Steveice10) buildtools repo. These must be added to your PATH.
* ~~[pp2d](https://github.com/BernardoGiordano/pp2d), which is included in the repo if you do a git clone --recursive.~~ Due to circumstances surrounding the privacy settings on the pp2d repo, the source files are now included directly within the repo.
* Git needs to be on your PATH, if building in a non-*nix environment.
# Building
First of all, make sure devkitPRO is properly installed and added to the PATH.
After that, open the directory you want to clone the repo into, and type: `git clone https://github.com/astronautlevel2/Anemone3DS/ --recursive`.
Instructions for installing zlib, jansson and libarchive can be found on the [3ds_portlibs repo](https://github.com/devkitPro/3ds_portlibs) (its easy, just run `make` and `make install-zlib`). After also adding [makerom](https://github.com/profi200/Project_CTR) and [bannertool](https://github.com/Steveice10/buildtools) to your PATH, just enter your directory and run `make`. All built files will be in `/out/`.
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 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
This project is licensed under the GNU GPLv3. See LICENSE.md for details. Additional terms 7b and 7c apply to this project.
# Credits
The following people contributed to Anemone3DS in some way. Without these people, Anemone3DS wouldn't exist, or wouldn't be as good as it is:
* [Daedreth](https://github.com/daedreth), who wrote the initial implementation of theme installation code and SMDH parsing.
* [LiquidFenrir](https://github.com/LiquidFenrir), who refactored a lot of my messy code and has been essential in development.
* [Kenn (mattkenster)](https://github.com/mattkenster), for designing the GUI, a number of sprites used in the application, and drawing the banner and icon.
The following people contributed to Anemone3DS in some way. Without these people, Anemone3DS wouldn't exist, or wouldn't be as good as it is: [CONTRIBUTORS.md](CONTRIBUTORS.md)
Special thanks go to these people who, while not directly contributing, helped immensely:
Most of the icons under `romfs` are from the site [icons8.com](https://icons8.com) and are licensed under the [CC-BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/3.0/)
Special thanks go to these people who, whilst not directly contributing, helped immensely:
* [Rinnegatamante](https://github.com/Rinnegatamante), whose code served as reference on theme installation.
* [SteveIce10](https://github.com/SteveIce10), whose QR code in FBI was essential.
* [BernardoGiordano](https://github.com/BernardoGiordano) for making pp2d, and being super responsive to feature requests and just general help.
* [yellows8](https://github.com/yellows8) for his home menu extdump tool, which was invaluable in debugging.
* the folks on #dev of Nintendo Homebrew, who helped with unicode shenanigans (especially [Stary2001](https://github.com/Stary2001), [Fenrir](https://github.com/FenrirWolf), and DanielKO).
* the maintainers for all used libraries, including ctrulib, zlib, citro3d, pp2d, quirc and minizip.
* the maintainers for all used libraries, including but not limited to ctrulib, zlib, citro3d, pp2d, and quirc.
* all the people who helped keep me going and motivated me to work. This includes, but is definitely not limited to:
+ The members of the [Nintendo Homebrew Discord](https://discord.gg/C29hYvh)

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

BIN
assets/shuffle_no_bgm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

BIN
assets/sort.png Normal file

Binary file not shown.

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

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -31,11 +31,13 @@
typedef struct {
u16 *camera_buffer;
u32 *texture_buffer;
C2D_Image image;
C3D_Tex *tex;
Handle mutex;
volatile bool finished;
volatile bool success;
Handle cancel;
Handle started;
bool capturing;
struct quirc* context;

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -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

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -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,34 +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_INSTALLED,
TEXTURE_PREVIEW_ICON,
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

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -31,6 +31,8 @@
#include "loading.h"
#include "colors.h"
#define MAX_LINES 10
typedef enum {
INSTALL_LOADING_THEMES,
INSTALL_LOADING_SPLASHES,
@@ -51,10 +53,68 @@ typedef enum {
INSTALL_LOADING_REMOTE_THEMES,
INSTALL_LOADING_REMOTE_SPLASHES,
INSTALL_LOADING_REMOTE_PREVIEW,
INSTALL_LOADING_REMOTE_BGM,
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,
@@ -75,23 +135,39 @@ 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);

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -40,7 +40,7 @@ u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf);
u32 zip_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char ** buf);
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf);
Result buf_to_file(u32 size, char *path, FS_Archive archive, char *buf);
void remake_file(char *path, FS_Archive archive, u32 size);
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf);
void remake_file(FS_Path path, FS_Archive archive, u32 size);
#endif

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -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,58 +56,57 @@ 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
}
}
};
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,
Instructions_s extra_instructions[3] = {
{
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to sort",
.instructions = {
{
L"\uE079 Jump in the list",
L"\uE07A Reload broken icons"
"\uE079 Sort by name",
"\uE07A Sort by author"
},
{
L"\uE07B Browse ThemePlaza",
"\uE07B Sort by filename",
NULL
},
{
@@ -115,10 +114,32 @@ Instructions_s extra_instructions = {
NULL
},
{
L"Exit",
"Exit",
NULL
}
}
},
{
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.instructions = {
{
"\uE079 Jump in the list",
"\uE07A Reload broken icons"
},
{
"\uE07B Browse ThemePlaza",
NULL,
},
{
"\uE004 Sorting menu",
NULL
},
{
"Exit",
NULL
}
}
},
};
#endif

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -28,6 +28,7 @@
#define LOADING_H
#include "common.h"
#include "music.h"
#include <jansson.h>
enum ICON_IDS_OFFSET {
@@ -38,6 +39,14 @@ enum ICON_IDS_OFFSET {
ICONS_OFFSET_AMOUNT,
};
typedef enum {
SORT_NONE,
SORT_NAME,
SORT_AUTHOR,
SORT_PATH,
} SortMode;
typedef struct {
u8 _padding1[4 + 2 + 2];
@@ -62,6 +71,7 @@ typedef struct {
bool is_zip;
bool in_shuffle;
bool no_bgm_shuffle;
bool installed;
json_int_t tp_download_id;
@@ -71,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;
@@ -88,6 +97,8 @@ typedef struct {
int entries_loaded;
int entry_size;
SortMode current_sort;
json_int_t tp_current_page;
json_int_t tp_page_count;
char * tp_search;
@@ -98,9 +109,19 @@ 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);
void load_icons_thread(void * void_arg);

54
include/music.h Normal file
View File

@@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef MUSIC_H
#define MUSIC_H
#include "common.h"
#include "fs.h"
#include "unicode.h"
#include <tremor/ivorbisfile.h>
#include <tremor/ivorbiscodec.h>
#define BUF_TO_READ 40960 // How much data should be buffered at a time
typedef struct {
OggVorbis_File vf;
ndspWaveBuf wave_buf[2];
float mix[12];
u8 buf_pos;
long data_read;
char *filebuf;
u32 filesize;
volatile bool stop;
Handle finished;
} audio_s;
void play_audio(audio_s *);
#endif

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -28,6 +28,7 @@
#define REMOTE_H
#include "common.h"
#include "draw.h"
#define THEMEPLAZA_BASE_URL "https://themeplaza.eu"
#define THEMEPLAZA_API_URL "/api/anemone/v1"
@@ -41,17 +42,15 @@
#define THEMEPLAZA_JSON_ERROR_MESSAGE_NOT_FOUND "No items found"
#define THEMEPLAZA_ENTRY_FORMAT THEMEPLAZA_BASE_API_URL "/query?item_id=%" JSON_INTEGER_FORMAT
#define THEMEPLAZA_JSON_ENTRY_NAME "title"
#define THEMEPLAZA_JSON_ENTRY_DESC "description"
#define THEMEPLAZA_JSON_ENTRY_AUTH "author"
#define THEMEPLAZA_DOWNLOAD_FORMAT THEMEPLAZA_BASE_URL "/download/%" JSON_INTEGER_FORMAT
#define THEMEPLAZA_PREVIEW_FORMAT THEMEPLAZA_DOWNLOAD_FORMAT "/preview"
#define THEMEPLAZA_BGM_FORMAT THEMEPLAZA_DOWNLOAD_FORMAT "/bgm"
#define THEMEPLAZA_ICON_FORMAT THEMEPLAZA_DOWNLOAD_FORMAT "/preview/icon"
#define THEMEPLAZA_SMDH_FORMAT THEMEPLAZA_DOWNLOAD_FORMAT "/smdh"
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
bool themeplaza_browser(EntryMode mode);
u32 http_get(const char *url, char ** filename, char ** buf);
u32 http_get(const char *url, char ** filename, char ** buf, InstallType install_type);
#endif

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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

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 : 0xAFEN
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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -27,7 +27,6 @@
#include "camera.h"
#include "quirc/quirc.h"
#include "pp2d/pp2d/pp2d.h"
#include "draw.h"
#include "fs.h"
@@ -37,24 +36,18 @@
#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");
svcSignalEvent(data->cancel);
while(!data->finished)
svcSleepThread(1000000);
svcCloseHandle(data->cancel);
data->capturing = false;
free(data->camera_buffer);
free(data->texture_buffer);
free(data->tex);
quirc_destroy(data->context);
free(data);
}
void capture_cam_thread(void *arg)
@@ -80,6 +73,7 @@ void capture_cam_thread(void *arg)
CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), (s16) transferUnit);
CAMU_StartCapture(PORT_CAM1);
svcSignalEvent(data->started);
bool cancel = false;
while (!cancel)
{
@@ -87,6 +81,7 @@ void capture_cam_thread(void *arg)
svcWaitSynchronizationN(&index, events, 3, false, U64_MAX);
switch(index) {
case 0:
DEBUG("Cancel event received\n");
cancel = true;
break;
case 1:
@@ -131,6 +126,40 @@ 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;
@@ -139,6 +168,12 @@ bool start_capture_cam(qr_data *data)
svcCreateMutex(&data->mutex, false);
if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL)
return false;
svcWaitSynchronization(data->started, U64_MAX);
if(threadCreate(update_ui, data, 0x10000, 0x1A, 1, true) == NULL)
{
exit_qr(data);
return false;
}
return true;
}
@@ -164,31 +199,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)
{
@@ -202,7 +222,7 @@ void update_qr(qr_data *data)
draw_install(INSTALL_DOWNLOAD);
char * zip_buf = NULL;
char * filename = NULL;
u32 zip_size = http_get((char*)scan_data.payload, &filename, &zip_buf);
u32 zip_size = http_get((char*)scan_data.payload, &filename, &zip_buf, INSTALL_DOWNLOAD);
if(zip_size != 0)
{
@@ -255,8 +275,8 @@ void update_qr(qr_data *data)
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
remake_file(path_to_file, ArchiveSD, zip_size);
buf_to_file(zip_size, path_to_file, ArchiveSD, zip_buf);
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
data->success = true;
}
else
@@ -286,14 +306,22 @@ bool init_qr(void)
qr_data *data = calloc(1, sizeof(qr_data));
data->capturing = false;
data->finished = false;
svcCreateEvent(&data->started, RESET_STICKY);
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;
free(data);
return (bool)data->success;
return 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(12, 58, 111, 255);
colors[COLOR_WHITE] = C2D_Color32(255, 255, 255, 255);
colors[COLOR_CURSOR] = C2D_Color32(200, 200, 200, 255);
colors[COLOR_BLACK] = C2D_Color32(0, 0, 0, 255);
colors[COLOR_RED] = C2D_Color32(229, 66, 66, 255);
colors[COLOR_YELLOW] = C2D_Color32(239, 220, 11, 255);
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -28,128 +28,283 @@
#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_INSTALLED, "romfs:/installed.png");
pp2d_load_texture_png(TEXTURE_DOWNLOAD, "romfs:/download.png");
pp2d_load_texture_png(TEXTURE_PREVIEW_ICON, "romfs:/preview.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, "Downloading...");
C2D_TextParse(&text[TEXT_INSTALL_CHECKING_DOWNLOAD], staticBuf, "Checking downloaded file...");
C2D_TextParse(&text[TEXT_INSTALL_ENTRY_DELETE], staticBuf, "Deleting from SD...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_THEMES], staticBuf, "Downloading theme list, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_SPLASHES], staticBuf, "Downloading splash list, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_PREVIEW], staticBuf, "Downloading preview, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, "Downloading BGM, please wait...");
for(int i = 0; i < TEXT_AMOUNT; i++)
C2D_TextOptimize(&text[i]);
}
void exit_screens(void)
{
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:
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.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();
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;
}
break;
}
}
bool draw_confirm(const char* conf_msg, Entry_List_s* list)
@@ -158,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();
@@ -172,76 +327,48 @@ 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)
{
if(type != INSTALL_NONE)
{
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);
}
}
void draw_install(InstallType type)
{
draw_base_interface();
switch(type)
{
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_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;
}
pp2d_end_draw();
draw_install_handler(type);
end_frame();
}
void draw_loading_bar(u32 current, u32 max, InstallType type)
{
draw_base_interface();
draw_install_handler(type);
set_screen(bottom);
double percent = 100*((double)current/(double)max);
u32 width = (u32)percent;
width *= 2;
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,
@@ -252,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)
@@ -299,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);
@@ -312,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++)
{
@@ -330,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;
@@ -345,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)
@@ -380,38 +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_EXIT, 320-120, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_DOWNLOAD, 320-96, 0, COLOR_WHITE);
for(int i = 0; i < MODE_AMOUNT; i++)
pp2d_draw_textf(320-(24*(i+1))+2.5, -3, 1, 1, COLOR_WHITE, "%c", mode_string[i][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;
}
@@ -422,29 +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_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++)
{
@@ -452,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;
@@ -461,44 +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)
{
C2D_SpriteSetPos(&sprite_shuffle_no_bgm, 320-24-4, vertical_offset);
C2D_DrawSpriteTinted(&sprite_shuffle_no_bgm, &tint);
}
else if(current_entry->in_shuffle)
{
C2D_SpriteSetPos(&sprite_shuffle, 320-24-4, vertical_offset);
C2D_DrawSpriteTinted(&sprite_shuffle, &tint);
}
if(current_entry->in_shuffle)
pp2d_draw_texture_blend(TEXTURE_SHUFFLE, 320-24-4, vertical_offset, font_color);
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

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -68,6 +68,9 @@ Result open_archives(void)
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Themes"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Splashes"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE "/cache"), FS_ATTRIBUTE_DIRECTORY);
u32 homeMenuPath[3] = {MEDIATYPE_SD, archive2, 0};
home.type = PATH_BINARY;
@@ -178,23 +181,26 @@ u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf)
return zip_to_buf(a, file_name, buf);
}
Result buf_to_file(u32 size, char *path, FS_Archive archive, char *buf)
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf)
{
Handle handle;
Result res = 0;
if (R_FAILED(res = FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_ASCII, path), FS_OPEN_WRITE, 0))) return res;
if (R_FAILED(res = FSUSER_OpenFile(&handle, archive, path, FS_OPEN_WRITE, 0))) return res;
if (R_FAILED(res = FSFILE_Write(handle, NULL, 0, buf, size, FS_WRITE_FLUSH))) return res;
if (R_FAILED(res = FSFILE_Close(handle))) return res;
return 0;
}
void remake_file(char *path, FS_Archive archive, u32 size)
void remake_file(FS_Path path, FS_Archive archive, u32 size)
{
Handle handle;
if (R_SUCCEEDED(FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0)))
if (R_SUCCEEDED(FSUSER_OpenFile(&handle, archive, path, FS_OPEN_READ, 0)))
{
FSFILE_Close(handle);
FSUSER_DeleteFile(archive, fsMakePath(PATH_ASCII, path));
FSUSER_DeleteFile(archive, path);
}
FSUSER_CreateFile(archive, fsMakePath(PATH_ASCII, path), 0, size);
FSUSER_CreateFile(archive, path, 0, size);
char *buf = calloc(size, 1);
buf_to_file(size, path, archive, buf);
free(buf);
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -25,11 +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)
@@ -54,70 +56,118 @@ 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));
for(u32 x = 0; x < width; x++)
{
for(u32 y = 0; y < height; y++)
{
unsigned int dest_pixel = (x + y*width);
unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
image[dest_pixel] = RGB565_TO_ABGR8(smdh->big_icon[source_pixel]);
}
}
free(info_buffer);
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
free(image);
parse_smdh(smdh, entry, fallback_name);
}
static int compare_entries(const void * a, const void * b)
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;
Icon_s * smdh = (Icon_s *)info_buffer;
return loadTextureIcon(smdh);
}
typedef int (*sort_comparator)(const void *, const void *);
static int compare_entries_by_name(const void * a, const void * b)
{
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->name, entry_b->name, 0x40*sizeof(u16));
}
static void sort_list(Entry_List_s * list)
static int compare_entries_by_author(const void * a, const void * b)
{
if(list->entries != NULL)
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->author, entry_b->author, 0x40*sizeof(u16));
}
static int compare_entries_by_filename(const void * a, const void * b)
{
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->path, entry_b->path, 0x106*sizeof(u16));
}
static void sort_list(Entry_List_s * list, sort_comparator compare_entries)
{
if(list->entries != NULL && list->entries != NULL)
qsort(list->entries, list->entries_count, sizeof(Entry_s), compare_entries); //alphabet sort
}
void sort_by_name(Entry_List_s * list)
{
sort_list(list, compare_entries_by_name);
list->current_sort = SORT_NAME;
}
void sort_by_author(Entry_List_s * list)
{
sort_list(list, compare_entries_by_author);
list->current_sort = SORT_AUTHOR;
}
void sort_by_filename(Entry_List_s * list)
{
sort_list(list, compare_entries_by_filename);
list->current_sort = SORT_PATH;
}
Result load_entries(const char * loading_path, Entry_List_s * list)
{
Handle dir_handle;
@@ -160,13 +210,11 @@ 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);
sort_list(list);
return res;
}
@@ -193,13 +241,15 @@ 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);
int offset = i;
if(offset < 0)
offset += list->entries_count;
@@ -207,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);
@@ -281,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))
@@ -297,39 +345,38 @@ static void load_icons(Entry_List_s * current_list)
if(abs(delta) >= current_list->entries_count - current_list->entries_loaded*(ICONS_OFFSET_AMOUNT-1))
delta = -SIGN(delta) * (current_list->entries_count - abs(delta));
int absdelta = abs(delta);
int starti = current_list->scroll;
int endi = starti + abs(delta);
int endi = starti + absdelta;
if(delta < 0)
{
endi -= abs(delta) + 1;
starti += abs(delta) - 1;
endi -= absdelta + 1;
starti += absdelta - 1;
}
int ctr = 0;
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *));
ssize_t * ids = calloc(abs(delta), sizeof(ssize_t));
Entry_s ** entries = calloc(absdelta, sizeof(Entry_s *));
int * indexes = calloc(absdelta, 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
}
@@ -340,43 +387,190 @@ 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
static int cutoff[] = {
0, // absdelta = 1
0, // absdelta = 2
0, // absdelta = 3
0, // absdelta = 4
1, // absdelta = 5, 1 visible to be replaced before allowing drawing
2, // absdelta = 6, 2 visible to be replaced before allowing drawing
3, // absdelta = 7, 3 visible to be replaced before allowing drawing
};
int actual_cutoff = cutoff[absdelta-1];
if(actual_cutoff == 0)
{
svcReleaseMutex(mutex);
released = true;
svcSleepThread(1e7);
}
if(delta > 0)
{
starti = 0;
endi = absdelta;
}
else
{
starti = absdelta-1;
endi = -1;
}
int change = SIGN(delta);
#undef SIGN
svcSleepThread(1e6);
for(int i = 0; i < abs(delta); i++)
for(int i = starti; i != endi; i += change)
{
Entry_s current_entry = *entries[i];
ssize_t id = ids[i];
load_smdh_icon(current_entry, id);
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 == actual_cutoff)
{
svcReleaseMutex(mutex);
released = true;
svcSleepThread(1e7);
}
}
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;
@@ -394,38 +588,79 @@ bool load_preview(Entry_List_s list, int * preview_offset)
return false;
}
bool ret = false;
u8 * image = NULL;
unsigned int width = 0, height = 0;
bool ret = load_preview_from_buffer(preview_buffer, size, preview_image, preview_offset);
free(preview_buffer);
if((lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size)) == 0) // no error
if(ret)
{
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);
free(preview_buffer);
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("<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("<load_audio> Filesize: %ld\n", audio->filesize);
if(file != NULL)
{
int e = ov_open(file, &audio->vf, NULL, 0);
if (e < 0)
{
DEBUG("<load_audio> Vorbis: %d\n", e);
free(audio->filebuf);
free(audio);
fclose(file);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
}
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("<load_audio> Using stereo\n");
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); // 2 channels == Stereo
} else {
DEBUG("<load_audio> Invalid number of channels\n");
free(audio->filebuf);
free(audio);
fclose(file);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
}
audio->wave_buf[0].nsamples = audio->wave_buf[1].nsamples = vi->rate / 4; // 4 bytes per sample, samples = rate (bytes) / 4
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("<load_audio> Success!\n");
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
} else {
free(audio->filebuf);
free(audio);
DEBUG("<load_audio> fmemopen failed!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
}
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -30,18 +30,21 @@
#include "splashes.h"
#include "draw.h"
#include "camera.h"
#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};
@@ -75,8 +78,10 @@ static void init_services(void)
cfguInit();
ptmuInit();
acInit();
dspfirm = !ndspInit();
APT_GetAppCpuTimeLimit(&old_time_limit);
APT_SetAppCpuTimeLimit(30);
// aptSetHomeAllowed(false);
httpcInit(0);
archive_result = open_archives();
if(envIsHomebrew())
@@ -95,6 +100,7 @@ static void exit_services(void)
if (old_time_limit != UINT32_MAX) APT_SetAppCpuTimeLimit(old_time_limit);
httpcExit();
acExit();
ndspExit();
}
static void stop_install_check(void)
@@ -111,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();
@@ -132,8 +154,13 @@ void free_lists(void)
void exit_function(bool power_pressed)
{
if(audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
}
free_lists();
svcCloseHandle(update_icons_handle);
svcCloseHandle(update_icons_mutex);
exit_screens();
exit_services();
@@ -161,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++)
{
@@ -173,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;
@@ -187,13 +211,12 @@ static void load_lists(Entry_List_s * lists)
if(current_list->entries_count > current_list->entries_loaded*ICONS_OFFSET_AMOUNT)
iconLoadingThread_arg.run_thread = true;
sort_by_name(current_list);
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;
@@ -280,9 +303,31 @@ static void change_selected(Entry_List_s * list, int change_value)
static void toggle_shuffle(Entry_List_s * list)
{
Entry_s * current_entry = &list->entries[list->selected_entry];
if(current_entry->in_shuffle) list->shuffle_count--;
else list->shuffle_count++;
current_entry->in_shuffle = !current_entry->in_shuffle;
if(current_entry->in_shuffle)
{
if(current_entry->no_bgm_shuffle)
{
current_entry->in_shuffle = false;
current_entry->no_bgm_shuffle = false;
list->shuffle_count--;
}
else
{
current_entry->no_bgm_shuffle = true;
}
}
else
{
current_entry->in_shuffle = true;
list->shuffle_count++;
}
}
static inline void wait_scroll(void)
{
released = true;
svcReleaseMutex(update_icons_mutex);
svcSleepThread(FASTSCROLL_WAIT);
}
int main(void)
@@ -291,12 +336,12 @@ int main(void)
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;
@@ -316,11 +361,13 @@ int main(void)
bool qr_mode = false;
bool install_mode = false;
bool extra_mode = false;
C2D_Image preview = {0};
while(aptMainLoop())
{
if(quit)
{
free_preview(preview);
exit_function(false);
return 0;
}
@@ -345,10 +392,25 @@ int main(void)
if(install_mode)
instructions = install_instructions;
if(extra_mode)
instructions = extra_instructions;
{
int index = 1;
bool key_l = (kDown | kHeld) & KEY_L;
bool key_r = (kDown | kHeld) & KEY_R;
if(key_l ^ key_r)
{
if(key_l)
index = 0;
// else if(key_r) // uncomment when we use the right menu. we don't for now
// index = 2;
}
instructions = extra_instructions[index];
}
if(qr_mode) take_picture();
else if(preview_mode) draw_preview(TEXTURE_PREVIEW, preview_offset);
else if(preview_mode)
{
draw_preview(preview, preview_offset);
}
else {
if(!iconLoadingThread_arg.run_thread)
{
@@ -357,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;
@@ -381,6 +449,9 @@ int main(void)
else if(!qr_mode && !preview_mode && kDown & KEY_R) //toggle QR mode
{
enable_qr:
draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], "Loading QR Scanner...");
end_frame();
if(R_SUCCEEDED(camInit()))
{
camExit();
@@ -408,18 +479,47 @@ 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);
{
preview_mode = load_preview(*current_list, &preview, &preview_offset);
if(preview_mode)
{
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
{
preview_mode = false;
if(current_mode == MODE_THEMES && audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
}
continue;
}
else if(preview_mode && kDown & (KEY_B | KEY_TOUCH))
{
preview_mode = false;
if(current_mode == MODE_THEMES && audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
continue;
}
}
@@ -524,6 +624,10 @@ int main(void)
if(kUp & KEY_X)
extra_mode = false;
if(!extra_mode)
{
bool key_l = (kDown | kHeld) & KEY_L;
bool key_r = (kDown | kHeld) & KEY_R;
if(!(key_l ^ key_r))
{
if((kDown | kHeld) & KEY_DLEFT)
{
@@ -539,16 +643,34 @@ int main(void)
jump:
jump_menu(current_list);
}
else if((kDown | kHeld) & KEY_DRIGHT)
{
}
else if((kDown | kHeld) & KEY_DDOWN)
{
load_icons_first(current_list, false);
}
}
else if(key_l)
{
if((kDown | kHeld) & KEY_DLEFT)
{
sort_path:
sort_by_filename(current_list);
load_icons_first(current_list, false);
}
else if(((kDown | kHeld)) & KEY_DUP)
{
sort_name:
sort_by_name(current_list);
load_icons_first(current_list, false);
}
else if(((kDown | kHeld)) & KEY_DDOWN)
{
sort_author:
sort_by_author(current_list);
load_icons_first(current_list, false);
}
}
}
continue;
}
@@ -564,6 +686,14 @@ int main(void)
case MODE_SPLASHES:
draw_install(INSTALL_SPLASH);
splash_install(*current_entry);
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * splash = &current_list->entries[i];
if(splash == current_entry)
splash->installed = true;
else
splash->installed = false;
}
break;
default:
break;
@@ -624,22 +754,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
@@ -665,6 +795,23 @@ int main(void)
{
change_selected(current_list, -current_list->entries_per_screen_v);
}
else if(BETWEEN(320-144, x, 320-120))
{
switch(current_list->current_sort)
{
case SORT_NAME:
goto sort_author;
break;
case SORT_AUTHOR:
goto sort_path;
break;
case SORT_PATH:
goto sort_name;
break;
default:
break;
}
}
else if(BETWEEN(320-120, x, 320-96))
{
goto enable_qr;
@@ -717,6 +864,9 @@ int main(void)
}
}
free_preview(preview);
// aptSetHomeAllowed(true);
exit_function(true);
return 0;
}

85
source/music.c Normal file
View File

@@ -0,0 +1,85 @@
/*
* 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 "music.h"
#include "loading.h"
// Play a given audio struct
Result update_audio(audio_s *audio)
{
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("<update_audio> Attempting ov_read\n");
int bitstream;
u32 read = ov_read(&audio->vf, (char*)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, &bitstream); // read 1 vorbis packet into wave buffer
DEBUG("<update_audio> ov_read successful\n");
if (read <= 0) // EoF or error
{
ov_clear(&audio->vf);
if (read == 0) // EoF
{
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("<update_audio> Vorbis play error: %ld\n", read);
ndspChnReset(0);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
}
} else
{
audio->data_read += read;
if (read == size) {
audio->data_read = 0;
ndspChnWaveBufAdd(0, &audio->wave_buf[audio->buf_pos]); // Add buffer to ndsp
audio->buf_pos = 1 - audio->buf_pos; // switch to other buffer to load and prepare it while the current one is playing
}
}
}
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
}
void thread_audio(void* data) {
audio_s *audio = (audio_s*)data;
while(!audio->stop) {
update_audio(audio);
}
free(audio->filebuf);
ov_clear(&audio->vf);
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);
svcCloseHandle(audio->finished);
free(audio);
}
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

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -26,28 +26,28 @@
#include "remote.h"
#include "loading.h"
#include "draw.h"
#include "fs.h"
#include "pp2d/pp2d/pp2d.h"
#include "unicode.h"
#include "music.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
}
}
@@ -56,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
}
}
@@ -76,106 +76,117 @@ 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",
NULL
"\uE07B Toggle splash/theme",
"\uE07C Reload without cache"
},
{
NULL,
NULL
},
{
L"Exit",
"Exit",
NULL
}
}
};
static void load_remote_entry(Entry_s * entry)
static void free_icons(Entry_List_s * list)
{
char * entry_json = NULL;
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);
not_cached = !smdh_size || ignore_cache; // if the size is 0, the file wasn't there
if(not_cached)
{
free(smdh_buf);
smdh_buf = NULL;
char * api_url = NULL;
asprintf(&api_url, THEMEPLAZA_ENTRY_FORMAT, entry->tp_download_id);
u32 json_len = http_get(api_url, NULL, &entry_json);
asprintf(&api_url, THEMEPLAZA_SMDH_FORMAT, entry->tp_download_id);
smdh_size = http_get(api_url, NULL, &smdh_buf, INSTALL_NONE);
free(api_url);
}
if(json_len)
if(!smdh_size)
{
json_error_t error;
json_t *root = json_loadb(entry_json, json_len, 0, &error);
if(root)
{
const char *key;
json_t *value;
json_object_foreach(root, key, value)
{
if(json_is_string(value))
{
if(!strcmp(key, THEMEPLAZA_JSON_ENTRY_NAME))
{
utf8_to_utf16(entry->name, (u8*)json_string_value(value), 0x40);
free(smdh_buf);
smdh_buf = NULL;
}
}
else if(!strcmp(key, THEMEPLAZA_JSON_ENTRY_DESC))
utf8_to_utf16(entry->desc, (u8*)json_string_value(value), 0x80);
else if(!strcmp(key, THEMEPLAZA_JSON_ENTRY_AUTH))
utf8_to_utf16(entry->author, (u8*)json_string_value(value), 0x40);
}
}
}
else
DEBUG("json error on line %d: %s\n", error.line, error.text);
Icon_s * smdh = (Icon_s *)smdh_buf;
json_decref(root);
u16 fallback_name[0x81] = {0};
utf8_to_utf16(fallback_name, (u8*)"No name", 0x80);
parse_smdh(smdh, entry, fallback_name);
C2D_Image * image = loadTextureIcon(smdh);
if(not_cached)
{
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, entry->path), FS_ATTRIBUTE_DIRECTORY);
u16 path[0x107] = {0};
strucat(path, entry->path);
struacat(path, "/info.smdh");
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_size);
buf_to_file(smdh_size, fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_buf);
}
free(entry_json);
free(smdh_buf);
return image;
}
static void load_remote_icon(size_t textureID, json_int_t id)
{
char * icon_data = NULL;
char * icon_url = NULL;
asprintf(&icon_url, THEMEPLAZA_ICON_FORMAT, id);
u32 icon_size = http_get(icon_url, NULL, &icon_data);
free(icon_url);
pp2d_free_texture(textureID);
pp2d_load_texture_png_memory(textureID, icon_data, icon_size);
free(icon_data);
}
static void load_remote_entries(Entry_List_s * list, json_t *ids_array)
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;
json_t * id = NULL;
json_array_foreach(ids_array, i, id)
{
size_t offset = i;
Entry_s * current_entry = &list->entries[offset];
draw_loading_bar(i, list->entries_count, type);
Entry_s * current_entry = &list->entries[i];
current_entry->tp_download_id = json_integer_value(id);
load_remote_entry(current_entry);
size_t textureID = list->texture_id_offset + i;
load_remote_icon(textureID, current_entry->tp_download_id);
list->icons_ids[offset] = textureID;
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);
list->icons[i] = load_remote_smdh(current_entry, ignore_cache);
}
}
static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mode)
static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mode, bool ignore_cache)
{
if(page > list->tp_page_count)
page = 1;
@@ -192,12 +203,11 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
char * page_json = NULL;
char * api_url = NULL;
asprintf(&api_url, THEMEPLAZA_PAGE_FORMAT, page, mode+1, list->tp_search);
u32 json_len = http_get(api_url, NULL, &page_json);
u32 json_len = http_get(api_url, NULL, &page_json, INSTALL_NONE);
free(api_url);
if(json_len)
{
list->texture_id_offset = TEXTURE_REMOTE_ICONS;
list->tp_current_page = page;
list->mode = mode;
list->entry_size = entry_size[mode];
@@ -215,7 +225,7 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
if(json_is_integer(value) && !strcmp(key, THEMEPLAZA_JSON_PAGE_COUNT))
list->tp_page_count = json_integer_value(value);
else if(json_is_array(value) && !strcmp(key, THEMEPLAZA_JSON_PAGE_IDS))
load_remote_entries(list, value);
load_remote_entries(list, value, ignore_cache, loading_screen);
else if(json_is_string(value) && !strcmp(key, THEMEPLAZA_JSON_ERROR_MESSAGE) && !strcmp(json_string_value(value), THEMEPLAZA_JSON_ERROR_MESSAGE_NOT_FOUND))
throw_error("No results for this search.", ERROR_LEVEL_WARNING);
}
@@ -231,22 +241,31 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
free(page_json);
}
static char previous_preview_url[0x100] = {0};
static bool load_remote_preview(Entry_List_s list, int * preview_offset)
static u16 previous_path_preview[0x106] = {0};
static bool load_remote_preview(Entry_s * entry, C2D_Image* preview_image, int * preview_offset)
{
Entry_s entry = list.entries[list.selected_entry];
bool not_cached = true;
if(!memcmp(&previous_path_preview, entry->path, 0x106*sizeof(u16))) return true;
char * preview_png = NULL;
u32 preview_size = load_data("/preview.png", *entry, &preview_png);
not_cached = !preview_size;
if(not_cached)
{
free(preview_png);
preview_png = NULL;
char * preview_url = NULL;
asprintf(&preview_url, THEMEPLAZA_PREVIEW_FORMAT, entry.tp_download_id);
if(!strncmp(previous_preview_url, preview_url, 0x100))
{
free(preview_url);
return true;
}
asprintf(&preview_url, THEMEPLAZA_PREVIEW_FORMAT, entry->tp_download_id);
draw_install(INSTALL_LOADING_REMOTE_PREVIEW);
char * preview_png = NULL;
u32 preview_size = http_get(preview_url, NULL, &preview_png);
preview_size = http_get(preview_url, NULL, &preview_png, INSTALL_LOADING_REMOTE_PREVIEW);
free(preview_url);
}
if(!preview_size)
{
@@ -254,43 +273,55 @@ static bool load_remote_preview(Entry_List_s list, 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
if(ret && not_cached) // only save the preview if it loaded correctly - isn't corrupted
{
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
}
u16 path[0x107] = {0};
strucat(path, entry->path);
struacat(path, "/preview.png");
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, preview_size);
buf_to_file(preview_size, fsMakePath(PATH_UTF16, path), ArchiveSD, preview_png);
}
// mark the new preview as loaded for optimisation
strncpy(previous_preview_url, preview_url, 0x100);
// 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);
free(preview_url);
free(preview_png);
return ret;
}
static u16 previous_path_bgm[0x106] = {0};
static void load_remote_bgm(Entry_s * entry)
{
if(!memcmp(&previous_path_bgm, entry->path, 0x106*sizeof(u16))) return;
char * bgm_ogg = NULL;
u32 bgm_size = load_data("/bgm.ogg", *entry, &bgm_ogg);
if(!bgm_size)
{
free(bgm_ogg);
bgm_ogg = NULL;
char * bgm_url = NULL;
asprintf(&bgm_url, THEMEPLAZA_BGM_FORMAT, entry->tp_download_id);
draw_install(INSTALL_LOADING_REMOTE_BGM);
bgm_size = http_get(bgm_url, NULL, &bgm_ogg, INSTALL_LOADING_REMOTE_BGM);
free(bgm_url);
u16 path[0x107] = {0};
strucat(path, entry->path);
struacat(path, "/bgm.ogg");
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, bgm_size);
buf_to_file(bgm_size, fsMakePath(PATH_UTF16, path), ArchiveSD, bgm_ogg);
memcpy(&previous_path_bgm, entry->path, 0x106*sizeof(u16));
}
free(bgm_ogg);
}
static void download_remote_entry(Entry_s * entry, EntryMode mode)
{
char * download_url = NULL;
@@ -299,7 +330,7 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
char * zip_buf = NULL;
char * filename = NULL;
draw_install(INSTALL_DOWNLOAD);
u32 zip_size = http_get(download_url, &filename, &zip_buf);
u32 zip_size = http_get(download_url, &filename, &zip_buf, INSTALL_DOWNLOAD);
free(download_url);
char path_to_file[0x107] = {0};
@@ -311,8 +342,8 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
strcat(path_to_file, ".zip");
DEBUG("Saving to sd: %s\n", path_to_file);
remake_file(path_to_file, ArchiveSD, zip_size);
buf_to_file(zip_size, path_to_file, ArchiveSD, zip_buf);
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
free(zip_buf);
}
@@ -350,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);
@@ -361,7 +392,7 @@ static void jump_menu(Entry_List_s * list)
{
json_int_t newpage = (json_int_t)atoi(numbuf);
if(newpage != list->tp_current_page)
load_remote_list(list, newpage, list->mode);
load_remote_list(list, newpage, list->mode, false);
}
}
@@ -375,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);
@@ -389,7 +420,7 @@ static void search_menu(Entry_List_s * list)
search[i] = '+';
}
list->tp_search = search;
load_remote_list(list, 1, list->mode);
load_remote_list(list, 1, list->mode, false);
}
else
{
@@ -425,11 +456,13 @@ bool themeplaza_browser(EntryMode mode)
bool preview_mode = false;
int preview_offset = 0;
audio_s * audio = NULL;
Entry_List_s list = {0};
Entry_List_s * current_list = &list;
current_list->tp_search = strdup("");
load_remote_list(current_list, 1, mode);
load_remote_list(current_list, 1, mode, false);
C2D_Image preview = {0};
bool extra_mode = false;
@@ -439,7 +472,9 @@ bool themeplaza_browser(EntryMode mode)
break;
if(preview_mode)
draw_preview(TEXTURE_REMOTE_PREVIEW, preview_offset);
{
draw_preview(preview, preview_offset);
}
else
{
Instructions_s instructions = browser_instructions[mode];
@@ -447,20 +482,24 @@ bool themeplaza_browser(EntryMode mode)
instructions = extra_instructions;
draw_grid_interface(current_list, instructions);
}
pp2d_end_draw();
end_frame();
hidScanInput();
u32 kDown = hidKeysDown();
u32 kHeld = hidKeysHeld();
u32 kUp = hidKeysUp();
if(kDown & KEY_START)
{
exit:
quit = true;
downloaded = false;
if(audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
break;
}
@@ -479,7 +518,7 @@ bool themeplaza_browser(EntryMode mode)
free(current_list->tp_search);
current_list->tp_search = strdup("");
load_remote_list(current_list, 1, mode);
load_remote_list(current_list, 1, mode, false);
}
else if((kDown | kHeld) & KEY_DUP)
{
@@ -487,7 +526,7 @@ bool themeplaza_browser(EntryMode mode)
}
else if((kDown | kHeld) & KEY_DRIGHT)
{
load_remote_list(current_list, current_list->tp_current_page, mode, true);
}
else if((kDown | kHeld) & KEY_DDOWN)
{
@@ -504,14 +543,39 @@ bool themeplaza_browser(EntryMode mode)
{
toggle_preview:
if(!preview_mode)
preview_mode = load_remote_preview(*current_list, &preview_offset);
{
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));
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 != NULL)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
}
}
else if(kDown & KEY_B)
{
if(preview_mode)
{
preview_mode = false;
if(mode == MODE_THEMES && audio != NULL)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
}
else
break;
}
@@ -530,11 +594,11 @@ bool themeplaza_browser(EntryMode mode)
}
else if(kDown & KEY_L)
{
load_remote_list(current_list, current_list->tp_current_page-1, mode);
load_remote_list(current_list, current_list->tp_current_page-1, mode, false);
}
else if(kDown & KEY_R)
{
load_remote_list(current_list, current_list->tp_current_page+1, mode);
load_remote_list(current_list, current_list->tp_current_page+1, mode, false);
}
// Movement in the UI
@@ -556,28 +620,6 @@ bool themeplaza_browser(EntryMode mode)
change_selected(current_list, 1);
}
// Fast scroll using circle pad
else if(kHeld & KEY_CPAD_UP)
{
change_selected(current_list, -1);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_DOWN)
{
change_selected(current_list, 1);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_LEFT)
{
change_selected(current_list, -current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_RIGHT)
{
change_selected(current_list, current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
}
touch:
if((kDown | kHeld) & KEY_TOUCH)
{
@@ -595,6 +637,12 @@ bool themeplaza_browser(EntryMode mode)
if(preview_mode)
{
preview_mode = false;
if(mode == MODE_THEMES && audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
continue;
}
else if(y < 24)
@@ -628,11 +676,11 @@ bool themeplaza_browser(EntryMode mode)
{
if(BETWEEN(0, x, border))
{
load_remote_list(current_list, current_list->tp_current_page-1, mode);
load_remote_list(current_list, current_list->tp_current_page-1, mode, false);
}
else if(BETWEEN(320-border, x, 320))
{
load_remote_list(current_list, current_list->tp_current_page+1, mode);
load_remote_list(current_list, current_list->tp_current_page+1, mode, false);
}
}
}
@@ -660,14 +708,16 @@ 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;
}
u32 http_get(const char *url, char ** filename, char ** buf)
u32 http_get(const char *url, char ** filename, char ** buf, InstallType install_type)
{
Result ret;
httpcContext context;
@@ -722,7 +772,7 @@ u32 http_get(const char *url, char ** filename, char ** buf)
{
httpcCloseContext(&context);
if (new_url != NULL) free(new_url);
DEBUG("status_code\n");
DEBUG("status_code, %lu\n", status_code);
return 0;
}
@@ -791,6 +841,9 @@ u32 http_get(const char *url, char ** filename, char ** buf)
ret = httpcDownloadData(&context, (*(u8**)buf) + size, 0x1000, &read_size);
size += read_size;
if(content_size && install_type != INSTALL_NONE)
draw_loading_bar(size, content_size, install_type);
if (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING)
{
last_buf = *buf;
@@ -820,5 +873,6 @@ u32 http_get(const char *url, char ** filename, char ** buf)
httpcCloseContext(&context);
free(new_url);
DEBUG("size: %lu\n", size);
return size;
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -41,15 +41,15 @@ void splash_install(Entry_s splash)
u32 size = load_data("/splash.bin", splash, &screen_buf);
if(size != 0)
{
remake_file("/luma/splash.bin", ArchiveSD, size);
buf_to_file(size, "/luma/splash.bin", ArchiveSD, screen_buf);
remake_file(fsMakePath(PATH_ASCII, "/luma/splash.bin"), ArchiveSD, size);
buf_to_file(size, fsMakePath(PATH_ASCII, "/luma/splash.bin"), ArchiveSD, screen_buf);
}
u32 bottom_size = load_data("/splashbottom.bin", splash, &screen_buf);
if(bottom_size != 0)
{
remake_file("/luma/splashbottom.bin", ArchiveSD, bottom_size);
buf_to_file(bottom_size, "/luma/splashbottom.bin", ArchiveSD, screen_buf);
remake_file(fsMakePath(PATH_ASCII, "/luma/splashbottom.bin"), ArchiveSD, bottom_size);
buf_to_file(bottom_size, fsMakePath(PATH_ASCII, "/luma/splashbottom.bin"), ArchiveSD, screen_buf);
}
if(size == 0 && bottom_size == 0)

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -56,12 +56,14 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
}
char * padded = NULL;
int shuffle_count = 0;
Handle body_cache_handle;
if(installmode & THEME_INSTALL_BODY)
{
remake_file("/BodyCache_rd.bin", ArchiveThemeExt, BODY_CACHE_SIZE * MAX_SHUFFLE_THEMES);
remake_file(fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), ArchiveThemeExt, BODY_CACHE_SIZE * MAX_SHUFFLE_THEMES);
FSUSER_OpenFile(&body_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), FS_OPEN_WRITE, 0);
}
@@ -83,14 +85,29 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
}
shuffle_body_sizes[shuffle_count] = body_size;
FSFILE_Write(body_cache_handle, NULL, BODY_CACHE_SIZE * shuffle_count, body, body_size, FS_WRITE_FLUSH);
padded = calloc(BODY_CACHE_SIZE, sizeof(char));
memcpy(padded, body, body_size);
free(body);
FSFILE_Write(body_cache_handle, NULL, BODY_CACHE_SIZE * shuffle_count, padded, BODY_CACHE_SIZE, FS_WRITE_FLUSH);
free(padded);
padded = NULL;
}
if(installmode & THEME_INSTALL_BGM)
{
char bgm_cache_path[26] = {0};
sprintf(bgm_cache_path, "/BgmCache_%.2i.bin", shuffle_count);
if(current_theme->no_bgm_shuffle)
{
music = NULL;
music_size = 0;
}
else
{
music_size = load_data("/bgm.bcstm", *current_theme, &music);
if(music_size > BGM_MAX_SIZE)
@@ -99,25 +116,43 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
DEBUG("bgm too big\n");
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
}
}
shuffle_music_sizes[shuffle_count] = music_size;
remake_file(bgm_cache_path, ArchiveThemeExt, BGM_MAX_SIZE);
buf_to_file(music_size, bgm_cache_path, ArchiveThemeExt, music);
remake_file(fsMakePath(PATH_ASCII, bgm_cache_path), ArchiveThemeExt, BGM_MAX_SIZE);
Handle bgm_cache_handle;
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 && music_size)
{
memcpy(padded, music, music_size);
free(music);
}
FSFILE_Write(bgm_cache_handle, NULL, 0, padded, BGM_MAX_SIZE, FS_WRITE_FLUSH);
FSFILE_Close(bgm_cache_handle);
free(padded);
padded = NULL;
}
shuffle_count++;
}
}
if(installmode & THEME_INSTALL_BGM)
{
char * blank = calloc(BGM_MAX_SIZE, sizeof(char));
for(int i = shuffle_count; i < MAX_SHUFFLE_THEMES; i++)
{
char bgm_cache_path[26] = {0};
sprintf(bgm_cache_path, "/BgmCache_%.2i.bin", i);
remake_file(bgm_cache_path, ArchiveThemeExt, BGM_MAX_SIZE);
remake_file(fsMakePath(PATH_ASCII, bgm_cache_path), ArchiveThemeExt, BGM_MAX_SIZE);
buf_to_file(BGM_MAX_SIZE, fsMakePath(PATH_ASCII, bgm_cache_path), ArchiveThemeExt, blank);
}
free(blank);
}
if(installmode & THEME_INSTALL_BODY)
@@ -140,7 +175,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
}
res = buf_to_file(body_size, "/BodyCache.bin", ArchiveThemeExt, body); // Write body data to file
res = buf_to_file(body_size, fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, body); // Write body data to file
free(body);
if(R_FAILED(res)) return res;
@@ -156,10 +191,16 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
}
res = buf_to_file(music_size, "/BgmCache.bin", ArchiveThemeExt, music);
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);
if(R_FAILED(res)) return res;
} else
{
music = calloc(BGM_MAX_SIZE, 1);
res = buf_to_file(BGM_MAX_SIZE, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
free(music);
}
}
@@ -195,7 +236,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
theme_manage->dlc_theme_content_index = 0xFF;
theme_manage->use_theme_cache = 0x0200;
res = buf_to_file(0x800, "/ThemeManage.bin", ArchiveThemeExt, thememanage_buf);
res = buf_to_file(0x800, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, thememanage_buf);
free(thememanage_buf);
if(R_FAILED(res)) return res;
//----------------------------------------
@@ -220,7 +261,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
}
}
res = buf_to_file(savedata_size, "/SaveData.dat", ArchiveHomeExt, savedata_buf);
res = buf_to_file(savedata_size, fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, savedata_buf);
free(savedata_buf);
if(R_FAILED(res)) return res;
//----------------------------------------

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* 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
@@ -48,7 +48,9 @@ void printu(u16 *input)
ssize_t buf_len = in_len + 1; // Plus 1 for proper null termination
wchar_t *buf = calloc(buf_len, sizeof(wchar_t));
utf16_to_utf32((u32*)buf, input, buf_len);
printf("%ls\n", buf);
char cbuf[0x106];
sprintf(cbuf, "%ls\n", buf);
DEBUG(cbuf);
free(buf);
}