Compare commits

...

24 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
47 changed files with 1892 additions and 541 deletions

View File

@ -1,5 +1,36 @@
# 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
@ -7,7 +38,7 @@
## Version 2.2.1
- add _SOCKLEN_T_DECLARED
- add `_SOCKLEN_T_DECLARED`
## Version 2.2.0

View File

@ -2023,7 +2023,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = PACKED
PREDEFINED = CTR_PACKED
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The

View File

@ -9,8 +9,8 @@ endif
include $(DEVKITARM)/base_rules
export LIBCTRU_MAJOR := 2
export LIBCTRU_MINOR := 2
export LIBCTRU_PATCH := 2
export LIBCTRU_MINOR := 4
export LIBCTRU_PATCH := 0
VERSION := $(LIBCTRU_MAJOR).$(LIBCTRU_MINOR).$(LIBCTRU_PATCH)

View File

@ -75,6 +75,7 @@ extern "C" {
#include <3ds/services/nfc.h>
#include <3ds/services/news.h>
#include <3ds/services/qtm.h>
#include <3ds/services/qtmc.h>
#include <3ds/services/srvpm.h>
#include <3ds/services/loader.h>
#include <3ds/services/y2r.h>

View File

@ -166,7 +166,7 @@ void gfxScreenSwapBuffers(gfxScreen_t scr, bool hasStereo);
* @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.
*/
DEPRECATED void gfxConfigScreen(gfxScreen_t scr, bool immediate);
CTR_DEPRECATED void gfxConfigScreen(gfxScreen_t scr, bool immediate);
/**
* @brief Updates the configuration of both screens.

View File

@ -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

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

View File

@ -147,7 +147,7 @@ typedef struct
} glasses_details;
/// Mole details
struct
struct
{
bool enable : 1;
u16 scale : 5;
@ -156,4 +156,4 @@ typedef struct
} mole_details;
u16 author_name[10]; ///< Name of Mii's author (Encoded using UTF16)
} PACKED MiiData;
} CTR_PACKED MiiData;

View File

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

View File

@ -176,7 +176,7 @@ bool aptShouldJumpToHome(void);
bool aptCheckHomePressRejected(void);
/// \deprecated Alias for \ref aptCheckHomePressRejected.
static inline DEPRECATED bool aptIsHomePressed(void)
static inline CTR_DEPRECATED bool aptIsHomePressed(void)
{
return aptCheckHomePressRejected();
}

View File

@ -20,18 +20,19 @@ 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.

View File

@ -202,7 +202,7 @@ typedef struct
} FS_IntegrityVerificationSeed;
/// Ext save data information.
typedef struct PACKED
typedef struct CTR_PACKED
{
FS_MediaType mediaType : 8; ///< Media type.
u8 unknown; ///< Unknown.
@ -647,7 +647,7 @@ Result FSUSER_GetLegacyRomHeader(FS_MediaType mediaType, u64 programId, void* he
* @brief Gets the legacy banner data of a program.
* @param mediaType Media type of the program.
* @param programId ID of the program.
* @param header Pointer to output the legacy banner data to. (size = 0x23C0)
* @param banner Pointer to output the legacy banner data to. (size = 0x23C0)
*/
Result FSUSER_GetLegacyBannerData(FS_MediaType mediaType, u64 programId, void* banner);

View File

@ -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);
@ -66,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

@ -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

@ -109,9 +109,10 @@ Result PTMSYSM_GetRtcTime(s64 *outMsY2k);
Result PTMSYSM_SetRtcTime(s64 msY2k);
/**
* @brief Returns 1 if it's a New 3DS, otherwise 0.
* @brief Checks whether the system is a New 3DS.
* @param[out] out Pointer to write the New 3DS flag to.
*/
Result PTMSYSM_CheckNew3DS(void);
Result PTMSYSM_CheckNew3DS(bool *out);
/**
* @brief Configures the New 3DS' CPU clock speed and L2 cache.

View File

@ -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

@ -526,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)
{
@ -537,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)
{
@ -546,7 +546,7 @@ 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)
{

View File

@ -42,6 +42,12 @@ 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)
{

View File

@ -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

@ -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

@ -10,7 +10,7 @@ void errorInit(errorConf* err, errorType type, CFG_Language lang)
{
memset(err, 0, sizeof(*err));
err->type = type;
err->useLanguage = lang;
err->useLanguage = lang + 1;
err->upperScreenFlag = ERROR_NORMAL;
err->eulaVersion = 0;
err->homeButton = true;

View File

@ -72,65 +72,6 @@ PrintConsole defaultConsole =
false //console initialized
};
static bool parseColor (char **esc, int *escLen, u16 *color, bool *custom)
{
unsigned int p;
unsigned int n;
unsigned int r;
unsigned int g;
unsigned int b;
int consumed;
if (sscanf (*esc, "%d;%n", &p, &consumed) != 1)
return false;
*esc += consumed;
*escLen -= consumed;
if (p == 5) {
if (sscanf (*esc, "%u%n", &n, &consumed) != 1)
return false;
*esc += consumed;
*escLen -= consumed;
if (n <= 15) {
*color = n;
*custom = false;
} else if (n <= 231) {
n -= 16;
r = n / 36;
g = (n - r * 36) / 6;
b = n - r * 36 - g * 6;
*color = RGB8_to_565 (colorCube[r], colorCube[g], colorCube[b]);
*custom = true;
} else if (n <= 255) {
n -= 232;
*color = RGB8_to_565 (grayScale[n], grayScale[n], grayScale[n]);
*custom = true;
} else {
return false;
}
return true;
} else if (p == 2) {
if (sscanf (*esc, "%u;%u;%u%n", &r, &g, &b, &consumed) != 3)
return false;
*esc += consumed;
*escLen -= consumed;
*color = RGB8_to_565 (r, g, b);
*custom = true;
return true;
}
return false;
}
PrintConsole currentCopy;
PrintConsole* currentConsole = &currentCopy;
@ -141,7 +82,7 @@ void consolePrintChar(int c);
void consoleDrawChar(int c);
//---------------------------------------------------------------------------------
static void consoleCls(char mode) {
static void consoleCls(int mode) {
//---------------------------------------------------------------------------------
int i = 0;
@ -149,8 +90,7 @@ static void consoleCls(char mode) {
switch (mode)
{
case '[':
case '0':
case 0:
{
colTemp = currentConsole->cursorX ;
rowTemp = currentConsole->cursorY ;
@ -162,7 +102,7 @@ static void consoleCls(char mode) {
currentConsole->cursorY = rowTemp;
break;
}
case '1':
case 1:
{
colTemp = currentConsole->cursorX ;
rowTemp = currentConsole->cursorY ;
@ -177,7 +117,7 @@ static void consoleCls(char mode) {
currentConsole->cursorY = rowTemp;
break;
}
case '2':
case 2:
{
currentConsole->cursorY = 0;
currentConsole->cursorX = 0;
@ -193,7 +133,7 @@ static void consoleCls(char mode) {
gfxFlushBuffers();
}
//---------------------------------------------------------------------------------
static void consoleClearLine(char mode) {
static void consoleClearLine(int mode) {
//---------------------------------------------------------------------------------
int i = 0;
@ -201,8 +141,7 @@ static void consoleClearLine(char mode) {
switch (mode)
{
case '[':
case '0':
case 0:
{
colTemp = currentConsole->cursorX ;
@ -214,7 +153,7 @@ static void consoleClearLine(char mode) {
break;
}
case '1':
case 1:
{
colTemp = currentConsole->cursorX ;
@ -228,7 +167,7 @@ static void consoleClearLine(char mode) {
break;
}
case '2':
case 2:
{
colTemp = currentConsole->cursorX ;
@ -271,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) {
//---------------------------------------------------------------------------------
@ -289,305 +495,165 @@ ssize_t con_write(struct _reent *r,void *fd,const char *ptr, size_t len) {
chr = *(tmp++);
i++; count++;
if ( chr == 0x1b && len > 1 && *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 {
bool custom;
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->flags &= ~CONSOLE_FG_CUSTOM;
currentConsole->fg = parameter - 30;
break;
case 38: // custom foreground color
if (parseColor (&escapeseq, &escapelen, &currentConsole->fg, &custom)) {
if (custom)
currentConsole->flags |= CONSOLE_FG_CUSTOM;
else
currentConsole->flags &= ~CONSOLE_FG_CUSTOM;
if (!custom && currentConsole->fg < 16) {
currentConsole->flags &= ~CONSOLE_COLOR_FAINT;
if (currentConsole->fg < 8)
currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
else
currentConsole->flags |= CONSOLE_COLOR_BOLD;
}
// consume next ; or m
++escapeseq;
--escapelen;
} else {
// stop processing
escapelen = 0;
}
break;
case 39: // reset foreground color
currentConsole->flags &= ~CONSOLE_FG_CUSTOM;
currentConsole->fg = 7;
break;
case 40 ... 47: // screen color
currentConsole->flags &= ~CONSOLE_BG_CUSTOM;
currentConsole->bg = parameter - 40;
break;
case 48: // custom background color
if (parseColor (&escapeseq, &escapelen, &currentConsole->bg, &custom)) {
if (custom)
currentConsole->flags |= CONSOLE_BG_CUSTOM;
else
currentConsole->flags &= ~CONSOLE_BG_CUSTOM;
// consume next ; or m
++escapeseq;
--escapelen;
} else {
// stop processing
escapelen = 0;
}
break;
case 49: // reset background color
currentConsole->flags &= ~CONSOLE_BG_CUSTOM;
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;
@ -647,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;
}
@ -673,7 +741,7 @@ PrintConsole* consoleInit(gfxScreen_t screen, PrintConsole* console) {
console->windowWidth = isWide ? 100 : 50;
}
consoleCls('2');
consoleCls(2);
return currentConsole;
@ -685,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);
@ -743,7 +811,7 @@ static void newRow() {
src += 240;
}
consoleClearLine('2');
consoleClearLine(2);
}
}
//---------------------------------------------------------------------------------
@ -874,7 +942,7 @@ void consolePrintChar(int c) {
//---------------------------------------------------------------------------------
void consoleClear(void) {
//---------------------------------------------------------------------------------
consoleCls('2');
consoleCls(2);
}
//---------------------------------------------------------------------------------
@ -892,5 +960,3 @@ void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height
console->cursorY = 0;
}

View File

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

View File

@ -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

@ -100,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;

View File

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

View File

@ -376,7 +376,7 @@ Result FSPXI_HasFile(Handle serviceHandle, FSPXI_Archive archive, bool* out, FS_
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if (out) *out = (bool)cmdbuf[2];
if (out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -396,7 +396,7 @@ Result FSPXI_HasDirectory(Handle serviceHandle, FSPXI_Archive archive, bool* out
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if (out) *out = (bool)cmdbuf[2];
if (out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -441,7 +441,7 @@ Result FSPXI_Unknown0x17(Handle serviceHandle, FSPXI_Archive archive, bool* out)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if (out) *out = (bool)cmdbuf[2];
if (out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -523,7 +523,7 @@ Result FSPXI_IsSdmcDetected(Handle serviceHandle, bool* out)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(out) *out = (bool)cmdbuf[2];
if(out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -537,7 +537,7 @@ Result FSPXI_IsSdmcWritable(Handle serviceHandle, bool* out)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(out) *out = (bool)cmdbuf[2];
if(out) *out = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -663,7 +663,7 @@ Result FSPXI_CardSlotIsInserted(Handle serviceHandle, bool* inserted)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(inserted) *inserted = (bool)cmdbuf[2];
if(inserted) *inserted = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -677,7 +677,7 @@ Result FSPXI_CardSlotPowerOn(Handle serviceHandle, bool* status)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(status) *status = (bool)cmdbuf[2];
if(status) *status = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -691,7 +691,7 @@ Result FSPXI_CardSlotPowerOff(Handle serviceHandle, bool* status)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(status) *status = (bool)cmdbuf[2];
if(status) *status = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}
@ -705,7 +705,7 @@ Result FSPXI_CardSlotGetCardIFPowerStatus(Handle serviceHandle, bool* status)
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(status) *status = (bool)cmdbuf[2];
if(status) *status = (bool)(cmdbuf[2] & 1);
return (Result) cmdbuf[1];
}

View File

@ -184,6 +184,12 @@ Result gspInit(void)
gspSharedMem = mappableAlloc(0x1000);
svcMapMemoryBlock(gspSharedMemHandle, (u32)gspSharedMem, MEMPERM_READWRITE, MEMPERM_DONTCARE);
// Initialize interrupt queue header
s32* sharedGspCmdBuf = (s32*)((u8*)gspSharedMem + 0x800 + gspThreadId*0x200);
do {
__ldrex(sharedGspCmdBuf);
} while (__strex(sharedGspCmdBuf, 0));
// Start event handling thread
gspRunEvents = true;
gspLastEvent = -1;

View File

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

View File

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

View File

@ -98,13 +98,14 @@ Result PTMSYSM_Awaken(void)
return (Result)cmdbuf[1];
}
Result PTMSYSM_CheckNew3DS(void)
Result PTMSYSM_CheckNew3DS(bool *out)
{
Result ret;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x040A,0,0); // 0x040A0000
if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return 0;
if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret;
*out = (bool)(cmdbuf[2] & 1); // if cmdbuf[1] is != 0 then this is uninitialized (this is fine)
return (Result)cmdbuf[1];
}

View File

@ -7,76 +7,389 @@
#include <3ds/synchronization.h>
#include <3ds/services/qtm.h>
#include <3ds/ipc.h>
#include <math.h>
Handle qtmHandle;
static int qtmRefCount;
Result qtmInit(void)
bool qtmCheckServicesRegistered(void)
{
Result ret=0;
bool registered = false;
return R_SUCCEEDED(srvIsServiceRegistered(&registered, "qtm:u")) && registered;
}
Result qtmInit(QtmServiceName serviceName)
{
const char *name = NULL;
switch (serviceName)
{
default:
case QTM_SERVICE_USER:
name = "qtm:u";
break;
case QTM_SERVICE_SYSTEM:
name = "qtm:s";
break;
case QTM_SERVICE_SYSTEM_PROCESS:
name = "qtm:sp";
break;
}
if (AtomicPostIncrement(&qtmRefCount)) return 0;
Result res = srvGetServiceHandle(&qtmHandle, name);
ret = srvGetServiceHandle(&qtmHandle, "qtm:u");
if (R_FAILED(ret)) ret = srvGetServiceHandle(&qtmHandle, "qtm:s");
if (R_FAILED(ret)) ret = srvGetServiceHandle(&qtmHandle, "qtm:sp");
if (R_FAILED(ret)) AtomicDecrement(&qtmRefCount);
return ret;
if (R_FAILED(res))
{
qtmHandle = 0;
AtomicDecrement(&qtmRefCount);
}
return res;
}
void qtmExit(void)
{
if (AtomicDecrement(&qtmRefCount)) return;
svcCloseHandle(qtmHandle);
qtmHandle = 0;
}
bool qtmCheckInitialized(void)
bool qtmIsInitialized(void)
{
return qtmRefCount>0;
// Use qtmHandle instead of qtmRefCount in the event user
// wants to bypass qtmInit and use *qtmGetSessionHandle() instead
// (e.g. stolen session).
return qtmHandle != 0;
}
bool qtmCheckHeadFullyDetected(QTM_HeadTrackingInfo *info)
Handle *qtmGetSessionHandle(void)
{
if(info==NULL)return false;
if(info->flags[0] && info->flags[1] && info->flags[2])return true;
return false;
return &qtmHandle;
}
Result qtmConvertCoordToScreen(QTM_HeadTrackingInfoCoord *coord, float *screen_width, float *screen_height, u32 *x, u32 *y)
Result QTMU_GetRawTrackingDataEx(QtmRawTrackingData *outData, s64 predictionTimePointOrZero)
{
float width = 200.0f;
float height = 160.0f;
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
if(coord==NULL || x==NULL || y==NULL)return -1;
cmdbuf[0] = IPC_MakeHeader(0x1, 2, 0); // 0x10080
memcpy(&cmdbuf[1], &predictionTimePointOrZero, 8);
if(screen_width)width = (*screen_width) / 2;
if(screen_height)height = (*screen_height) / 2;
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
if(coord->x > 1.0f || coord->x < -1.0f)return -2;
if(coord->y > 1.0f || coord->y < -1.0f)return -2;
*x = (u32)((coord->x * width) + width);
*y = (u32)((coord->y * height) + height);
return 0;
}
Result QTM_GetHeadTrackingInfo(u64 val, QTM_HeadTrackingInfo* out)
{
if(!qtmCheckInitialized())return -1;
Result ret = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x2,2,0); // 0x20080
cmdbuf[1] = val&0xFFFFFFFF;
cmdbuf[2] = val>>32;
if(R_FAILED(ret=svcSendSyncRequest(qtmHandle)))return ret;
if(out) memcpy(out, &cmdbuf[2], sizeof(QTM_HeadTrackingInfo));
memcpy(outData, &cmdbuf[2], sizeof(QtmRawTrackingData));
return cmdbuf[1];
}
Result QTMU_GetTrackingDataEx(QtmTrackingData *outData, s64 predictionTimePointOrZero)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x2, 2, 0); // 0x20080
memcpy(&cmdbuf[1], &predictionTimePointOrZero, 8);
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
memcpy(outData, &cmdbuf[2], sizeof(QtmTrackingData));
return cmdbuf[1];
}
float qtmComputeFovX(const QtmTrackingData *data)
{
return 2.0f * atanf(data->eyeWorldCoordinates[QTM_EYE_LEFT][0] / data->eyeCameraCoordinates[QTM_EYE_LEFT][0]);
}
float qtmComputeFovY(const QtmTrackingData *data)
{
return 2.0f * atanf(data->eyeWorldCoordinates[QTM_EYE_LEFT][1] / data->eyeCameraCoordinates[QTM_EYE_LEFT][1]);
}
float qtmComputeInverseAspectRatio(const QtmTrackingData *data)
{
return
(data->eyeWorldCoordinates[QTM_EYE_LEFT][1] * data->eyeCameraCoordinates[QTM_EYE_LEFT][0]) /
(data->eyeCameraCoordinates[QTM_EYE_LEFT][1] * data->eyeWorldCoordinates[QTM_EYE_LEFT][0]);
}
float qtmComputeHeadTiltAngle(const QtmTrackingData *data)
{
return atan2f(
data->eyeCameraCoordinates[QTM_EYE_RIGHT][1] - data->eyeCameraCoordinates[QTM_EYE_LEFT][1],
data->eyeCameraCoordinates[QTM_EYE_RIGHT][0] - data->eyeCameraCoordinates[QTM_EYE_LEFT][0]
);
}
float qtmEstimateEyeToCameraDistance(const QtmTrackingData *data)
{
float fovX = qtmComputeFovX(data);
float pxDistNorm = hypotf(
data->eyeWorldCoordinates[QTM_EYE_RIGHT][0] - data->eyeWorldCoordinates[QTM_EYE_LEFT][0],
data->eyeWorldCoordinates[QTM_EYE_RIGHT][1] - data->eyeWorldCoordinates[QTM_EYE_LEFT][1]
);
return 31.0f / tanf(0.5f * (pxDistNorm * fovX));
}
Result QTMU_EnableManualIrLedControl(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x3, 0, 0); // 0x30000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMU_DisableManualIrLedControl(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x4, 0, 0); // 0x40000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMU_SetIrLedStatus(bool on)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x5, 1, 0); // 0x50040
cmdbuf[1] = on;
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result qtmClearIrLedOverrides(void)
{
QTMU_EnableManualIrLedControl();
return QTMU_DisableManualIrLedControl();
}
Result QTMU_IsCurrentAppBlacklisted(bool *outBlacklisted)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x6, 0, 0); // 0x60000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
*outBlacklisted = (bool)(cmdbuf[2] & 1);
return cmdbuf[1];
}
Result QTMS_SetCentralBarrierPosition(float position)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x401, 1, 0); // 0x4010040
memcpy(&cmdbuf[1], &position, sizeof(float));
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMS_GetCameraLuminance(float *outLuminanceLux)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x402, 0, 0); // 0x4020000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
memcpy(outLuminanceLux, &cmdbuf[2], sizeof(float));
return cmdbuf[1];
}
Result QTMS_EnableAutoBarrierControl(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x403, 0, 0); // 0x4030000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMS_DisableAutoBarrierControl(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x404, 0, 0); // 0x4040000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMS_SetBarrierPosition(u8 position)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x405, 1, 0); // 0x4050040
cmdbuf[1] = position;
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMS_GetCurrentBarrierPosition(u8 *outPosition)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x406, 0, 0); // 0x4060000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
*outPosition = (u8)cmdbuf[2];
return cmdbuf[1];
}
Result QTMS_SetIrLedStatusOverride(bool on)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x407, 1, 0); // 0x4070040
cmdbuf[1] = on;
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMS_SetCalibrationData(const QtmCalibrationData *cal, bool saveCalToCfg)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x408, 7, 0); // 0x40801C0
memcpy(&cmdbuf[1], cal, sizeof(QtmCalibrationData));
cmdbuf[7] = saveCalToCfg;
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMS_GetQtmStatus(QtmStatus *outQtmStatus)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x409, 0, 0); // 0x4090000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
*outQtmStatus = (QtmStatus)cmdbuf[2];
return cmdbuf[1];
}
Result QTMS_SetQtmStatus(QtmStatus qtmStatus)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x40A, 1, 0); // 0x40A0040
cmdbuf[1] = qtmStatus;
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMSP_NotifyTopLcdModeChange(u8 newMode)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x801, 1, 0); // 0x8010040
cmdbuf[1] = newMode;
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMSP_NotifyTopLcdPowerOn(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x802, 0, 0); // 0x8020000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMSP_IsExpanderInUse(bool *outInUse)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x803, 0, 0); // 0x8040000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
*outInUse = (bool)(cmdbuf[2] & 1);
return cmdbuf[1];
}
Result QTMSP_NotifyTopLcdPowerOff(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x804, 0, 0); // 0x8040000
res = svcSendSyncRequest(qtmHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}

View File

@ -0,0 +1,101 @@
#include <stdlib.h>
#include <string.h>
#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/srv.h>
#include <3ds/synchronization.h>
#include <3ds/services/qtmc.h>
#include <3ds/ipc.h>
static Handle qtmcHandle;
static int qtmcRefCount;
Result qtmcInit(void)
{
if (AtomicPostIncrement(&qtmcRefCount)) return 0;
Result res = srvGetServiceHandle(&qtmcHandle, "qtm:c");
if (R_FAILED(res)) AtomicDecrement(&qtmcRefCount);
return res;
}
void qtmcExit(void)
{
if (AtomicDecrement(&qtmcRefCount)) return;
svcCloseHandle(qtmcHandle);
}
Handle *qtmcGetSessionHandle(void)
{
return &qtmcHandle;
}
Result QTMC_StartHardwareCheck(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x1, 0, 0); // 0x10000
res = svcSendSyncRequest(qtmcHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMC_StopHardwareCheck(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x2, 0, 0); // 0x20000
res = svcSendSyncRequest(qtmcHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMC_SetBarrierPattern(u32 pattern)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x3, 1, 0); // 0x30040
cmdbuf[1] = pattern;
res = svcSendSyncRequest(qtmcHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}
Result QTMC_WaitAndCheckExpanderWorking(bool *outWorking)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x4, 0, 0); // 0x40000
res = svcSendSyncRequest(qtmcHandle);
if (R_FAILED(res)) return res;
*outWorking = (bool)(cmdbuf[2] & 1);
return cmdbuf[1];
}
Result QTMC_SetIrLedStatusOverride(bool on)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x5, 1, 0); // 0x50040
cmdbuf[1] = (u32)on;
res = svcSendSyncRequest(qtmcHandle);
if (R_FAILED(res)) return res;
return cmdbuf[1];
}

View File

@ -6,10 +6,10 @@
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
{
int ret = 0;
int tmp_addrlen = 0x1c;
int tmp_addrlen = ADDR_STORAGE_LEN;
int fd, dev;
u32 *cmdbuf = getThreadCommandBuffer();
u8 tmpaddr[0x1c];
u8 tmpaddr[ADDR_STORAGE_LEN];
u32 saved_threadstorage[2];
sockfd = soc_get_fd(sockfd);
@ -27,7 +27,7 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
fd = __alloc_handle(dev);
if(fd < 0) return fd;
memset(tmpaddr, 0, 0x1c);
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
cmdbuf[0] = IPC_MakeHeader(0x4,2,2); // 0x40082
cmdbuf[1] = (u32)sockfd;
@ -61,9 +61,14 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
if(ret >= 0 && addr != NULL) {
addr->sa_family = tmpaddr[1];
if(*addrlen > tmpaddr[0])
*addrlen = tmpaddr[0];
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - 2);
socklen_t user_addrlen = tmpaddr[0];
if(addr->sa_family == AF_INET)
user_addrlen += 8; // Accounting for the 8 bytes of sin_zero padding, which must be written for compatibility.
if(*addrlen > user_addrlen)
*addrlen = user_addrlen;
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - sizeof(addr->sa_family));
}
if(ret < 0) {

View File

@ -8,7 +8,7 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
int ret = 0;
int tmp_addrlen = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u8 tmpaddr[0x1c];
u8 tmpaddr[ADDR_STORAGE_LEN];
sockfd = soc_get_fd(sockfd);
if(sockfd < 0) {
@ -16,12 +16,12 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
return -1;
}
memset(tmpaddr, 0, 0x1c);
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
if(addr->sa_family == AF_INET)
tmp_addrlen = 8;
else
tmp_addrlen = 0x1c;
tmp_addrlen = ADDR_STORAGE_LEN;
if(addrlen < tmp_addrlen) {
errno = EINVAL;

View File

@ -10,6 +10,7 @@
#include <3ds/services/soc.h>
#define SYNC_ERROR ENODEV
#define ADDR_STORAGE_LEN sizeof(struct sockaddr_storage)
extern Handle SOCU_handle;
extern Handle socMemhandle;

View File

@ -8,7 +8,7 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
int ret = 0;
int tmp_addrlen = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u8 tmpaddr[0x1c];
u8 tmpaddr[ADDR_STORAGE_LEN];
sockfd = soc_get_fd(sockfd);
if(sockfd < 0) {
@ -16,12 +16,12 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
return -1;
}
memset(tmpaddr, 0, 0x1c);
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
if(addr->sa_family == AF_INET)
tmp_addrlen = 8;
else
tmp_addrlen = 0x1c;
tmp_addrlen = ADDR_STORAGE_LEN;
if(addrlen < tmp_addrlen) {
errno = EINVAL;

View File

@ -8,7 +8,7 @@ int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_
int i,tmp_addrlen;
u32 *cmdbuf = getThreadCommandBuffer();
u32 saved_threadstorage[4];
u8 tmpaddr[0x1c]; // sockaddr size for the kernel is 0x1C (sockaddr_in6?)
u8 tmpaddr[ADDR_STORAGE_LEN]; // sockaddr size for the kernel is 0x1C (sockaddr_in6?)
if((host == NULL || hostlen == 0) && (serv == NULL || servlen == 0))
{
@ -18,7 +18,7 @@ int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_
if(sa->sa_family == AF_INET)
tmp_addrlen = 8;
else
tmp_addrlen = 0x1c;
tmp_addrlen = ADDR_STORAGE_LEN;
if(salen < tmp_addrlen) {
errno = EINVAL;

View File

@ -8,7 +8,7 @@ int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 saved_threadstorage[2];
u8 tmpaddr[0x1c];
u8 tmpaddr[ADDR_STORAGE_LEN];
sockfd = soc_get_fd(sockfd);
if(sockfd < 0) {
@ -16,16 +16,18 @@ int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
return -1;
}
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
cmdbuf[0] = IPC_MakeHeader(0x18,2,2); // 0x180082
cmdbuf[1] = (u32)sockfd;
cmdbuf[2] = 0x1c;
cmdbuf[2] = ADDR_STORAGE_LEN;
cmdbuf[3] = IPC_Desc_CurProcessId();
u32 * staticbufs = getThreadStaticBuffers();
saved_threadstorage[0] = staticbufs[0];
saved_threadstorage[1] = staticbufs[1];
staticbufs[0] = IPC_Desc_StaticBuffer(0x1c,0);
staticbufs[0] = IPC_Desc_StaticBuffer(ADDR_STORAGE_LEN,0);
staticbufs[1] = (u32)tmpaddr;
ret = svcSendSyncRequest(SOCU_handle);
@ -47,11 +49,15 @@ int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
return -1;
}
if(*addrlen > tmpaddr[0])
*addrlen = tmpaddr[0];
memset(addr, 0, sizeof(struct sockaddr));
addr->sa_family = tmpaddr[1];
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - 2);
socklen_t user_addrlen = tmpaddr[0];
if(addr->sa_family == AF_INET)
user_addrlen += 8;
if(*addrlen > user_addrlen)
*addrlen = user_addrlen;
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - sizeof(addr->sa_family));
return ret;
}

View File

@ -8,7 +8,7 @@ int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 saved_threadstorage[2];
u8 tmpaddr[0x1c];
u8 tmpaddr[ADDR_STORAGE_LEN];
sockfd = soc_get_fd(sockfd);
if(sockfd < 0) {
@ -16,16 +16,18 @@ int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
return -1;
}
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
cmdbuf[0] = IPC_MakeHeader(0x17,2,2); // 0x170082
cmdbuf[1] = (u32)sockfd;
cmdbuf[2] = 0x1c;
cmdbuf[2] = ADDR_STORAGE_LEN;
cmdbuf[3] = IPC_Desc_CurProcessId();
u32 * staticbufs = getThreadStaticBuffers();
saved_threadstorage[0] = staticbufs[0];
saved_threadstorage[1] = staticbufs[1];
staticbufs[0] = IPC_Desc_StaticBuffer(0x1c,0);
staticbufs[0] = IPC_Desc_StaticBuffer(ADDR_STORAGE_LEN,0);
staticbufs[1] = (u32)tmpaddr;
ret = svcSendSyncRequest(SOCU_handle);
@ -47,11 +49,15 @@ int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
return -1;
}
if(*addrlen > tmpaddr[0])
*addrlen = tmpaddr[0];
memset(addr, 0, sizeof(struct sockaddr));
addr->sa_family = tmpaddr[1];
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - 2);
socklen_t user_addrlen = tmpaddr[0];
if(addr->sa_family == AF_INET)
user_addrlen += 8;
if(*addrlen > user_addrlen)
*addrlen = user_addrlen;
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - sizeof(addr->sa_family));
return ret;
}

View File

@ -6,7 +6,7 @@
static int inet_pton4(const char *restrict src, void *restrict dst)
{
u8 ip[4] ALIGN(4);
u8 ip[4] CTR_ALIGN(4);
if(sscanf(src,"%hhu.%hhu.%hhu.%hhu",&ip[0], &ip[1], &ip[2], &ip[3]) != 4) return 0;
memcpy(dst,ip,4);

View File

@ -8,13 +8,13 @@ ssize_t socuipc_cmd7(int sockfd, void *buf, size_t len, int flags, struct sockad
int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 tmp_addrlen = 0;
u8 tmpaddr[0x1c];
u8 tmpaddr[ADDR_STORAGE_LEN];
u32 saved_threadstorage[2];
memset(tmpaddr, 0, 0x1c);
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
if(src_addr)
tmp_addrlen = 0x1c;
tmp_addrlen = ADDR_STORAGE_LEN;
cmdbuf[0] = IPC_MakeHeader(0x7,4,4); // 0x70104
cmdbuf[1] = (u32)sockfd;
@ -53,9 +53,14 @@ ssize_t socuipc_cmd7(int sockfd, void *buf, size_t len, int flags, struct sockad
if(src_addr != NULL) {
src_addr->sa_family = tmpaddr[1];
if(*addrlen > tmpaddr[0])
*addrlen = tmpaddr[0];
memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - 2);
socklen_t user_addrlen = tmpaddr[0];
if(src_addr->sa_family == AF_INET)
user_addrlen += 8;
if(*addrlen > user_addrlen)
*addrlen = user_addrlen;
memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - sizeof(src_addr->sa_family));
}
return ret;
@ -66,13 +71,13 @@ ssize_t socuipc_cmd8(int sockfd, void *buf, size_t len, int flags, struct sockad
int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 tmp_addrlen = 0;
u8 tmpaddr[0x1c];
u8 tmpaddr[ADDR_STORAGE_LEN];
u32 saved_threadstorage[4];
if(src_addr)
tmp_addrlen = 0x1c;
tmp_addrlen = ADDR_STORAGE_LEN;
memset(tmpaddr, 0, 0x1c);
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
cmdbuf[0] = 0x00080102;
cmdbuf[1] = (u32)sockfd;
@ -113,9 +118,14 @@ ssize_t socuipc_cmd8(int sockfd, void *buf, size_t len, int flags, struct sockad
if(src_addr != NULL) {
src_addr->sa_family = tmpaddr[1];
if(*addrlen > tmpaddr[0])
*addrlen = tmpaddr[0];
memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - 2);
socklen_t user_addrlen = tmpaddr[0];
if(src_addr->sa_family == AF_INET)
user_addrlen += 8;
if(*addrlen > user_addrlen)
*addrlen = user_addrlen;
memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - sizeof(src_addr->sa_family));
}
return ret;

View File

@ -8,15 +8,15 @@ ssize_t socuipc_cmd9(int sockfd, const void *buf, size_t len, int flags, const s
int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 tmp_addrlen = 0;
u8 tmpaddr[0x1c];
u8 tmpaddr[ADDR_STORAGE_LEN];
memset(tmpaddr, 0, 0x1c);
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
if(dest_addr) {
if(dest_addr->sa_family == AF_INET)
tmp_addrlen = 8;
else
tmp_addrlen = 0x1c;
tmp_addrlen = ADDR_STORAGE_LEN;
if(addrlen < tmp_addrlen) {
errno = EINVAL;
@ -62,15 +62,15 @@ ssize_t socuipc_cmda(int sockfd, const void *buf, size_t len, int flags, const s
int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 tmp_addrlen = 0;
u8 tmpaddr[0x1c];
u8 tmpaddr[ADDR_STORAGE_LEN];
memset(tmpaddr, 0, 0x1c);
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
if(dest_addr) {
if(dest_addr->sa_family == AF_INET)
tmp_addrlen = 8;
else
tmp_addrlen = 0x1c;
tmp_addrlen = ADDR_STORAGE_LEN;
if(addrlen < tmp_addrlen) {
errno = EINVAL;

View File

@ -26,7 +26,6 @@ BEGIN_ASM_FUNC initSystem, weak
END_ASM_FUNC
BEGIN_ASM_FUNC __ctru_exit, weak
bl __libc_fini_array
bl __appExit
ldr r2, =saved_stack

View File

@ -156,6 +156,84 @@ void __SYSCALL(exit)(int rc) {
__ctru_exit(rc);
}
int __SYSCALL(cond_signal)(_COND_T *cond)
{
CondVar_Signal((CondVar*)cond);
return 0;
}
int __SYSCALL(cond_broadcast)(_COND_T *cond)
{
CondVar_Broadcast((CondVar*)cond);
return 0;
}
int __SYSCALL(cond_wait)(_COND_T *cond, _LOCK_T *lock, uint64_t timeout_ns)
{
return CondVar_WaitTimeout((CondVar*)cond, lock, timeout_ns) ? ETIMEDOUT : 0;
}
int __SYSCALL(cond_wait_recursive)(_COND_T *cond, _LOCK_RECURSIVE_T *lock, uint64_t timeout_ns)
{
uint32_t thread_tag_backup = 0;
if (lock->counter != 1)
return EBADF;
thread_tag_backup = lock->thread_tag;
lock->thread_tag = 0;
lock->counter = 0;
int err = CondVar_WaitTimeout((CondVar*)cond, &lock->lock, timeout_ns);
lock->thread_tag = thread_tag_backup;
lock->counter = 1;
return err ? ETIMEDOUT : 0;
}
int __SYSCALL(thread_create)(struct __pthread_t **thread, void* (*func)(void*), void *arg, void *stack_addr, size_t stack_size)
{
if (stack_addr) {
return EINVAL;
}
if (!stack_size) {
stack_size = 32*1024;
}
Thread t = threadCreate((ThreadFunc)func, arg, stack_size, 0x3F, 0, false);
if (t) {
*thread = (struct __pthread_t*)t;
return 0;
}
return ENOMEM;
}
void*__SYSCALL(thread_join)(struct __pthread_t *thread)
{
threadJoin((Thread)thread, U64_MAX);
void* rc = (void*)threadGetExitCode((Thread)thread);
threadFree((Thread)thread);
return rc;
}
int __SYSCALL(thread_detach)(struct __pthread_t *thread)
{
threadDetach((Thread)thread);
return 0;
}
void __SYSCALL(thread_exit)(void *value)
{
threadExit((int)value);
}
struct __pthread_t *__SYSCALL(thread_self)(void)
{
return (struct __pthread_t*)threadGetCurrent();
}
void initThreadVars(struct Thread_tag *thread)
{
ThreadVars* tv = getThreadVars();