126 Commits

Author SHA1 Message Date
7035c416cf Properly free resources on invalid mime type 2024-05-20 21:33:46 -04:00
e498b3f24a Add libcurl for non-TP downloads 2024-05-20 21:31:24 -04:00
Alex Taber
610b21463d Update CONTRIBUTORS.md 2024-05-13 15:49:31 -04:00
arth
016e7fd738 Added Portuguese translation (#307) 2024-05-13 15:48:26 -04:00
Alex Taber
082b6f8f61 Update CONTRIBUTORS.md 2024-05-13 12:22:09 -04:00
20f3c503ad Update browser to not use combos 2024-05-12 21:51:13 -04:00
eae7a2250e Fix no themes/splashes buttons 2024-05-12 20:01:09 -04:00
22492339a6 Failsafe to prevent negative scrolls 2024-05-12 19:41:54 -04:00
9b098f6935 Add missing string, fix preview bug in browser 2024-05-12 17:46:55 -04:00
cooolgamer
3c55868183 Added French translation 2024-05-12 16:46:57 -04:00
Alex Taber
a43cbcca74 UX Overhaul (#305)
* Fix bgm checking bug

* Converted install and menu options into button menus rather than combos

* Fix sort order

* Various touch screen changes so that most functions can be done via touch

 * Dim theme list when navigating menus

* Translation framework implemented

* bug when no themes loaded fixed

* Fix theme preview regression introduced in d037691
2024-05-12 14:24:43 -04:00
Théo B
546d459696 Performance Improvements
Pass theme list & its entries around by reference rather than copying them.
Fix bug in async icon loading that caused icons to be loaded multiple times.

Original PR by @LiquidFenrir
2024-05-10 17:43:47 -04:00
d1f3dbf06b Throw warning on mono audio install. 2024-05-08 17:21:46 -04:00
cdf30f3bea Throw error on failed parental validation.
Also use CFGU instead of CFG for reading restrictions
2024-05-07 21:46:06 -04:00
c6b6f560be Require PIN for browser if browser access restricted 2024-05-07 21:25:45 -04:00
64d65417fa Disable home button while theme is installing 2024-04-26 00:21:10 -04:00
b19343e238 Draw no home icon in theme plaza browser 2024-04-24 23:37:13 -04:00
ecf9dc63ce Disable home button on theme install 2024-04-24 23:25:20 -04:00
7e2191ad86 Add x-zip-compressed to acceptable mime types
Closes #287
2024-04-24 14:42:10 -04:00
Jan
d037691418 assemble a splash preview if none was found (#277) 2024-04-24 14:32:54 -04:00
6b89496566 Properly handle Unicode file names on download
Closes #303. May still need additional testing
to ensure everything works properly, but preliminary
testing passes.
2024-04-24 14:17:55 -04:00
Théo B
36ca676c99 fix makefile (shader and texture rules) (#296)
* fix makefile (shader and texture rules)

they're now part of devkitARM base rules, not needed anymore, in fact there was a conflict. The t3x for romfs needed to be more specific. this is based on the changes to 3ds-examples/graphics/gpu/gpusprite which was the base of the post-citro2d rewrite Makefile

* copy paste error
2024-02-27 19:50:06 +00:00
cooolgamer
67fc17dddf Korean support (#290)
* Update themes.c

Addes KOR in dump themes

* Update fs.c

Added Kor support usable with This patch: https://github.com/ZeroSkill1/CTR-Hacking/tree/master/General-Hacking/Korean%20HOME%20Menu%20Theme%20Patch
2024-02-26 13:05:52 -05:00
Zemogiter
3fa73ee291 Update fs.h 2024-02-20 17:04:51 -05:00
Zemogiter
ae417f8f86 Compilation fixes 2024-02-14 09:30:04 -05:00
Dylan G
adccc70cca Fixed useless 404 error when a remote preview has no BGM 2022-07-31 20:41:22 +01:00
Dylan G
b66ca12039 Merge pull request #272 from LiquidFenrir/slight-music-rework
make audio safer
2022-06-17 23:05:22 +01:00
LiquidFenrir
4a56a883fa make audio safer
- centralized stop function
- freeing the struct not from the thread while waiting on handle in it
- thread not detached
- maybe fixes hang on exit from HM in ndsp status check loop
2022-06-14 13:02:15 +02:00
Dylan G
9ebfe387a0 Merge pull request #270 from LiquidFenrir/exit-fixes
fix multiple crashes
2022-06-10 23:07:34 +01:00
LiquidFenrir
806d0033de fix multiple crashes
on exit:
- bad timing when the install checks threads run, could crash
- quitting through HOME when a bgm was previewed in the browser
on http get failure:
- the quit flag would be enabled, but the browser wouldn't honour it
2022-06-05 11:49:09 +02:00
Dylan G
4c053bb447 Merge pull request #269 from LiquidFenrir/patch-2
fix dumping official body and bgm, and double free
2022-06-03 20:46:14 +01:00
Théo B
1ed6c46644 fix dumping official body and bgm, and double free
fixes #268
2022-06-03 21:40:23 +02:00
Dylan G
e22d09ba54 Typo in README
a stray backslash found its way into the first line
2022-05-30 14:24:23 +01:00
Dylan G
a0c16a64ec Search strings now escaped properly (fixes #267) 2022-05-30 12:04:18 +01:00
Dylan G
a1e7ed9924 Added header-only URL encoding/decoding 2022-05-30 12:03:11 +01:00
Dylan G
7f7fdc010a Fixed faulty FS error when a filename contains a / 2022-05-30 10:43:12 +01:00
Dylan G
b81a9aaa4c Dropped HTTPS for the browser as ThemePlaza is retiring TLSv1.1 2022-05-22 15:23:36 +01:00
Dylan G
6ba1ef111e Merge pull request #262 from LiquidFenrir/better-dump
add ability to dump all your official themes
2022-02-26 16:02:55 +00:00
Dylan G
0500b24431 Merge pull request #260 from LiquidFenrir/patch-1
Make BUF_TO_READ larger to fix audio in some cases
2022-02-26 15:47:30 +00:00
Dylan G
83071d3734 Merge pull request #250 from LiquidFenrir/shuffle-fix
make shuffle work on consoles that never used it
2022-02-26 15:47:11 +00:00
LiquidFenrir
4e2bea53c1 add ability to dump all your official themes
icon and name get extracted from the dlc data
requires libctru from commit 5f13628dac75206f0c97d29a7427ce8284d910f1 or older (added the am commands necessary to find the dlc data)
Makefile changed to reflect the macro change in unreleased libctru
2021-12-22 00:00:42 +01:00
Théo B
0da2594251 Make BUF_TO_READ larger to fix audio in some cases
If the preview .ogg has a samplerate > the BUF_TO_READ constant, there was an out of bounds write to the audio buffers which resulted in crackling.
Thus, upped it to 48000 (0x80-aligned, and pretty much the highest rate anything consumer goes). a bit big, but safe.
2021-09-26 12:10:25 +02:00
Dylan G
c5dc7448e4 Merge pull request #259 from LiquidFenrir/patch-1
Update CONTRIBUTORS.md
2021-08-08 19:05:37 +01:00
Théo B
999b764c26 Update CONTRIBUTORS.md
Might as well add my name there, since it's on the account as well now.
2021-08-08 19:27:36 +02:00
Dylan G
18cb5c616f Correctly handle cases where files already exist on the filesystem.
Also: patched a bug wherein the filename filter was acting up, transforming `file.zip` to `file-zip.zip`, for example.
2021-06-17 01:43:50 +01:00
Dylan G
3f2e4c03f3 Constness 2021-06-17 01:37:33 +01:00
Dylan G
163e12d38a Add issue templates (#249)
* Add issue templates

We've needed this for a while, due to bug reports that don't really mean anything.

* response to feedback
2021-06-14 13:04:33 -04:00
02c3e617ae Handle when content-disposition header is not present 2021-06-14 13:03:57 -04:00
LiquidFenrir
6161874d07 fix a shuffle weirdness (10 - N official ones)
from anemone, when setting a N theme shuffle, then a single theme you could only set 10 - N official themes on shuffle afterwards (and even then, it would fail and reset the theme stuff, leaving you able to set however many official themes on shuffle you wanted)
this skips the failing step
2021-04-03 21:55:52 +02:00
LiquidFenrir
cea9b8655a make shuffle work on consoles that never used it 2021-03-31 15:27:58 +02:00
Dylan G
7745530764 Fixed 4720a499 (TP returns 303 currently, not 404) => handle 303 well(?); pushed some code around 2021-03-11 13:47:24 +00:00
Dylan G
0bab0f6700 Patched httpc error message to actually be readable on console; also provides Result code 2021-03-11 12:34:03 +00:00
Dylan G
38ab370cf1 Merge pull request #247 from KennLDN/patch-1
Fix broken link in CONTRIBUTORS.md
2021-02-08 13:40:16 +00:00
kenn
5cea6c8df4 Fix broken link in CONTRIBUTORS.md 2021-02-08 13:38:32 +00:00
Alex Taber
e4e0118c1a Prevent out of bounds scrolling in the browser 2021-01-01 23:51:51 -05:00
Dylan G
accdaaed2a Reduced code duplication 2020-12-31 21:28:10 +00:00
Alex Taber
3fb90e8197 Remove unused variables 2020-12-24 19:03:47 -05:00
Dylan G
aba2cd5f18 Finally update copyright; Flask meta files removed 2020-12-24 23:38:58 +00:00
Dylan G
e5ea18e81a Cleared TODO 2020-12-24 23:34:17 +00:00
Dylan G
4720a49923 QoL: alert the user if they attempt to download a theme that has not been approved.
Stopgap solution; TP does not want to expose approval status in headers for security reasons.
2020-12-24 23:34:17 +00:00
Dylan G
7871225999 Fix potential null dereference 2020-12-24 23:34:17 +00:00
Dylan G
1e5d09e85a Major refactor to networking, including a fix for an incorrect error message on cancelling the swkbd for downloads 2020-12-24 23:34:17 +00:00
Alex Taber
a647494306 Fix another null dereference 2020-12-24 18:27:48 -05:00
Alex Taber
c801e28523 Prevent free(NULL) in free_icons 2020-12-24 17:13:06 -05:00
Alex Taber
c9e1420a00 re-add s upport for themes without an SMDH
Also change theme dump random color algo
2020-12-24 16:58:06 -05:00
Dylan G
bc2f4ec581 Finished up software keyboard input for filenames in http_get.
Closes #220.
Worth noting that the callback may be shared between this whatever @astronautlevel2 is working on (theme dumping, I think).
2020-12-24 20:01:38 +00:00
Alex Taber
9592d76c2c Fix color generation 2020-12-24 14:52:08 -05:00
Alex Taber
a266203a92 Add theme dump functionality 2020-12-24 14:34:56 -05:00
Alex Taber
17afdbaae2 Fix regression in c40696e982
Extra menu works again
2020-12-23 19:59:18 -05:00
Alex Taber
f6d7446eba Update themeplaza url 2020-12-23 18:46:03 -05:00
Alex Taber
c49129d408 Remove trello link
We don't use trello for feature tracking, link is very outdated
2020-12-23 17:37:29 -05:00
Alex Taber
f9a2ae5190 Fix RGB previews having inverted colors
Closes #227
2020-12-23 17:30:55 -05:00
Alex Taber
573b7d35e6 Fix potential hang in QR reader 2020-12-23 16:51:57 -05:00
a2b6eb94e4 Fix regressions introduced in 1c2e562dd6 2020-12-23 16:20:49 -05:00
Théo B
95ff2dd3ba Memory leak fix and camera multithreading safety improvement (#239)
* memory leak fix and attempt at optimizing space

* camera rework

try to use better locking algorithm (MRSW lock on wikipedia)

* add time print (toggleable) and stuff

remove old mixed qr thingss from main

* remove the dumb 3 bytes saving

* remove useless time measure code

* forgot to close the stop event handle

* fix memory leak when loading icon from smdh

* fix entry path on folders

optimization using memcpy cause it to have the "/info.smdh" when the entry is a folder. simply remove that with a memset to 0.

Co-authored-by: Alex Taber <astronautlevel2@users.noreply.github.com>
2020-12-21 22:31:38 -05:00
Dylan G
1c2e562dd6 Networking rework (#240)
* Began refactoring http_get. Still a few cases to handle, but it works.

* Introduced error enum; handle error 406; handle a misbehaving server; redirect_url leaked memory (L888)

* Allowed calls to http_get to pass NULL as acceptable_mime_types

* Removed status_code from header struct; eventually planning to move most of parse_header's logic back to http_get, so this makes sense

* Moved some more logic back into http_get
Note: currently behaves weirdly with some QR codes, including but probably not limited to ones pointing to TinyURL.

* Handle redirects correctly; other rearrangements

* Formatting

* Handle HTTP 303 See Other correctly

* Removed "Download failed" error in camera.c due to all related failures being handled in http_get

* Fixed missing loading bar; started working on unchunked download

* Added unchunked download

* Reintroduced the download progress bar, rolled back to always doing chunked download

* URL length caps at 2083 chars

* Slightly more efficient

* Correctly handle incorrect filesize header
TODO: reorder clauses to remove the goto?

* Fixed illegal characters, removed unnecessary logic (technically a regression, as we always have to realloc now)

* Finally finished up status code handling. Fixed a memory leak when the server fails conneg.
2020-12-21 21:11:11 -05:00
Dylan G
9cb13f6be1 Fix #326
Typo in button string.
2020-11-18 19:37:46 +00:00
6a51b4eae5 Fix misc. QR code bugs
Close data->started properly, don't double close data->cancel
Should fix the QR code hang
2020-06-22 12:27:14 -04:00
14d9e99b7d Properly set body_size
Fix regression in 36e8104dbb
2020-06-17 14:09:23 -04:00
36e8104dbb Automatically set BGM flag to enabled when doing a BGM install 2020-06-16 18:45:59 -04:00
6bafa6a22e Add lz decompression code
Also add byte search code
2020-06-15 23:10:30 -04:00
Dylan G
408b2903f2 Finally void-out textlen in the callbacks (no more compiler warnings) 2020-06-15 23:11:28 +01:00
fbca873f52 Reset cursor on page load (Closes #206) 2020-06-13 15:29:57 -04:00
d1a821edf5 Merge branch 'master' of github.com:astronautlevel2/Anemone3DS 2020-06-12 12:03:34 -04:00
a4532b0f3d Various fixes
Stop loading themes missing smdh files - this was causing scroll crashes due to how the icon loading code was working. In the future we will include a message about how themes are skipped due to lacking smdh files. Invalid zips also caused the same issue and are also now being properly ignored

Fix memory leak in the zip file loading code
2020-06-12 12:02:13 -04:00
Dylan G
f819f9d452 No longer assume that a QR filename contains a quote (Fixes #204) 2020-06-12 16:27:40 +01:00
2c2c7f4b33 Fix double free 2020-06-11 21:24:01 -04:00
b75d1acb21 Fix crash caused by quirc overflowing the 3ds stack in some instances 2020-06-11 20:41:17 -04:00
e8d0a2eb27 Fix similar (but unrelated to the previous) race condition 2020-06-11 14:34:14 -04:00
f4217888ce Fix bug caused by race between theme thread & QR reader UI thread 2020-06-11 13:48:34 -04:00
Dylan G
d358dd134e Updated quirc to v1.1 (2019); patched to remove compiler warnings 2020-06-03 02:13:07 +01:00
Dylan G
ca9da9d297 Update CONTRIBUTORS.md 2020-06-01 17:18:02 +01:00
Dylan G
71a4cde22e 64-bit makerom binaries now provided officially 2020-06-01 17:14:16 +01:00
Dylan G
f7404cf162 Patched memory leak (closes #217) 2020-06-01 15:02:10 +01:00
Dylan G
ef774ce0b4 Now compiles under GCC 10 (GCC 10 now defaults to -fno-common) 2020-05-31 17:45:05 +01:00
Wrong
193feedf25 Very minor fix for libctru 1.6.0 compatibility (#213) 2019-11-01 15:46:32 -04:00
Dylan G
ddfc39db56 matt changed his github username 2019-07-06 17:31:21 +01: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
81 changed files with 5995 additions and 11547 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +0,0 @@
{
"user": "astronautlevel2",
"name": "Anemone3DS",
"description": "A theme and boot splash manager for the Nintendo 3DS console"
}

39
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,39 @@
---
name: Bug report
about: Found a bug? Report it here
title: ''
labels: ''
assignees: ''
---
**Description**
A clear and concise description of what the bug is.
**Steps to reproduce**
***If you cannot reproduce the bug, please describe what you were doing in as much detail as possible***
*If you were scanning using the QR scanner, ensure you include a link to the theme you were attempting to download.*
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
Add screenshots of any error screens you encountered. Bug reports without screenshots will take longer to solve, so we recommend you get some!
**Crash dumps**
If your bug causes a crash in Luma3DS, please upload any crash dumps generated. You can find these in `SD:/luma/dumps/armX` (`X = 9 or 11`) with the name Luma3DS's exception handlers give you.
**System information**
*System model* e.g. new3DS XL
*System firmware version* e.g. 11.14.0 (this can be found in the System Settings applet by default)
*Anemone3DS version* e.g. v2.2.0 (found in the bottom-left of Anemone3DS)
*Luma3DS version* e.g. v10.2.1 (found in the menu when you hold Select on boot)
**If you are not on the [latest version of Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/releases/latest), your bug report will likely be closed. Ensure the bug occurs in the latest build!**

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

1
.gitignore vendored
View File

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

View File

@@ -1,10 +1,10 @@
# Main Contributors # Main Contributors
* Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2)) * Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2))
* [@LiquidFenrir](https://github.com/LiquidFenrir) * Théo B. ([@LiquidFenrir](https://github.com/LiquidFenrir))
* Dawid Eckert ([@daedreth](https://github.com/daedreth)) * Dawid Eckert ([@daedreth](https://github.com/daedreth))
* Dylan G. ([@helloman892](https://github.com/helloman892)) * Dylan G. ([@helloman892](https://github.com/helloman892))
* Nils P. ([@ZetaDesigns](https://github.com/ZetaDesigns)) * Nils P. ([@ZetaDesigns](https://github.com/ZetaDesigns))
* Matt Kenny ([@mattkenster](https://github.com/mattkenster)) * Matt Kenny ([@KennLDN](https://github.com/KennLDN))
# Minor Contributors # Minor Contributors
* Nic ([@Wizzrobes](https://github.com/Wizzrobes)) * Nic ([@Wizzrobes](https://github.com/Wizzrobes))
@@ -16,3 +16,8 @@
* Guillaume Gérard ([@GreatWizard](https://github.com/GreatWizard)) * Guillaume Gérard ([@GreatWizard](https://github.com/GreatWizard))
* Joel ([@joel16](https://github.com/joel16)) * Joel ([@joel16](https://github.com/joel16))
* [@thedax](https://github.com/thedax) * [@thedax](https://github.com/thedax)
* [@Wryyyong](https://github.com/Wryyyong)
# Translation Contributors
* [@cooolgamer](https://github.com/cooolgamer/) for French
* Arth ([@iveurne](https://github.com/iveurne)) for Portugese

196
Makefile
View File

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

View File

@@ -1,23 +1,19 @@
![# Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/blob/master/meta/banner.png) ![# Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/blob/master/meta/banner.png)
A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.\ A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.
To-do list here: https://trello.com/b/F1YSa1VK
# Dependencies # Dependencies
* devkitPro, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started). * devkitARM, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started).
* zlib, jansson, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702). * jansson, libvorbisidec, libpng, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702).
* A recent build of [makerom](https://github.com/profi200/Project_CTR) and the latest release of [bannertool](https://github.com/Steveice10/bannertool). These must be added to your PATH. * A 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).
* ~~[pp2d](https://github.com/BernardoGiordano/pp2d), which is included in the repo if you do a git clone --recursive.~~
Due to circumstances surrounding the privacy settings on the pp2d repo, the source files are now included directly within the repo.
* Git needs to be on your PATH (this is usually only manual under Windows).
# Building # Building
First of all, make sure devkitPro is properly installed and added to your PATH. 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 After that, open the directory you want to clone the repo into, and execute
`git clone https://github.com/astronautlevel2/Anemone3DS` (or any other cloning method). `git clone https://github.com/astronautlevel2/Anemone3DS` (or any other cloning method).
To install zlib, jansson and libarchive, begin by following the instructions found above ([here](https://devkitpro.org/viewtopic.php?f=13&t=8702)) on the devkitPro forums, and then install the dkP packages `3ds-zlib`, `3ds-jansson` and `3ds-libarchive`. 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`, `3ds-lz4`, and `3ds-libarchive` using `[sudo] [dkp-]pacman -S <package-name>`.
After also adding [makerom](https://github.com/profi200/Project_CTR) and [bannertool](https://github.com/Steveice10/buildtools) to your PATH, just enter your directory and run `make`. All built files will be in `/out/`.
After adding [makerom](https://github.com/profi200/Project_CTR) and [bannertool](https://github.com/Steveice10/buildtools) to your PATH, just enter your directory and run `make`. All built binaries will be in `/out/`.
# License # License
This project is licensed under the GNU GPLv3. See LICENSE.md for details. Additional terms 7b and 7c apply to this project. This project is licensed under the GNU GPLv3. See LICENSE.md for details. Additional terms 7b and 7c apply to this project.
@@ -25,6 +21,8 @@ This project is licensed under the GNU GPLv3. See LICENSE.md for details. Additi
# Credits # 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: [CONTRIBUTORS.md](CONTRIBUTORS.md) 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)
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: 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. * [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. * [SteveIce10](https://github.com/SteveIce10), whose QR code in FBI was essential.

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

BIN
assets/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

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

BIN
assets/bgm_only.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

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

BIN
assets/dump.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 390 B

After

Width:  |  Height:  |  Size: 390 B

View File

Before

Width:  |  Height:  |  Size: 404 B

After

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

BIN
assets/no_home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 307 B

BIN
assets/qr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

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

View File

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 291 B

28
assets/sprites.t3s Normal file
View File

@@ -0,0 +1,28 @@
--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
install.png
exit.png
installed.png
menu.png
no_home.png
preview.png
select.png
shuffle.png
shuffle_no_bgm.png
sort.png
start.png
qr.png
bgm_only.png
back.png
dump.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 * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -31,18 +31,23 @@
typedef struct { typedef struct {
u16 * camera_buffer; u16 * camera_buffer;
u32 *texture_buffer;
Handle mutex;
volatile bool finished;
volatile bool success;
Handle cancel;
bool capturing; Handle event_stop;
Thread cam_thread, ui_thread;
LightEvent event_cam_info, event_ui_info;
CondVar cond;
LightLock mut;
u32 num_readers_active;
bool writer_waiting;
bool writer_active;
bool any_update;
struct quirc * context; struct quirc * context;
} qr_data; } qr_data;
bool init_qr(void); bool init_qr(void);
void exit_qr(qr_data *data);
void take_picture(void);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -27,17 +27,23 @@
#ifndef COLORS_H #ifndef COLORS_H
#define COLORS_H #define COLORS_H
#define ABGR8(a, b, g, r) ((((a)&0xFF)<<0) | (((b)&0xFF)<<8) | (((g)&0xFF)<<16) | (((r)&0xFF)<<24)) #include "common.h"
#define RGBA8(r, g, b, a) ((((r)&0xFF)<<0) | (((g)&0xFF)<<8) | (((b)&0xFF)<<16) | (((a)&0xFF)<<24))
typedef u32 Color;
typedef enum { typedef enum {
COLOR_BACKGROUND = ABGR8(255, 32, 28, 35), //silver-y black COLOR_BACKGROUND, //silver-y black
COLOR_ACCENT = RGBA8(55, 122, 168, 255), COLOR_ACCENT,
COLOR_WHITE = RGBA8(255, 255, 255, 255), COLOR_WHITE,
COLOR_CURSOR = RGBA8(200, 200, 200, 255), COLOR_CURSOR,
COLOR_BLACK = RGBA8(0, 0, 0, 255), COLOR_BLACK,
COLOR_RED = RGBA8(200, 0, 0, 255), COLOR_RED,
COLOR_YELLOW = RGBA8(239, 220, 11, 255), COLOR_YELLOW,
} Color;
COLOR_AMOUNT,
} Colors_e;
extern Color colors[COLOR_AMOUNT];
void init_colors(void);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,8 @@
#define COMMON_H #define COMMON_H
#include <3ds.h> #include <3ds.h>
#include <citro3d.h>
#include <citro2d.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -36,11 +38,23 @@
#define DEBUG(...) fprintf(stderr, __VA_ARGS__) #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define POS() DEBUG("%s (line %d)...\n", __func__, __LINE__) #define POS() DEBUG("%s (line %d)...\n", __func__, __LINE__)
#define DEBUGPOS(...) \ #define DEBUGPOS(...) do {\
POS(); \ POS(); \
DEBUG(__VA_ARGS__) DEBUG(__VA_ARGS__); \
} while(0)
#define FASTSCROLL_WAIT 1.5e8 static inline int min(const int a, const int b)
{
return a > b ? b : a;
}
static inline int max(const int a, const int b)
{
return a < b ? b : a;
}
#define FASTSCROLL_WAIT 1e8
#define BETWEEN(min, x, max) (min < x && x < max)
typedef enum { typedef enum {
MODE_THEMES = 0, MODE_THEMES = 0,
@@ -49,40 +63,19 @@ typedef enum {
MODE_AMOUNT, MODE_AMOUNT,
} EntryMode; } EntryMode;
typedef enum {
DRAW_MODE_LIST = 0,
DRAW_MODE_INSTALL,
DRAW_MODE_EXTRA,
DRAW_MODE_AMOUNT,
} DrawMode;
extern const char * main_paths[MODE_AMOUNT]; extern const char * main_paths[MODE_AMOUNT];
extern const int entries_per_screen_v[MODE_AMOUNT]; extern const int entries_per_screen_v[MODE_AMOUNT];
extern const int entries_per_screen_h[MODE_AMOUNT]; extern const int entries_per_screen_h[MODE_AMOUNT];
extern const int entry_size[MODE_AMOUNT]; extern const int entry_size[MODE_AMOUNT];
extern bool quit; extern bool quit;
extern bool dspfirm;
enum TextureID {
TEXTURE_FONT_RESERVED = 0, // used by pp2d for the font
TEXTURE_ARROW,
TEXTURE_ARROW_SIDE,
TEXTURE_SHUFFLE,
TEXTURE_INSTALLED,
TEXTURE_PREVIEW_ICON,
TEXTURE_SORT,
TEXTURE_DOWNLOAD,
TEXTURE_BROWSE,
TEXTURE_LIST,
TEXTURE_EXIT,
TEXTURE_BATTERY_0,
TEXTURE_BATTERY_1,
TEXTURE_BATTERY_2,
TEXTURE_BATTERY_3,
TEXTURE_BATTERY_4,
TEXTURE_BATTERY_5,
TEXTURE_BATTERY_CHARGE,
TEXTURE_QR,
TEXTURE_PREVIEW,
TEXTURE_REMOTE_PREVIEW,
TEXTURE_SELECT_BUTTON,
TEXTURE_START_BUTTON,
// always the last
TEXTURE_REMOTE_ICONS,
TEXTURE_ICON = TEXTURE_REMOTE_ICONS + 24,
};
#endif #endif

9
include/conversion.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef CONVERISON_H
#define CONVERISON_H
#include "common.h"
size_t bin_to_abgr(char ** bufp, size_t size);
size_t png_to_abgr(char ** bufp, size_t size, u32 *height);
#endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -31,7 +31,9 @@
#include "loading.h" #include "loading.h"
#include "colors.h" #include "colors.h"
typedef enum { #define MAX_LINES 10
typedef enum InstallType_e {
INSTALL_LOADING_THEMES, INSTALL_LOADING_THEMES,
INSTALL_LOADING_SPLASHES, INSTALL_LOADING_SPLASHES,
INSTALL_LOADING_ICONS, INSTALL_LOADING_ICONS,
@@ -53,9 +55,72 @@ typedef enum {
INSTALL_LOADING_REMOTE_PREVIEW, INSTALL_LOADING_REMOTE_PREVIEW,
INSTALL_LOADING_REMOTE_BGM, INSTALL_LOADING_REMOTE_BGM,
INSTALL_DUMPING_THEME,
INSTALL_DUMPING_ALL_THEMES,
INSTALL_NONE, INSTALL_NONE,
} InstallType; } InstallType;
typedef enum {
// InstallType text
TEXT_INSTALL_LOADING_THEMES,
TEXT_INSTALL_LOADING_SPLASHES,
TEXT_INSTALL_LOADING_ICONS,
TEXT_INSTALL_SPLASH,
TEXT_INSTALL_SPLASH_DELETE,
TEXT_INSTALL_SINGLE,
TEXT_INSTALL_SHUFFLE,
TEXT_INSTALL_BGM,
TEXT_INSTALL_NO_BGM,
TEXT_INSTALL_DOWNLOAD,
TEXT_INSTALL_CHECKING_DOWNLOAD,
TEXT_INSTALL_ENTRY_DELETE,
TEXT_INSTALL_LOADING_REMOTE_THEMES,
TEXT_INSTALL_LOADING_REMOTE_SPLASHES,
TEXT_INSTALL_LOADING_REMOTE_PREVIEW,
TEXT_INSTALL_LOADING_REMOTE_BGM,
TEXT_INSTALL_DUMPING_THEME,
TEXT_INSTALL_DUMPING_ALL_THEMES,
// Other text
TEXT_VERSION,
TEXT_THEME_MODE,
TEXT_SPLASH_MODE,
TEXT_NO_THEME_FOUND,
TEXT_NO_SPLASH_FOUND,
TEXT_DOWNLOAD_FROM_QR,
TEXT_SWITCH_TO_SPLASHES,
TEXT_SWITCH_TO_THEMES,
TEXT_OR_START_TO_QUIT,
TEXT_BY_AUTHOR,
TEXT_SELECTED,
TEXT_SELECTED_SHORT,
TEXT_THEMEPLAZA_THEME_MODE,
TEXT_THEMEPLAZA_SPLASH_MODE,
TEXT_SEARCH,
TEXT_PAGE,
TEXT_ERROR_QUIT,
TEXT_ERROR_CONTINUE,
TEXT_CONFIRM_YES_NO,
TEXT_AMOUNT
} Text;
typedef enum { typedef enum {
ERROR_LEVEL_ERROR, ERROR_LEVEL_ERROR,
ERROR_LEVEL_WARNING, ERROR_LEVEL_WARNING,
@@ -66,7 +131,7 @@ typedef enum {
#define BUTTONS_INFO_LINES 4 #define BUTTONS_INFO_LINES 4
#define BUTTONS_INFO_COLUNMNS 2 #define BUTTONS_INFO_COLUNMNS 2
enum { typedef enum {
BUTTONS_Y_INFO = BUTTONS_START_Y+5, BUTTONS_Y_INFO = BUTTONS_START_Y+5,
BUTTONS_Y_LINE_1 = BUTTONS_START_Y + BUTTONS_STEP*1, BUTTONS_Y_LINE_1 = BUTTONS_START_Y + BUTTONS_STEP*1,
@@ -76,26 +141,43 @@ enum {
BUTTONS_X_LEFT = 20, BUTTONS_X_LEFT = 20,
BUTTONS_X_RIGHT = 200, BUTTONS_X_RIGHT = 200,
BUTTONS_X_MAX = 380,
} ButtonPos; } ButtonPos;
typedef struct { typedef struct {
const wchar_t * info_line; const char * info_line;
Color info_line_color; const char * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
const wchar_t * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
} Instructions_s; } Instructions_s;
extern C3D_RenderTarget * top;
extern C3D_RenderTarget * bottom;
extern C2D_TextBuf staticBuf, dynamicBuf;
extern C2D_Text text[TEXT_AMOUNT];
void init_screens(void); void init_screens(void);
void exit_screens(void); void exit_screens(void);
void throw_error(char* error, ErrorLevel level); void start_frame(void);
bool draw_confirm(const char* conf_msg, Entry_List_s* list); void end_frame(void);
void set_screen(C3D_RenderTarget * screen);
void draw_preview(ssize_t previewID, int preview_offset); void throw_error(const char * error, ErrorLevel level);
bool draw_confirm(const char * conf_msg, Entry_List_s * list, DrawMode draw_mode);
void draw_preview(C2D_Image preview, int preview_offset);
void draw_install(InstallType type); void draw_install(InstallType type);
void draw_loading_bar(u32 current, u32 max, InstallType type);
void draw_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_home(u64 start_time, u64 cur_time);
void draw_base_interface(void); void draw_base_interface(void);
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions); void draw_grid_interface(Entry_List_s * list, Instructions_s instructions, int extra_mode);
void draw_interface(Entry_List_s* list, Instructions_s instructions); void draw_interface(Entry_List_s * list, Instructions_s instructions, DrawMode draw_mode);
#endif #endif

106
include/entries_list.h Normal file
View File

@@ -0,0 +1,106 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2020 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 ENTRIES_LIST_H
#define ENTRIES_LIST_H
#include "common.h"
#include <jansson.h>
typedef enum {
SORT_NONE,
SORT_NAME,
SORT_AUTHOR,
SORT_PATH,
} SortMode;
typedef struct {
u16 path[0x106];
bool is_zip;
bool in_shuffle;
bool no_bgm_shuffle;
bool installed;
u32 placeholder_color; // doubles as not-info-loaded when == 0
json_int_t tp_download_id;
u16 name[0x41];
u16 desc[0x81];
u16 author[0x41];
} Entry_s;
typedef struct {
Tex3DS_SubTexture subtex;
u16 x, y;
} Entry_Icon_s;
typedef struct {
Entry_s * entries;
int entries_count;
int entries_capacity;
C3D_Tex icons_texture;
Entry_Icon_s * icons_info;
int previous_scroll;
int scroll;
int previous_selected;
int selected_entry;
int shuffle_count;
EntryMode mode;
int entries_per_screen_v; // rows of entries on 1 screen
int entries_per_screen_h; // columns of entries on 1 screen
int entries_loaded; // amount of entries on 1 screen
int entry_size; // size in pixels of an entry icon
SortMode current_sort;
json_int_t tp_current_page;
json_int_t tp_page_count;
char * tp_search;
const char * loading_path;
} Entry_List_s;
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);
// assumes list has been memset to 0
typedef enum InstallType_e InstallType;
Result load_entries(const char * loading_path, Entry_List_s * list, const InstallType loading_screen);
u32 load_data(const char * filename, const Entry_s * entry, char ** buf);
C2D_Image get_icon_at(Entry_List_s * list, size_t index);
// assumes list doesn't have any elements yet
void list_init_capacity(Entry_List_s * list, const int init_capacity);
// assumes list has been inited with a non zero capacity
ssize_t list_add_entry(Entry_List_s * list);
#endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -29,18 +29,41 @@
#include "common.h" #include "common.h"
FS_Archive ArchiveSD; #define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]*"
FS_Archive ArchiveHomeExt;
FS_Archive ArchiveThemeExt; extern FS_Archive ArchiveSD;
extern FS_Archive ArchiveHomeExt;
extern FS_Archive ArchiveThemeExt;
typedef struct {
u32 enable : 1;
u32 browser: 1;
u32 stereoscopic : 1;
u32 media_share : 1;
u32 online : 1;
u32 streetpass : 1;
u32 friends : 1;
u32 dsdownload : 1;
u32 shopping : 1;
u32 videos : 1;
u32 miiverse : 1;
u32 post : 1;
u32 null : 19;
u32 coppa : 1;
} Parental_Restrictions_s;
Result open_archives(void); Result open_archives(void);
Result close_archives(void); Result close_archives(void);
Result load_parental_controls(Parental_Restrictions_s *restrictions);
u32 file_to_buf(FS_Path path, FS_Archive archive, char ** buf); 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_memory_to_buf(const 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); u32 zip_file_to_buf(const char * file_name, const u16 * zip_path, char ** buf);
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char ** buf);
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char * in_buf, u32 size);
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char * buf); 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); void remake_file(FS_Path path, FS_Archive archive, u32 size);
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode);
#endif #endif

View File

@@ -1,148 +0,0 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef INSTRUCTIONS_H
#define INSTRUCTIONS_H
#include "draw.h"
#include "colors.h"
Instructions_s normal_instructions[MODE_AMOUNT] = {
{
.info_line = NULL,
.instructions = {
{
L"\uE000 Hold to install",
L"\uE001 Queue shuffle theme"
},
{
L"\uE002 Hold for more",
L"\uE003 Preview theme"
},
{
L"\uE004 Switch to splashes",
L"\uE005 Scan QR code"
},
{
L"Exit",
L"Delete from SD"
}
}
},
{
.info_line = NULL,
.instructions = {
{
L"\uE000 Install splash",
L"\uE001 Delete installed splash"
},
{
L"\uE002 Hold for more",
L"\uE003 Preview splash"
},
{
L"\uE004 Switch to themes",
L"\uE005 Scan QR code"
},
{
L"Exit",
L"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,
.instructions = {
{
L"\uE079 Normal install",
L"\uE07A Shuffle install"
},
{
L"\uE07B BGM-only install",
L"\uE07C No-BGM install"
},
{
NULL,
NULL
},
{
L"Exit",
NULL
}
}
};
Instructions_s extra_instructions[3] = {
{
.info_line = L"Release \uE002 to cancel or hold \uE006 and release \uE002 to sort",
.info_line_color = COLOR_WHITE,
.instructions = {
{
L"\uE079 Sort by name",
L"\uE07A Sort by author"
},
{
L"\uE07B Sort by filename",
NULL
},
{
NULL,
NULL
},
{
L"Exit",
NULL
}
}
},
{
.info_line = L"Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.info_line_color = COLOR_WHITE,
.instructions = {
{
L"\uE079 Jump in the list",
L"\uE07A Reload broken icons"
},
{
L"\uE07B Browse ThemePlaza",
NULL,
},
{
L"\uE004 Sorting menu",
NULL
},
{
L"Exit",
NULL
}
}
},
};
#endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -28,9 +28,16 @@
#define LOADING_H #define LOADING_H
#include "common.h" #include "common.h"
#include "entries_list.h"
#include "music.h" #include "music.h"
#include <jansson.h> #include <jansson.h>
// These values assume a horizontal orientation
#define TOP_SCREEN_WIDTH 400
#define BOTTOM_SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define SCREEN_COLOR_DEPTH 4
enum ICON_IDS_OFFSET { enum ICON_IDS_OFFSET {
ICONS_ABOVE = 0, ICONS_ABOVE = 0,
ICONS_VISIBLE, ICONS_VISIBLE,
@@ -39,14 +46,6 @@ enum ICON_IDS_OFFSET {
ICONS_OFFSET_AMOUNT, ICONS_OFFSET_AMOUNT,
}; };
typedef enum {
SORT_NONE,
SORT_NAME,
SORT_AUTHOR,
SORT_PATH,
} SortMode;
typedef struct { typedef struct {
u8 _padding1[4 + 2 + 2]; u8 _padding1[4 + 2 + 2];
@@ -60,66 +59,21 @@ typedef struct {
u16 big_icon[48 * 48]; u16 big_icon[48 * 48];
} Icon_s; } Icon_s;
typedef struct {
u16 name[0x41];
u16 desc[0x81];
u16 author[0x41];
u32 placeholder_color;
u16 path[0x106];
bool is_zip;
bool in_shuffle;
bool installed;
json_int_t tp_download_id;
} Entry_s;
typedef struct {
Entry_s * entries;
int entries_count;
ssize_t texture_id_offset;
ssize_t * icons_ids;
int previous_scroll;
int scroll;
int previous_selected;
int selected_entry;
int shuffle_count;
EntryMode mode;
int entries_per_screen_v;
int entries_per_screen_h;
int entries_loaded;
int entry_size;
SortMode current_sort;
json_int_t tp_current_page;
json_int_t tp_page_count;
char * tp_search;
} Entry_List_s;
typedef struct { typedef struct {
void ** thread_arg; void ** thread_arg;
volatile bool run_thread; volatile bool run_thread;
} Thread_Arg_s; } Thread_Arg_s;
void sort_by_name(Entry_List_s * list); void copy_texture_data(C3D_Tex * texture, const u16 * src, const Entry_Icon_s * current_icon);
void sort_by_author(Entry_List_s * list); void parse_smdh(Icon_s * icon, Entry_s * entry, const u16 * fallback_name);
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_from_buffer(char * row_pointers, u32 size, C2D_Image * preview_image, int * preview_offset, int height);
bool load_preview(Entry_List_s list, int * preview_offset); bool load_preview(const Entry_List_s * list, C2D_Image * preview_image, int * preview_offset);
Result load_audio(Entry_s, audio_s *); void free_preview(C2D_Image preview_image);
Result load_audio(const Entry_s *, audio_s *);
void load_icons_first(Entry_List_s * current_list, bool silent); void load_icons_first(Entry_List_s * current_list, bool silent);
void handle_scrolling(Entry_List_s * list); void handle_scrolling(Entry_List_s * list);
void load_icons_thread(void * void_arg); void load_icons_thread(void * void_arg);
u32 load_data(char * filename, Entry_s entry, char ** buf);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@
#include <tremor/ivorbisfile.h> #include <tremor/ivorbisfile.h>
#include <tremor/ivorbiscodec.h> #include <tremor/ivorbiscodec.h>
#define BUF_TO_READ 40960 // How much data should be buffered at a time #define BUF_TO_READ 48000 // How much data should be buffered at a time
typedef struct { typedef struct {
OggVorbis_File vf; OggVorbis_File vf;
@@ -42,11 +42,14 @@ typedef struct {
float mix[12]; float mix[12];
u8 buf_pos; u8 buf_pos;
long data_read; long data_read;
volatile bool stop;
char * filebuf; char * filebuf;
u32 filesize; u32 filesize;
volatile bool stop;
Thread playing_thread;
} audio_s; } audio_s;
void play_audio(audio_s *); void play_audio(audio_s *);
void stop_audio(audio_s **);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -28,8 +28,10 @@
#define REMOTE_H #define REMOTE_H
#include "common.h" #include "common.h"
#include "draw.h"
#include <ctype.h>
#define THEMEPLAZA_BASE_URL "https://themeplaza.eu" #define THEMEPLAZA_BASE_URL "http://themeplaza.art"
#define THEMEPLAZA_API_URL "/api/anemone/v1" #define THEMEPLAZA_API_URL "/api/anemone/v1"
#define THEMEPLAZA_BASE_API_URL THEMEPLAZA_BASE_URL THEMEPLAZA_API_URL #define THEMEPLAZA_BASE_API_URL THEMEPLAZA_BASE_URL THEMEPLAZA_API_URL
@@ -49,7 +51,18 @@
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT #define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
typedef struct {
char *result_buf;
size_t result_written;
size_t result_sz;
} curl_data;
typedef struct {
char *filename;
char *mime_type;
} curl_header;
bool themeplaza_browser(EntryMode mode); bool themeplaza_browser(EntryMode mode);
u32 http_get(const char *url, char ** filename, char ** buf); Result http_get(const char * url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -31,7 +31,7 @@
#include "loading.h" #include "loading.h"
void splash_delete(void); void splash_delete(void);
void splash_install(Entry_s splash); void splash_install(const Entry_s * splash);
void splash_check_installed(void * void_arg); void splash_check_installed(void * void_arg);

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -49,8 +49,9 @@ typedef struct {
u8 _padding1[0x13b8]; u8 _padding1[0x13b8];
ThemeEntry_s theme_entry; ThemeEntry_s theme_entry;
ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES]; ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES];
u8 _padding2[0xb]; u8 shuffle_seedA[0xb];
bool shuffle; u8 shuffle;
u8 shuffle_seedB[0xa];
} SaveData_dat_s; } SaveData_dat_s;
typedef struct { typedef struct {
@@ -69,11 +70,14 @@ typedef struct {
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES]; u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES];
} ThemeManage_bin_s; } ThemeManage_bin_s;
Result theme_install(Entry_s theme); Result theme_install(Entry_s * theme);
Result no_bgm_install(Entry_s theme); Result no_bgm_install(Entry_s * theme);
Result bgm_install(Entry_s theme); Result bgm_install(Entry_s * theme);
Result shuffle_install(Entry_List_s themes); Result shuffle_install(const Entry_List_s * themes);
Result dump_current_theme(void);
Result dump_all_themes(void);
void themes_check_installed(void * void_arg); void themes_check_installed(void * void_arg);

189
include/ui_strings.h Normal file
View File

@@ -0,0 +1,189 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2020 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 UISTRINGS_H
#define UISTRINGS_H
#include "colors.h"
#include "draw.h"
#include "common.h"
#define SPLASHES_STRINGS 2
#define THEMES_STRINGS 6
typedef struct {
const char *quit;
const char *thread_error;
const char *zip_not_theme_splash;
const char *file_not_zip;
const char *download_failed;
} Camera_Strings_s;
typedef struct {
const char *theme_mode;
const char *splash_mode;
const char *no_themes;
const char *no_splashes;
const char *qr_download;
const char *switch_splashes;
const char *switch_themes;
const char *quit;
const char *by;
const char *selected;
const char *sel;
const char *tp_theme_mode;
const char *tp_splash_mode;
const char *search;
const char *page;
const char *err_quit;
const char *warn_continue;
const char *yes_no;
const char *load_themes;
const char *load_splash;
const char *load_icons;
const char *install_splash;
const char *delete_splash;
const char *install_theme;
const char *install_shuffle;
const char *install_bgm;
const char *install_no_bgm;
const char *downloading;
const char *checking_dl;
const char *delete_sd;
const char *download_themes;
const char *download_splashes;
const char *download_preview;
const char *download_bgm;
const char *dump_single;
const char *dump_all_official;
float start_pos;
const char *shuffle;
} Draw_Strings_s;
typedef struct {
const char *illegal_input;
const char *new_or_overwrite;
const char *cancel;
const char *overwrite;
const char *rename;
const char *swkbd_fail;
const char *sd_full;
const char *fs_error;
} FS_Strings_s;
typedef struct {
const char *no_preview;
} Loading_Strings_s;
typedef struct {
const char *position_too_big;
const char *position_zero;
const char *jump_q;
const char *cancel;
const char *jump;
const char *no_theme_extdata;
const char *loading_qr;
const char *no_wifi;
const char *qr_homebrew;
const char *camera_broke;
const char *too_many_themes;
const char *not_enough_themes;
const char *uninstall_confirm;
const char *delete_confirm;
} Main_Strings_s;
typedef struct {
const char *no_results;
const char *check_wifi;
const char *new_page_big;
const char *new_page_zero;
const char *jump_page;
const char *cancel;
const char *jump;
const char *tags;
const char *search;
const char *parental_fail;
const char *zip_not_found;
const char *generic_httpc_error;
const char *http303_tp;
const char *http303;
const char *http404;
const char *http_err_url;
const char *http_errcode_generic;
const char *http401;
const char *http403;
const char *http407;
const char *http414;
const char *http418;
const char *http426;
const char *http451;
const char *http500;
const char *http502;
const char *http503;
const char *http504;
const char *http_unexpected;
} Remote_Strings_s;
typedef struct {
const char *no_splash_found;
const char *splash_disabled;
} Splashes_Strings_s;
typedef struct {
const char *no_body_found;
const char *mono_warn;
const char *illegal_char;
const char *name_folder;
const char *cancel;
const char *done;
} Themes_Strings_s;
typedef struct {
Instructions_s normal_instructions[MODE_AMOUNT];
Instructions_s install_instructions;
Instructions_s extra_instructions[3];
Camera_Strings_s camera;
Draw_Strings_s draw;
FS_Strings_s fs;
Loading_Strings_s loading;
Main_Strings_s main;
Remote_Strings_s remote;
Instructions_s remote_instructions[MODE_AMOUNT];
Instructions_s remote_extra_instructions;
Splashes_Strings_s splashes;
Themes_Strings_s themes;
} Language_s;
typedef enum {
LANGUAGE_EN,
LANGUAGE_AMOUNT,
} Language_Name;
Language_s init_strings(CFG_Language lang);
extern Language_s language;
#endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by

88
include/urls.h Normal file
View File

@@ -0,0 +1,88 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2020 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.
*/
static char from_hex(char c)
{
return isdigit(c) ? c - '0' : tolower(c) - 'a' + 10;
}
static char to_hex(char code)
{
static char hex[] = "0123456789abcdef";
return hex[code & 15];
}
// ensure caller frees
char * url_escape(char * url)
{
char * ptr = url;
char * buf = malloc(3 * strlen(url) + 1);
char * ptr_buf = buf;
while (*ptr)
{
if (isalnum((int)*ptr) || strchr("-_.~", *ptr))
*ptr_buf++ = *ptr;
else if (*ptr == ' ')
*ptr_buf++ = '+';
else
{
*ptr_buf++ = '%';
*ptr_buf++ = to_hex(*ptr >> 4);
*ptr_buf++ = to_hex(*ptr & 15);
}
ptr++;
}
*ptr_buf = '\0';
return buf;
}
// ensure caller frees
char * url_unescape(char * url)
{
char * ptr = url;
char * buf = malloc(strlen(url) + 1);
char * ptr_buf = buf;
while (*ptr)
{
if (*ptr == '%')
{
if (ptr[1] && ptr[2])
{
*ptr_buf++ = from_hex(ptr[1]) << 4 | from_hex(ptr[2]);
ptr += 2;
}
}
else if (*ptr == '+')
*ptr_buf++ = ' ';
else
*ptr_buf++ = *ptr;
ptr++;
}
*ptr_buf = '\0';
return buf;
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 149 KiB

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -27,44 +27,73 @@
#include "camera.h" #include "camera.h"
#include "quirc/quirc.h" #include "quirc/quirc.h"
#include "pp2d/pp2d/pp2d.h"
#include "draw.h" #include "draw.h"
#include "fs.h" #include "fs.h"
#include "loading.h" #include "loading.h"
#include "remote.h" #include "remote.h"
#include "ui_strings.h"
#include <archive.h> #include <archive.h>
#include <archive_entry.h> #include <archive_entry.h>
/* static void start_read(qr_data * data)
static u32 transfer_size;
static Handle event;
static struct quirc* context;
static u16 * camera_buf = NULL;
*/
void exit_qr(qr_data *data)
{ {
svcSignalEvent(data->cancel); LightLock_Lock(&data->mut);
while(!data->finished) while(data->writer_waiting || data->writer_active)
svcSleepThread(1000000); {
data->capturing = false; CondVar_WaitTimeout(&data->cond, &data->mut, 1000000);
free(data->camera_buffer);
free(data->texture_buffer);
quirc_destroy(data->context);
free(data);
} }
void capture_cam_thread(void *arg) AtomicIncrement(&data->num_readers_active);
LightLock_Unlock(&data->mut);
}
static void stop_read(qr_data * data)
{
LightLock_Lock(&data->mut);
AtomicDecrement(&data->num_readers_active);
if(data->num_readers_active == 0)
{
CondVar_Signal(&data->cond);
}
LightLock_Unlock(&data->mut);
}
static void start_write(qr_data * data)
{
LightLock_Lock(&data->mut);
data->writer_waiting = true;
while(data->num_readers_active)
{
CondVar_WaitTimeout(&data->cond, &data->mut, 1000000);
}
data->writer_waiting = false;
data->writer_active = true;
LightLock_Unlock(&data->mut);
}
static void stop_write(qr_data * data)
{
LightLock_Lock(&data->mut);
data->writer_active = false;
CondVar_Broadcast(&data->cond);
LightLock_Unlock(&data->mut);
}
static void capture_cam_thread(void * arg)
{ {
qr_data * data = (qr_data *) arg; qr_data * data = (qr_data *) arg;
Handle events[3] = {0};
events[0] = data->cancel;
u32 transferUnit;
u16 *buffer = calloc(1, 400 * 240 * sizeof(u16)); Handle cam_events[3] = {0};
cam_events[0] = data->event_stop;
u32 transferUnit;
const u32 bufsz = 400 * 240 * sizeof(u16);
u16 * buffer = linearAlloc(bufsz);
camInit(); camInit();
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A); CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A);
CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A); CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A);
@@ -73,36 +102,42 @@ void capture_cam_thread(void *arg)
CAMU_SetAutoExposure(SELECT_OUT1, true); CAMU_SetAutoExposure(SELECT_OUT1, true);
CAMU_SetAutoWhiteBalance(SELECT_OUT1, true); CAMU_SetAutoWhiteBalance(SELECT_OUT1, true);
CAMU_Activate(SELECT_OUT1); CAMU_Activate(SELECT_OUT1);
CAMU_GetBufferErrorInterruptEvent(&events[2], PORT_CAM1); CAMU_GetBufferErrorInterruptEvent(&cam_events[2], PORT_CAM1);
CAMU_SetTrimming(PORT_CAM1, false); CAMU_SetTrimming(PORT_CAM1, false);
CAMU_GetMaxBytes(&transferUnit, 400, 240); CAMU_GetMaxBytes(&transferUnit, 400, 240);
CAMU_SetTransferBytes(PORT_CAM1, transferUnit, 400, 240); CAMU_SetTransferBytes(PORT_CAM1, transferUnit, 400, 240);
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), (s16) transferUnit); CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
CAMU_StartCapture(PORT_CAM1); CAMU_StartCapture(PORT_CAM1);
bool cancel = false; bool cancel = false;
while (!cancel) while (!cancel)
{ {
s32 index = 0; s32 index = 0;
svcWaitSynchronizationN(&index, events, 3, false, U64_MAX); svcWaitSynchronizationN(&index, cam_events, 3, false, U64_MAX);
switch(index) { switch(index) {
case 0: case 0:
DEBUG("Cancel event received\n");
cancel = true; cancel = true;
break; break;
case 1: case 1:
svcCloseHandle(events[1]); svcCloseHandle(cam_events[1]);
events[1] = 0; cam_events[1] = 0;
svcWaitSynchronization(data->mutex, U64_MAX);
memcpy(data->camera_buffer, buffer, 400 * 240 * sizeof(u16)); start_write(data);
GSPGPU_FlushDataCache(data->camera_buffer, 400 * 240 * sizeof(u16)); memcpy(data->camera_buffer, buffer, bufsz);
svcReleaseMutex(data->mutex); data->any_update = true;
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit); stop_write(data);
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
break; break;
case 2: case 2:
svcCloseHandle(events[1]); svcCloseHandle(cam_events[1]);
events[1] = 0; cam_events[1] = 0;
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit); CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
CAMU_StartCapture(PORT_CAM1); CAMU_StartCapture(PORT_CAM1);
break; break;
default: default:
@@ -120,89 +155,198 @@ void capture_cam_thread(void *arg)
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_Activate(SELECT_NONE); CAMU_Activate(SELECT_NONE);
camExit(); camExit();
free(buffer);
for(int i = 0; i < 3; i++) { linearFree(buffer);
if(events[i] != 0) { for(int i = 1; i < 3; i++)
svcCloseHandle(events[i]); {
events[i] = 0; if(cam_events[i] != 0) {
svcCloseHandle(cam_events[i]);
cam_events[i] = 0;
} }
} }
svcCloseHandle(data->mutex);
data->finished = true;
}
bool start_capture_cam(qr_data *data) LightEvent_Signal(&data->event_cam_info);
}
static void update_ui(void * arg)
{ {
data->mutex = 0; qr_data * data = (qr_data *) arg;
data->cancel = 0; C3D_Tex tex;
svcCreateEvent(&data->cancel, RESET_STICKY);
svcCreateMutex(&data->mutex, false); static const Tex3DS_SubTexture subt3x = { 400, 240, 0.0f, 1.0f, 400.0f/512.0f, 1.0f - (240.0f/256.0f) };
if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL) C3D_TexInit(&tex, 512, 256, GPU_RGB565);
C3D_TexSetFilter(&tex, GPU_LINEAR, GPU_LINEAR);
while(svcWaitSynchronization(data->event_stop, 2 * 1000 * 1000ULL) == 0x09401BFE) // timeout of 2ms occured, still have 14 for copy and render
{
draw_base_interface();
// Untiled texture loading code adapted from FBI
start_read(data);
if(data->any_update)
{
for(u32 y = 0; y < 240; y++) {
const u32 srcPos = y * 400;
for(u32 x = 0; x < 400; x++) {
const 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)));
((u16 *)tex.data)[dstPos] = data->camera_buffer[srcPos + x];
}
}
data->any_update = false;
}
stop_read(data);
C2D_DrawImageAt((C2D_Image){ &tex, &subt3x }, 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], language.camera.quit);
end_frame();
}
C3D_TexDelete(&tex);
LightEvent_Signal(&data->event_ui_info);
}
static bool start_capture_cam(qr_data * data)
{
if((data->cam_thread = threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, false)) == NULL)
{
throw_error(language.camera.thread_error, ERROR_LEVEL_ERROR);
LightEvent_Signal(&data->event_cam_info);
LightEvent_Signal(&data->event_ui_info);
return false; return false;
}
if((data->ui_thread = threadCreate(update_ui, data, 0x10000, 0x1A, 1, false)) == NULL)
{
LightEvent_Signal(&data->event_ui_info);
return false;
}
return true; return true;
} }
void update_qr(qr_data *data) static bool update_qr(qr_data * data, struct quirc_data * scan_data)
{ {
hidScanInput();
if (hidKeysDown() & (KEY_R | KEY_B | KEY_TOUCH)) {
exit_qr(data);
return;
}
if (!data->capturing) {
if(start_capture_cam(data))
data->capturing = true;
else {
exit_qr(data);
return;
}
}
if (data->finished) {
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 w;
int h; int h;
u8 * image = (u8 *)quirc_begin(data->context, &w, &h); u8 * image = (u8 *)quirc_begin(data->context, &w, &h);
svcWaitSynchronization(data->mutex, U64_MAX);
for (ssize_t x = 0; x < w; x++) { start_read(data);
for (ssize_t y = 0; y < h; y++) { for (int y = 0; y < h; y++) {
u16 px = data->camera_buffer[y * 400 + x]; const int actual_y = y * w;
image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3); for (int x = 0; x < w; x++) {
const int actual_off = actual_y + x;
const u16 px = data->camera_buffer[actual_off];
image[actual_off] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
} }
} }
svcReleaseMutex(data->mutex); stop_read(data);
quirc_end(data->context); quirc_end(data->context);
if(quirc_count(data->context) > 0) if(quirc_count(data->context) > 0)
{ {
struct quirc_code code; struct quirc_code code;
struct quirc_data scan_data;
quirc_extract(data->context, 0, &code); quirc_extract(data->context, 0, &code);
if (!quirc_decode(&code, &scan_data)) if (!quirc_decode(&code, scan_data))
{ {
exit_qr(data); return true;
}
}
return false;
}
static void start_qr(qr_data * data)
{
svcCreateEvent(&data->event_stop, RESET_STICKY);
LightEvent_Init(&data->event_cam_info, RESET_STICKY);
LightEvent_Init(&data->event_ui_info, RESET_STICKY);
LightLock_Init(&data->mut);
CondVar_Init(&data->cond);
data->cam_thread = NULL;
data->ui_thread = NULL;
data->any_update = false;
data->context = quirc_new();
quirc_resize(data->context, 400, 240);
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
}
static void exit_qr(qr_data * data)
{
svcSignalEvent(data->event_stop);
LightEvent_Wait(&data->event_ui_info);
LightEvent_Clear(&data->event_ui_info);
if(data->ui_thread != NULL)
{
threadJoin(data->ui_thread, U64_MAX);
threadFree(data->ui_thread);
data->ui_thread = NULL;
}
LightEvent_Wait(&data->event_cam_info);
LightEvent_Clear(&data->event_cam_info);
if(data->cam_thread != NULL)
{
threadJoin(data->cam_thread, U64_MAX);
threadFree(data->cam_thread);
data->cam_thread = NULL;
}
free(data->camera_buffer);
data->camera_buffer = NULL;
svcCloseHandle(data->event_stop);
data->event_stop = 0;
}
bool init_qr(void)
{
qr_data data;
memset(&data, 0, sizeof(data));
start_qr(&data);
struct quirc_data * scan_data = calloc(1, sizeof(struct quirc_data));
const bool ready = start_capture_cam(&data);
bool finished = !ready;
while(!finished)
{
hidScanInput();
if (hidKeysDown() & (KEY_R | KEY_B | KEY_TOUCH))
{
break;
}
finished = update_qr(&data, scan_data);
svcSleepThread(50 * 1000 * 1000ULL); // only scan every 50ms
}
exit_qr(&data);
bool success = false;
if(finished && ready)
{
draw_install(INSTALL_DOWNLOAD); draw_install(INSTALL_DOWNLOAD);
char * zip_buf = NULL; char * zip_buf = NULL;
char * filename = NULL; char * filename = NULL;
u32 zip_size = http_get((char*)scan_data.payload, &filename, &zip_buf); u32 zip_size;
Result res = http_get((char*)scan_data->payload, &filename, &zip_buf, &zip_size, INSTALL_DOWNLOAD, "application/zip; application/x-zip-compressed");
if (R_FAILED(res))
{
free(filename);
free(zip_buf);
return false;
}
else if (R_DESCRIPTION(res) == RD_NO_DATA || R_DESCRIPTION(res) == RD_CANCEL_REQUESTED)
{
free(filename);
return true;
}
if(zip_size != 0) if(zip_size != 0)
{ {
@@ -249,51 +393,29 @@ void update_qr(qr_data *data)
if(mode != MODE_AMOUNT) if(mode != MODE_AMOUNT)
{ {
char path_to_file[0x107] = {0}; save_zip_to_sd(filename, zip_size, zip_buf, mode);
sprintf(path_to_file, "%s%s", main_paths[mode], filename); success = true;
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
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 else
{ {
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING); throw_error(language.camera.zip_not_theme_splash, ERROR_LEVEL_WARNING);
} }
} }
else else
{ {
throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING); throw_error(language.camera.file_not_zip, ERROR_LEVEL_WARNING);
} }
free(zip_buf);
} }
else else
{ {
throw_error("Download failed.", ERROR_LEVEL_WARNING); throw_error(language.camera.download_failed, ERROR_LEVEL_WARNING);
} }
free(filename); free(filename);
free(zip_buf);
}
} }
free(scan_data);
quirc_destroy(data.context);
return success;
} }
bool init_qr(void)
{
qr_data *data = calloc(1, sizeof(qr_data));
data->capturing = false;
data->finished = false;
data->context = quirc_new();
quirc_resize(data->context, 400, 240);
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
data->texture_buffer = calloc(1, 400 * 240 * sizeof(u32));
while (!data->finished) update_qr(data);
return (bool)data->success;
}

40
source/colors.c Normal file
View File

@@ -0,0 +1,40 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2020 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);
}

129
source/conversion.c Normal file
View File

@@ -0,0 +1,129 @@
#include "conversion.h"
#include "draw.h"
#include <png.h>
static void rotate_agbr_counterclockwise(char ** bufp, size_t size, size_t width)
{
uint32_t * buf = (uint32_t*)*bufp;
uint32_t * out = malloc(size);
size_t pixel_count = (size/4);
size_t height = pixel_count/width;
for (uint32_t h = 0; h < height; ++h)
{
for (uint32_t w = 0; w < width; ++w)
{
size_t buf_index = (w + (h * width));
size_t out_index = (height * (width-1)) - (height * w) + h;
out[out_index] = buf[buf_index];
}
}
free(*bufp);
*bufp = (char*)out;
}
size_t bin_to_abgr(char ** bufp, size_t size)
{
size_t out_size = (size / 3) * 4;
char* buf = malloc(out_size);
for (size_t i = 0, j = 0; i < size; i+=3, j+=4)
{
(buf+j)[0] = 0xFF; // A
(buf+j)[1] = (*bufp+i)[0]; // B
(buf+j)[2] = (*bufp+i)[1]; // G
(buf+j)[3] = (*bufp+i)[2]; // R
}
free(*bufp);
*bufp = buf;
// splash screens contain the raw framebuffer to put on the screen
// because the screens are mounted at a 90 degree angle we also need to rotate
// the output
rotate_agbr_counterclockwise(bufp, out_size, 240);
return out_size;
}
size_t png_to_abgr(char ** bufp, size_t size, u32 *height)
{
size_t out_size = 0;
if(size < 8 || png_sig_cmp((png_bytep)*bufp, 0, 8))
{
throw_error("Invalid preview.png", ERROR_LEVEL_WARNING);
return out_size;
}
uint32_t * buf = (uint32_t*)*bufp;
FILE * fp = fmemopen(buf, size, "rb");;
png_bytep * row_pointers = NULL;
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
png_init_io(png, fp);
png_read_info(png, info);
u32 width = png_get_image_width(png, info);
*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_add_alpha(png, 0xFF, PNG_FILLER_BEFORE);
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);
row_pointers = malloc(sizeof(png_bytep) * *height);
out_size = sizeof(u32) * (width * *height);
u32 * out = malloc(out_size);
for(u32 y = 0; y < *height; y++)
{
row_pointers[y] = (png_bytep)(out + (width * y));
}
png_read_image(png, row_pointers);
png_destroy_read_struct(&png, &info, NULL);
if (fp) fclose(fp);
if (row_pointers) free(row_pointers);
free(*bufp);
*bufp = (char*)out;
return out_size;
}

File diff suppressed because it is too large Load Diff

231
source/entries_list.c Normal file
View File

@@ -0,0 +1,231 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2020 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 "entries_list.h"
#include "loading.h"
#include "draw.h"
#include "fs.h"
#include "unicode.h"
void delete_entry(Entry_s * entry, bool is_file)
{
if(is_file)
FSUSER_DeleteFile(ArchiveSD, fsMakePath(PATH_UTF16, entry->path));
else
FSUSER_DeleteDirectoryRecursively(ArchiveSD, fsMakePath(PATH_UTF16, entry->path));
}
u32 load_data(const char * filename, const Entry_s * entry, char ** buf)
{
if(entry->is_zip)
{
return zip_file_to_buf(filename + 1, entry->path, buf); //the first character will always be '/' because of the other case
}
else
{
u16 path[0x106] = {0};
strucat(path, entry->path);
struacat(path, filename);
return file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, buf);
}
}
C2D_Image get_icon_at(Entry_List_s * list, size_t index)
{
return (C2D_Image){
.tex = &list->icons_texture,
.subtex = &list->icons_info[index].subtex,
};
}
static int compare_entries_base(const Entry_s * const a, const Entry_s * const b)
{
// entry->placeholder_color == 0 means it is not filled (no name, author, description)
// if a is filled and b is filled, return == 0
// if a is not filled and b is not filled, return == 0
// if a is not filled, return < 0
// if b is an unfilled entry, return > 0
return ((int)(a->placeholder_color != 0)) - ((int)(b->placeholder_color != 0));
}
typedef int (*sort_comparator)(const void *, const void *);
static int compare_entries_by_name(const void * a, const void * b)
{
const Entry_s * const entry_a = (const Entry_s *)a;
const Entry_s * const entry_b = (const Entry_s *)b;
const int base = compare_entries_base(entry_a, entry_b);
if(base)
return base;
return memcmp(entry_a->name, entry_b->name, 0x40 * sizeof(u16));
}
static int compare_entries_by_author(const void * a, const void * b)
{
const Entry_s * const entry_a = (const Entry_s *)a;
const Entry_s * const entry_b = (const Entry_s *)b;
const int base = compare_entries_base(entry_a, entry_b);
if(base)
return base;
return memcmp(entry_a->author, entry_b->author, 0x40 * sizeof(u16));
}
static int compare_entries_by_filename(const void * a, const void * b)
{
const Entry_s * const entry_a = (const Entry_s *)a;
const Entry_s * const entry_b = (const Entry_s *)b;
const int base = compare_entries_base(entry_a, entry_b);
if(base)
return base;
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;
}
#define LOADING_DIR_ENTRIES_COUNT 16
static FS_DirectoryEntry loading_dir_entries[LOADING_DIR_ENTRIES_COUNT];
Result load_entries(const char * loading_path, Entry_List_s * list, const InstallType loading_screen)
{
Handle dir_handle;
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, loading_path));
if(R_FAILED(res))
{
DEBUG("Failed to open folder: %s\n", loading_path);
return res;
}
list_init_capacity(list, LOADING_DIR_ENTRIES_COUNT);
u32 entries_read = LOADING_DIR_ENTRIES_COUNT;
while(entries_read == LOADING_DIR_ENTRIES_COUNT)
{
res = FSDIR_Read(dir_handle, &entries_read, LOADING_DIR_ENTRIES_COUNT, loading_dir_entries);
if(R_FAILED(res))
break;
for(u32 i = 0; i < entries_read; ++i)
{
const FS_DirectoryEntry * const dir_entry = &loading_dir_entries[i];
const bool is_zip = !strcmp(dir_entry->shortExt, "ZIP");
if(!(dir_entry->attributes & FS_ATTRIBUTE_DIRECTORY) && !is_zip)
continue;
const ssize_t new_entry_index = list_add_entry(list);
if(new_entry_index < 0)
{
// out of memory: still allow use of currently loaded entries.
// Many things might die, depending on the heap layout after
entries_read = 0;
break;
}
Entry_s * const current_entry = &list->entries[new_entry_index];
memset(current_entry, 0, sizeof(Entry_s));
struacat(current_entry->path, loading_path);
strucat(current_entry->path, dir_entry->name);
current_entry->is_zip = is_zip;
}
}
FSDIR_Close(dir_handle);
list->loading_path = loading_path;
const int loading_bar_ticks = list->entries_count / 10;
for(int i = 0, j = 0; i < list->entries_count; ++i)
{
// replaces (i % loading_bar_ticks) == 0
if(++j >= loading_bar_ticks)
{
j = 0;
draw_loading_bar(i, list->entries_count, loading_screen);
}
Entry_s * const current_entry = &list->entries[i];
char * buf = NULL;
u32 buflen = load_data("/info.smdh", current_entry, &buf);
parse_smdh(buflen == sizeof(Icon_s) ? (Icon_s *)buf : NULL, current_entry, current_entry->path + strlen(loading_path));
free(buf);
}
return res;
}
void list_init_capacity(Entry_List_s * list, const int init_capacity)
{
list->entries = malloc(init_capacity * sizeof(Entry_s));
list->entries_capacity = init_capacity;
}
#define LIST_CAPACITY_THRESHOLD 512
ssize_t list_add_entry(Entry_List_s * list)
{
if(list->entries_count == list->entries_capacity)
{
int next_capacity = list->entries_capacity;
// expand by doubling until we hit LIST_CAPACITY_THRESHOLD
// then simply increment by that, to have less extra space leftover
if(next_capacity < LIST_CAPACITY_THRESHOLD)
{
next_capacity *= 2;
}
else
{
next_capacity += LIST_CAPACITY_THRESHOLD;
}
Entry_s * const new_list = realloc(list->entries, next_capacity * sizeof(Entry_s));
if(new_list == NULL)
{
return -1;
}
list->entries = new_list;
list->entries_capacity = next_capacity;
}
return list->entries_count++;
}

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -27,11 +27,17 @@
#include <strings.h> #include <strings.h>
#include "fs.h" #include "fs.h"
#include "draw.h"
#include "unicode.h" #include "unicode.h"
#include "ui_strings.h"
#include <archive.h> #include <archive.h>
#include <archive_entry.h> #include <archive_entry.h>
FS_Archive ArchiveSD;
FS_Archive ArchiveHomeExt;
FS_Archive ArchiveThemeExt;
Result open_archives(void) Result open_archives(void)
{ {
romfsInit(); romfsInit();
@@ -59,6 +65,10 @@ Result open_archives(void)
archive1 = 0x000002ce; archive1 = 0x000002ce;
archive2 = 0x00000098; archive2 = 0x00000098;
break; break;
case 5:
archive1 = 0x000002cf;
archive2 = 0x000000a9;
break;
default: default:
archive1 = 0x00; archive1 = 0x00;
archive2 = 0x00; archive2 = 0x00;
@@ -102,6 +112,17 @@ Result close_archives(void)
return 0; return 0;
} }
Result load_parental_controls(Parental_Restrictions_s *restrictions)
{
char parental_data[0xC0] = {0};
Result res;
if (R_FAILED(res = CFGU_GetConfigInfoBlk2(0xC0, 0x000C0000, &parental_data))) return res;
memcpy(restrictions, parental_data, 4);
return 0;
}
u32 file_to_buf(FS_Path path, FS_Archive archive, char ** buf) u32 file_to_buf(FS_Path path, FS_Archive archive, char ** buf)
{ {
Handle file; Handle file;
@@ -119,7 +140,7 @@ u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
return (u32)size; return (u32)size;
} }
static u32 zip_to_buf(struct archive *a, char *file_name, char ** buf) static u32 zip_to_buf(struct archive * a, const char * file_name, char ** buf)
{ {
struct archive_entry * entry; struct archive_entry * entry;
@@ -147,7 +168,7 @@ static u32 zip_to_buf(struct archive *a, char *file_name, char ** buf)
return (u32)file_size; return (u32)file_size;
} }
u32 zip_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char ** buf) u32 zip_memory_to_buf(const char * file_name, void * zip_memory, size_t zip_size, char ** buf)
{ {
struct archive * a = archive_read_new(); struct archive * a = archive_read_new();
archive_read_support_format_zip(a); archive_read_support_format_zip(a);
@@ -162,7 +183,7 @@ u32 zip_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char
return zip_to_buf(a, file_name, buf); return zip_to_buf(a, file_name, buf);
} }
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf) u32 zip_file_to_buf(const char * file_name, const u16 * zip_path, char ** buf)
{ {
ssize_t len = strulen(zip_path, 0x106); ssize_t len = strulen(zip_path, 0x106);
char * path = calloc(sizeof(char), len * sizeof(u16)); char * path = calloc(sizeof(char), len * sizeof(u16));
@@ -172,6 +193,7 @@ u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf)
archive_read_support_format_zip(a); archive_read_support_format_zip(a);
int r = archive_read_open_filename(a, path, 0x4000); int r = archive_read_open_filename(a, path, 0x4000);
free(path);
if(r != ARCHIVE_OK) if(r != ARCHIVE_OK)
{ {
DEBUG("Invalid zip being opened\n"); DEBUG("Invalid zip being opened\n");
@@ -191,6 +213,148 @@ Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf)
return 0; return 0;
} }
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char ** buf)
{
Handle handle;
Result res = 0;
if (R_FAILED(res = FSUSER_OpenFile(&handle, archive, file_name, FS_OPEN_READ, 0))) {
DEBUG("%lu\n", res);
return 0;
}
u64 size;
FSFILE_GetSize(handle, &size);
char * temp_buf = NULL;
if(size != 0)
{
temp_buf = calloc(1, size);
FSFILE_Read(handle, NULL, 0, temp_buf, size);
}
FSFILE_Close(handle);
if (temp_buf[0] != 0x11) {
free(temp_buf);
return 0;
}
u32 output_size = temp_buf[1] | ((temp_buf[2] << 8) & 0xFF00) | ((temp_buf[3] << 16) & 0xFF0000);
printf("%ld\n", output_size);
*buf = calloc(1, output_size);
u32 pos = 4;
u32 cur_written = 0;
u8 counter = 0;
u8 mask = 0;
while (cur_written < output_size)
{
if (counter == 0) // read mask
{
mask = temp_buf[pos++];
counter++;
continue;
}
if ((mask >> (8 - counter)) & 0x01) // compressed block
{
int len = 0;
int disp = 0;
switch (temp_buf[pos] >> 4)
{
case 0:
len = temp_buf[pos++] << 4;
len |= temp_buf[pos] >> 4;
len += 0x11;
break;
case 1:
len = (temp_buf[pos++] & 0x0F) << 12;
len |= temp_buf[pos++] << 4;
len |= temp_buf[pos] >> 4;
len += 0x111;
break;
default:
len = (temp_buf[pos] >> 4) + 1;
}
disp = (temp_buf[pos++] & 0x0F) << 8;
disp |= temp_buf[pos++];
for (int i = 0; i < len; ++i)
{
*(*buf + cur_written + i) = *(*buf + cur_written - disp - 1 + i);
}
cur_written += len;
}
else // byte literal
{
*(*buf + cur_written) = temp_buf[pos++];
cur_written++;
}
if (++counter > 8) counter = 0;
}
free(temp_buf);
return cur_written;
}
// This is an awful algorithm to "compress" LZ11 data.
// We do this instead of actual LZ11 compression because of time -
// LZ11 requires a lot of mem searching which is painfully slow on 3DS.
// This process is nearly instant but means the resulting file is actually larger
// than the input file. I don't think this is a problem as the only file we compress like this
// is the theme data installed to extdata in some rare cases, which means only 1 file at most
// is ever compressed like this. I don't think 400 KB is that big a sacrifice for probably
// half a minute or so of time save - I may change my mind on this in the future, especially
// if i figure out a dynamic programming algorithm which ends up being significantly
// faster. Otherwise, I think this is probably a fine implementation.
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char * in_buf, u32 size)
{
char * output_buf = calloc(1, size * 2);
u32 output_size = 0;
u32 mask_pos = 0;
u32 bytes_processed = 0;
u8 counter = 0;
if (output_buf == NULL) return 0;
// Set header data for the LZ11 file - 0x11 is version (LZ11), next 3 bytes are size
output_buf[0] = 0x11;
output_buf[3] = (size & 0xFF0000) >> 16;
output_buf[2] = (size & 0xFF00) >> 8;
output_buf[1] = (size & 0xFF);
output_size += 4;
while (bytes_processed < size)
{
if (counter == 0)
{
mask_pos = output_size++;
output_buf[mask_pos] = 0;
}
output_buf[output_size++] = in_buf[bytes_processed++];
if (++counter == 8) counter = 0;
}
buf_to_file(output_size, path, archive, output_buf);
free(output_buf);
return output_size;
}
void remake_file(FS_Path path, FS_Archive archive, u32 size) void remake_file(FS_Path path, FS_Archive archive, u32 size)
{ {
Handle handle; Handle handle;
@@ -200,4 +364,122 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
FSUSER_DeleteFile(archive, path); FSUSER_DeleteFile(archive, path);
} }
FSUSER_CreateFile(archive, path, 0, size); FSUSER_CreateFile(archive, path, 0, size);
char * buf = calloc(size, 1);
buf_to_file(size, path, archive, buf);
free(buf);
}
static SwkbdCallbackResult fat32filter(void * user, const char ** ppMessage, const char * text, size_t textlen)
{
(void)textlen;
(void)user;
*ppMessage = language.fs.illegal_input;
if(strpbrk(text, ILLEGAL_CHARS))
{
DEBUG("illegal filename: %s\n", text);
return SWKBD_CALLBACK_CONTINUE;
}
return SWKBD_CALLBACK_OK;
}
// assumes the input buffer is a ZIP. if it isn't, why are you calling this?
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode)
{
static char path_to_file[32761]; // FAT32 paths can be quite long.
const int max_chars = 250;
char new_filename[max_chars + 5]; // .zip + \0
renamed:
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
// filter out characters illegal in FAT32 filenames
char * curr_filename = path_to_file + strlen(main_paths[mode]);
char * illegal_char = curr_filename;
while ((illegal_char = strpbrk(illegal_char, ILLEGAL_CHARS)))
{
DEBUG("Illegal char found in filename: %c\n", *illegal_char);
if (*illegal_char == '.')
{
// skip initial . (this is allowed)
if (illegal_char == curr_filename)
continue;
// skip extension delimiter
if (strpbrk(illegal_char + 1, ".") == NULL)
{
illegal_char++;
continue;
}
}
*illegal_char = '-';
}
// ensure the extension is .zip
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
DEBUG("path: %s\n", path_to_file);
u16 utf16path[0x106] = {0};
utf8_to_utf16(utf16path, (u8 *) path_to_file, 0x106);
FS_Path path = fsMakePath(PATH_UTF16, utf16path);
// check if file already exists, and if it does, prompt the user
// to overwrite or change name (or exit)
Result res = FSUSER_CreateFile(ArchiveSD, path, 0, size);
if (R_FAILED(res))
{
if (res == (long)0xC82044BE)
{
DEBUG("File already exists\n");
SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 3, max_chars / 2);
swkbdSetHintText(&swkbd, language.fs.new_or_overwrite);
swkbdSetFeatures(&swkbd, SWKBD_PREDICTIVE_INPUT | SWKBD_DARKEN_TOP_SCREEN);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, language.fs.cancel, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_MIDDLE, language.fs.overwrite, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, language.fs.rename, true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, SWKBD_FILTER_CALLBACK, -1);
swkbdSetFilterCallback(&swkbd, &fat32filter, NULL);
SwkbdButton button = swkbdInputText(&swkbd, new_filename, max_chars);
switch (button)
{
case SWKBD_BUTTON_RIGHT:
DEBUG("Renaming to %s\n", new_filename);
strcat(new_filename, ".zip");
filename = new_filename;
goto renamed;
case SWKBD_BUTTON_MIDDLE:
// we good
DEBUG("Overwriting %s\n", filename);
break;
case SWKBD_BUTTON_LEFT:
// do nothing
DEBUG("File rename cancelled\n");
return;
case SWKBD_BUTTON_NONE:
DEBUG("SWKBD broke wtf??? :- %x\n", swkbdGetResult(&swkbd));
return throw_error(language.fs.swkbd_fail, ERROR_LEVEL_WARNING);
}
}
else if (res == (long)0xC86044D2)
{
DEBUG("SD card is full\n");
return throw_error(language.fs.sd_full, ERROR_LEVEL_WARNING);
}
else
{
DEBUG("error: %lx\n", res);
return throw_error(language.fs.fs_error, ERROR_LEVEL_ERROR);
}
}
DEBUG("Saving to SD: %s\n", path_to_file);
remake_file(path, ArchiveSD, size);
buf_to_file(size, path, ArchiveSD, buf);
} }

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -25,179 +25,57 @@
*/ */
#include "loading.h" #include "loading.h"
#include "pp2d/pp2d/pp2d.h"
#include "fs.h" #include "fs.h"
#include "unicode.h" #include "unicode.h"
#include "music.h" #include "music.h"
#include "draw.h" #include "draw.h"
#include "conversion.h"
#include "ui_strings.h"
void delete_entry(Entry_s * entry, bool is_file) #include <png.h>
void copy_texture_data(C3D_Tex * texture, const u16 * src, const Entry_Icon_s * current_icon)
{ {
if(is_file) // pointer to rgb565, offset by the number of rows and columns specified by current_icon
FSUSER_DeleteFile(ArchiveSD, fsMakePath(PATH_UTF16, entry->path)); // (reminder that this is z order curve storage)
else u16 * dest = ((u16 *)texture->data) + (current_icon->y * texture->width) + (current_icon->x * 8);
FSUSER_DeleteDirectoryRecursively(ArchiveSD, fsMakePath(PATH_UTF16, entry->path)); for (int j = 0; j < 48; j += 8)
{
memcpy(dest, src, 48 * 8 * sizeof(u16));
src += 48 * 8;
dest += texture->width * 8;
}
GSPGPU_InvalidateDataCache(texture->data, texture->size);
} }
u32 load_data(char * filename, Entry_s entry, char ** buf) void parse_smdh(Icon_s * icon, Entry_s * entry, const u16 * fallback_name)
{ {
if(entry.is_zip) if(icon == NULL)
{ {
return zip_file_to_buf(filename+1, entry.path, buf); //the first character will always be '/' because of the other case
}
else
{
u16 path[0x106] = {0};
strucat(path, entry.path);
struacat(path, filename);
return file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, buf);
}
}
static void parse_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); memcpy(entry->name, fallback_name, 0x80);
utf8_to_utf16(entry->desc, (u8 *)"No description", 0x100); utf8_to_utf16(entry->desc, (u8 *)"No description", 0x100);
utf8_to_utf16(entry->author, (u8 *)"Unknown author", 0x80); utf8_to_utf16(entry->author, (u8 *)"Unknown author", 0x80);
entry->placeholder_color = RGBA8(rand() % 255, rand() % 255, rand() % 255, 255); entry->placeholder_color = C2D_Color32(rand() % 255, rand() % 255, rand() % 255, 255);
return; return;
} }
memcpy(entry->name, smdh->name, 0x40*sizeof(u16)); memcpy(entry->name, icon->name, 0x40 * sizeof(u16));
memcpy(entry->desc, smdh->desc, 0x80*sizeof(u16)); memcpy(entry->desc, icon->desc, 0x80 * sizeof(u16));
memcpy(entry->author, smdh->author, 0x40*sizeof(u16)); memcpy(entry->author, icon->author, 0x40 * sizeof(u16));
entry->placeholder_color = 0;
} }
static void load_smdh_icon(Entry_s entry, const ssize_t textureID) static Icon_s * load_entry_icon(const Entry_s * entry)
{ {
pp2d_free_texture(textureID);
char * info_buffer = NULL; char * info_buffer = NULL;
u64 size = load_data("/info.smdh", entry, &info_buffer); u32 size = load_data("/info.smdh", entry, &info_buffer);
if(!size) return; if(size != sizeof(Icon_s))
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); free(info_buffer);
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height); return NULL;
free(image);
} }
typedef int (*sort_comparator)(const void *, const void *); return (Icon_s *)info_buffer;
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 int compare_entries_by_author(const void * a, const void * b)
{
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;
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, loading_path));
if(R_FAILED(res))
{
DEBUG("Failed to open folder: %s\n", loading_path);
return res;
}
u32 entries_read = 1;
while(entries_read)
{
FS_DirectoryEntry dir_entry = {0};
res = FSDIR_Read(dir_handle, &entries_read, 1, &dir_entry);
if(R_FAILED(res) || entries_read == 0)
break;
if(!(dir_entry.attributes & FS_ATTRIBUTE_DIRECTORY) && strcmp(dir_entry.shortExt, "ZIP"))
continue;
list->entries_count++;
Entry_s * new_list = realloc(list->entries, list->entries_count * sizeof(Entry_s));
if(new_list == NULL)
{
free(list->entries);
list->entries = NULL;
res = -1;
DEBUG("break\n");
break;
}
else
list->entries = new_list;
Entry_s * current_entry = &(list->entries[list->entries_count-1]);
memset(current_entry, 0, sizeof(Entry_s));
struacat(current_entry->path, loading_path);
strucat(current_entry->path, dir_entry.name);
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
parse_smdh(current_entry, dir_entry.name);
}
FSDIR_Close(dir_handle);
return res;
} }
void load_icons_first(Entry_List_s * list, bool silent) void load_icons_first(Entry_List_s * list, bool silent)
@@ -223,36 +101,40 @@ void load_icons_first(Entry_List_s * list, bool silent)
endi = starti + list->entries_loaded * ICONS_OFFSET_AMOUNT; endi = starti + list->entries_loaded * ICONS_OFFSET_AMOUNT;
} }
list->icons_ids = calloc(endi-starti, sizeof(ssize_t)); for(int entry_i = starti, icon_i = 0; entry_i < endi; ++entry_i, ++icon_i)
ssize_t * icons_ids = list->icons_ids;
ssize_t id = list->texture_id_offset;
for(int i = starti; i < endi; i++, id++)
{ {
int offset = i; if(!silent)
draw_loading_bar(icon_i, endi-starti, INSTALL_LOADING_ICONS);
int offset = entry_i;
if(offset < 0) if(offset < 0)
offset += list->entries_count; offset += list->entries_count;
if(offset >= list->entries_count) if(offset >= list->entries_count)
offset -= list->entries_count; offset -= list->entries_count;
Entry_s current_entry = list->entries[offset]; Entry_s * const current_entry = &list->entries[offset];
load_smdh_icon(current_entry, id); Icon_s * const smdh = load_entry_icon(current_entry);
if(smdh != NULL)
icons_ids[i-starti] = id; {
if(current_entry->placeholder_color == 0)
parse_smdh(smdh, current_entry, current_entry->path + strlen(list->loading_path));
copy_texture_data(&list->icons_texture, smdh->big_icon, &list->icons_info[icon_i]);
free(smdh);
}
} }
} }
static void reverse(ssize_t a[], int sz) { static void reverse(Entry_Icon_s a[], int sz) {
int i, j; int i, j;
Entry_Icon_s tmp;
for (i = 0, j = sz; i < j; i++, j--) { for (i = 0, j = sz; i < j; i++, j--) {
ssize_t tmp = a[i]; memcpy(&tmp, &a[i], sizeof(Entry_Icon_s));
a[i] = a[j]; memcpy(&a[i], &a[j], sizeof(Entry_Icon_s));
a[j] = tmp; memcpy(&a[j], &tmp, sizeof(Entry_Icon_s));
} }
} }
static void rotate(ssize_t array[], int size, int amt) { static void rotate(Entry_Icon_s array[], int size, int amt) {
if (amt < 0) if (amt < 0)
amt = size + amt; amt = size + amt;
reverse(array, size-amt-1); reverse(array, size-amt-1);
@@ -311,15 +193,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) if(current_list == NULL || current_list->entries == NULL)
return; return false;
handle_scrolling(current_list); handle_scrolling(current_list);
if(current_list->entries_count <= current_list->entries_loaded * ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll) if(current_list->entries_count <= current_list->entries_loaded * ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
return; // 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)) #define SIGN(x) (x > 0 ? 1 : ((x < 0) ? -1 : 0))
@@ -337,29 +219,27 @@ static void load_icons(Entry_List_s * current_list)
} }
int ctr = 0; int ctr = 0;
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *)); Entry_s ** const entries = calloc(abs(delta), sizeof(Entry_s *));
ssize_t * ids = calloc(abs(delta), sizeof(ssize_t)); int * const indexes = calloc(abs(delta), sizeof(int));
bool released = false;
#define FIRST(arr) arr[0] Entry_Icon_s * const icons = current_list->icons_info;
#define LAST(arr) arr[current_list->entries_loaded*ICONS_OFFSET_AMOUNT - 1]
ssize_t * icons_ids = current_list->icons_ids;
for(int i = starti; i != endi; i++, ctr++) for(int i = starti; i != endi; i++, ctr++)
{ {
ssize_t id = 0; int index = 0;
int offset = i; 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, -SIGN(delta));
if(delta > 0) 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; offset += current_list->entries_loaded * ICONS_UNDER - delta;
} }
else else
{ {
id = FIRST(icons_ids); index = 0 - delta - 1 + i - starti;
offset -= current_list->entries_loaded * ICONS_VISIBLE; offset -= current_list->entries_loaded * ICONS_VISIBLE;
i -= 2; //i-- twice to counter the i++, needed only for this case i -= 2; //i-- twice to counter the i++, needed only for this case
} }
@@ -370,133 +250,246 @@ static void load_icons(Entry_List_s * current_list)
offset -= current_list->entries_count; offset -= current_list->entries_count;
entries[ctr] = &current_list->entries[offset]; entries[ctr] = &current_list->entries[offset];
ids[ctr] = id; indexes[ctr] = index;
} }
#undef FIRST
#undef LAST
#undef SIGN #undef SIGN
svcSleepThread(1e6); if(abs(delta) <= current_list->entries_loaded)
for(int i = 0; i < abs(delta); i++)
{ {
Entry_s current_entry = *entries[i]; svcReleaseMutex(mutex);
ssize_t id = ids[i]; released = true;
load_smdh_icon(current_entry, id); }
svcSleepThread(1e7);
starti = 0;
endi = abs(delta);
for(int i = starti; i < endi; i++)
{
Entry_s * const current_entry = entries[i];
const int index = indexes[i];
const Entry_Icon_s * const current_icon = &icons[index];
Icon_s * const smdh = load_entry_icon(current_entry);
if(smdh != NULL)
{
if(current_entry->placeholder_color == 0)
parse_smdh(smdh, current_entry, current_entry->path + strlen(current_list->loading_path));
copy_texture_data(&current_list->icons_texture, smdh->big_icon, current_icon);
free(smdh);
}
if(!released && i > endi/2)
{
svcReleaseMutex(mutex);
released = true;
}
} }
free(entries); free(entries);
free(ids); free(indexes);
current_list->previous_scroll = current_list->scroll; current_list->previous_scroll = current_list->scroll;
return released;
} }
void load_icons_thread(void * void_arg) void load_icons_thread(void * void_arg)
{ {
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg; Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
Handle update_request = *(Handle *)arg->thread_arg[1]; Handle mutex = *(Handle *)arg->thread_arg[1];
do do {
{ svcWaitSynchronization(mutex, U64_MAX);
svcWaitSynchronization(update_request, U64_MAX); Entry_List_s * const current_list = *(Entry_List_s ** volatile)arg->thread_arg[0];
svcClearEvent(update_request); const bool released = load_icons(current_list, mutex);
volatile Entry_List_s * current_list = *(volatile Entry_List_s **)arg->thread_arg[0]; if(!released)
load_icons((Entry_List_s *)current_list); svcReleaseMutex(mutex);
} while(arg->run_thread);
} }
while(arg->run_thread);
bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview_image, int * preview_offset, int height)
{
int width = (uint32_t)((size / 4) / height);
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 = (png_bytep)(row_pointers + (width * 4 * 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 - TOP_SCREEN_WIDTH) / 2;
return true;
} }
static u16 previous_path_preview[0x106] = {0}; static u16 previous_path_preview[0x106] = {0};
bool load_preview(Entry_List_s list, int * preview_offset) bool load_preview(const Entry_List_s * list, C2D_Image * preview_image, int * preview_offset)
{ {
if(list.entries == NULL) return false; if(list->entries == NULL) return false;
Entry_s entry = list.entries[list.selected_entry]; const Entry_s * entry = &list->entries[list->selected_entry];
if(!memcmp(&previous_path_preview, &entry.path, 0x106*sizeof(u16))) return true; if(!memcmp(&previous_path_preview, &entry->path, 0x106 * sizeof(u16))) return true;
char * preview_buffer = NULL; char * preview_buffer = NULL;
u64 size = load_data("/preview.png", entry, &preview_buffer); u32 size = load_data("/preview.png", entry, &preview_buffer);
u32 height = 480;
if(!size) if(size)
{
if (!(size = png_to_abgr(&preview_buffer, size, &height)))
{ {
free(preview_buffer);
throw_error("No preview found.", ERROR_LEVEL_WARNING);
return false; return false;
} }
bool ret = false;
u8 * image = NULL;
unsigned int width = 0, height = 0;
if((lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size)) == 0) // no error
{
for(u32 i = 0; i < width; i++)
{
for(u32 j = 0; j < height; j++)
{
u32* pixel = (u32*)(image + (i + j*width) * 4);
*pixel = __builtin_bswap32(*pixel); //swap from RGBA to ABGR, needed for pp2d
}
}
// mark the new preview as loaded for optimisation
memcpy(&previous_path_preview, &entry.path, 0x106*sizeof(u16));
// free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_PREVIEW);
pp2d_load_texture_memory(TEXTURE_PREVIEW, image, (u32)width, (u32)height);
*preview_offset = (width-400)/2;
ret = true;
} }
else else
{ {
throw_error("Corrupted/invalid preview.png", ERROR_LEVEL_WARNING); free(preview_buffer);
const int top_size = TOP_SCREEN_WIDTH * SCREEN_HEIGHT * SCREEN_COLOR_DEPTH;
const int out_size = top_size * 2;
char * rgba_buffer = malloc(out_size);
memset(rgba_buffer, 0, out_size);
bool found_splash = false;
// try to assembly a preview from the splash screens
size = load_data("/splash.bin", entry, &preview_buffer);
if (size)
{
found_splash = true;
bin_to_abgr(&preview_buffer, size);
memcpy(rgba_buffer, preview_buffer, top_size);
free(preview_buffer);
} }
free(image); size = load_data("/splashbottom.bin", entry, &preview_buffer);
if (size)
{
found_splash = true;
bin_to_abgr(&preview_buffer, size);
const int buffer_width = TOP_SCREEN_WIDTH * SCREEN_COLOR_DEPTH;
const int bottom_buffer_width = BOTTOM_SCREEN_WIDTH * SCREEN_COLOR_DEPTH;
const int bottom_centered_offset = (TOP_SCREEN_WIDTH - BOTTOM_SCREEN_WIDTH) / 2;
// Store the bottom splash screen under the top splash and centered
for (int i = 0; i < SCREEN_HEIGHT; ++i)
memcpy(
rgba_buffer + top_size + (buffer_width * i) + (bottom_centered_offset * SCREEN_COLOR_DEPTH),
preview_buffer + (bottom_buffer_width * i),
bottom_buffer_width
);
free(preview_buffer); free(preview_buffer);
}
if (!found_splash)
{
free(rgba_buffer);
throw_error(language.loading.no_preview, ERROR_LEVEL_WARNING);
return false;
}
size = out_size;
preview_buffer = rgba_buffer;
}
bool ret = load_preview_from_buffer(preview_buffer, size, preview_image, preview_offset, height);
free(preview_buffer);
if(ret)
{
// mark the new preview as loaded for optimisation
memcpy(&previous_path_preview, &entry->path, 0x106 * sizeof(u16));
}
return ret; return ret;
} }
void free_preview(C2D_Image preview)
{
if(preview.tex)
C3D_TexDelete(preview.tex);
free(preview.tex);
free((Tex3DS_SubTexture *)preview.subtex);
}
// Initialize the audio struct // Initialize the audio struct
Result load_audio(Entry_s entry, audio_s *audio) Result load_audio(const Entry_s * entry, audio_s * audio)
{ {
audio->filesize = load_data("/bgm.ogg", entry, &audio->filebuf); audio->filesize = load_data("/bgm.ogg", entry, &audio->filebuf);
if (audio->filesize == 0) {
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 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
ndspChnSetInterp(0, NDSP_INTERP_LINEAR); ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetRate(0, 44100);
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); // Tremor outputs ogg files in 16 bit PCM stereo
ndspChnSetMix(0, audio->mix); // See mix comment above ndspChnSetMix(0, audio->mix); // See mix comment above
FILE * file = fmemopen(audio->filebuf, audio->filesize, "rb"); FILE * file = fmemopen(audio->filebuf, audio->filesize, "rb");
DEBUG("<load_audio> Filesize: %ld\n", audio->filesize);
if(file != NULL) if(file != NULL)
{ {
int e = ov_open(file, &audio->vf, NULL, 0); int e = ov_open(file, &audio->vf, NULL, 0);
if (e < 0) if (e < 0)
{ {
char error[50]; DEBUG("<load_audio> Vorbis: %d\n", e);
sprintf(error, "Vorbis: %d\n", e); free(audio->filebuf);
DEBUG(error); free(audio);
fclose(file);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA); return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
} }
vorbis_info * vi = ov_info(&audio->vf, -1); vorbis_info * vi = ov_info(&audio->vf, -1);
ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file
if (vi->channels == 2) {
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].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].status = audio->wave_buf[1].status = NDSP_WBUF_DONE; // Used in play to stop from writing to current buffer
audio->wave_buf[0].data_vaddr = linearAlloc(BUF_TO_READ); // Most vorbis packets should only be 4 KB at most (?) Possibly dangerous assumption audio->wave_buf[0].data_vaddr = linearAlloc(BUF_TO_READ); // Most vorbis packets should only be 4 KB at most (?) Possibly dangerous assumption
audio->wave_buf[1].data_vaddr = linearAlloc(BUF_TO_READ); audio->wave_buf[1].data_vaddr = linearAlloc(BUF_TO_READ);
DEBUG("Success!"); DEBUG("<load_audio> Success!\n");
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS); return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
} else { } else {
free(audio->filebuf); free(audio->filebuf);
free(audio); free(audio);
DEBUG("File not found!\n"); DEBUG("<load_audio> fmemopen failed!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND); return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2015-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -32,24 +32,29 @@
#include "camera.h" #include "camera.h"
#include "music.h" #include "music.h"
#include "remote.h" #include "remote.h"
#include "instructions.h" #include "ui_strings.h"
#include "pp2d/pp2d/pp2d.h"
#include <time.h> #include <time.h>
bool quit = false; bool quit = false;
audio_s * audio; bool dspfirm = false;
static audio_s * audio = NULL;
static bool homebrew = false; static bool homebrew = false;
static bool installed_themes = false; static bool installed_themes = false;
bool home_displayed = false;
u64 time_home_pressed = 0;
static Thread iconLoadingThread = {0}; static Thread iconLoadingThread = {0};
static Thread_Arg_s iconLoadingThread_arg = {0}; static Thread_Arg_s iconLoadingThread_arg = {0};
static Handle update_icons_handle; static Handle update_icons_mutex;
static bool released = false;
static Thread installCheckThreads[MODE_AMOUNT] = {0}; static Thread install_check_threads[MODE_AMOUNT] = {0};
static Thread_Arg_s installCheckThreads_arg[MODE_AMOUNT] = {0}; static Thread_Arg_s install_check_threads_arg[MODE_AMOUNT] = {0};
static Entry_List_s lists[MODE_AMOUNT] = {0}; static Entry_List_s lists[MODE_AMOUNT] = {0};
Language_s language = {0};
int __stacksize__ = 64 * 1024; int __stacksize__ = 64 * 1024;
Result archive_result; Result archive_result;
u32 old_time_limit; u32 old_time_limit;
@@ -77,7 +82,7 @@ static void init_services(void)
cfguInit(); cfguInit();
ptmuInit(); ptmuInit();
acInit(); acInit();
ndspInit(); dspfirm = !ndspInit();
APT_GetAppCpuTimeLimit(&old_time_limit); APT_GetAppCpuTimeLimit(&old_time_limit);
APT_SetAppCpuTimeLimit(30); APT_SetAppCpuTimeLimit(30);
httpcInit(0); httpcInit(0);
@@ -105,7 +110,16 @@ static void stop_install_check(void)
{ {
for(int i = 0; i < MODE_AMOUNT; i++) for(int i = 0; i < MODE_AMOUNT; i++)
{ {
installCheckThreads_arg[i].run_thread = false; install_check_threads_arg[i].run_thread = false;
}
for(int i = 0; i < MODE_AMOUNT; i++)
{
if(install_check_threads[i] == NULL)
continue;
threadJoin(install_check_threads[i], U64_MAX);
threadFree(install_check_threads[i]);
install_check_threads[i] = NULL;
} }
} }
@@ -115,9 +129,11 @@ static void exit_thread(void)
{ {
DEBUG("exiting thread\n"); DEBUG("exiting thread\n");
iconLoadingThread_arg.run_thread = false; iconLoadingThread_arg.run_thread = false;
svcSignalEvent(update_icons_handle); svcReleaseMutex(update_icons_mutex);
svcWaitSynchronization(update_icons_mutex, U64_MAX);
threadJoin(iconLoadingThread, U64_MAX); threadJoin(iconLoadingThread, U64_MAX);
threadFree(iconLoadingThread); threadFree(iconLoadingThread);
iconLoadingThread = NULL;
} }
} }
@@ -126,9 +142,10 @@ void free_lists(void)
stop_install_check(); stop_install_check();
for(int i = 0; i < MODE_AMOUNT; i++) for(int i = 0; i < MODE_AMOUNT; i++)
{ {
Entry_List_s * current_list = &lists[i]; Entry_List_s * const current_list = &lists[i];
C3D_TexDelete(&current_list->icons_texture);
free(current_list->icons_info);
free(current_list->entries); free(current_list->entries);
free(current_list->icons_ids);
memset(current_list, 0, sizeof(Entry_List_s)); memset(current_list, 0, sizeof(Entry_List_s));
} }
exit_thread(); exit_thread();
@@ -136,8 +153,12 @@ void free_lists(void)
void exit_function(bool power_pressed) void exit_function(bool power_pressed)
{ {
if(audio)
{
stop_audio(&audio);
}
free_lists(); free_lists();
svcCloseHandle(update_icons_handle); svcCloseHandle(update_icons_mutex);
exit_screens(); exit_screens();
exit_services(); exit_services();
@@ -163,10 +184,20 @@ static void start_thread(void)
} }
} }
static u32 next_or_equal_power_of_2(u32 v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
static void load_lists(Entry_List_s * lists) static void load_lists(Entry_List_s * lists)
{ {
ssize_t texture_id_offset = TEXTURE_ICON;
free_lists(); free_lists();
for(int i = 0; i < MODE_AMOUNT; i++) for(int i = 0; i < MODE_AMOUNT; i++)
{ {
@@ -177,42 +208,70 @@ static void load_lists(Entry_List_s * lists)
loading_screen = INSTALL_LOADING_SPLASHES; loading_screen = INSTALL_LOADING_SPLASHES;
draw_install(loading_screen); draw_install(loading_screen);
draw_install(loading_screen);
Entry_List_s * current_list = &lists[i]; Entry_List_s * const current_list = &lists[i];
current_list->mode = i; current_list->mode = i;
current_list->entries_per_screen_v = entries_per_screen_v[i]; current_list->entries_per_screen_v = entries_per_screen_v[i];
current_list->entries_per_screen_h = 1; current_list->entries_per_screen_h = 1;
current_list->entries_loaded = current_list->entries_per_screen_v * current_list->entries_per_screen_h; current_list->entries_loaded = current_list->entries_per_screen_v * current_list->entries_per_screen_h;
current_list->entry_size = entry_size[i]; current_list->entry_size = entry_size[i];
Result res = load_entries(main_paths[i], current_list);
const int x_component = max(current_list->entries_per_screen_h, current_list->entries_per_screen_v);
const int y_component = min(current_list->entries_per_screen_h, current_list->entries_per_screen_v);
// A texture must have power of 2 dimensions (not necessarily the same)
// so, get the power of two greater than or equal to:
// - the size of the largest length (row or column) of icons for the width
// - the size of all of those lengths to fit the total for the height
C3D_TexInit(&current_list->icons_texture,
next_or_equal_power_of_2(x_component * current_list->entry_size),
next_or_equal_power_of_2(y_component * current_list->entry_size * ICONS_OFFSET_AMOUNT),
GPU_RGB565);
C3D_TexSetFilter(&current_list->icons_texture, GPU_NEAREST, GPU_NEAREST);
const float inv_width = 1.0f / current_list->icons_texture.width;
const float inv_height = 1.0f / current_list->icons_texture.height;
current_list->icons_info = (Entry_Icon_s *)calloc(x_component * y_component * ICONS_OFFSET_AMOUNT, sizeof(Entry_Icon_s));
for(int j = 0; j < y_component * ICONS_OFFSET_AMOUNT; ++j)
{
const int index = j * x_component;
for(int h = 0; h < x_component; ++h)
{
Entry_Icon_s * const icon_info = &current_list->icons_info[index + h];
icon_info->x = h * current_list->entry_size;
icon_info->y = j * current_list->entry_size;
icon_info->subtex.width = current_list->entry_size;
icon_info->subtex.height = current_list->entry_size;
icon_info->subtex.left = icon_info->x * inv_width;
icon_info->subtex.top = 1.0f - (icon_info->y * inv_height);
icon_info->subtex.right = icon_info->subtex.left + (icon_info->subtex.width * inv_width);
icon_info->subtex.bottom = icon_info->subtex.top - (icon_info->subtex.height * inv_height);
}
}
Result res = load_entries(main_paths[i], current_list, loading_screen);
if(R_SUCCEEDED(res)) if(R_SUCCEEDED(res))
{ {
if(current_list->entries_count > current_list->entries_loaded * ICONS_OFFSET_AMOUNT) if(current_list->entries_count > current_list->entries_loaded * ICONS_OFFSET_AMOUNT)
iconLoadingThread_arg.run_thread = true; iconLoadingThread_arg.run_thread = true;
sort_by_name(current_list);
DEBUG("total: %i\n", current_list->entries_count); DEBUG("total: %i\n", current_list->entries_count);
current_list->texture_id_offset = texture_id_offset; sort_by_name(current_list);
load_icons_first(current_list, false); load_icons_first(current_list, false);
texture_id_offset += current_list->entries_loaded*ICONS_OFFSET_AMOUNT;
void (*install_check_function)(void *) = NULL; void (*install_check_function)(void *) = NULL;
if(i == MODE_THEMES) if(i == MODE_THEMES)
install_check_function = themes_check_installed; install_check_function = themes_check_installed;
else if(i == MODE_SPLASHES) else if(i == MODE_SPLASHES)
install_check_function = splash_check_installed; install_check_function = splash_check_installed;
Thread_Arg_s * current_arg = &installCheckThreads_arg[i]; Thread_Arg_s * current_arg = &install_check_threads_arg[i];
current_arg->run_thread = true; current_arg->run_thread = true;
current_arg->thread_arg = (void **)current_list; current_arg->thread_arg = (void **)current_list;
if(install_check_function != NULL) if(install_check_function != NULL)
{ {
installCheckThreads[i] = threadCreate(install_check_function, current_arg, __stacksize__, 0x3f, -2, true); install_check_threads[i] = threadCreate(install_check_function, current_arg, __stacksize__, 0x3f, -2, false);
svcSleepThread(1e8); svcSleepThread(1e8);
} }
} }
@@ -222,15 +281,16 @@ static void load_lists(Entry_List_s * lists)
static SwkbdCallbackResult jump_menu_callback(void * entries_count, const char ** ppMessage, const char * text, size_t textlen) static SwkbdCallbackResult jump_menu_callback(void * entries_count, const char ** ppMessage, const char * text, size_t textlen)
{ {
(void)textlen;
int typed_value = atoi(text); int typed_value = atoi(text);
if(typed_value > *(int *)entries_count) if(typed_value > *(int *)entries_count)
{ {
*ppMessage = "The new position has to be\nsmaller or equal to the\nnumber of entries!"; *ppMessage = language.main.position_too_big;
return SWKBD_CALLBACK_CONTINUE; return SWKBD_CALLBACK_CONTINUE;
} }
else if(typed_value == 0) else if(typed_value == 0)
{ {
*ppMessage = "The new position has to\nbe positive!"; *ppMessage = language.main.position_zero;
return SWKBD_CALLBACK_CONTINUE; return SWKBD_CALLBACK_CONTINUE;
} }
return SWKBD_CALLBACK_OK; return SWKBD_CALLBACK_OK;
@@ -251,11 +311,11 @@ static void jump_menu(Entry_List_s * list)
sprintf(numbuf, "%i", list->selected_entry); sprintf(numbuf, "%i", list->selected_entry);
swkbdSetInitialText(&swkbd, numbuf); swkbdSetInitialText(&swkbd, numbuf);
sprintf(numbuf, "Where do you want to jump to?\nMay cause icons to reload."); sprintf(numbuf, language.main.jump_q);
swkbdSetHintText(&swkbd, numbuf); swkbdSetHintText(&swkbd, numbuf);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false); swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, language.main.cancel, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Jump", true); swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, language.main.jump, true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars); swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->entries_count); swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->entries_count);
@@ -267,6 +327,8 @@ static void jump_menu(Entry_List_s * list)
list->scroll = list->selected_entry; list->scroll = list->selected_entry;
if(list->scroll >= list->entries_count - list->entries_loaded) if(list->scroll >= list->entries_count - list->entries_loaded)
list->scroll = list->entries_count - list->entries_loaded - 1; list->scroll = list->entries_count - list->entries_loaded - 1;
if (list->scroll < 0)
list->scroll = 0;
} }
} }
@@ -286,23 +348,48 @@ static void change_selected(Entry_List_s * list, int change_value)
static void toggle_shuffle(Entry_List_s * list) static void toggle_shuffle(Entry_List_s * list)
{ {
Entry_s * current_entry = &list->entries[list->selected_entry]; Entry_s * current_entry = &list->entries[list->selected_entry];
if(current_entry->in_shuffle) list->shuffle_count--; if(current_entry->in_shuffle)
else list->shuffle_count++; {
current_entry->in_shuffle = !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) int main(void)
{ {
srand(time(NULL)); srand(time(NULL));
init_services(); init_services();
CFG_Language lang;
CFGU_GetSystemLanguage(&lang);
language = init_strings(lang);
init_screens(); init_screens();
svcCreateEvent(&update_icons_handle, RESET_ONESHOT); svcCreateMutex(&update_icons_mutex, true);
static Entry_List_s * current_list = NULL; static Entry_List_s * current_list = NULL;
void * iconLoadingThread_args_void[] = { void * iconLoadingThread_args_void[] = {
&current_list, &current_list,
&update_icons_handle, &update_icons_mutex,
}; };
iconLoadingThread_arg.thread_arg = iconLoadingThread_args_void; iconLoadingThread_arg.thread_arg = iconLoadingThread_args_void;
iconLoadingThread_arg.run_thread = false; iconLoadingThread_arg.run_thread = false;
@@ -319,22 +406,31 @@ int main(void)
bool preview_mode = false; bool preview_mode = false;
int preview_offset = 0; int preview_offset = 0;
bool qr_mode = false;
bool install_mode = false; bool install_mode = false;
DrawMode draw_mode = DRAW_MODE_LIST;
bool extra_mode = false; bool extra_mode = false;
int extra_index = 1;
C2D_Image preview = {0};
while(aptMainLoop()) while(aptMainLoop())
{ {
if(quit) if(quit)
{ {
free_preview(preview);
exit_function(false); exit_function(false);
return 0; return 0;
} }
if (aptCheckHomePressRejected() && !home_displayed)
{
time_home_pressed = svcGetSystemTick() / CPU_TICKS_PER_MSEC;
home_displayed = true;
}
#ifndef CITRA_MODE #ifndef CITRA_MODE
if(R_FAILED(archive_result) && current_mode == MODE_THEMES) if(R_FAILED(archive_result) && current_mode == MODE_THEMES)
{ {
throw_error("Theme extdata does not exist!\nSet a default theme from the home menu.", ERROR_LEVEL_ERROR); throw_error(language.main.no_theme_extdata, ERROR_LEVEL_ERROR);
quit = true; quit = true;
continue; continue;
} }
@@ -347,28 +443,17 @@ int main(void)
current_list = &lists[current_mode]; current_list = &lists[current_mode];
Instructions_s instructions = normal_instructions[current_mode]; Instructions_s instructions = language.normal_instructions[current_mode];
if(install_mode) if(install_mode)
instructions = install_instructions; instructions = language.install_instructions;
if(extra_mode) if(extra_mode)
{ {
int index = 1; instructions = language.extra_instructions[extra_index];
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(); if(preview_mode)
else if(preview_mode)
{ {
draw_preview(TEXTURE_PREVIEW, preview_offset); draw_preview(preview, preview_offset);
} }
else { else {
if(!iconLoadingThread_arg.run_thread) if(!iconLoadingThread_arg.run_thread)
@@ -378,30 +463,80 @@ int main(void)
} }
else else
{ {
svcSignalEvent(update_icons_handle); if(!released)
svcSleepThread(5e6); {
svcReleaseMutex(update_icons_mutex);
released = true;
}
svcWaitSynchronization(update_icons_mutex, U64_MAX);
} }
draw_interface(current_list, instructions); draw_interface(current_list, instructions, draw_mode);
svcSleepThread(1e7); svcSleepThread(1e7);
released = false;
} }
pp2d_end_draw(); if (home_displayed)
{
u64 cur_time = svcGetSystemTick() / CPU_TICKS_PER_MSEC;
draw_home(time_home_pressed, cur_time);
if (cur_time - time_home_pressed > 2000) home_displayed = false;
}
end_frame();
if(kDown & KEY_START) quit = true; if(kDown & KEY_START) quit = true;
if(!install_mode && !extra_mode) if(current_list->entries_count == 0)
{ {
if(!preview_mode && !qr_mode && kDown & KEY_L) //toggle between splashes and themes if (kDown & KEY_R)
{
goto enable_qr;
} else if (kDown & KEY_L)
{
goto switch_mode;
} else if (kDown & KEY_TOUCH)
{
touchPosition touch = {0};
hidTouchRead(&touch);
u16 x = touch.px;
u16 y = touch.py;
if (y < 24)
{
if(BETWEEN(320-24, x, 320))
{
goto switch_mode;
} else if(BETWEEN(320-48, x, 320-24))
{
quit = true;
continue;
} else if(BETWEEN(320-72, x, 320-48))
{
goto browse_themeplaza;
} else if(BETWEEN(320-96, x, 320-72))
{
goto enable_qr;
}
}
}
}
else if(!install_mode && !extra_mode)
{
if(!preview_mode && kDown & KEY_L) //toggle between splashes and themes
{ {
switch_mode: switch_mode:
current_mode++; current_mode++;
current_mode %= MODE_AMOUNT; current_mode %= MODE_AMOUNT;
continue; continue;
} }
else if(!qr_mode && !preview_mode && kDown & KEY_R) //toggle QR mode else if(!preview_mode && kDown & KEY_R) //toggle QR mode
{ {
enable_qr: enable_qr:
draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], language.main.loading_qr);
end_frame();
if(R_SUCCEEDED(camInit())) if(R_SUCCEEDED(camInit()))
{ {
camExit(); camExit();
@@ -416,37 +551,46 @@ int main(void)
} }
else else
{ {
throw_error("Please connect to Wi-Fi before scanning QR codes", ERROR_LEVEL_WARNING); throw_error(language.main.no_wifi, ERROR_LEVEL_WARNING);
} }
} }
else else
{ {
if(homebrew) if(homebrew)
throw_error("QR scanning doesnt work from the Homebrew\nLauncher, use the ThemePlaza browser instead.", ERROR_LEVEL_WARNING); throw_error(language.main.qr_homebrew, ERROR_LEVEL_WARNING);
else else
throw_error("Your camera seems to have a problem,\nunable to scan QR codes.", ERROR_LEVEL_WARNING); throw_error(language.main.camera_broke, ERROR_LEVEL_WARNING);
} }
continue; continue;
} }
else if(!qr_mode && kDown & KEY_Y) //toggle preview mode else if(kDown & KEY_Y && current_list->entries != NULL) //toggle preview mode
{ {
toggle_preview: toggle_preview:
if(!preview_mode) if(!preview_mode)
{ {
preview_mode = load_preview(*current_list, &preview_offset); preview_mode = load_preview(current_list, &preview, &preview_offset);
if(current_mode == MODE_THEMES) if(preview_mode)
{
end_frame();
draw_preview(preview, preview_offset);
end_frame();
if(current_mode == MODE_THEMES && dspfirm)
{ {
audio = calloc(1, sizeof(audio_s)); audio = calloc(1, sizeof(audio_s));
load_audio(current_list->entries[current_list->selected_entry], audio); Result r = load_audio(&current_list->entries[current_list->selected_entry], audio);
play_audio(audio); if (R_SUCCEEDED(r)) play_audio(audio);
else audio = NULL;
}
} }
} }
else else
{ {
preview_mode = false; preview_mode = false;
if(current_mode == MODE_THEMES && audio) if(current_mode == MODE_THEMES && audio)
audio->stop = true; {
stop_audio(&audio);
}
} }
continue; continue;
} }
@@ -454,12 +598,14 @@ int main(void)
{ {
preview_mode = false; preview_mode = false;
if(current_mode == MODE_THEMES && audio) if(current_mode == MODE_THEMES && audio)
audio->stop = true; {
stop_audio(&audio);
}
continue; continue;
} }
} }
if(qr_mode || preview_mode || current_list->entries == NULL) if(preview_mode || current_list->entries == NULL)
goto touch; goto touch;
int selected_entry = current_list->selected_entry; int selected_entry = current_list->selected_entry;
@@ -467,17 +613,55 @@ int main(void)
if(install_mode) if(install_mode)
{ {
if(kUp & KEY_A) if ((kDown | kHeld) & KEY_TOUCH)
{
touchPosition touch = {0};
hidTouchRead(&touch);
u16 x = touch.px;
u16 y = touch.py;
if (kDown & KEY_TOUCH)
{
if (y < 24)
{
if (BETWEEN(320-24, x, 320))
{
goto install_theme_single;
} else if (BETWEEN(320-48, x, 320-24))
{
goto install_theme_shuffle;
} else if (BETWEEN(320-72, x, 320-48))
{
goto install_theme_no_bgm;
} else if (BETWEEN(320-96, x, 320-72))
{
goto install_theme_bgm_only;
} else if (BETWEEN(320-120, x, 320-96))
{
goto install_leave;
}
}
}
}
if(kDown & KEY_B)
{
install_leave:
install_mode = false; install_mode = false;
if(!install_mode) draw_mode = DRAW_MODE_LIST;
{ }
if((kDown | kHeld) & KEY_DLEFT) else if(kDown & KEY_DLEFT)
{ {
install_theme_bgm_only:
install_mode = false;
draw_mode = DRAW_MODE_LIST;
aptSetHomeAllowed(false);
draw_install(INSTALL_BGM); draw_install(INSTALL_BGM);
if(R_SUCCEEDED(bgm_install(*current_entry))) if(R_SUCCEEDED(bgm_install(current_entry)))
{ {
for(int i = 0; i < current_list->entries_count; i++) for(int i = 0; i < current_list->entries_count; i++)
{ {
#define BETWEEN(min, x, max) (min < x && x < max)
Entry_s * theme = &current_list->entries[i]; Entry_s * theme = &current_list->entries[i];
if(theme == current_entry) if(theme == current_entry)
theme->installed = true; theme->installed = true;
@@ -487,12 +671,17 @@ int main(void)
installed_themes = true; installed_themes = true;
} }
} }
else if((kDown | kHeld) & KEY_DUP) else if(kDown & KEY_DUP)
{ {
install_theme_single:
install_mode = false;
draw_mode = DRAW_MODE_LIST;
aptSetHomeAllowed(false);
draw_install(INSTALL_SINGLE); draw_install(INSTALL_SINGLE);
if(R_SUCCEEDED(theme_install(*current_entry))) if(R_SUCCEEDED(theme_install(current_entry)))
{ {
for(int i = 0; i < current_list->entries_count; i++) for(int i = 0; i < current_list->entries_count; i++)
{ {
Entry_s * theme = &current_list->entries[i]; Entry_s * theme = &current_list->entries[i];
if(theme == current_entry) if(theme == current_entry)
@@ -503,10 +692,14 @@ int main(void)
installed_themes = true; installed_themes = true;
} }
} }
else if((kDown | kHeld) & KEY_DRIGHT) else if(kDown & KEY_DRIGHT)
{ {
install_theme_no_bgm:
install_mode = false;
draw_mode = DRAW_MODE_LIST;
aptSetHomeAllowed(false);
draw_install(INSTALL_NO_BGM); draw_install(INSTALL_NO_BGM);
if(R_SUCCEEDED(no_bgm_install(*current_entry))) if(R_SUCCEEDED(no_bgm_install(current_entry)))
{ {
for(int i = 0; i < current_list->entries_count; i++) for(int i = 0; i < current_list->entries_count; i++)
{ {
@@ -519,20 +712,24 @@ int main(void)
installed_themes = true; installed_themes = true;
} }
} }
else if((kDown | kHeld) & KEY_DDOWN) else if(kDown & KEY_DDOWN)
{ {
install_theme_shuffle:
install_mode = false;
draw_mode = DRAW_MODE_LIST;
if(current_list->shuffle_count > MAX_SHUFFLE_THEMES) if(current_list->shuffle_count > MAX_SHUFFLE_THEMES)
{ {
throw_error("You have too many themes selected.", ERROR_LEVEL_WARNING); throw_error(language.main.too_many_themes, ERROR_LEVEL_WARNING);
} }
else if(current_list->shuffle_count < 2) else if(current_list->shuffle_count < 2)
{ {
throw_error("You don't have enough themes selected.", ERROR_LEVEL_WARNING); throw_error(language.main.not_enough_themes, ERROR_LEVEL_WARNING);
} }
else else
{ {
aptSetHomeAllowed(false);
draw_install(INSTALL_SHUFFLE); draw_install(INSTALL_SHUFFLE);
Result res = shuffle_install(*current_list); Result res = shuffle_install(current_list);
if(R_FAILED(res)) DEBUG("shuffle install result: %lx\n", res); if(R_FAILED(res)) DEBUG("shuffle install result: %lx\n", res);
else else
{ {
@@ -551,20 +748,59 @@ int main(void)
} }
} }
} }
}
continue; continue;
} }
else if(extra_mode) else if(extra_mode)
{ {
if(kUp & KEY_X) if((kDown | kHeld) & KEY_TOUCH)
{
touchPosition touch = {0};
hidTouchRead(&touch);
u16 x = touch.px;
u16 y = touch.py;
if (kDown & KEY_TOUCH)
{
if (y < 24)
{
if (BETWEEN(320-24, x, 320))
{
goto browse_themeplaza;
} else if (BETWEEN(320-48, x, 320-24))
{
goto dump_single;
} else if (BETWEEN(320-72, x, 320-48))
{
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-96, x, 320-72))
{
extra_mode = false; extra_mode = false;
if(!extra_mode) extra_index = 1;
draw_mode = DRAW_MODE_LIST;
}
}
}
}
else if(extra_index == 1)
{ {
bool key_l = (kDown | kHeld) & KEY_L; if(kDown & KEY_B)
bool key_r = (kDown | kHeld) & KEY_R;
if(!(key_l ^ key_r))
{ {
if((kDown | kHeld) & KEY_DLEFT) extra_mode = false;
draw_mode = DRAW_MODE_LIST;
}
else if(kDown & KEY_DLEFT)
{ {
browse_themeplaza: browse_themeplaza:
if(themeplaza_browser(current_mode)) if(themeplaza_browser(current_mode))
@@ -572,38 +808,95 @@ int main(void)
current_mode = MODE_THEMES; current_mode = MODE_THEMES;
load_lists(lists); load_lists(lists);
} }
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
} }
else if((kDown | kHeld) & KEY_DUP) else if(kDown & KEY_DUP)
{ {
jump: jump:
jump_menu(current_list); jump_menu(current_list);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
} }
else if((kDown | kHeld) & KEY_DDOWN) else if(kDown & KEY_DDOWN)
{ {
load_icons_first(current_list, false); load_icons_first(current_list, false);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
} }
} else if (kDown & KEY_R)
else if(key_l)
{ {
if((kDown | kHeld) & KEY_DLEFT) extra_index = 2;
}
else if(kDown & KEY_L)
{
extra_index = 0;
}
}
else if(extra_index == 0)
{
if(kDown & KEY_DLEFT)
{ {
sort_path: sort_path:
sort_by_filename(current_list); sort_by_filename(current_list);
load_icons_first(current_list, false); load_icons_first(current_list, false);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
} }
else if(((kDown | kHeld)) & KEY_DUP) else if(kDown & KEY_DUP)
{ {
sort_name: sort_name:
sort_by_name(current_list); sort_by_name(current_list);
load_icons_first(current_list, false); load_icons_first(current_list, false);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
} }
else if(((kDown | kHeld)) & KEY_DDOWN) else if(kDown & KEY_DDOWN)
{ {
sort_author: sort_author:
sort_by_author(current_list); sort_by_author(current_list);
load_icons_first(current_list, false); load_icons_first(current_list, false);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
} }
else if (kDown & KEY_B)
{
extra_index = 1;
}
}
else if(extra_index == 2)
{
if(kDown & KEY_DUP)
{
dump_single:
draw_install(INSTALL_DUMPING_THEME);
Result res = dump_current_theme();
if (R_FAILED(res)) DEBUG("Dump theme result: %lx\n", res);
else load_lists(lists);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
}
else if(kDown & KEY_DDOWN)
{
draw_install(INSTALL_DUMPING_ALL_THEMES);
Result res = dump_all_themes();
if (R_FAILED(res)) DEBUG("Dump all themes result: %lx\n", res);
else load_lists(lists);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
}
else if(kDown &KEY_B)
{
extra_index = 1;
} }
} }
continue; continue;
@@ -617,10 +910,19 @@ int main(void)
{ {
case MODE_THEMES: case MODE_THEMES:
install_mode = true; install_mode = true;
draw_mode = DRAW_MODE_INSTALL;
break; break;
case MODE_SPLASHES: case MODE_SPLASHES:
draw_install(INSTALL_SPLASH); draw_install(INSTALL_SPLASH);
splash_install(*current_entry); splash_install(current_entry);
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * splash = &current_list->entries[i];
if(splash == current_entry)
splash->installed = true;
else
splash->installed = false;
}
break; break;
default: default:
break; break;
@@ -634,7 +936,7 @@ int main(void)
toggle_shuffle(current_list); toggle_shuffle(current_list);
break; break;
case MODE_SPLASHES: case MODE_SPLASHES:
if(draw_confirm("Are you sure you would like to delete\nthe installed splash?", current_list)) if(draw_confirm(language.main.uninstall_confirm, current_list, draw_mode))
{ {
draw_install(INSTALL_SPLASH_DELETE); draw_install(INSTALL_SPLASH_DELETE);
splash_delete(); splash_delete();
@@ -647,10 +949,11 @@ int main(void)
else if(kDown & KEY_X) else if(kDown & KEY_X)
{ {
extra_mode = true; extra_mode = true;
draw_mode = DRAW_MODE_EXTRA;
} }
else if(kDown & KEY_SELECT) else if(kDown & KEY_SELECT)
{ {
if(draw_confirm("Are you sure you would like to delete this?", current_list)) if(draw_confirm(language.main.delete_confirm, current_list, draw_mode))
{ {
draw_install(INSTALL_ENTRY_DELETE); draw_install(INSTALL_ENTRY_DELETE);
delete_entry(current_entry, current_entry->is_zip); delete_entry(current_entry, current_entry->is_zip);
@@ -681,22 +984,22 @@ int main(void)
else if(kHeld & KEY_CPAD_UP) else if(kHeld & KEY_CPAD_UP)
{ {
change_selected(current_list, -1); change_selected(current_list, -1);
svcSleepThread(FASTSCROLL_WAIT); wait_scroll();
} }
else if(kHeld & KEY_CPAD_DOWN) else if(kHeld & KEY_CPAD_DOWN)
{ {
change_selected(current_list, 1); change_selected(current_list, 1);
svcSleepThread(FASTSCROLL_WAIT); wait_scroll();
} }
else if(kHeld & KEY_CPAD_LEFT) else if(kHeld & KEY_CPAD_LEFT)
{ {
change_selected(current_list, -current_list->entries_per_screen_v); change_selected(current_list, -current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT); wait_scroll();
} }
else if(kHeld & KEY_CPAD_RIGHT) else if(kHeld & KEY_CPAD_RIGHT)
{ {
change_selected(current_list, current_list->entries_per_screen_v); change_selected(current_list, current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT); wait_scroll();
} }
// Movement using the touchscreen // Movement using the touchscreen
@@ -709,43 +1012,49 @@ int main(void)
u16 x = touch.px; u16 x = touch.px;
u16 y = touch.py; u16 y = touch.py;
u16 arrowStartX = 152; u16 arrowStartX = 136;
u16 arrowEndX = arrowStartX+16; u16 arrowEndX = arrowStartX+16;
#define BETWEEN(min, x, max) (min < x && x < max)
if(kDown & KEY_TOUCH) if(kDown & KEY_TOUCH)
{ {
if(y < 24) if(y < 24)
{ {
if(current_list->entries != NULL && BETWEEN(arrowStartX, x, arrowEndX) && current_list->scroll > 0) if(BETWEEN(320-168, x, 320-144))
{ {
change_selected(current_list, -current_list->entries_per_screen_v); if (current_mode == MODE_THEMES)
{
toggle_shuffle(current_list);
}
} }
else if(BETWEEN(320-144, x, 320-120)) else if(BETWEEN(320-144, x, 320-120))
{ {
switch(current_list->current_sort) extra_mode = true;
{ draw_mode = DRAW_MODE_EXTRA;
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)) else if(BETWEEN(320-120, x, 320-96))
{ {
goto enable_qr; if (current_mode == MODE_THEMES)
{
install_mode = true;
draw_mode = DRAW_MODE_INSTALL;
} else if (current_mode == 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;
}
}
} }
else if(BETWEEN(320-96, x, 320-72)) else if(BETWEEN(320-96, x, 320-72))
{ {
goto browse_themeplaza; goto enable_qr;
} }
else if(BETWEEN(320-72, x, 320-48)) else if(BETWEEN(320-72, x, 320-48))
{ {
@@ -762,7 +1071,11 @@ int main(void)
} }
else if(y >= 216) else if(y >= 216)
{ {
if(current_list->entries != NULL && BETWEEN(arrowStartX, x, arrowEndX) && current_list->scroll < current_list->entries_count - current_list->entries_per_screen_v) if(current_list->entries != NULL && BETWEEN(arrowStartX, x, arrowEndX) && current_list->scroll > 0)
{
change_selected(current_list, -current_list->entries_per_screen_v);
}
else if(current_list->entries != NULL && BETWEEN(arrowStartX + 16, x, arrowEndX + 16) && current_list->scroll < current_list->entries_count - current_list->entries_per_screen_v)
{ {
change_selected(current_list, current_list->entries_per_screen_v); change_selected(current_list, current_list->entries_per_screen_v);
} }
@@ -791,6 +1104,9 @@ int main(void)
} }
} }
free_preview(preview);
// aptSetHomeAllowed(true);
exit_function(true); exit_function(true);
return 0; return 0;
} }

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -30,13 +30,14 @@
// Play a given audio struct // Play a given audio struct
Result update_audio(audio_s * audio) Result update_audio(audio_s * audio)
{ {
long size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read; u32 size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read;
char size_info[50] = {0}; DEBUG("<update_audio> Audio Size: %ld\n", size);
sprintf(size_info, "Audio Size: %ld\n", size);
DEBUG(size_info);
if (audio->wave_buf[audio->buf_pos].status == NDSP_WBUF_DONE) // only run if the current selected buffer has already finished playing if (audio->wave_buf[audio->buf_pos].status == NDSP_WBUF_DONE) // only run if the current selected buffer has already finished playing
{ {
size_t read = ov_read(&audio->vf, (char*)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, NULL); // read 1 vorbis packet into wave buffer 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 if (read <= 0) // EoF or error
{ {
@@ -46,9 +47,7 @@ Result update_audio(audio_s *audio)
ov_open(fmemopen(audio->filebuf, audio->filesize, "rb"), &audio->vf, NULL, 0); // Reopen file. Don't need to reinit channel stuff since it's all the same as before ov_open(fmemopen(audio->filebuf, audio->filesize, "rb"), &audio->vf, NULL, 0); // Reopen file. Don't need to reinit channel stuff since it's all the same as before
} else // Error :( } else // Error :(
{ {
char error[100] = {0}; DEBUG("<update_audio> Vorbis play error: %ld\n", read);
sprintf(error, "Vorbis play error: %d\n", read);
DEBUG(error);
ndspChnReset(0); ndspChnReset(0);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA); return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
} }
@@ -70,10 +69,26 @@ void thread_audio(void* data) {
while(!audio->stop) { while(!audio->stop) {
update_audio(audio); update_audio(audio);
} }
ndspChnWaveBufClear(0);
ndspChnReset(0);
ov_clear(&audio->vf);
free(audio->filebuf); free(audio->filebuf);
free(audio); linearFree((void *)audio->wave_buf[0].data_vaddr);
linearFree((void *)audio->wave_buf[1].data_vaddr);
} }
void play_audio(audio_s * audio) { void play_audio(audio_s * audio) {
threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, true); audio->playing_thread = threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, false);
}
void stop_audio(audio_s ** audio_ptr) {
audio_s * audio = *audio_ptr;
if(audio->playing_thread)
{
audio->stop = true;
threadJoin(audio->playing_thread, U64_MAX);
threadFree(audio->playing_thread);
}
free(audio);
*audio_ptr = NULL;
} }

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 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
}; };
const static struct galois_field gf256 = { static const struct galois_field gf256 = {
.p = 255, .p = 255,
.log = gf256_log, .log = gf256_log,
.exp = gf256_exp .exp = gf256_exp
@@ -576,10 +576,11 @@ static quirc_decode_error_t codestream_ecc(struct quirc_data *data,
&quirc_version_db[data->version]; &quirc_version_db[data->version];
const struct quirc_rs_params *sb_ecc = &ver->ecc[data->ecc_level]; const struct quirc_rs_params *sb_ecc = &ver->ecc[data->ecc_level];
struct quirc_rs_params lb_ecc; struct quirc_rs_params lb_ecc;
int bc = ver->data_bytes / sb_ecc->bs; const int lb_count =
(ver->data_bytes - sb_ecc->bs * sb_ecc->ns) / (sb_ecc->bs + 1);
const int bc = lb_count + sb_ecc->ns;
const int ecc_offset = sb_ecc->dw * bc + lb_count;
int dst_offset = 0; int dst_offset = 0;
int lb_count = ver->data_bytes - bc * sb_ecc->bs;
int small_dw_total = bc * sb_ecc->dw;
int i; int i;
memcpy(&lb_ecc, sb_ecc, sizeof(lb_ecc)); memcpy(&lb_ecc, sb_ecc, sizeof(lb_ecc));
@@ -588,22 +589,16 @@ static quirc_decode_error_t codestream_ecc(struct quirc_data *data,
for (i = 0; i < bc; i++) { for (i = 0; i < bc; i++) {
uint8_t *dst = ds->data + dst_offset; uint8_t *dst = ds->data + dst_offset;
const struct quirc_rs_params *ecc = sb_ecc; const struct quirc_rs_params *ecc =
(i < sb_ecc->ns) ? sb_ecc : &lb_ecc;
const int num_ec = ecc->bs - ecc->dw;
quirc_decode_error_t err; quirc_decode_error_t err;
int j = 0; int j;
int k;
for (k = 0; k < sb_ecc->dw; k++) for (j = 0; j < ecc->dw; j++)
dst[j++] = ds->raw[k * bc + i]; dst[j] = ds->raw[j * bc + i];
for (j = 0; j < num_ec; j++)
if (i + lb_count >= bc) { dst[ecc->dw + j] = ds->raw[ecc_offset + j * bc + i];
dst[j++] = ds->raw[small_dw_total + i - lb_count];
ecc = &lb_ecc;
}
for (k = 0; k < sb_ecc->bs - sb_ecc->dw; k++)
dst[j++] = ds->raw[small_dw_total + lb_count + i +
k * bc];
err = correct_block(dst, ecc); err = correct_block(dst, ecc);
if (err) if (err)
@@ -729,10 +724,10 @@ static quirc_decode_error_t decode_alpha(struct quirc_data *data,
int bits = 13; int bits = 13;
int count; int count;
if (data->version < 7) if (data->version < 10)
bits = 9; bits = 9;
else if (data->version < 11) else if (data->version < 27)
bits = 10; bits = 11;
count = take_bits(ds, bits); count = take_bits(ds, bits);
if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD) if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD)
@@ -795,12 +790,18 @@ static quirc_decode_error_t decode_kanji(struct quirc_data *data,
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
int d = take_bits(ds, 13); int d = take_bits(ds, 13);
int msB = d / 0xc0;
int lsB = d % 0xc0;
int intermediate = (msB << 8) | lsB;
uint16_t sjw; uint16_t sjw;
if (d + 0x8140 >= 0x9ffc) if (intermediate + 0x8140 <= 0x9ffc) {
sjw = d + 0x8140; /* bytes are in the range 0x8140 to 0x9FFC */
else sjw = intermediate + 0x8140;
sjw = d + 0xc140; } else {
/* bytes are in the range 0xE040 to 0xEBBF */
sjw = intermediate + 0xc140;
}
data->payload[data->payload_len++] = sjw >> 8; data->payload[data->payload_len++] = sjw >> 8;
data->payload[data->payload_len++] = sjw & 0xff; data->payload[data->payload_len++] = sjw & 0xff;
@@ -873,7 +874,7 @@ static quirc_decode_error_t decode_payload(struct quirc_data *data,
done: done:
/* Add nul terminator to all payloads */ /* Add nul terminator to all payloads */
if (data->payload_len >= sizeof(data->payload)) if (data->payload_len >= (int) sizeof(data->payload))
data->payload_len--; data->payload_len--;
data->payload[data->payload_len] = 0; data->payload[data->payload_len] = 0;

View File

@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <limits.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
@@ -98,8 +99,8 @@ static void perspective_map(const double *c,
double x = (c[0]*u + c[1]*v + c[2]) / den; double x = (c[0]*u + c[1]*v + c[2]) / den;
double y = (c[3]*u + c[4]*v + c[5]) / den; double y = (c[3]*u + c[4]*v + c[5]) / den;
ret->x = rint(x); ret->x = (int) rint(x);
ret->y = rint(y); ret->y = (int) rint(y);
} }
static void perspective_unmap(const double *c, static void perspective_unmap(const double *c,
@@ -121,146 +122,22 @@ static void perspective_unmap(const double *c,
* Span-based floodfill routine * Span-based floodfill routine
*/ */
#define FLOOD_FILL_MAX_DEPTH 128
typedef void (*span_func_t)(void *user_data, int y, int left, int right); typedef void (*span_func_t)(void *user_data, int y, int left, int right);
#if 0 // recursive flood fill static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
span_func_t func, void *user_data,
#define FLOOD_FILL_MAX_DEPTH 4096 int depth)
struct flood_fill_params{
struct quirc *q;
int from;
int to;
span_func_t func;
void *user_data;
};
static struct flood_fill_params ffp;
static void flood_fill_rec(int x, int y, int depth)
{ {
int left = x; int left = x;
int right = x; int right = x;
int i; int i;
quirc_pixel_t *row = ffp.q->pixels + y * ffp.q->w;
if (!depth)
return;
while (left > 0 && row[left - 1] == ffp.from)
left--;
while (right < ffp.q->w - 1 && row[right + 1] == ffp.from)
right++;
/* Fill the extent */
for (i = left; i <= right; i++)
row[i] = ffp.to;
if (ffp.func)
ffp.func(ffp.user_data, y, left, right);
/* Seed new flood-fills */
if (y > 0) {
row = ffp.q->pixels + (y - 1) * ffp.q->w;
for (i = left; i <= right; i++)
if (row[i] == ffp.from)
flood_fill_rec(i, y - 1, depth - 1);
}
if (y < ffp.q->h - 1) {
row = ffp.q->pixels + (y + 1) * ffp.q->w;
for (i = left; i <= right; i++)
if (row[i] == ffp.from)
flood_fill_rec(i, y + 1, depth - 1);
}
}
static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
span_func_t func, void *user_data)
{
ffp.q = q;
ffp.from = from;
ffp.to = to;
ffp.func = func;
ffp.user_data = user_data;
flood_fill_rec(x, y, FLOOD_FILL_MAX_DEPTH);
}
#else // stacked flood fill
#define FILL_STACK_CHUNK_SIZE 0x400
struct fill_stack_chunk{
int x[FILL_STACK_CHUNK_SIZE];
int y[FILL_STACK_CHUNK_SIZE];
struct fill_stack_chunk *prev;
};
struct fill_stack{
struct fill_stack_chunk *last_chunk;
int index;
};
static void fill_stack_init(struct fill_stack *s){
s->last_chunk = NULL;
}
static int fill_stack_is_empty(struct fill_stack *s){
return s->last_chunk == NULL;
}
static void fill_stack_push(struct fill_stack *s, int x, int y){
struct fill_stack_chunk *c;
if(s->last_chunk != NULL && s->index < FILL_STACK_CHUNK_SIZE - 1){
c = s->last_chunk;
s->index++;
}else{
c = (struct fill_stack_chunk*)malloc(sizeof(struct fill_stack_chunk));
if(c == NULL){
return;
}
c->prev = s->last_chunk;
s->last_chunk = c;
s->index = 0;
}
c->x[s->index] = x;
c->y[s->index] = y;
}
static void fill_stack_pop(struct fill_stack *s, int *px, int *py){
struct fill_stack_chunk *c = s->last_chunk;
if(c == NULL){
return;
}
*px = c->x[s->index];
*py = c->y[s->index];
if(s->index > 0){
s->index--;
}else{
s->last_chunk = c->prev;
s->index = FILL_STACK_CHUNK_SIZE - 1;
free(c);
}
}
static void flood_fill_seed(struct quirc *q, int start_x, int start_y, int from, int to,
span_func_t func, void *user_data)
{
struct fill_stack s;
fill_stack_init(&s);
fill_stack_push(&s, start_x, start_y);
do{
int x = 0, y = 0;
fill_stack_pop(&s, &x, &y);
int left = x, right = x, i;
quirc_pixel_t *row = q->pixels + y * q->w; quirc_pixel_t *row = q->pixels + y * q->w;
if (depth >= FLOOD_FILL_MAX_DEPTH)
return;
while (left > 0 && row[left - 1] == from) while (left > 0 && row[left - 1] == from)
left--; left--;
@@ -280,7 +157,8 @@ static void flood_fill_seed(struct quirc *q, int start_x, int start_y, int from,
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
if (row[i] == from) if (row[i] == from)
fill_stack_push(&s, i, y - 1); flood_fill_seed(q, i, y - 1, from, to,
func, user_data, depth + 1);
} }
if (y < q->h - 1) { if (y < q->h - 1) {
@@ -288,66 +166,69 @@ static void flood_fill_seed(struct quirc *q, int start_x, int start_y, int from,
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
if (row[i] == from) if (row[i] == from)
fill_stack_push(&s, i, y + 1); flood_fill_seed(q, i, y + 1, from, to,
func, user_data, depth + 1);
} }
}while(!fill_stack_is_empty(&s));
} }
#endif
/************************************************************************ /************************************************************************
* Adaptive thresholding * Adaptive thresholding
*/ */
#define THRESHOLD_S_DEN 8 static uint8_t otsu(const struct quirc *q)
#define THRESHOLD_T 5
static void threshold(struct quirc *q)
{ {
int x, y; int numPixels = q->w * q->h;
int avg_w = 0;
int avg_u = 0;
int threshold_s = q->w / THRESHOLD_S_DEN;
quirc_pixel_t *row = q->pixels;
for (y = 0; y < q->h; y++) { // Calculate histogram
int row_average[q->w]; const int HISTOGRAM_SIZE = 256;
unsigned int histogram[HISTOGRAM_SIZE];
memset(row_average, 0, sizeof(row_average)); memset(histogram, 0, (HISTOGRAM_SIZE) * sizeof(unsigned int));
uint8_t* ptr = q->image;
for (x = 0; x < q->w; x++) { int length = numPixels;
int w, u; while (length--) {
uint8_t value = *ptr++;
if (y & 1) { histogram[value]++;
w = x;
u = q->w - 1 - x;
} else {
w = q->w - 1 - x;
u = x;
} }
avg_w = (avg_w * (threshold_s - 1)) / // Calculate weighted sum of histogram values
threshold_s + row[w]; int sum = 0;
avg_u = (avg_u * (threshold_s - 1)) / for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
threshold_s + row[u]; sum += i * histogram[i];
row_average[w] += avg_w;
row_average[u] += avg_u;
} }
for (x = 0; x < q->w; x++) { // Compute threshold
if (row[x] < row_average[x] * int sumB = 0;
(100 - THRESHOLD_T) / (200 * threshold_s)) int q1 = 0;
row[x] = QUIRC_PIXEL_BLACK; double max = 0;
else uint8_t threshold = 0;
row[x] = QUIRC_PIXEL_WHITE; for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
// Weighted background
q1 += histogram[i];
if (q1 == 0)
continue;
// Weighted foreground
const int q2 = numPixels - q1;
if (q2 == 0)
break;
sumB += i * histogram[i];
const double m1 = (double)sumB / q1;
const double m2 = ((double)sum - sumB) / q2;
const double m1m2 = m1 - m2;
const double variance = m1m2 * m1m2 * q1 * q2;
if (variance >= max) {
threshold = i;
max = variance;
}
} }
row += q->w; return threshold;
}
} }
static void area_count(void *user_data, int y, int left, int right) static void area_count(void *user_data, int y, int left, int right)
{ {
(void)y;
((struct quirc_region *)user_data)->count += right - left + 1; ((struct quirc_region *)user_data)->count += right - left + 1;
} }
@@ -380,7 +261,7 @@ static int region_code(struct quirc *q, int x, int y)
box->seed.y = y; box->seed.y = y;
box->capstone = -1; box->capstone = -1;
flood_fill_seed(q, x, y, pixel, region, area_count, box); flood_fill_seed(q, x, y, pixel, region, area_count, box, 0);
return region; return region;
} }
@@ -450,7 +331,7 @@ static void find_region_corners(struct quirc *q,
psd.scores[0] = -1; psd.scores[0] = -1;
flood_fill_seed(q, region->seed.x, region->seed.y, flood_fill_seed(q, region->seed.x, region->seed.y,
rcode, QUIRC_PIXEL_BLACK, rcode, QUIRC_PIXEL_BLACK,
find_one_corner, &psd); find_one_corner, &psd, 0);
psd.ref.x = psd.corners[0].x - psd.ref.x; psd.ref.x = psd.corners[0].x - psd.ref.x;
psd.ref.y = psd.corners[0].y - psd.ref.y; psd.ref.y = psd.corners[0].y - psd.ref.y;
@@ -468,7 +349,7 @@ static void find_region_corners(struct quirc *q,
flood_fill_seed(q, region->seed.x, region->seed.y, flood_fill_seed(q, region->seed.x, region->seed.y,
QUIRC_PIXEL_BLACK, rcode, QUIRC_PIXEL_BLACK, rcode,
find_other_corners, &psd); find_other_corners, &psd, 0);
} }
static void record_capstone(struct quirc *q, int ring, int stone) static void record_capstone(struct quirc *q, int ring, int stone)
@@ -541,7 +422,7 @@ static void finder_scan(struct quirc *q, int y)
{ {
quirc_pixel_t *row = q->pixels + y * q->w; quirc_pixel_t *row = q->pixels + y * q->w;
int x; int x;
int last_color; int last_color = 0;
int run_length = 0; int run_length = 0;
int run_count = 0; int run_count = 0;
int pb[5]; int pb[5];
@@ -887,7 +768,7 @@ static int fitness_all(const struct quirc *q, int index)
/* Check alignment patterns */ /* Check alignment patterns */
ap_count = 0; ap_count = 0;
while (info->apat[ap_count]) while ((ap_count < QUIRC_MAX_ALIGNMENT) && info->apat[ap_count])
ap_count++; ap_count++;
for (i = 1; i + 1 < ap_count; i++) { for (i = 1; i + 1 < ap_count; i++) {
@@ -973,7 +854,7 @@ static void rotate_capstone(struct quirc_capstone *cap,
struct quirc_point copy[4]; struct quirc_point copy[4];
int j; int j;
int best = 0; int best = 0;
int best_score = 0; int best_score = INT_MAX;
for (j = 0; j < 4; j++) { for (j = 0; j < 4; j++) {
struct quirc_point *p = &cap->corners[j]; struct quirc_point *p = &cap->corners[j];
@@ -1081,10 +962,10 @@ static void record_qr_grid(struct quirc *q, int a, int b, int c)
flood_fill_seed(q, reg->seed.x, reg->seed.y, flood_fill_seed(q, reg->seed.x, reg->seed.y,
qr->align_region, QUIRC_PIXEL_BLACK, qr->align_region, QUIRC_PIXEL_BLACK,
NULL, NULL); NULL, NULL, 0);
flood_fill_seed(q, reg->seed.x, reg->seed.y, flood_fill_seed(q, reg->seed.x, reg->seed.y,
QUIRC_PIXEL_BLACK, qr->align_region, QUIRC_PIXEL_BLACK, qr->align_region,
find_leftmost_to_line, &psd); find_leftmost_to_line, &psd, 0);
} }
} }
@@ -1190,17 +1071,18 @@ static void test_grouping(struct quirc *q, int i)
test_neighbours(q, i, &hlist, &vlist); test_neighbours(q, i, &hlist, &vlist);
} }
static void pixels_setup(struct quirc *q) static void pixels_setup(struct quirc *q, uint8_t threshold)
{ {
if (sizeof(*q->image) == sizeof(*q->pixels)) { if (QUIRC_PIXEL_ALIAS_IMAGE) {
q->pixels = (quirc_pixel_t *)q->image; q->pixels = (quirc_pixel_t *)q->image;
} else {
int x, y;
for (y = 0; y < q->h; y++) {
for (x = 0; x < q->w; x++) {
q->pixels[y * q->w + x] = q->image[y * q->w + x];
}
} }
uint8_t* source = q->image;
quirc_pixel_t* dest = q->pixels;
int length = q->w * q->h;
while (length--) {
uint8_t value = *source++;
*dest++ = (value < threshold) ? QUIRC_PIXEL_BLACK : QUIRC_PIXEL_WHITE;
} }
} }
@@ -1222,8 +1104,8 @@ void quirc_end(struct quirc *q)
{ {
int i; int i;
pixels_setup(q); uint8_t threshold = otsu(q);
threshold(q); pixels_setup(q, threshold);
for (i = 0; i < q->h; i++) for (i = 0; i < q->h; i++)
finder_scan(q, i); finder_scan(q, i);

View File

@@ -36,34 +36,73 @@ struct quirc *quirc_new(void)
void quirc_destroy(struct quirc *q) void quirc_destroy(struct quirc *q)
{ {
if (q->image)
free(q->image); free(q->image);
if (sizeof(*q->image) != sizeof(*q->pixels)) /* q->pixels may alias q->image when their type representation is of the
same size, so we need to be careful here to avoid a double free */
if (!QUIRC_PIXEL_ALIAS_IMAGE)
free(q->pixels); free(q->pixels);
free(q); free(q);
} }
int quirc_resize(struct quirc *q, int w, int h) int quirc_resize(struct quirc *q, int w, int h)
{ {
uint8_t *new_image = realloc(q->image, w * h); uint8_t *image = NULL;
quirc_pixel_t *pixels = NULL;
if (!new_image) /*
return -1; * XXX: w and h should be size_t (or at least unsigned) as negatives
* values would not make much sense. The downside is that it would break
* both the API and ABI. Thus, at the moment, let's just do a sanity
* check.
*/
if (w < 0 || h < 0)
goto fail;
if (sizeof(*q->image) != sizeof(*q->pixels)) { /*
size_t new_size = w * h * sizeof(quirc_pixel_t); * alloc a new buffer for q->image. We avoid realloc(3) because we want
quirc_pixel_t *new_pixels = realloc(q->pixels, new_size); * on failure to be leave `q` in a consistant, unmodified state.
if (!new_pixels) */
return -1; image = calloc(w, h);
q->pixels = new_pixels; if (!image)
goto fail;
/* compute the "old" (i.e. currently allocated) and the "new"
(i.e. requested) image dimensions */
size_t olddim = q->w * q->h;
size_t newdim = w * h;
size_t min = (olddim < newdim ? olddim : newdim);
/*
* copy the data into the new buffer, avoiding (a) to read beyond the
* old buffer when the new size is greater and (b) to write beyond the
* new buffer when the new size is smaller, hence the min computation.
*/
(void)memcpy(image, q->image, min);
/* alloc a new buffer for q->pixels if needed */
if (!QUIRC_PIXEL_ALIAS_IMAGE) {
pixels = calloc(newdim, sizeof(quirc_pixel_t));
if (!pixels)
goto fail;
} }
q->image = new_image; /* alloc succeeded, update `q` with the new size and buffers */
q->w = w; q->w = w;
q->h = h; q->h = h;
free(q->image);
q->image = image;
if (!QUIRC_PIXEL_ALIAS_IMAGE) {
free(q->pixels);
q->pixels = pixels;
}
return 0; return 0;
/* NOTREACHED */
fail:
free(image);
free(pixels);
return -1;
} }
int quirc_count(const struct quirc *q) int quirc_count(const struct quirc *q)
@@ -84,7 +123,7 @@ static const char *const error_table[] = {
const char *quirc_strerror(quirc_decode_error_t err) const char *quirc_strerror(quirc_decode_error_t err)
{ {
if (err >= 0 && err < sizeof(error_table) / sizeof(error_table[0])) if (err < sizeof(error_table) / sizeof(error_table[0]))
return error_table[err]; return error_table[err];
return "Unknown error"; return "Unknown error";

View File

@@ -19,6 +19,10 @@
#include <stdint.h> #include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct quirc; struct quirc;
/* Obtain the library version string. */ /* Obtain the library version string. */
@@ -117,7 +121,7 @@ struct quirc_code {
* is a bitmask giving the actual values of cells. If the cell * is a bitmask giving the actual values of cells. If the cell
* at (x, y) is black, then the following bit is set: * at (x, y) is black, then the following bit is set:
* *
* cell_bitmap[i << 3] & (1 << (i & 7)) * cell_bitmap[i >> 3] & (1 << (i & 7))
* *
* where i = (y * size) + x. * where i = (y * size) + x.
*/ */
@@ -162,4 +166,8 @@ void quirc_extract(const struct quirc *q, int index,
quirc_decode_error_t quirc_decode(const struct quirc_code *code, quirc_decode_error_t quirc_decode(const struct quirc_code *code,
struct quirc_data *data); struct quirc_data *data);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@@ -32,8 +32,10 @@
#define QUIRC_PERSPECTIVE_PARAMS 8 #define QUIRC_PERSPECTIVE_PARAMS 8
#if QUIRC_MAX_REGIONS < UINT8_MAX #if QUIRC_MAX_REGIONS < UINT8_MAX
#define QUIRC_PIXEL_ALIAS_IMAGE 1
typedef uint8_t quirc_pixel_t; typedef uint8_t quirc_pixel_t;
#elif QUIRC_MAX_REGIONS < UINT16_MAX #elif QUIRC_MAX_REGIONS < UINT16_MAX
#define QUIRC_PIXEL_ALIAS_IMAGE 0
typedef uint16_t quirc_pixel_t; typedef uint16_t quirc_pixel_t;
#else #else
#error "QUIRC_MAX_REGIONS > 65534 is not supported" #error "QUIRC_MAX_REGIONS > 65534 is not supported"
@@ -98,9 +100,9 @@ struct quirc {
#define QUIRC_MAX_ALIGNMENT 7 #define QUIRC_MAX_ALIGNMENT 7
struct quirc_rs_params { struct quirc_rs_params {
int bs; /* Block size */ int bs; /* Small block size */
int dw; /* Data words */ int dw; /* Small data words */
int ce; /* Correctable errors */ int ns; /* Number of small blocks */
}; };
struct quirc_version_info { struct quirc_version_info {

View File

@@ -22,400 +22,400 @@ const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1] = {
.data_bytes = 26, .data_bytes = 26,
.apat = {0}, .apat = {0},
.ecc = { .ecc = {
{.bs = 26, .dw = 16, .ce = 4}, {.bs = 26, .dw = 16, .ns = 1},
{.bs = 26, .dw = 19, .ce = 2}, {.bs = 26, .dw = 19, .ns = 1},
{.bs = 26, .dw = 9, .ce = 8}, {.bs = 26, .dw = 9, .ns = 1},
{.bs = 26, .dw = 13, .ce = 6} {.bs = 26, .dw = 13, .ns = 1}
} }
}, },
{ /* Version 2 */ { /* Version 2 */
.data_bytes = 44, .data_bytes = 44,
.apat = {6, 18, 0}, .apat = {6, 18, 0},
.ecc = { .ecc = {
{.bs = 44, .dw = 28, .ce = 8}, {.bs = 44, .dw = 28, .ns = 1},
{.bs = 44, .dw = 34, .ce = 4}, {.bs = 44, .dw = 34, .ns = 1},
{.bs = 44, .dw = 16, .ce = 14}, {.bs = 44, .dw = 16, .ns = 1},
{.bs = 44, .dw = 22, .ce = 11} {.bs = 44, .dw = 22, .ns = 1}
} }
}, },
{ /* Version 3 */ { /* Version 3 */
.data_bytes = 70, .data_bytes = 70,
.apat = {6, 22, 0}, .apat = {6, 22, 0},
.ecc = { .ecc = {
{.bs = 70, .dw = 44, .ce = 13}, {.bs = 70, .dw = 44, .ns = 1},
{.bs = 70, .dw = 55, .ce = 7}, {.bs = 70, .dw = 55, .ns = 1},
{.bs = 35, .dw = 13, .ce = 11}, {.bs = 35, .dw = 13, .ns = 2},
{.bs = 35, .dw = 17, .ce = 9} {.bs = 35, .dw = 17, .ns = 2}
} }
}, },
{ /* Version 4 */ { /* Version 4 */
.data_bytes = 100, .data_bytes = 100,
.apat = {6, 26, 0}, .apat = {6, 26, 0},
.ecc = { .ecc = {
{.bs = 50, .dw = 32, .ce = 9}, {.bs = 50, .dw = 32, .ns = 2},
{.bs = 100, .dw = 80, .ce = 10}, {.bs = 100, .dw = 80, .ns = 1},
{.bs = 25, .dw = 9, .ce = 8}, {.bs = 25, .dw = 9, .ns = 4},
{.bs = 50, .dw = 24, .ce = 13} {.bs = 50, .dw = 24, .ns = 2}
} }
}, },
{ /* Version 5 */ { /* Version 5 */
.data_bytes = 134, .data_bytes = 134,
.apat = {6, 30, 0}, .apat = {6, 30, 0},
.ecc = { .ecc = {
{.bs = 67, .dw = 43, .ce = 12}, {.bs = 67, .dw = 43, .ns = 2},
{.bs = 134, .dw = 108, .ce = 13}, {.bs = 134, .dw = 108, .ns = 1},
{.bs = 33, .dw = 11, .ce = 11}, {.bs = 33, .dw = 11, .ns = 2},
{.bs = 33, .dw = 15, .ce = 9} {.bs = 33, .dw = 15, .ns = 2}
} }
}, },
{ /* Version 6 */ { /* Version 6 */
.data_bytes = 172, .data_bytes = 172,
.apat = {6, 34, 0}, .apat = {6, 34, 0},
.ecc = { .ecc = {
{.bs = 43, .dw = 27, .ce = 8}, {.bs = 43, .dw = 27, .ns = 4},
{.bs = 86, .dw = 68, .ce = 9}, {.bs = 86, .dw = 68, .ns = 2},
{.bs = 43, .dw = 15, .ce = 14}, {.bs = 43, .dw = 15, .ns = 4},
{.bs = 43, .dw = 19, .ce = 12} {.bs = 43, .dw = 19, .ns = 4}
} }
}, },
{ /* Version 7 */ { /* Version 7 */
.data_bytes = 196, .data_bytes = 196,
.apat = {6, 22, 38, 0}, .apat = {6, 22, 38, 0},
.ecc = { .ecc = {
{.bs = 49, .dw = 31, .ce = 9}, {.bs = 49, .dw = 31, .ns = 4},
{.bs = 98, .dw = 78, .ce = 10}, {.bs = 98, .dw = 78, .ns = 2},
{.bs = 39, .dw = 13, .ce = 13}, {.bs = 39, .dw = 13, .ns = 4},
{.bs = 32, .dw = 14, .ce = 9} {.bs = 32, .dw = 14, .ns = 2}
} }
}, },
{ /* Version 8 */ { /* Version 8 */
.data_bytes = 242, .data_bytes = 242,
.apat = {6, 24, 42, 0}, .apat = {6, 24, 42, 0},
.ecc = { .ecc = {
{.bs = 60, .dw = 38, .ce = 11}, {.bs = 60, .dw = 38, .ns = 2},
{.bs = 121, .dw = 97, .ce = 12}, {.bs = 121, .dw = 97, .ns = 2},
{.bs = 40, .dw = 14, .ce = 13}, {.bs = 40, .dw = 14, .ns = 4},
{.bs = 40, .dw = 18, .ce = 11} {.bs = 40, .dw = 18, .ns = 4}
} }
}, },
{ /* Version 9 */ { /* Version 9 */
.data_bytes = 292, .data_bytes = 292,
.apat = {6, 26, 46, 0}, .apat = {6, 26, 46, 0},
.ecc = { .ecc = {
{.bs = 58, .dw = 36, .ce = 11}, {.bs = 58, .dw = 36, .ns = 3},
{.bs = 146, .dw = 116, .ce = 15}, {.bs = 146, .dw = 116, .ns = 2},
{.bs = 36, .dw = 12, .ce = 12}, {.bs = 36, .dw = 12, .ns = 4},
{.bs = 36, .dw = 16, .ce = 10} {.bs = 36, .dw = 16, .ns = 4}
} }
}, },
{ /* Version 10 */ { /* Version 10 */
.data_bytes = 346, .data_bytes = 346,
.apat = {6, 28, 50, 0}, .apat = {6, 28, 50, 0},
.ecc = { .ecc = {
{.bs = 69, .dw = 43, .ce = 13}, {.bs = 69, .dw = 43, .ns = 4},
{.bs = 86, .dw = 68, .ce = 9}, {.bs = 86, .dw = 68, .ns = 2},
{.bs = 43, .dw = 15, .ce = 14}, {.bs = 43, .dw = 15, .ns = 6},
{.bs = 43, .dw = 19, .ce = 12} {.bs = 43, .dw = 19, .ns = 6}
} }
}, },
{ /* Version 11 */ { /* Version 11 */
.data_bytes = 404, .data_bytes = 404,
.apat = {6, 30, 54, 0}, .apat = {6, 30, 54, 0},
.ecc = { .ecc = {
{.bs = 80, .dw = 50, .ce = 15}, {.bs = 80, .dw = 50, .ns = 1},
{.bs = 101, .dw = 81, .ce = 10}, {.bs = 101, .dw = 81, .ns = 4},
{.bs = 36, .dw = 12, .ce = 12}, {.bs = 36, .dw = 12, .ns = 3},
{.bs = 50, .dw = 22, .ce = 14} {.bs = 50, .dw = 22, .ns = 4}
} }
}, },
{ /* Version 12 */ { /* Version 12 */
.data_bytes = 466, .data_bytes = 466,
.apat = {6, 32, 58, 0}, .apat = {6, 32, 58, 0},
.ecc = { .ecc = {
{.bs = 58, .dw = 36, .ce = 11}, {.bs = 58, .dw = 36, .ns = 6},
{.bs = 116, .dw = 92, .ce = 12}, {.bs = 116, .dw = 92, .ns = 2},
{.bs = 42, .dw = 14, .ce = 14}, {.bs = 42, .dw = 14, .ns = 7},
{.bs = 46, .dw = 20, .ce = 14} {.bs = 46, .dw = 20, .ns = 4}
} }
}, },
{ /* Version 13 */ { /* Version 13 */
.data_bytes = 532, .data_bytes = 532,
.apat = {6, 34, 62, 0}, .apat = {6, 34, 62, 0},
.ecc = { .ecc = {
{.bs = 59, .dw = 37, .ce = 11}, {.bs = 59, .dw = 37, .ns = 8},
{.bs = 133, .dw = 107, .ce = 13}, {.bs = 133, .dw = 107, .ns = 4},
{.bs = 33, .dw = 11, .ce = 11}, {.bs = 33, .dw = 11, .ns = 12},
{.bs = 44, .dw = 20, .ce = 12} {.bs = 44, .dw = 20, .ns = 8}
} }
}, },
{ /* Version 14 */ { /* Version 14 */
.data_bytes = 581, .data_bytes = 581,
.apat = {6, 26, 46, 66, 0}, .apat = {6, 26, 46, 66, 0},
.ecc = { .ecc = {
{.bs = 65, .dw = 41, .ce = 12}, {.bs = 64, .dw = 40, .ns = 4},
{.bs = 109, .dw = 87, .ce = 11}, {.bs = 145, .dw = 115, .ns = 3},
{.bs = 36, .dw = 12, .ce = 12}, {.bs = 36, .dw = 12, .ns = 11},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 36, .dw = 16, .ns = 11}
} }
}, },
{ /* Version 15 */ { /* Version 15 */
.data_bytes = 655, .data_bytes = 655,
.apat = {6, 26, 48, 70, 0}, .apat = {6, 26, 48, 70, 0},
.ecc = { .ecc = {
{.bs = 65, .dw = 41, .ce = 12}, {.bs = 65, .dw = 41, .ns = 5},
{.bs = 109, .dw = 87, .ce = 11}, {.bs = 109, .dw = 87, .ns = 5},
{.bs = 36, .dw = 12, .ce = 12}, {.bs = 36, .dw = 12, .ns = 11},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 5}
} }
}, },
{ /* Version 16 */ { /* Version 16 */
.data_bytes = 733, .data_bytes = 733,
.apat = {6, 26, 50, 74, 0}, .apat = {6, 26, 50, 74, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ce = 14}, {.bs = 73, .dw = 45, .ns = 7},
{.bs = 122, .dw = 98, .ce = 12}, {.bs = 122, .dw = 98, .ns = 5},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 3},
{.bs = 43, .dw = 19, .ce = 12} {.bs = 43, .dw = 19, .ns = 15}
} }
}, },
{ /* Version 17 */ { /* Version 17 */
.data_bytes = 815, .data_bytes = 815,
.apat = {6, 30, 54, 78, 0}, .apat = {6, 30, 54, 78, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ce = 14}, {.bs = 74, .dw = 46, .ns = 10},
{.bs = 135, .dw = 107, .ce = 14}, {.bs = 135, .dw = 107, .ns = 1},
{.bs = 42, .dw = 14, .ce = 14}, {.bs = 42, .dw = 14, .ns = 2},
{.bs = 50, .dw = 22, .ce = 14} {.bs = 50, .dw = 22, .ns = 1}
} }
}, },
{ /* Version 18 */ { /* Version 18 */
.data_bytes = 901, .data_bytes = 901,
.apat = {6, 30, 56, 82, 0}, .apat = {6, 30, 56, 82, 0},
.ecc = { .ecc = {
{.bs = 69, .dw = 43, .ce = 13}, {.bs = 69, .dw = 43, .ns = 9},
{.bs = 150, .dw = 120, .ce = 15}, {.bs = 150, .dw = 120, .ns = 5},
{.bs = 42, .dw = 14, .ce = 14}, {.bs = 42, .dw = 14, .ns = 2},
{.bs = 50, .dw = 22, .ce = 14} {.bs = 50, .dw = 22, .ns = 17}
} }
}, },
{ /* Version 19 */ { /* Version 19 */
.data_bytes = 991, .data_bytes = 991,
.apat = {6, 30, 58, 86, 0}, .apat = {6, 30, 58, 86, 0},
.ecc = { .ecc = {
{.bs = 70, .dw = 44, .ce = 13}, {.bs = 70, .dw = 44, .ns = 3},
{.bs = 141, .dw = 113, .ce = 14}, {.bs = 141, .dw = 113, .ns = 3},
{.bs = 39, .dw = 13, .ce = 13}, {.bs = 39, .dw = 13, .ns = 9},
{.bs = 47, .dw = 21, .ce = 13} {.bs = 47, .dw = 21, .ns = 17}
} }
}, },
{ /* Version 20 */ { /* Version 20 */
.data_bytes = 1085, .data_bytes = 1085,
.apat = {6, 34, 62, 90, 0}, .apat = {6, 34, 62, 90, 0},
.ecc = { .ecc = {
{.bs = 67, .dw = 41, .ce = 13}, {.bs = 67, .dw = 41, .ns = 3},
{.bs = 135, .dw = 107, .ce = 14}, {.bs = 135, .dw = 107, .ns = 3},
{.bs = 43, .dw = 15, .ce = 14}, {.bs = 43, .dw = 15, .ns = 15},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 15}
} }
}, },
{ /* Version 21 */ { /* Version 21 */
.data_bytes = 1156, .data_bytes = 1156,
.apat = {6, 28, 50, 72, 92, 0}, .apat = {6, 28, 50, 72, 92, 0},
.ecc = { .ecc = {
{.bs = 68, .dw = 42, .ce = 13}, {.bs = 68, .dw = 42, .ns = 17},
{.bs = 144, .dw = 116, .ce = 14}, {.bs = 144, .dw = 116, .ns = 4},
{.bs = 46, .dw = 16, .ce = 15}, {.bs = 46, .dw = 16, .ns = 19},
{.bs = 50, .dw = 22, .ce = 14} {.bs = 50, .dw = 22, .ns = 17}
} }
}, },
{ /* Version 22 */ { /* Version 22 */
.data_bytes = 1258, .data_bytes = 1258,
.apat = {6, 26, 50, 74, 98, 0}, .apat = {6, 26, 50, 74, 98, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ce = 14}, {.bs = 74, .dw = 46, .ns = 17},
{.bs = 139, .dw = 111, .ce = 14}, {.bs = 139, .dw = 111, .ns = 2},
{.bs = 37, .dw = 13, .ce = 12}, {.bs = 37, .dw = 13, .ns = 34},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 7}
} }
}, },
{ /* Version 23 */ { /* Version 23 */
.data_bytes = 1364, .data_bytes = 1364,
.apat = {6, 30, 54, 78, 102, 0}, .apat = {6, 30, 54, 78, 102, 0},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ce = 14}, {.bs = 75, .dw = 47, .ns = 4},
{.bs = 151, .dw = 121, .ce = 15}, {.bs = 151, .dw = 121, .ns = 4},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 16},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 11}
} }
}, },
{ /* Version 24 */ { /* Version 24 */
.data_bytes = 1474, .data_bytes = 1474,
.apat = {6, 28, 54, 80, 106, 0}, .apat = {6, 28, 54, 80, 106, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ce = 14}, {.bs = 73, .dw = 45, .ns = 6},
{.bs = 147, .dw = 117, .ce = 15}, {.bs = 147, .dw = 117, .ns = 6},
{.bs = 46, .dw = 16, .ce = 15}, {.bs = 46, .dw = 16, .ns = 30},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 11}
} }
}, },
{ /* Version 25 */ { /* Version 25 */
.data_bytes = 1588, .data_bytes = 1588,
.apat = {6, 32, 58, 84, 110, 0}, .apat = {6, 32, 58, 84, 110, 0},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ce = 14}, {.bs = 75, .dw = 47, .ns = 8},
{.bs = 132, .dw = 106, .ce = 13}, {.bs = 132, .dw = 106, .ns = 8},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 22},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 7}
} }
}, },
{ /* Version 26 */ { /* Version 26 */
.data_bytes = 1706, .data_bytes = 1706,
.apat = {6, 30, 58, 86, 114, 0}, .apat = {6, 30, 58, 86, 114, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ce = 14}, {.bs = 74, .dw = 46, .ns = 19},
{.bs = 142, .dw = 114, .ce = 14}, {.bs = 142, .dw = 114, .ns = 10},
{.bs = 46, .dw = 16, .ce = 15}, {.bs = 46, .dw = 16, .ns = 33},
{.bs = 50, .dw = 22, .ce = 14} {.bs = 50, .dw = 22, .ns = 28}
} }
}, },
{ /* Version 27 */ { /* Version 27 */
.data_bytes = 1828, .data_bytes = 1828,
.apat = {6, 34, 62, 90, 118, 0}, .apat = {6, 34, 62, 90, 118, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ce = 14}, {.bs = 73, .dw = 45, .ns = 22},
{.bs = 152, .dw = 122, .ce = 15}, {.bs = 152, .dw = 122, .ns = 8},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 12},
{.bs = 53, .dw = 23, .ce = 15} {.bs = 53, .dw = 23, .ns = 8}
} }
}, },
{ /* Version 28 */ { /* Version 28 */
.data_bytes = 1921, .data_bytes = 1921,
.apat = {6, 26, 50, 74, 98, 122, 0}, .apat = {6, 26, 50, 74, 98, 122, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ce = 14}, {.bs = 73, .dw = 45, .ns = 3},
{.bs = 147, .dw = 117, .ce = 15}, {.bs = 147, .dw = 117, .ns = 3},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 11},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 4}
} }
}, },
{ /* Version 29 */ { /* Version 29 */
.data_bytes = 2051, .data_bytes = 2051,
.apat = {6, 30, 54, 78, 102, 126, 0}, .apat = {6, 30, 54, 78, 102, 126, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ce = 14}, {.bs = 73, .dw = 45, .ns = 21},
{.bs = 146, .dw = 116, .ce = 15}, {.bs = 146, .dw = 116, .ns = 7},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 19},
{.bs = 73, .dw = 45, .ce = 14} {.bs = 53, .dw = 23, .ns = 1}
} }
}, },
{ /* Version 30 */ { /* Version 30 */
.data_bytes = 2185, .data_bytes = 2185,
.apat = {6, 26, 52, 78, 104, 130, 0}, .apat = {6, 26, 52, 78, 104, 130, 0},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ce = 14}, {.bs = 75, .dw = 47, .ns = 19},
{.bs = 145, .dw = 115, .ce = 15}, {.bs = 145, .dw = 115, .ns = 5},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 23},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 15}
} }
}, },
{ /* Version 31 */ { /* Version 31 */
.data_bytes = 2323, .data_bytes = 2323,
.apat = {6, 30, 56, 82, 108, 134, 0}, .apat = {6, 30, 56, 82, 108, 134, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ce = 14}, {.bs = 74, .dw = 46, .ns = 2},
{.bs = 145, .dw = 115, .ce = 15}, {.bs = 145, .dw = 115, .ns = 13},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 23},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 42}
} }
}, },
{ /* Version 32 */ { /* Version 32 */
.data_bytes = 2465, .data_bytes = 2465,
.apat = {6, 34, 60, 86, 112, 138, 0}, .apat = {6, 34, 60, 86, 112, 138, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ce = 14}, {.bs = 74, .dw = 46, .ns = 10},
{.bs = 145, .dw = 115, .ce = 15}, {.bs = 145, .dw = 115, .ns = 17},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 19},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 10}
} }
}, },
{ /* Version 33 */ { /* Version 33 */
.data_bytes = 2611, .data_bytes = 2611,
.apat = {6, 30, 58, 96, 114, 142, 0}, .apat = {6, 30, 58, 86, 114, 142, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ce = 14}, {.bs = 74, .dw = 46, .ns = 14},
{.bs = 145, .dw = 115, .ce = 15}, {.bs = 145, .dw = 115, .ns = 17},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 11},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 29}
} }
}, },
{ /* Version 34 */ { /* Version 34 */
.data_bytes = 2761, .data_bytes = 2761,
.apat = {6, 34, 62, 90, 118, 146, 0}, .apat = {6, 34, 62, 90, 118, 146, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ce = 14}, {.bs = 74, .dw = 46, .ns = 14},
{.bs = 145, .dw = 115, .ce = 15}, {.bs = 145, .dw = 115, .ns = 13},
{.bs = 46, .dw = 16, .ce = 15}, {.bs = 46, .dw = 16, .ns = 59},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 44}
} }
}, },
{ /* Version 35 */ { /* Version 35 */
.data_bytes = 2876, .data_bytes = 2876,
.apat = {6, 30, 54, 78, 102, 126, 150}, .apat = {6, 30, 54, 78, 102, 126, 150},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ce = 14}, {.bs = 75, .dw = 47, .ns = 12},
{.bs = 151, .dw = 121, .ce = 15}, {.bs = 151, .dw = 121, .ns = 12},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 22},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 39}
} }
}, },
{ /* Version 36 */ { /* Version 36 */
.data_bytes = 3034, .data_bytes = 3034,
.apat = {6, 24, 50, 76, 102, 128, 154}, .apat = {6, 24, 50, 76, 102, 128, 154},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ce = 14}, {.bs = 75, .dw = 47, .ns = 6},
{.bs = 151, .dw = 121, .ce = 15}, {.bs = 151, .dw = 121, .ns = 6},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 2},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 46}
} }
}, },
{ /* Version 37 */ { /* Version 37 */
.data_bytes = 3196, .data_bytes = 3196,
.apat = {6, 28, 54, 80, 106, 132, 158}, .apat = {6, 28, 54, 80, 106, 132, 158},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ce = 14}, {.bs = 74, .dw = 46, .ns = 29},
{.bs = 152, .dw = 122, .ce = 15}, {.bs = 152, .dw = 122, .ns = 17},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 24},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 49}
} }
}, },
{ /* Version 38 */ { /* Version 38 */
.data_bytes = 3362, .data_bytes = 3362,
.apat = {6, 32, 58, 84, 110, 136, 162}, .apat = {6, 32, 58, 84, 110, 136, 162},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ce = 14}, {.bs = 74, .dw = 46, .ns = 13},
{.bs = 152, .dw = 122, .ce = 15}, {.bs = 152, .dw = 122, .ns = 4},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 42},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 48}
} }
}, },
{ /* Version 39 */ { /* Version 39 */
.data_bytes = 3532, .data_bytes = 3532,
.apat = {6, 26, 54, 82, 110, 138, 166}, .apat = {6, 26, 54, 82, 110, 138, 166},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ce = 14}, {.bs = 75, .dw = 47, .ns = 40},
{.bs = 147, .dw = 117, .ce = 15}, {.bs = 147, .dw = 117, .ns = 20},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 10},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 43}
} }
}, },
{ /* Version 40 */ { /* Version 40 */
.data_bytes = 3706, .data_bytes = 3706,
.apat = {6, 30, 58, 86, 114, 142, 170}, .apat = {6, 30, 58, 86, 114, 142, 170},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ce = 14}, {.bs = 75, .dw = 47, .ns = 18},
{.bs = 148, .dw = 118, .ce = 15}, {.bs = 148, .dw = 118, .ns = 19},
{.bs = 45, .dw = 15, .ce = 15}, {.bs = 45, .dw = 15, .ns = 20},
{.bs = 54, .dw = 24, .ce = 15} {.bs = 54, .dw = 24, .ns = 34}
} }
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include "unicode.h" #include "unicode.h"
#include "fs.h" #include "fs.h"
#include "draw.h" #include "draw.h"
#include "ui_strings.h"
void splash_delete(void) void splash_delete(void)
{ {
@@ -34,7 +35,7 @@ void splash_delete(void)
remove("/luma/splashbottom.bin"); remove("/luma/splashbottom.bin");
} }
void splash_install(Entry_s splash) void splash_install(const Entry_s * splash)
{ {
char *screen_buf = NULL; char *screen_buf = NULL;
@@ -54,7 +55,7 @@ void splash_install(Entry_s splash)
if(size == 0 && bottom_size == 0) if(size == 0 && bottom_size == 0)
{ {
throw_error("No splash.bin or splashbottom.bin found.\nIs this a splash?", ERROR_LEVEL_WARNING); throw_error(language.splashes.no_splash_found, ERROR_LEVEL_WARNING);
} }
else else
{ {
@@ -65,7 +66,7 @@ void splash_install(Entry_s splash)
if(config_buf[0xC] == 0) if(config_buf[0xC] == 0)
{ {
free(config_buf); free(config_buf);
throw_error("WARNING: Splashes are disabled in Luma Config", ERROR_LEVEL_WARNING); throw_error(language.splashes.splash_disabled, ERROR_LEVEL_WARNING);
} }
} }
} }
@@ -103,8 +104,8 @@ void splash_check_installed(void * void_arg)
for(int i = 0; i < list->entries_count && arg->run_thread; i++) for(int i = 0; i < list->entries_count && arg->run_thread; i++)
{ {
Entry_s * splash = &list->entries[i]; Entry_s * splash = &list->entries[i];
top_size = load_data("/splash.bin", *splash, &top_buf); top_size = load_data("/splash.bin", splash, &top_buf);
bottom_size = load_data("/splashbottom.bin", *splash, &bottom_buf); bottom_size = load_data("/splashbottom.bin", splash, &bottom_buf);
if(!top_size && !bottom_size) if(!top_size && !bottom_size)
{ {

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -28,11 +28,12 @@
#include "unicode.h" #include "unicode.h"
#include "fs.h" #include "fs.h"
#include "draw.h" #include "draw.h"
#include "ui_strings.h"
#define BODY_CACHE_SIZE 0x150000 #define BODY_CACHE_SIZE 0x150000
#define BGM_MAX_SIZE 0x337000 #define BGM_MAX_SIZE 0x337000
static Result install_theme_internal(Entry_List_s themes, int installmode) static Result install_theme_internal(const Entry_List_s * themes, int installmode)
{ {
Result res = 0; Result res = 0;
char * music = NULL; char * music = NULL;
@@ -41,16 +42,17 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
char * body = NULL; char * body = NULL;
u32 body_size = 0; u32 body_size = 0;
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0}; u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0};
bool mono_audio = false;
if(installmode & THEME_INSTALL_SHUFFLE) if(installmode & THEME_INSTALL_SHUFFLE)
{ {
if(themes.shuffle_count < 2) if(themes->shuffle_count < 2)
{ {
DEBUG("not enough themes selected for shuffle\n"); DEBUG("not enough themes selected for shuffle\n");
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION); return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
} }
if(themes.shuffle_count > MAX_SHUFFLE_THEMES) if(themes->shuffle_count > MAX_SHUFFLE_THEMES)
{ {
DEBUG("too many themes selected for shuffle\n"); DEBUG("too many themes selected for shuffle\n");
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION); return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
@@ -67,20 +69,20 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
FSUSER_OpenFile(&body_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), FS_OPEN_WRITE, 0); FSUSER_OpenFile(&body_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), FS_OPEN_WRITE, 0);
} }
for(int i = 0; i < themes.entries_count; i++) for(int i = 0; i < themes->entries_count; i++)
{ {
Entry_s * current_theme = &themes.entries[i]; const Entry_s * current_theme = &themes->entries[i];
if(current_theme->in_shuffle) if(current_theme->in_shuffle)
{ {
if(installmode & THEME_INSTALL_BODY) if(installmode & THEME_INSTALL_BODY)
{ {
body_size = load_data("/body_LZ.bin", *current_theme, &body); body_size = load_data("/body_LZ.bin", current_theme, &body);
if(body_size == 0) if(body_size == 0)
{ {
free(body); free(body);
DEBUG("body not found\n"); DEBUG("body not found\n");
throw_error("No body_LZ.bin found - is this a theme?", ERROR_LEVEL_WARNING); throw_error(language.themes.no_body_found, ERROR_LEVEL_WARNING);
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND); return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
} }
@@ -100,7 +102,15 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
{ {
char bgm_cache_path[26] = {0}; char bgm_cache_path[26] = {0};
sprintf(bgm_cache_path, "/BgmCache_%.2i.bin", shuffle_count); sprintf(bgm_cache_path, "/BgmCache_%.2i.bin", shuffle_count);
music_size = load_data("/bgm.bcstm", *current_theme, &music);
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) if(music_size > BGM_MAX_SIZE)
{ {
@@ -109,6 +119,15 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE); return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
} }
if (music_size > 0)
{
if (music[0x62] == 1)
{
mono_audio = true;
}
}
}
shuffle_music_sizes[shuffle_count] = music_size; shuffle_music_sizes[shuffle_count] = music_size;
remake_file(fsMakePath(PATH_ASCII, bgm_cache_path), ArchiveThemeExt, BGM_MAX_SIZE); remake_file(fsMakePath(PATH_ASCII, bgm_cache_path), ArchiveThemeExt, BGM_MAX_SIZE);
@@ -116,8 +135,11 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
FSUSER_OpenFile(&bgm_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, bgm_cache_path), FS_OPEN_WRITE, 0); FSUSER_OpenFile(&bgm_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, bgm_cache_path), FS_OPEN_WRITE, 0);
padded = calloc(BGM_MAX_SIZE, sizeof(char)); padded = calloc(BGM_MAX_SIZE, sizeof(char));
if(!current_theme->no_bgm_shuffle && music_size)
{
memcpy(padded, music, music_size); memcpy(padded, music, music_size);
free(music); free(music);
}
FSFILE_Write(bgm_cache_handle, NULL, 0, padded, BGM_MAX_SIZE, FS_WRITE_FLUSH); FSFILE_Write(bgm_cache_handle, NULL, 0, padded, BGM_MAX_SIZE, FS_WRITE_FLUSH);
@@ -150,7 +172,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
} }
else else
{ {
Entry_s current_theme = themes.entries[themes.selected_entry]; const Entry_s * current_theme = &themes->entries[themes->selected_entry];
if(installmode & THEME_INSTALL_BODY) if(installmode & THEME_INSTALL_BODY)
{ {
@@ -159,7 +181,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
{ {
free(body); free(body);
DEBUG("body not found\n"); DEBUG("body not found\n");
throw_error("No body_LZ.bin found - is this a theme?", ERROR_LEVEL_WARNING); throw_error(language.themes.no_body_found, ERROR_LEVEL_WARNING);
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND); return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
} }
@@ -179,10 +201,35 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE); return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
} }
if (music_size != 0)
{
if (music[0x62] == 1)
{
mono_audio = true;
}
remake_file(fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, BGM_MAX_SIZE);
res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music); res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
free(music); free(music);
char * body_buf = NULL;
u32 uncompressed_size = decompress_lz_file(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &body_buf);
if (body_buf[5] != 1)
{
installmode |= THEME_INSTALL_BODY;
body_buf[5] = 1;
body_size = compress_lz_file_fast(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, body_buf, uncompressed_size);
}
free(body_buf);
}
if(R_FAILED(res)) return res; 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);
} }
} }
@@ -229,58 +276,410 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
SaveData_dat_s * savedata = (SaveData_dat_s *)savedata_buf; SaveData_dat_s * savedata = (SaveData_dat_s *)savedata_buf;
memset(&savedata->theme_entry, 0, sizeof(ThemeEntry_s)); memset(&savedata->theme_entry, 0, sizeof(ThemeEntry_s));
savedata->theme_entry.type = 3;
savedata->theme_entry.index = 0xff;
savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE); savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE) ? 1 : 0;
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s) * MAX_SHUFFLE_THEMES);
if(installmode & THEME_INSTALL_SHUFFLE) if(installmode & THEME_INSTALL_SHUFFLE)
{ {
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES); for(int i = 0; i < themes->shuffle_count; i++)
for(int i = 0; i < themes.shuffle_count; i++)
{ {
savedata->shuffle_themes[i].type = 3; savedata->shuffle_themes[i].type = 3;
savedata->shuffle_themes[i].index = i; savedata->shuffle_themes[i].index = i;
} }
const u8 shuffle_seed[0xB] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
memcpy(savedata->shuffle_seedA, shuffle_seed, 0xB);
memcpy(savedata->shuffle_seedB, shuffle_seed, 0xA);
}
else
{
savedata->theme_entry.type = 3;
savedata->theme_entry.index = 0xff;
} }
res = buf_to_file(savedata_size, fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, savedata_buf); res = buf_to_file(savedata_size, fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, savedata_buf);
free(savedata_buf); free(savedata_buf);
if(R_FAILED(res)) return res; if(R_FAILED(res)) return res;
//---------------------------------------- //----------------------------------------
if (mono_audio)
{
throw_error(language.themes.mono_warn, ERROR_LEVEL_WARNING);
}
return 0;
}
Result theme_install(Entry_s * theme)
{
Entry_List_s list = {0};
list.entries_count = 1;
list.entries = theme;
list.selected_entry = 0;
return install_theme_internal(&list, THEME_INSTALL_BODY | THEME_INSTALL_BGM);
}
Result bgm_install(Entry_s * theme)
{
Entry_List_s list = {0};
list.entries_count = 1;
list.entries = theme;
list.selected_entry = 0;
return install_theme_internal(&list, THEME_INSTALL_BGM);
}
Result no_bgm_install(Entry_s * theme)
{
Entry_List_s list = {0};
list.entries_count = 1;
list.entries = theme;
list.selected_entry = 0;
return install_theme_internal(&list, THEME_INSTALL_BODY);
}
Result shuffle_install(const Entry_List_s * themes)
{
return install_theme_internal(themes, THEME_INSTALL_SHUFFLE | THEME_INSTALL_BODY | THEME_INSTALL_BGM);
}
static SwkbdCallbackResult
dir_name_callback(void * data, const char ** ppMessage, const char * text, size_t textlen)
{
(void)textlen;
(void)data;
if(strpbrk(text, "><\"?;:/\\+,.|[=]"))
{
*ppMessage = language.themes.illegal_char;
return SWKBD_CALLBACK_CONTINUE;
}
return SWKBD_CALLBACK_OK;
}
Result dump_current_theme(void)
{
const int max_chars = 255;
char * output_dir = calloc(max_chars + 1, sizeof(char));
SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_WESTERN, 2, max_chars);
swkbdSetHintText(&swkbd, language.themes.name_folder);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, language.themes.cancel, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, language.themes.done, true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, dir_name_callback, NULL);
SwkbdButton button = swkbdInputText(&swkbd, output_dir, max_chars);
if (button != SWKBD_BUTTON_CONFIRM)
{
DEBUG("<dump_theme> Something went wrong with getting swkbd\n");
return MAKERESULT(RL_FATAL, RS_CANCELED, RM_UTIL, RD_CANCEL_REQUESTED);
}
u16 path[0x107] = { 0 };
struacat(path, "/themes/");
struacat(path, output_dir);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, path), FS_ATTRIBUTE_DIRECTORY);
char * thememanage_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
u32 theme_size = theme_manage->body_size;
u32 bgm_size = theme_manage->music_size;
free(thememanage_buf);
char * temp_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &temp_buf);
u16 path_output[0x107] = { 0 };
memcpy(path_output, path, 0x107);
struacat(path_output, "/body_LZ.bin");
remake_file(fsMakePath(PATH_UTF16, path_output), ArchiveSD, theme_size);
buf_to_file(theme_size, fsMakePath(PATH_UTF16, path_output), ArchiveSD, temp_buf);
free(temp_buf);
temp_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, &temp_buf);
memcpy(path_output, path, 0x107);
struacat(path_output, "/bgm.bcstm");
remake_file(fsMakePath(PATH_UTF16, path_output), ArchiveSD, bgm_size);
buf_to_file(bgm_size, fsMakePath(PATH_UTF16, path_output), ArchiveSD, temp_buf);
free(temp_buf);
temp_buf = NULL;
char * smdh_file = calloc(1, 0x36c0);
smdh_file[0] = 0x53; // SMDH magic
smdh_file[1] = 0x4d;
smdh_file[2] = 0x44;
smdh_file[3] = 0x48;
struacat((u16 *) (smdh_file + 0x8), output_dir);
struacat((u16 *) (smdh_file + 0x88), "No description");
struacat((u16 *) (smdh_file + 0x188), "Unknown Author");
free(output_dir);
u8 r = rand() % 255;
u8 g = rand() % 255;
u8 b = rand() % 255;
u16 color = ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3);
for (int i = 0x2040; i < 0x36c0; i += 2)
{
*(smdh_file + i) = (color & 0xFF00) >> 8;
*(smdh_file + i + 1) = color & 0x00FF;
}
memcpy(path_output, path, 0x107);
struacat(path_output, "/info.smdh");
remake_file(fsMakePath(PATH_UTF16, path_output), ArchiveSD, 0x36c0);
buf_to_file(0x36c0, fsMakePath(PATH_UTF16, path_output), ArchiveSD, smdh_file);
free(smdh_file);
return 0; return 0;
} }
inline Result theme_install(Entry_s theme) Result dump_all_themes(void)
{ {
Entry_List_s list = {0}; const u32 high_id = 0x0004008c;
list.entries_count = 1; u32 low_id = 0;
list.entries = &theme; u8 regionCode, language;
list.selected_entry = 0; Result res = CFGU_SecureInfoGetRegion(&regionCode);
return install_theme_internal(list, THEME_INSTALL_BODY | THEME_INSTALL_BGM); if(R_FAILED(res))
return res;
res = CFGU_GetSystemLanguage(&language);
if(R_FAILED(res))
return res;
switch(regionCode)
{
case CFG_REGION_JPN:
low_id = 0x00008200;
break;
case CFG_REGION_USA:
low_id = 0x00008f00;
break;
case CFG_REGION_EUR:
low_id = 0x00009800;
break;
case CFG_REGION_KOR:
low_id = 0x0000a900;
break;
default:
return -1;
} }
inline Result bgm_install(Entry_s theme) const char * region_arr[7] = {
"JPN",
"USA",
"EUR",
"AUS",
"CHN",
"KOR",
"TWN",
};
const char * language_arr[12] = {
"jp",
"en",
"fr",
"de",
"it",
"es",
"zh",
"ko",
"nl",
"pt",
"ru",
"tw",
};
res = amAppInit();
if(R_FAILED(res))
return res;
Icon_s * smdh_data = calloc(1, sizeof(Icon_s));
smdh_data->_padding1[0] = 0x53; // SMDH magic
smdh_data->_padding1[1] = 0x4d;
smdh_data->_padding1[2] = 0x44;
smdh_data->_padding1[3] = 0x48;
utf8_to_utf16(smdh_data->author, (u8 *)"Nintendo", 0x40);
utf8_to_utf16(smdh_data->desc, (u8 *)"Official theme. For personal use only. Do not redistribute.", 0x80);
for(u32 dlc_index = 0; dlc_index <= 0xFF; ++dlc_index)
{ {
Entry_List_s list = {0}; const u64 titleId = ((u64)high_id << 32) | (low_id | dlc_index);
list.entries_count = 1;
list.entries = &theme; u32 count = 0;
list.selected_entry = 0; res = AMAPP_GetDLCContentInfoCount(&count, MEDIATYPE_SD, titleId);
return install_theme_internal(list, THEME_INSTALL_BGM); if(res == (Result)0xd8a083fa)
{
res = 0;
break;
}
else if(R_FAILED(res))
{
break;
} }
inline Result no_bgm_install(Entry_s theme) AM_ContentInfo * contentInfos = calloc(count, sizeof(AM_ContentInfo));
u32 readcount = 0;
res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos);
if(R_FAILED(res))
{ {
Entry_List_s list = {0}; free(contentInfos);
list.entries_count = 1; break;
list.entries = &theme;
list.selected_entry = 0;
return install_theme_internal(list, THEME_INSTALL_BODY);
} }
inline Result shuffle_install(Entry_List_s themes) u32 archivePath[4] = {low_id | dlc_index, high_id, MEDIATYPE_SD, 0};
FS_Path ncch_path;
ncch_path.type = PATH_BINARY;
ncch_path.size = 0x10;
ncch_path.data = archivePath;
FS_Archive ncch_archive;
res = FSUSER_OpenArchive(&ncch_archive, ARCHIVE_SAVEDATA_AND_CONTENT, ncch_path);
if(R_FAILED(res))
{ {
return install_theme_internal(themes, THEME_INSTALL_SHUFFLE | THEME_INSTALL_BODY | THEME_INSTALL_BGM); free(contentInfos);
break;
}
u32 metadataPath[5] = {0, 0, 0, 0, 0};
FS_Path metadata_path;
metadata_path.type = PATH_BINARY;
metadata_path.size = 0x14;
metadata_path.data = metadataPath;
Handle metadata_fh;
res = FSUSER_OpenFile(&metadata_fh, ncch_archive, metadata_path, FS_OPEN_READ, 0);
if(R_FAILED(res))
{
FSUSER_CloseArchive(ncch_archive);
free(contentInfos);
break;
}
res = romfsMountFromFile(metadata_fh, 0, "meta");
if(R_FAILED(res))
{
FSFILE_Close(metadata_fh);
FSUSER_CloseArchive(ncch_archive);
free(contentInfos);
break;
}
char contentinfoarchive_path[40] = {0};
sprintf(contentinfoarchive_path, "meta:/ContentInfoArchive_%s_%s.bin", region_arr[regionCode], language_arr[language]);
FILE * fh = fopen(contentinfoarchive_path, "rb");
if(fh != NULL)
{
for(u32 i = 0; i < readcount; ++i)
{
if(i == 0) continue;
AM_ContentInfo * content = &contentInfos[i];
if((content->flags & (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) == (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED))
{
long off = 0x8 + 0xC8 * i;
fseek(fh, off, SEEK_SET);
char content_data[0xc8] = {0};
fread(content_data, 1, 0xc8, fh);
u32 extra_index = 0;
memcpy(&extra_index, content_data + 0xC0, 4);
metadataPath[1] = content->index;
Handle theme_fh;
res = FSUSER_OpenFile(&theme_fh, ncch_archive, metadata_path, FS_OPEN_READ, 0);
if(R_FAILED(res))
{
DEBUG("theme open romfs error: %08lx\n", res);
break;
}
romfsMountFromFile(theme_fh, 0, "theme");
char themename[0x41] = {0};
memcpy(themename, content_data, 0x40);
char * illegal_char = themename;
while ((illegal_char = strpbrk(illegal_char, ILLEGAL_CHARS)))
{
*illegal_char = '-';
}
char path[0x107] = { 0 };
sprintf(path, "/Themes/Dump-%02lx-%ld-%s", dlc_index, extra_index, themename);
DEBUG("theme folder to create: %s\n", path);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, path), FS_ATTRIBUTE_DIRECTORY);
memset(smdh_data->name, 0, sizeof(smdh_data->name));
utf8_to_utf16(smdh_data->name, (u8 *)(content_data + 0), 0x40);
FILE * theme_file = fopen("theme:/body_LZ.bin", "rb");
if(theme_file)
{
fseek(theme_file, 0, SEEK_END);
long theme_size = ftell(theme_file);
fseek(theme_file, 0, SEEK_SET);
char * theme_data = malloc(theme_size);
fread(theme_data, 1, theme_size, theme_file);
fclose(theme_file);
char themepath[0x107] = {0};
sprintf(themepath, "%s/body_LZ.bin", path);
remake_file(fsMakePath(PATH_ASCII, themepath), ArchiveSD, theme_size);
buf_to_file(theme_size, fsMakePath(PATH_ASCII, themepath), ArchiveSD, theme_data);
free(theme_data);
}
FILE * bgm_file = fopen("theme:/bgm.bcstm", "rb");
if(bgm_file)
{
fseek(bgm_file, 0, SEEK_END);
long bgm_size = ftell(bgm_file);
fseek(bgm_file, 0, SEEK_SET);
char * bgm_data = malloc(bgm_size);
fread(bgm_data, 1, bgm_size, bgm_file);
fclose(bgm_file);
char bgmpath[0x107] = {0};
sprintf(bgmpath, "%s/bgm.bcstm", path);
remake_file(fsMakePath(PATH_ASCII, bgmpath), ArchiveSD, bgm_size);
buf_to_file(bgm_size, fsMakePath(PATH_ASCII, bgmpath), ArchiveSD, bgm_data);
free(bgm_data);
}
romfsUnmount("theme");
char icondatapath[0x107] = {0};
sprintf(icondatapath, "meta:/icons/%ld.icn", extra_index);
FILE * iconfile = fopen(icondatapath, "rb");
fread(smdh_data->big_icon, 1, sizeof(smdh_data->big_icon), iconfile);
fclose(iconfile);
strcat(path, "/info.smdh");
remake_file(fsMakePath(PATH_ASCII, path), ArchiveSD, 0x36c0);
buf_to_file(0x36c0, fsMakePath(PATH_ASCII, path), ArchiveSD, (char *)smdh_data);
}
}
fclose(fh);
fh = NULL;
}
free(contentInfos);
contentInfos = NULL;
romfsUnmount("meta");
// don't need to close the file opened for the metadata, romfsUnmount took ownership
FSUSER_CloseArchive(ncch_archive);
}
free(smdh_data);
amExit();
return res;
} }
void themes_check_installed(void * void_arg) void themes_check_installed(void * void_arg)
@@ -340,7 +739,7 @@ void themes_check_installed(void * void_arg)
{ {
Entry_s * theme = &list->entries[i]; Entry_s * theme = &list->entries[i];
char * theme_body = NULL; char * theme_body = NULL;
u32 theme_body_size = load_data("/body_LZ.bin", *theme, &theme_body); u32 theme_body_size = load_data("/body_LZ.bin", theme, &theme_body);
if(!theme_body_size) return; if(!theme_body_size) return;
u8 theme_body_hash[HASH_SIZE_BYTES]; u8 theme_body_hash[HASH_SIZE_BYTES];

1048
source/ui_strings.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -34,12 +34,13 @@ ssize_t strulen(const u16 *input, ssize_t max_len)
void struacat(u16 * input, const char * addition) void struacat(u16 * input, const char * addition)
{ {
ssize_t len = strulen(input, 0x106); const ssize_t len = strulen(input, 0x106);
for (u16 i = len; i < strlen(addition) + len; i++) const u16 stop_at = strlen(addition);
for (u16 i = 0; i < stop_at; i++)
{ {
input[i] = addition[i - len]; input[i + len] = addition[i];
} }
input[strlen(addition) + len] = 0; input[stop_at + len] = 0;
} }
void printu(u16 * input) void printu(u16 * input)
@@ -61,5 +62,6 @@ u16 *strucat(u16 *destination, const u16 *source)
ssize_t source_len = strulen(source, 0x106); ssize_t source_len = strulen(source, 0x106);
memcpy(&destination[dest_len], source, source_len * sizeof(u16)); memcpy(&destination[dest_len], source, source_len * sizeof(u16));
destination[min(dest_len + source_len, 0x106 - 1)] = 0;
return destination; return destination;
} }