Compare commits

...

158 Commits

Author SHA1 Message Date
yellows8
2c53af56cc
Release 3.5.1 2022-06-14 18:20:43 -04:00
yellows8
7bdfce172c
Fix temperature display on [14.0.0+]. 2022-03-24 20:53:19 -04:00
imxyd
34f7f16cb1 Update language.c
Improve Chinese language
2022-01-22 19:34:54 +01:00
yellows8
73d7108a62
Release 3.5.0 2021-10-29 19:47:15 -04:00
yellows8
422039f727
Use physfs instead of minizip for asset loading, etc. 2021-10-22 13:18:30 -04:00
yellows8
3f73855170
Added theme support for dirs and .zip via physfs. Closes #121. 2021-10-21 15:26:30 -04:00
qazrfv1234
6e654ec2af Update language.c (typo fixed) 2021-08-27 20:06:28 +02:00
Dave Murphy
5c85dd784b 3.4.1 release 2021-07-16 14:53:06 +01:00
Dave Murphy
8c87b1c46f ensure build picks up version from Makefile 2021-07-16 14:52:34 +01:00
fincs
47fd18cabb
Update psm charger type 2021-07-15 21:59:49 +02:00
Chronoss
c1640f4b54
Update language.c (#130) 2021-06-09 08:21:37 +01:00
HamletDuFromage
aef37e10f7
stat(path) before trying to launch a nro (#129) 2021-06-09 08:16:37 +01:00
Mehdi Khelfi
95411fe5e9
Added some spanish and french strings (#132) 2021-04-18 14:51:03 +02:00
yellows8
65f23b0bde
Release 3.4.0 2020-12-19 12:11:00 -05:00
fincs
20c1f00972
Update for libnx hid-refactor 2020-12-02 15:01:40 +01:00
fincs
fcbc56acc4
Improve performance by using deko3d to do the framebuffer conversion on the GPU 2020-11-11 17:00:25 +01:00
fincs
244d058f1b
Minor fixes 2020-11-11 12:40:41 +01:00
Mehdi Khelfi
df09e9ed1b Added French strings
Added 2 missing french translations in language.c
2020-10-31 18:52:55 +01:00
Chris Bradel
45efcfcb98
added momentum to touch drags (#124)
* added momentum to touch drags
2020-06-26 11:35:11 -04:00
yellows8
16958ac4ba
Fixed warnings, menuTimer multi-definition, and updated for latest libnx. 2020-05-05 18:47:40 -04:00
HookedBehemoth
c69f4c56c5 update for libnx pl init change 2020-04-15 18:18:22 +02:00
yellows8
3e451a9c8b
Added resources/eth_none_icon.png which was missing. 2020-04-10 00:54:10 -04:00
yellows8
090054fc86
Release v3.3.0 2020-04-06 10:37:55 -04:00
yellows8
e2a16a83e0
Netloader now uses SO_REUSEADDR, this fixes netloader activation temporarily failing after a netloader error previously occurred. 2020-04-06 10:36:47 -04:00
yellows8
ba330cfd84
Actually display errors from netloader activation. 2020-04-06 10:35:43 -04:00
yellows8
f4067d64f3
Added app_args config setting for fileassoc. 2020-04-06 10:34:44 -04:00
yellows8
88fe495bd8
Don't delete the fileassoc entry when loading the icon failed. 2020-04-04 12:55:55 -04:00
fincs
7f1a9f4f29
U64_MAX -> UINT64_MAX 2020-03-16 16:23:36 +01:00
yellows8
ec24d595d6
Updated the default highlightGradientEdgeColor. 2020-01-08 17:03:49 -05:00
Ivan Mazzoli
5dfb9ec328 Updated italian translation and added new ones 2020-01-07 16:13:39 +01:00
yellows8
a4ad7b01b7
The icon sizes for theme-layout menuListIcon/menuActiveEntryIcon can now be controlled with the size field, with the internal imageSize field being used when size is invalid. When entering Theme Menu, the initially selected menu-entry is now the currently configured theme. Minor improvements. 2020-01-04 18:08:54 -05:00
yellows8
6ee5d36084
Removed hbmenuLogoImage from theme_t and just determine which AssetId to use for logo during rendering, imageSize from the asset is now used instead of from the layoutobj. Removed drawImageFromLayout since it's no longer used, and removed imageSize from the layout objects which don't need it anymore. 2020-01-03 17:49:08 -05:00
yellows8
981749cfb7
Added highlightGradientEdgeColor for theme config. 2020-01-03 11:47:49 -05:00
yellows8
9eda227d33
Added logoColor to the theme config. 2020-01-02 22:48:48 -05:00
yellows8
d11585589e
Added support for using themes with extension '.romfs', which contains '/theme.cfg', optionally '/icon.jpg' for the menu-entry in theme-menu, and any assets. Added 'assets' group to theme config, this is only used with .romfs themes. Added 'backgroundImage' to layout theme config, which requires assets.background_image to be setup. Various improvements. 2020-01-02 11:08:17 -05:00
yellows8
42a4ad9787
Added support for using fileassoc with netloader, an error will now be thrown if the filename/file-extension used with netloader is not recognized. Various improvements. 2019-12-30 11:15:38 -05:00
yellows8
b40d558458
Fixed a crash in touch handling for star-button when the current directory is empty (no menu entries). Implemented layouts which can optionally be loaded from theme config, closes #83. 2019-12-26 01:40:54 -05:00
yellows8
387850d301
Added APP_AUTHOR to Makefile.nx. 2019-12-14 12:44:43 -05:00
yellows8
38b48a8609
Don't use hidGetHandheldMode() to determine whether to use handheld or non-handheld. Just accept input from controllers 0-7 and handheld regardless, as long as the controller is connected. Closes #108. 2019-12-11 20:13:18 -05:00
yellows8
bd3466f1e1
Release v3.2.0 2019-12-10 20:24:41 -05:00
yellows8
e5d9851250
Actually fix the stringop warning. 2019-12-10 20:16:59 -05:00
yellows8
15baea1e44
Fixed bounds-check in netloader loadnro cmd-line args validation. 2019-12-10 12:14:23 -05:00
yellows8
c2743dcdd5
Fixed a string warning with gcc 9.x (and added the same change for other lines). 2019-12-08 17:00:17 -05:00
yellows8
233b765300
Improved hid handling. 2019-12-08 14:46:42 -05:00
octopuserectus
a747d92826 Update controller code to accept inputs from all players
not only 0 and 1 (CONTROLLER_P1_AUTO)
2019-12-07 18:58:26 -05:00
yellows8
24ce4ff924
Updated menuEntryParseNacp for latest libnx. 2019-12-07 18:08:51 -05:00
yellows8
4ecd7401b6
Use nifmInitialize/nifmExit in netloader.c, since netstatusGetDetails() uses nifm directly and latest libnx no longer uses nifm in socket init/exit. 2019-11-18 12:06:54 -05:00
yellows8
a150cbe167
Updated font.c for latest libnx + minor other changes. 2019-10-11 01:49:28 -04:00
yellows8
2feb085504
Updated language handling for latest libnx. Added support for SetLanguage_ZHHANS/SetLanguage_ZHHANT, and changed STR_ZH/STR_TW to STR_ZH_HANS/STR_ZH_HANT (system-language is still not actually used). 2019-10-04 18:16:42 -04:00
yellows8
97ab367379
Use dirent d_type when available instead of accessing FsDirectoryEntry. 2019-09-30 17:58:30 -04:00
yellows8
793b912efd
Thermal display (closes #99) (#102)
* Display the temperature status. Moved statusGet code from drawNetwork() into drawStatus(). Added types in common.h to fix building with latest libnx nacp.h, with the pc-build.
2019-09-17 11:47:58 -04:00
yellows8
42efd240de
Release v3.1.1 2019-09-14 13:53:09 -04:00
yellows8
7d01d059df
Updated README. 2019-09-09 10:34:12 -04:00
BullyWiiPlaza
6a75feeb46 Clarify build instructions 2019-09-09 10:25:23 -04:00
yellows8
d7c37c6861
Release v3.1.0 2019-08-08 20:49:30 -04:00
yellows8
fa3d93d649
Enabled using setvbuf with adjustments, improves netloader perf. Fixed issue with large-chunksize, chunksize is now uint32_t and initialized to 0. Minor other change. 2019-08-08 17:57:17 -04:00
yellows8
ea4db4ff02
Moved the gfx for the current active entry down by 10 pixels. 2019-08-05 16:49:59 -04:00
fincs
d9effc3143
Display hbmenu and hbloader version, side by side. Align "Applet Mode" text to the right. 2019-08-05 22:45:01 +02:00
yellows8
6c84575ef7
Fixed buffer overflow when the netloader chunksize is too large, during NRO transfer (this will not occur with an unmodified nxlink). 2019-08-05 12:00:45 -04:00
fincs
ce35f40f31 Display "Applet Mode" indicator when running under an applet 2019-07-29 17:36:59 +02:00
yellows8
78da39d0a2
Draw the star icons as part of the text, instead of seperately. Removed unstar icon usage. 2019-07-13 18:03:17 -04:00
yellows8
f2e085b6be
Use enum from latest libnx for appletSetScreenShotPermission. 2019-07-13 11:20:37 -04:00
octopuserectus
3ceb44fd78 Change font size for back button label 2019-07-10 16:27:31 -04:00
octopuserectus
a123712db8 Fix inconsistensies in button label placement
Also fixes text overlap with the new icons in "Theme Menu"
2019-07-10 16:27:21 -04:00
friedkeenan
48a4819e67 Fix stars with custom themes 2019-07-09 15:33:42 -04:00
hippydave
d6c780256f Add starred favourites at start of list (#91)
* Add starred favourites at start of list
2019-07-09 13:33:21 -04:00
Dave Murphy
dcad6f2afa fix assets.zip dependency. Closes #88 2019-07-01 16:14:41 +02:00
yellows8
753a97ef7b
Don't lock the mutex before calling netstatusGetDetails, lock it afterwards and use tmp fields. 2019-06-25 16:21:30 -04:00
yellows8
f0ef77b2f2
Added support for loading {filename}.nacp similar to {filename}.jpg, when processing files for fileassoc. 2019-06-24 17:05:24 -04:00
yellows8
9e257d7606
Use netstatusGetDetails from a dedicated thread, since nifmGetInternetConnectionStatus can block for a few seconds. 2019-06-24 15:41:35 -04:00
yellows8
7c029c18b0
Moved status gfx around (charging icon moved to the right of the battery icon). Previously there was gfx overlap when the battery charge was 100%. 2019-06-24 11:43:15 -04:00
yellows8
3ea2af46a5
Added netstatus files which were missing from e917589. Closes #86. 2019-06-22 17:47:31 -04:00
yellows8
e91758984c
Display a netstatus icon. Updated/added icons from #79. Closes #79. 2019-06-20 17:34:22 -04:00
yellows8
527ecb6ce3
Fixed regression caused by 8531833 where the gfx for the current active menu entry wasn't rendered in some cases. 2019-06-17 19:27:59 -04:00
yellows8
85318335fb
Fixed slowdown due to drawing too many menu entries. Closes #84. 2019-06-17 18:56:38 -04:00
fincs
6ec7388834 Update makefile 2019-03-26 23:12:39 +01:00
yellows8
1e3f057b23 Updated nx_power.c for latest libnx & changed nx_power psm prefix to 'power'. 2019-01-02 03:51:49 -05:00
fincs
29caa76884 Adapt to new libnx NWindow/Framebuffer API 2018-12-19 19:39:29 +01:00
lookye
d4af9cd2b9 Update language.c - german language strings
Added some german language strings, corrected some small grammatical mistakes.
2018-12-03 11:47:43 -05:00
yellows8
4739c8e730 Release v3.0.1 2018-11-28 17:55:18 -05:00
Dachuu
2a3564f53a Added french strings 2018-11-04 13:25:51 -05:00
yellows8
8e8e62ac33 In menuScan(), don't add an extra '/' to the path when the cwd path already has it at the end, which happens with rootdir. This fixes rootdir support on 1.0.0. Added check for '/' in menuEntryLoad() for the dirlisting code which checks menuGetRootPath(). 2018-11-01 21:41:23 -04:00
yellows8
d2bb1da2fa Disable touch horizontal swipe handling when it would conflict with the app-listing, fixes issue #73. 2018-10-30 12:22:51 -04:00
yellows8
0580a6bcdc
Updated wiki URLs in README. 2018-10-29 22:26:13 -04:00
yellows8
357913b0bd Release v3.0.0 2018-10-29 20:58:38 -04:00
yellows8
bb53a8ac81 Load assets from a .zip in romfs (romfs/ dir for pc-build) using minizip, instead of embedding it in data/. Sync Makefile.nx with latest switch-examples Makefile, with romfs. Automatically build the assets .zip in Makefile. Moved data/ to assets/ and moved data/unused/ to assets_unused/. Use #error in nx_audio.c since it's not supported currently. 2018-10-29 00:30:09 -04:00
Daniel Bernard
bc6d98a534 Expand pow(#, 2) to optimize math in nx_touch 2018-10-28 19:45:07 -04:00
yellows8
1483f4d012 Fixed building for pc-build. 2018-10-28 14:48:44 -04:00
yellows8
d616ed02a7 Fixed buffer overflow vuln with netloader args introduced with the original netloader commit. Fixed unrelated bounds check in launchAddArg() which assumed there was nothing after argData_s.buf within argData_s, which was no longer the case once nxlink_host was added to argData_s. 2018-10-27 16:11:02 -04:00
yellows8
1435a2fb3b Use non-blocking for netloader_datafd. Added sendall which is used instead of calling send directly, the output len from sendall is now checked. Check recvall len output when transferring args. The blocking handling in recvall/sendall now uses netloaderGetExit(). netloader_error() now uses locking, and only writes to netloader_errortext when it's not set. netloader_error() also aborts when netloader exit was requested. 2018-10-27 14:42:07 -04:00
yellows8
982120a9fd Added touch input support for entering theme-menu via swipe-left. 2018-10-26 20:14:23 -04:00
yellows8
2695d48ba7 Use psm StateChangeEvent to cache isCharging. 2018-10-26 13:12:12 -04:00
yellows8
35f48a59d0 Use applet exit-locking. 2018-10-25 13:28:19 -04:00
yellows8
8888bff85f Fixed touch handling with netloader msgbox. Swipe up can now be used to exit netloader. Added menuIsNetloaderActive(). 2018-10-24 19:47:45 -04:00
yellows8
784dbc3623 Append output from 'git describe' to APP_VERSION when RELEASE isn't specified. 2018-10-24 19:25:31 -04:00
yellows8
d97eebc26f Display a progress bar for netloader in the msgbox. Adjusted the language.c netloader strings so that the total newlines for each StrId matches. Fixed GetTextXCoordinate with 'c'. Moved the menuMsgBoxSetNetloaderState call into menuUpdateNetloader and only pass netloaderState into menuUpdateNetloader, etc. Use theme colors in msgbox. Various msgbox adjustments. Added progressBarColor to theme. 2018-10-24 18:38:50 -04:00
yellows8
e648cc9485
Added build requirements to the README. 2018-10-23 23:08:05 -04:00
yellows8
86632292b0 Display netloader status with a modified msgbox.
* Enabled StrIds in language.c for this with some adjustments.
* Moved sockets init/exit into netloaderInit/netloaderExit, netloaderInit now returns Result.
* Changed the params for netloaderGetState to an output struct.
* In netloaderTask(), check netloader_exitflag at the end before clearing it.
* Moved code calling loadnro from netloader_loop into netloaderTask.
* Call thrd_sleep with the while-loop calling netloader_loop in netloaderTask.
* Various other changes.
2018-10-23 20:13:10 -04:00
yellows8
ba4c80d76d Fixed exiting with + button when netloader is active. 2018-10-22 22:13:33 -04:00
yellows8
6d6fb4e3a3 Allow aborting the netloader transfer when the user presses B. When decompress() aborts/fails, delete the file. When returning to HBMENU_DEFAULT from netloader, reload the menu since the netloader NRO may have been deleted due to transfer abort/failure. Minor adjustments. 2018-10-20 12:54:03 -04:00
yellows8
45a10488b1 Moved netloader to the worker thread. Minor improvements. 2018-10-20 11:13:29 -04:00
yellows8
59e2d7a306 Display an error screen with print-console instead of using fatalSimple. Display a msgbox in launchFile() when an error occurs instead of using fatalSimple. Better error handling etc in worker/launch. 2018-10-18 19:06:59 -04:00
yellows8
11dccb4fd0 Added worker, based on 3ds new-hbmenu. 2018-10-17 22:18:09 -04:00
yellows8
55efa03f15 Removed gfxWaitForVsync call which is no longer needed with latest libnx. 2018-10-17 13:57:47 -04:00
yellows8
437895a3c8 Display '{button} {text}' for theme-menu. 2018-10-17 12:10:45 -04:00
yellows8
80e2e0aae7 Display '{button} {text}' for netloader. Added theme handling for this. StrId_NetLoader is used for this, with '3dslink' removed from the text. 2018-10-17 10:17:47 -04:00
Dachuu
b28ddcaddc Added french strings (#69)
* Added french strings
2018-10-16 15:01:08 -04:00
yellows8
0abcb1172a Implemented support for file-associations, closes #25. Various improvements. Cleanup icon/icon_size state in menuEntryParseIcon() on failure. Added fsobjExists() which is now called at the start of menuEntryLoad(). Added menuEntryLoadExternalIcon() and menuEntryImportIconGfx(). 2018-10-10 18:55:37 -04:00
yellows8
40e971ba99 Fixed broken pc-build caused by the power commit. 2018-10-09 18:48:45 -04:00
Daniel Bernard
8b86b36393 Add support to display current charge, and indicate whether or not device is charging (#67)
* Add support to display current charge, and indicate whether or not device is charging.
Updated Makefile.pc to incorporate new icon binaries

* Refactored power-related code, added drawIcon

Added common power interface

* Add battery icon and shift charge text as required
2018-10-09 18:27:43 -04:00
Sunguk Lee
afa9bd49d0 Change Korean translation for StrId_Actions_Apply 2018-10-09 11:41:46 -04:00
yellows8
e1a1bac230 More pos adjustments for time-display and ThemeMenu text. 2018-10-06 21:59:15 -04:00
yellows8
a059b856b5 Swap y-pos for time-display and ThemeMenu text. 2018-10-06 11:12:33 -04:00
yellows8
020a9f61ba Enabled time display with some adjustments, localtime() is now used for this. 2018-10-05 21:13:52 -04:00
yellows8
e01ca3150e Moved path init code from menuStartup() into new func menuStartupPath(), which now creates the config dirs if needed. Added menuGetRootBasePath(), which is now used for config paths and pc-build fonts. Changed some funcs from () to (void). Fixed broken button handling in pc_main for theme-menu. This fixes pc-build support for config/theme-menu. 2018-10-01 12:53:42 -04:00
yellows8
6e11672e20 Improved waveBlendAdd, based on code from @fincs. 2018-09-30 22:05:19 -04:00
yellows8
589f3a5581
Updated README regarding JPEG library. 2018-09-30 18:20:19 -04:00
NightlyFox
1c8a446920 enhancement: replaced nanojpeg with turbojpeg library (#65) (Closes #7)
* enhancement: replaced nanojpeg with turbojpeg library
* added space after if ()
2018-09-30 18:13:33 -04:00
yellows8
d39efdaee1 Moved code for handling A-button from nx_main/pc_main into a dedicated func, which now only handles menu entries when hbmenu_state isn't netloader. Removed old code. 2018-09-29 17:57:32 -04:00
yellows8
69e11599f4 Moved string for default-theme-name into language.c, and fixed language.c whitespace. 2018-09-29 12:07:51 -04:00
yellows8
a9d7a47f2e Removed removeDriveFromPath() and store the theme filename in settings.cfg instead of the absolute path. 2018-09-29 11:58:29 -04:00
yellows8
5b35642de7 Fixed default theme handling in themeMenuScan(). 2018-09-28 23:35:31 -04:00
NightlyFox
992c4c482b Implemented a theme switcher feature (#62)
* Implemented Theme Menu.

* Minor adjustments.

* added two new theming attributes, borderColor, borderTextcolor, allows users to modify the boxes that surrounds the menu entries

* added theme info to config file, users can now add theme author, theme name, and theme version to a theme config file.

* tested building on mac osx, added to .gitignore and make clean for files generated on osx

* The path for the theme is now stored in a config string in settings.cfg, instead of a hard-coded theme.cfg path.

* added functions to create/modify settings config for hbmenu theme

* added Default theme entry that will always insert itself at the front of the list of themes

* added code for + and - button, using - button for theme menu now (button display for this is disabled).
2018-09-28 23:20:24 -04:00
newget
985dc946fe Improved korean 2018-09-15 17:22:46 -04:00
Dachuu
93591ef3e1 Added french string (#58)
Added french string to line 218 (StrId_LastLoadResult and StrId_NetLoaderOffline).
2018-09-15 17:20:55 -04:00
friedkeenan
8909142fb4 Implement Theming (#57)
* Implement theming and fix spelling errors, fixes issue #6.

Now requires libconfig
"separator" was spelled "seperator"

* Added default case for when the system theme is not the values for light/dark.

The default case acts like the system theme is the dark one.
2018-09-13 15:16:33 -04:00
SegFault42
4987cbddb1 fix memset error 2018-08-16 19:13:29 -04:00
Pika
8034053d99 fix parallel building (#54)
* fix parallel building
2018-08-14 20:25:32 -04:00
YY
13676f64fd Improved Japanese language support:
* Improved slight ~ serious Japanese translation miss.
* Added new STR_JP.
2018-07-31 20:34:52 -04:00
AntonioDePau
d36044daea Update language.c (#19)
* Update language.c
2018-07-31 19:43:53 -04:00
MCPE_PC
1e2c2cc877 🦄 Improve & add Korean language 2018-07-31 14:04:44 -04:00
Thompson Lee
03e7b110e7 Updated language.c for Traditional Chinese (#33)
* Updated language.c 

to correct current Traditional Chinese translations.
2018-07-31 13:00:59 -04:00
yellows8
509b24578c
Updated README. 2018-07-30 17:35:55 -04:00
Steven Mattera
00e96fd53b Added touch controls. (#41)
* Added touch controls.
2018-07-30 17:23:34 -04:00
yellows8
c4d22af61e Use SharedFont for the A/B button icons. 2018-07-27 21:05:25 -04:00
Connor Rigby
dbd1958837 Remove usage of freetype-config 2018-07-27 20:55:30 -04:00
yellows8
7c3bb175d9 Added font-loading support for the pc-build. 2018-07-24 21:50:01 -04:00
Dave Murphy
6544353245 create switch folder if it doesn't exist (#39)
Fixes #38.
2018-05-30 11:16:16 -04:00
Dave Murphy
4539b8e78d clean up includes for OSX 2018-05-12 16:57:17 +02:00
yellows8
94b1d648d1 Fixed string warnings with gcc 8.1.0. 2018-05-09 21:25:53 -04:00
yellows8
bbf6bf50f7 Always set PING_ENABLED=1, and removed outdated comment. 2018-05-08 16:28:55 -04:00
yellows8
1e232372d2 Use shared-font. Setting s_textLang is still disabled, until language.c is updated. Check for setsysInitialize failure. Moved .nxfnt files into data/unused/. Currently text will not display with the pc-build since no font is loaded for it. Disabled y+=baseline in text-drawing, and adjusted all callers y-pos to manually add the original baseline (so that y-pos matches with the different font). 2018-04-28 11:55:16 -04:00
yellows8
4563794505 Use nacp.h from libnx. Clear errortext in netloader_error(). 2018-04-26 17:28:58 -04:00
yellows8
8506e1b316 In netloader_error(), use same msgbox width/height as main() since 'OK' wasn't displayed properly. Also use snprintf instead of sprintf in netloader_error(). 2018-03-20 23:34:51 -04:00
yellows8
f738578dc1 Use a copy of the input text string for message-box. 2018-03-20 23:15:21 -04:00
yellows8
e61285c25e Added comment regarding audio data/. 2018-03-19 21:49:47 -04:00
yellows8
1364106d73 Use '(void)' for audio_initialize/audio_exit. 2018-03-19 20:57:55 -04:00
yellows8
22740b8f4b Added code for audio playback via ENABLE_AUDIO define, this is disabled by default. 2018-03-19 20:48:33 -04:00
Dave Murphy
03a4de8c25 use iquote for libnx headers on pc (#29) 2018-03-19 18:57:47 -04:00
yellows8
5731858272 Moved 'v' from APP_VERSION in Makefile to Makefile.nx/Makefile.pc, so that the version in the built nro-nacp doesn't include 'v'. Removed nxlink.h include since switch.h has this. 2018-03-19 18:36:58 -04:00
Dave Murphy
ebced71ec3 Add nxlink support (#27)
* Add nxlink support

* use libnx nro.h

* make sure path doesn't overflow

* convert indentation to spaces
2018-03-19 21:42:58 +01:00
Dave Murphy
e684bb1b71 fix old style initialisers 2018-03-14 23:49:03 +01:00
yellows8
a20cdb5781 Added STR_ES text for StrId_LastLoadResult and StrId_MsgBox_OK, from github-comments. 2018-03-11 19:58:41 -04:00
yellows8
9aaac6f01e Moved the message-box 'OK' text into language.c. Display the Result from envGetLastLoadResult() with a message-box when required. 2018-03-11 17:43:36 -04:00
Dave Murphy
27d195e118 Use __SWITCH__ rather than SWITCH 2018-03-11 00:37:03 +01:00
Dave Murphy
7d5617254a fix building on OSX
requires latest bin2s from https://github.com/devkitPro/general-tools
2018-03-05 20:42:24 +01:00
Adubbz
e93a484e86 Added basic message boxes 2018-03-05 20:41:42 +01:00
james
bec1047ab9 add icon.jpg 2018-02-28 23:43:29 +01:00
95 changed files with 5654 additions and 1755 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
.*/
*~ *~
*.exe *.exe
*.o *.o
@ -11,3 +12,5 @@ build
*.pfs0 *.pfs0
*.nacp *.nacp
*.nro *.nro
test.*
switch

View File

@ -1,18 +1,30 @@
export APP_VERSION := v2.0.0 export APP_VERSION := 3.5.1
ifeq ($(RELEASE),)
export APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always)
endif
.PHONY: clean all nx pc dist-bin .PHONY: clean all nx pc dist-bin
all: nx pc all: nx pc
dist-bin: romfs:
@mkdir -p romfs
romfs/assets.zip : romfs assets
@rm -f romfs/assets.zip
@zip -rj romfs/assets.zip assets
dist-bin: romfs/assets.zip
$(MAKE) -f Makefile.nx dist-bin $(MAKE) -f Makefile.nx dist-bin
nx: nx: romfs/assets.zip
$(MAKE) -f Makefile.nx $(MAKE) -f Makefile.nx
pc: pc: romfs/assets.zip
$(MAKE) -f Makefile.pc $(MAKE) -f Makefile.pc
clean: clean:
@rm -Rf romfs
$(MAKE) -f Makefile.pc clean $(MAKE) -f Makefile.pc clean
$(MAKE) -f Makefile.nx clean $(MAKE) -f Makefile.nx clean

View File

@ -15,8 +15,8 @@ include $(DEVKITPRO)/libnx/switch_rules
# SOURCES is a list of directories containing source code # SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files # DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files # INCLUDES is a list of directories containing header files
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". # ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
# #
# NO_ICON: if set to anything, do not use icon. # NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated. # NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional) # APP_TITLE is the name of the app stored in the .nacp file (Optional)
@ -28,15 +28,25 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg # - <Project name>.jpg
# - icon.jpg # - icon.jpg
# - <libnx folder>/default_icon.jpg # - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR)) TARGET := $(notdir $(CURDIR))
BUILD := build BUILD := build
SOURCES := common/ nx_main/ nx_main/loaders/ SOURCES := common/ nx_main/ nx_main/loaders/
DATA := data DATA := data
INCLUDES := include INCLUDES := include
EXEFS_SRC := exefs_src ROMFS := romfs
DIST_PATH := $(TARGET)_$(APP_VERSION) DIST_PATH := $(TARGET)_v$(APP_VERSION)
APP_AUTHOR := switchbrew
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# options for code generation # options for code generation
@ -44,16 +54,16 @@ DIST_PATH := $(TARGET)_$(APP_VERSION)
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \ CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES) $(ARCH) $(DEFINES) `freetype-config --cflags`
CFLAGS += $(INCLUDE) -DSWITCH -DVERSION=\"$(APP_VERSION)\" CFLAGS += $(INCLUDE) -D__SWITCH__ -DVERSION=\"v$(APP_VERSION)\"
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH) ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx -lm LIBS := -ldeko3d -lphysfs `freetype-config --libs` -lconfig -lturbojpeg -lpng
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing # list of directories containing libraries, this must be the top level containing
@ -96,8 +106,10 @@ else
endif endif
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \ export OFILES_BIN := $(addsuffix .o,$(BINFILES))
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
@ -105,7 +117,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),) ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg) icons := $(wildcard *.jpg)
@ -132,6 +155,10 @@ ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID) export NACPFLAGS += --titleid=$(APP_TITLEID)
endif endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all dist-bin .PHONY: $(BUILD) clean all dist-bin
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@ -144,7 +171,11 @@ $(BUILD):
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
clean: clean:
@echo clean ... @echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
dist-bin: all dist-bin: all
@ -161,11 +192,9 @@ DEPENDS := $(OFILES:.o=.d)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# main targets # main targets
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso all : $(OUTPUT).nro
$(OUTPUT).nso : $(OUTPUT).elf
ifeq ($(strip $(NO_NACP)),) ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -173,17 +202,25 @@ else
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nro : $(OUTPUT).elf
endif endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
menu.o : $(TOPDIR)/Makefile
$(OUTPUT).elf : $(OFILES) $(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data # you need a rule like this for each extension you use as binary data
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
%.bin.o : %.bin %.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
%.nxfnt.o : %.nxfnt
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@echo $(notdir $<) @echo $(notdir $<)
@$(bin2o) @$(bin2o)

View File

@ -1,93 +1,20 @@
# canned command sequence for binary data HOST_OS := $(shell uname -s)
#---------------------------------------------------------------------------------
define bin2o
bin2s $< | $(AS) -o $(@)
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h
endef
test : pc_main/main.cpp pc_main/pc_launch.c \ ifeq ($(strip $(HOST_OS)),Darwin)
common/menu.c common/font.c common/language.c common/launch.c \ BIN2S_FLAGS := --apple-llvm
common/menu-entry.c common/menu-list.c common/text.c \ endif
common/nanojpeg.c common/ui.c common/math.c common/theme.c \
build_pc/tahoma24.o build_pc/tahoma12.o build_pc/interuimedium20.o build_pc/interuimedium30.o \
build_pc/interuiregular14.o build_pc/interuiregular18.o \
build_pc/invalid_icon.bin.o build_pc/folder_icon.bin.o \
build_pc/button_a_light.bin.o build_pc/button_a_dark.bin.o build_pc/button_b_light.bin.o build_pc/button_b_dark.bin.o build_pc/hbmenu_logo_light.bin.o build_pc/hbmenu_logo_dark.bin.o
gcc -Wall -O2 -g0 -DVERSION=\"$(APP_VERSION)\" $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lm -I. -Ibuild_pc -o $@
build_pc/tahoma12.o : data/tahoma12.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/tahoma24.o : data/tahoma24.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/interuimedium20.o : data/interuimedium20.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/interuimedium30.o : data/interuimedium30.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/interuiregular14.o : data/interuiregular14.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/interuiregular18.o : data/interuiregular18.nxfnt
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/invalid_icon.bin.o : data/invalid_icon.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/folder_icon.bin.o : data/folder_icon.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/button_a_light.bin.o : data/button_a_light.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/button_a_dark.bin.o : data/button_a_dark.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/button_b_light.bin.o : data/button_b_light.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/button_b_dark.bin.o : data/button_b_dark.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/hbmenu_logo_light.bin.o : data/hbmenu_logo_light.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
build_pc/hbmenu_logo_dark.bin.o : data/hbmenu_logo_dark.bin
mkdir -p $(dir $@)
@echo $(notdir $<)
@$(bin2o)
ifneq (,$(findstring MINGW,$(HOST_OS)))
EXTRA_CFLAGS="-D__USE_MINGW_ANSI_STDIO"
EXTRA_LDFLAGS="-lws2_32"
endif
test : pc_main/main.cpp pc_main/pc_launch.c pc_main/pc_power.c pc_main/pc_netstatus.c pc_main/pc_thermalstatus.c \
common/menu.c common/font.c common/language.c common/launch.c common/worker.c common/status.c \
common/menu-entry.c common/menu-list.c common/message-box.c common/text.c \
common/ui.c common/assets.c common/math.c common/theme.c \
common/netloader.c
gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config freetype2 --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config freetype2 --libs` -lm -lphysfs -lz -lconfig -lturbojpeg -lpng $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@
clean: clean:
rm -rf build_pc/ test rm -rf build_pc/ test test.*

View File

@ -1,13 +1,31 @@
#### Usage ### Usage
See [Switchbrew](http://switchbrew.org/index.php?title=Homebrew_Applications) for SD layout, applications, etc. See [Homebrew_Applications](https://switchbrew.org/wiki/Homebrew_Applications) for SD layout and applications, etc. See [Switchbrew](https://switchbrew.org/wiki/Homebrew_Menu) for hbmenu docs.
#### Download ### Download
The latest release is available from the [releases](https://github.com/switchbrew/nx-hbmenu/releases/latest) page. The latest release is available from the [releases](https://github.com/switchbrew/nx-hbmenu/releases/latest) page.
#### Building ### Building
Build with ```make nx``` or just run ```make```. Build for the Nintendo Switch with ```make nx``` and for the PC with ```make pc```.
Running ```make``` builds for both systems.
The following [pacman packages](https://devkitpro.org/wiki/devkitPro_pacman) are required to build for Switch:
- `switch-dev`
- `switch-freetype`
- `switch-libconfig`
- `switch-libjpeg-turbo`
- `switch-physfs`
The following libraries are required to build for PC:
- `libfreetype`
- `libconfig`
- `libjpeg-turbo`
- `libphysfs`
Building for Switch/PC requires `zip`.
Since C11 threads are used, building for the PC may fail if C11 threads are not available.
#### Credits #### Credits
* This uses code based on 3DS [new-hbmenu](https://github.com/fincs/new-hbmenu). * This uses code based on 3DS [new-hbmenu](https://github.com/fincs/new-hbmenu).
* [nanojpeg](https://svn.emphy.de/nanojpeg/trunk/nanojpeg/nanojpeg.c) is used for handling JPEG icons. Due to this some JPEGs are not [supported](https://github.com/switchbrew/nx-hbmenu/issues/7). * `libjpeg-turbo` is used for handling JPEG icons. This library doesn't support lossless JPEG (likewise for official sw which uses `libjpeg-turbo`).

BIN
assets/airplane_icon.bin Normal file

Binary file not shown.

BIN
assets/battery_icon.bin Normal file

Binary file not shown.

BIN
assets/charging_icon.bin Normal file

Binary file not shown.

BIN
assets/eth_icon.bin Normal file

Binary file not shown.

BIN
assets/eth_none_icon.bin Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
assets/wifi1_icon.bin Normal file

Binary file not shown.

BIN
assets/wifi2_icon.bin Normal file

Binary file not shown.

BIN
assets/wifi3_icon.bin Normal file

Binary file not shown.

BIN
assets/wifi_none_icon.bin Normal file

Binary file not shown.

277
common/assets.c Normal file
View File

@ -0,0 +1,277 @@
#include "common.h"
#include <physfs.h>
#include <png.h>
#define GENASSET(_p, _mode, _w, _h) {{.path = _p, .imageMode = _mode, .imageSize = {_w, _h}}, {}}
static bool g_assetsInitialized = 0;
assetsDataEntry g_assetsDataList[AssetId_Max][2] = {
GENASSET("battery_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("charging_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("folder_icon.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("invalid_icon.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("hbmenu_logo_dark.bin", IMAGE_MODE_RGBA32, 140, 60),
GENASSET("hbmenu_logo_light.bin", IMAGE_MODE_RGBA32, 140, 60),
GENASSET("theme_icon_dark.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("theme_icon_light.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("airplane_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi1_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi2_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi3_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("eth_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("eth_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("", IMAGE_MODE_RGB24, 1280, 720),
};
static void assetsClearEntry(assetsDataEntry *entry) {
free(entry->buffer);
entry->size = 0;
entry->buffer = NULL;
memset(entry, 0, sizeof(*entry));
}
static void assetsSetPixelSize(assetsDataEntry *entry) {
switch (entry->imageMode) {
case IMAGE_MODE_RGB24:
entry->pixSize = 3;
break;
case IMAGE_MODE_RGBA32:
entry->pixSize = 4;
break;
}
}
Result assetsInit(void) {
bool ret=false;
int i, stopi;
assetsDataEntry *entry = NULL;
char tmp_path[PATH_MAX];
if (g_assetsInitialized) return 0;
#ifdef __SWITCH__
Result rc = romfsInit();
if (R_FAILED(rc)) return rc;
#endif
memset(tmp_path, 0, sizeof(tmp_path));
#ifdef __SWITCH__
strncpy(tmp_path, "romfs:/assets.zip", sizeof(tmp_path)-1);
#else
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/romfs/assets.zip", menuGetRootBasePath());
#endif
if (PHYSFS_mount(tmp_path, "", 0)) {
ret=true;
for (i=0; i<AssetId_Max; i++) {
stopi = i;
entry = &g_assetsDataList[i][0];
if (entry->path[0]) {
ret = assetsLoadData(i, NULL, NULL);
if (!ret) break;
}
}
if (!ret) {
for (i=0; i<stopi; i++) {
assetsClearEntry(&g_assetsDataList[i][0]);
}
}
if (ret) g_assetsInitialized = 1;
PHYSFS_unmount(tmp_path);
}
#ifdef __SWITCH__
romfsExit();
return ret ? 0 : MAKERESULT(Module_Libnx, LibnxError_IoError);
#else
return ret ? 0 : 1;
#endif
}
void assetsExit(void) {
if (!g_assetsInitialized) return;
g_assetsInitialized = 0;
for (int i=0; i<AssetId_Max; i++) {
assetsClearEntry(&g_assetsDataList[i][0]);
}
assetsClearTheme();
}
void assetsClearTheme(void) {
for (int i=0; i<AssetId_Max; i++) {
assetsClearEntry(&g_assetsDataList[i][1]);
}
}
bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height) {
int w,h,samp;
tjhandle _jpegDecompressor = tjInitDecompress();
if (_jpegDecompressor == NULL) {
return false;
}
if (tjDecompressHeader2(_jpegDecompressor, indata, indata_size, &w, &h, &samp) == -1) {
tjDestroy(_jpegDecompressor);
return false;
}
if (w != width || h != height ) {
tjDestroy(_jpegDecompressor);
return false;
}
if (tjDecompress2(_jpegDecompressor, indata, indata_size, outdata, w, 0, h, imageMode == IMAGE_MODE_RGB24 ? TJPF_RGB : TJPF_RGBA, TJFLAG_ACCURATEDCT) == -1) {
tjDestroy(_jpegDecompressor);
return false;
}
tjDestroy(_jpegDecompressor);
return true;
}
bool assetsLoadPngFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height) {
png_image image;
bool ret=true;
memset(&image, 0, sizeof(image));
image.version = PNG_IMAGE_VERSION;
if (png_image_begin_read_from_memory(&image, indata, indata_size) != 0) {
if (image.width != width || image.height != height)
ret = false;
if (ret) image.format = imageMode == IMAGE_MODE_RGB24 ? PNG_FORMAT_RGB : PNG_FORMAT_RGBA;
if (ret && png_image_finish_read(&image, NULL, outdata, 0, NULL) == 0)
ret = false;
}
else
ret = false;
png_image_free(&image);
return ret;
}
bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term) {
bool ret=true;
*data_buf = NULL;
if (filesize) *filesize = 0;
PHYSFS_Stat tmpstat={0};
if (!(PHYSFS_stat(path, &tmpstat) && tmpstat.filesize!=-1)) ret = false;
if (ret) {
size_t bufsize = tmpstat.filesize;
if (nul_term) bufsize++;
*data_buf = (u8*)malloc(bufsize);
if (*data_buf) memset(*data_buf, 0, bufsize);
else ret = false;
}
if (ret) {
PHYSFS_File *f = PHYSFS_openRead(path);
if (f==NULL) ret = false;
else {
ret = PHYSFS_readBytes(f, *data_buf, tmpstat.filesize) == tmpstat.filesize;
PHYSFS_close(f);
}
}
if (ret) {
if (filesize) *filesize = tmpstat.filesize;
}
else {
free(*data_buf);
*data_buf = NULL;
}
return ret;
}
bool assetsLoadData(AssetId id, const char *path, int *imageSize) {
if (id < 0 || id >= AssetId_Max) return false;
assetsDataEntry *entry = &g_assetsDataList[id][path ? 1 : 0];
if (entry->initialized) return false;
if (path) memset(entry, 0, sizeof(*entry));
if (imageSize) {
entry->imageSize[0] = imageSize[0];
entry->imageSize[1] = imageSize[1];
}
if (path) entry->imageMode = g_assetsDataList[id][0].imageMode;
assetsSetPixelSize(entry);
entry->size = entry->imageSize[0] * entry->imageSize[1] * entry->pixSize;
if (path) strncpy(entry->path, path, sizeof(entry->path)-1);
const char* ext = getExtension(entry->path);
bool ret=true;
size_t filesize=0;
if (ext==NULL) ret = false;
u8 *data_buf = NULL;
if (ret) {
entry->buffer = (u8*)malloc(entry->size);
if (entry->buffer) memset(entry->buffer, 0, entry->size);
else ret = false;
}
if (ret) ret = assetsPhysfsReadFile(entry->path, &data_buf, &filesize, false);
if (ret) {
if (strcasecmp(ext, ".bin")==0) {
if (filesize != entry->size) ret = false;
if (ret) memcpy(entry->buffer, data_buf, entry->size);
}
else if (strcasecmp(ext, ".jpg")==0 || strcasecmp(ext, ".jpeg")==0)
ret = assetsLoadJpgFromMemory(data_buf, filesize, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]);
else if (strcasecmp(ext, ".png")==0)
ret = assetsLoadPngFromMemory(data_buf, filesize, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]);
else
ret = false; // File extension not recognized.
}
if (ret) entry->initialized = true;
else assetsClearEntry(entry);
free(data_buf);
return ret;
}
void assetsGetData(AssetId id, assetsDataEntry **out) {
if (out) *out = NULL;
if (id < 0 || id >= AssetId_Max) return;
u32 pos = g_assetsDataList[id][1].initialized ? 1 : 0;
assetsDataEntry *entry = &g_assetsDataList[id][pos];
if (entry->initialized) *out = entry;
}
u8 *assetsGetDataBuffer(AssetId id) {
assetsDataEntry *entry = NULL;
assetsGetData(id, &entry);
return entry ? entry->buffer : NULL;
}

44
common/assets.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include "common.h"
typedef enum {
AssetId_battery_icon,
AssetId_charging_icon,
AssetId_folder_icon,
AssetId_invalid_icon,
AssetId_hbmenu_logo_dark,
AssetId_hbmenu_logo_light,
AssetId_theme_icon_dark,
AssetId_theme_icon_light,
AssetId_airplane_icon,
AssetId_wifi_none_icon,
AssetId_wifi1_icon,
AssetId_wifi2_icon,
AssetId_wifi3_icon,
AssetId_eth_icon,
AssetId_eth_none_icon,
AssetId_background_image,
AssetId_Max,
} AssetId;
typedef struct {
bool initialized;
u8 *buffer;
size_t size;
ImageMode imageMode;
size_t pixSize;
size_t imageSize[2];
char path[PATH_MAX];
} assetsDataEntry;
Result assetsInit(void);
void assetsExit(void);
void assetsClearTheme(void);
bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term);
bool assetsLoadData(AssetId id, const char *path, int *imageSize);
void assetsGetData(AssetId id, assetsDataEntry **out);
u8 *assetsGetDataBuffer(AssetId id);
bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height);

View File

@ -10,16 +10,34 @@
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <threads.h>
#ifndef __APPLE__
#include <malloc.h> #include <malloc.h>
#endif
#include <math.h> #include <math.h>
#ifdef SWITCH #ifdef __SWITCH__
#include <switch.h> #include <switch.h>
#endif #endif
#include <stdint.h> #include <stdint.h>
typedef uint8_t u8; typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32; typedef uint32_t u32;
typedef uint64_t u64; typedef uint64_t u64;
typedef int8_t s8;
typedef int32_t s32;
typedef u32 Result;
typedef void (*workerThreadFunc)(void *);
#ifdef _WIN32
#define DIRECTORY_SEPARATOR_CHAR '\\'
static const char DIRECTORY_SEPARATOR[] = "\\";
#else
#define DIRECTORY_SEPARATOR_CHAR '/'
static const char DIRECTORY_SEPARATOR[] = "/";
#endif
#define M_TAU (2*M_PI) #define M_TAU (2*M_PI)
@ -30,23 +48,76 @@ typedef union {
}; };
} color_t; } color_t;
typedef enum
{
ThemeLayoutId_Logo,
ThemeLayoutId_HbmenuVersion,
ThemeLayoutId_LoaderInfo,
ThemeLayoutId_AttentionText,
ThemeLayoutId_LogInfo,
ThemeLayoutId_InfoMsg,
ThemeLayoutId_MenuPath,
ThemeLayoutId_MenuTypeMsg,
ThemeLayoutId_MsgBoxSeparator,
ThemeLayoutId_MsgBoxBottomText,
ThemeLayoutId_BackgroundImage,
ThemeLayoutId_BackWave,
ThemeLayoutId_MiddleWave,
ThemeLayoutId_FrontWave,
ThemeLayoutId_ButtonA,
ThemeLayoutId_ButtonAText,
ThemeLayoutId_ButtonB,
ThemeLayoutId_ButtonBText,
ThemeLayoutId_ButtonY,
ThemeLayoutId_ButtonYText,
ThemeLayoutId_ButtonM,
ThemeLayoutId_ButtonMText,
ThemeLayoutId_ButtonX,
ThemeLayoutId_ButtonXText,
ThemeLayoutId_NetworkIcon,
ThemeLayoutId_BatteryCharge,
ThemeLayoutId_BatteryIcon,
ThemeLayoutId_ChargingIcon,
ThemeLayoutId_Status,
ThemeLayoutId_Temperature,
ThemeLayoutId_MenuList,
ThemeLayoutId_MenuListTiles,
ThemeLayoutId_MenuListIcon,
ThemeLayoutId_MenuListName,
ThemeLayoutId_MenuActiveEntryIcon,
ThemeLayoutId_MenuActiveEntryName,
ThemeLayoutId_MenuActiveEntryAuthor,
ThemeLayoutId_MenuActiveEntryVersion,
ThemeLayoutId_Total,
} ThemeLayoutId;
// when building for pc we need to include these separately
#ifndef __SWITCH__
#include "switch/nro.h"
#include "switch/nacp.h"
#endif
#include "font.h" #include "font.h"
#include "nacp.h"
#include "menu.h" #include "menu.h"
#include "text.h" #include "text.h"
#include "ui.h" #include "ui.h"
#include "assets.h"
#include "launch.h" #include "launch.h"
#include "nanojpeg.h" #include "worker.h"
#include <turbojpeg.h>
#include "math.h" #include "math.h"
#include "theme.h" #include "theme.h"
#include "message-box.h"
#include "power.h"
#include "netloader.h"
#include "netstatus.h"
#include "thermalstatus.h"
#include "status.h"
// when building for pc we need to include nro.h separately void menuStartupPath(void);
#ifndef SWITCH void menuStartup(void);
#include "nro.h" void themeMenuStartup(void);
#endif void menuLoop(void);
void menuStartup();
void menuLoop();
static inline uint8_t BlendColor(uint32_t src, uint32_t dst, uint8_t alpha) static inline uint8_t BlendColor(uint32_t src, uint32_t dst, uint8_t alpha)
{ {
@ -64,14 +135,14 @@ static inline color_t MakeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
return clr; return clr;
} }
#ifdef SWITCH #ifdef __SWITCH__
extern uint8_t* g_framebuf; extern uint8_t* g_framebuf;
extern u32 g_framebuf_width; extern u32 g_framebuf_width;
static inline void DrawPixel(uint32_t x, uint32_t y, color_t clr) static inline void DrawPixel(uint32_t x, uint32_t y, color_t clr)
{ {
if (x >= 1280 || y >= 720) if (x >= 1280 || y >= 720)
return; return;
u32 off = (y * g_framebuf_width + x)*4; u32 off = y*g_framebuf_width + x*4;
g_framebuf[off] = BlendColor(g_framebuf[off], clr.r, clr.a); off++; g_framebuf[off] = BlendColor(g_framebuf[off], clr.r, clr.a); off++;
g_framebuf[off] = BlendColor(g_framebuf[off], clr.g, clr.a); off++; g_framebuf[off] = BlendColor(g_framebuf[off], clr.g, clr.a); off++;
g_framebuf[off] = BlendColor(g_framebuf[off], clr.b, clr.a); off++; g_framebuf[off] = BlendColor(g_framebuf[off], clr.b, clr.a); off++;
@ -81,7 +152,7 @@ static inline void DrawPixelRaw(uint32_t x, uint32_t y, color_t clr)
{ {
if (x >= 1280 || y >= 720) if (x >= 1280 || y >= 720)
return; return;
u32 off = (y * g_framebuf_width + x)*4; u32 off = y*g_framebuf_width + x*4;
*((u32*)&g_framebuf[off]) = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24); *((u32*)&g_framebuf[off]) = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24);
} }
static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr) static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr)
@ -91,12 +162,12 @@ static inline void Draw4PixelsRaw(uint32_t x, uint32_t y, color_t clr)
u32 color = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24); u32 color = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24);
u128 val = color | ((u128)color<<32) | ((u128)color<<64) | ((u128)color<<96); u128 val = color | ((u128)color<<32) | ((u128)color<<64) | ((u128)color<<96);
u32 off = (y * g_framebuf_width + x)*4; u32 off = y*g_framebuf_width + x*4;
*((u128*)&g_framebuf[off]) = val; *((u128*)&g_framebuf[off]) = val;
} }
static inline color_t FetchPixelColor(uint32_t x, uint32_t y) static inline color_t FetchPixelColor(uint32_t x, uint32_t y)
{ {
u32 off = (y * g_framebuf_width + x)*4; u32 off = y*g_framebuf_width + x*4;
u32 val = *((u32*)&g_framebuf[off]); u32 val = *((u32*)&g_framebuf[off]);
u8 r = (u8)val; u8 r = (u8)val;
u8 g = (u8)(val>>8); u8 g = (u8)(val>>8);
@ -135,5 +206,13 @@ static inline color_t FetchPixelColor(uint32_t x, uint32_t y)
#endif #endif
void DrawPixel(uint32_t x, uint32_t y, color_t clr); void DrawPixel(uint32_t x, uint32_t y, color_t clr);
void DrawText(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text); void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text);
void DrawTextTruncate(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text); void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text);
void DrawTextFromLayoutRelative(ThemeLayoutId id, int base_x, int base_y, int *inPos, int *outPos, color_t clr, const char* text, const char align);
void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text);
void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t* height_out);
uint32_t GetTextXCoordinate(u32 font, uint32_t rX, const char* text, const char align);
uint32_t GetTextYCoordinate(u32 font, uint32_t rY, const char* text, const char align);
bool fontInitialize(void);
void fontExit();

View File

@ -1,6 +1,77 @@
#include "common.h" #include "common.h"
static inline const ffnt_page_t* FontGetPage(const ffnt_header_t* font, uint32_t page_id) #include <ft2build.h>
#include FT_FREETYPE_H
#ifdef __SWITCH__
#define FONT_FACES_MAX PlSharedFontType_Total
#else
#define FONT_FACES_MAX 2
#endif
#ifdef __SWITCH__
static bool s_plinited;
#endif
static FT_Error s_font_libret=1, s_font_facesret[FONT_FACES_MAX];
static FT_Library s_font_library;
static FT_Face s_font_faces[FONT_FACES_MAX];
static FT_Face s_font_lastusedface;
static s32 s_font_faces_total = 0;
static bool FontSetType(u32 font)
{
s32 i=0;
u32 scale=0;
FT_Error ret=0;
switch(font)
{
case interuiregular14:
scale = 4;
break;
case interuiregular18:
scale = 5;
break;
case interuimedium20:
scale = 6;
break;
case fontscale7:
scale = 7;
break;
case interuimedium30:
scale = 8;
break;
case largestar:
scale = 18;
break;
default:
return false;
break;
}
for (i=0; i<s_font_faces_total; i++) {
ret = FT_Set_Char_Size(
s_font_faces[i], /* handle to face object */
0, /* char_width in 1/64th of points */
scale*64, /* char_height in 1/64th of points */
300, /* horizontal device resolution */
300); /* vertical device resolution */
if (ret) return false;
}
return true;
}
/*static inline const ffnt_page_t* FontGetPage(const ffnt_header_t* font, uint32_t page_id)
{ {
//__builtin_printf("GetPage %u\n", (unsigned int)page_id); //__builtin_printf("GetPage %u\n", (unsigned int)page_id);
if (page_id >= font->npages) if (page_id >= font->npages)
@ -9,27 +80,69 @@ static inline const ffnt_page_t* FontGetPage(const ffnt_header_t* font, uint32_t
if (ent->size == 0) if (ent->size == 0)
return NULL; return NULL;
return (const ffnt_page_t*)((const uint8_t*)font + ent->offset); return (const ffnt_page_t*)((const uint8_t*)font + ent->offset);
} }*/
static inline bool FontLoadGlyph(glyph_t* glyph, const ffnt_header_t* font, uint32_t codepoint) static inline bool FontLoadGlyph(glyph_t* glyph, u32 font, uint32_t codepoint)
{ {
FT_Face face=0;
FT_Error ret=0;
FT_GlyphSlot slot;
FT_UInt glyph_index;
FT_Bitmap* bitmap;
s32 i=0;
//__builtin_printf("LoadGlyph %u\n", (unsigned int)codepoint); //__builtin_printf("LoadGlyph %u\n", (unsigned int)codepoint);
const ffnt_page_t* page = FontGetPage(font, codepoint >> 8); /*const ffnt_page_t* page = FontGetPage(font, codepoint >> 8);
if (!page) if (!page)
return false; return false;
codepoint &= 0xFF; codepoint &= 0xFF;
uint32_t off = page->hdr.pos[codepoint]; uint32_t off = page->hdr.pos[codepoint];
if (off == ~(uint32_t)0) if (off == ~(uint32_t)0)
return false; return false;*/
if (s_font_faces_total==0) return false;
for (i=0; i<s_font_faces_total; i++) {
face = s_font_faces[i];
s_font_lastusedface = face;
glyph_index = FT_Get_Char_Index(face, codepoint);
if (glyph_index==0) continue;
ret = FT_Load_Glyph(
face, /* handle to face object */
glyph_index, /* glyph index */
FT_LOAD_DEFAULT);
if (ret==0)
{
ret = FT_Render_Glyph( face->glyph, /* glyph slot */
FT_RENDER_MODE_NORMAL); /* render mode */
}
if (ret) return false;
break;
}
slot = face->glyph;
bitmap = &slot->bitmap;
//__builtin_printf("%c %u\n", (char)codepoint, (unsigned int)off); //__builtin_printf("%c %u\n", (char)codepoint, (unsigned int)off);
glyph->width = page->hdr.widths[codepoint]; /*glyph->width = page->hdr.widths[codepoint];
glyph->height = page->hdr.heights[codepoint]; glyph->height = page->hdr.heights[codepoint];
glyph->advance = page->hdr.advances[codepoint]; glyph->advance = page->hdr.advances[codepoint];
glyph->posX = page->hdr.posX[codepoint]; glyph->posX = page->hdr.posX[codepoint];
glyph->posY = page->hdr.posY[codepoint]; glyph->posY = page->hdr.posY[codepoint];
glyph->data = &page->data[off]; glyph->data = &page->data[off];*/
glyph->width = bitmap->width;
glyph->height = bitmap->rows;
glyph->pitch = bitmap->pitch;
glyph->data = bitmap->buffer;
glyph->advance = slot->advance.x >> 6;
glyph->posX = slot->bitmap_left;
glyph->posY = slot->bitmap_top;
return true; return true;
} }
@ -38,16 +151,17 @@ static void DrawGlyph(uint32_t x, uint32_t y, color_t clr, const glyph_t* glyph)
uint32_t i, j; uint32_t i, j;
const uint8_t* data = glyph->data; const uint8_t* data = glyph->data;
x += glyph->posX; x += glyph->posX;
y += glyph->posY; y -= glyph->posY; //y += glyph->posY;
//__builtin_printf("DrawGlyph %u %u %08X\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr); //__builtin_printf("DrawGlyph %u %u %08X\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr);
for (j = 0; j < glyph->height; j ++) for (j = 0; j < glyph->height; j ++)
{ {
for (i = 0; i < glyph->width; i ++) for (i = 0; i < glyph->width; i ++)
{ {
clr.a = *data++; clr.a = data[i];
if (!clr.a) continue; if (!clr.a) continue;
DrawPixel(x+i, y+j, clr); DrawPixel(x+i, y+j, clr);
} }
data+= glyph->pitch;
} }
} }
@ -118,11 +232,15 @@ static inline uint32_t DecodeUTF8(const char** ptr)
return 0xFFFD; return 0xFFFD;
} }
static void DrawText_(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text) static void DrawText_(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text)
{ {
//__builtin_printf("DrawText %u %u %08X %s\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr, text); //__builtin_printf("DrawText %u %u %08X %s\n", (unsigned int)x, (unsigned int)y, (unsigned int)clr.abgr, text);
y += font->baseline; //y += font->baseline;
uint32_t origX = x; uint32_t origX = x;
if (s_font_faces_total==0) return;
if (!FontSetType(font)) return;
s_font_lastusedface = s_font_faces[0];
while (*text) while (*text)
{ {
if (max_width && x-origX >= max_width) { if (max_width && x-origX >= max_width) {
@ -142,7 +260,7 @@ static void DrawText_(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t
} }
x = origX; x = origX;
y += font->height; y += s_font_lastusedface->size->metrics.height / 64;
continue; continue;
} }
@ -157,12 +275,200 @@ static void DrawText_(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t
} }
} }
void DrawText(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text) void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text)
{ {
DrawText_(font, x, y, clr, text, 0, NULL); DrawText_(font, x, y, clr, text, 0, NULL);
} }
void DrawTextTruncate(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text) void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text)
{
ThemeLayoutObject *obj = &themeCurrent.layoutObjects[id];
if (!obj->visible) return;
DrawText(obj->font, obj->posStart[0], obj->posStart[1], clr, text);
}
void DrawTextFromLayoutRelative(ThemeLayoutId id, int base_x, int base_y, int *inPos, int *outPos, color_t clr, const char* text, const char align)
{
ThemeLayoutObject *obj = &themeCurrent.layoutObjects[id];
base_x = obj->posType ? base_x + inPos[0] : inPos[0];
base_y = obj->posType ? base_y + inPos[1] : inPos[1];
base_x = GetTextXCoordinate(obj->font, base_x, text, align);
if (outPos) {
outPos[0] = base_x;
outPos[1] = base_y;
}
obj->posFinal[0] = base_x;
obj->posFinal[1] = base_y;
if (!obj->visible) return;
GetTextDimensions(obj->font, text, &obj->textSize[0], &obj->textSize[1]);
DrawText(obj->font, base_x, base_y, clr, text);
}
void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text)
{ {
DrawText_(font, x, y, clr, text, max_width, end_text); DrawText_(font, x, y, clr, text, max_width, end_text);
} }
void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t* height_out)
{
uint32_t x = 0;
uint32_t width = 0, height = 0;
if (s_font_faces_total==0) return;
if (!FontSetType(font)) return;
s_font_lastusedface = s_font_faces[0];
while (*text)
{
glyph_t glyph;
uint32_t codepoint = DecodeUTF8(&text);
if (codepoint == '\n')
{
x = 0;
height += s_font_lastusedface->size->metrics.height / 64;
continue;
}
if (!FontLoadGlyph(&glyph, font, codepoint))
{
if (!FontLoadGlyph(&glyph, font, '?'))
continue;
}
x += glyph.advance;
if (x > width)
width = x;
}
if(width_out) *width_out = width;
if(height_out) *height_out = height;
}
bool fontInitialize(void)
{
FT_Error ret=0;
s32 i;
for (i=0; i<FONT_FACES_MAX; i++) s_font_facesret[i] = 1;
ret = FT_Init_FreeType(&s_font_library);
s_font_libret = ret;
if (s_font_libret) return false;
#ifdef __SWITCH__
PlFontData fonts[PlSharedFontType_Total];
Result rc=0;
rc = plInitialize(PlServiceType_User);
if (R_SUCCEEDED(rc)) {
s_plinited = true;
rc = plGetSharedFont(textGetLanguageCode(), fonts, FONT_FACES_MAX, &s_font_faces_total);
}
if (R_FAILED(rc)) return false;
for (i=0; i<s_font_faces_total; i++) {
ret = FT_New_Memory_Face( s_font_library,
fonts[i].address, /* first byte in memory */
fonts[i].size, /* size in bytes */
0, /* face_index */
&s_font_faces[i]);
s_font_facesret[i] = ret;
if (ret) return false;
}
#endif
//These are loaded from "<original cwd>/fonts/<index>.ttf", these are user-supplied. Ideally these should be from plu SharedFont.
#ifndef __SWITCH__
char fontpath[PATH_MAX+1];
for (i=0; i<FONT_FACES_MAX; i++) {
memset(fontpath, 0, sizeof(fontpath));
snprintf(fontpath, sizeof(fontpath)-1, "%s%sfonts%s%u.ttf", menuGetRootBasePath(), DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, i);
ret = FT_New_Face( s_font_library,
fontpath,
0,
&s_font_faces[i]);
s_font_facesret[i] = ret;
if (ret) return false;
s_font_faces_total++;
}
#endif
return true;
}
void fontExit()
{
s32 i=0;
for (i=0; i<s_font_faces_total; i++)
if (s_font_facesret[i]==0) FT_Done_Face(s_font_faces[i]);
if (s_font_libret==0) FT_Done_FreeType(s_font_library);
#ifdef __SWITCH__
if (s_plinited) plExit();
#endif
}
/*Automatically gives you the desired x-coordinate
*based on the string length and desired alignment
*rY=reference point... where to align around
*align='t','b','c' translates to (top,bottom,center)
*'t' aligned, top of text aligns with rY,
*you get the rest....
*/
uint32_t GetTextYCoordinate(u32 font, uint32_t rY, const char* text, const char align) {
uint32_t height_o,width;
GetTextDimensions(font,text,&width,&height_o);
uint32_t height = (uint32_t)height_o;
uint32_t fC = (rY-height);
switch(align){
case 't':
default:
return rY;
case 'c':
return (rY+(height>>1));//>>1 is a bitwise shift for dividing by 2
case 'b':
if(fC<=0) return 0;
else return fC;
}
}
/*Automatically gives you the desired x-coordinate
*based on the string length and desired alignment
*rX=reference point... where to align around
*text=string you want to display
*align='r','l','c' translates to (right,left,center)
*'r' aligned, rX location = end of string, you get the rest...
*/
uint32_t GetTextXCoordinate(u32 font, uint32_t rX, const char* text, const char align) {
uint32_t height,width_o;
GetTextDimensions(font,text,&width_o,&height);
uint32_t fC = (rX-width_o);
switch(align){
case 'r':
if(fC<0) return 0;
else return fC;
case 'c':
return (rX-(width_o>>1));//>>1 is a bitwise shift for dividing by 2
case 'l':
default:
return rX;
}
}

View File

@ -26,19 +26,21 @@ typedef struct {
typedef struct { typedef struct {
uint8_t width, height; uint8_t width, height;
int8_t posX, posY, advance; int8_t posX, posY, advance, pitch;
const uint8_t* data; const uint8_t* data;
} glyph_t; } glyph_t;
extern const ffnt_header_t tahoma24_nxfnt; //extern const ffnt_header_t tahoma24_nxfnt;//These tahoma fonts aren't used anymore.
extern const ffnt_header_t tahoma12_nxfnt; //extern const ffnt_header_t tahoma12_nxfnt;
extern const ffnt_header_t interuimedium20_nxfnt; /*extern const ffnt_header_t interuimedium20_nxfnt;
extern const ffnt_header_t interuimedium30_nxfnt; extern const ffnt_header_t interuimedium30_nxfnt;
extern const ffnt_header_t interuiregular14_nxfnt; extern const ffnt_header_t interuiregular14_nxfnt;
extern const ffnt_header_t interuiregular18_nxfnt; extern const ffnt_header_t interuiregular18_nxfnt;*/
#define tahoma24 &tahoma24_nxfnt //#define tahoma24 &tahoma24_nxfnt
#define tahoma12 &tahoma12_nxfnt //#define tahoma12 &tahoma12_nxfnt
#define interuimedium20 &interuimedium20_nxfnt #define interuimedium20 2//&interuimedium20_nxfnt
#define interuimedium30 &interuimedium30_nxfnt #define interuimedium30 3//&interuimedium30_nxfnt
#define interuiregular14 &interuiregular14_nxfnt #define interuiregular14 0//&interuiregular14_nxfnt
#define interuiregular18 &interuiregular18_nxfnt #define interuiregular18 1//&interuiregular18_nxfnt
#define fontscale7 4
#define largestar 5

View File

@ -1,18 +1,18 @@
#include "language.h" #include "language.h"
#ifdef SWITCH #ifdef __SWITCH__
#define STR_JP(_str) [SetLanguage_JA] = _str #define STR_JP(_str) [SetLanguage_JA] = _str
#define STR_EN(_str) [SetLanguage_ENUS] = _str, [SetLanguage_ENGB] = _str #define STR_EN(_str) [SetLanguage_ENUS] = _str, [SetLanguage_ENGB] = _str
#define STR_FR(_str) [SetLanguage_FR] = _str, [SetLanguage_FRCA] = _str #define STR_FR(_str) [SetLanguage_FR] = _str, [SetLanguage_FRCA] = _str
#define STR_DE(_str) [SetLanguage_DE] = _str #define STR_DE(_str) [SetLanguage_DE] = _str
#define STR_IT(_str) [SetLanguage_IT] = _str #define STR_IT(_str) [SetLanguage_IT] = _str
#define STR_ES(_str) [SetLanguage_ES] = _str, [SetLanguage_ES419] = _str #define STR_ES(_str) [SetLanguage_ES] = _str, [SetLanguage_ES419] = _str
#define STR_ZH(_str) [SetLanguage_ZHCN] = _str #define STR_ZH_HANS(_str) [SetLanguage_ZHCN] = _str, [SetLanguage_ZHHANS] = _str
#define STR_KO(_str) [SetLanguage_KO] = _str #define STR_KO(_str) [SetLanguage_KO] = _str
#define STR_NL(_str) [SetLanguage_NL] = _str #define STR_NL(_str) [SetLanguage_NL] = _str
#define STR_PT(_str) [SetLanguage_PT] = _str #define STR_PT(_str) [SetLanguage_PT] = _str
#define STR_RU(_str) [SetLanguage_RU] = _str #define STR_RU(_str) [SetLanguage_RU] = _str
#define STR_TW(_str) [SetLanguage_ZHTW] = _str #define STR_ZH_HANT(_str) [SetLanguage_ZHTW] = _str, [SetLanguage_ZHHANT] = _str
#else #else
#define STR_JP(_str) [0] = _str #define STR_JP(_str) [0] = _str
#define STR_EN(_str) [1] = _str #define STR_EN(_str) [1] = _str
@ -20,15 +20,15 @@
#define STR_DE(_str) [3] = _str #define STR_DE(_str) [3] = _str
#define STR_IT(_str) [4] = _str #define STR_IT(_str) [4] = _str
#define STR_ES(_str) [5] = _str #define STR_ES(_str) [5] = _str
#define STR_ZH(_str) [6] = _str #define STR_ZH_HANS(_str) [6] = _str
#define STR_KO(_str) [7] = _str #define STR_KO(_str) [7] = _str
#define STR_NL(_str) [8] = _str #define STR_NL(_str) [8] = _str
#define STR_PT(_str) [9] = _str #define STR_PT(_str) [9] = _str
#define STR_RU(_str) [10] = _str #define STR_RU(_str) [10] = _str
#define STR_TW(_str) [11] = _str #define STR_ZH_HANT(_str) [11] = _str
#endif #endif
const char* const g_strings[StrId_Max][16] = const char* const g_strings[StrId_Max][17] =
{ {
[StrId_Loading] = [StrId_Loading] =
{ {
@ -42,8 +42,16 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Laden…"), STR_NL("Laden…"),
STR_KO("로딩중…"), STR_KO("로딩중…"),
STR_RU("загрузка…"), STR_RU("загрузка…"),
STR_ZH("加载中…"), STR_ZH_HANS("加载中…"),
STR_TW("加載中…"), STR_ZH_HANT("載入中…"),
},
[StrId_AppletMode] =
{
STR_EN("● Applet Mode ●"),
STR_ES("● Modo Applet ●"),
STR_FR("● Mode Applet ●"),
STR_ZH_HANS("● 小程序模式 ●"),
}, },
[StrId_Directory] = [StrId_Directory] =
@ -58,8 +66,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Map"), STR_NL("Map"),
STR_KO("디렉토리"), STR_KO("디렉토리"),
STR_RU("каталог"), STR_RU("каталог"),
STR_ZH("目录"), STR_ZH_HANS("目录"),
STR_TW("資料夾"), STR_ZH_HANT("資料夾"),
}, },
/*[StrId_DefaultLongTitle] = /*[StrId_DefaultLongTitle] =
@ -74,8 +82,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Homebrew toepassing"), STR_NL("Homebrew toepassing"),
STR_KO("홈브류 애플리케이션"), STR_KO("홈브류 애플리케이션"),
STR_RU("приложение хомебреw"), STR_RU("приложение хомебреw"),
STR_ZH("自制应用程序"), STR_ZH_HANS("自制应用程序"),
STR_TW("自製程式"), STR_ZH_HANT("自製程式"),
},*/ },*/
[StrId_DefaultPublisher] = [StrId_DefaultPublisher] =
@ -85,29 +93,29 @@ const char* const g_strings[StrId_Max][16] =
STR_DE("Unbekannter Autor"), STR_DE("Unbekannter Autor"),
STR_FR("Auteur inconnu"), STR_FR("Auteur inconnu"),
STR_IT("Autore sconosciuto"), STR_IT("Autore sconosciuto"),
STR_JP("未知の作者"), STR_JP("作者不明"),
STR_PT("Autor Desconhecido"), STR_PT("Autor Desconhecido"),
STR_NL("Auteur onbekend"), STR_NL("Auteur onbekend"),
STR_KO("작자미상"), STR_KO("알 수 없는 개발자"),
STR_RU("неизвестный автор"), STR_RU("неизвестный автор"),
STR_ZH("未知作者"), STR_ZH_HANS("未知作者"),
STR_TW("未知作者"), STR_ZH_HANT("作者未知"),
}, },
[StrId_IOError] = [StrId_IOError] =
{ {
STR_EN("I/O Error"), STR_EN("I/O Error"),
STR_ES("Error de E/S"), STR_ES("Error de E/S"),
STR_DE("E/A-Fehler"), STR_DE("I/O-Fehler"),
STR_FR("Erreur d'E/S"), STR_FR("Erreur d'E/S"),
STR_IT("Errore di I/O"), STR_IT("Errore I/O"),
STR_JP("入出力エラー"), STR_JP("入出力エラー"),
STR_PT("Erro de E/S"), STR_PT("Erro de E/S"),
STR_NL("I/O Fout"), STR_NL("I/O Fout"),
STR_KO("I/O 에러"), STR_KO("입출력 오류"),
STR_RU("I/O-ошибка"), STR_RU("I/O-ошибка"),
STR_ZH("读写出错"), STR_ZH_HANS("读写出错"),
STR_TW("讀寫錯誤"), STR_ZH_HANT("取存錯誤"),
}, },
[StrId_CouldNotOpenFile] = [StrId_CouldNotOpenFile] =
@ -122,8 +130,15 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Kan bestand niet openen:\n%s"), STR_NL("Kan bestand niet openen:\n%s"),
STR_KO("파일을 열 수 없습니다:\n%s"), STR_KO("파일을 열 수 없습니다:\n%s"),
STR_RU("Не могу открыть файл:\n%s"), STR_RU("Не могу открыть файл:\n%s"),
STR_ZH("无法打开文件:\n%s"), STR_ZH_HANS("无法打开文件:\n%s"),
STR_TW("開啓檔案失敗:\n%s"), STR_ZH_HANT("無法開啟檔案:\n%s"),
},
[StrId_NroNotFound] =
{
STR_EN("Could not find executable: %s"),
STR_FR("Impossible trouver l'exécutable : %s"),
STR_ZH_HANS("找不到可执行文件"),
}, },
[StrId_NoAppsFound_Title] = [StrId_NoAppsFound_Title] =
@ -138,8 +153,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Geen toepassingen gevonden"), STR_NL("Geen toepassingen gevonden"),
STR_KO("애플리케이션을 찾을 수 없습니다"), STR_KO("애플리케이션을 찾을 수 없습니다"),
STR_RU("приложение не найдено"), STR_RU("приложение не найдено"),
STR_ZH("找不到可执行的自制程序"), STR_ZH_HANS("找不到可执行的自制程序"),
STR_TW("未能找到可執行的自製程式"), STR_ZH_HANT("沒有可執行的自製程式"),
}, },
[StrId_NoAppsFound_Msg] = [StrId_NoAppsFound_Msg] =
@ -157,7 +172,7 @@ const char* const g_strings[StrId_Max][16] =
STR_DE( STR_DE(
"Auf der SD-Karte wurden keine Anwendungen\n" "Auf der SD-Karte wurden keine Anwendungen\n"
"gefunden. Stelle sicher, dass ein Verzeichnis\n" "gefunden. Stelle sicher, dass ein Verzeichnis\n"
"namens /switch im Wurzelverzeichnis der SD-Karte\n" "namens /switch im Hauptverzeichnis der SD-Karte\n"
"existiert und Anwendungen enthält!" "existiert und Anwendungen enthält!"
), ),
STR_FR( STR_FR(
@ -174,7 +189,7 @@ const char* const g_strings[StrId_Max][16] =
), ),
STR_JP( STR_JP(
"SDカードにアプリケーションが見つかりませんでした。\n" "SDカードにアプリケーションが見つかりませんでした。\n"
"SDカードのルートに「/switch」という名前のフォルダを\n" "SDカードのルートに「switch」という名前のフォルダを\n"
"作成してください。" "作成してください。"
), ),
STR_PT( STR_PT(
@ -190,27 +205,51 @@ const char* const g_strings[StrId_Max][16] =
"en de toepassingen bevat." "en de toepassingen bevat."
), ),
STR_KO( STR_KO(
"애플리케이션을 SD 카드에서 찾을 수 없습니다.\n" "SD 카드에서 애플리케이션을 찾을 수 없습니다.\n"
"SD 카드 최상단에 /switch 라는 이름의 폴더가 있는지,\n" "SD 카드 최상위에 /switch 폴더가 있고\n"
"애플리케이션을 포함하고 있는지 확인해 주십시오." "애플리케이션을 포함하는지 확인해 주십시오."
), ),
STR_RU( STR_RU(
"На SD-карте нет приложений.\n" "На SD-карте нет приложений.\n"
"Убедитесь, что на карте SD есть каталог с\n" "Убедитесь, что на карте SD есть каталог с\n"
"названием switch и она содержит приложения." "названием switch и она содержит приложения."
), ),
STR_ZH( STR_ZH_HANS(
"内存卡找不到任何可执行的应用程序\n" "找不到任何自制程序(nro)\n"
"请在内存卡的根目录建立「switch」子目录\n" "在SD卡根目录建立“switch”文件夹\n"
"存放自制应用软件至该目录" "将自制程序(nro)放在其中"
), ),
STR_TW( STR_ZH_HANT(
"記憶體找不到任何可執行的應用程式。\n" "記憶卡內沒有可供執行的應用程式。\n"
"請在記憶體建立「switch」資料夾\n" "請在根目錄下建立「switch」資料夾\n"
"然後儲存自製軟體到此處" "並將自製軟體複製到switch資料夾內"
), ),
}, },
[StrId_LastLoadResult] =
{
STR_EN("The last application returned an error:"),
STR_ES("La última aplicación devolvió un error:"),
STR_DE("Die letzte Anwendung erzeugte einen Fehler:"),
STR_FR("La dernière application a retourné une erreur:"),
STR_IT("L'ultima applicazione ha restituito un errore:"),
STR_JP("直前に実行したアプリでエラーが発生しました:"),
STR_KO("최근 애플리케이션에서 오류가 발생했습니다:"),
STR_ZH_HANS("程序运行后出现错误:"),
STR_ZH_HANT("程式執行後出現錯誤:"),
},
[StrId_AppLaunchError] =
{
STR_EN("Failed to launch the application:"),
STR_DE("Konnte die Anwendung nicht starten:"),
STR_FR("Erreur au lancement de l'application:"),
STR_IT("Errore nell'avvio dell'applicazione:"),
STR_ES("No se ha podido iniciar la aplicación:"),
STR_ZH_HANS("运行程序时发生错误:"),
STR_ZH_HANT("執行程式時發生錯誤:"),
},
[StrId_AppInfo_Author] = [StrId_AppInfo_Author] =
{ {
STR_EN("Author"), STR_EN("Author"),
@ -218,13 +257,13 @@ const char* const g_strings[StrId_Max][16] =
STR_DE("Autor"), STR_DE("Autor"),
STR_FR("Auteur"), STR_FR("Auteur"),
STR_IT("Autore"), STR_IT("Autore"),
STR_JP(""), STR_JP(""),
STR_PT("Autor"), STR_PT("Autor"),
STR_NL("Auteur"), STR_NL("Auteur"),
STR_KO(""), STR_KO("개발"),
STR_RU("автор"), STR_RU("автор"),
STR_ZH("作者"), STR_ZH_HANS("作者"),
STR_TW("作者"), STR_ZH_HANT("作者"),
}, },
[StrId_AppInfo_Version] = [StrId_AppInfo_Version] =
@ -237,10 +276,10 @@ const char* const g_strings[StrId_Max][16] =
STR_JP("バージョン"), STR_JP("バージョン"),
STR_PT("Versão"), STR_PT("Versão"),
STR_NL("Versie"), STR_NL("Versie"),
STR_KO("번역"), STR_KO("버전"),
STR_RU("Версия"), STR_RU("Версия"),
STR_ZH(""), STR_ZH_HANS(""),
STR_TW(""), STR_ZH_HANT(""),
}, },
[StrId_Actions_Launch] = [StrId_Actions_Launch] =
@ -248,37 +287,128 @@ const char* const g_strings[StrId_Max][16] =
STR_EN("Launch"), STR_EN("Launch"),
STR_ES("Lanzamiento"), STR_ES("Lanzamiento"),
STR_DE("Starten"), STR_DE("Starten"),
STR_FR("Lancement"), STR_FR("Lancer"),
STR_IT("Lanciare"), STR_IT("Avvia"),
STR_JP("打ち上げ"), STR_JP("起動"),
STR_PT("Lançamento"), STR_PT("Lançamento"),
STR_NL("Lancering"), STR_NL("Lancering"),
STR_KO("쏘다"), STR_KO("실행"),
STR_RU("запуск"), STR_RU("запуск"),
STR_ZH("发射"), STR_ZH_HANS("发射"),
STR_TW("发射"), STR_ZH_HANT("啟動"),
}, },
[StrId_Actions_Open] = [StrId_Actions_Open] =
{ {
STR_EN("Open"), STR_EN("Open"),
STR_ES("Abierto"), STR_ES("Abrir"),
STR_DE("Öffnen"), STR_DE("Öffnen"),
STR_FR("Ouvrir"), STR_FR("Ouvrir"),
STR_IT("Aperto"), STR_IT("Apri"),
STR_JP("いた"), STR_JP(""),
STR_PT("Aberto"), STR_PT("Abrir"),
STR_NL("Open"), STR_NL("Open"),
STR_KO(""), STR_KO(""),
STR_RU("открыто"), STR_RU("открыто"),
STR_ZH("打开"), STR_ZH_HANS("打开"),
STR_TW("打开"), STR_ZH_HANT("開啟"),
}, },
[StrId_Actions_Back] = [StrId_Actions_Back] =
{ {
STR_EN("Back"), STR_EN("Back"),
STR_ES("Volver"),
STR_DE("Zurück"), STR_DE("Zurück"),
STR_FR("Retour"),
STR_IT("Indietro"),
STR_JP("戻る"),
STR_PT("Regressar"),
STR_NL("Terug"),
STR_KO("뒤로 가기"),
STR_RU("возвращаться"),
STR_ZH_HANS("返回"),
STR_ZH_HANT("返回"),
},
[StrId_MsgBox_OK] =
{
STR_EN("OK"),
STR_DE("OK"),
STR_FR("OK"),
STR_IT("OK"),
STR_ES("Aceptar"),
STR_JP("了解"),
STR_KO("확인"),
STR_ZH_HANS("确认"),
STR_ZH_HANT("確認"),
},
[StrId_Actions_Apply] =
{
STR_EN("Apply"),
STR_FR("Appliquer"),
STR_DE("Anwenden"),
STR_ES("Aplicar"),
STR_IT("Applica"),
STR_JP("適用"),
STR_KO("적용"),
STR_ZH_HANS("应用"),
STR_ZH_HANT("套用"),
},
[StrId_Actions_Star] =
{
STR_EN("Star"),
STR_ES("Agregar a favoritos"),
STR_IT("Aggiungi ai preferiti"),
STR_FR("Ajouter aux favoris"),
STR_ZH_HANS("收藏"),
},
[StrId_Actions_Unstar] =
{
STR_EN("Unstar"),
STR_ES("Borrar de favoritos"),
STR_IT("Rimuovi dai preferiti"),
STR_FR("Retirer des favoris"),
STR_ZH_HANS("取消收藏"),
},
[StrId_ThemeMenu] =
{
STR_EN("Theme Menu"),
STR_FR("Menu thèmes"),
STR_DE("Theme Menü"),
STR_ES("Menú temático"),
STR_IT("Tema Menu"),
STR_JP("テーマメニュー"),
STR_KO("테마 메뉴"),
STR_ZH_HANS("主题菜单"),
STR_ZH_HANT("主題選單"),
},
[StrId_ThemeNotApplied] =
{
STR_EN("Theme cannot be applied because an error occurred."),
STR_DE("Das Theme konnte nicht geladen werden, da ein Fehler aufgetreten ist."),
STR_FR("Le thème ne peut pas être appliqué car une erreur est survenue."),
STR_ES("El tema no se pudo aplicar porque se ha producido un error."),
STR_IT("Il tema non è stato applicato a causa di un errore."),
STR_JP("エラーが発生したため、テーマを適用できませんでした。"),
STR_KO("오류가 발생 했기 때문에 테마를 적용할 수 없습니다."),
STR_ZH_HANS("由于发生错误, 无法应用主题。"),
STR_ZH_HANT("出現錯誤,無法套用主題。"),
},
[StrId_DefaultThemeName] =
{
STR_EN("Default Theme"),
STR_FR("Thème par défaut"),
STR_DE("Standard Theme"),
STR_IT("Tema di default"),
STR_ES("Tema por defecto"),
STR_ZH_HANS("默认主题"),
STR_ZH_HANT("預設主題"),
}, },
/*[StrId_Reboot] = /*[StrId_Reboot] =
@ -315,8 +445,8 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x81 Annulla" " \xEE\x80\x81 Annulla"
), ),
STR_JP( STR_JP(
"\xEE\x81\xB3HOMEに戻ることはできません\n" " \xEE\x81\xB3HOME に戻ることができませんでした\n"
"コンソールが今すぐ再起動する\n\n" "今すぐ本体を再起動してください\n\n"
" \xEE\x80\x80 再起動\n" " \xEE\x80\x80 再起動\n"
" \xEE\x80\x81 キャンセル" " \xEE\x80\x81 キャンセル"
), ),
@ -333,9 +463,9 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x81 Annuleren" " \xEE\x80\x81 Annuleren"
), ),
STR_KO( STR_KO(
"\xEE\x81\xB3으로 돌아갈 수 없습니다.\n" "\xEE\x81\xB3HOME 으로 돌아갈 수 없습니다.\n"
"당신의 기기를 리부팅 하려 합니다.\n\n" "콘솔을 재부팅할 것 입니다.\n\n"
" \xEE\x80\x80 부팅\n" " \xEE\x80\x80 부팅\n"
" \xEE\x80\x81 취소" " \xEE\x80\x81 취소"
), ),
STR_RU( STR_RU(
@ -344,13 +474,13 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x80 Перезагрузите\n" " \xEE\x80\x80 Перезагрузите\n"
" \xEE\x80\x81 Отмена" " \xEE\x80\x81 Отмена"
), ),
STR_ZH( STR_ZH_HANS(
"无法返回至主机的 \xEE\x81\xB3HOME 菜单。\n" "无法返回至主机的 \xEE\x81\xB3HOME 菜单。\n"
"您需要重新启动您的 3DS 设备。\n\n" "您需要重新启动您的 3DS 设备。\n\n"
" \xEE\x80\x80 重启设备\n" " \xEE\x80\x80 重启设备\n"
" \xEE\x80\x81 取消操作" " \xEE\x80\x81 取消操作"
), ),
STR_TW( STR_ZH_HANT(
"無法返回至主機的 \xEE\x81\xB3HOME 選單。\n" "無法返回至主機的 \xEE\x81\xB3HOME 選單。\n"
"您需要重新啓動您的 3DS 設備。\n\n" "您需要重新啓動您的 3DS 設備。\n\n"
" \xEE\x80\x80 重啓設備\n" " \xEE\x80\x80 重啓設備\n"
@ -391,8 +521,8 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x82 Riavvia" " \xEE\x80\x82 Riavvia"
), ),
STR_JP( STR_JP(
"あなたは今すぐ\xEE\x81\xB3HOMEに戻されます。\n\n" " \xEE\x81\xB3HOME に戻ろうとしています。\n\n"
" \xEE\x80\x80 戻る\n" " \xEE\x80\x80 了解\n"
" \xEE\x80\x81 キャンセル\n" " \xEE\x80\x81 キャンセル\n"
" \xEE\x80\x82 再起動" " \xEE\x80\x82 再起動"
), ),
@ -409,10 +539,10 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x82 Herstarten" " \xEE\x80\x82 Herstarten"
), ),
STR_KO( STR_KO(
"\xEE\x81\xB3홈으로 돌아가려 합니다.\n\n" "\xEE\x81\xB3HOME 으로 돌아갈 것 입니다.\n\n"
" \xEE\x80\x80 이동\n" " \xEE\x80\x80 돌아가기\n"
" \xEE\x80\x81 취소\n" " \xEE\x80\x81 취소\n"
" \xEE\x80\x82 부팅" " \xEE\x80\x82 부팅"
), ),
STR_RU( STR_RU(
"Вы возвращаетесь в \xEE\x81\xB3HOME.\n\n" "Вы возвращаетесь в \xEE\x81\xB3HOME.\n\n"
@ -420,13 +550,13 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x81 Отмена\n" " \xEE\x80\x81 Отмена\n"
" \xEE\x80\x82 Перезагрузите" " \xEE\x80\x82 Перезагрузите"
), ),
STR_ZH( STR_ZH_HANS(
"您即将返回到主機的 \xEE\x81\xB3HOME 菜单。\n\n" "您即将返回到主機的 \xEE\x81\xB3HOME 菜单。\n\n"
" \xEE\x80\x80 确认返回\n" " \xEE\x80\x80 确认返回\n"
" \xEE\x80\x81 取消操作\n" " \xEE\x80\x81 取消操作\n"
" \xEE\x80\x82 重启设备" " \xEE\x80\x82 重启设备"
), ),
STR_TW( STR_ZH_HANT(
"您即將返回到主機的 \xEE\x81\xB3HOME 選單。\n\n" "您即將返回到主機的 \xEE\x81\xB3HOME 選單。\n\n"
" \xEE\x80\x80 確認返回\n" " \xEE\x80\x80 確認返回\n"
" \xEE\x80\x81 取消操作\n" " \xEE\x80\x81 取消操作\n"
@ -438,7 +568,7 @@ const char* const g_strings[StrId_Max][16] =
{ {
STR_EN("Title selector"), STR_EN("Title selector"),
STR_ES("Selector de título"), STR_ES("Selector de título"),
STR_DE("Titel-Selektor"), STR_DE("Titel-Auswahl"),
STR_FR("Sélecteur de titre"), STR_FR("Sélecteur de titre"),
STR_IT("Selettore del titolo"), STR_IT("Selettore del titolo"),
STR_JP("タイトルセレクタ"), STR_JP("タイトルセレクタ"),
@ -446,18 +576,18 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Titel selector"), STR_NL("Titel selector"),
STR_KO("타이틀 선택기"), STR_KO("타이틀 선택기"),
STR_RU("Селектор заголовков"), STR_RU("Селектор заголовков"),
STR_ZH("应用启动器"), STR_ZH_HANS("应用启动器"),
STR_TW("自製程式啓動器"), STR_ZH_HANT("自製程式啓動器"),
}, },
[StrId_ErrorReadingTitleMetadata] = [StrId_ErrorReadingTitleMetadata] =
{ {
STR_EN("Error reading title metadata.\n%08lX%08lX@%d"), STR_EN("Error reading title metadata.\n%08lX%08lX@%d"),
STR_ES("Error leyendo los metadatos de los títulos.\n%08lX%08lX@%d"), STR_ES("Error leyendo los metadatos de los títulos.\n%08lX%08lX@%d"),
STR_DE("Fehler beim lesen der Titel-Metadaten.\n%08lX%08lX@%d"), STR_DE("Fehler beim Lesen der Titel-Metadaten.\n%08lX%08lX@%d"),
STR_FR( STR_FR(
"Erreur lors de la lecture des métadonnées\n" "Erreur lors de la lecture des métadonnées\n"
"de titre.\n%08lX%08lX@%d" "du titre.\n%08lX%08lX@%d"
), ),
STR_IT("Errore nella lettura dei metadata dei titoli.\n%08lX%08lX@%d"), STR_IT("Errore nella lettura dei metadata dei titoli.\n%08lX%08lX@%d"),
STR_JP("タイトルメタデータを読み取ることができませんでした。\n%08lX%08lX@%d"), STR_JP("タイトルメタデータを読み取ることができませんでした。\n%08lX%08lX@%d"),
@ -465,8 +595,8 @@ const char* const g_strings[StrId_Max][16] =
STR_NL("Fout bij het lezen van titel metadata.\n%08lX%08lX@%d"), STR_NL("Fout bij het lezen van titel metadata.\n%08lX%08lX@%d"),
STR_KO("타이틀 메타데이터를 읽는데 실패하였습니다.\n%08lX%08lX@%d"), STR_KO("타이틀 메타데이터를 읽는데 실패하였습니다.\n%08lX%08lX@%d"),
STR_RU("Ошибка чтения метаданных заголовка\n.%08lX%08lX@%d"), STR_RU("Ошибка чтения метаданных заголовка\n.%08lX%08lX@%d"),
STR_ZH("读取软件相关信息时发生错误:\n%08lX%08lX@%d"), STR_ZH_HANS("读取软件相关信息时发生错误:\n%08lX%08lX@%d"),
STR_TW("讀取軟體相關數據時發生錯誤:\n%08lX%08lX@%d"), STR_ZH_HANT("讀取軟體相關資訊時發生錯誤:\n%08lX%08lX@%d"),
}, },
[StrId_NoTitlesFound] = [StrId_NoTitlesFound] =
@ -479,10 +609,10 @@ const char* const g_strings[StrId_Max][16] =
STR_JP("タイトルが見つかりませんでした。"), STR_JP("タイトルが見つかりませんでした。"),
STR_PT("Nenhum título foi encontrado."), STR_PT("Nenhum título foi encontrado."),
STR_NL("Geen titels gevonden."), STR_NL("Geen titels gevonden."),
STR_KO("타이틀을 찾지 못하였습니다."), STR_KO("타이틀을 찾을 수 없습니다."),
STR_RU("Заголовки не обнаружены"), STR_RU("Заголовки не обнаружены"),
STR_ZH("主机内找不到任何软件。"), STR_ZH_HANS("主机内找不到任何软件。"),
STR_TW("主機内找不到任何軟體。"), STR_ZH_HANT("主機内找不到任何軟體。"),
}, },
[StrId_SelectTitle] = [StrId_SelectTitle] =
@ -537,12 +667,12 @@ const char* const g_strings[StrId_Max][16] =
" \xEE\x80\x80 Выберите\n" " \xEE\x80\x80 Выберите\n"
" \xEE\x80\x81 Отмена" " \xEE\x80\x81 Отмена"
), ),
STR_ZH( STR_ZH_HANS(
"请选择一个目标软件。\n\n" "请选择一个目标软件。\n\n"
" \xEE\x80\x80 确认\n" " \xEE\x80\x80 确认\n"
" \xEE\x80\x81 取消" " \xEE\x80\x81 取消"
), ),
STR_TW( STR_ZH_HANT(
"請選擇一個目標軟體。\n\n" "請選擇一個目標軟體。\n\n"
" \xEE\x80\x80 確認\n" " \xEE\x80\x80 確認\n"
" \xEE\x80\x81 取消" " \xEE\x80\x81 取消"
@ -577,7 +707,7 @@ const char* const g_strings[StrId_Max][16] =
"Utilizza un exploit diverso." "Utilizza un exploit diverso."
), ),
STR_JP( STR_JP(
"この自家製のエクスプロイトは、ターゲットタイトルの\n" "この自作エクスプロイトでは、ターゲットタイトルの\n"
"下でアプリを起動することができません。\n" "下でアプリを起動することができません。\n"
"別のエクスプロイトを使用してください。" "別のエクスプロイトを使用してください。"
), ),
@ -592,8 +722,8 @@ const char* const g_strings[StrId_Max][16] =
"Gebruik een andere exploit." "Gebruik een andere exploit."
), ),
STR_KO( STR_KO(
"이 홈브류 익스플로잇은 해당 타이틀에서 플리케이션을\n" "이 홈브류 익스플로잇은 해당 타이틀에서 플리케이션을\n"
"실행시키는 것을 지원하지 않습니다.\n" "실행는 것을 지원하지 않습니다.\n"
"다른 익스플로잇을 사용해 주십시오." "다른 익스플로잇을 사용해 주십시오."
), ),
STR_RU( STR_RU(
@ -601,14 +731,14 @@ const char* const g_strings[StrId_Max][16] =
"приложений под целевыми заголовками.\n" "приложений под целевыми заголовками.\n"
"Пожалуйста, используйте другой эксплойт." "Пожалуйста, используйте другой эксплойт."
), ),
STR_ZH( STR_ZH_HANS(
"您所利用漏洞启动的「自制软件启动器」,\n" "您所利用漏洞启动的「自制软件启动器」,\n"
"无法在当前选中的软件中启动自制软件。\n" "无法在当前选中的软件中启动自制软件。\n"
"请使用其它的漏洞来启动「自制软件启动器」。" "请使用其它的漏洞来启动「自制软件启动器」。"
), ),
STR_TW( STR_ZH_HANT(
"您所利用漏洞開啓的「自製軟體啓動器」\n" "您所利用漏洞開啓的「自製軟體啓動器」\n"
"無法在當前選中的軟體啓動自製軟\n" "無法在當前選中的軟體啓動自製軟\n"
"請利用其它漏洞來啓動「自製軟體啓動器」。" "請利用其它漏洞來啓動「自製軟體啓動器」。"
), ),
}, },
@ -625,7 +755,7 @@ const char* const g_strings[StrId_Max][16] =
), ),
STR_DE( STR_DE(
"Die ausgewählte Anwendung benötigt einen\n" "Die ausgewählte Anwendung benötigt einen\n"
"Titel der nicht installiert ist" "Titel der nicht installiert ist."
), ),
STR_FR( STR_FR(
"L'application sélectionnée requiert un titre\n" "L'application sélectionnée requiert un titre\n"
@ -637,7 +767,7 @@ const char* const g_strings[StrId_Max][16] =
), ),
STR_JP( STR_JP(
"このアプリを実行するために\n" "このアプリを実行するために\n"
"適切なタイトルがインストールされていません。" "必要なタイトルがインストールされていません。"
), ),
STR_PT( STR_PT(
"A aplicação que acabou de tentar executar requer\n" "A aplicação que acabou de tentar executar requer\n"
@ -648,37 +778,37 @@ const char* const g_strings[StrId_Max][16] =
"vereist een titel die niet geinstalleerd is." "vereist een titel die niet geinstalleerd is."
), ),
STR_KO( STR_KO(
"시도한 애플리케이션의 실행에 필요한 타이틀이\n" "해당 애플리케이션은 시스템에 설치되지 않은\n"
"시스템에 설치되어 있지 않습니다." "타이틀을 요구합니다."
), ),
STR_RU( STR_RU(
"Для приложения требуется зависимость,\n" "Для приложения требуется зависимость,\n"
"которая не установлена." "которая не установлена."
), ),
STR_ZH( STR_ZH_HANS(
"主机找不到该应用程序\n" "主机找不到该应用程序\n"
"所需求的软件。" "所需求的软件。"
), ),
STR_TW( STR_ZH_HANT(
"主機找不到該應用程式\n" "主機找不到該應用程式\n"
"所需求的軟體。" "所需求的軟體。"
), ),
},*/ },*/
/*[StrId_NetLoader] = [StrId_NetLoader] =
{ {
STR_EN("3dslink NetLoader"), STR_EN("NetLoader"),
STR_ES("Cargador de programas 3dslink"), STR_ES("Cargador de programas"),
STR_DE("3dslink Netzwerk-Loader"), STR_DE("Netzwerk-Loader"),
STR_FR("Chargeur de programme 3dslink"), STR_FR("NetLoader"),
STR_IT("Caricamento programmi 3dslink"), STR_IT("Caricamento programmi"),
STR_JP("3dslinkネットローダ"), STR_JP("ネットローダ"),
STR_PT("Carregador de programas 3dslink"), STR_PT("Carregador de programas"),
STR_NL("3dslink netwerk lader"), STR_NL("netwerk lader"),
STR_KO("3dslink 넷로더"), STR_KO("네트워크 로더"),
STR_RU("Загрузчик 3dslink"), STR_RU("Загрузчик"),
STR_ZH("3dslink 网络执行模块"), STR_ZH_HANS("网络执行模块"),
STR_TW("3dslink 網路執行模組"), STR_ZH_HANT("網路執行模組"),
}, },
[StrId_NetLoaderUnavailable] = [StrId_NetLoaderUnavailable] =
@ -686,15 +816,15 @@ const char* const g_strings[StrId_Max][16] =
STR_EN("The NetLoader is currently unavailable."), STR_EN("The NetLoader is currently unavailable."),
STR_ES("El cargador de programas no está disponible."), STR_ES("El cargador de programas no está disponible."),
STR_DE("Der Netzwerk-Loader ist zur Zeit nicht verfügbar."), STR_DE("Der Netzwerk-Loader ist zur Zeit nicht verfügbar."),
STR_FR("Le chargeur de programme 3dslink est indisponible."), STR_FR("Le programme nxlink est indisponible."),
STR_IT("Il caricamento programmi 3dslink non è disponibile."), STR_IT("Il caricamento programmi nxlink non è disponibile."),
STR_JP("3dslinkネットローダを利用できません。"), STR_JP("nxlinkネットローダは現在利用できません。"),
STR_PT("O carregador de programas está de momento indisponível."), STR_PT("O carregador de programas está de momento indisponível."),
STR_NL("De netwerk lader is niet beschikbaar."), STR_NL("De netwerk lader is niet beschikbaar."),
STR_KO("넷로더는 현재 사용이 불가능 합니다."), STR_KO("현재 네트워크 로더는 사용이 불가합니다."),
STR_RU("Загрузчик в настоящее время недоступен."), STR_RU("Загрузчик в настоящее время недоступен."),
STR_ZH("无法启动 3dslink 网络执行模块。"), STR_ZH_HANS("无法启动 nxlink 网络执行模块。"),
STR_TW("無法啓動 3dslink 網路執行模組。"), STR_ZH_HANT("無法啓動 nxlink 網路執行模組。"),
}, },
[StrId_NetLoaderError] = [StrId_NetLoaderError] =
@ -707,81 +837,74 @@ const char* const g_strings[StrId_Max][16] =
STR_JP("エラーが発生しました。\n技術的な詳細:[%s:%d]"), STR_JP("エラーが発生しました。\n技術的な詳細:[%s:%d]"),
STR_PT("Ocorreu um erro.\nDetalhes técnicos: [%s:%d]"), STR_PT("Ocorreu um erro.\nDetalhes técnicos: [%s:%d]"),
STR_NL("Er is een fout opgetreden\nTechnische details: [%s:%d]"), STR_NL("Er is een fout opgetreden\nTechnische details: [%s:%d]"),
STR_KO("에러가 발생했습니다.\n상세: [%s:%d]"), STR_KO("오류가 발생했습니다.\n기술적인 세부사항: [%s:%d]"),
STR_RU("Произошла ошибка.\nТехнические подробности: [%s:%d]"), STR_RU("Произошла ошибка.\nТехнические подробности: [%s:%d]"),
STR_ZH("发生错误。\n详细错误信息:[%s:%d]"), STR_ZH_HANS("发生错误。\n详细错误信息:[%s:%d]"),
STR_TW("發生錯誤。\n詳細錯誤資訊:[%s:%d]"), STR_ZH_HANT("發生錯誤。\n詳細錯誤資訊:[%s:%d]"),
}, },
[StrId_NetLoaderOffline] = [StrId_NetLoaderOffline] =
{ {
STR_EN("Offline, waiting for network…\n\n\n \xEE\x80\x81 Cancel"), STR_EN("Offline, waiting for network…"),
STR_ZH("无法连接网络,等待网络连接…\n\n\n \xEE\x80\x81 取消"), STR_DE("Offline, warte auf Netzwerk…"),
STR_TW("當前離線,等待網路連線…\n\n\n \xEE\x80\x81 取消"), STR_FR("Hors-ligne, en attente d'une connection..."),
STR_IT("Disconnesso, in attesa della connessione…\n\n\n \xEE\x80\x81 Annullare"), STR_IT("Disconnesso, in attesa della connessione…"),
STR_ES("Desconectado, esperando a la red..."),
STR_JP("オフラインです。ネットワーク接続を待っています…"),
STR_KO("연결 끊김, 네트워크 기다리는 중…"),
STR_ZH_HANS("无法连接网络,等待网络连接…"),
STR_ZH_HANT("目前已離線,等待網路連線…"),
}, },
[StrId_NetLoaderActive] = [StrId_NetLoaderActive] =
{ {
STR_EN( STR_EN(
"Waiting for 3dslink to connect…\n" "Waiting for nxlink to connect…\n"
"IP Addr: %lu.%lu.%lu.%lu, Port: %d\n\n" "IP Addr: %lu.%lu.%lu.%lu, Port: %d"
" \xEE\x80\x81 Cancel"
), ),
STR_ES( STR_ES(
"Esperando a que se conecte 3dslink…\n" "Esperando a que se conecte nxlink…\n"
"Dir.IP: %lu.%lu.%lu.%lu, Puerto: %d\n\n" "Dir.IP: %lu.%lu.%lu.%lu, Puerto: %d"
" \xEE\x80\x81 Cancelar"
), ),
STR_DE( STR_DE(
"Warte auf Verbindung von 3dslink…\n" "Warte auf Verbindung von nxlink…\n"
"IP Addr: %lu.%lu.%lu.%lu, Port: %d\n\n" "IP Addr: %lu.%lu.%lu.%lu, Port: %d"
" \xEE\x80\x81 Abbrechen"
), ),
STR_FR( STR_FR(
"En attente de la connexion de 3dslink…\n" "En attente de la connexion de nxlink…\n"
"Adr. IP : %lu.%lu.%lu.%lu, Port : %d\n\n" "Adr. IP : %lu.%lu.%lu.%lu, Port : %d"
" \xEE\x80\x81 Annuler"
), ),
STR_IT( STR_IT(
"In attesa della connessione di 3dslink…\n" "In attesa della connessione di nxlink…\n"
"Ind. IP : %lu.%lu.%lu.%lu, Porta : %d\n\n" "Ind. IP : %lu.%lu.%lu.%lu, Porta : %d"
" \xEE\x80\x81 Annullare"
), ),
STR_JP( STR_JP(
"3dslinkが接続するのを待っている…\n" "nxlinkが接続されるのを待っています…\n"
"IPアドレス%lu.%lu.%lu.%lu, ポート番号:%d\n\n" "IPアドレス%lu.%lu.%lu.%lu, ポート番号:%d"
" \xEE\x80\x81 キャンセル"
), ),
STR_PT( STR_PT(
"A aguardar pela conexão do 3dslink…\n" "A aguardar pela conexão do nxlink…\n"
"End. IP: %lu.%lu.%lu.%lu, Porta: %d\n\n" "End. IP: %lu.%lu.%lu.%lu, Porta: %d"
" \xEE\x80\x81 Cancelar"
), ),
STR_NL( STR_NL(
"Wachten op 3dslink verbinding…\n" "Wachten op nxlink verbinding…\n"
"IP Addr: %lu.%lu.%lu.%lu, Poort: %d\n\n" "IP Addr: %lu.%lu.%lu.%lu, Poort: %d"
" \xEE\x80\x81 Annuleren"
), ),
STR_KO( STR_KO(
"3dslink 의 연결을 대기중…\n" "nxlink의 연결을 대기중…\n"
"IP 주소: %lu.%lu.%lu.%lu, 포트: %d\n\n" "IP 주소: %lu.%lu.%lu.%lu, 포트: %d"
" \xEE\x80\x81 취소"
), ),
STR_RU( STR_RU(
"Ожидание подключения 3dslink…\n" "Ожидание подключения nxlink…\n"
"айпи адрес: %lu.%lu.%lu.%lu, Порт: %d\n\n" "айпи адрес: %lu.%lu.%lu.%lu, Порт: %d"
" \xEE\x80\x81 Отмена"
), ),
STR_ZH( STR_ZH_HANS(
"等待 3dslink 连接…\n" "等待 nxlink 连接…\n"
"IP 地址:%lu.%lu.%lu.%lu端口%d\n\n" "IP 地址:%lu.%lu.%lu.%lu端口%d"
" \xEE\x80\x81 取消等待"
), ),
STR_TW( STR_ZH_HANT(
"等待 3dslink 連接…\n" "等待 nxlink 連接…\n"
"IP 位址:%lu.%lu.%lu.%lu連接埠%d\n\n" "IP 位址:%lu.%lu.%lu.%lu連接埠%d"
" \xEE\x80\x81 取消等待"
), ),
}, },
@ -808,8 +931,8 @@ const char* const g_strings[StrId_Max][16] =
"%zu di %zu KiB scritti" "%zu di %zu KiB scritti"
), ),
STR_JP( STR_JP(
"データが転送されます…\n" "データを転送しています…\n"
"%zu / %zu KiB 書かれた" "%zu / %zu KiB 転送済み"
), ),
STR_PT( STR_PT(
"A transferir…\n" "A transferir…\n"
@ -821,20 +944,19 @@ const char* const g_strings[StrId_Max][16] =
), ),
STR_KO( STR_KO(
"전송중…\n" "전송중…\n"
"%zu / %zu KiB 전송" "%zu / %zu KiB 쓰여짐"
), ),
STR_RU( STR_RU(
"Передача…\n" "Передача…\n"
"%zu из %zu КИБ написано" "%zu из %zu КИБ написано"
), ),
STR_ZH( STR_ZH_HANS(
"正在传输…\n" "正在传输…\n"
"已完成 %zu / %zu KiB" "已完成 %zu / %zu KiB"
), ),
STR_TW( STR_ZH_HANT(
"正在傳輸…\n" "正在傳輸…\n"
"已完成 %zu / %zu KiB" "已完成 %zu / %zu KiB"
), ),
},*/ },
}; };

View File

@ -1,24 +1,34 @@
#pragma once #pragma once
#ifdef SWITCH #ifdef __SWITCH__
#include <switch.h> #include <switch.h>
#endif #endif
typedef enum typedef enum
{ {
StrId_Loading = 0, StrId_Loading = 0,
StrId_AppletMode,
StrId_Directory, StrId_Directory,
StrId_DefaultPublisher, StrId_DefaultPublisher,
StrId_IOError, StrId_IOError,
StrId_CouldNotOpenFile, StrId_CouldNotOpenFile,
StrId_NroNotFound,
StrId_NoAppsFound_Title, StrId_NoAppsFound_Title,
StrId_NoAppsFound_Msg, StrId_NoAppsFound_Msg,
StrId_LastLoadResult,
StrId_AppLaunchError,
StrId_AppInfo_Author, StrId_AppInfo_Author,
StrId_AppInfo_Version, StrId_AppInfo_Version,
StrId_Actions_Launch, StrId_Actions_Launch,
StrId_Actions_Open, StrId_Actions_Open,
StrId_Actions_Back, StrId_Actions_Back,
StrId_Actions_Apply,
StrId_Actions_Star,
StrId_Actions_Unstar,
StrId_MsgBox_OK,
StrId_Reboot, StrId_Reboot,
StrId_ReturnToHome, StrId_ReturnToHome,
@ -38,8 +48,12 @@ typedef enum
StrId_NetLoaderActive, StrId_NetLoaderActive,
StrId_NetLoaderTransferring, StrId_NetLoaderTransferring,
StrId_ThemeMenu,
StrId_ThemeNotApplied,
StrId_DefaultThemeName,
StrId_Max, StrId_Max,
} StrId; } StrId;
extern const char* const g_strings[StrId_Max][16]; extern const char* const g_strings[StrId_Max][17];

View File

@ -2,7 +2,7 @@
size_t launchAddArg(argData_s* ad, const char* arg) { size_t launchAddArg(argData_s* ad, const char* arg) {
size_t len = strlen(arg)+1; size_t len = strlen(arg)+1;
if ((ad->dst+len) >= (char*)(ad+1)) return len; // Overflow if ((ad->dst+len) >= (char*)(ad->buf + sizeof(ad->buf))) return len; // Overflow
ad->buf[0]++; ad->buf[0]++;
strcpy(ad->dst, arg); strcpy(ad->dst, arg);
ad->dst += len; ad->dst += len;

View File

@ -16,7 +16,7 @@ typedef struct
//void (* useTitle)(u64 tid, u8 mediatype); //void (* useTitle)(u64 tid, u8 mediatype);
} loaderFuncs_s; } loaderFuncs_s;
void launchInit(void); bool launchInit(void);
void launchExit(void); void launchExit(void);
const loaderFuncs_s* launchGetLoader(void); const loaderFuncs_s* launchGetLoader(void);
size_t launchAddArg(argData_s* ad, const char* arg); size_t launchAddArg(argData_s* ad, const char* arg);

View File

@ -4,6 +4,6 @@
float approxSin(float x); float approxSin(float x);
inline float clamp(float x, float min, float max) { static inline float clamp(float x, float min, float max) {
return fmin(fmax(x, min), max); return fmin(fmax(x, min), max);
} }

View File

@ -1,25 +1,28 @@
#include "common.h" #include "common.h"
#include <physfs.h>
void menuEntryInit(menuEntry_s* me, MenuEntryType type) { void menuEntryInit(menuEntry_s* me, MenuEntryType type) {
memset(me, 0, sizeof(*me)); memset(me, 0, sizeof(*me));
me->type = type; me->type = type;
} }
void menuEntryFree(menuEntry_s* me) { void menuEntryFree(menuEntry_s* me, bool skip_icongfx) {
me->icon_size = 0; me->icon_size = 0;
if (me->icon) { if (me->icon) {
free(me->icon); free(me->icon);
me->icon = NULL; me->icon = NULL;
} }
if (me->icon_gfx) { if (!skip_icongfx) {
free(me->icon_gfx); if (me->icon_gfx) {
me->icon_gfx = NULL; free(me->icon_gfx);
} me->icon_gfx = NULL;
}
if (me->icon_gfx_small) { if (me->icon_gfx_small) {
free(me->icon_gfx_small); free(me->icon_gfx_small);
me->icon_gfx_small = NULL; me->icon_gfx_small = NULL;
}
} }
if (me->nacp) { if (me->nacp) {
@ -33,6 +36,11 @@ bool fileExists(const char* path) {
return stat(path, &st)==0 && S_ISREG(st.st_mode); return stat(path, &st)==0 && S_ISREG(st.st_mode);
} }
bool fsobjExists(const char* path) {
struct stat st;
return stat(path, &st)==0;
}
static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) { static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) {
NroHeader header; NroHeader header;
NroAssetHeader asset_header; NroAssetHeader asset_header;
@ -73,6 +81,57 @@ static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) {
return ok; return ok;
} }
static bool menuEntryLoadExternalIcon(menuEntry_s* me, const char* path, bool data_source) {
struct stat st;
if (data_source) {
return assetsPhysfsReadFile(path, &me->icon, &me->icon_size, false);
}
if(stat(path, &st)==-1) return false;
FILE* f = fopen(path, "rb");
if (!f) return false;
me->icon_size = st.st_size;
me->icon = (uint8_t*)malloc(me->icon_size);
if (me->icon == NULL) {
fclose(f);
return false;
}
memset(me->icon, 0, me->icon_size);
bool ok = fread(me->icon, me->icon_size, 1, f) == 1;
fclose(f);
return ok;
}
static bool menuEntryImportIconGfx(menuEntry_s* me, uint8_t* icon_gfx, uint8_t* icon_gfx_small) {
size_t tmpsize;
if (icon_gfx == NULL || icon_gfx_small == NULL) return false;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
tmpsize = layoutobj->size[0]*layoutobj->size[1]*3;
me->icon_gfx = (uint8_t*)malloc(tmpsize);
if (me->icon_gfx) memcpy(me->icon_gfx, icon_gfx, tmpsize);
if (me->icon_gfx) {
tmpsize = layoutobj2->size[0]*layoutobj2->size[1]*3;
me->icon_gfx_small = (uint8_t*)malloc(tmpsize);
if (me->icon_gfx_small) memcpy(me->icon_gfx_small, icon_gfx_small, tmpsize);
if (me->icon_gfx_small == NULL) {
free(me->icon_gfx);
me->icon_gfx = NULL;
}
}
return me->icon_gfx && me->icon_gfx_small;
}
static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) { static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) {
NroHeader header; NroHeader header;
NroAssetHeader asset_header; NroAssetHeader asset_header;
@ -117,6 +176,26 @@ static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) {
return ok; return ok;
} }
static bool menuEntryLoadExternalNacp(menuEntry_s* me, const char* path) {
struct stat st;
if(stat(path, &st)==-1) return false;
FILE* f = fopen(path, "rb");
if (!f) return false;
me->nacp = (NacpStruct*)malloc(sizeof(NacpStruct));
if (me->nacp == NULL) {
fclose(f);
return false;
}
memset(me->nacp, 0, sizeof(NacpStruct));
bool ok = fread(me->nacp, sizeof(NacpStruct), 1, f) == 1;
fclose(f);
return ok;
}
/*static void fixSpaceNewLine(char* buf) { /*static void fixSpaceNewLine(char* buf) {
char *outp = buf, *inp = buf; char *outp = buf, *inp = buf;
char lastc = 0; char lastc = 0;
@ -131,20 +210,29 @@ static bool menuEntryLoadEmbeddedNacp(menuEntry_s* me) {
} while (lastc); } while (lastc);
}*/ }*/
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) { bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists) {
static char tempbuf[PATH_MAX+1]; int i=0, tmplen;
menu_s *menu_fileassoc = menuFileassocGetCurrent();
menuEntry_s* fileassoc_me = NULL;
char *strptr = NULL;
char tempbuf[PATH_MAX+16];
//bool isOldAppFolder = false; //bool isOldAppFolder = false;
if (check_exists && !fsobjExists(me->path)) return false;
tempbuf[PATH_MAX] = 0; tempbuf[PATH_MAX] = 0;
strcpy(me->name, name); strncpy(me->name, name, sizeof(me->name)-1);
if (me->type == ENTRY_TYPE_FOLDER) if (me->type == ENTRY_TYPE_FOLDER)
{ {
//Check for <dirpath>/<dirname>.nro //Check for <dirpath>/<dirname>.nro
snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s/%.*s.nro", (int)sizeof(tempbuf)/2, me->path, (int)sizeof(tempbuf)/2-7, name); snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s/%.*s.nro", (int)sizeof(tempbuf)/2, me->path, (int)sizeof(tempbuf)/2-7, name);
bool found = fileExists(tempbuf); bool found = fileExists(tempbuf);
bool fileassoc_flag = 0;
//Use the first .nro found in the directory, if there's only 1 NRO in the directory. Only used for paths starting with "sdmc:/switch/". //Use the first .nro found in the directory, if there's only 1 NRO in the directory. Only used for paths starting with "sdmc:/switch/".
if (!found && strncmp(me->path, "sdmc:/switch/", 13)==0) { tmplen = strlen(menuGetRootPath());
if (!found && strncmp(me->path, menuGetRootPath(), tmplen)==0 && me->path[tmplen]=='/') {
DIR* dir; DIR* dir;
struct dirent* dp; struct dirent* dp;
u32 nro_count=0; u32 nro_count=0;
@ -169,22 +257,44 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
} }
closedir(dir); closedir(dir);
} }
if (!found && menu_fileassoc->nEntries > 0) {
fileassoc_flag = 1;
dir = opendir(me->path);
if (dir) {
while ((dp = readdir(dir))) {
if (dp->d_name[0]=='.')//Check this here so that it's consistent with menuScan().
continue;
for (fileassoc_me = menu_fileassoc->firstEntry, i = 0; fileassoc_me; fileassoc_me = fileassoc_me->next, i++) {
if (!fileassoc_me->fileassoc_type) continue; //Only handle fileassoc entries for filenames, not file_extensions.
if (strcmp(dp->d_name, fileassoc_me->fileassoc_str)) continue;
snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s/%.*s", (int)sizeof(tempbuf)/2, me->path, (int)sizeof(tempbuf)/2-7, dp->d_name);
found = fileExists(tempbuf);
if (found) break;
}
if (found) break;
}
closedir(dir);
}
}
} }
if (found) if (found)
{ {
//isOldAppFolder = true; //isOldAppFolder = true;
shortcut = false; shortcut = false;
me->type = ENTRY_TYPE_FILE; me->type = fileassoc_flag ? ENTRY_TYPE_FILE_OTHER : ENTRY_TYPE_FILE;
strcpy(me->path, tempbuf); strcpy(me->path, tempbuf);
} }
} }
if (me->type == ENTRY_TYPE_FILE) if (me->type == ENTRY_TYPE_FILE)
{ {
strcpy(me->name, name); strncpy(me->author, textGetString(StrId_DefaultPublisher), sizeof(me->author)-1);
strcpy(me->author, textGetString(StrId_DefaultPublisher)); strncpy(me->version, "1.0.0", sizeof(me->version)-1);
strcpy(me->version, "1.0.0");
//shortcut_s sc; //shortcut_s sc;
@ -221,7 +331,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
char* ext = getExtension(tempbuf); char* ext = getExtension(tempbuf);
strcpy(ext, ".jpg"); strcpy(ext, ".jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf); iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) break; if (iconLoaded) break;
if (isOldAppFolder) if (isOldAppFolder)
@ -229,7 +339,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
char* slash = getSlash(tempbuf); char* slash = getSlash(tempbuf);
strcpy(slash, "/icon.jpg"); strcpy(slash, "/icon.jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf); iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) break; if (iconLoaded) break;
}*/ }*/
@ -293,68 +403,395 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
/*if (shortcut) /*if (shortcut)
shortcutFree(&sc);*/ shortcutFree(&sc);*/
} }
if (me->type == ENTRY_TYPE_THEME) {
config_t cfg = {0};
config_init(&cfg);
config_setting_t *themeInfo;
const char *name,
*author = textGetString(StrId_DefaultPublisher),
*version = "1.0.0";
const char* cfg_path = me->path;
const char* theme_archive_path = NULL;
const char* ext = getExtension(me->path);
bool good_cfg = false;
bool is_archive = false;
#ifdef __SWITCH__
bool is_romfs = false;
if (strcasecmp(ext, ".romfs")==0) {
if (R_FAILED(romfsMountFromFsdev(me->path, 0, "themetmp")))
return false;
is_romfs = true;
cfg_path = "themetmp:/theme.cfg";
theme_archive_path = "themetmp:/";
}
#endif
if (strcasecmp(ext, ".romfs")!=0 && strcasecmp(ext, ".cfg")!=0) {
theme_archive_path = me->path;
}
if (theme_archive_path) {
if (!PHYSFS_mount(theme_archive_path, "themetmp", 0)) cfg_path = NULL;
else {
is_archive = true;
cfg_path = "themetmp/theme.cfg";
}
}
if (cfg_path) {
if (!is_archive) good_cfg = config_read_file(&cfg, cfg_path);
else {
u8 *cfg_buf = NULL;
good_cfg = assetsPhysfsReadFile(cfg_path, &cfg_buf, NULL, true);
if (good_cfg) good_cfg = config_read_string(&cfg, (char*)cfg_buf);
free(cfg_buf);
}
}
if (good_cfg) {
themeInfo = config_lookup(&cfg, "themeInfo");
if (themeInfo != NULL) {
if(config_setting_lookup_string(themeInfo, "name", &name))
strncpy(me->name, name, sizeof(me->name)-1);
config_setting_lookup_string(themeInfo, "author", &author);
config_setting_lookup_string(themeInfo, "version", &version);
}
}
strncpy(me->author, author, sizeof(me->author)-1);
strncpy(me->version, version, sizeof(me->version)-1);
config_destroy(&cfg);
if (good_cfg && is_archive) {
bool iconLoaded = false;
iconLoaded = menuEntryLoadExternalIcon(me, "themetmp/icon.jpg", true);
if (iconLoaded) menuEntryParseIcon(me);
}
if (is_archive) PHYSFS_unmount(theme_archive_path);
#ifdef __SWITCH__
if (is_romfs) romfsUnmount("themetmp");
#endif
}
if (me->type == ENTRY_TYPE_FILE_OTHER)
{
if (menu_fileassoc->nEntries == 0) return false;
for (fileassoc_me = menu_fileassoc->firstEntry, i = 0; fileassoc_me; fileassoc_me = fileassoc_me->next, i++) {
//For fileassoc_type==0 compare the extension, otherwise compare the filename.
if (!fileassoc_me->fileassoc_type) {
strptr = getExtension(me->path);
}
if (fileassoc_me->fileassoc_type) {
strptr = getSlash(me->path);
if (strptr[0] == '/') strptr++;
}
if (strcmp(strptr, fileassoc_me->fileassoc_str)) continue;
//At this point a match was found.
me->type = ENTRY_TYPE_FILE;
//Attempt to load the icon from {me->path filepath with extension .jpg}, then on failure use the icon data from fileassoc_me.
memset(tempbuf, 0, sizeof(tempbuf));
strncpy(tempbuf, me->path, sizeof(tempbuf));
tempbuf[sizeof(tempbuf)-1] = 0;
strptr = getExtension(tempbuf);
strncpy(strptr, ".jpg", sizeof(tempbuf)-1 - ((ptrdiff_t)strptr - (ptrdiff_t)tempbuf));
bool iconLoaded = false;
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) menuEntryParseIcon(me);
if (iconLoaded && !(me->icon_gfx && me->icon_gfx_small)) iconLoaded = 0;
if (!iconLoaded && fileassoc_me->icon_gfx && fileassoc_me->icon_gfx_small)
iconLoaded = menuEntryImportIconGfx(me, fileassoc_me->icon_gfx, fileassoc_me->icon_gfx_small);
//Attempt to load the nacp from {me->path filepath with extension .nacp}, then on failure use the config from fileassoc_me.
memset(tempbuf, 0, sizeof(tempbuf));
strncpy(tempbuf, me->path, sizeof(tempbuf));
tempbuf[sizeof(tempbuf)-1] = 0;
strptr = getExtension(tempbuf);
strncpy(strptr, ".nacp", sizeof(tempbuf)-1 - ((ptrdiff_t)strptr - (ptrdiff_t)tempbuf));
bool nacpLoaded = menuEntryLoadExternalNacp(me, tempbuf);
if (nacpLoaded) menuEntryParseNacp(me);
else {
strncpy(me->author, fileassoc_me->author, sizeof(me->author));
me->author[sizeof(me->author)-1] = 0;
strncpy(me->version, fileassoc_me->version, sizeof(me->version));
me->version[sizeof(me->version)-1] = 0;
}
// Initialize the argument data
argData_s* ad = &me->args;
argData_s* ad_assoc = &fileassoc_me->args;
char *arg_src = (char*)&ad_assoc->buf[1];
bool ftoken_found=0;
ad->dst = (char*)&ad->buf[1];
for (u32 argi=0; argi<ad_assoc->buf[0]; argi++, arg_src+= strlen(arg_src)+1) {
if (argi) {
strptr = strchr(arg_src, '%');
if (strptr && strptr[0] && strptr[1] && (strptr == arg_src || strptr[-1] != '\\')) {
if (strptr[1] == 'f') {
memset(tempbuf, 0, sizeof(tempbuf));
snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s%s%s", (int)((uintptr_t)strptr-(uintptr_t)arg_src), arg_src, me->path, &strptr[2]);
launchAddArg(ad, tempbuf);
ftoken_found = 1;
continue;
}
}
}
launchAddArg(ad, arg_src);
}
if (!ftoken_found) launchAddArg(ad, me->path);
strncpy(me->path, fileassoc_me->path, sizeof(me->path));
me->path[sizeof(me->path)-1] = 0;
return true;
}
return false;
}
//check for .filename.star in same path
strptr = getSlash(me->path);
if (strptr[0] == '/') strptr++;
int strptrLen = strlen(strptr);
snprintf(me->starpath, sizeof(me->starpath)-1, "%.*s.%.*s.star", (int)(strlen(me->path) - strptrLen), me->path, (int)strptrLen, strptr);
me->starred = fileExists(me->starpath);
return true; return true;
} }
void menuEntryParseIcon(menuEntry_s* me) { void menuEntryFileassocLoad(const char* filepath) {
uint8_t *imageptr = NULL; bool success=0, iconLoaded=0;
size_t imagesize = 256*256*3; menuEntry_s* me = NULL;
config_setting_t *fileassoc = NULL, *targets = NULL, *target = NULL, *app_args = NULL, *target_args = NULL;
config_t cfg = {0};
int targets_len=0, args_len=0, i;
const char *strptr = NULL;
char app_path[PATH_MAX+8];
char main_icon_path[PATH_MAX+1];
char target_icon_path[PATH_MAX+1];
char target_file_extension[PATH_MAX+1];
char target_filename[PATH_MAX+1];
char app_author[ENTRY_AUTHORLENGTH+2];
char app_version[ENTRY_VERLENGTH+2];
uint8_t *app_icon_gfx = NULL;
uint8_t *app_icon_gfx_small = NULL;
config_init(&cfg);
memset(app_path, 0, sizeof(app_path));
memset(main_icon_path, 0, sizeof(main_icon_path));
memset(app_author, 0, sizeof(app_author));
memset(app_version, 0, sizeof(app_version));
if (!fileExists(filepath)) return;
if (config_read_file(&cfg, filepath)) {
fileassoc = config_lookup(&cfg, "fileassoc");
if (fileassoc != NULL) {
if (config_setting_lookup_string(fileassoc, "app_path", &strptr))
snprintf(app_path, sizeof(app_path)-1, "%s%s", menuGetRootBasePath(), strptr);
if (config_setting_lookup_string(fileassoc, "icon_path", &strptr))
snprintf(main_icon_path, sizeof(main_icon_path)-1, "%s%s", menuGetRootBasePath(), strptr);
app_args = config_setting_lookup(fileassoc, "app_args");
targets = config_setting_lookup(fileassoc, "targets");
if (app_path[0] && targets) {
targets_len = config_setting_length(targets);
if (targets_len > 0) {
//Load the author/version and icon data with the NRO app path.
me = menuCreateEntry(ENTRY_TYPE_FILE);
success = 0;
if (me) {
strncpy(me->path, app_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0;
strptr = getSlash(app_path);
if(strptr[0] == '/') strptr++;
if (menuEntryLoad(me, strptr, 0, true)) {
strncpy(app_author, me->author, sizeof(app_author));
app_author[sizeof(app_author)-1] = 0;
strncpy(app_version, me->version, sizeof(app_version));
app_version[sizeof(app_version)-1] = 0;
app_icon_gfx = me->icon_gfx;
app_icon_gfx_small = me->icon_gfx_small;
success = 1;
}
menuDeleteEntry(me, success);
me = NULL;
}
//Process the targets list.
if (success) {
for (i=0; i<targets_len; i++) {
target = config_setting_get_elem(targets, i);
if (target == NULL) continue;
memset(target_icon_path, 0, sizeof(target_icon_path));
memset(target_file_extension, 0, sizeof(target_file_extension));
memset(target_filename, 0, sizeof(target_filename));
if (config_setting_lookup_string(target, "icon_path", &strptr))
snprintf(target_icon_path, sizeof(target_icon_path)-1, "%s%s", menuGetRootBasePath(), strptr);
if (config_setting_lookup_string(target, "file_extension", &strptr))
strncpy(target_file_extension, strptr, sizeof(target_file_extension)-1);
if (config_setting_lookup_string(target, "filename", &strptr))
strncpy(target_filename, strptr, sizeof(target_filename)-1);
target_args = config_setting_lookup(target, "app_args");
//string_is_set for target_file_extension and target_filename must differ: only 1 can be set, not both set or both not set.
if ((target_file_extension[0]!=0) == (target_filename[0]!=0)) continue;
me = menuCreateEntry(ENTRY_TYPE_FILEASSOC);
iconLoaded = 0;
if (me) {
strncpy(me->path, app_path, sizeof(me->path));
me->path[sizeof(me->path)-1] = 0;
strncpy(me->author, app_author, sizeof(me->author));
me->author[sizeof(me->author)-1] = 0;
strncpy(me->version, app_version, sizeof(me->version));
me->version[sizeof(me->version)-1] = 0;
if (target_file_extension[0]) {
me->fileassoc_type = 0;
strncpy(me->fileassoc_str, target_file_extension, sizeof(me->fileassoc_str));
} else if (target_filename[0]) {
me->fileassoc_type = 1;
strncpy(me->fileassoc_str, target_filename, sizeof(me->fileassoc_str));
}
me->fileassoc_str[sizeof(me->fileassoc_str)-1] = 0;
if (target_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, target_icon_path, false);
if (!iconLoaded && main_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, main_icon_path, false);
if (iconLoaded) {
menuEntryParseIcon(me);
} else {
iconLoaded = menuEntryImportIconGfx(me, app_icon_gfx, app_icon_gfx_small);
}
argData_s* ad = &me->args;
ad->dst = (char*)&ad->buf[1];
launchAddArg(ad, me->path);
config_setting_t *config_args = target_args ? target_args : app_args;
if (config_args) {
args_len = config_setting_length(config_args);
for (int argi=0; argi<args_len; argi++) {
strptr = config_setting_get_string_elem(config_args, argi);
if (strptr==NULL) continue;
launchAddArg(ad, strptr);
}
}
}
if (me) menuFileassocAddEntry(me);
}
}
}
}
}
}
if (success) {
free(app_icon_gfx);
free(app_icon_gfx_small);
}
config_destroy(&cfg);
}
void menuEntryParseIcon(menuEntry_s* me) {
if (me->icon_size==0 || me->icon==NULL) return; if (me->icon_size==0 || me->icon==NULL) return;
njInit(); ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
if (njDecode(me->icon, me->icon_size) != NJ_OK) { size_t imagesize = layoutobj->imageSize[0]*layoutobj->imageSize[1]*3;
njDone(); bool ret=true;
return; uint8_t *tmp_gfx = (uint8_t*)malloc(imagesize);
}
if (tmp_gfx == NULL) ret = false;
if (ret) ret = assetsLoadJpgFromMemory(me->icon, me->icon_size, tmp_gfx, IMAGE_MODE_RGB24, layoutobj->imageSize[0], layoutobj->imageSize[1]);
if (ret) me->icon_gfx = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], IMAGE_MODE_RGB24);
if (ret && me->icon_gfx==NULL) ret = false;
me->icon_size = 0; me->icon_size = 0;
free(me->icon); free(me->icon);
me->icon = NULL; me->icon = NULL;
if ((njGetWidth() != 256 || njGetHeight() != 256 || (size_t)njGetImageSize() != imagesize) || njIsColor() != 1) {//The decoded image must be RGB and 256x256. if (ret) me->icon_gfx_small = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], IMAGE_MODE_RGB24);
njDone();
return; if (!ret || me->icon_gfx_small == NULL) {
free(me->icon_gfx);
me->icon_gfx = NULL;
} }
imageptr = njGetImage(); free(tmp_gfx);
if (imageptr == NULL) {
njDone();
return;
}
me->icon_gfx = (uint8_t*)malloc(imagesize);
if (me->icon_gfx == NULL) {
njDone();
return;
}
memcpy(me->icon_gfx, imageptr, imagesize);
njDone();
me->icon_gfx_small = downscaleIcon(me->icon_gfx);
} }
uint8_t *downscaleIcon(const uint8_t *image) { uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode) {
uint8_t *out = (uint8_t*)malloc(140*140*3); uint8_t *out;
switch (mode) {
case IMAGE_MODE_RGBA32:
out = (uint8_t*)malloc(destWidth*destHeight*4);
break;
default:
out = (uint8_t*)malloc(destWidth*destHeight*3);
break;
}
if (out == NULL) { if (out == NULL) {
return NULL; return NULL;
} }
if (srcWidth == destWidth && srcHeight == destHeight) {
memcpy(out, image, destWidth*destHeight*(mode==IMAGE_MODE_RGBA32 ? 4 : 3));
return out;
}
int tmpx, tmpy; int tmpx, tmpy;
int pos; int pos;
float sourceX, sourceY; float sourceX, sourceY;
int destWidth = 140, destHeight = 140; float xScale = (float)srcWidth / (float)destWidth;
float xScale = 256.0 / (float)destWidth; float yScale = (float)srcHeight / (float)destHeight;
float yScale = 256.0 / (float)destHeight;
int pixelX, pixelY; int pixelX, pixelY;
uint8_t r1, r2, r3, r4; uint8_t r1, r2, r3, r4;
uint8_t g1, g2, g3, g4; uint8_t g1, g2, g3, g4;
uint8_t b1, b2, b3, b4; uint8_t b1, b2, b3, b4;
uint8_t a1, a2, a3, a4;
float fx, fy, fx1, fy1; float fx, fy, fx1, fy1;
int w1, w2, w3, w4; int w1, w2, w3, w4;
@ -366,26 +803,57 @@ uint8_t *downscaleIcon(const uint8_t *image) {
pixelY = (int)sourceY; pixelY = (int)sourceY;
// get colours from four surrounding pixels // get colours from four surrounding pixels
pos = ((pixelY + 0) * 256 + pixelX + 0) * 3; if (mode == IMAGE_MODE_RGBA32)
pos = ((pixelY + 0) * srcWidth + pixelX + 0) * 4;
else
pos = ((pixelY + 0) * srcWidth + pixelX + 0) * 3;
r1 = image[pos+0]; r1 = image[pos+0];
g1 = image[pos+1]; g1 = image[pos+1];
b1 = image[pos+2]; b1 = image[pos+2];
if (mode == IMAGE_MODE_RGBA32)
a1 = image[pos+3];
if (mode == IMAGE_MODE_RGBA32)
pos = ((pixelY + 0) * srcWidth + pixelX + 1) * 4;
else
pos = ((pixelY + 0) * srcWidth + pixelX + 1) * 3;
pos = ((pixelY + 0) * 256 + pixelX + 1) * 3;
r2 = image[pos+0]; r2 = image[pos+0];
g2 = image[pos+1]; g2 = image[pos+1];
b2 = image[pos+2]; b2 = image[pos+2];
pos = ((pixelY + 1) * 256 + pixelX + 0) * 3; if (mode == IMAGE_MODE_RGBA32)
a2 = image[pos+3];
if (mode == IMAGE_MODE_RGBA32)
pos = ((pixelY + 1) * srcWidth + pixelX + 0) * 4;
else
pos = ((pixelY + 1) * srcWidth + pixelX + 0) * 3;
r3 = image[pos+0]; r3 = image[pos+0];
g3 = image[pos+1]; g3 = image[pos+1];
b3 = image[pos+2]; b3 = image[pos+2];
pos = ((pixelY + 1) * 256 + pixelX + 1) * 3; if (mode == IMAGE_MODE_RGBA32)
a3 = image[pos+3];
if (mode == IMAGE_MODE_RGBA32)
pos = ((pixelY + 1) * srcWidth + pixelX + 1) * 4;
else
pos = ((pixelY + 1) * srcWidth + pixelX + 1) * 3;
r4 = image[pos+0]; r4 = image[pos+0];
g4 = image[pos+1]; g4 = image[pos+1];
b4 = image[pos+2]; b4 = image[pos+2];
if (mode == IMAGE_MODE_RGBA32)
a4 = image[pos+3];
// determine weights // determine weights
fx = sourceX - pixelX; fx = sourceX - pixelX;
fy = sourceY - pixelY; fy = sourceY - pixelY;
@ -398,10 +866,17 @@ uint8_t *downscaleIcon(const uint8_t *image) {
w4 = (int)(fx*fy*256.0); w4 = (int)(fx*fy*256.0);
// set output pixels // set output pixels
pos = ((tmpy*destWidth) + tmpx) * 3; if (mode == IMAGE_MODE_RGBA32)
pos = ((tmpy*destWidth) + tmpx) * 4;
else
pos = ((tmpy*destWidth) + tmpx) * 3;
out[pos+0] = (uint8_t)((r1 * w1 + r2 * w2 + r3 * w3 + r4 * w4) >> 8); out[pos+0] = (uint8_t)((r1 * w1 + r2 * w2 + r3 * w3 + r4 * w4) >> 8);
out[pos+1] = (uint8_t)((g1 * w1 + g2 * w2 + g3 * w3 + g4 * w4) >> 8); out[pos+1] = (uint8_t)((g1 * w1 + g2 * w2 + g3 * w3 + g4 * w4) >> 8);
out[pos+2] = (uint8_t)((b1 * w1 + b2 * w2 + b3 * w3 + b4 * w4) >> 8); out[pos+2] = (uint8_t)((b1 * w1 + b2 * w2 + b3 * w3 + b4 * w4) >> 8);
if (mode == IMAGE_MODE_RGBA32)
out[pos+3] = (uint8_t)((a1 * w1 + a2 * w2 + a3 * w3 + a4 * w4) >> 8);
} }
} }
@ -409,13 +884,24 @@ uint8_t *downscaleIcon(const uint8_t *image) {
} }
void menuEntryParseNacp(menuEntry_s* me) { void menuEntryParseNacp(menuEntry_s* me) {
int lang = 0;//TODO: Update this once libnx supports settings get-language. NacpLanguageEntry *langentry = NULL;
if (me->nacp==NULL) return; if (me->nacp==NULL) return;
strncpy(me->name, me->nacp->lang[lang].name, sizeof(me->name)-1); strncpy(me->version, me->nacp->display_version, sizeof(me->version)-1);
strncpy(me->author, me->nacp->lang[lang].author, sizeof(me->author)-1);
strncpy(me->version, me->nacp->version, sizeof(me->version)-1); #ifdef __SWITCH__
Result rc=0;
rc = nacpGetLanguageEntry(me->nacp, &langentry);
if (R_SUCCEEDED(rc) && langentry!=NULL) {
#else
langentry = &me->nacp->lang[0];
if (1) {
#endif
strncpy(me->name, langentry->name, sizeof(me->name)-1);
strncpy(me->author, langentry->author, sizeof(me->author)-1);
}
free(me->nacp); free(me->nacp);
me->nacp = NULL; me->nacp = NULL;

View File

@ -1,25 +1,29 @@
#include "common.h" #include "common.h"
static menu_s s_menu[2]; static menu_s s_menu[2];
static bool s_curMenu; static menu_s s_menuFileassoc[2];
static bool s_curMenu, s_curMenuFileassoc;
menu_s* menuGetCurrent(void) { menu_s* menuGetCurrent(void) {
return &s_menu[s_curMenu]; return &s_menu[s_curMenu];
} }
static menuEntry_s* menuCreateEntry(MenuEntryType type) { menu_s* menuFileassocGetCurrent(void) {
return &s_menuFileassoc[s_curMenuFileassoc];
}
menuEntry_s* menuCreateEntry(MenuEntryType type) {
menuEntry_s* me = (menuEntry_s*)malloc(sizeof(menuEntry_s)); menuEntry_s* me = (menuEntry_s*)malloc(sizeof(menuEntry_s));
menuEntryInit(me, type); menuEntryInit(me, type);
return me; return me;
} }
static void menuDeleteEntry(menuEntry_s* me) { void menuDeleteEntry(menuEntry_s* me, bool skip_icongfx) {
menuEntryFree(me); menuEntryFree(me, skip_icongfx);
free(me); free(me);
} }
static void menuAddEntry(menuEntry_s* me) { static void _menuAddEntry(menu_s *m, menuEntry_s* me) {
menu_s* m = &s_menu[!s_curMenu];
me->menu = m; me->menu = m;
if (m->lastEntry) if (m->lastEntry)
{ {
@ -30,20 +34,54 @@ static void menuAddEntry(menuEntry_s* me) {
m->firstEntry = me; m->firstEntry = me;
m->lastEntry = me; m->lastEntry = me;
} }
m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++; m->nEntries ++;
} }
static void menuClear(void) { static void menuAddEntry(menuEntry_s* me) {
_menuAddEntry(&s_menu[!s_curMenu], me);
}
void menuFileassocAddEntry(menuEntry_s* me) {
_menuAddEntry(&s_menuFileassoc[!s_curMenuFileassoc], me);
}
static void menuAddEntryToFront(menuEntry_s* me) {
menu_s* m = &s_menu[!s_curMenu]; menu_s* m = &s_menu[!s_curMenu];
me->menu = m;
if (m->lastEntry)
{
me->next = m->firstEntry;
m->firstEntry = me;
} else
{
m->firstEntry = me;
m->lastEntry = me;
}
m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++;
}
static void _menuClear(menu_s* m) {
menuEntry_s *cur, *next; menuEntry_s *cur, *next;
for (cur = m->firstEntry; cur; cur = next) for (cur = m->firstEntry; cur; cur = next)
{ {
next = cur->next; next = cur->next;
menuDeleteEntry(cur); menuDeleteEntry(cur, 0);
} }
memset(m, 0, sizeof(*m)); memset(m, 0, sizeof(*m));
} }
static void menuClear(void) {
_menuClear(&s_menu[!s_curMenu]);
}
static void menuFileassocClear(void) {
_menuClear(&s_menuFileassoc[!s_curMenuFileassoc]);
}
static int menuEntryCmp(const void *p1, const void *p2) { static int menuEntryCmp(const void *p1, const void *p2) {
const menuEntry_s* lhs = *(menuEntry_s**)p1; const menuEntry_s* lhs = *(menuEntry_s**)p1;
const menuEntry_s* rhs = *(menuEntry_s**)p2; const menuEntry_s* rhs = *(menuEntry_s**)p2;
@ -60,20 +98,34 @@ static void menuSort(void) {
menu_s* m = &s_menu[!s_curMenu]; menu_s* m = &s_menu[!s_curMenu];
int nEntries = m->nEntries; int nEntries = m->nEntries;
if (nEntries==0) return; if (nEntries==0) return;
int nEntriesStar = 0, nEntriesNoStar = 0;
menuEntry_s** list = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*)); menuEntry_s** list = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*));
if(list == NULL) return; if(list == NULL) return;
menuEntry_s** listStar = (menuEntry_s**)calloc(nEntries, sizeof(menuEntry_s*));
if(listStar == NULL) {
free(list);
return;
}
menuEntry_s* p = m->firstEntry; menuEntry_s* p = m->firstEntry;
for(i = 0; i < nEntries; ++i) { for(i = 0; i < nEntries; ++i) {
list[i] = p; if (p->starred)
listStar[nEntriesStar++] = p;
else
list[nEntriesNoStar++] = p;
p = p->next; p = p->next;
} }
qsort(list, nEntries, sizeof(menuEntry_s*), menuEntryCmp); qsort(listStar, nEntriesStar, sizeof(menuEntry_s*), menuEntryCmp);
qsort(list, nEntriesNoStar, sizeof(menuEntry_s*), menuEntryCmp);
menuEntry_s** pp = &m->firstEntry; menuEntry_s** pp = &m->firstEntry;
for(i = 0; i < nEntries; ++i) { for(i = 0; i < nEntriesStar; ++i) {
*pp = listStar[i];
pp = &(*pp)->next;
}
for(i = 0; i < nEntriesNoStar; ++i) {
*pp = list[i]; *pp = list[i];
pp = &(*pp)->next; pp = &(*pp)->next;
} }
@ -81,13 +133,33 @@ static void menuSort(void) {
*pp = NULL; *pp = NULL;
free(list); free(list);
free(listStar);
}
void menuReorder (void) {
s_curMenu = !s_curMenu;
menuSort();
s_curMenu = !s_curMenu;
menuClear();
} }
int menuScan(const char* target) { int menuScan(const char* target) {
int pos;
char dirsep[8];
if (chdir(target) < 0) return 1; if (chdir(target) < 0) return 1;
if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL) if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL)
return 1; return 1;
memset(dirsep, 0, sizeof(dirsep));
dirsep[0] = '/';
//While cwd will not have '/' at the end normally, it will have it when cwd is the root dir ("sdmc:/"). Don't add '/' to the path below when it's already present.
pos = strlen(s_menu[!s_curMenu].dirname);
if (pos > 0) {
if (s_menu[!s_curMenu].dirname[pos-1] == '/') dirsep[0] = 0;
}
DIR* dir; DIR* dir;
struct dirent* dp; struct dirent* dp;
char tmp_path[PATH_MAX+1]; char tmp_path[PATH_MAX+1];
@ -104,13 +176,12 @@ int menuScan(const char* target) {
bool entrytype=0; bool entrytype=0;
memset(tmp_path, 0, sizeof(tmp_path)); memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s", s_menu[!s_curMenu].dirname, dp->d_name); snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s", s_menu[!s_curMenu].dirname, dirsep, dp->d_name);
#ifdef SWITCH #ifdef _DIRENT_HAVE_D_TYPE
fsdev_dir_t* dirSt = (fsdev_dir_t*)dir->dirData->dirStruct; if (dp->d_type == DT_UNKNOWN)
FsDirectoryEntry* entry = &dirSt->entry_data[dirSt->index]; continue;
entrytype = dp->d_type != DT_REG;
entrytype = entry->type == ENTRYTYPE_DIR;
#else #else
struct stat tmpstat; struct stat tmpstat;
@ -127,16 +198,21 @@ int menuScan(const char* target) {
const char* ext = getExtension(dp->d_name); const char* ext = getExtension(dp->d_name);
if (strcasecmp(ext, ".nro")==0/* || (shortcut = strcasecmp(ext, ".xml")==0)*/) if (strcasecmp(ext, ".nro")==0/* || (shortcut = strcasecmp(ext, ".xml")==0)*/)
me = menuCreateEntry(ENTRY_TYPE_FILE); me = menuCreateEntry(ENTRY_TYPE_FILE);
if (!me)
me = menuCreateEntry(ENTRY_TYPE_FILE_OTHER);
} }
if (!me) if (!me)
continue; continue;
strncpy(me->path, tmp_path, sizeof(me->path)-1); strncpy(me->path, tmp_path, sizeof(me->path)-1);
if (menuEntryLoad(me, dp->d_name, shortcut)) me->path[sizeof(me->path)-1] = 0;
if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me); menuAddEntry(me);
else else
menuDeleteEntry(me); menuDeleteEntry(me, 0);
} }
closedir(dir); closedir(dir);
@ -148,3 +224,105 @@ int menuScan(const char* target) {
return 0; return 0;
} }
int themeMenuScan(const char* target) {
menuClear();
if (chdir(target) < 0) return 1;
if (getcwd(s_menu[!s_curMenu].dirname, PATH_MAX+1) == NULL)
return 1;
DIR* dir;
struct dirent* dp;
char tmp_path[PATH_MAX+1];
dir = opendir(s_menu[!s_curMenu].dirname);
if (!dir) return 2;
while ((dp = readdir(dir)))
{
menuEntry_s* me = NULL;
bool shortcut = false;
if (dp->d_name[0]=='.')
continue;
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name);
bool entrytype=0;
#ifdef _DIRENT_HAVE_D_TYPE
if (dp->d_type == DT_UNKNOWN)
continue;
entrytype = dp->d_type != DT_REG;
#else
struct stat tmpstat;
if(stat(tmp_path, &tmpstat)==-1)
continue;
entrytype = (tmpstat.st_mode & S_IFMT) != S_IFREG;
#endif
const char* ext = getExtension(dp->d_name);
if (entrytype || strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0 || strcasecmp(ext, ".zip")==0)
me = menuCreateEntry(ENTRY_TYPE_THEME);
if (!me)
continue;
strncpy(me->path, tmp_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0;
if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me);
else
menuDeleteEntry(me, 0);
}
closedir(dir);
menuSort();
menuEntry_s* me = menuCreateEntry(ENTRY_TYPE_THEME);
if(me) {
if(menuEntryLoad(me, textGetString(StrId_DefaultThemeName), false, false))//Create Default theme Menu Entry
menuAddEntryToFront(me);
else
menuDeleteEntry(me, 0);
}
// Swap the menu and clear the previous menu
s_curMenu = !s_curMenu;
menuClear();
return 0;
}
int menuFileassocScan(const char* target) {
menuFileassocClear();
if (chdir(target) < 0) return 1;
if (getcwd(s_menuFileassoc[!s_curMenuFileassoc].dirname, PATH_MAX+1) == NULL)
return 1;
DIR* dir;
struct dirent* dp;
char tmp_path[PATH_MAX+1];
dir = opendir(s_menuFileassoc[!s_curMenuFileassoc].dirname);
if (!dir) return 2;
while ((dp = readdir(dir)))
{
if (dp->d_name[0]=='.')
continue;
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menuFileassoc[!s_curMenuFileassoc].dirname, dp->d_name);
const char* ext = getExtension(dp->d_name);
if (strcasecmp(ext, ".cfg")!=0)
continue;
menuEntryFileassocLoad(tmp_path);
}
closedir(dir);
// Swap the menu and clear the previous menu
s_curMenuFileassoc = !s_curMenuFileassoc;
menuFileassocClear();
return 0;
}

View File

@ -1,24 +1,189 @@
#include <time.h> #include <time.h>
#include "common.h" #include "common.h"
#include "netloader.h"
#include "invalid_icon_bin.h" #ifndef __SWITCH__
#include "folder_icon_bin.h" #include "switch/runtime/nxlink.h"
#endif
void launchMenuEntryTask(menuEntry_s* arg) double menuTimer;
{
char rootPathBase[PATH_MAX];
char rootPath[PATH_MAX+8];
uint8_t *folder_icon_large, *folder_icon_small;
uint8_t *invalid_icon_large, *invalid_icon_small;
uint8_t *theme_icon_large, *theme_icon_small;
void computeFrontGradient(color_t baseColor, int height);
void menuLoadFileassoc(void);
char *menuGetRootPath(void) {
return rootPath;
}
char *menuGetRootBasePath(void) {
return rootPathBase;
}
void launchMenuEntryTask(menuEntry_s* arg) {
menuEntry_s* me = arg; menuEntry_s* me = arg;
if (me->type == ENTRY_TYPE_FOLDER) if (me->type == ENTRY_TYPE_FOLDER)
menuScan(me->path); menuScan(me->path);
//changeDirTask(me->path); //changeDirTask(me->path);
else if(me->type == ENTRY_TYPE_THEME)
launchApplyThemeTask(me);
else else
launchMenuEntry(me); launchMenuEntry(me);
} }
typedef enum void toggleStarState(menuEntry_s* arg) {
menuEntry_s* me = arg;
if (me->starred) {
if (fileExists(me->starpath))
remove(me->starpath);
} else {
if (!fileExists(me->starpath)) {
FILE* f = fopen(me->starpath, "w");
if (f) fclose(f);
}
}
me->starred = fileExists(me->starpath);
//todo: error handling/message?
menuReorder();
menu_s* menu = menuGetCurrent();
menuEntry_s* meSearch = menu->firstEntry;
menu->curEntry = -1;
int i = 0;
while (menu->curEntry < 0) {
if (me == meSearch)
menu->curEntry = i;
else {
meSearch = meSearch->next;
i++;
}
}
}
static enum
{ {
IMAGE_MODE_RGB24, HBMENU_DEFAULT,
IMAGE_MODE_RGBA32 HBMENU_NETLOADER_ACTIVE,
} ImageMode; HBMENU_NETLOADER_ERROR,
HBMENU_NETLOADER_SUCCESS,
HBMENU_THEME_MENU,
} hbmenu_state = HBMENU_DEFAULT;
void launchMenuNetloaderTask() {
if(hbmenu_state == HBMENU_DEFAULT)
workerSchedule(netloaderTask, NULL);
}
void launchMenuBackTask() {
if(hbmenu_state == HBMENU_NETLOADER_ACTIVE) {
netloaderSignalExit();
}
else if(hbmenu_state == HBMENU_THEME_MENU) {
hbmenu_state = HBMENU_DEFAULT;
menuScan(rootPath);
}
else {
menuScan("..");
}
}
void menuHandleAButton(void) {
menu_s* menu = menuGetCurrent();
if (hbmenu_state != HBMENU_NETLOADER_ACTIVE && menuIsMsgBoxOpen()) {
menuCloseMsgBox();
}
else if (menu->nEntries > 0 && (hbmenu_state == HBMENU_DEFAULT || hbmenu_state == HBMENU_THEME_MENU))
{
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
launchMenuEntryTask(me);
//workerSchedule(launchMenuEntryTask, me);
}
}
void menuHandleXButton(void) {
menu_s* menu = menuGetCurrent();
if (menu->nEntries > 0 && hbmenu_state == HBMENU_DEFAULT) {
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
toggleStarState(me);
}
}
void menuStartupCommon(void) {
free(folder_icon_large);
free(folder_icon_small);
free(invalid_icon_large);
free(invalid_icon_small);
free(theme_icon_large);
free(theme_icon_small);
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
assetsDataEntry *data = NULL;
assetsGetData(AssetId_folder_icon, &data);
folder_icon_large = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], data->imageMode);
folder_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], data->imageMode);
assetsGetData(AssetId_invalid_icon, &data);
invalid_icon_large = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], data->imageMode);
invalid_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], data->imageMode);
if(themeGlobalPreset == THEME_PRESET_DARK)
assetsGetData(AssetId_theme_icon_dark, &data);
else
assetsGetData(AssetId_theme_icon_light, &data);
theme_icon_large = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], data->imageMode);
theme_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], data->imageMode);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
computeFrontGradient(themeCurrent.frontWaveColor, layoutobj->size[1]);
}
void menuThemeSelectCurrentEntry(void) {
menu_s* menu = menuGetCurrent();
char themePath[PATH_MAX] = {0};
GetThemePathFromConfig(themePath, PATH_MAX);
if (themePath[0]==0) menu->curEntry = 0;
else {
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; me != NULL; i ++, me = me->next) {
if (strcmp(me->path, themePath)==0) {
menu->curEntry = i;
break;
}
}
}
}
void launchApplyThemeTask(menuEntry_s* arg) {
const char* themePath = arg->path;
menu_s* menu = menuGetCurrent();
SetThemePathToConfig(themePath);
themeStartup(themeGlobalPreset);
menuStartupCommon();
menuLoadFileassoc();
if (hbmenu_state == HBMENU_THEME_MENU) { // Normally this should never be used outside of theme-menu.
themeMenuScan(menu->dirname);
menuThemeSelectCurrentEntry();
} else menuScan(menu->dirname);
}
bool menuIsNetloaderActive(void) {
return hbmenu_state == HBMENU_NETLOADER_ACTIVE;
}
//Draws an RGB888 or RGBA8888 image. //Draws an RGB888 or RGBA8888 image.
static void drawImage(int x, int y, int width, int height, const uint8_t *image, ImageMode mode) { static void drawImage(int x, int y, int width, int height, const uint8_t *image, ImageMode mode) {
@ -39,32 +204,45 @@ static void drawImage(int x, int y, int width, int height, const uint8_t *image,
current_color = MakeColor(image[pos+0], image[pos+1], image[pos+2], image[pos+3]); current_color = MakeColor(image[pos+0], image[pos+1], image[pos+2], image[pos+3]);
break; break;
} }
DrawPixel(x+tmpx, y+tmpy, current_color); DrawPixel(x+tmpx, y+tmpy, current_color);
} }
} }
} }
uint8_t *folder_icon_small; //Draws an RGBA8888 image masked by the passed color.
uint8_t *invalid_icon_small; static void drawIcon(int x, int y, int width, int height, const uint8_t *image, color_t color) {
double timer; int tmpx, tmpy;
int pos;
color_t current_color;
for (tmpy=0; tmpy<height; tmpy++) {
for (tmpx=0; tmpx<width; tmpx++) {
pos = ((tmpy*width) + tmpx) * 4;
current_color = MakeColor(color.r, color.g, color.b, image[pos+3]);
DrawPixel(x+tmpx, y+tmpy, current_color);
}
}
}
static void drawEntry(menuEntry_s* me, int off_x, int is_active) { static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
int x, y; int x, y;
int start_y = 720 - 100 - 145;//*(n % 2); int start_y = layoutobj->posStart[1];//*(n % 2);
int end_y = start_y + 140 + 32; int end_y = start_y + layoutobj->size[1];
int start_x = off_x;//(n / 2); int start_x = off_x;//(n / 2);
int end_x = start_x + 140; int end_x = start_x + layoutobj->size[0];
int j; int j;
const uint8_t *smallimg = NULL; const uint8_t *smallimg = NULL;
const uint8_t *largeimg = NULL; const uint8_t *largeimg = NULL;
char *strptr = NULL;
char tmpstr[1024]; char tmpstr[1024];
int border_start_x, border_end_x; int border_start_x, border_end_x;
int border_start_y, border_end_y; int border_start_y, border_end_y;
color_t border_color = MakeColor(255, 255, 255, 255); color_t border_color = themeCurrent.borderColor;
int shadow_start_y, shadow_y; int shadow_start_y, shadow_y;
int shadow_inset; int shadow_inset;
color_t shadow_color; color_t shadow_color;
@ -73,8 +251,8 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
int shadow_size = 4; int shadow_size = 4;
if (is_active) { if (is_active) {
highlight_multiplier = fmax(0.0, fabs(fmod(timer, 1.0) - 0.5) / 0.5); highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5);
border_color = MakeColor(themeCurrent.highlightColor.r + (255 - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (255 - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (255 - themeCurrent.highlightColor.b) * highlight_multiplier, 255); border_color = MakeColor(themeCurrent.highlightColor.r + (themeCurrent.highlightGradientEdgeColor.r - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (themeCurrent.highlightGradientEdgeColor.g - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (themeCurrent.highlightGradientEdgeColor.b - themeCurrent.highlightColor.b) * highlight_multiplier, 255);
border_start_x = start_x-6; border_start_x = start_x-6;
border_end_x = end_x+6; border_end_x = end_x+6;
border_start_y = start_y-5; border_start_y = start_y-5;
@ -96,7 +274,7 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
Draw4PixelsRaw(x, end_y +2, border_color); Draw4PixelsRaw(x, end_y +2, border_color);
Draw4PixelsRaw(x, start_y-3, border_color); Draw4PixelsRaw(x, start_y-3, border_color);
Draw4PixelsRaw(x, end_y +3, border_color); Draw4PixelsRaw(x, end_y +3, border_color);
if (is_active) { if (is_active) {
Draw4PixelsRaw(x, start_y-3, border_color); Draw4PixelsRaw(x, start_y-3, border_color);
Draw4PixelsRaw(x, end_y +3, border_color); Draw4PixelsRaw(x, end_y +3, border_color);
@ -142,7 +320,7 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
for (y=start_y; y<end_y; y++) { for (y=start_y; y<end_y; y++) {
for (x=start_x; x<end_x; x+=4) { for (x=start_x; x<end_x; x+=4) {
Draw4PixelsRaw(x, y, MakeColor(255, 255, 255, 255)); Draw4PixelsRaw(x, y, themeCurrent.borderColor);
} }
} }
@ -152,23 +330,29 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
} }
else if (me->type == ENTRY_TYPE_FOLDER) { else if (me->type == ENTRY_TYPE_FOLDER) {
smallimg = folder_icon_small; smallimg = folder_icon_small;
largeimg = folder_icon_bin; largeimg = folder_icon_large;
}
else if (me->type == ENTRY_TYPE_THEME){
smallimg = theme_icon_small;
largeimg = theme_icon_large;
} }
else { else {
smallimg = invalid_icon_small; smallimg = invalid_icon_small;
largeimg = invalid_icon_bin; largeimg = invalid_icon_large;
} }
if (smallimg) { if (smallimg) {
drawImage(start_x, start_y + 32, 140, 140, smallimg, IMAGE_MODE_RGB24); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
drawImage(start_x + layoutobj->posStart[0], start_y + layoutobj->posStart[1], layoutobj->size[0], layoutobj->size[1], smallimg, IMAGE_MODE_RGB24);
} }
if (is_active && largeimg) { layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
drawImage(117, 100, 256, 256, largeimg, IMAGE_MODE_RGB24); if (is_active && largeimg && layoutobj->visible) {
drawImage(layoutobj->posStart[0], layoutobj->posStart[1], layoutobj->size[0], layoutobj->size[1], largeimg, IMAGE_MODE_RGB24);
shadow_start_y = 100+256; shadow_start_y = layoutobj->posStart[1]+layoutobj->size[1];
border_start_x = 117; border_start_x = layoutobj->posStart[0];
border_end_x = 117+256; border_end_x = layoutobj->posStart[0]+layoutobj->size[0];
for (shadow_y=shadow_start_y; shadow_y <shadow_start_y+shadow_size; shadow_y++) { for (shadow_y=shadow_start_y; shadow_y <shadow_start_y+shadow_size; shadow_y++) {
for (x=border_start_x; x<border_end_x; x++) { for (x=border_start_x; x<border_end_x; x++) {
@ -182,21 +366,31 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
} }
} }
DrawTextTruncate(interuiregular14, start_x + 4, start_y + 4, MakeColor(64, 64, 64, 255), me->name, 140 - 32, "..."); if (me->type != ENTRY_TYPE_THEME)
strptr = me->starred ? themeCurrent.labelStarOnText : "";
else
strptr = "";
memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s%s", strptr, me->name);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListName];
DrawTextTruncate(layoutobj->font, start_x + layoutobj->posStart[0], start_y + layoutobj->posStart[1], themeCurrent.borderTextColor, tmpstr, layoutobj->size[0], "...");
if (is_active) { if (is_active) {
start_x = 1280 - 790; layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryName];
start_y = 135; if (layoutobj->visible) DrawTextTruncate(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr, layoutobj->size[0], "...");
DrawTextTruncate(interuimedium30, start_x, start_y, themeCurrent.textColor, me->name, 1280 - start_x - 120 ,"...");
if (me->type != ENTRY_TYPE_FOLDER) { if (me->type != ENTRY_TYPE_FOLDER) {
memset(tmpstr, 0, sizeof(tmpstr)); memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Author), me->author); snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Author), me->author);
DrawText(interuiregular14, start_x, start_y + 28 + 30, themeCurrent.textColor, tmpstr); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryAuthor];
if (layoutobj->visible) DrawText(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
memset(tmpstr, 0, sizeof(tmpstr)); memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Version), me->version); snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Version), me->version);
DrawText(interuiregular14, start_x, start_y + 28 + 30 + 18 + 6, themeCurrent.textColor, tmpstr); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion];
if (layoutobj->visible) DrawText(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
} }
} }
} }
@ -209,6 +403,8 @@ void computeFrontGradient(color_t baseColor, int height) {
float dark_mult, dark_sub = 75; float dark_mult, dark_sub = 75;
color_t color; color_t color;
if (height < 0 || height > 720) return;
for (y=0; y<720; y++) { for (y=0; y<720; y++) {
alpha = y - (720 - height); alpha = y - (720 - height);
@ -223,38 +419,84 @@ void computeFrontGradient(color_t baseColor, int height) {
} }
} }
void menuStartup() { void menuStartupPath(void) {
const char *path; char tmp_path[PATH_MAX+28];
#ifdef SWITCH #ifdef __SWITCH__
path = "sdmc:/switch"; strncpy(rootPathBase, "sdmc:", sizeof(rootPathBase)-1);
#else #else
path = "switch"; getcwd(rootPathBase, sizeof(rootPathBase));
#endif #endif
snprintf(rootPath, sizeof(rootPath)-1, "%s%s%s", rootPathBase, DIRECTORY_SEPARATOR, "switch");
menuScan(path); struct stat st = {0};
folder_icon_small = downscaleIcon(folder_icon_bin); if (stat(rootPath, &st) == -1) {
invalid_icon_small = downscaleIcon(invalid_icon_bin); mkdir(rootPath, 0755);
computeFrontGradient(themeCurrent.frontWaveColor, 280); }
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/themes", rootPathBase);
if (stat(tmp_path, &st) == -1) {
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config", rootPathBase);
mkdir(tmp_path, 0755);
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu", rootPathBase);
mkdir(tmp_path, 0755);
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/themes", rootPathBase);
mkdir(tmp_path, 0755);
}
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/fileassoc", rootPathBase);
if (stat(tmp_path, &st) == -1) {
mkdir(tmp_path, 0755);
}
}
void menuLoadFileassoc(void) {
char tmp_path[PATH_MAX+28];
memset(tmp_path, 0, sizeof(tmp_path)-1);
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/fileassoc", rootPathBase);
menuFileassocScan(tmp_path);
}
void menuStartup(void) {
menuLoadFileassoc();
menuScan(rootPath);
menuStartupCommon();
}
void themeMenuStartup(void) {
if(hbmenu_state != HBMENU_DEFAULT) return;
hbmenu_state = HBMENU_THEME_MENU;
char tmp_path[PATH_MAX+25];
snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s%s%s%s%s", rootPathBase, DIRECTORY_SEPARATOR, "config", DIRECTORY_SEPARATOR, "nx-hbmenu" , DIRECTORY_SEPARATOR, "themes");
themeMenuScan(tmp_path);
menuThemeSelectCurrentEntry();
} }
color_t waveBlendAdd(color_t a, color_t b, float alpha) { color_t waveBlendAdd(color_t a, color_t b, float alpha) {
return MakeColor(a.r+(b.r*alpha), a.g+b.g*alpha, a.b + b.b*alpha, 255); return MakeColor(a.r*(1.0f-alpha) + b.r*alpha, a.g*(1.0f-alpha) + b.g*alpha, a.b*(1.0f-alpha) + b.b*alpha, 255);
} }
void drawWave(int id, float timer, color_t color, int height, float phase, float speed) { void drawWave(int id, float timer, color_t color, int height, float phase, float speed) {
int x, y; int x, y;
float wave_top_y, alpha, one_minus_alpha; float wave_top_y, alpha, one_minus_alpha;
color_t existing_color, new_color; color_t existing_color, new_color;
if (height < 0 || height > 720) return;
height = 720 - height; height = 720 - height;
for (x=0; x<1280; x++) { for (x=0; x<1280; x++) {
wave_top_y = approxSin(x*speed/1280.0+timer+phase) * 10.0 + height; wave_top_y = approxSin(x*speed/1280.0+timer+phase) * 10.0 + height;
for (y=wave_top_y; y<720; y++) { for (y=wave_top_y; y<720; y++) {
if (id != 2 && y > wave_top_y + 30) if (id != 2 && y > wave_top_y + 30)
break; break;
alpha = y-wave_top_y; alpha = y-wave_top_y;
@ -262,11 +504,11 @@ void drawWave(int id, float timer, color_t color, int height, float phase, float
if (themeCurrent.enableWaveBlending) { if (themeCurrent.enableWaveBlending) {
existing_color = FetchPixelColor(x, y); existing_color = FetchPixelColor(x, y);
new_color = waveBlendAdd(existing_color, color, clamp(alpha, 0.0, 1.0) * 0.3); new_color = waveBlendAdd(existing_color, color, clamp(alpha, 0.0, 1.0) * 0.3);
} }
else if (alpha >= 0.3) { else if (alpha >= 0.3) {
if (id == 2) { if (id == 2) {
new_color = frontWaveGradient[y]; new_color = frontWaveGradient[y];
} }
else { // no anti-aliasing else { // no anti-aliasing
new_color = color; new_color = color;
} }
@ -283,107 +525,321 @@ void drawWave(int id, float timer, color_t color, int height, float phase, float
} }
} }
void drawTime() { void drawCharge() {
char chargeString[5];
uint32_t batteryCharge;
bool isCharging;
bool validPower;
validPower = powerGetDetails(&batteryCharge, &isCharging);
if (validPower)
{
batteryCharge = (batteryCharge > 100) ? 100 : batteryCharge;
sprintf(chargeString, "%d%%", batteryCharge);
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BatteryCharge];
if (layoutobj->visible) {
int tmpX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], chargeString, 'r');
DrawText(layoutobj->font, tmpX, layoutobj->posStart[1], themeCurrent.textColor, chargeString);
}
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BatteryIcon];
assetsDataEntry *data = NULL;
assetsGetData(AssetId_battery_icon, &data);
if (layoutobj->visible) drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ChargingIcon];
assetsGetData(AssetId_charging_icon, &data);
if (isCharging && layoutobj->visible)
drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor);
}
}
void drawNetwork(int tmpX, AssetId id) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_NetworkIcon];
assetsDataEntry *data = NULL;
assetsGetData(id, &data);
if (layoutobj->visible) drawIcon(layoutobj->posType ? tmpX + layoutobj->posStart[0] : layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor);
}
u32 drawStatus() {
bool netstatusFlag=0;
bool temperatureFlag=0;
s32 temperature=0;
AssetId id;
char tmpstr[32];
char timeString[9];
time_t unixTime = time(NULL); time_t unixTime = time(NULL);
struct tm* timeStruct = gmtime((const time_t *)&unixTime); struct tm* timeStruct = localtime((const time_t *)&unixTime);
int hours = timeStruct->tm_hour; int hours = timeStruct->tm_hour;
int minutes = timeStruct->tm_min; int minutes = timeStruct->tm_min;
int seconds = timeStruct->tm_sec; int seconds = timeStruct->tm_sec;
sprintf(timeString, "%02d:%02d:%02d", hours, minutes, seconds);
DrawText(interuimedium20, 1280 - (9 * 16) - 30, 30, MakeColor(255, 255, 255, 255), timeString); snprintf(tmpstr, sizeof(tmpstr)-1, "%02d:%02d:%02d", hours, minutes, seconds);
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_Status];
u32 tmpX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], tmpstr, 'r');
if (layoutobj->visible) DrawText(layoutobj->font, tmpX, layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
drawCharge();
if (statusGet(&netstatusFlag, &id, &temperatureFlag, &temperature)) {
if (netstatusFlag) drawNetwork(tmpX, id);
if (temperatureFlag) {
snprintf(tmpstr, sizeof(tmpstr)-1, "%d°C", temperature);
DrawTextFromLayout(ThemeLayoutId_Temperature, themeCurrent.textColor, tmpstr);
}
}
return tmpX;
} }
void drawBackBtn(menu_s* menu, bool emptyDir) { void drawButtons(menu_s* menu, bool emptyDir, int *out_basePos) {
int x_image = 1280 - 252 - 30 - 32; ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA];
int x_text = 1280 - 216 - 30 - 32; int basePos[2]={0};
if(emptyDir) { basePos[0] = layoutobj->posStart[0];
x_image = 1280 - 126 - 30 - 32; basePos[1] = layoutobj->posStart[1];
x_text = 1280 - 90 - 30 - 32;
} #ifdef __SWITCH__
#ifdef SWITCH
if (strcmp( menu->dirname, "sdmc:/") != 0) if (strcmp( menu->dirname, "sdmc:/") != 0)
#else #else
if (strcmp( menu->dirname, "/") != 0) if (strcmp( menu->dirname, "/") != 0)
#endif #endif
{ {
drawImage(x_image, 720 - 48, 32, 32, themeCurrent.buttonBImage, IMAGE_MODE_RGBA32); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonBText];
DrawText(interuiregular18, x_text, 720 - 47, themeCurrent.textColor, textGetString(StrId_Actions_Back)); DrawTextFromLayoutRelative(ThemeLayoutId_ButtonBText, basePos[0], basePos[1], !emptyDir ? layoutobj->posStart : layoutobj->posEnd, basePos, themeCurrent.textColor, textGetString(StrId_Actions_Back), 'l');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonB];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonB, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonBText, 'l');
} }
if(hbmenu_state == HBMENU_DEFAULT)
{
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonYText];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonYText, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, textGetString(StrId_NetLoader), 'r');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonY];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonY, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonYText, 'l');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonMText];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonMText, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, textGetString(StrId_ThemeMenu), 'r');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonM];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonM, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonMText, 'l');
}
out_basePos[0] = basePos[0];
out_basePos[1] = basePos[1];
} }
void menuLoop() { void menuUpdateNetloader(netloaderState *netloader_state) {
menuEntry_s* me; bool enable_progress = 0;
menu_s* menu = menuGetCurrent(); float progress = 0;
int i; char netloader_displaytext[260];
int x, y; char textbody[256];
for (y=0; y<450; y++) { memset(netloader_displaytext, 0, sizeof(netloader_displaytext));
memset(textbody, 0, sizeof(textbody));
u32 ip = gethostid();
if (ip == INADDR_LOOPBACK)
snprintf(textbody, sizeof(textbody)-1, "%s", textGetString(StrId_NetLoaderOffline));
else {
if (!netloader_state->sock_connected)
snprintf(textbody, sizeof(textbody)-1, textGetString(StrId_NetLoaderActive), ip&0xFF, (ip>>8)&0xFF, (ip>>16)&0xFF, (ip>>24)&0xFF, NXLINK_SERVER_PORT);
else {
enable_progress = 1;
progress = (float)netloader_state->filetotal / netloader_state->filelen;
snprintf(textbody, sizeof(textbody)-1, textGetString(StrId_NetLoaderTransferring), netloader_state->filetotal/1024, netloader_state->filelen/1024);
}
}
snprintf(netloader_displaytext, sizeof(netloader_displaytext)-1, "%s\n\n\n%s", textGetString(StrId_NetLoader), textbody);
menuMsgBoxSetNetloaderState(1, netloader_displaytext, enable_progress, progress);
}
void menuLoop(void) {
menuEntry_s* me;
menu_s* menu = NULL;
int i;
int x, y, endy = 720;
int curPos[2]={0};
netloaderState netloader_state;
ThemeLayoutObject *layoutobj = NULL;
for (i=0; i<3; i++) {
if (i==2) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackWave];
if (i==1) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave];
if (i==0) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
if (layoutobj->visible && layoutobj->size[1] >= 0 && layoutobj->size[1] <= 720-10) {
endy = 720 - layoutobj->size[1] + 10;
break;
}
}
for (y=0; y<endy; y++) {
for (x=0; x<1280; x+=4) {// don't draw bottom pixels as they are covered by the waves for (x=0; x<1280; x+=4) {// don't draw bottom pixels as they are covered by the waves
Draw4PixelsRaw(x, y, themeCurrent.backgroundColor); Draw4PixelsRaw(x, y, themeCurrent.backgroundColor);
} }
} }
drawWave(0, timer, themeCurrent.backWaveColor, 295, 0.0, 3.0); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage];
drawWave(1, timer, themeCurrent.middleWaveColor, 290, 2.0, 3.5); assetsDataEntry *data = NULL;
drawWave(2, timer, themeCurrent.frontWaveColor, 280, 4.0, -2.5); assetsGetData(AssetId_background_image, &data);
timer += 0.05; if (layoutobj->visible && data) drawImage(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], endy < data->imageSize[1] ? endy : data->imageSize[1], data->buffer, data->imageMode);
drawImage(40, 20, 140, 60, themeCurrent.hbmenuLogoImage, IMAGE_MODE_RGBA32); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackWave];
DrawText(interuiregular14, 180, 46, themeCurrent.textColor, VERSION); if (layoutobj->visible) drawWave(0, menuTimer, themeCurrent.backWaveColor, layoutobj->size[1], 0.0, 3.0);
DrawTextTruncate(interuiregular18, 40, 720 - 47, themeCurrent.textColor, menu->dirname, 918, "..."); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave];
if (layoutobj->visible) drawWave(1, menuTimer, themeCurrent.middleWaveColor, layoutobj->size[1], 2.0, 3.5);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
if (layoutobj->visible) drawWave(2, menuTimer, themeCurrent.frontWaveColor, layoutobj->size[1], 4.0, -2.5);
menuTimer += 0.05;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_Logo];
if(themeGlobalPreset == THEME_PRESET_DARK)
assetsGetData(AssetId_hbmenu_logo_dark, &data);
else assetsGetData(AssetId_hbmenu_logo_light, &data);
if (layoutobj->visible) {
if (!themeCurrent.logoColor_set)
drawImage(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, data->imageMode);
else {
drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.logoColor);
}
}
DrawTextFromLayout(ThemeLayoutId_HbmenuVersion, themeCurrent.textColor, VERSION);
u32 statusXPos = drawStatus();
#ifdef __SWITCH__
AppletType at = appletGetAppletType();
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_AttentionText];
if (at != AppletType_Application && at != AppletType_SystemApplication && layoutobj->visible) {
const char* appletMode = textGetString(StrId_AppletMode);
u32 x_pos = GetTextXCoordinate(layoutobj->font, statusXPos, appletMode, 'r');
DrawText(layoutobj->font, layoutobj->posType ? x_pos + layoutobj->posStart[0] : layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.attentionTextColor, appletMode);
}
const char* loaderInfo = envGetLoaderInfo();
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_LoaderInfo];
if (loaderInfo && layoutobj->visible) {
u32 x_pos = layoutobj->posStart[0];
char* spacePos = strchr(loaderInfo, ' ');
if (spacePos) {
char tempbuf[64] = {0};
size_t tempsize = spacePos - loaderInfo + 1;
if (tempsize > sizeof(tempbuf)-1) tempsize = sizeof(tempbuf)-1;
memcpy(tempbuf, loaderInfo, tempsize);
x_pos = GetTextXCoordinate(layoutobj->font, layoutobj->posEnd[0], tempbuf, 'r');
}
DrawText(layoutobj->font, x_pos, layoutobj->posStart[1], themeCurrent.textColor, loaderInfo);
}
#endif
#ifdef PERF_LOG_DRAW//Seperate from the PERF_LOG define since this might affect perf. #ifdef PERF_LOG_DRAW//Seperate from the PERF_LOG define since this might affect perf.
extern u64 g_tickdiff_vsync;
extern u64 g_tickdiff_frame; extern u64 g_tickdiff_frame;
char tmpstr[64]; char tmpstr[64];
snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_vsync);
DrawText(interuiregular14, 180 + 256, 46, themeCurrent.textColor, tmpstr);
snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_frame); snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_frame);
DrawText(interuiregular14, 180 + 256, 46 + 16, themeCurrent.textColor, tmpstr); DrawTextFromLayout(ThemeLayoutId_LogInfo, themeCurrent.textColor, tmpstr);
#endif #endif
//drawTime(); memset(&netloader_state, 0, sizeof(netloader_state));
netloaderGetState(&netloader_state);
if (menu->nEntries==0) if(hbmenu_state == HBMENU_DEFAULT && netloader_state.activated) {
hbmenu_state = HBMENU_NETLOADER_ACTIVE;
menuCloseMsgBox();
menuCreateMsgBox(780,300, "");
} else if(hbmenu_state == HBMENU_NETLOADER_ACTIVE && !netloader_state.activated && !netloader_state.launch_app) {
hbmenu_state = HBMENU_DEFAULT;
menuScan(".");//Reload the menu since netloader may have deleted the NRO if the transfer aborted.
menuCloseMsgBox();
menuMsgBoxSetNetloaderState(0, NULL, 0, 0);
}
if (netloader_state.errormsg[0]) {
menuCloseMsgBox();
menuCreateMsgBox(780,300, netloader_state.errormsg);
}
if(hbmenu_state == HBMENU_NETLOADER_ACTIVE) {
menuUpdateNetloader(&netloader_state);
}
menu = menuGetCurrent();
if (menu->nEntries==0 || hbmenu_state == HBMENU_NETLOADER_ACTIVE)
{ {
DrawText(interuiregular14, 64, 128, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg)); if (hbmenu_state == HBMENU_NETLOADER_ACTIVE) {
drawBackBtn(menu, true); if (netloader_state.launch_app) {
hbmenu_state = HBMENU_DEFAULT;
menuCloseMsgBox();
menuMsgBoxSetNetloaderState(0, NULL, 0, 0);
menuCreateMsgBox(240,240, textGetString(StrId_Loading));
launchMenuEntryTask(netloader_state.me);
}
} else {
DrawTextFromLayout(ThemeLayoutId_InfoMsg, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg));
}
drawButtons(menu, true, curPos);
} }
else else
{ {
static int x = 0;
static int v = 0; static int v = 0;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
if (menu->nEntries > 7) { // Gentle Realign only when not manually moving
int wanted_x = clamp(-menu->curEntry * (140 + 30), -(menu->nEntries - 7) * (140 + 30), 0); if (menu->slideSpeed == 0) {
x += v; if (menu->nEntries > entries_count) {
v += (wanted_x - x) / 3; int wanted_x = clamp(-menu->curEntry * layoutobj->posEnd[0], -(menu->nEntries - entries_count) * layoutobj->posEnd[0], 0);
v /= 2; menu->xPos += v;
v += (wanted_x - menu->xPos) / 3;
v /= 2;
}
else {
menu->xPos = v = 0;
}
} }
else { else {
x = v = 0; menu->xPos += menu->slideSpeed;
if (abs(menu->slideSpeed) > 2) {
// Slow down way faster when outside the normal bounds
if (menu->xPos > 0 || menu->xPos < -(menu->nEntries) * layoutobj->posEnd[0]) {
menu->slideSpeed *= .5f;
}
else {
menu->slideSpeed *= .9f;
}
}
else {
menu->slideSpeed = 0;
}
menu->curEntry = clamp(roundf(-((float) menu->xPos / layoutobj->posEnd[0])), 0, menu->nEntries);
} }
menuEntry_s *active_entry = NULL; menuEntry_s *active_entry = NULL;
// Draw menu entries // Draw menu entries
for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) { for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) {
int entry_start_x = 29 + i * (140 + 30); int entry_start_x = layoutobj->posStart[0] + i * layoutobj->posEnd[0];
int entry_draw_x = entry_start_x + menu->xPos;
int screen_width = 1280; int screen_width = 1280;
if (entry_start_x >= (screen_width - x)) if (entry_start_x >= (screen_width - menu->xPos))
break; break;
int is_active = i==menu->curEntry; int is_active = i==menu->curEntry;
@ -391,20 +847,61 @@ void menuLoop() {
if (is_active) if (is_active)
active_entry = me; active_entry = me;
drawEntry(me, entry_start_x + x, is_active); if (!is_active && entry_draw_x < -(layoutobj->posStart[0] + layoutobj->posEnd[0]))
continue;
drawEntry(me, entry_draw_x, is_active);
}
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuTypeMsg];
int getX=0;
if (layoutobj->visible) {
getX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], textGetString(StrId_ThemeMenu), 'r');
if(hbmenu_state == HBMENU_THEME_MENU) {
DrawText(layoutobj->font, getX, layoutobj->posStart[1], themeCurrent.textColor, textGetString(StrId_ThemeMenu));
} else {
//DrawText(interuiregular18, getX, 30 + 26 + 32 + 10, themeCurrent.textColor, textGetString(StrId_ThemeMenu));
//DrawText(fontscale7, getX - 40, 30 + 26 + 32 + 10, themeCurrent.textColor, themeCurrent.buttonMText);
}
} }
if(active_entry != NULL) { if(active_entry != NULL) {
if (active_entry->type != ENTRY_TYPE_FOLDER) { const char *buttonstr = "";
drawImage(1280 - 126 - 30 - 32, 720 - 48, 32, 32, themeCurrent.buttonAImage, IMAGE_MODE_RGBA32);
DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47, themeCurrent.textColor, textGetString(StrId_Actions_Launch)); if (active_entry->type == ENTRY_TYPE_THEME)
} buttonstr = textGetString(StrId_Actions_Apply);
else { else if (active_entry->type != ENTRY_TYPE_FOLDER)
drawImage(1280 - 126 - 30 - 32, 720 - 48, 32, 32, themeCurrent.buttonAImage, IMAGE_MODE_RGBA32); buttonstr = textGetString(StrId_Actions_Launch);
DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47, themeCurrent.textColor, textGetString(StrId_Actions_Open)); else
} buttonstr = textGetString(StrId_Actions_Open);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonAText];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonAText, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, buttonstr, 'l');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonA, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, themeCurrent.buttonAText, 'l');
}
drawButtons(menu, false, curPos);
if (active_entry && active_entry->type != ENTRY_TYPE_THEME) {
const char *buttonstr = "";
if (active_entry->starred)
buttonstr = textGetString(StrId_Actions_Unstar);
else
buttonstr = textGetString(StrId_Actions_Star);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonXText];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonXText, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, buttonstr, 'r');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonX];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonX, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, themeCurrent.buttonXText, 'l');
} }
drawBackBtn(menu, false);
} }
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuPath];
if (layoutobj->visible) DrawTextTruncate(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, menu->dirname, layoutobj->size[0], "...");
menuDrawMsgBox();
} }

View File

@ -1,5 +1,15 @@
#pragma once #pragma once
#ifndef _WIN32
#include <arpa/inet.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#undef DrawText
#undef MessageBox
#endif
#define ENTRY_NAMELENGTH 0x200 #define ENTRY_NAMELENGTH 0x200
#define ENTRY_AUTHORLENGTH 0x100 #define ENTRY_AUTHORLENGTH 0x100
#define ENTRY_VERLENGTH 0x10 #define ENTRY_VERLENGTH 0x10
@ -9,6 +19,9 @@ typedef enum
{ {
ENTRY_TYPE_FILE, ENTRY_TYPE_FILE,
ENTRY_TYPE_FOLDER, ENTRY_TYPE_FOLDER,
ENTRY_TYPE_THEME,
ENTRY_TYPE_FILEASSOC,
ENTRY_TYPE_FILE_OTHER,
} MenuEntryType; } MenuEntryType;
typedef struct menuEntry_s_tag menuEntry_s; typedef struct menuEntry_s_tag menuEntry_s;
@ -19,6 +32,8 @@ struct menu_s_tag
menuEntry_s *firstEntry, *lastEntry; menuEntry_s *firstEntry, *lastEntry;
int nEntries; int nEntries;
int curEntry; int curEntry;
int xPos;
int slideSpeed;
char dirname[PATH_MAX+1]; char dirname[PATH_MAX+1];
}; };
@ -27,6 +42,7 @@ typedef struct
{ {
char* dst; char* dst;
uint32_t buf[ENTRY_ARGBUFSIZE/sizeof(uint32_t)]; uint32_t buf[ENTRY_ARGBUFSIZE/sizeof(uint32_t)];
struct in_addr nxlink_host;
} argData_s; } argData_s;
struct menuEntry_s_tag struct menuEntry_s_tag
@ -35,9 +51,13 @@ struct menuEntry_s_tag
menuEntry_s* next; menuEntry_s* next;
MenuEntryType type; MenuEntryType type;
char path[PATH_MAX+1]; char path[PATH_MAX+8];
char starpath[PATH_MAX+8];
argData_s args; argData_s args;
bool fileassoc_type;//0=file_extension, 1 = filename
char fileassoc_str[PATH_MAX+1];//file_extension/filename
char name[ENTRY_NAMELENGTH+1]; char name[ENTRY_NAMELENGTH+1];
char author[ENTRY_AUTHORLENGTH+1]; char author[ENTRY_AUTHORLENGTH+1];
char version[ENTRY_VERLENGTH+1]; char version[ENTRY_VERLENGTH+1];
@ -46,20 +66,62 @@ struct menuEntry_s_tag
size_t icon_size; size_t icon_size;
uint8_t *icon_gfx; uint8_t *icon_gfx;
uint8_t *icon_gfx_small; uint8_t *icon_gfx_small;
bool starred;
NacpStruct *nacp; NacpStruct *nacp;
}; };
typedef enum
{
IMAGE_MODE_RGB24,
IMAGE_MODE_RGBA32
} ImageMode;
extern double menuTimer;
#ifdef __cplusplus
extern "C" {
#endif
void menuEntryInit(menuEntry_s* me, MenuEntryType type); void menuEntryInit(menuEntry_s* me, MenuEntryType type);
void menuEntryFree(menuEntry_s* me); void menuEntryFree(menuEntry_s* me, bool skip_icongfx);
bool fileExists(const char* path); bool fileExists(const char* path);
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut); bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists);
void menuEntryParseIcon(menuEntry_s* me); void menuEntryParseIcon(menuEntry_s* me);
uint8_t *downscaleIcon(const uint8_t *image); uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode);
void menuEntryParseNacp(menuEntry_s* me); void menuEntryParseNacp(menuEntry_s* me);
void menuEntryFileassocLoad(const char* filepath);
menuEntry_s* menuCreateEntry(MenuEntryType type);
void menuFileassocAddEntry(menuEntry_s* me);
void menuDeleteEntry(menuEntry_s* me, bool skip_icongfx);
menu_s* menuGetCurrent(void); menu_s* menuGetCurrent(void);
menu_s* menuFileassocGetCurrent(void);
void menuReorder (void);
int menuScan(const char* target); int menuScan(const char* target);
int themeMenuScan(const char* target);
int menuFileassocScan(const char* target);
void launchMenuEntryTask(menuEntry_s* arg);
void toggleStarState(menuEntry_s* arg);
void launchApplyThemeTask(menuEntry_s* arg);
void launchMenuBackTask();
void launchMenuNetloaderTask();
char *menuGetRootPath(void);
char *menuGetRootBasePath(void);
void menuHandleAButton(void);
void menuHandleXButton(void);
bool menuIsNetloaderActive(void);
#ifdef __cplusplus
}
#endif
static inline char* getExtension(const char* str) static inline char* getExtension(const char* str)
{ {

219
common/message-box.c Normal file
View File

@ -0,0 +1,219 @@
#include "common.h"
#include "message-box.h"
MessageBox currMsgBox;
static bool msgboxNetloaderEnabled;
static char msgboxNetloaderText[256];
static bool msgboxNetloaderProgressEnabled;
static float msgboxNetloaderProgress;
void drawMsgBoxBgToBuff(color_t *buff, int width, int height) {
int x, y;
int off;
int circle_center_x, circle_center_y;
int corner_size = 0;
float rad, alpha;
color_t base_color = themeCurrent.backgroundColor;
color_t color;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
for (y=0; y<height; y++) {
for (x=0; x<width; x++) {
if (corner_size > 0) {
if (x<corner_size && y<corner_size) { // top left corner
circle_center_x = corner_size-1;
circle_center_y = corner_size-1;
}
else if (x>width-corner_size && y<corner_size) { // top right corner
circle_center_x = width-corner_size;
circle_center_y = corner_size-1;
}
else if (x<corner_size && y>height-corner_size) { // bottom left corner
circle_center_x = corner_size-1;
circle_center_y = height-corner_size;
}
else if (x>width-corner_size && y>height-corner_size) { // bottom right corner
circle_center_x = width-corner_size;
circle_center_y = height-corner_size;
}
else {
circle_center_x = -1;
circle_center_y = -1;
}
if (circle_center_x == -1 && circle_center_y == -1) {
color = base_color;
}
else {
rad = sqrt(pow(circle_center_x - x, 2) + pow(circle_center_y - y, 2));
alpha = (float)corner_size - rad;
if (rad < corner_size) {
if (alpha < 1.0) {
color = MakeColor(base_color.r, base_color.g, base_color.b, base_color.a * alpha);
}
else
color = base_color;
}
else
color = MakeColor(0, 0, 0, 0);
}
}
else
color = base_color;
if (y == height + layoutobj->posStart[1]) {
color = themeCurrent.separatorColor;
}
off = (y * width + x);
*((uint32_t *)&buff[off]) = color.r | (color.g<<8) | (color.b<<16) | (color.a<<24);
}
}
}
void menuDrawMsgBox() {
if (!menuIsMsgBoxOpen())
return;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
int off;
int x, y;
int start_x = 1280 / 2 - currMsgBox.width / 2;
int start_y = 720 / 2 - currMsgBox.height / 2;
int end_x = start_x + currMsgBox.width;
uint32_t text_width, text_height;
color_t curr_color;
color_t border_color;
int sep_start_y = currMsgBox.height + layoutobj->posStart[1];
int border_thickness = 6;
int shadow_start_y, shadow_y;
int shadow_inset;
int shadow_size = 4;
float highlight_multiplier = highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5);
color_t shadow_color;
uint8_t shadow_alpha_base = 80;
const char* textptr = currMsgBox.text;
int progress_width = (int)(msgboxNetloaderProgress*currMsgBox.width);
char progress_text[32];
border_color = MakeColor(themeCurrent.highlightColor.r + (255 - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (255 - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (255 - themeCurrent.highlightColor.b) * highlight_multiplier, 255);
// Darken the background
for (y=0; y<720; y++) {
for (x=0; x<1280; x++) {
DrawPixel(x, y, MakeColor(0, 0, 0, 100));
}
}
// Draw the message box background
for (y=0; y<currMsgBox.height; y++) {
for (x=0; x<currMsgBox.width; x++) {
off = (y * currMsgBox.width + x);
curr_color = currMsgBox.bg[off];
if (!msgboxNetloaderEnabled) {
if (((x<border_thickness || x>=currMsgBox.width-border_thickness) && y>sep_start_y) ||
(y>sep_start_y && y<=sep_start_y+border_thickness) || (y>=currMsgBox.height-border_thickness)) {
curr_color = border_color;
}
}
else if (msgboxNetloaderProgressEnabled && y > sep_start_y && x < progress_width) {
curr_color = themeCurrent.progressBarColor;
}
DrawPixel(start_x+x, start_y+y, curr_color);
}
}
if (msgboxNetloaderEnabled) textptr = msgboxNetloaderText;
GetTextDimensions(interuiregular18, textptr, &text_width, &text_height);
x = GetTextXCoordinate(interuiregular18, start_x + (currMsgBox.width / 2), textptr, 'c');
if (text_width < currMsgBox.width && text_height < sep_start_y) {
DrawText(interuiregular18, x, start_y + (sep_start_y - text_height) / 2, themeCurrent.textColor, textptr);
}
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxBottomText];
y = start_y + currMsgBox.height + layoutobj->posStart[1];
if (!msgboxNetloaderEnabled) {
x = GetTextXCoordinate(interuimedium20, start_x + (currMsgBox.width / 2), textGetString(StrId_MsgBox_OK), 'c');
DrawText(interuimedium20, x, y, themeCurrent.textColor, textGetString(StrId_MsgBox_OK));
}
if (msgboxNetloaderEnabled && msgboxNetloaderProgressEnabled) {
memset(progress_text, 0, sizeof(progress_text));
snprintf(progress_text, sizeof(progress_text)-1, "%.02f%%", msgboxNetloaderProgress*100);
x = GetTextXCoordinate(interuiregular18, start_x + (currMsgBox.width / 2), progress_text, 'c');
DrawText(interuiregular18, x, y, themeCurrent.textColor, progress_text);
}
shadow_start_y = start_y + currMsgBox.height;
for (shadow_y=shadow_start_y; shadow_y <shadow_start_y+shadow_size; shadow_y++) {
for (x=start_x; x<end_x; x++) {
shadow_color = MakeColor(0, 0, 0, shadow_alpha_base * (1.0 - (float)(shadow_y - shadow_start_y) / ((float)shadow_size)));
shadow_inset =(shadow_y-shadow_start_y);
if (x >= start_x + shadow_inset && x <= end_x - shadow_inset) {
DrawPixel(x, shadow_y, shadow_color);
}
}
}
}
void menuCreateMsgBox(int width, int height, const char *text) {
if (menuIsMsgBoxOpen())
return;
char *new_text = strdup(text);
if (new_text==NULL)
return;
currMsgBox = (MessageBox) { width, height, NULL, new_text };
currMsgBox.bg = malloc(currMsgBox.width*currMsgBox.height*4);
if (currMsgBox.bg) {
drawMsgBoxBgToBuff(currMsgBox.bg, currMsgBox.width, currMsgBox.height);
}
}
bool menuIsMsgBoxOpen() {
return currMsgBox.width != 0 || currMsgBox.height != 0 || currMsgBox.bg || currMsgBox.text;
}
void menuCloseMsgBox() {
if (currMsgBox.bg) {
free(currMsgBox.bg);
currMsgBox.bg = NULL;
}
currMsgBox.width = currMsgBox.height = 0;
if (currMsgBox.text) {
free(currMsgBox.text);
currMsgBox.text = NULL;
}
}
MessageBox menuGetCurrentMsgBox() {
return currMsgBox;
}
void menuMsgBoxSetNetloaderState(bool enabled, const char *text, bool enable_progress, float progress) {
msgboxNetloaderEnabled = enabled;
memset(msgboxNetloaderText, 0, sizeof(msgboxNetloaderText));
if (text) strncpy(msgboxNetloaderText, text, sizeof(msgboxNetloaderText)-1);
msgboxNetloaderProgressEnabled = enable_progress;
msgboxNetloaderProgress = progress;
}

16
common/message-box.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
typedef struct
{
uint32_t width;
uint32_t height;
color_t *bg;
char *text;
} MessageBox;
void menuCreateMsgBox(int width, int height, const char *text);
void menuCloseMsgBox();
bool menuIsMsgBoxOpen();
void menuDrawMsgBox(void);
MessageBox menuGetCurrentMsgBox();
void menuMsgBoxSetNetloaderState(bool enabled, const char *text, bool enable_progress, float progress);

View File

@ -1,41 +0,0 @@
#pragma once
typedef struct {
char name[0x200];
char author[0x100];
} NacpLanguageEntry;
typedef struct {
NacpLanguageEntry lang[12];
NacpLanguageEntry lang_unk[4];//?
u8 x3000_unk[0x24];////Normally all-zero?
u32 x3024_unk;
u32 x3028_unk;
u32 x302C_unk;
u32 x3030_unk;
u32 x3034_unk;
u64 titleid0;
u8 x3040_unk[0x20];
char version[0x10];
u64 titleid_dlcbase;
u64 titleid1;
u32 x3080_unk;
u32 x3084_unk;
u32 x3088_unk;
u8 x308C_unk[0x24];//zeros?
u64 titleid2;
u64 titleids[7];//"Array of application titleIDs, normally the same as the above app-titleIDs. Only set for game-updates?"
u32 x30F0_unk;
u32 x30F4_unk;
u64 titleid3;//"Application titleID. Only set for game-updates?"
char bcat_passphrase[0x40];
u8 x3140_unk[0xEC0];//Normally all-zero?
} NacpStruct;

View File

@ -1,916 +0,0 @@
// NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder
// version 1.3.5 (2016-11-14)
// Copyright (c) 2009-2016 Martin J. Fiedler <martin.fiedler@gmx.net>
// published under the terms of the MIT license
//
// 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.
///////////////////////////////////////////////////////////////////////////////
// DOCUMENTATION SECTION //
// read this if you want to know what this is all about //
///////////////////////////////////////////////////////////////////////////////
// INTRODUCTION
// ============
//
// This is a minimal decoder for baseline JPEG images. It accepts memory dumps
// of JPEG files as input and generates either 8-bit grayscale or packed 24-bit
// RGB images as output. It does not parse JFIF or Exif headers; all JPEG files
// are assumed to be either grayscale or YCbCr. CMYK or other color spaces are
// not supported. All YCbCr subsampling schemes with power-of-two ratios are
// supported, as are restart intervals. Progressive or lossless JPEG is not
// supported.
// Summed up, NanoJPEG should be able to decode all images from digital cameras
// and most common forms of other non-progressive JPEG images.
// The decoder is not optimized for speed, it's optimized for simplicity and
// small code. Image quality should be at a reasonable level. A bicubic chroma
// upsampling filter ensures that subsampled YCbCr images are rendered in
// decent quality. The decoder is not meant to deal with broken JPEG files in
// a graceful manner; if anything is wrong with the bitstream, decoding will
// simply fail.
// The code should work with every modern C compiler without problems and
// should not emit any warnings. It uses only (at least) 32-bit integer
// arithmetic and is supposed to be endianness independent and 64-bit clean.
// However, it is not thread-safe.
// COMPILE-TIME CONFIGURATION
// ==========================
//
// The following aspects of NanoJPEG can be controlled with preprocessor
// defines:
//
// _NJ_EXAMPLE_PROGRAM = Compile a main() function with an example
// program.
// _NJ_INCLUDE_HEADER_ONLY = Don't compile anything, just act as a header
// file for NanoJPEG. Example:
// #define _NJ_INCLUDE_HEADER_ONLY
// #include "nanojpeg.c"
// int main(void) {
// njInit();
// // your code here
// njDone();
// }
// NJ_USE_LIBC=1 = Use the malloc(), free(), memset() and memcpy()
// functions from the standard C library (default).
// NJ_USE_LIBC=0 = Don't use the standard C library. In this mode,
// external functions njAlloc(), njFreeMem(),
// njFillMem() and njCopyMem() need to be defined
// and implemented somewhere.
// NJ_USE_WIN32=0 = Normal mode (default).
// NJ_USE_WIN32=1 = If compiling with MSVC for Win32 and
// NJ_USE_LIBC=0, NanoJPEG will use its own
// implementations of the required C library
// functions (default if compiling with MSVC and
// NJ_USE_LIBC=0).
// NJ_CHROMA_FILTER=1 = Use the bicubic chroma upsampling filter
// (default).
// NJ_CHROMA_FILTER=0 = Use simple pixel repetition for chroma upsampling
// (bad quality, but faster and less code).
// API
// ===
//
// For API documentation, read the "header section" below.
// EXAMPLE
// =======
//
// A few pages below, you can find an example program that uses NanoJPEG to
// convert JPEG files into PGM or PPM. To compile it, use something like
// gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c
// You may also add -std=c99 -Wall -Wextra -pedantic -Werror, if you want :)
// The only thing you might need is -Wno-shift-negative-value, because this
// code relies on the target machine using two's complement arithmetic, but
// the C standard does not, even though *any* practically useful machine
// nowadays uses two's complement.
///////////////////////////////////////////////////////////////////////////////
// HEADER SECTION //
// copy and pase this into nanojpeg.h if you want //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NANOJPEG_H
#define _NANOJPEG_H
// nj_result_t: Result codes for njDecode().
typedef enum _nj_result {
NJ_OK = 0, // no error, decoding successful
NJ_NO_JPEG, // not a JPEG file
NJ_UNSUPPORTED, // unsupported format
NJ_OUT_OF_MEM, // out of memory
NJ_INTERNAL_ERR, // internal error
NJ_SYNTAX_ERROR, // syntax error
__NJ_FINISHED, // used internally, will never be reported
} nj_result_t;
// njInit: Initialize NanoJPEG.
// For safety reasons, this should be called at least one time before using
// using any of the other NanoJPEG functions.
void njInit(void);
// njDecode: Decode a JPEG image.
// Decodes a memory dump of a JPEG file into internal buffers.
// Parameters:
// jpeg = The pointer to the memory dump.
// size = The size of the JPEG file.
// Return value: The error code in case of failure, or NJ_OK (zero) on success.
nj_result_t njDecode(const void* jpeg, const int size);
// njGetWidth: Return the width (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetWidth() is undefined.
int njGetWidth(void);
// njGetHeight: Return the height (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetHeight() is undefined.
int njGetHeight(void);
// njIsColor: Return 1 if the most recently decoded image is a color image
// (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result
// of njGetWidth() is undefined.
int njIsColor(void);
// njGetImage: Returns the decoded image data.
// Returns a pointer to the most recently image. The memory layout it byte-
// oriented, top-down, without any padding between lines. Pixels of color
// images will be stored as three consecutive bytes for the red, green and
// blue channels. This data format is thus compatible with the PGM or PPM
// file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8.
// If njDecode() failed, the result of njGetImage() is undefined.
unsigned char* njGetImage(void);
// njGetImageSize: Returns the size (in bytes) of the image data returned
// by njGetImage(). If njDecode() failed, the result of njGetImageSize() is
// undefined.
int njGetImageSize(void);
// njDone: Uninitialize NanoJPEG.
// Resets NanoJPEG's internal state and frees all memory that has been
// allocated at run-time by NanoJPEG. It is still possible to decode another
// image after a njDone() call.
void njDone(void);
#endif//_NANOJPEG_H
///////////////////////////////////////////////////////////////////////////////
// CONFIGURATION SECTION //
// adjust the default settings for the NJ_ defines here //
///////////////////////////////////////////////////////////////////////////////
#ifndef NJ_USE_LIBC
#define NJ_USE_LIBC 1
#endif
#ifndef NJ_USE_WIN32
#ifdef _MSC_VER
#define NJ_USE_WIN32 (!NJ_USE_LIBC)
#else
#define NJ_USE_WIN32 0
#endif
#endif
#ifndef NJ_CHROMA_FILTER
#define NJ_CHROMA_FILTER 1
#endif
///////////////////////////////////////////////////////////////////////////////
// EXAMPLE PROGRAM //
// just define _NJ_EXAMPLE_PROGRAM to compile this (requires NJ_USE_LIBC) //
///////////////////////////////////////////////////////////////////////////////
#ifdef _NJ_EXAMPLE_PROGRAM
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
int size;
char *buf;
FILE *f;
if (argc < 2) {
printf("Usage: %s <input.jpg> [<output.ppm>]\n", argv[0]);
return 2;
}
f = fopen(argv[1], "rb");
if (!f) {
printf("Error opening the input file.\n");
return 1;
}
fseek(f, 0, SEEK_END);
size = (int) ftell(f);
buf = (char*) malloc(size);
fseek(f, 0, SEEK_SET);
size = (int) fread(buf, 1, size, f);
fclose(f);
njInit();
if (njDecode(buf, size)) {
free((void*)buf);
printf("Error decoding the input file.\n");
return 1;
}
free((void*)buf);
f = fopen((argc > 2) ? argv[2] : (njIsColor() ? "nanojpeg_out.ppm" : "nanojpeg_out.pgm"), "wb");
if (!f) {
printf("Error opening the output file.\n");
return 1;
}
fprintf(f, "P%d\n%d %d\n255\n", njIsColor() ? 6 : 5, njGetWidth(), njGetHeight());
fwrite(njGetImage(), 1, njGetImageSize(), f);
fclose(f);
njDone();
return 0;
}
#endif
///////////////////////////////////////////////////////////////////////////////
// IMPLEMENTATION SECTION //
// you may stop reading here //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NJ_INCLUDE_HEADER_ONLY
#ifdef _MSC_VER
#define NJ_INLINE static __inline
#define NJ_FORCE_INLINE static __forceinline
#else
#define NJ_INLINE static inline
#define NJ_FORCE_INLINE static inline
#endif
#if NJ_USE_LIBC
#include <stdlib.h>
#include <string.h>
#define njAllocMem malloc
#define njFreeMem free
#define njFillMem memset
#define njCopyMem memcpy
#elif NJ_USE_WIN32
#include <windows.h>
#define njAllocMem(size) ((void*) LocalAlloc(LMEM_FIXED, (SIZE_T)(size)))
#define njFreeMem(block) ((void) LocalFree((HLOCAL) block))
NJ_INLINE void njFillMem(void* block, unsigned char value, int count) { __asm {
mov edi, block
mov al, value
mov ecx, count
rep stosb
} }
NJ_INLINE void njCopyMem(void* dest, const void* src, int count) { __asm {
mov edi, dest
mov esi, src
mov ecx, count
rep movsb
} }
#else
extern void* njAllocMem(int size);
extern void njFreeMem(void* block);
extern void njFillMem(void* block, unsigned char byte, int size);
extern void njCopyMem(void* dest, const void* src, int size);
#endif
typedef struct _nj_code {
unsigned char bits, code;
} nj_vlc_code_t;
typedef struct _nj_cmp {
int cid;
int ssx, ssy;
int width, height;
int stride;
int qtsel;
int actabsel, dctabsel;
int dcpred;
unsigned char *pixels;
} nj_component_t;
typedef struct _nj_ctx {
nj_result_t error;
const unsigned char *pos;
int size;
int length;
int width, height;
int mbwidth, mbheight;
int mbsizex, mbsizey;
int ncomp;
nj_component_t comp[3];
int qtused, qtavail;
unsigned char qtab[4][64];
nj_vlc_code_t vlctab[4][65536];
int buf, bufbits;
int block[64];
int rstinterval;
unsigned char *rgb;
} nj_context_t;
static nj_context_t nj;
static const char njZZ[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45,
38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 };
NJ_FORCE_INLINE unsigned char njClip(const int x) {
return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : (unsigned char) x);
}
#define W1 2841
#define W2 2676
#define W3 2408
#define W5 1609
#define W6 1108
#define W7 565
NJ_INLINE void njRowIDCT(int* blk) {
int x0, x1, x2, x3, x4, x5, x6, x7, x8;
if (!((x1 = blk[4] << 11)
| (x2 = blk[6])
| (x3 = blk[2])
| (x4 = blk[1])
| (x5 = blk[7])
| (x6 = blk[5])
| (x7 = blk[3])))
{
blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3;
return;
}
x0 = (blk[0] << 11) + 128;
x8 = W7 * (x4 + x5);
x4 = x8 + (W1 - W7) * x4;
x5 = x8 - (W1 + W7) * x5;
x8 = W3 * (x6 + x7);
x6 = x8 - (W3 - W5) * x6;
x7 = x8 - (W3 + W5) * x7;
x8 = x0 + x1;
x0 -= x1;
x1 = W6 * (x3 + x2);
x2 = x1 - (W2 + W6) * x2;
x3 = x1 + (W2 - W6) * x3;
x1 = x4 + x6;
x4 -= x6;
x6 = x5 + x7;
x5 -= x7;
x7 = x8 + x3;
x8 -= x3;
x3 = x0 + x2;
x0 -= x2;
x2 = (181 * (x4 + x5) + 128) >> 8;
x4 = (181 * (x4 - x5) + 128) >> 8;
blk[0] = (x7 + x1) >> 8;
blk[1] = (x3 + x2) >> 8;
blk[2] = (x0 + x4) >> 8;
blk[3] = (x8 + x6) >> 8;
blk[4] = (x8 - x6) >> 8;
blk[5] = (x0 - x4) >> 8;
blk[6] = (x3 - x2) >> 8;
blk[7] = (x7 - x1) >> 8;
}
NJ_INLINE void njColIDCT(const int* blk, unsigned char *out, int stride) {
int x0, x1, x2, x3, x4, x5, x6, x7, x8;
if (!((x1 = blk[8*4] << 8)
| (x2 = blk[8*6])
| (x3 = blk[8*2])
| (x4 = blk[8*1])
| (x5 = blk[8*7])
| (x6 = blk[8*5])
| (x7 = blk[8*3])))
{
x1 = njClip(((blk[0] + 32) >> 6) + 128);
for (x0 = 8; x0; --x0) {
*out = (unsigned char) x1;
out += stride;
}
return;
}
x0 = (blk[0] << 8) + 8192;
x8 = W7 * (x4 + x5) + 4;
x4 = (x8 + (W1 - W7) * x4) >> 3;
x5 = (x8 - (W1 + W7) * x5) >> 3;
x8 = W3 * (x6 + x7) + 4;
x6 = (x8 - (W3 - W5) * x6) >> 3;
x7 = (x8 - (W3 + W5) * x7) >> 3;
x8 = x0 + x1;
x0 -= x1;
x1 = W6 * (x3 + x2) + 4;
x2 = (x1 - (W2 + W6) * x2) >> 3;
x3 = (x1 + (W2 - W6) * x3) >> 3;
x1 = x4 + x6;
x4 -= x6;
x6 = x5 + x7;
x5 -= x7;
x7 = x8 + x3;
x8 -= x3;
x3 = x0 + x2;
x0 -= x2;
x2 = (181 * (x4 + x5) + 128) >> 8;
x4 = (181 * (x4 - x5) + 128) >> 8;
*out = njClip(((x7 + x1) >> 14) + 128); out += stride;
*out = njClip(((x3 + x2) >> 14) + 128); out += stride;
*out = njClip(((x0 + x4) >> 14) + 128); out += stride;
*out = njClip(((x8 + x6) >> 14) + 128); out += stride;
*out = njClip(((x8 - x6) >> 14) + 128); out += stride;
*out = njClip(((x0 - x4) >> 14) + 128); out += stride;
*out = njClip(((x3 - x2) >> 14) + 128); out += stride;
*out = njClip(((x7 - x1) >> 14) + 128);
}
#define njThrow(e) do { nj.error = e; return; } while (0)
#define njCheckError() do { if (nj.error) return; } while (0)
static int njShowBits(int bits) {
unsigned char newbyte;
if (!bits) return 0;
while (nj.bufbits < bits) {
if (nj.size <= 0) {
nj.buf = (nj.buf << 8) | 0xFF;
nj.bufbits += 8;
continue;
}
newbyte = *nj.pos++;
nj.size--;
nj.bufbits += 8;
nj.buf = (nj.buf << 8) | newbyte;
if (newbyte == 0xFF) {
if (nj.size) {
unsigned char marker = *nj.pos++;
nj.size--;
switch (marker) {
case 0x00:
case 0xFF:
break;
case 0xD9: nj.size = 0; break;
default:
if ((marker & 0xF8) != 0xD0)
nj.error = NJ_SYNTAX_ERROR;
else {
nj.buf = (nj.buf << 8) | marker;
nj.bufbits += 8;
}
}
} else
nj.error = NJ_SYNTAX_ERROR;
}
}
return (nj.buf >> (nj.bufbits - bits)) & ((1 << bits) - 1);
}
NJ_INLINE void njSkipBits(int bits) {
if (nj.bufbits < bits)
(void) njShowBits(bits);
nj.bufbits -= bits;
}
NJ_INLINE int njGetBits(int bits) {
int res = njShowBits(bits);
njSkipBits(bits);
return res;
}
NJ_INLINE void njByteAlign(void) {
nj.bufbits &= 0xF8;
}
static void njSkip(int count) {
nj.pos += count;
nj.size -= count;
nj.length -= count;
if (nj.size < 0) nj.error = NJ_SYNTAX_ERROR;
}
NJ_INLINE unsigned short njDecode16(const unsigned char *pos) {
return (pos[0] << 8) | pos[1];
}
static void njDecodeLength(void) {
if (nj.size < 2) njThrow(NJ_SYNTAX_ERROR);
nj.length = njDecode16(nj.pos);
if (nj.length > nj.size) njThrow(NJ_SYNTAX_ERROR);
njSkip(2);
}
NJ_INLINE void njSkipMarker(void) {
njDecodeLength();
njSkip(nj.length);
}
NJ_INLINE void njDecodeSOF(void) {
int i, ssxmax = 0, ssymax = 0;
nj_component_t* c;
njDecodeLength();
njCheckError();
if (nj.length < 9) njThrow(NJ_SYNTAX_ERROR);
if (nj.pos[0] != 8) njThrow(NJ_UNSUPPORTED);
nj.height = njDecode16(nj.pos+1);
nj.width = njDecode16(nj.pos+3);
if (!nj.width || !nj.height) njThrow(NJ_SYNTAX_ERROR);
nj.ncomp = nj.pos[5];
njSkip(6);
switch (nj.ncomp) {
case 1:
case 3:
break;
default:
njThrow(NJ_UNSUPPORTED);
}
if (nj.length < (nj.ncomp * 3)) njThrow(NJ_SYNTAX_ERROR);
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
c->cid = nj.pos[0];
if (!(c->ssx = nj.pos[1] >> 4)) njThrow(NJ_SYNTAX_ERROR);
if (c->ssx & (c->ssx - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two
if (!(c->ssy = nj.pos[1] & 15)) njThrow(NJ_SYNTAX_ERROR);
if (c->ssy & (c->ssy - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two
if ((c->qtsel = nj.pos[2]) & 0xFC) njThrow(NJ_SYNTAX_ERROR);
njSkip(3);
nj.qtused |= 1 << c->qtsel;
if (c->ssx > ssxmax) ssxmax = c->ssx;
if (c->ssy > ssymax) ssymax = c->ssy;
}
if (nj.ncomp == 1) {
c = nj.comp;
c->ssx = c->ssy = ssxmax = ssymax = 1;
}
nj.mbsizex = ssxmax << 3;
nj.mbsizey = ssymax << 3;
nj.mbwidth = (nj.width + nj.mbsizex - 1) / nj.mbsizex;
nj.mbheight = (nj.height + nj.mbsizey - 1) / nj.mbsizey;
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
c->width = (nj.width * c->ssx + ssxmax - 1) / ssxmax;
c->height = (nj.height * c->ssy + ssymax - 1) / ssymax;
c->stride = nj.mbwidth * c->ssx << 3;
if (((c->width < 3) && (c->ssx != ssxmax)) || ((c->height < 3) && (c->ssy != ssymax))) njThrow(NJ_UNSUPPORTED);
if (!(c->pixels = (unsigned char*) njAllocMem(c->stride * nj.mbheight * c->ssy << 3))) njThrow(NJ_OUT_OF_MEM);
}
if (nj.ncomp == 3) {
nj.rgb = (unsigned char*) njAllocMem(nj.width * nj.height * nj.ncomp);
if (!nj.rgb) njThrow(NJ_OUT_OF_MEM);
}
njSkip(nj.length);
}
NJ_INLINE void njDecodeDHT(void) {
int codelen, currcnt, remain, spread, i, j;
nj_vlc_code_t *vlc;
static unsigned char counts[16];
njDecodeLength();
njCheckError();
while (nj.length >= 17) {
i = nj.pos[0];
if (i & 0xEC) njThrow(NJ_SYNTAX_ERROR);
if (i & 0x02) njThrow(NJ_UNSUPPORTED);
i = (i | (i >> 3)) & 3; // combined DC/AC + tableid value
for (codelen = 1; codelen <= 16; ++codelen)
counts[codelen - 1] = nj.pos[codelen];
njSkip(17);
vlc = &nj.vlctab[i][0];
remain = spread = 65536;
for (codelen = 1; codelen <= 16; ++codelen) {
spread >>= 1;
currcnt = counts[codelen - 1];
if (!currcnt) continue;
if (nj.length < currcnt) njThrow(NJ_SYNTAX_ERROR);
remain -= currcnt << (16 - codelen);
if (remain < 0) njThrow(NJ_SYNTAX_ERROR);
for (i = 0; i < currcnt; ++i) {
register unsigned char code = nj.pos[i];
for (j = spread; j; --j) {
vlc->bits = (unsigned char) codelen;
vlc->code = code;
++vlc;
}
}
njSkip(currcnt);
}
while (remain--) {
vlc->bits = 0;
++vlc;
}
}
if (nj.length) njThrow(NJ_SYNTAX_ERROR);
}
NJ_INLINE void njDecodeDQT(void) {
int i;
unsigned char *t;
njDecodeLength();
njCheckError();
while (nj.length >= 65) {
i = nj.pos[0];
if (i & 0xFC) njThrow(NJ_SYNTAX_ERROR);
nj.qtavail |= 1 << i;
t = &nj.qtab[i][0];
for (i = 0; i < 64; ++i)
t[i] = nj.pos[i + 1];
njSkip(65);
}
if (nj.length) njThrow(NJ_SYNTAX_ERROR);
}
NJ_INLINE void njDecodeDRI(void) {
njDecodeLength();
njCheckError();
if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR);
nj.rstinterval = njDecode16(nj.pos);
njSkip(nj.length);
}
static int njGetVLC(nj_vlc_code_t* vlc, unsigned char* code) {
int value = njShowBits(16);
int bits = vlc[value].bits;
if (!bits) { nj.error = NJ_SYNTAX_ERROR; return 0; }
njSkipBits(bits);
value = vlc[value].code;
if (code) *code = (unsigned char) value;
bits = value & 15;
if (!bits) return 0;
value = njGetBits(bits);
if (value < (1 << (bits - 1)))
value += ((-1) << bits) + 1;
return value;
}
NJ_INLINE void njDecodeBlock(nj_component_t* c, unsigned char* out) {
unsigned char code = 0;
int value, coef = 0;
njFillMem(nj.block, 0, sizeof(nj.block));
c->dcpred += njGetVLC(&nj.vlctab[c->dctabsel][0], NULL);
nj.block[0] = (c->dcpred) * nj.qtab[c->qtsel][0];
do {
value = njGetVLC(&nj.vlctab[c->actabsel][0], &code);
if (!code) break; // EOB
if (!(code & 0x0F) && (code != 0xF0)) njThrow(NJ_SYNTAX_ERROR);
coef += (code >> 4) + 1;
if (coef > 63) njThrow(NJ_SYNTAX_ERROR);
nj.block[(int) njZZ[coef]] = value * nj.qtab[c->qtsel][coef];
} while (coef < 63);
for (coef = 0; coef < 64; coef += 8)
njRowIDCT(&nj.block[coef]);
for (coef = 0; coef < 8; ++coef)
njColIDCT(&nj.block[coef], &out[coef], c->stride);
}
NJ_INLINE void njDecodeScan(void) {
int i, mbx, mby, sbx, sby;
int rstcount = nj.rstinterval, nextrst = 0;
nj_component_t* c;
njDecodeLength();
njCheckError();
if (nj.length < (4 + 2 * nj.ncomp)) njThrow(NJ_SYNTAX_ERROR);
if (nj.pos[0] != nj.ncomp) njThrow(NJ_UNSUPPORTED);
njSkip(1);
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
if (nj.pos[0] != c->cid) njThrow(NJ_SYNTAX_ERROR);
if (nj.pos[1] & 0xEE) njThrow(NJ_SYNTAX_ERROR);
c->dctabsel = nj.pos[1] >> 4;
c->actabsel = (nj.pos[1] & 1) | 2;
njSkip(2);
}
if (nj.pos[0] || (nj.pos[1] != 63) || nj.pos[2]) njThrow(NJ_UNSUPPORTED);
njSkip(nj.length);
for (mbx = mby = 0;;) {
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c)
for (sby = 0; sby < c->ssy; ++sby)
for (sbx = 0; sbx < c->ssx; ++sbx) {
njDecodeBlock(c, &c->pixels[((mby * c->ssy + sby) * c->stride + mbx * c->ssx + sbx) << 3]);
njCheckError();
}
if (++mbx >= nj.mbwidth) {
mbx = 0;
if (++mby >= nj.mbheight) break;
}
if (nj.rstinterval && !(--rstcount)) {
njByteAlign();
i = njGetBits(16);
if (((i & 0xFFF8) != 0xFFD0) || ((i & 7) != nextrst)) njThrow(NJ_SYNTAX_ERROR);
nextrst = (nextrst + 1) & 7;
rstcount = nj.rstinterval;
for (i = 0; i < 3; ++i)
nj.comp[i].dcpred = 0;
}
}
nj.error = __NJ_FINISHED;
}
#if NJ_CHROMA_FILTER
#define CF4A (-9)
#define CF4B (111)
#define CF4C (29)
#define CF4D (-3)
#define CF3A (28)
#define CF3B (109)
#define CF3C (-9)
#define CF3X (104)
#define CF3Y (27)
#define CF3Z (-3)
#define CF2A (139)
#define CF2B (-11)
#define CF(x) njClip(((x) + 64) >> 7)
NJ_INLINE void njUpsampleH(nj_component_t* c) {
const int xmax = c->width - 3;
unsigned char *out, *lin, *lout;
int x, y;
out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
if (!out) njThrow(NJ_OUT_OF_MEM);
lin = c->pixels;
lout = out;
for (y = c->height; y; --y) {
lout[0] = CF(CF2A * lin[0] + CF2B * lin[1]);
lout[1] = CF(CF3X * lin[0] + CF3Y * lin[1] + CF3Z * lin[2]);
lout[2] = CF(CF3A * lin[0] + CF3B * lin[1] + CF3C * lin[2]);
for (x = 0; x < xmax; ++x) {
lout[(x << 1) + 3] = CF(CF4A * lin[x] + CF4B * lin[x + 1] + CF4C * lin[x + 2] + CF4D * lin[x + 3]);
lout[(x << 1) + 4] = CF(CF4D * lin[x] + CF4C * lin[x + 1] + CF4B * lin[x + 2] + CF4A * lin[x + 3]);
}
lin += c->stride;
lout += c->width << 1;
lout[-3] = CF(CF3A * lin[-1] + CF3B * lin[-2] + CF3C * lin[-3]);
lout[-2] = CF(CF3X * lin[-1] + CF3Y * lin[-2] + CF3Z * lin[-3]);
lout[-1] = CF(CF2A * lin[-1] + CF2B * lin[-2]);
}
c->width <<= 1;
c->stride = c->width;
njFreeMem((void*)c->pixels);
c->pixels = out;
}
NJ_INLINE void njUpsampleV(nj_component_t* c) {
const int w = c->width, s1 = c->stride, s2 = s1 + s1;
unsigned char *out, *cin, *cout;
int x, y;
out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
if (!out) njThrow(NJ_OUT_OF_MEM);
for (x = 0; x < w; ++x) {
cin = &c->pixels[x];
cout = &out[x];
*cout = CF(CF2A * cin[0] + CF2B * cin[s1]); cout += w;
*cout = CF(CF3X * cin[0] + CF3Y * cin[s1] + CF3Z * cin[s2]); cout += w;
*cout = CF(CF3A * cin[0] + CF3B * cin[s1] + CF3C * cin[s2]); cout += w;
cin += s1;
for (y = c->height - 3; y; --y) {
*cout = CF(CF4A * cin[-s1] + CF4B * cin[0] + CF4C * cin[s1] + CF4D * cin[s2]); cout += w;
*cout = CF(CF4D * cin[-s1] + CF4C * cin[0] + CF4B * cin[s1] + CF4A * cin[s2]); cout += w;
cin += s1;
}
cin += s1;
*cout = CF(CF3A * cin[0] + CF3B * cin[-s1] + CF3C * cin[-s2]); cout += w;
*cout = CF(CF3X * cin[0] + CF3Y * cin[-s1] + CF3Z * cin[-s2]); cout += w;
*cout = CF(CF2A * cin[0] + CF2B * cin[-s1]);
}
c->height <<= 1;
c->stride = c->width;
njFreeMem((void*) c->pixels);
c->pixels = out;
}
#else
NJ_INLINE void njUpsample(nj_component_t* c) {
int x, y, xshift = 0, yshift = 0;
unsigned char *out, *lin, *lout;
while (c->width < nj.width) { c->width <<= 1; ++xshift; }
while (c->height < nj.height) { c->height <<= 1; ++yshift; }
out = (unsigned char*) njAllocMem(c->width * c->height);
if (!out) njThrow(NJ_OUT_OF_MEM);
lin = c->pixels;
lout = out;
for (y = 0; y < c->height; ++y) {
lin = &c->pixels[(y >> yshift) * c->stride];
for (x = 0; x < c->width; ++x)
lout[x] = lin[x >> xshift];
lout += c->width;
}
c->stride = c->width;
njFreeMem((void*) c->pixels);
c->pixels = out;
}
#endif
NJ_INLINE void njConvert(void) {
int i;
nj_component_t* c;
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
#if NJ_CHROMA_FILTER
while ((c->width < nj.width) || (c->height < nj.height)) {
if (c->width < nj.width) njUpsampleH(c);
njCheckError();
if (c->height < nj.height) njUpsampleV(c);
njCheckError();
}
#else
if ((c->width < nj.width) || (c->height < nj.height))
njUpsample(c);
#endif
if ((c->width < nj.width) || (c->height < nj.height)) njThrow(NJ_INTERNAL_ERR);
}
if (nj.ncomp == 3) {
// convert to RGB
int x, yy;
unsigned char *prgb = nj.rgb;
const unsigned char *py = nj.comp[0].pixels;
const unsigned char *pcb = nj.comp[1].pixels;
const unsigned char *pcr = nj.comp[2].pixels;
for (yy = nj.height; yy; --yy) {
for (x = 0; x < nj.width; ++x) {
register int y = py[x] << 8;
register int cb = pcb[x] - 128;
register int cr = pcr[x] - 128;
*prgb++ = njClip((y + 359 * cr + 128) >> 8);
*prgb++ = njClip((y - 88 * cb - 183 * cr + 128) >> 8);
*prgb++ = njClip((y + 454 * cb + 128) >> 8);
}
py += nj.comp[0].stride;
pcb += nj.comp[1].stride;
pcr += nj.comp[2].stride;
}
} else if (nj.comp[0].width != nj.comp[0].stride) {
// grayscale -> only remove stride
unsigned char *pin = &nj.comp[0].pixels[nj.comp[0].stride];
unsigned char *pout = &nj.comp[0].pixels[nj.comp[0].width];
int y;
for (y = nj.comp[0].height - 1; y; --y) {
njCopyMem(pout, pin, nj.comp[0].width);
pin += nj.comp[0].stride;
pout += nj.comp[0].width;
}
nj.comp[0].stride = nj.comp[0].width;
}
}
void njInit(void) {
njFillMem(&nj, 0, sizeof(nj_context_t));
}
void njDone(void) {
int i;
for (i = 0; i < 3; ++i)
if (nj.comp[i].pixels) njFreeMem((void*) nj.comp[i].pixels);
if (nj.rgb) njFreeMem((void*) nj.rgb);
njInit();
}
nj_result_t njDecode(const void* jpeg, const int size) {
njDone();
nj.pos = (const unsigned char*) jpeg;
nj.size = size & 0x7FFFFFFF;
if (nj.size < 2) return NJ_NO_JPEG;
if ((nj.pos[0] ^ 0xFF) | (nj.pos[1] ^ 0xD8)) return NJ_NO_JPEG;
njSkip(2);
while (!nj.error) {
if ((nj.size < 2) || (nj.pos[0] != 0xFF)) return NJ_SYNTAX_ERROR;
njSkip(2);
switch (nj.pos[-1]) {
case 0xC0: njDecodeSOF(); break;
case 0xC4: njDecodeDHT(); break;
case 0xDB: njDecodeDQT(); break;
case 0xDD: njDecodeDRI(); break;
case 0xDA: njDecodeScan(); break;
case 0xFE: njSkipMarker(); break;
default:
if ((nj.pos[-1] & 0xF0) == 0xE0)
njSkipMarker();
else
return NJ_UNSUPPORTED;
}
}
if (nj.error != __NJ_FINISHED) return nj.error;
nj.error = NJ_OK;
njConvert();
return nj.error;
}
int njGetWidth(void) { return nj.width; }
int njGetHeight(void) { return nj.height; }
int njIsColor(void) { return (nj.ncomp != 1); }
unsigned char* njGetImage(void) { return (nj.ncomp == 1) ? nj.comp[0].pixels : nj.rgb; }
int njGetImageSize(void) { return nj.width * nj.height * nj.ncomp; }
#endif // _NJ_INCLUDE_HEADER_ONLY

View File

@ -1,66 +0,0 @@
///////////////////////////////////////////////////////////////////////////////
// HEADER SECTION //
// copy and pase this into nanojpeg.h if you want //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NANOJPEG_H
#define _NANOJPEG_H
// nj_result_t: Result codes for njDecode().
typedef enum _nj_result {
NJ_OK = 0, // no error, decoding successful
NJ_NO_JPEG, // not a JPEG file
NJ_UNSUPPORTED, // unsupported format
NJ_OUT_OF_MEM, // out of memory
NJ_INTERNAL_ERR, // internal error
NJ_SYNTAX_ERROR, // syntax error
__NJ_FINISHED, // used internally, will never be reported
} nj_result_t;
// njInit: Initialize NanoJPEG.
// For safety reasons, this should be called at least one time before using
// using any of the other NanoJPEG functions.
void njInit(void);
// njDecode: Decode a JPEG image.
// Decodes a memory dump of a JPEG file into internal buffers.
// Parameters:
// jpeg = The pointer to the memory dump.
// size = The size of the JPEG file.
// Return value: The error code in case of failure, or NJ_OK (zero) on success.
nj_result_t njDecode(const void* jpeg, const int size);
// njGetWidth: Return the width (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetWidth() is undefined.
int njGetWidth(void);
// njGetHeight: Return the height (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetHeight() is undefined.
int njGetHeight(void);
// njIsColor: Return 1 if the most recently decoded image is a color image
// (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result
// of njGetWidth() is undefined.
int njIsColor(void);
// njGetImage: Returns the decoded image data.
// Returns a pointer to the most recently image. The memory layout it byte-
// oriented, top-down, without any padding between lines. Pixels of color
// images will be stored as three consecutive bytes for the red, green and
// blue channels. This data format is thus compatible with the PGM or PPM
// file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8.
// If njDecode() failed, the result of njGetImage() is undefined.
unsigned char* njGetImage(void);
// njGetImageSize: Returns the size (in bytes) of the image data returned
// by njGetImage(). If njDecode() failed, the result of njGetImageSize() is
// undefined.
int njGetImageSize(void);
// njDone: Uninitialize NanoJPEG.
// Resets NanoJPEG's internal state and frees all memory that has been
// allocated at run-time by NanoJPEG. It is still possible to decode another
// image after a njDone() call.
void njDone(void);
#endif//_NANOJPEG_H

813
common/netloader.c Normal file
View File

@ -0,0 +1,813 @@
#include "common.h"
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <zlib.h>
#include <sys/types.h>
#ifndef __WIN32__
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
typedef int socklen_t;
typedef uint32_t in_addr_t;
#undef DrawText
#endif
#include "netloader.h"
#define PING_ENABLED 1
#ifndef __SWITCH__
#include "switch/runtime/nxlink.h"
#endif
#define ZLIB_CHUNK (16 * 1024)
#define FILE_BUFFER_SIZE (128*1024)
static int netloader_listenfd = -1;
static int netloader_datafd = -1;
#if PING_ENABLED
static int netloader_udpfd = -1;
#endif
static unsigned char in[ZLIB_CHUNK];
static unsigned char out[ZLIB_CHUNK];
static mtx_t netloader_mtx;
static menuEntry_s netloader_me;
static volatile bool netloader_initialized = 0;
static volatile bool netloader_exitflag = 0;
static volatile bool netloader_activated = 0, netloader_launchapp = 0;
static volatile size_t netloader_filelen, netloader_filetotal;
static volatile char netloader_errortext[1024];
static bool netloaderGetExit(void);
//---------------------------------------------------------------------------------
static void netloader_error(const char *func, int err) {
//---------------------------------------------------------------------------------
if (!netloader_initialized || netloaderGetExit()) return;
mtx_lock(&netloader_mtx);
if (netloader_errortext[0] == 0) {
memset((char*)netloader_errortext, 0, sizeof(netloader_errortext));
snprintf((char*)netloader_errortext, sizeof(netloader_errortext)-1, "%s: err=%d\n %s\n", func, err, strerror(errno));
}
mtx_unlock(&netloader_mtx);
}
//---------------------------------------------------------------------------------
static void netloader_socket_error(const char *func) {
//---------------------------------------------------------------------------------
int errcode;
#ifdef __WIN32__
errcode = WSAGetLastError();
#else
errcode = errno;
#endif
netloader_error(func,errcode);
}
//---------------------------------------------------------------------------------
void shutdownSocket(int socket) {
//---------------------------------------------------------------------------------
#ifdef __WIN32__
shutdown (socket, SD_SEND);
closesocket (socket);
#else
close(socket);
#endif
}
static const char DIRECTORY_THIS[] = ".";
static const char DIRECTORY_PARENT[] = "..";
//---------------------------------------------------------------------------------
static bool isDirectorySeparator(int c) {
//---------------------------------------------------------------------------------
return c == DIRECTORY_SEPARATOR_CHAR;
}
//---------------------------------------------------------------------------------
static void sanitisePath(char *path) {
//---------------------------------------------------------------------------------
char *tmpPath = strdup(path);
tmpPath[0] = 0;
char *dirStart = path;
char *curPath = tmpPath;
#ifdef _WIN32
while(dirStart[0]) {
if (dirStart[0] == '/') dirStart[0] =DIRECTORY_SEPARATOR_CHAR;
dirStart++;
}
#endif
dirStart = path;
while(isDirectorySeparator(dirStart[0])) dirStart++;
do {
char *dirEnd = strchr(dirStart, DIRECTORY_SEPARATOR_CHAR);
if (dirEnd) {
dirEnd++;
if(!strncmp(DIRECTORY_PARENT,dirStart,strlen(DIRECTORY_PARENT))) {
/* move back one directory */
size_t pathlen = strlen(tmpPath);
if(tmpPath[pathlen-1] == DIRECTORY_SEPARATOR_CHAR) tmpPath[pathlen-1] = 0;
char *prev = strrchr(tmpPath,DIRECTORY_SEPARATOR_CHAR);
if (prev) {
curPath = prev + 1;
} else {
curPath = tmpPath;
}
dirStart = dirEnd;
} else if (!strncmp(DIRECTORY_THIS,dirStart,strlen(DIRECTORY_THIS))) {
/* strip this entry */
dirStart = dirEnd;
} else {
size_t dirSize = dirEnd - dirStart;
strncpy(curPath,dirStart,dirSize);
curPath[dirSize] = 0;
curPath += dirSize;
dirStart += dirSize;
}
} else {
strcpy(curPath,dirStart);
dirStart += strlen(dirStart);
}
} while(dirStart[0]);
strcpy(path, tmpPath);
free(tmpPath);
}
//---------------------------------------------------------------------------------
static int set_socket_nonblocking(int sock) {
//---------------------------------------------------------------------------------
#ifndef __WIN32__
int flags = fcntl(sock, F_GETFL);
if(flags == -1) return -1;
int rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
if(rc != 0) return -1;
#else
u_long opt = 1;
ioctlsocket(sock, FIONBIO, &opt);
#endif
return 0;
}
//---------------------------------------------------------------------------------
static int recvall(int sock, void *buffer, int size, int flags) {
//---------------------------------------------------------------------------------
int len, sizeleft = size;
bool blockflag=0;
while (sizeleft) {
len = recv(sock,buffer,sizeleft,flags);
if (len == 0) {
size = 0;
break;
};
if (len != -1) {
sizeleft -=len;
buffer +=len;
} else {
#ifdef _WIN32
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
netloader_error("win socket error",errcode);
break;
}
else {
blockflag = 1;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
netloader_socket_error("recv");
break;
}
else {
blockflag = 1;
}
#endif
if (blockflag && netloaderGetExit()) return 0;
}
}
return size;
}
//---------------------------------------------------------------------------------
static int sendall(int sock, void *buffer, int size, int flags) {
//---------------------------------------------------------------------------------
int len, sizeleft = size;
bool blockflag=0;
while (sizeleft) {
len = send(sock,buffer,sizeleft,flags);
if (len == 0) {
size = 0;
break;
};
if (len != -1) {
sizeleft -=len;
buffer +=len;
} else {
#ifdef _WIN32
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
netloader_error("win socket error",errcode);
break;
}
else {
blockflag = 1;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
netloader_socket_error("recv");
break;
}
else {
blockflag = 1;
}
#endif
if (blockflag && netloaderGetExit()) return 0;
}
}
return size;
}
//---------------------------------------------------------------------------------
static int decompress(int sock, FILE *fh, size_t filesize) {
//---------------------------------------------------------------------------------
int ret;
unsigned have;
z_stream strm;
uint32_t chunksize=0;
/* allocate inflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
if (ret != Z_OK) {
netloader_error("inflateInit failed.",ret);
return ret;
}
size_t total = 0;
/* decompress until deflate stream ends or end of file */
do {
if (netloaderGetExit()) {
ret = Z_DATA_ERROR;
break;
}
int len = recvall(sock, &chunksize, 4, 0);
if (len != 4) {
(void)inflateEnd(&strm);
netloader_error("Error getting chunk size",len);
return Z_DATA_ERROR;
}
if (chunksize > sizeof(in)) {
(void)inflateEnd(&strm);
netloader_error("Invalid chunk size",chunksize);
return Z_DATA_ERROR;
}
strm.avail_in = recvall(sock,in,chunksize,0);
if (strm.avail_in == 0) {
(void)inflateEnd(&strm);
netloader_error("remote closed socket.",0);
return Z_DATA_ERROR;
}
strm.next_in = in;
/* run inflate() on input until output buffer not full */
do {
strm.avail_out = ZLIB_CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
case Z_STREAM_ERROR:
(void)inflateEnd(&strm);
netloader_error("inflate error",ret);
return ret;
}
have = ZLIB_CHUNK - strm.avail_out;
if (fwrite(out, 1, have, fh) != have || ferror(fh)) {
(void)inflateEnd(&strm);
netloader_error("file write error",0);
return Z_ERRNO;
}
total += have;
mtx_lock(&netloader_mtx);
netloader_filetotal = total;
mtx_unlock(&netloader_mtx);
//printf("%zu (%zd%%)",total, (100 * total) / filesize);
} while (strm.avail_out == 0);
/* done when inflate() says it's done */
} while (ret != Z_STREAM_END);
/* clean up and return */
(void)inflateEnd(&strm);
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
//---------------------------------------------------------------------------------
int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
//---------------------------------------------------------------------------------
int len, namelen, filelen;
char filepath[PATH_MAX+1];
len = recvall(sock, &namelen, 4, 0);
if (len != 4) {
netloader_error("Error getting name length", errno);
return -1;
}
if (namelen >= sizeof(filepath)-1) {
netloader_error("File-path length is too large",errno);
return -1;
}
len = recvall(sock, filepath, namelen, 0);
if (len != namelen) {
netloader_error("Error getting file-path", errno);
return -1;
}
filepath[namelen] = 0;
len = recvall(sock, &filelen, 4, 0);
if (len != 4) {
netloader_error("Error getting file length",errno);
return -1;
}
mtx_lock(&netloader_mtx);
netloader_filelen = filelen;
mtx_unlock(&netloader_mtx);
int response = 0;
sanitisePath(filepath);
snprintf(me->path, sizeof(me->path)-1, "%s%s%s", menuGetRootPath(), DIRECTORY_SEPARATOR, filepath);
// make sure it's terminated
me->path[sizeof(me->path)-1] = 0;
strncpy(filepath, me->path, sizeof(filepath)-1); // menuEntryLoad() below will overwrite me->path, so copy me->path to filepath and use that instead.
filepath[sizeof(filepath)-1] = 0;
argData_s* ad = &me->args;
ad->dst = (char*)&ad->buf[1];
ad->nxlink_host = remote;
const char* ext = getExtension(me->path);
if (ext && strcasecmp(ext, ".nro")==0)
launchAddArg(ad, me->path);
else {
me->type = ENTRY_TYPE_FILE_OTHER; // Handle fileassoc when extension isn't .nro.
if (!menuEntryLoad(me, "", false, false)) {
response = -3;
errno = EINVAL;
netloader_error("File-extension/filename not recognized",0);
}
menuEntryFree(me, false); // We don't need any of the buffers which may have been allocated.
}
#ifndef _WIN32
if (response == 0) {
int fd = open(filepath,O_CREAT|O_WRONLY, ACCESSPERMS);
if (fd < 0) {
response = -1;
netloader_error("open", errno);
} else {
if (ftruncate(fd,filelen) == -1) {
response = -2;
netloader_error("ftruncate",errno);
}
close(fd);
}
}
#endif
FILE *file = NULL;
if (response == 0) {
file = fopen(filepath,"wb");
if(file == NULL) {
perror("file");
response = -1;
}
}
send(sock,(char *)&response,sizeof(response),0);
char *writebuffer = NULL;
if (response == 0 ) {
writebuffer = malloc(FILE_BUFFER_SIZE);
if (writebuffer==NULL) {
netloader_error("Failed to allocate memory",ENOMEM);
response = -1;
}
else {
memset(writebuffer, 0, FILE_BUFFER_SIZE);
setvbuf(file,writebuffer,_IOFBF, FILE_BUFFER_SIZE);
}
}
if (response == 0 ) {
//printf("transferring %s\n%d bytes.\n", filepath, filelen);
if (decompress(sock,file,filelen)==Z_OK) {
int netloaded_cmdlen = 0;
len = sendall(sock,(char *)&response,sizeof(response),0);
if (len != sizeof(response)) {
netloader_error("Error sending response",errno);
response = -1;
}
//printf("\ntransferring command line\n");
if (response == 0 ) {
len = recvall(sock,(char*)&netloaded_cmdlen,4,0);
if (len != 4) {
netloader_error("Error getting netloaded_cmdlen",errno);
response = -1;
}
}
if (response == 0 ) {
if ((me->args.dst+netloaded_cmdlen) >= (char*)(me->args.buf + sizeof(me->args.buf))) netloaded_cmdlen = (uintptr_t)me->args.buf + sizeof(me->args.buf)-1 - (uintptr_t)me->args.dst;
len = recvall(sock,me->args.dst, netloaded_cmdlen,0);
if (len != netloaded_cmdlen) {
netloader_error("Error getting args",errno);
response = -1;
}
}
if (response == 0 ) {
while(netloaded_cmdlen) {
size_t len = strlen(me->args.dst) + 1;
ad->dst += len;
ad->buf[0]++;
netloaded_cmdlen -= len;
}
}
} else {
response = -1;
}
}
if (file) {
fflush(file);
fclose(file);
}
if (response == -1) unlink(filepath);
free(writebuffer);
return response;
}
//---------------------------------------------------------------------------------
int netloader_activate(void) {
//---------------------------------------------------------------------------------
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(NXLINK_SERVER_PORT);
#if PING_ENABLED
// create udp socket for broadcast ping
netloader_udpfd = socket(AF_INET, SOCK_DGRAM, 0);
if (netloader_udpfd < 0)
{
netloader_socket_error("udp socket");
return -1;
}
if(bind(netloader_udpfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
netloader_socket_error("bind udp socket");
return -1;
}
if (set_socket_nonblocking(netloader_udpfd) == -1)
{
netloader_socket_error("listen fcntl");
return -1;
}
#endif
// create listening socket on all addresses on NXLINK_SERVER_PORT
netloader_listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(netloader_listenfd < 0)
{
netloader_socket_error("socket");
return -1;
}
uint32_t tmpval=1;
int rc = setsockopt(netloader_listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&tmpval, sizeof(tmpval));
if(rc != 0)
{
netloader_socket_error("setsockopt");
return -1;
}
rc = bind(netloader_listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if(rc != 0)
{
netloader_socket_error("bind");
return -1;
}
if (set_socket_nonblocking(netloader_listenfd) == -1)
{
netloader_socket_error("listen fcntl");
return -1;
}
rc = listen(netloader_listenfd, 10);
if(rc != 0)
{
netloader_socket_error("listen");
return -1;
}
return 0;
}
//---------------------------------------------------------------------------------
int netloader_deactivate(void) {
//---------------------------------------------------------------------------------
// close all remaining sockets and allow mainloop to return to main menu
if(netloader_listenfd >= 0)
{
shutdownSocket(netloader_listenfd);
netloader_listenfd = -1;
}
if(netloader_datafd >= 0)
{
shutdownSocket(netloader_datafd);
netloader_datafd = -1;
}
#if PING_ENABLED
if(netloader_udpfd >= 0)
{
shutdownSocket(netloader_udpfd);
netloader_udpfd = -1;
}
#endif
return 0;
}
//---------------------------------------------------------------------------------
int netloader_loop(struct sockaddr_in *sa_remote) {
//---------------------------------------------------------------------------------
#if PING_ENABLED
char recvbuf[256];
socklen_t fromlen = sizeof(struct sockaddr_in);
int len = recvfrom(netloader_udpfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) sa_remote, &fromlen);
if (len!=-1) {
if (strncmp(recvbuf,"nxboot",strlen("nxboot")) == 0) {
sa_remote->sin_family=AF_INET;
sa_remote->sin_port=htons(NXLINK_CLIENT_PORT);
sendto(netloader_udpfd, "bootnx", strlen("bootnx"), 0, (struct sockaddr*) sa_remote,sizeof(struct sockaddr_in));
}
}
#endif
if(netloader_listenfd >= 0 && netloader_datafd < 0) {
socklen_t addrlen = sizeof(struct sockaddr_in);
netloader_datafd = accept(netloader_listenfd, (struct sockaddr*)sa_remote, &addrlen);
if(netloader_datafd < 0)
{
#ifdef _WIN32
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
netloader_error("accept", errcode);
return -1;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
netloader_error("accept", errno);
return -1;
}
#endif
}
else
{
if (set_socket_nonblocking(netloader_datafd) == -1)
{
netloader_socket_error("set_socket_nonblocking(netloader_datafd)");
return -1;
}
close(netloader_listenfd);
netloader_listenfd = -1;
return 1;
}
}
return 0;
}
void netloaderGetState(netloaderState *state) {
if(state==NULL)return;
mtx_lock(&netloader_mtx);
state->activated = netloader_activated;
state->launch_app = netloader_launchapp;
state->me = &netloader_me;
state->transferring = (netloader_datafd >= 0 && netloader_filelen);
state->sock_connected = netloader_datafd >= 0;
state->filelen = netloader_filelen;
state->filetotal = netloader_filetotal;
memset(state->errormsg, 0, sizeof(state->errormsg));
if(netloader_errortext[0]) {
strncpy(state->errormsg, (char*)netloader_errortext, sizeof(state->errormsg)-1);
memset((char*)netloader_errortext, 0, sizeof(netloader_errortext));
}
mtx_unlock(&netloader_mtx);
}
static bool netloaderGetExit(void) {
bool flag;
mtx_lock(&netloader_mtx);
flag = netloader_exitflag;
mtx_unlock(&netloader_mtx);
return flag;
}
void netloaderSignalExit(void) {
if (!netloader_initialized) return;
mtx_lock(&netloader_mtx);
netloader_exitflag = 1;
mtx_unlock(&netloader_mtx);
}
Result netloaderInit(void) {
Result rc=0;
if (netloader_initialized) return 0;
if (mtx_init(&netloader_mtx, mtx_plain) != thrd_success) return 1;
#ifdef __SWITCH__
rc = socketInitializeDefault();
if (R_SUCCEEDED(rc)) {
rc = nifmInitialize(NifmServiceType_User);
if (R_FAILED(rc)) socketExit();
}
#endif
#ifdef __WIN32__
WSADATA wsa_data;
if (WSAStartup (MAKEWORD(2,2), &wsa_data)) {
//netloader_error("WSAStartup failed\n",1);
rc = 2;
}
#endif
if (rc) {
mtx_destroy(&netloader_mtx);
return rc;
}
netloader_initialized = 1;
return 0;
}
void netloaderExit(void) {
if (!netloader_initialized) return;
netloader_initialized = 0;
mtx_destroy(&netloader_mtx);
#ifdef __SWITCH__
nifmExit();
socketExit();
#endif
#ifdef __WIN32__
WSACleanup ();
#endif
}
void netloaderTask(void* arg) {
int ret=0;
struct sockaddr_in sa_remote;
struct timespec duration = {.tv_nsec = 100000000};
menuEntryInit(&netloader_me,ENTRY_TYPE_FILE);
mtx_lock(&netloader_mtx);
netloader_exitflag = 0;
netloader_activated = 0;
netloader_launchapp = 0;
netloader_filelen = 0;
netloader_filetotal = 0;
mtx_unlock(&netloader_mtx);
if(netloader_activate() == 0) {
mtx_lock(&netloader_mtx);
netloader_activated = 1;
mtx_unlock(&netloader_mtx);
}
else {
netloader_deactivate();
return;
}
while((ret = netloader_loop(&sa_remote)) == 0 && !netloaderGetExit()) {
thrd_sleep(&duration, NULL);
}
if(ret == 1 && !netloaderGetExit()) {
int result = loadnro(&netloader_me, netloader_datafd,sa_remote.sin_addr);
if (result== 0) {
ret = 1;
} else {
ret = -1;
}
}
netloader_deactivate();
mtx_lock(&netloader_mtx);
if (ret==1 && !netloader_exitflag) netloader_launchapp = 1;//Access netloader_exitflag directly since the mutex is already locked.
netloader_exitflag = 0;
netloader_activated = 0;
mtx_unlock(&netloader_mtx);
}

23
common/netloader.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
typedef struct {
bool activated;
bool launch_app;
bool transferring;
bool sock_connected;
menuEntry_s *me;
size_t filelen, filetotal;
char errormsg[1025];
} netloaderState;
int netloader_activate(void);
int netloader_deactivate(void);
int netloader_loop(struct sockaddr_in *sa_remote);
Result netloaderInit(void);
void netloaderExit(void);
void netloaderTask(void* arg);
void netloaderGetState(netloaderState *state);
void netloaderSignalExit(void);

5
common/netstatus.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include "common.h"
bool netstatusGetDetails(AssetId *id);

View File

@ -1,54 +0,0 @@
/**
* @file nro.h
* @brief NRO headers.
* @copyright libnx Authors
*/
#pragma once
#define NROHEADER_MAGIC 0x304f524e
#define NROASSETHEADER_MAGIC 0x54455341
#define NROASSETHEADER_VERSION 0
/// Entry for each segment in the codebin.
typedef struct {
u32 file_off;
u32 size;
} NroSegment;
/// Offset 0x0 in the NRO.
typedef struct {
u32 unused;
u32 mod_offset;
u8 padding[8];
} NroStart;
/// This follows NroStart, the actual nro-header.
typedef struct {
u32 magic;
u32 unk1;
u32 size;
u32 unk2;
NroSegment segments[3];
u32 bss_size;
u32 unk3;
u8 build_id[0x20];
u8 padding[0x20];
} NroHeader;
/// Custom asset section.
typedef struct {
u64 offset;
u64 size;
} NroAssetSection;
/// Custom asset header.
typedef struct {
u32 magic;
u32 version;
NroAssetSection icon;
NroAssetSection nacp;
NroAssetSection romfs;
} NroAssetHeader;

8
common/power.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "common.h"
void powerInit(void);
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging);
void powerExit(void);

130
common/status.c Normal file
View File

@ -0,0 +1,130 @@
#include "status.h"
static bool s_statusInitialized = 0;
static thrd_t s_statusThread;
static cnd_t s_statusCdn;
static mtx_t s_statusMtx;
static mtx_t s_statusAccessMtx;
static bool s_statusExit;
static bool s_statusReady;
static bool s_statusNetFlag;
static AssetId s_statusNetAssetId;
static bool s_statusTemperatureFlag;
static s32 s_statusTemperature;
// This uses netstatusGetDetails from a dedicated thread, since nifmGetInternetConnectionStatus can block for a few seconds.
static int statusThreadProc(void* unused)
{
mtx_lock(&s_statusMtx);
struct timespec timeout = {0};
bool tmpflag=0;
bool thermalflag=0;
bool thermal_initialized=0;
AssetId tmpid;
s32 temperature;
thermal_initialized = thermalstatusInit();
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec++;
for (;;)
{
cnd_timedwait(&s_statusCdn, &s_statusMtx, &timeout);
if (s_statusExit)
break;
tmpflag = netstatusGetDetails(&tmpid);
if (thermal_initialized) thermalflag = thermalstatusGetDetails(&temperature);
mtx_lock(&s_statusAccessMtx);
s_statusNetFlag = tmpflag;
s_statusNetAssetId = tmpid;
s_statusTemperatureFlag = thermalflag;
s_statusTemperature = temperature;
s_statusReady = 1;
mtx_unlock(&s_statusAccessMtx);
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec++;
}
mtx_unlock(&s_statusMtx);
if (thermal_initialized) thermalstatusExit();
return 0;
}
bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature) {
if (!s_statusReady) return 0;
mtx_lock(&s_statusAccessMtx);
*netstatusFlag = s_statusNetFlag;
*netstatusAssetId = s_statusNetAssetId;
*temperatureFlag = s_statusTemperatureFlag;
*temperature = s_statusTemperature;
mtx_unlock(&s_statusAccessMtx);
return 1;
}
bool statusInit(void)
{
if (s_statusInitialized) return 1;
if (cnd_init(&s_statusCdn) != thrd_success) return 0;
if (mtx_init(&s_statusMtx, mtx_plain) != thrd_success) {
cnd_destroy(&s_statusCdn);
return 0;
}
if (mtx_init(&s_statusAccessMtx, mtx_plain) != thrd_success) {
mtx_destroy(&s_statusMtx);
cnd_destroy(&s_statusCdn);
return 0;
}
if (thrd_create(&s_statusThread, statusThreadProc, 0) != thrd_success) {
mtx_destroy(&s_statusAccessMtx);
mtx_destroy(&s_statusMtx);
cnd_destroy(&s_statusCdn);
return 0;
}
s_statusInitialized = 1;
return 1;
}
void statusExit(void)
{
int res=0;
if (!s_statusInitialized) return;
s_statusInitialized = 0;
mtx_lock(&s_statusMtx);
s_statusExit = true;
cnd_signal(&s_statusCdn);
mtx_unlock(&s_statusMtx);
thrd_join(s_statusThread, &res);
s_statusReady = 0;
mtx_destroy(&s_statusAccessMtx);
mtx_destroy(&s_statusMtx);
cnd_destroy(&s_statusCdn);
}

7
common/status.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "common.h"
bool statusInit(void);
void statusExit(void);
bool statusGet(bool *netstatusFlag, AssetId *netstatusAssetId, bool *temperatureFlag, s32 *temperature);

View File

@ -1,27 +1,31 @@
#include "text.h" #include "text.h"
#ifdef SWITCH static u64 s_textLanguageCode = 0;
#ifdef __SWITCH__
static int s_textLang = SetLanguage_ENUS; static int s_textLang = SetLanguage_ENUS;
#else #else
static int s_textLang = 1; static int s_textLang = 1;
#endif #endif
void textInit(void) { Result textInit(void) {
#ifdef SWITCH #ifdef __SWITCH__
//u64 LanguageCode=0; SetLanguage Language=SetLanguage_ENUS;
//s32 Language=0;
s_textLang = SetLanguage_ENUS; s_textLang = Language;
//TODO: Re-enable this once the font supports all used languages.
/*Result rc = setInitialize(); Result rc = setInitialize();
if (R_SUCCEEDED(rc)) rc = setGetSystemLanguage(&LanguageCode); if (R_SUCCEEDED(rc)) rc = setGetSystemLanguage(&s_textLanguageCode);
if (R_SUCCEEDED(rc)) rc = setMakeLanguage(LanguageCode, &Language); if (R_SUCCEEDED(rc)) rc = setMakeLanguage(s_textLanguageCode, &Language);
if (R_SUCCEEDED(rc) && Language < 16) s_textLang = Language; //if (R_SUCCEEDED(rc) && Language < 17) s_textLang = Language;//TODO: Re-enable this once language.c supports all used languages.
setExit();*/ setExit();
if (R_FAILED(rc)) return rc;
#else #else
s_textLang = 1; s_textLang = 1;
#endif #endif
}
return 0;
}
int textGetLang(void) { int textGetLang(void) {
return s_textLang; return s_textLang;
@ -29,10 +33,14 @@ int textGetLang(void) {
const char* textGetString(StrId id) { const char* textGetString(StrId id) {
const char* str = g_strings[id][s_textLang]; const char* str = g_strings[id][s_textLang];
#ifdef SWITCH #ifdef __SWITCH__
if (!str) str = g_strings[id][SetLanguage_ENUS]; if (!str) str = g_strings[id][SetLanguage_ENUS];
#else #else
if (!str) str = g_strings[id][1]; if (!str) str = g_strings[id][1];
#endif #endif
return str; return str;
} }
u64 textGetLanguageCode(void) {
return s_textLanguageCode;
}

View File

@ -2,6 +2,7 @@
#include "common.h" #include "common.h"
#include "language.h" #include "language.h"
void textInit(void); Result textInit(void);
int textGetLang(void); int textGetLang(void);
const char* textGetString(StrId id); const char* textGetString(StrId id);
u64 textGetLanguageCode(void);

View File

@ -1,41 +1,717 @@
#include "theme.h" #include "theme.h"
#include "button_a_light_bin.h" #include <physfs.h>
#include "button_a_dark_bin.h"
#include "button_b_light_bin.h" theme_t themeCurrent;
#include "button_b_dark_bin.h" ThemePreset themeGlobalPreset;
#include "hbmenu_logo_light_bin.h"
#include "hbmenu_logo_dark_bin.h" bool colorFromSetting(config_setting_t *rgba, color_t *col) {
if(rgba == NULL)
return false;
*col = MakeColor(config_setting_get_int_elem(rgba, 0), config_setting_get_int_elem(rgba, 1), config_setting_get_int_elem(rgba, 2), config_setting_get_int_elem(rgba, 3));
return true;
}
bool intElemFromSetting(config_setting_t *setting, int *out, size_t count) {
if (!setting || config_setting_length(setting) < count)
return false;
for (size_t i=0; i<count; i++) {
out[i] = config_setting_get_int_elem(setting, i);
}
return true;
}
bool layoutObjectFromSetting(config_setting_t *layout_setting, ThemeLayoutObject *obj, bool ignore_cfg_visible) {
int tmp=0;
ThemeLayoutObject tmpobj={0};
if (!layout_setting)
return false;
memcpy(tmpobj.posStart, obj->posStart, sizeof(obj->posStart));
memcpy(tmpobj.posEnd, obj->posEnd, sizeof(obj->posEnd));
memcpy(tmpobj.size, obj->size, sizeof(obj->size));
if (config_setting_lookup_bool(layout_setting, "visible", &tmp)==CONFIG_TRUE)
tmpobj.visible = tmp;
else
tmpobj.visible = obj->visible;
if (config_setting_lookup_bool(layout_setting, "posType", &tmp)==CONFIG_TRUE)
tmpobj.posType = tmp;
else
tmpobj.posType = obj->posType;
intElemFromSetting(config_setting_lookup(layout_setting, "posStart"), tmpobj.posStart, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "posEnd"), tmpobj.posEnd, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "size"), tmpobj.size, 2);
if (!tmpobj.posType && (tmpobj.posStart[0] < 0 || tmpobj.posStart[1] < 0 || tmpobj.posEnd[0] < 0 || tmpobj.posEnd[1] < 0))
return false;
if (tmpobj.size[0] < 0 || tmpobj.size[1] < 0)
return false;
obj->posStart[0] = tmpobj.posStart[0];
obj->posStart[1] = tmpobj.posStart[1];
obj->posEnd[0] = tmpobj.posEnd[0];
obj->posEnd[1] = tmpobj.posEnd[1];
if (!ignore_cfg_visible) obj->visible = tmpobj.visible;
obj->posType = tmpobj.posType;
obj->size[0] = tmpobj.size[0];
obj->size[1] = tmpobj.size[1];
return true;
}
bool assetObjectFromSetting(config_setting_t *asset_setting, AssetId id, ThemeLayoutObject *layoutobj) {
int imageSize[2]={0};
const char *path = NULL;
char tmp_path[PATH_MAX];
if (!asset_setting)
return false;
if (config_setting_lookup_string(asset_setting, "path", &path)==CONFIG_FALSE)
return false;
if (!intElemFromSetting(config_setting_lookup(asset_setting, "imageSize"), imageSize, 2))
return false;
if (imageSize[0] <= 0 || imageSize[1] <= 0 || imageSize[0] > 1280 || imageSize[1] > 720)
return false;
if (layoutobj && (imageSize[0] != layoutobj->imageSize[0] || imageSize[1] != layoutobj->imageSize[1]))
return false;
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "theme/%s", path);
return assetsLoadData(id, tmp_path, imageSize);
}
void themeStartup(ThemePreset preset) { void themeStartup(ThemePreset preset) {
themeGlobalPreset = preset;
theme_t themeLight = (theme_t) {
.textColor = MakeColor(0, 0, 0, 255),
.attentionTextColor = MakeColor(255, 0, 0, 255),
.frontWaveColor = MakeColor(100, 212, 250, 255),
.middleWaveColor = MakeColor(100, 153, 255, 255),
.backWaveColor = MakeColor(154, 171, 255, 255),
.backgroundColor = MakeColor(233, 236, 241, 255),
.highlightColor = MakeColor(91, 237, 224, 255),
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
.separatorColor = MakeColor(219, 218, 219, 255),
.borderColor = MakeColor(255,255,255,255),
.borderTextColor = MakeColor(64,64,64,255),
.progressBarColor = MakeColor(0,224,0,255),
.enableWaveBlending = 0,
.buttonAText = "\uE0E0",
.buttonBText = "\uE0E1",
.buttonXText = "\uE0E2",
.buttonYText = "\uE0E3",
.buttonPText = "\uE0EF",
.buttonMText = "\uE0F0",
.labelStarOnText = "\u2605",
.labelStarOffText = "\u2606",
};
theme_t themeDark = (theme_t) {
.textColor = MakeColor(255, 255, 255, 255),
.attentionTextColor = MakeColor(255, 0, 0, 255),
.frontWaveColor = MakeColor(96, 204, 204, 255),
.middleWaveColor = MakeColor(66, 154, 159, 255),
.backWaveColor = MakeColor(73, 103, 169, 255),
.backgroundColor = MakeColor(45, 45, 50, 255),
.highlightColor = MakeColor(91, 237, 224, 255),
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
.separatorColor = MakeColor(219, 218, 219, 255),
.borderColor = MakeColor(255,255,255,255),
.borderTextColor = MakeColor(64,64,64,255),
.progressBarColor = MakeColor(0,224,0,255),
.enableWaveBlending = 0,
.buttonAText = "\uE0A0",
.buttonBText = "\uE0A1",
.buttonXText = "\uE0A2",
.buttonYText = "\uE0A3",
.buttonPText = "\uE0B3",
.buttonMText = "\uE0B4",
.labelStarOnText = "\u2605",
.labelStarOffText = "\u2606",
};
theme_t themeCommon = {
.layoutObjects = {
[ThemeLayoutId_Logo] = {
.visible = true,
.posType = false,
.posStart = {40, 20},
},
[ThemeLayoutId_HbmenuVersion] = {
.visible = true,
.posType = false,
.posStart = {184, 46 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_LoaderInfo] = {
.visible = true,
.posType = true,
.posStart = {43, 46 + 18 + 20},
.posEnd = {184},
.font = interuiregular14,
},
[ThemeLayoutId_AttentionText] = {
.visible = true,
.posType = true,
.posStart = {-32, 46 + 18},
.font = interuimedium30,
},
[ThemeLayoutId_LogInfo] = {
.visible = true,
.posType = false,
.posStart = {180 + 256, 46 + 16 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_InfoMsg] = {
.visible = true,
.posType = false,
.posStart = {64, 128 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_MenuPath] = {
.visible = true,
.posType = false,
.posStart = {40, 720 - 47 + 24},
.size = {380},
.font = interuiregular18,
},
[ThemeLayoutId_MenuTypeMsg] = {
.visible = true,
.posType = false,
.posStart = {1180, 30 + 26 + 32 + 20},
.font = interuiregular18,
},
[ThemeLayoutId_MsgBoxSeparator] = {
.visible = true,
.posType = true,
.posStart = {0, -80},
},
[ThemeLayoutId_MsgBoxBottomText] = {
.visible = true,
.posType = true,
.posStart = {0, -29},
},
// ThemeLayoutId_BackgroundImage is not set with the defaults.
[ThemeLayoutId_BackWave] = {
.visible = true,
.posType = true,
.size = {0, 295},
},
[ThemeLayoutId_MiddleWave] = {
.visible = true,
.posType = true,
.size = {0, 290},
},
[ThemeLayoutId_FrontWave] = {
.visible = true,
.posType = true,
.size = {0, 280},
},
[ThemeLayoutId_ButtonA] = {
.visible = true,
.posType = false,
.posStart = {1280 - 126 - 30 - 32, 720 - 47 + 24},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonAText] = {
.visible = true,
.posType = false,
.posStart = {1280 - 90 - 30 - 32, 720 - 47 + 24},
.touchSize = {0, 25},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonB] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.posEnd = {0},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonBText] = {
.visible = true,
.posType = true,
.posStart = {-90, 0},
.touchSize = {0, 32},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonY] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.font = fontscale7,
},
[ThemeLayoutId_ButtonYText] = {
.visible = true,
.posType = true,
.posStart = {-32, 0},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonM] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.font = fontscale7,
},
[ThemeLayoutId_ButtonMText] = {
.visible = true,
.posType = true,
.posStart = {-32, 0},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonX] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonXText] = {
.visible = true,
.posType = true,
.posStart = {-40 + 8, 0},
.touchSize = {0, 25},
.font = interuiregular18,
},
[ThemeLayoutId_NetworkIcon] = {
.visible = true,
.posType = true,
.posStart = {0, 0 + 47 + 10 + 3},
},
[ThemeLayoutId_BatteryCharge] = {
.visible = true,
.posType = false,
.posStart = {1180 - 10 - 24 - 8, 0 + 47 + 10 + 21 + 4},
.font = interuiregular14,
},
[ThemeLayoutId_BatteryIcon] = {
.visible = true,
.posType = false,
.posStart = {1180 - 8 - 24 - 8, 0 + 47 + 10 + 6},
},
[ThemeLayoutId_ChargingIcon] = {
.visible = true,
.posType = false,
.posStart = {1180 - 20, 0 + 47 + 10 + 6},
},
[ThemeLayoutId_Status] = {
.visible = true,
.posType = false,
.posStart = {1180, 0 + 47 + 10},
.font = interuimedium20,
},
[ThemeLayoutId_Temperature] = {
.visible = true,
.posType = false,
.posStart = {1180 + 4, 0 + 47 + 10 + + 21 + 6},
.font = interuiregular14,
},
[ThemeLayoutId_MenuList] = {
.visible = true,
.posType = false,
.posStart = {29, 720 - 100 - 145},
.posEnd = {140 + 30, 0},
.size = {140, 140 + 32},
},
[ThemeLayoutId_MenuListTiles] = {
.visible = true,
.posType = true,
.posEnd = {7, 0},
.size = {0, 0},
},
[ThemeLayoutId_MenuListIcon] = {
.visible = true,
.posType = true,
.posStart = {0, 32},
.size = {140, 140},
.imageSize = {256, 256},
},
[ThemeLayoutId_MenuListName] = {
.visible = true,
.posType = true,
.posStart = {4, 4 + 18},
.size = {140 - 32, 0},
.font = interuiregular14,
},
[ThemeLayoutId_MenuActiveEntryIcon] = {
.visible = true,
.posType = false,
.posStart = {117, 100+10},
.size = {256, 256},
.imageSize = {256, 256},
},
[ThemeLayoutId_MenuActiveEntryName] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 39},
.size = {790 - 120, 0},
.font = interuimedium30,
},
[ThemeLayoutId_MenuActiveEntryAuthor] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 28 + 30 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_MenuActiveEntryVersion] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 28 + 30 + 18 + 6 + 18},
.font = interuiregular14,
},
},
};
char themePath[PATH_MAX] = {0};
GetThemePathFromConfig(themePath, PATH_MAX);
theme_t *themeDefault;
config_t cfg = {0};
config_init(&cfg);
config_setting_t *theme = NULL, *layout = NULL, *assets = NULL;
color_t text, logoColor={0}, attentionText, frontWave, middleWave, backWave, background, highlight, highlightGradientEdgeColor, separator, borderColor, borderTextColor, progressBarColor;
int waveBlending;
const char *AText, *BText, *XText, *YText, *PText, *MText, *starOnText, *starOffText;
bool logoColor_set = false;
bool good_cfg = false;
#ifdef __SWITCH__
bool is_romfs = false;
#endif
bool is_archive = false;
const char* theme_archive_path = NULL;
assetsClearTheme();
if(themePath[0]!=0) {
const char* cfg_path = themePath;
const char* ext = getExtension(themePath);
#ifdef __SWITCH__
if (strcasecmp(ext, ".romfs")==0) {
if (R_FAILED(romfsMountFromFsdev(themePath, 0, "theme")))
cfg_path = NULL;
else {
is_romfs = true;
cfg_path = "theme:/theme.cfg";
theme_archive_path = "theme:/";
}
}
#endif
if (strcasecmp(ext, ".romfs")!=0 && strcasecmp(ext, ".cfg")!=0) {
theme_archive_path = themePath;
}
if (theme_archive_path) {
if (!PHYSFS_mount(theme_archive_path, "theme", 0)) cfg_path = NULL;
else {
is_archive = true;
cfg_path = "theme/theme.cfg";
}
}
if (cfg_path) {
if (!is_archive) good_cfg = config_read_file(&cfg, cfg_path);
else {
u8 *cfg_buf = NULL;
good_cfg = assetsPhysfsReadFile(cfg_path, &cfg_buf, NULL, true);
if (good_cfg) good_cfg = config_read_string(&cfg, (char*)cfg_buf);
free(cfg_buf);
}
}
}
switch (preset) { switch (preset) {
case THEME_PRESET_LIGHT: case THEME_PRESET_LIGHT:
themeCurrent = (theme_t) { default:
textColor: MakeColor(0, 0, 0, 255), themeDefault = &themeLight;
frontWaveColor: MakeColor(100, 212, 250, 255), if (good_cfg)
middleWaveColor: MakeColor(100, 153, 255, 255), theme = config_lookup(&cfg, "lightTheme");
backWaveColor: MakeColor(154, 171, 255, 255),
backgroundColor: MakeColor(233, 236, 241, 255),
highlightColor: MakeColor(91, 237, 224, 255),
enableWaveBlending: 0,
buttonAImage: button_a_light_bin,
buttonBImage: button_b_light_bin,
hbmenuLogoImage: hbmenu_logo_light_bin
};
break; break;
case THEME_PRESET_DARK: case THEME_PRESET_DARK:
themeCurrent = (theme_t) { themeDefault = &themeDark;
textColor: MakeColor(255, 255, 255, 255), if (good_cfg)
frontWaveColor: MakeColor(96, 204, 204, 255), theme = config_lookup(&cfg, "darkTheme");
middleWaveColor: MakeColor(66, 154, 159, 255),
backWaveColor: MakeColor(73, 103, 169, 255),
backgroundColor: MakeColor(45, 45, 50, 255),
highlightColor: MakeColor(91, 237, 224, 255),
enableWaveBlending: 0,
buttonAImage: button_a_dark_bin,
buttonBImage: button_b_dark_bin,
hbmenuLogoImage: hbmenu_logo_dark_bin
};
break; break;
} }
}
if (good_cfg) {
if (theme != NULL) {
if (!colorFromSetting(config_setting_lookup(theme, "textColor"), &text))
text = themeDefault->textColor;
if (colorFromSetting(config_setting_lookup(theme, "logoColor"), &logoColor))
logoColor_set = true;
if (!colorFromSetting(config_setting_lookup(theme, "attentionTextColor"), &attentionText))
attentionText = themeDefault->attentionTextColor;
if (!colorFromSetting(config_setting_lookup(theme, "frontWaveColor"), &frontWave))
frontWave = themeDefault->frontWaveColor;
if (!colorFromSetting(config_setting_lookup(theme, "middleWaveColor"), &middleWave))
middleWave = themeDefault->middleWaveColor;
if (!colorFromSetting(config_setting_lookup(theme, "backWaveColor"), &backWave))
backWave = themeDefault->backWaveColor;
if (!colorFromSetting(config_setting_lookup(theme, "backgroundColor"), &background))
background = themeDefault->backgroundColor;
if (!colorFromSetting(config_setting_lookup(theme, "highlightColor"), &highlight))
highlight = themeDefault->highlightColor;
if (!colorFromSetting(config_setting_lookup(theme, "highlightGradientEdgeColor"), &highlightGradientEdgeColor))
highlightGradientEdgeColor = themeDefault->highlightGradientEdgeColor;
if (!colorFromSetting(config_setting_lookup(theme, "separatorColor"), &separator))
separator = themeDefault->separatorColor;
if (!colorFromSetting(config_setting_lookup(theme, "borderColor"), &borderColor))
borderColor = themeDefault->borderColor;
if (!colorFromSetting(config_setting_lookup(theme, "borderTextColor"), &borderTextColor))
borderTextColor = themeDefault->borderTextColor;
if (!colorFromSetting(config_setting_lookup(theme, "progressBarColor"), &progressBarColor))
progressBarColor = themeDefault->progressBarColor;
if (!config_setting_lookup_int(theme, "enableWaveBlending", &waveBlending))
waveBlending = themeDefault->enableWaveBlending;
if (!config_setting_lookup_string(theme, "buttonAText", &AText))
AText = themeDefault->buttonAText;
if (!config_setting_lookup_string(theme, "buttonBText", &BText))
BText = themeDefault->buttonBText;
if (!config_setting_lookup_string(theme, "buttonXText", &XText))
XText = themeDefault->buttonXText;
if (!config_setting_lookup_string(theme, "buttonYText", &YText))
YText = themeDefault->buttonYText;
if (!config_setting_lookup_string(theme, "buttonPText", &PText))
PText = themeDefault->buttonPText;
if (!config_setting_lookup_string(theme, "buttonMText", &MText))
MText = themeDefault->buttonMText;
if (!config_setting_lookup_string(theme, "labelStarOnText", &starOnText))
starOnText = themeDefault->labelStarOnText;
if (!config_setting_lookup_string(theme, "labelStarOffText", &starOffText))
starOffText = themeDefault->labelStarOffText;
themeCurrent = (theme_t) {
.textColor = text,
.logoColor = logoColor,
.attentionTextColor = attentionText,
.frontWaveColor = frontWave,
.middleWaveColor = middleWave,
.backWaveColor = backWave,
.backgroundColor = background,
.highlightColor = highlight,
.highlightGradientEdgeColor = highlightGradientEdgeColor,
.separatorColor = separator,
.borderColor = borderColor,
.borderTextColor = borderTextColor,
.progressBarColor = progressBarColor,
.logoColor_set = logoColor_set,
.enableWaveBlending = waveBlending,
};
strncpy(themeCurrent.buttonAText, AText, sizeof(themeCurrent.buttonAText));
themeCurrent.buttonAText[sizeof(themeCurrent.buttonAText)-1] = 0;
strncpy(themeCurrent.buttonBText, BText, sizeof(themeCurrent.buttonBText));
themeCurrent.buttonBText[sizeof(themeCurrent.buttonBText)-1] = 0;
strncpy(themeCurrent.buttonXText, XText, sizeof(themeCurrent.buttonXText));
themeCurrent.buttonXText[sizeof(themeCurrent.buttonXText)-1] = 0;
strncpy(themeCurrent.buttonYText, YText, sizeof(themeCurrent.buttonYText));
themeCurrent.buttonYText[sizeof(themeCurrent.buttonYText)-1] = 0;
strncpy(themeCurrent.buttonPText, PText, sizeof(themeCurrent.buttonPText));
themeCurrent.buttonPText[sizeof(themeCurrent.buttonPText)-1] = 0;
strncpy(themeCurrent.buttonMText, MText, sizeof(themeCurrent.buttonMText));
themeCurrent.buttonMText[sizeof(themeCurrent.buttonMText)-1] = 0;
strncpy(themeCurrent.labelStarOnText, starOnText, sizeof(themeCurrent.labelStarOnText));
themeCurrent.labelStarOnText[sizeof(themeCurrent.labelStarOnText)-1] = 0;
strncpy(themeCurrent.labelStarOffText, starOffText, sizeof(themeCurrent.labelStarOffText));
themeCurrent.labelStarOffText[sizeof(themeCurrent.labelStarOffText)-1] = 0;
} else {
themeCurrent = *themeDefault;
}
memcpy(themeCurrent.layoutObjects, themeCommon.layoutObjects, sizeof(themeCommon.layoutObjects));
layout = config_lookup(&cfg, "layout");
if (layout != NULL) {
layoutObjectFromSetting(config_setting_lookup(layout, "logo"), &themeCurrent.layoutObjects[ThemeLayoutId_Logo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "hbmenuVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_HbmenuVersion], true);
layoutObjectFromSetting(config_setting_lookup(layout, "loaderInfo"), &themeCurrent.layoutObjects[ThemeLayoutId_LoaderInfo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "attentionText"), &themeCurrent.layoutObjects[ThemeLayoutId_AttentionText], true);
layoutObjectFromSetting(config_setting_lookup(layout, "logInfo"), &themeCurrent.layoutObjects[ThemeLayoutId_LogInfo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "infoMsg"), &themeCurrent.layoutObjects[ThemeLayoutId_InfoMsg], true);
layoutObjectFromSetting(config_setting_lookup(layout, "menuPath"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuPath], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuTypeMsg"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuTypeMsg], false);
layoutObjectFromSetting(config_setting_lookup(layout, "msgBoxSeparator"), &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator], false);
layoutObjectFromSetting(config_setting_lookup(layout, "msgBoxBottomText"), &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxBottomText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "backgroundImage"), &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage], false);
layoutObjectFromSetting(config_setting_lookup(layout, "backWave"), &themeCurrent.layoutObjects[ThemeLayoutId_BackWave], false);
layoutObjectFromSetting(config_setting_lookup(layout, "middleWave"), &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave], false);
layoutObjectFromSetting(config_setting_lookup(layout, "frontWave"), &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonA"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonAText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonAText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonB"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonB], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonBText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonBText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonY"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonY], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonYText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonYText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonM"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonM], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonMText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonMText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonX"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonX], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonXText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonXText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "networkIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_NetworkIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "batteryCharge"), &themeCurrent.layoutObjects[ThemeLayoutId_BatteryCharge], false);
layoutObjectFromSetting(config_setting_lookup(layout, "batteryIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_BatteryIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "chargingIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_ChargingIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "status"), &themeCurrent.layoutObjects[ThemeLayoutId_Status], false);
layoutObjectFromSetting(config_setting_lookup(layout, "temperature"), &themeCurrent.layoutObjects[ThemeLayoutId_Temperature], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuList"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuList], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListTiles"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListName"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListName], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryName"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryName], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryAuthor"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryAuthor], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion], false);
}
if (is_archive) assets = config_lookup(&cfg, "assets");
if (is_archive && assets) {
assetObjectFromSetting(config_setting_lookup(assets, "battery_icon"), AssetId_battery_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "charging_icon"), AssetId_charging_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "folder_icon"), AssetId_folder_icon, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "invalid_icon"), AssetId_invalid_icon, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "theme_icon_dark"), AssetId_theme_icon_dark, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "theme_icon_light"), AssetId_theme_icon_light, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "airplane_icon"), AssetId_airplane_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi_none_icon"), AssetId_wifi_none_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi1_icon"), AssetId_wifi1_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi2_icon"), AssetId_wifi2_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi3_icon"), AssetId_wifi3_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "eth_icon"), AssetId_eth_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "eth_none_icon"), AssetId_eth_none_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "background_image"), AssetId_background_image, NULL);
}
} else {
themeCurrent = *themeDefault;
memcpy(themeCurrent.layoutObjects, themeCommon.layoutObjects, sizeof(themeCommon.layoutObjects));
}
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
if (layoutobj->posEnd[0] < 1) layoutobj->posEnd[0] = 1;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
if (layoutobj->size[0] <= 0 || layoutobj->size[1] <= 0 || layoutobj->size[0] > layoutobj->imageSize[0] || layoutobj->size[1] > layoutobj->imageSize[1]) {
layoutobj->size[0] = layoutobj->imageSize[0];
layoutobj->size[1] = layoutobj->imageSize[1];
}
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
if (layoutobj->size[0] <= 0 || layoutobj->size[1] <= 0 || layoutobj->size[0] > layoutobj->imageSize[0] || layoutobj->size[1] > layoutobj->imageSize[1]) {
layoutobj->size[0] = layoutobj->imageSize[0];
layoutobj->size[1] = layoutobj->imageSize[1];
}
config_destroy(&cfg);
if (is_archive) PHYSFS_unmount(theme_archive_path);
#ifdef __SWITCH__
if (is_romfs) romfsUnmount("theme");
#endif
}
void GetThemePathFromConfig(char* themePath, size_t size) {
const char* tmpThemePath = "";
config_t cfg = {0};
config_setting_t *settings = NULL;
char tmp_path[PATH_MAX+1] = {0};
char tmp_path_theme[PATH_MAX+1] = {0};
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/settings.cfg", menuGetRootBasePath());
snprintf(tmp_path_theme, sizeof(tmp_path_theme)-1, "%s/config/nx-hbmenu/themes/", menuGetRootBasePath());
bool good_cfg = config_read_file(&cfg, tmp_path);
if(good_cfg) {
settings = config_lookup(&cfg, "settings");
if(settings != NULL) {
if(config_setting_lookup_string(settings, "themePath", &tmpThemePath))
snprintf(themePath, size-1, "%s%s", tmp_path_theme, tmpThemePath);
}
}
config_destroy(&cfg);
}
void SetThemePathToConfig(const char* themePath) {
config_t cfg = {0};
config_init(&cfg);
char settingPath[PATH_MAX] = {0};
config_setting_t *root = NULL,
*group = NULL,
*settings = NULL;
themePath = getSlash(themePath);
if(themePath[0] == '/') themePath++;
#ifdef __SWITCH__
settingPath[0] = '/';
#endif
snprintf(settingPath, sizeof(settingPath)-1, "%s/config/nx-hbmenu/settings.cfg", menuGetRootBasePath());
bool good_cfg = config_read_file(&cfg, settingPath);
if(good_cfg) {
group = config_lookup(&cfg, "settings");
if(group != NULL)
settings = config_setting_lookup(group, "themePath");
if(settings != NULL)
config_setting_set_string(settings, themePath);
} else {
root = config_root_setting(&cfg);
if(root != NULL)
group = config_setting_add(root, "settings", CONFIG_TYPE_GROUP);
if(group != NULL)
settings = config_setting_add(group, "themePath", CONFIG_TYPE_STRING);
if(settings != NULL)
config_setting_set_string(settings, themePath);
}
if(!config_write_file(&cfg, settingPath)) {
menuCreateMsgBox(780, 300, textGetString(StrId_ThemeNotApplied));
}
config_destroy(&cfg);
}

View File

@ -1,20 +1,7 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
#include <libconfig.h>
typedef struct
{
color_t textColor;
color_t frontWaveColor;
color_t middleWaveColor;
color_t backWaveColor;
color_t backgroundColor;
color_t highlightColor;
bool enableWaveBlending;
const uint8_t *buttonAImage;
const uint8_t *buttonBImage;
const uint8_t *hbmenuLogoImage;
} theme_t;
typedef enum typedef enum
{ {
@ -22,6 +9,54 @@ typedef enum
THEME_PRESET_DARK, THEME_PRESET_DARK,
} ThemePreset; } ThemePreset;
void themeStartup(ThemePreset preset); typedef struct
{
bool visible;
bool posType; // false = absolute, true = relative
int posStart[2];
int posEnd[2];
int size[2]; // width/height
int imageSize[2]; // width/height for the actual image data
int touchSize[2];
int posFinal[2];
uint32_t textSize[2];
u32 font;
} ThemeLayoutObject;
theme_t themeCurrent; typedef struct
{
color_t textColor;
color_t logoColor;
color_t attentionTextColor;
color_t frontWaveColor;
color_t middleWaveColor;
color_t backWaveColor;
color_t backgroundColor;
color_t highlightColor;
color_t highlightGradientEdgeColor;
color_t separatorColor;
color_t borderColor;
color_t borderTextColor;
color_t progressBarColor;
bool logoColor_set;
bool enableWaveBlending;
char buttonAText[32];
char buttonBText[32];
char buttonXText[32];
char buttonYText[32];
char buttonPText[32];
char buttonMText[32];
char labelStarOnText[32];
char labelStarOffText[32];
ThemeLayoutObject layoutObjects[ThemeLayoutId_Total];
} theme_t;
bool colorFromSetting(config_setting_t *rgba, color_t *col);
void themeStartup(ThemePreset preset);
void GetThemePathFromConfig(char* themePath, size_t size);
void SetThemePathToConfig(const char* themePath);
extern theme_t themeCurrent;
extern ThemePreset themeGlobalPreset;

7
common/thermalstatus.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "common.h"
bool thermalstatusInit(void);
void thermalstatusExit(void);
bool thermalstatusGetDetails(s32 *temperature);

82
common/worker.c Normal file
View File

@ -0,0 +1,82 @@
#include "worker.h"
static bool s_workerInitialized = 0;
static thrd_t s_workerThread;
static cnd_t s_workerCdn;
static mtx_t s_workerMtx;
static volatile struct
{
workerThreadFunc func;
void* data;
bool exit;
} s_workerParam;
static int workerThreadProc(void* unused)
{
mtx_lock(&s_workerMtx);
for (;;)
{
cnd_wait(&s_workerCdn, &s_workerMtx);
if (s_workerParam.exit)
break;
s_workerParam.func(s_workerParam.data);
}
mtx_unlock(&s_workerMtx);
return 0;
}
bool workerInit(void)
{
if (s_workerInitialized) return 1;
if (cnd_init(&s_workerCdn) != thrd_success) return 0;
if (mtx_init(&s_workerMtx, mtx_plain) != thrd_success) {
cnd_destroy(&s_workerCdn);
return 0;
}
if (thrd_create(&s_workerThread, workerThreadProc, 0) != thrd_success) {
mtx_destroy(&s_workerMtx);
cnd_destroy(&s_workerCdn);
return 0;
}
s_workerInitialized = 1;
return 1;
}
void workerExit(void)
{
int res=0;
if (!s_workerInitialized) return;
s_workerInitialized = 0;
mtx_lock(&s_workerMtx);
s_workerParam.exit = true;
cnd_signal(&s_workerCdn);
mtx_unlock(&s_workerMtx);
thrd_join(s_workerThread, &res);
mtx_destroy(&s_workerMtx);
cnd_destroy(&s_workerCdn);
}
void workerSchedule(workerThreadFunc func, void* data)
{
if (!s_workerInitialized) return;
mtx_lock(&s_workerMtx);
s_workerParam.func = func;
s_workerParam.data = data;
cnd_signal(&s_workerCdn);
mtx_unlock(&s_workerMtx);
}

6
common/worker.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "common.h"
bool workerInit(void);
void workerExit(void);
void workerSchedule(workerThreadFunc func, void* data);

BIN
icon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,8 +1,10 @@
#include <inttypes.h>
#include "../common/common.h" #include "../common/common.h"
static char argBuf[ENTRY_ARGBUFSIZE]; static char argBuf[ENTRY_ARGBUFSIZE];
static void init_args(char *dst, size_t dst_maxsize, u32 *in_args, size_t size) static char *init_args(char *dst, size_t dst_maxsize, u32 *in_args, size_t size)
{ {
size_t tmplen; size_t tmplen;
u32 argi; u32 argi;
@ -36,6 +38,7 @@ static void init_args(char *dst, size_t dst_maxsize, u32 *in_args, size_t size)
dst_maxsize--; dst_maxsize--;
} }
} }
return dst;
} }
static bool init(void) static bool init(void)
@ -45,18 +48,47 @@ static bool init(void)
static void deinit(void) static void deinit(void)
{ {
} }
static void launchFile(const char* path, argData_s* args) static void launchFile(const char* path, argData_s* args)
{ {
char msg[256];
/*if (strncmp(path, "sdmc:/",6) == 0) /*if (strncmp(path, "sdmc:/",6) == 0)
path += 5;*/ path += 5;*/
memset(argBuf, 0, sizeof(argBuf)); memset(argBuf, 0, sizeof(argBuf));
uint32_t remote = args->nxlink_host.s_addr;
if (remote) {
char nxlinked[17];
sprintf(nxlinked,"%08" PRIx32 "_NXLINK_",remote);
launchAddArg(args, nxlinked);
}
init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf)); init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf));
Result rc = envSetNextLoad(path, argBuf);
if(R_FAILED(rc)) fatalSimple(rc);//TODO: How should failing be handled? struct stat st;
uiExitLoop();
if (stat(path, &st) == -1) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, textGetString(StrId_NroNotFound), path);
menuCreateMsgBox(780, 300, msg);
menuScan(".");
}
else {
Result rc = envSetNextLoad(path, argBuf);
if(R_FAILED(rc)) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_AppLaunchError), R_MODULE(rc), R_DESCRIPTION(rc));
menuCreateMsgBox(780, 300, msg);
}
else {
uiExitLoop();
}
}
} }
const loaderFuncs_s loader_builtin = const loaderFuncs_s loader_builtin =

View File

@ -1,118 +1,256 @@
#include <switch.h> #include <switch.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <physfs.h>
#include "../common/common.h" #include "../common/common.h"
#include "nx_graphics.h"
#include "nx_touch.h"
// Define the desired framebuffer resolution (here we set it to 720p).
#define FB_WIDTH 1280
#define FB_HEIGHT 720
uint8_t* g_framebuf; uint8_t* g_framebuf;
u32 g_framebuf_width; u32 g_framebuf_width;
PadState g_pad;
PadRepeater g_pad_repeater;
bool menuUpdateErrorScreen(void);
#ifdef PERF_LOG #ifdef PERF_LOG
u64 g_tickdiff_vsync=0;
u64 g_tickdiff_frame=0; u64 g_tickdiff_frame=0;
#endif #endif
#ifdef ENABLE_AUDIO
void audio_initialize(void);
void audio_exit(void);
#endif
extern u32 __nx_applet_exit_mode;
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
bool error_screen=0;
Result lastret=0;
Result rc=0;
char msg[256];
char errormsg[256];//Can't use StrId for these error messages since it would be unavailable if textInit fails.
#ifdef PERF_LOG #ifdef PERF_LOG
u64 start_tick=0; u64 start_tick=0;
#endif #endif
gfxInitDefault(); padConfigureInput(8, HidNpadStyleSet_NpadStandard);
padInitializeAny(&g_pad);
padRepeaterInitialize(&g_pad_repeater, 20, 10);
hidSetNpadHandheldActivationMode(HidNpadHandheldActivationMode_Single);
touchInit();
appletSetScreenShotPermission(1); memset(errormsg, 0, sizeof(errormsg));
ColorSetId theme; appletLockExit();
setsysInitialize(); appletSetScreenShotPermission(AppletScreenShotPermission_Enable);
setsysGetColorSetId(&theme);
themeStartup((ThemePreset)theme);
textInit();
menuStartup();
launchInit(); ColorSetId theme = ColorSetId_Light;
rc = setsysInitialize();
if (R_SUCCEEDED(rc)) {
setsysGetColorSetId(&theme);
setsysExit();
}
#ifdef PERF_LOG if (R_SUCCEEDED(rc)) {
gfxWaitForVsync(); rc = textInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: textInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
start_tick = svcGetSystemTick(); if (R_SUCCEEDED(rc)) menuStartupPath();
gfxWaitForVsync();
g_tickdiff_vsync = svcGetSystemTick() - start_tick; if (R_SUCCEEDED(rc)) {
if (!PHYSFS_init(argv[0])) {
rc = 1;
snprintf(errormsg, sizeof(errormsg)-1, "Error: PHYSFS_init() failed: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
}
}
if (R_SUCCEEDED(rc)) {
rc = assetsInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: assetsInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
if (R_SUCCEEDED(rc)) themeStartup((ThemePreset)theme);
if (R_SUCCEEDED(rc)) powerInit();
if (R_SUCCEEDED(rc)) {
rc = netloaderInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
if (R_SUCCEEDED(rc) && !workerInit()) {
rc = 1;
snprintf(errormsg, sizeof(errormsg)-1, "Error: workerInit() failed.");
}
if (R_SUCCEEDED(rc) && !statusInit()) {
rc = 1;
snprintf(errormsg, sizeof(errormsg)-1, "Error: statusInit() failed.");
}
if (R_SUCCEEDED(rc)) menuStartup();
if (R_SUCCEEDED(rc)) {
if (!launchInit()) {
rc = 2;
snprintf(errormsg, sizeof(errormsg)-1, "Error: launchInit() failed.");
}
}
if (R_SUCCEEDED(rc) && !fontInitialize()) {
rc = 3;
snprintf(errormsg, sizeof(errormsg)-1, "Error: fontInitialize() failed.");
}
#ifdef ENABLE_AUDIO
if (R_SUCCEEDED(rc)) audio_initialize();
#endif #endif
if (R_SUCCEEDED(rc)) {
lastret = envGetLastLoadResult();
if (R_FAILED(lastret)) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_LastLoadResult), R_MODULE(lastret), R_DESCRIPTION(lastret));
menuCreateMsgBox(780, 300, msg);
}
}
if (errormsg[0]) error_screen = 1;
if (!error_screen) {
graphicsInit(FB_WIDTH, FB_HEIGHT);
}
else {
consoleInit(NULL);
printf("%s\n", errormsg);
printf("Press the + button to exit.\n");
}
while (appletMainLoop()) while (appletMainLoop())
{ {
#ifdef PERF_LOG // Scan the gamepad. This should be done once for each frame
start_tick = svcGetSystemTick(); padUpdate(&g_pad);
#endif padRepeaterUpdate(&g_pad_repeater, padGetButtons(&g_pad) & (
HidNpadButton_AnyLeft | HidNpadButton_AnyUp | HidNpadButton_AnyRight | HidNpadButton_AnyDown
));
//Scan all the inputs. This should be done once for each frame if (!error_screen) {
hidScanInput(); if (!uiUpdate()) break;
g_framebuf = graphicsFrameBegin(&g_framebuf_width);
#ifdef PERF_LOG
start_tick = armGetSystemTick();
#endif
memset(g_framebuf, 237, g_framebuf_width * FB_HEIGHT);
menuLoop();
}
else {
if (menuUpdateErrorScreen()) break;
}
g_framebuf = gfxGetFramebuffer(&g_framebuf_width, NULL); if (!error_screen) {
memset(g_framebuf, 237, gfxGetFramebufferSize()); graphicsFrameEnd();
if (!uiUpdate()) break;
menuLoop();
gfxFlushBuffers(); #ifdef PERF_LOG
g_tickdiff_frame = armGetSystemTick() - start_tick;
#ifdef PERF_LOG #endif
g_tickdiff_frame = svcGetSystemTick() - start_tick; }
#endif else {
consoleUpdate(NULL);
gfxSwapBuffers(); }
gfxWaitForVsync();
} }
launchExit(); if (!error_screen) {
setsysExit(); graphicsExit();
}
else {
consoleExit(NULL);
__nx_applet_exit_mode = 1;
}
#ifdef ENABLE_AUDIO
audio_exit();
#endif
fontExit();
launchExit();
netloaderSignalExit();
statusExit();
workerExit();
netloaderExit();
powerExit();
assetsExit();
PHYSFS_deinit();
appletUnlockExit();
gfxExit();
return 0; return 0;
} }
void launchMenuEntryTask(menuEntry_s* arg); u64 menuGetKeysDown(void) {
u64 keys = padGetButtonsDown(&g_pad);
keys |= padRepeaterGetButtons(&g_pad_repeater);
return keys;
}
//This is implemented here due to the hid code. //This is implemented here due to the hid code.
bool menuUpdate(void) { bool menuUpdate(void) {
bool exitflag = 0; bool exitflag = 0;
menu_s* menu = menuGetCurrent(); menu_s* menu = menuGetCurrent();
u32 down = hidKeysDown(CONTROLLER_P1_AUTO); u64 down = menuGetKeysDown();
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
if (down & KEY_A) handleTouch(menu);
if (down & HidNpadButton_Y)
{ {
if (menu->nEntries > 0) launchMenuNetloaderTask();
{
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
launchMenuEntryTask(me);
//workerSchedule(launchMenuEntryTask, me);
}
} }
else if (down & KEY_B) else if (down & HidNpadButton_X)
{ {
if (strcmp( menu->dirname, "sdmc:/") != 0) menuHandleXButton();
{
//workerSchedule(changeDirTask, "..");
menuScan("..");
}
} }
else if (down & KEY_PLUS) else if (down & HidNpadButton_A)
{
menuHandleAButton();
}
else if (down & HidNpadButton_B)
{
launchMenuBackTask();
}
else if(down & HidNpadButton_Minus){
themeMenuStartup();
}
else if (down & HidNpadButton_Plus)
{ {
exitflag = 1; exitflag = 1;
} }
/*else if (down & KEY_Y)
{
workerSchedule(netloaderTask, NULL);
}*/
else if (menu->nEntries > 0) else if (menu->nEntries > 0)
{ {
int move = 0; int move = 0;
if (down & KEY_LEFT) move--; if (down & HidNpadButton_AnyLeft) move--;
if (down & KEY_RIGHT) move++; if (down & HidNpadButton_AnyRight) move++;
if (down & KEY_DOWN) move-=7; if (down & HidNpadButton_AnyDown) move-=entries_count;
if (down & KEY_UP) move+=7; if (down & HidNpadButton_AnyUp) move+=entries_count;
int newEntry = menu->curEntry + move; int newEntry = menu->curEntry + move;
if (newEntry < 0) newEntry = 0; if (newEntry < 0) newEntry = 0;
@ -122,3 +260,15 @@ bool menuUpdate(void) {
return exitflag; return exitflag;
} }
bool menuUpdateErrorScreen(void) {
bool exitflag = 0;
u64 down = menuGetKeysDown();
if (down & HidNpadButton_Plus)
{
exitflag = 1;
}
return exitflag;
}

166
nx_main/nx_audio.c Normal file
View File

@ -0,0 +1,166 @@
#include <switch.h>
#include <string.h>
#include <stdio.h>
#include "../common/common.h"
#ifdef ENABLE_AUDIO
#error "Audio is not supported currently."
#define SAMPLERATE 48000
#define BYTESPERSAMPLE 2
#define CHANNELCOUNT 2
static u8* raw_data, *raw_data2;
static u8 *audio_data;
static size_t audio_data_size;
static size_t audio_data_loopoffset;
static AudioOutBuffer source_buffer[2];
static Thread audio_thread;
static bool audio_thread_exitflag = 0;
static bool audio_thread_started = 0;
static void audio_playback_thread(void* arg)
{
bool playing = 0;
bool data_ready=0;
int j, count;
int bufi=0;
u64 offset=0;
u64 tmpsize=0;
u64 totalsize = audio_data_size;
AudioOutBuffer *released_buffer = NULL;
AudioOutBuffer *src_buf = NULL;
u32 released_count=0;
while (!audio_thread_exitflag)
{
if (!playing)
{
count = 2;
if (data_ready && released_count<2) count = 1;
for (j=0; j<count; j++)
{
tmpsize = source_buffer[0].buffer_size;
if (tmpsize > totalsize - offset) tmpsize = totalsize - offset;
if (!data_ready || released_count==2) {
src_buf = &source_buffer[bufi];
}
else {
src_buf = released_buffer;
}
src_buf->data_size = tmpsize;
memcpy(src_buf->buffer, &audio_data[offset], tmpsize);
offset+= tmpsize;
if (offset >= totalsize) offset = audio_data_loopoffset;
audoutAppendAudioOutBuffer(src_buf);
bufi = 1-bufi;
}
if (!data_ready) data_ready = 1;
playing = 1;
}
if (R_SUCCEEDED(audoutWaitPlayFinish(&released_buffer, &released_count, UINT64_MAX)))
playing = 0;
}
}
void audio_initialize(void)
{
Result rc=0;
u8 *audio_intro, *audio_loop;
size_t audio_intro_size, audio_loop_size;
audio_intro = (u8*)audio_intro_bin;
audio_intro_size = audio_intro_bin_size;
audio_loop = (u8*)audio_loop_bin;
audio_loop_size = audio_loop_bin_size;
audio_data_loopoffset = audio_intro_size;
audio_data_size = audio_intro_size + audio_loop_size;
u32 SAMPLESPERBUF = SAMPLERATE/4;
u32 raw_data_size = (SAMPLESPERBUF * CHANNELCOUNT * BYTESPERSAMPLE);
u32 raw_data_size_aligned = (raw_data_size + 0xfff) & ~0xfff;
audio_data = (u8*)malloc(audio_data_size);
raw_data = (u8*)memalign(0x1000, raw_data_size_aligned);
raw_data2 = (u8*)memalign(0x1000, raw_data_size_aligned);
if (audio_data==NULL || raw_data == NULL || raw_data2==NULL) {
free(audio_data);//free() checks NULL.
free(raw_data);
free(raw_data2);
audio_data = NULL;
raw_data = NULL;
raw_data2 = NULL;
return;
}
memset(audio_data, 0, audio_data_size);
memset(raw_data, 0, raw_data_size_aligned);
memset(raw_data2, 0, raw_data_size_aligned);
memcpy(audio_data, audio_intro, audio_intro_size);
memcpy(&audio_data[audio_data_loopoffset], audio_loop, audio_loop_size);
source_buffer[0].next = 0;
source_buffer[0].buffer = raw_data;
source_buffer[0].buffer_size = raw_data_size;
source_buffer[0].data_size = raw_data_size;
source_buffer[0].data_offset = 0;
memcpy(&source_buffer[1], &source_buffer[0], sizeof(AudioOutBuffer));
source_buffer[1].buffer = raw_data2;
if (R_SUCCEEDED(rc)) rc = audoutInitialize();
if (R_SUCCEEDED(rc)) rc = audoutStartAudioOut();
audio_thread_started = 0;
if (R_SUCCEEDED(rc)) rc = threadCreate(&audio_thread, audio_playback_thread, 0, 0x4000, 28, -2);
if (R_SUCCEEDED(rc)) rc = threadStart(&audio_thread);
if (R_SUCCEEDED(rc)) audio_thread_started = 1;
}
void audio_exit(void)
{
if (audio_thread_started) {
audio_thread_exitflag = 1;
threadWaitForExit(&audio_thread);
threadClose(&audio_thread);
}
audoutStopAudioOut();
audoutExit();
free(audio_data);
free(raw_data);
free(raw_data2);
audio_data = NULL;
raw_data = NULL;
raw_data2 = NULL;
}
#endif

141
nx_main/nx_graphics.c Normal file
View File

@ -0,0 +1,141 @@
#include <switch.h>
#include <deko3d.h>
#include "nx_graphics.h"
#define FB_NUM 2
#define CMDMEMSIZE 0x1000
static u32 s_fbWidth, s_fbHeight;
static DkDevice s_device;
static DkMemBlock s_fbMemBlock, s_workMemBlock, s_cmdMemBlock;
static DkSwapchain s_swapchain;
static DkCmdBuf s_cmdBuf;
static DkCmdList s_cmdLists[FB_NUM];
static DkFence s_fence;
static DkQueue s_queue;
void graphicsInit(u32 width, u32 height)
{
DkImageLayoutMaker imgLayoutMaker;
DkMemBlockMaker memBlockMaker;
// Create the device, which is the root object
DkDeviceMaker deviceMaker;
dkDeviceMakerDefaults(&deviceMaker);
s_device = dkDeviceCreate(&deviceMaker);
// Calculate layout for the framebuffers
DkImageLayout fbLayout;
dkImageLayoutMakerDefaults(&imgLayoutMaker, s_device);
imgLayoutMaker.flags = DkImageFlags_UsagePresent;
imgLayoutMaker.format = DkImageFormat_RGBA8_Unorm;
imgLayoutMaker.dimensions[0] = s_fbWidth = width;
imgLayoutMaker.dimensions[1] = s_fbHeight = height;
dkImageLayoutInitialize(&fbLayout, &imgLayoutMaker);
// Retrieve necessary size and alignment for the framebuffers
uint32_t fbSize = dkImageLayoutGetSize(&fbLayout);
uint32_t fbAlign = dkImageLayoutGetAlignment(&fbLayout);
fbSize = (fbSize + fbAlign - 1) &~ (fbAlign - 1);
// Create a memory block that will host the framebuffers
dkMemBlockMakerDefaults(&memBlockMaker, s_device, FB_NUM*fbSize);
memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image;
s_fbMemBlock = dkMemBlockCreate(&memBlockMaker);
// Initialize the framebuffers with the layout and backing memory we've just created
DkImage fbImages[FB_NUM];
DkImage const* swapchainImages[FB_NUM];
for (unsigned i = 0; i < FB_NUM; i ++)
{
swapchainImages[i] = &fbImages[i];
dkImageInitialize(&fbImages[i], &fbLayout, s_fbMemBlock, i*fbSize);
}
// Create a swapchain out of the framebuffers we've just initialized
DkSwapchainMaker swapchainMaker;
dkSwapchainMakerDefaults(&swapchainMaker, s_device, nwindowGetDefault(), swapchainImages, FB_NUM);
s_swapchain = dkSwapchainCreate(&swapchainMaker);
// Create a memory block for the linear framebuffer
dkMemBlockMakerDefaults(&memBlockMaker, s_device, width*height*4);
memBlockMaker.flags = DkMemBlockFlags_CpuCached | DkMemBlockFlags_GpuUncached;
s_workMemBlock = dkMemBlockCreate(&memBlockMaker);
// Create a memory block for the command lists
dkMemBlockMakerDefaults(&memBlockMaker, s_device, CMDMEMSIZE);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
s_cmdMemBlock = dkMemBlockCreate(&memBlockMaker);
// Create a command buffer
DkCmdBufMaker cmdBufMaker;
dkCmdBufMakerDefaults(&cmdBufMaker, s_device);
s_cmdBuf = dkCmdBufCreate(&cmdBufMaker);
dkCmdBufAddMemory(s_cmdBuf, s_cmdMemBlock, 0, CMDMEMSIZE);
// Define source for linear framebuffer copies
const DkCopyBuf linearSrc = {
.addr = dkMemBlockGetGpuAddr(s_workMemBlock),
.rowLength = 0,
.imageHeight = 0,
};
// Define rectangle for the copies
const DkImageRect copyRect = {
.x = 0, .y = 0, .z = 0,
.width = width, .height = height, .depth = 1,
};
// Record command lists for the copies
for (unsigned i = 0; i < FB_NUM; i ++) {
DkImageView tiledDst;
dkImageViewDefaults(&tiledDst, &fbImages[i]);
dkCmdBufCopyBufferToImage(s_cmdBuf, &linearSrc, &tiledDst, &copyRect, 0);
dkCmdBufSignalFence(s_cmdBuf, &s_fence, false);
s_cmdLists[i] = dkCmdBufFinishList(s_cmdBuf);
}
// Create a queue, to which we will submit our command lists
DkQueueMaker queueMaker;
dkQueueMakerDefaults(&queueMaker, s_device);
queueMaker.flags = 0; // we will only use this queue for transferring
s_queue = dkQueueCreate(&queueMaker);
}
void graphicsExit(void)
{
// Make sure the queue is idle before destroying anything
dkQueueWaitIdle(s_queue);
// Destroy all the resources we've created
dkQueueDestroy(s_queue);
dkCmdBufDestroy(s_cmdBuf);
dkMemBlockDestroy(s_cmdMemBlock);
dkMemBlockDestroy(s_workMemBlock);
dkSwapchainDestroy(s_swapchain);
dkMemBlockDestroy(s_fbMemBlock);
dkDeviceDestroy(s_device);
}
void* graphicsFrameBegin(u32* out_stride)
{
// Ensure the GPU is not reading from the framebuffer
dkFenceWait(&s_fence, -1);
// Return information
if (out_stride) *out_stride = s_fbWidth*4;
return dkMemBlockGetCpuAddr(s_workMemBlock);
}
void graphicsFrameEnd(void)
{
// Flush the linear framebuffer
dkMemBlockFlushCpuCache(s_workMemBlock, 0, s_fbWidth*s_fbHeight*4);
// Present a frame
int slot = dkQueueAcquireImage(s_queue, s_swapchain);
dkQueueSubmitCommands(s_queue, s_cmdLists[slot]);
dkQueuePresentImage(s_queue, s_swapchain, slot);
}

9
nx_main/nx_graphics.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <switch.h>
void graphicsInit(u32 width, u32 height);
void graphicsExit(void);
void* graphicsFrameBegin(u32* out_stride);
void graphicsFrameEnd(void);

View File

@ -2,25 +2,27 @@
static const loaderFuncs_s* s_loader; static const loaderFuncs_s* s_loader;
void launchInit(void) { bool launchInit(void) {
#define ADD_LOADER(_name) do \ #define ADD_LOADER(_name) do \
{ \ { \
extern const loaderFuncs_s _name; \ extern const loaderFuncs_s _name; \
if (_name.init()) \ if (_name.init()) \
{ \ { \
s_loader = &_name; \ s_loader = &_name; \
return; \ return 1; \
} \ } \
} while(0) } while(0)
ADD_LOADER(loader_builtin); ADD_LOADER(loader_builtin);
// Shouldn't happen // Shouldn't happen
fatalSimple(-1);//TODO: What value should be used for this? s_loader = NULL;
return 0;
} }
void launchExit(void) { void launchExit(void) {
s_loader->deinit(); if (s_loader) s_loader->deinit();
s_loader = NULL;
} }
const loaderFuncs_s* launchGetLoader(void) { const loaderFuncs_s* launchGetLoader(void) {
@ -71,5 +73,6 @@ void launchMenuEntry(menuEntry_s* me) {
descriptorScanFile(&me->descriptor, me->path);*/ descriptorScanFile(&me->descriptor, me->path);*/
// Launch it // Launch it
if (s_loader == NULL) return;
s_loader->launchFile(me->path, &me->args); s_loader->launchFile(me->path, &me->args);
} }

36
nx_main/nx_netstatus.c Normal file
View File

@ -0,0 +1,36 @@
#include "../common/common.h"
bool netstatusGetDetails(AssetId *id) {
Result rc=0;
NifmInternetConnectionType contype;
u32 wifiStrength=0;
NifmInternetConnectionStatus connectionStatus;
rc = nifmGetInternetConnectionStatus(&contype, &wifiStrength, &connectionStatus);
if (R_FAILED(rc)) {
*id = AssetId_airplane_icon;
return true;
}
if (contype == NifmInternetConnectionType_Ethernet) {
if (connectionStatus != NifmInternetConnectionStatus_Connected)
*id = AssetId_eth_none_icon;
else
*id = AssetId_eth_icon;
return true;
}
if (wifiStrength==0) {
*id = AssetId_wifi_none_icon;
return true;
}
if (wifiStrength==3)
*id = AssetId_wifi3_icon;
if (wifiStrength==2)
*id = AssetId_wifi2_icon;
else
*id = AssetId_wifi1_icon;
return true;
}

74
nx_main/nx_power.c Normal file
View File

@ -0,0 +1,74 @@
#include <switch.h>
#include "../common/common.h"
static bool powerInitialized;
static bool powerCacheInitialized;
static uint32_t powerCacheCharge;
static bool powerCacheIsCharging;
static PsmSession powerSession;
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
PsmChargerType charger = PsmChargerType_Unconnected;
bool hwReadsSucceeded = false;
bool use_cache = false;
Result rc = 0;
*isCharging = false;
*batteryCharge = 0;
if (powerInitialized) {
if (powerCacheInitialized) {
rc = psmWaitStateChangeEvent(&powerSession, 0);
if (R_FAILED(rc)) use_cache = true;
}
rc = psmGetBatteryChargePercentage(batteryCharge);
hwReadsSucceeded = R_SUCCEEDED(rc);
if (use_cache) {
*isCharging = powerCacheIsCharging;
}
else {
rc = psmGetChargerType(&charger);
hwReadsSucceeded &= R_SUCCEEDED(rc);
*isCharging = (charger != PsmChargerType_Unconnected);
}
powerCacheCharge = *batteryCharge;
powerCacheIsCharging = *isCharging;
powerCacheInitialized = true;
}
return hwReadsSucceeded;
}
void powerInit(void) {
uint32_t charge=0;
bool isCharging=0;
powerCacheInitialized = false;
powerCacheCharge = 0;
powerCacheIsCharging = false;
if (!powerInitialized) {
Result rc = psmInitialize();
if (R_SUCCEEDED(rc)) {
rc = psmBindStateChangeEvent(&powerSession, 1, 1, 1);
if (R_FAILED(rc)) psmExit();
if (R_SUCCEEDED(rc)) {
powerInitialized = true;
powerGetDetails(&charge, &isCharging);//Init the cache.
}
}
}
}
void powerExit(void) {
if (powerInitialized) {
psmUnbindStateChangeEvent(&powerSession);
psmExit();
powerInitialized = false;
powerCacheInitialized = false;
}
}

View File

@ -0,0 +1,14 @@
#include "../common/common.h"
bool thermalstatusInit(void) {
return R_SUCCEEDED(tsInitialize());
}
void thermalstatusExit(void) {
tsExit();
}
bool thermalstatusGetDetails(s32 *temperature) {
return R_SUCCEEDED(tsGetTemperature(TsLocation_Internal, temperature));
}

168
nx_main/nx_touch.c Normal file
View File

@ -0,0 +1,168 @@
#include "nx_touch.h"
#define TAP_MOVEMENT_GAP 20
#define VERTICAL_SWIPE_HORIZONTAL_PLAY 250
#define VERTICAL_SWIPE_MINIMUM_DISTANCE 300
#define HORIZONTAL_SWIPE_VERTICAL_PLAY 250
#define HORIZONTAL_SWIPE_MINIMUM_DISTANCE 300
#define distance(x1, y1, x2, y2) (int) sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
struct touchInfo_s touchInfo;
void touchInit(void) {
touchInfo.gestureInProgress = false;
touchInfo.isTap = true;
touchInfo.initMenuXPos = 0;
touchInfo.initMenuIndex = 0;
touchInfo.lastSlideSpeed = 0;
hidInitializeTouchScreen();
}
void handleTappingOnApp(menu_s* menu, int px) {
int i = 0;
menuEntry_s *me = NULL;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) {
int entry_start_x = layoutobj->posStart[0] + i * layoutobj->posEnd[0];
int screen_width = 1280;
if (entry_start_x >= (screen_width - menu->xPos))
break;
if (px >= (entry_start_x + menu->xPos) && px <= (entry_start_x + menu->xPos) + layoutobj->size[0]) {
launchMenuEntryTask(me);
break;
}
}
}
void handleTappingOnOpenLaunch(menu_s* menu) {
if (menu->nEntries > 0)
{
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
launchMenuEntryTask(me);
}
}
static inline bool checkInsideTextLayoutObject(ThemeLayoutId id, int x, int y) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[id];
if (!layoutobj->visible) return false;
return x > layoutobj->posFinal[0] && x < layoutobj->posFinal[0]+(layoutobj->touchSize[0] ? layoutobj->touchSize[0] : layoutobj->textSize[0]) && y > layoutobj->posFinal[1]-layoutobj->touchSize[1] && y < layoutobj->posFinal[1];
}
void handleTouch(menu_s* menu) {
ThemeLayoutObject *layoutobj = NULL;
HidTouchScreenState touch = {0};
hidGetTouchScreenStates(&touch, 1);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
// On touch start.
if (touch.count == 1 && !touchInfo.gestureInProgress) {
touchInfo.gestureInProgress = true;
touchInfo.firstTouch = touch.touches[0];
touchInfo.prevTouch = touch.touches[0];
touchInfo.isTap = true;
touchInfo.initMenuXPos = menu->xPos;
touchInfo.initMenuIndex = menu->curEntry;
touchInfo.lastSlideSpeed = 0;
menu->slideSpeed = 0;
}
// On touch moving.
else if (touch.count >= 1 && touchInfo.gestureInProgress) {
touchInfo.lastSlideSpeed = ((int)(touch.touches[0].x - touchInfo.prevTouch.x));
touchInfo.prevTouch = touch.touches[0];
if (touchInfo.isTap && (abs(touchInfo.firstTouch.x - touch.touches[0].x) > TAP_MOVEMENT_GAP || abs(touchInfo.firstTouch.y - touch.touches[0].y) > TAP_MOVEMENT_GAP)) {
touchInfo.isTap = false;
}
if (!menuIsMsgBoxOpen() && touchInfo.firstTouch.y > layoutobj->posStart[1] && touchInfo.firstTouch.y < layoutobj->posStart[1]+layoutobj->size[1] && !touchInfo.isTap && menu->nEntries > entries_count) {
if (!touchInfo.isTap) {
menu->slideSpeed = touchInfo.lastSlideSpeed;
}
}
}
// On touch end.
else if (touchInfo.gestureInProgress) {
int x1 = touchInfo.firstTouch.x;
int y1 = touchInfo.firstTouch.y;
int x2 = touchInfo.prevTouch.x;
int y2 = touchInfo.prevTouch.y;
if (!touchInfo.isTap) {
menu->slideSpeed = touchInfo.lastSlideSpeed;
}
bool netloader_active = menuIsNetloaderActive();
if (menuIsMsgBoxOpen() && !netloader_active) {
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
MessageBox currMsgBox = menuGetCurrentMsgBox();
int start_x = 1280 / 2 - currMsgBox.width / 2;
int start_y = (720 / 2 - currMsgBox.height / 2) + currMsgBox.height;
int end_x = start_x + currMsgBox.width;
int end_y = start_y;
start_y+= layoutobj->posStart[1];
if (x1 > start_x && x1 < end_x && y1 > start_y && y1 < end_y && touchInfo.isTap) {
menuCloseMsgBox();
}
} else if (touchInfo.isTap && !netloader_active) {
// App Icons
if (y1 > layoutobj->posStart[1] && y1 < layoutobj->posStart[1]+layoutobj->size[1]) {
handleTappingOnApp(menu, touchInfo.prevTouch.x);
}
// Bottom Buttons
else {
// Back Button
if (checkInsideTextLayoutObject(ThemeLayoutId_ButtonB, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonBText, x1, y1)) {
launchMenuBackTask();
}
// Open/Launch Button
else if (menu->nEntries != 0 && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonA, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonAText, x1, y1))) {
handleTappingOnOpenLaunch(menu);
}
// Star Button
else if (menu->nEntries != 0) {
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
if (me->type != ENTRY_TYPE_THEME && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonX, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonXText, x1, y1))) {
menuHandleXButton();
}
}
}
}
// Vertical Swipe
else if (abs(x1 - x2) < VERTICAL_SWIPE_HORIZONTAL_PLAY && distance(x1, y1, x2, y2) > VERTICAL_SWIPE_MINIMUM_DISTANCE) {
// Swipe up to go back
if (y1 - y2 > 0) {
launchMenuBackTask();
}
// Swipe down to go into netloader
else if (y1 - y2 < 0) {
launchMenuNetloaderTask();
}
}
// Horizontal Swipe
else if (y1 < layoutobj->posStart[1] && y2 < layoutobj->posStart[1]) {
if (abs(y1 - y2) < HORIZONTAL_SWIPE_VERTICAL_PLAY && distance(x1, y1, x2, y2) > HORIZONTAL_SWIPE_MINIMUM_DISTANCE) {
// Swipe left to go into theme-menu
if (x1 - x2 > 0) {
themeMenuStartup();
}
}
}
touchInfo.gestureInProgress = false;
}
}

17
nx_main/nx_touch.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <switch.h>
#include "../common/common.h"
struct touchInfo_s {
bool gestureInProgress;
HidTouchState firstTouch;
HidTouchState prevTouch;
bool isTap;
int initMenuXPos;
int initMenuIndex;
int lastSlideSpeed;
};
void touchInit(void);
void handleTouch(menu_s* menu);

View File

@ -1,6 +1,7 @@
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <physfs.h>
extern "C" { extern "C" {
@ -10,13 +11,20 @@ extern "C" {
color_t pixels[720][1280]; color_t pixels[720][1280];
int main() int main(int argc, char **argv)
{ {
sf::RenderWindow window(sf::VideoMode(1280, 720), "Test"); sf::RenderWindow window(sf::VideoMode(1280, 720), "Test");
window.setFramerateLimit(60); window.setFramerateLimit(60);
menuStartupPath();
PHYSFS_init(argv[0]);
assetsInit();
themeStartup(THEME_PRESET_LIGHT); themeStartup(THEME_PRESET_LIGHT);
textInit(); textInit();
fontInitialize();
netloaderInit();
workerInit();
statusInit();
menuStartup(); menuStartup();
while (window.isOpen()) while (window.isOpen())
@ -50,11 +58,17 @@ int main()
window.display(); window.display();
} }
netloaderSignalExit();
statusExit();
workerExit();
netloaderExit();
fontExit();
assetsExit();
PHYSFS_deinit();
return 0; return 0;
} }
extern "C" void launchMenuEntryTask(menuEntry_s* arg);
extern "C" bool menuUpdate(void) { extern "C" bool menuUpdate(void) {
//This is implemented here due to the hid code. //This is implemented here due to the hid code.
menu_s* menu = menuGetCurrent(); menu_s* menu = menuGetCurrent();
@ -63,21 +77,33 @@ extern "C" bool menuUpdate(void) {
int new_esc_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Escape); int new_esc_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Escape);
static int return_state = 0; static int return_state = 0;
int new_return_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Return); int new_return_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Return);
static int x_state = 0;
int new_x_state = sf::Keyboard::isKeyPressed(sf::Keyboard::X);
static int y_state = 0;
int new_y_state = sf::Keyboard::isKeyPressed(sf::Keyboard::Y);
static int t_state = 0;
int new_t_state = sf::Keyboard::isKeyPressed(sf::Keyboard::T);
if(!new_y_state && y_state)
{
launchMenuNetloaderTask();
}
if(!new_x_state && x_state)
{
menuHandleXButton();
}
if (!new_esc_state && esc_state) if (!new_esc_state && esc_state)
{ {
menuScan(".."); launchMenuBackTask();
}
else if(!new_t_state && t_state){
themeMenuStartup();
} }
else if (!new_return_state && return_state) else if (!new_return_state && return_state)
{ {
if (menu->nEntries > 0) menuHandleAButton();
{
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
launchMenuEntryTask(me);
//workerSchedule(launchMenuEntryTask, me);
}
} }
else if (menu->nEntries > 0) else if (menu->nEntries > 0)
{ {
@ -103,6 +129,8 @@ extern "C" bool menuUpdate(void) {
esc_state = new_esc_state; esc_state = new_esc_state;
return_state = new_return_state; return_state = new_return_state;
y_state = new_y_state;
t_state = new_t_state;
return 0; return 0;
} }

View File

@ -2,8 +2,8 @@
static const loaderFuncs_s* s_loader; static const loaderFuncs_s* s_loader;
void launchInit(void) { bool launchInit(void) {
return 1;
} }
void launchExit(void) { void launchExit(void) {

6
pc_main/pc_netstatus.c Normal file
View File

@ -0,0 +1,6 @@
#include "../common/common.h"
bool netstatusGetDetails(AssetId *id) {
*id = AssetId_wifi3_icon;
return true;
}

15
pc_main/pc_power.c Normal file
View File

@ -0,0 +1,15 @@
#include "../common/common.h"
void powerInit(void) {
}
void powerExit(void) {
}
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
*isCharging = false;
*batteryCharge = 100;
return false;
}

View File

@ -0,0 +1,15 @@
#include "../common/common.h"
bool thermalstatusInit(void) {
return false;
}
void thermalstatusExit(void) {
}
bool thermalstatusGetDetails(s32 *temperature) {
*temperature = 0;
return false;
}

BIN
resources/airplane_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
resources/battery_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
resources/charging_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
resources/eth_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

BIN
resources/eth_none_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
resources/wifi1_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
resources/wifi2_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
resources/wifi3_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB