Compare commits

..

No commits in common. "master" and "v2.1.0" have entirely different histories.

70 changed files with 692 additions and 2822 deletions

View File

@ -1,99 +1,5 @@
# Changelog # Changelog
## Version 2.4.0
- **Added full support of all QTM services**, with extensive documentation and technical details in `qtm.h` and `qtmc.h`** (breaking change); examples have been updated for this
- Added MCUHWC_SetInfoLedPattern
- Added ndspChnGetFormat
- Fixed PTMSYSM_CheckNew3DS
- Fixed NDMU_QueryStatus
- Fixed a few service commands improperly deserializing boolean output
- Fixed documentation of ACU_GetWifiStatus and ACU_SetAllowApType
- Fixed shaderInstanceInit to initialize all fields
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 2.3.0
- Fix typo in docs by @DeltaF1 in https://github.com/devkitPro/libctru/pull/525
- Add GPU_DOT3_RGBA texture combiner function by @oreo639 in https://github.com/devkitPro/libctru/pull/528
- FSUSER_GetLegacyBannerData: Fix documentation typo by @Tekito-256 in https://github.com/devkitPro/libctru/pull/526
- Add CTR_ prefix to ALIGN,PACKED,DEPRECATED macros by @glebm in https://github.com/devkitPro/libctru/pull/532
- Buffer console control sequences by @piepie62 in https://github.com/devkitPro/libctru/pull/522
- Prevent CPU from postponing threadOnException memory writes
- Add SSID-related ac:i functions
- ACI_LoadNetworkSetting, ACI_GetNetworkWirelessEssidSecuritySsid and acGetSessionHandle.
- Prevent double call of destructors on exit.
- Added experimental support for standard threading APIs (pthread, C threads, C++ std::thread)
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## New Contributors
- @DeltaF1 made their first contribution in https://github.com/devkitPro/libctru/pull/525
- @Tekito-256 made their first contribution in https://github.com/devkitPro/libctru/pull/526
- @glebm made their first contribution in https://github.com/devkitPro/libctru/pull/532
## Version 2.2.2
- archive_dev: Ensure path separator for local path
- adjust struct hostent for compatibilty
## Version 2.2.1
- add `_SOCKLEN_T_DECLARED`
## Version 2.2.0
- apt: add deliver arg support to chainloader
## Version 2.1.2
- Added cdc:CHK service wrappers
- Added support for `clock_gettime` (#495)
- svc: Fixed svcGetDmaState writing out-of-bounds data
- svc: Changed svcCreateCodeSet address parameters to pointer-sized integers, improve documentation
- fspxi: Fixed FSPXI_CreateFile and FSPXI_WriteFile (#496)
- ndsp: Added various ndspGet\* and ndspChnGet\* methods (#505, #506, #507)
- ir: Added IRU_GetSend/RecvFinishedEvent (#513)
- errf: Added ERRF_SetUserString and clarify documentation
- mcuhwc: Added mcuHwcGetSessionHandle
- apt: Fixed dirty homebrew chainload bug (used when Home Menu hasn't been started)
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 2.1.1
- FPSCR is now initialized with a predictable value in all threads (including the main thread).
- Added gspGetSessionHandle and gspLcdGetSessionHandle.
- Fixed bugs related to uninitialized data in srv/errf service wrappers.
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 2.1.0
**The #define for the 3DS platform has been changed to `__3DS__` (previously it was `_3DS`) - please update your Makefiles and your code**
### graphics
- Refactored VRAM allocators:
- Added proper handling for VRAM banks (A and B, 3 MiB each)
- Allocations no longer cross VRAM bank boundaries
- Added vramAllocAt, vramMemAlignAt
- Add gspIsPresentPending.
- Add return value to gspPresentBuffer.
- libctru console now supports SGR 38 and 48 escape sequences (needed by fmtlib).
- Fixed GPU_TEXFACE enum.
### filesystem
- Changed rename to replace existing files, for better compatibility with POSIX (#483)
- Added SDMMC speed info types, and more clock rates (#480).
### miscellaneous
- Added support for 3dslink stdio redirection (#488).
- Added ptm:gets, ptm:sets and more ptm service commands.
- Added AM_ContentInfo, AM_ContentInfoFlags structs.
- Added AMAPP_GetDLCContentInfoCount, AMAPP_ListDLCContentInfos.
- Fixed bug in Huffman decoder.
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
## Version 2.0.1 ## Version 2.0.1
- Added CFG_SystemModel enum. - Added CFG_SystemModel enum.

View File

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

View File

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

View File

@ -75,12 +75,10 @@ extern "C" {
#include <3ds/services/nfc.h> #include <3ds/services/nfc.h>
#include <3ds/services/news.h> #include <3ds/services/news.h>
#include <3ds/services/qtm.h> #include <3ds/services/qtm.h>
#include <3ds/services/qtmc.h>
#include <3ds/services/srvpm.h> #include <3ds/services/srvpm.h>
#include <3ds/services/loader.h> #include <3ds/services/loader.h>
#include <3ds/services/y2r.h> #include <3ds/services/y2r.h>
#include <3ds/services/mcuhwc.h> #include <3ds/services/mcuhwc.h>
#include <3ds/services/cdcchk.h>
#include <3ds/gpu/gx.h> #include <3ds/gpu/gx.h>
#include <3ds/gpu/gpu.h> #include <3ds/gpu/gpu.h>

View File

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

View File

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

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. * @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 This function has been superseded by \ref gfxScreenSwapBuffers, please use that instead.
*/ */
CTR_DEPRECATED void gfxConfigScreen(gfxScreen_t scr, bool immediate); DEPRECATED void gfxConfigScreen(gfxScreen_t scr, bool immediate);
/** /**
* @brief Updates the configuration of both screens. * @brief Updates the configuration of both screens.

View File

@ -368,8 +368,7 @@ typedef enum
GPU_ADD_SIGNED = 0x03, ///< Signed add. GPU_ADD_SIGNED = 0x03, ///< Signed add.
GPU_INTERPOLATE = 0x04, ///< Interpolate. GPU_INTERPOLATE = 0x04, ///< Interpolate.
GPU_SUBTRACT = 0x05, ///< Subtract. GPU_SUBTRACT = 0x05, ///< Subtract.
GPU_DOT3_RGB = 0x06, ///< Dot3. Scalar result is written to RGB only. GPU_DOT3_RGB = 0x06, ///< Dot3. RGB only.
GPU_DOT3_RGBA = 0x07, ///< Dot3. Scalar result is written to RGBA.
GPU_MULTIPLY_ADD = 0x08, ///< Multiply then add. GPU_MULTIPLY_ADD = 0x08, ///< Multiply then add.
GPU_ADD_MULTIPLY = 0x09, ///< Add then multiply. GPU_ADD_MULTIPLY = 0x09, ///< Add then multiply.
} GPU_COMBINEFUNC; } GPU_COMBINEFUNC;

View File

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

View File

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

View File

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

View File

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

View File

@ -4,8 +4,6 @@
*/ */
#pragma once #pragma once
#include <3ds/types.h>
/// Wifi security modes. /// Wifi security modes.
typedef enum { typedef enum {
AC_OPEN = 0, ///< Open authentication. AC_OPEN = 0, ///< Open authentication.
@ -18,16 +16,6 @@ typedef enum {
AC_WPA2_AES = 7, ///< WPA2 AES authentication. AC_WPA2_AES = 7, ///< WPA2 AES authentication.
} acSecurityMode; } 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. /// Struct to contain the data for connecting to a Wifi network from a stored slot.
typedef struct { typedef struct {
u8 reserved[0x200]; u8 reserved[0x200];
@ -39,15 +27,12 @@ Result acInit(void);
/// Exits AC. /// Exits AC.
void acExit(void); void acExit(void);
/// Gets the current AC session handle.
Handle *acGetSessionHandle(void);
/// Waits for the system to connect to the internet. /// Waits for the system to connect to the internet.
Result acWaitInternetConnection(void); Result acWaitInternetConnection(void);
/** /**
* @brief Describes the access point the console is currently connected to with AC_AP_TYPE_* flags. * @brief Gets the connected Wifi status.
* @param out Pointer to output the combination of AC_AP_TYPE_* flags describing the AP to. * @param out Pointer to output the connected Wifi status to. (0 = not connected, 1 = O3DS Internet, 2 = N3DS Internet)
*/ */
Result ACU_GetWifiStatus(u32 *out); Result ACU_GetWifiStatus(u32 *out);
@ -127,7 +112,7 @@ Result ACU_SetNetworkArea(acuConfig* config, u8 area);
/** /**
* @brief Sets the slot to use when connecting. * @brief Sets the slot to use when connecting.
* @param config Pointer to an acuConfig struct used with ACU_CreateDefaultConfig previously. * @param config Pointer to an acuConfig struct used with ACU_CreateDefaultConfig previously.
* @param type Allowed AP types bitmask, a combination of AC_AP_TYPE_* flags. * @param type Allowed slots flag. BIT(0) for slot 1, BIT(1) for slot 2, BIT(2) for slot 3.
*/ */
Result ACU_SetAllowApType(acuConfig* config, u8 type); Result ACU_SetAllowApType(acuConfig* config, u8 type);
@ -143,15 +128,3 @@ Result ACU_SetRequestEulaVersion(acuConfig* config);
* @param connectionHandle Handle created with svcCreateEvent to wait on until the connection succeeds or fails. * @param connectionHandle Handle created with svcCreateEvent to wait on until the connection succeeds or fails.
*/ */
Result ACU_ConnectAsync(const acuConfig* config, Handle connectionHandle); 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); bool aptCheckHomePressRejected(void);
/// \deprecated Alias for \ref aptCheckHomePressRejected. /// \deprecated Alias for \ref aptCheckHomePressRejected.
static inline CTR_DEPRECATED bool aptIsHomePressed(void) static inline DEPRECATED bool aptIsHomePressed(void)
{ {
return aptCheckHomePressRejected(); return aptCheckHomePressRejected();
} }
@ -237,22 +237,9 @@ void aptClearChainloader(void);
*/ */
void aptSetChainloader(u64 programID, u8 mediatype); void aptSetChainloader(u64 programID, u8 mediatype);
/// Configures the chainloader to launch the previous application.
void aptSetChainloaderToCaller(void);
/// Configures the chainloader to relaunch the current application (i.e. soft-reset) /// Configures the chainloader to relaunch the current application (i.e. soft-reset)
void aptSetChainloaderToSelf(void); void aptSetChainloaderToSelf(void);
/**
* @brief Sets the "deliver arg" and HMAC for the chainloader, which will
* be passed to the target 3DS/DS(i) application. The meaning of each
* parameter varies on a per-application basis.
* @param deliverArg Deliver arg to pass to the target application.
* @param deliverArgSize Size of the deliver arg, maximum 0x300 bytes.
* @param hmac HMAC buffer, 32 bytes. Use NULL to pass an all-zero dummy HMAC.
*/
void aptSetChainloaderArgs(const void *deliverArg, size_t deliverArgSize, const void *hmac);
/** /**
* @brief Gets an APT lock handle. * @brief Gets an APT lock handle.
* @param flags Flags to use. * @param flags Flags to use.
@ -578,4 +565,4 @@ Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr);
* @param sender Pointer to output the sender's AppID to. * @param sender Pointer to output the sender's AppID to.
* @param received Pointer to output whether an argument was received to. * @param received Pointer to output whether an argument was received to.
*/ */
Result APT_ReceiveDeliverArg(void* param, size_t paramSize, void* hmac, u64* sender, bool* received); Result APT_ReceiveDeliverArg(const void* param, size_t paramSize, const void* hmac, u64* sender, bool* received);

View File

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

View File

@ -20,19 +20,18 @@ typedef enum
/// Configuration language values. /// Configuration language values.
typedef enum typedef enum
{ {
CFG_LANGUAGE_DEFAULT = -1, ///< Use system language in errorInit CFG_LANGUAGE_JP = 0, ///< Japanese
CFG_LANGUAGE_JP, ///< Japanese CFG_LANGUAGE_EN = 1, ///< English
CFG_LANGUAGE_EN, ///< English CFG_LANGUAGE_FR = 2, ///< French
CFG_LANGUAGE_FR, ///< French CFG_LANGUAGE_DE = 3, ///< German
CFG_LANGUAGE_DE, ///< German CFG_LANGUAGE_IT = 4, ///< Italian
CFG_LANGUAGE_IT, ///< Italian CFG_LANGUAGE_ES = 5, ///< Spanish
CFG_LANGUAGE_ES, ///< Spanish CFG_LANGUAGE_ZH = 6, ///< Simplified Chinese
CFG_LANGUAGE_ZH, ///< Simplified Chinese CFG_LANGUAGE_KO = 7, ///< Korean
CFG_LANGUAGE_KO, ///< Korean CFG_LANGUAGE_NL = 8, ///< Dutch
CFG_LANGUAGE_NL, ///< Dutch CFG_LANGUAGE_PT = 9, ///< Portugese
CFG_LANGUAGE_PT, ///< Portugese CFG_LANGUAGE_RU = 10, ///< Russian
CFG_LANGUAGE_RU, ///< Russian CFG_LANGUAGE_TW = 11, ///< Traditional Chinese
CFG_LANGUAGE_TW, ///< Traditional Chinese
} CFG_Language; } CFG_Language;
// Configuration system model values. // Configuration system model values.

View File

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

View File

@ -89,12 +89,6 @@ Result gspInit(void);
/// Exits GSPGPU. /// Exits GSPGPU.
void gspExit(void); void gspExit(void);
/**
* @brief Gets a pointer to the current gsp::Gpu session handle.
* @return A pointer to the current gsp::Gpu session handle.
*/
Handle *gspGetSessionHandle(void);
/// Returns true if the application currently has GPU rights. /// Returns true if the application currently has GPU rights.
bool gspHasGpuRight(void); bool gspHasGpuRight(void);

View File

@ -20,12 +20,6 @@ Result gspLcdInit(void);
/// Exits GSPLCD. /// Exits GSPLCD.
void gspLcdExit(void); void gspLcdExit(void);
/**
* @brief Gets a pointer to the current gsp::Lcd session handle.
* @return A pointer to the current gsp::Lcd session handle.
*/
Handle *gspLcdGetSessionHandle(void);
/// Powers on both backlights. /// Powers on both backlights.
Result GSPLCD_PowerOnAllBacklights(void); Result GSPLCD_PowerOnAllBacklights(void);
@ -75,4 +69,4 @@ Result GSPLCD_SetBrightness(u32 screen, u32 brightness);
* @param screen Screen to set the brightness value of. * @param screen Screen to set the brightness value of.
* @param brightness Brightness value set. * @param brightness Brightness value set.
*/ */
Result GSPLCD_SetBrightnessRaw(u32 screen, u32 brightness); Result GSPLCD_SetBrightnessRaw(u32 screen, u32 brightness);

View File

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

View File

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

View File

@ -111,11 +111,10 @@ Result NDMU_ResumeScheduler(void);
Result NDMU_GetCurrentState(ndmState *state); Result NDMU_GetCurrentState(ndmState *state);
/** /**
* @brief Returns a daemon state. * @brief Returns the daemon state.
* @param daemon The specified daemon. * @param state Pointer to write the daemons state to.
* @param state Pointer to write the daemon state to.
*/ */
Result NDMU_QueryStatus(ndmDaemon daemon, ndmDaemonStatus *status); Result NDMU_QueryStatus(ndmDaemonStatus *status);
/** /**
* @brief Sets the scan interval. * @brief Sets the scan interval.

View File

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

View File

@ -1,559 +1,57 @@
/** /**
* @file qtm.h * @file qtm.h
* @brief QTM services. * @brief QTM service.
*
* 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 #pragma once
#include <3ds/types.h> //See also: http://3dbrew.org/wiki/QTM_Services
/// ID of QTM status data (fully enabled/SS3D disabled) in `cfg` /// Head tracking coordinate pair.
#define QTM_STATUS_CFG_BLK_ID 0x180000u typedef struct {
float x; ///< X coordinate.
float y; ///< Y coordinate.
} QTM_HeadTrackingInfoCoord;
/// ID of QTM calibration data in `cfg` /// Head tracking info.
#define QTM_CAL_CFG_BLK_ID 0x180001u 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;
/** /// Initializes QTM.
* @brief QTM enablement status (when cameras not in use by user), set by `qtm:s`. Result qtmInit(void);
* @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. /// Exits QTM.
void qtmExit(void); void qtmExit(void);
/// Checks whether or not a `qtm:u`, `qtm:s` or `qtm:sp` session is active. /**
bool qtmIsInitialized(void); * @brief Checks whether QTM is initialized.
* @return Whether QTM is initialized.
/// Returns a pointer to the current `qtm:u` / `qtm:s` / `qtm:sp` session handle. */
Handle *qtmGetSessionHandle(void); bool qtmCheckInitialized(void);
/** /**
* @brief Gets the current raw eye tracking data, with an optional prediction made for predictionTimePointOrZero = t+dt, * @brief Checks whether a head is fully detected.
* or for the current time point (QTM makes predictions based on gyro data since inner camera runs at 30 FPS). * @param info Tracking info to check.
*
* @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.
*/ */
Result QTMU_GetRawTrackingDataEx(QtmRawTrackingData *outData, s64 predictionTimePointOrZero); bool qtmCheckHeadFullyDetected(QTM_HeadTrackingInfo *info);
/** /**
* @brief Gets the current raw eye tracking data. * @brief Converts QTM coordinates to screen coordinates.
* * @param coord Coordinates to convert.
* @param[out] outData Where to write the raw tracking data to. Cleared to all-zero on failure (instead of being left uninitialized). * @param screen_width Width of the screen. Can be NULL to use the default value for the top screen.
* @return `0xC8A18008` if camera is in use by user, or `0xC8A183EF` if QTM is unavailable (in particular, QTM is always * @param screen_height Height of the screen. Can be NULL to use the default value for the top screen.
* unavailable on N2DSXL), Otherwise, 0 (success). Return value should be checked by caller. * @param x Pointer to output the screen X coordinate to.
* @note Consider using \ref QTMU_GetTrackingData instead. * @param y Pointer to output the screen Y coordinate to.
*/ */
static inline Result QTMU_GetRawTrackingData(QtmRawTrackingData *outData) Result qtmConvertCoordToScreen(QTM_HeadTrackingInfoCoord *coord, float *screen_width, float *screen_height, u32 *x, u32 *y);
{
return QTMU_GetRawTrackingDataEx(outData, 0LL);
}
/** /**
* @brief Gets the current normalized eye tracking data, made suitable for 3D programming with an optional prediction made * @brief Gets the current head tracking info.
* for predictionTimePointOrZero = t+dt, or for the current time point (QTM makes predictions based on gyro data since * @param val Normally 0.
* inner camera runs at 30 FPS). * @param out Pointer to write head tracking info to.
*
* @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 QTMU_GetTrackingDataEx(QtmTrackingData *outData, s64 predictionTimePointOrZero); Result QTM_GetHeadTrackingInfo(u64 val, QTM_HeadTrackingInfo* out);
/**
* @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

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

View File

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

View File

@ -497,8 +497,9 @@ typedef enum {
/// Information on address space for process. All sizes are in pages (0x1000 bytes) /// Information on address space for process. All sizes are in pages (0x1000 bytes)
typedef struct { typedef struct {
u8 name[8]; ///< ASCII name of codeset u8 name[8]; ///< ASCII name of codeset
u16 version; ///< Version field of codeset (unused) u16 unk1;
u16 padding[3]; ///< Padding u16 unk2;
u32 unk3;
u32 text_addr; ///< .text start address u32 text_addr; ///< .text start address
u32 text_size; ///< .text number of pages u32 text_size; ///< .text number of pages
u32 ro_addr; ///< .rodata start address u32 ro_addr; ///< .rodata start address
@ -508,9 +509,9 @@ typedef struct {
u32 text_size_total; ///< total pages for .text (aligned) u32 text_size_total; ///< total pages for .text (aligned)
u32 ro_size_total; ///< total pages for .rodata (aligned) u32 ro_size_total; ///< total pages for .rodata (aligned)
u32 rw_size_total; ///< total pages for .data, .bss (aligned) u32 rw_size_total; ///< total pages for .data, .bss (aligned)
u32 padding2; ///< Padding u32 unk4;
u64 program_id; ///< Program ID u64 program_id; ///< Program ID
} CodeSetHeader; } CodeSetInfo;
/// Information for the main thread of a process. /// Information for the main thread of a process.
typedef struct typedef struct
@ -526,7 +527,7 @@ typedef struct
/** /**
* @brief Gets the thread local storage buffer. * @brief Gets the thread local storage buffer.
* @return The thread local storage buffer. * @return The thread local storage bufger.
*/ */
static inline void* getThreadLocalStorage(void) static inline void* getThreadLocalStorage(void)
{ {
@ -537,7 +538,7 @@ static inline void* getThreadLocalStorage(void)
/** /**
* @brief Gets the thread command buffer. * @brief Gets the thread command buffer.
* @return The thread command buffer. * @return The thread command bufger.
*/ */
static inline u32* getThreadCommandBuffer(void) static inline u32* getThreadCommandBuffer(void)
{ {
@ -546,7 +547,7 @@ static inline u32* getThreadCommandBuffer(void)
/** /**
* @brief Gets the thread static buffer. * @brief Gets the thread static buffer.
* @return The thread static buffer. * @return The thread static bufger.
*/ */
static inline u32* getThreadStaticBuffers(void) static inline u32* getThreadStaticBuffers(void)
{ {
@ -698,7 +699,7 @@ Result svcQueryProcessMemory(MemInfo* info, PageInfo* out, Handle process, u32 a
Result svcOpenProcess(Handle* process, u32 processId); Result svcOpenProcess(Handle* process, u32 processId);
/// Exits the current process. /// Exits the current process.
void svcExitProcess(void) __attribute__((noreturn)); void __attribute__((noreturn)) svcExitProcess();
/** /**
* @brief Terminates a process. * @brief Terminates a process.
@ -755,24 +756,23 @@ Result svcCreatePort(Handle* portServer, Handle* portClient, const char* name, s
Result svcConnectToPort(volatile Handle* out, const char* portName); Result svcConnectToPort(volatile Handle* out, const char* portName);
/** /**
* @brief Sets up virtual address space for a new process. * @brief Sets up virtual address space for a new process
* @param[out] out Pointer to output the codeset handle to. * @param[out] out Pointer to output the code set handle to.
* @param info Codeset header, contains process name, titleId and segment info. * @param info Description for setting up the addresses
* @param textSegmentLma Address of executable segment in caller's address space. * @param code_ptr Pointer to .text in shared memory
* @param roSegmentLma Address of read-only segment in caller's address space. * @param ro_ptr Pointer to .rodata in shared memory
* @param dataSegmentLma Address of read-write segment in caller's address space. * @param data_ptr Pointer to .data in shared memory
* @note On success, the provided segments are unmapped from the caller's address space.
*/ */
Result svcCreateCodeSet(Handle* out, const CodeSetHeader* info, u32 textSegmentLma, u32 roSegmentLma, u32 dataSegmentLma); Result svcCreateCodeSet(Handle* out, const CodeSetInfo *info, void* code_ptr, void* ro_ptr, void* data_ptr);
/** /**
* @brief Create a new process. * @brief Sets up virtual address space for a new process
* @param[out] out Pointer to output the process handle to. * @param[out] out Pointer to output the process handle to.
* @param codeset Codeset created for this process. * @param codeset Codeset created for this process
* @param arm11KernelCaps Arm11 Kernel Capabilities from exheader. * @param arm11kernelcaps ARM11 Kernel Capabilities from exheader
* @param numArm11KernelCaps Number of kernel capabilities. * @param arm11kernelcaps_num Number of kernel capabilities
*/ */
Result svcCreateProcess(Handle* out, Handle codeset, const u32* arm11KernelCaps, s32 numArm11KernelCaps); Result svcCreateProcess(Handle* out, Handle codeset, const u32 *arm11kernelcaps, u32 arm11kernelcaps_num);
/** /**
* @brief Gets a process's affinity mask. * @brief Gets a process's affinity mask.

View File

@ -42,12 +42,6 @@ static inline void __dmb(void)
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory"); __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. /// Performs a clrex operation.
static inline void __clrex(void) static inline void __clrex(void)
{ {

View File

@ -117,7 +117,4 @@ static inline void threadOnException(ExceptionHandler handler, void* stack_top,
*(u32*)(tls + 0x40) = (u32)handler; *(u32*)(tls + 0x40) = (u32)handler;
*(u32*)(tls + 0x44) = (u32)stack_top; *(u32*)(tls + 0x44) = (u32)stack_top;
*(u32*)(tls + 0x48) = (u32)exception_data; *(u32*)(tls + 0x48) = (u32)exception_data;
__dsb();
__isb();
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -205,11 +205,9 @@ archive_fixpath(struct _reent *r,
strncpy(__ctru_dev_path_buf, path, PATH_MAX); strncpy(__ctru_dev_path_buf, path, PATH_MAX);
else else
{ {
size_t cwdlen = strlen(dev->cwd);
strncpy(__ctru_dev_path_buf, dev->cwd, PATH_MAX); strncpy(__ctru_dev_path_buf, dev->cwd, PATH_MAX);
__ctru_dev_path_buf[PATH_MAX] = '\0'; __ctru_dev_path_buf[PATH_MAX] = '\0';
strncat(__ctru_dev_path_buf, "/", PATH_MAX - cwdlen); strncat(__ctru_dev_path_buf, path, PATH_MAX);
strncat(__ctru_dev_path_buf, path, PATH_MAX - cwdlen - 1);
} }
if(__ctru_dev_path_buf[PATH_MAX] != 0) if(__ctru_dev_path_buf[PATH_MAX] != 0)

View File

@ -72,6 +72,65 @@ PrintConsole defaultConsole =
false //console initialized 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 currentCopy;
PrintConsole* currentConsole = &currentCopy; PrintConsole* currentConsole = &currentCopy;
@ -82,7 +141,7 @@ void consolePrintChar(int c);
void consoleDrawChar(int c); void consoleDrawChar(int c);
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
static void consoleCls(int mode) { static void consoleCls(char mode) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int i = 0; int i = 0;
@ -90,7 +149,8 @@ static void consoleCls(int mode) {
switch (mode) switch (mode)
{ {
case 0: case '[':
case '0':
{ {
colTemp = currentConsole->cursorX ; colTemp = currentConsole->cursorX ;
rowTemp = currentConsole->cursorY ; rowTemp = currentConsole->cursorY ;
@ -102,7 +162,7 @@ static void consoleCls(int mode) {
currentConsole->cursorY = rowTemp; currentConsole->cursorY = rowTemp;
break; break;
} }
case 1: case '1':
{ {
colTemp = currentConsole->cursorX ; colTemp = currentConsole->cursorX ;
rowTemp = currentConsole->cursorY ; rowTemp = currentConsole->cursorY ;
@ -117,7 +177,7 @@ static void consoleCls(int mode) {
currentConsole->cursorY = rowTemp; currentConsole->cursorY = rowTemp;
break; break;
} }
case 2: case '2':
{ {
currentConsole->cursorY = 0; currentConsole->cursorY = 0;
currentConsole->cursorX = 0; currentConsole->cursorX = 0;
@ -133,7 +193,7 @@ static void consoleCls(int mode) {
gfxFlushBuffers(); gfxFlushBuffers();
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
static void consoleClearLine(int mode) { static void consoleClearLine(char mode) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int i = 0; int i = 0;
@ -141,7 +201,8 @@ static void consoleClearLine(int mode) {
switch (mode) switch (mode)
{ {
case 0: case '[':
case '0':
{ {
colTemp = currentConsole->cursorX ; colTemp = currentConsole->cursorX ;
@ -153,7 +214,7 @@ static void consoleClearLine(int mode) {
break; break;
} }
case 1: case '1':
{ {
colTemp = currentConsole->cursorX ; colTemp = currentConsole->cursorX ;
@ -167,7 +228,7 @@ static void consoleClearLine(int mode) {
break; break;
} }
case 2: case '2':
{ {
colTemp = currentConsole->cursorX ; colTemp = currentConsole->cursorX ;
@ -210,273 +271,6 @@ static inline void consolePosition(int x, int y) {
currentConsole->cursorY = y - 1; 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) { ssize_t con_write(struct _reent *r,void *fd,const char *ptr, size_t len) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -495,165 +289,305 @@ ssize_t con_write(struct _reent *r,void *fd,const char *ptr, size_t len) {
chr = *(tmp++); chr = *(tmp++);
i++; count++; i++; count++;
switch (escapeSeq.state) if ( chr == 0x1b && len > 1 && *tmp == '[' ) {
{ bool escaping = true;
case ESC_NONE: char *escapeseq = tmp++;
if (chr == 0x1b) int escapelen = 1;
escapeSeq.state = ESC_START; i++; count++;
else
consolePrintChar(chr); do {
break; chr = *(tmp++);
case ESC_START: i++; count++; escapelen++;
if (chr == '[') int parameter, assigned, consumed;
{
escapeSeq.state = ESC_BUILDING_UNKNOWN; // make sure parameters are positive values and delimited by semicolon
memset(escapeSeq.rawBuf, 0, sizeof(escapeSeq.rawBuf)); if((chr >= '0' && chr <= '9') || chr == ';')
memset(escapeSeq.hasArg, 0, sizeof(escapeSeq.hasArg)); continue;
escapeSeq.color.bg = currentConsole->bg;
escapeSeq.color.fg = currentConsole->fg; switch (chr) {
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 // Cursor directional movement
//--------------------------------------- //---------------------------------------
case 'A': case 'A':
if (!escapeSeq.hasArg[0]) consumed = 0;
escapeSeq.directional.movement = 1; assigned = sscanf(escapeseq,"[%dA%n", &parameter, &consumed);
currentConsole->cursorY = (currentConsole->cursorY - escapeSeq.directional.movement) < 0 ? 0 : currentConsole->cursorY - escapeSeq.directional.movement; if (assigned==0) parameter = 1;
escapeSeq.state = ESC_NONE; if (consumed)
currentConsole->cursorY = (currentConsole->cursorY - parameter) < 0 ? 0 : currentConsole->cursorY - parameter;
escaping = false;
break; break;
case 'B': case 'B':
if (!escapeSeq.hasArg[0]) consumed = 0;
escapeSeq.directional.movement = 1; assigned = sscanf(escapeseq,"[%dB%n", &parameter, &consumed);
currentConsole->cursorY = (currentConsole->cursorY + escapeSeq.directional.movement) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY + escapeSeq.directional.movement; if (assigned==0) parameter = 1;
escapeSeq.state = ESC_NONE; if (consumed)
currentConsole->cursorY = (currentConsole->cursorY + parameter) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY + parameter;
escaping = false;
break; break;
case 'C': case 'C':
if (!escapeSeq.hasArg[0]) consumed = 0;
escapeSeq.directional.movement = 1; assigned = sscanf(escapeseq,"[%dC%n", &parameter, &consumed);
currentConsole->cursorX = (currentConsole->cursorX + escapeSeq.directional.movement) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX + escapeSeq.directional.movement; if (assigned==0) parameter = 1;
escapeSeq.state = ESC_NONE; if (consumed)
currentConsole->cursorX = (currentConsole->cursorX + parameter) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX + parameter;
escaping = false;
break; break;
case 'D': case 'D':
if (!escapeSeq.hasArg[0]) consumed = 0;
escapeSeq.directional.movement = 1; assigned = sscanf(escapeseq,"[%dD%n", &parameter, &consumed);
currentConsole->cursorX = (currentConsole->cursorX - escapeSeq.directional.movement) < 0 ? 0 : currentConsole->cursorX - escapeSeq.directional.movement; if (assigned==0) parameter = 1;
escapeSeq.state = ESC_NONE; if (consumed)
currentConsole->cursorX = (currentConsole->cursorX - parameter) < 0 ? 0 : currentConsole->cursorX - parameter;
escaping = false;
break; break;
//--------------------------------------- //---------------------------------------
// Cursor position movement // Cursor position movement
//--------------------------------------- //---------------------------------------
case 'H': case 'H':
case 'f': case 'f':
consolePosition(escapeSeq.hasArg[1] ? escapeSeq.absolute.x : 1, escapeSeq.hasArg[0] ? escapeSeq.absolute.y : 1); {
escapeSeq.state = ESC_NONE; 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;
break; break;
}
//--------------------------------------- //---------------------------------------
// Screen clear // Screen clear
//--------------------------------------- //---------------------------------------
case 'J': case 'J':
consoleCls(escapeSeq.hasArg[0] ? escapeSeq.clear.type : 0); if(escapelen <= 3)
escapeSeq.state = ESC_NONE; consoleCls(escapeseq[escapelen-2]);
escaping = false;
break; break;
//--------------------------------------- //---------------------------------------
// Line clear // Line clear
//--------------------------------------- //---------------------------------------
case 'K': case 'K':
consoleClearLine(escapeSeq.hasArg[0] ? escapeSeq.clear.type : 0); if(escapelen <= 3)
escapeSeq.state = ESC_NONE; consoleClearLine(escapeseq[escapelen-2]);
escaping = false;
break; break;
//--------------------------------------- //---------------------------------------
// Save cursor position // Save cursor position
//--------------------------------------- //---------------------------------------
case 's': case 's':
currentConsole->prevCursorX = currentConsole->cursorX ; if(escapelen == 2) {
currentConsole->prevCursorY = currentConsole->cursorY ; currentConsole->prevCursorX = currentConsole->cursorX ;
escapeSeq.state = ESC_NONE; currentConsole->prevCursorY = currentConsole->cursorY ;
}
escaping = false;
break; break;
//--------------------------------------- //---------------------------------------
// Load cursor position // Load cursor position
//--------------------------------------- //---------------------------------------
case 'u': case 'u':
currentConsole->cursorX = currentConsole->prevCursorX ; if(escapelen == 2) {
currentConsole->cursorY = currentConsole->prevCursorY ; currentConsole->cursorX = currentConsole->prevCursorX ;
escapeSeq.state = ESC_NONE; currentConsole->cursorY = currentConsole->prevCursorY ;
}
escaping = false;
break; break;
//--------------------------------------- //---------------------------------------
// Color scan codes // Color scan codes
//--------------------------------------- //---------------------------------------
case 'm': case 'm':
consoleColorStateShift(); escapeseq++;
consoleColorApply(); escapelen--;
escapeSeq.state = ESC_NONE;
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;
break; break;
default: default:
// some sort of unsupported escape; just gloss over it // some sort of unsupported escape; just gloss over it
escapeSeq.state = ESC_NONE; escaping = false;
break; break;
} }
break; } while (escaping);
default: continue;
switch (chr) }
{
case '0': consolePrintChar(chr);
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; return count;
@ -713,8 +647,6 @@ PrintConsole* consoleInit(gfxScreen_t screen, PrintConsole* console) {
setvbuf(stdout, NULL , _IONBF, 0); setvbuf(stdout, NULL , _IONBF, 0);
setvbuf(stderr, NULL , _IONBF, 0); setvbuf(stderr, NULL , _IONBF, 0);
memset(&escapeSeq, 0, sizeof(escapeSeq));
firstConsoleInit = false; firstConsoleInit = false;
} }
@ -741,7 +673,7 @@ PrintConsole* consoleInit(gfxScreen_t screen, PrintConsole* console) {
console->windowWidth = isWide ? 100 : 50; console->windowWidth = isWide ? 100 : 50;
} }
consoleCls(2); consoleCls('2');
return currentConsole; return currentConsole;
@ -753,18 +685,18 @@ void consoleDebugInit(debugDevice device){
int buffertype = _IONBF; int buffertype = _IONBF;
switch(device) switch(device) {
{
case debugDevice_SVC: case debugDevice_SVC:
devoptab_list[STD_ERR] = &dotab_svc; devoptab_list[STD_ERR] = &dotab_svc;
buffertype = _IOLBF; buffertype = _IOLBF;
break; break;
case debugDevice_CONSOLE: case debugDevice_CONSOLE:
devoptab_list[STD_ERR] = &dotab_stdout; devoptab_list[STD_ERR] = &dotab_stdout;
break; break;
case debugDevice_NULL: case debugDevice_NULL:
devoptab_list[STD_ERR] = &dotab_null; devoptab_list[STD_ERR] = &dotab_null;
break; break;
} }
setvbuf(stderr, NULL , buffertype, 0); setvbuf(stderr, NULL , buffertype, 0);
@ -811,7 +743,7 @@ static void newRow() {
src += 240; src += 240;
} }
consoleClearLine(2); consoleClearLine('2');
} }
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -942,7 +874,7 @@ void consolePrintChar(int c) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void consoleClear(void) { void consoleClear(void) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
consoleCls(2); consoleCls('2');
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -960,3 +892,5 @@ void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height
console->cursorY = 0; console->cursorY = 0;
} }

View File

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

View File

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

View File

@ -13,9 +13,17 @@ Result shaderInstanceInit(shaderInstance_s* si, DVLE_s* dvle)
{ {
if(!si || !dvle)return -1; if(!si || !dvle)return -1;
memset(si, 0, sizeof(*si));
si->dvle = dvle; 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; int i;
DVLE_constEntry_s* cnst = dvle->constTableData; DVLE_constEntry_s* cnst = dvle->constTableData;
if(cnst) if(cnst)

View File

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

View File

@ -100,11 +100,6 @@ void ndspChnSetFormat(int id, u16 format)
ndspChn[id].format = format; ndspChn[id].format = format;
} }
u16 ndspChnGetFormat(int id)
{
return ndspChn[id].format;
}
bool ndspChnIsPaused(int id) bool ndspChnIsPaused(int id)
{ {
return ndspChn[id].paused; return ndspChn[id].paused;
@ -128,12 +123,6 @@ void ndspChnSetInterp(int id, ndspInterpType type)
LightLock_Unlock(&chn->lock); LightLock_Unlock(&chn->lock);
} }
ndspInterpType ndspChnGetInterp(int id)
{
ndspChnSt* chn = &ndspChn[id];
return chn->interpType;
}
void ndspChnSetRate(int id, float rate) void ndspChnSetRate(int id, float rate)
{ {
ndspChnSt* chn = &ndspChn[id]; ndspChnSt* chn = &ndspChn[id];
@ -143,12 +132,6 @@ void ndspChnSetRate(int id, float rate)
LightLock_Unlock(&chn->lock); LightLock_Unlock(&chn->lock);
} }
float ndspChnGetRate(int id)
{
ndspChnSt* chn = &ndspChn[id];
return chn->rate;
}
void ndspChnSetMix(int id, float mix[12]) void ndspChnSetMix(int id, float mix[12])
{ {
ndspChnSt* chn = &ndspChn[id]; ndspChnSt* chn = &ndspChn[id];
@ -158,14 +141,6 @@ void ndspChnSetMix(int id, float mix[12])
LightLock_Unlock(&chn->lock); LightLock_Unlock(&chn->lock);
} }
void ndspChnGetMix(int id, float out_mix[12])
{
ndspChnSt* chn = &ndspChn[id];
LightLock_Lock(&chn->lock);
memcpy(out_mix, chn->mix, sizeof(ndspChn[id].mix));
LightLock_Unlock(&chn->lock);
}
void ndspChnSetAdpcmCoefs(int id, u16 coefs[16]) void ndspChnSetAdpcmCoefs(int id, u16 coefs[16])
{ {
ndspChnSt* chn = &ndspChn[id]; ndspChnSt* chn = &ndspChn[id];

View File

@ -612,11 +612,6 @@ void ndspSetMasterVol(float volume)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
float ndspGetMasterVol(void)
{
return ndspMaster.masterVol;
}
void ndspSetOutputMode(ndspOutputMode mode) void ndspSetOutputMode(ndspOutputMode mode)
{ {
LightLock_Lock(&ndspMaster.lock); LightLock_Lock(&ndspMaster.lock);
@ -625,11 +620,6 @@ void ndspSetOutputMode(ndspOutputMode mode)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
ndspOutputMode ndspGetOutputMode(void)
{
return ndspMaster.outputMode;
}
void ndspSetClippingMode(ndspClippingMode mode) void ndspSetClippingMode(ndspClippingMode mode)
{ {
LightLock_Lock(&ndspMaster.lock); LightLock_Lock(&ndspMaster.lock);
@ -638,11 +628,6 @@ void ndspSetClippingMode(ndspClippingMode mode)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
ndspClippingMode ndspGetClippingMode(void)
{
return ndspMaster.clippingMode;
}
void ndspSetOutputCount(int count) void ndspSetOutputCount(int count)
{ {
LightLock_Lock(&ndspMaster.lock); LightLock_Lock(&ndspMaster.lock);
@ -651,11 +636,6 @@ void ndspSetOutputCount(int count)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
int ndspGetOutputCount(void)
{
return ndspMaster.outputCount;
}
void ndspSetCapture(ndspWaveBuf* capture) void ndspSetCapture(ndspWaveBuf* capture)
{ {
ndspMaster.capture = capture; ndspMaster.capture = capture;
@ -675,11 +655,6 @@ void ndspSurroundSetDepth(u16 depth)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
u16 ndspSurroundGetDepth(void)
{
return ndspMaster.surround.depth;
}
void ndspSurroundSetPos(ndspSpeakerPos pos) void ndspSurroundSetPos(ndspSpeakerPos pos)
{ {
LightLock_Lock(&ndspMaster.lock); LightLock_Lock(&ndspMaster.lock);
@ -688,11 +663,6 @@ void ndspSurroundSetPos(ndspSpeakerPos pos)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
ndspSpeakerPos ndspSurroundGetPos(void)
{
return ndspMaster.surround.pos;
}
void ndspSurroundSetRearRatio(u16 ratio) void ndspSurroundSetRearRatio(u16 ratio)
{ {
LightLock_Lock(&ndspMaster.lock); LightLock_Lock(&ndspMaster.lock);
@ -701,11 +671,6 @@ void ndspSurroundSetRearRatio(u16 ratio)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
u16 ndspSurroundGetRearRatio(void)
{
return ndspMaster.surround.rearRatio;
}
void ndspAuxSetEnable(int id, bool enable) void ndspAuxSetEnable(int id, bool enable)
{ {
LightLock_Lock(&ndspMaster.lock); LightLock_Lock(&ndspMaster.lock);
@ -714,11 +679,6 @@ void ndspAuxSetEnable(int id, bool enable)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
bool ndspAuxIsEnabled(int id)
{
return ndspMaster.aux[id].enable;
}
void ndspAuxSetFrontBypass(int id, bool bypass) void ndspAuxSetFrontBypass(int id, bool bypass)
{ {
LightLock_Lock(&ndspMaster.lock); LightLock_Lock(&ndspMaster.lock);
@ -727,11 +687,6 @@ void ndspAuxSetFrontBypass(int id, bool bypass)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
bool ndspGetFrontBypass(int id)
{
return ndspMaster.aux[id].frontBypass;
}
void ndspAuxSetVolume(int id, float volume) void ndspAuxSetVolume(int id, float volume)
{ {
LightLock_Lock(&ndspMaster.lock); LightLock_Lock(&ndspMaster.lock);
@ -740,11 +695,6 @@ void ndspAuxSetVolume(int id, float volume)
LightLock_Unlock(&ndspMaster.lock); LightLock_Unlock(&ndspMaster.lock);
} }
float ndspAuxGetVolume(int id)
{
return ndspMaster.aux[id].volume;
}
void ndspAuxSetCallback(int id, ndspAuxCallback callback, void* data) void ndspAuxSetCallback(int id, ndspAuxCallback callback, void* data)
{ {
ndspMaster.aux[id].callback = callback; ndspMaster.aux[id].callback = callback;

View File

@ -6,7 +6,6 @@
#include <3ds/synchronization.h> #include <3ds/synchronization.h>
#include <3ds/services/ac.h> #include <3ds/services/ac.h>
#include <3ds/ipc.h> #include <3ds/ipc.h>
#include <string.h>
static Handle acHandle; static Handle acHandle;
static int acRefCount; static int acRefCount;
@ -31,11 +30,6 @@ void acExit(void)
svcCloseHandle(acHandle); svcCloseHandle(acHandle);
} }
Handle *acGetSessionHandle(void)
{
return &acHandle;
}
Result acWaitInternetConnection(void) Result acWaitInternetConnection(void)
{ {
Result ret = 0; Result ret = 0;
@ -290,36 +284,3 @@ Result ACU_GetProxyUserName(char *username)
return (Result)cmdbuf[1]; 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

@ -63,12 +63,9 @@ enum
static u8 aptHomeButtonState; static u8 aptHomeButtonState;
static u32 aptFlags; static u32 aptFlags;
static u32 aptParameters[0x1000/4]; static u32 aptParameters[0x1000/4];
static u64 aptChainloadTid;
static u8 aptChainloadDeliverArg[0x300];
static u32 aptChainloadDeliverArgSize = sizeof(aptChainloadDeliverArg);
static u8 aptChainloadHmac[0x20];
static u8 aptChainloadMediatype;
static u8 aptChainloadFlags; static u8 aptChainloadFlags;
static u64 aptChainloadTid;
static u8 aptChainloadMediatype;
typedef enum typedef enum
{ {
@ -318,9 +315,6 @@ static void aptClearJumpToHome(void)
void aptClearChainloader(void) void aptClearChainloader(void)
{ {
aptFlags &= ~FLAG_CHAINLOAD; aptFlags &= ~FLAG_CHAINLOAD;
aptChainloadDeliverArgSize = sizeof(aptChainloadDeliverArg);
memset(aptChainloadDeliverArg, 0, sizeof(aptChainloadDeliverArg));
memset(aptChainloadHmac, 0, sizeof(aptChainloadHmac));
} }
void aptSetChainloader(u64 programID, u8 mediatype) void aptSetChainloader(u64 programID, u8 mediatype)
@ -331,14 +325,6 @@ void aptSetChainloader(u64 programID, u8 mediatype)
aptChainloadMediatype = mediatype; aptChainloadMediatype = mediatype;
} }
void aptSetChainloaderToCaller(void)
{
aptFlags |= FLAG_CHAINLOAD;
aptChainloadFlags = 1;
aptChainloadTid = 0;
aptChainloadMediatype = 0;
}
void aptSetChainloaderToSelf(void) void aptSetChainloaderToSelf(void)
{ {
aptFlags |= FLAG_CHAINLOAD; aptFlags |= FLAG_CHAINLOAD;
@ -347,20 +333,6 @@ void aptSetChainloaderToSelf(void)
aptChainloadMediatype = 0; aptChainloadMediatype = 0;
} }
void aptSetChainloaderArgs(const void *deliverArg, size_t deliverArgSize, const void *hmac)
{
if (deliverArgSize >= sizeof(aptChainloadDeliverArg))
deliverArgSize = sizeof(aptChainloadDeliverArg);
aptChainloadDeliverArgSize = deliverArgSize;
memcpy(aptChainloadDeliverArg, deliverArg, deliverArgSize);
if (hmac != NULL)
memcpy(aptChainloadHmac, hmac, sizeof(aptChainloadHmac));
else
memset(aptChainloadHmac, 0, sizeof(aptChainloadHmac));
}
extern void (*__system_retAddr)(void); extern void (*__system_retAddr)(void);
static void aptExitProcess(void) static void aptExitProcess(void)
@ -373,7 +345,6 @@ void aptExit(void)
if (AtomicDecrement(&aptRefCount)) return; if (AtomicDecrement(&aptRefCount)) return;
bool closeAptLock = true; bool closeAptLock = true;
bool doDirtyChainload = false;
if (!aptIsCrippled()) if (!aptIsCrippled())
{ {
@ -398,14 +369,16 @@ void aptExit(void)
if (R_SUCCEEDED(APT_IsRegistered(aptGetMenuAppID(), &hmRegistered)) && hmRegistered) if (R_SUCCEEDED(APT_IsRegistered(aptGetMenuAppID(), &hmRegistered)) && hmRegistered)
{ {
// Normal, sane chainload // Normal, sane chainload
u8 param[0x300] = {0};
u8 hmac[0x20] = {0};
APT_PrepareToDoApplicationJump(aptChainloadFlags, aptChainloadTid, aptChainloadMediatype); APT_PrepareToDoApplicationJump(aptChainloadFlags, aptChainloadTid, aptChainloadMediatype);
APT_DoApplicationJump(aptChainloadDeliverArg, aptChainloadDeliverArgSize, aptChainloadHmac); APT_DoApplicationJump(param, sizeof(param), hmac);
} }
else else
{ {
// XX: HOME menu doesn't exist, so we need to use a workaround provided by Luma3DS // XX: HOME menu doesn't exist, so we need to use a workaround provided by Luma3DS
APT_Finalize(envGetAptAppId()); APT_Finalize(envGetAptAppId());
doDirtyChainload = true; srvPublishToSubscriber(0x3000, 0);
} }
// After a chainload has been applied, we don't need to manually close // After a chainload has been applied, we don't need to manually close
@ -440,30 +413,6 @@ void aptExit(void)
if (closeAptLock) if (closeAptLock)
svcCloseHandle(aptLockHandle); svcCloseHandle(aptLockHandle);
if (doDirtyChainload)
{
// Provided by Luma3DS
Handle notificationHandle = 0;
Result res = 0;
u32 notificationNumber = 0;
srvEnableNotification(&notificationHandle);
// Not needed, but official (sysmodule) code does this:
srvSubscribe(0x100);
// Make PM modify our run flags and ask us to terminate
srvPublishToSubscriber(0x3000, 0);
do
{
// Bail out after 3 seconds, we don't want to wait forever for this
res = svcWaitSynchronization(notificationHandle, 3 * 1000 * 1000LL);
res = res == 0 ? srvReceiveNotification(&notificationNumber) : res;
} while(res == 0 && notificationNumber != 0x100);
svcCloseHandle(notificationHandle);
}
} }
void aptEventHandler(void *arg) void aptEventHandler(void *arg)
@ -1456,7 +1405,7 @@ Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr)
return ret; return ret;
} }
Result APT_ReceiveDeliverArg(void* param, size_t paramSize, void* hmac, u64* sender, bool* received) Result APT_ReceiveDeliverArg(const void* param, size_t paramSize, const void* hmac, u64* sender, bool* received)
{ {
u32 cmdbuf[16]; u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x35,2,0); // 0x350080 cmdbuf[0]=IPC_MakeHeader(0x35,2,0); // 0x350080

View File

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

View File

@ -107,8 +107,8 @@ Result FSPXI_CreateFile(Handle serviceHandle, FSPXI_Archive archive, FS_Path pat
cmdbuf[6] = attributes; cmdbuf[6] = attributes;
cmdbuf[7] = (u32)fileSize; cmdbuf[7] = (u32)fileSize;
cmdbuf[8] = (u32)(fileSize >> 32); cmdbuf[8] = (u32)(fileSize >> 32);
cmdbuf[9] = IPC_Desc_PXIBuffer(path.size, 0, true); cmdbuf[8] = IPC_Desc_PXIBuffer(path.size, 0, true);
cmdbuf[10] = (u32) path.data; cmdbuf[9] = (u32) path.data;
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret; if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
@ -229,8 +229,8 @@ Result FSPXI_WriteFile(Handle serviceHandle, FSPXI_File file, u32* bytesWritten,
cmdbuf[2] = (u32)(file >> 32); cmdbuf[2] = (u32)(file >> 32);
cmdbuf[3] = (u32) offset; cmdbuf[3] = (u32) offset;
cmdbuf[4] = (u32) (offset >> 32); cmdbuf[4] = (u32) (offset >> 32);
cmdbuf[5] = size; cmdbuf[5] = flags;
cmdbuf[6] = flags; cmdbuf[6] = size;
cmdbuf[7] = IPC_Desc_PXIBuffer(size, 0, true); cmdbuf[7] = IPC_Desc_PXIBuffer(size, 0, true);
cmdbuf[8] = (u32) buffer; cmdbuf[8] = (u32) buffer;
@ -376,7 +376,7 @@ Result FSPXI_HasFile(Handle serviceHandle, FSPXI_Archive archive, bool* out, FS_
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret; if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if (out) *out = (bool)(cmdbuf[2] & 1); if (out) *out = (bool)cmdbuf[2];
return (Result) cmdbuf[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(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if (out) *out = (bool)(cmdbuf[2] & 1); if (out) *out = (bool)cmdbuf[2];
return (Result) cmdbuf[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(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if (out) *out = (bool)(cmdbuf[2] & 1); if (out) *out = (bool)cmdbuf[2];
return (Result) cmdbuf[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(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(out) *out = (bool)(cmdbuf[2] & 1); if(out) *out = (bool)cmdbuf[2];
return (Result) cmdbuf[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(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(out) *out = (bool)(cmdbuf[2] & 1); if(out) *out = (bool)cmdbuf[2];
return (Result) cmdbuf[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(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(inserted) *inserted = (bool)(cmdbuf[2] & 1); if(inserted) *inserted = (bool)cmdbuf[2];
return (Result) cmdbuf[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(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(status) *status = (bool)(cmdbuf[2] & 1); if(status) *status = (bool)cmdbuf[2];
return (Result) cmdbuf[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(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(status) *status = (bool)(cmdbuf[2] & 1); if(status) *status = (bool)cmdbuf[2];
return (Result) cmdbuf[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(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
if(status) *status = (bool)(cmdbuf[2] & 1); if(status) *status = (bool)cmdbuf[2];
return (Result) cmdbuf[1]; return (Result) cmdbuf[1];
} }

View File

@ -184,12 +184,6 @@ Result gspInit(void)
gspSharedMem = mappableAlloc(0x1000); gspSharedMem = mappableAlloc(0x1000);
svcMapMemoryBlock(gspSharedMemHandle, (u32)gspSharedMem, MEMPERM_READWRITE, MEMPERM_DONTCARE); 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 // Start event handling thread
gspRunEvents = true; gspRunEvents = true;
gspLastEvent = -1; gspLastEvent = -1;
@ -227,11 +221,6 @@ void gspExit(void)
svcCloseHandle(gspGpuHandle); svcCloseHandle(gspGpuHandle);
} }
Handle *gspGetSessionHandle(void)
{
return &gspGpuHandle;
}
bool gspHasGpuRight(void) bool gspHasGpuRight(void)
{ {
return gspGpuRight; return gspGpuRight;

View File

@ -26,11 +26,6 @@ void gspLcdExit(void)
svcCloseHandle(gspLcdHandle); svcCloseHandle(gspLcdHandle);
} }
Handle *gspLcdGetSessionHandle(void)
{
return &gspLcdHandle;
}
Result GSPLCD_PowerOnAllBacklights(void) Result GSPLCD_PowerOnAllBacklights(void)
{ {
u32 *cmdbuf = getThreadCommandBuffer(); u32 *cmdbuf = getThreadCommandBuffer();
@ -149,4 +144,4 @@ Result GSPLCD_SetBrightnessRaw(u32 screen, u32 brightness)
if (R_FAILED(ret = svcSendSyncRequest(gspLcdHandle))) return ret; if (R_FAILED(ret = svcSendSyncRequest(gspLcdHandle))) return ret;
return cmdbuf[1]; return cmdbuf[1];
} }

View File

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

View File

@ -1,4 +1,3 @@
#include <string.h>
#include <3ds/types.h> #include <3ds/types.h>
#include <3ds/svc.h> #include <3ds/svc.h>
#include <3ds/synchronization.h> #include <3ds/synchronization.h>
@ -24,11 +23,6 @@ void mcuHwcExit(void)
svcCloseHandle(mcuHwcHandle); svcCloseHandle(mcuHwcHandle);
} }
Handle* mcuHwcGetSessionHandle(void)
{
return &mcuHwcHandle;
}
Result MCUHWC_ReadRegister(u8 reg, void* data, u32 size) Result MCUHWC_ReadRegister(u8 reg, void* data, u32 size)
{ {
Result ret = 0; Result ret = 0;
@ -115,22 +109,6 @@ Result MCUHWC_SetWifiLedState(bool state)
return (Result)cmdbuf[1]; 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 MCUHWC_GetSoundSliderLevel(u8 *level)
{ {
Result ret = 0; Result ret = 0;

View File

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

View File

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

View File

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

View File

@ -7,389 +7,76 @@
#include <3ds/synchronization.h> #include <3ds/synchronization.h>
#include <3ds/services/qtm.h> #include <3ds/services/qtm.h>
#include <3ds/ipc.h> #include <3ds/ipc.h>
#include <math.h>
Handle qtmHandle; Handle qtmHandle;
static int qtmRefCount; static int qtmRefCount;
bool qtmCheckServicesRegistered(void) Result qtmInit(void)
{ {
bool registered = false; Result ret=0;
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; if (AtomicPostIncrement(&qtmRefCount)) return 0;
Result res = srvGetServiceHandle(&qtmHandle, name);
if (R_FAILED(res)) ret = srvGetServiceHandle(&qtmHandle, "qtm:u");
{ if (R_FAILED(ret)) ret = srvGetServiceHandle(&qtmHandle, "qtm:s");
qtmHandle = 0; if (R_FAILED(ret)) ret = srvGetServiceHandle(&qtmHandle, "qtm:sp");
AtomicDecrement(&qtmRefCount); if (R_FAILED(ret)) AtomicDecrement(&qtmRefCount);
} return ret;
return res;
} }
void qtmExit(void) void qtmExit(void)
{ {
if (AtomicDecrement(&qtmRefCount)) return; if (AtomicDecrement(&qtmRefCount)) return;
svcCloseHandle(qtmHandle); svcCloseHandle(qtmHandle);
qtmHandle = 0;
} }
bool qtmIsInitialized(void) bool qtmCheckInitialized(void)
{ {
// Use qtmHandle instead of qtmRefCount in the event user return qtmRefCount>0;
// wants to bypass qtmInit and use *qtmGetSessionHandle() instead
// (e.g. stolen session).
return qtmHandle != 0;
} }
Handle *qtmGetSessionHandle(void) bool qtmCheckHeadFullyDetected(QTM_HeadTrackingInfo *info)
{ {
return &qtmHandle; if(info==NULL)return false;
if(info->flags[0] && info->flags[1] && info->flags[2])return true;
return false;
} }
Result QTMU_GetRawTrackingDataEx(QtmRawTrackingData *outData, s64 predictionTimePointOrZero) Result qtmConvertCoordToScreen(QTM_HeadTrackingInfoCoord *coord, float *screen_width, float *screen_height, u32 *x, u32 *y)
{ {
Result res = 0; float width = 200.0f;
u32 *cmdbuf = getThreadCommandBuffer(); float height = 160.0f;
cmdbuf[0] = IPC_MakeHeader(0x1, 2, 0); // 0x10080 if(coord==NULL || x==NULL || y==NULL)return -1;
memcpy(&cmdbuf[1], &predictionTimePointOrZero, 8);
res = svcSendSyncRequest(qtmHandle); if(screen_width)width = (*screen_width) / 2;
if (R_FAILED(res)) return res; if(screen_height)height = (*screen_height) / 2;
memcpy(outData, &cmdbuf[2], sizeof(QtmRawTrackingData)); 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));
return cmdbuf[1]; 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

@ -1,101 +0,0 @@
#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 accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
{ {
int ret = 0; int ret = 0;
int tmp_addrlen = ADDR_STORAGE_LEN; int tmp_addrlen = 0x1c;
int fd, dev; int fd, dev;
u32 *cmdbuf = getThreadCommandBuffer(); u32 *cmdbuf = getThreadCommandBuffer();
u8 tmpaddr[ADDR_STORAGE_LEN]; u8 tmpaddr[0x1c];
u32 saved_threadstorage[2]; u32 saved_threadstorage[2];
sockfd = soc_get_fd(sockfd); sockfd = soc_get_fd(sockfd);
@ -27,7 +27,7 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
fd = __alloc_handle(dev); fd = __alloc_handle(dev);
if(fd < 0) return fd; if(fd < 0) return fd;
memset(tmpaddr, 0, ADDR_STORAGE_LEN); memset(tmpaddr, 0, 0x1c);
cmdbuf[0] = IPC_MakeHeader(0x4,2,2); // 0x40082 cmdbuf[0] = IPC_MakeHeader(0x4,2,2); // 0x40082
cmdbuf[1] = (u32)sockfd; cmdbuf[1] = (u32)sockfd;
@ -61,14 +61,9 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
if(ret >= 0 && addr != NULL) { if(ret >= 0 && addr != NULL) {
addr->sa_family = tmpaddr[1]; addr->sa_family = tmpaddr[1];
if(*addrlen > tmpaddr[0])
socklen_t user_addrlen = tmpaddr[0]; *addrlen = tmpaddr[0];
if(addr->sa_family == AF_INET) memcpy(addr->sa_data, &tmpaddr[2], *addrlen - 2);
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) { if(ret < 0) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@
static int inet_pton4(const char *restrict src, void *restrict dst) static int inet_pton4(const char *restrict src, void *restrict dst)
{ {
u8 ip[4] CTR_ALIGN(4); u8 ip[4] ALIGN(4);
if(sscanf(src,"%hhu.%hhu.%hhu.%hhu",&ip[0], &ip[1], &ip[2], &ip[3]) != 4) return 0; if(sscanf(src,"%hhu.%hhu.%hhu.%hhu",&ip[0], &ip[1], &ip[2], &ip[3]) != 4) return 0;
memcpy(dst,ip,4); 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; int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer(); u32 *cmdbuf = getThreadCommandBuffer();
u32 tmp_addrlen = 0; u32 tmp_addrlen = 0;
u8 tmpaddr[ADDR_STORAGE_LEN]; u8 tmpaddr[0x1c];
u32 saved_threadstorage[2]; u32 saved_threadstorage[2];
memset(tmpaddr, 0, ADDR_STORAGE_LEN); memset(tmpaddr, 0, 0x1c);
if(src_addr) if(src_addr)
tmp_addrlen = ADDR_STORAGE_LEN; tmp_addrlen = 0x1c;
cmdbuf[0] = IPC_MakeHeader(0x7,4,4); // 0x70104 cmdbuf[0] = IPC_MakeHeader(0x7,4,4); // 0x70104
cmdbuf[1] = (u32)sockfd; cmdbuf[1] = (u32)sockfd;
@ -53,14 +53,9 @@ ssize_t socuipc_cmd7(int sockfd, void *buf, size_t len, int flags, struct sockad
if(src_addr != NULL) { if(src_addr != NULL) {
src_addr->sa_family = tmpaddr[1]; src_addr->sa_family = tmpaddr[1];
if(*addrlen > tmpaddr[0])
socklen_t user_addrlen = tmpaddr[0]; *addrlen = tmpaddr[0];
if(src_addr->sa_family == AF_INET) memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - 2);
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; return ret;
@ -71,13 +66,13 @@ ssize_t socuipc_cmd8(int sockfd, void *buf, size_t len, int flags, struct sockad
int ret = 0; int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer(); u32 *cmdbuf = getThreadCommandBuffer();
u32 tmp_addrlen = 0; u32 tmp_addrlen = 0;
u8 tmpaddr[ADDR_STORAGE_LEN]; u8 tmpaddr[0x1c];
u32 saved_threadstorage[4]; u32 saved_threadstorage[4];
if(src_addr) if(src_addr)
tmp_addrlen = ADDR_STORAGE_LEN; tmp_addrlen = 0x1c;
memset(tmpaddr, 0, ADDR_STORAGE_LEN); memset(tmpaddr, 0, 0x1c);
cmdbuf[0] = 0x00080102; cmdbuf[0] = 0x00080102;
cmdbuf[1] = (u32)sockfd; cmdbuf[1] = (u32)sockfd;
@ -118,14 +113,9 @@ ssize_t socuipc_cmd8(int sockfd, void *buf, size_t len, int flags, struct sockad
if(src_addr != NULL) { if(src_addr != NULL) {
src_addr->sa_family = tmpaddr[1]; src_addr->sa_family = tmpaddr[1];
if(*addrlen > tmpaddr[0])
socklen_t user_addrlen = tmpaddr[0]; *addrlen = tmpaddr[0];
if(src_addr->sa_family == AF_INET) memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - 2);
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; 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; int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer(); u32 *cmdbuf = getThreadCommandBuffer();
u32 tmp_addrlen = 0; u32 tmp_addrlen = 0;
u8 tmpaddr[ADDR_STORAGE_LEN]; u8 tmpaddr[0x1c];
memset(tmpaddr, 0, ADDR_STORAGE_LEN); memset(tmpaddr, 0, 0x1c);
if(dest_addr) { if(dest_addr) {
if(dest_addr->sa_family == AF_INET) if(dest_addr->sa_family == AF_INET)
tmp_addrlen = 8; tmp_addrlen = 8;
else else
tmp_addrlen = ADDR_STORAGE_LEN; tmp_addrlen = 0x1c;
if(addrlen < tmp_addrlen) { if(addrlen < tmp_addrlen) {
errno = EINVAL; 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; int ret = 0;
u32 *cmdbuf = getThreadCommandBuffer(); u32 *cmdbuf = getThreadCommandBuffer();
u32 tmp_addrlen = 0; u32 tmp_addrlen = 0;
u8 tmpaddr[ADDR_STORAGE_LEN]; u8 tmpaddr[0x1c];
memset(tmpaddr, 0, ADDR_STORAGE_LEN); memset(tmpaddr, 0, 0x1c);
if(dest_addr) { if(dest_addr) {
if(dest_addr->sa_family == AF_INET) if(dest_addr->sa_family == AF_INET)
tmp_addrlen = 8; tmp_addrlen = 8;
else else
tmp_addrlen = ADDR_STORAGE_LEN; tmp_addrlen = 0x1c;
if(addrlen < tmp_addrlen) { if(addrlen < tmp_addrlen) {
errno = EINVAL; errno = EINVAL;

View File

@ -35,13 +35,10 @@ Result srvInit(void)
rc = svcDuplicateHandle(&srvHandle, *srvPmGetSessionHandle()); // Prior to system version 7.0 srv:pm was a superset of srv: rc = svcDuplicateHandle(&srvHandle, *srvPmGetSessionHandle()); // Prior to system version 7.0 srv:pm was a superset of srv:
else else
rc = svcConnectToPort(&srvHandle, "srv:"); rc = svcConnectToPort(&srvHandle, "srv:");
if (R_FAILED(rc)) goto end;
if (R_SUCCEEDED(rc)) rc = srvRegisterClient();
rc = srvRegisterClient(); end:
else
// Prior to system version 11.0, the kernel filled the resulting handle with junk in case of failure
srvHandle = 0;
if (R_FAILED(rc)) srvExit(); if (R_FAILED(rc)) srvExit();
return rc; return rc;
} }
@ -99,11 +96,11 @@ Result srvEnableNotification(Handle* semaphoreOut)
cmdbuf[0] = IPC_MakeHeader(0x2,0,0); cmdbuf[0] = IPC_MakeHeader(0x2,0,0);
rc = svcSendSyncRequest(srvHandle); if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
if(semaphoreOut) *semaphoreOut = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
return rc; if(semaphoreOut) *semaphoreOut = cmdbuf[3];
return cmdbuf[1];
} }
Result srvRegisterService(Handle* out, const char* name, int maxSessions) Result srvRegisterService(Handle* out, const char* name, int maxSessions)
@ -116,11 +113,11 @@ Result srvRegisterService(Handle* out, const char* name, int maxSessions)
cmdbuf[3] = strnlen(name, 8); cmdbuf[3] = strnlen(name, 8);
cmdbuf[4] = maxSessions; cmdbuf[4] = maxSessions;
rc = svcSendSyncRequest(srvHandle); if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
if(out) *out = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
return rc; if(out) *out = cmdbuf[3];
return cmdbuf[1];
} }
Result srvUnregisterService(const char* name) Result srvUnregisterService(const char* name)
@ -147,11 +144,11 @@ Result srvGetServiceHandleDirect(Handle* out, const char* name)
cmdbuf[3] = strnlen(name, 8); cmdbuf[3] = strnlen(name, 8);
cmdbuf[4] = (u32)srvGetBlockingPolicy(); // per-thread setting, default is blocking cmdbuf[4] = (u32)srvGetBlockingPolicy(); // per-thread setting, default is blocking
rc = svcSendSyncRequest(srvHandle); if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
if(out) *out = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
return rc; if(out) *out = cmdbuf[3];
return cmdbuf[1];
} }
Result srvRegisterPort(const char* name, Handle clientHandle) Result srvRegisterPort(const char* name, Handle clientHandle)
@ -194,11 +191,11 @@ Result srvGetPort(Handle* out, const char* name)
cmdbuf[3] = strnlen(name, 8); cmdbuf[3] = strnlen(name, 8);
cmdbuf[4] = 0x0; cmdbuf[4] = 0x0;
rc = svcSendSyncRequest(srvHandle); if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
if(out) *out = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
return rc; if(out) *out = cmdbuf[3];
return cmdbuf[1];
} }
Result srvWaitForPortRegistered(const char* name) Result srvWaitForPortRegistered(const char* name)
@ -248,11 +245,11 @@ Result srvReceiveNotification(u32* notificationIdOut)
cmdbuf[0] = IPC_MakeHeader(0xB,0,0); // 0xB0000 cmdbuf[0] = IPC_MakeHeader(0xB,0,0); // 0xB0000
rc = svcSendSyncRequest(srvHandle); if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
if(notificationIdOut) *notificationIdOut = R_SUCCEEDED(rc) ? cmdbuf[2] : 0;
return rc; if(notificationIdOut) *notificationIdOut = cmdbuf[2];
return cmdbuf[1];
} }
Result srvPublishToSubscriber(u32 notificationId, u32 flags) Result srvPublishToSubscriber(u32 notificationId, u32 flags)
@ -277,18 +274,12 @@ Result srvPublishAndGetSubscriber(u32* processIdCountOut, u32* processIdsOut, u3
cmdbuf[0] = IPC_MakeHeader(0xD,1,0); // 0xD0040 cmdbuf[0] = IPC_MakeHeader(0xD,1,0); // 0xD0040
cmdbuf[1] = notificationId; cmdbuf[1] = notificationId;
rc = svcSendSyncRequest(srvHandle); if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
if (R_SUCCEEDED(rc)) if(processIdCountOut) *processIdCountOut = cmdbuf[2];
{ if(processIdsOut) memcpy(processIdsOut, &cmdbuf[3], cmdbuf[2] * sizeof(u32));
if(processIdCountOut) *processIdCountOut = cmdbuf[2];
if(processIdsOut) memcpy(processIdsOut, &cmdbuf[3], cmdbuf[2] * sizeof(u32));
}
else if(processIdCountOut)
*processIdCountOut = 0;
return rc; return cmdbuf[1];
} }
Result srvIsServiceRegistered(bool* registeredOut, const char* name) Result srvIsServiceRegistered(bool* registeredOut, const char* name)
@ -300,12 +291,11 @@ Result srvIsServiceRegistered(bool* registeredOut, const char* name)
strncpy((char*) &cmdbuf[1], name,8); strncpy((char*) &cmdbuf[1], name,8);
cmdbuf[3] = strnlen(name, 8); cmdbuf[3] = strnlen(name, 8);
rc = svcSendSyncRequest(srvHandle); if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
if(registeredOut) *registeredOut = R_SUCCEEDED(rc) && (cmdbuf[2] & 0xFF) != 0; if(registeredOut) *registeredOut = cmdbuf[2] & 0xFF;
return rc; return cmdbuf[1];
} }
Result srvIsPortRegistered(bool* registeredOut, const char* name) Result srvIsPortRegistered(bool* registeredOut, const char* name)

View File

@ -510,7 +510,7 @@ SVC_BEGIN svcGetDmaState
str r0, [sp, #-4]! str r0, [sp, #-4]!
svc 0x57 svc 0x57
ldr r3, [sp], #4 ldr r3, [sp], #4
strb r1, [r3] str r1, [r3]
bx lr bx lr
SVC_END SVC_END

View File

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

View File

@ -3,19 +3,20 @@
#include <sys/lock.h> #include <sys/lock.h>
#include <sys/reent.h> #include <sys/reent.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <errno.h>
#include <3ds/types.h> #include <3ds/types.h>
#include <3ds/svc.h> #include <3ds/svc.h>
#include <3ds/env.h> #include <3ds/env.h>
#include <3ds/os.h> #include <3ds/os.h>
#include <3ds/synchronization.h> #include <3ds/synchronization.h>
#include "../internal.h" #include "../internal.h"
void __ctru_exit(int rc); void __ctru_exit(int rc);
extern const u8 __tdata_lma[];
extern const u8 __tdata_lma_end[];
extern u8 __tls_start[];
struct _reent* __SYSCALL(getreent)() struct _reent* __SYSCALL(getreent)()
{ {
ThreadVars* tv = getThreadVars(); ThreadVars* tv = getThreadVars();
@ -27,64 +28,6 @@ struct _reent* __SYSCALL(getreent)()
return tv->reent; return tv->reent;
} }
int __SYSCALL(clock_gettime)(clockid_t clock_id, struct timespec *tp) {
if (clock_id == CLOCK_REALTIME)
{
if (tp != NULL)
{
// Retrieve current time, adjusting epoch from 1900 to 1970
s64 ms_since_epoch = osGetTime() - 2208988800000ULL;
tp->tv_sec = ms_since_epoch / 1000;
tp->tv_nsec = (ms_since_epoch % 1000) * 1000000;
}
}
else if (clock_id == CLOCK_MONOTONIC)
{
if (tp != NULL)
{
// Use the ticks directly, as it offer the highest precision
u64 ticks_since_boot = svcGetSystemTick();
tp->tv_sec = ticks_since_boot / SYSCLOCK_ARM11;
tp->tv_nsec = ((ticks_since_boot % SYSCLOCK_ARM11) * 1000000000ULL) / SYSCLOCK_ARM11;
}
}
else
{
errno = EINVAL;
return -1;
}
return 0;
}
int __SYSCALL(clock_getres)(clockid_t clock_id, struct timespec *res) {
if (clock_id == CLOCK_REALTIME)
{
if (res != NULL)
{
res->tv_sec = 0;
res->tv_nsec = 1000000;
}
}
else if (clock_id == CLOCK_MONOTONIC)
{
if (res != NULL)
{
res->tv_sec = 0;
res->tv_nsec = 1;
}
}
else
{
errno = EINVAL;
return -1;
}
return 0;
}
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int __SYSCALL(gettod_r)(struct _reent *ptr, struct timeval *tp, struct timezone *tz) { int __SYSCALL(gettod_r)(struct _reent *ptr, struct timeval *tp, struct timezone *tz) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -94,7 +37,7 @@ int __SYSCALL(gettod_r)(struct _reent *ptr, struct timeval *tp, struct timezone
// Convert to struct timeval // Convert to struct timeval
tp->tv_sec = now / 1000; tp->tv_sec = now / 1000;
tp->tv_usec = (now % 1000) * 1000; tp->tv_usec = (now - 1000*tp->tv_sec) * 1000;
} }
if (tz != NULL) { if (tz != NULL) {
@ -156,110 +99,19 @@ void __SYSCALL(exit)(int rc) {
__ctru_exit(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();
tv->magic = THREADVARS_MAGIC;
tv->reent = thread != NULL ? &thread->reent : _impure_ptr;
tv->thread_ptr = thread;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
tv->tls_tp = (thread != NULL ? (u8*)thread->stacktop : __tls_start) - 8; // Arm ELF TLS ABI mandates an 8-byte header
#pragma GCC diagnostic pop
tv->srv_blocking_policy = false;
// Kernel does not initialize fpscr at all, so we must do it ourselves
// https://developer.arm.com/documentation/ddi0360/f/vfp-programmers-model/vfp11-system-registers/floating-point-status-and-control-register--fpscr
// All flags clear, all interrupts disabled, all instruction scalar.
// As for the 3 below fields: default NaN mode, flush-to-zero both enabled & round to nearest.
__builtin_arm_set_fpscr(BIT(25) | BIT(24) | (0u << 22));
}
void __system_initSyscalls(void) void __system_initSyscalls(void)
{ {
// Initialize thread vars for the main thread // Initialize thread vars for the main thread
initThreadVars(NULL); ThreadVars* tv = getThreadVars();
tv->magic = THREADVARS_MAGIC;
tv->reent = _impure_ptr;
tv->thread_ptr = NULL;
tv->tls_tp = __tls_start-8; // ARM ELF TLS ABI mandates an 8-byte header
tv->srv_blocking_policy = false;
u32 tls_size = __tdata_lma_end - __tdata_lma; u32 tls_size = __tdata_lma_end - __tdata_lma;
size_t tdata_start = alignTo((size_t)__tls_start, __tdata_align);
if (tls_size) if (tls_size)
memcpy((void*)tdata_start, __tdata_lma, tls_size); memcpy(__tls_start, __tdata_lma, tls_size);
} }

View File

@ -3,6 +3,22 @@
#include <malloc.h> #include <malloc.h>
#include <string.h> #include <string.h>
extern const u8 __tdata_lma[];
extern const u8 __tdata_lma_end[];
extern u8 __tls_start[];
extern u8 __tls_end[];
struct Thread_tag
{
Handle handle;
ThreadFunc ep;
void* arg;
int rc;
bool detached, finished;
struct _reent reent;
void* stacktop;
};
static void __panic(void) static void __panic(void)
{ {
svcBreak(USERBREAK_PANIC); svcBreak(USERBREAK_PANIC);
@ -12,33 +28,30 @@ static void __panic(void)
static void _thread_begin(void* arg) static void _thread_begin(void* arg)
{ {
Thread t = (Thread)arg; Thread t = (Thread)arg;
initThreadVars(t); ThreadVars* tv = getThreadVars();
tv->magic = THREADVARS_MAGIC;
tv->reent = &t->reent;
tv->thread_ptr = t;
tv->tls_tp = (u8*)t->stacktop-8; // ARM ELF TLS ABI mandates an 8-byte header
tv->srv_blocking_policy = false;
t->ep(t->arg); t->ep(t->arg);
threadExit(0); threadExit(0);
} }
Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int prio, int core_id, bool detached) Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int prio, int core_id, bool detached)
{ {
// The stack must be 8-aligned at minimum. size_t stackoffset = (sizeof(struct Thread_tag)+7)&~7;
size_t align = __tdata_align > 8 ? __tdata_align : 8; size_t allocsize = stackoffset + ((stack_size+7)&~7);
size_t stackoffset = alignTo(sizeof(struct Thread_tag), align);
size_t allocsize = alignTo(stackoffset + stack_size, align);
size_t tlssize = __tls_end-__tls_start; size_t tlssize = __tls_end-__tls_start;
size_t tlsloadsize = __tdata_lma_end-__tdata_lma; size_t tlsloadsize = __tdata_lma_end-__tdata_lma;
size_t tbsssize = tlssize - tlsloadsize; size_t tbsssize = tlssize-tlsloadsize;
// memalign seems to have an implicit requirement that (size % align) == 0.
// Without this, it seems to return NULL whenever (align > 8).
size_t size = alignTo(allocsize + tlssize, align);
// Guard against overflow // Guard against overflow
if (allocsize < stackoffset) return NULL; if (allocsize < stackoffset) return NULL;
if ((allocsize - stackoffset) < stack_size) return NULL; if ((allocsize-stackoffset) < stack_size) return NULL;
if (size < allocsize) return NULL; if ((allocsize+tlssize) < allocsize) return NULL;
Thread t = (Thread)memalign(align, size); Thread t = (Thread)memalign(8,allocsize+tlssize);
if (!t) return NULL; if (!t) return NULL;
t->ep = entrypoint; t->ep = entrypoint;
@ -47,14 +60,10 @@ Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int pri
t->finished = false; t->finished = false;
t->stacktop = (u8*)t + allocsize; t->stacktop = (u8*)t + allocsize;
// ThreadVars.tls_tp must be aligned correctly, so we bump tdata_start to
// ensure that after subtracting 8 bytes for the TLS header, it will be aligned.
size_t tdata_start = 8 + alignTo((size_t)t->stacktop - 8, align);
if (tlsloadsize) if (tlsloadsize)
memcpy((void*)tdata_start, __tdata_lma, tlsloadsize); memcpy(t->stacktop, __tdata_lma, tlsloadsize);
if (tbsssize) if (tbsssize)
memset((void*)tdata_start + tlsloadsize, 0, tbsssize); memset((u8*)t->stacktop+tlsloadsize, 0, tbsssize);
// Set up child thread's reent struct, inheriting standard file handles // Set up child thread's reent struct, inheriting standard file handles
_REENT_INIT_PTR(&t->reent); _REENT_INIT_PTR(&t->reent);