Compare commits

..

141 Commits

Author SHA1 Message Date
Andrea Ciliberti
1b1762f872 Return sockaddr_in length from functions that have in/out addrlen
Previously the socket funtions did not write the sockaddr_in struct fully and instead always returned
the "internal" length, which did not account for the zeroed padding.
2025-01-20 20:42:21 +01:00
Andrea Ciliberti
04b9418dd9 Use macro expansion for maximum sockaddr storage length rather than hardcoded value 2025-01-20 20:42:21 +01:00
kynex7510
30dc65121d Init gpu interrupt queue header 2024-11-20 19:12:01 +01:00
Dave Murphy
8cbcce773b
fix error language, closes #552 (#553) 2024-11-03 20:31:53 +00:00
fincs
a88b5af7a8
libctru v2.4.0 2024-09-17 00:11:09 +02:00
TuxSH
8e55cdf05d Add full support for all QTM services.
This commit adds support for all service commands of all 4 QTM services
(`qtm:u`, `qtm:s`, `qtm:sp` (which are built on top of one another) and
`qtm:c` ("hardware check")), with precise documentation about each command's
behaviour and about I/O internals.

The existing `qtm:c` code in libctru had a lot of misconceptions; this commit is
a full rewrite with breaking changes.

In particular: QTM does *not* track the position of 4 points on the user's face.
Instead, it tracks the position of the user's eyes (but not the direction they are
looking at) and reports their coordinates in camera space and in world space.
The data is presented in a console/hardware-agnostic way to the user.

QTM is a service process responsible for:

- eye tracking (see above). Head tracking data is available even when "super-stable 3D"
  is disabled
- parallax barrier management through the TI TCA6416A I2C->Parallel expander.
  The parallax barrier hardware on N3DS requires that software (in this case, QTM process)
  alternates between writing the barrier's mask pattern & polarity followed by the bit-negation
  of that pattern on polarity continuously, at all times
- automatic barrier management by adjusting it with the results of eye-tracking ("super-stable 3D")
- automatic (and manual) IR LED emitter management so that eye-tracking can work even in the dark
- fowarding camera brightness information from cam:q. GSP uses this for auto-brightness
2024-09-15 21:53:43 +02:00
Nikki
5f06a037d7
Add MCUHWC_SetInfoLedPattern (#549) 2024-09-15 20:45:49 +02:00
Rambo6Glaz
cc5b884d7d
Fix NDMU_QueryStatus (#543) 2024-09-15 18:13:27 +02:00
TuxSH
c5ecf54d51 Fix cmdbuf->bool conversion
Affected code was previously reading 3 uninitialized bytes, making the
conversion return wrong results.
2024-09-07 19:41:47 +02:00
TuxSH
faf5162b60 Fix PTMSYSM_CheckNew3DS, closes #540 2024-03-13 19:24:54 +01:00
TuxSH
a6a6548348 Fix documentation of two functions in ac.h 2024-02-08 00:25:27 +01:00
Kayden Tran
0da8705527
Added ndspChnGetFormat (#538) 2024-01-29 12:04:08 +00:00
fincs
d0936b879b
shaderInstanceInit: properly initialize structure (fix #535) 2023-12-16 16:25:47 +01:00
Dave Murphy
c579a65655
don't pollute system headers with 3ds headers 2023-11-05 11:39:10 +00:00
Dave Murphy
bf4a24a4e7
Changelog for 2.3.0 2023-10-28 20:08:55 +01:00
Chris Feger
bd69c96b7d
Buffer console control sequences (#522) 2023-10-28 19:48:16 +01:00
Gleb Mazovetskiy
41e31d3921
Add CTR_ prefix to ALIGN,PACKED,DEPRECATED macros (#532)
Co-authored-by: fincs <fincs@devkitpro.org>
2023-10-11 21:57:13 +02:00
Tekito_256
687aaf6cfc
FSUSER_GetLegacyBannerData: Fix documentation typo (#526) 2023-10-11 21:29:38 +02:00
Dave Murphy
4de90c3436
remove extra __libc_fini_array 2023-09-25 18:17:34 +01:00
TuxSH
5b2a9f6136 ac: add SSID-related ac:i functions
ACI_LoadNetworkSetting and ACI_GetNetworkWirelessEssidSecuritySsid, plus acGetSessionHandle.
2023-09-16 21:22:37 +02:00
TuxSH
0f098853f2 Add isb and prevent CPU from postponing threadOnException memory writes 2023-09-15 23:15:58 +02:00
oreo639
a4634c0290 Add GPU_DOT3_RGBA texture combiner function 2023-08-12 16:52:12 +02:00
fincs
e9fa000e94
Implement dkA r60 threading syscalls (pthread, std::thread support) 2023-06-24 16:07:06 +02:00
DeltaF1
0cbae436c8
Fix typo in docs (#525)
s/bufger/buffer
2023-05-15 18:19:35 +01:00
Dave Murphy
a9a9d9ab69
libctru v2.2.2 2023-05-08 00:11:49 +01:00
Dave Murphy
6714c04806
adjust struct hostent for compatibility 2023-05-08 00:09:58 +01:00
oreo639
39a53c4fe5
archive_dev: Ensure path separator for local path (#524)
Fixes issue where fopen("test.txt","r") opens 3dstest.txt instead of test.txt.

Also correct misuse of strncat() as count applies to src not dest.

See:
8136d94657
806a4d34c5
2023-05-07 12:08:13 +01:00
Dave Murphy
8d90551306
add define for curl 2023-04-18 12:03:44 +01:00
Dave Murphy
032f3dad40
libctru v2.2.0 2023-04-03 21:39:43 +01:00
TuxSH
b18f04d887 apt: add deliver arg support to chainloader + minor fixes 2023-02-25 20:39:04 +00:00
fincs
98324d412f
libctru v2.1.2 2023-02-09 20:11:27 +01:00
TuxSH
cd78fb05cb Change arg types of svcCreateCodeSet and improve documentation
Since kernel doesn't directly access the LMA, but just does memory
management on them instead, they should be passed as u32 (uptr) instead
of void *.

Also change CodeSetInfo to CodeSetHeader to avoid confusion with
ExHeader types.
2023-02-08 18:17:59 +00:00
TuxSH
a30628058c Implement cdc:CHK service 2023-02-08 00:26:18 +00:00
TuxSH
cb9d682f65 Fix svcGetDmaState
Kernel and official app stubs are buggy, as they read 4 bytes when the
enum is only one byte (meaning 3 garbage bytes).
2023-02-06 17:44:12 +00:00
TuxSH
1de86ea38a Add mcuHwcGetSessionHandle 2023-01-31 17:11:02 +00:00
TuxSH
e253c2c005 errf: implement ERRF_SetUserString & clarify 2023-01-11 23:49:31 +00:00
TuxSH
08b76e2e17 apt: fix dirty homebrew chainload
Fix chainload method used when HM is not launched.

We now wait for custom PM to change our launch flag and ask us to
terminate. We previously had a few issues:
- a potential race condition where we exit before PM changes our
  runflags (not observed)
- a kernel deadlock between ExitProcess and TerminateProcess (observed,
  this is due to Nintendo's lack of barrier in many of their atomics
  code)
- debuggers reporting that we were terminated, instead of exiting
  gracefully (not desirable)
2023-01-09 23:58:32 +00:00
GaryOderNichts
da323fa50b ir: Add IRU_GetSend/RecvFinishedEvent 2022-12-06 12:45:49 +01:00
TurtleP
af5321c78e spaces to tabs 2022-09-25 18:39:09 +02:00
TurtleP
58719f32ac fix ndspGetMasterVol and ndspChnGetMix 2022-09-14 23:52:19 +02:00
TurtleP
ef806d5bd8
add various ndspChnGet* methods (#506) 2022-09-14 19:35:11 +02:00
TurtleP
e6fcbef36c
add various ndspGet* methods (#505) 2022-09-14 19:34:32 +02:00
Ian Chamberlain
b20ac22ab7
Use __tdata_align to align thread local storage (#504) 2022-09-14 19:26:23 +02:00
csnikki
59cf50c201 Include for size_t 2022-08-12 16:45:58 +02:00
ZeroSkill
607f1d1fb6
Fix FSPXI_CreateFile and FSPXI_WriteFile (#496) 2022-06-29 18:19:50 +02:00
Théo B
10cf9bb95a
Mark svcExitProcess as taking no arguments (#499) 2022-06-29 12:37:10 +01:00
Teodor Spæren
813d28ddc4 Implement clock_gettime syscall
This implements the syscalls for `clock_gettime` and `clock_getres`. We
support two clocks: CLOCK_REALTIME and CLOCK_MONOTONIC. I've opted to
use the existing `osGetTime()` code for the realtime clock, because it's
known to work.

For CLOCK_MONOTONIC I've used `svcGetSystemTick()` directly, as it has a
higher resolution. We can ignore the drift and so on, because it's
supposed to be just the number of ticks since last boot.
2022-06-27 20:50:39 +02:00
Théo B
82f821156a
Minor style consistency fix for svcExitProcess (#498) 2022-06-26 17:57:28 +01:00
TuxSH
59e0d7596f Fix PMAPP_TerminateTitle 2022-05-24 19:51:52 +01:00
fincs
d4e08aa2cc
libctru v2.1.1 2022-05-22 16:35:03 +02:00
Dave Murphy
b76a245b79
disable array bounds checking for __tls_start 2022-05-02 15:10:17 +01:00
TuxSH
11686876ac Make sure FPSCR is properly initialized
This is a potentially breaking change as the register was left with an unpredictable/unspecified value.

Config: default NaN mode enabled, flush-to-zero enabled, and round to nearest.
2022-04-12 22:22:56 +01:00
TuxSH
c36d9cc4a6 Provide gspGetSessionHandle and gspLcdGetSessionHandle 2022-04-11 22:55:53 +01:00
TuxSH
cf538b1fa8 Make sure all data is initialized in srv and errf
Prior to system version 11.0, the kernel filled the resulting handle with junk in case of failure, when calling svcConnectToPort, etc.

In some situations libctru could accidentally close valid handles.
2022-04-11 21:15:08 +01:00
Eli Clark
ebb5305188
Fix a typo (NEOPT_IP_INFO comment) (#492) 2022-02-17 09:14:26 +00:00
fincs
4815537048
Add changelog for v2.1.0 2022-01-28 23:46:47 +01:00
Dave Murphy
4d36c74be4 next release is 2.1.0 2022-01-26 22:45:17 +00:00
Michael Theall
8ad7b5a71a Fix huffman decode 2022-01-26 22:41:22 +00:00
Michael Theall
bb9c49b84d Add support for SGR 38 and 48
This supports the escape sequences used by fmtlib
2022-01-26 22:41:22 +00:00
oreo639
2564d708e6 Add 3dslink stdio redirection 2022-01-26 18:16:03 +00:00
Dave Murphy
c83c12357e rework syscalls for devkitARM r57 2022-01-26 18:11:25 +00:00
Pixel-Pop
982be53414 DSP_WriteProcessPipe: Fix documentation typo 2022-01-21 18:08:04 +01:00
LiquidFenrir
5f13628dac add DLC title content-info count and data accessors
Named specially to indicate they require amAppInit instead of amInit
2021-12-21 23:48:33 +01:00
fincs
6360f4bdb1
VRAM allocator enhancements, see details:
- Added proper handling for VRAM banks (A and B, 3 MiB each)
- Allocations no longer cross VRAM bank boundaries
- Added vramAllocAt, vramMemAlignAt
2021-08-26 23:42:34 +02:00
fincs
48967dc417
Use __3DS__ instead of _3DS 2021-08-07 19:13:15 +02:00
Aly Cerruti
1f4669fa4c
Support replacing files in rename for better compatibility with POSIX (#483) 2021-04-16 23:01:40 +02:00
fincs
2a9f86fcbc
Add return value to gspPresentBuffer 2021-04-02 19:22:20 +02:00
fincs
bf06f27a0e
Add gspIsPresentPending 2021-03-31 14:49:07 +02:00
oreo639
4e25fb1d6c Switch to github actions 2021-03-01 03:20:15 +01:00
oreo639
858ef9408c Generate tagfile for use with c3d documentation 2021-03-01 03:20:15 +01:00
oreo639
1fb16e21c2 Correct malformed documentation in enums.h 2021-03-01 03:20:15 +01:00
TuxSH
75b4d1f563
Add SDMMC speed info types, and more clock rates (#480) 2021-02-01 14:05:04 +00:00
TuxSH
4fdc40228a Add ptm:gets, ptm:sets and more ptm service commands 2021-01-12 00:35:24 +01:00
fincs
e996cef24d
libctru v2.0.1 2020-12-19 17:15:52 +01:00
TuxSH
dcf83900b9 gspgpu: fix const correctness issues 2020-12-19 15:48:32 +01:00
fincs
02f516111f
srvpm: Correct the previous sysver check fix 2020-12-08 23:16:29 +01:00
Juan Antonio Sauceda
3cefb61d91 osGetWifiStrength: Fix typo in os.h 2020-08-03 01:30:13 +02:00
Vicki Pfau
d085de370e
cfgu: Add CFG_SystemModel enum (#472) 2020-08-01 11:51:46 +01:00
fincs
4866df79a5
condvar: fix embarrassing typo that prevented it from actually working 2020-07-23 02:45:39 +02:00
fincs
958e0c74ea
Travis is such a dumpster fire 2020-07-16 16:38:47 +02:00
fincs
e70908afc7
Release v2.0.0 2020-07-16 16:12:40 +02:00
fincs
e7b0b82bf6
osGetSystemVersionData: avoid leaking cfgu service on failure 2020-07-16 16:12:12 +02:00
fincs
5872ad60be
errf: use CUR_PROCESS_HANDLE instead of hardcoded number 2020-07-16 16:11:27 +02:00
Nanquitas
c1c93b0880 LightEvent: remove magic numbers 2020-07-15 00:16:16 +02:00
Nanquitas
bc6c097dbb Add LightEvent_WaitTimeout 2020-07-15 00:16:16 +02:00
fincs
3874788e85
gsplcd: It's 2020 and we still have typos in IPC wrapper code 2020-07-14 00:28:40 +02:00
fincs
846b79e5a6
cfgu: Correct CFG_Get/SetConfigInfoBlk2/4/8 parameter type 2020-07-13 21:56:08 +02:00
fincs
bd9e559309
Add gfxScreenSwapBuffers, see details:
- Added the ability to duplicate the left eye image to the right eye
  (allows apps to skip rendering to the right eye framebuffer when
   the 3d slider is set to 0.0)
- Deprecated gfxConfigScreen
2020-07-07 13:06:28 +02:00
fincs
4a9c547d8b
Add CondVar implementation 2020-07-07 12:44:18 +02:00
TuxSH
ef6289f977 Add svcControlPerformanceCounter 2020-07-07 12:43:41 +02:00
TuxSH
1ba8d40069 Add/update DMA svcs 2020-07-06 13:22:18 +02:00
TuxSH
58b30bad06 Reorder svcs and replace some void* params, see details:
- New category: "device drivers" (svc 0x5x)
- Use u32 instead of void* when process handles are required
2020-07-06 13:22:18 +02:00
fincs
577fe60268
archive_dev: Reduce maximum simultaneous opened archives from 32 to 8 in order to conserve memory 2020-07-03 20:24:11 +02:00
fincs
75a7312e40
Add romfsMountFromTitle and refactor os-versionbin.c to use it 2020-07-03 20:20:26 +02:00
fincs
24dded1c79
Remove long-since obsolete hb:HB code 2020-07-03 18:09:14 +02:00
fincs
33a570b1c3
Refactor default heap allocation code, see details:
- Use reslimit svcs to retrieve free mem (suggested by @TuxSH) - this
  removes the hardcoded assumption that the process is in APPLICATION.
- The algorithm for calculating default app/linear heap sizes has been
  tweaked to ensure 32MB of linear heap are available for normal apps
  running on Old3DS consoles under appmemtype 0 (i.e. the default).
- Misc cleanup and error checking.
2020-07-03 18:07:20 +02:00
fincs
da529aed21
romfs_dev: Backport proper multimount system from libnx with some tweaks 2020-07-03 00:24:32 +02:00
fincs
1c7914e2f4
archive/romfs: Reduce TLS usage by sharing buffers + changed archive_rename to use a VLA 2020-07-03 00:24:32 +02:00
fincs
3e1d03aecd
Fix typo in osKernelConfig_s 2020-07-03 00:23:42 +02:00
fincs
83e1f97b5b
Remove superfluous casts in FS_Path usage 2020-07-02 16:34:37 +02:00
fincs
818931b51c
Remove unmaintained cppcheck files 2020-07-02 16:32:58 +02:00
fincs
976d284f3a
Use MemPerm enum instead of magic numbers in svcMapMemoryBlock calls 2020-07-02 00:13:55 +02:00
fincs
4e387679f7
Add structs for kernel/shared pages + related refactor, see details:
- Added osKernelConfig_s/osSharedConfig_s structs
- Added OS_KernelConfig/OS_SharedConfig macros for accessing the pages
- Replaced 0x1FF8nnnn hardcoded addresses with struct accesses
- Added osIsHeadsetConnected function
- Refactored and rewritten RTC time support with the correct algorithm
  (many thanks to @TuxSH for reverse engineering the PTM sysmodule):
  - Fully added and documented the osTimeRef_s structure
    - This replaces the previous internal datetime_t struct type
  - Added osGetTimeRef function:
    - This replaces the previous internal getSysTime function
    - Added missing barrier to the lock free reading algorithm
  - osGetTime rewritten to implement the drift-correction algorithm
    - **This introduces a dependency to __aeabi_ldivmod**
  - __libctru_gtod rewritten to reuse osGetTime
2020-07-01 21:02:22 +02:00
fincs
53cbfc231f
Replace magic numbers with defines for Arm11 userland memory regions; clean up and enhance osConvertVirtToPhys 2020-07-01 20:48:18 +02:00
fincs
dfbfc1564f
os.h: Move MemRegion to svc.h; move osGetMemRegionUsed to header; correct osGetMemRegionUsed/Free return type 2020-07-01 18:11:31 +02:00
fincs
64f29fa024
Fix osStrError (previously it was completely broken...) 2020-07-01 00:21:40 +02:00
fincs
41d0d615f5
ndm: Add NDM_ prefix to enum members in order to avoid name collisions 2020-07-01 00:20:42 +02:00
fincs
9a707b2269
Improve safety of usermode synchronization primitives, see details:
- Added data memory barriers where required to ensure intercore safety
- Changed LightLock to handle 0 during locking instead of silently failing
  (this means trivially initialized/zerofilled LightLocks are now supported)
2020-07-01 00:17:08 +02:00
fincs
19fd446ac5
Add and use svcArbitrateAddressNoTimeout (minor ABI optimization) 2020-06-30 18:45:03 +02:00
piepie62
9974ed1aa3 Add LightSemaphore_TryAcquire and fix Acquire 2020-06-24 00:03:13 +02:00
fincs
8662687a2a
ndsp: Refactor sleep/wakeup code for maintainability/accuracy reasons 2020-06-23 00:36:21 +02:00
fincs
03c41754e8
ndsp: Make wavebuf handling more robust (and fix glaring omissions...) 2020-06-22 22:47:53 +02:00
fincs
2ff08fbac8
synchronization.h: Add __dmb() intrinsic 2020-06-22 22:08:21 +02:00
fincs
b93e7f19bf
Refactor DSP<->APT interaction, see details:
- DSP wrapper is now in charge of handling sleep/awake/cancel events
  - Added DSP hook mechanism (similar to APT hook)
  - The DSP component now gets automatically unloaded/reloaded as needed
- NDSP now uses DSP hook instead of APT hook to manage lifetime
  - Moved sleep/wakeup component management logic into NDSP
- APT now calls into DSP wrapper in order to arbitrate access to the DSP
  - Launching libapplets no longer relinquishes DSP rights - this means
    music can now be played in the background during libapplet activity
2020-06-22 00:34:38 +02:00
Ezekiel Bethel
b559d93eda
fix return value of romfs_stat for non-existent files/directories (#466) 2020-06-18 10:58:39 +01:00
fincs
bfc6ea48fb
gfx: Avoid graphical glitches after deinitialization 2020-06-14 01:20:46 +02:00
fincs
bf55423903
gspgpu: Add gspHasGpuRight 2020-06-14 01:19:13 +02:00
fincs
5ac710a0a2
console: Adjust for wide mode 2020-06-13 22:49:52 +02:00
fincs
d521c99769
gfx: Add support for the top screen's 800px high resolution mode (!!!) 2020-06-13 22:48:36 +02:00
fincs
09d629aa85
gspPresentBuffer: stride is u32 2020-06-13 21:57:09 +02:00
fincs
6ef91576ae
Overhaul gspgpu/gfx code, see details:
- GSPGPU changes:
  - gspInit now properly initializes the event queue, GSP shared memory
    and first-time initialization - previously this was done in gfxInit.
  - Removed gspInitEventHandler/gspExitEventHandler in line with above.
  - Added gspPresentBuffer for pushing a framebuffer to the internal
    GSP swap queue (previously this was an internal function in gfx.c).
  - Added defines for screen IDs and screen dimensions.
  - Removed sharedGspCmdBuf param from gspSubmitGxCommand (now uses
    the correct GSP shared memory address automatically).
  - Removed gxCmdBuf global variable (no longer needed as per above).
  - Removed GSPGPU_REBASE_REG (leftover from early 3DS homebrew).
- GFX changes:
  - Documentation overhaul and code cleanup.
  - Simplified implementation using the enhanced GSPGPU service wrapper.
  - Top left/right framebuffers now form a single allocation instead of
    being split into two separate allocations.
  - Fixed LCD configuration mode when framebuffers are on VRAM.
  - Removed the ability to forcefully swap the screens: gspPresentBuffer
    is now always used (i.e. GSP shared mem). The 'immediate' parameter
    of gfxConfigScreen now does nothing, and gfxSwapBuffers/Gpu now do
    the same thing.
  - Removed gfx{TopLeft,TopRight,Bottom}Framebuffers global variables
    (please use gfxGetFramebuffer instead as originally intended...)
2020-06-13 20:08:37 +02:00
oreo639
26f5f7eb33 Fix travis 2020-06-13 12:32:24 +02:00
fincs
9c1c847388
Introduce syncArbitrateAddress/WithTimeout (replaces __sync_get_arbiter) 2020-06-12 20:40:47 +02:00
fincs
528f8feb0b
Add gspGetBytesPerPixel (replaces internal function)
Also fixes GSPGPU_FramebufferFormat's name
2020-06-12 19:24:15 +02:00
fincs
6c0f7ac99c
threadCreate: correct misleading parameter name 2020-06-12 14:31:42 +02:00
fincs
f49efc3cc7
Add support for userAppInit/userAppExit (backported from libnx) 2020-06-12 14:16:46 +02:00
fincs
8e3c9f9784
Minor internal cleanup 2020-06-12 14:15:18 +02:00
fincs
1442eef9e4
Update travis config again 2020-06-11 20:01:32 +02:00
fincs
ea57fb8821
apt: Revamp chainload handling, see details:
- Added aptClearChainloader (clears pending chainloads)
- Added aptSetChainloaderToSelf (reboots to self, i.e. "soft-reset")
- Internal refactoring
2020-06-11 16:28:57 +02:00
fincs
80fad02255
Add travis badge 2020-06-11 03:43:39 +02:00
fincs
08bff23736
Attempt to fix travis 2020-06-11 03:38:42 +02:00
fincs
5986713066
apt: Refactor sleep/home state handling to be more intuitive, see details:
- Added functions for checking APT state:
  - aptIsActive
  - aptShouldClose
  - aptShouldJumpToHome
  - aptCheckHomePressRejected (replaces aptIsHomePressed)
- Added functions for handling incoming requests:
  - aptHandleSleep
  - aptHandleJumpToHome
- Added aptJumpToHomeMenu (callable anytime) because why not
- aptMainLoop is now aptHandleSleep/JumpToHome + return !aptShouldClose
- APTHOOK_ONEXIT is now called during aptExit
- Internal refactoring
2020-06-11 03:02:24 +02:00
fincs
b48b5da211
apt: Add APT_LockTransition, use it in aptWaitForWakeUp 2020-06-10 21:46:48 +02:00
fincs
6ce690828c
apt: Remove aptLaunchLibraryApplet's return value/hidden aptMainLoop call 2020-06-10 20:14:01 +02:00
fincs
ac8656f8b2
apt: Implement screen capture for libapplet transitions (+ related cleanup) 2020-06-10 19:47:17 +02:00
fincs
7171a87d53
Miscellaneous APT fixes and cleanup, see details:
- APT parameter handling now follows official sw a bit more closely
  (with the APT thread acting as a filter which then relays the
   status to the main thread using a LightEvent)
- RUNFLAG_APTREINIT (hax 2.x) handling code has been simplified and
  made (hopefully) more robust
- Fixed sleep handling when the app is inactive (i.e. app is suspended)
- APT debug callback now guarded by an #ifdef (LIBCTRU_APT_DEBUG)
- Removed old Citra response cmdheader workaround
- Other miscellaneous fixes I probably forgot to talk about
2020-06-10 02:08:01 +02:00
fincs
59b8fd8283
apt: Simplify event thread exit handling code 2020-06-09 18:01:11 +02:00
fincs
36b618e3bb
ndspChnReset: Bump syncCount in order to force the DSP to reset the channel 2020-06-08 00:24:24 +02:00
fincs
beaebaaf26
Update readme (again) with clarification 2020-06-02 18:01:22 +02:00
fincs
34f690d9d0
Update documentation url 2020-06-02 17:31:47 +02:00
mtheall
80be51e93b
Fix decompress out-of-bounds access (#463) 2020-06-01 09:03:12 +01:00
127 changed files with 6302 additions and 4967 deletions

21
.github/workflows/build.yaml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Build libctru
on:
push:
branches: [ master ]
pull_request:
jobs:
build:
name: Test build
runs-on: ubuntu-latest
container:
image: 'devkitpro/devkitarm'
steps:
- uses: actions/checkout@v2
with:
persist-credentials: false
- name: build
run: make -C libctru

40
.github/workflows/doxygen.yaml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Build documentation
on:
push:
tags: [ v* ]
jobs:
build:
name: Build documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
persist-credentials: false
- name: Get current tag
id: vars
run: echo ::set-output name=tag::${GITHUB_REF/refs\/tags\//}
- name: Set up Doxygen
run: sudo apt-get install -y doxygen
- name: Display Doxygen version
run: echo "Doxygen version $(doxygen -v)"
- name: Build documentation
run: |
git clone --branch=master --single-branch --depth 1 https://github.com/devkitPro/3ds-examples examples
cd libctru
CTRU_VERSION=${{ steps.vars.outputs.tag }} doxygen Doxyfile
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.7.1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: libctru/docs/html
CLEAN: true
SINGLE_COMMIT: true

View File

@ -1,38 +0,0 @@
language: c
os: linux
dist: xenial
sudo: required
services: docker
#Cache doxygen
cache:
directories:
- /home/travis/doxygen/doxygen-1.8.15/bin
before_install:
# Travis has an OLD doxygen build, so we fetch a recent one
- export DOXY_BINPATH=/home/travis/doxygen/doxygen-1.8.15/bin
- if [ ! -e "$DOXY_BINPATH/doxygen" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then mkdir -p ~/doxygen && cd ~/doxygen; fi
- if [ ! -e "$DOXY_BINPATH/doxygen" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then wget http://doxygen.nl/files/doxygen-1.8.15.linux.bin.tar.gz; fi
- if [ ! -e "$DOXY_BINPATH/doxygen" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then tar xzf doxygen-1.8.15.linux.bin.tar.gz; fi
- export PATH=$PATH:$DOXY_BINPATH
install:
- docker pull devkitpro/devkitarm
script:
- docker run -e ENABLE_COMPATIBILITY_REPORTING -v $TRAVIS_BUILD_DIR:/libctru devkitpro/devkitarm /bin/bash -ex /libctru/.travis/docker.sh
before_deploy:
- sh .travis/exportdoc.sh
deploy:
provider: pages
skip_cleanup: true
github_token: $GITHUB_TOKEN
local_dir: libctru/docs/html
on:
tags: true
branch: master

View File

@ -1,5 +0,0 @@
#!/bin/bash -ex
source /etc/profile.d/devkit-env.sh
make -C libctru/libctru

View File

@ -1,4 +0,0 @@
#!/bin/sh
git clone --branch=master --single-branch --depth 1 https://github.com/devkitPro/3ds-examples examples
cd libctru
doxygen Doxyfile

View File

@ -1,5 +1,212 @@
# Changelog
## Version 2.4.0
- **Added full support of all QTM services**, with extensive documentation and technical details in `qtm.h` and `qtmc.h`** (breaking change); examples have been updated for this
- Added MCUHWC_SetInfoLedPattern
- Added ndspChnGetFormat
- Fixed PTMSYSM_CheckNew3DS
- Fixed NDMU_QueryStatus
- Fixed a few service commands improperly deserializing boolean output
- Fixed documentation of ACU_GetWifiStatus and ACU_SetAllowApType
- Fixed shaderInstanceInit to initialize all fields
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 2.3.0
- Fix typo in docs by @DeltaF1 in https://github.com/devkitPro/libctru/pull/525
- Add GPU_DOT3_RGBA texture combiner function by @oreo639 in https://github.com/devkitPro/libctru/pull/528
- FSUSER_GetLegacyBannerData: Fix documentation typo by @Tekito-256 in https://github.com/devkitPro/libctru/pull/526
- Add CTR_ prefix to ALIGN,PACKED,DEPRECATED macros by @glebm in https://github.com/devkitPro/libctru/pull/532
- Buffer console control sequences by @piepie62 in https://github.com/devkitPro/libctru/pull/522
- Prevent CPU from postponing threadOnException memory writes
- Add SSID-related ac:i functions
- ACI_LoadNetworkSetting, ACI_GetNetworkWirelessEssidSecuritySsid and acGetSessionHandle.
- Prevent double call of destructors on exit.
- Added experimental support for standard threading APIs (pthread, C threads, C++ std::thread)
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## New Contributors
- @DeltaF1 made their first contribution in https://github.com/devkitPro/libctru/pull/525
- @Tekito-256 made their first contribution in https://github.com/devkitPro/libctru/pull/526
- @glebm made their first contribution in https://github.com/devkitPro/libctru/pull/532
## Version 2.2.2
- archive_dev: Ensure path separator for local path
- adjust struct hostent for compatibilty
## Version 2.2.1
- add `_SOCKLEN_T_DECLARED`
## Version 2.2.0
- apt: add deliver arg support to chainloader
## Version 2.1.2
- Added cdc:CHK service wrappers
- Added support for `clock_gettime` (#495)
- svc: Fixed svcGetDmaState writing out-of-bounds data
- svc: Changed svcCreateCodeSet address parameters to pointer-sized integers, improve documentation
- fspxi: Fixed FSPXI_CreateFile and FSPXI_WriteFile (#496)
- ndsp: Added various ndspGet\* and ndspChnGet\* methods (#505, #506, #507)
- ir: Added IRU_GetSend/RecvFinishedEvent (#513)
- errf: Added ERRF_SetUserString and clarify documentation
- mcuhwc: Added mcuHwcGetSessionHandle
- apt: Fixed dirty homebrew chainload bug (used when Home Menu hasn't been started)
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 2.1.1
- FPSCR is now initialized with a predictable value in all threads (including the main thread).
- Added gspGetSessionHandle and gspLcdGetSessionHandle.
- Fixed bugs related to uninitialized data in srv/errf service wrappers.
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 2.1.0
**The #define for the 3DS platform has been changed to `__3DS__` (previously it was `_3DS`) - please update your Makefiles and your code**
### graphics
- Refactored VRAM allocators:
- Added proper handling for VRAM banks (A and B, 3 MiB each)
- Allocations no longer cross VRAM bank boundaries
- Added vramAllocAt, vramMemAlignAt
- Add gspIsPresentPending.
- Add return value to gspPresentBuffer.
- libctru console now supports SGR 38 and 48 escape sequences (needed by fmtlib).
- Fixed GPU_TEXFACE enum.
### filesystem
- Changed rename to replace existing files, for better compatibility with POSIX (#483)
- Added SDMMC speed info types, and more clock rates (#480).
### miscellaneous
- Added support for 3dslink stdio redirection (#488).
- Added ptm:gets, ptm:sets and more ptm service commands.
- Added AM_ContentInfo, AM_ContentInfoFlags structs.
- Added AMAPP_GetDLCContentInfoCount, AMAPP_ListDLCContentInfos.
- Fixed bug in Huffman decoder.
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 2.0.1
- Added CFG_SystemModel enum.
- Fixed bug in condvar code.
- Fixed bug in srvpm code.
- Fixed const correctness issues in gspgpu code.
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 2.0.0
**This is a major release. Many essential components have been overhauled, although breaking changes are minimal and only affect rarely used functionality.**
### system
- Added support for userAppInit/userAppExit (backported from libnx).
- Changed default heap allocation logic to be more robust:
- Resource limit SVCs are now used to detect available memory.
- The heap size calculation algorithm was tweaked so that 32MB of linear heap are available for normal apps running under the default appmemtype layout on Old 3DS, as it was the case prior to libctru 1.8.0.
- SVC enhancements:
- Added svcControlPerformanceCounter with corresponding enums.
- Overhauled support for DMA related SVCs:
- Added svcRestartDma.
- Added enums and structures needed for DMA SVCs.
- Added dmaDeviceConfigInitDefault, dmaConfigInitDefault.
- Added svcArbitrateAddressNoTimeout (minor ABI optimization for the svcArbitrateAddress syscall when the timeout parameter is not used).
- Changed process memory SVCs to use u32 parameters instead of void\* for foreign-process addresses.
- Major refactor of OS related functions:
- Added defines for Arm11 userland memory region addresses/sizes.
- Added struct definitions for the kernel/system shared config pages: osKernelConfig_s, osSharedConfig_s.
- Added macros to access the kernel/system shared config pages: OS_KernelConfig, OS_SharedConfig.
- Fixed return type of osGetMemRegionUsed, osGetMemRegionFree (s64 -> u32).
- Refactored osConvertVirtToPhys and added support for the missing linearly mapped memory regions.
- Rewritten RTC time reading support to improve accuracy and match official logic more closely.
- Time drift correction is now implemented.
- Added osGetTimeRef function for reading the current reference timepoint published by the PTM sysmodule.
- Fixed osStrError to actually work properly.
- Cleaned up osGetSystemVersionData implementation.
- Other miscellaneous internal cleanup.
### synchronization
- Improved safety of usermode synchronization primitives when used in intercore contexts.
- Added CondVar synchronization primitive implementation.
- Added syncArbitrateAddress, syncArbitrateAddressWithTimeout.
- These functions replace \_\_sync\_get\_arbiter (which has been removed).
- Added \_\_dmb (Data Memory Barrier) intrinsic.
- Added LightEvent_WaitTimeout.
- Added LightSemaphore_TryAcquire.
- Changed LightLock to support 0 as a valid initial (unlocked) state.
- This effectively adds support for trivially initialized/zerofilled locks.
- Fixed bug in LightSemaphore_Acquire.
### graphics
- Major refactor of the GSP service wrapper. It should now be possible to use GSP directly without the gfx API, if the user so desires.
- Major internal refactor of the gfx wrapper API that increases maintainability.
- Added support for 800px wide mode with non-square pixels on the top screen - usable on every 3DS model except for Old 2DS (but *including* New 2DS XL).
- Added support for 800px wide mode to the libctru console.
- Transferred most of the GSP initialization duties to gspInit/gspExit (previously done by the gfx wrapper).
- Added defines for screen IDs and screen dimensions.
- Added gspPresentBuffer for pushing a framebuffer to the internal GSP swap queue (previously this was an internal function in the gfx wrapper).
- Added gspGetBytesPerPixel, gspHasGpuRight.
- Added gfxScreenSwapBuffers, with support for duplicating left->right eye content during stereoscopic 3D mode.
- Fixed LCD configuration mode when using framebuffers on VRAM with the gfx wrapper.
- Changed gfxExit to set LCD force-black status to true.
- Changed the gfx wrapper to always use gspPresentBuffer during swap. This means the immediate parameter of gfxConfigScreen no longer has any effect, and gfxSwapBuffers/gfxSwapBuffersGpu now do the same thing - that is, present the rendered content during the next VBlank.
- Renamed GSPGPU_FramebufferFormats enum to GSPGPU_FramebufferFormat.
- Deprecated gfxConfigScreen (use gfxScreenSwapBuffers instead).
- Removed gspInitEventHandler, gspExitEventHandler (now handled automatically inside gspInit/gspExit).
- Removed sharedGspCmdBuf param from gspSubmitGxCommand (now uses the proper GSP shared memory address automatically).
- Removed GSPGPU_REBASE_REG define (leftover from early 3DS homebrew).
- Removed numerous internal fields that were previously publicly accessible.
### audio
- DSP access rights are now managed using a hook mechanism, similar to the APT hook.
- This fixes audio playback during libapplet invocations, as they no longer relinquish DSP rights.
- Changed NDSP to use the DSP hook instead of the APT hook.
- Added dspIsComponentLoaded, dspHook, dspUnhook.
- Fixed and improved robustness of wavebuf handling in NDSP code.
- Fixed and refactored NDSP sleep/wakeup code to improve accuracy compared to official logic.
### filesystem
- Reduced TLS footprint of the archive/romfs devices by sharing buffers.
- Reduced the maximum number of concurrently registered archive devices from 32 to 8 in order to save memory.
- Backported multi-mount romfs system from libnx, with additional optimizations (breaking API change).
- Added romfsMountFromTitle.
- Fixed stat for romfs device to return -1 for non-existent files/directories.
### applet
- Major internal refactor of the APT service wrapper that should improve accuracy compared to official logic.
- Fixed sleep handling when the app is inactive (i.e. app is suspended).
- Added support for screen capture during libapplet transitions.
- Added support for proper DSP access right management.
- aptLaunchLibraryApplet no longer calls aptMainLoop internally. Library applet calls no longer return a bool value, users are advised to check APT state manually afterwards.
- Added functions for checking APT state: aptIsActive, aptShouldClose, aptShouldJumpToHome, aptCheckHomePressRejected (replaces aptIsHomePressed).
- Added functions for handling incoming requests: aptHandleSleep, aptHandleJumpToHome.
- Added aptJumpToHomeMenu.
- Changed aptMainLoop to use the new wrapper functions (this means aptMainLoop can now be replaced by custom logic if desired).
- Changed APTHOOK_ONEXIT to be invoked during aptExit instead of during aptMainLoop.
- Added aptClearChainloader, aptSetChainloaderToSelf.
### miscellaneous
- Fix decompress out-of-bounds access.
- Added NDM_ prefix to NDM enum members in order to avoid name collisions.
- Corrected parameter type in several CFGU functions.
- Corrected return value of GSPLCD_GetBrightness.
- Removed obsolete support for ninjhax 1.x's fake hb:HB service.
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 1.9.0
- hid: Added hidKeysDownRepeat, hidWaitForAnyEvent. Allow user override of irsst usage.

View File

@ -1,10 +1,14 @@
# libctru - CTR User Library
![Build Status](https://github.com/devkitPro/libctru/actions/workflows/build.yaml/badge.svg)
Library for writing user mode ARM11 code for the 3DS (CTR)
This library aims to provide the foundations necessary to write 3DS Homebrew, and straightforwardly access the different functionalities provided by the 3DS operating system.
It is not meant to provide higher level functions; to put things in perspective, the purpose of libctru would be to sit between the OS and a possible port of SDL rather than replace it.
*(Originally located at github.com/smealum/ctrulib)*
# Setup
libctru is just a library and needs a toolchain to function. devkitARM (created by [devkitPro](http://devkitpro.org)) is the officially supported ARM cross compiling toolchain, which provides the framework necessary to supply a usable POSIX-like environment, with working C and C++ standard libraries; as well as the tools required to compile homebrew in the 3DSX format, and assemble GPU shaders. The use of other ARM toolchains is severely discouraged.
@ -15,7 +19,7 @@ You may find instructions on how to install devkitARM on [the devkitPro Wiki](ht
# Documentation
The documentation is automatically built upon release and can be found at the following url: [http://smealum.github.io/ctrulib](http://smealum.github.io/ctrulib)
The documentation is automatically built upon release and can be found at the following url: https://devkitpro.github.io/libctru/
# License

1
libctru/.gitignore vendored
View File

@ -7,3 +7,4 @@ docs
internal_docs
*.bz2
.*/
*.tag

View File

@ -38,7 +38,7 @@ PROJECT_NAME = "libctru"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "$(TRAVIS_TAG)"
PROJECT_NUMBER = "$(CTRU_VERSION)"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@ -2023,7 +2023,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = PACKED
PREDEFINED = CTR_PACKED
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
@ -2067,7 +2067,7 @@ TAGFILES =
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE =
GENERATE_TAGFILE = libctru.tag
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be

View File

@ -8,8 +8,8 @@ endif
include $(DEVKITARM)/base_rules
export LIBCTRU_MAJOR := 1
export LIBCTRU_MINOR := 9
export LIBCTRU_MAJOR := 2
export LIBCTRU_MINOR := 4
export LIBCTRU_PATCH := 0
@ -50,7 +50,7 @@ CFLAGS := -g -Wall -Werror -mword-relocations \
$(ARCH) \
$(BUILD_CFLAGS)
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
CFLAGS += $(INCLUDE) -D__3DS__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
@ -145,7 +145,7 @@ lib/libctrud.a : lib debug $(SOURCES) $(INCLUDES)
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr release debug lib docs
@rm -fr release debug lib docs libctru.tag
#---------------------------------------------------------------------------------
else

View File

@ -8,6 +8,11 @@
extern "C" {
#endif
#if defined(_3DS) && !defined(__3DS__)
#warning "Please update your Makefile and replace -DARM11 -D_3DS with -D__3DS__"
#define __3DS__
#endif
//might be missing some
#include <3ds/types.h>
#include <3ds/result.h>
@ -60,6 +65,8 @@ extern "C" {
#include <3ds/services/ps.h>
#include <3ds/services/ptmu.h>
#include <3ds/services/ptmsysm.h>
#include <3ds/services/ptmgets.h>
#include <3ds/services/ptmsets.h>
#include <3ds/services/pxidev.h>
#include <3ds/services/pxipm.h>
#include <3ds/services/soc.h>
@ -68,11 +75,12 @@ extern "C" {
#include <3ds/services/nfc.h>
#include <3ds/services/news.h>
#include <3ds/services/qtm.h>
#include <3ds/services/qtmc.h>
#include <3ds/services/srvpm.h>
#include <3ds/services/loader.h>
#include <3ds/services/y2r.h>
#include <3ds/services/mcuhwc.h>
#include <3ds/services/hb.h>
#include <3ds/services/cdcchk.h>
#include <3ds/gpu/gx.h>
#include <3ds/gpu/gpu.h>
@ -93,6 +101,7 @@ extern "C" {
#include <3ds/mii.h>
#include <3ds/gdbhio_dev.h>
#include <3ds/3dslink.h>
#ifdef __cplusplus
}

View File

@ -0,0 +1,34 @@
/**
* @file 3dslink.h
* @brief Netloader (3dslink) utilities
*/
#pragma once
#include <stdbool.h>
struct in_addr;
/// Address of the host connected through 3dslink
extern struct in_addr __3dslink_host;
#define LINK3DS_COMM_PORT 17491 ///< 3dslink TCP server port
/**
* @brief Connects to the 3dslink host, setting up an output stream.
* @param[in] redirStdout Whether to redirect stdout to nxlink output.
* @param[in] redirStderr Whether to redirect stderr to nxlink output.
* @return Socket fd on success, negative number on failure.
* @note The socket should be closed with close() during application cleanup.
*/
int link3dsConnectToHost(bool redirStdout, bool redirStderr);
/// Same as \ref link3dsConnectToHost but redirecting both stdout/stderr.
static inline int link3dsStdio(void) {
return link3dsConnectToHost(true, true);
}
/// Same as \ref link3dsConnectToHost but redirecting only stderr.
static inline int link3dsStdioForDebug(void) {
return link3dsConnectToHost(false, true);
}

View File

@ -4,6 +4,8 @@
*/
#pragma once
#include <stddef.h>
/**
* @brief Allocates a 0x80-byte aligned buffer.
* @param size Size of the buffer to allocate.

View File

@ -4,6 +4,13 @@
*/
#pragma once
typedef enum vramAllocPos
{
VRAM_ALLOC_A = BIT(0),
VRAM_ALLOC_B = BIT(1),
VRAM_ALLOC_ANY = VRAM_ALLOC_A | VRAM_ALLOC_B,
} vramAllocPos;
/**
* @brief Allocates a 0x80-byte aligned buffer.
* @param size Size of the buffer to allocate.
@ -11,6 +18,14 @@
*/
void* vramAlloc(size_t size);
/**
* @brief Allocates a 0x80-byte aligned buffer in the given VRAM bank.
* @param size Size of the buffer to allocate.
* @param pos VRAM bank to use (see \ref vramAllocPos).
* @return The allocated buffer.
*/
void* vramAllocAt(size_t size, vramAllocPos pos);
/**
* @brief Allocates a buffer aligned to the given size.
* @param size Size of the buffer to allocate.
@ -19,6 +34,15 @@ void* vramAlloc(size_t size);
*/
void* vramMemAlign(size_t size, size_t alignment);
/**
* @brief Allocates a buffer aligned to the given size in the given VRAM bank.
* @param size Size of the buffer to allocate.
* @param alignment Alignment to use.
* @param pos VRAM bank to use (see \ref vramAllocPos).
* @return The allocated buffer.
*/
void* vramMemAlignAt(size_t size, size_t alignment, vramAllocPos pos);
/**
* @brief Reallocates a buffer.
* Note: Not implemented yet.

View File

@ -89,9 +89,8 @@ void miiSelectorInit(MiiSelectorConf *conf);
* @brief Launch the Mii selector library applet
*
* @param conf Configuration determining how the applet should behave
* @param returnbuf Data returned by the applet
*/
Result miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn* returnbuf);
void miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn* returnbuf);
/**
* @brief Sets title of the Mii selector library applet

View File

@ -94,8 +94,8 @@ typedef struct PrintConsole
int windowHeight; ///< Window height in characters (not implemented)
int tabSize; ///< Size of a tab
int fg; ///< Foreground color
int bg; ///< Background color
u16 fg; ///< Foreground color
u16 bg; ///< Background color
int flags; ///< Reverse/bright flags
ConsolePrint PrintChar; ///< Callback for printing a character. Should return true if it has handled rendering the graphics (else the print engine will attempt to render via tiles).
@ -112,6 +112,8 @@ typedef struct PrintConsole
#define CONSOLE_COLOR_REVERSE (1<<6) ///< Reversed color text
#define CONSOLE_CONCEAL (1<<7) ///< Concealed text
#define CONSOLE_CROSSED_OUT (1<<8) ///< Crossed out text
#define CONSOLE_FG_CUSTOM (1<<9) ///< Foreground custom color
#define CONSOLE_BG_CUSTOM (1<<10) ///< Background custom color
/// Console debug devices supported by libnds.
typedef enum {

View File

@ -9,12 +9,12 @@
/// Types of errors that can be thrown by err:f.
typedef enum {
ERRF_ERRTYPE_GENERIC = 0, ///< For generic errors. Shows miscellaneous info.
ERRF_ERRTYPE_MEM_CORRUPT = 1, ///< Same output as generic, but informs the user that "the System Memory has been damaged".
ERRF_ERRTYPE_CARD_REMOVED = 2, ///< Displays the "The Game Card was removed." message.
ERRF_ERRTYPE_EXCEPTION = 3, ///< For exceptions, or more specifically 'crashes'. union data should be exception_data.
ERRF_ERRTYPE_FAILURE = 4, ///< For general failure. Shows a message. union data should have a string set in failure_mesg
ERRF_ERRTYPE_LOGGED = 5, ///< Outputs logs to NAND in some cases.
ERRF_ERRTYPE_GENERIC = 0, ///< Generic fatal error. Shows miscellaneous info, including the address of the caller
ERRF_ERRTYPE_NAND_DAMAGED = 1, ///< Damaged NAND (CC_ERROR after reading CSR)
ERRF_ERRTYPE_CARD_REMOVED = 2, ///< Game content storage medium (cartridge and/or SD card) ejected. Not logged
ERRF_ERRTYPE_EXCEPTION = 3, ///< CPU or VFP exception
ERRF_ERRTYPE_FAILURE = 4, ///< Fatal error with a message instead of the caller's address
ERRF_ERRTYPE_LOG_ONLY = 5, ///< Log-level failure. Does not display the exception and does not force the system to reboot
} ERRF_ErrType;
/// Types of 'Exceptions' thrown for ERRF_ERRTYPE_EXCEPTION
@ -46,9 +46,9 @@ typedef struct {
u16 revLow; ///< Low revision ID
u32 resCode; ///< Result code
u32 pcAddr; ///< PC address at exception
u32 procId; ///< Process ID.
u64 titleId; ///< Title ID.
u64 appTitleId; ///< Application Title ID.
u32 procId; ///< Process ID of the caller
u64 titleId; ///< Title ID of the caller
u64 appTitleId; ///< Title ID of the running application
union {
ERRF_ExceptionData exception_data; ///< Data for when type is ERRF_ERRTYPE_EXCEPTION
char failure_mesg[0x60]; ///< String for when type is ERRF_ERRTYPE_FAILURE
@ -68,13 +68,19 @@ void errfExit(void);
Handle *errfGetSessionHandle(void);
/**
* @brief Throws a system error and possibly results in ErrDisp triggering.
* @brief Throws a system error and possibly logs it.
* @param[in] error Error to throw.
*
* After performing this, the system may panic and need to be rebooted. Extra information will be displayed on the
* top screen with a developer console or the proper patches in a CFW applied.
* ErrDisp may convert the error info to \ref ERRF_ERRTYPE_NAND_DAMAGED or \ref ERRF_ERRTYPE_CARD_REMOVED
* depending on the error code.
*
* The error may not be shown and execution aborted until errfExit(void) is called.
* Except with \ref ERRF_ERRTYPE_LOG_ONLY, the system will panic and will need to be rebooted.
* Fatal error information will also be logged into a file, unless the type either \ref ERRF_ERRTYPE_NAND_DAMAGED
* or \ref ERRF_ERRTYPE_CARD_REMOVED.
*
* No error will be shown if the system is asleep.
*
* On retail units with vanilla firmware, no detailed information will be displayed on screen.
*
* You may wish to use ERRF_ThrowResult() or ERRF_ThrowResultWithMessage() instead of
* constructing the ERRF_FatalErrInfo struct yourself.
@ -82,35 +88,44 @@ Handle *errfGetSessionHandle(void);
Result ERRF_Throw(const ERRF_FatalErrInfo* error);
/**
* @brief Throws a system error with the given Result code.
* @brief Throws (and logs) a system error with the given Result code.
* @param[in] failure Result code to throw.
*
* This calls ERRF_Throw() with error type ERRF_ERRTYPE_GENERIC and fills in the required data.
* This calls \ref ERRF_Throw with error type \ref ERRF_ERRTYPE_GENERIC and fills in the required data.
*
* This function \em does fill in the address where this function was called from.
*
* See https://3dbrew.org/wiki/ERR:Throw#Generic for expected top screen output
* on development units/patched ErrDisp.
*/
Result ERRF_ThrowResult(Result failure);
/**
* @brief Logs a system error with the given Result code.
* @param[in] failure Result code to log.
*
* Similar to \ref ERRF_Throw, except that it does not display anything on the screen,
* nor does it force the system to reboot.
*
* This function \em does fill in the address where this function was called from.
*/
Result ERRF_LogResult(Result failure);
/**
* @brief Throws a system error with the given Result code and message.
* @param[in] failure Result code to throw.
* @param[in] message The message to display.
*
* This calls ERRF_Throw() with error type ERRF_ERRTYPE_FAILURE and fills in the required data.
* This calls \ref ERRF_Throw with error type \ref ERRF_ERRTYPE_FAILURE and fills in the required data.
*
* This function does \em not fill in the address where this function was called from because it
* would not be displayed.
*
* The message is only displayed on development units/patched ErrDisp.
*
* See https://3dbrew.org/wiki/ERR:Throw#Result_Failure for expected top screen output
* on development units/patched ErrDisp.
*/
Result ERRF_ThrowResultWithMessage(Result failure, const char* message);
/**
* @brief Specify an additional user string to use for error reporting.
* @param[in] user_string User string (up to 256 bytes, not including NUL byte)
*/
Result ERRF_SetUserString(const char* user_string);
/**
* @brief Handles an exception using ErrDisp.
* @param excep Exception information

View File

@ -1,9 +1,14 @@
/**
* @file gfx.h
* @brief LCD Screens manipulation
* @brief Simple framebuffer API
*
* This header provides functions to configure and manipulate the two screens, including double buffering and 3D activation.
* This API provides basic functionality needed to bring up framebuffers for both screens,
* as well as managing display mode (stereoscopic 3D) and double buffering.
* It is mainly an abstraction over the gsp service.
*
* Please note that the 3DS uses *portrait* screens rotated 90 degrees counterclockwise.
* Width/height refer to the physical dimensions of the screen; that is, the top screen
* is 240 pixels wide and 400 pixels tall; while the bottom screen is 240x320.
*/
#pragma once
@ -16,36 +21,29 @@
/// Converts packed RGB8 to packed RGB565.
#define RGB8_to_565(r,g,b) (((b)>>3)&0x1f)|((((g)>>2)&0x3f)<<5)|((((r)>>3)&0x1f)<<11)
/// Available screens.
typedef enum
{
GFX_TOP = 0, ///< Top screen
GFX_BOTTOM = 1 ///< Bottom screen
/// Screen IDs.
typedef enum {
GFX_TOP = GSP_SCREEN_TOP, ///< Top screen
GFX_BOTTOM = GSP_SCREEN_BOTTOM, ///< Bottom screen
} gfxScreen_t;
/**
* @brief Side of top screen framebuffer.
* @brief Top screen framebuffer side.
*
* This is to be used only when the 3D is enabled.
* Use only GFX_LEFT if this concerns the bottom screen or if 3D is disabled.
* This is only meaningful when stereoscopic 3D is enabled on the top screen.
* In any other case, use \ref GFX_LEFT.
*/
typedef enum
{
typedef enum {
GFX_LEFT = 0, ///< Left eye framebuffer
GFX_RIGHT = 1, ///< Right eye framebuffer
} gfx3dSide_t;
///@name System related
///@name Initialization and deinitialization
///@{
/**
* @brief Initializes the LCD framebuffers with default parameters
*
* By default ctrulib will configure the LCD framebuffers with the @ref GSP_BGR8_OES format in linear memory.
* This is the same as calling : @code gfxInit(GSP_BGR8_OES,GSP_BGR8_OES,false); @endcode
*
* @note You should always call @ref gfxExit once done to free the memory and services
* This is equivalent to calling: @code gfxInit(GSP_BGR8_OES,GSP_BGR8_OES,false); @endcode
*/
void gfxInitDefault(void);
@ -55,117 +53,128 @@ void gfxInitDefault(void);
* @param bottomFormat The format of the bottom screen framebuffers.
* @param vramBuffers Whether to allocate the framebuffers in VRAM.
*
* This function will allocate the memory for the framebuffers and open a gsp service session.
* It will also bind the newly allocated framebuffers to the LCD screen and setup the VBlank event.
* This function allocates memory for the framebuffers in the specified memory region.
* Initially, stereoscopic 3D is disabled and double buffering is enabled.
*
* The 3D stereoscopic display is will be disabled.
*
* @note Even if the double buffering is disabled, it will allocate two buffer per screen.
* @note You should always call @ref gfxExit once done to free the memory and services
* @note This function internally calls \ref gspInit.
*/
void gfxInit(GSPGPU_FramebufferFormats topFormat, GSPGPU_FramebufferFormats bottomFormat, bool vrambuffers);
void gfxInit(GSPGPU_FramebufferFormat topFormat, GSPGPU_FramebufferFormat bottomFormat, bool vrambuffers);
/**
* @brief Closes the gsp service and frees the framebuffers.
*
* Just call it when you're done.
* @brief Deinitializes and frees the LCD framebuffers.
* @note This function internally calls \ref gspExit.
*/
void gfxExit(void);
///@}
///@name Control
///@{
/**
* @brief Enables the 3D stereoscopic effect.
* @param enable Enables the 3D effect if true, disables it if false.
* @brief Enables or disables the 3D stereoscopic effect on the top screen.
* @param enable Pass true to enable, false to disable.
* @note Stereoscopic 3D is disabled by default.
*/
void gfxSet3D(bool enable);
/**
* @brief Retrieves the status of the 3D stereoscopic effect.
* @brief Retrieves the status of the 3D stereoscopic effect on the top screen.
* @return true if 3D enabled, false otherwise.
*/
bool gfxIs3D(void);
/**
* @brief Changes the color format of a screen
* @param screen The screen of which format should be changed
* @param format One of the gsp pixel formats.
* @brief Retrieves the status of the 800px (double-height) high resolution display mode of the top screen.
* @return true if wide mode enabled, false otherwise.
*/
void gfxSetScreenFormat(gfxScreen_t screen, GSPGPU_FramebufferFormats format);
bool gfxIsWide(void);
/**
* @brief Gets a screen pixel format.
* @param screen Screen to get the pixel format of.
* @return the pixel format of the chosen screen set by ctrulib.
* @brief Enables or disables the 800px (double-height) high resolution display mode of the top screen.
* @param enable Pass true to enable, false to disable.
* @note Wide mode is disabled by default.
* @note Wide and stereoscopic 3D modes are mutually exclusive.
* @note In wide mode pixels are not square, since scanlines are half as tall as they normally are.
* @warning Wide mode does not work on Old 2DS consoles (however it does work on New 2DS XL consoles).
*/
GSPGPU_FramebufferFormats gfxGetScreenFormat(gfxScreen_t screen);
void gfxSetWide(bool enable);
/**
* @brief Sets whether to use ctrulib's double buffering
* @param screen Screen to toggle double buffering for.
* @param doubleBuffering Whether to use double buffering.
*
* ctrulib is by default using a double buffering scheme.
* If you do not want to swap one of the screen framebuffers when @ref gfxSwapBuffers or @ref gfxSwapBuffers is called,
* then you have to disable double buffering.
*
* It is however recommended to call @ref gfxSwapBuffers even if double buffering is disabled
* for both screens if you want to keep the gsp configuration up to date.
* @brief Changes the pixel format of a screen.
* @param screen Screen ID (see \ref gfxScreen_t)
* @param format Pixel format (see \ref GSPGPU_FramebufferFormat)
* @note If the currently allocated framebuffers are too small for the specified format,
* they are freed and new ones are reallocated.
*/
void gfxSetDoubleBuffering(gfxScreen_t screen, bool doubleBuffering);
void gfxSetScreenFormat(gfxScreen_t screen, GSPGPU_FramebufferFormat format);
/**
* @brief Flushes the current framebuffers
* @brief Retrieves the current pixel format of a screen.
* @param screen Screen ID (see \ref gfxScreen_t)
* @return Pixel format (see \ref GSPGPU_FramebufferFormat)
*/
GSPGPU_FramebufferFormat gfxGetScreenFormat(gfxScreen_t screen);
/**
* @brief Enables or disables double buffering on a screen.
* @param screen Screen ID (see \ref gfxScreen_t)
* @param enable Pass true to enable, false to disable.
* @note Double buffering is enabled by default.
*/
void gfxSetDoubleBuffering(gfxScreen_t screen, bool enable);
///@}
///@name Rendering and presentation
///@{
/**
* @brief Retrieves the framebuffer of the specified screen to which graphics should be rendered.
* @param screen Screen ID (see \ref gfxScreen_t)
* @param side Framebuffer side (see \ref gfx3dSide_t) (pass \ref GFX_LEFT if not using stereoscopic 3D)
* @param width Pointer that will hold the width of the framebuffer in pixels.
* @param height Pointer that will hold the height of the framebuffer in pixels.
* @return A pointer to the current framebuffer of the chosen screen.
*
* Use this if the data within your framebuffers changes a lot and that you want to make sure everything was updated correctly.
* This shouldn't be needed and has a significant overhead.
* Please remember that the returned pointer will change every frame if double buffering is enabled.
*/
u8* gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16* width, u16* height);
/**
* @brief Flushes the data cache for the current framebuffers.
* @warning This is **only used during software rendering**. Since this function has significant overhead,
* it is preferred to call this only once per frame, after all software rendering is completed.
*/
void gfxFlushBuffers(void);
/**
* @brief Updates the configuration of the specified screen (swapping the buffers if double-buffering is enabled).
* @param scr Screen to configure.
* @param immediate Whether to apply the updated configuration immediately or let GSPGPU apply it after the next GX transfer completes.
* @brief Updates the configuration of the specified screen, swapping the buffers if double buffering is enabled.
* @param scr Screen ID (see \ref gfxScreen_t)
* @param hasStereo For the top screen in 3D mode: true if the framebuffer contains individual images
* for both eyes, or false if the left image should be duplicated to the right eye.
* @note Previously rendered content will be displayed on the screen after the next VBlank.
* @note This function is still useful even if double buffering is disabled, as it must be used to commit configuration changes.
* @warning Only call this once per screen per frame, otherwise graphical glitches will occur
* since this API does not implement triple buffering.
*/
void gfxConfigScreen(gfxScreen_t scr, bool immediate);
void gfxScreenSwapBuffers(gfxScreen_t scr, bool hasStereo);
/**
* @brief Swaps the buffers and sets the gsp state
*
* This is to be called to update the gsp state and swap the framebuffers.
* LCD rendering should start as soon as the gsp state is set.
* When using the GPU, call @ref gfxSwapBuffers instead.
* @brief Same as \ref gfxScreenSwapBuffers, but with hasStereo set to true.
* @param scr Screen ID (see \ref gfxScreen_t)
* @param immediate This parameter no longer has any effect and is thus ignored.
* @deprecated This function has been superseded by \ref gfxScreenSwapBuffers, please use that instead.
*/
CTR_DEPRECATED void gfxConfigScreen(gfxScreen_t scr, bool immediate);
/**
* @brief Updates the configuration of both screens.
* @note This function is equivalent to: \code gfxScreenSwapBuffers(GFX_TOP,true); gfxScreenSwapBuffers(GFX_BOTTOM,true); \endcode
*/
void gfxSwapBuffers(void);
/**
* @brief Swaps the framebuffers
*
* This is the version to be used with the GPU since the GPU will use the gsp shared memory,
* so the gsp state mustn't be set directly by the user.
*/
/// Same as \ref gfxSwapBuffers (formerly different).
void gfxSwapBuffersGpu(void);
///@}
///@name Helper
///@{
/**
* @brief Retrieves a framebuffer information.
* @param screen Screen to retrieve framebuffer information for.
* @param side Side of the screen to retrieve framebuffer information for.
* @param width Pointer that will hold the width of the framebuffer in pixels.
* @param height Pointer that will hold the height of the framebuffer in pixels.
* @return A pointer to the current framebuffer of the choosen screen.
*
* Please remember that the returned pointer will change after each call to gfxSwapBuffers if double buffering is enabled.
*/
u8* gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16* width, u16* height);
///@}
//global variables
extern u8* gfxTopLeftFramebuffers[2];
extern u8* gfxTopRightFramebuffers[2];
extern u8* gfxBottomFramebuffers[2];

View File

@ -78,16 +78,16 @@ typedef enum
GPU_ETC1A4 = 0xD, ///< ETC1 texture compression + 4-bit Alpha
} GPU_TEXCOLOR;
// Texture faces.
/// Texture faces.
typedef enum
{
GPU_TEXFACE_2D = 0, // 2D face
GPU_POSITIVE_X = 0, // +X face
GPU_NEGATIVE_X = 1, // -X face
GPU_POSITIVE_Y = 2, // +Y face
GPU_NEGATIVE_Y = 3, // -Y face
GPU_POSITIVE_Z = 4, // +Z face
GPU_NEGATIVE_Z = 5, // -Z face
GPU_TEXFACE_2D = 0, ///< 2D face
GPU_POSITIVE_X = 0, ///< +X face
GPU_NEGATIVE_X = 1, ///< -X face
GPU_POSITIVE_Y = 2, ///< +Y face
GPU_NEGATIVE_Y = 3, ///< -Y face
GPU_POSITIVE_Z = 4, ///< +Z face
GPU_NEGATIVE_Z = 5, ///< -Z face
} GPU_TEXFACE;
/// Procedural texture clamp modes.
@ -368,7 +368,8 @@ typedef enum
GPU_ADD_SIGNED = 0x03, ///< Signed add.
GPU_INTERPOLATE = 0x04, ///< Interpolate.
GPU_SUBTRACT = 0x05, ///< Subtract.
GPU_DOT3_RGB = 0x06, ///< Dot3. RGB only.
GPU_DOT3_RGB = 0x06, ///< Dot3. Scalar result is written to RGB only.
GPU_DOT3_RGBA = 0x07, ///< Dot3. Scalar result is written to RGBA.
GPU_MULTIPLY_ADD = 0x08, ///< Multiply then add.
GPU_ADD_MULTIPLY = 0x09, ///< Add then multiply.
} GPU_COMBINEFUNC;

View File

@ -13,7 +13,7 @@
/**
* @brief Supported transfer pixel formats.
* @sa GSPGPU_FramebufferFormats
* @sa GSPGPU_FramebufferFormat
*/
typedef enum
{
@ -65,8 +65,6 @@ typedef enum
/// Flushes the command list.
#define GX_CMDLIST_FLUSH BIT(1)
extern u32* gxCmdBuf; ///< GX command buffer.
/// GX command entry
typedef union
{

View File

@ -70,7 +70,7 @@ static inline u32 IPC_Desc_CurProcessId(void)
return 0x20;
}
static inline DEPRECATED u32 IPC_Desc_CurProcessHandle(void)
static inline CTR_DEPRECATED u32 IPC_Desc_CurProcessHandle(void)
{
return IPC_Desc_CurProcessId();
}

View File

@ -156,4 +156,4 @@ typedef struct
} mole_details;
u16 author_name[10]; ///< Name of Mii's author (Encoded using UTF16)
} PACKED MiiData;
} CTR_PACKED MiiData;

View File

@ -107,6 +107,14 @@ void ndspChnSetPaused(int id, bool paused);
*/
void ndspChnSetFormat(int id, u16 format);
/**
*
* @brief Gets the format of a channel.
* @param id ID of the channel (0..23).
* @return The format of the channel.
*/
u16 ndspChnGetFormat(int id);
/**
* @brief Sets the interpolation type of a channel.
* @param id ID of the channel (0..23).
@ -114,6 +122,13 @@ void ndspChnSetFormat(int id, u16 format);
*/
void ndspChnSetInterp(int id, ndspInterpType type);
/**
* @brief Gets the interpolation type of a channel.
* @param id ID of the channel (0..23).
* @return The interpolation type of the channel.
*/
ndspInterpType ndspChnGetInterp(int id);
/**
* @brief Sets the sample rate of a channel.
* @param id ID of the channel (0..23).
@ -121,6 +136,13 @@ void ndspChnSetInterp(int id, ndspInterpType type);
*/
void ndspChnSetRate(int id, float rate);
/**
* @brief Gets the sample rate of a channel.
* @param id ID of the channel (0..23).
* @return The sample rate of the channel.
*/
float ndspChnGetRate(int id);
/**
* @brief Sets the mix parameters (volumes) of a channel.
* @param id ID of the channel (0..23).
@ -134,6 +156,13 @@ void ndspChnSetRate(int id, float rate);
*/
void ndspChnSetMix(int id, float mix[12]);
/**
* @brief Gets the mix parameters (volumes) of a channel.
* @param id ID of the channel (0..23)
* @param mix Mix parameters to write out to. See \ref ndspChnSetMix.
*/
void ndspChnGetMix(int id, float mix[12]);
/**
* @brief Sets the DSPADPCM coefficients of a channel.
* @param id ID of the channel (0..23).

View File

@ -118,24 +118,48 @@ u32 ndspGetFrameCount(void);
*/
void ndspSetMasterVol(float volume);
/**
* @brief Gets the master volume.
* @return The master volume.
*/
float ndspGetMasterVol(void);
/**
* @brief Sets the output mode.
* @param mode Output mode to set. Defaults to NDSP_OUTPUT_STEREO.
*/
void ndspSetOutputMode(ndspOutputMode mode);
/**
* @brief Gets the output mode.
* @return The output mode.
*/
ndspOutputMode ndspGetOutputMode(void);
/**
* @brief Sets the clipping mode.
* @param mode Clipping mode to set. Defaults to NDSP_CLIP_SOFT.
*/
void ndspSetClippingMode(ndspClippingMode mode);
/**
* @brief Gets the clipping mode.
* @return The clipping mode.
*/
ndspClippingMode ndspGetClippingMode(void);
/**
* @brief Sets the output count.
* @param count Output count to set. Defaults to 2.
*/
void ndspSetOutputCount(int count);
/**
* @brief Gets the output count.
* @return The output count.
*/
int ndspGetOutputCount(void);
/**
* @brief Sets the wave buffer to capture audio to.
* @param capture Wave buffer to capture to.
@ -158,17 +182,35 @@ void ndspSetCallback(ndspCallback callback, void* data);
*/
void ndspSurroundSetDepth(u16 depth);
/**
* @brief Gets the surround sound depth.
* @return The surround sound depth.
*/
u16 ndspSurroundGetDepth(void);
/**
* @brief Sets the surround sound position.
* @param pos Position to set. Defaults to NDSP_SPKPOS_SQUARE.
*/
void ndspSurroundSetPos(ndspSpeakerPos pos);
/**
* @brief Gets the surround sound position.
* @return The surround sound speaker position.
*/
ndspSpeakerPos ndspSurroundGetPos(void);
/**
* @brief Sets the surround sound rear ratio.
* @param ratio Rear ratio to set. Defaults to 0x8000.
*/
void ndspSurroundSetRearRatio(u16 ratio);
/**
* @brief Gets the surround sound rear ratio.
* @return The rear ratio.
*/
u16 ndspSurroundGetRearRatio(void);
///@}
///@name Auxiliary output
@ -180,6 +222,13 @@ void ndspSurroundSetRearRatio(u16 ratio);
*/
void ndspAuxSetEnable(int id, bool enable);
/**
* @brief Gets whether auxiliary output is enabled.
* @param id ID of the auxiliary output.
* @return Whether auxiliary output is enabled.
*/
bool ndspAuxIsEnabled(int id);
/**
* @brief Configures whether an auxiliary output should use front bypass.
* @param id ID of the auxiliary output.
@ -187,6 +236,13 @@ void ndspAuxSetEnable(int id, bool enable);
*/
void ndspAuxSetFrontBypass(int id, bool bypass);
/**
* @brief Gets whether auxiliary output front bypass is enabled.
* @param id ID of the auxiliary output.
* @return Whether auxiliary output front bypass is enabled.
*/
bool ndspAuxGetFrontBypass(int id);
/**
* @brief Sets the volume of an auxiliary output.
* @param id ID of the auxiliary output.
@ -194,6 +250,13 @@ void ndspAuxSetFrontBypass(int id, bool bypass);
*/
void ndspAuxSetVolume(int id, float volume);
/**
* @brief Gets the volume of an auxiliary output.
* @param id ID of the auxiliary output.
* @return Volume of the auxiliary output.
*/
float ndspAuxGetVolume(int id);
/**
* @brief Sets the callback of an auxiliary output.
* @param id ID of the auxiliary output.

View File

@ -5,10 +5,22 @@
#pragma once
#include "svc.h"
#define SYSCLOCK_SOC (16756991)
#define SYSCLOCK_ARM9 (SYSCLOCK_SOC * 8)
///< The external clock rate for the SoC.
#define SYSCLOCK_SOC (16756991u)
///< The base system clock rate (for I2C, NDMA, etc.).
#define SYSCLOCK_SYS (SYSCLOCK_SOC * 2)
///< The base clock rate for the SDMMC controller (and some other peripherals).
#define SYSCLOCK_SDMMC (SYSCLOCK_SYS * 2)
///< The clock rate for the Arm9.
#define SYSCLOCK_ARM9 (SYSCLOCK_SYS * 4)
///< The clock rate for the Arm11 in CTR mode and in \ref svcGetSystemTick.
#define SYSCLOCK_ARM11 (SYSCLOCK_ARM9 * 2)
#define SYSCLOCK_ARM11_NEW (SYSCLOCK_ARM11 * 3)
///< The clock rate for the Arm11 in LGR1 mode.
#define SYSCLOCK_ARM11_LGR1 (SYSCLOCK_ARM11 * 2)
///< The clock rate for the Arm11 in LGR2 mode.
#define SYSCLOCK_ARM11_LGR2 (SYSCLOCK_ARM11 * 3)
///< The highest possible clock rate for the Arm11 on known New 3DS units.
#define SYSCLOCK_ARM11_NEW SYSCLOCK_ARM11_LGR2
#define CPU_TICKS_PER_MSEC (SYSCLOCK_ARM11 / 1000.0)
#define CPU_TICKS_PER_USEC (SYSCLOCK_ARM11 / 1000000.0)
@ -26,14 +38,98 @@
/// Retrieves the revision version from a packed system version.
#define GET_VERSION_REVISION(version) (((version)>> 8)&0xFF)
/// Memory regions.
typedef enum
#define OS_HEAP_AREA_BEGIN 0x08000000 ///< Start of the heap area in the virtual address space
#define OS_HEAP_AREA_END 0x0E000000 ///< End of the heap area in the virtual address space
#define OS_MAP_AREA_BEGIN 0x10000000 ///< Start of the mappable area in the virtual address space
#define OS_MAP_AREA_END 0x14000000 ///< End of the mappable area in the virtual address space
#define OS_OLD_FCRAM_VADDR 0x14000000 ///< Old pre-8.x linear FCRAM mapping virtual address
#define OS_OLD_FCRAM_PADDR 0x20000000 ///< Old pre-8.x linear FCRAM mapping physical address
#define OS_OLD_FCRAM_SIZE 0x8000000 ///< Old pre-8.x linear FCRAM mapping size (128 MiB)
#define OS_QTMRAM_VADDR 0x1E800000 ///< New3DS QTM memory virtual address
#define OS_QTMRAM_PADDR 0x1F000000 ///< New3DS QTM memory physical address
#define OS_QTMRAM_SIZE 0x400000 ///< New3DS QTM memory size (4 MiB; last 128 KiB reserved by kernel)
#define OS_MMIO_VADDR 0x1EC00000 ///< Memory mapped IO range virtual address
#define OS_MMIO_PADDR 0x10100000 ///< Memory mapped IO range physical address
#define OS_MMIO_SIZE 0x400000 ///< Memory mapped IO range size (4 MiB)
#define OS_VRAM_VADDR 0x1F000000 ///< VRAM virtual address
#define OS_VRAM_PADDR 0x18000000 ///< VRAM physical address
#define OS_VRAM_SIZE 0x600000 ///< VRAM size (6 MiB)
#define OS_DSPRAM_VADDR 0x1FF00000 ///< DSP memory virtual address
#define OS_DSPRAM_PADDR 0x1FF00000 ///< DSP memory physical address
#define OS_DSPRAM_SIZE 0x80000 ///< DSP memory size (512 KiB)
#define OS_KERNELCFG_VADDR 0x1FF80000 ///< Kernel configuration page virtual address
#define OS_SHAREDCFG_VADDR 0x1FF81000 ///< Shared system configuration page virtual address
#define OS_FCRAM_VADDR 0x30000000 ///< Linear FCRAM mapping virtual address
#define OS_FCRAM_PADDR 0x20000000 ///< Linear FCRAM mapping physical address
#define OS_FCRAM_SIZE 0x10000000 ///< Linear FCRAM mapping size (256 MiB)
#define OS_KernelConfig ((osKernelConfig_s const*)OS_KERNELCFG_VADDR) ///< Pointer to the kernel configuration page (see \ref osKernelConfig_s)
#define OS_SharedConfig ((osSharedConfig_s*)OS_SHAREDCFG_VADDR) ///< Pointer to the shared system configuration page (see \ref osSharedConfig_s)
/// Kernel configuration page (read-only).
typedef struct
{
MEMREGION_ALL = 0, ///< All regions.
MEMREGION_APPLICATION = 1, ///< APPLICATION memory.
MEMREGION_SYSTEM = 2, ///< SYSTEM memory.
MEMREGION_BASE = 3, ///< BASE memory.
} MemRegion;
u32 kernel_ver;
u32 update_flag;
u64 ns_tid;
u32 kernel_syscore_ver;
u8 env_info;
u8 unit_info;
u8 boot_env;
u8 unk_0x17;
u32 kernel_ctrsdk_ver;
u32 unk_0x1c;
u32 firmlaunch_flags;
u8 unk_0x24[0xc];
u32 app_memtype;
u8 unk_0x34[0xc];
u32 memregion_sz[3];
u8 unk_0x4c[0x14];
u32 firm_ver;
u32 firm_syscore_ver;
u32 firm_ctrsdk_ver;
} osKernelConfig_s;
/// Time reference information struct (filled in by PTM).
typedef struct
{
u64 value_ms; ///< Milliseconds elapsed since January 1900 when this structure was last updated
u64 value_tick; ///< System ticks elapsed since boot when this structure was last updated
s64 sysclock_hz;///< System clock frequency in Hz adjusted using RTC measurements (usually around \ref SYSCLOCK_ARM11)
s64 drift_ms; ///< Measured time drift of the system clock (according to the RTC) in milliseconds since the last update
} osTimeRef_s;
/// Shared system configuration page structure (read-only or read-write depending on exheader).
typedef struct
{
vu32 timeref_cnt;
u8 running_hw;
u8 mcu_hwinfo;
u8 unk_0x06[0x1A];
volatile osTimeRef_s timeref[2];
u8 wifi_macaddr[6];
vu8 wifi_strength;
vu8 network_state;
u8 unk_0x68[0x18];
volatile float slider_3d;
vu8 led_3d;
vu8 led_battery;
vu8 unk_flag;
u8 unk_0x87;
u8 unk_0x88[0x18];
vu64 menu_tid;
vu64 cur_menu_tid;
u8 unk_0xB0[0x10];
vu8 headset_connected;
} osSharedConfig_s;
/// Tick counter.
typedef struct
@ -75,7 +171,7 @@ void* osConvertOldLINEARMemToNew(const void* vaddr);
*
* This can be used to get some details about an error returned by a service call.
*/
const char* osStrError(u32 error);
const char* osStrError(Result error);
/**
* @brief Gets the system's FIRM version.
@ -85,7 +181,7 @@ const char* osStrError(u32 error);
*/
static inline u32 osGetFirmVersion(void)
{
return (*(vu32*)0x1FF80060) & ~0xFF;
return OS_KernelConfig->firm_ver &~ 0xFF;
}
/**
@ -100,19 +196,19 @@ static inline u32 osGetFirmVersion(void)
*/
static inline u32 osGetKernelVersion(void)
{
return (*(vu32*)0x1FF80000) & ~0xFF;
return OS_KernelConfig->kernel_ver &~ 0xFF;
}
/// Gets the system's "core version" (2 on NATIVE_FIRM, 3 on SAFE_FIRM, etc.)
static inline u32 osGetSystemCoreVersion(void)
{
return *(vu32*)0x1FF80010;
return OS_KernelConfig->kernel_syscore_ver;
}
/// Gets the system's memory layout ID (0-5 on Old 3DS, 6-8 on New 3DS)
static inline u32 osGetApplicationMemType(void)
{
return *(vu32*)0x1FF80030;
return OS_KernelConfig->app_memtype;
}
/**
@ -125,7 +221,7 @@ static inline u32 osGetMemRegionSize(MemRegion region)
if(region == MEMREGION_ALL) {
return osGetMemRegionSize(MEMREGION_APPLICATION) + osGetMemRegionSize(MEMREGION_SYSTEM) + osGetMemRegionSize(MEMREGION_BASE);
} else {
return *(vu32*) (0x1FF80040 + (region - 1) * 0x4);
return OS_KernelConfig->memregion_sz[region-1];
}
}
@ -134,18 +230,29 @@ static inline u32 osGetMemRegionSize(MemRegion region)
* @param region Memory region to check.
* @return The number of used bytes of memory.
*/
s64 osGetMemRegionUsed(MemRegion region);
static inline u32 osGetMemRegionUsed(MemRegion region)
{
s64 mem_used;
svcGetSystemInfo(&mem_used, 0, region);
return mem_used;
}
/**
* @brief Gets the number of free bytes within the specified memory region.
* @param region Memory region to check.
* @return The number of free bytes of memory.
*/
static inline s64 osGetMemRegionFree(MemRegion region)
static inline u32 osGetMemRegionFree(MemRegion region)
{
return (s64) osGetMemRegionSize(region) - osGetMemRegionUsed(region);
return osGetMemRegionSize(region) - osGetMemRegionUsed(region);
}
/**
* @brief Reads the latest reference timepoint published by PTM.
* @return Structure (see \ref osTimeRef_s).
*/
osTimeRef_s osGetTimeRef(void);
/**
* @brief Gets the current time.
* @return The number of milliseconds since 1st Jan 1900 00:00.
@ -184,7 +291,7 @@ double osTickCounterRead(const TickCounter* cnt);
* @return The current Wifi signal strength.
*
* Valid values are 0-3:
* - 0 means the singal strength is terrible or the 3DS is disconnected from
* - 0 means the signal strength is terrible or the 3DS is disconnected from
* all networks.
* - 1 means the signal strength is bad.
* - 2 means the signal strength is decent.
@ -196,7 +303,7 @@ double osTickCounterRead(const TickCounter* cnt);
*/
static inline u8 osGetWifiStrength(void)
{
return *(vu8*)0x1FF81066;
return OS_SharedConfig->wifi_strength;
}
/**
@ -205,7 +312,16 @@ static inline u8 osGetWifiStrength(void)
*/
static inline float osGet3DSliderState(void)
{
return *(volatile float*)0x1FF81080;
return OS_SharedConfig->slider_3d;
}
/**
* @brief Checks whether a headset is connected.
* @return true or false.
*/
static inline bool osIsHeadsetConnected(void)
{
return OS_SharedConfig->headset_connected != 0;
}
/**

View File

@ -5,6 +5,7 @@
#pragma once
#include <3ds/types.h>
#include <3ds/services/fs.h>
/// RomFS header.
typedef struct
@ -45,36 +46,48 @@ typedef struct
u16 name[]; ///< Name. (UTF-16)
} romfs_file;
struct romfs_mount;
/**
* @brief Mounts the Application's RomFS.
* @param mount Output mount handle
* @param name Device mount name.
* @remark This function is intended to be used to access one's own RomFS.
* If the application is running as 3DSX, it mounts the embedded RomFS section inside the 3DSX.
* If on the other hand it's an NCCH, it behaves identically to \ref romfsMountFromCurrentProcess.
*/
Result romfsMount(struct romfs_mount **mount);
static inline Result romfsInit(void)
{
return romfsMount(NULL);
}
Result romfsMountSelf(const char *name);
/**
* @brief Mounts RomFS from an open file.
* @param file Handle of the RomFS file.
* @param fd FSFILE handle of the RomFS image.
* @param offset Offset of the RomFS within the file.
* @param mount Output mount handle
* @param name Device mount name.
*/
Result romfsMountFromFile(Handle file, u32 offset, struct romfs_mount **mount);
static inline Result romfsInitFromFile(Handle file, u32 offset)
{
return romfsMountFromFile(file, offset, NULL);
}
Result romfsMountFromFile(Handle fd, u32 offset, const char *name);
/// Bind the RomFS mount
Result romfsBind(struct romfs_mount *mount);
/**
* @brief Mounts RomFS using the current process host program RomFS.
* @param name Device mount name.
*/
Result romfsMountFromCurrentProcess(const char *name);
/**
* @brief Mounts RomFS from the specified title.
* @param tid Title ID
* @param mediatype Mediatype
* @param name Device mount name.
*/
Result romfsMountFromTitle(u64 tid, FS_MediaType mediatype, const char* name);
/// Unmounts the RomFS device.
Result romfsUnmount(struct romfs_mount *mount);
Result romfsUnmount(const char *name);
/// Wrapper for \ref romfsMountSelf with the default "romfs" device name.
static inline Result romfsInit(void)
{
return romfsMountSelf("romfs");
}
/// Wrapper for \ref romfsUnmount with the default "romfs" device name.
static inline Result romfsExit(void)
{
return romfsUnmount(NULL);
return romfsUnmount("romfs");
}

View File

@ -4,6 +4,8 @@
*/
#pragma once
#include <3ds/types.h>
/// Wifi security modes.
typedef enum {
AC_OPEN = 0, ///< Open authentication.
@ -16,6 +18,16 @@ typedef enum {
AC_WPA2_AES = 7, ///< WPA2 AES authentication.
} acSecurityMode;
/// Wifi access point types (bitfield).
enum {
AC_AP_TYPE_NONE = 0, ///< No access point/none allowed.
AC_AP_TYPE_SLOT1 = BIT(1), ///< Slot 1 in System Settings.
AC_AP_TYPE_SLOT2 = BIT(2), ///< Slot 2 in System Settings.
AC_AP_TYPE_SLOT3 = BIT(3), ///< Slot 3 in System Settings.
AC_AP_TYPE_ALL = 0x7FFFFFFF, ///< All access point types allowed.
};
/// Struct to contain the data for connecting to a Wifi network from a stored slot.
typedef struct {
u8 reserved[0x200];
@ -27,12 +39,15 @@ Result acInit(void);
/// Exits AC.
void acExit(void);
/// Gets the current AC session handle.
Handle *acGetSessionHandle(void);
/// Waits for the system to connect to the internet.
Result acWaitInternetConnection(void);
/**
* @brief Gets the connected Wifi status.
* @param out Pointer to output the connected Wifi status to. (0 = not connected, 1 = O3DS Internet, 2 = N3DS Internet)
* @brief Describes the access point the console is currently connected to with AC_AP_TYPE_* flags.
* @param out Pointer to output the combination of AC_AP_TYPE_* flags describing the AP to.
*/
Result ACU_GetWifiStatus(u32 *out);
@ -112,7 +127,7 @@ Result ACU_SetNetworkArea(acuConfig* config, u8 area);
/**
* @brief Sets the slot to use when connecting.
* @param config Pointer to an acuConfig struct used with ACU_CreateDefaultConfig previously.
* @param type Allowed slots flag. BIT(0) for slot 1, BIT(1) for slot 2, BIT(2) for slot 3.
* @param type Allowed AP types bitmask, a combination of AC_AP_TYPE_* flags.
*/
Result ACU_SetAllowApType(acuConfig* config, u8 type);
@ -128,3 +143,15 @@ Result ACU_SetRequestEulaVersion(acuConfig* config);
* @param connectionHandle Handle created with svcCreateEvent to wait on until the connection succeeds or fails.
*/
Result ACU_ConnectAsync(const acuConfig* config, Handle connectionHandle);
/**
* @brief Selects the WiFi configuration slot for further ac:i operations.
* @param slot WiFi slot (0, 1 or 2).
*/
Result ACI_LoadNetworkSetting(u32 slot);
/**
* @brief Fetches the SSID of the previously selected WiFi configuration slot.
* @param[out] ssid Pointer to the output buffer of size 32B the SSID will be stored in.
*/
Result ACI_GetNetworkWirelessEssidSecuritySsid(void *ssid);

View File

@ -56,6 +56,23 @@ typedef struct {
u64 titlesFreeSpace; ///< Free space for titles.
} AM_TWLPartitionInfo;
/// Contains information about a title's content.
typedef struct {
u16 index; ///< Index of the content in the title.
u16 type; ///< ?
u32 contentId; ///< ID of the content in the title.
u64 size; ///< Size of the content in the title.
u8 flags; ///< @ref AM_ContentInfoFlags
u8 padding[7]; ///< Padding
} AM_ContentInfo;
/// Title ContentInfo flags.
typedef enum
{
AM_CONTENT_DOWNLOADED = BIT(0), ///< ?
AM_CONTENT_OWNED = BIT(1) ///< ?
} AM_ContentInfoFlags;
/// Initializes AM. This doesn't initialize with "am:app", see amAppInit().
Result amInit(void);
@ -500,3 +517,22 @@ Result AM_DeleteAllExpiredTitles(FS_MediaType mediatype);
/// Deletes all TWL titles.
Result AM_DeleteAllTwlTitles(void);
/**
* @brief Gets the number of content index installed under the specified DLC title.
* @param[out] count Pointer to output the number of content indices to.
* @param mediatype Media type of the title.
* @param titleID Title ID to retrieve the count for (high-id is 0x0004008C).
*/
Result AMAPP_GetDLCContentInfoCount(u32* count, FS_MediaType mediatype, u64 titleID);
/**
* @brief Gets content infos installed under the specified DLC title.
* @param[out] contentInfoRead Pointer to output the number of content infos read to.
* @param mediatype Media type of the title.
* @param titleID Title ID to retrieve the content infos for (high-id is 0x0004008C).
* @param contentInfoCount Number of content infos to retrieve.
* @param offset Offset from the first content index the count starts at.
* @param[out] contentInfos Pointer to output the content infos read to.
*/
Result AMAPP_ListDLCContentInfos(u32* contentInfoRead, FS_MediaType mediatype, u64 titleID, u32 contentInfoCount, u32 offset, AM_ContentInfo* contentInfos);

View File

@ -148,39 +148,52 @@ void aptExit(void);
*/
Result aptSendCommand(u32* aptcmdbuf);
/**
* @brief Gets whether to allow the system to enter sleep mode.
* @return Whether sleep mode is allowed.
*/
/// Returns true if the application is currently in the foreground.
bool aptIsActive(void);
/// Returns true if the system has told the application to close.
bool aptShouldClose(void);
/// Returns true if the system can enter sleep mode while the application is active.
bool aptIsSleepAllowed(void);
/**
* @brief Sets whether to allow the system to enter sleep mode.
* @param allowed Whether to allow sleep mode.
*/
/// Configures whether the system can enter sleep mode while the application is active.
void aptSetSleepAllowed(bool allowed);
/**
* @brief Gets whether to allow the system to go back to HOME menu.
* @return Whether going back to HOME menu is allowed.
*/
/// Handles incoming sleep mode requests.
void aptHandleSleep(void);
/// Returns true if the user can press the HOME button to jump back to the HOME menu while the application is active.
bool aptIsHomeAllowed(void);
/**
* @brief Sets whether to allow the system to go back to HOME menu.
* @param allowed Whether going back to HOME menu is allowed.
*/
/// Configures whether the user can press the HOME button to jump back to the HOME menu while the application is active.
void aptSetHomeAllowed(bool allowed);
/**
* @brief Returns when the HOME button is pressed.
* @return Whether the HOME button is being pressed.
*/
bool aptIsHomePressed(void);
/// Returns true if the system requires the application to jump back to the HOME menu.
bool aptShouldJumpToHome(void);
/// Returns true if there is an incoming HOME button press rejected by the policy set by \ref aptSetHomeAllowed (use this to show a "no HOME allowed" icon).
bool aptCheckHomePressRejected(void);
/// \deprecated Alias for \ref aptCheckHomePressRejected.
static inline CTR_DEPRECATED bool aptIsHomePressed(void)
{
return aptCheckHomePressRejected();
}
/// Jumps back to the HOME menu.
void aptJumpToHomeMenu(void);
/// Handles incoming jump-to-HOME requests.
static inline void aptHandleJumpToHome(void)
{
if (aptShouldJumpToHome())
aptJumpToHomeMenu();
}
/**
* @brief Processes the current APT status. Generally used within a main loop.
* @return Whether the application should continue running.
* @brief Main function which handles sleep mode and HOME/power buttons - call this at the beginning of every frame.
* @return true if the application should keep running, false otherwise (see \ref aptShouldClose).
*/
bool aptMainLoop(void);
@ -211,17 +224,35 @@ void aptSetMessageCallback(aptMessageCb callback, void* user);
* @param buf Input/output buffer that contains launch parameters on entry and result data on exit.
* @param bufsize Size of the buffer.
* @param handle Handle to pass to the library applet.
* @return Whether the application should continue running after the library applet launch.
*/
bool aptLaunchLibraryApplet(NS_APPID appId, void* buf, size_t bufsize, Handle handle);
void aptLaunchLibraryApplet(NS_APPID appId, void* buf, size_t bufsize, Handle handle);
/// Clears the chainloader state.
void aptClearChainloader(void);
/**
* @brief Sets the chainloader target.
* @brief Configures the chainloader to launch a specific application.
* @param programID ID of the program to chainload to.
* @param mediatype Media type of the program to chainload to.
*/
void aptSetChainloader(u64 programID, u8 mediatype);
/// Configures the chainloader to launch the previous application.
void aptSetChainloaderToCaller(void);
/// Configures the chainloader to relaunch the current application (i.e. soft-reset)
void aptSetChainloaderToSelf(void);
/**
* @brief Sets the "deliver arg" and HMAC for the chainloader, which will
* be passed to the target 3DS/DS(i) application. The meaning of each
* parameter varies on a per-application basis.
* @param deliverArg Deliver arg to pass to the target application.
* @param deliverArgSize Size of the deliver arg, maximum 0x300 bytes.
* @param hmac HMAC buffer, 32 bytes. Use NULL to pass an all-zero dummy HMAC.
*/
void aptSetChainloaderArgs(const void *deliverArg, size_t deliverArgSize, const void *hmac);
/**
* @brief Gets an APT lock handle.
* @param flags Flags to use.
@ -372,6 +403,13 @@ Result APT_AppletUtility(int id, void* out, size_t outSize, const void* in, size
/// Sleeps if shell is closed (?).
Result APT_SleepIfShellClosed(void);
/**
* @brief Locks a transition (?).
* @param transition Transition ID.
* @param flag Flag (?)
*/
Result APT_LockTransition(u32 transition, bool flag);
/**
* @brief Tries to lock a transition (?).
* @param transition Transition ID.
@ -540,4 +578,4 @@ Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr);
* @param sender Pointer to output the sender's AppID to.
* @param received Pointer to output whether an argument was received to.
*/
Result APT_ReceiveDeliverArg(const void* param, size_t paramSize, const void* hmac, u64* sender, bool* received);
Result APT_ReceiveDeliverArg(void* param, size_t paramSize, void* hmac, u64* sender, bool* received);

View File

@ -0,0 +1,90 @@
/**
* @file cdcchk.h
* @brief CODEC Hardware Check service.
*/
#pragma once
#include <3ds/types.h>
/// I2S line enumeration
typedef enum {
CODEC_I2S_LINE_1, ///< Primary I2S line, used by DSP/Mic (configurable)/GBA sound controller.
CODEC_I2S_LINE_2, ///< Secondary I2S line, used by CSND hardware.
} CodecI2sLine;
/// Initializes CDCCHK.
Result cdcChkInit(void);
/// Exits CDCCHK.
void cdcChkExit(void);
/**
* @brief Gets a pointer to the current cdc:CHK session handle.
* @return A pointer to the current cdc:CHK session handle.
*/
Handle *cdcChkGetSessionHandle(void);
/**
* @brief Reads multiple registers from the CODEC, using the old
* SPI hardware interface and a 4MHz baudrate.
* @param pageId CODEC Page ID.
* @param initialRegAddr Address of the CODEC register to start with.
* @param[out] outData Where to write the read data to.
* @param size Number of registers to read (bytes to read, max. 64).
*/
Result CDCCHK_ReadRegisters1(u8 pageId, u8 initialRegAddr, void *outData, size_t size);
/**
* @brief Reads multiple registers from the CODEC, using the new
* SPI hardware interface and a 16MHz baudrate.
* @param pageId CODEC Page ID.
* @param initialRegAddr Address of the CODEC register to start with.
* @param[out] outData Where to read the data to.
* @param size Number of registers to read (bytes to read, max. 64).
*/
Result CDCCHK_ReadRegisters2(u8 pageId, u8 initialRegAddr, void *outData, size_t size);
/**
* @brief Writes multiple registers to the CODEC, using the old
* SPI hardware interface and a 4MHz baudrate.
* @param pageId CODEC Page ID.
* @param initialRegAddr Address of the CODEC register to start with.
* @param data Where to read the data to write from.
* @param size Number of registers to write (bytes to read, max. 64).
*/
Result CDCCHK_WriteRegisters1(u8 pageId, u8 initialRegAddr, const void *data, size_t size);
/**
* @brief Writes multiple registers to the CODEC, using the new
* SPI hardware interface and a 16MHz baudrate.
* @param pageId CODEC Page ID.
* @param initialRegAddr Address of the CODEC register to start with.
* @param data Where to read the data to write from.
* @param size Number of registers to write (bytes to read, max. 64).
*/
Result CDCCHK_WriteRegisters2(u8 pageId, u8 initialRegAddr, const void *data, size_t size);
/**
* @brief Reads a single register from the NTR PMIC.
* @param[out] outData Where to read the data to (1 byte).
* @param regAddr Register address.
* @note The NTR PMIC is emulated by the CODEC hardware and sends
* IRQs to the MCU when relevant.
*/
Result CDCCHK_ReadNtrPmicRegister(u8 *outData, u8 regAddr);
/**
* @brief Writes a single register from the NTR PMIC.
* @param regAddr Register address.
* @param data Data to write (1 byte).
* @note The NTR PMIC is emulated by the CODEC hardware and sends
* IRQs to the MCU when relevant.
*/
Result CDCCHK_WriteNtrPmicRegister(u8 regAddr, u8 data);
/**
* @brief Sets the DAC volume level for the specified I2S line.
* @param i2sLine I2S line to set the volume for.
* @param volume Volume level (-128 to 0).
*/
Result CDCCHK_SetI2sVolume(CodecI2sLine i2sLine, s8 volume);

View File

@ -3,6 +3,7 @@
* @brief CFGU (Configuration) Service
*/
#pragma once
#include <3ds/types.h>
/// Configuration region values.
typedef enum
@ -19,20 +20,32 @@ typedef enum
/// Configuration language values.
typedef enum
{
CFG_LANGUAGE_JP = 0, ///< Japanese
CFG_LANGUAGE_EN = 1, ///< English
CFG_LANGUAGE_FR = 2, ///< French
CFG_LANGUAGE_DE = 3, ///< German
CFG_LANGUAGE_IT = 4, ///< Italian
CFG_LANGUAGE_ES = 5, ///< Spanish
CFG_LANGUAGE_ZH = 6, ///< Simplified Chinese
CFG_LANGUAGE_KO = 7, ///< Korean
CFG_LANGUAGE_NL = 8, ///< Dutch
CFG_LANGUAGE_PT = 9, ///< Portugese
CFG_LANGUAGE_RU = 10, ///< Russian
CFG_LANGUAGE_TW = 11, ///< Traditional Chinese
CFG_LANGUAGE_DEFAULT = -1, ///< Use system language in errorInit
CFG_LANGUAGE_JP, ///< Japanese
CFG_LANGUAGE_EN, ///< English
CFG_LANGUAGE_FR, ///< French
CFG_LANGUAGE_DE, ///< German
CFG_LANGUAGE_IT, ///< Italian
CFG_LANGUAGE_ES, ///< Spanish
CFG_LANGUAGE_ZH, ///< Simplified Chinese
CFG_LANGUAGE_KO, ///< Korean
CFG_LANGUAGE_NL, ///< Dutch
CFG_LANGUAGE_PT, ///< Portugese
CFG_LANGUAGE_RU, ///< Russian
CFG_LANGUAGE_TW, ///< Traditional Chinese
} CFG_Language;
// Configuration system model values.
typedef enum
{
CFG_MODEL_3DS = 0, ///< Old 3DS (CTR)
CFG_MODEL_3DSXL = 1, ///< Old 3DS XL (SPR)
CFG_MODEL_N3DS = 2, ///< New 3DS (KTR)
CFG_MODEL_2DS = 3, ///< Old 2DS (FTR)
CFG_MODEL_N3DSXL = 4, ///< New 3DS XL (RED)
CFG_MODEL_N2DSXL = 5, ///< New 2DS XL (JAN)
} CFG_SystemModel;
/// Initializes CFGU.
Result cfguInit(void);
@ -60,7 +73,7 @@ Result CFGU_GetRegionCanadaUSA(u8* value);
/**
* @brief Gets the system's model.
* @param model Pointer to output the model to. (0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)
* @param model Pointer to output the model to. (see @ref CFG_SystemModel)
*/
Result CFGU_GetSystemModel(u8* model);
@ -96,7 +109,7 @@ Result CFGU_IsNFCSupported(bool* isSupported);
* @param blkID ID of the block to retrieve.
* @param outData Pointer to write the block data to.
*/
Result CFGU_GetConfigInfoBlk2(u32 size, u32 blkID, u8* outData);
Result CFGU_GetConfigInfoBlk2(u32 size, u32 blkID, void* outData);
/**
* @brief Gets a config info block with flags = 4.
@ -104,7 +117,7 @@ Result CFGU_GetConfigInfoBlk2(u32 size, u32 blkID, u8* outData);
* @param blkID ID of the block to retrieve.
* @param outData Pointer to write the block data to.
*/
Result CFG_GetConfigInfoBlk4(u32 size, u32 blkID, u8* outData);
Result CFG_GetConfigInfoBlk4(u32 size, u32 blkID, void* outData);
/**
* @brief Gets a config info block with flags = 8.
@ -112,7 +125,7 @@ Result CFG_GetConfigInfoBlk4(u32 size, u32 blkID, u8* outData);
* @param blkID ID of the block to retrieve.
* @param outData Pointer to write the block data to.
*/
Result CFG_GetConfigInfoBlk8(u32 size, u32 blkID, u8* outData);
Result CFG_GetConfigInfoBlk8(u32 size, u32 blkID, void* outData);
/**
* @brief Sets a config info block with flags = 4.
@ -120,7 +133,7 @@ Result CFG_GetConfigInfoBlk8(u32 size, u32 blkID, u8* outData);
* @param blkID ID of the block to retrieve.
* @param inData Pointer to block data to write.
*/
Result CFG_SetConfigInfoBlk4(u32 size, u32 blkID, u8* inData);
Result CFG_SetConfigInfoBlk4(u32 size, u32 blkID, const void* inData);
/**
* @brief Sets a config info block with flags = 8.
@ -128,7 +141,7 @@ Result CFG_SetConfigInfoBlk4(u32 size, u32 blkID, u8* inData);
* @param blkID ID of the block to retrieve.
* @param inData Pointer to block data to write.
*/
Result CFG_SetConfigInfoBlk8(u32 size, u32 blkID, u8* inData);
Result CFG_SetConfigInfoBlk8(u32 size, u32 blkID, const void* inData);
/**

View File

@ -13,12 +13,23 @@ typedef enum
DSP_INTERRUPT_PIPE = 2 ///< Pipe interrupt.
} DSP_InterruptType;
/// DSP pipe directions.
/// DSP hook types.
typedef enum
{
DSP_PIPE_INPUT = 0, ///< DSP to ARM
DSP_PIPE_OUTPUT = 1 ///< ARM to DSP
} DSP_PipeDirection;
DSPHOOK_ONSLEEP = 0, ///< DSP is going to sleep.
DSPHOOK_ONWAKEUP = 1, ///< DSP is waking up.
DSPHOOK_ONCANCEL = 2, ///< DSP was sleeping and the app was cancelled.
} DSP_HookType;
/// DSP hook function.
typedef void (* dspHookFn)(DSP_HookType hook);
/// DSP hook cookie.
typedef struct tag_dspHookCookie
{
struct tag_dspHookCookie* next; ///< Next cookie.
dspHookFn callback; ///< Hook callback.
} dspHookCookie;
/**
* @brief Initializes the dsp service.
@ -35,6 +46,22 @@ Result dspInit(void);
*/
void dspExit(void);
/// Returns true if a component is loaded, false otherwise.
bool dspIsComponentLoaded(void);
/**
* @brief Sets up a DSP status hook.
* @param cookie Hook cookie to use.
* @param callback Function to call when DSP's status changes.
*/
void dspHook(dspHookCookie* cookie, dspHookFn callback);
/**
* @brief Removes a DSP status hook.
* @param cookie Hook cookie to remove.
*/
void dspUnhook(dspHookCookie* cookie);
/**
* @brief Checks if a headphone is inserted.
* @param is_inserted Pointer to output the insertion status to.
@ -114,7 +141,7 @@ Result DSP_RegisterInterruptEvents(Handle handle, u32 interrupt, u32 channel);
Result DSP_ReadPipeIfPossible(u32 channel, u32 peer, void* buffer, u16 length, u16* length_read);
/**
* @param Writes to a pipe.
* @brief Writes to a pipe.
* @param channel unknown. Usually 2
* @param buffer The message to send to the DSP process
* @param length Length of the message

View File

@ -202,7 +202,7 @@ typedef struct
} FS_IntegrityVerificationSeed;
/// Ext save data information.
typedef struct PACKED
typedef struct CTR_PACKED
{
FS_MediaType mediaType : 8; ///< Media type.
u8 unknown; ///< Unknown.
@ -235,6 +235,14 @@ typedef struct
const void* data; ///< Pointer to FS path data.
} FS_Path;
/// SDMC/NAND speed information
typedef struct
{
bool highSpeedModeEnabled; ///< Whether or not High Speed Mode is enabled.
bool usesHighestClockRate; ///< Whether or not a clock divider of 2 is being used.
u16 sdClkCtrl; ///< The value of the SD_CLK_CTRL register.
} FS_SdMmcSpeedInfo;
/// Filesystem archive handle, providing access to a filesystem's contents.
typedef u64 FS_Archive;
@ -467,13 +475,13 @@ Result FSUSER_GetNandCid(u8* out, u32 length);
* @brief Gets the SDMC speed info.
* @param speedInfo Pointer to output the speed info to.
*/
Result FSUSER_GetSdmcSpeedInfo(u32 *speedInfo);
Result FSUSER_GetSdmcSpeedInfo(FS_SdMmcSpeedInfo *speedInfo);
/**
* @brief Gets the NAND speed info.
* @param speedInfo Pointer to output the speed info to.
*/
Result FSUSER_GetNandSpeedInfo(u32 *speedInfo);
Result FSUSER_GetNandSpeedInfo(FS_SdMmcSpeedInfo *speedInfo);
/**
* @brief Gets the SDMC log.
@ -639,7 +647,7 @@ Result FSUSER_GetLegacyRomHeader(FS_MediaType mediaType, u64 programId, void* he
* @brief Gets the legacy banner data of a program.
* @param mediaType Media type of the program.
* @param programId ID of the program.
* @param header Pointer to output the legacy banner data to. (size = 0x23C0)
* @param banner Pointer to output the legacy banner data to. (size = 0x23C0)
*/
Result FSUSER_GetLegacyBannerData(FS_MediaType mediaType, u64 programId, void* banner);

View File

@ -251,13 +251,13 @@ Result FSPXI_GetNandCid(Handle serviceHandle, void* out, u32 size);
* @brief Gets the SDMC speed info
* @param out Buffer to output the speed info to.
*/
Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, u32* out);
Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, FS_SdMmcSpeedInfo* out);
/**
* @brief Gets the NAND speed info
* @param out Buffer to output the speed info to.
*/
Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, u32* out);
Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, FS_SdMmcSpeedInfo* out);
/**
* @brief Gets the SDMC log

View File

@ -4,7 +4,12 @@
*/
#pragma once
#define GSPGPU_REBASE_REG(r) ((r)-0x1EB00000)
#define GSP_SCREEN_TOP 0 ///< ID of the top screen.
#define GSP_SCREEN_BOTTOM 1 ///< ID of the bottom screen.
#define GSP_SCREEN_WIDTH 240 ///< Width of the top/bottom screens.
#define GSP_SCREEN_HEIGHT_TOP 400 ///< Height of the top screen.
#define GSP_SCREEN_HEIGHT_TOP_2X 800 ///< Height of the top screen (2x).
#define GSP_SCREEN_HEIGHT_BOTTOM 320 ///< Height of the bottom screen.
/// Framebuffer information.
typedef struct
@ -26,7 +31,7 @@ typedef enum
GSP_RGB565_OES=2, ///< RGB565. (2 bytes)
GSP_RGB5_A1_OES=3, ///< RGB5A1. (2 bytes)
GSP_RGBA4_OES=4 ///< RGBA4. (2 bytes)
} GSPGPU_FramebufferFormats;
} GSPGPU_FramebufferFormat;
/// Capture info entry.
typedef struct
@ -57,12 +62,61 @@ typedef enum
GSPGPU_EVENT_MAX, ///< Used to know how many events there are.
} GSPGPU_Event;
/**
* @brief Gets the number of bytes per pixel for the specified format.
* @param format See \ref GSPGPU_FramebufferFormat.
* @return Bytes per pixel.
*/
static inline unsigned gspGetBytesPerPixel(GSPGPU_FramebufferFormat format)
{
switch (format)
{
case GSP_RGBA8_OES:
return 4;
default:
case GSP_BGR8_OES:
return 3;
case GSP_RGB565_OES:
case GSP_RGB5_A1_OES:
case GSP_RGBA4_OES:
return 2;
}
}
/// Initializes GSPGPU.
Result gspInit(void);
/// Exits GSPGPU.
void gspExit(void);
/**
* @brief Gets a pointer to the current gsp::Gpu session handle.
* @return A pointer to the current gsp::Gpu session handle.
*/
Handle *gspGetSessionHandle(void);
/// Returns true if the application currently has GPU rights.
bool gspHasGpuRight(void);
/**
* @brief Presents a buffer to the specified screen.
* @param screen Screen ID (see \ref GSP_SCREEN_TOP and \ref GSP_SCREEN_BOTTOM)
* @param swap Specifies which set of framebuffer registers to configure and activate (0 or 1)
* @param fb_a Pointer to the framebuffer (in stereo mode: left eye)
* @param fb_b Pointer to the secondary framebuffer (only used in stereo mode for the right eye, otherwise pass the same as fb_a)
* @param stride Stride in bytes between scanlines
* @param mode Mode configuration to be written to LCD register
* @return true if a buffer had already been presented to the screen but not processed yet by GSP, false otherwise.
* @note The most recently presented buffer is processed and configured during the specified screen's next VBlank event.
*/
bool gspPresentBuffer(unsigned screen, unsigned swap, const void* fb_a, const void* fb_b, u32 stride, u32 mode);
/**
* @brief Returns true if a prior \ref gspPresentBuffer command is still pending to be processed by GSP.
* @param screen Screen ID (see \ref GSP_SCREEN_TOP and \ref GSP_SCREEN_BOTTOM)
*/
bool gspIsPresentPending(unsigned screen);
/**
* @brief Configures a callback to run when a GSPGPU event occurs.
* @param id ID of the event.
@ -72,17 +126,6 @@ void gspExit(void);
*/
void gspSetEventCallback(GSPGPU_Event id, ThreadFunc cb, void* data, bool oneShot);
/**
* @brief Initializes the GSPGPU event handler.
* @param gspEvent Event handle to use.
* @param gspSharedMem GSP shared memory.
* @param gspThreadId ID of the GSP thread.
*/
Result gspInitEventHandler(Handle gspEvent, vu8* gspSharedMem, u8 gspThreadId);
/// Exits the GSPGPU event handler.
void gspExitEventHandler(void);
/**
* @brief Waits for a GSPGPU event to occur.
* @param id ID of the event.
@ -124,10 +167,9 @@ GSPGPU_Event gspWaitForAnyEvent(void);
/**
* @brief Submits a GX command.
* @param sharedGspCmdBuf Command buffer to use.
* @param gxCommand GX command to execute.
*/
Result gspSubmitGxCommand(u32* sharedGspCmdBuf, u32 gxCommand[0x8]);
Result gspSubmitGxCommand(const u32 gxCommand[0x8]);
/**
* @brief Acquires GPU rights.
@ -164,7 +206,7 @@ Result GSPGPU_SetLcdForceBlack(u8 flags);
* @param screenid ID of the screen to update.
* @param framebufinfo Framebuffer information to update with.
*/
Result GSPGPU_SetBufferSwap(u32 screenid, GSPGPU_FramebufferInfo*framebufinfo);
Result GSPGPU_SetBufferSwap(u32 screenid, const GSPGPU_FramebufferInfo* framebufinfo);
/**
* @brief Flushes memory from the data cache.
@ -186,7 +228,7 @@ Result GSPGPU_InvalidateDataCache(const void* adr, u32 size);
* @param data Data to write.
* @param size Size of the data to write.
*/
Result GSPGPU_WriteHWRegs(u32 regAddr, u32* data, u8 size);
Result GSPGPU_WriteHWRegs(u32 regAddr, const u32* data, u8 size);
/**
* @brief Writes to GPU hardware registers with a mask.
@ -196,7 +238,7 @@ Result GSPGPU_WriteHWRegs(u32 regAddr, u32* data, u8 size);
* @param maskdata Data of the mask.
* @param masksize Size of the mask.
*/
Result GSPGPU_WriteHWRegsWithMask(u32 regAddr, u32* data, u8 datasize, u32* maskdata, u8 masksize);
Result GSPGPU_WriteHWRegsWithMask(u32 regAddr, const u32* data, u8 datasize, const u32* maskdata, u8 masksize);
/**
* @brief Reads from GPU hardware registers.

View File

@ -3,13 +3,14 @@
* @brief GSPLCD service.
*/
#pragma once
#include <3ds/gfx.h> // For gfxScreen_t
#include <3ds/types.h>
#include <3ds/services/gspgpu.h>
/// LCD screens.
enum
{
GSPLCD_SCREEN_TOP = BIT(GFX_TOP), ///< Top screen.
GSPLCD_SCREEN_BOTTOM = BIT(GFX_BOTTOM), ///< Bottom screen.
GSPLCD_SCREEN_TOP = BIT(GSP_SCREEN_TOP), ///< Top screen.
GSPLCD_SCREEN_BOTTOM = BIT(GSP_SCREEN_BOTTOM), ///< Bottom screen.
GSPLCD_SCREEN_BOTH = GSPLCD_SCREEN_TOP | GSPLCD_SCREEN_BOTTOM, ///< Both screens.
};
@ -19,6 +20,12 @@ Result gspLcdInit(void);
/// Exits GSPLCD.
void gspLcdExit(void);
/**
* @brief Gets a pointer to the current gsp::Lcd session handle.
* @return A pointer to the current gsp::Lcd session handle.
*/
Handle *gspLcdGetSessionHandle(void);
/// Powers on both backlights.
Result GSPLCD_PowerOnAllBacklights(void);

View File

@ -1,38 +0,0 @@
/**
* @file hb.h
* @brief HB (Homebrew) service.
*/
#pragma once
// WARNING ! THIS FILE PROVIDES AN INTERFACE TO A NON-OFFICIAL SERVICE PROVIDED BY NINJHAX
// BY USING COMMANDS FROM THIS SERVICE YOU WILL LIKELY MAKE YOUR APPLICATION INCOMPATIBLE WITH OTHER HOMEBREW LAUNCHING METHODS
// A GOOD WAY TO COPE WITH THIS IS TO CHECK THE OUTPUT OF hbInit FOR ERRORS
#include <3ds/types.h>
/// Initializes HB.
Result hbInit(void);
/// Exits HB.
void hbExit(void);
/// Flushes/invalidates the entire data/instruction cache.
Result HB_FlushInvalidateCache(void);
/**
* @brief Fetches the address for Ninjhax 1.x bootloader addresses.
* @param load3dsx void (*callBootloader)(Handle hb, Handle file);
* @param setArgv void (*setArgs)(u32* src, u32 length);
*/
Result HB_GetBootloaderAddresses(void** load3dsx, void** setArgv);
/**
* @brief Changes the permissions of a given number of pages at address addr to mode.
* Should it fail, the appropriate kernel error code will be returned and *reprotectedPages (if not NULL)
* will be set to the number of sequential pages which were successfully reprotected + 1
* @param addr Address to reprotect.
* @param pages Number of pages to reprotect.
* @param mode Mode to reprotect to.
* @param reprotectedPages Number of successfully reprotected pages, on failure.
*/
Result HB_ReprotectMemory(u32* addr, u32 pages, u32 mode, u32* reprotectedPages);

View File

@ -91,3 +91,15 @@ Result IRU_SetIRLEDState(u32 value);
* @param out Pointer to write the IR LED state to.
*/
Result IRU_GetIRLEDRecvState(u32 *out);
/**
* @brief Gets an event which is signaled once a send finishes.
* @param out Pointer to write the event handle to.
*/
Result IRU_GetSendFinishedEvent(Handle *out);
/**
* @brief Gets an event which is signaled once a receive finishes.
* @param out Pointer to write the event handle to.
*/
Result IRU_GetRecvFinishedEvent(Handle *out);

View File

@ -4,8 +4,7 @@
*/
#pragma once
typedef enum
{
typedef enum {
LED_NORMAL = 1, ///< The normal mode of the led
LED_SLEEP_MODE, ///< The led pulses slowly as it does in the sleep mode
LED_OFF, ///< Switch off power led
@ -14,12 +13,29 @@ typedef enum
LED_BLINK_RED, ///< Blinking red state of power led and notification led
} powerLedState;
typedef struct InfoLedPattern
{
u8 delay; ///< Delay between pattern values, 1/16th of a second (1 second = 0x10)
u8 smoothing; ///< Smoothing between pattern values (higher = smoother)
u8 loopDelay; ///< Delay between pattern loops, 1/16th of a second (1 second = 0x10, 0xFF = pattern is played only once)
u8 blinkSpeed; ///< Blink speed, when smoothing == 0x00
u8 redPattern[32]; ///< Pattern for red component
u8 greenPattern[32]; ///< Pattern for green component
u8 bluePattern[32]; ///< Pattern for blue component
} InfoLedPattern;
/// Initializes mcuHwc.
Result mcuHwcInit(void);
/// Exits mcuHwc.
void mcuHwcExit(void);
/**
* @brief Gets the current mcuHwc session handle.
* @return A pointer to the current mcuHwc session handle.
*/
Handle* mcuHwcGetSessionHandle(void);
/**
* @brief Reads data from an i2c device3 register
* @param reg Register number. See https://www.3dbrew.org/wiki/I2C_Registers#Device_3 for more info
@ -60,6 +76,12 @@ Result MCUHWC_GetSoundSliderLevel(u8 *level);
*/
Result MCUHWC_SetWifiLedState(bool state);
/**
* @brief Sets the notification LED pattern
* @param pattern Pattern for the notification LED.
*/
Result MCUHWC_SetInfoLedPattern(const InfoLedPattern* pattern);
/**
* @brief Sets Power LED state
* @param state powerLedState State of power LED.

View File

@ -6,54 +6,54 @@
/// Exclusive states.
typedef enum {
EXCLUSIVE_STATE_NONE = 0,
EXCLUSIVE_STATE_INFRASTRUCTURE = 1,
EXCLUSIVE_STATE_LOCAL_COMMUNICATIONS = 2,
EXCLUSIVE_STATE_STREETPASS = 3,
EXCLUSIVE_STATE_STREETPASS_DATA = 4,
NDM_EXCLUSIVE_STATE_NONE = 0,
NDM_EXCLUSIVE_STATE_INFRASTRUCTURE = 1,
NDM_EXCLUSIVE_STATE_LOCAL_COMMUNICATIONS = 2,
NDM_EXCLUSIVE_STATE_STREETPASS = 3,
NDM_EXCLUSIVE_STATE_STREETPASS_DATA = 4,
} ndmExclusiveState;
/// Current states.
typedef enum {
STATE_INITIAL = 0,
STATE_SUSPENDED = 1,
STATE_INFRASTRUCTURE_CONNECTING = 2,
STATE_INFRASTRUCTURE_CONNECTED = 3,
STATE_INFRASTRUCTURE_WORKING = 4,
STATE_INFRASTRUCTURE_SUSPENDING = 5,
STATE_INFRASTRUCTURE_FORCE_SUSPENDING = 6,
STATE_INFRASTRUCTURE_DISCONNECTING = 7,
STATE_INFRASTRUCTURE_FORCE_DISCONNECTING = 8,
STATE_CEC_WORKING = 9,
STATE_CEC_FORCE_SUSPENDING = 10,
STATE_CEC_SUSPENDING = 11,
NDM_STATE_INITIAL = 0,
NDM_STATE_SUSPENDED = 1,
NDM_STATE_INFRASTRUCTURE_CONNECTING = 2,
NDM_STATE_INFRASTRUCTURE_CONNECTED = 3,
NDM_STATE_INFRASTRUCTURE_WORKING = 4,
NDM_STATE_INFRASTRUCTURE_SUSPENDING = 5,
NDM_STATE_INFRASTRUCTURE_FORCE_SUSPENDING = 6,
NDM_STATE_INFRASTRUCTURE_DISCONNECTING = 7,
NDM_STATE_INFRASTRUCTURE_FORCE_DISCONNECTING = 8,
NDM_STATE_CEC_WORKING = 9,
NDM_STATE_CEC_FORCE_SUSPENDING = 10,
NDM_STATE_CEC_SUSPENDING = 11,
} ndmState;
// Daemons.
typedef enum {
DAEMON_CEC = 0,
DAEMON_BOSS = 1,
DAEMON_NIM = 2,
DAEMON_FRIENDS = 3,
NDM_DAEMON_CEC = 0,
NDM_DAEMON_BOSS = 1,
NDM_DAEMON_NIM = 2,
NDM_DAEMON_FRIENDS = 3,
} ndmDaemon;
/// Used to specify multiple daemons.
typedef enum {
DAEMON_MASK_CEC = BIT(DAEMON_CEC),
DAEMON_MASK_BOSS = BIT(DAEMON_BOSS),
DAEMON_MASK_NIM = BIT(DAEMON_NIM),
DAEMON_MASK_FRIENDS = BIT(DAEMON_FRIENDS),
DAEMON_MASK_BACKGROUOND = DAEMON_MASK_CEC | DAEMON_MASK_BOSS | DAEMON_MASK_NIM,
DAEMON_MASK_ALL = DAEMON_MASK_CEC | DAEMON_MASK_BOSS | DAEMON_MASK_NIM | DAEMON_MASK_FRIENDS,
DAEMON_MASK_DEFAULT = DAEMON_MASK_CEC | DAEMON_MASK_FRIENDS,
NDM_DAEMON_MASK_CEC = BIT(NDM_DAEMON_CEC),
NDM_DAEMON_MASK_BOSS = BIT(NDM_DAEMON_BOSS),
NDM_DAEMON_MASK_NIM = BIT(NDM_DAEMON_NIM),
NDM_DAEMON_MASK_FRIENDS = BIT(NDM_DAEMON_FRIENDS),
NDM_DAEMON_MASK_BACKGROUOND = NDM_DAEMON_MASK_CEC | NDM_DAEMON_MASK_BOSS | NDM_DAEMON_MASK_NIM,
NDM_DAEMON_MASK_ALL = NDM_DAEMON_MASK_CEC | NDM_DAEMON_MASK_BOSS | NDM_DAEMON_MASK_NIM | NDM_DAEMON_MASK_FRIENDS,
NDM_DAEMON_MASK_DEFAULT = NDM_DAEMON_MASK_CEC | NDM_DAEMON_MASK_FRIENDS,
} ndmDaemonMask;
// Daemon status.
typedef enum {
DAEMON_STATUS_BUSY = 0,
DAEMON_STATUS_IDLE = 1,
DAEMON_STATUS_SUSPENDING = 2,
DAEMON_STATUS_SUSPENDED = 3,
NDM_DAEMON_STATUS_BUSY = 0,
NDM_DAEMON_STATUS_IDLE = 1,
NDM_DAEMON_STATUS_SUSPENDING = 2,
NDM_DAEMON_STATUS_SUSPENDED = 3,
} ndmDaemonStatus;
/// Initializes ndmu.
@ -111,10 +111,11 @@ Result NDMU_ResumeScheduler(void);
Result NDMU_GetCurrentState(ndmState *state);
/**
* @brief Returns the daemon state.
* @param state Pointer to write the daemons state to.
* @brief Returns a daemon state.
* @param daemon The specified daemon.
* @param state Pointer to write the daemon state to.
*/
Result NDMU_QueryStatus(ndmDaemonStatus *status);
Result NDMU_QueryStatus(ndmDaemon daemon, ndmDaemonStatus *status);
/**
* @brief Sets the scan interval.

View File

@ -0,0 +1,25 @@
/**
* @file ptmgets.h
* @brief PTMGETS service.
*/
#pragma once
#include <3ds/types.h>
/// Initializes PTMGETS.
Result ptmGetsInit(void);
/// Exits PTMGETS.
void ptmGetsExit(void);
/**
* @brief Gets a pointer to the current ptm:gets session handle.
* @return A pointer to the current ptm:gets session handle.
*/
Handle *ptmGetsGetSessionHandle(void);
/**
* @brief Gets the system time.
* @param[out] outMsY2k The pointer to write the number of milliseconds since 01/01/2000 to.
*/
Result PTMGETS_GetSystemTime(s64 *outMsY2k);

View File

@ -0,0 +1,25 @@
/**
* @file ptmsets.h
* @brief PTMSETS service.
*/
#pragma once
#include <3ds/types.h>
/// Initializes PTMSETS.
Result ptmSetsInit(void);
/// Exits PTMSETS.
void ptmSetsExit(void);
/**
* @brief Gets a pointer to the current ptm:sets session handle.
* @return A pointer to the current ptm:sets session handle.
*/
Handle *ptmSetsGetSessionHandle(void);
/**
* @brief Sets the system time.
* @param msY2k The number of milliseconds since 01/01/2000.
*/
Result PTMSETS_SetSystemTime(s64 msY2k);

View File

@ -48,6 +48,12 @@ Result ptmSysmInit(void);
/// Exits ptm:sysm.
void ptmSysmExit(void);
/**
* @brief Gets a pointer to the current ptm:sysm session handle.
* @return A pointer to the current ptm:sysm session handle.
*/
Handle *ptmSysmGetSessionHandle(void);
/// Requests to enter sleep mode.
Result PTMSYSM_RequestSleep(void);
@ -81,12 +87,18 @@ Result PTMSYSM_GetWakeReason(PtmSleepConfig *outSleepConfig);
/// Cancels the "half-awake" state and fully wakes up the 3DS after some delay.
Result PTMSYSM_Awaken(void);
/**
* @brief Sets the user time by updating the user time offset.
* @param msY2k The number of milliseconds since 01/01/2000.
*/
Result PTMSYSM_SetUserTime(s64 msY2k);
/// Invalidates the "system time" (cfg block 0x30002)
Result PTMSYSM_InvalidateSystemTime(void);
/**
* @brief Reads the time and date coming from the RTC and converts the result.
* @returns The number of milliseconds since 01/01/2000.
* @param[out] outMsY2k The pointer to write the number of milliseconds since 01/01/2000 to.
*/
Result PTMSYSM_GetRtcTime(s64 *outMsY2k);
@ -97,9 +109,10 @@ Result PTMSYSM_GetRtcTime(s64 *outMsY2k);
Result PTMSYSM_SetRtcTime(s64 msY2k);
/**
* @brief Returns 1 if it's a New 3DS, otherwise 0.
* @brief Checks whether the system is a New 3DS.
* @param[out] out Pointer to write the New 3DS flag to.
*/
Result PTMSYSM_CheckNew3DS(void);
Result PTMSYSM_CheckNew3DS(bool *out);
/**
* @brief Configures the New 3DS' CPU clock speed and L2 cache.

View File

@ -10,6 +10,12 @@ Result ptmuInit(void);
/// Exits PTMU.
void ptmuExit(void);
/**
* @brief Gets a pointer to the current ptm:u session handle.
* @return A pointer to the current ptm:u session handle.
*/
Handle *ptmuGetSessionHandle(void);
/**
* @brief Gets the system's current shell state.
* @param out Pointer to write the current shell state to. (0 = closed, 1 = open)

View File

@ -1,57 +1,559 @@
/**
* @file qtm.h
* @brief QTM service.
* @brief QTM services.
*
* QTM is responsible for the following:
* - tracking and predicting the position of the user's eyes. This is done by using the inner
* camera sampling at 320x240px at 30 FPS, and by using the gyroscope to predict the position
* of the eyes between two camera samples (effectively doubling the tracking rate).
* The API reporting eye tracking data is actually *console-agnostic*. This concept is most
* likely covered by patent US9098112B2
* - automatically managing the IR LED emitter used for eye tracking in low-light conditions
* - managing the state of the parallax barrier by controlling the positions of the barrier's
* mask (opaque/transparent state by repeating pattern of 12 units, plus polarity). This is
* done via a TI TCA6416A I2C->Parallel expander (highlighted in yellow in this teardown photo:
* https://guide-images.cdn.ifixit.com/igi/IKlW6WTZKKmapYkt.full, with the expected 12 traces
* being clearly visible near the ribbon cable slot)
* - updating the barrier position according to eye tracking data relative to an optimal setting
* stored in calibration data: this is what Nintendo calls "Super Stable 3D" (SS3D); not done
* when in 2D mode
*
* Head-tracking can be used even if SS3D is disabled.
*
* SS3D works as follows:
* - compute the raw X and Y pixel coordinates for the eye midpoint:
* `rawX = (leftEyeRawPxCoords.x + rightEyeRawPxCoords.x) / 2`
* `rawY = (leftEyeRawPxCoords.y + rightEyeRawPxCoords.y) / 2`
* - rotate the value around the optical Z-axis using the optimal eye-to-camera angle from
* calibration data, with a rotation matrix
* - normalize the X value:
* `xC = (rawX / 320) - 0.5`
* - transform into world space coordinate, using fovX from calibration (convert to radians first).
* Note that this fovX doesn't take lens distortion into account and is slightly different from
* the hardcoded angle used in \ref QTMU_GetTrackingData
* `x = xC * tan(fovX/2)`
* - multiply by length of adjacent side (eye-to-camera distance) to get the length of the opposite
* side. This is the eye horizontal deviation to camera lens in mm, which is then converted to
* number of iod/12 units:
* `delta = x * optimalDistance / (iod/12)`
* - we then obtain the new target position of the parallax barrier (expressed in iod/12 units,
* mod iod/12):
* `pos = centerPos + delta`
* - the value is then rounded to nearest integer. To avoid artifacts, if the rounded value is
* going to increase, then 0.01 is subtracted, and if it is going to decrease, then 0.01 is added.
* The value is rounded again to compute the final discrete value written to the expander (barrier
* position).
* - note: all calculation in QTM and otherwise assume interocular distance to be 62mm (the average).
* Assumedly, if the user's IOD is different, then "optimal distance to screen" effectively changes
* for that user.
*
* QTM services are not present on O3DS, thus caller must call \ref qtmCheckServicesRegistered to check
* if the services are registered. Moreover, since QTM functionality might not always be available (due
* to blacklist or console being N2DSXL), `qtm:u` users should check Result return values, and `qtm:s`
* users can call \ref QTMS_SetQtmStatus to check the actual availability status.
*
* Considering that the eye tracking data reporting API is hardware-agnostic, it is advisable to
* hardcode neither camera aspect ratio (even if it is 4/3 on real hardware) and resolution nor
* field-of-view angle.
*
* There is a separate QTM service, `qtm:c` ("hardware check"), that lets you manipulate parallax barrier
* pattern directly. It is covered in \ref qtmc.h instead.
*/
#pragma once
//See also: http://3dbrew.org/wiki/QTM_Services
#include <3ds/types.h>
/// Head tracking coordinate pair.
typedef struct {
float x; ///< X coordinate.
float y; ///< Y coordinate.
} QTM_HeadTrackingInfoCoord;
/// ID of QTM status data (fully enabled/SS3D disabled) in `cfg`
#define QTM_STATUS_CFG_BLK_ID 0x180000u
/// Head tracking info.
typedef struct {
u8 flags[5]; ///< Flags.
u8 padding[3]; ///< Padding.
float floatdata_x08; ///< Unknown. Not used by System_Settings.
QTM_HeadTrackingInfoCoord coords0[4]; ///< Head coordinates.
u32 unk_x2c[5]; ///< Unknown. Not used by System_Settings.
} QTM_HeadTrackingInfo;
/// ID of QTM calibration data in `cfg`
#define QTM_CAL_CFG_BLK_ID 0x180001u
/// Initializes QTM.
Result qtmInit(void);
/**
* @brief QTM enablement status (when cameras not in use by user), set by `qtm:s`.
* @note Manual IR LED control, camera lux, and `qtm:c` commands remain available
* for use on N3DS and N3DSXL regardless.
*/
typedef enum QtmStatus {
QTM_STATUS_ENABLED = 0, ///< QTM is fully enabled.
QTM_STATUS_SS3D_DISABLED = 1, ///< QTM "super stable 3D" feature is disabled. Parallax barrier hardware state is configured to match O3DS.
/**
* @brief QTM is unavailable: either "blacklisted" (usually by NS) for the current title, **or console is a N2DSXL**.
*
* In this state, all QTM functionality is disabled. This includes "super-stable 3D"
* (ie. auto barrier adjustment) including `qtm:s` manual barrier position setting functions,
* head tracking, IR LED control and camera luminance reporting (400.0 is returned instead).
*
* @note `qtm:c` barrier hardware state setting function (\ref blah) bypasses this state.
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
* to be done, and is in fact never done by official software.
*/
QTM_STATUS_UNAVAILABLE = 2,
} QtmStatus;
/// QTM status data (fully enabled/SS3D disabled) in `cfg`. Usually all-zero on N2DSXL.
typedef struct QtmStatusCfgData {
QtmStatus defaultStats; ///< QTM status at boot (fully enabled or SS3D disabled).
/**
* @brief "Global variable" (.data) section load mode? Unused.
* From CTRAging:
* - 0: "normal"
* - 1: "single reacq"
* - 2: "double reacq"
- 3/4/5: "w2w copy 1/10/100"
*/
u8 gvLoadMode;
u8 _padding[2]; ///< Padding.
} QtmStatusCfgData;
/// QTM calibration data (fully enabled/SS3D disabled) in `cfg`. Usually all-zero on N2DSXL.
typedef struct QtmCalibrationData {
/**
* @brief Neutral (center) barrier position/offset (with slit width of 6 units), when the user is
* facing directly facing the camera, that is to say, their eye midpoint normalized X coord
* in the camera's plane is 0, assuming the user's head is located at the expected viewing distance
* and at the expected eye-to-camera angle (as per the rest of this structure).
* This is expressed in terms of iod/12 units modulo iod/12 (thus, range is 0 to 11 included),
* with IOD (interocular distance) assumed to be 62mm.
* @note This field is floating-point for QTM auto-adjustment purposes, however the actual barrier
* position in hardware is an integer.
* @note This is the field that System Settings lets you add -1.0 to +1.0 to.
* @note Moreover, this field can be directly changed through \ref QTMS_SetCenterBarrierPosition.
*/
float centerBarrierPosition;
float translationX; ///< Lens X coord in inner camera space? Very low value and seems to be unused.
float translationY; ///< Lens Y coord in inner camera space? Very low value and seems to be unused.
float rotationZ; ///< Optimal eye-to-camera angle, in radians, without accounting for lens distortion.
float fovX; ///< Camera's horizontal FoV in degrees, without accounting for lens distortion.
float viewingDistance; ///< Optimal viewing distance between user and top screen, assuming iod to be 62mm.
} QtmCalibrationData;
/// Left eye or right eye, for \ref QtmTrackingData and \ref QtmRawTrackingData
typedef enum QtmEyeSide {
QTM_EYE_LEFT = 0, ///< Left eye.
QTM_EYE_RIGHT = 1, ///< Right eye.
QTM_EYE_NUM, ///< Number of eyes.
} QtmEyeSide;
/// QTM raw eye tracking data
typedef struct QtmRawTrackingData {
/**
* @brief Eye position detected or predicted, equals (confidenceLevel > 0).
* If false, QTM will attempt to make a guess based on gyro data.
* If the console isn't moving either, then QTM will assume the user's eyes are progressively
* moving back to face the screen.
*/
bool eyesTracked; ///< Eye position detected or predicted, equals (confidenceLevel > 0).
u8 _padding[3]; ///< Padding.
u32 singletonQtmPtr; ///< Pointer to eye-tracking singleton pointer, in QTM's .bss, located in N3DS extra memory.
float confidenceLevel; ///< Eye tracking confidence level (0 to 1).
/**
* @brief Raw predicted or detected eye coordinates. Each eye is represented as one point.
* Fractional part is *not* necessarily zero.
* @note X coord is within 0 to 320.
* @note Y coord is within 0 to 240.
*/
float rawEyeCameraCoordinates[QTM_EYE_NUM][2];
float dPitch; ///< Difference in gyro pitch from position at console boot.
float dYaw; ///< Difference in gyro yaw from position at console boot.
float dRoll; ///< Difference in gyro roll from position at console boot.
s64 samplingTick; ///< Time point the current measurements were made.
} QtmRawTrackingData;
/// QTM processed eye tracking data, suitable for 3D programming
typedef struct QtmTrackingData {
bool eyesTracked; ///< Eye position detected or tracked with some confidence, equals (confidenceLevel > 0). Even if false, QTM may make a guess
bool faceDetected; ///< Whether or not the entirety of the user's face has been detected with good confidence.
bool eyesDetected; ///< Whether or not the user's eyes have actually been detected with full confidence.
u8 _unused; ///< Unused.
bool clamped; ///< Whether or not the normalized eye coordinates have been clamped after accounting for lens distortion.
u8 _padding[3]; ///< Padding.
float confidenceLevel; ///< Eye tracking confidence level (0 to 1).
/**
* @brief Normalized eye coordinates, for each eye, after accounting for lens distortion, centered around camera.
* X coord is in the -1 to 1 range, and Y coord range depends on inverse aspect ratio (-0.75 to 0.75 on real hardware).
* @note On real hardware, X coord equals `((rawX / 160.0) - 1.00) * 1.0639` before clamping.
* @note On real hardware, Y coord equals `((rawY / 160.0) - 0.75) * 1.0637` before clamping.
*/
float eyeCameraCoordinates[QTM_EYE_NUM][2];
/**
* @brief Normalized eye coordinates, for each eye, in world space.
* Corresponds to \ref eyeCameraCoordinates multiplied by tangent of field of view.
* @note On real hardware, X coord equals `eyeCameraCoordinates.x * tan(64.9 deg / 2)`.
* @note On real hardware, Y coord equals `eyeCameraCoordinates.x * tan(51.0 deg / 2)`.
*/
float eyeWorldCoordinates[QTM_EYE_NUM][2];
float dPitch; ///< Difference in gyro pitch from position at console boot.
float dYaw; ///< Difference in gyro yaw from position at console boot.
float dRoll; ///< Difference in gyro roll from position at console boot.
s64 samplingTick; ///< Time point the current measurements were made.
} QtmTrackingData;
/// QTM service name enum, excluding `qtm:c`
typedef enum QtmServiceName {
/**
* @brief `qtm:u`: has eye-tracking commands and IR LED control commands, but for some
* reason cannot fetch ambiant lux data from the camera's luminosity sensor.
*/
QTM_SERVICE_USER = 0,
/**
* @brief `qtm:s`: has access to all `qtm:u` commands, plus luminosity sensor, plus
* manual barrier position setting and calibration adjustment commands.
* Automatic barrier control is reenabled on session exit.
*/
QTM_SERVICE_SYSTEM = 1,
/**
* @brief `qtm:sp`: has access to all `qtm:s` (and `qtm:u`) commands, and merely has a
* few more commands that GSP uses to notify QTM of 2D<>3D mode switches and
* power events. Automatic barrier control is reenabled on session exit.
* GSP always keeps a `qtm:sp` sessions open (at least on latest system version),
* whereas NS opens then immediately closes a `qtm:sp` sessions only when dealing
* with a "blacklisted" application (that is, almost never).
*/
QTM_SERVICE_SYSTEM_PROCESS = 2,
} QtmServiceName;
/**
* @brief Check whether or not QTM services are registered.
* @return True on O3DS systems, false on N3DS systems.
*/
bool qtmCheckServicesRegistered(void);
/**
* @brief Initializes QTM (except `qtm:c`).
* Excluding `qtm:c`, QTM has three main services.
* Only 3 sessions (2 until 9.3.0 sysupdate) for ALL services COMBINED, including `qtm:c`,
* can be open at a time.
* Refer to \ref QtmServiceName enum value descriptions to see which service to choose.
*
* @param serviceName QTM service name enum value (corresponding to `qtm:u`, `qtm:s` and `qtm:sp`
* respectively).
* @note Result of \ref qtmCheckServicesRegistered should be checked before calling this function.
*/
Result qtmInit(QtmServiceName serviceName);
/// Exits QTM.
void qtmExit(void);
/**
* @brief Checks whether QTM is initialized.
* @return Whether QTM is initialized.
*/
bool qtmCheckInitialized(void);
/// Checks whether or not a `qtm:u`, `qtm:s` or `qtm:sp` session is active.
bool qtmIsInitialized(void);
/// Returns a pointer to the current `qtm:u` / `qtm:s` / `qtm:sp` session handle.
Handle *qtmGetSessionHandle(void);
/**
* @brief Checks whether a head is fully detected.
* @param info Tracking info to check.
* @brief Gets the current raw eye tracking data, with an optional prediction made for predictionTimePointOrZero = t+dt,
* or for the current time point (QTM makes predictions based on gyro data since inner camera runs at 30 FPS).
*
* @param[out] outData Where to write the raw tracking data to. Cleared to all-zero on failure (instead of being left uninitialized).
* @param predictionTimePointOrZero Either zero, or the time point (in system ticks) for which to make a prediction for.
* Maximum 1 frame (at 30 FPS) in the past, and up to 5 frames in the future.
* @return `0xC8A18008` if camera is in use by user, or `0xC8A183EF` if QTM is unavailable (in particular, QTM is always
* unavailable on N2DSXL), Otherwise, 0 (success). Return value should be checked by caller.
* @note Consider using \ref QTMU_GetTrackingDataEx instead.
*/
bool qtmCheckHeadFullyDetected(QTM_HeadTrackingInfo *info);
Result QTMU_GetRawTrackingDataEx(QtmRawTrackingData *outData, s64 predictionTimePointOrZero);
/**
* @brief Converts QTM coordinates to screen coordinates.
* @param coord Coordinates to convert.
* @param screen_width Width of the screen. Can be NULL to use the default value for the top screen.
* @param screen_height Height of the screen. Can be NULL to use the default value for the top screen.
* @param x Pointer to output the screen X coordinate to.
* @param y Pointer to output the screen Y coordinate to.
* @brief Gets the current raw eye tracking data.
*
* @param[out] outData Where to write the raw tracking data to. Cleared to all-zero on failure (instead of being left uninitialized).
* @return `0xC8A18008` if camera is in use by user, or `0xC8A183EF` if QTM is unavailable (in particular, QTM is always
* unavailable on N2DSXL), Otherwise, 0 (success). Return value should be checked by caller.
* @note Consider using \ref QTMU_GetTrackingData instead.
*/
Result qtmConvertCoordToScreen(QTM_HeadTrackingInfoCoord *coord, float *screen_width, float *screen_height, u32 *x, u32 *y);
static inline Result QTMU_GetRawTrackingData(QtmRawTrackingData *outData)
{
return QTMU_GetRawTrackingDataEx(outData, 0LL);
}
/**
* @brief Gets the current head tracking info.
* @param val Normally 0.
* @param out Pointer to write head tracking info to.
* @brief Gets the current normalized eye tracking data, made suitable for 3D programming with an optional prediction made
* for predictionTimePointOrZero = t+dt, or for the current time point (QTM makes predictions based on gyro data since
* inner camera runs at 30 FPS).
*
* @param[out] outData Where to write the raw tracking data to. Cleared to all-zero on failure (instead of being left uninitialized).
* @param predictionTimePointOrZero Either zero, or the time point (in system ticks) for which to make a prediction for.
* Maximum 1 frame (at 30 FPS) in the past, and up to 5 frames in the future.
* @return `0xC8A18008` if camera is in use by user, or `0xC8A183EF` if QTM is unavailable (in particular, QTM is always
* unavailable on N2DSXL). Otherwise, 0 (success). Return value should be checked by caller.
* @note This can, for example, be used in games to allow the user to control the scene's camera with their own face.
*/
Result QTM_GetHeadTrackingInfo(u64 val, QTM_HeadTrackingInfo* out);
Result QTMU_GetTrackingDataEx(QtmTrackingData *outData, s64 predictionTimePointOrZero);
/**
* @brief Gets the current normalized eye tracking data, made suitable for 3D programming.
*
* @param[out] outData Where to write the raw tracking data to. Cleared to all-zero on failure (instead of being left uninitialized).
* @return `0xC8A18008` if camera is in use by user, or `0xC8A183EF` if QTM is unavailable (in particular, QTM is always
* unavailable on N2DSXL). Otherwise, 0 (success). Return value should be checked by caller.
* @note This can, for example, be used in games to allow the user to control the scene's camera with their own face.
*/
static inline Result QTMU_GetTrackingData(QtmTrackingData *outData)
{
return QTMU_GetTrackingDataEx(outData, 0LL);
}
/**
* @brief Computes an approximation of the horizontal angular field of view of the camera based on eye tracking data.
*
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
* @return Horizontal angular field of view in radians. Corresponds to 64.9 degrees on real hardware.
*/
float qtmComputeFovX(const QtmTrackingData *data);
/**
* @brief Computes an approximation of the vertical angular field of view of the camera based on eye tracking data.
*
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
* @return Vertical angular field of view in radians. Corresponds to 51.0 degrees on real hardware.
*/
float qtmComputeFovY(const QtmTrackingData *data);
/**
* @brief Computes a rough approximation of the inverse of the aspect ration of the camera based on eye tracking data.
*
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
* @return Rough approximation of the inverse of the aspect ratio of the camera. Aspect ratio is exactly 0.75 on real hardware.
*/
float qtmComputeInverseAspectRatio(const QtmTrackingData *data);
/**
* @brief Computes the user's head tilt angle, that is, the angle between the line through both eyes and the camera's
* horizontal axis in camera space.
*
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
* @return Horizontal head angle relative to camera, in radians.
*/
float qtmComputeHeadTiltAngle(const QtmTrackingData *data);
/**
* @brief Estimates the distance between the user's eyes and the camera, based on
* eye tracking data. This may be a little bit inaccurate, as this assumes
* interocular distance of 62mm (like all 3DS software does), and that both
* eyes are at the same distance from the screen.
*
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
* @return Eye-to-camera distance in millimeters.
*/
float qtmEstimateEyeToCameraDistance(const QtmTrackingData *data);
/**
* @brief Temporarily enables manual control of the IR LED by user, disabling its automatic control.
* If not already done, this also turns off the IR LED. This setting is cleared when user closes the console's shell.
* @return Always 0 (success).
*/
Result QTMU_EnableManualIrLedControl(void);
/**
* @brief Temporarily disables manual control of the IR LED by user, re-enabling its automatic control.
* If not already done, this also turns off the IR LED.
* @return Always 0 (success).
*/
Result QTMU_DisableManualIrLedControl(void);
/**
* @brief Turns the IR LED on or off during manual control. \ref QTMU_EnableManualIrLedControl must have been called.
*
* @param on Whether to turn the IR LED on or off.
* @return `0xC8A18005` if manual control was not enabled or if the operation failed, `0xC8A18008` if camera is in use
* by user, or `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL).
* Otherwise, 0 (success).
*/
Result QTMU_SetIrLedStatus(bool on);
/**
* @brief Attempts to clear IR LED overrides from any of the relevant commands in `qtm:u`, `qtm:s` (and `qtm:c`) commands
* by calling \ref QTMU_EnableManualIrLedControl followed by \ref QTMU_DisableManualIrLedControl, so that auto IR LED
* management takes place again.
* @return The value returned by \ref QTMU_DisableManualIrLedControl.
*/
Result qtmClearIrLedOverrides(void);
/**
* @brief Checks whether or not QTM has been blacklisted, ie. that it has been made unavailable.
* In detail, this means that the last call to \ref QTMS_SetQtmStatus was made with argument \ref QTM_STATUS_UNAVAILABLE,
* usually by NS. This feature seems to only be used for some internal test titles.
*
* @param[out] outBlacklisted Whether or not QTM is unavailable. Always true on N2DSXL.
* @return Always 0 (success).
* @note On N2DSXL, even though status is always supposed to be \ref QTM_STATUS_UNAVAILABLE, this function often returns true
* (because NS doesn't change QTM's status if title isn't blacklisted). Do not rely on this for N2DSXL detection.
* @note Refer to https://www.3dbrew.org/wiki/NS_CFA for a list of title UIDs this is used for.
*/
Result QTMU_IsCurrentAppBlacklisted(bool *outBlacklisted);
/**
* @brief Sets the neutral (center) barrier position/offset in calibration, _without_ saving it to `cfg`.
* Takes effect immediately. SS3D works by calculating the position of the eye midpoint, rotated
* by the ideal eye-to-camera angle, expressed in (iod/12 units, iod assumed to be 62mm).
*
* @param position Center barrier position, in terms of iod/12 units modulo iod/12.
* @note This field is floating-point for QTM auto-adjustment purposes, however the actual barrier position
* in hardware is an integer.
* @note This is the field that System Settings lets you add -1.0 to +1.0 to.
* @note There is no "get" counterpart for this.
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
0 (success).
*/
Result QTMS_SetCenterBarrierPosition(float position);
/**
* @brief Gets the average ambient luminance as perceived by the inner camera (in lux).
* If QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), returns 400.0 instead
* of the actual luminance.
*
* @param[out] outLuminanceLux Where to write the luminance to. Always 400.0 on N2DSXL.
* @note Camera exposure, and in particular auto-exposure affects the returned luminance value. This must be
* taken into consideration, because this value can thus surge when user covers the inner camera.
* @return Always 0 (success).
*/
Result QTMS_GetCameraLuminance(float *outLuminanceLux);
/**
* @brief Enables automatic barrier control when in 3D mode with "super stable 3D" enabled.
*
* @note This is automatically called upon `qtm:s` and `qtm:sp` session exit.
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
0 (success).
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
* to be done, and is in fact never done by official software. If that is regardless the case,
* this function here does nothing.
*/
Result QTMS_EnableAutoBarrierControl(void);
/**
* @brief Temporarily disables automatic barrier control (when in 3D mode with "super stable 3D" enabled).
*
* @note This is automatically called upon `qtm:s` and `qtm:sp` session exit.
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
0 (success).
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
* to be done, and is in fact never done by official software. If that is regardless the case,
* this function here does nothing.
*/
Result QTMS_DisableAutoBarrierControl(void);
/**
* @brief Temporarily sets the parallax barrier's position (offset in iod/12 units, assuming slit width of 6 units).
* Does nothing in 2D mode and/or if "super stable 3D" is disabled.
*
* @param position Parallax barrier position (offset in units), must be between 0 and 11 (both included)
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), 0xE0E18002
* if \p position is not in range, otherwise 0 (success).
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
* to be done, and is in fact never done by official software. If that is regardless the case,
* this function here does nothing.
* @note No effect when the screen is in 2D mode.
* @see QTMC_SetBarrierPattern
*/
Result QTMS_SetBarrierPosition(u8 position);
/**
* @brief Gets the current position of the parallax barrier (offset in iod/12 units, slit width of 6 units).
* When "super stable 3D" is disabled, returns 13 instead.
*
* @param[out] outPosition Where to write the barrier's position to.
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
0 (success).
* @note When SS3D is disabled, this returns 13 to \p outPosition . When in 2D mode, the returned position is not
updated.
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
* to be done, and is in fact never done by official software. If that is regardless the case,
* this function here returns 13 to \p outPosition .
* @see QTMC_SetBarrierPattern
*/
Result QTMS_GetCurrentBarrierPosition(u8 *outPosition);
/**
* @brief Temporarily overrides IR LED state. Requires "manual control" from `qtm:u` to be disabled, and has
* lower priority than it.
*
* @param on Whether to turn the IR LED on or off.
* @return `0xC8A18005` if manual control was enabled or if the operation failed, `0xC8A18008` if camera is in use
* by user (unless "hardware check" API enabled), or `0xC8A18009` if QTM is unavailable (in particular,
* QTM is always unavailable on N2DSXL). Otherwise, 0 (success).
*/
Result QTMS_SetIrLedStatusOverride(bool on);
/**
* @brief Sets calibration data, taking effect immediately, and optionally saves it to `cfg`.
*
* @param cal Pointer to calibration data.
* @param saveCalToCfg Whether or not to persist the calibration data in `cfg`.
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
whatever `cfg:s` commands return (if used), or 0 (success).
* @note There is no "get" counterpart for this function, and there is no way to see the current calibration data
in use unless it has been saved to `cfg`.
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
* to be done, and is in fact never done by official software. If that is regardless the case,
* this function here doesn't apply calibrations parameters (they may still be saved, however,
* even though QTM calibration blocks are always normally 0 on N2DSXL).
*/
Result QTMS_SetCalibrationData(const QtmCalibrationData *cal, bool saveCalToCfg);
/**
* @brief Gets the current QTM status (enabled/ss3d disabled/unavailable).
*
* @param[out] outQtmStatus Where to write the QTM status to.
* @return Always 0.
*/
Result QTMS_GetQtmStatus(QtmStatus *outQtmStatus);
/**
* @brief Gets the current QTM status (enabled/ss3d disabled/unavailable). Also sets or clear the
* "blacklisted" flag returned by \ref QTMU_IsCurrentAppBlacklisted.
*
* @param qtmStatus QTM status to set. If equal to \ref QTM_STATUS_UNAVAILABLE, sets the "blacklisted" flag,
* otherwise clears it.
* @return `0xE0E18002` if enum value is invalid, otherwise 0 (success).
* @note System settings uses this to disable super-stable 3D, and NS to "blacklist" (make QTM unavailable)
* specific applications.
*/
Result QTMS_SetQtmStatus(QtmStatus qtmStatus);
/**
* @brief Called by GSP's LCD driver to signal 2D<>3D mode change
* @param newMode 0 for 2D, 1 for 800px 2D (unused for this function, same as 0), 2 for 3D
* @return Always 0 (success).
*/
Result QTMSP_NotifyTopLcdModeChange(u8 newMode);
/**
* @brief Called by GSP's LCD driver during top LCD power-on to signal to QTM that it may power on
* and/or reconfigure then use the TI TCA6416A expander. In the process, QTM re-creates its
* expander thread.
* @return Always 0 (success).
*/
Result QTMSP_NotifyTopLcdPowerOn(void);
/**
* @brief Called by GSP's LCD driver to know whether or not QTM's expander thread is using
* the TI TCA6416A expander; it is waiting for this to become true/false during LCD
* power on/power off to proceed. Always false on N2DSXL.
* @param[out] outActive Where to write the "in use" status to.
* @return Always 0 (success).
*/
Result QTMSP_IsExpanderInUse(bool *outActive);
/**
* @brief Called by GSP's LCD driver during top LCD power-on to signal to QTM that it needs to
* switch the parallax barrier state to a 2D state (all-transparent mask). Causes QTM's
* expander thread to exit, relinquishing its `i2c::QTM` session with it.
* @return Always 0 (success).
*/
Result QTMSP_NotifyTopLcdPowerOff(void);

View File

@ -0,0 +1,117 @@
/**
* @file qtmc.h
* @brief QTM Hardware Check service.
*
* Allows direct control over the parallax barrier's pattern and polarity phase through the
* TI TCA6416A I2C->Parallel expander, as well as control over IR LED state even when camera
* is used by user.
*
* TI TCA6416A I2C->Parallel expander is located on bus I2C1 (PA 0x10161000) device ID 0x40.
*
* The top screen parallax barrier was covered by patent US20030234980A1.
*/
#pragma once
#include <3ds/types.h>
/**
* @brief Initializes `qtm:c`.
* Only 3 sessions (2 until 9.3.0 sysupdate) for ALL services COMBINED, including the main
* services, can be open at a time.
*/
Result qtmcInit(void);
/// Exits `qtm:c`.
void qtmcExit(void);
/// Returns a pointer to the current `qtm:c` session handle.
Handle *qtmcGetSessionHandle(void);
/**
* @brief Starts the QTM Hardware Check API. This must be called before using any other `qtm:c` command,
* and causes barrier pattern to be overriden by what was last set in \ref QTMC_SetBarrierPattern,
* **even in 2D mode**. Also allows IR LED state to be overridden even if user uses the inner camera.
* @return `0xD82183F9` if already started, otherwise 0 (success).
*/
Result QTMC_StartHardwareCheck(void);
/**
* @brief Stops the QTM Hardware Check API. Restore normal barrier and IR LED management behavior.
* @return `0xD82183F8` if API not started, otherwise 0 (success).
*/
Result QTMC_StopHardwareCheck(void);
/**
* @brief Sets the parallax barrier's mask pattern and polarity phase (12+1 bits).
*
* Bit11 to 0 correspond to a repeating barrier mask pattern, 0 meaning the corresponding mask unit is
* transparent and 1 that it is opaque. The direction is: left->right corresponds to MSB->LSB.
*
* Bit12 is the polarity bit.
*
* QTM's expander management thread repeatedly writes (on every loop iteration) the current mask pattern
* plus polarity bit, whether it is normally set or overridden by `qtm:c`, then on the following set,
* negates both (it writes pattern ^ 0x1FFF). This is done at all times, even it 2D mode.
*
* The register being written to are regId 0x02 and 0x03 (output ports).
* TI TCA6416A I2C->Parallel expander is located on bus I2C1 (PA 0x10161000) device ID 0x40.
*
* This function has no effect on N2DSXL.
*
* @param pattern Barrier mask pattern (bit12: polarity, bit11-0: 12-bit mask pattern)
* @return `0xD82183F8` if API not started, otherwise 0 (success).
* @see Patent US20030234980A1 for a description of parallax barriers.
* @example The mask pattern used for super-stable 3D are as follows (position 0 to 11):
*
* 000011111100
* 000001111110
* 000000111111
* 100000011111
* 110000001111
* 111000000111
* 111100000011
* 111110000001
* 111111000000
* 011111100000
* 001111110000
* 000111111000
*
* When SS3D is disabled (ie. it tries to match O3DS behavior), then pattern becomes:
* 111100000111
* Notice that the slit width is reduced from 6 to 5 units there.
*
* For 2D it is all-zero:
* 000000000000
*
* 2D pattern is automatically set on QTM process init and exit.
*/
Result QTMC_SetBarrierPattern(u32 pattern);
/**
* @brief Waits for the expander management thread to (re)initalize the TI TCA6416A I2C->Parallel expander,
* then checks if that expander is behaving as expected (responds with the port direction config
* it has been configured with): it checks whether all ports have been configured as outputs.
*
* On N2DSXL, this function waits forever and never returns.
*
* In detail, the hardware init procedure for the expander is as follows (as done by the expander mgmt. thread):
* - configure enable expander pin on SoC: set GPIO3.bit11 to OUTPUT, then set to 1
* - on the expander (I2C1 deviceId 0x40), set all ports to OUTPUT (regId 0x06, 0x07)
* - on the expander, write 0 (all-transparent mask/2D) to the data registers (regId 0x02, 0x03)
*
* @param[out] outWorking Where to write the working status to. If true, expander is present working.
* If false, the expander is present but is misbehaving. If the function does not
* return, then expander is missing (e.g. on N2DSXL).
* @return `0xD82183F8` if API not started, otherwise 0 (success).
*/
Result QTMC_WaitAndCheckExpanderWorking(bool *outWorking);
/**
* @brief Temporarily overrides IR LED state. Requires "manual control" from `qtm:u` to be disabled, and has
* lower priority than it. Same implementation as \ref QTMS_SetIrLedStatusOverride.
*
* @param on Whether to turn the IR LED on or off.
* @return `0xD82183F8` if API not started, `0xC8A18005` if manual control was enabled or if the operation failed,
* or `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL). Otherwise, 0 (success).
*/
Result QTMC_SetIrLedStatusOverride(bool on);

View File

@ -16,7 +16,7 @@ typedef enum
{
NETOPT_MAC_ADDRESS = 0x1004, ///< The mac address of the interface (u32 mac[6])
NETOPT_ARP_TABLE = 0x3002, ///< The ARP table @see SOCU_ARPTableEntry
NETOPT_IP_INFO = 0x4003, ///< The cureent IP setup @see SOCU_IPInfo
NETOPT_IP_INFO = 0x4003, ///< The current IP setup @see SOCU_IPInfo
NETOPT_IP_MTU = 0x4004, ///< The value of the IP MTU (u32)
NETOPT_ROUTING_TABLE = 0x4006, ///< The routing table @see SOCU_RoutingTableEntry
NETOPT_UDP_NUMBER = 0x8002, ///< The number of sockets in the UDP table (u32)

View File

@ -62,6 +62,15 @@ typedef enum {
MEMPERM_DONTCARE = 0x10000000, ///< Don't care
} MemPerm;
/// Memory regions.
typedef enum
{
MEMREGION_ALL = 0, ///< All regions.
MEMREGION_APPLICATION = 1, ///< APPLICATION memory.
MEMREGION_SYSTEM = 2, ///< SYSTEM memory.
MEMREGION_BASE = 3, ///< BASE memory.
} MemRegion;
/// Memory information.
typedef struct {
u32 base_addr; ///< Base address.
@ -125,10 +134,160 @@ typedef enum {
///@}
///@name Device drivers
///@{
/// DMA transfer state.
typedef enum {
DMASTATE_STARTING = 0, ///< DMA transfer involving at least one device is starting and has not reached DMAWFP yet.
DMASTATE_WFP_DST = 1, ///< DMA channel is in WFP state for the destination device (2nd loop iteration onwards).
DMASTATE_WFP_SRC = 2, ///< DMA channel is in WFP state for the source device (2nd loop iteration onwards).
DMASTATE_RUNNING = 3, ///< DMA transfer is running.
DMASTATE_DONE = 4, ///< DMA transfer is done.
} DmaState;
/// Configuration flags for \ref DmaConfig.
enum {
DMACFG_SRC_IS_DEVICE = BIT(0), ///< DMA source is a device/peripheral. Address will not auto-increment.
DMACFG_DST_IS_DEVICE = BIT(1), ///< DMA destination is a device/peripheral. Address will not auto-increment.
DMACFG_WAIT_AVAILABLE = BIT(2), ///< Make \ref svcStartInterProcessDma wait for the channel to be unlocked.
DMACFG_KEEP_LOCKED = BIT(3), ///< Keep the channel locked after the transfer. Required for \ref svcRestartDma.
DMACFG_USE_SRC_CONFIG = BIT(6), ///< Use the provided source device configuration even if the DMA source is not a device.
DMACFG_USE_DST_CONFIG = BIT(7), ///< Use the provided destination device configuration even if the DMA destination is not a device.
};
/// Configuration flags for \ref svcRestartDma.
enum {
DMARST_UNLOCK = BIT(0), ///< Unlock the channel after transfer.
DMARST_RESUME_DEVICE = BIT(1), ///< Replace DMAFLUSHP instructions by NOP (they may not be regenerated even if this flag is not set).
};
/**
* @brief Device configuration structure, part of \ref DmaConfig.
* @note
* - if (and only if) src/dst is a device, then src/dst won't be auto-incremented.
* - the kernel uses DMAMOV instead of DMAADNH, when having to decrement (possibly working around an erratum);
* this forces all loops to be unrolled -- you need to keep that in mind when using negative increments, as the kernel
* uses a limit of 100 DMA instruction bytes per channel.
*/
typedef struct {
s8 deviceId; ///< DMA device ID.
s8 allowedAlignments; ///< Mask of allowed access alignments (8, 4, 2, 1).
s16 burstSize; ///< Number of bytes transferred in a burst loop. Can be 0 (in which case the max allowed alignment is used as unit).
s16 transferSize; ///< Number of bytes transferred in a "transfer" loop (made of burst loops).
s16 burstStride; ///< Burst loop stride, can be <= 0.
s16 transferStride; ///< "Transfer" loop stride, can be <= 0.
} DmaDeviceConfig;
/// Configuration stucture for \ref svcStartInterProcessDma.
typedef struct {
s8 channelId; ///< Channel ID (Arm11: 0-7, Arm9: 0-1). Use -1 to auto-assign to a free channel (Arm11: 3-7, Arm9: 0-1).
s8 endianSwapSize; ///< Endian swap size (can be 0).
u8 flags; ///< DMACFG_* flags.
u8 _padding;
DmaDeviceConfig srcCfg; ///< Source device configuration, read if \ref DMACFG_SRC_IS_DEVICE and/or \ref DMACFG_USE_SRC_CONFIG are set.
DmaDeviceConfig dstCfg; ///< Destination device configuration, read if \ref DMACFG_SRC_IS_DEVICE and/or \ref DMACFG_USE_SRC_CONFIG are set.
} DmaConfig;
///@}
///@name Debugging
///@{
/// Operations for \ref svcControlPerformanceCounter
typedef enum {
PERFCOUNTEROP_ENABLE = 0, ///< Enable and lock perfmon. functionality.
PERFCOUNTEROP_DISABLE = 1, ///< Disable and forcibly unlock perfmon. functionality.
PERFCOUNTEROP_GET_VALUE = 2, ///< Get the value of a counter register.
PERFCOUNTEROP_SET_VALUE = 3, ///< Set the value of a counter register.
PERFCOUNTEROP_GET_OVERFLOW_FLAGS = 4, ///< Get the overflow flags for all CP15 and SCU counters.
PERFCOUNTEROP_RESET = 5, ///< Reset the value and/or overflow flags of selected counters.
PERFCOUNTEROP_GET_EVENT = 6, ///< Get the event ID associated to a particular counter.
PERFCOUNTEROP_SET_EVENT = 7, ///< Set the event ID associated to a paritcular counter.
PERFCOUNTEROP_SET_VIRTUAL_COUNTER_ENABLED = 8, ///< (Dis)allow the kernel to track counter overflows and to use 64-bit counter values.
} PerfCounterOperation;
/// Performance counter register IDs (CP15 and SCU).
typedef enum
{
// CP15 registers:
PERFCOUNTERREG_CORE_BASE = 0,
PERFCOUNTERREG_CORE_COUNT_REG_0 = PERFCOUNTERREG_CORE_BASE, ///< CP15 PMN0.
PERFCOUNTERREG_CORE_COUNT_REG_1, ///< CP15 PMN1.
PERFCOUNTERREG_CORE_CYCLE_COUNTER, ///< CP15 CCNT.
// SCU registers
PERFCOUNTERREG_SCU_BASE = 0x10,
PERFCOUNTERREG_SCU_0 = PERFCOUNTERREG_SCU_BASE, ///< SCU MN0.
PERFCOUNTERREG_SCU_1, ///< SCU MN1.
PERFCOUNTERREG_SCU_2, ///< SCU MN2.
PERFCOUNTERREG_SCU_3, ///< SCU MN3.
PERFCOUNTERREG_SCU_4, ///< SCU MN4. Prod-N3DS only. IRQ line missing.
PERFCOUNTERREG_SCU_5, ///< SCU MN5. Prod-N3DS only. IRQ line missing.
PERFCOUNTERREG_SCU_6, ///< SCU MN6. Prod-N3DS only. IRQ line missing.
PERFCOUNTERREG_SCU_7, ///< SCU MN7. Prod-N3DS only. IRQ line missing.
} PerfCounterRegister;
/**
* @brief Performance counter event IDs (CP15 or SCU).
*
* @note Refer to:
* - CP15: https://developer.arm.com/documentation/ddi0360/e/control-coprocessor-cp15/register-descriptions/c15--performance-monitor-control-register--pmnc-
* - SCU: https://developer.arm.com/documentation/ddi0360/e/mpcore-private-memory-region/about-the-mpcore-private-memory-region/performance-monitor-event-registers
*/
typedef enum
{
// Core events:
PERFCOUNTEREVT_CORE_BASE = 0x0,
PERFCOUNTEREVT_CORE_INST_CACHE_MISS = PERFCOUNTEREVT_CORE_BASE,
PERFCOUNTEREVT_CORE_STALL_BY_LACK_OF_INST,
PERFCOUNTEREVT_CORE_STALL_BY_DATA_HAZARD,
PERFCOUNTEREVT_CORE_INST_MICRO_TLB_MISS,
PERFCOUNTEREVT_CORE_DATA_MICRO_TLB_MISS,
PERFCOUNTEREVT_CORE_BRANCH_INST,
PERFCOUNTEREVT_CORE_BRANCH_NOT_PREDICTED,
PERFCOUNTEREVT_CORE_BRANCH_MISS_PREDICTED,
PERFCOUNTEREVT_CORE_INST_EXECUTED,
PERFCOUNTEREVT_CORE_FOLDED_INST_EXECUTED,
PERFCOUNTEREVT_CORE_DATA_CACHE_READ,
PERFCOUNTEREVT_CORE_DATA_CACHE_READ_MISS,
PERFCOUNTEREVT_CORE_DATA_CACHE_WRITE,
PERFCOUNTEREVT_CORE_DATA_CACHE_WRITE_MISS,
PERFCOUNTEREVT_CORE_DATA_CACHE_LINE_EVICTION,
PERFCOUNTEREVT_CORE_PC_CHANGED,
PERFCOUNTEREVT_CORE_MAIN_TLB_MISS,
PERFCOUNTEREVT_CORE_EXTERNAL_REQUEST,
PERFCOUNTEREVT_CORE_STALL_BY_LSU_FULL,
PERFCOUNTEREVT_CORE_STORE_BUFFER_DRAIN,
PERFCOUNTEREVT_CORE_MERGE_IN_STORE_BUFFER,
PERFCOUNTEREVT_CORE_CYCLE_COUNT = PERFCOUNTEREVT_CORE_BASE + 0xFF, ///< One cycle elapsed.
PERFCOUNTEREVT_CORE_CYCLE_COUNT_64 = PERFCOUNTEREVT_CORE_BASE + 0xFFF, ///< 64 cycles elapsed.
PERFCOUNTEREVT_SCU_BASE = 0x1000,
PERFCOUNTEREVT_SCU_DISABLED = PERFCOUNTEREVT_SCU_BASE,
PERFCOUNTEREVT_SCU_LINEFILL_MISS_FROM_CORE0,
PERFCOUNTEREVT_SCU_LINEFILL_MISS_FROM_CORE1,
PERFCOUNTEREVT_SCU_LINEFILL_MISS_FROM_CORE2,
PERFCOUNTEREVT_SCU_LINEFILL_MISS_FROM_CORE3,
PERFCOUNTEREVT_SCU_LINEFILL_HIT_FROM_CORE0,
PERFCOUNTEREVT_SCU_LINEFILL_HIT_FROM_CORE1,
PERFCOUNTEREVT_SCU_LINEFILL_HIT_FROM_CORE2,
PERFCOUNTEREVT_SCU_LINEFILL_HIT_FROM_CORE3,
PERFCOUNTEREVT_SCU_LINE_MISSING_FROM_CORE0,
PERFCOUNTEREVT_SCU_LINE_MISSING_FROM_CORE1,
PERFCOUNTEREVT_SCU_LINE_MISSING_FROM_CORE2,
PERFCOUNTEREVT_SCU_LINE_MISSING_FROM_CORE3,
PERFCOUNTEREVT_SCU_LINE_MIGRATION,
PERFCOUNTEREVT_SCU_READ_BUSY_PORT0,
PERFCOUNTEREVT_SCU_READ_BUSY_PORT1,
PERFCOUNTEREVT_SCU_WRITE_BUSY_PORT0,
PERFCOUNTEREVT_SCU_WRITE_BUSY_PORT1,
PERFCOUNTEREVT_SCU_EXTERNAL_READ,
PERFCOUNTEREVT_SCU_EXTERNAL_WRITE,
PERFCOUNTEREVT_SCU_CYCLE_COUNT = PERFCOUNTEREVT_SCU_BASE + 0x1F,
} PerfCounterEvent;
/// Event relating to the attachment of a process.
typedef struct {
u64 program_id; ///< ID of the program.
@ -338,9 +497,8 @@ typedef enum {
/// Information on address space for process. All sizes are in pages (0x1000 bytes)
typedef struct {
u8 name[8]; ///< ASCII name of codeset
u16 unk1;
u16 unk2;
u32 unk3;
u16 version; ///< Version field of codeset (unused)
u16 padding[3]; ///< Padding
u32 text_addr; ///< .text start address
u32 text_size; ///< .text number of pages
u32 ro_addr; ///< .rodata start address
@ -350,9 +508,9 @@ typedef struct {
u32 text_size_total; ///< total pages for .text (aligned)
u32 ro_size_total; ///< total pages for .rodata (aligned)
u32 rw_size_total; ///< total pages for .data, .bss (aligned)
u32 unk4;
u32 padding2; ///< Padding
u64 program_id; ///< Program ID
} CodeSetInfo;
} CodeSetHeader;
/// Information for the main thread of a process.
typedef struct
@ -368,7 +526,7 @@ typedef struct
/**
* @brief Gets the thread local storage buffer.
* @return The thread local storage bufger.
* @return The thread local storage buffer.
*/
static inline void* getThreadLocalStorage(void)
{
@ -379,7 +537,7 @@ static inline void* getThreadLocalStorage(void)
/**
* @brief Gets the thread command buffer.
* @return The thread command bufger.
* @return The thread command buffer.
*/
static inline u32* getThreadCommandBuffer(void)
{
@ -388,13 +546,45 @@ static inline u32* getThreadCommandBuffer(void)
/**
* @brief Gets the thread static buffer.
* @return The thread static bufger.
* @return The thread static buffer.
*/
static inline u32* getThreadStaticBuffers(void)
{
return (u32*)((u8*)getThreadLocalStorage() + 0x180);
}
///@name Device drivers
///@{
/// Writes the default DMA device config that the kernel uses when DMACFG_*_IS_DEVICE and DMACFG_*_USE_CFG are not set
static inline void dmaDeviceConfigInitDefault(DmaDeviceConfig *cfg)
{
// Kernel uses this default instance if _IS_DEVICE and _USE_CFG are not set
*cfg = (DmaDeviceConfig) {
.deviceId = -1,
.allowedAlignments = 8 | 4 | 2 | 1,
.burstSize = 0x80,
.transferSize = 0,
.burstStride = 0x80,
.transferStride = 0,
};
}
/// Initializes a \ref DmaConfig instance with sane defaults for RAM<>RAM tranfers
static inline void dmaConfigInitDefault(DmaConfig *cfg)
{
*cfg = (DmaConfig) {
.channelId = -1,
.endianSwapSize = 0,
.flags = DMACFG_WAIT_AVAILABLE,
._padding = 0,
.srcCfg = {},
.dstCfg = {},
};
}
///@}
///@name Memory management
///@{
/**
@ -478,31 +668,6 @@ Result svcUnmapProcessMemory(Handle process, u32 destAddress, u32 size);
*/
Result svcUnmapMemoryBlock(Handle memblock, u32 addr);
/**
* @brief Begins an inter-process DMA.
* @param[out] dma Pointer to output the handle of the DMA to.
* @param dstProcess Destination process.
* @param dst Buffer to write data to.
* @param srcprocess Source process.
* @param src Buffer to read data from.
* @param size Size of the data to DMA.
* @param dmaConfig DMA configuration data.
*/
Result svcStartInterProcessDma(Handle* dma, Handle dstProcess, void* dst, Handle srcProcess, const void* src, u32 size, void* dmaConfig);
/**
* @brief Terminates an inter-process DMA.
* @param dma Handle of the DMA.
*/
Result svcStopDma(Handle dma);
/**
* @brief Gets the state of an inter-process DMA.
* @param[out] dmaState Pointer to output the state of the DMA to.
* @param dma Handle of the DMA.
*/
Result svcGetDmaState(void* dmaState, Handle dma);
/**
* @brief Queries memory information.
* @param[out] info Pointer to output memory info to.
@ -520,29 +685,6 @@ Result svcQueryMemory(MemInfo* info, PageInfo* out, u32 addr);
*/
Result svcQueryProcessMemory(MemInfo* info, PageInfo* out, Handle process, u32 addr);
/**
* @brief Invalidates a process's data cache.
* @param process Handle of the process.
* @param addr Address to invalidate.
* @param size Size of the memory to invalidate.
*/
Result svcInvalidateProcessDataCache(Handle process, void* addr, u32 size);
/**
* @brief Cleans a process's data cache.
* @param process Handle of the process.
* @param addr Address to clean.
* @param size Size of the memory to clean.
*/
Result svcStoreProcessDataCache(Handle process, void* addr, u32 size);
/**
* @brief Flushes (cleans and invalidates) a process's data cache.
* @param process Handle of the process.
* @param addr Address to flush.
* @param size Size of the memory to flush.
*/
Result svcFlushProcessDataCache(Handle process, void const* addr, u32 size);
///@}
@ -556,7 +698,7 @@ Result svcFlushProcessDataCache(Handle process, void const* addr, u32 size);
Result svcOpenProcess(Handle* process, u32 processId);
/// Exits the current process.
void svcExitProcess() __attribute__((noreturn));
void svcExitProcess(void) __attribute__((noreturn));
/**
* @brief Terminates a process.
@ -613,23 +755,24 @@ Result svcCreatePort(Handle* portServer, Handle* portClient, const char* name, s
Result svcConnectToPort(volatile Handle* out, const char* portName);
/**
* @brief Sets up virtual address space for a new process
* @brief Sets up virtual address space for a new process.
* @param[out] out Pointer to output the codeset handle to.
* @param info Description for setting up the addresses
* @param code_ptr Pointer to .text in shared memory
* @param ro_ptr Pointer to .rodata in shared memory
* @param data_ptr Pointer to .data in shared memory
* @param info Codeset header, contains process name, titleId and segment info.
* @param textSegmentLma Address of executable segment in caller's address space.
* @param roSegmentLma Address of read-only segment in caller's address space.
* @param dataSegmentLma Address of read-write segment in caller's address space.
* @note On success, the provided segments are unmapped from the caller's address space.
*/
Result svcCreateCodeSet(Handle* out, const CodeSetInfo *info, void* code_ptr, void* ro_ptr, void* data_ptr);
Result svcCreateCodeSet(Handle* out, const CodeSetHeader* info, u32 textSegmentLma, u32 roSegmentLma, u32 dataSegmentLma);
/**
* @brief Sets up virtual address space for a new process
* @brief Create a new process.
* @param[out] out Pointer to output the process handle to.
* @param codeset Codeset created for this process
* @param arm11kernelcaps ARM11 Kernel Capabilities from exheader
* @param arm11kernelcaps_num Number of kernel capabilities
* @param codeset Codeset created for this process.
* @param arm11KernelCaps Arm11 Kernel Capabilities from exheader.
* @param numArm11KernelCaps Number of kernel capabilities.
*/
Result svcCreateProcess(Handle* out, Handle codeset, const u32 *arm11kernelcaps, u32 arm11kernelcaps_num);
Result svcCreateProcess(Handle* out, Handle codeset, const u32* arm11KernelCaps, s32 numArm11KernelCaps);
/**
* @brief Gets a process's affinity mask.
@ -915,18 +1058,22 @@ Result svcCreateAddressArbiter(Handle *arbiter);
* @param addr A pointer to a s32 value.
* @param type Type of action to be performed by the arbiter
* @param value Number of threads to signal if using @ref ARBITRATION_SIGNAL, or the value used for comparison.
*
* This will perform an arbitration based on #type. The comparisons are done between #value and the value at the address #addr.
*
* @code
* s32 val=0;
* // Does *nothing* since val >= 0
* svcCreateAddressArbiter(arbiter,&val,ARBITRATION_WAIT_IF_LESS_THAN,0,0);
* // Thread will wait for a signal or wake up after 10000000 nanoseconds because val < 1.
* svcCreateAddressArbiter(arbiter,&val,ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT,1,10000000ULL);
* @endcode
* @param timeout_ns Optional timeout in nanoseconds when using TIMEOUT actions, ignored otherwise. If not needed, use \ref svcArbitrateAddressNoTimeout instead.
* @note Usage of this syscall entails an implicit Data Memory Barrier (dmb).
* @warning Please use \ref syncArbitrateAddressWithTimeout instead.
*/
Result svcArbitrateAddress(Handle arbiter, u32 addr, ArbitrationType type, s32 value, s64 nanoseconds);
Result svcArbitrateAddress(Handle arbiter, u32 addr, ArbitrationType type, s32 value, s64 timeout_ns);
/**
* @brief Same as \ref svcArbitrateAddress but with the timeout_ns parameter undefined.
* @param arbiter Handle of the arbiter
* @param addr A pointer to a s32 value.
* @param type Type of action to be performed by the arbiter
* @param value Number of threads to signal if using @ref ARBITRATION_SIGNAL, or the value used for comparison.
* @note Usage of this syscall entails an implicit Data Memory Barrier (dmb).
* @warning Please use \ref syncArbitrateAddress instead.
*/
Result svcArbitrateAddressNoTimeout(Handle arbiter, u32 addr, ArbitrationType type, s32 value);
/**
* @brief Sends a synchronized request to a session handle.
@ -964,21 +1111,6 @@ Result svcAcceptSession(Handle* session, Handle port);
*/
Result svcReplyAndReceive(s32* index, const Handle* handles, s32 handleCount, Handle replyTarget);
/**
* @brief Binds an event or semaphore handle to an ARM11 interrupt.
* @param interruptId Interrupt identfier (see https://www.3dbrew.org/wiki/ARM11_Interrupts).
* @param eventOrSemaphore Event or semaphore handle to bind to the given interrupt.
* @param priority Priority of the interrupt for the current process.
* @param isManualClear Indicates whether the interrupt has to be manually cleared or not (= level-high active).
*/
Result svcBindInterrupt(u32 interruptId, Handle eventOrSemaphore, s32 priority, bool isManualClear);
/**
* @brief Unbinds an event or semaphore handle from an ARM11 interrupt.
* @param interruptId Interrupt identfier, see (see https://www.3dbrew.org/wiki/ARM11_Interrupts).
* @param eventOrSemaphore Event or semaphore handle to unbind from the given interrupt.
*/
Result svcUnbindInterrupt(u32 interruptId, Handle eventOrSemaphore);
///@}
///@name Time
@ -1048,6 +1180,94 @@ Result svcGetHandleInfo(s64* out, Handle handle, u32 param);
*/
Result svcGetSystemInfo(s64* out, u32 type, s32 param);
/**
* @brief Sets the current kernel state.
* @param type Type of state to set (the other parameters depend on it).
*/
Result svcKernelSetState(u32 type, ...);
///@}
///@name Device drivers
///@{
/**
* @brief Binds an event or semaphore handle to an ARM11 interrupt.
* @param interruptId Interrupt identfier (see https://www.3dbrew.org/wiki/ARM11_Interrupts).
* @param eventOrSemaphore Event or semaphore handle to bind to the given interrupt.
* @param priority Priority of the interrupt for the current process.
* @param isManualClear Indicates whether the interrupt has to be manually cleared or not (= level-high active).
*/
Result svcBindInterrupt(u32 interruptId, Handle eventOrSemaphore, s32 priority, bool isManualClear);
/**
* @brief Unbinds an event or semaphore handle from an ARM11 interrupt.
* @param interruptId Interrupt identfier, see (see https://www.3dbrew.org/wiki/ARM11_Interrupts).
* @param eventOrSemaphore Event or semaphore handle to unbind from the given interrupt.
*/
Result svcUnbindInterrupt(u32 interruptId, Handle eventOrSemaphore);
/**
* @brief Invalidates a process's data cache.
* @param process Handle of the process.
* @param addr Address to invalidate.
* @param size Size of the memory to invalidate.
*/
Result svcInvalidateProcessDataCache(Handle process, u32 addr, u32 size);
/**
* @brief Cleans a process's data cache.
* @param process Handle of the process.
* @param addr Address to clean.
* @param size Size of the memory to clean.
*/
Result svcStoreProcessDataCache(Handle process, u32 addr, u32 size);
/**
* @brief Flushes (cleans and invalidates) a process's data cache.
* @param process Handle of the process.
* @param addr Address to flush.
* @param size Size of the memory to flush.
*/
Result svcFlushProcessDataCache(Handle process, u32 addr, u32 size);
/**
* @brief Begins an inter-process DMA transfer.
* @param[out] dma Pointer to output the handle of the DMA channel object to.
* @param dstProcess Destination process handle.
* @param dstAddr Address in the destination process to write data to.
* @param srcProcess Source process handle.
* @param srcAddr Address in the source to read data from.
* @param size Size of the data to transfer.
* @param cfg Configuration structure.
* @note The handle is signaled when the transfer finishes.
*/
Result svcStartInterProcessDma(Handle *dma, Handle dstProcess, u32 dstAddr, Handle srcProcess, u32 srcAddr, u32 size, const DmaConfig *cfg);
/**
* @brief Stops an inter-process DMA transfer.
* @param dma Handle of the DMA channel object.
*/
Result svcStopDma(Handle dma);
/**
* @brief Gets the state of an inter-process DMA transfer.
* @param[out] state Pointer to output the state of the DMA transfer to.
* @param dma Handle of the DMA channel object.
*/
Result svcGetDmaState(DmaState *state, Handle dma);
/**
* @brief Restarts a DMA transfer, using the same configuration as before.
* @param[out] state Pointer to output the state of the DMA transfer to.
* @param dma Handle of the DMA channel object.
* @param dstAddr Address in the destination process to write data to.
* @param srcAddr Address in the source to read data from.
* @param size Size of the data to transfer.
* @param flags Restart flags, \ref DMARST_UNLOCK and/or \ref DMARST_RESUME_DEVICE.
* @note The first transfer has to be configured with \ref DMACFG_KEEP_LOCKED.
*/
Result svcRestartDma(Handle dma, u32 dstAddr, u32 srcAddr, u32 size, s8 flags);
/**
* @brief Sets the GPU protection register to restrict the range of the GPU DMA. 11.3+ only.
* @param useApplicationRestriction Whether to use the register value used for APPLICATION titles.
@ -1060,14 +1280,8 @@ Result svcSetGpuProt(bool useApplicationRestriction);
*/
Result svcSetWifiEnabled(bool enabled);
/**
* @brief Sets the current kernel state.
* @param type Type of state to set (the other parameters depend on it).
*/
Result svcKernelSetState(u32 type, ...);
///@}
///@name Debugging
///@{
/**
@ -1090,6 +1304,34 @@ void svcBreakRO(UserBreakType breakReason, const void* croInfo, u32 croInfoSize)
* @param length Length of the string to output, needs to be positive.
*/
Result svcOutputDebugString(const char* str, s32 length);
/**
* @brief Controls performance monitoring on the CP15 interface and the SCU.
* The meaning of the parameters depend on the operation.
* @param[out] out Output.
* @param op Operation, see details.
* @param param1 First parameter.
* @param param2 Second parameter.
* @details The operations are the following:
* - \ref PERFCOUNTEROP_ENABLE (void) -> void, tries to enable and lock perfmon. functionality.
* - \ref PERFCOUNTEROP_DISABLE (void) -> void, disable and forcibly unlocks perfmon. functionality.
* - \ref PERFCOUNTEROP_GET_VALUE (\ref PerfCounterRegister reg) -> u64, gets the value of a particular counter register.
* - \ref PERFCOUNTEROP_SET_VALUE (\ref PerfCounterRegister reg, u64 value) -> void, sets the value of a particular counter register.
* - \ref PERFCOUNTEROP_GET_OVERFLOW_FLAGS (void) -> u32, gets the overflow flags of all CP15 and SCU registers.
* - Format is a bitfield of \ref PerfCounterRegister.
* - \ref PERFCOUNTEROP_RESET (u32 valueResetMask, u32 overflowFlagResetMask) -> void, resets the value and/or
* overflow flags of selected registers.
* - Format is two bitfields of \ref PerfCounterRegister.
* - \ref PERFCOUNTEROP_GET_EVENT (\ref PerfCounterRegister reg) -> \ref PerfCounterEvent, gets the event associated
* to a particular counter register.
* - \ref PERFCOUNTEROP_SET_EVENT (\ref PerfCounterRegister reg, \ref PerfCounterEvent) -> void, sets the event associated
* to a particular counter register.
* - \ref PERFCOUNTEROP_SET_VIRTUAL_COUNTER_ENABLED (bool enabled) -> void, (dis)allows the kernel to track counter overflows
* and to use 64-bit counter values.
*/
Result svcControlPerformanceCounter(u64 *out, PerfCounterOperation op, u32 param1, u64 param2);
/**
* @brief Creates a debug handle for an active process.
* @param[out] debug Pointer to output the created debug handle to.

114
libctru/include/3ds/synchronization.h Normal file → Executable file
View File

@ -12,6 +12,9 @@ typedef _LOCK_T LightLock;
/// A recursive lock.
typedef _LOCK_RECURSIVE_T RecursiveLock;
/// A condition variable.
typedef s32 CondVar;
/// A light event.
typedef struct
{
@ -33,6 +36,18 @@ static inline void __dsb(void)
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 4" :: [val] "r" (0) : "memory");
}
/// Performs a Data Memory Barrier operation.
static inline void __dmb(void)
{
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory");
}
/// Performs an Instruction Synchronization Barrier (officially "flush prefetch buffer") operation.
static inline void __isb(void)
{
__asm__ __volatile__("mcr p15, 0, %[val], c7, c5, 4" :: [val] "r" (0) : "memory");
}
/// Performs a clrex operation.
static inline void __clrex(void)
{
@ -126,10 +141,40 @@ static inline bool __strexb(u8* addr, u8 val)
#define AtomicSwap(ptr, value) __atomic_exchange_n((u32*)(ptr), (value), __ATOMIC_SEQ_CST)
/**
* @brief Retrieves the synchronization subsystem's address arbiter handle.
* @return The synchronization subsystem's address arbiter handle.
* @brief Function used to implement user-mode synchronization primitives.
* @param addr Pointer to a signed 32-bit value whose address will be used to identify waiting threads.
* @param type Type of action to be performed by the arbiter
* @param value Number of threads to signal if using @ref ARBITRATION_SIGNAL, or the value used for comparison.
*
* This will perform an arbitration based on #type. The comparisons are done between #value and the value at the address #addr.
*
* @code
* s32 val=0;
* // Does *nothing* since val >= 0
* syncArbitrateAddress(&val,ARBITRATION_WAIT_IF_LESS_THAN,0);
* @endcode
*
* @note Usage of this function entails an implicit Data Memory Barrier (dmb).
*/
Handle __sync_get_arbiter(void);
Result syncArbitrateAddress(s32* addr, ArbitrationType type, s32 value);
/**
* @brief Function used to implement user-mode synchronization primitives (with timeout).
* @param addr Pointer to a signed 32-bit value whose address will be used to identify waiting threads.
* @param type Type of action to be performed by the arbiter (must use \ref ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT or \ref ARBITRATION_DECREMENT_AND_WAIT_IF_LESS_THAN_TIMEOUT)
* @param value Number of threads to signal if using @ref ARBITRATION_SIGNAL, or the value used for comparison.
*
* This will perform an arbitration based on #type. The comparisons are done between #value and the value at the address #addr.
*
* @code
* s32 val=0;
* // Thread will wait for a signal or wake up after 10000000 nanoseconds because val < 1.
* syncArbitrateAddressWithTimeout(&val,ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT,1,10000000LL);
* @endcode
*
* @note Usage of this function entails an implicit Data Memory Barrier (dmb).
*/
Result syncArbitrateAddressWithTimeout(s32* addr, ArbitrationType type, s32 value, s64 timeout_ns);
/**
* @brief Initializes a light lock.
@ -181,6 +226,53 @@ int RecursiveLock_TryLock(RecursiveLock* lock);
*/
void RecursiveLock_Unlock(RecursiveLock* lock);
/**
* @brief Initializes a condition variable.
* @param cv Pointer to the condition variable.
*/
void CondVar_Init(CondVar* cv);
/**
* @brief Waits on a condition variable.
* @param cv Pointer to the condition variable.
* @param lock Pointer to the lock to atomically unlock/relock during the wait.
*/
void CondVar_Wait(CondVar* cv, LightLock* lock);
/**
* @brief Waits on a condition variable with a timeout.
* @param cv Pointer to the condition variable.
* @param lock Pointer to the lock to atomically unlock/relock during the wait.
* @param timeout_ns Timeout in nanoseconds.
* @return Zero on success, non-zero on failure.
*/
int CondVar_WaitTimeout(CondVar* cv, LightLock* lock, s64 timeout_ns);
/**
* @brief Wakes up threads waiting on a condition variable.
* @param cv Pointer to the condition variable.
* @param num_threads Maximum number of threads to wake up (or \ref ARBITRATION_SIGNAL_ALL to wake them all).
*/
void CondVar_WakeUp(CondVar* cv, s32 num_threads);
/**
* @brief Wakes up a single thread waiting on a condition variable.
* @param cv Pointer to the condition variable.
*/
static inline void CondVar_Signal(CondVar* cv)
{
CondVar_WakeUp(cv, 1);
}
/**
* @brief Wakes up all threads waiting on a condition variable.
* @param cv Pointer to the condition variable.
*/
static inline void CondVar_Broadcast(CondVar* cv)
{
CondVar_WakeUp(cv, ARBITRATION_SIGNAL_ALL);
}
/**
* @brief Initializes a light event.
* @param event Pointer to the event.
@ -219,6 +311,14 @@ int LightEvent_TryWait(LightEvent* event);
*/
void LightEvent_Wait(LightEvent* event);
/**
* @brief Waits on a light event until either the event is signaled or the timeout is reached.
* @param event Pointer to the event.
* @param timeout_ns Timeout in nanoseconds.
* @return Non-zero on timeout, zero otherwise.
*/
int LightEvent_WaitTimeout(LightEvent* event, s64 timeout_ns);
/**
* @brief Initializes a light semaphore.
* @param event Pointer to the semaphore.
@ -234,6 +334,14 @@ void LightSemaphore_Init(LightSemaphore* semaphore, s16 initial_count, s16 max_c
*/
void LightSemaphore_Acquire(LightSemaphore* semaphore, s32 count);
/**
* @brief Attempts to acquire a light semaphore.
* @param semaphore Pointer to the semaphore.
* @param count Acquire count
* @return Zero on success, non-zero on failure
*/
int LightSemaphore_TryAcquire(LightSemaphore* semaphore, s32 count);
/**
* @brief Releases a light semaphore.
* @param semaphore Pointer to the semaphore.

View File

@ -33,7 +33,7 @@ typedef void (*ExceptionHandler)(ERRF_ExceptionInfo* excep, CpuRegisters* regs);
* For userland apps, this has to be within the range [0x18;0x3F].
* The main thread usually has a priority of 0x30, but not always. Use svcGetThreadPriority() if you need
* to create a thread with a priority that is explicitly greater or smaller than that of the main thread.
* @param affinity The ID of the processor the thread should be ran on. Processor IDs are labeled starting from 0.
* @param core_id The ID of the processor the thread should be ran on. Processor IDs are labeled starting from 0.
* On Old3DS it must be <2, and on New3DS it must be <4.
* Pass -1 to execute the thread on all CPUs and -2 to execute the thread on the default CPU (read from the Exheader).
* @param detached When set to true, the thread is automatically freed when it finishes.
@ -48,7 +48,7 @@ typedef void (*ExceptionHandler)(ERRF_ExceptionInfo* excep, CpuRegisters* regs);
* @note Default exit code of a thread is 0.
* @warning @ref svcExitThread should never be called from the thread, use @ref threadExit instead.
*/
Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int prio, int affinity, bool detached);
Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int prio, int core_id, bool detached);
/**
* @brief Retrieves the OS thread handle of a libctru thread.
@ -117,4 +117,7 @@ static inline void threadOnException(ExceptionHandler handler, void* stack_top,
*(u32*)(tls + 0x40) = (u32)handler;
*(u32*)(tls + 0x44) = (u32)stack_top;
*(u32*)(tls + 0x48) = (u32)exception_data;
__dsb();
__isb();
}

View File

@ -47,16 +47,16 @@ typedef void (*voidfn)(void);
#define BIT(n) (1U<<(n))
/// Aligns a struct (and other types?) to m, making sure that the size of the struct is a multiple of m.
#define ALIGN(m) __attribute__((aligned(m)))
#define CTR_ALIGN(m) __attribute__((aligned(m)))
/// Packs a struct (and other types?) so it won't include padding bytes.
#define PACKED __attribute__((packed))
#define CTR_PACKED __attribute__((packed))
#ifndef LIBCTRU_NO_DEPRECATION
#ifndef CTR_NO_DEPRECATION
/// Flags a function as deprecated.
#define DEPRECATED __attribute__ ((deprecated))
#define CTR_DEPRECATED __attribute__ ((deprecated))
#else
/// Flags a function as deprecated.
#define DEPRECATED
#define CTR_DEPRECATED
#endif
/// Structure representing CPU registers
@ -71,7 +71,7 @@ typedef struct {
/// Structure representing FPU registers
typedef struct {
union {
struct PACKED { double d[16]; }; ///< d0-d15.
struct CTR_PACKED { double d[16]; }; ///< d0-d15.
float s[32]; ///< s0-s31.
};
u32 fpscr; ///< fpscr.

View File

@ -9,14 +9,13 @@
#define TRY_AGAIN 4
struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
char *h_addr;
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
uint16_t h_addrtype; /* host address type */
uint16_t h_length; /* length of address */
char **h_addr_list; /* list of addresses from name server */
};
#define h_addr h_addr_list[0] /* for backward compatibility */
#define AI_PASSIVE 0x01
#define AI_CANONNAME 0x02

View File

@ -1,7 +1,5 @@
#pragma once
#include <3ds/types.h>
#define POLLIN 0x01
#define POLLPRI 0x02
#define POLLHUP 0x04 // unknown ???
@ -9,7 +7,7 @@
#define POLLOUT 0x10
#define POLLNVAL 0x20
typedef u32 nfds_t;
typedef unsigned int nfds_t;
struct pollfd
{

View File

@ -44,6 +44,8 @@
#define SO_BROADCAST 0x0000 // unrequired, included for compatibility
#define _SOCKLEN_T_DECLARED
typedef uint32_t socklen_t;
typedef uint16_t sa_family_t;

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="1"/>

48
libctru/source/3dslink.c Normal file
View File

@ -0,0 +1,48 @@
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <3ds/3dslink.h>
struct in_addr __3dslink_host = {0};
static int sock = -1;
int link3dsConnectToHost(bool redirStdout, bool redirStderr)
{
int ret = -1;
struct sockaddr_in srv_addr;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (!sock) {
return ret;
}
bzero(&srv_addr, sizeof srv_addr);
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr = __3dslink_host;
srv_addr.sin_port = htons(LINK3DS_COMM_PORT);
ret = connect(sock, (struct sockaddr *) &srv_addr, sizeof(srv_addr));
if (ret != 0) {
close(sock);
return -1;
}
if (redirStdout) {
// redirect stdout
fflush(stdout);
dup2(sock, STDOUT_FILENO);
}
if (redirStderr) {
// redirect stderr
fflush(stderr);
dup2(sock, STDERR_FILENO);
}
return sock;
}

View File

@ -27,18 +27,9 @@ static bool linearInit()
void* linearMemAlign(size_t size, size_t alignment)
{
// Enforce minimum alignment
if (alignment < 16)
alignment = 16;
// Convert alignment to shift amount
int shift;
for (shift = 4; shift < 32; shift ++)
{
if ((1U<<shift) == alignment)
break;
}
if (shift == 32) // Invalid alignment
// Convert alignment to shift
int shift = alignmentToShift(alignment);
if (shift < 0)
return nullptr;
// Initialize the pool if it is not ready

View File

@ -2,6 +2,15 @@
#include <3ds/types.h>
#include <stdlib.h>
static inline int alignmentToShift(size_t alignment)
{
if (alignment < 16)
alignment = 16;
else if (alignment & (alignment - 1))
return -1; // Not a power of two
return __builtin_ffs(alignment)-1;
}
struct MemChunk
{
u8* addr;

View File

@ -1,6 +1,7 @@
extern "C"
{
#include <3ds/types.h>
#include <3ds/os.h>
#include <3ds/allocator/vram.h>
#include <3ds/util/rbtree.h>
}
@ -8,60 +9,107 @@ extern "C"
#include "mem_pool.h"
#include "addrmap.h"
static MemPool sVramPool;
static MemPool sVramPoolA, sVramPoolB;
static bool vramInit()
{
auto blk = MemBlock::Create((u8*)0x1F000000, 0x00600000);
if (blk)
if (sVramPoolA.Ready() || sVramPoolB.Ready())
return true;
auto blkA = MemBlock::Create((u8*)OS_VRAM_VADDR, OS_VRAM_SIZE/2);
if (!blkA)
return false;
auto blkB = MemBlock::Create((u8*)OS_VRAM_VADDR + OS_VRAM_SIZE/2, OS_VRAM_SIZE/2);
if (!blkB)
{
sVramPool.AddBlock(blk);
free(blkA);
return false;
}
sVramPoolA.AddBlock(blkA);
sVramPoolB.AddBlock(blkB);
rbtree_init(&sAddrMap, addrMapNodeComparator);
return true;
}
return false;
static MemPool* vramPoolForAddr(void* addr)
{
uintptr_t addr_ = (uintptr_t)addr;
if (addr_ < OS_VRAM_VADDR)
return nullptr;
if (addr_ < OS_VRAM_VADDR + OS_VRAM_SIZE/2)
return &sVramPoolA;
if (addr_ < OS_VRAM_VADDR + OS_VRAM_SIZE)
return &sVramPoolB;
return nullptr;
}
void* vramAlloc(size_t size)
{
return vramMemAlignAt(size, 0x80, VRAM_ALLOC_ANY);
}
void* vramAllocAt(size_t size, vramAllocPos pos)
{
return vramMemAlignAt(size, 0x80, pos);
}
void* vramMemAlign(size_t size, size_t alignment)
{
// Enforce minimum alignment
if (alignment < 16)
alignment = 16;
// Convert alignment to shift amount
int shift;
for (shift = 4; shift < 32; shift ++)
{
if ((1U<<shift) == alignment)
break;
return vramMemAlignAt(size, alignment, VRAM_ALLOC_ANY);
}
if (shift == 32) // Invalid alignment
void* vramMemAlignAt(size_t size, size_t alignment, vramAllocPos pos)
{
// Convert alignment to shift
int shift = alignmentToShift(alignment);
if (shift < 0)
return nullptr;
// Initialize the pool if it is not ready
if (!sVramPool.Ready() && !vramInit())
// Initialize the allocator if it is not ready
if (!vramInit())
return nullptr;
// Allocate the chunk
MemChunk chunk;
if (!sVramPool.Allocate(chunk, size, shift))
bool didAlloc = false;
switch (pos & VRAM_ALLOC_ANY)
{
default:
break;
case VRAM_ALLOC_A:
didAlloc = sVramPoolA.Allocate(chunk, size, shift);
break;
case VRAM_ALLOC_B:
didAlloc = sVramPoolB.Allocate(chunk, size, shift);
break;
case VRAM_ALLOC_ANY:
{
// Crude attempt at "load balancing" VRAM A and B
bool prefer_a = sVramPoolA.GetFreeSpace() >= sVramPoolB.GetFreeSpace();
MemPool& firstPool = prefer_a ? sVramPoolA : sVramPoolB;
MemPool& secondPool = prefer_a ? sVramPoolB : sVramPoolA;
didAlloc = firstPool.Allocate(chunk, size, shift);
if (!didAlloc) didAlloc = secondPool.Allocate(chunk, size, shift);
break;
}
}
if (!didAlloc)
return nullptr;
auto node = newNode(chunk);
if (!node)
{
sVramPool.Deallocate(chunk);
vramPoolForAddr(chunk.addr)->Deallocate(chunk);
return nullptr;
}
if (rbtree_insert(&sAddrMap, &node->node));
return chunk.addr;
}
void* vramAlloc(size_t size)
{
return vramMemAlign(size, 0x80);
}
void* vramRealloc(void* mem, size_t size)
{
// TODO
@ -80,7 +128,7 @@ void vramFree(void* mem)
if (!node) return;
// Free the chunk
sVramPool.Deallocate(node->chunk);
vramPoolForAddr(mem)->Deallocate(node->chunk);
// Free the node
delNode(node);
@ -88,5 +136,5 @@ void vramFree(void* mem)
u32 vramSpaceFree()
{
return sVramPool.GetFreeSpace();
return sVramPoolA.GetFreeSpace() + sVramPoolB.GetFreeSpace();
}

View File

@ -1,4 +1,3 @@
#include <3ds.h>
#include <string.h>
#include <3ds/types.h>
#include <3ds/synchronization.h>
@ -11,7 +10,7 @@ void errorInit(errorConf* err, errorType type, CFG_Language lang)
{
memset(err, 0, sizeof(*err));
err->type = type;
err->useLanguage = lang;
err->useLanguage = lang + 1;
err->upperScreenFlag = ERROR_NORMAL;
err->eulaVersion = 0;
err->homeButton = true;

View File

@ -18,7 +18,7 @@ void miiSelectorInit(MiiSelectorConf *conf)
conf->mii_whitelist[i] = 1;
}
Result miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn *returnbuf)
void miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn *returnbuf)
{
union {
MiiSelectorConf config;
@ -28,11 +28,9 @@ Result miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn *returnb
memcpy(&ctx.config, conf, sizeof(MiiSelectorConf));
ctx.config.magic = MIISELECTOR_MAGIC;
Result ret = aptLaunchLibraryApplet(APPID_APPLETED, &ctx.config, sizeof(MiiSelectorConf), 0);
if(R_SUCCEEDED(ret) && returnbuf)
aptLaunchLibraryApplet(APPID_APPLETED, &ctx.config, sizeof(MiiSelectorConf), 0);
if(returnbuf)
memcpy(returnbuf, &ctx.ret, sizeof(MiiSelectorReturn));
return ret;
}
static void miiSelectorConvertToUTF8(char* out, const u16* in, int max)

View File

@ -238,20 +238,11 @@ SwkbdButton swkbdInputText(SwkbdState* swkbd, char* buf, size_t bufsize)
// Launch swkbd
memset(swkbd->reserved, 0, sizeof(swkbd->reserved));
if (extra.callback) aptSetMessageCallback(swkbdMessageCallback, &extra);
bool ret = aptLaunchLibraryApplet(APPID_SOFTWARE_KEYBOARD, swkbd, sizeof(*swkbd), swkbdSharedMemHandle);
aptLaunchLibraryApplet(APPID_SOFTWARE_KEYBOARD, swkbd, sizeof(*swkbd), swkbdSharedMemHandle);
if (extra.callback) aptSetMessageCallback(NULL, NULL);
svcCloseHandle(swkbdSharedMemHandle);
SwkbdButton button = SWKBD_BUTTON_NONE;
if (ret)
{
u16* text16 = (u16*)(swkbdSharedMem+swkbd->text_offset);
text16[swkbd->text_length] = 0;
swkbdConvertToUTF8(buf, text16, bufsize-1);
if (swkbd->save_state_flags & BIT(0)) memcpy(extra.status_data, swkbdSharedMem+swkbd->status_offset, sizeof(SwkbdStatusData));
if (swkbd->save_state_flags & BIT(1)) memcpy(extra.learning_data, swkbdSharedMem+swkbd->learning_offset, sizeof(SwkbdLearningData));
switch (swkbd->result)
{
case SWKBD_D1_CLICK0:
@ -269,7 +260,12 @@ SwkbdButton swkbdInputText(SwkbdState* swkbd, char* buf, size_t bufsize)
default:
break;
}
}
u16* text16 = (u16*)(swkbdSharedMem+swkbd->text_offset);
text16[swkbd->text_length] = 0;
swkbdConvertToUTF8(buf, text16, bufsize-1);
if (swkbd->save_state_flags & BIT(0)) memcpy(extra.status_data, swkbdSharedMem+swkbd->status_offset, sizeof(SwkbdStatusData));
if (swkbd->save_state_flags & BIT(1)) memcpy(extra.learning_data, swkbdSharedMem+swkbd->learning_offset, sizeof(SwkbdLearningData));
free(swkbdSharedMem);
return button;

View File

@ -14,6 +14,7 @@
#include <3ds/services/fs.h>
#include <3ds/util/utf.h>
#include "path_buf.h"
/*! @internal
*
@ -106,13 +107,10 @@ typedef struct
static bool archive_initialized = false;
static s32 archive_device_cwd;
static archive_fsdevice archive_devices[32];
static archive_fsdevice archive_devices[8];
/*! @endcond */
static __thread char __fixedpath[PATH_MAX+1];
static __thread uint16_t __utf16path[PATH_MAX+1];
static archive_fsdevice *archiveFindDevice(const char *name)
{
u32 i;
@ -204,17 +202,19 @@ archive_fixpath(struct _reent *r,
}
if(path[0] == '/')
strncpy(__fixedpath, path, PATH_MAX);
strncpy(__ctru_dev_path_buf, path, PATH_MAX);
else
{
strncpy(__fixedpath, dev->cwd, PATH_MAX);
__fixedpath[PATH_MAX] = '\0';
strncat(__fixedpath, path, PATH_MAX);
size_t cwdlen = strlen(dev->cwd);
strncpy(__ctru_dev_path_buf, dev->cwd, PATH_MAX);
__ctru_dev_path_buf[PATH_MAX] = '\0';
strncat(__ctru_dev_path_buf, "/", PATH_MAX - cwdlen);
strncat(__ctru_dev_path_buf, path, PATH_MAX - cwdlen - 1);
}
if(__fixedpath[PATH_MAX] != 0)
if(__ctru_dev_path_buf[PATH_MAX] != 0)
{
__fixedpath[PATH_MAX] = 0;
__ctru_dev_path_buf[PATH_MAX] = 0;
r->_errno = ENAMETOOLONG;
return NULL;
}
@ -222,7 +222,7 @@ archive_fixpath(struct _reent *r,
if(device)
*device = dev;
return __fixedpath;
return __ctru_dev_path_buf;
}
static const FS_Path
@ -238,23 +238,23 @@ archive_utf16path(struct _reent *r,
if(archive_fixpath(r, path, device) == NULL)
return fspath;
units = utf8_to_utf16(__utf16path, (const uint8_t*)__fixedpath, PATH_MAX);
units = utf8_to_utf16(__ctru_dev_utf16_buf, (const uint8_t*)__ctru_dev_path_buf, PATH_MAX);
if(units < 0)
{
r->_errno = EILSEQ;
return fspath;
}
if(units >= PATH_MAX)
if(units > PATH_MAX)
{
r->_errno = ENAMETOOLONG;
return fspath;
}
__utf16path[units] = 0;
__ctru_dev_utf16_buf[units] = 0;
fspath.type = PATH_UTF16;
fspath.size = (units+1)*sizeof(uint16_t);
fspath.data = (const u8*)__utf16path;
fspath.data = __ctru_dev_utf16_buf;
return fspath;
}
@ -356,7 +356,7 @@ Result archiveMountSdmc(void)
ssize_t units;
uint32_t code;
char *p;
FS_Path sdmcPath = { PATH_EMPTY, 1, (u8*)"" };
FS_Path sdmcPath = { PATH_EMPTY, 1, "" };
FS_Archive sdmcArchive;
Result rc = 0;
@ -379,15 +379,15 @@ Result archiveMountSdmc(void)
{
if(FindDevice(__system_argv[0]) == rc)
{
strncpy(__fixedpath,__system_argv[0],PATH_MAX);
if(__fixedpath[PATH_MAX] != 0)
strncpy(__ctru_dev_path_buf,__system_argv[0],PATH_MAX);
if(__ctru_dev_path_buf[PATH_MAX] != 0)
{
__fixedpath[PATH_MAX] = 0;
__ctru_dev_path_buf[PATH_MAX] = 0;
}
else
{
char *last_slash = NULL;
p = __fixedpath;
p = __ctru_dev_path_buf;
do
{
units = decode_utf8(&code, (const uint8_t*)p);
@ -406,7 +406,7 @@ Result archiveMountSdmc(void)
if(last_slash != NULL)
{
last_slash[0] = 0;
chdir(__fixedpath);
chdir(__ctru_dev_path_buf);
}
}
}
@ -927,7 +927,7 @@ archive_chdir(struct _reent *r,
if(R_SUCCEEDED(rc))
{
FSDIR_Close(fd);
strncpy(device->cwd, __fixedpath, PATH_MAX + 1);
strncpy(device->cwd, __ctru_dev_path_buf, PATH_MAX + 1);
device->cwd[PATH_MAX] = '\0';
return 0;
}
@ -952,7 +952,6 @@ archive_rename(struct _reent *r,
{
Result rc;
FS_Path fs_path_old, fs_path_new;
static __thread uint16_t __utf16path_old[PATH_MAX+1];
archive_fsdevice *sourceDevice = r->deviceData;
archive_fsdevice *destDevice = NULL;
@ -967,8 +966,9 @@ archive_rename(struct _reent *r,
if(fs_path_old.data == NULL)
return -1;
memcpy(__utf16path_old, __utf16path, sizeof(__utf16path));
fs_path_old.data = (const u8*)__utf16path_old;
uint8_t old_path_copy[fs_path_old.size];
memcpy(old_path_copy, fs_path_old.data, fs_path_old.size);
fs_path_old.data = old_path_copy;
fs_path_new = archive_utf16path(r, newName, &destDevice);
if(fs_path_new.data == NULL)
@ -982,12 +982,29 @@ archive_rename(struct _reent *r,
}
rc = FSUSER_RenameFile(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
if(R_SUCCEEDED(rc))
return 0;
/* if the file at the target destination exists, overwrite it */
if(R_FAILED(rc) && R_DESCRIPTION(rc) == RD_ALREADY_EXISTS) {
rc = FSUSER_DeleteFile(sourceDevice->archive, fs_path_new);
if(R_FAILED(rc)) {
r->_errno = archive_translate_error(rc);
return -1;
}
rc = FSUSER_RenameFile(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
if(R_SUCCEEDED(rc)) return 0;
} else if(R_SUCCEEDED(rc)) return 0;
rc = FSUSER_RenameDirectory(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
if(R_SUCCEEDED(rc))
return 0;
/* if the directory at the target destination exists, overwrite it */
if(R_FAILED(rc) && R_DESCRIPTION(rc) == RD_ALREADY_EXISTS) {
/* only overwrite empty directories */
rc = FSUSER_DeleteDirectory(sourceDevice-> archive, fs_path_new);
if(R_FAILED(rc)) {
r->_errno = archive_translate_error(rc);
return -1;
}
rc = FSUSER_RenameDirectory(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
if(R_SUCCEEDED(rc)) return 0;
} else if(R_SUCCEEDED(rc)) return 0;
r->_errno = archive_translate_error(rc);
return -1;

View File

@ -37,6 +37,16 @@ static u16 colorTable[] = {
RGB8_to_565( 96, 96, 96), // faint white
};
static const u8 colorCube[] = {
0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff,
};
static const u8 grayScale[] = {
0x08, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e,
0x58, 0x62, 0x6c, 0x76, 0x80, 0x8a, 0x94, 0x9e,
0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee,
};
PrintConsole defaultConsole =
{
//Font:
@ -72,7 +82,7 @@ void consolePrintChar(int c);
void consoleDrawChar(int c);
//---------------------------------------------------------------------------------
static void consoleCls(char mode) {
static void consoleCls(int mode) {
//---------------------------------------------------------------------------------
int i = 0;
@ -80,8 +90,7 @@ static void consoleCls(char mode) {
switch (mode)
{
case '[':
case '0':
case 0:
{
colTemp = currentConsole->cursorX ;
rowTemp = currentConsole->cursorY ;
@ -93,7 +102,7 @@ static void consoleCls(char mode) {
currentConsole->cursorY = rowTemp;
break;
}
case '1':
case 1:
{
colTemp = currentConsole->cursorX ;
rowTemp = currentConsole->cursorY ;
@ -108,7 +117,7 @@ static void consoleCls(char mode) {
currentConsole->cursorY = rowTemp;
break;
}
case '2':
case 2:
{
currentConsole->cursorY = 0;
currentConsole->cursorX = 0;
@ -124,7 +133,7 @@ static void consoleCls(char mode) {
gfxFlushBuffers();
}
//---------------------------------------------------------------------------------
static void consoleClearLine(char mode) {
static void consoleClearLine(int mode) {
//---------------------------------------------------------------------------------
int i = 0;
@ -132,8 +141,7 @@ static void consoleClearLine(char mode) {
switch (mode)
{
case '[':
case '0':
case 0:
{
colTemp = currentConsole->cursorX ;
@ -145,7 +153,7 @@ static void consoleClearLine(char mode) {
break;
}
case '1':
case 1:
{
colTemp = currentConsole->cursorX ;
@ -159,7 +167,7 @@ static void consoleClearLine(char mode) {
break;
}
case '2':
case 2:
{
colTemp = currentConsole->cursorX ;
@ -202,6 +210,273 @@ static inline void consolePosition(int x, int y) {
currentConsole->cursorY = y - 1;
}
static struct
{
union
{
struct
{
int movement;
} directional;
struct
{
int y;
int x;
} absolute;
struct
{
int type;
} clear;
struct
{
int args[3];
int flags;
u16 fg;
u16 bg;
} color;
int rawBuf[5];
};
int argIdx;
bool hasArg[3];
enum ESC_STATE
{
ESC_NONE,
ESC_START,
ESC_BUILDING_UNKNOWN,
ESC_BUILDING_FORMAT_UNKNOWN,
ESC_BUILDING_FORMAT_FG,
ESC_BUILDING_FORMAT_BG,
ESC_BUILDING_FORMAT_FG_NONRGB,
ESC_BUILDING_FORMAT_BG_NONRGB,
ESC_BUILDING_FORMAT_FG_RGB,
ESC_BUILDING_FORMAT_BG_RGB,
} state;
} escapeSeq;
static void consoleHandleColorEsc(int code)
{
switch (escapeSeq.state)
{
case ESC_BUILDING_FORMAT_UNKNOWN:
switch (code)
{
case 0: // reset
escapeSeq.color.flags = 0;
escapeSeq.color.bg = 0;
escapeSeq.color.fg = 7;
break;
case 1: // bold
escapeSeq.color.flags &= ~CONSOLE_COLOR_FAINT;
escapeSeq.color.flags |= CONSOLE_COLOR_BOLD;
break;
case 2: // faint
escapeSeq.color.flags &= ~CONSOLE_COLOR_BOLD;
escapeSeq.color.flags |= CONSOLE_COLOR_FAINT;
break;
case 3: // italic
escapeSeq.color.flags |= CONSOLE_ITALIC;
break;
case 4: // underline
escapeSeq.color.flags |= CONSOLE_UNDERLINE;
break;
case 5: // blink slow
escapeSeq.color.flags &= ~CONSOLE_BLINK_FAST;
escapeSeq.color.flags |= CONSOLE_BLINK_SLOW;
break;
case 6: // blink fast
escapeSeq.color.flags &= ~CONSOLE_BLINK_SLOW;
escapeSeq.color.flags |= CONSOLE_BLINK_FAST;
break;
case 7: // reverse video
escapeSeq.color.flags |= CONSOLE_COLOR_REVERSE;
break;
case 8: // conceal
escapeSeq.color.flags |= CONSOLE_CONCEAL;
break;
case 9: // crossed-out
escapeSeq.color.flags |= CONSOLE_CROSSED_OUT;
break;
case 21: // bold off
escapeSeq.color.flags &= ~CONSOLE_COLOR_BOLD;
break;
case 22: // normal color
escapeSeq.color.flags &= ~CONSOLE_COLOR_BOLD;
escapeSeq.color.flags &= ~CONSOLE_COLOR_FAINT;
break;
case 23: // italic off
escapeSeq.color.flags &= ~CONSOLE_ITALIC;
break;
case 24: // underline off
escapeSeq.color.flags &= ~CONSOLE_UNDERLINE;
break;
case 25: // blink off
escapeSeq.color.flags &= ~CONSOLE_BLINK_SLOW;
escapeSeq.color.flags &= ~CONSOLE_BLINK_FAST;
break;
case 27: // reverse off
escapeSeq.color.flags &= ~CONSOLE_COLOR_REVERSE;
break;
case 29: // crossed-out off
escapeSeq.color.flags &= ~CONSOLE_CROSSED_OUT;
break;
case 30 ... 37: // writing color
escapeSeq.color.flags &= ~CONSOLE_FG_CUSTOM;
escapeSeq.color.fg = code - 30;
break;
case 38: // custom foreground color
escapeSeq.state = ESC_BUILDING_FORMAT_FG;
break;
case 39: // reset foreground color
escapeSeq.color.flags &= ~CONSOLE_FG_CUSTOM;
escapeSeq.color.fg = 7;
break;
case 40 ... 47: // screen color
escapeSeq.color.flags &= ~CONSOLE_BG_CUSTOM;
escapeSeq.color.bg = code - 40;
break;
case 48: // custom background color
escapeSeq.state = ESC_BUILDING_FORMAT_BG;
break;
case 49: // reset background color
escapeSeq.color.flags &= ~CONSOLE_BG_CUSTOM;
escapeSeq.color.fg = 0;
break;
}
break;
case ESC_BUILDING_FORMAT_FG:
if (escapeSeq.color.args[0] == 5)
escapeSeq.state = ESC_BUILDING_FORMAT_FG_NONRGB;
else if (escapeSeq.color.args[0] == 2)
escapeSeq.state = ESC_BUILDING_FORMAT_FG_RGB;
else
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
break;
case ESC_BUILDING_FORMAT_BG:
if (escapeSeq.color.args[0] == 5)
escapeSeq.state = ESC_BUILDING_FORMAT_BG_NONRGB;
else if (escapeSeq.color.args[0] == 2)
escapeSeq.state = ESC_BUILDING_FORMAT_BG_RGB;
else
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
break;
case ESC_BUILDING_FORMAT_FG_NONRGB:
if (code <= 15) {
escapeSeq.color.fg = code;
escapeSeq.color.flags &= ~CONSOLE_FG_CUSTOM;
} else if (code <= 231) {
code -= 16;
unsigned int r = code / 36;
unsigned int g = (code - r * 36) / 6;
unsigned int b = code - r * 36 - g * 6;
escapeSeq.color.fg = RGB8_to_565 (colorCube[r], colorCube[g], colorCube[b]);
escapeSeq.color.flags |= CONSOLE_FG_CUSTOM;
} else if (code <= 255) {
code -= 232;
escapeSeq.color.fg = RGB8_to_565 (grayScale[code], grayScale[code], grayScale[code]);
escapeSeq.color.flags |= CONSOLE_FG_CUSTOM;
}
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
break;
case ESC_BUILDING_FORMAT_BG_NONRGB:
if (code <= 15) {
escapeSeq.color.bg = code;
escapeSeq.color.flags &= ~CONSOLE_BG_CUSTOM;
} else if (code <= 231) {
code -= 16;
unsigned int r = code / 36;
unsigned int g = (code - r * 36) / 6;
unsigned int b = code - r * 36 - g * 6;
escapeSeq.color.bg = RGB8_to_565 (colorCube[r], colorCube[g], colorCube[b]);
escapeSeq.color.flags |= CONSOLE_BG_CUSTOM;
} else if (code <= 255) {
code -= 232;
escapeSeq.color.bg = RGB8_to_565 (grayScale[code], grayScale[code], grayScale[code]);
escapeSeq.color.flags |= CONSOLE_BG_CUSTOM;
}
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
break;
case ESC_BUILDING_FORMAT_FG_RGB:
escapeSeq.color.fg = RGB8_to_565((unsigned int)escapeSeq.color.args[0], (unsigned int)escapeSeq.color.args[1], (unsigned int)escapeSeq.color.args[2]);
escapeSeq.color.flags |= CONSOLE_FG_CUSTOM;
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
break;
case ESC_BUILDING_FORMAT_BG_RGB:
escapeSeq.color.bg = RGB8_to_565((unsigned int)escapeSeq.color.args[0], (unsigned int)escapeSeq.color.args[1], (unsigned int)escapeSeq.color.args[2]);
escapeSeq.color.flags |= CONSOLE_BG_CUSTOM;
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
break;
default:
break;
}
escapeSeq.argIdx = 0;
}
static void consoleColorStateShift(void)
{
switch (escapeSeq.state)
{
case ESC_BUILDING_UNKNOWN:
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
if (escapeSeq.hasArg[0])
consoleHandleColorEsc(escapeSeq.color.args[0]);
if (escapeSeq.hasArg[1])
consoleHandleColorEsc(escapeSeq.color.args[1]);
escapeSeq.argIdx = 0;
escapeSeq.hasArg[0] = escapeSeq.hasArg[1] = false;
break;
case ESC_BUILDING_FORMAT_BG:
case ESC_BUILDING_FORMAT_FG:
case ESC_BUILDING_FORMAT_FG_NONRGB:
case ESC_BUILDING_FORMAT_BG_NONRGB:
consoleHandleColorEsc(escapeSeq.color.args[0]);
escapeSeq.argIdx = 0;
escapeSeq.hasArg[0] = escapeSeq.hasArg[1] = false;
break;
case ESC_BUILDING_FORMAT_FG_RGB:
case ESC_BUILDING_FORMAT_BG_RGB:
if (escapeSeq.argIdx < 3)
escapeSeq.argIdx++;
else
consoleHandleColorEsc(0); // Nothing passed here because three RGB items
break;
default:
break;
}
}
static void consoleColorApply(void)
{
currentConsole->bg = escapeSeq.color.bg;
currentConsole->fg = escapeSeq.color.fg;
currentConsole->flags = escapeSeq.color.flags;
}
//---------------------------------------------------------------------------------
ssize_t con_write(struct _reent *r,void *fd,const char *ptr, size_t len) {
//---------------------------------------------------------------------------------
@ -220,260 +495,165 @@ ssize_t con_write(struct _reent *r,void *fd,const char *ptr, size_t len) {
chr = *(tmp++);
i++; count++;
if ( chr == 0x1b && *tmp == '[' ) {
bool escaping = true;
char *escapeseq = tmp++;
int escapelen = 1;
i++; count++;
switch (escapeSeq.state)
{
case ESC_NONE:
if (chr == 0x1b)
escapeSeq.state = ESC_START;
else
consolePrintChar(chr);
break;
case ESC_START:
if (chr == '[')
{
escapeSeq.state = ESC_BUILDING_UNKNOWN;
memset(escapeSeq.rawBuf, 0, sizeof(escapeSeq.rawBuf));
memset(escapeSeq.hasArg, 0, sizeof(escapeSeq.hasArg));
escapeSeq.color.bg = currentConsole->bg;
escapeSeq.color.fg = currentConsole->fg;
escapeSeq.color.flags = currentConsole->flags;
escapeSeq.argIdx = 0;
}
else
{
consolePrintChar(0x1b);
consolePrintChar(chr);
escapeSeq.state = ESC_NONE;
}
break;
case ESC_BUILDING_UNKNOWN:
switch (chr)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
escapeSeq.hasArg[escapeSeq.argIdx] = true;
escapeSeq.rawBuf[escapeSeq.argIdx] = escapeSeq.rawBuf[escapeSeq.argIdx] * 10 + (chr - '0');
break;
case ';':
if (escapeSeq.argIdx < 2)
escapeSeq.argIdx++;
else
consoleColorStateShift();
break;
do {
chr = *(tmp++);
i++; count++; escapelen++;
int parameter, assigned, consumed;
// make sure parameters are positive values and delimited by semicolon
if((chr >= '0' && chr <= '9') || chr == ';')
continue;
switch (chr) {
//---------------------------------------
// Cursor directional movement
//---------------------------------------
case 'A':
consumed = 0;
assigned = sscanf(escapeseq,"[%dA%n", &parameter, &consumed);
if (assigned==0) parameter = 1;
if (consumed)
currentConsole->cursorY = (currentConsole->cursorY - parameter) < 0 ? 0 : currentConsole->cursorY - parameter;
escaping = false;
if (!escapeSeq.hasArg[0])
escapeSeq.directional.movement = 1;
currentConsole->cursorY = (currentConsole->cursorY - escapeSeq.directional.movement) < 0 ? 0 : currentConsole->cursorY - escapeSeq.directional.movement;
escapeSeq.state = ESC_NONE;
break;
case 'B':
consumed = 0;
assigned = sscanf(escapeseq,"[%dB%n", &parameter, &consumed);
if (assigned==0) parameter = 1;
if (consumed)
currentConsole->cursorY = (currentConsole->cursorY + parameter) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY + parameter;
escaping = false;
if (!escapeSeq.hasArg[0])
escapeSeq.directional.movement = 1;
currentConsole->cursorY = (currentConsole->cursorY + escapeSeq.directional.movement) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY + escapeSeq.directional.movement;
escapeSeq.state = ESC_NONE;
break;
case 'C':
consumed = 0;
assigned = sscanf(escapeseq,"[%dC%n", &parameter, &consumed);
if (assigned==0) parameter = 1;
if (consumed)
currentConsole->cursorX = (currentConsole->cursorX + parameter) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX + parameter;
escaping = false;
if (!escapeSeq.hasArg[0])
escapeSeq.directional.movement = 1;
currentConsole->cursorX = (currentConsole->cursorX + escapeSeq.directional.movement) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX + escapeSeq.directional.movement;
escapeSeq.state = ESC_NONE;
break;
case 'D':
consumed = 0;
assigned = sscanf(escapeseq,"[%dD%n", &parameter, &consumed);
if (assigned==0) parameter = 1;
if (consumed)
currentConsole->cursorX = (currentConsole->cursorX - parameter) < 0 ? 0 : currentConsole->cursorX - parameter;
escaping = false;
if (!escapeSeq.hasArg[0])
escapeSeq.directional.movement = 1;
currentConsole->cursorX = (currentConsole->cursorX - escapeSeq.directional.movement) < 0 ? 0 : currentConsole->cursorX - escapeSeq.directional.movement;
escapeSeq.state = ESC_NONE;
break;
//---------------------------------------
// Cursor position movement
//---------------------------------------
case 'H':
case 'f':
{
int x, y;
char c;
if(sscanf(escapeseq,"[%d;%d%c", &y, &x, &c) == 3 && (c == 'f' || c == 'H')) {
consolePosition(x, y);
escaping = false;
consolePosition(escapeSeq.hasArg[1] ? escapeSeq.absolute.x : 1, escapeSeq.hasArg[0] ? escapeSeq.absolute.y : 1);
escapeSeq.state = ESC_NONE;
break;
}
x = y = 1;
if(sscanf(escapeseq,"[%d;%c", &y, &c) == 2 && (c == 'f' || c == 'H')) {
consolePosition(x, y);
escaping = false;
break;
}
x = y = 1;
if(sscanf(escapeseq,"[;%d%c", &x, &c) == 2 && (c == 'f' || c == 'H')) {
consolePosition(x, y);
escaping = false;
break;
}
x = y = 1;
if(sscanf(escapeseq,"[;%c", &c) == 1 && (c == 'f' || c == 'H')) {
consolePosition(x, y);
escaping = false;
break;
}
// invalid format
escaping = false;
break;
}
//---------------------------------------
// Screen clear
//---------------------------------------
case 'J':
if(escapelen <= 3)
consoleCls(escapeseq[escapelen-2]);
escaping = false;
consoleCls(escapeSeq.hasArg[0] ? escapeSeq.clear.type : 0);
escapeSeq.state = ESC_NONE;
break;
//---------------------------------------
// Line clear
//---------------------------------------
case 'K':
if(escapelen <= 3)
consoleClearLine(escapeseq[escapelen-2]);
escaping = false;
consoleClearLine(escapeSeq.hasArg[0] ? escapeSeq.clear.type : 0);
escapeSeq.state = ESC_NONE;
break;
//---------------------------------------
// Save cursor position
//---------------------------------------
case 's':
if(escapelen == 2) {
currentConsole->prevCursorX = currentConsole->cursorX ;
currentConsole->prevCursorY = currentConsole->cursorY ;
}
escaping = false;
escapeSeq.state = ESC_NONE;
break;
//---------------------------------------
// Load cursor position
//---------------------------------------
case 'u':
if(escapelen == 2) {
currentConsole->cursorX = currentConsole->prevCursorX ;
currentConsole->cursorY = currentConsole->prevCursorY ;
}
escaping = false;
escapeSeq.state = ESC_NONE;
break;
//---------------------------------------
// Color scan codes
//---------------------------------------
case 'm':
escapeseq++;
escapelen--;
do {
parameter = 0;
if (escapelen == 1) {
consumed = 1;
} else if (memchr(escapeseq,';',escapelen)) {
sscanf(escapeseq,"%d;%n", &parameter, &consumed);
} else {
sscanf(escapeseq,"%dm%n", &parameter, &consumed);
}
escapeseq += consumed;
escapelen -= consumed;
switch(parameter) {
case 0: // reset
currentConsole->flags = 0;
currentConsole->bg = 0;
currentConsole->fg = 7;
break;
case 1: // bold
currentConsole->flags &= ~CONSOLE_COLOR_FAINT;
currentConsole->flags |= CONSOLE_COLOR_BOLD;
break;
case 2: // faint
currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
currentConsole->flags |= CONSOLE_COLOR_FAINT;
break;
case 3: // italic
currentConsole->flags |= CONSOLE_ITALIC;
break;
case 4: // underline
currentConsole->flags |= CONSOLE_UNDERLINE;
break;
case 5: // blink slow
currentConsole->flags &= ~CONSOLE_BLINK_FAST;
currentConsole->flags |= CONSOLE_BLINK_SLOW;
break;
case 6: // blink fast
currentConsole->flags &= ~CONSOLE_BLINK_SLOW;
currentConsole->flags |= CONSOLE_BLINK_FAST;
break;
case 7: // reverse video
currentConsole->flags |= CONSOLE_COLOR_REVERSE;
break;
case 8: // conceal
currentConsole->flags |= CONSOLE_CONCEAL;
break;
case 9: // crossed-out
currentConsole->flags |= CONSOLE_CROSSED_OUT;
break;
case 21: // bold off
currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
break;
case 22: // normal color
currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
currentConsole->flags &= ~CONSOLE_COLOR_FAINT;
break;
case 23: // italic off
currentConsole->flags &= ~CONSOLE_ITALIC;
break;
case 24: // underline off
currentConsole->flags &= ~CONSOLE_UNDERLINE;
break;
case 25: // blink off
currentConsole->flags &= ~CONSOLE_BLINK_SLOW;
currentConsole->flags &= ~CONSOLE_BLINK_FAST;
break;
case 27: // reverse off
currentConsole->flags &= ~CONSOLE_COLOR_REVERSE;
break;
case 29: // crossed-out off
currentConsole->flags &= ~CONSOLE_CROSSED_OUT;
break;
case 30 ... 37: // writing color
currentConsole->fg = parameter - 30;
break;
case 39: // reset foreground color
currentConsole->fg = 7;
break;
case 40 ... 47: // screen color
currentConsole->bg = parameter - 40;
break;
case 49: // reset background color
currentConsole->fg = 0;
break;
}
} while (escapelen > 0);
escaping = false;
consoleColorStateShift();
consoleColorApply();
escapeSeq.state = ESC_NONE;
break;
default:
// some sort of unsupported escape; just gloss over it
escaping = false;
escapeSeq.state = ESC_NONE;
break;
}
break;
default:
switch (chr)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
escapeSeq.hasArg[escapeSeq.argIdx] = true;
escapeSeq.rawBuf[escapeSeq.argIdx] = escapeSeq.rawBuf[escapeSeq.argIdx] * 10 + (chr - '0');
break;
case ';':
consoleColorStateShift();
break;
case 'm':
consoleColorStateShift();
consoleColorApply();
escapeSeq.state = ESC_NONE;
break;
default:
// some sort of unsupported escape; just gloss over it
escapeSeq.state = ESC_NONE;
break;
}
} while (escaping);
continue;
}
consolePrintChar(chr);
}
return count;
@ -533,6 +713,8 @@ PrintConsole* consoleInit(gfxScreen_t screen, PrintConsole* console) {
setvbuf(stdout, NULL , _IONBF, 0);
setvbuf(stderr, NULL , _IONBF, 0);
memset(&escapeSeq, 0, sizeof(escapeSeq));
firstConsoleInit = false;
}
@ -554,12 +736,12 @@ PrintConsole* consoleInit(gfxScreen_t screen, PrintConsole* console) {
console->frameBuffer = (u16*)gfxGetFramebuffer(screen, GFX_LEFT, NULL, NULL);
if(screen==GFX_TOP) {
console->consoleWidth = 50;
console->windowWidth = 50;
bool isWide = gfxIsWide();
console->consoleWidth = isWide ? 100 : 50;
console->windowWidth = isWide ? 100 : 50;
}
consoleCls('2');
consoleCls(2);
return currentConsole;
@ -571,8 +753,8 @@ void consoleDebugInit(debugDevice device){
int buffertype = _IONBF;
switch(device) {
switch(device)
{
case debugDevice_SVC:
devoptab_list[STD_ERR] = &dotab_svc;
buffertype = _IOLBF;
@ -629,7 +811,7 @@ static void newRow() {
src += 240;
}
consoleClearLine('2');
consoleClearLine(2);
}
}
//---------------------------------------------------------------------------------
@ -640,24 +822,29 @@ void consoleDrawChar(int c) {
u8 *fontdata = currentConsole->font.gfx + (8 * c);
int writingColor = currentConsole->fg;
int screenColor = currentConsole->bg;
u16 fg = currentConsole->fg;
u16 bg = currentConsole->bg;
if (!(currentConsole->flags & CONSOLE_FG_CUSTOM)) {
if (currentConsole->flags & CONSOLE_COLOR_BOLD) {
writingColor += 8;
fg = colorTable[fg + 8];
} else if (currentConsole->flags & CONSOLE_COLOR_FAINT) {
writingColor += 16;
fg = colorTable[fg + 16];
} else {
fg = colorTable[fg];
}
}
if (!(currentConsole->flags & CONSOLE_BG_CUSTOM)) {
bg = colorTable[bg];
}
if (currentConsole->flags & CONSOLE_COLOR_REVERSE) {
int tmp = writingColor;
writingColor = screenColor;
screenColor = tmp;
u16 tmp = fg;
fg = bg;
bg = tmp;
}
u16 bg = colorTable[screenColor];
u16 fg = colorTable[writingColor];
u8 b1 = *(fontdata++);
u8 b2 = *(fontdata++);
u8 b3 = *(fontdata++);
@ -755,7 +942,7 @@ void consolePrintChar(int c) {
//---------------------------------------------------------------------------------
void consoleClear(void) {
//---------------------------------------------------------------------------------
iprintf("\x1b[2J");
consoleCls(2);
}
//---------------------------------------------------------------------------------
@ -773,5 +960,3 @@ void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height
console->cursorY = 0;
}

View File

@ -18,10 +18,12 @@ Result errfInit(void)
if (AtomicPostIncrement(&errfRefCount)) return 0;
rc = svcConnectToPort(&errfHandle, "err:f");
if (R_FAILED(rc)) goto end;
if (R_FAILED(rc))
{
errfHandle = 0;
errfExit();
}
end:
if (R_FAILED(rc)) errfExit();
return rc;
}
@ -29,7 +31,8 @@ void errfExit(void)
{
if (AtomicDecrement(&errfRefCount))
return;
svcCloseHandle(errfHandle);
if (errfHandle != 0) svcCloseHandle(errfHandle);
errfHandle = 0;
}
Handle* errfGetSessionHandle(void)
@ -39,7 +42,7 @@ Handle* errfGetSessionHandle(void)
Result ERRF_Throw(const ERRF_FatalErrInfo* error)
{
uint32_t *cmdbuf = getThreadCommandBuffer();
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x1,32,0); // 0x10800
memcpy(&cmdbuf[1], error, sizeof(ERRF_FatalErrInfo));
@ -54,7 +57,7 @@ Result ERRF_Throw(const ERRF_FatalErrInfo* error)
static inline void getCommonErrorData(ERRF_FatalErrInfo* error, Result failure)
{
error->resCode = failure;
svcGetProcessId(&error->procId, 0xFFFF8001);
svcGetProcessId(&error->procId, CUR_PROCESS_HANDLE);
}
Result ERRF_ThrowResult(Result failure)
@ -80,11 +83,33 @@ Result ERRF_ThrowResult(Result failure)
return ret;
}
Result ERRF_LogResult(Result failure)
{
ERRF_FatalErrInfo error;
Result ret;
if (R_FAILED(ret = errfInit()))
return ret;
memset(&error, 0, sizeof(error));
error.type = ERRF_ERRTYPE_LOG_ONLY;
// pcAddr is not used by ErrDisp for ERRF_ERRTYPE_FAILURE
error.pcAddr = (u32)__builtin_extract_return_addr(__builtin_return_address(0));
getCommonErrorData(&error, failure);
ret = ERRF_Throw(&error);
errfExit();
return ret;
}
Result ERRF_ThrowResultWithMessage(Result failure, const char* message)
{
ERRF_FatalErrInfo error;
Result ret;
size_t msglen;
if (R_FAILED(ret = errfInit()))
return ret;
@ -94,11 +119,12 @@ Result ERRF_ThrowResultWithMessage(Result failure, const char* message)
error.type = ERRF_ERRTYPE_FAILURE;
getCommonErrorData(&error, failure);
if ((msglen = strlen(message)) > sizeof(error.data.failure_mesg) - 1)
msglen = sizeof(error.data.failure_mesg) - 1;
memcpy(error.data.failure_mesg, message, msglen);
error.data.failure_mesg[msglen] = '\0';
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
// Official client code always copies at most 95 bytes + NUL byte, but server codes uses %.96s
// and explicitely handles 96 non-NUL bytes.
strncpy(error.data.failure_mesg, message, sizeof(error.data.failure_mesg));
#pragma GCC diagnostic pop
ret = ERRF_Throw(&error);
@ -107,6 +133,28 @@ Result ERRF_ThrowResultWithMessage(Result failure, const char* message)
return ret;
}
Result ERRF_SetUserString(const char* user_string)
{
Result ret = errfInit();
size_t size = strnlen(user_string, 256);
if (R_FAILED(ret))
return ret;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x2,1,2); // 0x20042
cmdbuf[1] = size; // unused
cmdbuf[2] = IPC_Desc_StaticBuffer(size, 0);
cmdbuf[3] = (u32)user_string;
if (R_SUCCEEDED(ret = svcSendSyncRequest(errfHandle)))
ret = cmdbuf[1];
errfExit();
return ret;
}
void ERRF_ExceptionHandler(ERRF_ExceptionInfo* excep, CpuRegisters* regs)
{
ERRF_FatalErrInfo error;

View File

@ -205,7 +205,7 @@ static int _gdbExportSeekFlag(int flag)
// https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat
typedef u32 gdbhio_time_t;
struct PACKED ALIGN(4) gdbhio_stat {
struct CTR_PACKED CTR_ALIGN(4) gdbhio_stat {
u32 gst_dev; /* device */
u32 gst_ino; /* inode */
gdbhio_mode_t gst_mode; /* protection */

View File

@ -1,382 +1,189 @@
#include <stdlib.h>
#include <stdio.h>
#include <3ds/types.h>
#include <3ds/gfx.h>
#include <3ds/svc.h>
#include <3ds/synchronization.h>
#include <3ds/allocator/linear.h>
#include <3ds/allocator/mappable.h>
#include <3ds/allocator/vram.h>
#include <3ds/gpu/gx.h>
#include <3ds/services/gspgpu.h>
#include <3ds/gfx.h>
GSPGPU_FramebufferInfo topFramebufferInfo, bottomFramebufferInfo;
static u8* gfxTopFramebuffers[2];
static u8* gfxBottomFramebuffers[2];
static u32 gfxTopFramebufferMaxSize;
static u32 gfxBottomFramebufferMaxSize;
static GSPGPU_FramebufferFormat gfxFramebufferFormats[2];
u8 gfxThreadID;
u8* gfxSharedMemory;
static enum {
MODE_2D = 0,
MODE_3D = 1,
MODE_WIDE = 2,
} gfxTopMode;
static bool gfxIsVram;
static u8 gfxCurBuf[2];
static u8 gfxIsDoubleBuf[2];
u8* gfxTopLeftFramebuffers[2];
u8* gfxTopRightFramebuffers[2];
u8* gfxBottomFramebuffers[2];
u32 gfxTopFramebufferMaxSize;
u32 gfxBottomFramebufferMaxSize;
static bool enable3d;
static u8 currentBuffer[2];
static int doubleBuf[2] = {1,1};
Handle gspEvent, gspSharedMemHandle;
static GSPGPU_FramebufferFormats topFormat = GSP_BGR8_OES;
static GSPGPU_FramebufferFormats botFormat = GSP_BGR8_OES;
static GSPGPU_FramebufferInfo* const framebufferInfoSt[] = { &topFramebufferInfo, &bottomFramebufferInfo };
static void (*screenFree)(void *) = NULL;
static void *(*screenAlloc)(size_t) = NULL;
static void (*screenFree)(void *);
static void *(*screenAlloc)(size_t);
void gfxSet3D(bool enable)
{
enable3d=enable;
gfxTopMode = enable ? MODE_3D : MODE_2D;
}
bool gfxIs3D(void)
{
return enable3d;
return gfxTopMode == MODE_3D;
}
u32 __get_bytes_per_pixel(GSPGPU_FramebufferFormats format) {
switch(format) {
case GSP_RGBA8_OES:
return 4;
case GSP_BGR8_OES:
return 3;
case GSP_RGB565_OES:
case GSP_RGB5_A1_OES:
case GSP_RGBA4_OES:
return 2;
void gfxSetWide(bool enable)
{
gfxTopMode = enable ? MODE_WIDE : MODE_2D;
}
return 3;
bool gfxIsWide(void)
{
return gfxTopMode == MODE_WIDE;
}
void gfxSetScreenFormat(gfxScreen_t screen, GSPGPU_FramebufferFormats format) {
if (screenAlloc == NULL || screenFree == NULL) return;
void gfxSetScreenFormat(gfxScreen_t screen, GSPGPU_FramebufferFormat format)
{
u32 reqSize = GSP_SCREEN_WIDTH * gspGetBytesPerPixel(format);
u8** framebuffers;
u32* maxSize;
if (screen == GFX_TOP)
{
u32 topSize = 400 * 240 * __get_bytes_per_pixel(format);
if (gfxTopFramebufferMaxSize < topSize)
reqSize *= GSP_SCREEN_HEIGHT_TOP_2X;
framebuffers = gfxTopFramebuffers;
maxSize = &gfxTopFramebufferMaxSize;
}
else // GFX_BOTTOM
{
screenFree(gfxTopLeftFramebuffers[0]);
screenFree(gfxTopLeftFramebuffers[1]);
screenFree(gfxTopRightFramebuffers[0]);
screenFree(gfxTopRightFramebuffers[1]);
gfxTopLeftFramebuffers[0]=screenAlloc(topSize);
gfxTopLeftFramebuffers[1]=screenAlloc(topSize);
gfxTopRightFramebuffers[0]=screenAlloc(topSize);
gfxTopRightFramebuffers[1]=screenAlloc(topSize);
gfxTopFramebufferMaxSize = topSize;
}
topFormat = format;
}else{
u32 bottomSize = 320 * 240 * __get_bytes_per_pixel(format);
if (gfxBottomFramebufferMaxSize < bottomSize)
{
screenFree(gfxBottomFramebuffers[0]);
screenFree(gfxBottomFramebuffers[1]);
gfxBottomFramebuffers[0]=screenAlloc(bottomSize);
gfxBottomFramebuffers[1]=screenAlloc(bottomSize);
gfxBottomFramebufferMaxSize = bottomSize;
}
botFormat = format;
}
reqSize *= GSP_SCREEN_HEIGHT_BOTTOM;
framebuffers = gfxBottomFramebuffers;
maxSize = &gfxBottomFramebufferMaxSize;
}
GSPGPU_FramebufferFormats gfxGetScreenFormat(gfxScreen_t screen) {
if (*maxSize < reqSize)
{
if (framebuffers[0]) screenFree(framebuffers[0]);
if (framebuffers[1]) screenFree(framebuffers[1]);
framebuffers[0] = (u8*)screenAlloc(reqSize);
framebuffers[1] = (u8*)screenAlloc(reqSize);
*maxSize = reqSize;
}
gfxFramebufferFormats[screen] = format;
}
GSPGPU_FramebufferFormat gfxGetScreenFormat(gfxScreen_t screen)
{
return gfxFramebufferFormats[screen];
}
void gfxSetDoubleBuffering(gfxScreen_t screen, bool enable)
{
gfxIsDoubleBuf[screen] = enable ? 1 : 0; // make sure they're the integer values '1' and '0'
}
static void gfxPresentFramebuffer(gfxScreen_t screen, u8 id, bool hasStereo)
{
u32 stride = GSP_SCREEN_WIDTH*gspGetBytesPerPixel(gfxFramebufferFormats[screen]);
u32 mode = gfxFramebufferFormats[screen];
const u8 *fb_a, *fb_b;
if (screen == GFX_TOP)
return topFormat;
{
fb_a = gfxTopFramebuffers[id];
switch (gfxTopMode)
{
default:
case MODE_2D:
mode |= BIT(6);
fb_b = fb_a;
break;
case MODE_3D:
mode |= BIT(5);
fb_b = hasStereo ? (fb_a + gfxTopFramebufferMaxSize/2) : fb_a;
break;
case MODE_WIDE:
fb_b = fb_a;
break;
}
}
else
return botFormat;
{
fb_a = gfxBottomFramebuffers[id];
fb_b = fb_a;
}
void gfxSetDoubleBuffering(gfxScreen_t screen, bool doubleBuffering) {
doubleBuf[screen] = doubleBuffering ? 1 : 0; // make sure they're the integer values '1' and '0'
if (!gfxIsVram)
mode |= 1<<8;
else
mode |= 3<<8;
gspPresentBuffer(screen, id, fb_a, fb_b, stride, mode);
}
static void gfxSetFramebufferInfo(gfxScreen_t screen, u8 id)
{
if(screen==GFX_TOP)
{
topFramebufferInfo.active_framebuf=id;
topFramebufferInfo.framebuf0_vaddr=(u32*)gfxTopLeftFramebuffers[id];
if(enable3d)topFramebufferInfo.framebuf1_vaddr=(u32*)gfxTopRightFramebuffers[id];
else topFramebufferInfo.framebuf1_vaddr=topFramebufferInfo.framebuf0_vaddr;
topFramebufferInfo.framebuf_widthbytesize=240*__get_bytes_per_pixel(topFormat);
u8 bit5=(enable3d!=0);
topFramebufferInfo.format=((1)<<8)|((1^bit5)<<6)|((bit5)<<5)|topFormat;
topFramebufferInfo.framebuf_dispselect=id;
topFramebufferInfo.unk=0x00000000;
}else{
bottomFramebufferInfo.active_framebuf=id;
bottomFramebufferInfo.framebuf0_vaddr=(u32*)gfxBottomFramebuffers[id];
bottomFramebufferInfo.framebuf1_vaddr=0x00000000;
bottomFramebufferInfo.framebuf_widthbytesize=240*__get_bytes_per_pixel(botFormat);
bottomFramebufferInfo.format=botFormat;
bottomFramebufferInfo.framebuf_dispselect=id;
bottomFramebufferInfo.unk=0x00000000;
}
}
static void gfxWriteFramebufferInfo(gfxScreen_t screen)
{
s32* framebufferInfoHeader=(s32*)(gfxSharedMemory+0x200+gfxThreadID*0x80);
if(screen==GFX_BOTTOM)framebufferInfoHeader+=0x10;
GSPGPU_FramebufferInfo* framebufferInfo=(GSPGPU_FramebufferInfo*)&framebufferInfoHeader[1];
u8 pos = 1 - *(u8*)framebufferInfoHeader;
framebufferInfo[pos]=*framebufferInfoSt[screen];
__dsb();
union
{
s32 header;
struct { u8 swap, update; };
} info;
do
{
info.header = __ldrex(framebufferInfoHeader);
info.swap = pos;
info.update = 1;
} while (__strex(framebufferInfoHeader, info.header));
}
static inline void gfxWriteGxReg(u32 offset, u32 data)
{
GSPGPU_WriteHWRegs(0x400000 + offset, &data, 4);
}
static inline void gfxWriteGxRegMasked(u32 offset, u32 data, u32 mask)
{
GSPGPU_WriteHWRegsWithMask(0x400000 + offset, &data, 4, &mask, 4);
}
static void gfxGxHwInit(void)
{
// SDK apps have this exact sequence (except for GPUREG_START_DRAW_FUNC0)
// Some GPU-internal init registers
gfxWriteGxReg(0x1000, 0);
gfxWriteGxReg(0x1080, 0x12345678);
gfxWriteGxReg(0x10C0, 0xFFFFFFF0);
gfxWriteGxReg(0x10D0, 1);
// Ensure GPUREG_START_DRAW_FUNC0 starts off in configuration mode
gfxWriteGxReg(0x1914, 1);
// Top screen LCD configuration, see https://www.3dbrew.org/wiki/GPU/External_Registers#LCD_Source_Framebuffer_Setup
// Top screen sync registers:
gfxWriteGxReg(0x0400, 0x1C2);
gfxWriteGxReg(0x0404, 0xD1);
gfxWriteGxReg(0x0408, 0x1C1);
gfxWriteGxReg(0x040C, 0x1C1);
gfxWriteGxReg(0x0410, 0);
gfxWriteGxReg(0x0414, 0xCF);
gfxWriteGxReg(0x0418, 0xD1);
gfxWriteGxReg(0x041C, (0x1C5 << 16) | 0x1C1);
gfxWriteGxReg(0x0420, 0x10000);
gfxWriteGxReg(0x0424, 0x19D);
gfxWriteGxReg(0x0428, 2);
gfxWriteGxReg(0x042C, 0x192);
gfxWriteGxReg(0x0430, 0x192);
gfxWriteGxReg(0x0434, 0x192);
gfxWriteGxReg(0x0438, 1);
gfxWriteGxReg(0x043C, 2);
gfxWriteGxReg(0x0440, (0x196 << 16) | 0x192);
gfxWriteGxReg(0x0444, 0);
gfxWriteGxReg(0x0448, 0);
// Top screen fb geometry
gfxWriteGxReg(0x045C, (400 << 16) | 240); // dimensions
gfxWriteGxReg(0x0460, (0x1C1 << 16) | 0xD1);
gfxWriteGxReg(0x0464, (0x192 << 16) | 2);
// Top screen framebuffer format (initial)
gfxWriteGxReg(0x0470, 0x80340);
// Top screen unknown reg @ 0x9C
gfxWriteGxReg(0x049C, 0);
// Bottom screen LCD configuration
// Bottom screen sync registers:
gfxWriteGxReg(0x0500, 0x1C2);
gfxWriteGxReg(0x0504, 0xD1);
gfxWriteGxReg(0x0508, 0x1C1);
gfxWriteGxReg(0x050C, 0x1C1);
gfxWriteGxReg(0x0510, 0xCD);
gfxWriteGxReg(0x0514, 0xCF);
gfxWriteGxReg(0x0518, 0xD1);
gfxWriteGxReg(0x051C, (0x1C5 << 16) | 0x1C1);
gfxWriteGxReg(0x0520, 0x10000);
gfxWriteGxReg(0x0524, 0x19D);
gfxWriteGxReg(0x0528, 0x52);
gfxWriteGxReg(0x052C, 0x192);
gfxWriteGxReg(0x0530, 0x192);
gfxWriteGxReg(0x0534, 0x4F);
gfxWriteGxReg(0x0538, 0x50);
gfxWriteGxReg(0x053C, 0x52);
gfxWriteGxReg(0x0540, (0x198 << 16) | 0x194);
gfxWriteGxReg(0x0544, 0);
gfxWriteGxReg(0x0548, 0x11);
// Bottom screen fb geometry
gfxWriteGxReg(0x055C, (320 << 16) | 240); // dimensions
gfxWriteGxReg(0x0560, (0x1C1 << 16) | 0xD1);
gfxWriteGxReg(0x0564, (0x192 << 16) | 0x52);
// Bottom screen framebuffer format (initial)
gfxWriteGxReg(0x0570, 0x80300);
// Bottom screen unknown reg @ 0x9C
gfxWriteGxReg(0x059C, 0);
// Initial, blank framebuffer (top left A/B, bottom A/B, top right A/B)
gfxWriteGxReg(0x0468, 0x18300000);
gfxWriteGxReg(0x046C, 0x18300000);
gfxWriteGxReg(0x0568, 0x18300000);
gfxWriteGxReg(0x056C, 0x18300000);
gfxWriteGxReg(0x0494, 0x18300000);
gfxWriteGxReg(0x0498, 0x18300000);
// Framebuffer select: A
gfxWriteGxReg(0x0478, 1);
gfxWriteGxReg(0x0578, 1);
// Clear DMA transfer (PPF) "transfer finished" bit
gfxWriteGxRegMasked(0x0C18, 0, 0xFF00);
// GX_GPU_CLK |= 0x70000 (value is 0x100 when gsp starts, enough to at least display framebuffers & have memory fill work)
// This enables the clock to some GPU components
gfxWriteGxReg(0x0004, 0x70100);
// Clear Memory Fill (PSC0 and PSC1) "busy" and "finished" bits
gfxWriteGxRegMasked(0x001C, 0, 0xFF);
gfxWriteGxRegMasked(0x002C, 0, 0xFF);
// More init registers
gfxWriteGxReg(0x0050, 0x22221200);
gfxWriteGxRegMasked(0x0054, 0xFF2, 0xFFFF);
// Enable some LCD clocks (?) (unsure)
gfxWriteGxReg(0x0474, 0x10501);
gfxWriteGxReg(0x0574, 0x10501);
}
void gfxInit(GSPGPU_FramebufferFormats topFormat, GSPGPU_FramebufferFormats bottomFormat, bool vrambuffers)
void gfxInit(GSPGPU_FramebufferFormat topFormat, GSPGPU_FramebufferFormat bottomFormat, bool vrambuffers)
{
if (vrambuffers)
{
screenAlloc = vramAlloc;
screenFree = vramFree;
} else {
gfxIsVram = true;
}
else
{
screenAlloc = linearAlloc;
screenFree = linearFree;
gfxIsVram = false;
}
// Initialize GSP
gspInit();
gfxSharedMemory=(u8*)mappableAlloc(0x1000);
GSPGPU_AcquireRight(0);
//setup our gsp shared mem section
svcCreateEvent(&gspEvent, RESET_ONESHOT);
// The 0x2A07 success code is returned only for the very first call to that function (globally)
if (GSPGPU_RegisterInterruptRelayQueue(gspEvent, 0x1, &gspSharedMemHandle, &gfxThreadID) == 0x2A07)
gfxGxHwInit();
svcMapMemoryBlock(gspSharedMemHandle, (u32)gfxSharedMemory, 0x3, 0x10000000);
// default gspHeap configuration :
// topleft1 0x00000000-0x00046500
// topleft2 0x00046500-0x0008CA00
// bottom1 0x0008CA00-0x000C4E00
// bottom2 0x000C4E00-0x000FD200
// if 3d enabled :
// topright1 0x000FD200-0x00143700
// topright2 0x00143700-0x00189C00
u32 topSize = 400 * 240 * __get_bytes_per_pixel(topFormat);
u32 bottomSize = 320 * 240 * __get_bytes_per_pixel(bottomFormat);
gfxTopLeftFramebuffers[0]=screenAlloc(topSize);
gfxTopLeftFramebuffers[1]=screenAlloc(topSize);
gfxBottomFramebuffers[0]=screenAlloc(bottomSize);
gfxBottomFramebuffers[1]=screenAlloc(bottomSize);
gfxTopRightFramebuffers[0]=screenAlloc(topSize);
gfxTopRightFramebuffers[1]=screenAlloc(topSize);
gfxTopFramebufferMaxSize = topSize;
gfxBottomFramebufferMaxSize = bottomSize;
enable3d=false;
//set requested modes
// Initialize configuration
gfxSet3D(false);
gfxSetScreenFormat(GFX_TOP, topFormat);
gfxSetScreenFormat(GFX_BOTTOM, bottomFormat);
gfxSetDoubleBuffering(GFX_TOP, true);
gfxSetDoubleBuffering(GFX_BOTTOM, true);
//initialize framebuffer info structures
gfxSetFramebufferInfo(GFX_TOP, 0);
gfxSetFramebufferInfo(GFX_BOTTOM, 0);
// Present the framebuffers
gfxCurBuf[0] = gfxCurBuf[1] = 0;
gfxPresentFramebuffer(GFX_TOP, 0, false);
gfxPresentFramebuffer(GFX_BOTTOM, 0, false);
//GSP shared mem : 0x2779F000
gxCmdBuf=(u32*)(gfxSharedMemory+0x800+gfxThreadID*0x200);
currentBuffer[0]=0;
currentBuffer[1]=0;
// Initialize event handler and wait for VBlank
gspInitEventHandler(gspEvent, (vu8*) gfxSharedMemory, gfxThreadID);
// Wait for VBlank and turn the LCD on
gspWaitForVBlank();
GSPGPU_SetLcdForceBlack(0x0);
}
void gfxInitDefault(void) {
void gfxInitDefault(void)
{
gfxInit(GSP_BGR8_OES,GSP_BGR8_OES,false);
}
void gfxExit(void)
{
if (screenFree == NULL) return;
if (screenFree == NULL)
return;
// Exit event handler
gspExitEventHandler();
// Free framebuffers
screenFree(gfxTopRightFramebuffers[1]);
screenFree(gfxTopRightFramebuffers[0]);
screenFree(gfxBottomFramebuffers[1]);
screenFree(gfxBottomFramebuffers[0]);
screenFree(gfxTopLeftFramebuffers[1]);
screenFree(gfxTopLeftFramebuffers[0]);
//unmap GSP shared mem
svcUnmapMemoryBlock(gspSharedMemHandle, (u32)gfxSharedMemory);
GSPGPU_UnregisterInterruptRelayQueue();
svcCloseHandle(gspSharedMemHandle);
if(gfxSharedMemory != NULL)
if (gspHasGpuRight())
{
mappableFree(gfxSharedMemory);
gfxSharedMemory = NULL;
// Wait for VBlank and turn the LCD off
gspWaitForVBlank();
GSPGPU_SetLcdForceBlack(0x1);
}
svcCloseHandle(gspEvent);
GSPGPU_ReleaseRight();
// Free framebuffers
screenFree(gfxTopFramebuffers[0]);
screenFree(gfxTopFramebuffers[1]);
screenFree(gfxBottomFramebuffers[0]);
screenFree(gfxBottomFramebuffers[1]);
gfxTopFramebuffers[0] = gfxTopFramebuffers[1] = NULL;
gfxBottomFramebuffers[0] = gfxBottomFramebuffers[1] = NULL;
gfxTopFramebufferMaxSize = gfxBottomFramebufferMaxSize = 0;
// Deinitialize GSP
gspExit();
screenFree = NULL;
@ -384,46 +191,75 @@ void gfxExit(void)
u8* gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16* width, u16* height)
{
if(width)*width=240;
unsigned id = gfxCurBuf[screen]^gfxIsDoubleBuf[screen];
unsigned scr_width = GSP_SCREEN_WIDTH;
unsigned scr_height;
u8* fb;
if (screen == GFX_TOP)
{
if(height)*height=400;
return (side==GFX_LEFT || !enable3d)?(gfxTopLeftFramebuffers[currentBuffer[0]^doubleBuf[0]]):(gfxTopRightFramebuffers[currentBuffer[0]^doubleBuf[0]]);
}else{
if(height)*height=320;
return gfxBottomFramebuffers[currentBuffer[1]^doubleBuf[1]];
fb = gfxTopFramebuffers[id];
scr_height = GSP_SCREEN_HEIGHT_TOP;
switch (gfxTopMode)
{
default:
case MODE_2D:
break;
case MODE_3D:
if (side != GFX_LEFT)
fb += gfxTopFramebufferMaxSize/2;
break;
case MODE_WIDE:
scr_height = GSP_SCREEN_HEIGHT_TOP_2X;
break;
}
}
else // GFX_BOTTOM
{
fb = gfxBottomFramebuffers[id];
scr_height = GSP_SCREEN_HEIGHT_BOTTOM;
}
if (width)
*width = scr_width;
if (height)
*height = scr_height;
return fb;
}
void gfxFlushBuffers(void)
{
u32 topSize = 400 * 240 * __get_bytes_per_pixel(gfxGetScreenFormat(GFX_TOP));
u32 bottomSize = 320 * 240 * __get_bytes_per_pixel(gfxGetScreenFormat(GFX_BOTTOM));
const u32 baseSize = GSP_SCREEN_WIDTH * gspGetBytesPerPixel(gfxGetScreenFormat(GFX_TOP));
const u32 topSize = GSP_SCREEN_HEIGHT_TOP * baseSize;
const u32 topSize2x = GSP_SCREEN_HEIGHT_TOP_2X * baseSize;
const u32 bottomSize = GSP_SCREEN_HEIGHT_BOTTOM * baseSize;
GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL), topSize);
if(enable3d)GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL), topSize);
GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL), gfxTopMode == MODE_WIDE ? topSize2x : topSize);
if (gfxTopMode == MODE_3D)
GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL), topSize);
GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), bottomSize);
}
void gfxScreenSwapBuffers(gfxScreen_t scr, bool hasStereo)
{
gfxCurBuf[scr] ^= gfxIsDoubleBuf[scr];
gfxPresentFramebuffer(scr, gfxCurBuf[scr], hasStereo);
}
void gfxConfigScreen(gfxScreen_t scr, bool immediate)
{
currentBuffer[scr]^=doubleBuf[scr];
gfxSetFramebufferInfo(scr, currentBuffer[scr]);
if (immediate)
GSPGPU_SetBufferSwap(scr, framebufferInfoSt[scr]);
else
gfxWriteFramebufferInfo(scr);
gfxScreenSwapBuffers(scr, true);
}
void gfxSwapBuffers(void)
{
gfxConfigScreen(GFX_TOP, true);
gfxConfigScreen(GFX_BOTTOM, true);
gfxScreenSwapBuffers(GFX_TOP, true);
gfxScreenSwapBuffers(GFX_BOTTOM, true);
}
void gfxSwapBuffersGpu(void)
{
gfxConfigScreen(GFX_TOP, false);
gfxConfigScreen(GFX_BOTTOM, false);
gfxScreenSwapBuffers(GFX_TOP, true);
gfxScreenSwapBuffers(GFX_BOTTOM, true);
}

View File

@ -9,7 +9,6 @@
#include <3ds/gpu/gx.h>
#include <3ds/services/gspgpu.h>
u32* gxCmdBuf;
static gxCmdQueue_s* boundQueue;
// Dummy version to avoid linking in gxqueue.c if not actually used
@ -30,7 +29,7 @@ static Result submitGxCommand(u32 gxCommand[0x8])
return 0;
}
else
return gspSubmitGxCommand(gxCmdBuf, gxCommand);
return gspSubmitGxCommand(gxCommand);
}
Result GX_RequestDma(u32* src, u32* dst, u32 length)
@ -115,5 +114,5 @@ Result GX_FlushCacheRegions(u32* buf0a, u32 buf0s, u32* buf1a, u32 buf1s, u32* b
gxCommand[6]=(u32)buf2s; //buf2 size
gxCommand[7]=0x0;
return gspSubmitGxCommand(gxCmdBuf, gxCommand);
return gspSubmitGxCommand(gxCommand);
}

View File

@ -8,7 +8,6 @@
#define MAX_PARALLEL_CMDS 3
extern u32* gxCmdBuf;
static gxCmdQueue_s* curQueue;
static bool isActive, isRunning, shouldStop;
static LightLock queueLock = 1;
@ -21,7 +20,7 @@ static void gxCmdQueueDoCommands(void)
while (curQueue->curEntry < curQueue->numEntries && batchSize--)
{
gxCmdEntry_s* entry = &curQueue->entries[curQueue->curEntry++];
gspSubmitGxCommand(gxCmdBuf, entry->data);
gspSubmitGxCommand(entry->data);
}
}

View File

@ -13,17 +13,9 @@ Result shaderInstanceInit(shaderInstance_s* si, DVLE_s* dvle)
{
if(!si || !dvle)return -1;
memset(si, 0, sizeof(*si));
si->dvle = dvle;
si->boolUniforms = 0;
si->boolUniformMask = 0;
si->intUniforms[0] = 0x00000000;
si->intUniforms[1] = 0x00000000;
si->intUniforms[2] = 0x00000000;
si->intUniforms[3] = 0x00000000;
si->float24Uniforms = NULL;
si->intUniformMask = 0;
int i;
DVLE_constEntry_s* cnst = dvle->constTableData;
if(cnst)

View File

@ -8,6 +8,12 @@
#define THREADVARS_MAGIC 0x21545624 // !TV$
#define FS_OVERRIDE_MAGIC 0x21465324 // !FS$
extern const size_t __tdata_align;
extern const u8 __tdata_lma[];
extern const u8 __tdata_lma_end[];
extern u8 __tls_start[];
extern u8 __tls_end[];
// Keep this structure under 0x80 bytes
typedef struct
{
@ -31,7 +37,24 @@ typedef struct
bool srv_blocking_policy;
} ThreadVars;
struct Thread_tag
{
Handle handle;
ThreadFunc ep;
void* arg;
int rc;
bool detached, finished;
struct _reent reent;
void* stacktop;
};
static inline ThreadVars* getThreadVars(void)
{
return (ThreadVars*)getThreadLocalStorage();
}
void initThreadVars(struct Thread_tag *thread);
static inline size_t alignTo(const size_t base, const size_t align) {
return (base + (align - 1)) & ~(align - 1);
}

View File

@ -50,10 +50,14 @@ void ndspChnReset(int id)
ndspChnSt* chn = &ndspChn[id];
LightLock_Lock(&chn->lock);
chn->flags = ~0;
chn->syncCount = 1;
chn->syncCount ++;
chn->waveBufSeqPos = 0;
chn->samplePos = 0;
chn->waveBuf = NULL;
while (chn->waveBuf)
{
chn->waveBuf->status = NDSP_WBUF_DONE;
chn->waveBuf = chn->waveBuf->next;
}
chn->wavBufCount = 0;
chn->wavBufIdNext = 0;
chn->wavBufSeq = 0;
@ -96,6 +100,11 @@ void ndspChnSetFormat(int id, u16 format)
ndspChn[id].format = format;
}
u16 ndspChnGetFormat(int id)
{
return ndspChn[id].format;
}
bool ndspChnIsPaused(int id)
{
return ndspChn[id].paused;
@ -119,6 +128,12 @@ void ndspChnSetInterp(int id, ndspInterpType type)
LightLock_Unlock(&chn->lock);
}
ndspInterpType ndspChnGetInterp(int id)
{
ndspChnSt* chn = &ndspChn[id];
return chn->interpType;
}
void ndspChnSetRate(int id, float rate)
{
ndspChnSt* chn = &ndspChn[id];
@ -128,6 +143,12 @@ void ndspChnSetRate(int id, float rate)
LightLock_Unlock(&chn->lock);
}
float ndspChnGetRate(int id)
{
ndspChnSt* chn = &ndspChn[id];
return chn->rate;
}
void ndspChnSetMix(int id, float mix[12])
{
ndspChnSt* chn = &ndspChn[id];
@ -137,6 +158,14 @@ void ndspChnSetMix(int id, float mix[12])
LightLock_Unlock(&chn->lock);
}
void ndspChnGetMix(int id, float out_mix[12])
{
ndspChnSt* chn = &ndspChn[id];
LightLock_Lock(&chn->lock);
memcpy(out_mix, chn->mix, sizeof(ndspChn[id].mix));
LightLock_Unlock(&chn->lock);
}
void ndspChnSetAdpcmCoefs(int id, u16 coefs[16])
{
ndspChnSt* chn = &ndspChn[id];
@ -150,7 +179,11 @@ void ndspChnWaveBufClear(int id)
{
ndspChnSt* chn = &ndspChn[id];
LightLock_Lock(&chn->lock);
chn->waveBuf = NULL;
while (chn->waveBuf)
{
chn->waveBuf->status = NDSP_WBUF_DONE;
chn->waveBuf = chn->waveBuf->next;
}
chn->waveBufSeqPos = 0;
chn->wavBufCount = 0;
chn->wavBufIdNext = 0;
@ -167,6 +200,12 @@ void ndspChnWaveBufAdd(int id, ndspWaveBuf* buf)
if (!buf->nsamples) return;
LightLock_Lock(&chn->lock);
if (buf->status == NDSP_WBUF_QUEUED || buf->status == NDSP_WBUF_PLAYING)
{
// Wavebuf is already queued, avoid requeuing it...
LightLock_Unlock(&chn->lock);
return;
}
buf->next = NULL;
buf->status = NDSP_WBUF_QUEUED;
ndspWaveBuf* cb = chn->waveBuf;
@ -274,6 +313,8 @@ void ndspiInitChn(void)
for (i = 0; i < 24; i ++)
{
LightLock_Init(&ndspChn[i].lock);
ndspChn[i].syncCount = 0;
ndspChn[i].waveBuf = NULL;
ndspChnReset(i);
}
}
@ -463,31 +504,37 @@ void ndspiReadChnState(void)
if (wb)
{
ndspWaveBuf* doneList = NULL;
if (chn->wavBufCount)
while (chn->wavBufCount)
{
while (wb->sequence_id != seqId)
u16 wbSeqId = wb->sequence_id;
if (wbSeqId == seqId)
{
wb->status = NDSP_WBUF_PLAYING;
break;
}
chn->wavBufCount--;
bool shouldBreak = seqId == 0 && (wb->sequence_id == st->lastSeqId || st->lastSeqId == 0);
ndspWaveBuf* next = wb->next;
wb->next = doneList;
doneList = wb;
wb = next;
if (!wb || shouldBreak || chn->wavBufCount == 0)
if (seqId == 0 && (wbSeqId == st->lastSeqId || st->lastSeqId == 0))
break;
}
if (wb)
wb->status = NDSP_WBUF_PLAYING;
}
if (seqId == 0)
chn->wavBufCount = 0;
__dmb();
chn->waveBuf = wb;
for (; doneList; doneList = doneList->next)
doneList->status = NDSP_WBUF_DONE;
}
LightLock_Unlock(&chn->lock);
}
chn->playing = (st->flags & 0xFF) ? true : false;
chn->playing = (st->flags & 0xFF) == 1;
}
}
}

View File

@ -9,7 +9,7 @@
u16 ndspFrameId, ndspBufferCurId, ndspBufferId;
void* ndspVars[16][2];
static bool bComponentLoaded = false, bDspReady = false, bSleeping = false, bActuallySleeping = false, bNeedsSync = false;
static bool bDspReady, bEnteringSleep, bSleeping, bCancelReceived;
static u32 droppedFrames, frameCount;
static const void* componentBin;
@ -17,7 +17,7 @@ static u32 componentSize;
static u16 componentProgMask, componentDataMask;
static bool componentFree;
static aptHookCookie aptCookie;
static dspHookCookie ndspHookCookie;
static Handle irqEvent, dspSem;
static LightEvent sleepEvent;
@ -28,18 +28,31 @@ static u8 dspVar5Backup[0x1080];
static volatile bool ndspThreadRun;
static Thread ndspThread;
static Result ndspLoadComponent(void)
{
if (!componentBin) return 1;
return DSP_LoadComponent(componentBin, componentSize, componentProgMask, componentDataMask, &bComponentLoaded);
}
static inline void ndspWaitForIrq(void)
static inline bool ndspWaitForIrq(u64 timeout_ns)
{
LightLock_Lock(&ndspMutex);
svcWaitSynchronization(irqEvent, U64_MAX);
// BUG: Official sw has a race condition here when entering sleep mode. DSP state might
// have already been torn down, and thus this ends up waiting on an invalid (0) handle.
// There's code that tries to panic if an error happens, however said error handler fails
// to actually panic because it checks that the result code level is specifically 'Fatal'.
// Note that the "invalid handle" result code has a 'Permanent' level...
// We will instead handle invalid handles properly and immediately return.
bool waitOk = false;
if (irqEvent)
{
Result rc = svcWaitSynchronization(irqEvent, timeout_ns);
if (R_FAILED(rc))
svcBreak(USERBREAK_PANIC); // Shouldn't happen.
waitOk = R_DESCRIPTION(rc) != RD_TIMEOUT;
}
if (waitOk)
svcClearEvent(irqEvent);
LightLock_Unlock(&ndspMutex);
return waitOk;
}
static inline void ndspSetCounter(int a, int counter)
@ -101,12 +114,24 @@ static void ndspInitMaster(void)
{
memset(&ndspMaster, 0, sizeof(ndspMaster));
LightLock_Init(&ndspMaster.lock);
ndspMaster.flags = ~0;
ndspMaster.masterVol = 1.0f;
ndspMaster.outputMode = NDSP_OUTPUT_STEREO;
ndspMaster.clippingMode = NDSP_CLIP_SOFT;
ndspMaster.outputCount = 2;
ndspMaster.surround.depth = 0x7FFF;
ndspMaster.surround.rearRatio = 0x8000;
// Use the output mode set in system settings, if available
Result rc = cfguInit();
if (R_SUCCEEDED(rc))
{
u8 outMode;
rc = CFGU_GetConfigInfoBlk2(sizeof(outMode), 0x70001, &outMode);
if (R_SUCCEEDED(rc))
ndspMaster.outputMode = outMode;
cfguExit();
}
}
static void ndspUpdateMaster(void)
@ -117,7 +142,7 @@ static void ndspUpdateMaster(void)
u32 flags = m->flags, mflags = ndspMaster.flags;
int i;
m->headsetConnected = *(vu8*)0x1FF810C0;
m->headsetConnected = osIsHeadsetConnected();
flags |= 0x10000000;
if (mflags & MFLAG_MASTERVOL)
@ -209,13 +234,10 @@ static Result ndspInitialize(bool resume)
{
Result rc;
rc = ndspLoadComponent();
if (R_FAILED(rc)) return rc;
rc = svcCreateEvent(&irqEvent, RESET_STICKY);
if (R_FAILED(rc)) goto _fail1;
rc = DSP_RegisterInterruptEvents(irqEvent, 2, 2);
rc = DSP_RegisterInterruptEvents(irqEvent, DSP_INTERRUPT_PIPE, 2);
if (R_FAILED(rc)) goto _fail2;
rc = DSP_GetSemaphoreHandle(&dspSem);
@ -223,42 +245,49 @@ static Result ndspInitialize(bool resume)
DSP_SetSemaphoreMask(0x2000);
u16 val = resume ? 2 : 0;
if (resume)
{
memcpy(ndspVars[5][0], dspVar5Backup, sizeof(dspVar5Backup));
__dsb();
}
u16 val = resume ? 2 : 0;
DSP_WriteProcessPipe(2, &val, 4);
DSP_SetSemaphore(0x4000);
ndspWaitForIrq();
ndspWaitForIrq(U64_MAX);
DSP_ReadPipeIfPossible(2, 0, &val, sizeof(val), NULL);
u16 vars[16];
DSP_ReadPipeIfPossible(2, 0, vars, val*2, NULL);
int i;
for (i = 0; i < val; i ++)
for (unsigned i = 0; i < val; i ++)
{
DSP_ConvertProcessAddressFromDspDram(vars[i], (u32*)&ndspVars[i][0]);
DSP_ConvertProcessAddressFromDspDram(vars[i] | 0x10000, (u32*)&ndspVars[i][1]);
}
DSP_SetSemaphore(0x4000);
frameCount = 0;
ndspFrameId = 4;
ndspSetCounter(0, 4);
ndspFrameId++;
svcSignalEvent(dspSem);
ndspBufferCurId = ndspFrameId & 1;
ndspBufferId = ndspFrameId & 1;
bDspReady = true;
ndspDirtyMaster();
ndspUpdateMaster();
if (resume)
{
ndspiDirtyChn();
ndspiUpdateChn();
// Force update effect params here
}
ndspDirtyMaster();
ndspUpdateMaster();
// TODO: force update effect params
}
return 0;
_fail3:
@ -266,57 +295,80 @@ _fail3:
_fail2:
svcCloseHandle(irqEvent);
_fail1:
DSP_UnloadComponent();
return rc;
}
static void ndspFinalize(bool suspend)
{
LightLock_Lock(&ndspMutex);
u16 val = suspend ? 3 : 1;
DSP_WriteProcessPipe(2, &val, 4);
for (;;)
{
bool ready;
bool ready = false;
DSP_RecvDataIsReady(0, &ready);
if (ready)
{
val = 0;
DSP_RecvData(0, &val);
if (val == 1)
break;
}
svcSleepThread(4888000); // 4.888ms (approx. one sound frame)
}
if (suspend)
memcpy(dspVar5Backup, ndspVars[5][0], sizeof(dspVar5Backup));
DSP_RegisterInterruptEvents(0, 2, 2);
svcCloseHandle(irqEvent);
svcCloseHandle(dspSem);
DSP_UnloadComponent();
bComponentLoaded = false;
LightLock_Lock(&ndspMutex);
bDspReady = false;
svcCloseHandle(irqEvent);
irqEvent = 0;
DSP_RegisterInterruptEvents(0, DSP_INTERRUPT_PIPE, 2);
svcCloseHandle(dspSem);
dspSem = 0;
LightLock_Unlock(&ndspMutex);
}
static void ndspAptHook(APT_HookType hook, void* param)
static void ndspHookCallback(DSP_HookType hook)
{
switch (hook)
{
case APTHOOK_ONRESTORE:
case APTHOOK_ONWAKEUP:
bSleeping = false;
ndspInitialize(true);
if (bActuallySleeping)
case DSPHOOK_ONSLEEP:
if (!bSleeping)
{
bActuallySleeping = false;
bEnteringSleep = true;
ndspFinalize(true);
bSleeping = true;
}
break;
case DSPHOOK_ONWAKEUP:
if (bSleeping)
{
Result res = ndspInitialize(true);
if (R_FAILED(res))
svcBreak(USERBREAK_PANIC); // Shouldn't happen.
bSleeping = false;
bEnteringSleep = false;
__dsb();
LightEvent_Signal(&sleepEvent);
}
break;
case APTHOOK_ONSUSPEND:
case APTHOOK_ONSLEEP:
bSleeping = true;
ndspFinalize(true);
case DSPHOOK_ONCANCEL:
if (bSleeping)
{
bCancelReceived = true;
bSleeping = false;
bEnteringSleep = false;
__dsb();
LightEvent_Signal(&sleepEvent);
}
break;
default:
@ -326,13 +378,36 @@ static void ndspAptHook(APT_HookType hook, void* param)
static void ndspSync(void)
{
if (bSleeping)
// If we are about to sleep...
if (bEnteringSleep)
{
bActuallySleeping = true;
LightEvent_Wait(&sleepEvent);
// Check whether the DSP is still running by attempting to wait with a timeout
if (ndspWaitForIrq(9776000)) // 9.776ms (approx. two sound frames)
goto _receiveState; // it's not, so just proceed as usual
// The wait failed, so the DSP is indeed sleeping
bSleeping = true;
}
ndspWaitForIrq();
// If we are sleeping, wait for the DSP to wake up
if (bSleeping)
{
LightEvent_Wait(&sleepEvent);
LightEvent_Clear(&sleepEvent);
}
// If we were cancelled, do a dummy wait instead
if (bCancelReceived)
{
svcSleepThread(4888000); // 4.888ms (approx. one sound frame)
return;
}
// In any other case - wait for the DSP to notify us
ndspWaitForIrq(U64_MAX);
_receiveState:
// Receive and update state (if the DSP is ready)
if (bDspReady)
{
int counter = ndspGetCounter(~ndspFrameId & 1);
@ -346,7 +421,6 @@ static void ndspSync(void)
ndspUpdateCapture((s16*)ndspVars[6][ndspBufferId], 160);
droppedFrames += *((u16*)ndspVars[5][ndspBufferId] + 1);
}
bNeedsSync = false;
}
}
@ -357,19 +431,15 @@ static void ndspThreadMain(void* arg)
{
ndspSync();
// Call callbacks here
if (ndspMaster.callback)
ndspMaster.callback(ndspMaster.callbackData);
if (bSleeping || !bDspReady)
if (bSleeping || bCancelReceived || !bDspReady)
continue;
if (bNeedsSync)
ndspSync();
ndspUpdateMaster();
// Call aux user callback here if enabled
// Execute DSP effects here
// TODO: call aux user callback if enabled
// TODO: execute DSP effects
ndspiUpdateChn();
ndspSetCounter(ndspBufferCurId, ndspFrameId++);
@ -377,7 +447,6 @@ static void ndspThreadMain(void* arg)
ndspBufferCurId = ndspFrameId & 1;
frameCount++;
bNeedsSync = true;
}
}
@ -403,8 +472,8 @@ static bool ndspFindAndLoadComponent(void)
do
{
static const char dsp_filename[] = "/3ds/dspfirm.cdc";
FS_Path archPath = { PATH_EMPTY, 1, (u8*)"" };
FS_Path filePath = { PATH_ASCII, sizeof(dsp_filename), (u8*)dsp_filename };
FS_Path archPath = { PATH_EMPTY, 1, "" };
FS_Path filePath = { PATH_ASCII, sizeof(dsp_filename), dsp_filename };
rc = FSUSER_OpenFileDirectly(&rsrc, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0);
if (R_FAILED(rc)) break;
@ -433,7 +502,7 @@ static bool ndspFindAndLoadComponent(void)
{
extern u32 fake_heap_end;
u32 mapAddr = (fake_heap_end+0xFFF) &~ 0xFFF;
rc = svcMapMemoryBlock(rsrc, mapAddr, 0x3, 0x3);
rc = svcMapMemoryBlock(rsrc, mapAddr, MEMPERM_READWRITE, MEMPERM_READWRITE);
if (R_FAILED(rc)) break;
componentSize = *(u32*)(mapAddr + 0x104);
@ -465,31 +534,26 @@ Result ndspInit(void)
}
LightLock_Init(&ndspMutex);
ndspInitMaster();
ndspiInitChn();
rc = cfguInit();
if (R_SUCCEEDED(rc))
{
u8 outMode;
rc = CFGU_GetConfigInfoBlk2(sizeof(outMode), 0x70001, &outMode);
if (R_SUCCEEDED(rc))
ndspMaster.outputMode = outMode;
cfguExit();
}
LightEvent_Init(&sleepEvent, RESET_STICKY);
rc = dspInit();
if (R_FAILED(rc)) goto _fail1;
rc = DSP_LoadComponent(componentBin, componentSize, componentProgMask, componentDataMask, NULL);
if (R_FAILED(rc)) goto _fail2;
rc = ndspInitialize(false);
if (R_FAILED(rc)) goto _fail2;
LightEvent_Init(&sleepEvent, RESET_ONESHOT);
ndspiInitChn();
ndspInitMaster();
ndspUpdateMaster(); // official sw does this upfront, not sure what's the point
// TODO: initialize effect params
ndspThread = threadCreate(ndspThreadMain, 0x0, NDSP_THREAD_STACK_SIZE, 0x18, -2, true);
if (!ndspThread) goto _fail3;
aptHook(&aptCookie, ndspAptHook, NULL);
dspHook(&ndspHookCookie, ndspHookCallback);
return 0;
_fail3:
@ -510,20 +574,19 @@ _fail0:
void ndspExit(void)
{
if (AtomicDecrement(&ndspRefCount)) return;
if (!bDspReady) return;
ndspThreadRun = false;
if (bActuallySleeping)
{
bActuallySleeping = false;
LightEvent_Signal(&sleepEvent);
}
threadJoin(ndspThread, U64_MAX);
aptUnhook(&aptCookie);
if (!bSleeping)
dspUnhook(&ndspHookCookie);
if (!bCancelReceived)
ndspFinalize(false);
bEnteringSleep = false;
bSleeping = false;
bNeedsSync = false;
bCancelReceived = false;
dspExit();
if (componentFree)
{
free((void*)componentBin);
@ -549,6 +612,11 @@ void ndspSetMasterVol(float volume)
LightLock_Unlock(&ndspMaster.lock);
}
float ndspGetMasterVol(void)
{
return ndspMaster.masterVol;
}
void ndspSetOutputMode(ndspOutputMode mode)
{
LightLock_Lock(&ndspMaster.lock);
@ -557,6 +625,11 @@ void ndspSetOutputMode(ndspOutputMode mode)
LightLock_Unlock(&ndspMaster.lock);
}
ndspOutputMode ndspGetOutputMode(void)
{
return ndspMaster.outputMode;
}
void ndspSetClippingMode(ndspClippingMode mode)
{
LightLock_Lock(&ndspMaster.lock);
@ -565,6 +638,11 @@ void ndspSetClippingMode(ndspClippingMode mode)
LightLock_Unlock(&ndspMaster.lock);
}
ndspClippingMode ndspGetClippingMode(void)
{
return ndspMaster.clippingMode;
}
void ndspSetOutputCount(int count)
{
LightLock_Lock(&ndspMaster.lock);
@ -573,6 +651,11 @@ void ndspSetOutputCount(int count)
LightLock_Unlock(&ndspMaster.lock);
}
int ndspGetOutputCount(void)
{
return ndspMaster.outputCount;
}
void ndspSetCapture(ndspWaveBuf* capture)
{
ndspMaster.capture = capture;
@ -592,6 +675,11 @@ void ndspSurroundSetDepth(u16 depth)
LightLock_Unlock(&ndspMaster.lock);
}
u16 ndspSurroundGetDepth(void)
{
return ndspMaster.surround.depth;
}
void ndspSurroundSetPos(ndspSpeakerPos pos)
{
LightLock_Lock(&ndspMaster.lock);
@ -600,6 +688,11 @@ void ndspSurroundSetPos(ndspSpeakerPos pos)
LightLock_Unlock(&ndspMaster.lock);
}
ndspSpeakerPos ndspSurroundGetPos(void)
{
return ndspMaster.surround.pos;
}
void ndspSurroundSetRearRatio(u16 ratio)
{
LightLock_Lock(&ndspMaster.lock);
@ -608,6 +701,11 @@ void ndspSurroundSetRearRatio(u16 ratio)
LightLock_Unlock(&ndspMaster.lock);
}
u16 ndspSurroundGetRearRatio(void)
{
return ndspMaster.surround.rearRatio;
}
void ndspAuxSetEnable(int id, bool enable)
{
LightLock_Lock(&ndspMaster.lock);
@ -616,6 +714,11 @@ void ndspAuxSetEnable(int id, bool enable)
LightLock_Unlock(&ndspMaster.lock);
}
bool ndspAuxIsEnabled(int id)
{
return ndspMaster.aux[id].enable;
}
void ndspAuxSetFrontBypass(int id, bool bypass)
{
LightLock_Lock(&ndspMaster.lock);
@ -624,6 +727,11 @@ void ndspAuxSetFrontBypass(int id, bool bypass)
LightLock_Unlock(&ndspMaster.lock);
}
bool ndspGetFrontBypass(int id)
{
return ndspMaster.aux[id].frontBypass;
}
void ndspAuxSetVolume(int id, float volume)
{
LightLock_Lock(&ndspMaster.lock);
@ -632,6 +740,11 @@ void ndspAuxSetVolume(int id, float volume)
LightLock_Unlock(&ndspMaster.lock);
}
float ndspAuxGetVolume(int id)
{
return ndspMaster.aux[id].volume;
}
void ndspAuxSetCallback(int id, ndspAuxCallback callback, void* data)
{
ndspMaster.aux[id].callback = callback;

View File

@ -1,21 +1,18 @@
#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/os.h>
#include <3ds/svc.h>
#include <3ds/romfs.h>
#include <3ds/services/ptmsysm.h>
#include <3ds/services/fs.h>
#include <3ds/services/cfgu.h>
#include <sys/time.h>
#include <reent.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
//See here regarding regions: http://3dbrew.org/wiki/Nandrw/sys/SecureInfo_A
static u32 __NVer_tidlow_regionarray[7] = {
#define TID_HIGH 0x000400DB00000000ULL
static const u32 __NVer_tidlow_regionarray[7] =
{
0x00016202, //JPN
0x00016302, //USA
0x00016102, //EUR
@ -25,88 +22,54 @@ static u32 __NVer_tidlow_regionarray[7] = {
0x00016602, //TWN
};
static u32 __CVer_tidlow_regionarray[7] = {
static const u32 __CVer_tidlow_regionarray[7] =
{
0x00017202, //JPN
0x00017302, //USA
0x00017102, //EUR
0x00017202, //"AUS"
0x00017402, //CHN
0x00017502, //KOR
0x00017602 //TWN
0x00017602, //TWN
};
static Result __read_versionbin(FS_ArchiveID archiveId, FS_Path archivePath, FS_Path fileLowPath, OS_VersionBin *versionbin)
static Result osReadVersionBin(u64 tid, OS_VersionBin *versionbin)
{
Result ret = 0;
Handle filehandle = 0;
FILE *f = NULL;
struct romfs_mount *mount;
Result ret = romfsMountFromTitle(tid, MEDIATYPE_NAND, "ver");
if (R_FAILED(ret))
return ret;
ret = FSUSER_OpenFileDirectly(&filehandle, archiveId, archivePath, fileLowPath, FS_OPEN_READ, 0x0);
if(R_FAILED(ret))return ret;
ret = romfsMountFromFile(filehandle, 0x0, &mount);
if(R_FAILED(ret))return ret;
f = fopen("romfs:/version.bin", "r");
if(f==NULL)
{
ret = errno;
}
FILE* f = fopen("ver:/version.bin", "r");
if (!f)
ret = MAKERESULT(RL_PERMANENT, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
else
{
if(fread(versionbin, 1, sizeof(OS_VersionBin), f) != sizeof(OS_VersionBin))ret = -10;
if (fread(versionbin, 1, sizeof(OS_VersionBin), f) != sizeof(OS_VersionBin))
ret = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, RM_APPLICATION, RD_NO_DATA);
fclose(f);
}
romfsUnmount(mount);
romfsUnmount("ver");
return ret;
}
Result osGetSystemVersionData(OS_VersionBin *nver_versionbin, OS_VersionBin *cver_versionbin)
{
Result ret=0;
Result ret = cfguInit();
if(R_FAILED(ret))return ret;
u8 region=0;
u32 archive_lowpath_data[0x10>>2];
u32 file_lowpath_data[0x14>>2];
FS_ArchiveID archiveId;
FS_Path archivePath;
FS_Path fileLowPath;
memset(archive_lowpath_data, 0, sizeof(archive_lowpath_data));
memset(file_lowpath_data, 0, sizeof(file_lowpath_data));
archiveId = 0x2345678a;
archivePath.type = PATH_BINARY;
archivePath.size = 0x10;
archivePath.data = archive_lowpath_data;
fileLowPath.type = PATH_BINARY;
fileLowPath.size = 0x14;
fileLowPath.data = file_lowpath_data;
archive_lowpath_data[1] = 0x000400DB;
ret = cfguInit();
if(R_FAILED(ret))return ret;
ret = CFGU_SecureInfoGetRegion(&region);
if(R_FAILED(ret))return ret;
if(region>=7)return -9;
cfguExit();
archive_lowpath_data[0] = __NVer_tidlow_regionarray[region];
ret = __read_versionbin(archiveId, archivePath, fileLowPath, nver_versionbin);
if(R_FAILED(ret))return ret;
archive_lowpath_data[0] = __CVer_tidlow_regionarray[region];
ret = __read_versionbin(archiveId, archivePath, fileLowPath, cver_versionbin);
if(region>=7)return MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, RM_APPLICATION, RD_OUT_OF_RANGE);
ret = osReadVersionBin(TID_HIGH | __NVer_tidlow_regionarray[region], nver_versionbin);
if(R_FAILED(ret))return ret;
ret = osReadVersionBin(TID_HIGH | __CVer_tidlow_regionarray[region], cver_versionbin);
return ret;
}
@ -121,7 +84,7 @@ Result osGetSystemVersionDataString(OS_VersionBin *nver_versionbin, OS_VersionBi
ret = osGetSystemVersionData(nver_versionbin, cver_versionbin);
if(R_FAILED(ret))return ret;
snprintf(sysverstr, sysverstr_maxsize, "%u.%u.%u-%u%c\n", cver_versionbin->mainver, cver_versionbin->minor, cver_versionbin->build, nver_versionbin->mainver, nver_versionbin->region);
snprintf(sysverstr, sysverstr_maxsize, "%u.%u.%u-%u%c", cver_versionbin->mainver, cver_versionbin->minor, cver_versionbin->build, nver_versionbin->mainver, nver_versionbin->region);
return 0;
}

View File

@ -2,6 +2,7 @@
#include <3ds/result.h>
#include <3ds/os.h>
#include <3ds/svc.h>
#include <3ds/synchronization.h>
#include <3ds/services/ptmsysm.h>
#include <sys/time.h>
@ -13,30 +14,24 @@ static inline double u64_to_double(u64 value) {
return (((double)(u32)(value >> 32))*0x100000000ULL+(u32)value);
}
typedef struct {
u64 date_time;
u64 update_tick;
//...
} datetime_t;
#define __datetime_selector (*(vu32*)0x1FF81000)
#define __datetime0 (*(volatile datetime_t*)0x1FF81020)
#define __datetime1 (*(volatile datetime_t*)0x1FF81040)
__attribute__((weak)) bool __ctru_speedup = false;
//---------------------------------------------------------------------------------
u32 osConvertVirtToPhys(const void* addr) {
//---------------------------------------------------------------------------------
u32 vaddr = (u32)addr;
if(vaddr >= 0x14000000 && vaddr < 0x1c000000)
return vaddr + 0x0c000000; // LINEAR heap
if(vaddr >= 0x1F000000 && vaddr < 0x1F600000)
return vaddr - 0x07000000; // VRAM
if(vaddr >= 0x1FF00000 && vaddr < 0x1FF80000)
return vaddr + 0x00000000; // DSP memory
if(vaddr >= 0x30000000 && vaddr < 0x40000000)
return vaddr - 0x10000000; // Only available under FIRM v8+ for certain processes.
#define CONVERT_REGION(_name) \
if (vaddr >= OS_##_name##_VADDR && vaddr < (OS_##_name##_VADDR + OS_##_name##_SIZE)) \
return vaddr + (OS_##_name##_PADDR - OS_##_name##_VADDR);
CONVERT_REGION(FCRAM);
CONVERT_REGION(VRAM);
CONVERT_REGION(OLD_FCRAM);
CONVERT_REGION(DSPRAM);
CONVERT_REGION(QTMRAM);
CONVERT_REGION(MMIO);
#undef CONVERT_REGION
return 0;
}
@ -44,72 +39,53 @@ u32 osConvertVirtToPhys(const void* addr) {
void* osConvertOldLINEARMemToNew(const void* addr) {
//---------------------------------------------------------------------------------
u32 vaddr = (u32)addr;
if(vaddr >= 0x30000000 && vaddr < 0x40000000)return (void*)vaddr;
if(vaddr >= 0x14000000 && vaddr < 0x1c000000)return (void*)(vaddr+0x1c000000);
return 0;
}
s64 osGetMemRegionUsed(MemRegion region) {
s64 mem_used;
svcGetSystemInfo(&mem_used, 0, region);
return mem_used;
if (vaddr >= OS_FCRAM_VADDR && vaddr < (OS_FCRAM_VADDR+OS_FCRAM_SIZE))
return (void*)vaddr;
if (vaddr >= OS_OLD_FCRAM_VADDR && vaddr < (OS_FCRAM_VADDR+OS_OLD_FCRAM_SIZE))
return (void*)(vaddr + (OS_FCRAM_VADDR-OS_OLD_FCRAM_VADDR));
return NULL;
}
//---------------------------------------------------------------------------------
static datetime_t getSysTime(void) {
osTimeRef_s osGetTimeRef(void) {
//---------------------------------------------------------------------------------
u32 s1, s2 = __datetime_selector & 1;
datetime_t dt;
osTimeRef_s tr;
u32 next = OS_SharedConfig->timeref_cnt;
u32 cur;
do {
s1 = s2;
if(!s1)
dt = __datetime0;
else
dt = __datetime1;
s2 = __datetime_selector & 1;
} while(s2 != s1);
cur = next;
tr = OS_SharedConfig->timeref[cur&1];
__dmb();
next = OS_SharedConfig->timeref_cnt;
} while (cur != next);
return dt;
return tr;
}
//---------------------------------------------------------------------------------
int __libctru_gtod(struct _reent *ptr, struct timeval *tp, struct timezone *tz) {
//---------------------------------------------------------------------------------
if (tp != NULL) {
datetime_t dt = getSysTime();
u64 delta = svcGetSystemTick() - dt.update_tick;
u32 offset = (u32)(u64_to_double(delta)/CPU_TICKS_PER_USEC);
// adjust from 1900 to 1970
u64 now = ((dt.date_time - 2208988800000ULL) * 1000) + offset;
tp->tv_sec = u64_to_double(now)/1000000.0;
tp->tv_usec = now - ((tp->tv_sec) * 1000000);
}
if (tz != NULL) {
tz->tz_minuteswest = 0;
tz->tz_dsttime = 0;
}
return 0;
}
// Returns number of milliseconds since 1st Jan 1900 00:00.
//---------------------------------------------------------------------------------
u64 osGetTime(void) {
//---------------------------------------------------------------------------------
datetime_t dt = getSysTime();
// Read the latest time reference point published by PTM
osTimeRef_s tr = osGetTimeRef();
u64 delta = svcGetSystemTick() - dt.update_tick;
// Calculate the time elapsed since the reference point using the system clock
s64 elapsed_tick = svcGetSystemTick() - tr.value_tick;
s64 elapsed_ms = elapsed_tick * 1000 / tr.sysclock_hz;
return dt.date_time + (u32)(u64_to_double(delta)/CPU_TICKS_PER_MSEC);
// Apply the drift adjustment if present:
// Every time PTM publishes a new reference point it measures by how long the
// system clock has drifted with respect to the RTC. It also recalculates the
// system clock frequency using RTC data in order to minimize future drift.
// The idea behind the following logic is to reapply the inaccuracy to the
// calculated timestamp, but gradually reducing it to zero over the course of
// an hour. This ensures the monotonic growth of the returned time value.
const s64 hour_in_ms = 60*60*1000; // milliseconds in an hour
if (tr.drift_ms != 0 && elapsed_ms < hour_in_ms)
elapsed_ms += tr.drift_ms * (hour_in_ms - elapsed_ms) / hour_in_ms;
// Return the final timestamp
return tr.value_ms + elapsed_ms;
}
//---------------------------------------------------------------------------------
@ -119,30 +95,32 @@ double osTickCounterRead(const TickCounter* cnt) {
}
//---------------------------------------------------------------------------------
const char* osStrError(u32 error) {
const char* osStrError(Result error) {
//---------------------------------------------------------------------------------
switch((error>>26) & 0x3F) {
case 0:
switch(R_SUMMARY(error)) {
case RS_SUCCESS:
return "Success.";
case 1:
case RS_NOP:
return "Nothing happened.";
case 2:
case RS_WOULDBLOCK:
return "Would block.";
case 3:
case RS_OUTOFRESOURCE:
return "Not enough resources.";
case 4:
case RS_NOTFOUND:
return "Not found.";
case 5:
case RS_INVALIDSTATE:
return "Invalid state.";
case 6:
case RS_NOTSUPPORTED:
return "Unsupported.";
case 7:
case RS_INVALIDARG:
return "Invalid argument.";
case 8:
case RS_WRONGARG:
return "Wrong argument.";
case 9:
return "Interrupted.";
case 10:
case RS_CANCELED:
return "Cancelled.";
case RS_STATUSCHANGED:
return "Status changed.";
case RS_INTERNAL:
return "Internal error.";
default:
return "Unknown.";

View File

@ -0,0 +1,4 @@
#include "path_buf.h"
char __thread __ctru_dev_path_buf[PATH_MAX+1];
uint16_t __thread __ctru_dev_utf16_buf[PATH_MAX+1];

View File

@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>
#include <limits.h>
extern char __thread __ctru_dev_path_buf[PATH_MAX+1];
extern uint16_t __thread __ctru_dev_utf16_buf[PATH_MAX+1];

View File

@ -16,8 +16,12 @@
#include <3ds/util/utf.h>
#include <3ds/env.h>
#include "path_buf.h"
typedef struct romfs_mount
{
devoptab_t device;
char name[32];
Handle fd;
time_t mtime;
u32 offset;
@ -31,9 +35,6 @@ typedef struct romfs_mount
extern int __system_argc;
extern char** __system_argv;
static char __component[PATH_MAX+1];
static uint16_t __utf16path[PATH_MAX+1];
#define romFS_root(m) ((romfs_dir*)(m)->dirTable)
#define romFS_dir(m,x) ((romfs_dir*) ((u8*)(m)->dirTable + (x)))
#define romFS_file(m,x) ((romfs_file*)((u8*)(m)->fileTable + (x)))
@ -85,9 +86,8 @@ typedef struct
u32 childFile;
} romfs_diriter;
static devoptab_t romFS_devoptab =
static const devoptab_t romFS_devoptab =
{
.name = "romfs",
.structSize = sizeof(romfs_fileobj),
.open_r = romfs_open,
.close_r = romfs_close,
@ -102,7 +102,6 @@ static devoptab_t romFS_devoptab =
.dirreset_r = romfs_dirreset,
.dirnext_r = romfs_dirnext,
.dirclose_r = romfs_dirclose,
.deviceData = 0,
};
//-----------------------------------------------------------------------------
@ -125,9 +124,6 @@ typedef struct
u32 fsOffset;
} _3DSX_Header;
static Result romfsMountCommon(romfs_mount *mount);
static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath);
__attribute__((weak)) const char* __romfs_path = NULL;
static romfs_mount *romfs_mount_list = NULL;
@ -152,16 +148,23 @@ static void romfs_remove(romfs_mount *mount)
static romfs_mount* romfs_alloc(void)
{
romfs_mount *mount = (romfs_mount*)calloc(1, sizeof(romfs_mount));
romfs_mount *mount = (romfs_mount*)malloc(sizeof(romfs_mount));
if (mount)
{
memset(mount, 0, sizeof(*mount));
memcpy(&mount->device, &romFS_devoptab, sizeof(romFS_devoptab));
mount->device.name = mount->name;
mount->device.deviceData = mount;
romfs_insert(mount);
}
return mount;
}
static void romfs_free(romfs_mount *mount)
{
FSFILE_Close(mount->fd);
romfs_remove(mount);
free(mount->fileTable);
free(mount->fileHashTable);
@ -170,222 +173,182 @@ static void romfs_free(romfs_mount *mount)
free(mount);
}
Result romfsMount(struct romfs_mount **p)
Result romfsMountFromFile(Handle fd, u32 offset, const char *name)
{
romfs_mount *mount = romfs_alloc();
if (mount == NULL)
return 99;
if (envIsHomebrew())
{
// RomFS appended to a 3DSX file
FSFILE_Close(fd);
return MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_ROMFS, RD_OUT_OF_MEMORY);
}
mount->fd = fd;
mount->offset = offset;
mount->mtime = time(NULL);
strncpy(mount->name, name, sizeof(mount->name)-1);
if (_romfs_read(mount, 0, &mount->header, sizeof(mount->header)) != sizeof(mount->header))
goto fail_io;
mount->dirHashTable = (u32*)malloc(mount->header.dirHashTableSize);
if (!mount->dirHashTable)
goto fail_oom;
if (!_romfs_read_chk(mount, mount->header.dirHashTableOff, mount->dirHashTable, mount->header.dirHashTableSize))
goto fail_io;
mount->dirTable = malloc(mount->header.dirTableSize);
if (!mount->dirTable)
goto fail_oom;
if (!_romfs_read_chk(mount, mount->header.dirTableOff, mount->dirTable, mount->header.dirTableSize))
goto fail_io;
mount->fileHashTable = (u32*)malloc(mount->header.fileHashTableSize);
if (!mount->fileHashTable)
goto fail_oom;
if (!_romfs_read_chk(mount, mount->header.fileHashTableOff, mount->fileHashTable, mount->header.fileHashTableSize))
goto fail_io;
mount->fileTable = malloc(mount->header.fileTableSize);
if (!mount->fileTable)
goto fail_oom;
if (!_romfs_read_chk(mount, mount->header.fileTableOff, mount->fileTable, mount->header.fileTableSize))
goto fail_io;
mount->cwd = romFS_root(mount);
if (AddDevice(&mount->device) < 0)
goto fail_oom;
return 0;
fail_oom:
romfs_free(mount);
return MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_ROMFS, RD_OUT_OF_MEMORY);
fail_io:
romfs_free(mount);
return MAKERESULT(RL_FATAL, RS_INVALIDSTATE, RM_ROMFS, RD_NOT_FOUND);
}
Result romfsMountSelf(const char* name)
{
// If we are not 3DSX, we need to mount this process' real RomFS
if (!envIsHomebrew())
return romfsMountFromCurrentProcess(name);
// Otherwise, we use the embedded RomFS inside the 3DSX file
// Retrieve the filename of our 3DSX file
const char* filename = __romfs_path;
if (__system_argc > 0 && __system_argv[0])
filename = __system_argv[0];
if (!filename)
{
romfs_free(mount);
return 1;
}
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_ROMFS, RD_NO_DATA);
// Adjust the path name
if (strncmp(filename, "sdmc:/", 6) == 0)
filename += 5;
else if (strncmp(filename, "3dslink:/", 9) == 0)
{
strncpy(__component, "/3ds", PATH_MAX);
strncat(__component, filename+8, PATH_MAX);
__component[PATH_MAX] = 0;
filename = __component;
strncpy(__ctru_dev_path_buf, "/3ds", PATH_MAX);
strncat(__ctru_dev_path_buf, filename+8, PATH_MAX);
__ctru_dev_path_buf[PATH_MAX] = 0;
filename = __ctru_dev_path_buf;
}
else
{
romfs_free(mount);
return 2;
}
return MAKERESULT(RL_USAGE, RS_NOTSUPPORTED, RM_ROMFS, RD_NOT_IMPLEMENTED);
ssize_t units = utf8_to_utf16(__utf16path, (const uint8_t*)filename, PATH_MAX);
if (units < 0)
{
romfs_free(mount);
return 3;
}
if (units >= PATH_MAX)
{
romfs_free(mount);
return 4;
}
__utf16path[units] = 0;
// Convert the path to UTF-16
ssize_t units = utf8_to_utf16(__ctru_dev_utf16_buf, (const uint8_t*)filename, PATH_MAX);
if (units < 0 || units > PATH_MAX)
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_ROMFS, RD_OUT_OF_RANGE);
__ctru_dev_utf16_buf[units] = 0;
FS_Path archPath = { PATH_EMPTY, 1, (u8*)"" };
FS_Path filePath = { PATH_UTF16, (units+1)*2, (u8*)__utf16path };
// Open the file directly
Handle fd = 0;
FS_Path archPath = { PATH_EMPTY, 1, "" };
FS_Path filePath = { PATH_UTF16, (units+1)*2, __ctru_dev_utf16_buf };
Result rc = FSUSER_OpenFileDirectly(&fd, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0);
if (R_FAILED(rc))
return rc;
Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0);
// Read and parse the header
_3DSX_Header hdr;
u32 bytesRead = 0;
rc = FSFILE_Read(fd, &bytesRead, 0, &hdr, sizeof(hdr));
if (R_FAILED(rc))
{
romfs_free(mount);
FSFILE_Close(fd);
return rc;
}
romfsInitMtime(mount, ARCHIVE_SDMC, archPath, filePath);
_3DSX_Header hdr;
if (!_romfs_read_chk(mount, 0, &hdr, sizeof(hdr))) goto _fail0;
if (hdr.magic != _3DSX_MAGIC) goto _fail0;
if (hdr.headerSize < sizeof(hdr)) goto _fail0;
mount->offset = hdr.fsOffset;
if (!mount->offset) goto _fail0;
}
else
// Validate the 3DSX header
if (bytesRead != sizeof(hdr) || hdr.magic != _3DSX_MAGIC || hdr.headerSize < sizeof(hdr))
{
// Regular RomFS
u8 zeros[0xC];
memset(zeros, 0, sizeof(zeros));
FSFILE_Close(fd);
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_ROMFS, RD_NOT_FOUND);
}
FS_Path archPath = { PATH_EMPTY, 1, (u8*)"" };
// Mount the file
return romfsMountFromFile(fd, hdr.fsOffset, name);
}
Result romfsMountFromCurrentProcess(const char *name)
{
// Set up FS_Path structures
u8 zeros[0xC] = {0};
FS_Path archPath = { PATH_EMPTY, 1, "" };
FS_Path filePath = { PATH_BINARY, sizeof(zeros), zeros };
Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0);
if (R_FAILED(rc))
{
romfs_free(mount);
// Open the RomFS file and mount it
Handle fd = 0;
Result rc = FSUSER_OpenFileDirectly(&fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0);
if (R_SUCCEEDED(rc))
rc = romfsMountFromFile(fd, 0, name);
return rc;
}
romfsInitMtime(mount, ARCHIVE_ROMFS, archPath, filePath);
}
Result ret = romfsMountCommon(mount);
if(R_SUCCEEDED(ret) && p)
*p = mount;
return ret;
_fail0:
FSFILE_Close(mount->fd);
romfs_free(mount);
return 10;
}
Result romfsMountFromFile(Handle file, u32 offset, struct romfs_mount **p)
Result romfsMountFromTitle(u64 tid, FS_MediaType mediatype, const char* name)
{
romfs_mount *mount = romfs_alloc();
// Set up FS_Path structures
u32 archPathData[4] = { (u32)tid, (u32)(tid>>32), (u8)mediatype, 0 };
u32 filePathData[5] = { 0 };
FS_Path archPath = { PATH_BINARY, sizeof(archPathData), archPathData };
FS_Path filePath = { PATH_BINARY, sizeof(filePathData), filePathData };
// Open the RomFS file and mount it
Handle fd = 0;
Result rc = FSUSER_OpenFileDirectly(&fd, ARCHIVE_SAVEDATA_AND_CONTENT, archPath, filePath, FS_OPEN_READ, 0);
if (R_SUCCEEDED(rc))
rc = romfsMountFromFile(fd, 0, name);
return rc;
}
Result romfsUnmount(const char* name)
{
// Find the mount
romfs_mount* mount = romfs_mount_list;
while (mount)
{
if (strncmp(mount->name, name, sizeof(mount->name)) == 0)
break;
mount = mount->next;
}
if (mount == NULL)
return 99;
return MAKERESULT(RL_STATUS, RS_NOTFOUND, RM_ROMFS, RD_NOT_FOUND);
mount->fd = file;
mount->offset = offset;
// Remove device
char tmpname[34];
unsigned len = strlen(name);
memcpy(tmpname, mount->name, len);
tmpname[len] = ':';
tmpname[len+1] = 0;
RemoveDevice(tmpname);
Result ret = romfsMountCommon(mount);
if(R_SUCCEEDED(ret) && p)
*p = mount;
return ret;
}
Result romfsMountCommon(romfs_mount *mount)
{
if (_romfs_read(mount, 0, &mount->header, sizeof(mount->header)) != sizeof(mount->header))
goto fail;
mount->dirHashTable = (u32*)malloc(mount->header.dirHashTableSize);
if (!mount->dirHashTable)
goto fail;
if (!_romfs_read_chk(mount, mount->header.dirHashTableOff, mount->dirHashTable, mount->header.dirHashTableSize))
goto fail;
mount->dirTable = malloc(mount->header.dirTableSize);
if (!mount->dirTable)
goto fail;
if (!_romfs_read_chk(mount, mount->header.dirTableOff, mount->dirTable, mount->header.dirTableSize))
goto fail;
mount->fileHashTable = (u32*)malloc(mount->header.fileHashTableSize);
if (!mount->fileHashTable)
goto fail;
if (!_romfs_read_chk(mount, mount->header.fileHashTableOff, mount->fileHashTable, mount->header.fileHashTableSize))
goto fail;
mount->fileTable = malloc(mount->header.fileTableSize);
if (!mount->fileTable)
goto fail;
if (!_romfs_read_chk(mount, mount->header.fileTableOff, mount->fileTable, mount->header.fileTableSize))
goto fail;
mount->cwd = romFS_root(mount);
// add device if this is the first one
if(mount->next == NULL && AddDevice(&romFS_devoptab) < 0)
goto fail;
return 0;
fail:
FSFILE_Close(mount->fd);
// Free the mount
romfs_free(mount);
return 10;
}
static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath)
{
u64 mtime;
FS_Archive arch;
Result rc;
mount->mtime = time(NULL);
rc = FSUSER_OpenArchive(&arch, archId, archPath);
if (R_FAILED(rc))
return;
rc = FSUSER_ControlArchive(arch, ARCHIVE_ACTION_GET_TIMESTAMP,
(void*)filePath.data, filePath.size,
&mtime, sizeof(mtime));
FSUSER_CloseArchive(arch);
if (R_FAILED(rc))
return;
/* convert from milliseconds to seconds */
mtime /= 1000;
/* convert from 2000-based timestamp to UNIX timestamp */
mtime += 946684800;
mount->mtime = mtime;
}
Result romfsBind(struct romfs_mount *mount)
{
for(romfs_mount **it = &romfs_mount_list; *it; it = &(*it)->next)
{
if(*it == mount)
{
*it = mount->next;
romfs_insert(mount);
return 0;
}
}
return 99;
}
Result romfsUnmount(struct romfs_mount *mount)
{
if(mount)
{
// unmount specific
FSFILE_Close(mount->fd);
romfs_free(mount);
}
else
{
// unmount everything
while(romfs_mount_list)
{
FSFILE_Close(romfs_mount_list->fd);
romfs_free(romfs_mount_list);
}
}
// if no more mounts, remove device
if(romfs_mount_list == NULL)
RemoveDevice("romfs:");
return 0;
}
@ -457,7 +420,7 @@ static int navigateToDir(romfs_mount *mount, romfs_dir** ppDir, const char** pPa
while (**pPath)
{
char* slashPos = strchr(*pPath, '/');
char* component = __component;
char* component = __ctru_dev_path_buf;
if (slashPos)
{
@ -487,13 +450,13 @@ static int navigateToDir(romfs_mount *mount, romfs_dir** ppDir, const char** pPa
}
}
units = utf8_to_utf16(__utf16path, (const uint8_t*)component, PATH_MAX);
units = utf8_to_utf16(__ctru_dev_utf16_buf, (const uint8_t*)component, PATH_MAX);
if (units < 0)
return EILSEQ;
if (units >= PATH_MAX)
if (units > PATH_MAX)
return ENAMETOOLONG;
*ppDir = searchForDir(mount, *ppDir, __utf16path, units);
*ppDir = searchForDir(mount, *ppDir, __ctru_dev_utf16_buf, units);
if (!*ppDir)
return ENOENT;
}
@ -569,7 +532,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags,
{
romfs_fileobj* fileobj = (romfs_fileobj*)fileStruct;
fileobj->mount = romfs_mount_list;
fileobj->mount = (romfs_mount*)r->deviceData;
if ((flags & O_ACCMODE) != O_RDONLY)
{
@ -582,7 +545,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags,
if (r->_errno != 0)
return -1;
ssize_t units = utf8_to_utf16(__utf16path, (const uint8_t*)path, PATH_MAX);
ssize_t units = utf8_to_utf16(__ctru_dev_utf16_buf, (const uint8_t*)path, PATH_MAX);
if (units <= 0)
{
r->_errno = EILSEQ;
@ -594,7 +557,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags,
return -1;
}
romfs_file* file = searchForFile(fileobj->mount, curDir, __utf16path, units);
romfs_file* file = searchForFile(fileobj->mount, curDir, __ctru_dev_utf16_buf, units);
if (!file)
{
if(flags & O_CREAT)
@ -698,7 +661,7 @@ int romfs_fstat(struct _reent *r, void *fd, struct stat *st)
int romfs_stat(struct _reent *r, const char *path, struct stat *st)
{
romfs_mount* mount = romfs_mount_list;
romfs_mount* mount = (romfs_mount*)r->deviceData;
romfs_dir* curDir = NULL;
r->_errno = navigateToDir(mount, &curDir, &path, false);
if(r->_errno != 0)
@ -710,26 +673,26 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st)
return 0;
}
ssize_t units = utf8_to_utf16(__utf16path, (const uint8_t*)path, PATH_MAX);
ssize_t units = utf8_to_utf16(__ctru_dev_utf16_buf, (const uint8_t*)path, PATH_MAX);
if (units <= 0)
{
r->_errno = EILSEQ;
return -1;
}
if (units >= PATH_MAX)
if (units > PATH_MAX)
{
r->_errno = ENAMETOOLONG;
return -1;
}
romfs_dir* dir = searchForDir(mount, curDir, __utf16path, units);
romfs_dir* dir = searchForDir(mount, curDir, __ctru_dev_utf16_buf, units);
if(dir)
{
fill_dir(st, mount, dir);
return 0;
}
romfs_file* file = searchForFile(mount, curDir, __utf16path, units);
romfs_file* file = searchForFile(mount, curDir, __ctru_dev_utf16_buf, units);
if(file)
{
fill_file(st, mount, file);
@ -737,12 +700,12 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st)
}
r->_errno = ENOENT;
return 1;
return -1;
}
int romfs_chdir(struct _reent *r, const char *path)
{
romfs_mount* mount = romfs_mount_list;
romfs_mount* mount = (romfs_mount*)r->deviceData;
romfs_dir* curDir = NULL;
r->_errno = navigateToDir(mount, &curDir, &path, true);
if (r->_errno != 0)
@ -756,7 +719,7 @@ DIR_ITER* romfs_diropen(struct _reent *r, DIR_ITER *dirState, const char *path)
{
romfs_diriter* iter = (romfs_diriter*)(dirState->dirStruct);
romfs_dir* curDir = NULL;
iter->mount = romfs_mount_list;
iter->mount = (romfs_mount*)r->deviceData;
r->_errno = navigateToDir(iter->mount, &curDir, &path, true);
if(r->_errno != 0)
@ -822,9 +785,9 @@ int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct s
/* convert name from UTF-16 to UTF-8 */
memset(filename, 0, NAME_MAX);
memcpy(__utf16path, dir->name, dir->nameLen*sizeof(uint16_t));
__utf16path[dir->nameLen/sizeof(uint16_t)] = 0;
units = utf16_to_utf8((uint8_t*)filename, __utf16path, NAME_MAX);
memcpy(__ctru_dev_utf16_buf, dir->name, dir->nameLen*sizeof(uint16_t));
__ctru_dev_utf16_buf[dir->nameLen/sizeof(uint16_t)] = 0;
units = utf16_to_utf8((uint8_t*)filename, __ctru_dev_utf16_buf, NAME_MAX);
if(units < 0)
{
@ -853,9 +816,9 @@ int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct s
/* convert name from UTF-16 to UTF-8 */
memset(filename, 0, NAME_MAX);
memcpy(__utf16path, file->name, file->nameLen*sizeof(uint16_t));
__utf16path[file->nameLen/sizeof(uint16_t)] = 0;
units = utf16_to_utf8((uint8_t*)filename, __utf16path, NAME_MAX);
memcpy(__ctru_dev_utf16_buf, file->name, file->nameLen*sizeof(uint16_t));
__ctru_dev_utf16_buf[file->nameLen/sizeof(uint16_t)] = 0;
units = utf16_to_utf8((uint8_t*)filename, __ctru_dev_utf16_buf, NAME_MAX);
if(units < 0)
{

View File

@ -6,6 +6,7 @@
#include <3ds/synchronization.h>
#include <3ds/services/ac.h>
#include <3ds/ipc.h>
#include <string.h>
static Handle acHandle;
static int acRefCount;
@ -30,6 +31,11 @@ void acExit(void)
svcCloseHandle(acHandle);
}
Handle *acGetSessionHandle(void)
{
return &acHandle;
}
Result acWaitInternetConnection(void)
{
Result ret = 0;
@ -284,3 +290,36 @@ Result ACU_GetProxyUserName(char *username)
return (Result)cmdbuf[1];
}
Result ACI_LoadNetworkSetting(u32 slot)
{
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x401,1,0); // 0x04010040
cmdbuf[1] = slot;
Result ret = 0;
if(R_FAILED(ret = svcSendSyncRequest(acHandle))) return ret;
return (Result)cmdbuf[1];
}
Result ACI_GetNetworkWirelessEssidSecuritySsid(void *ssid)
{
u32* cmdbuf = getThreadCommandBuffer();
u32* staticbufs = getThreadStaticBuffers();
cmdbuf[0] = IPC_MakeHeader(0x40F,0,0); // 0x040F0000
u32 staticbufBackup[2];
memcpy(staticbufBackup, staticbufs, 8);
staticbufs[0] = IPC_Desc_StaticBuffer(0x20, 0); // at most 32 bytes
staticbufs[1] = (u32)ssid;
Result ret = svcSendSyncRequest(acHandle);
memcpy(staticbufs, staticbufBackup, 8);
return R_SUCCEEDED(ret) ? (Result)cmdbuf[1] : ret;
}

View File

@ -1031,3 +1031,41 @@ Result AM_DeleteAllTwlTitles(void)
return (Result)cmdbuf[1];
}
Result AMAPP_GetDLCContentInfoCount(u32* count, FS_MediaType mediatype, u64 titleID)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x1001,3,0); // 0x100100C0
cmdbuf[1] = (u32)mediatype;
cmdbuf[2] = titleID & 0xffffffff;
cmdbuf[3] = (u32)(titleID >> 32);
if(R_FAILED(ret = svcSendSyncRequest(amHandle))) return ret;
*count = cmdbuf[2];
return (Result)cmdbuf[1];
}
Result AMAPP_ListDLCContentInfos(u32* contentInfoRead, FS_MediaType mediatype, u64 titleID, u32 contentInfoCount, u32 offset, AM_ContentInfo* contentInfos)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = (IPC_MakeHeader(0x1003,5,2)); // 0x10030142
cmdbuf[1] = contentInfoCount;
cmdbuf[2] = (u32)mediatype;
cmdbuf[3] = titleID & 0xffffffff;
cmdbuf[4] = (u32)(titleID >> 32);
cmdbuf[5] = offset;
cmdbuf[6] = IPC_Desc_Buffer(contentInfoCount * 0x18, IPC_BUFFER_W);
cmdbuf[7] = (u32)contentInfos;
if(R_FAILED(ret = svcSendSyncRequest(amHandle))) return ret;
*contentInfoRead = cmdbuf[2];
return (Result)cmdbuf[1];
}

View File

@ -12,6 +12,7 @@
#include <3ds/services/apt.h>
#include <3ds/services/gspgpu.h>
#include <3ds/services/ptmsysm.h> // for PtmWakeEvents
#include <3ds/allocator/mappable.h>
#include <3ds/ipc.h>
#include <3ds/env.h>
#include <3ds/thread.h>
@ -21,33 +22,53 @@
static int aptRefCount = 0;
static Handle aptLockHandle;
static Handle aptEvents[3];
static Handle aptEvents[2];
static LightEvent aptReceiveEvent;
static LightEvent aptSleepEvent;
static Thread aptEventHandlerThread;
static bool aptEventHandlerThreadQuit;
static aptHookCookie aptFirstHook;
static aptMessageCb aptMessageFunc;
static void* aptMessageFuncData;
enum
{
// Current applet state
FLAG_ACTIVE = BIT(0),
FLAG_ALLOWSLEEP = BIT(1),
FLAG_ORDERTOCLOSE = BIT(2),
FLAG_SHUTDOWN = BIT(3),
FLAG_POWERBUTTON = BIT(4),
FLAG_WKUPBYCANCEL = BIT(5),
FLAG_WANTSTOSLEEP = BIT(6),
FLAG_SLEEPING = BIT(7),
FLAG_EXITED = BIT(31),
FLAG_SLEEPING = BIT(1),
// Sleep handling flags
FLAG_ALLOWSLEEP = BIT(2),
FLAG_SHOULDSLEEP = BIT(3),
// Home button flags
FLAG_ALLOWHOME = BIT(4),
FLAG_SHOULDHOME = BIT(5),
FLAG_HOMEREJECTED = BIT(6),
// Power button flags
FLAG_POWERBUTTON = BIT(7),
FLAG_SHUTDOWN = BIT(8),
// Close handling flags
FLAG_ORDERTOCLOSE = BIT(9),
FLAG_CANCELLED = BIT(10),
// Miscellaneous
FLAG_DSPWAKEUP = BIT(29),
FLAG_CHAINLOAD = BIT(30),
FLAG_SPURIOUS = BIT(31),
};
static u8 aptHomeButtonState;
static u8 aptRecentHomeButtonState;
static u32 aptFlags = FLAG_ALLOWSLEEP;
static bool aptHomeAllowed = true;
static u32 aptFlags;
static u32 aptParameters[0x1000/4];
static u64 aptChainloadTid;
static u8 aptChainloadDeliverArg[0x300];
static u32 aptChainloadDeliverArgSize = sizeof(aptChainloadDeliverArg);
static u8 aptChainloadHmac[0x20];
static u8 aptChainloadMediatype;
static u8 aptChainloadFlags;
typedef enum
{
@ -63,10 +84,17 @@ typedef enum
static void aptEventHandler(void *arg);
static APT_Command aptWaitForWakeUp(APT_Transition transition);
// The following function can be overriden in order to log APT signals and notifications for debugging purposes
__attribute__((weak)) void _aptDebug(int a, int b)
{
}
// The following function can be overridden in order to log APT signals and notifications for debugging purposes
#ifdef LIBCTRU_APT_DEBUG
__attribute__((weak)) void _aptDebug(int a, int b) { }
#else
#define _aptDebug(a,b) ((void)0)
#endif
// APT<->DSP interaction functions (stubbed when not using DSP)
__attribute__((weak)) bool aptDspSleep(void) { return false; }
__attribute__((weak)) void aptDspWakeup(void) { }
__attribute__((weak)) void aptDspCancel(void) { }
static void aptCallHook(APT_HookType hookType)
{
@ -132,7 +160,7 @@ Result aptSendCommand(u32* aptcmdbuf)
res = svcSendSyncRequest(aptuHandle);
if (R_SUCCEEDED(res))
{
memcpy(aptcmdbuf, cmdbuf, 4*16);//4*countPrmWords(cmdbuf[0])); // Workaround for Citra failing to emulate response cmdheaders
memcpy(aptcmdbuf, cmdbuf, 4*countPrmWords(cmdbuf[0]));
res = aptcmdbuf[1];
}
svcCloseHandle(aptuHandle);
@ -141,37 +169,16 @@ Result aptSendCommand(u32* aptcmdbuf)
return res;
}
static void aptClearParamQueue(void)
static void aptInitCaptureInfo(aptCaptureBufInfo* capinfo, const GSPGPU_CaptureInfo* gspcapinfo)
{
// Check for parameters?
for (;;)
{
APT_Command cmd;
Result res = APT_GlanceParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), NULL, &cmd, NULL, NULL);
if (R_FAILED(res) || cmd==APTCMD_NONE) break;
_aptDebug(2, cmd);
svcClearEvent(aptEvents[2]);
APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL);
}
}
static void aptInitCaptureInfo(aptCaptureBufInfo* capinfo)
{
GSPGPU_CaptureInfo gspcapinfo;
memset(&gspcapinfo, 0, sizeof(gspcapinfo));
// Get display-capture info from GSP.
GSPGPU_ImportDisplayCaptureInfo(&gspcapinfo);
// Fill in display-capture info for NS.
capinfo->is3D = (gspcapinfo.screencapture[0].format & 0x20) != 0;
capinfo->is3D = (gspcapinfo->screencapture[0].format & 0x20) != 0;
capinfo->top.format = gspcapinfo.screencapture[0].format & 0x7;
capinfo->bottom.format = gspcapinfo.screencapture[1].format & 0x7;
capinfo->top.format = gspcapinfo->screencapture[0].format & 0x7;
capinfo->bottom.format = gspcapinfo->screencapture[1].format & 0x7;
u32 __get_bytes_per_pixel(u32 format);
u32 main_pixsz = __get_bytes_per_pixel(capinfo->top.format);
u32 sub_pixsz = __get_bytes_per_pixel(capinfo->bottom.format);
u32 main_pixsz = gspGetBytesPerPixel((GSPGPU_FramebufferFormat)capinfo->top.format);
u32 sub_pixsz = gspGetBytesPerPixel((GSPGPU_FramebufferFormat)capinfo->bottom.format);
capinfo->bottom.leftOffset = 0;
capinfo->bottom.rightOffset = 0;
@ -190,8 +197,6 @@ Result aptInit(void)
if (AtomicPostIncrement(&aptRefCount)) return 0;
aptHomeAllowed = osGetSystemCoreVersion() == 2;
// Retrieve APT lock
ret = APT_GetLockHandle(0x0, &aptLockHandle);
if (R_FAILED(ret)) goto _fail;
@ -199,53 +204,45 @@ Result aptInit(void)
// Initialize APT
APT_AppletAttr attr = aptMakeAppletAttr(APTPOS_APP, false, false);
ret = APT_Initialize(envGetAptAppId(), attr, &aptEvents[1], &aptEvents[2]);
ret = APT_Initialize(envGetAptAppId(), attr, &aptEvents[0], &aptEvents[1]);
if (R_FAILED(ret)) goto _fail2;
// Initialize light events
LightEvent_Init(&aptReceiveEvent, RESET_STICKY);
LightEvent_Init(&aptSleepEvent, RESET_ONESHOT);
// Create APT event handler thread
aptEventHandlerThreadQuit = false;
aptEventHandlerThread = threadCreate(aptEventHandler, 0x0, APT_HANDLER_STACKSIZE, 0x31, -2, true);
if (!aptEventHandlerThread) goto _fail3;
// By default allow sleep mode and home button presses
aptFlags = FLAG_ALLOWSLEEP;
if (osGetSystemCoreVersion() == 2) // ... except in safe mode, which doesn't have home menu running
aptFlags |= FLAG_ALLOWHOME;
// Enable APT
ret = APT_Enable(attr);
if (R_FAILED(ret)) goto _fail3;
// Create APT close event
ret = svcCreateEvent(&aptEvents[0], RESET_STICKY);
if (R_FAILED(ret)) goto _fail3;
// Initialize APT sleep event
LightEvent_Init(&aptSleepEvent, RESET_ONESHOT);
// Create APT event handler thread
aptEventHandlerThread = threadCreate(aptEventHandler, 0x0, APT_HANDLER_STACKSIZE, 0x31, -2, true);
if (!aptEventHandlerThread) goto _fail4;
// Get information about ourselves
APT_GetAppletInfo(envGetAptAppId(), &aptChainloadTid, &aptChainloadMediatype, NULL, NULL, NULL);
// Special handling for aptReinit (aka hax)
APT_Transition transition = TR_ENABLE;
if (aptIsReinit())
{
transition = TR_JUMPTOMENU;
// Clear out any pending parameters
bool success = false;
do
ret = APT_CancelParameter(APPID_NONE, envGetAptAppId(), &success);
while (success);
// APT thinks the application is suspended, so we need to tell it to unsuspend us.
APT_PrepareToJumpToApplication(false);
APT_JumpToApplication(NULL, 0, 0);
}
// If the homebrew environment requires it, chainload-to-self by default
if (aptIsChainload())
aptSetChainloaderToSelf();
// Wait for wakeup
aptWaitForWakeUp(transition);
aptWaitForWakeUp(TR_ENABLE);
// Special handling for aptReinit (aka hax 2.x):
// In certain cases when running under hax 2.x, we may receive a spurious
// second wakeup command. Therefore we must silently drop it in order to
// avoid keeping stale commands in APT's internal buffer.
if (aptIsReinit())
aptFlags |= FLAG_SPURIOUS;
return 0;
_fail4:
svcCloseHandle(aptEvents[0]);
_fail3:
svcCloseHandle(aptEvents[0]);
svcCloseHandle(aptEvents[1]);
svcCloseHandle(aptEvents[2]);
_fail2:
svcCloseHandle(aptLockHandle);
_fail:
@ -253,6 +250,16 @@ _fail:
return ret;
}
bool aptIsActive(void)
{
return (aptFlags & FLAG_ACTIVE) != 0;
}
bool aptShouldClose(void)
{
return (aptFlags & (FLAG_ORDERTOCLOSE|FLAG_CANCELLED)) != 0;
}
bool aptIsSleepAllowed(void)
{
return (aptFlags & FLAG_ALLOWSLEEP) != 0;
@ -275,29 +282,90 @@ void aptSetSleepAllowed(bool allowed)
bool aptIsHomeAllowed(void)
{
return aptHomeAllowed;
return (aptFlags & FLAG_ALLOWHOME) != 0;
}
void aptSetHomeAllowed(bool allowed)
{
aptHomeAllowed = allowed;
if (allowed)
aptFlags |= FLAG_ALLOWHOME;
else
aptFlags &= ~FLAG_ALLOWHOME;
}
bool aptIsHomePressed(void)
bool aptShouldJumpToHome(void)
{
return aptRecentHomeButtonState;
return aptHomeButtonState || (aptFlags & (FLAG_SHOULDHOME|FLAG_POWERBUTTON)) != 0;
}
bool aptCheckHomePressRejected(void)
{
if (aptFlags & FLAG_HOMEREJECTED)
{
aptFlags &= ~FLAG_HOMEREJECTED;
return true;
}
return false;
}
static void aptClearJumpToHome(void)
{
aptHomeButtonState = 0;
APT_UnlockTransition(0x01);
APT_SleepIfShellClosed();
}
void aptClearChainloader(void)
{
aptFlags &= ~FLAG_CHAINLOAD;
aptChainloadDeliverArgSize = sizeof(aptChainloadDeliverArg);
memset(aptChainloadDeliverArg, 0, sizeof(aptChainloadDeliverArg));
memset(aptChainloadHmac, 0, sizeof(aptChainloadHmac));
}
void aptSetChainloader(u64 programID, u8 mediatype)
{
aptFlags |= FLAG_CHAINLOAD;
aptChainloadFlags = 0;
aptChainloadTid = programID;
aptChainloadMediatype = mediatype;
}
void aptSetChainloaderToCaller(void)
{
aptFlags |= FLAG_CHAINLOAD;
aptChainloadFlags = 1;
aptChainloadTid = 0;
aptChainloadMediatype = 0;
}
void aptSetChainloaderToSelf(void)
{
aptFlags |= FLAG_CHAINLOAD;
aptChainloadFlags = 2;
aptChainloadTid = 0;
aptChainloadMediatype = 0;
}
void aptSetChainloaderArgs(const void *deliverArg, size_t deliverArgSize, const void *hmac)
{
if (deliverArgSize >= sizeof(aptChainloadDeliverArg))
deliverArgSize = sizeof(aptChainloadDeliverArg);
aptChainloadDeliverArgSize = deliverArgSize;
memcpy(aptChainloadDeliverArg, deliverArg, deliverArgSize);
if (hmac != NULL)
memcpy(aptChainloadHmac, hmac, sizeof(aptChainloadHmac));
else
memset(aptChainloadHmac, 0, sizeof(aptChainloadHmac));
}
extern void (*__system_retAddr)(void);
static void aptExitProcess(void)
{
APT_CloseApplication(NULL, 0, 0);
svcExitProcess();
}
void aptExit(void)
@ -305,91 +373,233 @@ void aptExit(void)
if (AtomicDecrement(&aptRefCount)) return;
bool closeAptLock = true;
bool doDirtyChainload = false;
if (!aptIsCrippled())
{
bool exited = (aptFlags & FLAG_EXITED) != 0;
if (exited || !aptIsReinit())
bool doClose;
if (aptShouldClose())
{
if (!exited && aptIsChainload())
// The system instructed us to close, so do just that
aptCallHook(APTHOOK_ONEXIT);
doClose = true;
}
else if (aptIsReinit())
{
// Check if Home Menu exists and has been launched
// The homebrew environment expects APT to be reinitializable, so unregister ourselves without closing
APT_Finalize(envGetAptAppId());
doClose = false;
}
else if (aptFlags & FLAG_CHAINLOAD)
{
// A chainload target is configured, so perform a jump to it
// Doing this requires help from HOME menu, so ensure that it is running
bool hmRegistered;
if (R_SUCCEEDED(APT_IsRegistered(APPID_HOMEMENU, &hmRegistered)) && hmRegistered)
if (R_SUCCEEDED(APT_IsRegistered(aptGetMenuAppID(), &hmRegistered)) && hmRegistered)
{
// Normal, sane chainload
u8 param[0x300] = {0};
u8 hmac[0x20] = {0};
APT_PrepareToDoApplicationJump(0, aptChainloadTid, aptChainloadMediatype);
APT_DoApplicationJump(param, sizeof(param), hmac);
APT_PrepareToDoApplicationJump(aptChainloadFlags, aptChainloadTid, aptChainloadMediatype);
APT_DoApplicationJump(aptChainloadDeliverArg, aptChainloadDeliverArgSize, aptChainloadHmac);
}
else
{
// Dirty workaround w/ custom notification
// XX: HOME menu doesn't exist, so we need to use a workaround provided by Luma3DS
APT_Finalize(envGetAptAppId());
srvPublishToSubscriber(0x3000, 0);
}
while (aptMainLoop())
svcSleepThread(25*1000*1000);
doDirtyChainload = true;
}
// After a chainload has been applied, we don't need to manually close
doClose = false;
__system_retAddr = NULL;
}
else
{
// None of the other situations apply, so close anyway by default
doClose = true;
}
// If needed, perform the APT application closing sequence
if (doClose)
{
APT_PrepareToCloseApplication(true);
extern void (*__system_retAddr)(void);
// APT_CloseApplication kills us if we aren't signed up for srv closing notifications, so
// defer APT_CloseApplication for as long as possible (TODO: actually use srv notif instead)
__system_retAddr = aptExitProcess;
closeAptLock = false;
srvInit(); // Keep srv initialized
} else
{
APT_Finalize(envGetAptAppId());
aptClearParamQueue();
}
aptEventHandlerThreadQuit = true;
svcSignalEvent(aptEvents[0]);
threadJoin(aptEventHandlerThread, U64_MAX);
int i;
for (i = 0; i < 3; i ++)
for (i = 0; i < 2; i ++)
svcCloseHandle(aptEvents[i]);
}
if (closeAptLock)
svcCloseHandle(aptLockHandle);
if (doDirtyChainload)
{
// Provided by Luma3DS
Handle notificationHandle = 0;
Result res = 0;
u32 notificationNumber = 0;
srvEnableNotification(&notificationHandle);
// Not needed, but official (sysmodule) code does this:
srvSubscribe(0x100);
// Make PM modify our run flags and ask us to terminate
srvPublishToSubscriber(0x3000, 0);
do
{
// Bail out after 3 seconds, we don't want to wait forever for this
res = svcWaitSynchronization(notificationHandle, 3 * 1000 * 1000LL);
res = res == 0 ? srvReceiveNotification(&notificationNumber) : res;
} while(res == 0 && notificationNumber != 0x100);
svcCloseHandle(notificationHandle);
}
}
void aptEventHandler(void *arg)
{
for (;;)
while (!aptEventHandlerThreadQuit)
{
s32 id = 0;
svcWaitSynchronizationN(&id, aptEvents, 2, 0, U64_MAX);
if (aptEventHandlerThreadQuit)
break;
// If the receive event is still signaled, sleep for a bit and retry
if (LightEvent_TryWait(&aptReceiveEvent))
{
_aptDebug(222, 0);
svcSleepThread(10000000); // 10ms
svcSignalEvent(aptEvents[id]);
continue;
}
// This is done by official sw, even though APT events are oneshot...
svcClearEvent(aptEvents[id]);
if (id != 1) break;
// Relay receive events to our light event
if (id == 1)
{
NS_APPID sender;
APT_Command cmd;
Result res = APT_GlanceParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), &sender, &cmd, NULL, NULL);
if (R_FAILED(res))
continue; // Official sw panics here - we instead swallow the (non-)event.
_aptDebug(2, cmd); _aptDebug(22, sender);
// NOTE: Official software handles the following parameter types here:
// - APTCMD_MESSAGE (cancelled afterwards) (we handle it in aptReceiveParameter instead)
// - APTCMD_REQUEST (cancelled afterwards) (only sent to and handled by libapplets?)
// - APTCMD_DSP_SLEEP (*NOT* cancelled afterwards)
// - APTCMD_DSP_WAKEUP (*NOT* cancelled afterwards)
// We will handle the following:
switch (cmd)
{
case APTCMD_DSP_SLEEP:
// Handle DSP sleep requests
aptDspSleep();
break;
case APTCMD_DSP_WAKEUP:
// Handle DSP wakeup requests
aptFlags &= ~FLAG_DSPWAKEUP;
aptDspWakeup();
break;
case APTCMD_WAKEUP_PAUSE:
// Handle spurious APTCMD_WAKEUP_PAUSE parameters
// (see aptInit for more details on the hax 2.x spurious wakeup problem)
if (aptFlags & FLAG_SPURIOUS)
{
APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL);
aptFlags &= ~FLAG_SPURIOUS;
break;
}
// Fallthrough otherwise
default:
// Others not accounted for -> pass it on to aptReceiveParameter
LightEvent_Signal(&aptReceiveEvent);
break;
}
continue;
}
APT_Signal signal;
Result res = APT_InquireNotification(envGetAptAppId(), &signal);
if (R_FAILED(res)) break;
if (R_FAILED(res))
continue;
_aptDebug(1, signal);
switch (signal)
{
case APTSIGNAL_HOMEBUTTON:
if (!aptHomeButtonState) aptHomeButtonState = 1;
break;
case APTSIGNAL_HOMEBUTTON2:
if (!aptHomeButtonState) aptHomeButtonState = 2;
if (!aptIsActive())
break;
else if (!aptIsHomeAllowed())
{
aptFlags |= FLAG_HOMEREJECTED;
aptClearJumpToHome();
}
else if (!aptHomeButtonState)
aptHomeButtonState = signal == APTSIGNAL_HOMEBUTTON ? 1 : 2;
break;
case APTSIGNAL_SLEEP_QUERY:
APT_ReplySleepQuery(envGetAptAppId(), aptIsSleepAllowed() ? APTREPLY_ACCEPT : APTREPLY_REJECT);
{
APT_QueryReply reply;
if (aptShouldClose())
// Reject sleep if we are expected to close
reply = APTREPLY_REJECT;
else if (aptIsActive())
// Accept sleep based on user setting if we are active
reply = aptIsSleepAllowed() ? APTREPLY_ACCEPT : APTREPLY_REJECT;
else
// Accept sleep if we are inactive regardless of user setting
reply = APTREPLY_ACCEPT;
_aptDebug(10, aptFlags);
_aptDebug(11, reply);
APT_ReplySleepQuery(envGetAptAppId(), reply);
break;
}
case APTSIGNAL_SLEEP_CANCEL:
// Do something maybe?
if (aptIsActive())
aptFlags &= ~FLAG_SHOULDSLEEP;
break;
case APTSIGNAL_SLEEP_ENTER:
aptFlags |= FLAG_WANTSTOSLEEP;
_aptDebug(10, aptFlags);
if (aptDspSleep())
aptFlags |= FLAG_DSPWAKEUP;
if (aptIsActive())
aptFlags |= FLAG_SHOULDSLEEP;
else
// Since we are not active, this must be handled here.
APT_ReplySleepNotificationComplete(envGetAptAppId());
break;
case APTSIGNAL_SLEEP_WAKEUP:
if (aptFlags & FLAG_DSPWAKEUP)
{
aptFlags &= ~FLAG_DSPWAKEUP;
aptDspWakeup();
}
if (!aptIsActive())
break;
if (aptFlags & FLAG_SLEEPING)
LightEvent_Signal(&aptSleepEvent);
else
aptFlags &= ~FLAG_WANTSTOSLEEP;
aptFlags &= ~FLAG_SHOULDSLEEP;
break;
case APTSIGNAL_SHUTDOWN:
aptFlags |= FLAG_ORDERTOCLOSE | FLAG_SHUTDOWN;
@ -401,7 +611,16 @@ void aptEventHandler(void *arg)
aptFlags &= ~FLAG_POWERBUTTON;
break;
case APTSIGNAL_TRY_SLEEP:
{
// Official software performs this APT_SleepSystem command here, although
// its purpose is unclear. For completeness' sake, we'll do it as well.
static const struct PtmWakeEvents s_sleepWakeEvents = {
.pdn_wake_events = 0,
.mcu_interupt_mask = BIT(6),
};
APT_SleepSystem(&s_sleepWakeEvents);
break;
}
case APTSIGNAL_ORDERTOCLOSE:
aptFlags |= FLAG_ORDERTOCLOSE;
break;
@ -417,8 +636,8 @@ static Result aptReceiveParameter(APT_Command* cmd, size_t* actualSize, Handle*
size_t temp_actualSize;
if (!actualSize) actualSize = &temp_actualSize;
svcWaitSynchronization(aptEvents[2], U64_MAX);
svcClearEvent(aptEvents[2]);
LightEvent_Wait(&aptReceiveEvent);
LightEvent_Clear(&aptReceiveEvent);
Result res = APT_ReceiveParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), &sender, cmd, actualSize, handle);
if (R_SUCCEEDED(res) && *cmd == APTCMD_MESSAGE && aptMessageFunc)
aptMessageFunc(aptMessageFuncData, sender, aptParameters, *actualSize);
@ -429,9 +648,9 @@ APT_Command aptWaitForWakeUp(APT_Transition transition)
{
APT_Command cmd;
APT_NotifyToWait(envGetAptAppId());
aptFlags &= ~FLAG_ACTIVE;
if (transition != TR_ENABLE)
APT_SleepIfShellClosed();
aptFlags &= ~FLAG_ACTIVE;
for (;;)
{
Result res = aptReceiveParameter(&cmd, NULL, NULL);
@ -452,8 +671,16 @@ APT_Command aptWaitForWakeUp(APT_Transition transition)
aptCallHook(APTHOOK_ONRESTORE);
}
if (cmd == APTCMD_WAKEUP_CANCEL)
aptFlags |= FLAG_WKUPBYCANCEL;
if (cmd == APTCMD_WAKEUP_CANCEL || cmd == APTCMD_WAKEUP_CANCELALL)
{
aptDspCancel();
if (cmd == APTCMD_WAKEUP_CANCEL) // for some reason, not for CANCELALL... is this a bug in official sw?
aptFlags |= FLAG_CANCELLED;
} else if (cmd != APTCMD_WAKEUP_LAUNCHAPP)
{
aptFlags &= ~FLAG_DSPWAKEUP;
aptDspWakeup();
}
if (cmd != APTCMD_WAKEUP_JUMPTOHOME)
{
@ -461,31 +688,61 @@ APT_Command aptWaitForWakeUp(APT_Transition transition)
APT_SleepIfShellClosed();
} else
{
bool dummy;
APT_TryLockTransition(0x01, &dummy);
aptFlags |= FLAG_SHOULDHOME;
aptHomeButtonState = 1;
APT_LockTransition(0x01, true);
}
if (transition == TR_JUMPTOMENU || transition == TR_LIBAPPLET || transition == TR_SYSAPPLET || transition == TR_APPJUMP)
{
if (cmd != APTCMD_WAKEUP_JUMPTOHOME)
{
aptHomeButtonState = 0;
APT_UnlockTransition(0x01);
APT_SleepIfShellClosed();
}
aptClearJumpToHome();
}
return cmd;
}
static void aptConvertScreenForCapture(void* dst, const void* src, u32 height, GSPGPU_FramebufferFormat format)
{
const u32 width = 240;
const u32 width_po2 = 1U << (32 - __builtin_clz(width-1)); // next_po2(240) = 256
const u32 bpp = gspGetBytesPerPixel(format);
const u32 tilesize = 8*8*bpp;
// Terrible conversion code that is also probably really slow
u8* out = (u8*)dst;
const u8* in = (u8*)src;
for (u32 tiley = 0; tiley < height; tiley += 8)
{
u32 tilex = 0;
for (tilex = 0; tilex < width; tilex += 8)
{
for (u32 y = 0; y < 8; y ++)
{
for (u32 x = 0; x < 8; x ++)
{
static const u8 morton_x[] = { 0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15 };
static const u8 morton_y[] = { 0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a };
unsigned inoff = bpp*(width*(tiley+y)+(tilex+x));
unsigned outoff = bpp*(morton_x[x] + morton_y[y]);
for (u32 c = 0; c < bpp; c ++)
out[outoff+c] = in[inoff+c];
}
}
out += tilesize;
}
for (; tilex < width_po2; tilex += 8)
out += tilesize;
}
}
static void aptScreenTransfer(NS_APPID appId, bool sysApplet)
{
aptCallHook(APTHOOK_ONSUSPEND);
GSPGPU_SaveVramSysArea();
aptCaptureBufInfo capinfo;
aptInitCaptureInfo(&capinfo);
// Retrieve display capture info from GSP
GSPGPU_CaptureInfo gspcapinfo = {0};
GSPGPU_ImportDisplayCaptureInfo(&gspcapinfo);
// Wait for the target applet to be registered
for (;;)
{
bool tmp;
@ -494,6 +751,11 @@ static void aptScreenTransfer(NS_APPID appId, bool sysApplet)
svcSleepThread(10000000);
}
// Calculate the layout/size of the capture memory block
aptCaptureBufInfo capinfo;
aptInitCaptureInfo(&capinfo, &gspcapinfo);
// Request the capture memory block to be allocated
for (;;)
{
Result res = APT_SendParameter(envGetAptAppId(), appId, sysApplet ? APTCMD_SYSAPPLET_REQUEST : APTCMD_REQUEST, &capinfo, sizeof(capinfo), 0);
@ -501,26 +763,67 @@ static void aptScreenTransfer(NS_APPID appId, bool sysApplet)
svcSleepThread(10000000);
}
// Receive the response from APT
Handle hCapMemBlk = 0;
for (;;)
{
APT_Command cmd;
Result res = aptReceiveParameter(&cmd, NULL, NULL);
Result res = aptReceiveParameter(&cmd, NULL, &hCapMemBlk);
if (R_SUCCEEDED(res) && cmd==APTCMD_RESPONSE)
break;
}
APT_SendCaptureBufferInfo(&capinfo);
GSPGPU_ReleaseRight();
// For library applets, we need to manually do the capture ourselves
// (this involves mapping the memory block and doing the conversion)
if (!sysApplet)
{
void* map = mappableAlloc(capinfo.size);
if (map)
{
Result res = svcMapMemoryBlock(hCapMemBlk, (u32)map, MEMPERM_READWRITE, MEMPERM_READWRITE);
if (R_SUCCEEDED(res))
{
aptConvertScreenForCapture( // Bottom screen
(u8*)map + capinfo.bottom.leftOffset,
gspcapinfo.screencapture[1].framebuf0_vaddr,
320, (GSPGPU_FramebufferFormat)capinfo.bottom.format);
aptConvertScreenForCapture( // Top screen (Left eye)
(u8*)map + capinfo.top.leftOffset,
gspcapinfo.screencapture[0].framebuf0_vaddr,
400, (GSPGPU_FramebufferFormat)capinfo.top.format);
if (capinfo.is3D)
aptConvertScreenForCapture( // Top screen (Right eye)
(u8*)map + capinfo.top.rightOffset,
gspcapinfo.screencapture[0].framebuf1_vaddr,
400, (GSPGPU_FramebufferFormat)capinfo.top.format);
svcUnmapMemoryBlock(hCapMemBlk, (u32)map);
}
mappableFree(map);
}
}
static void aptProcessJumpToMenu(void)
// Close the capture memory block handle
if (hCapMemBlk)
svcCloseHandle(hCapMemBlk);
// Send capture buffer information back to APT
APT_SendCaptureBufferInfo(&capinfo);
}
void aptJumpToHomeMenu(void)
{
bool sleep = aptIsSleepAllowed();
aptSetSleepAllowed(false);
aptClearParamQueue();
aptFlags &= ~(FLAG_SHOULDHOME|FLAG_SPURIOUS); // If we haven't received a spurious wakeup by now, we probably never will (see aptInit)
APT_PrepareToJumpToHomeMenu();
aptCallHook(APTHOOK_ONSUSPEND);
GSPGPU_SaveVramSysArea();
aptScreenTransfer(aptGetMenuAppID(), true);
aptDspSleep();
GSPGPU_ReleaseRight();
APT_JumpToHomeMenu(NULL, 0, 0);
aptFlags &= ~FLAG_ACTIVE;
@ -529,45 +832,27 @@ static void aptProcessJumpToMenu(void)
aptSetSleepAllowed(sleep);
}
bool aptMainLoop(void)
void aptHandleSleep(void)
{
if (aptIsCrippled()) return true;
if (aptFlags & FLAG_EXITED) return false;
if (!(aptFlags & FLAG_SHOULDSLEEP))
return;
if (aptRecentHomeButtonState) aptRecentHomeButtonState = 0;
if (aptFlags & FLAG_WANTSTOSLEEP)
{
aptFlags = (aptFlags &~ FLAG_WANTSTOSLEEP) | FLAG_SLEEPING;
aptFlags = (aptFlags &~ FLAG_SHOULDSLEEP) | FLAG_SLEEPING;
aptCallHook(APTHOOK_ONSLEEP);
APT_ReplySleepNotificationComplete(envGetAptAppId());
LightEvent_Wait(&aptSleepEvent);
aptFlags &= ~FLAG_SLEEPING;
if (aptFlags & FLAG_ACTIVE)
if (aptIsActive())
GSPGPU_SetLcdForceBlack(0);
aptCallHook(APTHOOK_ONWAKEUP);
}
else if ((aptFlags & FLAG_POWERBUTTON) || aptHomeButtonState)
{
if (aptHomeAllowed || (aptFlags & FLAG_POWERBUTTON))
aptProcessJumpToMenu();
else
{
aptRecentHomeButtonState = aptHomeButtonState;
aptHomeButtonState = 0;
APT_UnlockTransition(0x01);
}
}
if (aptFlags & (FLAG_ORDERTOCLOSE|FLAG_WKUPBYCANCEL))
bool aptMainLoop(void)
{
aptFlags |= FLAG_EXITED;
aptCallHook(APTHOOK_ONEXIT);
return false;
}
return true;
aptHandleSleep();
aptHandleJumpToHome();
return !aptShouldClose();
}
void aptHook(aptHookCookie* cookie, aptHookFn callback, void* param)
@ -600,16 +885,20 @@ void aptSetMessageCallback(aptMessageCb callback, void* user)
aptMessageFuncData = user;
}
bool aptLaunchLibraryApplet(NS_APPID appId, void* buf, size_t bufsize, Handle handle)
void aptLaunchLibraryApplet(NS_APPID appId, void* buf, size_t bufsize, Handle handle)
{
bool sleep = aptIsSleepAllowed();
aptSetSleepAllowed(false);
aptClearParamQueue();
aptFlags &= ~FLAG_SPURIOUS; // If we haven't received a spurious wakeup by now, we probably never will (see aptInit)
APT_PrepareToStartLibraryApplet(appId);
aptSetSleepAllowed(sleep);
aptCallHook(APTHOOK_ONSUSPEND);
GSPGPU_SaveVramSysArea();
aptScreenTransfer(appId, false);
GSPGPU_ReleaseRight();
aptSetSleepAllowed(false);
APT_StartLibraryApplet(appId, buf, bufsize, handle);
@ -618,8 +907,6 @@ bool aptLaunchLibraryApplet(NS_APPID appId, void* buf, size_t bufsize, Handle ha
aptWaitForWakeUp(TR_LIBAPPLET);
memcpy(buf, aptParameters, bufsize);
aptSetSleepAllowed(sleep);
return aptMainLoop();
}
Result APT_GetLockHandle(u16 flags, Handle* lockHandle)
@ -763,10 +1050,7 @@ Result APT_InquireNotification(u32 appID, APT_Signal* signalType)
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
{
_aptDebug(1, cmdbuf[2]);
*signalType=cmdbuf[2];
}
return ret;
}
@ -862,6 +1146,18 @@ Result APT_SleepIfShellClosed(void)
return APT_AppletUtility(4, &out, sizeof(out), &in, sizeof(in));
}
Result APT_LockTransition(u32 transition, bool flag)
{
const struct
{
u32 transition;
bool flag;
u8 padding[3];
} in = { transition, flag, {0} };
u8 out;
return APT_AppletUtility(5, &out, sizeof(out), &in, sizeof(in));
}
Result APT_TryLockTransition(u32 transition, bool* succeeded)
{
return APT_AppletUtility(6, &succeeded, sizeof(succeeded), &transition, sizeof(transition));
@ -923,7 +1219,6 @@ Result APT_ReceiveParameter(NS_APPID appID, void* buffer, size_t bufferSize, NS_
if (R_SUCCEEDED(ret))
{
_aptDebug(2, cmdbuf[3]);
if (sender) *sender =cmdbuf[2];
if (command) *command =cmdbuf[3];
if (actualSize) *actualSize=cmdbuf[4];
@ -1161,7 +1456,7 @@ Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr)
return ret;
}
Result APT_ReceiveDeliverArg(const void* param, size_t paramSize, const void* hmac, u64* sender, bool* received)
Result APT_ReceiveDeliverArg(void* param, size_t paramSize, void* hmac, u64* sender, bool* received)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x35,2,0); // 0x350080

View File

@ -0,0 +1,162 @@
#include <string.h>
#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/srv.h>
#include <3ds/synchronization.h>
#include <3ds/ipc.h>
#include <3ds/services/cdcchk.h>
static Handle cdcChkHandle;
static int cdcChkRefCount;
Result cdcChkInit(void)
{
if (AtomicPostIncrement(&cdcChkRefCount))
return 0;
Result res = srvGetServiceHandle(&cdcChkHandle, "cdc:CHK");
if (R_FAILED(res))
AtomicDecrement(&cdcChkRefCount);
return res;
}
void cdcChkExit(void)
{
if (AtomicDecrement(&cdcChkRefCount))
return;
svcCloseHandle(cdcChkHandle);
}
Handle *cdcChkGetSessionHandle(void)
{
return &cdcChkHandle;
}
Result CDCCHK_ReadRegisters1(u8 pageId, u8 initialRegAddr, void *outData, size_t size)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 *staticbufs = getThreadStaticBuffers();
u32 staticbufBackup[2];
memcpy(staticbufBackup, staticbufs, 8);
staticbufs[0] = IPC_Desc_StaticBuffer(size, 0);
staticbufs[1] = (u32)outData;
cmdbuf[0] = IPC_MakeHeader(1, 3, 0); // 0x100C0
cmdbuf[1] = pageId;
cmdbuf[2] = initialRegAddr;
cmdbuf[3] = size;
ret = svcSendSyncRequest(cdcChkHandle);
memcpy(staticbufs, staticbufBackup, 8);
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
}
Result CDCCHK_ReadRegisters2(u8 pageId, u8 initialRegAddr, void *outData, size_t size)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 *staticbufs = getThreadStaticBuffers();
u32 staticbufBackup[2];
memcpy(staticbufBackup, staticbufs, 8);
staticbufs[0] = IPC_Desc_StaticBuffer(size, 0);
staticbufs[1] = (u32)outData;
cmdbuf[0] = IPC_MakeHeader(2, 3, 0); // 0x200C0
cmdbuf[1] = pageId;
cmdbuf[2] = initialRegAddr;
cmdbuf[3] = size;
ret = svcSendSyncRequest(cdcChkHandle);
memcpy(staticbufs, staticbufBackup, 8);
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
}
Result CDCCHK_WriteRegisters1(u8 pageId, u8 initialRegAddr, const void *data, size_t size)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(3, 3, 2); // 0x300C2
cmdbuf[1] = pageId;
cmdbuf[2] = initialRegAddr;
cmdbuf[3] = size;
cmdbuf[4] = IPC_Desc_StaticBuffer(size, 0);
cmdbuf[5] = (u32)data;
ret = svcSendSyncRequest(cdcChkHandle);
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
}
Result CDCCHK_WriteRegisters2(u8 pageId, u8 initialRegAddr, const void *data, size_t size)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(4, 3, 2); // 0x400C2
cmdbuf[1] = pageId;
cmdbuf[2] = initialRegAddr;
cmdbuf[3] = size;
cmdbuf[4] = IPC_Desc_StaticBuffer(size, 0);
cmdbuf[5] = (u32)data;
ret = svcSendSyncRequest(cdcChkHandle);
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
}
Result CDCCHK_ReadNtrPmicRegister(u8 *outData, u8 regAddr)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(5, 1, 0); // 0x50040
cmdbuf[1] = regAddr;
ret = svcSendSyncRequest(cdcChkHandle);
if (R_SUCCEEDED(ret))
{
*outData = (u8)cmdbuf[2];
ret = cmdbuf[1];
}
return ret;
}
Result CDCCHK_WriteNtrPmicRegister(u8 regAddr, u8 data)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(6, 2, 0); // 0x60080
cmdbuf[1] = regAddr;
cmdbuf[2] = data;
ret = svcSendSyncRequest(cdcChkHandle);
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
}
Result CDCCHK_SetI2sVolume(CodecI2sLine i2sLine, s8 volume)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(7, 2, 0); // 0x70080
cmdbuf[1] = (u32)i2sLine;
cmdbuf[2] = volume;
ret = svcSendSyncRequest(cdcChkHandle);
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
}

View File

@ -149,7 +149,7 @@ Result CFGU_IsNFCSupported(bool* isSupported)
// See here for block IDs:
// http://3dbrew.org/wiki/Config_Savegame#Configuration_blocks
Result CFGU_GetConfigInfoBlk2(u32 size, u32 blkID, u8* outData)
Result CFGU_GetConfigInfoBlk2(u32 size, u32 blkID, void* outData)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
@ -165,7 +165,7 @@ Result CFGU_GetConfigInfoBlk2(u32 size, u32 blkID, u8* outData)
return (Result)cmdbuf[1];
}
Result CFG_GetConfigInfoBlk4(u32 size, u32 blkID, u8* outData)
Result CFG_GetConfigInfoBlk4(u32 size, u32 blkID, void* outData)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
@ -181,7 +181,7 @@ Result CFG_GetConfigInfoBlk4(u32 size, u32 blkID, u8* outData)
return (Result)cmdbuf[1];
}
Result CFG_GetConfigInfoBlk8(u32 size, u32 blkID, u8* outData)
Result CFG_GetConfigInfoBlk8(u32 size, u32 blkID, void* outData)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
@ -197,7 +197,7 @@ Result CFG_GetConfigInfoBlk8(u32 size, u32 blkID, u8* outData)
return (Result)cmdbuf[1];
}
Result CFG_SetConfigInfoBlk4(u32 size, u32 blkID, u8* inData)
Result CFG_SetConfigInfoBlk4(u32 size, u32 blkID, const void* inData)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
@ -213,7 +213,7 @@ Result CFG_SetConfigInfoBlk4(u32 size, u32 blkID, u8* inData)
return (Result)cmdbuf[1];
}
Result CFG_SetConfigInfoBlk8(u32 size, u32 blkID, u8* inData)
Result CFG_SetConfigInfoBlk8(u32 size, u32 blkID, const void* inData)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();

View File

@ -207,7 +207,7 @@ Result csndInit(void)
goto cleanup1;
}
ret = svcMapMemoryBlock(csndSharedMemBlock, (u32)csndSharedMem, 3, 0x10000000);
ret = svcMapMemoryBlock(csndSharedMemBlock, (u32)csndSharedMem, MEMPERM_READWRITE, MEMPERM_DONTCARE);
if (R_FAILED(ret)) goto cleanup2;
memset((void*)csndSharedMem, 0, csndSharedMemSize);

View File

@ -9,6 +9,20 @@
static Handle dspHandle;
static int dspRefCount;
static dspHookCookie dspFirstHook;
static bool dspComponentLoaded, dspSleeping;
static const void* dspSavedCompBin;
static u32 dspSavedCompSize;
static u16 dspSavedCompProgMask, dspSavedCompDataMask;
static void dspCallHook(DSP_HookType hookType)
{
dspHookCookie* c;
for (c = &dspFirstHook; c && c->callback; c = c->next)
c->callback(hookType);
}
Result dspInit(void)
{
Result ret = 0;
@ -16,18 +30,91 @@ Result dspInit(void)
if (AtomicPostIncrement(&dspRefCount)) return 0;
ret = srvGetServiceHandle(&dspHandle, "dsp::DSP");
if (R_SUCCEEDED(ret)) DSP_UnloadComponent();
else AtomicDecrement(&dspRefCount);
if (R_FAILED(ret))
{
AtomicDecrement(&dspRefCount);
return ret;
}
// Force unload component (if exists)
dspComponentLoaded = true;
DSP_UnloadComponent();
dspSleeping = false;
dspSavedCompBin = NULL;
dspSavedCompSize = 0;
dspSavedCompProgMask = 0;
dspSavedCompDataMask = 0;
return 0;
}
void dspExit(void)
{
if (AtomicDecrement(&dspRefCount)) return;
DSP_UnloadComponent();
svcCloseHandle(dspHandle);
}
bool dspIsComponentLoaded(void)
{
return dspComponentLoaded;
}
void dspHook(dspHookCookie* cookie, dspHookFn callback)
{
if (!callback) return;
dspHookCookie* hook = &dspFirstHook;
*cookie = *hook; // Structure copy.
hook->next = cookie;
hook->callback = callback;
}
void dspUnhook(dspHookCookie* cookie)
{
dspHookCookie* hook;
for (hook = &dspFirstHook; hook; hook = hook->next)
{
if (hook->next == cookie)
{
*hook = *cookie; // Structure copy.
break;
}
}
}
bool aptDspSleep(void)
{
if (!dspComponentLoaded || dspSleeping)
return false;
dspCallHook(DSPHOOK_ONSLEEP);
DSP_UnloadComponent();
dspSleeping = true;
return true;
}
void aptDspWakeup(void)
{
if (!dspSleeping)
return;
Result ret = DSP_LoadComponent(dspSavedCompBin, dspSavedCompSize, dspSavedCompProgMask, dspSavedCompDataMask, NULL);
if (R_FAILED(ret))
svcBreak(USERBREAK_PANIC); // Shouldn't happen.
dspCallHook(DSPHOOK_ONWAKEUP);
dspSleeping = false;
}
void aptDspCancel(void)
{
if (!dspSleeping)
return;
dspCallHook(DSPHOOK_ONCANCEL);
}
Result DSP_GetHeadphoneStatus(bool* is_inserted)
{
Result ret = 0;
@ -96,6 +183,17 @@ Result DSP_GetSemaphoreHandle(Handle* semaphore)
Result DSP_LoadComponent(const void* component, u32 size, u16 prog_mask, u16 data_mask, bool* is_loaded)
{
if (dspComponentLoaded)
{
if (is_loaded) *is_loaded = dspComponentLoaded;
return 0;
}
dspSavedCompBin = component;
dspSavedCompSize = size;
dspSavedCompProgMask = prog_mask;
dspSavedCompDataMask = data_mask;
Result ret = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x11,3,2);
@ -105,12 +203,18 @@ Result DSP_LoadComponent(const void* component, u32 size, u16 prog_mask, u16 dat
cmdbuf[4] = IPC_Desc_Buffer(size,IPC_BUFFER_R);
cmdbuf[5] = (u32) component;
if (R_FAILED(ret = svcSendSyncRequest(dspHandle))) return ret;
*is_loaded = cmdbuf[2] & 0xFF;
dspComponentLoaded = cmdbuf[2] & 0xFF;
if (is_loaded) *is_loaded = dspComponentLoaded;
return cmdbuf[1];
}
Result DSP_UnloadComponent(void)
{
if (!dspComponentLoaded)
return 0;
dspComponentLoaded = false;
Result ret = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x12,0,0);

View File

@ -566,7 +566,7 @@ Result FSUSER_GetNandCid(u8* out, u32 length)
return cmdbuf[1];
}
Result FSUSER_GetSdmcSpeedInfo(u32 *speedInfo)
Result FSUSER_GetSdmcSpeedInfo(FS_SdMmcSpeedInfo *speedInfo)
{
u32 *cmdbuf = getThreadCommandBuffer();
@ -575,12 +575,11 @@ Result FSUSER_GetSdmcSpeedInfo(u32 *speedInfo)
Result ret = 0;
if(R_FAILED(ret = svcSendSyncRequest(fsSession()))) return ret;
if(speedInfo) *speedInfo = cmdbuf[2];
if(speedInfo) memcpy(speedInfo, &cmdbuf[2], sizeof(FS_SdMmcSpeedInfo));
return cmdbuf[1];
}
Result FSUSER_GetNandSpeedInfo(u32 *speedInfo)
Result FSUSER_GetNandSpeedInfo(FS_SdMmcSpeedInfo *speedInfo)
{
u32 *cmdbuf = getThreadCommandBuffer();
@ -589,7 +588,7 @@ Result FSUSER_GetNandSpeedInfo(u32 *speedInfo)
Result ret = 0;
if(R_FAILED(ret = svcSendSyncRequest(fsSession()))) return ret;
if(speedInfo) *speedInfo = cmdbuf[2];
if(speedInfo) memcpy(speedInfo, &cmdbuf[2], sizeof(FS_SdMmcSpeedInfo));
return cmdbuf[1];
}

View File

@ -107,8 +107,8 @@ Result FSPXI_CreateFile(Handle serviceHandle, FSPXI_Archive archive, FS_Path pat
cmdbuf[6] = attributes;
cmdbuf[7] = (u32)fileSize;
cmdbuf[8] = (u32)(fileSize >> 32);
cmdbuf[8] = IPC_Desc_PXIBuffer(path.size, 0, true);
cmdbuf[9] = (u32) path.data;
cmdbuf[9] = IPC_Desc_PXIBuffer(path.size, 0, true);
cmdbuf[10] = (u32) path.data;
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
@ -229,8 +229,8 @@ Result FSPXI_WriteFile(Handle serviceHandle, FSPXI_File file, u32* bytesWritten,
cmdbuf[2] = (u32)(file >> 32);
cmdbuf[3] = (u32) offset;
cmdbuf[4] = (u32) (offset >> 32);
cmdbuf[5] = flags;
cmdbuf[6] = size;
cmdbuf[5] = size;
cmdbuf[6] = flags;
cmdbuf[7] = IPC_Desc_PXIBuffer(size, 0, true);
cmdbuf[8] = (u32) buffer;
@ -376,7 +376,7 @@ Result FSPXI_HasFile(Handle serviceHandle, FSPXI_Archive archive, bool* out, FS_
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if (out) *out = (bool)cmdbuf[2];
if (out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -396,7 +396,7 @@ Result FSPXI_HasDirectory(Handle serviceHandle, FSPXI_Archive archive, bool* out
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if (out) *out = (bool)cmdbuf[2];
if (out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -441,7 +441,7 @@ Result FSPXI_Unknown0x17(Handle serviceHandle, FSPXI_Archive archive, bool* out)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if (out) *out = (bool)cmdbuf[2];
if (out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -523,7 +523,7 @@ Result FSPXI_IsSdmcDetected(Handle serviceHandle, bool* out)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(out) *out = (bool)cmdbuf[2];
if(out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -537,7 +537,7 @@ Result FSPXI_IsSdmcWritable(Handle serviceHandle, bool* out)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(out) *out = (bool)cmdbuf[2];
if(out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -572,7 +572,7 @@ Result FSPXI_GetNandCid(Handle serviceHandle, void* out, u32 size)
return (Result) cmdbuf[1];
}
Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, u32* out)
Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, FS_SdMmcSpeedInfo* out)
{
Result ret = 0;
u32* cmdbuf = getThreadCommandBuffer();
@ -581,12 +581,12 @@ Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, u32* out)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(out) *out = cmdbuf[2];
if(out) memcpy(out, &cmdbuf[2], sizeof(FS_SdMmcSpeedInfo));
return (Result) cmdbuf[1];
}
Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, u32* out)
Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, FS_SdMmcSpeedInfo* out)
{
Result ret = 0;
u32* cmdbuf = getThreadCommandBuffer();
@ -595,7 +595,7 @@ Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, u32* out)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(out) *out = cmdbuf[2];
if(out) memcpy(out, &cmdbuf[2], sizeof(FS_SdMmcSpeedInfo));
return (Result) cmdbuf[1];
}
@ -663,7 +663,7 @@ Result FSPXI_CardSlotIsInserted(Handle serviceHandle, bool* inserted)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(inserted) *inserted = (bool)cmdbuf[2];
if(inserted) *inserted = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -677,7 +677,7 @@ Result FSPXI_CardSlotPowerOn(Handle serviceHandle, bool* status)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(status) *status = (bool)cmdbuf[2];
if(status) *status = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -691,7 +691,7 @@ Result FSPXI_CardSlotPowerOff(Handle serviceHandle, bool* status)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(status) *status = (bool)cmdbuf[2];
if(status) *status = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -705,7 +705,7 @@ Result FSPXI_CardSlotGetCardIFPowerStatus(Handle serviceHandle, bool* status)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(status) *status = (bool)cmdbuf[2];
if(status) *status = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}

View File

@ -5,46 +5,280 @@
#include <3ds/svc.h>
#include <3ds/srv.h>
#include <3ds/synchronization.h>
#include <3ds/allocator/mappable.h>
#include <3ds/services/gspgpu.h>
#include <3ds/ipc.h>
#include <3ds/thread.h>
#define GSP_EVENT_STACK_SIZE 0x1000
Handle gspGpuHandle;
static Handle gspGpuHandle;
static int gspRefCount;
static s32 gspLastEvent = -1;
static Handle gspSharedMemHandle;
static void* gspSharedMem;
static u8 gspThreadId;
static bool gspGpuRight;
static Handle gspEvent;
static Thread gspEventThread;
static volatile bool gspRunEvents;
static s32 gspLastEvent;
static LightEvent gspEvents[GSPGPU_EVENT_MAX];
static vu32 gspEventCounts[GSPGPU_EVENT_MAX];
static ThreadFunc gspEventCb[GSPGPU_EVENT_MAX];
static void* gspEventCbData[GSPGPU_EVENT_MAX];
static bool gspEventCbOneShot[GSPGPU_EVENT_MAX];
static volatile bool gspRunEvents;
static Thread gspEventThread;
static Handle gspEvent;
static vu8* gspEventData;
static void gspEventThreadMain(void *arg);
Handle __sync_get_arbiter(void);
static inline void gspWriteGxReg(u32 offset, u32 data)
{
GSPGPU_WriteHWRegs(0x400000 + offset, &data, 4);
}
static inline void gspWriteGxRegMasked(u32 offset, u32 data, u32 mask)
{
GSPGPU_WriteHWRegsWithMask(0x400000 + offset, &data, 4, &mask, 4);
}
// Hardware initialization for first-time GSP users (matching official software).
static void gspHardwareInit(void)
{
// Some GPU-internal init registers
gspWriteGxReg(0x1000, 0);
gspWriteGxReg(0x1080, 0x12345678);
gspWriteGxReg(0x10C0, 0xFFFFFFF0);
gspWriteGxReg(0x10D0, 1);
gspWriteGxReg(0x1914, 1); // homebrew addition: make sure GPUREG_START_DRAW_FUNC0 starts off in configuration mode
// Top screen LCD configuration, see https://www.3dbrew.org/wiki/GPU/External_Registers#LCD_Source_Framebuffer_Setup
// Top screen sync registers:
gspWriteGxReg(0x0400, 0x1C2);
gspWriteGxReg(0x0404, 0xD1);
gspWriteGxReg(0x0408, 0x1C1);
gspWriteGxReg(0x040C, 0x1C1);
gspWriteGxReg(0x0410, 0);
gspWriteGxReg(0x0414, 0xCF);
gspWriteGxReg(0x0418, 0xD1);
gspWriteGxReg(0x041C, (0x1C5 << 16) | 0x1C1);
gspWriteGxReg(0x0420, 0x10000);
gspWriteGxReg(0x0424, 0x19D);
gspWriteGxReg(0x0428, 2);
gspWriteGxReg(0x042C, 0x192);
gspWriteGxReg(0x0430, 0x192);
gspWriteGxReg(0x0434, 0x192);
gspWriteGxReg(0x0438, 1);
gspWriteGxReg(0x043C, 2);
gspWriteGxReg(0x0440, (0x196 << 16) | 0x192);
gspWriteGxReg(0x0444, 0);
gspWriteGxReg(0x0448, 0);
// Top screen fb geometry
gspWriteGxReg(0x045C, (400 << 16) | 240); // dimensions
gspWriteGxReg(0x0460, (0x1C1 << 16) | 0xD1);
gspWriteGxReg(0x0464, (0x192 << 16) | 2);
// Top screen framebuffer format (initial)
gspWriteGxReg(0x0470, 0x80340);
// Top screen unknown reg @ 0x9C
gspWriteGxReg(0x049C, 0);
// Bottom screen LCD configuration
// Bottom screen sync registers:
gspWriteGxReg(0x0500, 0x1C2);
gspWriteGxReg(0x0504, 0xD1);
gspWriteGxReg(0x0508, 0x1C1);
gspWriteGxReg(0x050C, 0x1C1);
gspWriteGxReg(0x0510, 0xCD);
gspWriteGxReg(0x0514, 0xCF);
gspWriteGxReg(0x0518, 0xD1);
gspWriteGxReg(0x051C, (0x1C5 << 16) | 0x1C1);
gspWriteGxReg(0x0520, 0x10000);
gspWriteGxReg(0x0524, 0x19D);
gspWriteGxReg(0x0528, 0x52);
gspWriteGxReg(0x052C, 0x192);
gspWriteGxReg(0x0530, 0x192);
gspWriteGxReg(0x0534, 0x4F);
gspWriteGxReg(0x0538, 0x50);
gspWriteGxReg(0x053C, 0x52);
gspWriteGxReg(0x0540, (0x198 << 16) | 0x194);
gspWriteGxReg(0x0544, 0);
gspWriteGxReg(0x0548, 0x11);
// Bottom screen fb geometry
gspWriteGxReg(0x055C, (320 << 16) | 240); // dimensions
gspWriteGxReg(0x0560, (0x1C1 << 16) | 0xD1);
gspWriteGxReg(0x0564, (0x192 << 16) | 0x52);
// Bottom screen framebuffer format (initial)
gspWriteGxReg(0x0570, 0x80300);
// Bottom screen unknown reg @ 0x9C
gspWriteGxReg(0x059C, 0);
// Initial, blank framebuffer (top left A/B, bottom A/B, top right A/B)
gspWriteGxReg(0x0468, 0x18300000);
gspWriteGxReg(0x046C, 0x18300000);
gspWriteGxReg(0x0568, 0x18300000);
gspWriteGxReg(0x056C, 0x18300000);
gspWriteGxReg(0x0494, 0x18300000);
gspWriteGxReg(0x0498, 0x18300000);
// Framebuffer select: A
gspWriteGxReg(0x0478, 1);
gspWriteGxReg(0x0578, 1);
// Clear DMA transfer (PPF) "transfer finished" bit
gspWriteGxRegMasked(0x0C18, 0, 0xFF00);
// GX_GPU_CLK |= 0x70000 (value is 0x100 when gsp starts, enough to at least display framebuffers & have memory fill work)
// This enables the clock to some GPU components
gspWriteGxReg(0x0004, 0x70100);
// Clear Memory Fill (PSC0 and PSC1) "busy" and "finished" bits
gspWriteGxRegMasked(0x001C, 0, 0xFF);
gspWriteGxRegMasked(0x002C, 0, 0xFF);
// More init registers
gspWriteGxReg(0x0050, 0x22221200);
gspWriteGxRegMasked(0x0054, 0xFF2, 0xFFFF);
// Enable some LCD clocks (?) (unsure)
gspWriteGxReg(0x0474, 0x10501);
gspWriteGxReg(0x0574, 0x10501);
}
Result gspInit(void)
{
Result res=0;
Result ret=0;
if (AtomicPostIncrement(&gspRefCount)) return 0;
res = srvGetServiceHandle(&gspGpuHandle, "gsp::Gpu");
if (R_FAILED(res)) AtomicDecrement(&gspRefCount);
return res;
// Initialize events
for (int i = 0; i < GSPGPU_EVENT_MAX; i ++)
LightEvent_Init(&gspEvents[i], RESET_STICKY);
// Retrieve a GSP service session handle
ret = srvGetServiceHandle(&gspGpuHandle, "gsp::Gpu");
if (R_FAILED(ret)) goto _fail0;
// Acquire GPU rights
ret = GSPGPU_AcquireRight(0);
if (R_FAILED(ret)) goto _fail1;
// Register ourselves as a user of graphics hardware
svcCreateEvent(&gspEvent, RESET_ONESHOT);
ret = GSPGPU_RegisterInterruptRelayQueue(gspEvent, 0x1, &gspSharedMemHandle, &gspThreadId);
if (R_FAILED(ret))
goto _fail2;
// Initialize the hardware if we are the first process to register
if (ret == 0x2A07)
gspHardwareInit();
// Map GSP shared memory
gspSharedMem = mappableAlloc(0x1000);
svcMapMemoryBlock(gspSharedMemHandle, (u32)gspSharedMem, MEMPERM_READWRITE, MEMPERM_DONTCARE);
// Initialize interrupt queue header
s32* sharedGspCmdBuf = (s32*)((u8*)gspSharedMem + 0x800 + gspThreadId*0x200);
do {
__ldrex(sharedGspCmdBuf);
} while (__strex(sharedGspCmdBuf, 0));
// Start event handling thread
gspRunEvents = true;
gspLastEvent = -1;
gspEventThread = threadCreate(gspEventThreadMain, 0x0, GSP_EVENT_STACK_SIZE, 0x1A, -2, true);
return 0;
_fail2:
GSPGPU_ReleaseRight();
_fail1:
svcCloseHandle(gspGpuHandle);
_fail0:
AtomicDecrement(&gspRefCount);
return ret;
}
void gspExit(void)
{
if (AtomicDecrement(&gspRefCount)) return;
// Stop event handling thread
gspRunEvents = false;
svcSignalEvent(gspEvent);
threadJoin(gspEventThread, U64_MAX);
// Unmap and close GSP shared memory
svcUnmapMemoryBlock(gspSharedMemHandle, (u32)gspSharedMem);
svcCloseHandle(gspSharedMemHandle);
mappableFree(gspSharedMem);
// Unregister ourselves
GSPGPU_UnregisterInterruptRelayQueue();
// Release GPU rights and close the service handle
GSPGPU_ReleaseRight();
svcCloseHandle(gspGpuHandle);
}
Handle *gspGetSessionHandle(void)
{
return &gspGpuHandle;
}
bool gspHasGpuRight(void)
{
return gspGpuRight;
}
bool gspPresentBuffer(unsigned screen, unsigned swap, const void* fb_a, const void* fb_b, u32 stride, u32 mode)
{
GSPGPU_FramebufferInfo info;
info.active_framebuf = swap;
info.framebuf0_vaddr = (u32*)fb_a;
info.framebuf1_vaddr = (u32*)fb_b;
info.framebuf_widthbytesize = stride;
info.format = mode;
info.framebuf_dispselect = swap;
info.unk = 0;
s32* fbInfoHeader = (s32*)((u8*)gspSharedMem + 0x200 + gspThreadId*0x80 + screen*0x40);
GSPGPU_FramebufferInfo* fbInfos = (GSPGPU_FramebufferInfo*)&fbInfoHeader[1];
unsigned pos = 1 - (*fbInfoHeader & 0xff);
fbInfos[pos] = info;
__dsb();
union
{
s32 header;
struct { u8 swap, update; };
} u;
bool ret;
do
{
u.header = __ldrex(fbInfoHeader);
ret = u.update != 0;
u.swap = pos;
u.update = 1;
} while (__strex(fbInfoHeader, u.header));
return ret;
}
bool gspIsPresentPending(unsigned screen)
{
s32* fbInfoHeader = (s32*)((u8*)gspSharedMem + 0x200 + gspThreadId*0x80 + screen*0x40);
return (*fbInfoHeader & 0xff00) != 0;
}
void gspSetEventCallback(GSPGPU_Event id, ThreadFunc cb, void* data, bool oneShot)
{
if(id>= GSPGPU_EVENT_MAX)return;
@ -54,29 +288,6 @@ void gspSetEventCallback(GSPGPU_Event id, ThreadFunc cb, void* data, bool oneSho
gspEventCbOneShot[id] = oneShot;
}
Result gspInitEventHandler(Handle _gspEvent, vu8* _gspSharedMem, u8 gspThreadId)
{
// Initialize events
int i;
for (i = 0; i < GSPGPU_EVENT_MAX; i ++)
LightEvent_Init(&gspEvents[i], RESET_STICKY);
// Start event thread
gspEvent = _gspEvent;
gspEventData = _gspSharedMem + gspThreadId*0x40;
gspRunEvents = true;
gspEventThread = threadCreate(gspEventThreadMain, 0x0, GSP_EVENT_STACK_SIZE, 0x1A, -2, true);
return 0;
}
void gspExitEventHandler(void)
{
// Stop event thread
gspRunEvents = false;
svcSignalEvent(gspEvent);
threadJoin(gspEventThread, U64_MAX);
}
void gspWaitForEvent(GSPGPU_Event id, bool nextEvent)
{
if(id>= GSPGPU_EVENT_MAX)return;
@ -103,15 +314,16 @@ GSPGPU_Event gspWaitForAnyEvent(void)
}
} while (__strex(&gspLastEvent, -1));
if (x < 0)
svcArbitrateAddress(__sync_get_arbiter(), (u32)&gspLastEvent, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0);
syncArbitrateAddress(&gspLastEvent, ARBITRATION_WAIT_IF_LESS_THAN, 0);
} while (x < 0);
return (GSPGPU_Event)x;
}
static int popInterrupt()
static int popInterrupt(void)
{
int curEvt;
bool strexFailed;
u8* gspEventData = (u8*)gspSharedMem + gspThreadId*0x40;
do {
union {
struct {
@ -156,6 +368,9 @@ void gspEventThreadMain(void *arg)
svcWaitSynchronization(gspEvent, U64_MAX);
svcClearEvent(gspEvent);
if (!gspRunEvents)
break;
while (true)
{
int curEvt = popInterrupt();
@ -177,8 +392,7 @@ void gspEventThreadMain(void *arg)
do
__ldrex(&gspLastEvent);
while (__strex(&gspLastEvent, curEvt));
svcArbitrateAddress(__sync_get_arbiter(), (u32)&gspLastEvent, ARBITRATION_SIGNAL, 1, 0);
gspEventCounts[curEvt]++;
syncArbitrateAddress(&gspLastEvent, ARBITRATION_SIGNAL, 1);
}
}
}
@ -187,10 +401,9 @@ void gspEventThreadMain(void *arg)
//essentially : get commandIndex and totalCommands, calculate offset of new command, copy command and update totalCommands
//use LDREX/STREX because this data may also be accessed by the GSP module and we don't want to break stuff
//(mostly, we could overwrite the buffer header with wrong data and make the GSP module reexecute old commands)
Result gspSubmitGxCommand(u32* sharedGspCmdBuf, u32 gxCommand[0x8])
Result gspSubmitGxCommand(const u32 gxCommand[0x8])
{
if(!sharedGspCmdBuf || !gxCommand)return -1;
u32* sharedGspCmdBuf = (u32*)((u8*)gspSharedMem + 0x800 + gspThreadId*0x200);
u32 cmdBufHeader = __ldrex((s32*)sharedGspCmdBuf);
u8 commandIndex=cmdBufHeader&0xFF;
@ -219,7 +432,7 @@ Result gspSubmitGxCommand(u32* sharedGspCmdBuf, u32 gxCommand[0x8])
return 0;
}
Result GSPGPU_WriteHWRegs(u32 regAddr, u32* data, u8 size)
Result GSPGPU_WriteHWRegs(u32 regAddr, const u32* data, u8 size)
{
if(size>0x80 || !data)return -1;
@ -236,7 +449,7 @@ Result GSPGPU_WriteHWRegs(u32 regAddr, u32* data, u8 size)
return cmdbuf[1];
}
Result GSPGPU_WriteHWRegsWithMask(u32 regAddr, u32* data, u8 datasize, u32* maskdata, u8 masksize)
Result GSPGPU_WriteHWRegsWithMask(u32 regAddr, const u32* data, u8 datasize, const u32* maskdata, u8 masksize)
{
if(datasize>0x80 || !data)return -1;
@ -272,7 +485,7 @@ Result GSPGPU_ReadHWRegs(u32 regAddr, u32* data, u8 size)
return cmdbuf[1];
}
Result GSPGPU_SetBufferSwap(u32 screenid, GSPGPU_FramebufferInfo*framebufinfo)
Result GSPGPU_SetBufferSwap(u32 screenid, const GSPGPU_FramebufferInfo*framebufinfo)
{
u32 *cmdbuf = getThreadCommandBuffer();
@ -370,6 +583,8 @@ Result GSPGPU_UnregisterInterruptRelayQueue(void)
Result GSPGPU_AcquireRight(u8 flags)
{
if(gspGpuRight) return 0;
u32* cmdbuf=getThreadCommandBuffer();
cmdbuf[0]=IPC_MakeHeader(0x16,1,2); // 0x160042
cmdbuf[1]=flags;
@ -378,17 +593,21 @@ Result GSPGPU_AcquireRight(u8 flags)
Result ret=0;
if(R_FAILED(ret=svcSendSyncRequest(gspGpuHandle)))return ret;
if(R_SUCCEEDED(cmdbuf[1])) gspGpuRight=true;
return cmdbuf[1];
}
Result GSPGPU_ReleaseRight(void)
{
if(!gspGpuRight) return 0;
u32* cmdbuf=getThreadCommandBuffer();
cmdbuf[0]=IPC_MakeHeader(0x17,0,0); // 0x170000
Result ret=0;
if(R_FAILED(ret=svcSendSyncRequest(gspGpuHandle)))return ret;
if(R_SUCCEEDED(cmdbuf[1])) gspGpuRight=false;
return cmdbuf[1];
}

View File

@ -26,6 +26,11 @@ void gspLcdExit(void)
svcCloseHandle(gspLcdHandle);
}
Handle *gspLcdGetSessionHandle(void)
{
return &gspLcdHandle;
}
Result GSPLCD_PowerOnAllBacklights(void)
{
u32 *cmdbuf = getThreadCommandBuffer();
@ -115,7 +120,7 @@ Result GSPLCD_GetBrightness(u32 screen, u32 *brightness)
*brightness = cmdbuf[2];
return cmdbuf[2];
return cmdbuf[1];
}
Result GSPLCD_SetBrightness(u32 screen, u32 brightness)

View File

@ -1,77 +0,0 @@
#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/srv.h>
#include <3ds/synchronization.h>
#include <3ds/env.h>
#include <3ds/ipc.h>
static Handle hbHandle;
static int hbRefCount;
Result hbInit(void)
{
Result res=0;
if (AtomicPostIncrement(&hbRefCount)) return 0;
Handle temp = envGetHandle("hb:HB");
res = temp ? svcDuplicateHandle(&hbHandle, temp) : MAKERESULT(RL_STATUS,RS_NOTFOUND,RM_APPLICATION,RD_NOT_FOUND);
if (R_FAILED(res)) AtomicDecrement(&hbRefCount);
return res;
}
void hbExit(void)
{
if (AtomicDecrement(&hbRefCount)) return;
svcCloseHandle(hbHandle);
}
Result HB_FlushInvalidateCache(void)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x1,1,2); // 0x10042
cmdbuf[1] = 0x00000000;
cmdbuf[2] = IPC_Desc_SharedHandles(1);
cmdbuf[3] = CUR_PROCESS_HANDLE;
if(R_FAILED(ret = svcSendSyncRequest(hbHandle))) return ret;
return (Result)cmdbuf[1];
}
Result HB_GetBootloaderAddresses(void** load3dsx, void** setArgv)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x6,0,0); // 0x60000
if(R_FAILED(ret = svcSendSyncRequest(hbHandle))) return ret;
if(load3dsx)*load3dsx=(void*)cmdbuf[2];
if(setArgv)*setArgv=(void*)cmdbuf[3];
return (Result)cmdbuf[1];
}
Result HB_ReprotectMemory(u32* addr, u32 pages, u32 mode, u32* reprotectedPages)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x9,3,0); // 0x900C0
cmdbuf[1] = (u32)addr;
cmdbuf[2] = pages;
cmdbuf[3] = mode;
if(R_FAILED(ret = svcSendSyncRequest(hbHandle))) return ret;
if(reprotectedPages)
{
if(R_SUCCEEDED(ret))*reprotectedPages=(u32)cmdbuf[2];
else *reprotectedPages=0;
}
return (Result)cmdbuf[1];
}

View File

@ -237,3 +237,33 @@ Result IRU_GetIRLEDRecvState(u32 *out)
return ret;
}
Result IRU_GetSendFinishedEvent(Handle *out)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0xD,0,0); // 0xD0000
if(R_FAILED(ret = svcSendSyncRequest(iruHandle)))return ret;
ret = (Result)cmdbuf[1];
*out = (Handle)cmdbuf[3];
return ret;
}
Result IRU_GetRecvFinishedEvent(Handle *out)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0xE,0,0); // 0xE0000
if(R_FAILED(ret = svcSendSyncRequest(iruHandle)))return ret;
ret = (Result)cmdbuf[1];
*out = (Handle)cmdbuf[3];
return ret;
}

View File

@ -46,7 +46,7 @@ Result irrstInit(void)
goto cleanup1;
}
if(R_FAILED(ret = svcMapMemoryBlock(irrstMemHandle, (u32)irrstSharedMem, MEMPERM_READ, 0x10000000))) goto cleanup2;
if(R_FAILED(ret = svcMapMemoryBlock(irrstMemHandle, (u32)irrstSharedMem, MEMPERM_READ, MEMPERM_DONTCARE))) goto cleanup2;
return 0;

View File

@ -1,3 +1,4 @@
#include <string.h>
#include <3ds/types.h>
#include <3ds/svc.h>
#include <3ds/synchronization.h>
@ -23,6 +24,11 @@ void mcuHwcExit(void)
svcCloseHandle(mcuHwcHandle);
}
Handle* mcuHwcGetSessionHandle(void)
{
return &mcuHwcHandle;
}
Result MCUHWC_ReadRegister(u8 reg, void* data, u32 size)
{
Result ret = 0;
@ -109,6 +115,22 @@ Result MCUHWC_SetWifiLedState(bool state)
return (Result)cmdbuf[1];
}
Result MCUHWC_SetInfoLedPattern(const InfoLedPattern* pattern)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x0A,25,0); // 0xA0640
cmdbuf[1] = ((u32)pattern->blinkSpeed << 24) | ((u32)pattern->loopDelay << 16) | ((u32)pattern->smoothing << 8) | pattern->delay;
memcpy(&cmdbuf[2], pattern->redPattern, sizeof(pattern->redPattern));
memcpy(&cmdbuf[10], pattern->greenPattern, sizeof(pattern->greenPattern));
memcpy(&cmdbuf[18], pattern->bluePattern, sizeof(pattern->bluePattern));
if(R_FAILED(ret = svcSendSyncRequest(mcuHwcHandle))) return ret;
return (Result)cmdbuf[1];
}
Result MCUHWC_GetSoundSliderLevel(u8 *level)
{
Result ret = 0;

View File

@ -165,11 +165,12 @@ Result NDMU_GetCurrentState(ndmState *state)
return (Result)cmdbuf[1];
}
Result NDMU_QueryStatus(ndmDaemonStatus *status)
Result NDMU_QueryStatus(ndmDaemon daemon, ndmDaemonStatus *status)
{
u32* cmdbuf=getThreadCommandBuffer();
cmdbuf[0]=IPC_MakeHeader(0xD,1,0); // 0xD0000
cmdbuf[0]=IPC_MakeHeader(0xD,1,0); // 0xD0040
cmdbuf[1]=daemon;
Result ret=0;
if(R_FAILED(ret=svcSendSyncRequest(ndmuHandle)))return ret;

View File

@ -79,7 +79,7 @@ Result PMAPP_TerminateTitle(u64 titleId, s64 timeout)
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x3, 4, 0); // 0x40100
cmdbuf[0] = IPC_MakeHeader(0x4, 4, 0); // 0x40100
cmdbuf[1] = (u32)titleId;
cmdbuf[2] = (u32)(titleId >> 32);
cmdbuf[3] = (u32)timeout;

View File

@ -0,0 +1,43 @@
#include <string.h>
#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/srv.h>
#include <3ds/synchronization.h>
#include <3ds/services/ptmgets.h>
#include <3ds/ipc.h>
static Handle ptmGetsHandle;
static int ptmGetsRefCount;
Result ptmGetsInit(void)
{
if (AtomicPostIncrement(&ptmGetsRefCount)) return 0;
Result res = srvGetServiceHandle(&ptmGetsHandle, "ptm:gets");
if (R_FAILED(res)) AtomicDecrement(&ptmGetsRefCount);
return res;
}
void ptmGetsExit(void)
{
if (AtomicDecrement(&ptmGetsRefCount)) return;
svcCloseHandle(ptmGetsHandle);
}
Handle *ptmGetsGetSessionHandle(void)
{
return &ptmGetsHandle;
}
Result PTMGETS_GetSystemTime(s64 *outMsY2k)
{
Result ret;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x401,0,0); // 0x04010000
if(R_FAILED(ret = svcSendSyncRequest(ptmGetsHandle)))return ret;
memcpy(outMsY2k, &cmdbuf[2], 8);
return (Result)cmdbuf[1];
}

View File

@ -0,0 +1,42 @@
#include <string.h>
#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/srv.h>
#include <3ds/synchronization.h>
#include <3ds/services/ptmsets.h>
#include <3ds/ipc.h>
static Handle ptmSetsHandle;
static int ptmSetsRefCount;
Result ptmSetsInit(void)
{
if (AtomicPostIncrement(&ptmSetsRefCount)) return 0;
Result res = srvGetServiceHandle(&ptmSetsHandle, "ptm:sets");
if (R_FAILED(res)) AtomicDecrement(&ptmSetsRefCount);
return res;
}
void ptmSetsExit(void)
{
if (AtomicDecrement(&ptmSetsRefCount)) return;
svcCloseHandle(ptmSetsHandle);
}
Handle *ptmSetsGetSessionHandle(void)
{
return &ptmSetsHandle;
}
Result PTMSETS_SetSystemTime(s64 msY2k)
{
Result ret;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x1,2,0); // 0x00010080
memcpy(&cmdbuf[1], &msY2k, 8);
if(R_FAILED(ret = svcSendSyncRequest(ptmSetsHandle)))return ret;
return (Result)cmdbuf[1];
}

View File

@ -24,6 +24,11 @@ void ptmSysmExit(void)
svcCloseHandle(ptmSysmHandle);
}
Handle *ptmSysmGetSessionHandle(void)
{
return &ptmSysmHandle;
}
Result PTMSYSM_RequestSleep(void)
{
Result ret;
@ -93,13 +98,14 @@ Result PTMSYSM_Awaken(void)
return (Result)cmdbuf[1];
}
Result PTMSYSM_CheckNew3DS(void)
Result PTMSYSM_CheckNew3DS(bool *out)
{
Result ret;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x040A,0,0); // 0x040A0000
if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return 0;
if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret;
*out = (bool)(cmdbuf[2] & 1); // if cmdbuf[1] is != 0 then this is uninitialized (this is fine)
return (Result)cmdbuf[1];
}
@ -144,6 +150,18 @@ Result PTMSYSM_RebootAsync(u64 timeout)
return (Result)cmdbuf[1];
}
Result PTMSYSM_SetUserTime(s64 msY2k)
{
Result ret;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x80C,2,0); // 0x080C0080
memcpy(&cmdbuf[1], &msY2k, 8);
if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret;
return (Result)cmdbuf[1];
}
Result PTMSYSM_InvalidateSystemTime(void)
{
Result ret;

View File

@ -24,6 +24,11 @@ void ptmuExit(void)
svcCloseHandle(ptmuHandle);
}
Handle *ptmuGetSessionHandle(void)
{
return &ptmuHandle;
}
Result PTMU_GetShellState(u8 *out)
{
Result ret=0;

Some files were not shown because too many files have changed in this diff Show More