Compare commits

..

202 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
Dave Murphy
8f06d03ca6 update Changelog and version for next release 2020-05-27 15:41:32 +01:00
Dave Murphy
26e4f20078 increase decompress buffer size
speeds up decompression when user hasn't increased file buffer
2020-05-27 15:41:32 +01:00
Michael Theall
59e12a1b5e Fix stat for romfs:/ 2020-05-27 13:02:35 +02:00
Glazed_Belmont
2e48185aed
Fix the typo for consoleClear() (#460)
Changed screan to screen
2020-05-26 01:48:11 +01:00
Dave Murphy
aefdcf203d implement lstat with stat
fat32 has no symlinks to follow or not follow
2020-05-25 01:37:34 +01:00
TuxSH
0053d8d076 Add ptm rtc time commands 2020-05-17 22:24:15 +02:00
TuxSH
e3be4b8678 Add sleep state FSM handling service commands 2020-05-17 17:45:52 +02:00
TuxSH
1bafb08f34 Add hidKeysDownRepeat 2020-05-14 22:25:16 +02:00
TuxSH
69483a473a Revamp mappableAlloc 2020-05-14 00:30:53 +02:00
TuxSH
c9326f6bce
hid: add hidWaitForAnyEvent, allow user to override irrst usage check (#454) 2020-05-13 23:09:55 +02:00
Dave Murphy
e86e906895 next version is 1.8.0 2020-05-10 00:20:46 +01:00
Dave Murphy
48f330e56a add changelog entry for 1.8.0 2020-05-09 23:49:36 +01:00
Dave Murphy
ed59061172 add changelog entry for 1.7.0 2020-05-09 23:47:43 +01:00
TuxSH
9d27eb80de APT workaround when Home Menu is not launched, etc. 2020-05-10 00:06:50 +02:00
TuxSH
8d64007d70
Weaken more init/env functions (#451) 2020-05-09 18:22:51 +01:00
TuxSH
65ff0a1fcc Change allocateHeaps logic to properly handle appmemtype 5 2020-05-09 17:41:26 +02:00
fincs
1389fcdfcd
gdbhio: Avoid using reserved st_* identifiers for custom types 2020-05-08 03:29:05 +02:00
fincs
b03b64dd09
Add MEMPERM_READWRITE and MEMPERM_READEXECUTE 2020-05-08 03:16:30 +02:00
Chris Feger
750bceebf2
Add FSPXI interfaces (#446) 2020-05-08 02:20:48 +02:00
TuxSH
b161b7e578 Add GSPGPU_ResetGpuCore 2020-05-07 00:14:02 +02:00
TuxSH
51c0bdfc90 Add gpu hw init on (0x2A07) 2020-05-07 00:14:02 +02:00
TuxSH
6f34257b57 Make initSystem and __ctru_exit weak. Use asm macros 2020-05-06 21:50:20 +02:00
fincs
92ddd9e9b3
gfxWriteFramebufferInfo: use correct atomic update code 2020-05-04 21:17:21 +02:00
fincs
98664af53c
Clean up and optimize LightLock_Lock 2020-05-04 18:34:13 +02:00
TuxSH
fddc517a6b Fix srv/srvpm sysver checks:
- Kernel major version is 3 on SAFE_FIRM
- osGetFirmVersion() is not always equal to osGetKernelVersion()
2020-05-02 14:19:48 +02:00
piepie62
1ca796b3a3 Add invalid glyph index protection 2020-04-24 12:36:23 +02:00
Chris Feger
175dd62a90
Add archive STDIO device driver (#443) 2020-04-15 23:06:36 +01:00
zhupengfei
87734ae8d9 cam: Fix CAMU_GetLatestVsyncTiming
The previous implementation was wrong and result in a guaranteed prefetch abort
2020-04-16 00:05:25 +02:00
Lioncash
7956f6ba24 services/mvd: Remove redundant conditional in mvdstdRenderVideoFrame()
This condition is already checked further up in the function.
2020-04-15 09:13:18 +01:00
Lioncash
0e4d8fbd05 servicers/fs: Remove redundant conditional in FSUSER_OpenArchive()
This is already checked against at the beginning of the function.
2020-04-15 09:13:18 +01:00
Patrik Rak
6bca5af849
Make the FIONBIO ioctl actually work. (#441)
The fcntl needs the original `sockfd`, not the remapped one.
2020-04-15 09:09:17 +01:00
Patrik Rak
8566366b02 Fix dspInit() error handling in ndspInit().
The early return after dspInit() failure obviously keeps the refcount increased and the component loaded. This change properly unwinds all state changes.
2019-10-04 14:36:06 +02:00
LiquidFenrir
8acc1871bc Add AC commands that allow forcing a wifi connection (#438) 2019-09-21 02:49:41 +02:00
fincs
751dd3b42d Release 1.6.0 2019-06-28 17:21:14 +02:00
TuxSH
ed7674626e Implement Luma3DS gdb hio (#433) 2019-06-23 18:06:47 +01:00
Dave Murphy
754c334c29
use temp var for misaligned struct member (#437) 2019-06-06 00:08:49 +01:00
WinterMute
e842cfd011 print string when it's not null 2019-06-05 15:30:06 +01:00
Kartik
188d175579 Fix select() 2019-05-16 18:14:54 +02:00
Chris Feger
309c3f1d7b Fix a possible null pointer issue (#434) 2019-05-14 11:03:24 +01:00
Oreo639
d3ea737071 Add miiSelector configuration functions (#429) 2019-05-04 01:40:00 +02:00
Oreo639
c550a22259 Fix macro PACKED being displayed as variable name 2019-05-04 01:38:29 +02:00
Oreo639
67933816ba Improve travis script 2019-04-30 13:56:50 +02:00
Oreo639
59b71196ac Fix blocking and add aptIsHomePressed 2019-04-25 02:52:00 +02:00
Alex Taber
f919af3182 Rename home_allowed to aptHomeAllowed 2019-04-25 02:52:00 +02:00
LiquidFenrir
9b4ac1118d Allow blocking HOME button
Signed-off-by: Alex Taber <alexftaber@gmail.com>
2019-04-25 02:52:00 +02:00
hax0kartik
d0b6343342 Do not include 3ds.h within ctrulib headers 2019-04-01 17:38:14 +02:00
hax0kartik
d4036d97ef Add Mii struct 2019-04-01 17:38:14 +02:00
Oreo639
fd1006b682 Update travis to build Doxygen. 2019-03-28 12:38:40 +01:00
Chris Feger
2750c4a70b Rework font API to support loading fonts other than the shared system font (#416) 2019-03-28 02:32:07 +01:00
TuxSH
09688ea6fc PM services overhaul 2019-03-28 01:04:02 +01:00
TuxSH
8ffc9461ea LOADER_RegisterProgram: use FS_ProgramInfo 2019-03-28 00:44:43 +01:00
TuxSH
0ed1f2beb2 IPC_Desc_CurProcessHandle => IPC_Desc_CurProcessId 2019-03-28 00:44:32 +01:00
TuxSH
7663a701c0 Add undocumented N3DS PM launch flags 2019-03-24 23:26:43 +01:00
TuxSH
0536cc5493 Loader bugfixes 2019-03-22 14:26:51 +01:00
fincs
5fd623da44 Fix typo in svc.s 2019-03-21 23:06:12 +01:00
Dave Murphy
da0af223a2
Update URL for devkitPro installers 2019-03-17 23:38:03 +00:00
TuxSH
ffba52bcd0 Add PM launch flags 2019-03-17 20:46:04 +01:00
TuxSH
79ee260c15 Implement PxiPM 2019-03-11 21:06:34 +01:00
TuxSH
dacacfa403 Implement fs:REG 2019-03-11 21:06:34 +01:00
fincs
550793b262 Minor text fixes 2019-03-10 21:14:25 +01:00
Slattz
67f10f0ba9 Fix frdGetSessionHandle declaration 2019-02-12 12:28:10 +01:00
198 changed files with 12775 additions and 7397 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

2
.gitignore vendored
View File

@ -9,5 +9,3 @@ docs/
examples/
internal_docs
build.sh
libctru/source/util/rbtree/test/*.o
libctru/source/util/rbtree/test/rbtree_test

View File

@ -1,35 +0,0 @@
language: c
sudo: required
services: docker
#Cache doxygen
cache:
directories:
- /home/travis/doxygen/doxygen-1.8.15/bin
env:
global:
- secure: "SYuVfyfmO6P+KnOXFZpNbBLO3QpGZJjIvFJHix3+xAfjENZoCzn+A7Gmcbm07VN0UB9sDJrYklyUrUepVWSuOd+FiNEyiLi4oYXxFzDjA9am7W1nFEaBAw6Ilifg7KX2OlClXaF7iiQ1udtmN9Uw+1LwfpldDDqti1H9TANaKHc="
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" ]; then mkdir -p ~/doxygen && cd ~/doxygen; fi
- if [ ! -e "$DOXY_BINPATH/doxygen" ]; then wget http://doxygen.nl/files/doxygen-1.8.15.linux.bin.tar.gz; fi
- if [ ! -e "$DOXY_BINPATH/doxygen" ]; 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
after_success:
- cd $TRAVIS_BUILD_DIR
- #Build the doxygen files and upload to GH pages
- git config --global user.email "travis@travis-ci.org"
- git config --global user.name "TravisCI-DocBuilder"
# Build the doxygen documentation and push it to gh-pages if this is a tagged release
- sh exportdoc.sh

View File

@ -1,4 +0,0 @@
#!/bin/sh -ex
docker pull devkitpro/devkitarm

View File

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

View File

@ -1,5 +1,257 @@
# 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.
- Add ptm rtc time commands
- Add sleep state FSM handling service commands
- Revamp mappableAlloc
- Fixed stat on romfs for directories, implemented lstat via stat (FAT32 has no symlinks)
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 1.8.0
- Added support for environments where the home menu is not launched (such as running under SAFE_FIRM)
- Added FSPXI service wrappers
- Changed heap allocation logic to be more flexible; this fixes support for certain system memory layouts
- Cleaned up and optimized light lock locking code
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 1.7.0
- fix FIONBIO ioctl
- Fix CAMU_GetLatestVsyncTiming
- Add archive STDIO device driver
- Add AC commands that allow forcing a wifi connection
- Fix dspInit() error handling in ndspInit()
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 1.6.0
- Major overhaul to font loading code in order to support loading external fonts.
- Major overhaul to PM service wrappers, now with separate support for pm:app and pm:dbg.
- Added Rosalina GDB host IO (gdbhio) support.
- Added support for the fs:REG service.
- Added support for the PxiPM service.
- Added PM launch flag definitions.
- Added SO_BROADCAST.
- Added new APT helper functions: aptIsHomeAllowed, aptSetHomeAllowed, aptIsHomePressed.
- Added support for the Mii selector libapplet, and other improvements to Mii support.
- Renamed IPC_Desc_CurProcessHandle to IPC_Desc_CurProcessId.
- Changed signature of LOADER_RegisterProgram to use FS_ProgramInfo structs.
- Fixed IPC bugs in the Loader service.
- Fixed svcCreateResourceLimit.
- Fixed corner case bug in GPUCMD_Add.
- Fixed bugs in select and herror.
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 1.5.1
- Added support for the FRD service.
@ -50,14 +302,14 @@
- svcCreateSessionToPort
- svcSetGpuProt
- svcSetWifiEnabled
- Additional functions
- Additional functions
- implement httpcAddPostDataBinary
- implement PTMU_GetAdapterState
- implement GSPLCD_GetBrightness
- add threadDetach
- srv fixes
- Fix srvPublishToSubscriber documentation
- Fix handling of service/named port names of length 8
- Fix handling of service/named port names of length 8
- Fix srvRegisterPort
- debugging support
- Add support for user-specified exception handlers
@ -69,7 +321,7 @@
- Correct GPU_PROCTEX_LUTID definition
- Add GPU_FOGMODE, GPU_GASMODE and GPU_GASLUTINPUT
- Other improvements and minor adjustments for overall system stability to enhance the user experience
## Version 1.2.1
- Added NFC support

View File

@ -1,21 +1,25 @@
# 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.
The most recent version of devkitARM is always recommended. The [installers/setup scripts](https://sourceforge.net/projects/devkitpro/files/Automated%20Installer/) supplied by devkitPro install a prebuilt copy of the latest stable version of libctru, which is recommended for general use. Please note that devkitPro has a policy of keeping legacy code to a minimum, so a library upgrade may result in older code failing to compile or behave properly. Developers are encouraged to keep their code working with the latest versions of the tools and libraries.
The most recent version of devkitARM is always recommended. The [installers/setup scripts](https://devkitpro.org/wiki/devkitPro_pacman) supplied by devkitPro install a prebuilt copy of the latest stable version of libctru, which is recommended for general use. Please note that devkitPro has a policy of keeping legacy code to a minimum, so a library upgrade may result in older code failing to compile or behave properly. Developers are encouraged to keep their code working with the latest versions of the tools and libraries.
You may find instructions on how to install devkitARM on [the devkitPro Wiki](http://devkitpro.org/wiki/Getting_Started).
# 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

View File

@ -1,13 +0,0 @@
#!/bin/sh
if [ "$TRAVIS_REPO_SLUG" = "smealum/ctrulib" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ -n "$TRAVIS_TAG" ]; then
git clone --branch=gh-pages --single-branch --depth 1 --no-checkout https://${GH_TOKEN}@github.com/$TRAVIS_REPO_SLUG docs
git clone --branch=master --single-branch --depth 1 https://github.com/devkitPro/3ds-examples examples
cd libctru
doxygen Doxyfile
mv ./docs/html/* ../docs
cd ../docs
git add --all
git commit -m"Doc generated from commit $TRAVIS_COMMIT"
git push -f origin gh-pages
fi

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
@ -1983,7 +1983,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@ -1991,7 +1991,7 @@ MACRO_EXPANSION = NO
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
@ -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 =
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,9 +8,9 @@ endif
include $(DEVKITARM)/base_rules
export LIBCTRU_MAJOR := 1
export LIBCTRU_MINOR := 5
export LIBCTRU_PATCH := 1
export LIBCTRU_MAJOR := 2
export LIBCTRU_MINOR := 4
export LIBCTRU_PATCH := 0
VERSION := $(LIBCTRU_MAJOR).$(LIBCTRU_MINOR).$(LIBCTRU_PATCH)
@ -50,11 +50,11 @@ 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
ASFLAGS := -g $(ARCH)
ASFLAGS := -g $(ARCH) $(INCLUDE)
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
@ -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>
@ -40,6 +45,8 @@ extern "C" {
#include <3ds/services/csnd.h>
#include <3ds/services/dsp.h>
#include <3ds/services/fs.h>
#include <3ds/services/fspxi.h>
#include <3ds/services/fsreg.h>
#include <3ds/services/frd.h>
#include <3ds/services/gspgpu.h>
#include <3ds/services/gsplcd.h>
@ -53,22 +60,27 @@ extern "C" {
#include <3ds/services/nwmext.h>
#include <3ds/services/ir.h>
#include <3ds/services/ns.h>
#include <3ds/services/pm.h>
#include <3ds/services/pmapp.h>
#include <3ds/services/pmdbg.h>
#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>
#include <3ds/services/mic.h>
#include <3ds/services/mvd.h>
#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>
@ -83,9 +95,13 @@ extern "C" {
#include <3ds/applets/miiselector.h>
#include <3ds/sdmc.h>
#include <3ds/archive.h>
#include <3ds/romfs.h>
#include <3ds/font.h>
#include <3ds/mii.h>
#include <3ds/gdbhio_dev.h>
#include <3ds/3dslink.h>
#ifdef __cplusplus
}
@ -136,4 +152,3 @@ extern "C" {
* @example threads/thread-basic/source/main.c
* @example time/rtc/source/main.c
*/

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,27 +4,24 @@
*/
#pragma once
#include <3ds/types.h>
/**
* @brief Reserves a mappable memory area.
* @param size Size of the area to reserve.
* @brief Initializes the mappable allocator.
* @param addrMin Minimum address.
* @param addrMax Maxium address.
*/
void mappableInit(u32 addrMin, u32 addrMax);
/**
* @brief Finds a mappable memory area.
* @param size Size of the area to find.
* @return The mappable area.
*/
void* mappableAlloc(size_t size);
/**
* @brief Retrieves the allocated size of a mappable area.
* @return The size of the mappable area.
*/
size_t mappableGetSize(void* mem);
/**
* @brief Frees a mappable area.
* @brief Frees a mappable area (stubbed).
* @param mem Mappable area to free.
*/
void mappableFree(void* mem);
/**
* @brief Gets the current mappable free space.
* @return The current mappable free space.
*/
u32 mappableSpaceFree(void);

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

@ -5,6 +5,7 @@
#pragma once
#include <3ds/types.h>
#include <3ds/mii.h>
/// Magic value needed to launch the applet.
#define MIISELECTOR_MAGIC 0x13DE28CF
@ -21,25 +22,25 @@
/// Parameter structure passed to AppletEd
typedef struct
{
char enable_cancel_button; ///< Enables canceling of selection if nonzero.
char enable_selecting_guests; ///< Makes Guets Miis selectable if nonzero.
char show_on_top_screen; ///< Shows applet on top screen if nonzero,
u8 enable_cancel_button; ///< Enables canceling of selection if nonzero.
u8 enable_selecting_guests; ///< Makes Guets Miis selectable if nonzero.
u8 show_on_top_screen; ///< Shows applet on top screen if nonzero,
///< otherwise show it on the bottom screen.
char _unk0x3[5]; ///< @private
u8 _unk0x3[5]; ///< @private
u16 title[MIISELECTOR_TITLE_LEN]; ///< UTF16-LE string displayed at the top of the applet. If
///< set to the empty string, a default title is displayed.
char _unk0x88[4]; ///< @private
char show_guest_page; ///< If nonzero, the applet shows a page with Guest
u8 _unk0x88[4]; ///< @private
u8 show_guest_page; ///< If nonzero, the applet shows a page with Guest
///< Miis on launch.
char _unk0x8D[3]; ///< @private
u8 _unk0x8D[3]; ///< @private
u32 initial_index; ///< Index of the initially selected Mii. If
///< @ref MiiSelectorConf.show_guest_page is
///< set, this is the index of a Guest Mii,
///< otherwise that of a user Mii.
char mii_guest_whitelist[MIISELECTOR_GUESTMII_SLOTS]; ///< Each byte set to a nonzero value
u8 mii_guest_whitelist[MIISELECTOR_GUESTMII_SLOTS]; ///< Each byte set to a nonzero value
///< enables its corresponding Guest
///< Mii to be enabled for selection.
char mii_whitelist[MIISELECTOR_USERMII_SLOTS]; ///< Each byte set to a nonzero value enables
u8 mii_whitelist[MIISELECTOR_USERMII_SLOTS]; ///< Each byte set to a nonzero value enables
///< its corresponding user Mii to be enabled
///< for selection.
u16 _unk0xFE; ///< @private
@ -47,9 +48,6 @@ typedef struct
///< applet.
} MiiSelectorConf;
/// Size of the data describing a single Mii
#define MIISELECTOR_MIIDATA_SIZE 0x5c
/// Maximum length of the localized name of a Guest Mii
#define MIISELECTOR_GUESTMII_NAME_LEN 12
@ -61,7 +59,7 @@ typedef struct
u32 guest_mii_was_selected; ///< 1 if a Guest Mii was selected, 0 otherwise.
u32 guest_mii_index; ///< Index of the selected Guest Mii,
///< 0xFFFFFFFF if no guest was selected.
char mii[MIISELECTOR_MIIDATA_SIZE]; ///< Data of selected Mii.
MiiData mii; ///< Data of selected Mii.
u16 _pad0x68; ///< @private
u16 checksum; ///< Checksum of the returned Mii data.
///< Stored as a big-endian value; use
@ -72,20 +70,119 @@ typedef struct
///< string). Zeroed otherwise.
} MiiSelectorReturn;
/// AppletEd options
enum
{
MIISELECTOR_CANCEL = BIT(0), ///< Show the cancel button
MIISELECTOR_GUESTS = BIT(1), ///< Make Guets Miis selectable
MIISELECTOR_TOP = BIT(2), ///< Show AppletEd on top screen
MIISELECTOR_GUESTSTART = BIT(3), ///< Start on guest page
};
/**
* @brief Initialize Mii selector config
* @param conf Pointer to Miiselector config.
*/
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
*
* @param conf Pointer to miiSelector configuration
* @param text Title text of Mii selector
*/
void miiSelectorSetTitle(MiiSelectorConf *conf, const char* text);
/**
* @brief Specifies which special options are enabled in the Mii selector
*
* @param conf Pointer to miiSelector configuration
* @param options Options bitmask
*/
void miiSelectorSetOptions(MiiSelectorConf *conf, u32 options);
/**
* @brief Specifies which guest Miis will be selectable
*
* @param conf Pointer to miiSelector configuration
* @param index Index of the guest Miis that will be whitelisted.
* @ref MIISELECTOR_GUESTMII_SLOTS can be used to whitelist all the guest Miis.
*/
void miiSelectorWhitelistGuestMii(MiiSelectorConf *conf, u32 index);
/**
* @brief Specifies which guest Miis will be unselectable
*
* @param conf Pointer to miiSelector configuration
* @param index Index of the guest Miis that will be blacklisted.
* @ref MIISELECTOR_GUESTMII_SLOTS can be used to blacklist all the guest Miis.
*/
void miiSelectorBlacklistGuestMii(MiiSelectorConf *conf, u32 index);
/**
* @brief Specifies which user Miis will be selectable
*
* @param conf Pointer to miiSelector configuration
* @param index Index of the user Miis that will be whitelisted.
* @ref MIISELECTOR_USERMII_SLOTS can be used to whitlist all the user Miis
*/
void miiSelectorWhitelistUserMii(MiiSelectorConf *conf, u32 index);
/**
* @brief Specifies which user Miis will be selectable
*
* @param conf Pointer to miiSelector configuration
* @param index Index of the user Miis that will be blacklisted.
* @ref MIISELECTOR_USERMII_SLOTS can be used to blacklist all the user Miis
*/
void miiSelectorBlacklistUserMii(MiiSelectorConf *conf, u32 index);
/**
* @brief Specifies which Mii the cursor should start from
*
* @param conf Pointer to miiSelector configuration
* @param index Indexed number of the Mii that the cursor will start on.
* If there is no mii with that index, the the cursor will start at the Mii
* with the index 0 (the personal Mii).
*/
static inline void miiSelectorSetInitialIndex(MiiSelectorConf *conf, u32 index)
{
conf->initial_index = index;
}
/**
* @brief Get Mii name
*
* @param returnbuf Pointer to miiSelector return
* @param out String containing a Mii's name
* @param max_size Size of string. Since UTF8 characters range in size from 1-3 bytes
* (assuming that no non-BMP characters are used), this value should be 36 (or 30 if you are not
* dealing with guest miis).
*/
void miiSelectorReturnGetName(const MiiSelectorReturn *returnbuf, char* out, size_t max_size);
/**
* @brief Get Mii Author
*
* @param returnbuf Pointer to miiSelector return
* @param out String containing a Mii's author
* @param max_size Size of string. Since UTF8 characters range in size from 1-3 bytes
* (assuming that no non-BMP characters are used), this value should be 30.
*/
void miiSelectorReturnGetAuthor(const MiiSelectorReturn *returnbuf, char* out, size_t max_size);
/**
* @brief Verifies that the Mii data returned from the applet matches its
* checksum
*
* @param returnbuffer Buffer filled by Mii selector applet
*
* @param returnbuf Buffer filled by Mii selector applet
* @return `true` if `returnbuf->checksum` is the same as the one computed from `returnbuf`
*/
bool miiSelectorChecksumIsValid(const MiiSelectorReturn *returnbuf);

View File

@ -0,0 +1,43 @@
/**
* @file archive.h
* @brief FS_Archive driver
*/
#pragma once
#include <sys/types.h>
#include <3ds/types.h>
#include <3ds/services/fs.h>
#define ARCHIVE_DIRITER_MAGIC 0x68637261 /* "arch" */
/*! Open directory struct */
typedef struct
{
u32 magic; /*! "arch" */
Handle fd; /*! CTRU handle */
ssize_t index; /*! Current entry index */
size_t size; /*! Current batch size */
FS_DirectoryEntry entry_data[32]; /*! Temporary storage for reading entries */
} archive_dir_t;
/// Mounts the SD
Result archiveMountSdmc(void);
/// Mounts and opens an archive as deviceName
/// Returns either an archive open error code, or -1 for generic failure
Result archiveMount(FS_ArchiveID archiveID, FS_Path archivePath, const char *deviceName);
/// Uses FSUSER_ControlArchive with control action ARCHIVE_ACTION_COMMIT_SAVE_DATA on the opened archive. Not done automatically at unmount.
/// Returns -1 if the specified device is not found
Result archiveCommitSaveData(const char *deviceName);
/// Unmounts the specified device, closing its archive in the process
/// Returns -1 if the specified device was not found
Result archiveUnmount(const char *deviceName);
/// Unmounts all devices and cleans up any resources used by the driver
Result archiveUnmountAll(void);
/// Get a file's mtime
Result archive_getmtime(const char *name, u64 *mtime);

View File

@ -0,0 +1,21 @@
#pragma once
#if !__ASSEMBLER__
#error This header file is only for use in assembly files!
#endif // !__ASSEMBLER__
.macro BEGIN_ASM_FUNC name, linkage=global, section=text
.section .\section\().\name, "ax", %progbits
.align 2
.\linkage \name
.type \name, %function
.func \name
.cfi_sections .debug_frame
.cfi_startproc
\name:
.endm
.macro END_ASM_FUNC
.cfi_endproc
.endfunc
.endm

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 {
@ -166,7 +168,7 @@ PrintConsole* consoleInit(gfxScreen_t screen, PrintConsole* console);
*/
void consoleDebugInit(debugDevice device);
/// Clears the screan by using iprintf("\x1b[2J");
/// Clears the screen by using iprintf("\x1b[2J");
void consoleClear(void);
#ifdef __cplusplus

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,12 +46,12 @@ 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
char failure_mesg[0x60]; ///< String for when type is ERRF_ERRTYPE_FAILURE
} data; ///< The different types of data for errors.
} ERRF_FatalErrInfo;
@ -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

@ -157,49 +157,80 @@ enum
/// Ensures the shared system font is mapped.
Result fontEnsureMapped(void);
/// Retrieves the font information structure of the shared system font.
static inline FINF_s* fontGetInfo(void)
/**
* @brief Fixes the pointers internal to a just-loaded font
* @param font Font to fix
* @remark Should never be run on the system font, and only once on any other font.
*/
void fontFixPointers(CFNT_s* font);
/// Gets the currently loaded system font
static inline CFNT_s* fontGetSystemFont(void)
{
extern CFNT_s* g_sharedFont;
return &g_sharedFont->finf;
if (!g_sharedFont)
fontEnsureMapped();
return g_sharedFont;
}
/// Retrieves the texture sheet information of the shared system font.
static inline TGLP_s* fontGetGlyphInfo(void)
/**
* @brief Retrieves the font information structure of a font.
* @param font Pointer to font structure. If NULL, the shared system font is used.
*/
static inline FINF_s* fontGetInfo(CFNT_s* font)
{
return fontGetInfo()->tglp;
if (!font)
font = fontGetSystemFont();
return &font->finf;
}
/**
* @brief Retrieves the texture sheet information of a font.
* @param font Pointer to font structure. If NULL, the shared system font is used.
*/
static inline TGLP_s* fontGetGlyphInfo(CFNT_s* font)
{
if (!font)
font = fontGetSystemFont();
return fontGetInfo(font)->tglp;
}
/**
* @brief Retrieves the pointer to texture data for the specified texture sheet.
* @param font Pointer to font structure. If NULL, the shared system font is used.
* @param sheetIndex Index of the texture sheet.
*/
static inline void* fontGetGlyphSheetTex(int sheetIndex)
static inline void* fontGetGlyphSheetTex(CFNT_s* font, int sheetIndex)
{
TGLP_s* tglp = fontGetGlyphInfo();
if (!font)
font = fontGetSystemFont();
TGLP_s* tglp = fontGetGlyphInfo(font);
return &tglp->sheetData[sheetIndex*tglp->sheetSize];
}
/**
* @brief Retrieves the glyph index of the specified Unicode codepoint.
* @param font Pointer to font structure. If NULL, the shared system font is used.
* @param codePoint Unicode codepoint.
*/
int fontGlyphIndexFromCodePoint(u32 codePoint);
int fontGlyphIndexFromCodePoint(CFNT_s* font, u32 codePoint);
/**
* @brief Retrieves character width information of the specified glyph.
* @param font Pointer to font structure. If NULL, the shared system font is used.
* @param glyphIndex Index of the glyph.
*/
charWidthInfo_s* fontGetCharWidthInfo(int glyphIndex);
charWidthInfo_s* fontGetCharWidthInfo(CFNT_s* font, int glyphIndex);
/**
* @brief Calculates position information for the specified glyph.
* @param out Output structure in which to write the information.
* @param font Pointer to font structure. If NULL, the shared system font is used.
* @param glyphIndex Index of the glyph.
* @param flags Calculation flags (see GLYPH_POS_* flags).
* @param scaleX Scale factor to apply horizontally.
* @param scaleY Scale factor to apply vertically.
*/
void fontCalcGlyphPos(fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY);
void fontCalcGlyphPos(fontGlyphPos_s* out, CFNT_s* font, int glyphIndex, u32 flags, float scaleX, float scaleY);
///@}

View File

@ -0,0 +1,29 @@
/**
* @file gdbhio.h
* @brief Luma3DS GDB HIO (called File I/O in GDB documentation) functions.
*/
#pragma once
#include <fcntl.h>
#include <sys/time.h>
#include <sys/stat.h>
#define GDBHIO_STDIN_FILENO 0
#define GDBHIO_STDOUT_FILENO 1
#define GDBHIO_STDERR_FILENO 2
int gdbHioOpen(const char *pathname, int flags, mode_t mode);
int gdbHioClose(int fd);
int gdbHioRead(int fd, void *buf, unsigned int count);
int gdbHioWrite(int fd, const void *buf, unsigned int count);
off_t gdbHioLseek(int fd, off_t offset, int flag);
int gdbHioRename(const char *oldpath, const char *newpath);
int gdbHioUnlink(const char *pathname);
int gdbHioStat(const char *pathname, struct stat *st);
int gdbHioFstat(int fd, struct stat *st);
int gdbHioGettimeofday(struct timeval *tv, void *tz);
int gdbHioIsatty(int fd);
///< Host I/O 'system' function, requires 'set remote system-call-allowed 1'.
int gdbHioSystem(const char *command);

View File

@ -0,0 +1,37 @@
/**
* @file gdbhio_dev.h
* @brief Luma3DS GDB HIO (called File I/O in GDB documentation) devoptab wrapper.
*/
#pragma once
#include <stdbool.h>
struct timeval;
///< Initializes the GDB HIO devoptab wrapper, returns 0 on success, -1 on failure.
int gdbHioDevInit(void);
///< Deinitializes the GDB HIO devoptab wrapper.
void gdbHioDevExit(void);
///< Returns a file descriptor mapping to the GDB client console's standard input stream.
int gdbHioDevGetStdin(void);
///< Returns a file descriptor mapping to the GDB client console's standard output stream.
int gdbHioDevGetStdout(void);
///< Returns a file descriptor mapping to the GDB client console's standard error stream.
int gdbHioDevGetStderr(void);
///< Redirects 0 to 3 of the application's standard streams to GDB client console's. Returns -1, -2, or -3, resp., on failure; 0 on success.
int gdbHioDevRedirectStdStreams(bool in, bool out, bool err);
///< GDB HIO POSIX function gettimeofday.
int gdbHioDevGettimeofday(struct timeval *tv, void *tz);
///< GDB HIO POSIX function isatty.
int gdbHioDevIsatty(int fd);
///< GDB HIO POSIX function system. Requires 'set remote system-call-allowed 1'.
int gdbHioDevSystem(const char *command);

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
}gfxScreen_t;
/// 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
{
GFX_LEFT = 0, ///< Left eye framebuffer
GFX_RIGHT = 1,///< Right eye framebuffer
}gfx3dSide_t;
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

@ -60,16 +60,21 @@ static inline u32 IPC_Desc_MoveHandles(unsigned number)
}
/**
* @brief Returns the code to ask the kernel to fill the handle with the current process handle.
* @return The code to request the current process handle.
* @brief Returns the code to ask the kernel to fill the handle with the current process ID.
* @return The code to request the current process ID.
*
* The next value is a placeholder that will be replaced by the current process handle by the kernel.
* The next value is a placeholder that will be replaced by the current process ID by the kernel.
*/
static inline u32 IPC_Desc_CurProcessHandle(void)
static inline u32 IPC_Desc_CurProcessId(void)
{
return 0x20;
}
static inline CTR_DEPRECATED u32 IPC_Desc_CurProcessHandle(void)
{
return IPC_Desc_CurProcessId();
}
/**
* @brief Creates a header describing a static buffer.
* @param size Size of the buffer. Max ?0x03FFFF?.

159
libctru/include/3ds/mii.h Normal file
View File

@ -0,0 +1,159 @@
/**
* @file mii.h
* @brief Shared Mii struct.
*
* @see https://www.3dbrew.org/wiki/Mii#Mii_format
*/
#pragma once
#include <3ds/types.h>
/// Shared Mii struct
typedef struct
{
u8 magic; ///< Always 3?
/// Mii options
struct
{
bool allow_copying : 1; ///< True if copying is allowed
bool is_private_name : 1; ///< Private name?
u8 region_lock : 2; ///< Region lock (0=no lock, 1=JPN, 2=USA, 3=EUR)
u8 char_set : 2; ///< Character set (0=JPN+USA+EUR, 1=CHN, 2=KOR, 3=TWN)
} mii_options;
/// Mii position in Mii selector or Mii maker
struct
{
u8 page_index : 4; ///< Page index of Mii
u8 slot_index : 4; ///< Slot offset of Mii on its Page
} mii_pos;
/// Console Identity
struct
{
u8 unknown0 : 4; ///< Mabye padding (always seems to be 0)?
u8 origin_console : 3; ///< Console that the Mii was created on (1=WII, 2=DSI, 3=3DS)
} console_identity;
u64 system_id; ///< Identifies the system that the Mii was created on (Determines pants)
u32 mii_id; ///< ID of Mii
u8 mac[6]; ///< Creator's system's full MAC address
u8 pad[2]; ///< Padding
/// Mii details
struct {
bool sex : 1; ///< Sex of Mii (False=Male, True=Female)
u16 bday_month : 4; ///< Month of Mii's birthday
u16 bday_day : 5; ///< Day of Mii's birthday
u16 shirt_color : 4; ///< Color of Mii's shirt
bool favorite : 1; ///< Whether the Mii is one of your 10 favorite Mii's
} mii_details;
u16 mii_name[10]; ///< Name of Mii (Encoded using UTF16)
u8 height; ///< How tall the Mii is
u8 width; ///< How wide the Mii is
/// Face style
struct
{
bool disable_sharing : 1; ///< Whether or not Sharing of the Mii is allowed
u8 shape : 4; ///< Face shape
u8 skinColor : 3; ///< Color of skin
} face_style;
/// Face details
struct
{
u8 wrinkles : 4;
u8 makeup : 4;
} face_details;
u8 hair_style;
/// Hair details
struct
{
u8 color : 3;
bool flip : 1;
} hair_details;
/// Eye details
struct
{
u32 style : 6;
u32 color : 3;
u32 scale : 4;
u32 yscale : 3;
u32 rotation : 5;
u32 xspacing : 4;
u32 yposition : 5;
} eye_details;
/// Eyebrow details
struct
{
u32 style : 5;
u32 color : 3;
u32 scale : 4;
u32 yscale : 3;
u32 pad : 1;
u32 rotation : 5;
u32 xspacing : 4;
u32 yposition : 5;
} eyebrow_details;
/// Nose details
struct
{
u16 style : 5;
u16 scale : 4;
u16 yposition : 5;
} nose_details;
/// Mouth details
struct
{
u16 style : 6;
u16 color : 3;
u16 scale : 4;
u16 yscale : 3;
} mouth_details;
/// Mustache details
struct
{
u16 mouth_yposition : 5;
u16 mustach_style : 3;
u16 pad : 2;
} mustache_details;
/// Beard details
struct
{
u16 style : 3;
u16 color : 3;
u16 scale : 4;
u16 ypos : 5;
} beard_details;
/// Glasses details
struct
{
u16 style : 4;
u16 color : 3;
u16 scale : 4;
u16 ypos : 5;
} glasses_details;
/// Mole details
struct
{
bool enable : 1;
u16 scale : 5;
u16 xpos : 5;
u16 ypos : 5;
} mole_details;
u16 author_name[10]; ///< Name of Mii's author (Encoded using UTF16)
} 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)
#define SYSCLOCK_ARM11 (SYSCLOCK_ARM9 * 2)
#define SYSCLOCK_ARM11_NEW (SYSCLOCK_ARM11 * 3)
///< 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)
///< 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,7 +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 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 OS_KernelConfig->app_memtype;
}
/**
@ -113,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];
}
}
@ -122,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.
@ -172,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.
@ -184,7 +303,7 @@ double osTickCounterRead(const TickCounter* cnt);
*/
static inline u8 osGetWifiStrength(void)
{
return *(vu8*)0x1FF81066;
return OS_SharedConfig->wifi_strength;
}
/**
@ -193,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

@ -1,34 +0,0 @@
/**
* @file sdmc.h
* @brief SDMC driver.
*/
#pragma once
#include <sys/types.h>
#include <3ds/types.h>
#include <3ds/services/fs.h>
#define SDMC_DIRITER_MAGIC 0x73646D63 /* "sdmc" */
/*! Open directory struct */
typedef struct
{
u32 magic; /*! "sdmc" */
Handle fd; /*! CTRU handle */
ssize_t index; /*! Current entry index */
size_t size; /*! Current batch size */
FS_DirectoryEntry entry_data[32]; /*! Temporary storage for reading entries */
} sdmc_dir_t;
/// Initializes the SDMC driver.
Result sdmcInit(void);
/// Enable/disable copy in sdmc_write
void sdmcWriteSafe(bool enable);
/// Exits the SDMC driver.
Result sdmcExit(void);
/// Get a file's mtime
Result sdmc_getmtime(const char *name, u64 *mtime);

View File

@ -4,30 +4,50 @@
*/
#pragma once
#include <3ds/types.h>
/// Wifi security modes.
typedef enum {
AC_OPEN = 0, ///< Open authentication.
AC_WEP_40BIT = 1, ///< WEP 40-bit authentication.
AC_WEP_104BIT = 2, ///< WEP 104-bit authentication.
AC_WEP_128BIT = 3, ///< WEP 128-bit authentication.
AC_WPA_TKIP = 4, ///< WPA TKIP authentication.
AC_WPA2_TKIP = 5, ///< WPA2 TKIP authentication.
AC_WPA_AES = 6, ///< WPA AES authentication.
AC_WPA2_AES = 7, ///< WPA2 AES authentication.
AC_OPEN = 0, ///< Open authentication.
AC_WEP_40BIT = 1, ///< WEP 40-bit authentication.
AC_WEP_104BIT = 2, ///< WEP 104-bit authentication.
AC_WEP_128BIT = 3, ///< WEP 128-bit authentication.
AC_WPA_TKIP = 4, ///< WPA TKIP authentication.
AC_WPA2_TKIP = 5, ///< WPA2 TKIP authentication.
AC_WPA_AES = 6, ///< WPA AES authentication.
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];
} acuConfig;
/// Initializes AC.
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);
@ -90,3 +110,48 @@ Result ACU_GetLastErrorCode(u32* errorCode);
* @param errorCode Pointer to output the error code to.
*/
Result ACU_GetLastDetailErrorCode(u32* errorCode);
/**
* @brief Prepares a buffer to hold the configuration data to start a connection.
* @param config Pointer to an acuConfig struct to contain the data.
*/
Result ACU_CreateDefaultConfig(acuConfig* config);
/**
* @brief Sets something that makes the connection reliable.
* @param config Pointer to an acuConfig struct used with ACU_CreateDefaultConfig previously.
* @param area Always 2 ?
*/
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 AP types bitmask, a combination of AC_AP_TYPE_* flags.
*/
Result ACU_SetAllowApType(acuConfig* config, u8 type);
/**
* @brief Sets something that makes the connection reliable.
* @param config Pointer to an acuConfig struct used with ACU_CreateDefaultConfig previously.
*/
Result ACU_SetRequestEulaVersion(acuConfig* config);
/**
* @brief Starts the connection procedure.
* @param config Pointer to an acuConfig struct used with ACU_CreateDefaultConfig previously.
* @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

@ -45,6 +45,8 @@ typedef enum {
typedef u8 APT_AppletAttr;
struct PtmWakeEvents;
/// Create an APT_AppletAttr bitfield from its components.
static inline APT_AppletAttr aptMakeAppletAttr(APT_AppletPos pos, bool manualGpuRights, bool manualDspRights)
{
@ -146,21 +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);
/// 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);
/// 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);
/// 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);
@ -191,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.
@ -326,6 +377,13 @@ Result APT_IsRegistered(NS_APPID appID, bool* out);
*/
Result APT_InquireNotification(u32 appID, APT_Signal* signalType);
/**
* @brief Requests to enter sleep mode, and later sets wake events if allowed to.
* @param wakeEvents The wake events. Limited to "shell" (bit 1) for the PDN wake events part
* and "shell opened", "shell closed" and "HOME button pressed" for the MCU interrupts part.
*/
Result APT_SleepSystem(const struct PtmWakeEvents *wakeEvents);
/**
* @brief Notifies an application to wait.
* @param appID ID of the application.
@ -345,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.
@ -513,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

@ -3,7 +3,7 @@
* @brief Friend Services
*/
#pragma once
#include <3ds.h>
#include <3ds/mii.h>
#define FRIEND_SCREEN_NAME_SIZE 0xB ///< 11-byte UTF-16 screen name
#define FRIEND_COMMENT_SIZE 0x21 ///< 33-byte UTF-16 comment
@ -26,26 +26,6 @@ typedef struct
u32 version;
u32 unk;
} TitleData;
/// Structure containing basic Mii information.
typedef struct
{
u32 mii_id;
u64 system_id;
u32 cdate;
u8 mac[0x6];
u16 padding;
u16 misc1;
u16 mii_name[0xB];
u8 width;
u8 height;
u32 misc2;
u32 unknown1;
u32 misc3;
u32 unknown2;
u8 allow_copy;
u8 unknown3[0x7];
u16 author[0xB];
} MiiData;
/// Friend profile data
typedef struct
@ -97,7 +77,7 @@ Result frdInit(void);
void frdExit(void);
/// Get FRD handle.
Handle *frdSessionGetHandle(void);
Handle *frdGetSessionHandle(void);
/**
* @brief Gets the login status of the current user.
* @param state Pointer to write the current user's login status to.

View File

@ -135,6 +135,7 @@ typedef enum
{
ARCHIVE_ACTION_COMMIT_SAVE_DATA = 0, ///< Commits save data changes. No inputs/outputs.
ARCHIVE_ACTION_GET_TIMESTAMP = 1, ///< Retrieves a file's last-modified timestamp. In: "u16*, UTF-16 Path", Out: "u64, Time Stamp".
ARCHIVE_ACTION_UNKNOWN = 0x789D, //< Unknown action; calls FSPXI command 0x56. In: "FS_Path instance", Out: "u32[4], Unknown"
} FS_ArchiveAction;
/// Secure save control actions.
@ -201,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.
@ -234,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;
@ -444,7 +453,7 @@ Result FSUSER_IsSdmcDetected(bool *detected);
/**
* @brief Gets whether the SD card is writable.
* @param detected Pointer to output the writable status to.
* @param writable Pointer to output the writable status to.
*/
Result FSUSER_IsSdmcWritable(bool *writable);
@ -466,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.
@ -537,7 +546,7 @@ Result FSUSER_CardNorDirectCommandWithAddress(u8 commandId, u32 address);
* @param size Size of the output buffer.
* @param output Output buffer.
*/
Result FSUSER_CardNorDirectRead(u8 commandId, u32 size, u8* output);
Result FSUSER_CardNorDirectRead(u8 commandId, u32 size, void* output);
/**
* @brief Executes a CARDNOR direct read with an address.
@ -546,7 +555,7 @@ Result FSUSER_CardNorDirectRead(u8 commandId, u32 size, u8* output);
* @param size Size of the output buffer.
* @param output Output buffer.
*/
Result FSUSER_CardNorDirectReadWithAddress(u8 commandId, u32 address, u32 size, u8* output);
Result FSUSER_CardNorDirectReadWithAddress(u8 commandId, u32 address, u32 size, void* output);
/**
* @brief Executes a CARDNOR direct write.
@ -554,7 +563,7 @@ Result FSUSER_CardNorDirectReadWithAddress(u8 commandId, u32 address, u32 size,
* @param size Size of the input buffer.
* @param output Input buffer.
*/
Result FSUSER_CardNorDirectWrite(u8 commandId, u32 size, u8* input);
Result FSUSER_CardNorDirectWrite(u8 commandId, u32 size, const void* input);
/**
* @brief Executes a CARDNOR direct write with an address.
@ -563,7 +572,7 @@ Result FSUSER_CardNorDirectWrite(u8 commandId, u32 size, u8* input);
* @param size Size of the input buffer.
* @param input Input buffer.
*/
Result FSUSER_CardNorDirectWriteWithAddress(u8 commandId, u32 address, u32 size, u8* input);
Result FSUSER_CardNorDirectWriteWithAddress(u8 commandId, u32 address, u32 size, const void* input);
/**
* @brief Executes a CARDNOR 4xIO direct read.
@ -572,7 +581,7 @@ Result FSUSER_CardNorDirectWriteWithAddress(u8 commandId, u32 address, u32 size,
* @param size Size of the output buffer.
* @param output Output buffer.
*/
Result FSUSER_CardNorDirectRead_4xIO(u8 commandId, u32 address, u32 size, u8* output);
Result FSUSER_CardNorDirectRead_4xIO(u8 commandId, u32 address, u32 size, void* output);
/**
* @brief Executes a CARDNOR direct CPU write without verify.
@ -580,7 +589,7 @@ Result FSUSER_CardNorDirectRead_4xIO(u8 commandId, u32 address, u32 size, u8* ou
* @param size Size of the input buffer.
* @param output Input buffer.
*/
Result FSUSER_CardNorDirectCpuWriteWithoutVerify(u32 address, u32 size, u8* input);
Result FSUSER_CardNorDirectCpuWriteWithoutVerify(u32 address, u32 size, const void* input);
/**
* @brief Executes a CARDNOR direct sector erase without verify.
@ -610,7 +619,7 @@ Result FSUSER_SetCardSpiBaudRate(FS_CardSpiBaudRate baudRate);
/**
* @brief Sets the CARDSPI bus mode.
* @param baudRate Bus mode to set.
* @param busMode Bus mode to set.
*/
Result FSUSER_SetCardSpiBusMode(FS_CardSpiBusMode busMode);
@ -632,15 +641,15 @@ Result FSUSER_GetSpecialContentIndex(u16* index, FS_MediaType mediaType, u64 pro
* @param programId ID of the program.
* @param header Pointer to output the legacy ROM header to. (size = 0x3B4)
*/
Result FSUSER_GetLegacyRomHeader(FS_MediaType mediaType, u64 programId, u8* header);
Result FSUSER_GetLegacyRomHeader(FS_MediaType mediaType, u64 programId, void* header);
/**
* @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, u8* banner);
Result FSUSER_GetLegacyBannerData(FS_MediaType mediaType, u64 programId, void* banner);
/**
* @brief Checks a process's authority to access a save data archive.
@ -697,7 +706,7 @@ Result FSUSER_GetFormatInfo(u32* totalSize, u32* directories, u32* files, bool*
* @param programId ID of the program.
* @param header Pointer to output the legacy ROM header to.
*/
Result FSUSER_GetLegacyRomHeader2(u32 headerSize, FS_MediaType mediaType, u64 programId, u8* header);
Result FSUSER_GetLegacyRomHeader2(u32 headerSize, FS_MediaType mediaType, u64 programId, void* header);
/**
* @brief Gets the CTR SDMC root path.
@ -745,7 +754,7 @@ Result FSUSER_FormatSaveData(FS_ArchiveID archiveId, FS_Path path, u32 blocks, u
* @param programId ID of the program.
* @param header Pointer to output the legacy sub banner data to.
*/
Result FSUSER_GetLegacySubBannerData(u32 bannerSize, FS_MediaType mediaType, u64 programId, u8* banner);
Result FSUSER_GetLegacySubBannerData(u32 bannerSize, FS_MediaType mediaType, u64 programId, void* banner);
/**
* @brief Hashes the given data and outputs a SHA256 hash.
@ -762,7 +771,7 @@ Result FSUSER_UpdateSha256Context(const void* data, u32 inputSize, u8* hash);
* @param size Size of the buffer.
* @param data Buffer to read to.
*/
Result FSUSER_ReadSpecialFile(u32* bytesRead, u64 fileOffset, u32 size, u8* data);
Result FSUSER_ReadSpecialFile(u32* bytesRead, u64 fileOffset, u32 size, void* data);
/**
* @brief Gets the size of a special file.
@ -871,7 +880,7 @@ Result FSUSER_SetCtrCardLatencyParameter(u64 latency, bool emulateEndurance);
/**
* @brief Toggles cleaning up invalid save data.
* @param Whether to enable cleaning up invalid save data.
* @param enable Whether to enable cleaning up invalid save data.
*/
Result FSUSER_SwitchCleanupInvalidSaveData(bool enable);

View File

@ -0,0 +1,613 @@
/**
* @file fspxi.h
* @brief Service interface for PxiFS services. This is normally not accessible to userland apps. https://3dbrew.org/wiki/Filesystem_services_PXI
*/
#pragma once
#include <3ds/services/fs.h>
#include <3ds/types.h>
typedef u64 FSPXI_Archive;
typedef u64 FSPXI_File;
typedef u64 FSPXI_Directory;
/**
* @brief Opens a file.
* @param out Pointer to output the file handle to.
* @param archive Archive containing the file.
* @param path Path of the file.
* @param flags Flags to open the file with.
* @param attributes Attributes of the file.
*/
Result FSPXI_OpenFile(Handle serviceHandle, FSPXI_File* out, FSPXI_Archive archive, FS_Path path, u32 flags, u32 attributes);
/**
* @brief Deletes a file.
* @param archive Archive containing the file.
* @param path Path of the file.
*/
Result FSPXI_DeleteFile(Handle serviceHandle, FSPXI_Archive archive, FS_Path path);
/**
* @brief Renames a file.
* @param srcArchive Archive containing the source file.
* @param srcPath Path of the source file.
* @param dstArchive Archive containing the destination file.
* @param dstPath Path of the destination file.
*/
Result FSPXI_RenameFile(Handle serviceHandle, FSPXI_Archive srcArchive, FS_Path srcPath, FSPXI_Archive dstArchive, FS_Path dstPath);
/**
* @brief Deletes a directory.
* @param archive Archive containing the directory.
* @param path Path of the directory.
*/
Result FSPXI_DeleteDirectory(Handle serviceHandle, FSPXI_Archive archive, FS_Path path);
/**
* @brief Creates a file.
* @param archive Archive to create the file in.
* @param path Path of the file.
* @param attributes Attributes of the file.
* @param size Size of the file.
*/
Result FSPXI_CreateFile(Handle serviceHandle, FSPXI_Archive archive, FS_Path path, u32 attributes, u64 fileSize);
/**
* @brief Creates a directory.
* @param archive Archive to create the directory in.
* @param path Path of the directory.
* @param attributes Attributes of the directory.
*/
Result FSPXI_CreateDirectory(Handle serviceHandle, FSPXI_Archive archive, FS_Path path, u32 attributes);
/**
* @brief Renames a directory.
* @param srcArchive Archive containing the source directory.
* @param srcPath Path of the source directory.
* @param dstArchive Archive containing the destination directory.
* @param dstPath Path of the destination directory.
*/
Result FSPXI_RenameDirectory(Handle serviceHandle, FSPXI_Archive srcArchive, FS_Path srcPath, FSPXI_Archive dstArchive, FS_Path dstPath);
/**
* @brief Opens a directory.
* @param out Pointer to output the directory handle to.
* @param archive Archive containing the directory.
* @param path Path of the directory.
*/
Result FSPXI_OpenDirectory(Handle serviceHandle, FSPXI_Directory* out, FSPXI_Archive archive, FS_Path path);
/**
* @brief Reads from a file.
* @param file File to read from.
* @param bytesRead Pointer to output the number of read bytes to.
* @param offset Offset to read from.
* @param buffer Buffer to read to.
* @param size Size of the buffer.
*/
Result FSPXI_ReadFile(Handle serviceHandle, FSPXI_File file, u32* bytesRead, u64 offset, void* buffer, u32 size);
/**
* @brief Calculate SHA256 of a file.
* @param file File to calculate the hash of.
* @param buffer Buffer to output the hash to.
* @param size Size of the buffer.
*/
Result FSPXI_CalculateFileHashSHA256(Handle serviceHandle, FSPXI_File file, void* buffer, u32 size);
/**
* @brief Writes to a file.
* @param file File to write to.
* @param bytesWritten Pointer to output the number of bytes written to.
* @param offset Offset to write to.
* @param buffer Buffer to write from.
* @param size Size of the buffer.
* @param flags Flags to use when writing.
*/
Result FSPXI_WriteFile(Handle serviceHandle, FSPXI_File file, u32* bytesWritten, u64 offset, const void* buffer, u32 size, u32 flags);
/**
* @brief Calculates the MAC used in a DISA/DIFF header?
* @param file Unsure
* @param inBuffer 0x100-byte DISA/DIFF input buffer.
* @param inSize Size of inBuffer.
* @param outBuffer Buffer to write MAC to.
* @param outSize Size of outBuffer.
*/
Result FSPXI_CalcSavegameMAC(Handle serviceHandle, FSPXI_File file, const void* inBuffer, u32 inSize, void* outBuffer, u32 outSize);
/**
* @brief Get size of a file
* @param file File to get the size of.
* @param size Pointer to output size to.
*/
Result FSPXI_GetFileSize(Handle serviceHandle, FSPXI_File file, u64* size);
/**
* @brief Set size of a file
* @param file File to set the size of
* @param size Size to set the file to
*/
Result FSPXI_SetFileSize(Handle serviceHandle, FSPXI_File file, u64 size);
/**
* @brief Close a file
* @param file File to close
*/
Result FSPXI_CloseFile(Handle serviceHandle, FSPXI_File file);
/**
* @brief Reads one or more directory entries.
* @param directory Directory to read from.
* @param entriesRead Pointer to output the number of entries read to.
* @param entryCount Number of entries to read.
* @param entryOut Pointer to output directory entries to.
*/
Result FSPXI_ReadDirectory(Handle serviceHandle, FSPXI_Directory directory, u32* entriesRead, u32 entryCount, FS_DirectoryEntry* entries);
/**
* @brief Close a directory
* @param directory Directory to close.
*/
Result FSPXI_CloseDirectory(Handle serviceHandle, FSPXI_Directory directory);
/**
* @brief Opens an archive.
* @param archive Pointer to output the opened archive to.
* @param id ID of the archive.
* @param path Path of the archive.
*/
Result FSPXI_OpenArchive(Handle serviceHandle, FSPXI_Archive* archive, FS_ArchiveID archiveID, FS_Path path);
/**
* @brief Checks if the archive contains a file at path.
* @param archive Archive to check.
* @param out Pointer to output existence to.
* @param path Path to check for file
*/
Result FSPXI_HasFile(Handle serviceHandle, FSPXI_Archive archive, bool* out, FS_Path path);
/**
* @brief Checks if the archive contains a directory at path.
* @param archive Archive to check.
* @param out Pointer to output existence to.
* @param path Path to check for directory
*/
Result FSPXI_HasDirectory(Handle serviceHandle, FSPXI_Archive archive, bool* out, FS_Path path);
/**
* @brief Commits an archive's save data.
* @param archive Archive to commit.
* @param id Archive action sent by FSUSER_ControlArchive. Must not be 0 or 0x789D
* @remark Unsure why id is sent. This appears to be the default action for FSUSER_ControlArchive, with every action other than 0 and 0x789D being sent to this command.
*/
Result FSPXI_CommitSaveData(Handle serviceHandle, FSPXI_Archive archive, u32 id);
/**
* @brief Close an archive
* @param archive Archive to close.
*/
Result FSPXI_CloseArchive(Handle serviceHandle, FSPXI_Archive archive);
/**
* @brief Unknown 0x17. Appears to be an "is archive handle valid" command?
* @param archive Archive handle to check validity of.
* @param out Pointer to output validity to.
*/
Result FSPXI_Unknown0x17(Handle serviceHandle, FSPXI_Archive archive, bool* out);
/**
* @brief Gets the inserted card type.
* @param out Pointer to output the card type to.
*/
Result FSPXI_GetCardType(Handle serviceHandle, FS_CardType* out);
/**
* @brief Gets the SDMC archive resource information.
* @param out Pointer to output the archive resource information to.
*/
Result FSPXI_GetSdmcArchiveResource(Handle serviceHandle, FS_ArchiveResource* out);
/**
* @brief Gets the NAND archive resource information.
* @param out Pointer to output the archive resource information to.
*/
Result FSPXI_GetNandArchiveResource(Handle serviceHandle, FS_ArchiveResource* out);
/**
* @brief Gets the error code from the SDMC FatFS driver
* @param out Pointer to output the error code to
*/
Result FSPXI_GetSdmcFatFsError(Handle serviceHandle, u32* out);
/**
* @brief Gets whether PXIFS0 detects the SD
* @param out Pointer to output the detection status to
*/
Result FSPXI_IsSdmcDetected(Handle serviceHandle, bool* out);
/**
* @brief Gets whether PXIFS0 can write to the SD
* @param out Pointer to output the writable status to
*/
Result FSPXI_IsSdmcWritable(Handle serviceHandle, bool* out);
/**
* @brief Gets the SDMC CID
* @param out Buffer to output the CID to.
* @param size Size of buffer.
*/
Result FSPXI_GetSdmcCid(Handle serviceHandle, void* out, u32 size);
/**
* @brief Gets the NAND CID
* @param out Buffer to output the CID to.
* @param size Size of buffer.
*/
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, FS_SdMmcSpeedInfo* out);
/**
* @brief Gets the NAND speed info
* @param out Buffer to output the speed info to.
*/
Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, FS_SdMmcSpeedInfo* out);
/**
* @brief Gets the SDMC log
* @param out Buffer to output the log to.
* @param size Size of buffer.
*/
Result FSPXI_GetSdmcLog(Handle serviceHandle, void* out, u32 size);
/**
* @brief Gets the NAND log
* @param out Buffer to output the log to.
* @param size Size of buffer.
*/
Result FSPXI_GetNandLog(Handle serviceHandle, void* out, u32 size);
/// Clears the SDMC log
Result FSPXI_ClearSdmcLog(Handle serviceHandle);
/// Clears the NAND log
Result FSPXI_ClearNandLog(Handle serviceHandle);
/**
* @brief Gets whether a card is inserted.
* @param inserted Pointer to output the insertion status to.
*/
Result FSPXI_CardSlotIsInserted(Handle serviceHandle, bool* inserted);
/**
* @brief Powers on the card slot.
* @param status Pointer to output the power status to.
*/
Result FSPXI_CardSlotPowerOn(Handle serviceHandle, bool* status);
/**
* @brief Powers off the card slot.
* @param status Pointer to output the power status to.
*/
Result FSPXI_CardSlotPowerOff(Handle serviceHandle, bool* status);
/**
* @brief Gets the card's power status.
* @param status Pointer to output the power status to.
*/
Result FSPXI_CardSlotGetCardIFPowerStatus(Handle serviceHandle, bool* status);
/**
* @brief Executes a CARDNOR direct command.
* @param commandId ID of the command.
*/
Result FSPXI_CardNorDirectCommand(Handle serviceHandle, u8 commandId);
/**
* @brief Executes a CARDNOR direct command with an address.
* @param commandId ID of the command.
* @param address Address to provide.
*/
Result FSPXI_CardNorDirectCommandWithAddress(Handle serviceHandle, u8 commandId, u32 address);
/**
* @brief Executes a CARDNOR direct read.
* @param commandId ID of the command.
* @param size Size of the output buffer.
* @param output Output buffer.
*/
Result FSPXI_CardNorDirectRead(Handle serviceHandle, u8 commandId, u32 size, void* output);
/**
* @brief Executes a CARDNOR direct read with an address.
* @param commandId ID of the command.
* @param address Address to provide.
* @param size Size of the output buffer.
* @param output Output buffer.
*/
Result FSPXI_CardNorDirectReadWithAddress(Handle serviceHandle, u8 commandId, u32 address, u32 size, void* output);
/**
* @brief Executes a CARDNOR direct write.
* @param commandId ID of the command.
* @param size Size of the input buffer.
* @param output Input buffer.
* @remark Stubbed in latest firmware, since ?.?.?
*/
Result FSPXI_CardNorDirectWrite(Handle serviceHandle, u8 commandId, u32 size, const void* input);
/**
* @brief Executes a CARDNOR direct write with an address.
* @param commandId ID of the command.
* @param address Address to provide.
* @param size Size of the input buffer.
* @param input Input buffer.
*/
Result FSPXI_CardNorDirectWriteWithAddress(Handle serviceHandle, u8 commandId, u32 address, u32 size, const void* input);
/**
* @brief Executes a CARDNOR 4xIO direct read.
* @param commandId ID of the command.
* @param address Address to provide.
* @param size Size of the output buffer.
* @param output Output buffer.
*/
Result FSPXI_CardNorDirectRead_4xIO(Handle serviceHandle, u8 commandId, u32 address, u32 size, void* output);
/**
* @brief Executes a CARDNOR direct CPU write without verify.
* @param address Address to provide.
* @param size Size of the input buffer.
* @param output Input buffer.
*/
Result FSPXI_CardNorDirectCpuWriteWithoutVerify(Handle serviceHandle, u32 address, u32 size, const void* input);
/**
* @brief Executes a CARDNOR direct sector erase without verify.
* @param address Address to provide.
*/
Result FSPXI_CardNorDirectSectorEraseWithoutVerify(Handle serviceHandle, u32 address);
/**
* @brief Gets an NCCH's product info
* @param info Pointer to output the product info to.
* @param archive Open NCCH content archive
*/
Result FSPXI_GetProductInfo(Handle serviceHandle, FS_ProductInfo* info, FSPXI_Archive archive);
/**
* @brief Sets the CARDSPI baud rate.
* @param baudRate Baud rate to set.
*/
Result FSPXI_SetCardSpiBaudrate(Handle serviceHandle, FS_CardSpiBaudRate baudRate);
/**
* @brief Sets the CARDSPI bus mode.
* @param busMode Bus mode to set.
*/
Result FSPXI_SetCardSpiBusMode(Handle serviceHandle, FS_CardSpiBusMode busMode);
/**
* @brief Sends initialization info to ARM9
* @param unk FS sends *(0x1FF81086)
*/
Result FSPXI_SendInitializeInfoTo9(Handle serviceHandle, u8 unk);
/**
* @brief Creates ext save data.
* @param info Info of the save data.
*/
Result FSPXI_CreateExtSaveData(Handle serviceHandle, FS_ExtSaveDataInfo info);
/**
* @brief Deletes ext save data.
* @param info Info of the save data.
*/
Result FSPXI_DeleteExtSaveData(Handle serviceHandle, FS_ExtSaveDataInfo info);
/**
* @brief Enumerates ext save data.
* @param idsWritten Pointer to output the number of IDs written to.
* @param idsSize Size of the IDs buffer.
* @param mediaType Media type to enumerate over.
* @param idSize Size of each ID element.
* @param shared Whether to enumerate shared ext save data.
* @param ids Pointer to output IDs to.
*/
Result FSPXI_EnumerateExtSaveData(Handle serviceHandle, u32* idsWritten, u32 idsSize, FS_MediaType mediaType, u32 idSize, bool shared, u8* ids);
/**
* @brief Gets a special content's index.
* @param index Pointer to output the index to.
* @param mediaType Media type of the special content.
* @param programId Program ID owning the special content.
* @param type Type of special content.
*/
Result FSPXI_GetSpecialContentIndex(Handle serviceHandle, u16* index, FS_MediaType mediaType, u64 programId, FS_SpecialContentType type);
/**
* @brief Gets the legacy ROM header of a program.
* @param mediaType Media type of the program.
* @param programId ID of the program.
* @param header Pointer to output the legacy ROM header to. (size = 0x3B4)
*/
Result FSPXI_GetLegacyRomHeader(Handle serviceHandle, FS_MediaType mediaType, u64 programId, void* header);
/**
* @brief Gets the legacy banner data of a program.
* @param mediaType Media type of the program.
* @param programId ID of the program.
* @param banner Pointer to output the legacy banner data to. (size = 0x23C0)
* @param unk Unknown. Always 1?
*/
Result FSPXI_GetLegacyBannerData(Handle serviceHandle, FS_MediaType mediaType, u64 programId, void* banner, u8 unk);
/**
* @brief Formats the CARDNOR device.
* @param unk Unknown. Transaction?
*/
Result FSPXI_FormatCardNorDevice(Handle serviceHandle, u32 unk);
/// Deletes the 3DS SDMC root.
Result FSPXI_DeleteSdmcRoot(Handle serviceHandle);
/// Deletes all ext save data on the NAND.
Result FSPXI_DeleteAllExtSaveDataOnNand(Handle serviceHandle);
/// Initializes the CTR file system.
Result FSPXI_InitializeCtrFilesystem(Handle serviceHandle);
/// Creates the FS seed.
Result FSPXI_CreateSeed(Handle serviceHandle);
/**
* @brief Gets the CTR SDMC root path.
* @param out Pointer to output the root path to.
* @param length Length of the output buffer in bytes.
*/
Result FSPXI_GetSdmcCtrRootPath(Handle serviceHandle, u16* out, u32 length);
/**
* @brief Gets an archive's resource information.
* @param archiveResource Pointer to output the archive resource information to.
* @param mediaType System media type to check.
*/
Result FSPXI_GetArchiveResource(Handle serviceHandle, FS_ArchiveResource* archiveResource, FS_SystemMediaType mediaType);
/**
* @brief Exports the integrity verification seed.
* @param seed Pointer to output the seed to.
*/
Result FSPXI_ExportIntegrityVerificationSeed(Handle serviceHandle, FS_IntegrityVerificationSeed* seed);
/**
* @brief Imports an integrity verification seed.
* @param seed Seed to import.
*/
Result FSPXI_ImportIntegrityVerificationSeed(Handle serviceHandle, const FS_IntegrityVerificationSeed* seed);
/**
* @brief Gets the legacy sub banner data of a program.
* @param bannerSize Size of the banner.
* @param mediaType Media type of the program.
* @param programId ID of the program.
* @param header Pointer to output the legacy sub banner data to.
*/
Result FSPXI_GetLegacySubBannerData(Handle serviceHandle, u32 bannerSize, FS_MediaType mediaType, u64 programId, void* banner);
/**
* @brief Generates random bytes. Uses same code as PSPXI_GenerateRandomBytes
* @param buf Buffer to output random bytes to.
* @param size Size of buffer.
*/
Result FSPXI_GenerateRandomBytes(Handle serviceHandle, void* buffer, u32 size);
/**
* @brief Gets the last modified time of a file in an archive.
* @param archive The archive that contains the file.
* @param out The pointer to write the timestamp to.
* @param path The UTF-16 path of the file.
* @param size The size of the path.
*/
Result FSPXI_GetFileLastModified(Handle serviceHandle, FSPXI_Archive archive, u64* out, const u16* path, u32 size);
/**
* @brief Reads from a special file.
* @param bytesRead Pointer to output the number of bytes read to.
* @param fileOffset Offset of the file.
* @param size Size of the buffer.
* @param data Buffer to read to.
*/
Result FSPXI_ReadSpecialFile(Handle serviceHandle, u32* bytesRead, u64 fileOffset, u32 size, void* data);
/**
* @brief Gets the size of a special file.
* @param fileSize Pointer to output the size to.
*/
Result FSPXI_GetSpecialFileSize(Handle serviceHandle, u64* fileSize);
/**
* @brief Initiates a device move as the source device.
* @param context Pointer to output the context to.
*/
Result FSPXI_StartDeviceMoveAsSource(Handle serviceHandle, FS_DeviceMoveContext* context);
/**
* @brief Initiates a device move as the destination device.
* @param context Context to use.
* @param clear Whether to clear the device's data first.
*/
Result FSPXI_StartDeviceMoveAsDestination(Handle serviceHandle, FS_DeviceMoveContext context, bool clear);
/**
* @brief Reads data and stores SHA256 hashes of blocks
* @param file File to read from.
* @param bytesRead Pointer to output the number of read bytes to.
* @param offset Offset to read from.
* @param readBuffer Pointer to store read data in.
* @param readBufferSize Size of readBuffer.
* @param hashtable Pointer to store SHA256 hashes in.
* @param hashtableSize Size of hashtable.
* @param unk Unknown. Always 0x00001000? Possibly block size?
*/
Result FSPXI_ReadFileSHA256(Handle serviceHandle, FSPXI_File file, u32* bytesRead, u64 offset, void* readBuffer, u32 readBufferSize, void* hashtable, u32 hashtableSize, u32 unk);
/**
* @brief Assumedly writes data and stores SHA256 hashes of blocks
* @param file File to write to.
* @param bytesWritten Pointer to output the number of written bytes to.
* @param offset Offset to write to.
* @param writeBuffer Buffer to write from.
* @param writeBufferSize Size of writeBuffer.
* @param hashtable Pointer to store SHA256 hashes in.
* @param hashtableSize Size of hashtable
* @param unk1 Unknown. Might match with ReadFileSHA256's unknown?
* @param unk2 Unknown. Might match with ReadFileSHA256's unknown?
*/
Result FSPXI_WriteFileSHA256(Handle serviceHandle, FSPXI_File file, u32* bytesWritten, u64 offset, const void* writeBuffer, u32 writeBufferSize, void* hashtable, u32 hashtableSize, u32 unk1, u32 unk2);
/**
* @brief Configures CTRCARD latency emulation.
* @param latency Latency to apply.
*/
Result FSPXI_SetCtrCardLatencyParameter(Handle serviceHandle, u64 latency);
/**
* @brief Sets the file system priority.
* @param priority Priority to set.
*/
Result FSPXI_SetPriority(Handle serviceHandle, u32 priority);
/**
* @brief Toggles cleaning up invalid save data.
* @param enable Whether to enable cleaning up invalid save data.
*/
Result FSPXI_SwitchCleanupInvalidSaveData(Handle serviceHandle, bool enable);
/**
* @brief Enumerates system save data.
* @param idsWritten Pointer to output the number of IDs written to.
* @param idsSize Size of the IDs buffer.
* @param ids Pointer to output IDs to.
*/
Result FSPXI_EnumerateSystemSaveData(Handle serviceHandle, u32* idsWritten, u32 idsSize, u32* ids);
/**
* @brief Reads the NAND report.
* @param unk Unknown
* @param buffer Buffer to write the report to.
* @param size Size of buffer
*/
Result FSPXI_ReadNandReport(Handle serviceHandle, void* buffer, u32 size, u32 unk);
/**
* @brief Unknown command 0x56
* @remark Called by FSUSER_ControlArchive with ArchiveAction 0x789D
*/
Result FSPXI_Unknown0x56(Handle serviceHandle, u32 out[4], FS_Archive archive, FS_Path path);

View File

@ -0,0 +1,63 @@
/**
* @file fsReg.h
* @brief Filesystem registry service
*/
#pragma once
#include <3ds/exheader.h>
#include <3ds/services/fs.h>
/// Initializes fs:REG.
Result fsRegInit(void);
/// Exits fs:REG.
void fsRegExit(void);
/**
* @brief Gets the current fs:REG session handle.
* @return The current fs:REG session handle.
*/
Handle *fsRegGetSessionHandle(void);
/**
* @brief Registers a program's storage information.
* @param pid The Process ID of the program.
* @param programHandle The program handle.
* @param programInfo Information about the program.
* @param storageInfo Storage information to register.
*/
Result FSREG_Register(u32 pid, u64 programHandle, const FS_ProgramInfo *programInfo, const ExHeader_Arm11StorageInfo *storageInfo);
/**
* @brief Unregisters a program's storage information.
* @param pid The Process ID of the program.
*/
Result FSREG_Unregister(u32 pid);
/**
* @brief Retrives the exheader information set(s) (SCI+ACI) about a program.
* @param exheaderInfos[out] Pointer to the output exheader information set(s).
* @param maxNumEntries The maximum number of entries.
* @param programHandle The program handle.
*/
Result FSREG_GetProgramInfo(ExHeader_Info *exheaderInfos, u32 maxNumEntries, u64 programHandle);
/**
* @brief Loads a program.
* @param programHandle[out] Pointer to the output the program handle to.
* @param programInfo Information about the program to load.
*/
Result FSREG_LoadProgram(u64 *programHandle, const FS_ProgramInfo *programInfo);
/**
* @brief Unloads a program.
* @param programHandle The program handle.
*/
Result FSREG_UnloadProgram(u64 programHandle);
/**
* @brief Checks if a program has been loaded by fs:REG.
* @param programHandle The program handle.
*/
Result FSREG_CheckHostLoadId(u64 programHandle);

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.
@ -142,11 +184,14 @@ Result GSPGPU_ReleaseRight(void);
* @brief Retrieves display capture info.
* @param captureinfo Pointer to output capture info to.
*/
Result GSPGPU_ImportDisplayCaptureInfo(GSPGPU_CaptureInfo*captureinfo);
Result GSPGPU_ImportDisplayCaptureInfo(GSPGPU_CaptureInfo* captureinfo);
/// Sames the VRAM sys area.
/// Saves the VRAM sys area.
Result GSPGPU_SaveVramSysArea(void);
/// Resets the GPU
Result GSPGPU_ResetGpuCore(void);
/// Restores the VRAM sys area.
Result GSPGPU_RestoreVramSysArea(void);
@ -161,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.
@ -183,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.
@ -193,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.
@ -222,4 +267,4 @@ Result GSPGPU_TriggerCmdReqQueue(void);
* @brief Sets 3D_LEDSTATE to the input state value.
* @param disable False = 3D LED enable, true = 3D LED disable.
*/
Result GSPGPU_SetLedForceOff(bool disable);
Result GSPGPU_SetLedForceOff(bool disable);

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);
@ -68,4 +75,4 @@ Result GSPLCD_SetBrightness(u32 screen, u32 brightness);
* @param screen Screen to set the brightness value of.
* @param brightness Brightness value set.
*/
Result GSPLCD_SetBrightnessRaw(u32 screen, u32 brightness);
Result GSPLCD_SetBrightnessRaw(u32 screen, u32 brightness);

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,6 +91,13 @@ Result hidInit(void);
/// Exits HID.
void hidExit(void);
/**
* @brief Sets the key repeat parameters for @ref hidKeysRepeat.
* @param delay Initial delay.
* @param interval Repeat interval.
*/
void hidSetRepeatParameters(u32 delay, u32 interval);
/// Scans HID for input data.
void hidScanInput(void);
@ -109,7 +116,14 @@ u32 hidKeysHeld(void);
u32 hidKeysDown(void);
/**
* @brief Returns a bitmask of newly released buttons, this frame.
* @brief Returns a bitmask of newly pressed or repeated buttons, this frame.
* Individual buttons can be extracted using binary AND.
* @return 32-bit bitmask of newly pressed or repeated buttons.
*/
u32 hidKeysDownRepeat(void);
/**
* @brief Returns a bitmask of newly released buttons, this frame.
* Individual buttons can be extracted using binary AND.
* @return 32-bit bitmask of newly released buttons.
*/
@ -146,6 +160,14 @@ void hidGyroRead(angularRate* rate);
*/
void hidWaitForEvent(HID_Event id, bool nextEvent);
/**
* @brief Waits for any HID or IRRST event.
* @param nextEvents Whether to discard the current events and wait for the next events.
* @param cancelEvent Optional additional handle to wait on, otherwise 0.
* @param timeout Timeout.
*/
Result hidWaitForAnyEvent(bool nextEvents, Handle cancelEvent, s64 timeout);
/// Compatibility macro for hidScanInput.
#define scanKeys hidScanInput
/// Compatibility macro for hidKeysHeld.

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

@ -14,6 +14,9 @@ extern Handle irrstMemHandle;
/// IRRST's shared memory.
extern vu32* irrstSharedMem;
/// IRRST's state update event
extern Handle irrstEvent;
/// Initializes IRRST.
Result irrstInit(void);

View File

@ -24,15 +24,13 @@ Result LOADER_LoadProcess(Handle* process, u64 programHandle);
/**
* @brief Registers a program (along with its update).
* @param[out] programHandle Pointer to output the program handle to.
* @param titleId The program's title ID.
* @param mediaType The program's media type.
* @param updateTitleId The program update's title ID.
* @param updateMediaType The program update's media type.
* @param programInfo The program info.
* @param programInfo The program update info.
*/
Result LOADER_RegisterProgram(u64* programHandle, u64 titleId, FS_MediaType mediaType, u64 updateTitleId, FS_MediaType updateMediaType);
Result LOADER_RegisterProgram(u64* programHandle, const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate);
/**
* @brief Registers a program (along with its update).
* @brief Unregisters a program (along with its update).
* @param programHandle The handle of the program to unregister.
*/
Result LOADER_UnregisterProgram(u64 programHandle);

View File

@ -4,15 +4,25 @@
*/
#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
LED_RED, ///< Red state of the led
LED_BLUE, ///< Blue state of the led
LED_BLINK_RED, ///< Blinking red state of power led and notification led
} powerLedState;
typedef struct InfoLedPattern
{
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
LED_RED, ///< Red state of the led
LED_BLUE, ///< Blue state of the led
LED_BLINK_RED, ///< Blinking red state of power led and notification led
}powerLedState;
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);
@ -20,6 +30,12 @@ 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

@ -1,68 +0,0 @@
/**
* @file pm.h
* @brief PM (Process Manager) service.
*/
#pragma once
/// Initializes PM.
Result pmInit(void);
/// Exits PM.
void pmExit(void);
/**
* @brief Launches a title.
* @param mediatype Mediatype of the title.
* @param titleid ID of the title.
* @param launch_flags Flags to launch the title with.
*/
Result PM_LaunchTitle(u8 mediatype, u64 titleid, u32 launch_flags);
/**
* @brief Gets launch flags from a title's exheader.
* @param mediatype Mediatype of the title.
* @param titleid ID of the title.
* @param out Pointer to write the launch flags to.
*/
Result PM_GetTitleExheaderFlags(u8 mediatype, u64 titleid, u8* out);
/**
* @brief Sets the current FIRM launch parameters.
* @param size Size of the FIRM launch parameter buffer.
* @param in Buffer to retrieve the launch parameters from.
*/
Result PM_SetFIRMLaunchParams(u32 size, u8* in);
/**
* @brief Gets the current FIRM launch parameters.
* @param size Size of the FIRM launch parameter buffer.
* @param out Buffer to write the launch parameters to.
*/
Result PM_GetFIRMLaunchParams(u32 size, u8* out);
/**
* @brief Sets the current FIRM launch parameters.
* @param firm_titleid_low Low Title ID of the FIRM title to launch.
* @param size Size of the FIRM launch parameter buffer.
* @param in Buffer to retrieve the launch parameters from.
*/
Result PM_LaunchFIRMSetParams(u32 firm_titleid_low, u32 size, u8* in);
/**
* @brief Terminates the current Application
* @param timeout Timeout in nanoseconds
*/
Result PM_TerminateCurrentApplication(u64 timeout);
/**
* @brief Terminates the specified Process
* @param pid Process-ID of the process to terminate
* @param timeout Timeout on nanoseconds
*/
Result PM_TerminateProcess(u8 pid, u64 timeout);
/**
* @brief Unregisters a process
* @param tid TitleID of the process to unregister
*/
Result PM_UnregisterProcess(u64 tid);

View File

@ -0,0 +1,123 @@
/**
* @file pmapp.h
* @brief PM (Process Manager) application service.
*/
#pragma once
#include <3ds/services/fs.h>
#include <3ds/exheader.h>
/// Launch flags for PM launch commands.
enum {
PMLAUNCHFLAG_NORMAL_APPLICATION = BIT(0),
PMLAUNCHFLAG_LOAD_DEPENDENCIES = BIT(1),
PMLAUNCHFLAG_NOTIFY_TERMINATION = BIT(2),
PMLAUNCHFLAG_QUEUE_DEBUG_APPLICATION = BIT(3),
PMLAUNCHFLAG_TERMINATION_NOTIFICATION_MASK = 0xF0,
PMLAUNCHFLAG_FORCE_USE_O3DS_APP_MEM = BIT(8), ///< Forces the usage of the O3DS system mode app memory setting even if N3DS system mode is not "Legacy". Dev4 and Dev5 not supported. N3DS only.
PMLAUNCHFLAG_FORCE_USE_O3DS_MAX_APP_MEM = BIT(9), ///< In conjunction with the above, forces the 96MB app memory setting. N3DS only.
PMLAUNCHFLAG_USE_UPDATE_TITLE = BIT(16),
};
/// Initializes pm:app.
Result pmAppInit(void);
/// Exits pm:app.
void pmAppExit(void);
/**
* @brief Gets the current pm:app session handle.
* @return The current pm:app session handle.
*/
Handle *pmAppGetSessionHandle(void);
/**
* @brief Launches a title.
* @param programInfo Program information of the title.
* @param launchFlags Flags to launch the title with.
*/
Result PMAPP_LaunchTitle(const FS_ProgramInfo *programInfo, u32 launchFlags);
/**
* @brief Launches a title, applying patches.
* @param programInfo Program information of the title.
* @param programInfoUpdate Program information of the update title.
* @param launchFlags Flags to launch the title with.
*/
Result PMAPP_LaunchTitleUpdate(const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags);
/**
* @brief Gets a title's ExHeader Arm11CoreInfo and SystemInfo flags.
* @param[out] outCoreInfo Pointer to write the ExHeader Arm11CoreInfo to.
* @param[out] outSiFlags Pointer to write the ExHeader SystemInfo flags to.
* @param programInfo Program information of the title.
*/
Result PMAPP_GetTitleExheaderFlags(ExHeader_Arm11CoreInfo* outCoreInfo, ExHeader_SystemInfoFlags* outSiFlags, const FS_ProgramInfo *programInfo);
/**
* @brief Sets the current FIRM launch parameters.
* @param size Size of the FIRM launch parameter buffer.
* @param in Buffer to retrieve the launch parameters from.
*/
Result PMAPP_SetFIRMLaunchParams(u32 size, const void* in);
/**
* @brief Gets the current FIRM launch parameters.
* @param size Size of the FIRM launch parameter buffer.
* @param[out] out Buffer to write the launch parameters to.
*/
Result PMAPP_GetFIRMLaunchParams(void *out, u32 size);
/**
* @brief Sets the current FIRM launch parameters.
* @param firmTidLow Low Title ID of the FIRM title to launch.
* @param size Size of the FIRM launch parameter buffer.
* @param in Buffer to retrieve the launch parameters from.
*/
Result PMAPP_LaunchFIRMSetParams(u32 firmTidLow, u32 size, const void* in);
/**
* @brief Terminate most processes, to prepare for a reboot or a shutdown.
* @param timeout Time limit in ns for process termination, after which the remaining processes are killed.
*/
Result PMAPP_PrepareForReboot(s64 timeout);
/**
* @brief Terminates the current Application
* @param timeout Timeout in nanoseconds
*/
Result PMAPP_TerminateCurrentApplication(s64 timeout);
/**
* @brief Terminates the processes having the specified titleId.
* @param titleId Title ID of the processes to terminate
* @param timeout Timeout in nanoseconds
*/
Result PMAPP_TerminateTitle(u64 titleId, s64 timeout);
/**
* @brief Terminates the specified process
* @param pid Process-ID of the process to terminate
* @param timeout Timeout in nanoseconds
*/
Result PMAPP_TerminateProcess(u32 pid, s64 timeout);
/**
* @brief Unregisters a process
* @param tid TitleID of the process to unregister
*/
Result PMAPP_UnregisterProcess(u64 tid);
/**
* @brief Sets the APPLICATION cputime reslimit.
* @param cpuTime Reslimit value.
* @note cpuTime can be no higher than reslimitdesc[0] & 0x7F in exheader (or 80 if the latter is 0).
*/
Result PMAPP_SetAppResourceLimit(s64 cpuTime);
/**
* @brief Gets the APPLICATION cputime reslimit.
* @param[out] cpuTime Pointer to write the reslimit value to.
*/
Result PMAPP_GetAppResourceLimit(s64 *outCpuTime);

View File

@ -0,0 +1,41 @@
/**
* @file pmdbg.h
* @brief PM (Process Manager) debug service.
*/
#pragma once
#include <3ds/services/pmapp.h>
/// Initializes pm:dbg.
Result pmDbgInit(void);
/// Exits pm:dbg.
void pmDbgExit(void);
/**
* @brief Gets the current pm:dbg session handle.
* @return The current pm:dbg session handle.
*/
Handle *pmDbgGetSessionHandle(void);
/**
* @brief Enqueues an application for debug after setting cpuTime to 0, and returns a debug handle to it.
* If another process was enqueued, this just calls @ref RunQueuedProcess instead.
* @param[out] Pointer to output the debug handle to.
* @param programInfo Program information of the title.
* @param launchFlags Flags to launch the title with.
*/
Result PMDBG_LaunchAppDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
/**
* @brief Launches an application for debug after setting cpuTime to 0.
* @param programInfo Program information of the title.
* @param launchFlags Flags to launch the title with.
*/
Result PMDBG_LaunchApp(const FS_ProgramInfo *programInfo, u32 launchFlags);
/**
* @brief Runs the queued process and returns a debug handle to it.
* @param[out] Pointer to output the debug handle to.
*/
Result PMDBG_RunQueuedProcess(Handle *outDebug);

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

@ -4,17 +4,115 @@
*/
#pragma once
#include <3ds/types.h>
/// PDN wake events and MCU interrupts to select, combined with those of other processes
typedef struct PtmWakeEvents {
u32 pdn_wake_events; ///< Written to PDN_WAKE_EVENTS. Don't select bit26 (MCU), PTM will do it automatically.
u32 mcu_interupt_mask; ///< MCU interrupts to check when a MCU wake event happens.
} PtmWakeEvents;
typedef struct {
PtmWakeEvents exit_sleep_events; ///< Wake events for which the system should fully wake up.
PtmWakeEvents continue_sleep_events; ///< Wake events for which the system should return to sleep.
} PtmSleepConfig;
enum {
// Sleep FSM notification IDs
PTMNOTIFID_SLEEP_REQUESTED = 0x101, ///< @ref PTMSYSM_RequestSleep has been called (ack = 3)
PTMNOTIFID_SLEEP_DENIED = 0x102, ///< The sleep request has been denied by @ref PTMSYSM_ReplyToSleepQuery(true) (no ack required).
PTMNOTIFID_SLEEP_ALLOWED = 0x103, ///< The sleep request has been allowed by @ref PTMSYSM_ReplyToSleepQuery(false) (ack = 1).
PTMNOTIFID_GOING_TO_SLEEP = 0x104, ///< All processes not having "RunnableOnSleep" have been paused & the system is about to go to sleep (ack = 0).
PTMNOTIFID_FULLY_WAKING_UP = 0x105, ///< The system has been woken up, and the paused processes are about to be unpaused (ack = 1).
PTMNOTIFID_FULLY_AWAKE = 0x106, ///< The system is fully awake (no ack required).
PTMNOTIFID_HALF_AWAKE = 0x107, ///< The system has been woken up but is about to go to sleep again (ack = 2).
PTMNOTIFID_SHUTDOWN = 0x108, ///< The system is about to power off or reboot.
PTMNOTIFID_BATTERY_VERY_LOW = 0x211, ///< The battery level has reached 5% or below.
PTMNOTIFID_BATTERY_LOW = 0x212, ///< The battery level has reached 10% or below.
};
/// See @ref PTMSYSM_NotifySleepPreparationComplete. Corresponds to the number of potentially remaning notifs. until sleep/wakeup.
static inline s32 ptmSysmGetNotificationAckValue(u32 id)
{
static const s32 values[] = { 3, -1, 1, 0, 0, -1, 2 };
if (id < PTMNOTIFID_SLEEP_REQUESTED || id > PTMNOTIFID_HALF_AWAKE)
return -1;
return values[id - PTMNOTIFID_SLEEP_REQUESTED];
}
/// Initializes ptm:sysm.
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);
/**
* @brief return 1 if it's a New 3DS otherwise, return 0 for Old 3DS.
* @brief Accepts or denies the incoming sleep mode request.
* @param deny Whether or not to deny the sleep request.
* @note If deny = false, this is equivalent to calling @ref PTMSYSM_NotifySleepPreparationComplete(3)
*/
Result PTMSYSM_CheckNew3DS(void);
Result PTMSYSM_ReplyToSleepQuery(bool deny);
/**
* @brief Acknowledges the current sleep notification and advance the internal sleep mode FSM. All subscribers must reply.
* @param ackValue Use @ref ptmSysmGetNotificationAckValue
* @note @ref PTMNOTIFID_SLEEP_DENIED and @ref PTMNOTIFID_FULLY_AWAKE don't require this.
*/
Result PTMSYSM_NotifySleepPreparationComplete(s32 ackValue);
/**
* @brief Sets the wake events (two sets: when to fully wake up and when to return to sleep).
* @param sleepConfig Pointer to the two sets of wake events.
* @note Can only be called just before acknowledging @ref PTMNOTIFID_GOING_TO_SLEEP or @ref PTMNOTIFID_HALF_AWAKE.
*/
Result PTMSYSM_SetWakeEvents(const PtmSleepConfig *sleepConfig);
/**
* @brief Gets the wake reason (only the first applicable wake event is taken into account).
* @param sleepConfig Pointer to the two sets of wake events. Only the relevant set will be filled.
*/
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.
* @param[out] outMsY2k The pointer to write the number of milliseconds since 01/01/2000 to.
*/
Result PTMSYSM_GetRtcTime(s64 *outMsY2k);
/**
* @brief Writes the time and date coming to the RTC, after conversion.
* @param msY2k The number of milliseconds since 01/01/2000.
*/
Result PTMSYSM_SetRtcTime(s64 msY2k);
/**
* @brief Checks whether the system is a New 3DS.
* @param[out] out Pointer to write the New 3DS flag to.
*/
Result PTMSYSM_CheckNew3DS(bool *out);
/**
* @brief Configures the New 3DS' CPU clock speed and L2 cache.
@ -23,13 +121,13 @@ Result PTMSYSM_CheckNew3DS(void);
Result PTMSYSM_ConfigureNew3DSCPU(u8 value);
/**
* @brief Trigger a hardware system shutdown via the MCU
* @param timeout: timeout passed to PMApp:TerminateNonEssential.
* @brief Trigger a hardware system shutdown via the MCU.
* @param timeout: timeout passed to PMApp:ShutdownAsync (PrepareForReboot).
*/
Result PTMSYSM_ShutdownAsync(u64 timeout);
/**
* @brief Trigger a hardware system reboot via the MCU.
* @param timeout: timeout passed to PMApp:TerminateNonEssential.
* @param timeout: timeout passed to PMApp:ShutdownAsync (PrepareForReboot).
*/
Result PTMSYSM_RebootAsync(u64 timeout);

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

@ -0,0 +1,42 @@
/**
* @file pxipm.h
* @brief Process Manager PXI service
*/
#pragma once
#include <3ds/exheader.h>
#include <3ds/services/fs.h>
/// Initializes PxiPM.
Result pxiPmInit(void);
/// Exits PxiPM.
void pxiPmExit(void);
/**
* @brief Gets the current PxiPM session handle.
* @return The current PxiPM session handle.
*/
Handle *pxiPmGetSessionHandle(void);
/**
* @brief Retrives the exheader information set(s) (SCI+ACI) about a program.
* @param exheaderInfos[out] Pointer to the output exheader information set.
* @param programHandle The program handle.
*/
Result PXIPM_GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle);
/**
* @brief Loads a program and registers it to Process9.
* @param programHandle[out] Pointer to the output the program handle to.
* @param programInfo Information about the program to load.
* @param updateInfo Information about the program update to load.
*/
Result PXIPM_RegisterProgram(u64 *programHandle, const FS_ProgramInfo *programInfo, const FS_ProgramInfo *updateInfo);
/**
* @brief Unloads a program and unregisters it from Process9.
* @param programHandle The program handle.
*/
Result PXIPM_UnregisterProgram(u64 programHandle);

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

@ -57,20 +57,31 @@ typedef enum {
MEMPERM_READ = 1, ///< Readable
MEMPERM_WRITE = 2, ///< Writable
MEMPERM_EXECUTE = 4, ///< Executable
MEMPERM_READWRITE = MEMPERM_READ | MEMPERM_WRITE, ///< Readable and writable
MEMPERM_READEXECUTE = MEMPERM_READ | MEMPERM_EXECUTE, ///< Readable and executable
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.
u32 size; ///< Size.
u32 perm; ///< Memory permissions. See @ref MemPerm
u32 state; ///< Memory state. See @ref MemState
u32 base_addr; ///< Base address.
u32 size; ///< Size.
u32 perm; ///< Memory permissions. See @ref MemPerm
u32 state; ///< Memory state. See @ref MemState
} MemInfo;
/// Memory page information.
typedef struct {
u32 flags; ///< Page flags.
u32 flags; ///< Page flags.
} PageInfo;
/// Arbitration modes.
@ -123,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.
@ -336,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
@ -348,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
@ -366,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)
{
@ -377,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)
{
@ -386,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
///@{
/**
@ -476,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.
@ -518,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);
///@}
@ -554,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.
@ -611,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
* @param[out] out Pointer to output the code set 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
* @brief Sets up virtual address space for a new process.
* @param[out] out Pointer to output the codeset handle to.
* @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.
@ -913,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.
@ -962,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
@ -1046,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.
@ -1058,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
///@{
/**
@ -1088,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,9 +33,9 @@ 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.
* 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 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.
* @return The libctru thread handle on success, NULL on failure.
*
@ -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.
@ -105,7 +105,7 @@ void threadExit(int rc) __attribute__((noreturn));
*
* To have CPU exceptions reported through this mechanism, it is normally necessary that UNITINFO is set to a non-zero value when Kernel11 starts,
* and this mechanism is also controlled by @ref svcKernelSetState type 6, see 3dbrew.
*
*
* VFP exceptions are always reported this way even if the process is being debugged using the debug SVCs.
*
* The current thread need not be 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

@ -4,12 +4,6 @@
*/
#pragma once
#ifdef _3DS
#include <3ds/synchronization.h>
#else
#include <pthread.h>
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
@ -27,23 +21,16 @@ typedef int (*rbtree_node_comparator_t)(const rbtree_node_t *lhs,
/// An rbtree node.
struct rbtree_node
{
uint32_t prefix[32]; ///< Guard prefix.
char nodeTag[4]; ///< Node tag.
uintptr_t parent_color; ///< Parent color.
rbtree_node_t *child[2]; ///< Node children.
rbtree_t *tree; ///< Owning tree.
uint32_t *suffix[32]; ///< Guard suffix.
uintptr_t parent_color; ///< Parent color.
rbtree_node_t *child[2]; ///< Node children.
};
/// An rbtree.
struct rbtree
{
char treeTag[4]; ///< Tree tag.
rbtree_node_t *root; ///< Root node.
rbtree_node_comparator_t comparator; ///< Node comparator.
size_t size; ///< Size.
LightLock lock; ///< Tree mutex.
bool busy; ///< Busy flag.
};
#ifdef __cplusplus

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

@ -7,13 +7,11 @@ extern "C"
#include "mem_pool.h"
#include "addrmap.h"
#include "lock.h"
extern u32 __ctru_linear_heap;
extern u32 __ctru_linear_heap_size;
static MemPool sLinearPool;
static LightLock sLock = 1;
static bool linearInit()
{
@ -29,22 +27,12 @@ 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
LockGuard guard(sLock);
if (!sLinearPool.Ready() && !linearInit())
return nullptr;
@ -59,9 +47,7 @@ void* linearMemAlign(size_t size, size_t alignment)
sLinearPool.Deallocate(chunk);
return nullptr;
}
if (rbtree_insert(&sAddrMap, &node->node) != &node->node)
svcBreak(USERBREAK_PANIC);
if (rbtree_insert(&sAddrMap, &node->node));
return chunk.addr;
}
@ -78,14 +64,12 @@ void* linearRealloc(void* mem, size_t size)
size_t linearGetSize(void* mem)
{
LockGuard guard(sLock);
auto node = getNode(mem);
return node ? node->chunk.size : 0;
}
void linearFree(void* mem)
{
LockGuard guard(sLock);
auto node = getNode(mem);
if (!node) return;
@ -98,6 +82,5 @@ void linearFree(void* mem)
u32 linearSpaceFree()
{
LockGuard guard(sLock);
return sLinearPool.GetFreeSpace();
}

View File

@ -1,23 +0,0 @@
#pragma once
extern "C"
{
#include <3ds/synchronization.h>
}
class LockGuard
{
public:
~LockGuard()
{
LightLock_Unlock(&lock);
}
LockGuard(LightLock &lock) : lock(lock)
{
LightLock_Lock(&lock);
}
private:
LightLock &lock;
};

View File

@ -0,0 +1,59 @@
#include <3ds/allocator/mappable.h>
#include <3ds/svc.h>
#include <3ds/result.h>
static u32 minAddr, maxAddr, currentAddr;
void mappableInit(u32 addrMin, u32 addrMax)
{
minAddr = addrMin;
maxAddr = addrMax;
currentAddr = minAddr;
}
static u32 mappableFindAddressWithin(u32 start, u32 end, u32 size)
{
MemInfo info;
PageInfo pgInfo;
u32 addr = start;
while (addr >= start && (addr + size) < end && (addr + size) >= addr)
{
if (R_FAILED(svcQueryMemory(&info, &pgInfo, addr)))
return 0;
if (info.state == MEMSTATE_FREE)
{
u32 sz = info.size - (addr - info.base_addr); // a free block might cover all the memory, etc.
if (sz >= size)
return addr;
}
addr = info.base_addr + info.size;
}
return 0;
}
void* mappableAlloc(size_t size)
{
// Round up, can only allocate in page units
size = (size + 0xFFF) & ~0xFFF;
u32 addr = mappableFindAddressWithin(currentAddr, maxAddr, size);
if (addr == 0)
{
// Need to rollover (maybe)
addr = mappableFindAddressWithin(minAddr, currentAddr, size);
if (addr == 0)
return NULL;
}
currentAddr = addr + size >= maxAddr ? minAddr : addr + size;
return (void *)addr;
}
void mappableFree(void* mem)
{
(void)mem;
}

View File

@ -1,73 +0,0 @@
extern "C"
{
#include <3ds/types.h>
#include <3ds/allocator/mappable.h>
#include <3ds/util/rbtree.h>
}
#include "mem_pool.h"
#include "addrmap.h"
#include "lock.h"
static MemPool sMappablePool;
static LightLock sLock = 1;
static bool mappableInit()
{
auto blk = MemBlock::Create((u8*)0x10000000, 0x04000000);
if (blk)
{
sMappablePool.AddBlock(blk);
rbtree_init(&sAddrMap, addrMapNodeComparator);
return true;
}
return false;
}
void* mappableAlloc(size_t size)
{
// Initialize the pool if it is not ready
LockGuard guard(sLock);
if (!sMappablePool.Ready() && !mappableInit())
return nullptr;
// Allocate the chunk
MemChunk chunk;
if (!sMappablePool.Allocate(chunk, size, 12))
return nullptr;
auto node = newNode(chunk);
if (!node)
{
sMappablePool.Deallocate(chunk);
return nullptr;
}
if (rbtree_insert(&sAddrMap, &node->node));
return chunk.addr;
}
size_t mappableGetSize(void* mem)
{
LockGuard guard(sLock);
auto node = getNode(mem);
return node ? node->chunk.size : 0;
}
void mappableFree(void* mem)
{
LockGuard guard(sLock);
auto node = getNode(mem);
if (!node) return;
// Free the chunk
sMappablePool.Deallocate(node->chunk);
// Free the node
delNode(node);
}
u32 mappableSpaceFree()
{
LockGuard guard(sLock);
return sMappablePool.GetFreeSpace();
}

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,70 +1,115 @@
extern "C"
{
#include <3ds/types.h>
#include <3ds/os.h>
#include <3ds/allocator/vram.h>
#include <3ds/util/rbtree.h>
}
#include "mem_pool.h"
#include "addrmap.h"
#include "lock.h"
static MemPool sVramPool;
static LightLock sLock = 1;
static MemPool sVramPoolA, sVramPoolB;
static bool vramInit()
{
auto blk = MemBlock::Create((u8*)0x1F000000, 0x00600000);
if (blk)
{
sVramPool.AddBlock(blk);
rbtree_init(&sAddrMap, addrMapNodeComparator);
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)
{
free(blkA);
return false;
}
return false;
sVramPoolA.AddBlock(blkA);
sVramPoolB.AddBlock(blkB);
rbtree_init(&sAddrMap, addrMapNodeComparator);
return true;
}
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;
return vramMemAlignAt(size, alignment, VRAM_ALLOC_ANY);
}
// Convert alignment to shift amount
int shift;
for (shift = 4; shift < 32; shift ++)
{
if ((1U<<shift) == alignment)
break;
}
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
LockGuard guard(sLock);
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
@ -73,19 +118,17 @@ void* vramRealloc(void* mem, size_t size)
size_t vramGetSize(void* mem)
{
LockGuard guard(sLock);
auto node = getNode(mem);
return node ? node->chunk.size : 0;
}
void vramFree(void* mem)
{
LockGuard guard(sLock);
auto node = getNode(mem);
if (!node) return;
// Free the chunk
sVramPool.Deallocate(node->chunk);
vramPoolForAddr(mem)->Deallocate(node->chunk);
// Free the node
delNode(node);
@ -93,6 +136,5 @@ void vramFree(void* mem)
u32 vramSpaceFree()
{
LockGuard guard(sLock);
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;
@ -40,7 +39,7 @@ static void errorConvertToUTF16(u16* out, const char* in, size_t max)
return;
}
out[units] = 0;
out[units] = 0;
}
void errorText(errorConf *err, const char* text)
@ -49,6 +48,6 @@ void errorText(errorConf *err, const char* text)
}
void errorDisp(errorConf* err)
{
{
aptLaunchLibraryApplet(APPID_ERROR, err, sizeof(*err), 0);
}

View File

@ -1,12 +1,24 @@
#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/services/apt.h>
#include <3ds/util/utf.h>
#include <3ds/applets/miiselector.h>
#include <string.h> // for memcpy
Result miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn *returnbuf)
void miiSelectorInit(MiiSelectorConf *conf)
{
memset(conf, 0, sizeof(*conf));
for (int i = 0; i < MIISELECTOR_GUESTMII_SLOTS; i ++)
conf->mii_guest_whitelist[i] = 1;
for (int i = 0; i < MIISELECTOR_USERMII_SLOTS; i ++)
conf->mii_whitelist[i] = 1;
}
void miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn *returnbuf)
{
union {
MiiSelectorConf config;
@ -16,34 +28,146 @@ 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));
}
static void miiSelectorConvertToUTF8(char* out, const u16* in, int max)
{
if (!in || !*in)
{
out[0] = 0;
return;
}
return ret;
ssize_t units = utf16_to_utf8((uint8_t*)out, in, max);
if (units < 0)
{
out[0] = 0;
return;
}
out[units] = 0;
}
static void miiSelectorConvertToUTF16(u16* out, const char* in, int max)
{
if (!in || !*in)
{
out[0] = 0;
return;
}
ssize_t units = utf8_to_utf16(out, (const uint8_t*)in, max);
if (units < 0)
{
out[0] = 0;
return;
}
out[units] = 0;
}
void miiSelectorSetTitle(MiiSelectorConf *conf, const char* text)
{
miiSelectorConvertToUTF16(conf->title, text, MIISELECTOR_TITLE_LEN);
}
void miiSelectorSetOptions(MiiSelectorConf *conf, u32 options)
{
static const u8 miiSelectorOptions[] =
{
offsetof(MiiSelectorConf, enable_cancel_button),
offsetof(MiiSelectorConf, enable_selecting_guests),
offsetof(MiiSelectorConf, show_on_top_screen),
offsetof(MiiSelectorConf, show_guest_page),
};
for (int i = 0; i < sizeof(miiSelectorOptions); i ++)
*((u8*)conf + miiSelectorOptions[i]) = (options & BIT(i)) ? 1 : 0;
}
void miiSelectorWhitelistGuestMii(MiiSelectorConf *conf, u32 index)
{
if (index < MIISELECTOR_GUESTMII_SLOTS)
conf->mii_guest_whitelist[index] = 1;
else if (index == MIISELECTOR_GUESTMII_SLOTS)
for (int i = 0; i < MIISELECTOR_GUESTMII_SLOTS; i ++)
conf->mii_guest_whitelist[i] = 1;
}
void miiSelectorBlacklistGuestMii(MiiSelectorConf *conf, u32 index)
{
if (index < MIISELECTOR_GUESTMII_SLOTS)
conf->mii_guest_whitelist[index] = 0;
else if (index == MIISELECTOR_GUESTMII_SLOTS)
for (int i = 0; i < MIISELECTOR_GUESTMII_SLOTS; i ++)
conf->mii_guest_whitelist[i] = 0;
}
void miiSelectorWhitelistUserMii(MiiSelectorConf *conf, u32 index)
{
if (index < MIISELECTOR_USERMII_SLOTS)
conf->mii_whitelist[index] = 1;
else if (index == MIISELECTOR_USERMII_SLOTS)
for (int i = 0; i < MIISELECTOR_USERMII_SLOTS; i ++)
conf->mii_whitelist[i] = 1;
}
void miiSelectorBlacklistUserMii(MiiSelectorConf *conf, u32 index)
{
if (index < MIISELECTOR_USERMII_SLOTS)
conf->mii_whitelist[index] = 0;
else if (index == MIISELECTOR_USERMII_SLOTS)
for (int i = 0; i < MIISELECTOR_USERMII_SLOTS; i ++)
conf->mii_whitelist[i] = 0;
}
void miiSelectorReturnGetName(const MiiSelectorReturn *returnbuf, char* out, size_t max_size)
{
if (!out)
return;
if (returnbuf->guest_mii_was_selected)
miiSelectorConvertToUTF8(out, returnbuf->guest_mii_name, max_size);
else
{
u16 temp[10];
memcpy(temp, returnbuf->mii.mii_name, sizeof(temp));
miiSelectorConvertToUTF8(out, temp, max_size);
}
}
void miiSelectorReturnGetAuthor(const MiiSelectorReturn *returnbuf, char* out, size_t max_size)
{
if (!out)
return;
u16 temp[10];
memcpy(temp, returnbuf->mii.author_name, sizeof(temp));
miiSelectorConvertToUTF8(out, temp, max_size);
}
static u16 crc16_ccitt(void const *buf, size_t len, uint32_t starting_val)
{
if(buf == NULL) {
if (!buf)
return -1;
}
u8 const *cbuf = buf;
u32 crc = starting_val;
static const u16 POLY = 0x1021;
for(size_t i = 0; i < len; i++) {
for(int bit = 7; bit >= 0; bit--) {
for (size_t i = 0; i < len; i++)
{
for (int bit = 7; bit >= 0; bit--)
crc = ((crc << 1) | ((cbuf[i] >> bit) & 0x1)) ^ (crc & 0x8000 ? POLY : 0);
}
}
for(int _ = 0; _ < 16; _++) {
for (int _ = 0; _ < 16; _++)
crc = (crc << 1) ^ (crc & 0x8000 ? POLY : 0);
}
return (u16)(crc & 0xffff);
}

View File

@ -238,39 +238,35 @@ 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)
switch (swkbd->result)
{
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:
case SWKBD_D2_CLICK0:
button = SWKBD_BUTTON_LEFT;
break;
case SWKBD_D2_CLICK1:
button = SWKBD_BUTTON_MIDDLE;
break;
case SWKBD_D0_CLICK:
case SWKBD_D1_CLICK1:
case SWKBD_D2_CLICK2:
button = SWKBD_BUTTON_RIGHT;
break;
default:
break;
}
case SWKBD_D1_CLICK0:
case SWKBD_D2_CLICK0:
button = SWKBD_BUTTON_LEFT;
break;
case SWKBD_D2_CLICK1:
button = SWKBD_BUTTON_MIDDLE;
break;
case SWKBD_D0_CLICK:
case SWKBD_D1_CLICK1:
case SWKBD_D2_CLICK2:
button = SWKBD_BUTTON_RIGHT;
break;
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;
}

1551
libctru/source/archive_dev.c Normal file

File diff suppressed because it is too large Load Diff

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++;
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) {
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;
//---------------------------------------
// 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;
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;
consolePosition(escapeSeq.hasArg[1] ? escapeSeq.absolute.x : 1, escapeSeq.hasArg[0] ? escapeSeq.absolute.y : 1);
escapeSeq.state = ESC_NONE;
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;
currentConsole->prevCursorX = currentConsole->cursorX ;
currentConsole->prevCursorY = currentConsole->cursorY ;
escapeSeq.state = ESC_NONE;
break;
//---------------------------------------
// Load cursor position
//---------------------------------------
case 'u':
if(escapelen == 2) {
currentConsole->cursorX = currentConsole->prevCursorX ;
currentConsole->cursorY = currentConsole->prevCursorY ;
}
escaping = false;
currentConsole->cursorX = currentConsole->prevCursorX ;
currentConsole->cursorY = currentConsole->prevCursorY ;
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;
}
} while (escaping);
continue;
}
consolePrintChar(chr);
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;
}
}
}
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,18 +753,18 @@ void consoleDebugInit(debugDevice device){
int buffertype = _IONBF;
switch(device) {
case debugDevice_SVC:
devoptab_list[STD_ERR] = &dotab_svc;
buffertype = _IOLBF;
break;
case debugDevice_CONSOLE:
devoptab_list[STD_ERR] = &dotab_stdout;
break;
case debugDevice_NULL:
devoptab_list[STD_ERR] = &dotab_null;
break;
switch(device)
{
case debugDevice_SVC:
devoptab_list[STD_ERR] = &dotab_svc;
buffertype = _IOLBF;
break;
case debugDevice_CONSOLE:
devoptab_list[STD_ERR] = &dotab_stdout;
break;
case debugDevice_NULL:
devoptab_list[STD_ERR] = &dotab_null;
break;
}
setvbuf(stderr, NULL , buffertype, 0);
@ -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_COLOR_BOLD) {
writingColor += 8;
} else if (currentConsole->flags & CONSOLE_COLOR_FAINT) {
writingColor += 16;
if (!(currentConsole->flags & CONSOLE_FG_CUSTOM)) {
if (currentConsole->flags & CONSOLE_COLOR_BOLD) {
fg = colorTable[fg + 8];
} else if (currentConsole->flags & CONSOLE_COLOR_FAINT) {
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

@ -1,81 +0,0 @@
#include "crc32c.h"
static const uint32_t crc32cTable[] =
{
0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
};
uint32_t crc32c(const void *buffer, size_t size, uint32_t crc)
{
const uint8_t *ptr = (const uint8_t*)buffer;
crc ^= ~(uint32_t)0;
for(size_t i = 0; i < size; ++i)
crc = crc32cTable[(crc ^ ptr[i]) & 0xFF] ^ (crc >> 8);
return ~crc;
}

View File

@ -1,6 +0,0 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
uint32_t crc32c(const void *buffer, size_t size, uint32_t crc);

View File

@ -37,7 +37,7 @@ static int __name_cmp(const char* a, const char* b) {
return 0;
}
Handle envGetHandle(const char* name) {
Handle __attribute__((weak)) envGetHandle(const char* name) {
if(__service_ptr == NULL)
return 0;
@ -52,7 +52,7 @@ Handle envGetHandle(const char* name) {
return 0;
}
void envDestroyHandles(void) {
void __attribute__((weak)) envDestroyHandles(void) {
if(__service_ptr == NULL)
return;

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;
@ -125,7 +173,7 @@ void ERRF_ExceptionHandler(ERRF_ExceptionInfo* excep, CpuRegisters* regs)
error.pcAddr = regs->pc;
getCommonErrorData(&error, 0);
error.data.exception_data.excep = *excep;
error.data.exception_data.regs = *regs;
error.data.exception_data.regs = *regs;
ret = ERRF_Throw(&error);

View File

@ -8,7 +8,6 @@
CFNT_s* g_sharedFont;
static u32 sharedFontAddr;
static int charPerSheet;
Result fontEnsureMapped(void)
{
@ -26,17 +25,33 @@ Result fontEnsureMapped(void)
return res;
g_sharedFont = (CFNT_s*)(sharedFontAddr+0x80);
charPerSheet = g_sharedFont->finf.tglp->nRows * g_sharedFont->finf.tglp->nLines;
return 0;
}
int fontGlyphIndexFromCodePoint(u32 codePoint)
void fontFixPointers(CFNT_s* font)
{
int ret = g_sharedFont->finf.alterCharIndex;
font->finf.tglp = (TGLP_s*)((u32)(font->finf.tglp) + (u32) font);
font->finf.tglp->sheetData = (u8*)((u32)(font->finf.tglp->sheetData) + (u32) font);
font->finf.cmap = (CMAP_s*)((u32)(font->finf.cmap) + (u32) font);
for (CMAP_s* cmap = font->finf.cmap; cmap->next; cmap = cmap->next)
cmap->next = (CMAP_s*)((u32)(cmap->next) + (u32) font);
font->finf.cwdh = (CWDH_s*)((u32)(font->finf.cwdh) + (u32) font);
for (CWDH_s* cwdh = font->finf.cwdh; cwdh->next; cwdh = cwdh->next)
cwdh->next = (CWDH_s*)((u32)(cwdh->next) + (u32) font);
}
int fontGlyphIndexFromCodePoint(CFNT_s* font, u32 codePoint)
{
if (!font)
font = g_sharedFont;
if (!font)
return -1;
int ret = 0xFFFF;
if (codePoint < 0x10000)
{
CMAP_s* cmap;
for (cmap = g_sharedFont->finf.cmap; cmap; cmap = cmap->next)
for (CMAP_s* cmap = font->finf.cmap; cmap; cmap = cmap->next)
{
if (codePoint < cmap->codeBegin || codePoint > cmap->codeEnd)
continue;
@ -64,29 +79,44 @@ int fontGlyphIndexFromCodePoint(u32 codePoint)
}
}
}
if (ret == 0xFFFF) // Bogus CMAP entry. Probably exist to save space by using TABLE mappings?
{
if (font->finf.alterCharIndex == 0xFFFF)
return -1;
else
return font->finf.alterCharIndex;
}
return ret;
}
charWidthInfo_s* fontGetCharWidthInfo(int glyphIndex)
charWidthInfo_s* fontGetCharWidthInfo(CFNT_s* font, int glyphIndex)
{
if (!font)
font = g_sharedFont;
if (!font)
return NULL;
charWidthInfo_s* info = NULL;
CWDH_s* cwdh;
for (cwdh = g_sharedFont->finf.cwdh; cwdh && !info; cwdh = cwdh->next)
for (CWDH_s* cwdh = font->finf.cwdh; cwdh && !info; cwdh = cwdh->next)
{
if (glyphIndex < cwdh->startIndex || glyphIndex > cwdh->endIndex)
continue;
info = &cwdh->widths[glyphIndex - cwdh->startIndex];
}
if (!info)
info = &g_sharedFont->finf.defaultWidth;
info = &font->finf.defaultWidth;
return info;
}
void fontCalcGlyphPos(fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY)
void fontCalcGlyphPos(fontGlyphPos_s* out, CFNT_s* font, int glyphIndex, u32 flags, float scaleX, float scaleY)
{
FINF_s* finf = &g_sharedFont->finf;
if (!font)
font = g_sharedFont;
if (!font)
return;
FINF_s* finf = &font->finf;
TGLP_s* tglp = finf->tglp;
charWidthInfo_s* cwi = fontGetCharWidthInfo(glyphIndex);
charWidthInfo_s* cwi = fontGetCharWidthInfo(font, glyphIndex);
int charPerSheet = font->finf.tglp->nRows * font->finf.tglp->nLines;
int sheetId = glyphIndex / charPerSheet;
int glInSheet = glyphIndex % charPerSheet;

403
libctru/source/gdbhio.c Normal file
View File

@ -0,0 +1,403 @@
#include <3ds/gdbhio.h>
#include <3ds/svc.h>
#include <3ds/result.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#define GDBHIO_O_RDONLY 0x0
#define GDBHIO_O_WRONLY 0x1
#define GDBHIO_O_RDWR 0x2
#define GDBHIO_O_ACCMODE 0x3
#define GDBHIO_O_APPEND 0x8
#define GDBHIO_O_CREAT 0x200
#define GDBHIO_O_TRUNC 0x400
#define GDBHIO_O_EXCL 0x800
#define GDBHIO_O_SUPPORTED (GDBHIO_O_RDONLY | GDBHIO_O_WRONLY| \
GDBHIO_O_RDWR | GDBHIO_O_APPEND| \
GDBHIO_O_CREAT | GDBHIO_O_TRUNC| \
GDBHIO_O_EXCL)
#define GDBHIO_S_IFREG 0100000
#define GDBHIO_S_IFDIR 040000
#define GDBHIO_S_IFCHR 020000
#define GDBHIO_S_IRUSR 0400
#define GDBHIO_S_IWUSR 0200
#define GDBHIO_S_IXUSR 0100
#define GDBHIO_S_IRWXU (GDBHIO_S_IRUSR | GDBHIO_S_IWUSR | GDBHIO_S_IXUSR)
#define GDBHIO_S_IRGRP 040
#define GDBHIO_S_IWGRP 020
#define GDBHIO_S_IXGRP 010
#define GDBHIO_S_IRWXG (GDBHIO_S_IRGRP | GDBHIO_S_IWGRP | GDBHIO_S_IXGRP)
#define GDBHIO_S_IROTH 04
#define GDBHIO_S_IWOTH 02
#define GDBHIO_S_IXOTH 01
#define GDBHIO_S_IRWXO (GDBHIO_S_IROTH | GDBHIO_S_IWOTH | GDBHIO_S_IXOTH)
#define GDBHIO_S_SUPPORTED (GDBHIO_S_IFREG|GDBHIO_S_IFDIR| \
GDBHIO_S_IRWXU|GDBHIO_S_IRWXG| \
GDBHIO_S_IRWXO)
#define GDBHIO_SEEK_SET 0
#define GDBHIO_SEEK_CUR 1
#define GDBHIO_SEEK_END 2
#define GDBHIO_EPERM 1
#define GDBHIO_ENOENT 2
#define GDBHIO_EINTR 4
#define GDBHIO_EIO 5
#define GDBHIO_EBADF 9
#define GDBHIO_EACCES 13
#define GDBHIO_EFAULT 14
#define GDBHIO_EBUSY 16
#define GDBHIO_EEXIST 17
#define GDBHIO_ENODEV 19
#define GDBHIO_ENOTDIR 20
#define GDBHIO_EISDIR 21
#define GDBHIO_EINVAL 22
#define GDBHIO_ENFILE 23
#define GDBHIO_EMFILE 24
#define GDBHIO_EFBIG 27
#define GDBHIO_ENOSPC 28
#define GDBHIO_ESPIPE 29
#define GDBHIO_EROFS 30
#define GDBHIO_ENOSYS 88
#define GDBHIO_ENAMETOOLONG 91
#define GDBHIO_EUNKNOWN 9999
typedef struct PackedGdbHioRequest
{
char magic[4]; // "GDB\x00"
u32 version;
// Request
char functionName[16+1];
char paramFormat[8+1];
u64 parameters[8];
size_t stringLengths[8];
// Return
s64 retval;
int gdbErrno;
bool ctrlC;
} PackedGdbHioRequest;
static __thread bool g_gdbHioWasInterruptedByCtrlC = false;
bool gdbHioWasInterruptedByCtrlC(void)
{
return g_gdbHioWasInterruptedByCtrlC;
}
static int _gdbHioImportErrno(int errnum)
{
switch (errnum) {
case GDBHIO_EPERM: return EPERM;
case GDBHIO_ENOENT: return ENOENT;
case GDBHIO_EINTR: return EINTR;
case GDBHIO_EIO: return EIO;
case GDBHIO_EBADF: return EBADF;
case GDBHIO_EACCES: return EACCES;
case GDBHIO_EFAULT: return EFAULT;
case GDBHIO_EBUSY: return EBUSY;
case GDBHIO_EEXIST: return EEXIST;
case GDBHIO_ENODEV: return ENODEV;
case GDBHIO_ENOTDIR: return ENOTDIR;
case GDBHIO_EISDIR: return EISDIR;
case GDBHIO_EINVAL: return EINVAL;
case GDBHIO_ENFILE: return ENFILE;
case GDBHIO_EMFILE: return EMFILE;
case GDBHIO_EFBIG: return EFBIG;
case GDBHIO_ENOSPC: return ENOSPC;
case GDBHIO_ESPIPE: return ESPIPE;
case GDBHIO_EROFS: return EROFS;
case GDBHIO_ENOSYS: return ENOSYS;
case GDBHIO_ENAMETOOLONG: return ENAMETOOLONG;
default: return EPIPE;
}
}
static int _gdbHioExportOpenFlags(int flags)
{
int outflags = 0;
if (flags & O_CREAT) outflags |= GDBHIO_O_CREAT;
if (flags & O_EXCL) outflags |= GDBHIO_O_EXCL;
if (flags & O_TRUNC) outflags |= GDBHIO_O_TRUNC;
if (flags & O_APPEND) outflags |= GDBHIO_O_APPEND;
switch (flags & O_ACCMODE) {
case O_RDONLY:
outflags |= GDBHIO_O_RDONLY;
break;
case O_WRONLY:
outflags |= GDBHIO_O_WRONLY;
break;
case O_RDWR:
outflags |= GDBHIO_O_RDWR;
break;
default:
break;
}
// Note: O_BINARY is implicit if the host supports it
return outflags;
}
typedef s32 gdbhio_mode_t;
static mode_t _gdbHioImportFileMode(gdbhio_mode_t gdbMode)
{
mode_t mode = 0;
if (mode & ~GDBHIO_S_SUPPORTED) return -1;
if (gdbMode & GDBHIO_S_IFREG) mode |= S_IFREG;
if (gdbMode & GDBHIO_S_IFDIR) mode |= S_IFDIR;
if (gdbMode & GDBHIO_S_IFCHR) mode |= S_IFCHR;
if (gdbMode & GDBHIO_S_IRUSR) mode |= S_IRUSR;
if (gdbMode & GDBHIO_S_IWUSR) mode |= S_IWUSR;
if (gdbMode & GDBHIO_S_IXUSR) mode |= S_IXUSR;
if (gdbMode & GDBHIO_S_IRGRP) mode |= S_IRGRP;
if (gdbMode & GDBHIO_S_IWGRP) mode |= S_IWGRP;
if (gdbMode & GDBHIO_S_IXGRP) mode |= S_IXGRP;
if (gdbMode & GDBHIO_S_IROTH) mode |= S_IROTH;
if (gdbMode & GDBHIO_S_IWOTH) mode |= S_IWOTH;
if (gdbMode & GDBHIO_S_IXOTH) mode |= S_IXOTH;
return mode;
}
static int _gdbHioExportFileMode(mode_t mode)
{
gdbhio_mode_t gdbMode = 0;
if (mode & ~GDBHIO_S_SUPPORTED) return -1;
if (mode & S_IFREG) gdbMode |= GDBHIO_S_IFREG;
if (mode & S_IFDIR) gdbMode |= GDBHIO_S_IFDIR;
if (mode & S_IFCHR) gdbMode |= GDBHIO_S_IFCHR;
if (mode & S_IRUSR) gdbMode |= GDBHIO_S_IRUSR;
if (mode & S_IWUSR) gdbMode |= GDBHIO_S_IWUSR;
if (mode & S_IXUSR) gdbMode |= GDBHIO_S_IXUSR;
if (mode & S_IRGRP) gdbMode |= GDBHIO_S_IRGRP;
if (mode & S_IWGRP) gdbMode |= GDBHIO_S_IWGRP;
if (mode & S_IXGRP) gdbMode |= GDBHIO_S_IXGRP;
if (mode & S_IROTH) gdbMode |= GDBHIO_S_IROTH;
if (mode & S_IWOTH) gdbMode |= GDBHIO_S_IWOTH;
if (mode & S_IXOTH) gdbMode |= GDBHIO_S_IXOTH;
return mode;
}
static int _gdbExportSeekFlag(int flag)
{
switch (flag) {
case SEEK_SET: return GDBHIO_SEEK_SET;
case SEEK_CUR: return GDBHIO_SEEK_CUR;
case SEEK_END: return GDBHIO_SEEK_END;
default: return GDBHIO_SEEK_SET;
}
}
// https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat
typedef u32 gdbhio_time_t;
struct CTR_PACKED CTR_ALIGN(4) gdbhio_stat {
u32 gst_dev; /* device */
u32 gst_ino; /* inode */
gdbhio_mode_t gst_mode; /* protection */
u32 gst_nlink; /* number of hard links */
u32 gst_uid; /* user ID of owner */
u32 gst_gid; /* group ID of owner */
u32 gst_rdev; /* device type (if inode device) */
u64 gst_size; /* total size, in bytes */
u64 gst_blksize; /* blocksize for filesystem I/O */
u64 gst_blocks; /* number of blocks allocated */
gdbhio_time_t gst_atime; /* time of last access */
gdbhio_time_t gst_mtime; /* time of last modification */
gdbhio_time_t gst_ctime; /* time of last change */
};
static inline u32 _gdbHioImportScalar32(u32 v)
{
return __builtin_bswap32(v);
}
static inline u64 _gdbHioImportScalar64(u64 v)
{
return __builtin_bswap64(v);
}
static void _gdbHioImportStructStat(struct stat *out, const struct gdbhio_stat *in)
{
memset(out, 0, sizeof(struct stat));
out->st_dev = _gdbHioImportScalar32(in->gst_dev);
out->st_ino = _gdbHioImportScalar32(in->gst_ino);
out->st_mode = _gdbHioImportFileMode(_gdbHioImportScalar32(in->gst_mode));
out->st_nlink = _gdbHioImportScalar32(in->gst_nlink);
out->st_uid = _gdbHioImportScalar32(in->gst_uid);
out->st_gid = _gdbHioImportScalar32(in->gst_gid);
out->st_rdev = _gdbHioImportScalar32(in->gst_rdev);
out->st_size = (off_t)_gdbHioImportScalar64(in->gst_size);
out->st_blksize = (blksize_t)_gdbHioImportScalar64(in->gst_blksize);
out->st_blocks = (blkcnt_t)_gdbHioImportScalar64(in->gst_blocks);
out->st_atime = _gdbHioImportScalar32(in->gst_atime);
out->st_mtime = _gdbHioImportScalar32(in->gst_mtime);
out->st_ctime = _gdbHioImportScalar32(in->gst_ctime);
}
struct gdbhio_timeval {
gdbhio_time_t tv_sec; /* second */
u64 tv_usec; /* microsecond */
};
static void _gdbHioImportStructTimeval(struct timeval *out, const struct gdbhio_timeval *in)
{
out->tv_sec = _gdbHioImportScalar32(in->tv_sec);
out->tv_usec = _gdbHioImportScalar64(in->tv_usec);
}
static void _gdbHioSetErrno(int gdbErrno, bool ctrlC)
{
if (gdbErrno != 0) {
errno = _gdbHioImportErrno(gdbErrno);
}
g_gdbHioWasInterruptedByCtrlC = ctrlC;
}
static s64 _gdbHioSendSyncRequest64V(const char *name, const char *paramFormat, va_list args)
{
PackedGdbHioRequest req = {{0}};
memcpy(req.magic, "GDB", 4);
strncpy(req.functionName, name, 16);
strncpy(req.paramFormat, paramFormat, 8);
u32 numStrs = 0;
const char *str;
for (u32 i = 0; i < 8 && paramFormat[i] != 0; i++) {
switch (paramFormat[i]) {
case 'i':
case 'I':
req.parameters[i] = va_arg(args, u32);
break;
case 'l':
case 'L':
req.parameters[i] = va_arg(args, u64);
break;
case 'p':
req.parameters[i] = va_arg(args, uintptr_t);
break;
case 's':
str = va_arg(args, const char *);
req.parameters[i] = (uintptr_t)str;
req.stringLengths[numStrs++] = strlen(str)+1;
break;
default:
break;
}
}
if (R_FAILED(svcOutputDebugString((const char *)&req, 0)) || req.paramFormat[0] != 0) {
errno = EPIPE;
g_gdbHioWasInterruptedByCtrlC = false;
return -1;
}
_gdbHioSetErrno(req.gdbErrno, req.ctrlC);
return req.retval;
}
static s64 _gdbHioSendSyncRequest64(const char *name, const char *paramFormat, ...)
{
s64 ret = 0;
va_list args;
va_start(args, paramFormat);
ret = _gdbHioSendSyncRequest64V(name, paramFormat, args);
va_end(args);
return ret;
}
static int _gdbHioSendSyncRequest(const char *name, const char *paramFormat, ...)
{
s64 ret = 0;
va_list args;
va_start(args, paramFormat);
ret = _gdbHioSendSyncRequest64V(name, paramFormat, args);
va_end(args);
return (int)ret;
}
int gdbHioOpen(const char *pathname, int flags, mode_t mode)
{
return _gdbHioSendSyncRequest("open", "siI", pathname, _gdbHioExportOpenFlags(flags), _gdbHioExportFileMode(mode));
}
int gdbHioClose(int fd)
{
return _gdbHioSendSyncRequest("close", "i", fd);
}
int gdbHioRead(int fd, void *buf, unsigned int count)
{
return _gdbHioSendSyncRequest("read", "ipI", fd, buf, count);
}
int gdbHioWrite(int fd, const void *buf, unsigned int count)
{
return _gdbHioSendSyncRequest("write", "ipI", fd, buf, count);
}
off_t gdbHioLseek(int fd, off_t offset, int flag)
{
return _gdbHioSendSyncRequest64("lseek", "ili", fd, offset, _gdbExportSeekFlag(flag));
}
int gdbHioRename(const char *oldpath, const char *newpath)
{
return _gdbHioSendSyncRequest("rename", "ss", oldpath, newpath);
}
int gdbHioUnlink(const char *pathname)
{
return _gdbHioSendSyncRequest("unlink", "s", pathname);
}
int gdbHioStat(const char *pathname, struct stat *st)
{
struct gdbhio_stat gst;
int ret = _gdbHioSendSyncRequest("stat", "sp", pathname, &gst);
_gdbHioImportStructStat(st, &gst);
return ret;
}
int gdbHioFstat(int fd, struct stat *st)
{
struct gdbhio_stat gst;
int ret = _gdbHioSendSyncRequest("fstat", "ip", fd, &gst);
_gdbHioImportStructStat(st, &gst);
return ret;
}
int gdbHioGettimeofday(struct timeval *tv, void *tz)
{
// GDB ignores tz and passes NULL
struct gdbhio_timeval gtv;
int ret = _gdbHioSendSyncRequest("gettimeofday", "pp", &gtv, tz);
_gdbHioImportStructTimeval(tv, &gtv);
return ret;
}
int gdbHioIsatty(int fd)
{
return _gdbHioSendSyncRequest("isatty", "i", fd);
}
// Requires set remote system-call-allowed 1
int gdbHioSystem(const char *command)
{
return _gdbHioSendSyncRequest("system", "s", command);
}

250
libctru/source/gdbhio_dev.c Normal file
View File

@ -0,0 +1,250 @@
#include <3ds/gdbhio_dev.h>
#include <3ds/gdbhio.h>
#include <sys/iosupport.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
static int g_gdbHioStdinFd = -1, g_gdbHioStdoutFd = -1, g_gdbHioStderrFd = -1;
static int _gdbHioGetFd(int fd)
{
__handle *handle = __get_handle(fd);
if (handle == NULL) {
errno = EBADF;
return -1;
}
if(strcmp(devoptab_list[handle->device]->name, "gdbhio") != 0) {
errno = EBADF;
return -1;
}
return *(int *)handle->fileStruct;
}
static inline int _gdbHioGetFdFromPtr(void *fdptr)
{
return *(int *)fdptr;
}
static inline const char *_gdbHioSkipMountpoint(const char *pathname)
{
return strncmp(pathname, "gdbhio:", 7) == 0 ? pathname + 7 : pathname;
}
static int _gdbHioDevOpen(struct _reent *r, void *fdptr, const char *pathname, int flags, int mode)
{
(void)r;
pathname = _gdbHioSkipMountpoint(pathname);
int ret = gdbHioOpen(pathname, flags, mode);
if (ret < 0) {
return ret;
} else {
*(int *)fdptr = ret;
return 0;
}
}
static int _gdbHioDevClose(struct _reent *r, void *fdptr)
{
(void)r;
int fd = _gdbHioGetFdFromPtr(fdptr);
if (fd == g_gdbHioStdinFd)
g_gdbHioStdinFd = -1;
else if (fd == g_gdbHioStdoutFd)
g_gdbHioStdoutFd = -1;
else if (fd == g_gdbHioStderrFd)
g_gdbHioStderrFd = -1;
else
return gdbHioClose(fd);
return 0;
}
static ssize_t _gdbHioDevRead(struct _reent *r, void *fdptr, char *buf, size_t count)
{
(void)r;
return gdbHioRead(_gdbHioGetFdFromPtr(fdptr), buf, (unsigned int)count);
}
static ssize_t _gdbHioDevWrite(struct _reent *r, void *fdptr, const char *buf, size_t count)
{
(void)r;
return gdbHioWrite(_gdbHioGetFdFromPtr(fdptr), buf, (unsigned int)count);
}
static off_t _gdbHioDevLseek(struct _reent *r, void *fdptr, off_t offset, int flag)
{
(void)r;
return gdbHioLseek(_gdbHioGetFdFromPtr(fdptr), offset, flag);
}
static int _gdbHioDevRename(struct _reent *r, const char *oldpath, const char *newpath)
{
(void)r;
return gdbHioRename(_gdbHioSkipMountpoint(oldpath), _gdbHioSkipMountpoint(newpath));
}
static int _gdbHioDevUnlink(struct _reent *r, const char *pathname)
{
(void)r;
return gdbHioUnlink(_gdbHioSkipMountpoint(pathname));
}
static int _gdbHioDevStat(struct _reent *r, const char *pathname, struct stat *st)
{
(void)r;
return gdbHioStat(_gdbHioSkipMountpoint(pathname), st);
}
static int _gdbHioDevFstat(struct _reent *r, void *fdptr, struct stat *st)
{
(void)r;
return gdbHioFstat(_gdbHioGetFdFromPtr(fdptr), st);
}
static int _gdbHioDevImportFd(int gdbFd)
{
int fd, dev;
dev = FindDevice("gdbhio:");
if(dev == -1)
return -1;
fd = __alloc_handle(dev);
if(fd == -1)
return -1;
*(int *)__get_handle(fd)->fileStruct = gdbFd;
return fd;
}
static const devoptab_t g_gdbHioDevoptab = {
.name = "gdbhio",
.structSize = sizeof(int),
.open_r = _gdbHioDevOpen,
.close_r = _gdbHioDevClose,
.write_r = _gdbHioDevWrite,
.read_r = _gdbHioDevRead,
.seek_r = _gdbHioDevLseek,
.fstat_r = _gdbHioDevFstat,
.stat_r = _gdbHioDevStat,
.link_r = NULL,
.unlink_r = _gdbHioDevUnlink,
.chdir_r = NULL,
.rename_r = _gdbHioDevRename,
.mkdir_r = NULL,
.dirStateSize = 0,
.diropen_r = NULL,
.dirreset_r = NULL,
.dirnext_r = NULL,
.dirclose_r = NULL,
.statvfs_r = NULL,
.ftruncate_r = NULL,
.fsync_r = NULL,
.deviceData = 0,
.chmod_r = NULL,
.fchmod_r = NULL,
.rmdir_r = NULL,
};
int gdbHioDevGettimeofday(struct timeval *tv, void *tz)
{
return gdbHioGettimeofday(tv, tz);
}
int gdbHioDevIsatty(int fd)
{
return gdbHioIsatty(_gdbHioGetFd(fd));
}
int gdbHioDevSystem(const char *command)
{
return gdbHioSystem(command);
}
int gdbHioDevInit(void)
{
int dev = FindDevice("gdbhio:");
if (dev >= 0) {
return -1;
}
dev = AddDevice(&g_gdbHioDevoptab);
if (dev < 0) return -1;
return 0;
}
void gdbHioDevExit(void)
{
close(g_gdbHioStdinFd);
close(g_gdbHioStdoutFd);
close(g_gdbHioStderrFd);
RemoveDevice("gdbhio:");
}
int gdbHioDevGetStdin(void)
{
if (g_gdbHioStdinFd < 0)
g_gdbHioStdinFd = _gdbHioDevImportFd(GDBHIO_STDIN_FILENO);
return g_gdbHioStdinFd;
}
int gdbHioDevGetStdout(void)
{
if (g_gdbHioStdoutFd < 0)
g_gdbHioStdoutFd = _gdbHioDevImportFd(GDBHIO_STDOUT_FILENO);
return g_gdbHioStdoutFd;
}
int gdbHioDevGetStderr(void)
{
if (g_gdbHioStderrFd < 0)
g_gdbHioStderrFd = _gdbHioDevImportFd(GDBHIO_STDERR_FILENO);
return g_gdbHioStderrFd;
}
int gdbHioDevRedirectStdStreams(bool in, bool out, bool err)
{
int ret = 0;
if (in) {
if (gdbHioDevGetStdin() < 0) return -1;
ret = dup2(g_gdbHioStdinFd, STDIN_FILENO);
if (ret < 0) return -1;
if (ret != g_gdbHioStdinFd) {
close(g_gdbHioStdinFd);
g_gdbHioStdinFd = STDIN_FILENO;
}
}
if (out) {
if (gdbHioDevGetStdout() < 0) return -2;
ret = dup2(g_gdbHioStdoutFd, STDOUT_FILENO);
if (ret < 0) return -2;
if (ret != g_gdbHioStdoutFd) {
close(g_gdbHioStdoutFd);
g_gdbHioStdoutFd = STDOUT_FILENO;
}
}
if (err) {
if (gdbHioDevGetStderr() < 0) return -3;
ret = dup2(g_gdbHioStderrFd, STDERR_FILENO);
if (ret < 0) return -3;
if (ret != g_gdbHioStderrFd) {
close(g_gdbHioStderrFd);
g_gdbHioStderrFd = STDERR_FILENO;
}
}
return 0;
}

View File

@ -1,256 +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;
}
return 3;
void gfxSetWide(bool enable)
{
gfxTopMode = enable ? MODE_WIDE : MODE_2D;
}
void gfxSetScreenFormat(gfxScreen_t screen, GSPGPU_FramebufferFormats format) {
if (screenAlloc == NULL || screenFree == NULL) return;
if(screen==GFX_TOP)
bool gfxIsWide(void)
{
return gfxTopMode == MODE_WIDE;
}
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)
{
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_TOP_2X;
framebuffers = gfxTopFramebuffers;
maxSize = &gfxTopFramebufferMaxSize;
}
else // GFX_BOTTOM
{
reqSize *= GSP_SCREEN_HEIGHT_BOTTOM;
framebuffers = gfxBottomFramebuffers;
maxSize = &gfxBottomFramebufferMaxSize;
}
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_FramebufferFormats gfxGetScreenFormat(gfxScreen_t screen) {
if(screen==GFX_TOP)
return topFormat;
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)
{
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;
}
void gfxSetDoubleBuffering(gfxScreen_t screen, bool doubleBuffering) {
doubleBuf[screen] = doubleBuffering ? 1 : 0; // make sure they're the integer values '1' and '0'
}
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;
fb_a = gfxBottomFramebuffers[id];
fb_b = fb_a;
}
if (!gfxIsVram)
mode |= 1<<8;
else
mode |= 3<<8;
gspPresentBuffer(screen, id, fb_a, fb_b, stride, mode);
}
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];
union
{
s32 header;
struct { u8 swap, update; };
} info;
info.header = __ldrex(framebufferInfoHeader);
info.swap = !info.swap;
u8 pos = info.swap;
info.update = 1;
framebufferInfo[pos]=*framebufferInfoSt[screen];
while (__strex(framebufferInfoHeader,info.header))
{
info.header = __ldrex(framebufferInfoHeader);
info.swap = pos;
info.update = 1;
}
}
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 {
screenAlloc=linearAlloc;
screenFree=linearFree;
screenAlloc = vramAlloc;
screenFree = vramFree;
gfxIsVram = true;
}
else
{
screenAlloc = linearAlloc;
screenFree = linearFree;
gfxIsVram = false;
}
// Initialize GSP
gspInit();
gfxSharedMemory=(u8*)mappableAlloc(0x1000);
// Initialize configuration
gfxSet3D(false);
gfxSetScreenFormat(GFX_TOP, topFormat);
gfxSetScreenFormat(GFX_BOTTOM, bottomFormat);
gfxSetDoubleBuffering(GFX_TOP, true);
gfxSetDoubleBuffering(GFX_BOTTOM, true);
GSPGPU_AcquireRight(0x0);
// Present the framebuffers
gfxCurBuf[0] = gfxCurBuf[1] = 0;
gfxPresentFramebuffer(GFX_TOP, 0, false);
gfxPresentFramebuffer(GFX_BOTTOM, 0, false);
//setup our gsp shared mem section
svcCreateEvent(&gspEvent, RESET_ONESHOT);
GSPGPU_RegisterInterruptRelayQueue(gspEvent, 0x1, &gspSharedMemHandle, &gfxThreadID);
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
gfxSetScreenFormat(GFX_TOP,topFormat);
gfxSetScreenFormat(GFX_BOTTOM,bottomFormat);
//initialize framebuffer info structures
gfxSetFramebufferInfo(GFX_TOP, 0);
gfxSetFramebufferInfo(GFX_BOTTOM, 0);
//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;
@ -258,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 (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)
{
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)
break;
}
if (wb)
wb->status = NDSP_WBUF_PLAYING;
break;
}
chn->wavBufCount--;
ndspWaveBuf* next = wb->next;
wb->next = doneList;
doneList = wb;
wb = next;
if (seqId == 0 && (wbSeqId == st->lastSeqId || st->lastSeqId == 0))
break;
}
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;
}
}
}

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