293 Commits

Author SHA1 Message Date
0a23b20f6e Backup badges if no badges found 2024-06-17 18:57:26 -04:00
a8cc3ebfc3 Filter newline and carriage returns when dumping themes 2024-06-17 17:04:35 -04:00
b1454bf2a1 Fix debug statement 2024-06-17 14:38:42 -04:00
1a2efdc129 Remove illegal fat32 chars in badge dump 2024-06-17 14:24:57 -04:00
e613a525a5 Properly close directories when installing badges 2024-06-17 10:52:10 -04:00
Alex Taber
a563683d32 Translation updates (#321)
Co-authored-by: Gato-kun <146145363+Gatokun@users.noreply.github.com>
Co-authored-by: arth <122994355+iveurne@users.noreply.github.com>
Co-authored-by: cooolgamer <64099608+cooolgamer@users.noreply.github.com>
2024-06-14 13:31:59 -04:00
0420b23fbf Add check for body_buf & properly free buffers
Also remove unused extdata dump method
2024-06-13 18:28:20 -04:00
c3e09adcb2 Badge dump overhaul 2024-06-12 18:04:33 -04:00
4669f13d05 Some changes to scroll algorithm 2024-06-12 11:04:52 -04:00
80dd00933b Properly zero files, badge/set index (unused?) 2024-06-11 17:01:05 -04:00
cooolgamer
bb18085413 UI Changes, moved hamburger menu (#317) 2024-06-11 10:52:16 -04:00
29233eadfb Progress bar more accurate 2024-06-10 11:27:09 -04:00
e40cd00754 Avoid loading 15 MB files into RAM ever 2024-06-10 10:48:34 -04:00
7abf989de8 Add progress bar for shuffle install 2024-06-08 12:29:35 -04:00
2ed3d8b34d Add progress bar for badge install 2024-06-08 10:43:35 -04:00
01e7f89a9a Cache last search and restore it upon failed search.
Closes #316
2024-06-08 00:54:23 -04:00
df4f6a8736 Add install_badges string to Spanish 2024-06-08 00:24:36 -04:00
4fdfad0c0a Properly follow channel info offsets 2024-06-07 23:15:10 -04:00
arth
74509d75db Portugese translation update (#314)
- Translated untranslated strings

- Fixed & shortened a few strings
2024-06-07 20:40:44 -04:00
Gato-kun
bb1e0ac872 Spanish
I really hope i did it right this time
2024-06-07 20:39:58 -04:00
cooolgamer
d426143eaf More French translations + Themes,Splashs,Badges cycling on TP using L&R (#313) 2024-06-07 10:04:59 -04:00
b2374deaf5 Better RAM Usage, proper null safety
Null safety added to some FS functions

Don't attempt to open multiple 15 MB files at once

Some strings changes so the themeplaza browser is better
2024-06-05 14:33:02 -04:00
d79a96fd75 Fix outdated english string 2024-06-04 22:46:45 -04:00
c2edc50750 Backup badge extdata before installing badges 2024-06-04 22:38:19 -04:00
8e876d027b Remove most compiler warnings 2024-06-04 17:43:27 -04:00
f8fba122ac Update README 2024-06-04 17:28:43 -04:00
e09c33f0e8 BCSTM Player 2024-06-04 17:17:16 -04:00
8f676eafae Fix preview regression introduced in df4c81c 2024-06-04 16:34:20 -04:00
77138530ed Update contributors 2024-06-03 15:46:03 -04:00
9860dec103 Ask on invalid zip if zip is badge file 2024-06-03 10:23:18 -04:00
1a01ba502f Add install badges from zip file 2024-06-03 00:58:07 -04:00
df4c81c96d Download SMDH files for badges
Thanks to ThemePlaza for providing these to make browser icons work
Also fixed badge previews by increasing tex size if the preview
image is larger than 512 pixel high
2024-06-02 20:51:39 -04:00
30833995d1 Add spanish translation
Also some minor badge changes

Co-authored-by: Tristanabs <76444200+Tristanabs@users.noreply.github.com>
2024-06-01 22:19:53 -04:00
b0db54e71a TP Badge Browser. Incomplete - see notes
TODO:

- Load icons for browser

- Fix previews (max tex size 512x512 and preview image 512x1024?
2024-05-31 12:05:18 -04:00
4d9cde01c4 Initial badges work
Additional cleanup probably necessary, some more feature work still in progress (zip files)
2024-05-30 15:45:57 -04:00
Zemogiter
8c42a23eeb Update README.md (#309)
Included lib-curl as a requirement for successful build.
2024-05-24 18:39:49 +01:00
arth
970b3c9749 Small fixes on Portuguese (#308) 2024-05-21 11:15:56 -04:00
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
LiquidFenrir
8022ed1682 add bgm preview to the themeplaza browser
and prevent some errors
2018-05-10 13:53:51 +02:00
5520ec2b2f Move load_audio into loading.c for consistency 2018-05-10 07:40:28 -04:00
f2aa7420df Add support for zip BGM preview 2018-05-10 06:49:03 -04:00
05b56b1aa1 Add BGM Preview Functionality
Some code adapted from Themely. Thanks to ErmanSayin
2018-05-09 22:26:39 -04:00
LiquidFenrir
cac2450fde fix copy-paste error 2018-05-09 00:22:34 +02:00
Alex Taber
1c24d08eef restore shuffle install speed back to roughly 30 seconds (#156) 2018-05-08 18:17:14 -04:00
Alex Taber
24b12023ca always 0-pad stuff (#155) 2018-05-08 16:39:22 -04:00
FrozenChen
9ccc705046 Make uncompressed logo smaller because of home menu bug (#150)
Closes #146
2018-04-15 21:17:31 -04:00
95d8808a73 Copyright Update
* Replaced contributors section in README with CONTRIBUTORS.md
* Updated copyright notice to include all contributors
* Updated SMDH
2018-04-10 16:09:10 -04:00
LiquidFenrir
6c2f09147f Themeplaza caching (#147)
* change fs to use a FS_Path argument for remake_file and buf_to_file

* more debug information when downloading

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

* add preview to cache

* this wasnt meant to be added now
2018-04-10 15:12:37 -04:00
LiquidFenrir
7b1d6f9860 implement L and R as extra menus instead of extra keys for X (#148)
L is now the sorting menu
R is unused
2018-04-09 19:28:37 -04:00
LiquidFenrir
6f7c2489d5 Different sorting modes (#143) (#145) 2018-04-04 22:04:45 +02:00
LiquidFenrir
38e70df17b Forgot to update the bottom screen when there's no entry (fix #144) 2018-04-04 11:40:38 +02:00
Helloman892
ad6117404f updated readme to fit: dkP updates, buildtools updates, library changes, lexigraphical errors 2018-04-03 23:48:47 +01:00
LiquidFenrir
47de3eb171 use X for extra options, behaves like A for installing themes 2018-04-03 21:17:34 +02:00
LiquidFenrir
02c1aab3f3 add warning for people using *hax entrypoints 2018-04-02 13:42:00 +02:00
LiquidFenrir
cd69aa7ef7 Switch to libarchive and camera autodetect between splashes and themes (#142)
* switch to libarchive and get rid of minizip, introduce zip_memory_to_buf

* automatically detect whether the downloaded zip is a splash or a theme

* slightly simpler way
2018-04-01 22:21:31 +02:00
LiquidFenrir
a2b5788fe8 Themeplaza browser (#140)
* builds at least

* meh, multithreading will come later. or never

* movement added, and correct grid mode

* switching splash/themes when in browser mode

* closer to the actual themeplaza menu

* bring back downloading from qr

* show a download screen when downloading from browser

* fix selecting with touchscreen in browser mode

* update readme for jansson

* fix quitting with start in browser mode

* add jump menu for browser mode

* rotate is broken, add working touchscreen page changing

* allow quitting preview mode with B in browser mode

* proper way to have portlibs

* add searching

* show error when search has no results

* always free entries and icon ids
2018-03-31 20:31:46 -04:00
LiquidFenrir
5090da114f fix shuffle brick (#134) 2018-01-28 02:37:52 +01:00
Dylan G
f01e347b17 Merge pull request #129 from TurtleP/master
add Flask meta
2018-01-13 21:36:19 +00:00
=
82f6e073ce add Flask meta 2018-01-13 16:32:55 -05:00
LiquidFenrir
ed94610d0f fix citra compatibility 2018-01-02 13:14:53 +01:00
LiquidFenrir
fbbb4f590d fix jump menu scrolling bug
leave everything to handle_scrolling
2018-01-01 23:37:14 +01:00
LiquidFenrir
d2e65d1edc add shutdown fix to missing extdata error 2017-12-31 17:38:31 +01:00
LiquidFenrir
fd7e932df3 add warning when not finding files when installing a splash, and dont delete previous one if none are found 2017-12-31 16:57:51 +01:00
Helloman892
b2e81528af added warning if not theme 2017-12-31 15:55:29 +00:00
LiquidFenrir
f5dd25369a check return values when installing themes before checking the installed mark 2017-12-31 16:47:13 +01:00
LiquidFenrir
fc9d0be42e prevent black background when loading themes 2017-12-31 16:46:46 +01:00
LiquidFenrir
c6f3af7350 Reload lists after scanning, and move text to bottom to leave top solely to camera (#126)
* reload lists after qr code is scanned

* move "press R to exit" to bottom screen
2017-12-31 16:01:26 +01:00
642b9f9c13 Update credits 2017-12-31 01:47:46 -05:00
10b130b416 Multithread QR scanning 2017-12-31 01:20:38 -05:00
LiquidFenrir
f65b32c90c Other scrolling method - fix hack (#122)
* other method: leave scrolling to icon updating function
downside: scroll can lag behind the selected entry if going too fast

* bring back looping so scrolling doesnt lag behind as much when going out of bounds
only about a second lag, then a second again for icons to load

* this method doesnt need to know the ids

* fix memory leak

* optimize for invisibility

* fix lockup when install checking threads are finished

* less magic, again
make icon loading functions/numbers more general

* increase wait time for fastcroll to give people more time to react, and to prevent some lag for the loading function
2017-12-30 23:01:31 -05:00
LiquidFenrir
841773d9e4 fix pressing the power button shutting down the console instead of showing the poweroff screen (#123) 2017-12-30 23:01:01 -05:00
FrozenChen
a764ea9bed Add logo. Artwork by kenn#7347.(Now to correct branch) (#124) 2017-12-31 02:26:11 +01:00
LiquidFenrir
45349a4ba3 Dynamic icons loading & other improvements (#119)
* icons are black, but it's a start

* after testing, info_buffer in load_smdh_icon is for some reason all 0

* working! same speed as pre-threading (I think)
but the icons lag behind a bit.
Still, should allow for infinite themes

* not needed anymore

* fix icons not being loaded when switching modes

* stop trying to load icons if there arent enough entries

* swapping logic almost there...

* only need to update when on the main screen

* fix blind spot and typo

* allow optimizations maybe?
maybe will break stuff. just revert if it's the case

* fix blind spot when going up after cycling

* add swapping when changing 1 screen at a time (left/right)

* not needed anymore, since icon ids are fixed

* simpler scrolling

* dont reload everything when cycling

* other method for storing ids, maybe better logic later

* fix crash

* attempt at better and clearer algorithm for swapping

* optimization, swapping still broken but this was needed

* fix cycling/using left resulting in reversed icons

* fix icons scrolling in reverse
and fix the bug that introduced (same as switch-case method): scroll not following the selected entry when using left/right

* don't break when using left/right near the top/end

* correctly return from failed realloc during entries load

* move freeing the entries and killing the icon updating thread to exit_function

* fix icons being shifted the wrong value when cycling

* only exit using the shutdown screen method when needed

* show simple loading screen for themes and splashes

* only have the thread when needed
should consume less battery for people with low amount of splashes and themes

* fix instructions showing over the "no X found" screen

* fix instructions showing over confirm text

* fix overlapping and going out of bounds with few entries

* add quitting preview and qr mode with B

* add touchscreen controls

* cleaner BETWEEN macro

* only allow changing screens with touchscreen when arrows are visible

* tabs vs spaces

* fix selecting an entry that's not there using the touchscreen
and allow holding for selecting individual entries (not using the arrows)

* fix crash when deleting all entries and downloading one with QR

* add indicator for already installed themes/splashes
threaded as not to slow down initial loading too much, and imo cool effect as they load

* optimization for theme installed check

* make icon swapping thread higher priority to prevent problems with searching the installed themes/splashes

* add indicator with number of themes and selected theme

* add X to reload icons if it breaks

* add touchscreen controls:
- toggle shuffle
- toggle preview
- reload icons
- switch modes
- enable QR

* more usable thread args

* fix crash when closing the application too close to launch

* add hack to solve the scrolling problem.
Warning: will cause some lag for about 1-2 seconds, so I recommend using the jump menu
this will be removed once the bug is figured out
2017-12-28 15:30:23 -05:00
Helloman892
c85d9d978d updated pp2d 2017-12-18 00:48:37 +00:00
LiquidFenrir
db0804af3b support all values returned from PTMU_GetBatteryLevel (#116)
and change the battery images style to look more like a battery
2017-12-15 21:02:33 +01:00
LiquidFenrir
310c92bee2 only mark preview as loaded if the png parsing actually worked, and add a warning (#112) 2017-12-15 20:37:29 +01:00
Dylan G
b59e5fc078 Ask for confirmation before deletion from SD (#113)
* added awful way to confirm deletion

* improve solution for asking for confirmation
based on throw_error

* fix COLOR_WHITE evaluating to -1 (outside the u32 range)

* add confirmation for splash deletion
2017-12-10 23:55:55 +00:00
LiquidFenrir
6eabfc84ab General instructions-drawing function (#111)
1 info line above, 4 rows, 2 columns.
2017-12-10 18:19:26 +01:00
LiquidFenrir
cb88c7e2dc make rule for simpler citra testing (#110)
disables the 2 disruptive parts
2017-12-09 06:57:48 +01:00
LiquidFenrir
b72f266e40 Fix the theme extdata error screen (#109)
* fix theme extdata not being present hanging the console

* custom draw_text_center function that takes care of newlines so the theme extdata error text is no longer broken
2017-12-09 06:50:34 +01:00
LiquidFenrir
f261f152c9 fix zip handles not being closed when file is not found
prevented zip themes/splashes with smdh in them to be deleted
2017-12-09 05:55:42 +01:00
LiquidFenrir
2848cade74 adapt draw for new start glyph 2017-12-09 04:29:42 +01:00
Nic
05d3ac73c0 Added new start glyph to romfs (#108) 2017-12-09 04:26:13 +01:00
LiquidFenrir
a92db32d67 New controls (#106)
* make theme delete function general and clean it up

* add steam big picture-like controls

* aesthetic changes
2017-12-09 04:25:49 +01:00
Nic
78bdb0d738 Proposed enhancement to Issue #94 on the select button "minus" (#104)
* Added select graphic and code to replace minus

* Actually added the select PNG to romfs
2017-12-08 14:01:01 -05:00
LiquidFenrir
23c98a4ed1 fix fastscroll 2017-12-08 18:26:58 +01:00
LiquidFenrir
15be473684 typo 2017-12-08 18:14:08 +01:00
LiquidFenrir
fb18dce177 fix overlapping icons if you have both themes and splashes (#105) 2017-12-08 17:11:52 +00:00
LiquidFenrir
adf2a50143 slightly better installing text, and fix battery icon in citra 2017-12-08 14:30:54 +01:00
LiquidFenrir
0f27cb1259 show warning when too many shuffle items are selected instead of erroring during the install 2017-12-07 16:47:22 +01:00
LiquidFenrir
c1ef601c2f fix preview logic to prevent old previews showing if none is found in the newly selected entry 2017-12-07 16:46:53 +01:00
LiquidFenrir
7c3ca57e07 lower risk of memory weirdness on quit...maybe 2017-12-05 20:29:54 +01:00
LiquidFenrir
0818389752 Less magic numbers, and centralized theme install function. (#103)
* less magic numbers

* part 2: installing themes

* fix simple theme install
that wasnt such a good idea

* fix shuffle count

* fix shuffle theme install

* change padding name
2017-12-05 17:29:31 +01:00
Helloman892
6be1b5e217 Added no_bgm_install(), ready for the menu. Or something. 2017-12-03 04:05:11 +00:00
LiquidFenrir
d937ae1716 Major rewrite: less repetition (#101)
* first step of rewriting: at least it compiles™

* fix tabs/spaces

* fix dabort on load

* fix preview crash

* sorting isnt done outside of loading

* step one of making remake_file useless

* camera/qr code cleanup

* fix dabort when folder is empty, and bring back preview optimization

* fix button for switching modes and show mode when folder is empty

* fix scanning qrs

* no more splash discrimination

* add debug helpers

* fix theme installing
turns out that wasnt such a good idea

* fix battery icon

* mistake

* themeplaza compatibility

* make use of load_data

* don't drink and copy-paste, kids

* fix user-agent

* remove useless

* cleanup includes

* not even used

* add splash buttons

* upgrade buttons drawing

* fix controls while preview is up

* improve positions
2017-12-02 10:09:38 -05:00
Helloman892
ab4ead03b5 Fixed minor spelling error in Makefile, and therefore in the output SMDH 2017-11-21 23:02:25 +00:00
ZetaDesigns
026bb367bc put themext error out of a void + no shuffle error 2017-11-17 22:00:15 +01:00
Helloman892
5c52b475ce Merge branch 'stable' of github.com:astronautlevel2/Anemone3ds into stable 2017-10-28 04:59:51 +01:00
Helloman892
32116aa8ed Added splash qsort, because I forgot to when I added theme qsort (<O_O<) <(O_O)> (>O_O>) 2017-10-28 04:59:30 +01:00
Dylan G
f01793321e Update README to match master
last one i promise
2017-10-22 01:29:53 +01:00
Dylan G
3cfbf3c35b Brought changes over from master/#92
All commits post-this one should be on stable.
2017-10-22 00:59:06 +01:00
Dylan G
36106b72f2 In-line with master updates (pushed to wrong branch) 2017-10-22 00:45:09 +01:00
Dylan G
1d95f47840 Removed submodule source/pp2d 2017-10-22 00:43:29 +01:00
Helloman892
204d5e8a85 added throw_error() for non-theme/splash QR targets 2017-10-16 19:46:20 +01:00
Luís Marques
6c3fdbaf31 Seems to stop it from crashing on non-theme QR scan. (#96)
This seems to stop Non-theme QR crash.
2017-10-16 09:46:53 -04:00
Dylan G
b5c78cf68b Implemented alphanum quicksort 2017-10-15 13:18:53 +01:00
24810a48ad Make sure that the buffer text is copied into is properly null-terminated 2017-09-29 21:56:44 -04:00
Dylan G
5d2119344a Getting ever closer to fixing the SMDH bug 2017-09-30 02:10:33 +01:00
Dylan G
492ea13ef6 Fixing a bad fix of mine '_>' 2017-09-30 02:00:00 +01:00
Helloman892
f99f744000 fixed theme extdata corruption when installing shuffle themes after
scanning a qr code
2017-09-16 17:48:47 +01:00
4fb10029bb Merge branch 'master' of github.com:3dsfug/Anemone3DS 2017-09-11 09:49:03 -04:00
7644974705 Add check for WiFi when scanning with QR code 2017-09-11 09:48:52 -04:00
Dylan G
b7379af2f3 added trello link 2017-09-10 14:57:26 +01:00
26d8c283b3 Add warning about splashes being disabled 2017-09-09 20:19:30 -04:00
4ceac06f0c improve no splashes interface 2017-09-09 20:12:10 -04:00
337693ca13 Improve no themes interface and create folders on boot 2017-09-09 20:10:36 -04:00
8ba1fed5d7 Change QR reader behavior, speed up 2017-09-09 11:33:54 -04:00
Helloman892
d99f1c97a0 fixes splash install
If splash.bin and/or splashbottom.bin didn't already exist, splashes would not install.

Also added a line break to separate splash_delete(), for consistency.
2017-09-09 13:07:31 +01:00
ZetaDesigns
1a1b3e25b0 Quick fix II - The sequel 2017-09-09 11:54:50 +02:00
c92c20d4e8 Quick fix 2017-09-09 00:26:40 -04:00
ef456c7e9e Splash previews 2017-09-09 00:12:16 -04:00
1103885baf Splash QR codes 2017-09-08 23:34:16 -04:00
5564ffe31b Merge branch 'master' of github.com:3dsfug/Anemone3DS 2017-09-08 23:26:59 -04:00
97ab2307f8 Slightly improve QR code speed 2017-09-08 23:26:49 -04:00
ZetaDesigns
47c64828a4 Merge branch 'master' of https://github.com/astronautlevel2/Anemone3DS 2017-09-08 23:18:41 +02:00
d3aa231c3f Update pp2d 2017-09-08 15:59:32 -04:00
Helloman892
3fb86c80be finally fixed everything
homebrew wasn't actually global, smh
2017-09-08 20:00:21 +01:00
ZetaDesigns
67566330a5 fix indentation and **properly** fix the compile error 2017-09-08 20:52:26 +02:00
68805337bf Merge branch 'master' of github.com:3dsfug/Anemone3DS 2017-09-08 14:04:53 -04:00
Helloman892
416cca88d3 fixing compile error
Upon attempting to build the previous commit, I discovered that Zeta was referring to a var that wasn't declared. Fixed.
2017-09-08 17:57:57 +01:00
2be3bde64d Merge branch 'master' of github.com:3dsfug/Anemone3DS 2017-09-08 07:21:34 -04:00
c46c5ff8cb More CAMU stuff 2017-09-08 07:21:31 -04:00
Helloman892
6b5b391d7b finally got theme deletion working + qr stuff
Theme deletion [SDMC] now works, although no button currently has it assigned. The QR reader now works properly with Unicode filenames.
2017-09-07 22:36:44 +01:00
ZetaDesigns
dc1cbda232 you saw nothing 2017-09-07 22:45:15 +02:00
ZetaDesigns
e63a4066a1 error codes now properly pause 2017-09-07 22:43:32 +02:00
2e71fc5954 Draw SMDH info 2017-09-07 13:55:52 -04:00
778e1a8331 maybe fix splashes 2017-09-07 13:35:56 -04:00
0871f4f30b Break QR code reader trying to make it faster 2017-09-07 13:08:38 -04:00
62f4356a17 Change splashes to support SMDH and zip files 2017-09-07 10:08:52 -04:00
ZetaDesigns
3e2c5994ca fix dabort when no theme data
themes still wont install like this
2017-09-06 18:33:23 +02:00
ZetaDesigns
674fa9d9bf add "downloading" text to qrcode scanning
i know it's not the prettiest idea to put it in draw_theme_install, but it seemed like the most fitting, and i didnt want to create another function for it
2017-09-06 17:49:10 +02:00
Nils P
897dc52424 Merge pull request #64 from saibotu/pr-bgmcrash
Fixed crash when installing themes without bgm
2017-09-06 17:03:51 +02:00
saibotu
379cea8db1 Fixed crash when installing themes without bgm 2017-09-06 15:36:02 +02:00
saibotu
e46783a3c4 Made buf_to_file always return Result 2017-09-06 15:35:31 +02:00
Helloman892
3fd4548d64 Merge pull request #62 from thedax/warning-fix
Fix potential bugs and two warnings relating to them
2017-09-06 10:19:07 +01:00
thedax
34ede05b93 Fix potential bugs and two warnings relating to them
Memset doesn't automatically set the entire size of each element, it's necessary to multiply by sizeof(<typename>) if it's larger than 1 byte, which in this case, u16, is (two bytes).
2017-09-06 05:01:42 -04:00
Helloman892
80b9aa9cdb Merge pull request #60 from Uyuiyu/master
Fix build warning
2017-09-06 09:20:46 +01:00
Uyuiyu
33cce81914 signed 2017-09-06 02:16:03 +02:00
Nils P
424fffd35f Merge pull request #57 from astronautlevel2/zeta-patch
Make Errorcodes great again (well, sorta)
2017-09-05 21:40:17 +02:00
ZetaDesigns
5b067cfac0 fixing some indentation 2017-09-05 21:37:48 +02:00
ZetaDesigns
539e95253c finish up basic errorcode functions 2017-09-05 21:31:50 +02:00
ZetaDesigns
a77b29da92 add errorcodes 2017-09-05 21:15:19 +02:00
Helloman892
dcac5401c1 several text drawing edits
All info.smdh fields now wrap after 300-odd pixels.
2017-09-05 13:10:43 +01:00
Helloman892
1467b1f72b Merge branch 'master' of https://github.com/astronautlevel2/Anemone3DS 2017-09-05 10:57:20 +01:00
Helloman892
9199936215 per PR #52 2017-09-05 10:56:00 +01:00
Helloman892
91fa4fa412 Resolves #22 (#52)
3 commits squashed and merged
2017-09-05 10:55:21 +01:00
Helloman892
1c6ae493d5 Merge pull request #51 from astronautlevel2/hm892
Fast paging [issue #14]
2017-09-05 01:30:33 +01:00
Helloman892
4eb969e84d fast page scrolling 2017-09-05 01:25:48 +01:00
Helloman892
70a40fae44 issue #14 commit for review (#50) 2017-09-04 19:25:52 -04:00
Helloman892
3d17509229 Merge pull request #49 from Helloman892/master
Resolving #35 as best as possible
2017-09-04 22:50:06 +01:00
Helloman892
a38508c30f undoes part of commit d3a8f79 2017-09-04 22:43:42 +01:00
Helloman892
0ddb35d3d7 see #35
percentage bar isn't practical.
counter was quite simple.
2017-09-04 22:41:26 +01:00
Helloman892
e03d150840 Merge pull request #45 from GreatWizard/optimiz_images
Clean battery images and reduce png size
2017-09-04 21:25:57 +01:00
Guillaume Gérard
4607885fe6 Clean battery images and reduce png size 2017-09-04 22:11:52 +02:00
Helloman892
d3a8f79958 beginnings of shuffled theme counter + a couple qr bugfixes 2017-09-04 20:13:09 +01:00
Helloman892
a08c032ccc Update main.c 2017-09-04 19:54:38 +01:00
Helloman892
bdaa8b52cc Update themes.c 2017-09-04 19:53:41 +01:00
Helloman892
8dc7f54f7e attempt to allow 10 themes to be installed 2017-09-04 19:46:55 +01:00
93 changed files with 10905 additions and 5038 deletions

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

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "source/pp2d"]
path = source/pp2d
url = https://github.com/BernardoGiordano/pp2d.git

24
CONTRIBUTORS.md Normal file
View File

@@ -0,0 +1,24 @@
# Main Contributors
* Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2))
* Théo B. ([@LiquidFenrir](https://github.com/LiquidFenrir))
* Dawid Eckert ([@daedreth](https://github.com/daedreth))
* Dylan G. ([@helloman892](https://github.com/helloman892))
* Nils P. ([@ZetaDesigns](https://github.com/ZetaDesigns))
* Matt Kenny ([@KennLDN](https://github.com/KennLDN))
# Minor Contributors
* Nic ([@Wizzrobes](https://github.com/Wizzrobes))
* [@saibotu](https://github.com/saibotu)
* Jeremy Postelnek ([@TurtleP](https://github.com/TurtleP))
* [@FrozenChen](https://github.com/FrozenChen)
* Luís Marques ([@luigoalma](https://github.com/luigoalma))
* [@uyuiyu](https://github.com/uyuiyu)
* Guillaume Gérard ([@GreatWizard](https://github.com/GreatWizard))
* Joel ([@joel16](https://github.com/joel16))
* [@thedax](https://github.com/thedax)
* [@Wryyyong](https://github.com/Wryyyong)
# Translation Contributors
* [@cooolgamer](https://github.com/cooolgamer/) for French
* Arth ([@iveurne](https://github.com/iveurne)) for Portugese
* [@Tristanabs](https://github.com/Tristanabs) and Angelpro09_xd for Spanish

204
Makefile
View File

@@ -9,18 +9,43 @@ 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 themeing tool for the 3DS APP_DESCRIPTION := A complete theming tool for the 3DS
APP_AUTHOR := astronautlevel and daedreth 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/minizip 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
@@ -33,40 +58,55 @@ BANNER_IMAGE := meta/banner.png
RSF_PATH := meta/app.rsf RSF_PATH := meta/app.rsf
# If left blank, makerom will use the default Homebrew logo # If left blank, makerom will use the default Homebrew logo
LOGO := 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
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
CFLAGS := -g -Wall -Wextra -mword-relocations \ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
-ffunction-sections \ -ffunction-sections \
$(ARCH) $(ARCH)
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE 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 libcurl vorbisidec libarchive jansson libpng`
ifneq ($(strip $(CITRA_MODE)),)
CFLAGS += -DCITRA_MODE
endif
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 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 := -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
# include and lib # include and lib
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
LIBDIRS := $(CTRULIB) $(DEVKITPRO)/portlibs/armv6k LIBDIRS := $(CTRULIB) $(PORTLIBS)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@@ -80,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)
@@ -89,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)/*.*)))
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@@ -105,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) \
@@ -115,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)))
@@ -136,53 +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
#---------------------------------------------------------------------------------
$(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)
@@ -197,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,32 +1,38 @@
![# 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 Nintendo3DS, written in C. A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.
# Dependencies # Dependencies
* zlib, which can be retrieved from the [3ds_portlibs](https://github.com/devkitPro/3ds_portlibs). * devkitARM, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started).
* [makerom](https://github.com/profi200/Project_CTR) and [bannertool](https://github.com/Steveice10/buildtools), which can be retrieved from [SteveIce10's](https://github.com/Steveice10) buildtools repo. These must be added to your PATH. * jansson, libvorbisidec, libpng, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702).
* [pp2d](https://github.com/BernardoGiordano/pp2d), which is included in the repo if you do a git clone --recursive. * 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.
# Building # Building
First of all, make sure devkitPRO is properly installed and added to the 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 type: `git clone https://github.com/astronautlevel2/Anemone3DS/ --recursive`. After that, open the directory you want to clone the repo into, and execute
Instructions for installing zlib can be found on the [3ds_portlibs repo](https://github.com/devkitPro/3ds_portlibs) (its easy, just run `make` and `make install-zlib`). After also adding [makerom](https://github.com/profi200/Project_CTR) and [bannertool](https://github.com/Steveice10/buildtools) to your PATH, just enter your directory and run `make`. All built files will be in `/out/`. `git clone https://github.com/astronautlevel2/Anemone3DS` (or any other cloning method).
To install the prerequisite libraries, begin by ensuring devkitPro pacman (and the base install group, `3ds-dev`) is installed, and then install the dkP packages `3ds-jansson`, `3ds-libvorbisidec`, `3ds-libpng`, `3ds-lz4`, `3ds-libarchive` and `3ds-curl` using `[sudo] [dkp-]pacman -S <package-name>`.
After adding [makerom](https://github.com/profi200/Project_CTR) and [bannertool](https://github.com/Steveice10/buildtools) to your PATH, just enter your directory and run `make`. All built binaries will be in `/out/`.
# License # 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.
# 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: 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)
* [Daedreth](https://github.com/daedreth), who wrote the initial implementation of theme application code and SMDH parsing.
* [LiquidFenrir](https://github.com/LiquidFenrir), who refactored a lot of my messy GUI code and wrote the image preview from zip code, as well as the icon code.
* [Sono](https://github.com/MarcuzD), who wrote the BCSTM playback code.
* [Kenn (mattkenster)](https://github.com/mattkenster), for designing the GUI, a number of sprites used in the application, and drawing the banner and icon.
Special thanks go to these people who, while not directly contributing, helped immensely: Most of the icons under `romfs` are from the site [icons8.com](https://icons8.com) and are licensed under the [CC-BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/3.0/)
Special thanks go to these people who, whilst not directly contributing, helped immensely:
* [Rinnegatamante](https://github.com/Rinnegatamante), whose code served as reference on theme installation. * [Rinnegatamante](https://github.com/Rinnegatamante), whose code served as reference on theme installation.
* [SteveIce10](https://github.com/SteveIce10), whose QR code in FBI was essential.
* [BernardoGiordano](https://github.com/BernardoGiordano) for making pp2d, and being super responsive to feature requests and just general help. * [BernardoGiordano](https://github.com/BernardoGiordano) for making pp2d, and being super responsive to feature requests and just general help.
* [yellows8](https://github.com/yellows8) for his home menu extdump tool, which was invaluable in debugging. * [yellows8](https://github.com/yellows8) for his home menu extdump tool, which was invaluable in debugging.
* the folks on #dev of Nintendo Homebrew, who helped with unicode shenanigans (especially [Stary2001](https://github.com/Stary2001), [Fenrir](https://github.com/FenrirWolf), and DanielKO). * [MrCheeze](https://github.com/MrCheeze) and [AntiMach](https://github.com/AntiMach) whose GYTB and ABE code served as a reference on badge management
* the maintainers for all used libraries, including ctrulib, zlib, citro3d, pp2d, quirc and minizip. * [Tobid7](https://github.com/tobid7) whose BCSTM-Player project served as a reference on the BCSTM format
* all the people who helped keep me going and motivated me to work. This includes, but is definitely not limited to: * The folks on #dev of Nintendo Homebrew, who helped with unicode shenanigans (especially [Stary2001](https://github.com/Stary2001), [Fenrir](https://github.com/FenrirWolf), and DanielKO).
* The maintainers for all used libraries, including but not limited to ctrulib, libarchive, citro3d, citro2d, and quirc. An especially big thanks to the devkitPro team for maintaining a phenomenal toolchain.
* All the people who helped keep me going and motivated me to work. This includes, but is definitely not limited to:
+ The members of the [Nintendo Homebrew Discord](https://discord.gg/C29hYvh) + The members of the [Nintendo Homebrew Discord](https://discord.gg/C29hYvh)
+ The members of the __Secret Shack Service Discord__ + The members of the __Secret Shack Service Discord__

BIN
assets/arrow_down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
assets/arrow_left.png Normal file

Binary file not shown.

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

BIN
assets/arrow_up.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
assets/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

BIN
assets/badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
assets/battery0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

BIN
assets/battery1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

BIN
assets/battery2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

BIN
assets/battery3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
assets/battery4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
assets/battery5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
assets/bgm_only.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
assets/browse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

BIN
assets/charging.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

BIN
assets/dump.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
assets/exit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

BIN
assets/install.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

BIN
assets/installed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

BIN
assets/menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

BIN
assets/no_home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
assets/preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

BIN
assets/qr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
assets/select.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
assets/shuffle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

BIN
assets/shuffle_no_bgm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

BIN
assets/sort.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

29
assets/sprites.t3s Normal file
View File

@@ -0,0 +1,29 @@
--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
badge.png

BIN
assets/start.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

42
include/badges.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2024 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 BADGES_H
#define BADGES_H
#include "common.h"
#include "fs.h"
#include "conversion.h"
#include "unicode.h"
#define MAX_BADGE 1000
#define BADGE_DATA_SIZE 0xF4DF80
#define BADGE_MNG_SIZE 0xD4A8
Result install_badges(void);
Result extract_badges(void);
#endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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,13 +29,25 @@
#include "common.h" #include "common.h"
struct quirc* context; typedef struct {
u16 *buf; u16 * camera_buffer;
bool qr_mode;
void init_qr(void); Handle event_stop;
void exit_qr(void); Thread cam_thread, ui_thread;
void take_picture(void);
Result http_get(char *url, char *path);
#endif 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;
} qr_data;
bool init_qr(void);
#endif

49
include/colors.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* 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 COLORS_H
#define COLORS_H
#include "common.h"
typedef u32 Color;
typedef enum {
COLOR_BACKGROUND, //silver-y black
COLOR_ACCENT,
COLOR_WHITE,
COLOR_CURSOR,
COLOR_BLACK,
COLOR_RED,
COLOR_YELLOW,
COLOR_AMOUNT,
} Colors_e;
extern Color colors[COLOR_AMOUNT];
void init_colors(void);
#endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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,33 +28,62 @@
#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>
#include <string.h> #include <string.h>
#define THEMES_PATH "/Themes/" #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define SPLASHES_PATH "/Splashes/" #define POS() DEBUG("%s (line %d)...\n", __func__, __LINE__)
#define SINGLE_INSTALL 0 #define DEBUGPOS(...) do {\
#define SHUFFLE_INSTALL 1 POS(); \
#define BGM_INSTALL 2 DEBUG(__VA_ARGS__); \
#define UNINSTALL 3 } while(0)
static const int THEMES_PER_SCREEN = 4; 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;
}
enum TextureID { #define FASTSCROLL_WAIT 1e8
TEXTURE_FONT_RESERVED = 0, //used by pp2d for the font
TEXTURE_ARROW,
TEXTURE_SHUFFLE,
TEXTURE_BATTERY_1,
TEXTURE_BATTERY_2,
TEXTURE_BATTERY_3,
TEXTURE_BATTERY_4,
TEXTURE_BATTERY_5,
TEXTURE_BATTERY_CHARGE,
TEXTURE_QR,
TEXTURE_PREVIEW,
};
#endif #define BETWEEN(min, x, max) (min < x && x < max)
typedef enum {
MODE_THEMES = 0,
MODE_SPLASHES,
MODE_AMOUNT,
} EntryMode;
typedef enum {
DRAW_MODE_LIST = 0,
DRAW_MODE_INSTALL,
DRAW_MODE_EXTRA,
DRAW_MODE_AMOUNT,
} DrawMode;
typedef enum {
REMOTE_MODE_THEMES = 0,
REMOTE_MODE_SPLASHES,
REMOTE_MODE_BADGES,
REMOTE_MODE_AMOUNT,
} RemoteMode;
extern const char * main_paths[MODE_AMOUNT];
extern const int entries_per_screen_v[MODE_AMOUNT];
extern const int entries_per_screen_h[MODE_AMOUNT];
extern const int entry_size[MODE_AMOUNT];
extern bool quit;
extern bool dspfirm;
#endif

11
include/conversion.h Normal file
View File

@@ -0,0 +1,11 @@
#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);
int pngToRGB565(char *png_buf, u64 fileSize, u16 *rgb_buf_64x64, u8 *alpha_buf_64x64, u16 *rgb_buf_32x32, u8 *alpha_buf_32x32, bool set_icon);
int rgb565ToPngFile(char *filename, u16 *rgb_buf, u8 *alpha_buf, int width, int height);
#endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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,18 +27,165 @@
#ifndef DRAW_H #ifndef DRAW_H
#define DRAW_H #define DRAW_H
#include "themes.h" #include "common.h"
#include "splashes.h" #include "loading.h"
#include "camera.h" #include "colors.h"
#define MAX_LINES 10
typedef enum InstallType_e {
INSTALL_LOADING_THEMES,
INSTALL_LOADING_SPLASHES,
INSTALL_LOADING_ICONS,
INSTALL_SPLASH,
INSTALL_SPLASH_DELETE,
INSTALL_SINGLE,
INSTALL_SHUFFLE,
INSTALL_BGM,
INSTALL_NO_BGM,
INSTALL_DOWNLOAD,
INSTALL_CHECKING_DOWNLOAD,
INSTALL_ENTRY_DELETE,
INSTALL_LOADING_REMOTE_THEMES,
INSTALL_LOADING_REMOTE_SPLASHES,
INSTALL_LOADING_REMOTE_BADGES,
INSTALL_LOADING_REMOTE_PREVIEW,
INSTALL_LOADING_REMOTE_BGM,
INSTALL_DUMPING_THEME,
INSTALL_DUMPING_ALL_THEMES,
INSTALL_DUMPING_BADGES,
INSTALL_BADGES,
INSTALL_NONE,
} InstallType;
typedef enum {
// InstallType text
TEXT_INSTALL_LOADING_THEMES,
TEXT_INSTALL_LOADING_SPLASHES,
TEXT_INSTALL_LOADING_ICONS,
TEXT_INSTALL_SPLASH,
TEXT_INSTALL_SPLASH_DELETE,
TEXT_INSTALL_SINGLE,
TEXT_INSTALL_SHUFFLE,
TEXT_INSTALL_BGM,
TEXT_INSTALL_NO_BGM,
TEXT_INSTALL_DOWNLOAD,
TEXT_INSTALL_CHECKING_DOWNLOAD,
TEXT_INSTALL_ENTRY_DELETE,
TEXT_INSTALL_LOADING_REMOTE_THEMES,
TEXT_INSTALL_LOADING_REMOTE_SPLASHES,
TEXT_INSTALL_LOADING_REMOTE_BADGES,
TEXT_INSTALL_LOADING_REMOTE_PREVIEW,
TEXT_INSTALL_LOADING_REMOTE_BGM,
TEXT_INSTALL_DUMPING_THEME,
TEXT_INSTALL_DUMPING_ALL_THEMES,
TEXT_INSTALL_DUMPING_BADGES,
TEXT_INSTALL_BADGES,
// 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_THEMEPLAZA_BADGE_MODE,
TEXT_SEARCH,
TEXT_PAGE,
TEXT_ERROR_QUIT,
TEXT_ERROR_CONTINUE,
TEXT_CONFIRM_YES_NO,
TEXT_AMOUNT
} Text;
typedef enum {
ERROR_LEVEL_ERROR,
ERROR_LEVEL_WARNING,
} ErrorLevel;
#define BUTTONS_START_Y 130
#define BUTTONS_STEP 22
#define BUTTONS_INFO_LINES 4
#define BUTTONS_INFO_COLUNMNS 2
typedef enum {
BUTTONS_Y_INFO = BUTTONS_START_Y+5,
BUTTONS_Y_LINE_1 = BUTTONS_START_Y + BUTTONS_STEP*1,
BUTTONS_Y_LINE_2 = BUTTONS_START_Y + BUTTONS_STEP*2,
BUTTONS_Y_LINE_3 = BUTTONS_START_Y + BUTTONS_STEP*3,
BUTTONS_Y_LINE_4 = BUTTONS_START_Y + BUTTONS_STEP*4,
BUTTONS_X_LEFT = 20,
BUTTONS_X_RIGHT = 200,
BUTTONS_X_MAX = 380,
} ButtonPos;
typedef struct {
const char * info_line;
const char * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
} Instructions_s;
extern C3D_RenderTarget * top;
extern C3D_RenderTarget * bottom;
extern C2D_TextBuf staticBuf, dynamicBuf;
extern C2D_Text text[TEXT_AMOUNT];
void init_screens(void); void init_screens(void);
void exit_screens(void); void exit_screens(void);
void draw_base_interface(void); void start_frame(void);
void draw_qr(void); void end_frame(void);
void draw_theme_install(int install_type); void set_screen(C3D_RenderTarget * screen);
void draw_theme_interface(Theme_s * themes_list, int theme_count, int selected_theme, bool preview_mode);
void draw_splash_install(int install_type);
void draw_splash_interface(Splash_s *splashes_list, int splash_count, int selected_splash, bool preview_mode);
#endif 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, float preview_scale);
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_grid_interface(Entry_List_s * list, Instructions_s instructions, int extra_mode);
void draw_interface(Entry_List_s * list, Instructions_s instructions, DrawMode draw_mode);
bool draw_confirm_no_interface(const char *conf_msg);
#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-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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,18 +28,47 @@
#define FS_H #define FS_H
#include "common.h" #include "common.h"
#include "badges.h"
FS_Archive ArchiveSD; #define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]*\n\r"
FS_Archive ArchiveHomeExt;
FS_Archive ArchiveThemeExt; extern FS_Archive ArchiveSD;
extern FS_Archive ArchiveHomeExt;
extern FS_Archive ArchiveThemeExt;
extern FS_Archive ArchiveBadgeExt;
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 open_badge_extdata(void);
Result close_archives(void); Result close_archives(void);
Result load_parental_controls(Parental_Restrictions_s *restrictions);
u64 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_file_to_buf(char *file_name, u16 *zip_path, 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(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);
u32 buf_to_file(u32 size, char *path, FS_Archive archive, char *buf); Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char * buf);
void remake_file(char *path, FS_Archive archive, u32 size); Result zero_handle_memeasy(Handle handle);
void remake_file(FS_Path path, FS_Archive archive, u32 size);
void save_zip_to_sd(char * filename, u32 size, char * buf, RemoteMode mode);
s16 for_each_file_zip(u16 *zip_path, u32 (*zip_iter_callback)(char *filebuf, u64 file_size, const char *name, void *userdata), void *userdata);
#endif #endif

80
include/loading.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* 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 LOADING_H
#define LOADING_H
#include "common.h"
#include "entries_list.h"
#include "music.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 {
ICONS_ABOVE = 0,
ICONS_VISIBLE,
ICONS_UNDER,
ICONS_OFFSET_AMOUNT,
};
typedef struct {
u8 _padding1[4 + 2 + 2];
u16 name[0x40];
u16 desc[0x80];
u16 author[0x40];
u8 _padding2[0x2000 - 0x200 + 0x30 + 0x8];
u16 small_icon[24 * 24];
u16 big_icon[48 * 48];
} Icon_s;
typedef struct {
void ** thread_arg;
volatile bool run_thread;
} Thread_Arg_s;
void copy_texture_data(C3D_Tex * texture, const u16 * src, const Entry_Icon_s * current_icon);
void parse_smdh(Icon_s * icon, Entry_s * entry, const u16 * fallback_name);
bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview_image, int * preview_offset, int height);
bool load_preview(const Entry_List_s * list, C2D_Image * preview_image, int * preview_offset);
void free_preview(C2D_Image preview_image);
Result load_audio(const Entry_s *, audio_s *);
Result load_audio_ogg(const Entry_s * entry, audio_ogg_s * audio);
void load_icons_first(Entry_List_s * current_list, bool silent);
void handle_scrolling(Entry_List_s * list);
void load_icons_thread(void * void_arg);
#endif

88
include/music.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.
*/
#ifndef MUSIC_H
#define MUSIC_H
#include "common.h"
#include "fs.h"
#include "unicode.h"
#include <tremor/ivorbisfile.h>
#include <tremor/ivorbiscodec.h>
#define BUFFER_COUNT 8
#define BUF_TO_READ 48000 // How much data should be buffered at a time for ogg
typedef struct {
char *music_buf;
size_t music_size;
size_t cursor;
u32 last_time;
bool is_little_endian;
bool is_looping;
u32 info_offset;
u32 data_offset;
u8 channel_count;
u32 sample_rate;
u32 loop_start;
u32 loop_end;
u32 num_blocks;
u32 block_size;
u32 block_samples;
u32 last_block_samples;
u32 last_block_size;
u32 current_block;
unsigned short adpcm_coefs[2][16];
ndspWaveBuf wave_buf[2][BUFFER_COUNT];
ndspAdpcmData adpcm_data[2][2];
unsigned short channel[2];
unsigned int active_channels;
u8 *buffer_data[2][BUFFER_COUNT];
volatile bool stop;
Thread playing_thread;
} audio_s;
typedef struct {
OggVorbis_File vf;
ndspWaveBuf wave_buf[2];
float mix[12];
u8 buf_pos;
long data_read;
char * filebuf;
u32 filesize;
volatile bool stop;
Thread playing_thread;
} audio_ogg_s;
void play_audio(audio_s *);
void stop_audio(audio_s **);
void play_audio_ogg(audio_ogg_s *);
void stop_audio_ogg(audio_ogg_s **);
#endif

74
include/remote.h Normal file
View File

@@ -0,0 +1,74 @@
/*
* 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 REMOTE_H
#define REMOTE_H
#include "common.h"
#include "draw.h"
#include <ctype.h>
#define THEMEPLAZA_BASE_URL "http://themeplaza.art"
#define THEMEPLAZA_API_URL "/api/anemone/v1"
#define THEMEPLAZA_BASE_API_URL THEMEPLAZA_BASE_URL THEMEPLAZA_API_URL
#define THEMEPLAZA_PAGE_FORMAT THEMEPLAZA_BASE_API_URL "/list?page=%" JSON_INTEGER_FORMAT "&category=%i&query=%s"
#define THEMEPLAZA_JSON_PAGE_COUNT "pages"
#define THEMEPLAZA_JSON_PAGE_IDS "items"
#define THEMEPLAZA_QUERY_ENTRY_INFO THEMEPLAZA_BASE_API_URL "/query?item_id=%" JSON_INTEGER_FORMAT
#define THEMEPLAZA_JSON_TITLE "title"
#define THEMEPLAZA_JSON_AUTHOR "author"
#define THEMEPLAZA_JSON_DESC "description"
#define THEMEPLAZA_JSON_SUCCESS "success"
#define THEMEPLAZA_JSON_ERROR_MESSAGE "message"
#define THEMEPLAZA_JSON_ERROR_MESSAGE_NOT_FOUND "No items found"
#define THEMEPLAZA_DOWNLOAD_FORMAT THEMEPLAZA_BASE_URL "/download/%" JSON_INTEGER_FORMAT
#define THEMEPLAZA_PREVIEW_FORMAT THEMEPLAZA_DOWNLOAD_FORMAT "/preview"
#define THEMEPLAZA_BGM_FORMAT THEMEPLAZA_DOWNLOAD_FORMAT "/bgm"
#define THEMEPLAZA_ICON_FORMAT THEMEPLAZA_DOWNLOAD_FORMAT "/preview/icon"
#define THEMEPLAZA_SMDH_FORMAT THEMEPLAZA_DOWNLOAD_FORMAT "/smdh"
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
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(RemoteMode mode);
Result http_get(const char * url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types);
#endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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,14 +28,11 @@
#define SPLASHES_H #define SPLASHES_H
#include "common.h" #include "common.h"
#include "loading.h"
typedef struct{ void splash_delete(void);
u16 name[0x106]; void splash_install(const Entry_s * splash);
u16 top_path[0x106];
u16 bottom_path[0x106]; void splash_check_installed(void * void_arg);
} Splash_s;
Result get_splashes(Splash_s** splashes_list, int *splash_count);
void splash_install(Splash_s splash_to_install);
void splash_delete();
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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,32 +28,57 @@
#define THEMES_H #define THEMES_H
#include "common.h" #include "common.h"
#include "loading.h"
#define MAX_SHUFFLE_THEMES 10
enum ThemeInstall {
THEME_INSTALL_SHUFFLE = BIT(0),
THEME_INSTALL_BODY = BIT(1),
THEME_INSTALL_BGM = BIT(2),
};
typedef struct { typedef struct {
u16 name[0x40]; u32 index;
u16 desc[0x80]; u8 dlc_tid_low_bits;
u16 author[0x40]; u8 type;
u16 unk;
u32 placeholder_color; } ThemeEntry_s;
ssize_t icon_id;
bool has_preview;
int preview_offset;
u16 path[0x106];
bool is_zip;
bool in_shuffle;
} Theme_s;
Theme_s * themes_list; typedef struct {
int theme_count; u8 _padding1[0x13b8];
ThemeEntry_s theme_entry;
ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES];
u8 shuffle_seedA[0xb];
u8 shuffle;
u8 shuffle_seedB[0xa];
} SaveData_dat_s;
void load_theme_preview(Theme_s *theme); typedef struct {
Result get_themes(Theme_s **themes_list, int *theme_count); u32 unk1;
void add_theme(Theme_s **themes_list, int *theme_count, char *path, char *filename); u32 unk2;
Result single_install(Theme_s theme); u32 body_size;
Result shuffle_install(Theme_s *themes_list, int theme_count); u32 music_size;
Result bgm_install(Theme_s bgm_to_install); u32 unk3;
u32 unk4;
u32 dlc_theme_content_index;
u32 use_theme_cache;
#endif u8 _padding1[0x338 - 8 * sizeof(u32)];
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES];
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES];
} ThemeManage_bin_s;
Result theme_install(Entry_s * theme);
Result no_bgm_install(Entry_s * theme);
Result bgm_install(Entry_s * theme);
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);
#endif

196
include/ui_strings.h Normal file
View File

@@ -0,0 +1,196 @@
/*
* 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"
typedef struct {
const char *quit;
const char *thread_error;
const char *zip_not_theme_splash;
const char *file_not_zip;
const char *download_failed;
const char *badge_question;
} 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 *tp_badge_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_badges;
const char *download_preview;
const char *download_bgm;
const char *dump_single;
const char *dump_all_official;
const char *dump_badges;
const char *install_badges;
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 {
const char *extdata_locked;
} Badge_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[REMOTE_MODE_AMOUNT];
Instructions_s remote_extra_instructions[3];
Splashes_Strings_s splashes;
Themes_Strings_s themes;
Badge_Strings_s badges;
} 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-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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,9 +29,10 @@
#include "common.h" #include "common.h"
ssize_t strulen(const u16*, ssize_t); void replace_chars(u16 *input, char *remove, u16 with);
void struacat(u16 *input, const char *addition); size_t strulen(const u16 *, ssize_t);
void printu(u16 *input); void struacat(u16 * input, const char * addition);
u16 *strucat(u16 *destination, const u16 *source); void printu(u16 * input);
size_t strucat(u16 * destination, const u16 * source);
#endif #endif

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
meta/logo.bin Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

798
source/badges.c Normal file
View File

@@ -0,0 +1,798 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2024 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.
*/
// Badge implementation adapted from GYTB by MrCheeze and ABE by AntiMach
// https://github.com/AntiMach/advanced-badge-editor
// https://github.com/MrCheeze/GYTB
#include "badges.h"
#include "draw.h"
#include "ui_strings.h"
static Handle actHandle;
Handle badgeDataHandle;
char *badgeMngBuffer;
u16 *rgb_buf_64x64;
u16 *rgb_buf_32x32;
u8 *alpha_buf_64x64;
u8 *alpha_buf_32x32;
u64 progress_finish;
u64 progress_status;
Result actInit(void)
{
return srvGetServiceHandle(&actHandle, "act:u");
}
Result actExit(void)
{
return svcCloseHandle(actHandle);
}
Result ACTU_Initialize(u32 sdkVersion, u32 memSize, Handle handle)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = 0x00010084;
cmdbuf[1] = sdkVersion;
cmdbuf[2] = memSize;
cmdbuf[3] = 0x20;
cmdbuf[4] = 0x0;
cmdbuf[5] = 0x0;
cmdbuf[6] = handle;
if ((ret = svcSendSyncRequest(actHandle)) != 0) return ret;
return (Result) cmdbuf[1];
}
Result ACTU_GetAccountDataBlock(u32 slot, u32 size, u32 blockId, u32 *output)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = 0x000600C2;
cmdbuf[1] = slot;
cmdbuf[2] = size;
cmdbuf[3] = blockId;
cmdbuf[4] = (size << 4) | 12;
cmdbuf[5] = (u32) output;
if ((ret = svcSendSyncRequest(actHandle)) != 0) return ret;
return (Result) cmdbuf[1];
}
void remove_exten(u16 *filename)
{
for (int i = 0; i < strulen(filename, 0x8A); ++i)
{
if (filename[i] == '.')
{
filename[i] = 0x00;
return;
}
}
}
u64 getShortcut(char *filename)
{
u64 shortcut = 0xFFFFFFFFFFFFFFFF;
char *p1 = strchr(filename, '.');
if (p1 == NULL) return shortcut;
++p1;
char *p2 = strchr(p1, '.');
if (p2 == NULL) return shortcut;
if (p2-p1 != 8) return shortcut;
unsigned int lowpath;
if (sscanf(p1, "%08x", &lowpath) != 1) return shortcut;
shortcut = 0x0004001000000000 + lowpath;
DEBUG("Shortcut %16llx found for %s\n", shortcut, filename);
return shortcut;
}
int install_badge_generic(char *file_buf, u64 file_size, u16 *name, int *badge_count, int set_id)
{
int badges_in_image = pngToRGB565(file_buf, file_size, rgb_buf_64x64, alpha_buf_64x64, rgb_buf_32x32, alpha_buf_32x32, false);
char utf8_name[512] = {0};
utf16_to_utf8((u8 *) utf8_name, name, 0x8A);
u64 shortcut = getShortcut(utf8_name);
int badges_installed = 0;
for (int badge = 0; badge < badges_in_image && *badge_count < 1000; ++badge)
{
remove_exten(name);
for (int j = 0; j < 16; ++j) // Copy name for all 16 languages
{
FSFILE_Write(badgeDataHandle, NULL, 0x35E80 + *badge_count * 16 * 0x8A + j * 0x8A, name, 0x8A, 0);
}
FSFILE_Write(badgeDataHandle, NULL, 0x318F80 + *badge_count * 0x2800, rgb_buf_64x64 + badge * 64 * 64, 64 * 64 * 2, 0);
FSFILE_Write(badgeDataHandle, NULL, 0x31AF80 + *badge_count * 0x2800, alpha_buf_64x64 + badge * 64 * 64/2, 64 * 64/2, 0);
FSFILE_Write(badgeDataHandle, NULL, 0xCDCF80 + *badge_count * 0xA00, rgb_buf_32x32 + badge * 32 * 32, 32 * 32 * 2, 0);
FSFILE_Write(badgeDataHandle, NULL, 0xCDD780 + *badge_count * 0xA00, alpha_buf_32x32 + badge * 32 * 32/2, 32 * 32/2, 0);
int badge_id = *badge_count + 1;
memcpy(badgeMngBuffer + 0x3E8 + *badge_count * 0x28 + 0x4, &badge_id, 4);
memcpy(badgeMngBuffer + 0x3E8 + *badge_count * 0x28 + 0x8, &set_id, 4);
memcpy(badgeMngBuffer + 0x3E8 + *badge_count*0x28 + 0xC, badge_count, 2);
badgeMngBuffer[0x3E8 + *badge_count*0x28 + 0x12] = 255; // Quantity Low
badgeMngBuffer[0x3E8 + *badge_count*0x28 + 0x13] = 255; // Quantity High
memcpy(badgeMngBuffer + 0x3E8 + *badge_count*0x28 + 0x18, &shortcut, 8);
memcpy(badgeMngBuffer + 0x3E8 + *badge_count*0x28 + 0x20, &shortcut, 8); // u64 shortcut[2], not sure what second is for
badgeMngBuffer[0x358 + *badge_count/8] |= 1 << (*badge_count % 8); // enabled badges bitfield
badges_installed++;
*badge_count += 1;
}
return badges_installed;
}
int install_badge_png(FS_Path badge_path, FS_DirectoryEntry badge_file, int *badge_count, int set_id)
{
u64 res;
char *file_buf = NULL;
res = file_to_buf(badge_path, ArchiveSD, &file_buf);
if (res != badge_file.fileSize)
{
return -1;
}
int badges = install_badge_generic(file_buf, badge_file.fileSize, badge_file.name, badge_count, set_id);
free(file_buf);
return badges;
}
typedef struct {
int *badge_count;
int set_id;
int installed;
} zip_userdata;
u32 zip_callback(char *file_buf, u64 file_size, const char *name, void *userdata)
{
progress_finish += 1;
zip_userdata *data = (zip_userdata *) userdata;
u16 *utf16_name = calloc(strlen(name), sizeof(u16));
utf8_to_utf16(utf16_name, (u8 *) name, strlen(name));
data->installed += install_badge_generic(file_buf, file_size, utf16_name, data->badge_count, data->set_id);
free(utf16_name);
progress_status += 1;
return 0;
}
int install_badge_zip(u16 *path, int *badge_count, int set_id)
{
zip_userdata data = {0};
data.set_id = set_id;
data.badge_count = badge_count;
for_each_file_zip(path, zip_callback, &data);
return data.installed;
}
int install_badge_dir(FS_DirectoryEntry set_dir, int *badge_count, int set_id)
{
Result res;
Handle folder;
int start_idx = *badge_count;
char *icon_buf = NULL;
int icon_size = 0;
FS_DirectoryEntry *badge_files = calloc(1024, sizeof(FS_DirectoryEntry));
u16 path[512] = {0};
u16 set_icon[17] = {0};
utf8_to_utf16(set_icon, (u8 *) "_seticon.png", 16);
struacat(path, "/Badges/");
strucat(path, set_dir.name);
res = FSUSER_OpenDirectory(&folder, ArchiveSD, fsMakePath(PATH_UTF16, path));
if (R_FAILED(res))
{
return -1;
}
u32 entries_read;
res = FSDIR_Read(folder, &entries_read, 1024, badge_files);
int badges_in_set = 0;
progress_finish += entries_read;
for (u32 i = 0; i < entries_read && *badge_count < 1000; ++i)
{
if (!strcmp(badge_files[i].shortExt, "PNG"))
{
memset(path, 0, 512 * sizeof(u16));
struacat(path, "/Badges/");
strucat(path, set_dir.name);
struacat(path, "/");
strucat(path, badge_files[i].name);
if (!memcmp(set_icon, badge_files[i].name, 16))
{
DEBUG("Found set icon for folder set %d\n", set_id);
icon_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &icon_buf);
continue;
}
badges_in_set += install_badge_png(fsMakePath(PATH_UTF16, path), badge_files[i], badge_count, set_id);
} else if (!strcmp(badge_files[i].shortExt, "ZIP"))
{
memset(path, 0, 512 * sizeof(u16));
struacat(path, "/Badges/");
strucat(path, set_dir.name);
struacat(path, "/");
strucat(path, badge_files[i].name);
badges_in_set += install_badge_zip(path, badge_count, set_id);
}
progress_status += 1;
draw_loading_bar(progress_status, progress_finish, INSTALL_BADGES);
}
if (!badges_in_set)
{
goto end;
}
int set_index = set_id - 1;
u32 total_count = 0xFFFF * badges_in_set;
for (int i = 0; i < 16; ++i)
{
FSFILE_Write(badgeDataHandle, NULL, set_index * 0x8A0 + i * 0x8A, set_dir.name, strulen(set_dir.name, 0x45) * 2 + 2, 0);
}
badgeMngBuffer[0x3D8 + set_index/8] |= 0 << (set_index % 8);
memset(badgeMngBuffer + 0xA028 + set_index * 0x30, 0xFF, 8);
badgeMngBuffer[0xA028 + 0xC + set_index * 0x30] = 0x10; // bytes 13 and 14 are 0x2710
badgeMngBuffer[0xA028 + 0xD + set_index * 0x30] = 0x27;
memcpy(badgeMngBuffer + 0xA028 + 0x10 + set_index * 0x30, &set_id, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x14 + set_index * 0x30, &set_index, 4);
memset(badgeMngBuffer + 0xA028 + 0x18 + set_index * 0x30, 0xFF, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x1C + set_index * 0x30, &badges_in_set, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x20 + set_index * 0x30, &total_count, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x24 + set_index * 0x30, &start_idx, 4);
int icon = pngToRGB565(icon_buf, icon_size, rgb_buf_64x64, alpha_buf_64x64, rgb_buf_32x32, alpha_buf_32x32, true);
if (icon == 0)
{
DEBUG("Falling back on default icon\n");
if (icon_buf) free(icon_buf);
FILE *fp = fopen("romfs:/hb_set.png", "rb");
fseek(fp, 0L, SEEK_END);
icon_size = ftell(fp);
icon_buf = malloc(icon_size);
fseek(fp, 0L, SEEK_SET);
fread(icon_buf, 1, icon_size, fp);
fclose(fp);
pngToRGB565(icon_buf, icon_size, rgb_buf_64x64, alpha_buf_64x64, rgb_buf_32x32, alpha_buf_32x32, true);
}
free(icon_buf);
FSFILE_Write(badgeDataHandle, NULL, 0x250F80 + set_index * 0x2000, rgb_buf_64x64, 64 * 64 * 2, 0);
badgeMngBuffer[0x3D8 + set_index/8] |= 0 << (set_index % 8);
end:
free(badge_files);
FSDIR_Close(folder);
return badges_in_set;
}
typedef struct set_node_s {
u32 set_id;
u32 set_index;
struct set_node_s *next;
} SetNode;
u32 get_set_index(SetNode *head, u32 set_id)
{
SetNode *cursor = head;
while (cursor != NULL)
{
if (cursor->set_id == set_id)
return cursor->set_index;
cursor = cursor->next;
}
return 0xFFFFFFFF;
}
void free_list(SetNode *head)
{
SetNode *cursor = head;
while (cursor)
{
SetNode *next = cursor->next;
free(cursor);
cursor = next;
}
}
SetNode * extract_sets(char *badgeMngBuffer, Handle backupDataHandle)
{
u32 setCount = *((u32 *) (badgeMngBuffer + 0x4));
if (setCount == 0) return NULL;
SetNode *head = calloc(1, sizeof(SetNode));
SetNode *cursor = head;
u16 *icon_rgb_buf = malloc(64 * 64 * 2);
u8 *icon_alpha_buf = malloc(64 * 64 * 0.5);
for (u32 i = 0; i < setCount; ++i)
{
memcpy(&(cursor->set_id), &badgeMngBuffer[0xA028 + 0x30 * i + 0x10], 4);
memcpy(&(cursor->set_index), &badgeMngBuffer[0xA028 + 0x30 * i + 0x14], 4);
if (cursor->set_id != 0xEFBE) // 0xEFBE is GYTB Set ID; GYTB doesn't properly create sets, so skip
{
DEBUG("Processing icon for set %lu at index %lu\n", cursor->set_id, cursor->set_index);
u16 utf16SetName[0x46] = {0};
FSFILE_Read(backupDataHandle, NULL, cursor->set_index * 16 * 0x8A, utf16SetName, 0x8A);
replace_chars(utf16SetName, ILLEGAL_CHARS, u'-');
u16 set_path[256] = {0};
struacat(set_path, "/3ds/" APP_TITLE "/BadgeBackups/");
size_t set_name_len = strucat(set_path, utf16SetName);
if (!set_name_len)
{
struacat(set_path, "Unknown Set");
}
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, set_path), FS_ATTRIBUTE_DIRECTORY);
memset(icon_alpha_buf, 255, 64 * 64 * 0.5);
FSFILE_Read(backupDataHandle, NULL, 0x250F80 + cursor->set_index * 0x2000, icon_rgb_buf, 0x2000);
char filename[256] = {0};
utf16_to_utf8((u8 *) filename, set_path, 256);
strcat(filename, "/_seticon.png");
DEBUG("%s\n", filename);
rgb565ToPngFile(filename, icon_rgb_buf, icon_alpha_buf, 48, 48);
}
cursor->next = calloc(1, sizeof(SetNode));
cursor = cursor->next;
}
free(icon_rgb_buf);
free(icon_alpha_buf);
return head;
}
Result extract_badges(void)
{
DEBUG("Dumping installed badges...\n");
char *badgeMngBuffer = NULL;
u32 size = file_to_buf(fsMakePath(PATH_ASCII, "/BadgeMngFile.dat"), ArchiveBadgeExt, &badgeMngBuffer);
DEBUG("%lu bytes read\n", size);
Result res = 0;
SetNode *head;
Handle backupDataHandle;
u16 *badge_rgb_buf = malloc(64 * 64 * 2);
u8 *badge_alpha_buf = malloc(64 * 64 * 0.5);
u32 badge_count = 0;
memcpy(&badge_count, badgeMngBuffer + 0x8, 4);
DEBUG("%lu badges found\n", badge_count);
if (badge_count > 0)
{
res = FSUSER_OpenFile(&backupDataHandle, ArchiveBadgeExt, fsMakePath(PATH_ASCII, "/BadgeData.dat"), FS_OPEN_READ, 0);
if (R_FAILED(res))
{
free(badgeMngBuffer);
free(badge_rgb_buf);
free(badge_alpha_buf);
char err_string[128] = {0};
sprintf(err_string, language.badges.extdata_locked, res);
throw_error(err_string, ERROR_LEVEL_WARNING);
DEBUG("backupDataHandle open failed\n");
return -1;
}
head = extract_sets(badgeMngBuffer, backupDataHandle);
} else {
free(badgeMngBuffer);
free(badge_rgb_buf);
free(badge_alpha_buf);
return 0;
}
for (u32 i = 0; i < badge_count; ++i)
{
u32 badgeId;
memcpy(&badgeId, badgeMngBuffer + 0x3E8 + i * 0x28 + 0x4, 4);
u32 badgeSetId;
memcpy(&badgeSetId, badgeMngBuffer + 0x3E8 + i * 0x28 + 0x8, 4);
u16 badgeSubId;
memcpy(&badgeSubId, badgeMngBuffer + 0x3E8 + i * 0x28 + 0xE, 2);
u32 shortcut;
memcpy(&shortcut, badgeMngBuffer + 0x3E8 + i * 0x28 + 0x18, 4);
char filename[512] = {0};
u16 utf16Name[0x46] = {0};
FSFILE_Read(backupDataHandle, NULL, 0x35E80 + i * 16 * 0x8A, utf16Name, 0x8A);
replace_chars(utf16Name, ILLEGAL_CHARS, u'-');
char utf8Name[256] = {0};
res = utf16_to_utf8((u8 *) utf8Name, utf16Name, 256);
char dir[256] = "/3ds/" APP_TITLE "/BadgeBackups/";
if (badgeSetId != 0xEFBE) // 0xEFBE is GYTB Set ID; GYTB doesn't properly create sets, so skip
{
u32 set_index = get_set_index(head, badgeSetId);
if (set_index == 0xFFFFFFFF) {
sprintf(dir, "/3ds" APP_TITLE "/BadgeBackups/Unknown Set");
} else
{
u16 utf16SetName[0x46] = {0};
FSFILE_Read(backupDataHandle, NULL, set_index * 16 * 0x8A, utf16SetName, 0x8A);
replace_chars(utf16SetName, ILLEGAL_CHARS, u'-');
char utf8SetName[128] = {0};
res = utf16_to_utf8((u8 *) utf8SetName, utf16SetName, 128);
if (!res)
strncpy(utf8SetName, "Unknown Set", 128);
DEBUG("UTF-8 Set Name: %s; ID: %lx\n", utf8SetName, badgeSetId);
sprintf(dir, "/3ds/" APP_TITLE "/BadgeBackups/%s", utf8SetName);
}
}
if (shortcut == 0xFFFFFFFF)
{
sprintf(filename, "%s/%s.%lx.%x.png", dir, utf8Name, badgeId, badgeSubId);
} else
{
sprintf(filename, "%s/%s.%08lx.%lx.%x.png", dir, utf8Name, shortcut, badgeId, badgeSubId);
}
DEBUG("Dump filename: %s\n", filename);
FSFILE_Read(backupDataHandle, NULL, 0x318F80 + i * 0x2800, badge_rgb_buf, 0x2000);
FSFILE_Read(backupDataHandle, NULL, 0x318F80 + i * 0x2800 + 0x2000, badge_alpha_buf, 0x800);
rgb565ToPngFile(filename, badge_rgb_buf, badge_alpha_buf, 64, 64);
draw_loading_bar(i + 1, badge_count, INSTALL_DUMPING_BADGES);
}
free(badgeMngBuffer);
free(badge_rgb_buf);
free(badge_alpha_buf);
free_list(head);
FSFILE_Close(backupDataHandle);
return res;
}
Result backup_badges_fast(void)
{
char *badgeMng = NULL;
DEBUG("writing badge data: making files...\n");
char mng_path[128] = "/3ds/" APP_TITLE "/BadgeMngFile.dat";
char data_path[128] = "/3ds/" APP_TITLE "/BadgeData.dat";
DEBUG("mng_path: %s, data_path: %s\n", mng_path, data_path);
Handle dataHandle = 0;
Handle sdHandle = 0;
DEBUG("loading existing badge mng file...\n");
u32 mngRead = file_to_buf(fsMakePath(PATH_ASCII, "/BadgeMngFile.dat"), ArchiveBadgeExt, &badgeMng);
DEBUG("loading existing badge data file\n");
Result res = FSUSER_OpenFile(&dataHandle, ArchiveBadgeExt, fsMakePath(PATH_ASCII, "/BadgeData.dat"), FS_OPEN_READ, 0);
if (mngRead != BADGE_MNG_SIZE || R_FAILED(res))
{
char err_string[128] = {0};
sprintf(err_string, language.badges.extdata_locked, res);
throw_error(err_string, ERROR_LEVEL_WARNING);
if (badgeMng) free(badgeMng);
if (dataHandle) FSFILE_Close(dataHandle);
FSFILE_Close(sdHandle);
return -1;
}
remake_file(fsMakePath(PATH_ASCII, mng_path), ArchiveSD, BADGE_MNG_SIZE);
FSUSER_CreateFile(ArchiveSD, fsMakePath(PATH_ASCII, data_path), 0, BADGE_DATA_SIZE);
FSUSER_OpenFile(&sdHandle, ArchiveSD, fsMakePath(PATH_ASCII, data_path), FS_OPEN_WRITE, 0);
DEBUG("writing badge data: writing BadgeMngFile...\n");
res = buf_to_file(mngRead, fsMakePath(PATH_ASCII, mng_path), ArchiveSD, badgeMng);
if (R_FAILED(res))
{
DEBUG("Failed to write badgemngfile: 0x%08lx\n", res);
free(badgeMng);
FSFILE_Close(dataHandle);
FSFILE_Close(sdHandle);
return -1;
}
DEBUG("writing badge data: writing badgedata...\n");
char *buf = malloc(0x10000);
u64 size = BADGE_DATA_SIZE;
u64 cur = 0;
while (size > 0)
{
u32 read = 0;
res = FSFILE_Read(dataHandle, &read, cur, buf, min(0x10000, size));
res = FSFILE_Write(sdHandle, NULL, cur, buf, read, FS_WRITE_FLUSH);
size -= read;
cur += read;
}
free(badgeMng);
free(buf);
FSFILE_Close(dataHandle);
FSFILE_Close(sdHandle);
return 0;
}
Result install_badges(void)
{
Handle handle = 0;
Handle folder = 0;
Result res = 0;
draw_loading_bar(0, 1, INSTALL_BADGES);
{
char testpath[128] = "/3ds/" APP_TITLE "/BadgeData.dat";
if (R_FAILED(res = FSUSER_OpenFile(&handle, ArchiveSD, fsMakePath(PATH_ASCII, testpath), FS_OPEN_READ, 0)))
{
if (R_SUMMARY(res) == RS_NOTFOUND)
{
res = backup_badges_fast();
if (R_FAILED(res)) return res;
} else
{
DEBUG("????: 0x%08lx\n", res);
}
}
}
if (handle) FSFILE_Close(handle);
DEBUG("Initializing ACT\n");
res = actInit();
if (R_FAILED(res))
{
DEBUG("actInit() failed!\n");
return res;
}
DEBUG("Initializing ACTU\n");
res = ACTU_Initialize(0xB0502C8, 0, 0);
if (R_FAILED(res))
{
DEBUG("ACTU_Initialize failed! %08lx\n", res);
return res;
}
DEBUG("Getting NNID\n");
u32 nnidNum = 0xFFFFFFFF;
res = ACTU_GetAccountDataBlock(0xFE, 4, 12, &nnidNum);
if (R_FAILED(res))
{
DEBUG("ACTU_GetAccountDataBlock failed! %08lx\n", res);
return res;
}
DEBUG("NNID found: 0x%08lx\n", nnidNum);
badgeMngBuffer = NULL;
badgeDataHandle = 0;
rgb_buf_64x64 = NULL;
rgb_buf_32x32 = NULL;
alpha_buf_64x64 = NULL;
alpha_buf_32x32 = NULL;
DEBUG("Opening badge directory\n");
FS_DirectoryEntry *badge_files = calloc(1024, sizeof(FS_DirectoryEntry));
res = FSUSER_OpenDirectory(&folder, ArchiveSD, fsMakePath(PATH_ASCII, "/Badges/"));
if (R_FAILED(res))
{
DEBUG("Failed to open folder: %lx\n", res);
goto end;
}
u32 entries_read;
res = FSDIR_Read(folder, &entries_read, 1024, badge_files);
DEBUG("%lu files found\n", entries_read);
rgb_buf_64x64 = malloc(12*6*64*64*2); //12x6 badges in sheet max, 64x64 pixel badges, 2 bytes per RGB data
alpha_buf_64x64 = malloc(12*6*64*64/2); //Same thing, but 2 pixels of alpha data per byte
rgb_buf_32x32 = malloc(12*6*32*32*2); //Same thing, but 32x32
alpha_buf_32x32 = malloc(12*6*32*32/2);
res = FSUSER_OpenFile(&badgeDataHandle, ArchiveBadgeExt, fsMakePath(PATH_ASCII, "/BadgeData.dat"), FS_OPEN_WRITE, 0);
badgeMngBuffer = calloc(1, BADGE_MNG_SIZE);
if (!rgb_buf_64x64)
{
DEBUG("rgb_buf_64x64 alloc failed\n");
goto end;
}
if (!alpha_buf_64x64)
{
DEBUG("alpha_buf_64x64 alloc failed\n");
goto end;
}
if (!rgb_buf_32x32)
{
DEBUG("rgb_buf_32x32 alloc failed\n");
goto end;
}
if (!alpha_buf_32x32)
{
DEBUG("alpha_buf_32x32 alloc failed\n");
goto end;
}
if (R_FAILED(res))
{
badgeDataHandle = 0;
char err_string[128] = {0};
sprintf(err_string, language.badges.extdata_locked, res);
throw_error(err_string, ERROR_LEVEL_WARNING);
DEBUG("badgeDataHandle open failed\n");
goto end;
}
if (!badgeMngBuffer)
{
DEBUG("badgeMngBuffer alloc failed\n");
goto end;
}
zero_handle_memeasy(badgeDataHandle);
int badge_count = 0;
int set_count = 0;
int default_set = 0;
int default_set_count = 0;
int default_idx = 0;
progress_finish = entries_read + 12;
progress_status = 12;
draw_loading_bar(progress_status, progress_finish, INSTALL_BADGES);
for (u32 i = 0; i < entries_read && badge_count < 1000; ++i)
{
if (!strcmp(badge_files[i].shortExt, "PNG"))
{
if (default_set == 0)
{
set_count += 1;
default_set = set_count;
default_idx = badge_count;
}
u16 path[0x512] = {0};
struacat(path, "/Badges/");
strucat(path, badge_files[i].name);
default_set_count += install_badge_png(fsMakePath(PATH_UTF16, path), badge_files[i], &badge_count, default_set);
} else if (!strcmp(badge_files[i].shortExt, "ZIP"))
{
if (default_set == 0)
{
set_count += 1;
default_set = set_count;
}
u16 path[0x512] = {0};
struacat(path, "/Badges/");
strucat(path, badge_files[i].name);
default_set_count += install_badge_zip(path, &badge_count, default_set);
} else if (badge_files[i].attributes & FS_ATTRIBUTE_DIRECTORY)
{
set_count += 1;
u32 count = install_badge_dir(badge_files[i], &badge_count, set_count);
if (count == 0)
set_count -= 1;
}
progress_status += 1;
draw_loading_bar(progress_status, progress_finish, INSTALL_BADGES);
}
DEBUG("Badges installed - doing metadata\n");
if (default_set != 0)
{
int default_index = default_set - 1;
u32 total_count = 0xFFFF * default_set_count;
for (int i = 0; i < 16; ++i)
{
u16 name[0x8A/2] = {0};
utf8_to_utf16(name, (u8 *) "Other Badges", 0x8A);
FSFILE_Write(badgeDataHandle, NULL, default_index * 0x8A0 + i * 0x8A, &name, strulen(name, 0x45) * 2, 0);
}
badgeMngBuffer[0x3D8 + default_index/8] |= 1 << (default_index % 8);
memset(badgeMngBuffer + 0xA028 + default_index * 0x30, 0xFF, 8);
badgeMngBuffer[0xA028 + 0xC + default_index * 0x30] = 0x10; // bytes 13 and 14 are 0x2710
badgeMngBuffer[0xA028 + 0xD + default_index * 0x30] = 0x27;
memcpy(badgeMngBuffer + 0xA028 + 0x10 + default_index * 0x30, &default_set, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x14 + default_index * 0x30, &default_index, 4);
memset(badgeMngBuffer + 0xA028 + 0x18 + default_index * 0x30, 0xFF, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x1C + default_index * 0x30, &default_set_count, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x20 + default_index * 0x30, &total_count, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x24 + default_index * 0x30, &default_idx, 4);
FILE *fp = fopen("romfs:/anemone_set.png", "rb");
fseek(fp, 0L, SEEK_END);
ssize_t size = ftell(fp);
char *icon_buf = malloc(size);
fseek(fp, 0L, SEEK_SET);
fread(icon_buf, 1, size, fp);
fclose(fp);
pngToRGB565(icon_buf, size, rgb_buf_64x64, alpha_buf_64x64, rgb_buf_32x32, alpha_buf_32x32, true);
free(icon_buf);
FSFILE_Write(badgeDataHandle, NULL, 0x250F80 + default_index * 0x2000, rgb_buf_64x64, 64 * 64 * 2, 0);
}
FSFILE_Flush(badgeDataHandle);
u32 total_badges = 0xFFFF * badge_count; // Quantity * unique badges?
badgeMngBuffer[0x4] = set_count; // badge set count
memcpy(badgeMngBuffer + 0x8, &badge_count, 4);
badgeMngBuffer[0x10] = 0xFF; // Selected Set - 0xFFFFFFFF is all badges
badgeMngBuffer[0x11] = 0xFF;
badgeMngBuffer[0x12] = 0xFF;
badgeMngBuffer[0x13] = 0xFF;
memcpy(badgeMngBuffer + 0x18, &total_badges, 4);
memcpy(badgeMngBuffer + 0x1C, &nnidNum, 4);
for (int i = set_count; i < 100; ++i) // default values for remaining sets
{
memset(badgeMngBuffer + 0xA028 + 0x30 * i, 0xFF, 0x8);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x8, 0x00, 0x4);
badgeMngBuffer[0xA028 + 0x30 * i + 0xC] = 0x10;
badgeMngBuffer[0xA028 + 0x30 * i + 0xD] = 0x27;
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0xE, 0x0, 0x2);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x10, 0xFF, 0xC);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x1C, 0x00, 0x8);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x24, 0xFF, 0x4);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x28, 0x00, 0x8);
}
res = FSUSER_OpenFile(&handle, ArchiveBadgeExt, fsMakePath(PATH_ASCII, "/BadgeMngFile.dat"), FS_OPEN_READ, 0);
if (res == 0)
{
FSFILE_Read(handle, NULL, 0xB2E8, badgeMngBuffer+0xB2E8, 360 * 0x18);
FSFILE_Close(handle);
}
res = buf_to_file(BADGE_MNG_SIZE, fsMakePath(PATH_ASCII, "/BadgeMngFile.dat"), ArchiveBadgeExt, badgeMngBuffer);
if (res)
{
DEBUG("Error writing badge manage data! %lx\n", res);
char err_string[128] = {0};
sprintf(err_string, language.badges.extdata_locked, res);
throw_error(err_string, ERROR_LEVEL_WARNING);
goto end;
}
end:
actExit();
if (rgb_buf_64x64) free(rgb_buf_64x64);
if (alpha_buf_64x64) free(alpha_buf_64x64);
if (rgb_buf_32x32) free(rgb_buf_32x32);
if (alpha_buf_32x32) free(alpha_buf_32x32);
if (handle) FSFILE_Close(handle);
if (folder) FSDIR_Close(folder);
if (badgeDataHandle) FSFILE_Close(badgeDataHandle);
if (badgeMngBuffer) free(badgeMngBuffer);
if (badge_files) free(badge_files);
return res;
}

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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,183 +27,403 @@
#include "camera.h" #include "camera.h"
#include "quirc/quirc.h" #include "quirc/quirc.h"
#include "draw.h" #include "draw.h"
#include "fs.h" #include "fs.h"
#include "themes.h" #include "loading.h"
#include "remote.h"
#include "ui_strings.h"
void init_qr(void) #include <archive.h>
#include <archive_entry.h>
static void start_read(qr_data * data)
{ {
camInit(); LightLock_Lock(&data->mut);
CAMU_SetSize(SELECT_OUT1_OUT2, SIZE_CTR_TOP_LCD, CONTEXT_A); while(data->writer_waiting || data->writer_active)
CAMU_SetOutputFormat(SELECT_OUT1_OUT2, OUTPUT_RGB_565, CONTEXT_A); {
CondVar_WaitTimeout(&data->cond, &data->mut, 1000000);
}
CAMU_SetNoiseFilter(SELECT_OUT1_OUT2, true); AtomicIncrement(&data->num_readers_active);
CAMU_SetAutoExposure(SELECT_OUT1_OUT2, true); LightLock_Unlock(&data->mut);
CAMU_SetAutoWhiteBalance(SELECT_OUT1_OUT2, true); }
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;
CAMU_SetTrimming(PORT_CAM1, false); while(data->num_readers_active)
{
CondVar_WaitTimeout(&data->cond, &data->mut, 1000000);
}
buf = malloc(sizeof(u16) * 400 * 240 * 2); data->writer_waiting = false;
data->writer_active = true;
context = quirc_new(); LightLock_Unlock(&data->mut);
quirc_resize(context, 400, 240); }
static void stop_write(qr_data * data)
{
LightLock_Lock(&data->mut);
data->writer_active = false;
CondVar_Broadcast(&data->cond);
LightLock_Unlock(&data->mut);
} }
void exit_qr(void) static void capture_cam_thread(void * arg)
{ {
qr_data * data = (qr_data *) arg;
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();
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A);
CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A);
CAMU_SetFrameRate(SELECT_OUT1, FRAME_RATE_30);
CAMU_SetNoiseFilter(SELECT_OUT1, true);
CAMU_SetAutoExposure(SELECT_OUT1, true);
CAMU_SetAutoWhiteBalance(SELECT_OUT1, true);
CAMU_Activate(SELECT_OUT1);
CAMU_GetBufferErrorInterruptEvent(&cam_events[2], PORT_CAM1);
CAMU_SetTrimming(PORT_CAM1, false);
CAMU_GetMaxBytes(&transferUnit, 400, 240);
CAMU_SetTransferBytes(PORT_CAM1, transferUnit, 400, 240);
CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
CAMU_StartCapture(PORT_CAM1);
bool cancel = false;
while (!cancel)
{
s32 index = 0;
svcWaitSynchronizationN(&index, cam_events, 3, false, U64_MAX);
switch(index) {
case 0:
DEBUG("Cancel event received\n");
cancel = true;
break;
case 1:
svcCloseHandle(cam_events[1]);
cam_events[1] = 0;
start_write(data);
memcpy(data->camera_buffer, buffer, bufsz);
data->any_update = true;
stop_write(data);
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
break;
case 2:
svcCloseHandle(cam_events[1]);
cam_events[1] = 0;
CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
CAMU_StartCapture(PORT_CAM1);
break;
default:
break;
}
}
CAMU_StopCapture(PORT_CAM1);
bool busy = false;
while(R_SUCCEEDED(CAMU_IsBusy(&busy, PORT_CAM1)) && busy) {
svcSleepThread(1000000);
}
CAMU_ClearBuffer(PORT_CAM1);
CAMU_Activate(SELECT_NONE); CAMU_Activate(SELECT_NONE);
camExit(); camExit();
quirc_destroy(context);
free(buf); linearFree(buffer);
for(int i = 1; i < 3; i++)
{
if(cam_events[i] != 0) {
svcCloseHandle(cam_events[i]);
cam_events[i] = 0;
}
}
LightEvent_Signal(&data->event_cam_info);
} }
void take_picture(void) static void update_ui(void * arg)
{ {
u32 transfer_size; qr_data * data = (qr_data *) arg;
Handle cam_handle = 0; C3D_Tex tex;
CAMU_GetMaxBytes(&transfer_size, 400, 240);
CAMU_SetTransferBytes(PORT_BOTH, transfer_size, 400, 240); static const Tex3DS_SubTexture subt3x = { 400, 240, 0.0f, 1.0f, 400.0f/512.0f, 1.0f - (240.0f/256.0f) };
CAMU_Activate(SELECT_OUT1_OUT2); C3D_TexInit(&tex, 512, 256, GPU_RGB565);
CAMU_ClearBuffer(PORT_BOTH);
CAMU_SynchronizeVsyncTiming(SELECT_OUT1, SELECT_OUT2); C3D_TexSetFilter(&tex, GPU_LINEAR, GPU_LINEAR);
CAMU_StartCapture(PORT_BOTH);
CAMU_SetReceiving(&cam_handle, buf, PORT_CAM1, 400 * 240 * 2, transfer_size); while(svcWaitSynchronization(data->event_stop, 2 * 1000 * 1000ULL) == 0x09401BFE) // timeout of 2ms occured, still have 14 for copy and render
svcWaitSynchronization(cam_handle, U64_MAX); {
CAMU_StopCapture(PORT_BOTH); draw_base_interface();
svcCloseHandle(cam_handle);
CAMU_Activate(PORT_NONE); // 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)
Putting this in camera because I'm too lazy to make a network.c
This'll probably get refactored later
*/
Result http_get(char *url, char *path)
{ {
Result ret; if((data->cam_thread = threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, false)) == NULL)
httpcContext context; {
char *new_url = NULL; throw_error(language.camera.thread_error, ERROR_LEVEL_ERROR);
u32 status_code; LightEvent_Signal(&data->event_cam_info);
u32 content_size = 0; LightEvent_Signal(&data->event_ui_info);
u32 read_size = 0; return false;
u32 size = 0; }
u8 *buf; if((data->ui_thread = threadCreate(update_ui, data, 0x10000, 0x1A, 1, false)) == NULL)
u8 *last_buf; {
LightEvent_Signal(&data->event_ui_info);
return false;
}
return true;
}
do { static bool update_qr(qr_data * data, struct quirc_data * scan_data)
ret = httpcOpenContext(&context, HTTPC_METHOD_GET, url, 1); {
ret = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); // should let us do https int w;
ret = httpcSetKeepAlive(&context, HTTPC_KEEPALIVE_ENABLED); int h;
ret = httpcAddRequestHeaderField(&context, "User-Agent", "Anemone3DS/1.1.0"); u8 * image = (u8 *)quirc_begin(data->context, &w, &h);
ret = httpcAddRequestHeaderField(&context, "Connection", "Keep-Alive");
ret = httpcBeginRequest(&context); start_read(data);
if (ret != 0) for (int y = 0; y < h; y++) {
const int actual_y = y * w;
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);
}
}
stop_read(data);
quirc_end(data->context);
if(quirc_count(data->context) > 0)
{
struct quirc_code code;
quirc_extract(data->context, 0, &code);
if (!quirc_decode(&code, scan_data))
{ {
httpcCloseContext(&context); return true;
if (new_url != NULL) free(new_url); }
return ret; }
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;
} }
ret = httpcGetResponseStatusCode(&context, &status_code); finished = update_qr(&data, scan_data);
if(ret!=0){ svcSleepThread(50 * 1000 * 1000ULL); // only scan every 50ms
httpcCloseContext(&context); }
if(new_url!=NULL) free(new_url);
return ret; exit_qr(&data);
bool success = false;
if(finished && ready)
{
draw_install(INSTALL_DOWNLOAD);
char * zip_buf = NULL;
char * filename = NULL;
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 ((status_code >= 301 && status_code <= 303) || (status_code >= 307 && status_code <= 308)) if(zip_size != 0)
{ {
if (new_url == NULL) new_url = malloc(0x1000); draw_install(INSTALL_CHECKING_DOWNLOAD);
ret = httpcGetResponseHeader(&context, "Location", new_url, 0x1000);
url = new_url;
httpcCloseContext(&context);
}
} while ((status_code >= 301 && status_code <= 303) || (status_code >= 307 && status_code <= 308));
if (status_code != 200) struct archive * a = archive_read_new();
{ archive_read_support_format_zip(a);
httpcCloseContext(&context);
if (new_url != NULL) free(new_url);
return ret;
}
ret = httpcGetDownloadSizeState(&context, NULL, &content_size); int r = archive_read_open_memory(a, zip_buf, zip_size);
if (ret != 0) archive_read_free(a);
{
httpcCloseContext(&context);
if (new_url != NULL) free(new_url);
return ret;
}
buf = malloc(0x1000); if(r == ARCHIVE_OK)
if (buf == NULL)
{
httpcCloseContext(&context);
free(new_url);
return -2;
}
char *content_disposition = malloc(1024);
ret = httpcGetResponseHeader(&context, "Content-Disposition", content_disposition, 1024);
if (ret != 0)
{
free(content_disposition);
free(new_url);
free(buf);
}
char *filename;
filename = strtok(content_disposition, "\"");
filename = strtok(NULL, "\"");
char *illegal_characters = "\"?;:/\\+";
for (ssize_t i = 0; i < strlen(filename); i++)
{
for (ssize_t n = 0; n < strlen(illegal_characters); n++)
{
if (filename[i] == illegal_characters[n])
{ {
filename[i] = '-'; RemoteMode mode = REMOTE_MODE_AMOUNT;
char * buf = NULL;
do {
if(zip_memory_to_buf("body_LZ.bin", zip_buf, zip_size, &buf) != 0)
{
mode = REMOTE_MODE_THEMES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splash.bin", zip_buf, zip_size, &buf) != 0)
{
mode = REMOTE_MODE_SPLASHES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splashbottom.bin", zip_buf, zip_size, &buf) != 0)
{
mode = REMOTE_MODE_SPLASHES;
break;
}
}
while(false);
free(buf);
buf = NULL;
if(mode != REMOTE_MODE_AMOUNT)
{
save_zip_to_sd(filename, zip_size, zip_buf, mode);
success = true;
}
else
{
bool badge = draw_confirm_no_interface(language.camera.badge_question);
if (badge)
{
save_zip_to_sd(filename, zip_size, zip_buf, REMOTE_MODE_BADGES);
// don't set success since we don't need to reload lists for badge zips
} else
{
throw_error(language.camera.zip_not_theme_splash, ERROR_LEVEL_WARNING);
}
}
} }
} else
}
do {
ret = httpcDownloadData(&context, buf + size, 0x1000, &read_size);
size += read_size;
if (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING)
{
last_buf = buf;
buf = realloc(buf, size + 0x1000);
if (buf == NULL)
{ {
httpcCloseContext(&context); throw_error(language.camera.file_not_zip, ERROR_LEVEL_WARNING);
free(last_buf);
return ret;
} }
free(zip_buf);
}
else
{
throw_error(language.camera.download_failed, ERROR_LEVEL_WARNING);
} }
} while (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING);
last_buf = buf; free(filename);
buf = realloc(buf, size);
if (buf == NULL)
{
httpcCloseContext(&context);
free(last_buf);
return -1;
} }
char path_to_file[0x106] = {0}; free(scan_data);
strcpy(path_to_file, path); quirc_destroy(data.context);
strcat(path_to_file, filename); return success;
remake_file(path_to_file, ArchiveSD, size); }
buf_to_file(size, path_to_file, ArchiveSD, (char*)buf);
add_theme(&themes_list, &theme_count, path_to_file, filename);
exit_qr();
return 0;
}

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);
}

318
source/conversion.c Normal file
View File

@@ -0,0 +1,318 @@
#include "conversion.h"
#include "draw.h"
#include <png.h>
// don't be fooled - this function always expects 64x64 input buffers. Width/height only
// control output resolution
int rgb565ToPngFile(char *filename, u16 *rgb_buf, u8 *alpha_buf, int width, int height)
{
FILE *fp = fopen(filename, "wb");
u8 *image = malloc(64 * 64 * 4);
if (!image) return -1;
int i, x, y, r, g, b, a;
for (i = 0; i < 64 * 64; ++i)
{
r = (rgb_buf[i] & 0xF800) >> 11;
g = (rgb_buf[i] & 0x07E0) >> 5;
b = (rgb_buf[i] & 0x001F);
a = (alpha_buf[i/2] >> (4*(i%2))) & 0x0F;
r = round(r * 255.0 / 31.0);
g = round(g * 255.0 / 63.0);
b = round(b * 255.0 / 31.0);
a = a * 0x11;
x = 8*((i/64)%8) + (((i%64)&0x01) >> 0) + (((i%64)&0x04) >> 1) + (((i%64)&0x10) >> 2);
y = 8*(i/512) + (((i%64)&0x02) >> 1) + (((i%64)&0x08) >> 2) + (((i%64)&0x20) >> 3);
image[y * 64 * 4 + x * 4 + 0] = r;
image[y * 64 * 4 + x * 4 + 1] = g;
image[y * 64 * 4 + x * 4 + 2] = b;
image[y * 64 * 4 + x * 4 + 3] = a;
}
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
setjmp(png_jmpbuf(png));
png_init_io(png, fp);
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png, info);
png_bytep row = malloc(4 * width * sizeof(png_byte));
for (y = 0; y < height; ++y)
{
for (x = 0; x < width; ++x)
{
row[x * 4 + 0] = image[(y * 64 + x) * 4 + 0];
row[x * 4 + 1] = image[(y * 64 + x) * 4 + 1];
row[x * 4 + 2] = image[(y * 64 + x) * 4 + 2];
row[x * 4 + 3] = image[(y * 64 + x) * 4 + 3];
}
png_write_row(png, row);
}
png_write_end(png, info);
png_free_data(png, info, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png, NULL);
free(row);
free(image);
fflush(fp);
fclose(fp);
return 0;
}
int pngToRGB565(char *png_buf, u64 fileSize, u16 *rgb_buf_64x64, u8 *alpha_buf_64x64, u16 *rgb_buf_32x32, u8 *alpha_buf_32x32, bool set_icon)
{
if (png_buf == NULL) return 0;
if (fileSize < 8 || png_sig_cmp((png_bytep) png_buf, 0, 8))
{
return 0;
}
u32 *buf = (u32 *) png_buf;
FILE *fp = fmemopen(buf, fileSize, "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);
u32 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);
if (set_icon && (width != 48 || height != 48))
{
DEBUG("Invalid set icon?\n");
return 0;
}
if (!set_icon && (width < 64 || height < 64 || width % 64 != 0 || height % 64 != 0 || width > 12 * 64 || height > 6 * 64))
{
DEBUG("Invalid png found...\n");
return 0;
}
if (bit_depth == 16)
png_set_strip_16(png);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
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);
if (color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_read_update_info(png, info);
u32 x, y, r, g, b, a;
memset(alpha_buf_64x64, 0, 12*6*64*64/2);
memset(alpha_buf_32x32, 0, 12*6*32*32/2);
row_pointers = malloc(sizeof(png_bytep) * height);
for (y = 0; y < height; y++)
{
row_pointers[y] = (png_byte *) malloc(png_get_rowbytes(png, info));
}
png_read_image(png, row_pointers);
png_destroy_read_struct(&png, &info, NULL);
if (fp) fclose(fp);
for (y = 0; y < height; ++y)
{
png_bytep row = row_pointers[y];
for (x = 0; x < width; ++x)
{
png_bytep px = &(row[x * 4]);
r = px[0] >> 3;
g = px[1] >> 2;
b = px[2] >> 3;
a = px[3] >> 4;
// rgb565 conversion code adapted from GYTB
int rgb565_index = 8*64*((y/8)%8) | 64*((x/8)%8) | 32*((y/4)%2) | 16*((x/4)%2) | 8*((y/2)%2) | 4*((x/2)%2) | 2*(y%2) | (x%2);
rgb565_index |= 64*64*(height/64)*(x/64) + 64*64*(y/64); // account for multiple badges from 1 image
rgb_buf_64x64[rgb565_index] = (r << 11) | (g << 5) | b;
alpha_buf_64x64[rgb565_index / 2] |= a << (4 * (x % 2));
}
}
for (y = 0; y < height; y += 2)
{
png_bytep row = row_pointers[y];
png_bytep nextrow = row_pointers[y+1];
for (x = 0; x < width; x += 2)
{
png_bytep px1 = &(row[x * 4]);
png_bytep px2 = &(nextrow[x * 4]);
png_bytep px3 = &(row[(x + 1) * 4]);
png_bytep px4 = &(nextrow[(x + 1) * 4]);
r = (px1[0] + px2[0] + px3[0] + px4[0]) >> 5;
g = (px1[1] + px2[1] + px3[1] + px4[1]) >> 4;
b = (px1[2] + px2[2] + px3[2] + px4[2]) >> 5;
a = (px1[3] + px2[3] + px3[3] + px4[3]) >> 6;
int x2 = x/2;
int y2 = y/2;
int rgb565_index = 4*64*((y2/8)%4) | 64*((x2/8)%4) | 32*((y2/4)%2) | 16*((x2/4)%2) | 8*((y2/2)%2) | 4*((x2/2)%2) | 2*(y2%2) | (x2%2);
rgb565_index |= 32*32*(height/64)*(x/64) + 32*32*(y/64);
rgb_buf_32x32[rgb565_index] = (r << 11) | (g << 5) | b;
alpha_buf_32x32[rgb565_index / 2] |= a << (4 * (x2%2));
}
}
for (y = 0; y < height; y++)
{
free(row_pointers[y]);
}
free(row_pointers);
if (!set_icon)
return (height/64)*(width/64);
else
return (height/48)*(width/48);
}
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-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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,23 +27,54 @@
#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 "remote.h"
#include "minizip/unzip.h" #include <archive.h>
#include <archive_entry.h>
int filename_compare(__attribute__((unused)) unzFile file, const char *current_filename, const char *filename) FS_Archive ArchiveSD;
FS_Archive ArchiveHomeExt;
FS_Archive ArchiveThemeExt;
FS_Archive ArchiveBadgeExt;
Result createExtSaveData(u32 extdataID)
{ {
return strcasecmp(current_filename, filename); u8 null_smdh[0x36C0] = {0};
Handle *handle = fsGetSessionHandle();
u32 *cmdbuf = getThreadCommandBuffer();
u32 directory_limit = 1000;
u32 file_limit = 1000;
cmdbuf[0] = 0x08300182;
cmdbuf[1] = MEDIATYPE_SD;
cmdbuf[2] = extdataID;
cmdbuf[3] = 0;
cmdbuf[4] = 0x36C0;
cmdbuf[5] = directory_limit;
cmdbuf[6] = file_limit;
cmdbuf[7] = (0x36C0 << 4) | 0xA;
cmdbuf[8] = (u32)&null_smdh;
Result ret = 0;
if ((ret = svcSendSyncRequest(*handle)))
return ret;
return cmdbuf[1];
} }
Result open_archives(void) Result open_archives(void)
{ {
romfsInit();
u8 regionCode; u8 regionCode;
u32 archive1; u32 archive1;
u32 archive2; u32 archive2;
Result retValue; Result res = 0;
FS_Path home; FS_Path home;
FS_Path theme; FS_Path theme;
@@ -63,114 +94,567 @@ 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;
} }
retValue = FSUSER_OpenArchive(&ArchiveSD, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")); if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveSD, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) return res;
if(R_FAILED(retValue)) return retValue;
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Themes"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Splashes"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Badges"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Badges/ThemePlaza Badges"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE "/cache"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE "/BadgeBackups"), FS_ATTRIBUTE_DIRECTORY);
u32 homeMenuPath[3] = {MEDIATYPE_SD, archive2, 0}; u32 homeMenuPath[3] = {MEDIATYPE_SD, archive2, 0};
home.type = PATH_BINARY; home.type = PATH_BINARY;
home.size = 0xC; home.size = 0xC;
home.data = homeMenuPath; home.data = homeMenuPath;
retValue = FSUSER_OpenArchive(&ArchiveHomeExt, ARCHIVE_EXTDATA, home); if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveHomeExt, ARCHIVE_EXTDATA, home))) return res;
if(R_FAILED(retValue)) return retValue;
u32 themePath[3] = {MEDIATYPE_SD, archive1, 0}; u32 themePath[3] = {MEDIATYPE_SD, archive1, 0};
theme.type = PATH_BINARY; theme.type = PATH_BINARY;
theme.size = 0xC; theme.size = 0xC;
theme.data = themePath; theme.data = themePath;
retValue = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme); if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme))) return res;
if(R_FAILED(retValue)) return retValue;
Handle test_handle;
romfsInit(); if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ, 0))) return res;
FSFILE_Close(test_handle);
return 0;
}
Result open_badge_extdata()
{
Handle test_handle;
FS_Path badge;
Result res = 0;
u32 badgePath[3] = {MEDIATYPE_SD, 0x000014d1, 0};
badge.type = PATH_BINARY;
badge.size = 0xC;
badge.data = badgePath;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveBadgeExt, ARCHIVE_EXTDATA, badge)))
{
if (R_SUMMARY(res) == RS_NOTFOUND)
{
DEBUG("Extdata not found - creating\n");
createExtSaveData(0x000014d1);
FSUSER_OpenArchive(&ArchiveBadgeExt, ARCHIVE_EXTDATA, badge);
} else
{
DEBUG("Unknown extdata error\n");
return res;
}
}
if (R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveBadgeExt, fsMakePath(PATH_ASCII, "/BadgeData.dat"), FS_OPEN_READ, 0)))
{
if (R_SUMMARY(res) == RS_NOTFOUND)
{
FSUSER_CreateFile(ArchiveBadgeExt, fsMakePath(PATH_ASCII, "/BadgeData.dat"), 0, BADGE_DATA_SIZE);
FSUSER_OpenFile(&test_handle, ArchiveBadgeExt, fsMakePath(PATH_ASCII, "/BadgeData.dat"), FS_OPEN_WRITE, 0);
FSFILE_Flush(test_handle);
}
DEBUG("Error 0x%08ld opening BadgeData.dat, retrying\n", res);
}
FSFILE_Close(test_handle);
if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveBadgeExt, fsMakePath(PATH_ASCII, "/BadgeMngFile.dat"), FS_OPEN_READ, 0)))
{
DEBUG("Error 0x%08ld opening BadgeMngFile.dat, retrying\n", res);
if (R_SUMMARY(res) == RS_NOTFOUND)
{
remake_file(fsMakePath(PATH_ASCII, "/BadgeMngFile.dat"), ArchiveBadgeExt, BADGE_MNG_SIZE);
}
}
FSFILE_Close(test_handle);
if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveSD, fsMakePath(PATH_ASCII, "/Badges/ThemePlaza Badges/_seticon.png"), FS_OPEN_READ, 0)))
{
FILE *fp = fopen("romfs:/tp_set.png", "rb");
fseek(fp, 0L, SEEK_END);
ssize_t size = ftell(fp);
char *icon_buf = malloc(size);
fseek(fp, 0L, SEEK_SET);
fread(icon_buf, 1, size, fp);
fclose(fp);
remake_file(fsMakePath(PATH_ASCII, "/Badges/ThemePlaza Badges/_seticon.png"), ArchiveSD, size);
buf_to_file(size, fsMakePath(PATH_ASCII, "/Badges/ThemePlaza Badges/_seticon.png"), ArchiveSD, icon_buf);
DEBUG("res: 0x%08lx\n", res);
free(icon_buf);
}
return 0; return 0;
} }
Result close_archives(void) Result close_archives(void)
{ {
Result retValue; Result res;
if(R_FAILED(res = FSUSER_CloseArchive(ArchiveSD))) return res;
if(R_FAILED(res = FSUSER_CloseArchive(ArchiveHomeExt))) return res;
if(R_FAILED(res = FSUSER_CloseArchive(ArchiveThemeExt))) return res;
if(R_FAILED(res = FSUSER_CloseArchive(ArchiveBadgeExt))) return res;
retValue = FSUSER_CloseArchive(ArchiveSD);
if(R_FAILED(retValue)) return retValue;
retValue = FSUSER_CloseArchive(ArchiveHomeExt);
if(R_FAILED(retValue)) return retValue;
retValue = FSUSER_CloseArchive(ArchiveThemeExt);
if(R_FAILED(retValue)) return retValue;
return 0; return 0;
} }
u64 file_to_buf(FS_Path path, FS_Archive archive, char** buf) 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)
{ {
Handle file; Handle file;
Result res = FSUSER_OpenFile(&file, archive, path, FS_OPEN_READ, 0); Result res = 0;
if (R_FAILED(res)) return 0; if (R_FAILED(res = FSUSER_OpenFile(&file, archive, path, FS_OPEN_READ, 0)))
{
DEBUG("file_to_buf failed - 0x%08lx\n", res);
return 0;
}
u64 size; u64 size;
FSFILE_GetSize(file, &size); FSFILE_GetSize(file, &size);
*buf = malloc(size); if(size != 0)
FSFILE_Read(file, NULL, 0, *buf, size); {
*buf = calloc(1, size);
if (*buf == NULL)
{
DEBUG("Error allocating buffer - out of memory??\n");
return 0;
}
FSFILE_Read(file, NULL, 0, *buf, size);
}
FSFILE_Close(file); FSFILE_Close(file);
return size; return (u32)size;
} }
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf) s16 for_each_file_zip(u16 *zip_path, u32 (*zip_iter_callback)(char *filebuf, u64 file_size, const char *name, void *userdata), void *userdata)
{ {
struct archive *a = archive_read_new();
archive_read_support_format_zip(a);
ssize_t len = strulen(zip_path, 0x106); ssize_t len = strulen(zip_path, 0x106);
char * path = calloc(len, sizeof(u16));
utf16_to_utf8((u8 *)path, zip_path, len * sizeof(u16));
DEBUG("Attempting to open zip %s\n", path);
u8 *path = calloc(sizeof(u8), len * 4); int r = archive_read_open_filename(a, path, 0x4000);
utf16_to_utf8(path, zip_path, len * 4); free(path);
if(r != ARCHIVE_OK)
unzFile zip_handle = unzOpen((char*)path);
if (zip_handle == NULL) return 0;
u32 file_size = 0;
int status = unzLocateFile(zip_handle, file_name, filename_compare);
if (status == UNZ_OK)
{ {
unz_file_info *file_info = malloc(sizeof(unz_file_info)); DEBUG("Invalid zip being opened\n");
unzGetCurrentFileInfo(zip_handle, file_info, NULL, 0, NULL, 0, NULL, 0); return -1;
file_size = file_info->uncompressed_size; }
*buf = malloc(file_size);
unzOpenCurrentFile(zip_handle);
unzReadCurrentFile(zip_handle, *buf, file_size);
unzCloseCurrentFile(zip_handle);
unzClose(zip_handle);
free(path); struct archive_entry *entry;
free(file_info); u64 file_size = 0;
return file_size; while (archive_read_next_header(a, &entry) == ARCHIVE_OK)
} else { {
free(path); file_size = archive_entry_size(entry);
puts("fileziprip"); char *buf = calloc(file_size, sizeof(char));
archive_read_data(a, buf, file_size);
zip_iter_callback(buf, file_size, archive_entry_pathname(entry), userdata);
free(buf);
}
archive_read_free(a);
return 0;
}
static u32 zip_to_buf(struct archive * a, const char * file_name, char ** buf)
{
struct archive_entry * entry;
bool found = false;
u64 file_size = 0;
while(!found && archive_read_next_header(a, &entry) == ARCHIVE_OK)
{
found = !strcasecmp(archive_entry_pathname(entry), file_name);
}
if(found)
{
file_size = archive_entry_size(entry);
*buf = calloc(file_size, sizeof(char));
archive_read_data(a, *buf, file_size);
}
else
{
DEBUG("Couldn't find file in zip\n");
}
archive_read_free(a);
return (u32)file_size;
}
u32 zip_memory_to_buf(const char * file_name, void * zip_memory, size_t zip_size, char ** buf)
{
struct archive * a = archive_read_new();
archive_read_support_format_zip(a);
int r = archive_read_open_memory(a, zip_memory, zip_size);
if(r != ARCHIVE_OK)
{
DEBUG("Invalid zip being opened from memory\n");
return 0; return 0;
} }
return zip_to_buf(a, file_name, buf);
} }
u32 buf_to_file(u32 size, char *path, FS_Archive archive, char *buf) u32 zip_file_to_buf(const char * file_name, const u16 * zip_path, char ** buf)
{ {
Handle handle; ssize_t len = strulen(zip_path, 0x106);
u32 bytes = 0; char * path = calloc(len, sizeof(u16));
Result res = FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_ASCII, path), FS_OPEN_WRITE, 0); utf16_to_utf8((u8 *)path, zip_path, len * sizeof(u16));
if (R_FAILED(res)) return res;
res = FSFILE_Write(handle, &bytes, 0, buf, size, FS_WRITE_FLUSH); struct archive * a = archive_read_new();
if (R_FAILED(res)) return res; archive_read_support_format_zip(a);
res = FSFILE_Close(handle);
if (R_FAILED(res)) return res; int r = archive_read_open_filename(a, path, 0x4000);
return bytes; free(path);
if(r != ARCHIVE_OK)
{
DEBUG("Invalid zip being opened\n");
return 0;
}
return zip_to_buf(a, file_name, buf);
} }
void remake_file(char *path, FS_Archive archive, u32 size) Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char * buf)
{ {
Handle handle; Handle handle;
if (R_SUCCEEDED(FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0))) Result res = 0;
if (R_FAILED(res = FSUSER_OpenFile(&handle, archive, path, FS_OPEN_WRITE, 0))) return res;
if (R_FAILED(res = FSFILE_Write(handle, NULL, 0, buf, size, FS_WRITE_FLUSH))) return res;
if (R_FAILED(res = FSFILE_Close(handle))) return res;
return 0;
}
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)
{
Handle handle;
if (R_SUCCEEDED(FSUSER_OpenFile(&handle, archive, path, FS_OPEN_READ, 0)))
{ {
FSFILE_Close(handle); FSFILE_Close(handle);
FSUSER_DeleteFile(archive, fsMakePath(PATH_ASCII, path)); FSUSER_DeleteFile(archive, path);
} }
FSUSER_CreateFile(archive, fsMakePath(PATH_ASCII, path), 0, size); Result res = FSUSER_CreateFile(archive, path, 0, size);
} DEBUG("Remake file res: 0x%08lx\n", res);
char * buf = calloc(size, 1);
if (buf == NULL)
{
DEBUG("out of memory - not overwriting file?\n");
} else {
buf_to_file(size, path, archive, buf);
free(buf);
}
}
Result zero_handle_memeasy(Handle handle)
{
u64 size = 0;
u64 cur = 0;
FSFILE_GetSize(handle, &size);
char *zero_buf = calloc(1, 0x10000);
while (size > 0x10000)
{
FSFILE_Write(handle, NULL, cur, &zero_buf, 0x10000, 0);
cur += 0x10000;
size -= 0x10000;
}
FSFILE_Write(handle, NULL, cur, &zero_buf, size, 0);
free(zero_buf);
return 0;
}
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, RemoteMode 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:
char * curr_filename;
if (mode == REMOTE_MODE_BADGES)
{
DEBUG("Remote mode badges! Saving to /Badges/ThemePlaza Badges/\n");
sprintf(path_to_file, "%s%s", "/Badges/ThemePlaza Badges/", filename);
curr_filename = path_to_file + strlen("/Badges/ThemePlaza Badges/");
} else
{
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
curr_filename = path_to_file + strlen(main_paths[mode]);
}
DEBUG("Filtering out illegal chars...\n");
// filter out characters illegal in FAT32 filenames
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 = '-';
}
DEBUG("Checking extension\n");
// 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);
}

513
source/loading.c Normal file
View File

@@ -0,0 +1,513 @@
/*
* 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 "loading.h"
#include "fs.h"
#include "unicode.h"
#include "music.h"
#include "draw.h"
#include "conversion.h"
#include "ui_strings.h"
#include <png.h>
void copy_texture_data(C3D_Tex * texture, const u16 * src, const Entry_Icon_s * current_icon)
{
// pointer to rgb565, offset by the number of rows and columns specified by current_icon
// (reminder that this is z order curve storage)
u16 * dest = ((u16 *)texture->data) + (current_icon->y * texture->width) + (current_icon->x * 8);
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);
}
void parse_smdh(Icon_s * icon, Entry_s * entry, const u16 * fallback_name)
{
if(icon == NULL)
{
memcpy(entry->name, fallback_name, 0x80);
utf8_to_utf16(entry->desc, (u8 *)"No description", 0x100);
utf8_to_utf16(entry->author, (u8 *)"Unknown author", 0x80);
entry->placeholder_color = C2D_Color32(rand() % 255, rand() % 255, rand() % 255, 255);
return;
}
memcpy(entry->name, icon->name, 0x40 * sizeof(u16));
memcpy(entry->desc, icon->desc, 0x80 * sizeof(u16));
memcpy(entry->author, icon->author, 0x40 * sizeof(u16));
entry->placeholder_color = 0;
}
static Icon_s * load_entry_icon(const Entry_s * entry)
{
char * info_buffer = NULL;
u32 size = load_data("/info.smdh", entry, &info_buffer);
if(size != sizeof(Icon_s))
{
free(info_buffer);
return NULL;
}
return (Icon_s *)info_buffer;
}
void load_icons_first(Entry_List_s * list, bool silent)
{
if(list == NULL || list->entries == NULL) return;
if(!silent)
draw_install(INSTALL_LOADING_ICONS);
int starti = 0, endi = 0;
if(list->entries_count <= list->entries_loaded * ICONS_OFFSET_AMOUNT)
{
DEBUG("small load\n");
// if the list is one that doesnt need swapping, load everything at once
endi = list->entries_count;
}
else
{
DEBUG("extended load\n");
// otherwise, load around to prepare for swapping
starti = list->scroll - list->entries_loaded * ICONS_VISIBLE;
endi = starti + list->entries_loaded * ICONS_OFFSET_AMOUNT;
}
for(int entry_i = starti, icon_i = 0; entry_i < endi; ++entry_i, ++icon_i)
{
if(!silent)
draw_loading_bar(icon_i, endi-starti, INSTALL_LOADING_ICONS);
int offset = entry_i;
if(offset < 0)
offset += list->entries_count;
if(offset >= list->entries_count)
offset -= list->entries_count;
Entry_s * const current_entry = &list->entries[offset];
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(list->loading_path));
copy_texture_data(&list->icons_texture, smdh->big_icon, &list->icons_info[icon_i]);
free(smdh);
}
}
}
static void reverse(Entry_Icon_s a[], int sz) {
int i, j;
Entry_Icon_s tmp;
for (i = 0, j = sz; i < j; i++, j--) {
memcpy(&tmp, &a[i], sizeof(Entry_Icon_s));
memcpy(&a[i], &a[j], sizeof(Entry_Icon_s));
memcpy(&a[j], &tmp, sizeof(Entry_Icon_s));
}
}
static void rotate(Entry_Icon_s array[], int size, int amt) {
if (amt < 0)
amt = size + amt;
reverse(array, size-amt-1);
reverse(array+size-amt, amt-1);
reverse(array, size-1);
}
void handle_scrolling(Entry_List_s * list)
{
// Scroll the menu up or down if the selected theme is out of its bounds
//----------------------------------------------------------------
if(list->entries_count > list->entries_loaded)
{
int change = 0;
if(list->entries_count > list->entries_loaded * 2 && list->previous_scroll < list->entries_loaded && list->selected_entry >= list->entries_count - list->entries_loaded)
{
list->scroll = list->entries_count - list->entries_loaded;
}
else if(list->entries_count > list->entries_loaded * 2 && list->selected_entry < list->entries_loaded && list->previous_selected >= list->entries_count - list->entries_loaded)
{
list->scroll = 0;
}
else if(list->selected_entry == list->previous_selected+1 && list->selected_entry == list->scroll+list->entries_loaded)
{
change = 1;
}
else if(list->selected_entry == list->previous_selected-1 && list->selected_entry == list->scroll-1)
{
change = -1;
}
else if(list->selected_entry == list->previous_selected+list->entries_loaded)
{
change = list->entries_loaded;
}
else if(list->selected_entry == list->previous_selected-list->entries_loaded)
{
change = -list->entries_loaded;
}
else if(list->selected_entry >= list->scroll + list->entries_loaded || list->selected_entry < list->scroll)
{
change = list->selected_entry - list->scroll;
}
list->scroll += change;
if(list->scroll < 0)
list->scroll = 0;
else if(list->scroll > list->entries_count - list->entries_loaded)
list->scroll = list->entries_count - list->entries_loaded;
if(!change)
list->previous_selected = list->selected_entry;
else
list->previous_selected += change;
}
//----------------------------------------------------------------
}
static bool load_icons(Entry_List_s * current_list, Handle mutex)
{
if(current_list == NULL || current_list->entries == NULL)
return false;
handle_scrolling(current_list);
if(current_list->entries_count <= current_list->entries_loaded * ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
return 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))
int delta = current_list->scroll - current_list->previous_scroll;
if(abs(delta) >= current_list->entries_count - current_list->entries_loaded * (ICONS_OFFSET_AMOUNT-1))
delta = -SIGN(delta) * (current_list->entries_count - abs(delta));
int starti = current_list->scroll;
int endi = starti + abs(delta);
if(delta < 0)
{
endi -= abs(delta) + 1;
starti += abs(delta) - 1;
}
int ctr = 0;
Entry_s ** const entries = calloc(abs(delta), sizeof(Entry_s *));
int * const indexes = calloc(abs(delta), sizeof(int));
bool released = false;
Entry_Icon_s * const icons = current_list->icons_info;
for(int i = starti; i != endi; i++, ctr++)
{
int index = 0;
int offset = i;
rotate(icons, ICONS_OFFSET_AMOUNT * current_list->entries_loaded, -SIGN(delta));
if(delta > 0)
{
index = current_list->entries_loaded * ICONS_OFFSET_AMOUNT - delta + i - starti;
offset += current_list->entries_loaded * ICONS_UNDER - delta;
}
else
{
index = 0 - delta - 1 + i - starti;
offset -= current_list->entries_loaded * ICONS_VISIBLE;
i -= 2; //i-- twice to counter the i++, needed only for this case
}
if(offset < 0)
offset += current_list->entries_count;
if(offset >= current_list->entries_count)
offset -= current_list->entries_count;
entries[ctr] = &current_list->entries[offset];
indexes[ctr] = index;
}
#undef SIGN
if(abs(delta) <= current_list->entries_loaded)
{
svcReleaseMutex(mutex);
released = true;
}
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(indexes);
current_list->previous_scroll = current_list->scroll;
return released;
}
void load_icons_thread(void * void_arg)
{
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
Handle mutex = *(Handle *)arg->thread_arg[1];
do {
svcWaitSynchronization(mutex, U64_MAX);
Entry_List_s * const current_list = *(Entry_List_s ** volatile)arg->thread_arg[0];
const bool released = load_icons(current_list, mutex);
if(!released)
svcReleaseMutex(mutex);
} 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);
float tex_height = height > 512.0f ? 1024.0f : 512.0f;
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/tex_height);
preview_image->subtex = subt3x;
C3D_TexInit(preview_image->tex, 512, tex_height, 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};
bool load_preview(const Entry_List_s * list, C2D_Image * preview_image, int * preview_offset)
{
if(list->entries == NULL) return false;
const Entry_s * entry = &list->entries[list->selected_entry];
if(!memcmp(&previous_path_preview, &entry->path, 0x106 * sizeof(u16))) return true;
char * preview_buffer = NULL;
u32 size = load_data("/preview.png", entry, &preview_buffer);
u32 height = 480;
if(size)
{
if (!(size = png_to_abgr(&preview_buffer, size, &height)))
{
return false;
}
}
else
{
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);
}
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);
}
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;
}
void free_preview(C2D_Image preview)
{
if(preview.tex)
C3D_TexDelete(preview.tex);
free(preview.tex);
free((Tex3DS_SubTexture *)preview.subtex);
}
// Initialize the audio struct
Result load_audio(const Entry_s * entry, audio_s * audio)
{
audio->music_size = load_data("/bgm.bcstm", entry, &audio->music_buf);
if (audio->music_size == 0) {
free(audio);
DEBUG("<load_audio> File not found!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
}
audio->cursor = 0;
audio->last_time = 0;
audio->current_block = 0;
audio->stop = false;
audio->active_channels = 0;
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
}
Result load_audio_ogg(const Entry_s * entry, audio_ogg_s * audio)
{
audio->filesize = load_data("/bgm.ogg", entry, &audio->filebuf);
if (audio->filesize == 0) {
free(audio);
DEBUG("<load_audio> File not found!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
}
audio->mix[0] = audio->mix[1] = 1.0f; // Determines volume for the 12 (?) different outputs. See http://smealum.github.io/ctrulib/channel_8h.html#a30eb26f1972cc3ec28370263796c0444
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetMix(0, audio->mix); // See mix comment above
FILE * file = fmemopen(audio->filebuf, audio->filesize, "rb");
DEBUG("<load_audio> Filesize: %ld\n", audio->filesize);
if(file != NULL)
{
int e = ov_open(file, &audio->vf, NULL, 0);
if (e < 0)
{
DEBUG("<load_audio> Vorbis: %d\n", e);
free(audio->filebuf);
free(audio);
fclose(file);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
}
vorbis_info * vi = ov_info(&audio->vf, -1);
ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file
if (vi->channels == 2) {
DEBUG("<load_audio> Using stereo\n");
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); // 2 channels == Stereo
} else {
DEBUG("<load_audio> Invalid number of channels\n");
free(audio->filebuf);
free(audio);
fclose(file);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
}
audio->wave_buf[0].nsamples = audio->wave_buf[1].nsamples = vi->rate / 4; // 4 bytes per sample, samples = rate (bytes) / 4
audio->wave_buf[0].status = audio->wave_buf[1].status = NDSP_WBUF_DONE; // Used in play to stop from writing to current buffer
audio->wave_buf[0].data_vaddr = linearAlloc(BUF_TO_READ); // Most vorbis packets should only be 4 KB at most (?) Possibly dangerous assumption
audio->wave_buf[1].data_vaddr = linearAlloc(BUF_TO_READ);
DEBUG("<load_audio> Success!\n");
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
} else {
free(audio->filebuf);
free(audio);
DEBUG("<load_audio> fmemopen failed!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,137 +0,0 @@
/* crypt.h -- base code for traditional PKWARE encryption
Version 1.01e, February 12th, 2005
Copyright (C) 1998-2005 Gilles Vollant
Modifications for Info-ZIP crypting
Copyright (C) 2003 Terry Thorsen
This code is a modified version of crypting code in Info-ZIP distribution
Copyright (C) 1990-2000 Info-ZIP. All rights reserved.
See the Info-ZIP LICENSE file version 2000-Apr-09 or later for terms of use
which also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
The encryption/decryption parts of this source code (as opposed to the
non-echoing password parts) were originally written in Europe. The
whole source package can be freely distributed, including from the USA.
(Prior to January 2000, re-export from the US was a violation of US law.)
This encryption code is a direct transcription of the algorithm from
Roger Schlafly, described by Phil Katz in the file appnote.txt. This
file (appnote.txt) is distributed with the PKZIP program (even in the
version without encryption capabilities).
If you don't need crypting in your application, just define symbols
NOCRYPT and NOUNCRYPT.
Mar 8th, 2016 - Lucio Cosmo
Fixed support for 64bit builds for archives with "PKWARE" password.
Changed long, unsigned long, unsigned to unsigned int in
access functions to crctables and pkeys
*/
#define CRC32(c, b) ((*(pcrc_32_tab+(((unsigned int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
/***********************************************************************
* Return the next byte in the pseudo-random sequence
*/
static int decrypt_byte(unsigned int* pkeys)
{
unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
* unpredictable manner on 16-bit systems; not a problem
* with any known compiler so far, though */
temp = ((unsigned int)(*(pkeys+2)) & 0xffff) | 2;
return (unsigned int)(((temp * (temp ^ 1)) >> 8) & 0xff);
}
/***********************************************************************
* Update the encryption keys with the next byte of plain text
*/
static int update_keys(unsigned int* pkeys,const unsigned int* pcrc_32_tab,int c)
{
(*(pkeys+0)) = CRC32((*(pkeys+0)), c);
(*(pkeys+1)) += (*(pkeys+0)) & 0xff;
(*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
{
register int keyshift = (int)((*(pkeys+1)) >> 24);
(*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
}
return c;
}
/***********************************************************************
* Initialize the encryption keys and the random header according to
* the given password.
*/
static void init_keys(const char* passwd,unsigned int* pkeys,const unsigned int* pcrc_32_tab)
{
*(pkeys+0) = 305419896L;
*(pkeys+1) = 591751049L;
*(pkeys+2) = 878082192L;
while (*passwd != 0)
{
update_keys(pkeys,pcrc_32_tab,(int)*passwd);
passwd++;
}
}
#define zdecode(pkeys,pcrc_32_tab,c) \
(update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys)))
#define zencode(pkeys,pcrc_32_tab,c,t) \
(t=decrypt_byte(pkeys), update_keys(pkeys,pcrc_32_tab,c), t^(c))
#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
#define RAND_HEAD_LEN 12
/* "last resort" source for second part of crypt seed pattern */
# ifndef ZCR_SEED2
# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
# endif
static int crypthead(const char* passwd, /* password string */
unsigned char* buf, /* where to write header */
int bufSize,
unsigned int* pkeys,
const unsigned int* pcrc_32_tab,
unsigned int crcForCrypting)
{
int n; /* index in random header */
int t; /* temporary */
int c; /* random byte */
unsigned char header[RAND_HEAD_LEN-2]; /* random header */
static unsigned calls = 0; /* ensure different random header each time */
if (bufSize < RAND_HEAD_LEN)
return 0;
/* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
* output of rand() to get less predictability, since rand() is
* often poorly implemented.
*/
if (++calls == 1)
{
srand((unsigned)(time(NULL) ^ ZCR_SEED2));
}
init_keys(passwd, pkeys, pcrc_32_tab);
for (n = 0; n < RAND_HEAD_LEN-2; n++)
{
c = (rand() >> 7) & 0xff;
header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
}
/* Encrypt random header (last two bytes is high word of crc) */
init_keys(passwd, pkeys, pcrc_32_tab);
for (n = 0; n < RAND_HEAD_LEN-2; n++)
{
buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
}
buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
return n;
}
#endif

View File

@@ -1,380 +0,0 @@
/* ioapi.h -- IO base function header for compress/uncompress .zip
part of the MiniZip project
Copyright (C) 1998-2010 Gilles Vollant
http://www.winimage.com/zLibDll/minizip.html
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson
http://result42.com
This program is distributed under the terms of the same license as zlib.
See the accompanying LICENSE file for the full text of the license.
*/
//taken and adapted from https://stackoverflow.com/a/13492589
// save diagnostic state
#pragma GCC diagnostic push
// turn off the specific warning. Can also use "-Wall"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <stdlib.h>
#include <string.h>
#include "ioapi.h"
#if defined(_WIN32)
# define snprintf _snprintf
#endif
#ifdef __APPLE__
/* In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions */
# define FOPEN_FUNC(filename, mode) fopen(filename, mode)
# define FTELLO_FUNC(stream) ftello(stream)
# define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
#else
# define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
# define FTELLO_FUNC(stream) ftello64(stream)
# define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
#endif
/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
#ifndef SEEK_CUR
# define SEEK_CUR 1
#endif
#ifndef SEEK_END
# define SEEK_END 2
#endif
#ifndef SEEK_SET
# define SEEK_SET 0
#endif
voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)
{
if (pfilefunc->zfile_func64.zopen64_file != NULL)
return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode);
return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode);
}
voidpf call_zopendisk64 OF((const zlib_filefunc64_32_def* pfilefunc, voidpf filestream, int number_disk, int mode))
{
if (pfilefunc->zfile_func64.zopendisk64_file != NULL)
return (*(pfilefunc->zfile_func64.zopendisk64_file)) (pfilefunc->zfile_func64.opaque,filestream,number_disk,mode);
return (*(pfilefunc->zopendisk32_file))(pfilefunc->zfile_func64.opaque,filestream,number_disk,mode);
}
long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)
{
uLong offsetTruncated;
if (pfilefunc->zfile_func64.zseek64_file != NULL)
return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin);
offsetTruncated = (uLong)offset;
if (offsetTruncated != offset)
return -1;
return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin);
}
ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)
{
uLong tell_uLong;
if (pfilefunc->zfile_func64.zseek64_file != NULL)
return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream);
tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream);
if ((tell_uLong) == 0xffffffff)
return (ZPOS64_T)-1;
return tell_uLong;
}
void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32)
{
p_filefunc64_32->zfile_func64.zopen64_file = NULL;
p_filefunc64_32->zfile_func64.zopendisk64_file = NULL;
p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file;
p_filefunc64_32->zopendisk32_file = p_filefunc32->zopendisk_file;
p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file;
p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file;
p_filefunc64_32->zfile_func64.ztell64_file = NULL;
p_filefunc64_32->zfile_func64.zseek64_file = NULL;
p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file;
p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque;
p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file;
p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file;
}
static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode));
static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size));
static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size));
static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream));
static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream));
static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream));
typedef struct
{
FILE *file;
int filenameLength;
void *filename;
} FILE_IOPOSIX;
static voidpf file_build_ioposix(FILE *file, const char *filename)
{
FILE_IOPOSIX *ioposix = NULL;
if (file == NULL)
return NULL;
ioposix = (FILE_IOPOSIX*)malloc(sizeof(FILE_IOPOSIX));
ioposix->file = file;
ioposix->filenameLength = strlen(filename) + 1;
ioposix->filename = (char*)malloc(ioposix->filenameLength * sizeof(char));
strncpy(ioposix->filename, filename, ioposix->filenameLength);
return (voidpf)ioposix;
}
static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode)
{
FILE* file = NULL;
const char* mode_fopen = NULL;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
mode_fopen = "rb";
else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
mode_fopen = "r+b";
else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
mode_fopen = "wb";
if ((filename != NULL) && (mode_fopen != NULL))
{
file = fopen(filename, mode_fopen);
return file_build_ioposix(file, filename);
}
return file;
}
static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode)
{
FILE* file = NULL;
const char* mode_fopen = NULL;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
mode_fopen = "rb";
else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
mode_fopen = "r+b";
else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
mode_fopen = "wb";
if ((filename != NULL) && (mode_fopen != NULL))
{
file = FOPEN_FUNC((const char*)filename, mode_fopen);
return file_build_ioposix(file, (const char*)filename);
}
return file;
}
static voidpf ZCALLBACK fopendisk64_file_func (voidpf opaque, voidpf stream, int number_disk, int mode)
{
FILE_IOPOSIX *ioposix = NULL;
char *diskFilename = NULL;
voidpf ret = NULL;
int i = 0;
if (stream == NULL)
return NULL;
ioposix = (FILE_IOPOSIX*)stream;
diskFilename = (char*)malloc(ioposix->filenameLength * sizeof(char));
strncpy(diskFilename, ioposix->filename, ioposix->filenameLength);
for (i = ioposix->filenameLength - 1; i >= 0; i -= 1)
{
if (diskFilename[i] != '.')
continue;
snprintf(&diskFilename[i], ioposix->filenameLength - i, ".z%02d", number_disk + 1);
break;
}
if (i >= 0)
ret = fopen64_file_func(opaque, diskFilename, mode);
free(diskFilename);
return ret;
}
static voidpf ZCALLBACK fopendisk_file_func (voidpf opaque, voidpf stream, int number_disk, int mode)
{
FILE_IOPOSIX *ioposix = NULL;
char *diskFilename = NULL;
voidpf ret = NULL;
int i = 0;
if (stream == NULL)
return NULL;
ioposix = (FILE_IOPOSIX*)stream;
diskFilename = (char*)malloc(ioposix->filenameLength * sizeof(char));
strncpy(diskFilename, ioposix->filename, ioposix->filenameLength);
for (i = ioposix->filenameLength - 1; i >= 0; i -= 1)
{
if (diskFilename[i] != '.')
continue;
snprintf(&diskFilename[i], ioposix->filenameLength - i, ".z%02d", number_disk + 1);
break;
}
if (i >= 0)
ret = fopen_file_func(opaque, diskFilename, mode);
free(diskFilename);
return ret;
}
static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size)
{
FILE_IOPOSIX *ioposix = NULL;
uLong ret;
if (stream == NULL)
return -1;
ioposix = (FILE_IOPOSIX*)stream;
ret = (uLong)fread(buf, 1, (size_t)size, ioposix->file);
return ret;
}
static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size)
{
FILE_IOPOSIX *ioposix = NULL;
uLong ret;
if (stream == NULL)
return -1;
ioposix = (FILE_IOPOSIX*)stream;
ret = (uLong)fwrite(buf, 1, (size_t)size, ioposix->file);
return ret;
}
static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream)
{
FILE_IOPOSIX *ioposix = NULL;
long ret = -1;
if (stream == NULL)
return ret;
ioposix = (FILE_IOPOSIX*)stream;
ret = ftell(ioposix->file);
return ret;
}
static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream)
{
FILE_IOPOSIX *ioposix = NULL;
ZPOS64_T ret = -1;
if (stream == NULL)
return ret;
ioposix = (FILE_IOPOSIX*)stream;
ret = FTELLO_FUNC(ioposix->file);
return ret;
}
static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin)
{
FILE_IOPOSIX *ioposix = NULL;
int fseek_origin = 0;
long ret = 0;
if (stream == NULL)
return -1;
ioposix = (FILE_IOPOSIX*)stream;
switch (origin)
{
case ZLIB_FILEFUNC_SEEK_CUR:
fseek_origin = SEEK_CUR;
break;
case ZLIB_FILEFUNC_SEEK_END:
fseek_origin = SEEK_END;
break;
case ZLIB_FILEFUNC_SEEK_SET:
fseek_origin = SEEK_SET;
break;
default:
return -1;
}
if (fseek(ioposix->file, offset, fseek_origin) != 0)
ret = -1;
return ret;
}
static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)
{
FILE_IOPOSIX *ioposix = NULL;
int fseek_origin = 0;
long ret = 0;
if (stream == NULL)
return -1;
ioposix = (FILE_IOPOSIX*)stream;
switch (origin)
{
case ZLIB_FILEFUNC_SEEK_CUR:
fseek_origin = SEEK_CUR;
break;
case ZLIB_FILEFUNC_SEEK_END:
fseek_origin = SEEK_END;
break;
case ZLIB_FILEFUNC_SEEK_SET:
fseek_origin = SEEK_SET;
break;
default:
return -1;
}
if(FSEEKO_FUNC(ioposix->file, offset, fseek_origin) != 0)
ret = -1;
return ret;
}
static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream)
{
FILE_IOPOSIX *ioposix = NULL;
int ret = -1;
if (stream == NULL)
return ret;
ioposix = (FILE_IOPOSIX*)stream;
if (ioposix->filename != NULL)
free(ioposix->filename);
ret = fclose(ioposix->file);
free(ioposix);
return ret;
}
static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream)
{
FILE_IOPOSIX *ioposix = NULL;
int ret = -1;
if (stream == NULL)
return ret;
ioposix = (FILE_IOPOSIX*)stream;
ret = ferror(ioposix->file);
return ret;
}
void fill_fopen_filefunc (zlib_filefunc_def* pzlib_filefunc_def)
{
pzlib_filefunc_def->zopen_file = fopen_file_func;
pzlib_filefunc_def->zopendisk_file = fopendisk_file_func;
pzlib_filefunc_def->zread_file = fread_file_func;
pzlib_filefunc_def->zwrite_file = fwrite_file_func;
pzlib_filefunc_def->ztell_file = ftell_file_func;
pzlib_filefunc_def->zseek_file = fseek_file_func;
pzlib_filefunc_def->zclose_file = fclose_file_func;
pzlib_filefunc_def->zerror_file = ferror_file_func;
pzlib_filefunc_def->opaque = NULL;
}
void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def)
{
pzlib_filefunc_def->zopen64_file = fopen64_file_func;
pzlib_filefunc_def->zopendisk64_file = fopendisk64_file_func;
pzlib_filefunc_def->zread_file = fread_file_func;
pzlib_filefunc_def->zwrite_file = fwrite_file_func;
pzlib_filefunc_def->ztell64_file = ftell64_file_func;
pzlib_filefunc_def->zseek64_file = fseek64_file_func;
pzlib_filefunc_def->zclose_file = fclose_file_func;
pzlib_filefunc_def->zerror_file = ferror_file_func;
pzlib_filefunc_def->opaque = NULL;
}
// turn the warnings back on
#pragma GCC diagnostic pop

View File

@@ -1,158 +0,0 @@
/* ioapi.h -- IO base function header for compress/uncompress .zip
part of the MiniZip project
Copyright (C) 1998-2010 Gilles Vollant
http://www.winimage.com/zLibDll/minizip.html
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson
http://result42.com
This program is distributed under the terms of the same license as zlib.
See the accompanying LICENSE file for the full text of the license.
*/
#ifndef _ZLIBIOAPI64_H
#define _ZLIBIOAPI64_H
#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
# ifndef __USE_FILE_OFFSET64
# define __USE_FILE_OFFSET64
# endif
# ifndef __USE_LARGEFILE64
# define __USE_LARGEFILE64
# endif
# ifndef _LARGEFILE64_SOURCE
# define _LARGEFILE64_SOURCE
# endif
# ifndef _FILE_OFFSET_BIT
# define _FILE_OFFSET_BIT 64
# endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <zlib.h>
#define fopen64 fopen
#define ftello64 ftell
#define fseeko64 fseek
/* a type choosen by DEFINE */
#ifdef HAVE_64BIT_INT_CUSTOM
typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T;
#else
# ifdef HAVE_STDINT_H
# include "stdint.h"
typedef uint64_t ZPOS64_T;
# else
# if defined(_MSC_VER) || defined(__BORLANDC__)
typedef unsigned __int64 ZPOS64_T;
# else
typedef unsigned long long int ZPOS64_T;
# endif
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define ZLIB_FILEFUNC_SEEK_CUR (1)
#define ZLIB_FILEFUNC_SEEK_END (2)
#define ZLIB_FILEFUNC_SEEK_SET (0)
#define ZLIB_FILEFUNC_MODE_READ (1)
#define ZLIB_FILEFUNC_MODE_WRITE (2)
#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
#define ZLIB_FILEFUNC_MODE_EXISTING (4)
#define ZLIB_FILEFUNC_MODE_CREATE (8)
#ifndef ZCALLBACK
# if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || \
defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
# define ZCALLBACK CALLBACK
# else
# define ZCALLBACK
# endif
#endif
typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode));
typedef voidpf (ZCALLBACK *opendisk_file_func) OF((voidpf opaque, voidpf stream, int number_disk, int mode));
typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
/* here is the "old" 32 bits structure structure */
typedef struct zlib_filefunc_def_s
{
open_file_func zopen_file;
opendisk_file_func zopendisk_file;
read_file_func zread_file;
write_file_func zwrite_file;
tell_file_func ztell_file;
seek_file_func zseek_file;
close_file_func zclose_file;
testerror_file_func zerror_file;
voidpf opaque;
} zlib_filefunc_def;
typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode));
typedef voidpf (ZCALLBACK *opendisk64_file_func)OF((voidpf opaque, voidpf stream, int number_disk, int mode));
typedef struct zlib_filefunc64_def_s
{
open64_file_func zopen64_file;
opendisk64_file_func zopendisk64_file;
read_file_func zread_file;
write_file_func zwrite_file;
tell64_file_func ztell64_file;
seek64_file_func zseek64_file;
close_file_func zclose_file;
testerror_file_func zerror_file;
voidpf opaque;
} zlib_filefunc64_def;
void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def));
/* now internal definition, only for zip.c and unzip.h */
typedef struct zlib_filefunc64_32_def_s
{
zlib_filefunc64_def zfile_func64;
open_file_func zopen32_file;
opendisk_file_func zopendisk32_file;
tell_file_func ztell32_file;
seek_file_func zseek32_file;
} zlib_filefunc64_32_def;
#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size))
#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size))
/*#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream))*/
/*#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode))*/
#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream))
#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream))
voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode));
voidpf call_zopendisk64 OF((const zlib_filefunc64_32_def* pfilefunc, voidpf filestream, int number_disk, int mode));
long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin));
ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream));
void fill_zlib_filefunc64_32_def_from_filefunc32 OF((zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32));
#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode)))
#define ZOPENDISK64(filefunc,filestream,diskn,mode) (call_zopendisk64((&(filefunc)),(filestream),(diskn),(mode)))
#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream)))
#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode)))
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,202 +0,0 @@
/* ioapi_mem.h -- IO base function header for compress/uncompress .zip
files using zlib + zip or unzip API
This version of ioapi is designed to access memory rather than files.
We do use a region of memory to put data in to and take it out of. We do
not have auto-extending buffers and do not inform anyone else that the
data has been written. It is really intended for accessing a zip archive
embedded in an application such that I can write an installer with no
external files. Creation of archives has not been attempted, although
parts of the framework are present.
Based on Unzip ioapi.c version 0.22, May 19th, 2003
Copyright (C) 1998-2003 Gilles Vollant
(C) 2003 Justin Fletcher
This file is under the same license as the Unzip tool it is distributed
with.
*/
//taken and adapted from https://stackoverflow.com/a/13492589
// save diagnostic state
#pragma GCC diagnostic push
// turn off the specific warning. Can also use "-Wall"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zlib.h"
#include "ioapi.h"
#include "ioapi_mem.h"
#ifndef IOMEM_BUFFERSIZE
# define IOMEM_BUFFERSIZE (64 * 1024)
#endif
voidpf ZCALLBACK fopen_mem_func (opaque, filename, mode)
voidpf opaque;
const char* filename;
int mode;
{
ourmemory_t *mem = (ourmemory_t *)opaque;
if (mem == NULL)
return NULL; /* Mem structure passed in was null */
if (mode & ZLIB_FILEFUNC_MODE_CREATE)
{
if (mem->grow)
{
mem->size = IOMEM_BUFFERSIZE;
mem->base = (char *)malloc(mem->size);
}
mem->limit = 0; /* When writing we start with 0 bytes written */
}
else
mem->limit = mem->size;
mem->cur_offset = 0;
return mem;
}
voidpf ZCALLBACK fopendisk_mem_func (opaque, stream, number_disk, mode)
voidpf opaque;
voidpf stream;
int number_disk;
int mode;
{
/* Not used */
return NULL;
}
uLong ZCALLBACK fread_mem_func (opaque, stream, buf, size)
voidpf opaque;
voidpf stream;
void* buf;
uLong size;
{
ourmemory_t *mem = (ourmemory_t *)stream;
if (size > mem->size - mem->cur_offset)
size = mem->size - mem->cur_offset;
memcpy(buf, mem->base + mem->cur_offset, size);
mem->cur_offset += size;
return size;
}
uLong ZCALLBACK fwrite_mem_func (opaque, stream, buf, size)
voidpf opaque;
voidpf stream;
const void* buf;
uLong size;
{
ourmemory_t *mem = (ourmemory_t *)stream;
char *newbase = NULL;
uLong newmemsize = 0;
if (size > mem->size - mem->cur_offset)
{
if (mem->grow)
{
newmemsize = mem->size;
if (size < IOMEM_BUFFERSIZE)
newmemsize += IOMEM_BUFFERSIZE;
else
newmemsize += size;
newbase = (char *)malloc(newmemsize);
memcpy(newbase, mem->base, mem->size);
free(mem->base);
mem->base = newbase;
mem->size = newmemsize;
}
else
size = mem->size - mem->cur_offset;
}
memcpy(mem->base + mem->cur_offset, buf, size);
mem->cur_offset += size;
if (mem->cur_offset > mem->limit)
mem->limit = mem->cur_offset;
return size;
}
long ZCALLBACK ftell_mem_func (opaque, stream)
voidpf opaque;
voidpf stream;
{
ourmemory_t *mem = (ourmemory_t *)stream;
return mem->cur_offset;
}
long ZCALLBACK fseek_mem_func (opaque, stream, offset, origin)
voidpf opaque;
voidpf stream;
uLong offset;
int origin;
{
ourmemory_t *mem = (ourmemory_t *)stream;
uLong new_pos;
switch (origin)
{
case ZLIB_FILEFUNC_SEEK_CUR:
new_pos = mem->cur_offset + offset;
break;
case ZLIB_FILEFUNC_SEEK_END:
new_pos = mem->limit + offset;
break;
case ZLIB_FILEFUNC_SEEK_SET:
new_pos = offset;
break;
default:
return -1;
}
if (new_pos > mem->size)
return 1; /* Failed to seek that far */
mem->cur_offset = new_pos;
return 0;
}
int ZCALLBACK fclose_mem_func (opaque, stream)
voidpf opaque;
voidpf stream;
{
/* Even with grow = 1, caller must always free() memory */
return 0;
}
int ZCALLBACK ferror_mem_func (opaque, stream)
voidpf opaque;
voidpf stream;
{
/* We never return errors */
return 0;
}
void fill_memory_filefunc (pzlib_filefunc_def, ourmem)
zlib_filefunc_def* pzlib_filefunc_def;
ourmemory_t *ourmem;
{
pzlib_filefunc_def->zopen_file = fopen_mem_func;
pzlib_filefunc_def->zopendisk_file = fopendisk_mem_func;
pzlib_filefunc_def->zread_file = fread_mem_func;
pzlib_filefunc_def->zwrite_file = fwrite_mem_func;
pzlib_filefunc_def->ztell_file = ftell_mem_func;
pzlib_filefunc_def->zseek_file = fseek_mem_func;
pzlib_filefunc_def->zclose_file = fclose_mem_func;
pzlib_filefunc_def->zerror_file = ferror_mem_func;
pzlib_filefunc_def->opaque = ourmem;
}
// turn the warnings back on
#pragma GCC diagnostic pop

View File

@@ -1,51 +0,0 @@
/* ioapi_mem.h -- IO base function header for compress/uncompress .zip
files using zlib + zip or unzip API
This version of ioapi is designed to access memory rather than files.
We do use a region of memory to put data in to and take it out of.
Copyright (C) 1998-2003 Gilles Vollant
(C) 2003 Justin Fletcher
This program is distributed under the terms of the same license as zlib.
See the accompanying LICENSE file for the full text of the license.
*/
#ifndef _IOAPI_MEM_H
#define _IOAPI_MEM_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zlib.h"
#include "ioapi.h"
#ifdef __cplusplus
extern "C" {
#endif
voidpf ZCALLBACK fopen_mem_func OF((voidpf opaque,const char* filename,int mode));
voidpf ZCALLBACK fopendisk_mem_func OF((voidpf opaque, voidpf stream, int number_disk, int mode));
uLong ZCALLBACK fread_mem_func OF((voidpf opaque,voidpf stream,void* buf,uLong size));
uLong ZCALLBACK fwrite_mem_func OF((voidpf opaque,voidpf stream,const void* buf,uLong size));
long ZCALLBACK ftell_mem_func OF((voidpf opaque,voidpf stream));
long ZCALLBACK fseek_mem_func OF((voidpf opaque,voidpf stream,uLong offset,int origin));
int ZCALLBACK fclose_mem_func OF((voidpf opaque,voidpf stream));
int ZCALLBACK ferror_mem_func OF((voidpf opaque,voidpf stream));
typedef struct ourmemory_s {
char *base; /* Base of the region of memory we're using */
uLong size; /* Size of the region of memory we're using */
uLong limit; /* Furthest we've written */
uLong cur_offset; /* Current offset in the area */
int grow; /* Growable memory buffer */
} ourmemory_t;
void fill_memory_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def, ourmemory_t *ourmem));
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,320 +0,0 @@
/* unzip.h -- IO for uncompress .zip files using zlib
Version 1.1, February 14h, 2010
part of the MiniZip project
Copyright (C) 1998-2010 Gilles Vollant
http://www.winimage.com/zLibDll/minizip.html
Modifications of Unzip for Zip64
Copyright (C) 2007-2008 Even Rouault
Modifications for Zip64 support on both zip and unzip
Copyright (C) 2009-2010 Mathias Svensson
http://result42.com
This program is distributed under the terms of the same license as zlib.
See the accompanying LICENSE file for the full text of the license.
*/
#ifndef _UNZ_H
#define _UNZ_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _ZLIB_H
#include <zlib.h>
#endif
#ifndef _ZLIBIOAPI_H
#include "ioapi.h"
#endif
#ifdef HAVE_BZIP2
#include "bzlib.h"
#endif
#define Z_BZIP2ED 12
#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
/* like the STRICT of WIN32, we define a pointer that cannot be converted
from (void*) without cast */
typedef struct TagunzFile__ { int unused; } unzFile__;
typedef unzFile__ *unzFile;
#else
typedef voidp unzFile;
#endif
#define UNZ_OK (0)
#define UNZ_END_OF_LIST_OF_FILE (-100)
#define UNZ_ERRNO (Z_ERRNO)
#define UNZ_EOF (0)
#define UNZ_PARAMERROR (-102)
#define UNZ_BADZIPFILE (-103)
#define UNZ_INTERNALERROR (-104)
#define UNZ_CRCERROR (-105)
/* tm_unz contain date/time info */
typedef struct tm_unz_s
{
uInt tm_sec; /* seconds after the minute - [0,59] */
uInt tm_min; /* minutes after the hour - [0,59] */
uInt tm_hour; /* hours since midnight - [0,23] */
uInt tm_mday; /* day of the month - [1,31] */
uInt tm_mon; /* months since January - [0,11] */
uInt tm_year; /* years - [1980..2044] */
} tm_unz;
/* unz_global_info structure contain global data about the ZIPfile
These data comes from the end of central dir */
typedef struct unz_global_info64_s
{
ZPOS64_T number_entry; /* total number of entries in the central dir on this disk */
uLong number_disk_with_CD; /* number the the disk with central dir, used for spanning ZIP*/
uLong size_comment; /* size of the global comment of the zipfile */
} unz_global_info64;
typedef struct unz_global_info_s
{
uLong number_entry; /* total number of entries in the central dir on this disk */
uLong number_disk_with_CD; /* number the the disk with central dir, used for spanning ZIP*/
uLong size_comment; /* size of the global comment of the zipfile */
} unz_global_info;
/* unz_file_info contain information about a file in the zipfile */
typedef struct unz_file_info64_s
{
uLong version; /* version made by 2 bytes */
uLong version_needed; /* version needed to extract 2 bytes */
uLong flag; /* general purpose bit flag 2 bytes */
uLong compression_method; /* compression method 2 bytes */
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
uLong crc; /* crc-32 4 bytes */
ZPOS64_T compressed_size; /* compressed size 8 bytes */
ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */
uLong size_filename; /* filename length 2 bytes */
uLong size_file_extra; /* extra field length 2 bytes */
uLong size_file_comment; /* file comment length 2 bytes */
uLong disk_num_start; /* disk number start 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
tm_unz tmu_date;
ZPOS64_T disk_offset;
uLong size_file_extra_internal;
} unz_file_info64;
typedef struct unz_file_info_s
{
uLong version; /* version made by 2 bytes */
uLong version_needed; /* version needed to extract 2 bytes */
uLong flag; /* general purpose bit flag 2 bytes */
uLong compression_method; /* compression method 2 bytes */
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
uLong crc; /* crc-32 4 bytes */
uLong compressed_size; /* compressed size 4 bytes */
uLong uncompressed_size; /* uncompressed size 4 bytes */
uLong size_filename; /* filename length 2 bytes */
uLong size_file_extra; /* extra field length 2 bytes */
uLong size_file_comment; /* file comment length 2 bytes */
uLong disk_num_start; /* disk number start 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
tm_unz tmu_date;
uLong disk_offset;
} unz_file_info;
/***************************************************************************/
/* Opening and close a zip file */
extern unzFile ZEXPORT unzOpen OF((const char *path));
extern unzFile ZEXPORT unzOpen64 OF((const void *path));
/* Open a Zip file.
path should contain the full pathname (by example, on a Windows XP computer
"c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip".
return NULL if zipfile cannot be opened or doesn't exist
return unzFile handle if no error
NOTE: The "64" function take a const void* pointer, because the path is just the value passed to the
open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path
is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* does not describe the reality */
extern unzFile ZEXPORT unzOpen2 OF((const char *path, zlib_filefunc_def* pzlib_filefunc_def));
/* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write operations */
extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, zlib_filefunc64_def* pzlib_filefunc_def));
/* Open a Zip file, like unz64Open, but provide a set of file low level API for read/write 64-bit operations */
extern int ZEXPORT unzClose OF((unzFile file));
/* Close a ZipFile opened with unzOpen. If there is files inside the .Zip opened with unzOpenCurrentFile,
these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
return UNZ_OK if there is no error */
extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, unz_global_info *pglobal_info));
extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, unz_global_info64 *pglobal_info));
/* Write info about the ZipFile in the *pglobal_info structure.
return UNZ_OK if no error */
extern int ZEXPORT unzGetGlobalComment OF((unzFile file, char *comment, uLong comment_size));
/* Get the global comment string of the ZipFile, in the comment buffer.
uSizeBuf is the size of the szComment buffer.
return the number of byte copied or an error code <0 */
/***************************************************************************/
/* Reading the content of the current zipfile, you can open it, read data from it, and close it
(you can close it before reading all the file) */
extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
/* Open for reading data the current file in the zipfile.
return UNZ_OK if no error */
extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, const char* password));
/* Open for reading data the current file in the zipfile.
password is a crypting password
return UNZ_OK if no error */
extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, int* method, int* level, int raw));
/* Same as unzOpenCurrentFile, but open for read raw the file (not uncompress)
if raw==1 *method will receive method of compression, *level will receive level of compression
NOTE: you can set level parameter as NULL (if you did not want known level,
but you CANNOT set method parameter as NULL */
extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, int* method, int* level, int raw, const char* password));
/* Same as unzOpenCurrentFile, but takes extra parameter password for encrypted files */
extern int ZEXPORT unzReadCurrentFile OF((unzFile file, voidp buf, unsigned len));
/* Read bytes from the current file (opened by unzOpenCurrentFile)
buf contain buffer where data must be copied
len the size of buf.
return the number of byte copied if somes bytes are copied
return 0 if the end of file was reached
return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */
extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, unz_file_info *pfile_info, char *filename,
uLong filename_size, void *extrafield, uLong extrafield_size, char *comment, uLong comment_size));
extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, unz_file_info64 *pfile_info, char *filename,
uLong filename_size, void *extrafield, uLong extrafield_size, char *comment, uLong comment_size));
/* Get Info about the current file
pfile_info if != NULL, the *pfile_info structure will contain somes info about the current file
filename if != NULL, the file name string will be copied in filename
filename_size is the size of the filename buffer
extrafield if != NULL, the extra field information from the central header will be copied in to
extrafield_size is the size of the extraField buffer
comment if != NULL, the comment string of the file will be copied in to
comment_size is the size of the comment buffer */
extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file));
extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, voidp buf, unsigned len));
/* Read extra field from the current file (opened by unzOpenCurrentFile)
This is the local-header version of the extra field (sometimes, there is
more info in the local-header version than in the central-header)
if buf == NULL, it return the size of the local extra field
if buf != NULL, len is the size of the buffer, the extra header is copied in buf.
return number of bytes copied in buf, or (if <0) the error code */
extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
/* Close the file in zip opened with unzOpenCurrentFile
return UNZ_CRCERROR if all the file was read but the CRC is not good */
/***************************************************************************/
/* Browse the directory of the zipfile */
typedef int (*unzFileNameComparer)(unzFile file, const char *filename1, const char *filename2);
typedef int (*unzIteratorFunction)(unzFile file);
typedef int (*unzIteratorFunction2)(unzFile file, unz_file_info64 *pfile_info, char *filename,
uLong filename_size, void *extrafield, uLong extrafield_size, char *comment, uLong comment_size);
extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
/* Set the current file of the zipfile to the first file.
return UNZ_OK if no error */
extern int ZEXPORT unzGoToFirstFile2 OF((unzFile file, unz_file_info64 *pfile_info, char *filename,
uLong filename_size, void *extrafield, uLong extrafield_size, char *comment, uLong comment_size));
/* Set the current file of the zipfile to the first file and retrieves the current info on success.
Not as seek intensive as unzGoToFirstFile + unzGetCurrentFileInfo.
return UNZ_OK if no error */
extern int ZEXPORT unzGoToNextFile OF((unzFile file));
/* Set the current file of the zipfile to the next file.
return UNZ_OK if no error
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest */
extern int ZEXPORT unzGoToNextFile2 OF((unzFile file, unz_file_info64 *pfile_info, char *filename,
uLong filename_size, void *extrafield, uLong extrafield_size, char *comment, uLong comment_size));
/* Set the current file of the zipfile to the next file and retrieves the current
info on success. Does less seeking around than unzGotoNextFile + unzGetCurrentFileInfo.
return UNZ_OK if no error
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest */
extern int ZEXPORT unzLocateFile OF((unzFile file, const char *filename, unzFileNameComparer filename_compare_func));
/* Try locate the file szFileName in the zipfile. For custom filename comparison pass in comparison function.
return UNZ_OK if the file is found (it becomes the current file)
return UNZ_END_OF_LIST_OF_FILE if the file is not found */
/***************************************************************************/
/* Raw access to zip file */
typedef struct unz_file_pos_s
{
uLong pos_in_zip_directory; /* offset in zip file directory */
uLong num_of_file; /* # of file */
} unz_file_pos;
extern int ZEXPORT unzGetFilePos OF((unzFile file, unz_file_pos* file_pos));
extern int ZEXPORT unzGoToFilePos OF((unzFile file, unz_file_pos* file_pos));
typedef struct unz64_file_pos_s
{
ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */
ZPOS64_T num_of_file; /* # of file */
} unz64_file_pos;
extern int ZEXPORT unzGetFilePos64 OF((unzFile file, unz64_file_pos* file_pos));
extern int ZEXPORT unzGoToFilePos64 OF((unzFile file, const unz64_file_pos* file_pos));
extern uLong ZEXPORT unzGetOffset OF((unzFile file));
extern ZPOS64_T ZEXPORT unzGetOffset64 OF((unzFile file));
/* Get the current file offset */
extern int ZEXPORT unzSetOffset OF((unzFile file, uLong pos));
extern int ZEXPORT unzSetOffset64 OF((unzFile file, ZPOS64_T pos));
/* Set the current file offset */
extern z_off_t ZEXPORT unztell OF((unzFile file));
extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file));
/* return current position in uncompressed data */
extern int ZEXPORT unzseek OF((unzFile file, z_off_t offset, int origin));
extern int ZEXPORT unzseek64 OF((unzFile file, ZPOS64_T offset, int origin));
/* Seek within the uncompressed data if compression method is storage */
extern int ZEXPORT unzeof OF((unzFile file));
/* return 1 if the end of file was reached, 0 elsewhere */
/***************************************************************************/
#ifdef __cplusplus
}
#endif
#endif /* _UNZ_H */

379
source/music.c Normal file
View File

@@ -0,0 +1,379 @@
/*
* 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 "music.h"
#include "loading.h"
// BCSTM Player adapted from BCSTM-Player by tobid7
// https://github.com/NPI-D7/BCSTM-Player/blob/main/source/bcstm.hpp
u32 read32(char *music_buf, size_t *cursor)
{
u32 ret;
memcpy(&ret, music_buf + *cursor, 4);
*cursor += 4;
return ret;
}
u16 read16(char *music_buf, size_t *cursor)
{
u16 ret;
memcpy(&ret, music_buf + *cursor, 2);
*cursor += 2;
return ret;
}
u8 read8(char *music_buf, size_t *cursor)
{
u8 ret;
memcpy(&ret, music_buf + *cursor, 1);
*cursor += 1;
return ret;
}
Result update_audio_ogg(audio_ogg_s * audio)
{
u32 size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read;
DEBUG("<update_audio> Audio Size: %ld\n", size);
if (audio->wave_buf[audio->buf_pos].status == NDSP_WBUF_DONE) // only run if the current selected buffer has already finished playing
{
DEBUG("<update_audio> Attempting ov_read\n");
int bitstream;
u32 read = ov_read(&audio->vf, (char *)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, &bitstream); // read 1 vorbis packet into wave buffer
DEBUG("<update_audio> ov_read successful\n");
if (read <= 0) // EoF or error
{
ov_clear(&audio->vf);
if (read == 0) // EoF
{
ov_open(fmemopen(audio->filebuf, audio->filesize, "rb"), &audio->vf, NULL, 0); // Reopen file. Don't need to reinit channel stuff since it's all the same as before
} else // Error :(
{
DEBUG("<update_audio> Vorbis play error: %ld\n", read);
ndspChnReset(0);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
}
} else
{
audio->data_read += read;
if (read == size) {
audio->data_read = 0;
ndspChnWaveBufAdd(0, &audio->wave_buf[audio->buf_pos]); // Add buffer to ndsp
audio->buf_pos = 1 - audio->buf_pos; // switch to other buffer to load and prepare it while the current one is playing
}
}
}
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
}
void thread_audio_ogg(void * data) {
audio_ogg_s * audio = (audio_ogg_s *)data;
while(!audio->stop) {
update_audio_ogg(audio);
}
ndspChnWaveBufClear(0);
ndspChnReset(0);
ov_clear(&audio->vf);
free(audio->filebuf);
linearFree((void *)audio->wave_buf[0].data_vaddr);
linearFree((void *)audio->wave_buf[1].data_vaddr);
}
void play_audio_ogg(audio_ogg_s * audio) {
audio->playing_thread = threadCreate(thread_audio_ogg, audio, 0x1000, 0x3F, 1, false);
}
void stop_audio_ogg(audio_ogg_s ** audio_ptr) {
audio_ogg_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;
}
typedef struct {
u16 type;
u32 target;
} bcstm_reference;
int init_audio(audio_s *audio)
{
u32 magic = read32(audio->music_buf, &audio->cursor);
DEBUG("Loading music, music_size: %d, magic: 0x%08lx\n", audio->music_size, magic);
audio->is_little_endian = read16(audio->music_buf, &audio->cursor) == 0xFEFF;
audio->info_offset = 0;
audio->data_offset = 0;
if (magic != 0x4D545343) // CSTM
{
free(audio->music_buf);
return -1;
}
audio->cursor = 0x10;
u16 sbc = read16(audio->music_buf, &audio->cursor);
audio->cursor += 2;
for (u16 i = 0; i < sbc; ++i)
{
u16 sec = read16(audio->music_buf, &audio->cursor);
audio->cursor += 2;
u32 off = read32(audio->music_buf, &audio->cursor);
audio->cursor += 4;
DEBUG("Reading sbc: %04x, %08lx\n", sec, off);
if (sec == 0x4000) // Info block
audio->info_offset = off;
if (sec == 0x4002) // Data block
audio->data_offset = off;
}
DEBUG("Info offset: 0x%08lx, data offset: 0x%08lx\n", audio->info_offset, audio->data_offset);
if (audio->data_offset == 0 || audio->info_offset == 0)
{
free(audio->music_buf);
return -2;
}
audio->cursor = audio->info_offset + 0x20;
if (read8(audio->music_buf, &audio->cursor) != 2) // Encoding - 2 is DSP_ADPCM
{
free(audio->music_buf);
return -3;
}
audio->is_looping = read8(audio->music_buf, &audio->cursor);
audio->channel_count = read8(audio->music_buf, &audio->cursor);
if (audio->channel_count > 2)
{
free(audio->music_buf);
return -4;
}
DEBUG("Channel count: %d, is looping: %d\n", audio->channel_count, audio->is_looping);
audio->cursor = audio->info_offset + 0x24;
audio->sample_rate = read32(audio->music_buf, &audio->cursor);
u32 _loop_pos = read32(audio->music_buf, &audio->cursor);
u32 _loop_end = read32(audio->music_buf, &audio->cursor);
audio->num_blocks = read32(audio->music_buf, &audio->cursor);
audio->block_size = read32(audio->music_buf, &audio->cursor);
audio->block_samples = read32(audio->music_buf, &audio->cursor);
audio->cursor += 4;
audio->last_block_samples = read32(audio->music_buf, &audio->cursor);
audio->last_block_size = read32(audio->music_buf, &audio->cursor);
DEBUG("sample_rate: %lu, loop_start: %lu, loop_end: %lu, num_blocks: %lu, block_size: %lu, block_samples: %lu, last_block_samples: %lu, last_block_size: %lu\n",
audio->sample_rate, _loop_pos, _loop_end, audio->num_blocks, audio->block_size, audio->block_samples, audio->last_block_samples, audio->last_block_size);
audio->loop_start = _loop_pos / audio->block_samples;
audio->loop_end = (_loop_end % audio->block_samples ? audio->num_blocks : _loop_end / audio->block_samples);
while (read32(audio->music_buf, &audio->cursor) != 0x4102); // find channel info header
audio->cursor -= 4;
u32 start_table = audio->cursor - 4;
bcstm_reference channel_refs[2] = {0};
for (u8 i = 0; i < audio->channel_count; ++i)
{
DEBUG("Processing channel_ref at %08x...\n", audio->cursor);
channel_refs[i].type = read16(audio->music_buf, &audio->cursor);
audio->cursor += 2;
channel_refs[i].target = start_table + read32(audio->music_buf, &audio->cursor);
DEBUG("Type: %04x; Target: %08lx\n", channel_refs[i].type, channel_refs[i].target);
}
for (u8 i = 0; i < audio->channel_count; ++i)
{
audio->cursor = channel_refs[i].target;
if (read16(audio->music_buf, &audio->cursor) != 0x300) continue; // Bad bcstm - channel info != DSP ADPCM info
audio->cursor += 2;
audio->cursor += read32(audio->music_buf, &audio->cursor) - 8;
memcpy(audio->adpcm_coefs[i], audio->music_buf + audio->cursor, sizeof(unsigned short) * 16);
audio->cursor += sizeof(unsigned short) * 16;
memcpy(&(audio->adpcm_data[i][0]), audio->music_buf + audio->cursor, sizeof(ndspAdpcmData));
audio->cursor += sizeof(ndspAdpcmData);
memcpy(&(audio->adpcm_data[i][1]), audio->music_buf + audio->cursor, sizeof(ndspAdpcmData));
audio->cursor += sizeof(ndspAdpcmData);
audio->cursor += 2; // skip padding
}
audio->cursor = audio->data_offset + 0x20;
return 0;
}
int start_play(audio_s *audio) {
audio->current_block = 0;
for (u8 i = 0; i < audio->channel_count; ++i)
{
audio->channel[i] = 0;
while (audio->channel[i] < 24 && ((audio->active_channels >> audio->channel[i]) & 1)) {
audio->channel[i]++;
}
if (audio->channel[i] == 24) {
return -1;
}
audio->active_channels |= 1 << audio->channel[i];
ndspChnWaveBufClear(audio->channel[i]);
static float mix[16];
ndspChnSetFormat(audio->channel[i], NDSP_FORMAT_ADPCM | NDSP_3D_SURROUND_PREPROCESSED);
ndspChnSetRate(audio->channel[i], audio->sample_rate);
if (audio->channel_count == 1)
{
mix[0] = mix[1] = 0.5f;
} else if (audio->channel_count == 2)
{
if (i == 0)
{
mix[0] = 0.8f;
mix[1] = 0.0f;
mix[2] = 0.2f;
mix[3] = 0.0f;
} else
{
mix[0] = 0.0f;
mix[1] = 0.8f;
mix[2] = 0.0f;
mix[3] = 0.2f;
}
}
ndspChnSetMix(audio->channel[i], mix);
ndspChnSetAdpcmCoefs(audio->channel[i], audio->adpcm_coefs[i]);
for (u8 j = 0; j < BUFFER_COUNT; ++j)
{
memset(&(audio->wave_buf[i][j]), 0, sizeof(ndspWaveBuf));
audio->wave_buf[i][j].status = NDSP_WBUF_DONE;
audio->buffer_data[i][j] = linearAlloc(audio->block_size);
}
}
return 0;
}
void fill_buffers(audio_s *audio)
{
DEBUG("Filling buffers...\n");
for (u8 bufIndex = 0; bufIndex < BUFFER_COUNT; ++bufIndex)
{
if (audio->wave_buf[0][bufIndex].status != NDSP_WBUF_DONE) continue;
if (audio->channel_count == 2 && audio->wave_buf[1][bufIndex].status != NDSP_WBUF_DONE) continue;
if (audio->is_looping && audio->current_block == audio->loop_end)
{
audio->current_block = audio->loop_start;
audio->cursor = audio->data_offset + 0x20 + audio->block_size * audio->channel_count * audio->loop_start;
}
if (!audio->is_looping && audio->current_block == audio->loop_end)
{
audio->stop = true;
return;
}
for (u8 channelIndex = 0; channelIndex < audio->channel_count; ++channelIndex)
{
ndspWaveBuf *buf = &(audio->wave_buf[channelIndex][bufIndex]);
memset(buf, 0, sizeof(ndspWaveBuf));
buf->data_adpcm = audio->buffer_data[channelIndex][bufIndex];
memcpy(buf->data_adpcm, audio->music_buf + audio->cursor, (audio->current_block == audio->num_blocks - 1) ? audio->last_block_size : audio->block_size);
audio->cursor += (audio->current_block == audio->num_blocks - 1) ? audio->last_block_size : audio->block_size;
DSP_FlushDataCache(buf->data_adpcm, audio->block_size);
if (audio->current_block == 0)
buf->adpcm_data = &(audio->adpcm_data[channelIndex][0]);
else if (audio->current_block == audio->loop_start)
buf->adpcm_data = &(audio->adpcm_data[channelIndex][1]);
if (audio->current_block == audio->num_blocks - 1)
buf->nsamples = audio->last_block_samples;
else
buf->nsamples = audio->block_samples;
ndspChnWaveBufAdd(audio->channel[channelIndex], buf);
}
audio->current_block++;
}
}
// Play a given audio struct
void update_audio(audio_s * audio)
{
u32 current_time = svcGetSystemTick();
if (current_time - audio->last_time > 1e8)
{
fill_buffers(audio);
audio->last_time = current_time;
}
}
void thread_audio(void * data) {
audio_s * audio = (audio_s *)data;
int res = init_audio(audio);
if (res < 0)
{
return;
}
start_play(audio);
while(!audio->stop) {
update_audio(audio);
}
for (u8 i = 0; i < audio->channel_count; ++i)
{
ndspChnWaveBufClear(audio->channel[i]);
ndspChnReset(audio->channel[i]);
audio->active_channels &= ~(1 << audio->channel[i]);
for (u8 j = 0; j < BUFFER_COUNT; ++j) {
linearFree(audio->buffer_data[i][j]);
}
}
free(audio->music_buf);
}
void play_audio(audio_s * audio) {
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;
}

Submodule source/pp2d deleted from 77d7b18ec9

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,233 +122,113 @@ 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; quirc_pixel_t *row = q->pixels + y * q->w;
if (!depth) if (depth >= FLOOD_FILL_MAX_DEPTH)
return; return;
while (left > 0 && row[left - 1] == ffp.from) while (left > 0 && row[left - 1] == from)
left--; left--;
while (right < ffp.q->w - 1 && row[right + 1] == ffp.from) while (right < q->w - 1 && row[right + 1] == from)
right++; right++;
/* Fill the extent */ /* Fill the extent */
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
row[i] = ffp.to; row[i] = to;
if (ffp.func) if (func)
ffp.func(ffp.user_data, y, left, right); func(user_data, y, left, right);
/* Seed new flood-fills */ /* Seed new flood-fills */
if (y > 0) { if (y > 0) {
row = ffp.q->pixels + (y - 1) * ffp.q->w; row = q->pixels + (y - 1) * q->w;
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
if (row[i] == ffp.from) if (row[i] == from)
flood_fill_rec(i, y - 1, depth - 1); flood_fill_seed(q, i, y - 1, from, to,
func, user_data, depth + 1);
} }
if (y < ffp.q->h - 1) { if (y < q->h - 1) {
row = ffp.q->pixels + (y + 1) * ffp.q->w; row = q->pixels + (y + 1) * q->w;
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
if (row[i] == ffp.from) if (row[i] == from)
flood_fill_rec(i, y + 1, depth - 1); flood_fill_seed(q, i, y + 1, from, to,
func, user_data, 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;
while (left > 0 && row[left - 1] == from)
left--;
while (right < q->w - 1 && row[right + 1] == from)
right++;
/* Fill the extent */
for (i = left; i <= right; i++)
row[i] = to;
if (func)
func(user_data, y, left, right);
/* Seed new flood-fills */
if (y > 0) {
row = q->pixels + (y - 1) * q->w;
for (i = left; i <= right; i++)
if (row[i] == from)
fill_stack_push(&s, i, y - 1);
}
if (y < q->h - 1) {
row = q->pixels + (y + 1) * q->w;
for (i = left; i <= right; i++)
if (row[i] == from)
fill_stack_push(&s, i, y + 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)) /
threshold_s + row[w];
avg_u = (avg_u * (threshold_s - 1)) /
threshold_s + row[u];
row_average[w] += avg_w;
row_average[u] += avg_u;
}
for (x = 0; x < q->w; x++) {
if (row[x] < row_average[x] *
(100 - THRESHOLD_T) / (200 * threshold_s))
row[x] = QUIRC_PIXEL_BLACK;
else
row[x] = QUIRC_PIXEL_WHITE;
}
row += q->w;
} }
// Calculate weighted sum of histogram values
int sum = 0;
for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
sum += i * histogram[i];
}
// Compute threshold
int sumB = 0;
int q1 = 0;
double max = 0;
uint8_t threshold = 0;
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;
}
}
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++) { uint8_t* source = q->image;
for (x = 0; x < q->w; x++) { quirc_pixel_t* dest = q->pixels;
q->pixels[y * q->w + x] = q->image[y * q->w + x]; 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); /* q->pixels may alias q->image when their type representation is of the
if (sizeof(*q->image) != sizeof(*q->pixels)) 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}
} }
} }
}; };

1367
source/remote.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-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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
@@ -26,87 +26,106 @@
#include "splashes.h" #include "splashes.h"
#include "unicode.h" #include "unicode.h"
#include "fs.h" #include "fs.h"
#include "draw.h"
#include "ui_strings.h"
Result get_splashes(Splash_s** splashes_list, int *splash_count) void splash_delete(void)
{
Result res = 0;
Handle dir_handle;
res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, SPLASHES_PATH));
if (R_FAILED(res))
return res;
u32 entries_read = 1;
while (entries_read)
{
FS_DirectoryEntry entry = {0};
res = FSDIR_Read(dir_handle, &entries_read, 1, &entry);
if (R_FAILED(res) || entries_read == 0)
break;
if (!(entry.attributes & FS_ATTRIBUTE_DIRECTORY))
continue;
*splash_count += entries_read;
*splashes_list= realloc(*splashes_list, (*splash_count) * sizeof(Splash_s));
if (splashes_list == NULL)
break;
Splash_s *current_splash = &(*splashes_list)[*splash_count-1];
memset(current_splash, 0, sizeof(Splash_s));
u16 splash_path[0x106] = {0};
struacat(splash_path, SPLASHES_PATH);
strucat(splash_path, entry.name);
u16 top_path[0x106] = {0};
memcpy(top_path, splash_path, 0x106);
struacat(top_path, "/splash.bin");
u16 bottom_path[0x106] = {0};
memcpy(bottom_path, splash_path, 0x106);
struacat(bottom_path, "/splashbottom.bin");
char *temp_buf = NULL;
u32 bytes = file_to_buf(fsMakePath(PATH_UTF16, top_path), ArchiveSD, &temp_buf);
if (!bytes)
{
memset(top_path, 0, 0x106);
}
free(temp_buf);
temp_buf = NULL;
bytes = file_to_buf(fsMakePath(PATH_UTF16, bottom_path), ArchiveSD, &temp_buf);
if (!bytes)
{
memset(bottom_path, 0, 0x106);
}
free(temp_buf);
memcpy(current_splash->name, entry.name, 0x106);
memcpy(current_splash->top_path, top_path, 0x106);
memcpy(current_splash->bottom_path, bottom_path, 0x106);
}
FSDIR_Close(dir_handle);
return res;
}
void splash_delete()
{ {
remove("/luma/splash.bin"); remove("/luma/splash.bin");
remove("/luma/splashbottom.bin"); remove("/luma/splashbottom.bin");
} }
void splash_install(Splash_s splash_to_install)
void splash_install(const Entry_s * splash)
{ {
char *screen_buf; char *screen_buf = NULL;
u32 size = file_to_buf(fsMakePath(PATH_UTF16, splash_to_install.top_path), ArchiveSD, &screen_buf);
if (size) u32 size = load_data("/splash.bin", splash, &screen_buf);
if(size != 0)
{ {
remake_file("/luma/splash.bin", ArchiveSD, size); remake_file(fsMakePath(PATH_ASCII, "/luma/splash.bin"), ArchiveSD, size);
buf_to_file(size, "/luma/splash.bin", ArchiveSD, screen_buf); buf_to_file(size, fsMakePath(PATH_ASCII, "/luma/splash.bin"), ArchiveSD, screen_buf);
free(screen_buf);
} }
size = file_to_buf(fsMakePath(PATH_UTF16, splash_to_install.bottom_path), ArchiveSD, &screen_buf); u32 bottom_size = load_data("/splashbottom.bin", splash, &screen_buf);
if (size) if(bottom_size != 0)
{ {
remake_file("/luma/splashbottom.bin", ArchiveSD, size); remake_file(fsMakePath(PATH_ASCII, "/luma/splashbottom.bin"), ArchiveSD, bottom_size);
buf_to_file(size, "/luma/splashbottom.bin", ArchiveSD, screen_buf); buf_to_file(bottom_size, fsMakePath(PATH_ASCII, "/luma/splashbottom.bin"), ArchiveSD, screen_buf);
free(screen_buf);
} }
}
if(size == 0 && bottom_size == 0)
{
throw_error(language.splashes.no_splash_found, ERROR_LEVEL_WARNING);
}
else
{
char *config_buf;
size = file_to_buf(fsMakePath(PATH_ASCII, "/luma/config.bin"), ArchiveSD, &config_buf);
if(size)
{
if(config_buf[0xC] == 0)
{
free(config_buf);
throw_error(language.splashes.splash_disabled, ERROR_LEVEL_WARNING);
}
}
}
}
void splash_check_installed(void * void_arg)
{
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
Entry_List_s * list = (Entry_List_s *)arg->thread_arg;
if(list == NULL || list->entries == NULL) return;
#ifndef CITRA_MODE
char * top_buf = NULL;
u32 top_size = file_to_buf(fsMakePath(PATH_ASCII, "/luma/splash.bin"), ArchiveSD, &top_buf);
char * bottom_buf = NULL;
u32 bottom_size = file_to_buf(fsMakePath(PATH_ASCII, "/luma/splashbottom.bin"), ArchiveSD, &bottom_buf);
if(!top_size && !bottom_size)
{
free(top_buf);
free(bottom_buf);
return;
}
#define HASH_SIZE_BYTES 256/8
u8 top_hash[HASH_SIZE_BYTES] = {0};
FSUSER_UpdateSha256Context(top_buf, top_size, top_hash);
free(top_buf);
top_buf = NULL;
u8 bottom_hash[HASH_SIZE_BYTES] = {0};
FSUSER_UpdateSha256Context(bottom_buf, bottom_size, bottom_hash);
free(bottom_buf);
bottom_buf = NULL;
for(int i = 0; i < list->entries_count && arg->run_thread; i++)
{
Entry_s * splash = &list->entries[i];
top_size = load_data("/splash.bin", splash, &top_buf);
bottom_size = load_data("/splashbottom.bin", splash, &bottom_buf);
if(!top_size && !bottom_size)
{
continue;
}
u8 splash_top_hash[HASH_SIZE_BYTES] = {0};
FSUSER_UpdateSha256Context(top_buf, top_size, splash_top_hash);
free(top_buf);
top_buf = NULL;
u8 splash_bottom_hash[HASH_SIZE_BYTES] = {0};
FSUSER_UpdateSha256Context(bottom_buf, bottom_size, splash_bottom_hash);
free(bottom_buf);
bottom_buf = NULL;
if(!memcmp(splash_bottom_hash, bottom_hash, HASH_SIZE_BYTES) && !memcmp(splash_top_hash, top_hash, HASH_SIZE_BYTES))
{
splash->installed = true;
break;
}
}
#endif
}

File diff suppressed because it is too large Load Diff

2088
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-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth") * 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
@@ -26,38 +26,53 @@
#include "unicode.h" #include "unicode.h"
ssize_t strulen(const u16 *input, ssize_t max_len) void replace_chars(u16 *input, char *remove, u16 with)
{
for (u16 *cursor = input; *cursor != '\0'; cursor++)
{
if (strchr(remove, (char) (*cursor & 0xFF)))
{
*cursor = with;
}
}
}
size_t strulen(const u16 * input, ssize_t max_len)
{ {
for (int i = 0; i < max_len; i++) if (input[i] == 0) return i; for (int i = 0; i < max_len; i++) if (input[i] == 0) return i;
return max_len; return 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)
{ {
ssize_t in_len = strulen(input, 0x106); ssize_t in_len = strulen(input, 0x106);
ssize_t buf_len = in_len + 1; // Plus 1 for proper null termination ssize_t buf_len = in_len + 1; // Plus 1 for proper null termination
wchar_t *buf = calloc(buf_len, sizeof(wchar_t)); wchar_t * buf = calloc(buf_len, sizeof(wchar_t));
utf16_to_utf32((u32*)buf, input, buf_len); utf16_to_utf32((u32 *)buf, input, buf_len);
printf("%ls\n", buf); char cbuf[0x106];
sprintf(cbuf, "%ls\n", buf);
DEBUG(cbuf);
free(buf); free(buf);
} }
u16 *strucat(u16 *destination, const u16 *source) size_t strucat(u16 * destination, const u16 * source)
{ {
ssize_t dest_len = strulen(destination, 0x106); size_t dest_len = strulen(destination, 0x106);
ssize_t source_len = strulen(source, 0x106); size_t source_len = strulen(source, 0x106);
memcpy(&destination[dest_len], source, source_len * sizeof(u16)); memcpy(&destination[dest_len], source, source_len * sizeof(u16));
return destination; destination[min(dest_len + source_len, 0x106 - 1)] = 0;
return source_len;
} }