185 Commits

Author SHA1 Message Date
730a0cd3d7 Add libvorbisdec to deps (closes #162) 2018-05-13 09:21:41 -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
04ebe9e498 Merge branch 'master' of github.com:3dsfug/Anemone3DS 2017-09-04 14:40:28 -04:00
02526707a0 Fix invalid characters in QR codes 2017-09-04 14:40:20 -04:00
ZetaDesigns
494962dc88 ...and also block it in preview mode
i'm such a idiot
2017-09-04 20:07:49 +02:00
ZetaDesigns
d0979126f7 Merge branch 'master' of https://github.com/astronautlevel2/Anemone3DS 2017-09-04 20:07:10 +02:00
ZetaDesigns
57c4da7614 Block QRMode in Splashmode 2017-09-04 20:03:25 +02:00
9ab442c598 Merge branch 'master' of github.com:3dsfug/Anemone3DS 2017-09-04 13:40:41 -04:00
03497a79ef Fix green tint on CTR cameras 2017-09-04 13:40:33 -04:00
ZetaDesigns
1433002082 Merge branch 'master' of https://github.com/astronautlevel2/Anemone3DS 2017-09-04 19:31:47 +02:00
ZetaDesigns
242f10ec0a Add QRCode reference 2017-09-04 19:31:44 +02:00
Joel
292c100480 Implement fast scroll using circle pad. (#41)
Not too fast yet not too slow either.
2017-09-04 13:23:18 -04:00
Helloman892
cca45bff0f Resolves QR issue (#40)
Swaps b and g channels so they're proper
2017-09-04 09:56:51 -04:00
Helloman892
630cc41759 updating README.md (#38)
* correcting mild spelling mistake

* more readme edits
2017-09-04 08:31:33 -04:00
5fc499ff78 Automatically update themes list when a new theme is downloaded 2017-09-04 02:42:27 -04:00
3241544296 (Mostly) finish QR support 2017-09-04 02:03:58 -04:00
7aed454297 Increase stack size to decode larger QR codes properly 2017-09-03 15:53:41 -04:00
Nils P
c539fa239c Add Building section 2017-09-03 18:57:40 +02:00
Alex Taber
b7d15da092 Qr stuff (#32)
QRs are now scanned and decoded, but they aren't downloaded.
2017-09-03 12:52:02 -04:00
Nils P
9a34a17fe8 Changing all Battery related icons (#29) 2017-09-03 10:19:54 -04:00
4084408562 Fix arm11 dabort when switching apps through home menu, START no longer reboots in some cases 2017-09-02 23:07:47 -04:00
9afbe22df7 Merge branch 'master' of github.com:3dsfug/Anemone3DS 2017-09-02 17:26:10 -04:00
448a9ceb8a Fix no-smdh case 2017-09-02 17:25:59 -04:00
Laine
25aa02008e Update README.md (#25)
* fix readme

* fix?

* fix?

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md
2017-09-02 15:59:55 -04:00
49e6c6f827 Add splash uninstall screen 2017-09-02 12:16:17 -04:00
365ae87477 Merge branch 'master' of github.com:3dsfug/Anemone3DS 2017-09-02 11:57:18 -04:00
f461b3b5b5 Remove GPIO error on reboot, change battery % indicator to battery graphics 2017-09-02 11:57:02 -04:00
Nils P
6ea3904682 Adding splash deletion feature (#24)
* Adding splash deletion feature

* Add Mention for deleting splashes
2017-09-02 11:45:56 -04:00
aac093f466 Resolved merge conflicts 2017-09-02 10:08:52 -04:00
9442afb608 Change stuff with MCUHWC, reboot functionality 2017-09-02 10:05:42 -04:00
Helloman892
91f54ff22e Start button functionality (#21)
* srvPublishToSubscriber

Pressing Start from the app now sends the user to the Power Off screen.

* *hax check

As per Sono, checks whether the current environment is *hax. If it isn't, it goes to the home screen; otherwise, it restarts the console.

* Bugfixes

This may work?

* Revert "Bugfixes"

This reverts commit 7c382d719a.
2017-09-02 10:05:18 -04:00
pokecraft98
aa593c61a6 Added invite to theme plaza discord (#23) 2017-09-02 10:05:10 -04:00
Nils P
2a8e3176a8 Readme Overhaul (#20)
* Readme Overhaul

* Linking to Project_CTR for makerom
2017-09-02 09:23:48 -04:00
thedax
c84f40cf57 3dstool isn't really needed for romfs generation. (#19) 2017-09-01 19:33:24 -04:00
Alex Taber
45cbf0761b Add dependencies 2017-09-01 15:07:27 -04:00
8c7c409bca Update pp2d 2017-09-01 12:51:47 -04:00
d9ddd68d2e Change to using the homebrew logo I guess 2017-09-01 12:51:25 -04:00
d93f4f9207 Add install BGM option 2017-09-01 12:31:55 -04:00
867f4b61ab Fix splash install when /luma/splash.bin or /luma/splashbottom.bin were missing 2017-09-01 12:11:25 -04:00
72153ba15d Make controls more untuitive 2017-09-01 12:02:09 -04:00
c4a096249c Fix softlock and make error more descriptive, and fixed preview offset 2017-09-01 11:55:32 -04:00
LiquidFenrir
91ba5fed49 previews overhaul (#13)
* allow quitting if there are no themes or splashes

* load preview only once if the user toggles more than once
2017-09-01 10:12:26 -04:00
73 changed files with 16445 additions and 4137 deletions

BIN
.flask/banner.t3x Normal file

Binary file not shown.

BIN
.flask/icon.t3x Normal file

Binary file not shown.

5
.flask/meta.json Normal file
View File

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

3
.gitmodules vendored
View File

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

18
CONTRIBUTORS.md Normal file
View File

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

View File

@@ -11,14 +11,14 @@ include $(DEVKITARM)/3ds_rules
# Your values.
APP_TITLE := Anemone3DS
APP_DESCRIPTION := A complete themeing tool for the 3DS
APP_AUTHOR := astronautlevel and daedreth
APP_DESCRIPTION := A complete theming tool for the 3DS
APP_AUTHOR := Anemone3DS Team
TARGET := $(subst $e ,_,$(notdir $(APP_TITLE)))
OUTDIR := out
BUILD := build
SOURCES := source source/pp2d/pp2d source/minizip
SOURCES := source source/pp2d/pp2d source/quirc
INCLUDES := include
ROMFS := romfs
@@ -33,7 +33,7 @@ BANNER_IMAGE := meta/banner.png
RSF_PATH := meta/app.rsf
# 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)
@@ -49,24 +49,29 @@ ICON_FLAGS := nosavebackups,visible
#---------------------------------------------------------------------------------
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 \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/')
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(revision)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(revision)\"" -DAPP_TITLE="\"$(APP_TITLE)\""
ifneq ($(strip $(CITRA_MODE)),)
CFLAGS += -DCITRA_MODE
endif
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lcitro3d -lctrud -lm -lz
LIBS := -lvorbisidec -logg -larchive -ljansson -lcitro3d -lctrud -lm -lz
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(CTRULIB) $(DEVKITPRO)/portlibs/armv6k
LIBDIRS := $(CTRULIB) $(PORTLIBS)
#---------------------------------------------------------------------------------
@@ -144,6 +149,9 @@ all: 3dsx cia
3dsx: $(BUILD) $(OUTPUT).3dsx
cia : $(BUILD) $(OUTPUT).cia
citra: export CITRA_MODE = 1
citra: 3dsx
#---------------------------------------------------------------------------------
$(BUILD):
@mkdir -p $(OUTDIR)
@@ -167,9 +175,9 @@ MAKEROM ?= makerom
MAKEROM_ARGS := -elf "$(OUTPUT).elf" -rsf "$(RSF_PATH)" -banner "$(BUILD)/banner.bnr" -icon "$(BUILD)/icon.icn" -DAPP_TITLE="$(APP_TITLE)" -DAPP_PRODUCT_CODE="$(PRODUCT_CODE)" -DAPP_UNIQUE_ID="$(UNIQUE_ID)"
ifneq ($(strip $(ROMFS)),)
MAKEROM_ARGS += -romfs "$(BUILD)/romfs.bin"
endif
#ifneq ($(strip $(ROMFS)),)
# MAKEROM_ARGS += -romfs "$(BUILD)/romfs.bin"
#endif
ifneq ($(strip $(LOGO)),)
MAKEROM_ARGS += -logo "$(LOGO)"
endif
@@ -178,7 +186,7 @@ ifeq ($(strip $(ROMFS)),)
$(OUTPUT).cia: $(OUTPUT).elf $(BUILD)/banner.bnr $(BUILD)/icon.icn
$(MAKEROM) -f cia -o "$@" -target t -exefslogo $(MAKEROM_ARGS)
else
$(OUTPUT).cia: $(OUTPUT).elf $(BUILD)/romfs.bin $(BUILD)/banner.bnr $(BUILD)/icon.icn
$(OUTPUT).cia: $(OUTPUT).elf $(BUILD)/banner.bnr $(BUILD)/icon.icn
$(MAKEROM) -f cia -o "$@" -target t -exefslogo $(MAKEROM_ARGS)
endif
@@ -204,11 +212,6 @@ $(BUILD)/icon.icn : $(APP_ICON)
$(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i "$(APP_ICON)" -f "$(ICON_FLAGS)" -o "$@"
3DSTOOL ?= 3dstool
$(BUILD)/romfs.bin : $(ROMFS)
$(3DSTOOL) -ctf romfs "$@" --romfs-dir "$(ROMFS)"
#---------------------------------------------------------------------------------
else

View File

@@ -1,27 +1,41 @@
# Anemone3DS
A Theme Manager for the Nintendo3DS, written in C
![# Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/blob/master/meta/banner.png)
A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.\
To-do list here: https://trello.com/b/F1YSa1VK
# Dependencies
This depends on zlib. Get it from portlibs.
* devkitPro, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started).
* zlib, jansson, libvorbisidec, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702).
* A recent build of [makerom](https://github.com/profi200/Project_CTR) and the latest release of [bannertool](https://github.com/Steveice10/bannertool). These must be added to your PATH.
A 64-bit Windows binary of makerom is available [here](https://hm892.s-ul.eu/U0Irkqih).
* ~~[pp2d](https://github.com/BernardoGiordano/pp2d), which is included in the repo if you do a git clone --recursive.~~
Due to circumstances surrounding the privacy settings on the pp2d repo, the source files are now included directly within the repo.
* Git needs to be on your PATH (this is usually only manual under Windows).
# Building
First of all, make sure devkitPro is properly installed and added to your PATH.
After that, open the directory you want to clone the repo into, and execute
`git clone https://github.com/astronautlevel2/Anemone3DS` (or any other cloning method).
To install zlib, jansson and libarchive, begin by following the instructions found above ([here](https://devkitpro.org/viewtopic.php?f=13&t=8702)) on the devkitPro forums, and then install the dkP packages `3ds-zlib`, `3ds-jansson` and `3ds-libarchive`.
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/`.
# License
This project is licensed under the GNU GPLv3. See LICENSE.md for details. Additional terms 7b and 7c apply to this project.
# Credits
The following people contributed to Anemone3DS in some way. Without these people, Anemone3DS wouldn't exist, or wouldn't be as good as it is: [CONTRIBUTORS.md](CONTRIBUTORS.md)
The following people contributed to Anemone3DS in some way. Without these people, Anemone3DS wouldn't exist, or wouldn't be as good as it is:
* Daedreth, who wrote the initial implementation of theme application code and SMDH parsing
* 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, who wrote the BCSTM playback code
* Kenn (mattkenster) for designing the GUI, a number of sprites used in the application, and drawing the banner and icon.
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, while not directly contributing, helped immensely:
* Rinnegatamante, whose code served as reference on theme installation.
* BernardoGiordano for making pp2d, and being super responsive to feature requests and just general help
* 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, Fenrir, and DanielKO).
* the maintainers for all used libraries, including ctrulib, zlib, citro3d, pp2d and minizip.
Special thanks go to these people who, whilst not directly contributing, helped immensely:
* [Rinnegatamante](https://github.com/Rinnegatamante), whose code served as reference on theme installation.
* [SteveIce10](https://github.com/SteveIce10), whose QR code in FBI was essential.
* [BernardoGiordano](https://github.com/BernardoGiordano) for making pp2d, and being super responsive to feature requests and just general help.
* [yellows8](https://github.com/yellows8) for his home menu extdump tool, which was invaluable in debugging.
* the folks on #dev of Nintendo Homebrew, who helped with unicode shenanigans (especially [Stary2001](https://github.com/Stary2001), [Fenrir](https://github.com/FenrirWolf), and DanielKO).
* the maintainers for all used libraries, including but not limited to ctrulib, zlib, citro3d, pp2d, and quirc.
* all the people who helped keep me going and motivated me to work. This includes, but is definitely not limited to:
* The members of the Nintendo Homebrew Discord
* The members of the Secret Shack Service Discord
* The members of the ThemePlaza Discord
+ The members of the [Nintendo Homebrew Discord](https://discord.gg/C29hYvh)
+ The members of the __Secret Shack Service Discord__
+ The members of the [ThemePlaza Discord](https://discord.gg/2hUQwXz)

48
include/camera.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef CAMERA_H
#define CAMERA_H
#include "common.h"
typedef struct {
u16 *camera_buffer;
u32 *texture_buffer;
Handle mutex;
volatile bool finished;
volatile bool success;
Handle cancel;
bool capturing;
struct quirc* context;
} qr_data;
bool init_qr(void);
void exit_qr(qr_data *data);
void take_picture(void);
#endif

43
include/colors.h Normal file
View File

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

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,16 +33,57 @@
#include <stdlib.h>
#include <string.h>
#define THEMES_PATH "/Themes/"
#define SPLASHES_PATH "/Splashes/"
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define POS() DEBUG("%s (line %d)...\n", __func__, __LINE__)
static const int THEMES_PER_SCREEN = 4;
#define DEBUGPOS(...) \
POS(); \
DEBUG(__VA_ARGS__)
#define FASTSCROLL_WAIT 1.5e8
typedef enum {
MODE_THEMES = 0,
MODE_SPLASHES,
MODE_AMOUNT,
} EntryMode;
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;
enum TextureID {
TEXTURE_FONT_RESERVED = 0, //used by pp2d for the font
TEXTURE_FONT_RESERVED = 0, // used by pp2d for the font
TEXTURE_ARROW,
TEXTURE_ARROW_SIDE,
TEXTURE_SHUFFLE,
TEXTURE_SHUFFLE_NO_BGM,
TEXTURE_INSTALLED,
TEXTURE_PREVIEW_ICON,
TEXTURE_SORT,
TEXTURE_DOWNLOAD,
TEXTURE_BROWSE,
TEXTURE_LIST,
TEXTURE_EXIT,
TEXTURE_BATTERY_0,
TEXTURE_BATTERY_1,
TEXTURE_BATTERY_2,
TEXTURE_BATTERY_3,
TEXTURE_BATTERY_4,
TEXTURE_BATTERY_5,
TEXTURE_BATTERY_CHARGE,
TEXTURE_QR,
TEXTURE_PREVIEW,
TEXTURE_REMOTE_PREVIEW,
TEXTURE_SELECT_BUTTON,
TEXTURE_START_BUTTON,
// always the last
TEXTURE_REMOTE_ICONS,
TEXTURE_ICON = TEXTURE_REMOTE_ICONS + 24,
};
#endif

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,14 +27,76 @@
#ifndef DRAW_H
#define DRAW_H
#include "themes.h"
#include "splashes.h"
#include "common.h"
#include "loading.h"
#include "colors.h"
typedef enum {
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_PREVIEW,
INSTALL_LOADING_REMOTE_BGM,
INSTALL_NONE,
} InstallType;
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
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,
} ButtonPos;
typedef struct {
const wchar_t * info_line;
Color info_line_color;
const wchar_t * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
} Instructions_s;
void init_screens(void);
void exit_screens(void);
void draw_theme_install(bool shuffle_install);
void draw_theme_interface(Theme_s * themes_list, int theme_count, int selected_theme, bool preview_mode);
void draw_splash_interface(Splash_s *splashes_list, int splash_count, int selected_splash, bool preview_mode);
void throw_error(char* error, ErrorLevel level);
bool draw_confirm(const char* conf_msg, Entry_List_s* list);
void draw_preview(ssize_t previewID, int preview_offset);
void draw_install(InstallType type);
void draw_loading_bar(u32 current, u32 max, InstallType type);
void draw_base_interface(void);
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions);
void draw_interface(Entry_List_s* list, Instructions_s instructions);
#endif

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,10 +36,11 @@ FS_Archive ArchiveThemeExt;
Result open_archives(void);
Result close_archives(void);
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_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char ** buf);
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf);
u32 buf_to_file(u32 size, char *path, FS_Archive archive, char *buf);
void remake_file(char *path, FS_Archive archive, u32 size);
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf);
void remake_file(FS_Path path, FS_Archive archive, u32 size);
#endif

148
include/instructions.h Normal file
View File

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

126
include/loading.h Normal file
View File

@@ -0,0 +1,126 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef LOADING_H
#define LOADING_H
#include "common.h"
#include "music.h"
#include <jansson.h>
enum ICON_IDS_OFFSET {
ICONS_ABOVE = 0,
ICONS_VISIBLE,
ICONS_UNDER,
ICONS_OFFSET_AMOUNT,
};
typedef enum {
SORT_NONE,
SORT_NAME,
SORT_AUTHOR,
SORT_PATH,
} SortMode;
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 {
u16 name[0x41];
u16 desc[0x81];
u16 author[0x41];
u32 placeholder_color;
u16 path[0x106];
bool is_zip;
bool in_shuffle;
bool no_bgm_shuffle;
bool installed;
json_int_t tp_download_id;
} Entry_s;
typedef struct {
Entry_s * entries;
int entries_count;
ssize_t texture_id_offset;
ssize_t * icons_ids;
int previous_scroll;
int scroll;
int previous_selected;
int selected_entry;
int shuffle_count;
EntryMode mode;
int entries_per_screen_v;
int entries_per_screen_h;
int entries_loaded;
int entry_size;
SortMode current_sort;
json_int_t tp_current_page;
json_int_t tp_page_count;
char * tp_search;
} Entry_List_s;
typedef struct {
void ** thread_arg;
volatile bool run_thread;
} Thread_Arg_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);
Result load_entries(const char * loading_path, Entry_List_s * list);
bool load_preview(Entry_List_s list, int * preview_offset);
Result load_audio(Entry_s, audio_s *);
void load_icons_first(Entry_List_s * current_list, bool silent);
void handle_scrolling(Entry_List_s * list);
void load_icons_thread(void * void_arg);
u32 load_data(char * filename, Entry_s entry, char ** buf);
#endif

54
include/music.h Normal file
View File

@@ -0,0 +1,54 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef MUSIC_H
#define MUSIC_H
#include "common.h"
#include "fs.h"
#include "unicode.h"
#include <tremor/ivorbisfile.h>
#include <tremor/ivorbiscodec.h>
#define BUF_TO_READ 40960 // How much data should be buffered at a time
typedef struct {
OggVorbis_File vf;
ndspWaveBuf wave_buf[2];
float mix[12];
u8 buf_pos;
long data_read;
char *filebuf;
u32 filesize;
volatile bool stop;
Handle finished;
} audio_s;
void play_audio(audio_s *);
#endif

56
include/remote.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef REMOTE_H
#define REMOTE_H
#include "common.h"
#include "draw.h"
#define THEMEPLAZA_BASE_URL "https://themeplaza.eu"
#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_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
bool themeplaza_browser(EntryMode mode);
u32 http_get(const char *url, char ** filename, char ** buf, InstallType install_type);
#endif

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,14 +28,11 @@
#define SPLASHES_H
#include "common.h"
#include "loading.h"
typedef struct{
u16 name[0x106];
u16 top_path[0x106];
u16 bottom_path[0x106];
} Splash_s;
void splash_delete(void);
void splash_install(Entry_s splash);
Result get_splashes(Splash_s** splashes_list, int *splash_count);
void splash_install(Splash_s splash_to_install);
void splash_check_installed(void * void_arg);
#endif

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,26 +28,53 @@
#define THEMES_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 {
u16 name[0x40];
u16 desc[0x80];
u16 author[0x40];
u32 index;
u8 dlc_tid_low_bits;
u8 type;
u16 unk;
} ThemeEntry_s;
bool has_icon;
ssize_t icon_id;
typedef struct {
u8 _padding1[0x13b8];
ThemeEntry_s theme_entry;
ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES];
u8 _padding2[0xb];
bool shuffle;
} SaveData_dat_s;
bool has_preview;
typedef struct {
u32 unk1;
u32 unk2;
u32 body_size;
u32 music_size;
u32 unk3;
u32 unk4;
u32 dlc_theme_content_index;
u32 use_theme_cache;
u16 path[0x106];
bool is_zip;
u8 _padding1[0x338 - 8*sizeof(u32)];
bool in_shuffle;
} Theme_s;
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES];
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES];
} ThemeManage_bin_s;
void load_theme_preview(Theme_s *theme);
Result get_themes(Theme_s **themes_list, int *theme_count);
Result single_install(Theme_s theme);
Result shuffle_install(Theme_s *themes_list, int theme_count);
Result theme_install(Entry_s theme);
Result no_bgm_install(Entry_s theme);
Result bgm_install(Entry_s theme);
Result shuffle_install(Entry_List_s themes);
void themes_check_installed(void * void_arg);
#endif

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -3,7 +3,7 @@ BasicInfo:
CompanyCode : "00"
ProductCode : "CTR-P-ANEM"
ContentType : Application
Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem
Logo : Homebrew # Nintendo / Licensed / Distributed / iQue / iQueForSystem
TitleInfo:
UniqueId : 0xAFEN
@@ -33,6 +33,9 @@ SystemControlInfo:
RemasterVersion: 7
StackSize: 0x40000
RomFs:
RootPath : romfs
# DO NOT EDIT BELOW HERE OR PROGRAMS WILL NOT LAUNCH (most likely)
AccessControlInfo:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

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

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
romfs/arrow_side.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
romfs/battery0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

BIN
romfs/battery1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

BIN
romfs/battery2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

BIN
romfs/battery3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
romfs/battery4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
romfs/battery5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
romfs/browse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

BIN
romfs/charging.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

BIN
romfs/download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

BIN
romfs/exit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

BIN
romfs/installed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

BIN
romfs/list.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

BIN
romfs/preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

BIN
romfs/select.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 658 B

BIN
romfs/shuffle_no_bgm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

BIN
romfs/sort.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

BIN
romfs/start.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

302
source/camera.c Normal file
View File

@@ -0,0 +1,302 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "camera.h"
#include "quirc/quirc.h"
#include "pp2d/pp2d/pp2d.h"
#include "draw.h"
#include "fs.h"
#include "loading.h"
#include "remote.h"
#include <archive.h>
#include <archive_entry.h>
/*
static u32 transfer_size;
static Handle event;
static struct quirc* context;
static u16 * camera_buf = NULL;
*/
void exit_qr(qr_data *data)
{
DEBUG("Exiting QR\n");
svcSignalEvent(data->cancel);
while(!data->finished)
svcSleepThread(1000000);
svcCloseHandle(data->cancel);
data->capturing = false;
free(data->camera_buffer);
free(data->texture_buffer);
quirc_destroy(data->context);
}
void capture_cam_thread(void *arg)
{
qr_data *data = (qr_data *) arg;
Handle events[3] = {0};
events[0] = data->cancel;
u32 transferUnit;
u16 *buffer = calloc(1, 400 * 240 * sizeof(u16));
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(&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(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), (s16) transferUnit);
CAMU_StartCapture(PORT_CAM1);
bool cancel = false;
while (!cancel)
{
s32 index = 0;
svcWaitSynchronizationN(&index, events, 3, false, U64_MAX);
switch(index) {
case 0:
DEBUG("Cancel event received\n");
cancel = true;
break;
case 1:
svcCloseHandle(events[1]);
events[1] = 0;
svcWaitSynchronization(data->mutex, U64_MAX);
memcpy(data->camera_buffer, buffer, 400 * 240 * sizeof(u16));
GSPGPU_FlushDataCache(data->camera_buffer, 400 * 240 * sizeof(u16));
svcReleaseMutex(data->mutex);
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit);
break;
case 2:
svcCloseHandle(events[1]);
events[1] = 0;
CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), 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);
camExit();
free(buffer);
for(int i = 0; i < 3; i++) {
if(events[i] != 0) {
svcCloseHandle(events[i]);
events[i] = 0;
}
}
svcCloseHandle(data->mutex);
data->finished = true;
}
bool start_capture_cam(qr_data *data)
{
data->mutex = 0;
data->cancel = 0;
svcCreateEvent(&data->cancel, RESET_STICKY);
svcCreateMutex(&data->mutex, false);
if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL)
return false;
return true;
}
void update_qr(qr_data *data)
{
hidScanInput();
if (hidKeysDown() & (KEY_R | KEY_B | KEY_TOUCH)) {
exit_qr(data);
return;
}
if (!data->capturing) {
if(start_capture_cam(data))
data->capturing = true;
else {
exit_qr(data);
return;
}
}
if (data->finished) {
exit_qr(data);
return;
}
for (int i = 0; i < 240 * 400; i++)
{
data->texture_buffer[i] = RGB565_TO_ABGR8(data->camera_buffer[i]);
}
draw_base_interface();
pp2d_free_texture(TEXTURE_QR);
pp2d_load_texture_memory(TEXTURE_QR, data->texture_buffer, 400, 240);
pp2d_draw_texture(TEXTURE_QR, 0, 0);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, RGBA8(255, 255, 255, 255), "Press \uE005 To Quit");
pp2d_end_draw();
int w;
int h;
u8 *image = (u8*) quirc_begin(data->context, &w, &h);
svcWaitSynchronization(data->mutex, U64_MAX);
for (ssize_t x = 0; x < w; x++) {
for (ssize_t y = 0; y < h; y++) {
u16 px = data->camera_buffer[y * 400 + x];
image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
}
}
svcReleaseMutex(data->mutex);
quirc_end(data->context);
if(quirc_count(data->context) > 0)
{
struct quirc_code code;
struct quirc_data scan_data;
quirc_extract(data->context, 0, &code);
if (!quirc_decode(&code, &scan_data))
{
exit_qr(data);
draw_install(INSTALL_DOWNLOAD);
char * zip_buf = NULL;
char * filename = NULL;
u32 zip_size = http_get((char*)scan_data.payload, &filename, &zip_buf, INSTALL_DOWNLOAD);
if(zip_size != 0)
{
draw_install(INSTALL_CHECKING_DOWNLOAD);
struct archive *a = archive_read_new();
archive_read_support_format_zip(a);
int r = archive_read_open_memory(a, zip_buf, zip_size);
archive_read_free(a);
if(r == ARCHIVE_OK)
{
EntryMode mode = MODE_AMOUNT;
char * buf = NULL;
do {
if(zip_memory_to_buf("body_LZ.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_THEMES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splash.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splashbottom.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
}
while(false);
free(buf);
buf = NULL;
if(mode != MODE_AMOUNT)
{
char path_to_file[0x107] = {0};
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
data->success = true;
}
else
{
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING);
}
}
else
{
throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING);
}
}
else
{
throw_error("Download failed.", ERROR_LEVEL_WARNING);
}
free(filename);
free(zip_buf);
}
}
}
bool init_qr(void)
{
qr_data *data = calloc(1, sizeof(qr_data));
data->capturing = false;
data->finished = false;
data->context = quirc_new();
quirc_resize(data->context, 400, 240);
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
data->texture_buffer = calloc(1, 400 * 240 * sizeof(u32));
while (!data->finished) update_qr(data);
bool success = data->success;
free(data);
return success;
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,19 +25,13 @@
*/
#include "draw.h"
#include "unicode.h"
#include "colors.h"
#include "pp2d/pp2d/pp2d.h"
#include <time.h>
enum Colors {
COLOR_BACKGROUND = ABGR8(255, 32, 28, 35), //silver-y black
COLOR_ACCENT = RGBA8(55, 122, 168, 255),
COLOR_WHITE = RGBA8(255, 255, 255, 255),
COLOR_CURSOR = RGBA8(200, 200, 200, 255),
COLOR_BLACK = RGBA8(0, 0, 0, 255),
};
void init_screens(void)
{
pp2d_init();
@@ -46,7 +40,25 @@ void init_screens(void)
pp2d_set_screen_color(GFX_BOTTOM, COLOR_BACKGROUND);
pp2d_load_texture_png(TEXTURE_ARROW, "romfs:/arrow.png");
pp2d_load_texture_png(TEXTURE_ARROW_SIDE, "romfs:/arrow_side.png");
pp2d_load_texture_png(TEXTURE_SHUFFLE, "romfs:/shuffle.png");
pp2d_load_texture_png(TEXTURE_SHUFFLE_NO_BGM, "romfs:/shuffle_no_bgm.png");
pp2d_load_texture_png(TEXTURE_INSTALLED, "romfs:/installed.png");
pp2d_load_texture_png(TEXTURE_PREVIEW_ICON, "romfs:/preview.png");
pp2d_load_texture_png(TEXTURE_SORT, "romfs:/sort.png");
pp2d_load_texture_png(TEXTURE_DOWNLOAD, "romfs:/download.png");
pp2d_load_texture_png(TEXTURE_BROWSE, "romfs:/browse.png");
pp2d_load_texture_png(TEXTURE_LIST, "romfs:/list.png");
pp2d_load_texture_png(TEXTURE_EXIT, "romfs:/exit.png");
pp2d_load_texture_png(TEXTURE_BATTERY_0, "romfs:/battery0.png");
pp2d_load_texture_png(TEXTURE_BATTERY_1, "romfs:/battery1.png");
pp2d_load_texture_png(TEXTURE_BATTERY_2, "romfs:/battery2.png");
pp2d_load_texture_png(TEXTURE_BATTERY_3, "romfs:/battery3.png");
pp2d_load_texture_png(TEXTURE_BATTERY_4, "romfs:/battery4.png");
pp2d_load_texture_png(TEXTURE_BATTERY_5, "romfs:/battery5.png");
pp2d_load_texture_png(TEXTURE_BATTERY_CHARGE, "romfs:/charging.png");
pp2d_load_texture_png(TEXTURE_SELECT_BUTTON, "romfs:/select.png");
pp2d_load_texture_png(TEXTURE_START_BUTTON, "romfs:/start.png");
}
void exit_screens(void)
@@ -54,208 +66,468 @@ void exit_screens(void)
pp2d_exit();
}
static Result MCUHWC_GetBatteryLevel(u8 *out) // Code taken from daedreth's fork of lpp-3ds
{
#define TRY(expr) if(R_FAILED(res = (expr))) { svcCloseHandle(mcuhwcHandle); return res; }
Result res;
Handle mcuhwcHandle;
TRY(srvGetServiceHandle(&mcuhwcHandle, "mcu::HWC"));
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = 0x50000;
TRY(svcSendSyncRequest(mcuhwcHandle));
*out = (u8) cmdbuf[2];
svcCloseHandle(mcuhwcHandle);
return cmdbuf[1];
#undef TRY
}
static int theme_vertical_scroll = 0;
static int splash_vertical_scroll = 0;
void draw_base_interface(void)
{
pp2d_begin_draw(GFX_TOP);
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
pp2d_draw_rectangle(0, 0, 400, 23, COLOR_ACCENT);
time_t t = time(NULL);
struct tm tm = *localtime(&t);
pp2d_draw_textf(7, 2, 0.6, 0.6, COLOR_WHITE, "%.2i", tm.tm_hour);
pp2d_draw_text(28, 2, 0.6, 0.6, COLOR_WHITE, (tm.tm_sec % 2 == 1) ? ":" : " ");
pp2d_draw_text(28, 1, 0.6, 0.6, COLOR_WHITE, (tm.tm_sec % 2 == 1) ? ":" : " ");
pp2d_draw_textf(34, 2, 0.6, 0.6, COLOR_WHITE, "%.2i", tm.tm_min);
u8 battery_val = 0;
MCUHWC_GetBatteryLevel(&battery_val);
pp2d_draw_textf(350, 2, 0.6, 0.6, COLOR_WHITE, "%i%%", battery_val);
#ifndef CITRA_MODE
u8 battery_charging = 0;
PTMU_GetBatteryChargeState(&battery_charging);
u8 battery_status = 0;
PTMU_GetBatteryLevel(&battery_status);
pp2d_draw_texture(TEXTURE_BATTERY_0 + battery_status, 357, 2);
pp2d_draw_on(GFX_BOTTOM);
if(battery_charging)
pp2d_draw_texture(TEXTURE_BATTERY_CHARGE, 357, 2);
#endif
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_rectangle(0, 0, 320, 24, COLOR_ACCENT);
pp2d_draw_rectangle(0, 216, 320, 24, COLOR_ACCENT);
pp2d_draw_on(GFX_TOP);
pp2d_draw_text(7, 219, 0.6, 0.6, COLOR_WHITE, VERSION);
pp2d_draw_on(GFX_TOP, GFX_LEFT);
}
void draw_theme_install(bool shuffle_install)
static void draw_text_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const char* text)
{
char * _text = strdup(text);
float prevY = y;
int offset = 0;
while(true)
{
char *nline = strchr(_text+offset, '\n');
int nlinepos = 0;
if(nline != NULL)
{
nlinepos = nline-_text;
_text[nlinepos] = '\0';
}
pp2d_draw_text_center(target, prevY, scaleX, scaleY, color, _text+offset);
if(nline == NULL) break;
else
{
prevY += pp2d_get_text_height(_text+offset, scaleX, scaleY);
_text[nlinepos] = '\n';
offset = nlinepos+1;
}
}
free(_text);
}
void throw_error(char* error, ErrorLevel level)
{
switch(level)
{
case ERROR_LEVEL_ERROR:
while(aptMainLoop())
{
hidScanInput();
u32 kDown = hidKeysDown();
draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.6, 0.6, COLOR_RED, error);
pp2d_draw_wtext_center(GFX_TOP, 150, 0.6, 0.6, COLOR_WHITE, L"Press \uE000 to shut down.");
pp2d_end_draw();
if(kDown & KEY_A) break;
}
break;
case ERROR_LEVEL_WARNING:
while(aptMainLoop())
{
hidScanInput();
u32 kDown = hidKeysDown();
draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.6, 0.6, COLOR_YELLOW, error);
pp2d_draw_wtext_center(GFX_TOP, 150, 0.6, 0.6, COLOR_WHITE, L"Press \uE000 to continue.");
pp2d_end_draw();
if(kDown & KEY_A) break;
}
break;
}
}
bool draw_confirm(const char* conf_msg, Entry_List_s* list)
{
while(aptMainLoop())
{
Instructions_s instructions = {0};
draw_interface(list, instructions);
pp2d_draw_on(GFX_TOP, GFX_LEFT);
draw_text_center(GFX_TOP, BUTTONS_Y_LINE_1, 0.7, 0.7, COLOR_YELLOW, conf_msg);
pp2d_draw_wtext_center(GFX_TOP, BUTTONS_Y_LINE_3, 0.6, 0.6, COLOR_WHITE, L"\uE000 Yes \uE001 No");
pp2d_end_draw();
hidScanInput();
u32 kDown = hidKeysDown();
if(kDown & KEY_A) return true;
if(kDown & KEY_B) return false;
}
return false;
}
void draw_preview(ssize_t previewID, int preview_offset)
{
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
pp2d_draw_texture_part(previewID, 0, 0, preview_offset, 0, 400, 240);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_texture_part(previewID, 0, 0, 40 + preview_offset, 240, 320, 240);
}
static void draw_install_handler(InstallType type)
{
switch(type)
{
case INSTALL_LOADING_THEMES:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Loading themes, please wait...");
break;
case INSTALL_LOADING_SPLASHES:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Loading splashes, please wait...");
break;
case INSTALL_LOADING_ICONS:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Loading icons, please wait...");
break;
case INSTALL_LOADING_REMOTE_THEMES:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading theme list, please wait...");
break;
case INSTALL_LOADING_REMOTE_SPLASHES:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading splash list, please wait...");
break;
case INSTALL_LOADING_REMOTE_PREVIEW:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading preview, please wait...");
break;
case INSTALL_LOADING_REMOTE_BGM:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading BGM, please wait...");
break;
case INSTALL_SINGLE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing a single theme...");
break;
case INSTALL_SHUFFLE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing shuffle themes...");
break;
case INSTALL_BGM:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing BGM-only theme...");
break;
case INSTALL_DOWNLOAD:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading...");
break;
case INSTALL_CHECKING_DOWNLOAD:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Checking downloaded file...");
break;
case INSTALL_SPLASH:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing a splash...");
break;
case INSTALL_SPLASH_DELETE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Deleting installed splash...");
break;
case INSTALL_ENTRY_DELETE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Deleting from SD...");
break;
case INSTALL_NO_BGM:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing theme without BGM...");
break;
default:
break;
}
}
void draw_install(InstallType type)
{
draw_base_interface();
pp2d_draw_textf(20, 30, 0.7, 0.7, COLOR_WHITE, "Installing a %s theme...", !shuffle_install ? "single" : "shuffle");
draw_install_handler(type);
pp2d_end_draw();
}
void draw_theme_interface(Theme_s * themes_list, int theme_count, int selected_theme, bool preview_mode)
void draw_loading_bar(u32 current, u32 max, InstallType type)
{
if (themes_list == NULL)
{
pp2d_begin_draw(GFX_TOP);
pp2d_draw_text_center(GFX_TOP, 100, 1, 1, COLOR_WHITE, "FAILURE");
draw_base_interface();
draw_install_handler(type);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
double percent = 100*((double)current/(double)max);
u32 width = (u32)percent;
width *= 2;
pp2d_draw_rectangle(60-1, 110-1, 200+2, 20+2, COLOR_CURSOR);
pp2d_draw_rectangle(60, 110, width, 20, COLOR_ACCENT);
pp2d_end_draw();
}
static void draw_instructions(Instructions_s instructions)
{
pp2d_draw_on(GFX_TOP, GFX_LEFT);
if(instructions.info_line != NULL)
pp2d_draw_wtext_center(GFX_TOP, BUTTONS_Y_INFO, 0.55, 0.55, instructions.info_line_color, instructions.info_line);
const int y_lines[BUTTONS_INFO_LINES-1] = {
BUTTONS_Y_LINE_1,
BUTTONS_Y_LINE_2,
BUTTONS_Y_LINE_3,
};
for(int i = 0; i < BUTTONS_INFO_LINES-1; i++)
{
if(instructions.instructions[i][0] != NULL)
pp2d_draw_wtext(BUTTONS_X_LEFT, y_lines[i], 0.6, 0.6, COLOR_WHITE, instructions.instructions[i][0]);
if(instructions.instructions[i][1] != NULL)
pp2d_draw_wtext(BUTTONS_X_RIGHT, y_lines[i], 0.6, 0.6, COLOR_WHITE, instructions.instructions[i][1]);
}
const wchar_t * start_line = instructions.instructions[BUTTONS_INFO_LINES-1][0];
if(start_line != NULL)
{
pp2d_draw_texture(TEXTURE_START_BUTTON, BUTTONS_X_LEFT-10, BUTTONS_Y_LINE_4 + 3);
pp2d_draw_wtext(BUTTONS_X_LEFT+26, BUTTONS_Y_LINE_4, 0.6, 0.6, COLOR_WHITE, start_line);
}
const wchar_t * select_line = instructions.instructions[BUTTONS_INFO_LINES-1][1];
if(select_line != NULL)
{
pp2d_draw_texture(TEXTURE_SELECT_BUTTON, BUTTONS_X_RIGHT-10, BUTTONS_Y_LINE_4 + 3);
pp2d_draw_wtext(BUTTONS_X_RIGHT+26, BUTTONS_Y_LINE_4, 0.6, 0.6, COLOR_WHITE, select_line);
}
}
static void draw_entry_info(Entry_s * entry)
{
float wrap = 363;
wchar_t author[0x41] = {0};
utf16_to_utf32((u32*)author, entry->author, 0x40);
pp2d_draw_text(20, 35, 0.5, 0.5, COLOR_WHITE, "By ");
pp2d_draw_wtext_wrap(40, 35, 0.5, 0.5, COLOR_WHITE, wrap, author);
wchar_t title[0x41] = {0};
utf16_to_utf32((u32*)title, entry->name, 0x40);
pp2d_draw_wtext_wrap(20, 50, 0.7, 0.7, COLOR_WHITE, wrap, title);
int width = (int)pp2d_get_wtext_width(title, 0.7, 0.7);
int height = (int)pp2d_get_wtext_height(title, 0.7, 0.7);
int count = ((width - (width % (int)wrap))/wrap) + 1;
wchar_t description[0x81] = {0};
utf16_to_utf32((u32*)description, entry->desc, 0x80);
pp2d_draw_wtext_wrap(20, 50+count*height, 0.5, 0.5, COLOR_WHITE, wrap, description);
}
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
{
draw_base_interface();
EntryMode current_mode = list->mode;
const char* mode_string[MODE_AMOUNT] = {
"ThemePlaza Theme mode",
"ThemePlaza Splash mode",
};
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, mode_string[current_mode]);
draw_instructions(instructions);
int selected_entry = list->selected_entry;
Entry_s * current_entry = &list->entries[selected_entry];
draw_entry_info(current_entry);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_text(7, 3, 0.6, 0.6, COLOR_WHITE, "Search...");
pp2d_draw_texture_blend(TEXTURE_LIST, 320-96, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_EXIT, 320-72, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_PREVIEW_ICON, 320-48, 0, COLOR_WHITE);
pp2d_draw_textf(320-24+2.5, -3, 1, 1, COLOR_WHITE, "%c", mode_string[!list->mode][11]);
pp2d_draw_texture(TEXTURE_ARROW_SIDE, 3, 114);
pp2d_draw_texture_flip(TEXTURE_ARROW_SIDE, 308, 114, HORIZONTAL);
for(int i = list->scroll; i < (list->entries_loaded + list->scroll); i++)
{
if(i >= list->entries_count) break;
current_entry = &list->entries[i];
wchar_t name[0x41] = {0};
utf16_to_utf32((u32*)name, current_entry->name, 0x40);
int vertical_offset = 0;
int horizontal_offset = i - list->scroll;
vertical_offset = horizontal_offset/list->entries_per_screen_h;
horizontal_offset %= list->entries_per_screen_h;
horizontal_offset *= list->entry_size;
vertical_offset *= list->entry_size;
vertical_offset += 24;
horizontal_offset += 16;
if(!current_entry->placeholder_color)
{
ssize_t id = list->icons_ids[i];
pp2d_draw_texture(id, horizontal_offset, vertical_offset);
}
else
pp2d_draw_rectangle(horizontal_offset, vertical_offset, list->entry_size, list->entry_size, current_entry->placeholder_color);
if(i == selected_entry)
{
unsigned int border_width = 3;
pp2d_draw_rectangle(horizontal_offset, vertical_offset, border_width, list->entry_size, COLOR_CURSOR);
pp2d_draw_rectangle(horizontal_offset, vertical_offset, list->entry_size, border_width, COLOR_CURSOR);
pp2d_draw_rectangle(horizontal_offset, vertical_offset+list->entry_size-border_width, list->entry_size, border_width, COLOR_CURSOR);
pp2d_draw_rectangle(horizontal_offset+list->entry_size-border_width, vertical_offset, border_width, list->entry_size, COLOR_CURSOR);
}
}
char entries_count_str[0x20] = {0};
sprintf(entries_count_str, "/%" JSON_INTEGER_FORMAT, list->tp_page_count);
float x = 316;
x -= pp2d_get_text_width(entries_count_str, 0.6, 0.6);
pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, entries_count_str);
char selected_entry_str[0x20] = {0};
sprintf(selected_entry_str, "%" JSON_INTEGER_FORMAT, list->tp_current_page);
x -= pp2d_get_text_width(selected_entry_str, 0.6, 0.6);
pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, selected_entry_str);
pp2d_draw_text(176, 219, 0.6, 0.6, COLOR_WHITE, "Page:");
}
void draw_interface(Entry_List_s* list, Instructions_s instructions)
{
draw_base_interface();
EntryMode current_mode = list->mode;
const char* mode_string[MODE_AMOUNT] = {
"Theme mode",
"Splash mode",
};
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, mode_string[current_mode]);
if(list->entries == NULL)
{
const char* mode_found_string[MODE_AMOUNT] = {
"No themes found",
"No splashes found",
};
pp2d_draw_text_center(GFX_TOP, 80, 0.7, 0.7, COLOR_YELLOW, mode_found_string[current_mode]);
pp2d_draw_text_center(GFX_TOP, 110, 0.7, 0.7, COLOR_YELLOW, "Press \uE005 to download from QR");
const char* mode_switch_string[MODE_AMOUNT] = {
"Or \uE004 to switch to splashes",
"Or \uE004 to switch to themes",
};
pp2d_draw_text_center(GFX_TOP, 140, 0.7, 0.7, COLOR_YELLOW, mode_switch_string[current_mode]);
pp2d_draw_text_center(GFX_TOP, 170, 0.7, 0.7, COLOR_YELLOW, "Or to quit");
pp2d_texture_select(TEXTURE_START_BUTTON, 162, 173);
pp2d_texture_blend(COLOR_YELLOW);
pp2d_texture_scale(1.25, 1.4);
pp2d_texture_draw();
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_texture_blend(TEXTURE_SORT, 320-144, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_DOWNLOAD, 320-120, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_BROWSE, 320-96, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_EXIT, 320-72, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_PREVIEW_ICON, 320-48, 0, COLOR_WHITE);
pp2d_draw_textf(320-24+2.5, -3, 1, 1, COLOR_WHITE, "%c", mode_string[!list->mode][0]);
return;
}
Theme_s current_theme = themes_list[selected_theme];
draw_instructions(instructions);
if (preview_mode)
int selected_entry = list->selected_entry;
Entry_s * current_entry = &list->entries[selected_entry];
draw_entry_info(current_entry);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
switch(current_mode)
{
if (current_theme.has_preview)
{
pp2d_begin_draw(GFX_TOP);
pp2d_draw_texture_part(TEXTURE_PREVIEW, 0, 0, 6, 0, 400, 240);
pp2d_draw_on(GFX_BOTTOM);
pp2d_draw_texture_part(TEXTURE_PREVIEW, 0, 0, 46, 240, 320, 240);
}
}
else
{
draw_base_interface();
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, "Theme mode");
wchar_t title[0x40] = {0};
utf16_to_utf32((u32*)title, current_theme.name, 0x40);
pp2d_draw_wtext(20, 30, 0.7, 0.7, COLOR_WHITE, title);
wchar_t author[0x40] = {0};
utf16_to_utf32((u32*)author, current_theme.author, 0x40);
pp2d_draw_text(20, 50, 0.5, 0.5, COLOR_WHITE, "By: ");
pp2d_draw_wtext(44, 50, 0.5, 0.5, COLOR_WHITE, author);
wchar_t description[0x80] = {0};
utf16_to_utf32((u32*)description, current_theme.desc, 0x80);
pp2d_draw_wtext(20, 65, 0.5, 0.5, COLOR_WHITE, description);
pp2d_draw_wtext_center(GFX_TOP, 150, 0.7, 0.7, COLOR_WHITE, L"\uE000 Install Theme \uE004 Switch to Splashes");
pp2d_draw_wtext_center(GFX_TOP, 180, 0.7, 0.7, COLOR_WHITE, L"\uE001 Queue Shuffle \uE046 Install shuffle");
pp2d_draw_wtext_center(GFX_TOP, 210, 0.7, 0.7, COLOR_WHITE, L"\uE003 Preview Theme");
pp2d_draw_on(GFX_BOTTOM);
// Scroll the menu up or down if the selected theme is out of its bounds
//----------------------------------------------------------------
for (int i = 0; i < theme_count; i++) {
if (theme_count <= THEMES_PER_SCREEN)
case MODE_THEMES:
pp2d_draw_textf(7, 3, 0.6, 0.6, list->shuffle_count <= 10 && list->shuffle_count >= 2 ? COLOR_WHITE : COLOR_RED, "Shuffle: %i/10", list->shuffle_count);
break;
default:
break;
if (theme_vertical_scroll > selected_theme)
theme_vertical_scroll--;
if ((i < selected_theme) && \
((selected_theme - theme_vertical_scroll) >= THEMES_PER_SCREEN) && \
(theme_vertical_scroll != ( - THEMES_PER_SCREEN)))
theme_vertical_scroll++;
}
//----------------------------------------------------------------
pp2d_draw_texture_blend(TEXTURE_SORT, 320-144, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_DOWNLOAD, 320-120, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_BROWSE, 320-96, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_EXIT, 320-72, 0, COLOR_WHITE);
pp2d_draw_texture_blend(TEXTURE_PREVIEW_ICON, 320-48, 0, COLOR_WHITE);
pp2d_draw_textf(320-24+2.5, -3, 1, 1, COLOR_WHITE, "%c", mode_string[!list->mode][0]);
// Show arrows if there are themes out of bounds
//----------------------------------------------------------------
if (theme_vertical_scroll > 0)
pp2d_draw_texture(TEXTURE_ARROW, 155, 6);
if (theme_vertical_scroll + THEMES_PER_SCREEN < theme_count)
pp2d_draw_texture_flip(TEXTURE_ARROW, 155, 224, VERTICAL);
if(list->scroll > 0)
pp2d_draw_texture(TEXTURE_ARROW, 152, 4);
if(list->scroll + list->entries_loaded < list->entries_count)
pp2d_draw_texture_flip(TEXTURE_ARROW, 152, 220, VERTICAL);
for (int i = theme_vertical_scroll; i < (THEMES_PER_SCREEN + theme_vertical_scroll); i++)
for(int i = list->scroll; i < (list->entries_loaded + list->scroll); i++)
{
if (i >= theme_count)
break;
if(i >= list->entries_count) break;
current_theme = themes_list[i];
wchar_t name[0x80] = {0};
utf16_to_utf32((u32*)name, current_theme.name, 0x80);
current_entry = &list->entries[i];
wchar_t name[0x41] = {0};
utf16_to_utf32((u32*)name, current_entry->name, 0x40);
int vertical_offset = i - list->scroll;
int horizontal_offset = 0;
horizontal_offset *= list->entry_size;
vertical_offset *= list->entry_size;
vertical_offset += 24;
int vertical_offset = 48 * (i-theme_vertical_scroll);
u32 font_color = COLOR_WHITE;
if (i == selected_theme)
if(i == selected_entry)
{
font_color = COLOR_BLACK;
pp2d_draw_rectangle(0, 24 + vertical_offset, 320, 48, COLOR_CURSOR);
}
pp2d_draw_wtext(54, 40 + vertical_offset, 0.55, 0.55, font_color, name);
if (current_theme.has_icon)
pp2d_draw_texture(current_theme.icon_id, 0, 24 + vertical_offset);
if (current_theme.in_shuffle)
pp2d_draw_texture_blend(TEXTURE_SHUFFLE, 280, 32 + vertical_offset, font_color);
}
pp2d_draw_rectangle(0, vertical_offset, 320, list->entry_size, COLOR_CURSOR);
}
pp2d_end_draw();
}
void draw_splash_interface(Splash_s *splashes_list, int splash_count, int selected_splash, bool preview_mode)
{
if (splashes_list == NULL)
{
pp2d_begin_draw(GFX_TOP);
pp2d_draw_text_center(GFX_TOP, 100, 1, 1, COLOR_WHITE, "FAILURE");
pp2d_end_draw();
return;
}
Splash_s current_splash = splashes_list[selected_splash];
if (preview_mode)
{
// TODO: Splash Previews
} else {
draw_base_interface();
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, "Splash mode");
pp2d_draw_wtext_center(GFX_TOP, 210, 0.7, 0.7, COLOR_WHITE, L"\uE000 Install Splash \uE004 Switch to Themes");
pp2d_draw_on(GFX_BOTTOM);
for (int i = 0; i < splash_count; i++) {
if (splash_count <= THEMES_PER_SCREEN)
break;
if (splash_vertical_scroll > selected_splash)
splash_vertical_scroll--;
if ((i < selected_splash) && \
((selected_splash - splash_vertical_scroll) >= THEMES_PER_SCREEN) && \
(splash_vertical_scroll != ( - THEMES_PER_SCREEN)))
splash_vertical_scroll++;
}
if (splash_vertical_scroll > 0)
pp2d_draw_texture(TEXTURE_ARROW, 155, 6);
if (splash_vertical_scroll + THEMES_PER_SCREEN < splash_count)
pp2d_draw_texture_flip(TEXTURE_ARROW, 155, 224, VERTICAL);
for (int i = splash_vertical_scroll; i < (THEMES_PER_SCREEN + splash_vertical_scroll); i++)
{
if (i >= splash_count)
break;
current_splash = splashes_list[i];
wchar_t name[0x106] = {0};
utf16_to_utf32((u32*)name, current_splash.name, 0x106);
int vertical_offset = 48 * (i-splash_vertical_scroll);
u32 font_color = COLOR_WHITE;
if (i == selected_splash)
{
font_color = COLOR_BLACK;
pp2d_draw_rectangle(0, 24 + vertical_offset, 320, 48, COLOR_CURSOR);
}
pp2d_draw_wtext(15, 40 + vertical_offset, 0.55, 0.55, font_color, name);
}
}
pp2d_end_draw();
pp2d_draw_wtext(list->entry_size+6, vertical_offset + 16, 0.55, 0.55, font_color, name);
if(current_entry->no_bgm_shuffle)
pp2d_draw_texture_blend(TEXTURE_SHUFFLE_NO_BGM, 320-24-4, vertical_offset, font_color);
else if(current_entry->in_shuffle)
pp2d_draw_texture_blend(TEXTURE_SHUFFLE, 320-24-4, vertical_offset, font_color);
if(current_entry->installed)
pp2d_draw_texture_blend(TEXTURE_INSTALLED, 320-24-4, vertical_offset + 22, font_color);
if(!current_entry->placeholder_color)
{
ssize_t id = 0;
if(list->entries_count > list->entries_loaded*ICONS_OFFSET_AMOUNT)
id = list->icons_ids[ICONS_VISIBLE*list->entries_loaded + (i - list->scroll)];
else
id = list->icons_ids[i];
pp2d_draw_texture(id, horizontal_offset, vertical_offset);
}
else
pp2d_draw_rectangle(horizontal_offset, vertical_offset, list->entry_size, list->entry_size, current_entry->placeholder_color);
}
char entries_count_str[0x20] = {0};
sprintf(entries_count_str, "/%i", list->entries_count);
float x = 316;
x -= pp2d_get_text_width(entries_count_str, 0.6, 0.6);
pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, entries_count_str);
char selected_entry_str[0x20] = {0};
sprintf(selected_entry_str, "%i", selected_entry + 1);
x -= pp2d_get_text_width(selected_entry_str, 0.6, 0.6);
pp2d_draw_text(x, 219, 0.6, 0.6, COLOR_WHITE, selected_entry_str);
pp2d_draw_text(176, 219, 0.6, 0.6, COLOR_WHITE, list->entries_count < 1000 ? "Selected:" : "Sel.:");
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,21 +29,17 @@
#include "fs.h"
#include "unicode.h"
#include "minizip/unzip.h"
int filename_compare(__attribute__((unused)) unzFile file, const char *current_filename, const char *filename)
{
return strcasecmp(current_filename, filename);
}
#include <archive.h>
#include <archive_entry.h>
Result open_archives(void)
{
romfsInit();
u8 regionCode;
u32 archive1;
u32 archive2;
Result retValue;
Result res = 0;
FS_Path home;
FS_Path theme;
@@ -51,6 +47,10 @@ Result open_archives(void)
CFGU_SecureInfoGetRegion(&regionCode);
switch(regionCode)
{
case 0:
archive1 = 0x000002cc;
archive2 = 0x00000082;
break;
case 1:
archive1 = 0x000002cd;
archive2 = 0x0000008f;
@@ -59,118 +59,145 @@ Result open_archives(void)
archive1 = 0x000002ce;
archive2 = 0x00000098;
break;
case 3:
archive1 = 0x000002cc;
archive2 = 0x00000082;
break;
default:
archive1 = 0x00;
archive2 = 0x00;
}
retValue = FSUSER_OpenArchive(&ArchiveSD, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""));
if(R_FAILED(retValue)) return retValue;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveSD, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) return res;
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Themes"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Splashes"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE "/cache"), FS_ATTRIBUTE_DIRECTORY);
u32 homeMenuPath[3] = {MEDIATYPE_SD, archive2, 0};
home.type = PATH_BINARY;
home.size = 0xC;
home.data = homeMenuPath;
retValue = FSUSER_OpenArchive(&ArchiveHomeExt, ARCHIVE_EXTDATA, home);
if(R_FAILED(retValue)) return retValue;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveHomeExt, ARCHIVE_EXTDATA, home))) return res;
u32 themePath[3] = {MEDIATYPE_SD, archive1, 0};
theme.type = PATH_BINARY;
theme.size = 0xC;
theme.data = themePath;
retValue = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme);
if(R_FAILED(retValue)) return retValue;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme))) return res;
Handle test_handle;
if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ, 0))) return res;
FSFILE_Close(test_handle);
romfsInit();
return 0;
}
Result close_archives(void)
{
Result retValue;
Result 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;
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;
return 0;
}
u64 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
{
Handle file;
Result res = FSUSER_OpenFile(&file, archive, path, FS_OPEN_READ, 0);
if (R_FAILED(res)) return 0;
Result res = 0;
if (R_FAILED(res = FSUSER_OpenFile(&file, archive, path, FS_OPEN_READ, 0))) return 0;
u64 size;
FSFILE_GetSize(file, &size);
*buf = malloc(size);
if(size != 0)
{
*buf = calloc(1, size);
FSFILE_Read(file, NULL, 0, *buf, size);
}
FSFILE_Close(file);
return size;
return (u32)size;
}
static u32 zip_to_buf(struct archive *a, 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(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 zip_to_buf(a, file_name, buf);
}
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf)
{
ssize_t len = strulen(zip_path, 0x106);
char *path = calloc(sizeof(char), len*sizeof(u16));
utf16_to_utf8((u8*)path, zip_path, len*sizeof(u16));
u8 *path = calloc(sizeof(u8), len * 4);
utf16_to_utf8(path, zip_path, len * 4);
struct archive *a = archive_read_new();
archive_read_support_format_zip(a);
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)
int r = archive_read_open_filename(a, path, 0x4000);
if(r != ARCHIVE_OK)
{
unz_file_info *file_info = malloc(sizeof(unz_file_info));
unzGetCurrentFileInfo(zip_handle, file_info, NULL, 0, NULL, 0, NULL, 0);
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);
free(file_info);
return file_size;
} else {
free(path);
puts("fileziprip");
DEBUG("Invalid zip being opened\n");
return 0;
}
return zip_to_buf(a, file_name, buf);
}
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)
{
Handle handle;
u32 bytes = 0;
Result res = FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_ASCII, path), FS_OPEN_WRITE, 0);
if (R_FAILED(res)) return res;
res = FSFILE_Write(handle, &bytes, 0, buf, size, FS_WRITE_FLUSH);
if (R_FAILED(res)) return res;
res = FSFILE_Close(handle);
if (R_FAILED(res)) return res;
return bytes;
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;
}
void remake_file(char *path, FS_Archive archive, u32 size)
void remake_file(FS_Path path, FS_Archive archive, u32 size)
{
Handle handle;
if (R_SUCCEEDED(FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0)))
if (R_SUCCEEDED(FSUSER_OpenFile(&handle, archive, path, FS_OPEN_READ, 0)))
{
FSFILE_Close(handle);
FSUSER_DeleteFile(archive, fsMakePath(PATH_ASCII, path));
FSUSER_DeleteFile(archive, path);
}
FSUSER_CreateFile(archive, fsMakePath(PATH_ASCII, path), 0, size);
FSUSER_CreateFile(archive, path, 0, size);
}

521
source/loading.c Normal file
View File

@@ -0,0 +1,521 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "loading.h"
#include "pp2d/pp2d/pp2d.h"
#include "fs.h"
#include "unicode.h"
#include "music.h"
#include "draw.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(char * filename, 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);
}
}
static void parse_smdh(Entry_s * entry, const u16 * fallback_name)
{
char *info_buffer = NULL;
u64 size = load_data("/info.smdh", *entry, &info_buffer);
Icon_s * smdh = (Icon_s *)info_buffer;
if(!size)
{
free(info_buffer);
memcpy(entry->name, fallback_name, 0x80);
utf8_to_utf16(entry->desc, (u8*)"No description", 0x100);
utf8_to_utf16(entry->author, (u8*)"Unknown author", 0x80);
entry->placeholder_color = RGBA8(rand() % 255, rand() % 255, rand() % 255, 255);
return;
}
memcpy(entry->name, smdh->name, 0x40*sizeof(u16));
memcpy(entry->desc, smdh->desc, 0x80*sizeof(u16));
memcpy(entry->author, smdh->author, 0x40*sizeof(u16));
}
static void load_smdh_icon(Entry_s entry, const ssize_t textureID)
{
pp2d_free_texture(textureID);
char *info_buffer = NULL;
u64 size = load_data("/info.smdh", entry, &info_buffer);
if(!size) return;
Icon_s * smdh = (Icon_s *)info_buffer;
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
for(u32 x = 0; x < width; x++)
{
for(u32 y = 0; y < height; y++)
{
unsigned int dest_pixel = (x + y*width);
unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
image[dest_pixel] = RGB565_TO_ABGR8(smdh->big_icon[source_pixel]);
}
}
free(info_buffer);
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
free(image);
}
typedef int (*sort_comparator)(const void *, const void *);
static int compare_entries_by_name(const void * a, const void * b)
{
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->name, entry_b->name, 0x40*sizeof(u16));
}
static int compare_entries_by_author(const void * a, const void * b)
{
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->author, entry_b->author, 0x40*sizeof(u16));
}
static int compare_entries_by_filename(const void * a, const void * b)
{
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->path, entry_b->path, 0x106*sizeof(u16));
}
static void sort_list(Entry_List_s * list, sort_comparator compare_entries)
{
if(list->entries != NULL && list->entries != NULL)
qsort(list->entries, list->entries_count, sizeof(Entry_s), compare_entries); //alphabet sort
}
void sort_by_name(Entry_List_s * list)
{
sort_list(list, compare_entries_by_name);
list->current_sort = SORT_NAME;
}
void sort_by_author(Entry_List_s * list)
{
sort_list(list, compare_entries_by_author);
list->current_sort = SORT_AUTHOR;
}
void sort_by_filename(Entry_List_s * list)
{
sort_list(list, compare_entries_by_filename);
list->current_sort = SORT_PATH;
}
Result load_entries(const char * loading_path, Entry_List_s * list)
{
Handle dir_handle;
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, loading_path));
if(R_FAILED(res))
{
DEBUG("Failed to open folder: %s\n", loading_path);
return res;
}
u32 entries_read = 1;
while(entries_read)
{
FS_DirectoryEntry dir_entry = {0};
res = FSDIR_Read(dir_handle, &entries_read, 1, &dir_entry);
if(R_FAILED(res) || entries_read == 0)
break;
if(!(dir_entry.attributes & FS_ATTRIBUTE_DIRECTORY) && strcmp(dir_entry.shortExt, "ZIP"))
continue;
list->entries_count++;
Entry_s * new_list = realloc(list->entries, list->entries_count * sizeof(Entry_s));
if(new_list == NULL)
{
free(list->entries);
list->entries = NULL;
res = -1;
DEBUG("break\n");
break;
}
else
list->entries = new_list;
Entry_s * current_entry = &(list->entries[list->entries_count-1]);
memset(current_entry, 0, sizeof(Entry_s));
struacat(current_entry->path, loading_path);
strucat(current_entry->path, dir_entry.name);
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
parse_smdh(current_entry, dir_entry.name);
}
FSDIR_Close(dir_handle);
return res;
}
void load_icons_first(Entry_List_s * list, bool silent)
{
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;
}
list->icons_ids = calloc(endi-starti, sizeof(ssize_t));
ssize_t * icons_ids = list->icons_ids;
ssize_t id = list->texture_id_offset;
for(int i = starti; i < endi; i++, id++)
{
if(!silent)
draw_loading_bar(i - starti, endi-starti, INSTALL_LOADING_ICONS);
int offset = i;
if(offset < 0)
offset += list->entries_count;
if(offset >= list->entries_count)
offset -= list->entries_count;
Entry_s current_entry = list->entries[offset];
load_smdh_icon(current_entry, id);
icons_ids[i-starti] = id;
}
}
static void reverse(ssize_t a[], int sz) {
int i, j;
for (i = 0, j = sz; i < j; i++, j--) {
ssize_t tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
static void rotate(ssize_t 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)
{
for(int i = 0; i < list->entries_count; i++)
{
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 || list->selected_entry >= list->scroll + list->entries_loaded)
{
change = list->entries_loaded;
}
else if(list->selected_entry == list->previous_selected-list->entries_loaded || list->selected_entry < list->scroll)
{
change = -list->entries_loaded;
}
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 void load_icons(Entry_List_s * current_list)
{
if(current_list == NULL || current_list->entries == NULL)
return;
handle_scrolling(current_list);
if(current_list->entries_count <= current_list->entries_loaded*ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
return; // return if the list is one that doesnt need swapping, or if nothing changed
#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 ** entries = calloc(abs(delta), sizeof(Entry_s *));
ssize_t * ids = calloc(abs(delta), sizeof(ssize_t));
#define FIRST(arr) arr[0]
#define LAST(arr) arr[current_list->entries_loaded*ICONS_OFFSET_AMOUNT - 1]
ssize_t * icons_ids = current_list->icons_ids;
for(int i = starti; i != endi; i++, ctr++)
{
ssize_t id = 0;
int offset = i;
rotate(icons_ids, ICONS_OFFSET_AMOUNT*current_list->entries_loaded, -1*SIGN(delta));
if(delta > 0)
{
id = LAST(icons_ids);
offset += current_list->entries_loaded*ICONS_UNDER - delta;
}
else
{
id = FIRST(icons_ids);
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];
ids[ctr] = id;
}
#undef FIRST
#undef LAST
#undef SIGN
svcSleepThread(1e6);
for(int i = 0; i < abs(delta); i++)
{
Entry_s current_entry = *entries[i];
ssize_t id = ids[i];
load_smdh_icon(current_entry, id);
}
free(entries);
free(ids);
current_list->previous_scroll = current_list->scroll;
}
void load_icons_thread(void * void_arg)
{
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
Handle update_request = *(Handle *)arg->thread_arg[1];
do
{
svcWaitSynchronization(update_request, U64_MAX);
svcClearEvent(update_request);
volatile Entry_List_s * current_list = *(volatile Entry_List_s **)arg->thread_arg[0];
load_icons((Entry_List_s *)current_list);
}
while(arg->run_thread);
}
static u16 previous_path_preview[0x106] = {0};
bool load_preview(Entry_List_s list, int * preview_offset)
{
if(list.entries == NULL) return false;
Entry_s entry = list.entries[list.selected_entry];
if(!memcmp(&previous_path_preview, &entry.path, 0x106*sizeof(u16))) return true;
char *preview_buffer = NULL;
u64 size = load_data("/preview.png", entry, &preview_buffer);
if(!size)
{
free(preview_buffer);
throw_error("No preview found.", ERROR_LEVEL_WARNING);
return false;
}
bool ret = false;
u8 * image = NULL;
unsigned int width = 0, height = 0;
if((lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size)) == 0) // no error
{
for(u32 i = 0; i < width; i++)
{
for(u32 j = 0; j < height; j++)
{
u32* pixel = (u32*)(image + (i + j*width) * 4);
*pixel = __builtin_bswap32(*pixel); //swap from RGBA to ABGR, needed for pp2d
}
}
// mark the new preview as loaded for optimisation
memcpy(&previous_path_preview, &entry.path, 0x106*sizeof(u16));
// free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_PREVIEW);
pp2d_load_texture_memory(TEXTURE_PREVIEW, image, (u32)width, (u32)height);
*preview_offset = (width-400)/2;
ret = true;
}
else
{
throw_error("Corrupted/invalid preview.png", ERROR_LEVEL_WARNING);
}
free(image);
free(preview_buffer);
return ret;
}
// Initialize the audio struct
Result load_audio(Entry_s entry, audio_s *audio)
{
audio->filesize = load_data("/bgm.ogg", entry, &audio->filebuf);
if (audio->filesize == 0) {
free(audio);
DEBUG("File not found!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
}
audio->mix[0] = audio->mix[1] = 1.0f; // Determines volume for the 12 (?) different outputs. See http://smealum.github.io/ctrulib/channel_8h.html#a30eb26f1972cc3ec28370263796c0444
svcCreateEvent(&audio->finished, RESET_STICKY);
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetMix(0, audio->mix); // See mix comment above
FILE *file = fmemopen(audio->filebuf, audio->filesize, "rb");
DEBUG("Filesize: %lld\n", audio->filesize);
if(file != NULL)
{
int e = ov_open(file, &audio->vf, NULL, 0);
if (e < 0)
{
DEBUG("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("Using stereo\n");
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); // 2 channels == Stereo
} else {
DEBUG("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("Success!\n");
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
} else {
free(audio->filebuf);
free(audio);
DEBUG("fmemopen failed!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
}
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,194 +25,801 @@
*/
#include "fs.h"
#include "loading.h"
#include "themes.h"
#include "splashes.h"
#include "draw.h"
#include "camera.h"
#include "music.h"
#include "remote.h"
#include "instructions.h"
#include "pp2d/pp2d/pp2d.h"
#include <time.h>
int init_services(void)
bool quit = false;
audio_s * audio = NULL;
static bool homebrew = false;
static bool installed_themes = false;
static Thread iconLoadingThread = {0};
static Thread_Arg_s iconLoadingThread_arg = {0};
static Handle update_icons_handle;
static Thread installCheckThreads[MODE_AMOUNT] = {0};
static Thread_Arg_s installCheckThreads_arg[MODE_AMOUNT] = {0};
static Entry_List_s lists[MODE_AMOUNT] = {0};
int __stacksize__ = 64 * 1024;
Result archive_result;
u32 old_time_limit;
const char * main_paths[MODE_AMOUNT] = {
"/Themes/",
"/Splashes/",
};
const int entries_per_screen_v[MODE_AMOUNT] = {
4,
4,
};
const int entries_per_screen_h[MODE_AMOUNT] = { //for themeplaza browser
6,
6,
};
const int entry_size[MODE_AMOUNT] = {
48,
48,
};
static void init_services(void)
{
consoleDebugInit(debugDevice_SVC);
cfguInit();
open_archives();
ptmSysmInit();
return 0;
ptmuInit();
acInit();
ndspInit();
APT_GetAppCpuTimeLimit(&old_time_limit);
APT_SetAppCpuTimeLimit(30);
httpcInit(0);
archive_result = open_archives();
if(envIsHomebrew())
{
s64 out;
svcGetSystemInfo(&out, 0x10000, 0);
homebrew = !out;
}
}
int exit_services(void)
static void exit_services(void)
{
close_archives();
cfguExit();
return 0;
ptmuExit();
if (old_time_limit != UINT32_MAX) APT_SetAppCpuTimeLimit(old_time_limit);
httpcExit();
acExit();
ndspExit();
}
static void stop_install_check(void)
{
for(int i = 0; i < MODE_AMOUNT; i++)
{
installCheckThreads_arg[i].run_thread = false;
}
}
static void exit_thread(void)
{
if(iconLoadingThread_arg.run_thread)
{
DEBUG("exiting thread\n");
iconLoadingThread_arg.run_thread = false;
svcSignalEvent(update_icons_handle);
threadJoin(iconLoadingThread, U64_MAX);
threadFree(iconLoadingThread);
}
}
void free_lists(void)
{
stop_install_check();
for(int i = 0; i < MODE_AMOUNT; i++)
{
Entry_List_s * current_list = &lists[i];
free(current_list->entries);
free(current_list->icons_ids);
memset(current_list, 0, sizeof(Entry_List_s));
}
exit_thread();
}
void exit_function(bool power_pressed)
{
if(audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
}
free_lists();
svcCloseHandle(update_icons_handle);
exit_screens();
exit_services();
if(!power_pressed && installed_themes)
{
if(homebrew)
{
APT_HardwareResetAsync();
}
else
{
srvPublishToSubscriber(0x202, 0);
}
}
}
static void start_thread(void)
{
if(iconLoadingThread_arg.run_thread)
{
DEBUG("starting thread\n");
iconLoadingThread = threadCreate(load_icons_thread, &iconLoadingThread_arg, __stacksize__, 0x38, -2, false);
}
}
static void load_lists(Entry_List_s * lists)
{
ssize_t texture_id_offset = TEXTURE_ICON;
free_lists();
for(int i = 0; i < MODE_AMOUNT; i++)
{
InstallType loading_screen = INSTALL_NONE;
if(i == MODE_THEMES)
loading_screen = INSTALL_LOADING_THEMES;
else if(i == MODE_SPLASHES)
loading_screen = INSTALL_LOADING_SPLASHES;
draw_install(loading_screen);
draw_install(loading_screen);
Entry_List_s * current_list = &lists[i];
current_list->mode = i;
current_list->entries_per_screen_v = entries_per_screen_v[i];
current_list->entries_per_screen_h = 1;
current_list->entries_loaded = current_list->entries_per_screen_v * current_list->entries_per_screen_h;
current_list->entry_size = entry_size[i];
Result res = load_entries(main_paths[i], current_list);
if(R_SUCCEEDED(res))
{
if(current_list->entries_count > current_list->entries_loaded*ICONS_OFFSET_AMOUNT)
iconLoadingThread_arg.run_thread = true;
sort_by_name(current_list);
DEBUG("total: %i\n", current_list->entries_count);
current_list->texture_id_offset = texture_id_offset;
load_icons_first(current_list, false);
texture_id_offset += current_list->entries_loaded*ICONS_OFFSET_AMOUNT;
void (*install_check_function)(void*) = NULL;
if(i == MODE_THEMES)
install_check_function = themes_check_installed;
else if(i == MODE_SPLASHES)
install_check_function = splash_check_installed;
Thread_Arg_s * current_arg = &installCheckThreads_arg[i];
current_arg->run_thread = true;
current_arg->thread_arg = (void**)current_list;
if(install_check_function != NULL)
{
installCheckThreads[i] = threadCreate(install_check_function, current_arg, __stacksize__, 0x3f, -2, true);
svcSleepThread(1e8);
}
}
}
start_thread();
}
static SwkbdCallbackResult jump_menu_callback(void* entries_count, const char** ppMessage, const char* text, size_t textlen)
{
int typed_value = atoi(text);
if(typed_value > *(int*)entries_count)
{
*ppMessage = "The new position has to be\nsmaller or equal to the\nnumber of entries!";
return SWKBD_CALLBACK_CONTINUE;
}
else if(typed_value == 0)
{
*ppMessage = "The new position has to\nbe positive!";
return SWKBD_CALLBACK_CONTINUE;
}
return SWKBD_CALLBACK_OK;
}
static void jump_menu(Entry_List_s * list)
{
if(list == NULL) return;
char numbuf[64] = {0};
SwkbdState swkbd;
sprintf(numbuf, "%i", list->entries_count);
int max_chars = strlen(numbuf);
swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 2, max_chars);
sprintf(numbuf, "%i", list->selected_entry);
swkbdSetInitialText(&swkbd, numbuf);
sprintf(numbuf, "Where do you want to jump to?\nMay cause icons to reload.");
swkbdSetHintText(&swkbd, numbuf);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Jump", true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->entries_count);
memset(numbuf, 0, sizeof(numbuf));
SwkbdButton button = swkbdInputText(&swkbd, numbuf, sizeof(numbuf));
if(button == SWKBD_BUTTON_CONFIRM)
{
list->selected_entry = atoi(numbuf) - 1;
list->scroll = list->selected_entry;
if(list->scroll >= list->entries_count - list->entries_loaded)
list->scroll = list->entries_count - list->entries_loaded - 1;
}
}
static void change_selected(Entry_List_s * list, int change_value)
{
if(abs(change_value) >= list->entries_count) return;
int newval = list->selected_entry + change_value;
if(newval < 0)
newval += list->entries_count;
newval %= list->entries_count;
list->selected_entry = newval;
}
static void toggle_shuffle(Entry_List_s * list)
{
Entry_s * current_entry = &list->entries[list->selected_entry];
if(current_entry->in_shuffle)
{
if(current_entry->no_bgm_shuffle)
{
current_entry->in_shuffle = false;
current_entry->no_bgm_shuffle = false;
list->shuffle_count--;
}
else
{
current_entry->no_bgm_shuffle = true;
}
}
else
{
current_entry->in_shuffle = true;
list->shuffle_count++;
}
}
int main(void)
{
srand(time(NULL));
init_services();
init_screens();
svcCreateEvent(&update_icons_handle, RESET_ONESHOT);
int theme_count = 0;
Theme_s * themes_list = NULL;
Result res = get_themes(&themes_list, &theme_count);
if (R_FAILED(res))
{
//don't need to worry about possible textures (icons, previews), that's freed by pp2d itself
free(themes_list);
themes_list = NULL;
}
int splash_count = 0;
Splash_s *splashes_list = NULL;
res = get_splashes(&splashes_list, &splash_count);
if (R_FAILED(res))
{
//don't need to worry about possible textures (icons, previews), that's freed by pp2d itself
free(splashes_list);
splashes_list = NULL;
}
static Entry_List_s * current_list = NULL;
void * iconLoadingThread_args_void[] = {
&current_list,
&update_icons_handle,
};
iconLoadingThread_arg.thread_arg = iconLoadingThread_args_void;
iconLoadingThread_arg.run_thread = false;
#ifndef CITRA_MODE
if(R_SUCCEEDED(archive_result))
load_lists(lists);
#else
load_lists(lists);
#endif
EntryMode current_mode = MODE_THEMES;
bool splash_mode = false;
int selected_splash = 0;
int selected_theme = 0;
int shuffle_theme_count = 0;
bool preview_mode = false;
int preview_offset = 0;
while(aptMainLoop())
bool qr_mode = false;
bool install_mode = false;
bool extra_mode = false;
while(true)
{
if(quit)
{
exit_function(false);
return 0;
}
#ifndef CITRA_MODE
if(R_FAILED(archive_result) && current_mode == MODE_THEMES)
{
throw_error("Theme extdata does not exist!\nSet a default theme from the home menu.", ERROR_LEVEL_ERROR);
quit = true;
continue;
}
#endif
hidScanInput();
u32 kDown = hidKeysDown();
u32 kHeld = hidKeysHeld();
u32 kUp = hidKeysUp();
if (!splash_mode) draw_theme_interface(themes_list, theme_count, selected_theme, preview_mode);
else draw_splash_interface(splashes_list, splash_count, selected_splash, preview_mode);
current_list = &lists[current_mode];
if (themes_list == NULL && !splash_mode)
Instructions_s instructions = normal_instructions[current_mode];
if(install_mode)
instructions = install_instructions;
if(extra_mode)
{
int index = 1;
bool key_l = (kDown | kHeld) & KEY_L;
bool key_r = (kDown | kHeld) & KEY_R;
if(key_l ^ key_r)
{
if(key_l)
index = 0;
// else if(key_r) // uncomment when we use the right menu. we don't for now
// index = 2;
}
instructions = extra_instructions[index];
}
if(qr_mode) take_picture();
else if(preview_mode)
{
draw_preview(TEXTURE_PREVIEW, preview_offset);
}
else {
if(!iconLoadingThread_arg.run_thread)
{
handle_scrolling(current_list);
current_list->previous_scroll = current_list->scroll;
}
else
{
svcSignalEvent(update_icons_handle);
svcSleepThread(5e6);
}
draw_interface(current_list, instructions);
svcSleepThread(1e7);
}
pp2d_end_draw();
if(kDown & KEY_START) quit = true;
if(!install_mode && !extra_mode)
{
if(!preview_mode && !qr_mode && kDown & KEY_L) //toggle between splashes and themes
{
switch_mode:
current_mode++;
current_mode %= MODE_AMOUNT;
continue;
if (splashes_list == NULL && splash_mode)
continue;
Theme_s * current_theme = &themes_list[selected_theme];
Splash_s *current_splash = &splashes_list[selected_splash];
if (kDown & KEY_Y)
}
else if(!qr_mode && !preview_mode && kDown & KEY_R) //toggle QR mode
{
if (!preview_mode)
enable_qr:
if(R_SUCCEEDED(camInit()))
{
load_theme_preview(current_theme);
if (current_theme->has_preview)
camExit();
u32 out;
ACU_GetWifiStatus(&out);
if(out)
{
preview_mode = true;
if(init_qr())
{
load_lists(lists);
}
}
else
preview_mode = false;
{
throw_error("Please connect to Wi-Fi before scanning QR codes", ERROR_LEVEL_WARNING);
}
}
else
{
if(homebrew)
throw_error("QR scanning doesnt work from the Homebrew\nLauncher, use the ThemePlaza browser instead.", ERROR_LEVEL_WARNING);
else
throw_error("Your camera seems to have a problem,\nunable to scan QR codes.", ERROR_LEVEL_WARNING);
}
//don't allow anything while the preview is up
if (preview_mode)
continue;
}
else if(!qr_mode && kDown & KEY_Y) //toggle preview mode
{
toggle_preview:
if(!preview_mode)
{
preview_mode = load_preview(*current_list, &preview_offset);
if(current_mode == MODE_THEMES)
{
audio = calloc(1, sizeof(audio_s));
Result r = load_audio(current_list->entries[current_list->selected_entry], audio);
if (R_SUCCEEDED(r)) play_audio(audio);
else audio = NULL;
}
}
else
{
preview_mode = false;
if(current_mode == MODE_THEMES && audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
}
continue;
}
else if(preview_mode && kDown & (KEY_B | KEY_TOUCH))
{
preview_mode = false;
if(current_mode == MODE_THEMES && audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
continue;
}
}
if(qr_mode || preview_mode || current_list->entries == NULL)
goto touch;
int selected_entry = current_list->selected_entry;
Entry_s * current_entry = &current_list->entries[selected_entry];
if(install_mode)
{
if(kUp & KEY_A)
install_mode = false;
if(!install_mode)
{
if((kDown | kHeld) & KEY_DLEFT)
{
draw_install(INSTALL_BGM);
if(R_SUCCEEDED(bgm_install(*current_entry)))
{
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * theme = &current_list->entries[i];
if(theme == current_entry)
theme->installed = true;
else
theme->installed = false;
}
installed_themes = true;
}
}
else if((kDown | kHeld) & KEY_DUP)
{
draw_install(INSTALL_SINGLE);
if(R_SUCCEEDED(theme_install(*current_entry)))
{
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * theme = &current_list->entries[i];
if(theme == current_entry)
theme->installed = true;
else
theme->installed = false;
}
installed_themes = true;
}
}
else if((kDown | kHeld) & KEY_DRIGHT)
{
draw_install(INSTALL_NO_BGM);
if(R_SUCCEEDED(no_bgm_install(*current_entry)))
{
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * theme = &current_list->entries[i];
if(theme == current_entry)
theme->installed = true;
else
theme->installed = false;
}
installed_themes = true;
}
}
else if((kDown | kHeld) & KEY_DDOWN)
{
if(current_list->shuffle_count > MAX_SHUFFLE_THEMES)
{
throw_error("You have too many themes selected.", ERROR_LEVEL_WARNING);
}
else if(current_list->shuffle_count < 2)
{
throw_error("You don't have enough themes selected.", ERROR_LEVEL_WARNING);
}
else
{
draw_install(INSTALL_SHUFFLE);
Result res = shuffle_install(*current_list);
if(R_FAILED(res)) DEBUG("shuffle install result: %lx\n", res);
else
{
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * theme = &current_list->entries[i];
if(theme->in_shuffle)
{
theme->in_shuffle = false;
theme->installed = true;
}
else theme->installed = false;
}
current_list->shuffle_count = 0;
installed_themes = true;
}
}
}
}
continue;
}
else if(extra_mode)
{
if(kUp & KEY_X)
extra_mode = false;
if(!extra_mode)
{
bool key_l = (kDown | kHeld) & KEY_L;
bool key_r = (kDown | kHeld) & KEY_R;
if(!(key_l ^ key_r))
{
if((kDown | kHeld) & KEY_DLEFT)
{
browse_themeplaza:
if(themeplaza_browser(current_mode))
{
current_mode = MODE_THEMES;
load_lists(lists);
}
}
else if((kDown | kHeld) & KEY_DUP)
{
jump:
jump_menu(current_list);
}
else if((kDown | kHeld) & KEY_DDOWN)
{
load_icons_first(current_list, false);
}
}
else if(key_l)
{
if((kDown | kHeld) & KEY_DLEFT)
{
sort_path:
sort_by_filename(current_list);
load_icons_first(current_list, false);
}
else if(((kDown | kHeld)) & KEY_DUP)
{
sort_name:
sort_by_name(current_list);
load_icons_first(current_list, false);
}
else if(((kDown | kHeld)) & KEY_DDOWN)
{
sort_author:
sort_by_author(current_list);
load_icons_first(current_list, false);
}
}
}
continue;
}
// Actions
else if (kDown & KEY_L)
{
splash_mode = !splash_mode;
}
else if (kDown & KEY_X)
{
// install_bgm(current_theme);
}
else if (kDown & KEY_A)
{
if (splash_mode)
{
splash_install(*current_splash);
} else {
draw_theme_install(false);
single_install(*current_theme);
}
}
else if (kDown & KEY_B)
if(kDown & KEY_A)
{
if (splash_mode)
switch(current_mode)
{
} else {
if (shuffle_theme_count < 10)
{
if (current_theme->in_shuffle) shuffle_theme_count--;
else shuffle_theme_count++;
current_theme->in_shuffle = !(current_theme->in_shuffle);
} else {
if (current_theme->in_shuffle) {
shuffle_theme_count--;
current_theme->in_shuffle = false;
case MODE_THEMES:
install_mode = true;
break;
case MODE_SPLASHES:
draw_install(INSTALL_SPLASH);
splash_install(*current_entry);
break;
default:
break;
}
}
else if(kDown & KEY_B)
{
switch(current_mode)
{
case MODE_THEMES:
toggle_shuffle(current_list);
break;
case MODE_SPLASHES:
if(draw_confirm("Are you sure you would like to delete\nthe installed splash?", current_list))
{
draw_install(INSTALL_SPLASH_DELETE);
splash_delete();
}
break;
default:
break;
}
}
else if (kDown & KEY_SELECT)
else if(kDown & KEY_X)
{
if (splash_mode)
{
} else {
if (shuffle_theme_count > 0)
{
draw_theme_install(true);
shuffle_install(themes_list, theme_count);
extra_mode = true;
}
else if(kDown & KEY_SELECT)
{
if(draw_confirm("Are you sure you would like to delete this?", current_list))
{
draw_install(INSTALL_ENTRY_DELETE);
delete_entry(current_entry, current_entry->is_zip);
load_lists(lists);
}
}
// Movement in the UI
else if (kDown & KEY_DOWN)
else if(kDown & KEY_UP)
{
if (splash_mode)
{
selected_splash++;
if (selected_splash >= splash_count)
selected_splash = splash_count-1;
} else {
selected_theme++;
if (selected_theme >= theme_count)
selected_theme = theme_count-1;
change_selected(current_list, -1);
}
}
else if (kDown & KEY_UP)
else if(kDown & KEY_DOWN)
{
if (splash_mode)
{
selected_splash--;
if (selected_splash < 0)
selected_splash = 0;
} else {
selected_theme--;
if (selected_theme < 0)
selected_theme = 0;
}
change_selected(current_list, 1);
}
// Quick moving
else if (kDown & KEY_LEFT)
else if(kDown & KEY_LEFT)
{
if (splash_mode) selected_splash = 0;
else selected_theme = 0;
change_selected(current_list, -current_list->entries_per_screen_v);
}
else if (kDown & KEY_RIGHT)
else if(kDown & KEY_RIGHT)
{
if (splash_mode) selected_splash = splash_count - 1;
else selected_theme = theme_count-1;
change_selected(current_list, current_list->entries_per_screen_v);
}
if (kDown & KEY_START)
// Fast scroll using circle pad
else if(kHeld & KEY_CPAD_UP)
{
exit_screens();
exit_services();
PTMSYSM_RebootAsync(0);
ptmSysmExit();
change_selected(current_list, -1);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_DOWN)
{
change_selected(current_list, 1);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_LEFT)
{
change_selected(current_list, -current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_RIGHT)
{
change_selected(current_list, current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
}
// Movement using the touchscreen
touch:
if((kDown | kHeld) & KEY_TOUCH)
{
touchPosition touch = {0};
hidTouchRead(&touch);
u16 x = touch.px;
u16 y = touch.py;
u16 arrowStartX = 152;
u16 arrowEndX = arrowStartX+16;
#define BETWEEN(min, x, max) (min < x && x < max)
if(kDown & KEY_TOUCH)
{
if(y < 24)
{
if(current_list->entries != NULL && BETWEEN(arrowStartX, x, arrowEndX) && current_list->scroll > 0)
{
change_selected(current_list, -current_list->entries_per_screen_v);
}
else if(BETWEEN(320-144, x, 320-120))
{
switch(current_list->current_sort)
{
case SORT_NAME:
goto sort_author;
break;
case SORT_AUTHOR:
goto sort_path;
break;
case SORT_PATH:
goto sort_name;
break;
default:
break;
}
}
else if(BETWEEN(320-120, x, 320-96))
{
goto enable_qr;
}
else if(BETWEEN(320-96, x, 320-72))
{
goto browse_themeplaza;
}
else if(BETWEEN(320-72, x, 320-48))
{
quit = true;
}
else if(BETWEEN(320-48, x, 320-24))
{
goto toggle_preview;
}
else if(BETWEEN(320-24, x, 320))
{
goto switch_mode;
}
}
else if(y >= 216)
{
if(current_list->entries != NULL && BETWEEN(arrowStartX, x, arrowEndX) && current_list->scroll < current_list->entries_count - current_list->entries_per_screen_v)
{
change_selected(current_list, current_list->entries_per_screen_v);
}
else if(current_list->entries != NULL && BETWEEN(176, x, 320))
{
goto jump;
}
}
}
else
{
if(current_list->entries != NULL && BETWEEN(24, y, 216))
{
for(int i = 0; i < current_list->entries_loaded; i++)
{
u16 miny = 24 + current_list->entry_size*i;
u16 maxy = miny + current_list->entry_size;
if(BETWEEN(miny, y, maxy) && current_list->scroll + i < current_list->entries_count)
{
current_list->selected_entry = current_list->scroll + i;
break;
}
}
}
}
}
}
free(themes_list);
exit_function(true);
return 0;
}

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 */

85
source/music.c Normal file
View File

@@ -0,0 +1,85 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "music.h"
#include "loading.h"
// Play a given audio struct
Result update_audio(audio_s *audio)
{
long size = BUF_TO_READ - audio->data_read;
DEBUG("Audio Size: %ld\n", size);
if (audio->wave_buf[audio->buf_pos].status == NDSP_WBUF_DONE) // only run if the current selected buffer has already finished playing
{
DEBUG("Attempting ov_read\n");
int bitstream;
size_t read = ov_read(&audio->vf, (char*)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, &bitstream); // read 1 vorbis packet into wave buffer
DEBUG("ov_read successful\n");
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("Vorbis play error: %d\n", read);
ndspChnReset(0);
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
}
} else
{
audio->data_read += read;
if (read == size) {
audio->data_read = 0;
ndspChnWaveBufAdd(0, &audio->wave_buf[audio->buf_pos]); // Add buffer to ndsp
audio->buf_pos = 1 - audio->buf_pos; // switch to other buffer to load and prepare it while the current one is playing
}
}
}
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
}
void thread_audio(void* data) {
audio_s *audio = (audio_s*)data;
while(!audio->stop) {
update_audio(audio);
}
free(audio->filebuf);
ov_clear(&audio->vf);
linearFree(audio->wave_buf[0].data_vaddr);
linearFree(audio->wave_buf[1].data_vaddr);
while (audio->wave_buf[0].status != NDSP_WBUF_DONE || audio->wave_buf[1].status != NDSP_WBUF_DONE) svcSleepThread(1e7);
svcSignalEvent(audio->finished);
svcSleepThread(1e8);
svcCloseHandle(audio->finished);
free(audio);
}
void play_audio(audio_s *audio) {
threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, true);
}

Submodule source/pp2d deleted from cb235fa83b

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

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

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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

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

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

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

View File

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

918
source/quirc/decode.c Normal file
View File

@@ -0,0 +1,918 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "quirc_internal.h"
#include <string.h>
#include <stdlib.h>
#define MAX_POLY 64
/************************************************************************
* Galois fields
*/
struct galois_field {
int p;
const uint8_t *log;
const uint8_t *exp;
};
static const uint8_t gf16_exp[16] = {
0x01, 0x02, 0x04, 0x08, 0x03, 0x06, 0x0c, 0x0b,
0x05, 0x0a, 0x07, 0x0e, 0x0f, 0x0d, 0x09, 0x01
};
static const uint8_t gf16_log[16] = {
0x00, 0x0f, 0x01, 0x04, 0x02, 0x08, 0x05, 0x0a,
0x03, 0x0e, 0x09, 0x07, 0x06, 0x0d, 0x0b, 0x0c
};
static const struct galois_field gf16 = {
.p = 15,
.log = gf16_log,
.exp = gf16_exp
};
static const uint8_t gf256_exp[256] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9,
0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35,
0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0,
0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc,
0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f,
0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88,
0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93,
0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9,
0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa,
0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e,
0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4,
0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e,
0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef,
0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5,
0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83,
0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01
};
static const uint8_t gf256_log[256] = {
0x00, 0xff, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6,
0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81,
0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21,
0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9,
0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd,
0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd,
0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e,
0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b,
0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d,
0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c,
0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd,
0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e,
0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76,
0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa,
0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51,
0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8,
0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
};
const static struct galois_field gf256 = {
.p = 255,
.log = gf256_log,
.exp = gf256_exp
};
/************************************************************************
* Polynomial operations
*/
static void poly_add(uint8_t *dst, const uint8_t *src, uint8_t c,
int shift, const struct galois_field *gf)
{
int i;
int log_c = gf->log[c];
if (!c)
return;
for (i = 0; i < MAX_POLY; i++) {
int p = i + shift;
uint8_t v = src[i];
if (p < 0 || p >= MAX_POLY)
continue;
if (!v)
continue;
dst[p] ^= gf->exp[(gf->log[v] + log_c) % gf->p];
}
}
static uint8_t poly_eval(const uint8_t *s, uint8_t x,
const struct galois_field *gf)
{
int i;
uint8_t sum = 0;
uint8_t log_x = gf->log[x];
if (!x)
return s[0];
for (i = 0; i < MAX_POLY; i++) {
uint8_t c = s[i];
if (!c)
continue;
sum ^= gf->exp[(gf->log[c] + log_x * i) % gf->p];
}
return sum;
}
/************************************************************************
* Berlekamp-Massey algorithm for finding error locator polynomials.
*/
static void berlekamp_massey(const uint8_t *s, int N,
const struct galois_field *gf,
uint8_t *sigma)
{
uint8_t C[MAX_POLY];
uint8_t B[MAX_POLY];
int L = 0;
int m = 1;
uint8_t b = 1;
int n;
memset(B, 0, sizeof(B));
memset(C, 0, sizeof(C));
B[0] = 1;
C[0] = 1;
for (n = 0; n < N; n++) {
uint8_t d = s[n];
uint8_t mult;
int i;
for (i = 1; i <= L; i++) {
if (!(C[i] && s[n - i]))
continue;
d ^= gf->exp[(gf->log[C[i]] +
gf->log[s[n - i]]) %
gf->p];
}
mult = gf->exp[(gf->p - gf->log[b] + gf->log[d]) % gf->p];
if (!d) {
m++;
} else if (L * 2 <= n) {
uint8_t T[MAX_POLY];
memcpy(T, C, sizeof(T));
poly_add(C, B, mult, m, gf);
memcpy(B, T, sizeof(B));
L = n + 1 - L;
b = d;
m = 1;
} else {
poly_add(C, B, mult, m, gf);
m++;
}
}
memcpy(sigma, C, MAX_POLY);
}
/************************************************************************
* Code stream error correction
*
* Generator polynomial for GF(2^8) is x^8 + x^4 + x^3 + x^2 + 1
*/
static int block_syndromes(const uint8_t *data, int bs, int npar, uint8_t *s)
{
int nonzero = 0;
int i;
memset(s, 0, MAX_POLY);
for (i = 0; i < npar; i++) {
int j;
for (j = 0; j < bs; j++) {
uint8_t c = data[bs - j - 1];
if (!c)
continue;
s[i] ^= gf256_exp[((int)gf256_log[c] +
i * j) % 255];
}
if (s[i])
nonzero = 1;
}
return nonzero;
}
static void eloc_poly(uint8_t *omega,
const uint8_t *s, const uint8_t *sigma,
int npar)
{
int i;
memset(omega, 0, MAX_POLY);
for (i = 0; i < npar; i++) {
const uint8_t a = sigma[i];
const uint8_t log_a = gf256_log[a];
int j;
if (!a)
continue;
for (j = 0; j + 1 < MAX_POLY; j++) {
const uint8_t b = s[j + 1];
if (i + j >= npar)
break;
if (!b)
continue;
omega[i + j] ^=
gf256_exp[(log_a + gf256_log[b]) % 255];
}
}
}
static quirc_decode_error_t correct_block(uint8_t *data,
const struct quirc_rs_params *ecc)
{
int npar = ecc->bs - ecc->dw;
uint8_t s[MAX_POLY];
uint8_t sigma[MAX_POLY];
uint8_t sigma_deriv[MAX_POLY];
uint8_t omega[MAX_POLY];
int i;
/* Compute syndrome vector */
if (!block_syndromes(data, ecc->bs, npar, s))
return QUIRC_SUCCESS;
berlekamp_massey(s, npar, &gf256, sigma);
/* Compute derivative of sigma */
memset(sigma_deriv, 0, MAX_POLY);
for (i = 0; i + 1 < MAX_POLY; i += 2)
sigma_deriv[i] = sigma[i + 1];
/* Compute error evaluator polynomial */
eloc_poly(omega, s, sigma, npar - 1);
/* Find error locations and magnitudes */
for (i = 0; i < ecc->bs; i++) {
uint8_t xinv = gf256_exp[255 - i];
if (!poly_eval(sigma, xinv, &gf256)) {
uint8_t sd_x = poly_eval(sigma_deriv, xinv, &gf256);
uint8_t omega_x = poly_eval(omega, xinv, &gf256);
uint8_t error = gf256_exp[(255 - gf256_log[sd_x] +
gf256_log[omega_x]) % 255];
data[ecc->bs - i - 1] ^= error;
}
}
if (block_syndromes(data, ecc->bs, npar, s))
return QUIRC_ERROR_DATA_ECC;
return QUIRC_SUCCESS;
}
/************************************************************************
* Format value error correction
*
* Generator polynomial for GF(2^4) is x^4 + x + 1
*/
#define FORMAT_MAX_ERROR 3
#define FORMAT_SYNDROMES (FORMAT_MAX_ERROR * 2)
#define FORMAT_BITS 15
static int format_syndromes(uint16_t u, uint8_t *s)
{
int i;
int nonzero = 0;
memset(s, 0, MAX_POLY);
for (i = 0; i < FORMAT_SYNDROMES; i++) {
int j;
s[i] = 0;
for (j = 0; j < FORMAT_BITS; j++)
if (u & (1 << j))
s[i] ^= gf16_exp[((i + 1) * j) % 15];
if (s[i])
nonzero = 1;
}
return nonzero;
}
static quirc_decode_error_t correct_format(uint16_t *f_ret)
{
uint16_t u = *f_ret;
int i;
uint8_t s[MAX_POLY];
uint8_t sigma[MAX_POLY];
/* Evaluate U (received codeword) at each of alpha_1 .. alpha_6
* to get S_1 .. S_6 (but we index them from 0).
*/
if (!format_syndromes(u, s))
return QUIRC_SUCCESS;
berlekamp_massey(s, FORMAT_SYNDROMES, &gf16, sigma);
/* Now, find the roots of the polynomial */
for (i = 0; i < 15; i++)
if (!poly_eval(sigma, gf16_exp[15 - i], &gf16))
u ^= (1 << i);
if (format_syndromes(u, s))
return QUIRC_ERROR_FORMAT_ECC;
*f_ret = u;
return QUIRC_SUCCESS;
}
/************************************************************************
* Decoder algorithm
*/
struct datastream {
uint8_t raw[QUIRC_MAX_PAYLOAD];
int data_bits;
int ptr;
uint8_t data[QUIRC_MAX_PAYLOAD];
};
static inline int grid_bit(const struct quirc_code *code, int x, int y)
{
int p = y * code->size + x;
return (code->cell_bitmap[p >> 3] >> (p & 7)) & 1;
}
static quirc_decode_error_t read_format(const struct quirc_code *code,
struct quirc_data *data, int which)
{
int i;
uint16_t format = 0;
uint16_t fdata;
quirc_decode_error_t err;
if (which) {
for (i = 0; i < 7; i++)
format = (format << 1) |
grid_bit(code, 8, code->size - 1 - i);
for (i = 0; i < 8; i++)
format = (format << 1) |
grid_bit(code, code->size - 8 + i, 8);
} else {
static const int xs[15] = {
8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0
};
static const int ys[15] = {
0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8
};
for (i = 14; i >= 0; i--)
format = (format << 1) | grid_bit(code, xs[i], ys[i]);
}
format ^= 0x5412;
err = correct_format(&format);
if (err)
return err;
fdata = format >> 10;
data->ecc_level = fdata >> 3;
data->mask = fdata & 7;
return QUIRC_SUCCESS;
}
static int mask_bit(int mask, int i, int j)
{
switch (mask) {
case 0: return !((i + j) % 2);
case 1: return !(i % 2);
case 2: return !(j % 3);
case 3: return !((i + j) % 3);
case 4: return !(((i / 2) + (j / 3)) % 2);
case 5: return !((i * j) % 2 + (i * j) % 3);
case 6: return !(((i * j) % 2 + (i * j) % 3) % 2);
case 7: return !(((i * j) % 3 + (i + j) % 2) % 2);
}
return 0;
}
static int reserved_cell(int version, int i, int j)
{
const struct quirc_version_info *ver = &quirc_version_db[version];
int size = version * 4 + 17;
int ai = -1, aj = -1, a;
/* Finder + format: top left */
if (i < 9 && j < 9)
return 1;
/* Finder + format: bottom left */
if (i + 8 >= size && j < 9)
return 1;
/* Finder + format: top right */
if (i < 9 && j + 8 >= size)
return 1;
/* Exclude timing patterns */
if (i == 6 || j == 6)
return 1;
/* Exclude version info, if it exists. Version info sits adjacent to
* the top-right and bottom-left finders in three rows, bounded by
* the timing pattern.
*/
if (version >= 7) {
if (i < 6 && j + 11 >= size)
return 1;
if (i + 11 >= size && j < 6)
return 1;
}
/* Exclude alignment patterns */
for (a = 0; a < QUIRC_MAX_ALIGNMENT && ver->apat[a]; a++) {
int p = ver->apat[a];
if (abs(p - i) < 3)
ai = a;
if (abs(p - j) < 3)
aj = a;
}
if (ai >= 0 && aj >= 0) {
a--;
if (ai > 0 && ai < a)
return 1;
if (aj > 0 && aj < a)
return 1;
if (aj == a && ai == a)
return 1;
}
return 0;
}
static void read_bit(const struct quirc_code *code,
struct quirc_data *data,
struct datastream *ds, int i, int j)
{
int bitpos = ds->data_bits & 7;
int bytepos = ds->data_bits >> 3;
int v = grid_bit(code, j, i);
if (mask_bit(data->mask, i, j))
v ^= 1;
if (v)
ds->raw[bytepos] |= (0x80 >> bitpos);
ds->data_bits++;
}
static void read_data(const struct quirc_code *code,
struct quirc_data *data,
struct datastream *ds)
{
int y = code->size - 1;
int x = code->size - 1;
int dir = -1;
while (x > 0) {
if (x == 6)
x--;
if (!reserved_cell(data->version, y, x))
read_bit(code, data, ds, y, x);
if (!reserved_cell(data->version, y, x - 1))
read_bit(code, data, ds, y, x - 1);
y += dir;
if (y < 0 || y >= code->size) {
dir = -dir;
x -= 2;
y += dir;
}
}
}
static quirc_decode_error_t codestream_ecc(struct quirc_data *data,
struct datastream *ds)
{
const struct quirc_version_info *ver =
&quirc_version_db[data->version];
const struct quirc_rs_params *sb_ecc = &ver->ecc[data->ecc_level];
struct quirc_rs_params lb_ecc;
int bc = ver->data_bytes / sb_ecc->bs;
int dst_offset = 0;
int lb_count = ver->data_bytes - bc * sb_ecc->bs;
int small_dw_total = bc * sb_ecc->dw;
int i;
memcpy(&lb_ecc, sb_ecc, sizeof(lb_ecc));
lb_ecc.dw++;
lb_ecc.bs++;
for (i = 0; i < bc; i++) {
uint8_t *dst = ds->data + dst_offset;
const struct quirc_rs_params *ecc = sb_ecc;
quirc_decode_error_t err;
int j = 0;
int k;
for (k = 0; k < sb_ecc->dw; k++)
dst[j++] = ds->raw[k * bc + i];
if (i + lb_count >= bc) {
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);
if (err)
return err;
dst_offset += ecc->dw;
}
ds->data_bits = dst_offset * 8;
return QUIRC_SUCCESS;
}
static inline int bits_remaining(const struct datastream *ds)
{
return ds->data_bits - ds->ptr;
}
static int take_bits(struct datastream *ds, int len)
{
int ret = 0;
while (len && (ds->ptr < ds->data_bits)) {
uint8_t b = ds->data[ds->ptr >> 3];
int bitpos = ds->ptr & 7;
ret <<= 1;
if ((b << bitpos) & 0x80)
ret |= 1;
ds->ptr++;
len--;
}
return ret;
}
static int numeric_tuple(struct quirc_data *data,
struct datastream *ds,
int bits, int digits)
{
int tuple;
int i;
if (bits_remaining(ds) < bits)
return -1;
tuple = take_bits(ds, bits);
for (i = digits - 1; i >= 0; i--) {
data->payload[data->payload_len + i] = tuple % 10 + '0';
tuple /= 10;
}
data->payload_len += digits;
return 0;
}
static quirc_decode_error_t decode_numeric(struct quirc_data *data,
struct datastream *ds)
{
int bits = 14;
int count;
if (data->version < 10)
bits = 10;
else if (data->version < 27)
bits = 12;
count = take_bits(ds, bits);
if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD)
return QUIRC_ERROR_DATA_OVERFLOW;
while (count >= 3) {
if (numeric_tuple(data, ds, 10, 3) < 0)
return QUIRC_ERROR_DATA_UNDERFLOW;
count -= 3;
}
if (count >= 2) {
if (numeric_tuple(data, ds, 7, 2) < 0)
return QUIRC_ERROR_DATA_UNDERFLOW;
count -= 2;
}
if (count) {
if (numeric_tuple(data, ds, 4, 1) < 0)
return QUIRC_ERROR_DATA_UNDERFLOW;
count--;
}
return QUIRC_SUCCESS;
}
static int alpha_tuple(struct quirc_data *data,
struct datastream *ds,
int bits, int digits)
{
int tuple;
int i;
if (bits_remaining(ds) < bits)
return -1;
tuple = take_bits(ds, bits);
for (i = 0; i < digits; i++) {
static const char *alpha_map =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
data->payload[data->payload_len + digits - i - 1] =
alpha_map[tuple % 45];
tuple /= 45;
}
data->payload_len += digits;
return 0;
}
static quirc_decode_error_t decode_alpha(struct quirc_data *data,
struct datastream *ds)
{
int bits = 13;
int count;
if (data->version < 7)
bits = 9;
else if (data->version < 11)
bits = 10;
count = take_bits(ds, bits);
if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD)
return QUIRC_ERROR_DATA_OVERFLOW;
while (count >= 2) {
if (alpha_tuple(data, ds, 11, 2) < 0)
return QUIRC_ERROR_DATA_UNDERFLOW;
count -= 2;
}
if (count) {
if (alpha_tuple(data, ds, 6, 1) < 0)
return QUIRC_ERROR_DATA_UNDERFLOW;
count--;
}
return QUIRC_SUCCESS;
}
static quirc_decode_error_t decode_byte(struct quirc_data *data,
struct datastream *ds)
{
int bits = 16;
int count;
int i;
if (data->version < 10)
bits = 8;
count = take_bits(ds, bits);
if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD)
return QUIRC_ERROR_DATA_OVERFLOW;
if (bits_remaining(ds) < count * 8)
return QUIRC_ERROR_DATA_UNDERFLOW;
for (i = 0; i < count; i++)
data->payload[data->payload_len++] = take_bits(ds, 8);
return QUIRC_SUCCESS;
}
static quirc_decode_error_t decode_kanji(struct quirc_data *data,
struct datastream *ds)
{
int bits = 12;
int count;
int i;
if (data->version < 10)
bits = 8;
else if (data->version < 27)
bits = 10;
count = take_bits(ds, bits);
if (data->payload_len + count * 2 + 1 > QUIRC_MAX_PAYLOAD)
return QUIRC_ERROR_DATA_OVERFLOW;
if (bits_remaining(ds) < count * 13)
return QUIRC_ERROR_DATA_UNDERFLOW;
for (i = 0; i < count; i++) {
int d = take_bits(ds, 13);
uint16_t sjw;
if (d + 0x8140 >= 0x9ffc)
sjw = d + 0x8140;
else
sjw = d + 0xc140;
data->payload[data->payload_len++] = sjw >> 8;
data->payload[data->payload_len++] = sjw & 0xff;
}
return QUIRC_SUCCESS;
}
static quirc_decode_error_t decode_eci(struct quirc_data *data,
struct datastream *ds)
{
if (bits_remaining(ds) < 8)
return QUIRC_ERROR_DATA_UNDERFLOW;
data->eci = take_bits(ds, 8);
if ((data->eci & 0xc0) == 0x80) {
if (bits_remaining(ds) < 8)
return QUIRC_ERROR_DATA_UNDERFLOW;
data->eci = (data->eci << 8) | take_bits(ds, 8);
} else if ((data->eci & 0xe0) == 0xc0) {
if (bits_remaining(ds) < 16)
return QUIRC_ERROR_DATA_UNDERFLOW;
data->eci = (data->eci << 16) | take_bits(ds, 16);
}
return QUIRC_SUCCESS;
}
static quirc_decode_error_t decode_payload(struct quirc_data *data,
struct datastream *ds)
{
while (bits_remaining(ds) >= 4) {
quirc_decode_error_t err = QUIRC_SUCCESS;
int type = take_bits(ds, 4);
switch (type) {
case QUIRC_DATA_TYPE_NUMERIC:
err = decode_numeric(data, ds);
break;
case QUIRC_DATA_TYPE_ALPHA:
err = decode_alpha(data, ds);
break;
case QUIRC_DATA_TYPE_BYTE:
err = decode_byte(data, ds);
break;
case QUIRC_DATA_TYPE_KANJI:
err = decode_kanji(data, ds);
break;
case 7:
err = decode_eci(data, ds);
break;
default:
goto done;
}
if (err)
return err;
if (!(type & (type - 1)) && (type > data->data_type))
data->data_type = type;
}
done:
/* Add nul terminator to all payloads */
if (data->payload_len >= sizeof(data->payload))
data->payload_len--;
data->payload[data->payload_len] = 0;
return QUIRC_SUCCESS;
}
quirc_decode_error_t quirc_decode(const struct quirc_code *code,
struct quirc_data *data)
{
quirc_decode_error_t err;
struct datastream ds;
if ((code->size - 17) % 4)
return QUIRC_ERROR_INVALID_GRID_SIZE;
memset(data, 0, sizeof(*data));
memset(&ds, 0, sizeof(ds));
data->version = (code->size - 17) / 4;
if (data->version < 1 ||
data->version > QUIRC_MAX_VERSION)
return QUIRC_ERROR_INVALID_VERSION;
/* Read format information -- try both locations */
err = read_format(code, data, 0);
if (err)
err = read_format(code, data, 1);
if (err)
return err;
read_data(code, data, &ds);
err = codestream_ecc(data, &ds);
if (err)
return err;
err = decode_payload(data, &ds);
if (err)
return err;
return QUIRC_SUCCESS;
}

1265
source/quirc/identify.c Normal file

File diff suppressed because it is too large Load Diff

91
source/quirc/quirc.c Normal file
View File

@@ -0,0 +1,91 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include "quirc_internal.h"
const char *quirc_version(void)
{
return "1.0";
}
struct quirc *quirc_new(void)
{
struct quirc *q = malloc(sizeof(*q));
if (!q)
return NULL;
memset(q, 0, sizeof(*q));
return q;
}
void quirc_destroy(struct quirc *q)
{
if (q->image)
free(q->image);
if (sizeof(*q->image) != sizeof(*q->pixels))
free(q->pixels);
free(q);
}
int quirc_resize(struct quirc *q, int w, int h)
{
uint8_t *new_image = realloc(q->image, w * h);
if (!new_image)
return -1;
if (sizeof(*q->image) != sizeof(*q->pixels)) {
size_t new_size = w * h * sizeof(quirc_pixel_t);
quirc_pixel_t *new_pixels = realloc(q->pixels, new_size);
if (!new_pixels)
return -1;
q->pixels = new_pixels;
}
q->image = new_image;
q->w = w;
q->h = h;
return 0;
}
int quirc_count(const struct quirc *q)
{
return q->num_grids;
}
static const char *const error_table[] = {
[QUIRC_SUCCESS] = "Success",
[QUIRC_ERROR_INVALID_GRID_SIZE] = "Invalid grid size",
[QUIRC_ERROR_INVALID_VERSION] = "Invalid version",
[QUIRC_ERROR_FORMAT_ECC] = "Format data ECC failure",
[QUIRC_ERROR_DATA_ECC] = "ECC failure",
[QUIRC_ERROR_UNKNOWN_DATA_TYPE] = "Unknown data type",
[QUIRC_ERROR_DATA_OVERFLOW] = "Data overflow",
[QUIRC_ERROR_DATA_UNDERFLOW] = "Data underflow"
};
const char *quirc_strerror(quirc_decode_error_t err)
{
if (err >= 0 && err < sizeof(error_table) / sizeof(error_table[0]))
return error_table[err];
return "Unknown error";
}

165
source/quirc/quirc.h Normal file
View File

@@ -0,0 +1,165 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef QUIRC_H_
#define QUIRC_H_
#include <stdint.h>
struct quirc;
/* Obtain the library version string. */
const char *quirc_version(void);
/* Construct a new QR-code recognizer. This function will return NULL
* if sufficient memory could not be allocated.
*/
struct quirc *quirc_new(void);
/* Destroy a QR-code recognizer. */
void quirc_destroy(struct quirc *q);
/* Resize the QR-code recognizer. The size of an image must be
* specified before codes can be analyzed.
*
* This function returns 0 on success, or -1 if sufficient memory could
* not be allocated.
*/
int quirc_resize(struct quirc *q, int w, int h);
/* These functions are used to process images for QR-code recognition.
* quirc_begin() must first be called to obtain access to a buffer into
* which the input image should be placed. Optionally, the current
* width and height may be returned.
*
* After filling the buffer, quirc_end() should be called to process
* the image for QR-code recognition. The locations and content of each
* code may be obtained using accessor functions described below.
*/
uint8_t *quirc_begin(struct quirc *q, int *w, int *h);
void quirc_end(struct quirc *q);
/* This structure describes a location in the input image buffer. */
struct quirc_point {
int x;
int y;
};
/* This enum describes the various decoder errors which may occur. */
typedef enum {
QUIRC_SUCCESS = 0,
QUIRC_ERROR_INVALID_GRID_SIZE,
QUIRC_ERROR_INVALID_VERSION,
QUIRC_ERROR_FORMAT_ECC,
QUIRC_ERROR_DATA_ECC,
QUIRC_ERROR_UNKNOWN_DATA_TYPE,
QUIRC_ERROR_DATA_OVERFLOW,
QUIRC_ERROR_DATA_UNDERFLOW
} quirc_decode_error_t;
/* Return a string error message for an error code. */
const char *quirc_strerror(quirc_decode_error_t err);
/* Limits on the maximum size of QR-codes and their content. */
#define QUIRC_MAX_BITMAP 3917
#define QUIRC_MAX_PAYLOAD 8896
/* QR-code ECC types. */
#define QUIRC_ECC_LEVEL_M 0
#define QUIRC_ECC_LEVEL_L 1
#define QUIRC_ECC_LEVEL_H 2
#define QUIRC_ECC_LEVEL_Q 3
/* QR-code data types. */
#define QUIRC_DATA_TYPE_NUMERIC 1
#define QUIRC_DATA_TYPE_ALPHA 2
#define QUIRC_DATA_TYPE_BYTE 4
#define QUIRC_DATA_TYPE_KANJI 8
/* Common character encodings */
#define QUIRC_ECI_ISO_8859_1 1
#define QUIRC_ECI_IBM437 2
#define QUIRC_ECI_ISO_8859_2 4
#define QUIRC_ECI_ISO_8859_3 5
#define QUIRC_ECI_ISO_8859_4 6
#define QUIRC_ECI_ISO_8859_5 7
#define QUIRC_ECI_ISO_8859_6 8
#define QUIRC_ECI_ISO_8859_7 9
#define QUIRC_ECI_ISO_8859_8 10
#define QUIRC_ECI_ISO_8859_9 11
#define QUIRC_ECI_WINDOWS_874 13
#define QUIRC_ECI_ISO_8859_13 15
#define QUIRC_ECI_ISO_8859_15 17
#define QUIRC_ECI_SHIFT_JIS 20
#define QUIRC_ECI_UTF_8 26
/* This structure is used to return information about detected QR codes
* in the input image.
*/
struct quirc_code {
/* The four corners of the QR-code, from top left, clockwise */
struct quirc_point corners[4];
/* The number of cells across in the QR-code. The cell bitmap
* is a bitmask giving the actual values of cells. If the cell
* at (x, y) is black, then the following bit is set:
*
* cell_bitmap[i << 3] & (1 << (i & 7))
*
* where i = (y * size) + x.
*/
int size;
uint8_t cell_bitmap[QUIRC_MAX_BITMAP];
};
/* This structure holds the decoded QR-code data */
struct quirc_data {
/* Various parameters of the QR-code. These can mostly be
* ignored if you only care about the data.
*/
int version;
int ecc_level;
int mask;
/* This field is the highest-valued data type found in the QR
* code.
*/
int data_type;
/* Data payload. For the Kanji datatype, payload is encoded as
* Shift-JIS. For all other datatypes, payload is ASCII text.
*/
uint8_t payload[QUIRC_MAX_PAYLOAD];
int payload_len;
/* ECI assignment number */
uint32_t eci;
};
/* Return the number of QR-codes identified in the last processed
* image.
*/
int quirc_count(const struct quirc *q);
/* Extract the QR-code specified by the given index. */
void quirc_extract(const struct quirc *q, int index,
struct quirc_code *code);
/* Decode a QR-code, returning the payload data. */
quirc_decode_error_t quirc_decode(const struct quirc_code *code,
struct quirc_data *data);
#endif

View File

@@ -0,0 +1,114 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef QUIRC_INTERNAL_H_
#define QUIRC_INTERNAL_H_
#include "quirc.h"
#define QUIRC_PIXEL_WHITE 0
#define QUIRC_PIXEL_BLACK 1
#define QUIRC_PIXEL_REGION 2
#ifndef QUIRC_MAX_REGIONS
#define QUIRC_MAX_REGIONS 254
#endif
#define QUIRC_MAX_CAPSTONES 32
#define QUIRC_MAX_GRIDS 8
#define QUIRC_PERSPECTIVE_PARAMS 8
#if QUIRC_MAX_REGIONS < UINT8_MAX
typedef uint8_t quirc_pixel_t;
#elif QUIRC_MAX_REGIONS < UINT16_MAX
typedef uint16_t quirc_pixel_t;
#else
#error "QUIRC_MAX_REGIONS > 65534 is not supported"
#endif
struct quirc_region {
struct quirc_point seed;
int count;
int capstone;
};
struct quirc_capstone {
int ring;
int stone;
struct quirc_point corners[4];
struct quirc_point center;
double c[QUIRC_PERSPECTIVE_PARAMS];
int qr_grid;
};
struct quirc_grid {
/* Capstone indices */
int caps[3];
/* Alignment pattern region and corner */
int align_region;
struct quirc_point align;
/* Timing pattern endpoints */
struct quirc_point tpep[3];
int hscan;
int vscan;
/* Grid size and perspective transform */
int grid_size;
double c[QUIRC_PERSPECTIVE_PARAMS];
};
struct quirc {
uint8_t *image;
quirc_pixel_t *pixels;
int w;
int h;
int num_regions;
struct quirc_region regions[QUIRC_MAX_REGIONS];
int num_capstones;
struct quirc_capstone capstones[QUIRC_MAX_CAPSTONES];
int num_grids;
struct quirc_grid grids[QUIRC_MAX_GRIDS];
};
/************************************************************************
* QR-code version information database
*/
#define QUIRC_MAX_VERSION 40
#define QUIRC_MAX_ALIGNMENT 7
struct quirc_rs_params {
int bs; /* Block size */
int dw; /* Data words */
int ce; /* Correctable errors */
};
struct quirc_version_info {
int data_bytes;
int apat[QUIRC_MAX_ALIGNMENT];
struct quirc_rs_params ecc[4];
};
extern const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1];
#endif

421
source/quirc/version_db.c Normal file
View File

@@ -0,0 +1,421 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "quirc_internal.h"
const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1] = {
{0},
{ /* Version 1 */
.data_bytes = 26,
.apat = {0},
.ecc = {
{.bs = 26, .dw = 16, .ce = 4},
{.bs = 26, .dw = 19, .ce = 2},
{.bs = 26, .dw = 9, .ce = 8},
{.bs = 26, .dw = 13, .ce = 6}
}
},
{ /* Version 2 */
.data_bytes = 44,
.apat = {6, 18, 0},
.ecc = {
{.bs = 44, .dw = 28, .ce = 8},
{.bs = 44, .dw = 34, .ce = 4},
{.bs = 44, .dw = 16, .ce = 14},
{.bs = 44, .dw = 22, .ce = 11}
}
},
{ /* Version 3 */
.data_bytes = 70,
.apat = {6, 22, 0},
.ecc = {
{.bs = 70, .dw = 44, .ce = 13},
{.bs = 70, .dw = 55, .ce = 7},
{.bs = 35, .dw = 13, .ce = 11},
{.bs = 35, .dw = 17, .ce = 9}
}
},
{ /* Version 4 */
.data_bytes = 100,
.apat = {6, 26, 0},
.ecc = {
{.bs = 50, .dw = 32, .ce = 9},
{.bs = 100, .dw = 80, .ce = 10},
{.bs = 25, .dw = 9, .ce = 8},
{.bs = 50, .dw = 24, .ce = 13}
}
},
{ /* Version 5 */
.data_bytes = 134,
.apat = {6, 30, 0},
.ecc = {
{.bs = 67, .dw = 43, .ce = 12},
{.bs = 134, .dw = 108, .ce = 13},
{.bs = 33, .dw = 11, .ce = 11},
{.bs = 33, .dw = 15, .ce = 9}
}
},
{ /* Version 6 */
.data_bytes = 172,
.apat = {6, 34, 0},
.ecc = {
{.bs = 43, .dw = 27, .ce = 8},
{.bs = 86, .dw = 68, .ce = 9},
{.bs = 43, .dw = 15, .ce = 14},
{.bs = 43, .dw = 19, .ce = 12}
}
},
{ /* Version 7 */
.data_bytes = 196,
.apat = {6, 22, 38, 0},
.ecc = {
{.bs = 49, .dw = 31, .ce = 9},
{.bs = 98, .dw = 78, .ce = 10},
{.bs = 39, .dw = 13, .ce = 13},
{.bs = 32, .dw = 14, .ce = 9}
}
},
{ /* Version 8 */
.data_bytes = 242,
.apat = {6, 24, 42, 0},
.ecc = {
{.bs = 60, .dw = 38, .ce = 11},
{.bs = 121, .dw = 97, .ce = 12},
{.bs = 40, .dw = 14, .ce = 13},
{.bs = 40, .dw = 18, .ce = 11}
}
},
{ /* Version 9 */
.data_bytes = 292,
.apat = {6, 26, 46, 0},
.ecc = {
{.bs = 58, .dw = 36, .ce = 11},
{.bs = 146, .dw = 116, .ce = 15},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 36, .dw = 16, .ce = 10}
}
},
{ /* Version 10 */
.data_bytes = 346,
.apat = {6, 28, 50, 0},
.ecc = {
{.bs = 69, .dw = 43, .ce = 13},
{.bs = 86, .dw = 68, .ce = 9},
{.bs = 43, .dw = 15, .ce = 14},
{.bs = 43, .dw = 19, .ce = 12}
}
},
{ /* Version 11 */
.data_bytes = 404,
.apat = {6, 30, 54, 0},
.ecc = {
{.bs = 80, .dw = 50, .ce = 15},
{.bs = 101, .dw = 81, .ce = 10},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 12 */
.data_bytes = 466,
.apat = {6, 32, 58, 0},
.ecc = {
{.bs = 58, .dw = 36, .ce = 11},
{.bs = 116, .dw = 92, .ce = 12},
{.bs = 42, .dw = 14, .ce = 14},
{.bs = 46, .dw = 20, .ce = 14}
}
},
{ /* Version 13 */
.data_bytes = 532,
.apat = {6, 34, 62, 0},
.ecc = {
{.bs = 59, .dw = 37, .ce = 11},
{.bs = 133, .dw = 107, .ce = 13},
{.bs = 33, .dw = 11, .ce = 11},
{.bs = 44, .dw = 20, .ce = 12}
}
},
{ /* Version 14 */
.data_bytes = 581,
.apat = {6, 26, 46, 66, 0},
.ecc = {
{.bs = 65, .dw = 41, .ce = 12},
{.bs = 109, .dw = 87, .ce = 11},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 15 */
.data_bytes = 655,
.apat = {6, 26, 48, 70, 0},
.ecc = {
{.bs = 65, .dw = 41, .ce = 12},
{.bs = 109, .dw = 87, .ce = 11},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 16 */
.data_bytes = 733,
.apat = {6, 26, 50, 74, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 122, .dw = 98, .ce = 12},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 43, .dw = 19, .ce = 12}
}
},
{ /* Version 17 */
.data_bytes = 815,
.apat = {6, 30, 54, 78, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 135, .dw = 107, .ce = 14},
{.bs = 42, .dw = 14, .ce = 14},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 18 */
.data_bytes = 901,
.apat = {6, 30, 56, 82, 0},
.ecc = {
{.bs = 69, .dw = 43, .ce = 13},
{.bs = 150, .dw = 120, .ce = 15},
{.bs = 42, .dw = 14, .ce = 14},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 19 */
.data_bytes = 991,
.apat = {6, 30, 58, 86, 0},
.ecc = {
{.bs = 70, .dw = 44, .ce = 13},
{.bs = 141, .dw = 113, .ce = 14},
{.bs = 39, .dw = 13, .ce = 13},
{.bs = 47, .dw = 21, .ce = 13}
}
},
{ /* Version 20 */
.data_bytes = 1085,
.apat = {6, 34, 62, 90, 0},
.ecc = {
{.bs = 67, .dw = 41, .ce = 13},
{.bs = 135, .dw = 107, .ce = 14},
{.bs = 43, .dw = 15, .ce = 14},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 21 */
.data_bytes = 1156,
.apat = {6, 28, 50, 72, 92, 0},
.ecc = {
{.bs = 68, .dw = 42, .ce = 13},
{.bs = 144, .dw = 116, .ce = 14},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 22 */
.data_bytes = 1258,
.apat = {6, 26, 50, 74, 98, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 139, .dw = 111, .ce = 14},
{.bs = 37, .dw = 13, .ce = 12},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 23 */
.data_bytes = 1364,
.apat = {6, 30, 54, 78, 102, 0},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 24 */
.data_bytes = 1474,
.apat = {6, 28, 54, 80, 106, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 147, .dw = 117, .ce = 15},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 25 */
.data_bytes = 1588,
.apat = {6, 32, 58, 84, 110, 0},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 132, .dw = 106, .ce = 13},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 26 */
.data_bytes = 1706,
.apat = {6, 30, 58, 86, 114, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 142, .dw = 114, .ce = 14},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 27 */
.data_bytes = 1828,
.apat = {6, 34, 62, 90, 118, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 53, .dw = 23, .ce = 15}
}
},
{ /* Version 28 */
.data_bytes = 1921,
.apat = {6, 26, 50, 74, 98, 122, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 147, .dw = 117, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 29 */
.data_bytes = 2051,
.apat = {6, 30, 54, 78, 102, 126, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 146, .dw = 116, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 73, .dw = 45, .ce = 14}
}
},
{ /* Version 30 */
.data_bytes = 2185,
.apat = {6, 26, 52, 78, 104, 130, 0},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 31 */
.data_bytes = 2323,
.apat = {6, 30, 56, 82, 108, 134, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 32 */
.data_bytes = 2465,
.apat = {6, 34, 60, 86, 112, 138, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 33 */
.data_bytes = 2611,
.apat = {6, 30, 58, 96, 114, 142, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 34 */
.data_bytes = 2761,
.apat = {6, 34, 62, 90, 118, 146, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 35 */
.data_bytes = 2876,
.apat = {6, 30, 54, 78, 102, 126, 150},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 36 */
.data_bytes = 3034,
.apat = {6, 24, 50, 76, 102, 128, 154},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 37 */
.data_bytes = 3196,
.apat = {6, 28, 54, 80, 106, 132, 158},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 38 */
.data_bytes = 3362,
.apat = {6, 32, 58, 84, 110, 136, 162},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 39 */
.data_bytes = 3532,
.apat = {6, 26, 54, 82, 110, 138, 166},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 147, .dw = 117, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 40 */
.data_bytes = 3706,
.apat = {6, 30, 58, 86, 114, 142, 170},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 148, .dw = 118, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
}
};

931
source/remote.c Normal file
View File

@@ -0,0 +1,931 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "remote.h"
#include "loading.h"
#include "fs.h"
#include "unicode.h"
#include "music.h"
#include "pp2d/pp2d/pp2d.h"
static Instructions_s browser_instructions[MODE_AMOUNT] = {
{
.info_line = NULL,
.instructions = {
{
L"\uE000 Download theme",
L"\uE001 Go back"
},
{
L"\uE002 Hold for more",
L"\uE003 Preview theme"
},
{
L"\uE004 Previous page",
L"\uE005 Next page"
},
{
L"Exit",
NULL
}
}
},
{
.info_line = NULL,
.instructions = {
{
L"\uE000 Download splash",
L"\uE001 Go back"
},
{
L"\uE002 Hold for more",
L"\uE003 Preview splash"
},
{
L"\uE004 Previous page",
L"\uE005 Next page"
},
{
L"Exit",
NULL
}
}
}
};
static Instructions_s extra_instructions = {
.info_line = L"Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.info_line_color = COLOR_WHITE,
.instructions = {
{
L"\uE079 Jump to page",
L"\uE07A Search tags"
},
{
L"\uE07B Toggle splash/theme",
L"\uE07C Reload without cache"
},
{
NULL,
NULL
},
{
L"Exit",
NULL
}
}
};
static void load_remote_smdh(Entry_s * entry, size_t textureID, bool ignore_cache)
{
bool not_cached = true;
char * smdh_buf = NULL;
u32 smdh_size = load_data("/info.smdh", *entry, &smdh_buf);
Icon_s * smdh = (Icon_s *)smdh_buf;
not_cached = !smdh_size || ignore_cache; // if the size is 0, the file wasn't there
if(not_cached)
{
free(smdh_buf);
smdh_buf = NULL;
char * api_url = NULL;
asprintf(&api_url, THEMEPLAZA_SMDH_FORMAT, entry->tp_download_id);
smdh_size = http_get(api_url, NULL, &smdh_buf, INSTALL_NONE);
free(api_url);
smdh = (Icon_s *)smdh_buf;
}
if(!smdh_size)
{
free(smdh_buf);
utf8_to_utf16(entry->name, (u8*)"No name", 0x80);
utf8_to_utf16(entry->desc, (u8*)"No description", 0x100);
utf8_to_utf16(entry->author, (u8*)"Unknown author", 0x80);
entry->placeholder_color = RGBA8(rand() % 255, rand() % 255, rand() % 255, 255);
return;
}
memcpy(entry->name, smdh->name, 0x40*sizeof(u16));
memcpy(entry->desc, smdh->desc, 0x80*sizeof(u16));
memcpy(entry->author, smdh->author, 0x40*sizeof(u16));
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
for(u32 x = 0; x < width; x++)
{
for(u32 y = 0; y < height; y++)
{
unsigned int dest_pixel = (x + y*width);
unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
image[dest_pixel] = RGB565_TO_ABGR8(smdh->big_icon[source_pixel]);
}
}
pp2d_free_texture(textureID);
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
free(image);
if(not_cached)
{
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, entry->path), FS_ATTRIBUTE_DIRECTORY);
u16 path[0x107] = {0};
strucat(path, entry->path);
struacat(path, "/info.smdh");
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_size);
buf_to_file(smdh_size, fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_buf);
}
free(smdh_buf);
}
static void load_remote_entries(Entry_List_s * list, json_t *ids_array, bool ignore_cache, InstallType type)
{
list->entries_count = json_array_size(ids_array);
free(list->entries);
list->entries = calloc(list->entries_count, sizeof(Entry_s));
free(list->icons_ids);
list->icons_ids = calloc(list->entries_count, sizeof(ssize_t));
list->entries_loaded = list->entries_count;
size_t i = 0;
json_t * id = NULL;
json_array_foreach(ids_array, i, id)
{
draw_loading_bar(i, list->entries_count, type);
size_t offset = i;
Entry_s * current_entry = &list->entries[offset];
current_entry->tp_download_id = json_integer_value(id);
size_t textureID = list->texture_id_offset + i;
char * entry_path = NULL;
asprintf(&entry_path, CACHE_PATH_FORMAT, current_entry->tp_download_id);
utf8_to_utf16(current_entry->path, (u8*)entry_path, 0x106);
free(entry_path);
load_remote_smdh(current_entry, textureID, ignore_cache);
list->icons_ids[offset] = textureID;
}
}
static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mode, bool ignore_cache)
{
if(page > list->tp_page_count)
page = 1;
if(page <= 0)
page = list->tp_page_count;
InstallType loading_screen = INSTALL_NONE;
if(mode == MODE_THEMES)
loading_screen = INSTALL_LOADING_REMOTE_THEMES;
else if(mode == MODE_SPLASHES)
loading_screen = INSTALL_LOADING_REMOTE_SPLASHES;
draw_install(loading_screen);
char * page_json = NULL;
char * api_url = NULL;
asprintf(&api_url, THEMEPLAZA_PAGE_FORMAT, page, mode+1, list->tp_search);
u32 json_len = http_get(api_url, NULL, &page_json, INSTALL_NONE);
free(api_url);
if(json_len)
{
list->texture_id_offset = TEXTURE_REMOTE_ICONS;
list->tp_current_page = page;
list->mode = mode;
list->entry_size = entry_size[mode];
list->entries_per_screen_v = entries_per_screen_v[mode];
list->entries_per_screen_h = entries_per_screen_h[mode];
json_error_t error;
json_t *root = json_loadb(page_json, json_len, 0, &error);
if(root)
{
const char *key;
json_t *value;
json_object_foreach(root, key, value)
{
if(json_is_integer(value) && !strcmp(key, THEMEPLAZA_JSON_PAGE_COUNT))
list->tp_page_count = json_integer_value(value);
else if(json_is_array(value) && !strcmp(key, THEMEPLAZA_JSON_PAGE_IDS))
load_remote_entries(list, value, ignore_cache, loading_screen);
else if(json_is_string(value) && !strcmp(key, THEMEPLAZA_JSON_ERROR_MESSAGE) && !strcmp(json_string_value(value), THEMEPLAZA_JSON_ERROR_MESSAGE_NOT_FOUND))
throw_error("No results for this search.", ERROR_LEVEL_WARNING);
}
}
else
DEBUG("json error on line %d: %s\n", error.line, error.text);
json_decref(root);
}
else
throw_error("Couldn't download ThemePlaza data.\nMake sure WiFi is on.", ERROR_LEVEL_WARNING);
free(page_json);
}
static u16 previous_path_preview[0x106] = {0};
static bool load_remote_preview(Entry_s * entry, int * preview_offset)
{
bool not_cached = true;
if(!memcmp(&previous_path_preview, entry->path, 0x106*sizeof(u16))) return true;
char * preview_png = NULL;
u32 preview_size = load_data("/preview.png", *entry, &preview_png);
not_cached = !preview_size;
if(not_cached)
{
free(preview_png);
preview_png = NULL;
char * preview_url = NULL;
asprintf(&preview_url, THEMEPLAZA_PREVIEW_FORMAT, entry->tp_download_id);
draw_install(INSTALL_LOADING_REMOTE_PREVIEW);
preview_size = http_get(preview_url, NULL, &preview_png, INSTALL_LOADING_REMOTE_PREVIEW);
free(preview_url);
}
if(!preview_size)
{
free(preview_png);
return false;
}
bool ret = false;
u8 * image = NULL;
unsigned int width = 0, height = 0;
if((lodepng_decode32(&image, &width, &height, (u8*)preview_png, preview_size)) == 0) // no error
{
for(u32 i = 0; i < width; i++)
{
for(u32 j = 0; j < height; j++)
{
u32* pixel = (u32*)(image + (i + j*width) * 4);
*pixel = __builtin_bswap32(*pixel); //swap from RGBA to ABGR, needed for pp2d
}
}
// mark the new preview as loaded for optimisation
memcpy(&previous_path_preview, entry->path, 0x106*sizeof(u16));
// free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_REMOTE_PREVIEW);
pp2d_load_texture_memory(TEXTURE_REMOTE_PREVIEW, image, (u32)width, (u32)height);
*preview_offset = (width-400)/2;
ret = true;
}
else
{
throw_error("Corrupted/invalid preview.png", ERROR_LEVEL_WARNING);
}
free(image);
if(not_cached)
{
u16 path[0x107] = {0};
strucat(path, entry->path);
struacat(path, "/preview.png");
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, preview_size);
buf_to_file(preview_size, fsMakePath(PATH_UTF16, path), ArchiveSD, preview_png);
}
free(preview_png);
return ret;
}
static u16 previous_path_bgm[0x106] = {0};
static void load_remote_bgm(Entry_s * entry)
{
if(!memcmp(&previous_path_bgm, entry->path, 0x106*sizeof(u16))) return;
char * bgm_ogg = NULL;
u32 bgm_size = load_data("/bgm.ogg", *entry, &bgm_ogg);
if(!bgm_size)
{
free(bgm_ogg);
bgm_ogg = NULL;
char * bgm_url = NULL;
asprintf(&bgm_url, THEMEPLAZA_BGM_FORMAT, entry->tp_download_id);
draw_install(INSTALL_LOADING_REMOTE_BGM);
bgm_size = http_get(bgm_url, NULL, &bgm_ogg, INSTALL_LOADING_REMOTE_BGM);
free(bgm_url);
u16 path[0x107] = {0};
strucat(path, entry->path);
struacat(path, "/bgm.ogg");
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, bgm_size);
buf_to_file(bgm_size, fsMakePath(PATH_UTF16, path), ArchiveSD, bgm_ogg);
memcpy(&previous_path_bgm, entry->path, 0x106*sizeof(u16));
}
free(bgm_ogg);
}
static void download_remote_entry(Entry_s * entry, EntryMode mode)
{
char * download_url = NULL;
asprintf(&download_url, THEMEPLAZA_DOWNLOAD_FORMAT, entry->tp_download_id);
char * zip_buf = NULL;
char * filename = NULL;
draw_install(INSTALL_DOWNLOAD);
u32 zip_size = http_get(download_url, &filename, &zip_buf, INSTALL_DOWNLOAD);
free(download_url);
char path_to_file[0x107] = {0};
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
free(filename);
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
DEBUG("Saving to sd: %s\n", path_to_file);
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
free(zip_buf);
}
static SwkbdCallbackResult jump_menu_callback(void* page_number, const char** ppMessage, const char* text, size_t textlen)
{
int typed_value = atoi(text);
if(typed_value > *(json_int_t*)page_number)
{
*ppMessage = "The new page has to be\nsmaller or equal to the\nnumber of pages!";
return SWKBD_CALLBACK_CONTINUE;
}
else if(typed_value == 0)
{
*ppMessage = "The new position has to\nbe positive!";
return SWKBD_CALLBACK_CONTINUE;
}
return SWKBD_CALLBACK_OK;
}
static void jump_menu(Entry_List_s * list)
{
if(list == NULL) return;
char numbuf[64] = {0};
SwkbdState swkbd;
sprintf(numbuf, "%" JSON_INTEGER_FORMAT, list->tp_page_count);
int max_chars = strlen(numbuf);
swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 2, max_chars);
sprintf(numbuf, "%" JSON_INTEGER_FORMAT, list->tp_current_page);
swkbdSetInitialText(&swkbd, numbuf);
sprintf(numbuf, "Which page do you want to jump to?");
swkbdSetHintText(&swkbd, numbuf);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Jump", true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->tp_page_count);
memset(numbuf, 0, sizeof(numbuf));
SwkbdButton button = swkbdInputText(&swkbd, numbuf, sizeof(numbuf));
if(button == SWKBD_BUTTON_CONFIRM)
{
json_int_t newpage = (json_int_t)atoi(numbuf);
if(newpage != list->tp_current_page)
load_remote_list(list, newpage, list->mode, false);
}
}
static void search_menu(Entry_List_s * list)
{
const int max_chars = 256;
char * search = calloc(max_chars+1, sizeof(char));
SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_WESTERN, 2, max_chars);
swkbdSetHintText(&swkbd, "Which tags do you want to search for?");
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Search", true);
swkbdSetValidation(&swkbd, SWKBD_NOTBLANK, 0, max_chars);
SwkbdButton button = swkbdInputText(&swkbd, search, max_chars);
if(button == SWKBD_BUTTON_CONFIRM)
{
free(list->tp_search);
for(unsigned int i = 0; i < strlen(search); i++)
{
if(search[i] == ' ')
search[i] = '+';
}
list->tp_search = search;
load_remote_list(list, 1, list->mode, false);
}
else
{
free(search);
}
}
static void change_selected(Entry_List_s * list, int change_value)
{
if(abs(change_value) >= list->entries_count) return;
int newval = list->selected_entry + change_value;
if(abs(change_value) == 1)
{
if(newval < 0)
newval += list->entries_per_screen_h;
if(newval/list->entries_per_screen_h != list->selected_entry/list->entries_per_screen_h)
newval += list->entries_per_screen_h*(-change_value);
}
else
{
if(newval < 0)
newval += list->entries_per_screen_h*list->entries_per_screen_v;
newval %= list->entries_count;
}
list->selected_entry = newval;
}
bool themeplaza_browser(EntryMode mode)
{
bool downloaded = false;
bool preview_mode = false;
int preview_offset = 0;
audio_s * audio = NULL;
Entry_List_s list = {0};
Entry_List_s * current_list = &list;
current_list->tp_search = strdup("");
load_remote_list(current_list, 1, mode, false);
bool extra_mode = false;
while(aptMainLoop())
{
if(current_list->entries == NULL)
break;
if(preview_mode)
draw_preview(TEXTURE_REMOTE_PREVIEW, preview_offset);
else
{
Instructions_s instructions = browser_instructions[mode];
if(extra_mode)
instructions = extra_instructions;
draw_grid_interface(current_list, instructions);
}
pp2d_end_draw();
hidScanInput();
u32 kDown = hidKeysDown();
u32 kHeld = hidKeysHeld();
u32 kUp = hidKeysUp();
if(kDown & KEY_START)
{
exit:
quit = true;
downloaded = false;
if(audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
break;
}
if(extra_mode)
{
if(kUp & KEY_X)
extra_mode = false;
if(!extra_mode)
{
if((kDown | kHeld) & KEY_DLEFT)
{
change_mode:
mode++;
mode %= MODE_AMOUNT;
free(current_list->tp_search);
current_list->tp_search = strdup("");
load_remote_list(current_list, 1, mode, false);
}
else if((kDown | kHeld) & KEY_DUP)
{
jump_menu(current_list);
}
else if((kDown | kHeld) & KEY_DRIGHT)
{
load_remote_list(current_list, current_list->tp_current_page, mode, true);
}
else if((kDown | kHeld) & KEY_DDOWN)
{
search_menu(current_list);
}
}
continue;
}
int selected_entry = current_list->selected_entry;
Entry_s * current_entry = &current_list->entries[selected_entry];
if(kDown & KEY_Y)
{
toggle_preview:
if(!preview_mode)
{
preview_mode = load_remote_preview(current_entry, &preview_offset);
if(mode == MODE_THEMES)
{
load_remote_bgm(current_entry);
audio = calloc(1, sizeof(audio_s));
load_audio(*current_entry, audio);
play_audio(audio);
}
}
else
{
preview_mode = false;
if(mode == MODE_THEMES && audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
}
}
else if(kDown & KEY_B)
{
if(preview_mode)
{
preview_mode = false;
if(mode == MODE_THEMES && audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
}
else
break;
}
if(preview_mode)
goto touch;
if(kDown & KEY_A)
{
download_remote_entry(current_entry, mode);
downloaded = true;
}
else if(kDown & KEY_X)
{
extra_mode = true;
}
else if(kDown & KEY_L)
{
load_remote_list(current_list, current_list->tp_current_page-1, mode, false);
}
else if(kDown & KEY_R)
{
load_remote_list(current_list, current_list->tp_current_page+1, mode, false);
}
// Movement in the UI
else if(kDown & KEY_UP)
{
change_selected(current_list, -current_list->entries_per_screen_h);
}
else if(kDown & KEY_DOWN)
{
change_selected(current_list, current_list->entries_per_screen_h);
}
// Quick moving
else if(kDown & KEY_LEFT)
{
change_selected(current_list, -1);
}
else if(kDown & KEY_RIGHT)
{
change_selected(current_list, 1);
}
// Fast scroll using circle pad
else if(kHeld & KEY_CPAD_UP)
{
change_selected(current_list, -1);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_DOWN)
{
change_selected(current_list, 1);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_LEFT)
{
change_selected(current_list, -current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
}
else if(kHeld & KEY_CPAD_RIGHT)
{
change_selected(current_list, current_list->entries_per_screen_v);
svcSleepThread(FASTSCROLL_WAIT);
}
touch:
if((kDown | kHeld) & KEY_TOUCH)
{
touchPosition touch = {0};
hidTouchRead(&touch);
u16 x = touch.px;
u16 y = touch.py;
#define BETWEEN(min, x, max) (min < x && x < max)
int border = 16;
if(kDown & KEY_TOUCH)
{
if(preview_mode)
{
preview_mode = false;
if(mode == MODE_THEMES && audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
continue;
}
else if(y < 24)
{
if(BETWEEN(0, x, 80))
{
search_menu(current_list);
}
else if(BETWEEN(320-96, x, 320-72))
{
break;
}
else if(BETWEEN(320-72, x, 320-48))
{
goto exit;
}
else if(BETWEEN(320-48, x, 320-24))
{
goto toggle_preview;
}
else if(BETWEEN(320-24, x, 320))
{
goto change_mode;
}
}
else if(BETWEEN(240-24, y, 240) && BETWEEN(176, x, 320))
{
jump_menu(current_list);
}
else
{
if(BETWEEN(0, x, border))
{
load_remote_list(current_list, current_list->tp_current_page-1, mode, false);
}
else if(BETWEEN(320-border, x, 320))
{
load_remote_list(current_list, current_list->tp_current_page+1, mode, false);
}
}
}
else
{
if(preview_mode)
{
preview_mode = false;
continue;
}
else if(BETWEEN(24, y, 240-24))
{
if(BETWEEN(border, x, 320-border))
{
x -= border;
x /= current_list->entry_size;
y -= 24;
y /= current_list->entry_size;
int new_selected = y*current_list->entries_per_screen_h + x;
if(new_selected < current_list->entries_count)
current_list->selected_entry = new_selected;
}
}
}
}
}
free(current_list->entries);
free(current_list->icons_ids);
free(current_list->tp_search);
return downloaded;
}
u32 http_get(const char *url, char ** filename, char ** buf, InstallType install_type)
{
Result ret;
httpcContext context;
char *new_url = NULL;
u32 status_code;
u32 content_size = 0;
u32 read_size = 0;
u32 size = 0;
char *last_buf;
do {
ret = httpcOpenContext(&context, HTTPC_METHOD_GET, url, 1);
if (ret != 0)
{
httpcCloseContext(&context);
if (new_url != NULL) free(new_url);
DEBUG("httpcOpenContext %.8lx\n", ret);
return 0;
}
ret = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); // should let us do https
ret = httpcSetKeepAlive(&context, HTTPC_KEEPALIVE_ENABLED);
ret = httpcAddRequestHeaderField(&context, "User-Agent", USER_AGENT);
ret = httpcAddRequestHeaderField(&context, "Connection", "Keep-Alive");
ret = httpcBeginRequest(&context);
if (ret != 0)
{
httpcCloseContext(&context);
if (new_url != NULL) free(new_url);
DEBUG("httpcBeginRequest %.8lx\n", ret);
return 0;
}
ret = httpcGetResponseStatusCode(&context, &status_code);
if(ret!=0){
httpcCloseContext(&context);
if(new_url!=NULL) free(new_url);
DEBUG("httpcGetResponseStatusCode\n");
return 0;
}
if ((status_code >= 301 && status_code <= 303) || (status_code >= 307 && status_code <= 308))
{
if (new_url == NULL) new_url = malloc(0x1000);
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)
{
httpcCloseContext(&context);
if (new_url != NULL) free(new_url);
DEBUG("status_code, %lu\n", status_code);
return 0;
}
ret = httpcGetDownloadSizeState(&context, NULL, &content_size);
if (ret != 0)
{
httpcCloseContext(&context);
if (new_url != NULL) free(new_url);
DEBUG("httpcGetDownloadSizeState\n");
return 0;
}
*buf = malloc(0x1000);
if (*buf == NULL)
{
httpcCloseContext(&context);
free(new_url);
DEBUG("malloc\n");
return 0;
}
if(filename)
{
char *content_disposition = calloc(1024, sizeof(char));
ret = httpcGetResponseHeader(&context, "Content-Disposition", content_disposition, 1024);
if (ret != 0)
{
free(content_disposition);
free(new_url);
free(*buf);
DEBUG("httpcGetResponseHeader\n");
return 0;
}
char * tok = strtok(content_disposition, "\"");
tok = strtok(NULL, "\"");
if(!(tok))
{
free(content_disposition);
free(new_url);
free(*buf);
throw_error("Target is not valid!", ERROR_LEVEL_WARNING);
DEBUG("filename\n");
return 0;
}
char *illegal_characters = "\"?;:/\\+";
for (size_t i = 0; i < strlen(tok); i++)
{
for (size_t n = 0; n < strlen(illegal_characters); n++)
{
if ((tok)[i] == illegal_characters[n])
{
(tok)[i] = '-';
}
}
}
*filename = calloc(1024, sizeof(char));
strcpy(*filename, tok);
free(content_disposition);
}
do {
ret = httpcDownloadData(&context, (*(u8**)buf) + size, 0x1000, &read_size);
size += read_size;
if(content_size && install_type != INSTALL_NONE)
draw_loading_bar(size, content_size, install_type);
if (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING)
{
last_buf = *buf;
*buf = realloc(*buf, size + 0x1000);
if (*buf == NULL)
{
httpcCloseContext(&context);
free(new_url);
free(last_buf);
DEBUG("NULL\n");
return 0;
}
}
} while (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING);
last_buf = *buf;
*buf = realloc(*buf, size);
if (*buf == NULL)
{
httpcCloseContext(&context);
free(new_url);
free(last_buf);
DEBUG("realloc\n");
return 0;
}
httpcCloseContext(&context);
free(new_url);
DEBUG("size: %lu\n", size);
return size;
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,81 +26,105 @@
#include "splashes.h"
#include "unicode.h"
#include "fs.h"
#include "draw.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;
remove("/luma/splash.bin");
remove("/luma/splashbottom.bin");
}
u32 entries_read = 1;
while (entries_read)
void splash_install(Entry_s splash)
{
char *screen_buf = NULL;
u32 size = load_data("/splash.bin", splash, &screen_buf);
if(size != 0)
{
FS_DirectoryEntry entry = {0};
res = FSDIR_Read(dir_handle, &entries_read, 1, &entry);
if (R_FAILED(res) || entries_read == 0)
break;
remake_file(fsMakePath(PATH_ASCII, "/luma/splash.bin"), ArchiveSD, size);
buf_to_file(size, fsMakePath(PATH_ASCII, "/luma/splash.bin"), ArchiveSD, screen_buf);
}
if (!(entry.attributes & FS_ATTRIBUTE_DIRECTORY))
u32 bottom_size = load_data("/splashbottom.bin", splash, &screen_buf);
if(bottom_size != 0)
{
remake_file(fsMakePath(PATH_ASCII, "/luma/splashbottom.bin"), ArchiveSD, bottom_size);
buf_to_file(bottom_size, fsMakePath(PATH_ASCII, "/luma/splashbottom.bin"), ArchiveSD, screen_buf);
}
if(size == 0 && bottom_size == 0)
{
throw_error("No splash.bin or splashbottom.bin found.\nIs this a splash?", 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("WARNING: Splashes are disabled in Luma Config", 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;
}
*splash_count += entries_read;
*splashes_list= realloc(*splashes_list, (*splash_count) * sizeof(Splash_s));
if (splashes_list == NULL)
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;
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_install(Splash_s splash_to_install)
{
char *screen_buf;
u32 size = file_to_buf(fsMakePath(PATH_UTF16, splash_to_install.top_path), ArchiveSD, &screen_buf);
if (size)
{
buf_to_file(size, "/luma/splash.bin", ArchiveSD, screen_buf);
free(screen_buf);
}
size = file_to_buf(fsMakePath(PATH_UTF16, splash_to_install.bottom_path), ArchiveSD, &screen_buf);
if (size)
{
buf_to_file(size, "/luma/splashbottom.bin", ArchiveSD, screen_buf);
free(screen_buf);
}
#endif
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,415 +27,352 @@
#include "themes.h"
#include "unicode.h"
#include "fs.h"
#include "draw.h"
#include "pp2d/pp2d/pp2d.h"
#include "pp2d/pp2d/lodepng.h"
#define BODY_CACHE_SIZE 0x150000
#define BGM_MAX_SIZE 0x337000
void load_theme_preview(Theme_s *theme)
{
//free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_PREVIEW);
char *preview_buffer = NULL;
u64 size = 0;
if (!(theme->is_zip))
{
u16 path_to_preview[0x106] = {0};
strucat(path_to_preview, theme->path);
struacat(path_to_preview, "/preview.png");
size = file_to_buf(fsMakePath(PATH_UTF16, path_to_preview), ArchiveSD, &preview_buffer);
} else {
size = zip_file_to_buf("preview.png", theme->path, &preview_buffer);
}
if (!size)
{
free(preview_buffer);
return;
}
u8 * image = NULL;
unsigned int width = 0, height = 0;
int result = lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size);
if (result == 0) // no error
{
for (u32 i = 0; i < width; i++)
{
for (u32 j = 0; j < height; j++)
{
u32 p = (i + j*width) * 4;
u8 r = *(u8*)(image + p);
u8 g = *(u8*)(image + p + 1);
u8 b = *(u8*)(image + p + 2);
u8 a = *(u8*)(image + p + 3);
*(image + p) = a;
*(image + p + 1) = b;
*(image + p + 2) = g;
*(image + p + 3) = r;
}
}
theme->has_preview = true;
pp2d_load_texture_memory(TEXTURE_PREVIEW, image, (u32)width, (u32)height);
}
free(image);
free(preview_buffer);
}
static void parse_smdh(Theme_s *theme, ssize_t textureID)
{
char *info_buffer = NULL;
u64 size = 0;
if (!(theme->is_zip))
{
u16 path_to_smdh[0x106] = {0};
strucat(path_to_smdh, theme->path);
struacat(path_to_smdh, "/info.smdh");
size = file_to_buf(fsMakePath(PATH_UTF16, path_to_smdh), ArchiveSD, &info_buffer);
} else {
size = zip_file_to_buf("info.smdh", theme->path, &info_buffer);
}
if (!size)
{
free(info_buffer);
return;
}
memcpy(theme->name, info_buffer + 0x08, 0x80);
memcpy(theme->desc, info_buffer + 0x88, 0x100);
memcpy(theme->author, info_buffer + 0x188, 0x80);
u16 *icon_data = malloc(0x1200);
memcpy(icon_data, info_buffer + 0x24C0, 0x1200);
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
for (u32 x = 0; x < width; x++)
{
for (u32 y = 0; y < height; y++)
{
unsigned int dest_pixel = (x + y*width);
unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
u8 r = ((icon_data[source_pixel] >> 11) & 0b11111) << 3;
u8 g = ((icon_data[source_pixel] >> 5) & 0b111111) << 2;
u8 b = (icon_data[source_pixel] & 0b11111) << 3;
u8 a = 0xFF;
image[dest_pixel] = (r << 24) | (g << 16) | (b << 8) | a;
}
}
theme->has_icon = true;
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
theme->icon_id = textureID;
free(image);
free(icon_data);
free(info_buffer);
}
Result get_themes(Theme_s **themes_list, int *theme_count)
static Result install_theme_internal(Entry_List_s themes, int installmode)
{
Result res = 0;
Handle dir_handle;
res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, THEMES_PATH));
if (R_FAILED(res))
return res;
char* music = NULL;
u32 music_size = 0;
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES] = {0};
char* body = NULL;
u32 body_size = 0;
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0};
u32 entries_read = 1;
while (entries_read)
if(installmode & THEME_INSTALL_SHUFFLE)
{
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) && strcmp(entry.shortExt, "ZIP"))
continue;
*theme_count += entries_read;
*themes_list = realloc(*themes_list, (*theme_count) * sizeof(Theme_s));
if (*themes_list == NULL)
break;
Theme_s* current_theme = &(*themes_list)[*theme_count-1];
memset(current_theme, 0, sizeof(Theme_s));
u16 theme_path[0x106] = {0};
struacat(theme_path, THEMES_PATH);
strucat(theme_path, entry.name);
char pathchar[0x106] = {0};
utf16_to_utf8((u8*)pathchar, theme_path, 0x106);
memcpy(current_theme->path, theme_path, 0x106 * sizeof(u16));
current_theme->is_zip = !strcmp(entry.shortExt, "ZIP");
ssize_t iconID = TEXTURE_PREVIEW + *theme_count;
parse_smdh(current_theme, iconID);
if(themes.shuffle_count < 2)
{
DEBUG("not enough themes selected for shuffle\n");
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
}
FSDIR_Close(dir_handle);
return res;
}
// Install a single theme
Result single_install(Theme_s theme_to_install)
{
char *body;
char *music;
char *savedata_buf;
char *thememanage_buf;
u32 body_size;
u32 music_size;
u32 savedata_size;
printf("Writing SaveData.dat...\n");
savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
savedata_buf[0x141b] = 0;
memset(&savedata_buf[0x13b8], 0, 8);
savedata_buf[0x13bd] = 3;
savedata_buf[0x13b8] = 0xff;
u32 size = buf_to_file(savedata_size, "/SaveData.dat", ArchiveHomeExt, savedata_buf);
free(savedata_buf);
// Open body cache file. Test if theme is zipped
printf("Writing BodyCache.bin...\n");
if (theme_to_install.is_zip)
if(themes.shuffle_count > MAX_SHUFFLE_THEMES)
{
body_size = zip_file_to_buf("body_LZ.bin", theme_to_install.path, &body);
}
else
{
u16 path[0x106] = {0};
memcpy(path, theme_to_install.path, 0x106 * sizeof(u16));
struacat(path, "/body_lz.bin");
body_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &body);
DEBUG("too many themes selected for shuffle\n");
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
}
if (body_size == 0)
char * padded = NULL;
int shuffle_count = 0;
Handle body_cache_handle;
if(installmode & THEME_INSTALL_BODY)
{
remake_file(fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), ArchiveThemeExt, BODY_CACHE_SIZE * MAX_SHUFFLE_THEMES);
FSUSER_OpenFile(&body_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), FS_OPEN_WRITE, 0);
}
for(int i = 0; i < themes.entries_count; i++)
{
Entry_s * current_theme = &themes.entries[i];
if(current_theme->in_shuffle)
{
if(installmode & THEME_INSTALL_BODY)
{
body_size = load_data("/body_LZ.bin", *current_theme, &body);
if(body_size == 0)
{
free(body);
puts("bodyrip");
DEBUG("body not found\n");
throw_error("No body_LZ.bin found - is this a theme?", ERROR_LEVEL_WARNING);
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
}
size = buf_to_file(body_size, "/BodyCache.bin", ArchiveThemeExt, body); // Write body data to file
shuffle_body_sizes[shuffle_count] = body_size;
padded = calloc(BODY_CACHE_SIZE, sizeof(char));
memcpy(padded, body, body_size);
free(body);
if (size == 0) return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
FSFILE_Write(body_cache_handle, NULL, BODY_CACHE_SIZE * shuffle_count, padded, BODY_CACHE_SIZE, FS_WRITE_FLUSH);
printf("Writing BgmCache.bin...\n");
if (theme_to_install.is_zip) // Same as above but this time with bgm
{
music_size = zip_file_to_buf("bgm.bcstm", theme_to_install.path, &music);
} else {
u16 path[0x106] = {0};
memcpy(path, theme_to_install.path, 0x106 * sizeof(u16));
struacat(path, "/bgm.bcstm");
music_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &music);
free(padded);
padded = NULL;
}
if (music_size == 0)
if(installmode & THEME_INSTALL_BGM)
{
char bgm_cache_path[26] = {0};
sprintf(bgm_cache_path, "/BgmCache_%.2i.bin", shuffle_count);
if(current_theme->no_bgm_shuffle)
{
music = NULL;
music_size = 0;
}
else
{
music_size = load_data("/bgm.bcstm", *current_theme, &music);
if(music_size > BGM_MAX_SIZE)
{
free(music);
music = calloc(1, 3371008);
} else if (music_size > 3371008) {
DEBUG("bgm too big\n");
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
}
}
shuffle_music_sizes[shuffle_count] = music_size;
remake_file(fsMakePath(PATH_ASCII, bgm_cache_path), ArchiveThemeExt, BGM_MAX_SIZE);
Handle bgm_cache_handle;
FSUSER_OpenFile(&bgm_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, bgm_cache_path), FS_OPEN_WRITE, 0);
padded = calloc(BGM_MAX_SIZE, sizeof(char));
if(!current_theme->no_bgm_shuffle)
{
memcpy(padded, music, music_size);
free(music);
puts("musicrip");
}
FSFILE_Write(bgm_cache_handle, NULL, 0, padded, BGM_MAX_SIZE, FS_WRITE_FLUSH);
FSFILE_Close(bgm_cache_handle);
free(padded);
padded = NULL;
}
shuffle_count++;
}
}
if(installmode & THEME_INSTALL_BGM)
{
char * blank = calloc(BGM_MAX_SIZE, sizeof(char));
for(int i = shuffle_count; i < MAX_SHUFFLE_THEMES; i++)
{
char bgm_cache_path[26] = {0};
sprintf(bgm_cache_path, "/BgmCache_%.2i.bin", i);
remake_file(fsMakePath(PATH_ASCII, bgm_cache_path), ArchiveThemeExt, BGM_MAX_SIZE);
buf_to_file(BGM_MAX_SIZE, fsMakePath(PATH_ASCII, bgm_cache_path), ArchiveThemeExt, blank);
}
free(blank);
}
if(installmode & THEME_INSTALL_BODY)
{
FSFILE_Close(body_cache_handle);
}
}
else
{
Entry_s current_theme = themes.entries[themes.selected_entry];
if(installmode & THEME_INSTALL_BODY)
{
body_size = load_data("/body_LZ.bin", current_theme, &body);
if(body_size == 0)
{
free(body);
DEBUG("body not found\n");
throw_error("No body_LZ.bin found - is this a theme?", ERROR_LEVEL_WARNING);
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
}
res = buf_to_file(body_size, fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, body); // Write body data to file
free(body);
if(R_FAILED(res)) return res;
}
if(installmode & THEME_INSTALL_BGM)
{
music_size = load_data("/bgm.bcstm", current_theme, &music);
if(music_size > BGM_MAX_SIZE)
{
free(music);
DEBUG("bgm too big\n");
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
}
size = buf_to_file(music_size, "/BgmCache.bin", ArchiveThemeExt, music);
res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
free(music);
if (size == 0) return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
if(R_FAILED(res)) return res;
} else
{
music = calloc(BGM_MAX_SIZE, 1);
res = buf_to_file(BGM_MAX_SIZE, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
free(music);
}
}
printf("Writing ThemeManage.bin...\n");
//----------------------------------------
char* thememanage_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
thememanage_buf[0x00] = 1;
thememanage_buf[0x01] = 0;
thememanage_buf[0x02] = 0;
thememanage_buf[0x03] = 0;
thememanage_buf[0x04] = 0;
thememanage_buf[0x05] = 0;
thememanage_buf[0x06] = 0;
thememanage_buf[0x07] = 0;
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
u32 *body_size_location = (u32*)(&thememanage_buf[0x8]);
u32 *music_size_location = (u32*)(&thememanage_buf[0xC]);
*body_size_location = body_size;
*music_size_location = music_size;
theme_manage->unk1 = 1;
theme_manage->unk2 = 0;
thememanage_buf[0x10] = 0xFF;
thememanage_buf[0x14] = 0x01;
thememanage_buf[0x18] = 0xFF;
thememanage_buf[0x1D] = 0x02;
if(installmode & THEME_INSTALL_SHUFFLE)
{
theme_manage->music_size = 0;
theme_manage->body_size = 0;
memset(&thememanage_buf[0x338], 0, 4);
memset(&thememanage_buf[0x340], 0, 4);
memset(&thememanage_buf[0x360], 0, 4);
memset(&thememanage_buf[0x368], 0, 4);
size = buf_to_file(0x800, "/ThemeManage.bin", ArchiveThemeExt, thememanage_buf);
for(int i = 0; i < MAX_SHUFFLE_THEMES; i++)
{
theme_manage->shuffle_body_sizes[i] = shuffle_body_sizes[i];
theme_manage->shuffle_music_sizes[i] = shuffle_music_sizes[i];
}
}
else
{
if(installmode & THEME_INSTALL_BGM)
theme_manage->music_size = music_size;
if(installmode & THEME_INSTALL_BODY)
theme_manage->body_size = body_size;
}
theme_manage->unk3 = 0xFF;
theme_manage->unk4 = 1;
theme_manage->dlc_theme_content_index = 0xFF;
theme_manage->use_theme_cache = 0x0200;
res = buf_to_file(0x800, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, thememanage_buf);
free(thememanage_buf);
if(R_FAILED(res)) return res;
//----------------------------------------
printf("Done!\n");
//----------------------------------------
char* savedata_buf = NULL;
u32 savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
SaveData_dat_s* savedata = (SaveData_dat_s*)savedata_buf;
memset(&savedata->theme_entry, 0, sizeof(ThemeEntry_s));
savedata->theme_entry.type = 3;
savedata->theme_entry.index = 0xff;
savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE);
if(installmode & THEME_INSTALL_SHUFFLE)
{
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES);
for(int i = 0; i < themes.shuffle_count; i++)
{
savedata->shuffle_themes[i].type = 3;
savedata->shuffle_themes[i].index = i;
}
}
res = buf_to_file(savedata_size, fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, savedata_buf);
free(savedata_buf);
if(R_FAILED(res)) return res;
//----------------------------------------
return 0;
}
Result shuffle_install(Theme_s *themes_list, int theme_count)
inline Result theme_install(Entry_s theme)
{
u8 count = 0;
Theme_s *shuffle_themes[10] = {0};
u32 body_sizes[10] = {0};
u32 bgm_sizes[10] = {0};
for (int i = 0; i < theme_count; i++)
{
if (count > 9) return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
if (themes_list[i].in_shuffle)
{
shuffle_themes[count++] = &themes_list[i];
}
}
for (int i = 0; i < count; i++)
{
printu(shuffle_themes[i]->name);
}
Entry_List_s list = {0};
list.entries_count = 1;
list.entries = &theme;
list.selected_entry = 0;
return install_theme_internal(list, THEME_INSTALL_BODY | THEME_INSTALL_BGM);
}
char *savedata_buf;
u32 size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
if (size == 0)
{
return MAKERESULT(RL_USAGE, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
}
inline Result bgm_install(Entry_s theme)
{
Entry_List_s list = {0};
list.entries_count = 1;
list.entries = &theme;
list.selected_entry = 0;
return install_theme_internal(list, THEME_INSTALL_BGM);
}
savedata_buf[0x141b] = 1;
memset(&savedata_buf[0x13b8], 0, 8);
savedata_buf[0x13bd] = 3;
savedata_buf[0x13b8] = 0xff;
inline Result no_bgm_install(Entry_s theme)
{
Entry_List_s list = {0};
list.entries_count = 1;
list.entries = &theme;
list.selected_entry = 0;
return install_theme_internal(list, THEME_INSTALL_BODY);
}
for (int i = 0; i < 10; i++)
{
memset(&savedata_buf[0x13c0 + 0x8 * i], 0, 8); // clear any existing theme structure. 8 is the length of the theme structure, so 8 * i is the pos of the current one
if (count > i) // if we are still installing themes...
{
savedata_buf[0x13c0 + (8 * i)] = i; // index
savedata_buf[0x13c0 + (8 * i) + 5] = 3; // persistence (?)
}
}
inline Result shuffle_install(Entry_List_s themes)
{
return install_theme_internal(themes, THEME_INSTALL_SHUFFLE | THEME_INSTALL_BODY | THEME_INSTALL_BGM);
}
buf_to_file(size, "/SaveData.dat", ArchiveHomeExt, savedata_buf);
void themes_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* savedata_buf = NULL;
u32 savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
if(!savedata_size) return;
SaveData_dat_s* savedata = (SaveData_dat_s*)savedata_buf;
bool shuffle = savedata->shuffle;
free(savedata_buf);
remake_file("/BodyCache_rd.bin", ArchiveThemeExt, 0x150000 * 10); // Enough space for 10 theme files
Handle body_cache_handle;
FSUSER_OpenFile(&body_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), FS_OPEN_WRITE, 0);
for (int i = 0; i < 10; i++)
{
if (count > i)
{
if (shuffle_themes[i]->is_zip)
{
char *body_buf;
u32 body_size = zip_file_to_buf("body_LZ.bin", shuffle_themes[i]->path, &body_buf);
body_sizes[i] = body_size;
FSFILE_Write(body_cache_handle, NULL, 0x150000 * i, body_buf, body_size, FS_WRITE_FLUSH);
free(body_buf);
} else {
u16 path[0x106] = {0};
strucat(path, shuffle_themes[i]->path);
struacat(path, "/body_LZ.bin");
char *body_buf;
u32 body_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &body_buf);
body_sizes[i] = body_size;
FSFILE_Write(body_cache_handle, NULL, 0x150000 * i, body_buf, body_size, FS_WRITE_FLUSH);
free(body_buf);
}
}
}
#define HASH_SIZE_BYTES 256/8
u8 body_hash[MAX_SHUFFLE_THEMES][HASH_SIZE_BYTES];
memset(body_hash, 0, MAX_SHUFFLE_THEMES*HASH_SIZE_BYTES);
FSFILE_Close(body_cache_handle);
char* thememanage_buf = NULL;
u32 theme_manage_size = file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
if(!theme_manage_size) return;
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
for (int i = 0; i < 10; i++)
{
char bgm_cache_path[17] = {0};
sprintf(bgm_cache_path, "/BgmCache_%.2i.bin", i);
remake_file(bgm_cache_path, ArchiveThemeExt, 3371008);
if (count > i)
{
char *music_buf;
u32 music_size;
if (shuffle_themes[i]->is_zip)
{
music_size = zip_file_to_buf("bgm.bcstm", shuffle_themes[i]->path, &music_buf);
} else {
u16 path[0x106] = {0};
strucat(path, shuffle_themes[i]->path);
struacat(path, "/bgm.bcstm");
music_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &music_buf);
}
if (!music_size)
{
char *empty = calloc(1, 3371008);
buf_to_file(3371008, bgm_cache_path, ArchiveThemeExt, empty);
bgm_sizes[i] = 0;
free(empty);
continue;
}
bgm_sizes[i] = music_size;
buf_to_file(music_size, bgm_cache_path, ArchiveThemeExt, music_buf);
free(music_buf);
} else {
char *empty = calloc(1, 3371008);
buf_to_file(3371008, bgm_cache_path, ArchiveThemeExt, empty);
bgm_sizes[i] = 0;
free(empty);
}
}
char *thememanage_buf;
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
thememanage_buf[0x00] = 1; // Unknown, normally 0x1 with size of 4
thememanage_buf[0x01] = 0;
thememanage_buf[0x02] = 0;
thememanage_buf[0x03] = 0;
thememanage_buf[0x04] = 0; // Unknown, normally 0 with size of 4
thememanage_buf[0x05] = 0;
thememanage_buf[0x06] = 0;
thememanage_buf[0x07] = 0;
thememanage_buf[0x10] = 0xFF; // Unknown
thememanage_buf[0x14] = 0x01; // Unkown
thememanage_buf[0x18] = 0xFF; // DLC theme index - 0xFF indicates no DLC theme
thememanage_buf[0x1D] = 0x02; // Unknown, usually 0x200 to indicate theme-cache is being used
u32 *bodysizeloc = (u32*) (&thememanage_buf[0x08]); // Set non-shuffle theme sizes to 0
u32 *bgmsizeloc = (u32*) (&thememanage_buf[0x0C]);
*bodysizeloc = (u32) 0;
*bgmsizeloc = (u32) 0;
for (int i = 0; i < 10; i++)
{
bodysizeloc = (u32*) (&thememanage_buf[0x338 + (4 * i)]); // body size info for shuffle themes starts at 0x338 and is 4 bytes for each theme
bgmsizeloc = (u32*) (&thememanage_buf[0x360 + (4 * i)]); // same thing for bgm but starting at 0x360
*bodysizeloc = body_sizes[i]; // We don't need to check if we've already installed all the themes because all sizes initialized to 0
*bgmsizeloc = bgm_sizes[i];
}
buf_to_file(0x800, "/ThemeManage.bin", ArchiveThemeExt, thememanage_buf);
u32 single_body_size = theme_manage->body_size;
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0};
memcpy(shuffle_body_sizes, theme_manage->shuffle_body_sizes, sizeof(u32)*MAX_SHUFFLE_THEMES);
free(thememanage_buf);
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_COMMON, RD_SUCCESS);
if(shuffle)
{
char * body_buf = NULL;
u32 body_cache_size = file_to_buf(fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), ArchiveThemeExt, &body_buf);
if(!body_cache_size) return;
for(int i = 0; i < MAX_SHUFFLE_THEMES; i++)
{
FSUSER_UpdateSha256Context(body_buf + BODY_CACHE_SIZE*i, shuffle_body_sizes[i], body_hash[i]);
}
free(body_buf);
}
else
{
char * body_buf = NULL;
u32 body_size = file_to_buf(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &body_buf);
if(!body_size) return;
u8 * hash = body_hash[0];
FSUSER_UpdateSha256Context(body_buf, single_body_size, hash);
free(body_buf);
}
int total_installed = 0;
for(int i = 0; i < list->entries_count && total_installed < MAX_SHUFFLE_THEMES && arg->run_thread; i++)
{
Entry_s * theme = &list->entries[i];
char * theme_body = NULL;
u32 theme_body_size = load_data("/body_LZ.bin", *theme, &theme_body);
if(!theme_body_size) return;
u8 theme_body_hash[HASH_SIZE_BYTES];
FSUSER_UpdateSha256Context(theme_body, theme_body_size, theme_body_hash);
free(theme_body);
for(int j = 0; j < MAX_SHUFFLE_THEMES; j++)
{
if(!memcmp(body_hash[j], theme_body_hash, HASH_SIZE_BYTES))
{
theme->installed = true;
total_installed++;
if(!shuffle) break; //only need to check the first if the installed theme inst shuffle
}
}
}
#endif
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -48,7 +48,9 @@ void printu(u16 *input)
ssize_t buf_len = in_len + 1; // Plus 1 for proper null termination
wchar_t *buf = calloc(buf_len, sizeof(wchar_t));
utf16_to_utf32((u32*)buf, input, buf_len);
printf("%ls\n", buf);
char cbuf[0x106];
sprintf(cbuf, "%ls\n", buf);
DEBUG(cbuf);
free(buf);
}