Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1b1762f872 | ||
![]() |
04b9418dd9 | ||
![]() |
30dc65121d | ||
![]() |
8cbcce773b | ||
![]() |
a88b5af7a8 | ||
![]() |
8e55cdf05d | ||
![]() |
5f06a037d7 | ||
![]() |
cc5b884d7d | ||
![]() |
c5ecf54d51 | ||
![]() |
faf5162b60 | ||
![]() |
a6a6548348 | ||
![]() |
0da8705527 | ||
![]() |
d0936b879b |
12
Changelog.md
12
Changelog.md
@ -1,5 +1,17 @@
|
||||
# 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
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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];
|
||||
@ -34,8 +46,8 @@ Handle *acGetSessionHandle(void);
|
||||
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);
|
||||
|
||||
@ -115,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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
117
libctru/include/3ds/services/qtmc.h
Normal file
117
libctru/include/3ds/services/qtmc.h
Normal 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);
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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(®istered, "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];
|
||||
}
|
||||
|
101
libctru/source/services/qtmc.c
Normal file
101
libctru/source/services/qtmc.c
Normal 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];
|
||||
}
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user