diff --git a/libctru/Makefile b/libctru/Makefile index df51cae..74b999d 100644 --- a/libctru/Makefile +++ b/libctru/Makefile @@ -30,6 +30,7 @@ SOURCES := source \ source/ndsp \ source/services \ source/services/soc \ + source/applets \ source/util/rbtree \ source/util/utf \ source/system diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index 8975a65..7b8ed57 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -14,6 +14,7 @@ extern "C" { #include <3ds/ipc.h> #include <3ds/svc.h> #include <3ds/srv.h> +#include <3ds/errf.h> #include <3ds/os.h> #include <3ds/synchronization.h> #include <3ds/thread.h> @@ -28,6 +29,7 @@ extern "C" { #include <3ds/services/ac.h> #include <3ds/services/am.h> +#include <3ds/services/ampxi.h> #include <3ds/services/apt.h> #include <3ds/services/cam.h> #include <3ds/services/cfgnor.h> @@ -49,6 +51,7 @@ extern "C" { #include <3ds/services/ps.h> #include <3ds/services/ptmu.h> #include <3ds/services/ptmsysm.h> +#include <3ds/services/pxidev.h> #include <3ds/services/soc.h> #include <3ds/services/mic.h> #include <3ds/services/mvd.h> @@ -59,17 +62,16 @@ extern "C" { #include <3ds/services/y2r.h> #include <3ds/services/hb.h> -#include <3ds/services/ampxi.h> - #include <3ds/gpu/gx.h> #include <3ds/gpu/gpu.h> -#include <3ds/gpu/gpu-old.h> #include <3ds/gpu/shbin.h> #include <3ds/gpu/shaderProgram.h> #include <3ds/ndsp/ndsp.h> #include <3ds/ndsp/channel.h> +#include <3ds/applets/swkbd.h> + #include <3ds/sdmc.h> #include <3ds/romfs.h> #include <3ds/font.h> @@ -104,4 +106,4 @@ extern "C" { * @example threads/event/source/main.c * @example time/rtc/source/main.c */ - + diff --git a/libctru/include/3ds/applets/swkbd.h b/libctru/include/3ds/applets/swkbd.h new file mode 100644 index 0000000..02c6e21 --- /dev/null +++ b/libctru/include/3ds/applets/swkbd.h @@ -0,0 +1,320 @@ +/** + * @file swkbd.h + * @brief Software keyboard applet. + */ +#pragma once + +/// Keyboard types. +typedef enum +{ + SWKBD_TYPE_NORMAL = 0, ///< Normal keyboard with several pages (QWERTY/accents/symbol/mobile) + SWKBD_TYPE_QWERTY, ///< QWERTY keyboard only. + SWKBD_TYPE_NUMPAD, ///< Number pad. + SWKBD_TYPE_WESTERN, ///< On JPN systems, a text keyboard without Japanese input capabilities, otherwise same as SWKBD_TYPE_NORMAL. +} SwkbdType; + +/// Accepted input types. +typedef enum +{ + SWKBD_ANYTHING = 0, ///< All inputs are accepted. + SWKBD_NOTEMPTY, ///< Empty inputs are not accepted. + SWKBD_NOTEMPTY_NOTBLANK, ///< Empty or blank inputs (consisting solely of whitespace) are not accepted. + SWKBD_NOTBLANK_NOTEMPTY = SWKBD_NOTEMPTY_NOTBLANK, + SWKBD_NOTBLANK, ///< Blank inputs (consisting solely of whitespace) are not accepted, but empty inputs are. + SWKBD_FIXEDLEN, ///< The input must have a fixed length (specified by maxTextLength in swkbdInit). +} SwkbdValidInput; + +/// Keyboard dialog buttons. +typedef enum +{ + SWKBD_BUTTON_LEFT = 0, ///< Left button (usually Cancel) + SWKBD_BUTTON_MIDDLE, ///< Middle button (usually I Forgot) + SWKBD_BUTTON_RIGHT, ///< Right button (usually OK) + SWKBD_BUTTON_CONFIRM = SWKBD_BUTTON_RIGHT, + SWKBD_BUTTON_NONE, ///< No button (returned by swkbdInputText in special cases) +} SwkbdButton; + +/// Keyboard password modes. +typedef enum +{ + SWKBD_PASSWORD_NONE = 0, ///< Characters are not concealed. + SWKBD_PASSWORD_HIDE, ///< Characters are concealed immediately. + SWKBD_PASSWORD_HIDE_DELAY, ///< Characters are concealed a second after they've been typed. +} SwkbdPasswordMode; + +/// Keyboard input filtering flags. +enum +{ + SWKBD_FILTER_DIGITS = BIT(0), ///< Disallow the use of more than a certain number of digits (0 or more) + SWKBD_FILTER_AT = BIT(1), ///< Disallow the use of the @ sign. + SWKBD_FILTER_PERCENT = BIT(2), ///< Disallow the use of the % sign. + SWKBD_FILTER_BACKSLASH = BIT(3), ///< Disallow the use of the \ sign. + SWKBD_FILTER_PROFANITY = BIT(4), ///< Disallow profanity using Nintendo's profanity filter. + SWKBD_FILTER_CALLBACK = BIT(5), ///< Use a callback in order to check the input. +}; + +/// Keyboard features. +enum +{ + SWKBD_PARENTAL = BIT(0), ///< Parental PIN mode. + SWKBD_DARKEN_TOP_SCREEN = BIT(1), ///< Darken the top screen when the keyboard is shown. + SWKBD_PREDICTIVE_INPUT = BIT(2), ///< Enable predictive input (necessary for Kanji input in JPN systems). + SWKBD_MULTILINE = BIT(3), ///< Enable multiline input. + SWKBD_FIXED_WIDTH = BIT(4), ///< Enable fixed-width mode. + SWKBD_ALLOW_HOME = BIT(5), ///< Allow the usage of the HOME button. + SWKBD_ALLOW_RESET = BIT(6), ///< Allow the usage of a software-reset combination. + SWKBD_ALLOW_POWER = BIT(7), ///< Allow the usage of the POWER button. + SWKBD_DEFAULT_QWERTY = BIT(9), ///< Default to the QWERTY page when the keyboard is shown. +}; + +/// Keyboard filter callback return values. +typedef enum +{ + SWKBD_CALLBACK_OK = 0, ///< Specifies that the input is valid. + SWKBD_CALLBACK_CLOSE, ///< Displays an error message, then closes the keyboard. + SWKBD_CALLBACK_CONTINUE, ///< Displays an error message and continues displaying the keyboard. +} SwkbdCallbackResult; + +/// Keyboard return values. +typedef enum +{ + SWKBD_NONE = -1, ///< Dummy/unused. + SWKBD_INVALID_INPUT = -2, ///< Invalid parameters to swkbd. + SWKBD_OUTOFMEM = -3, ///< Out of memory. + + SWKBD_D0_CLICK = 0, ///< The button was clicked in 1-button dialogs. + SWKBD_D1_CLICK0, ///< The left button was clicked in 2-button dialogs. + SWKBD_D1_CLICK1, ///< The right button was clicked in 2-button dialogs. + SWKBD_D2_CLICK0, ///< The left button was clicked in 3-button dialogs. + SWKBD_D2_CLICK1, ///< The middle button was clicked in 3-button dialogs. + SWKBD_D2_CLICK2, ///< The right button was clicked in 3-button dialogs. + + SWKBD_HOMEPRESSED = 10, ///< The HOME button was pressed. + SWKBD_RESETPRESSED, ///< The soft-reset key combination was pressed. + SWKBD_POWERPRESSED, ///< The POWER button was pressed. + + SWKBD_PARENTAL_OK = 20, ///< The parental PIN was verified successfully. + SWKBD_PARENTAL_FAIL, ///< The parental PIN was incorrect. + + SWKBD_BANNED_INPUT = 30, ///< The filter callback returned SWKBD_CALLBACK_CLOSE. +} SwkbdResult; + +/// Maximum dictionary word length, in UTF-16 code units. +#define SWKBD_MAX_WORD_LEN 40 +/// Maximum button text length, in UTF-16 code units. +#define SWKBD_MAX_BUTTON_TEXT_LEN 16 +/// Maximum hint text length, in UTF-16 code units. +#define SWKBD_MAX_HINT_TEXT_LEN 64 +/// Maximum filter callback error message length, in UTF-16 code units. +#define SWKBD_MAX_CALLBACK_MSG_LEN 256 + +/// Keyboard dictionary word for predictive input. +typedef struct +{ + u16 reading[SWKBD_MAX_WORD_LEN+1]; ///< Reading of the word (that is, the string that needs to be typed). + u16 word[SWKBD_MAX_WORD_LEN+1]; ///< Spelling of the word. + u8 language; ///< Language the word applies to. + bool all_languages; ///< Specifies if the word applies to all languages. +} SwkbdDictWord; + +/// Keyboard filter callback function. +typedef SwkbdCallbackResult (* SwkbdCallbackFn)(void* user, const char** ppMessage, const char* text, size_t textlen); +/// Keyboard status data. +typedef struct { u32 data[0x11]; } SwkbdStatusData; +/// Keyboard predictive input learning data. +typedef struct { u32 data[0x291B]; } SwkbdLearningData; + +/// Internal libctru book-keeping structure for software keyboards. +typedef struct +{ + const char* initial_text; + const SwkbdDictWord* dict; + SwkbdStatusData* status_data; + SwkbdLearningData* learning_data; + SwkbdCallbackFn callback; + void* callback_user; +} SwkbdExtra; + +/// Software keyboard parameter structure, it shouldn't be modified directly. +typedef struct +{ + int type; + int num_buttons_m1; + int valid_input; + int password_mode; + int is_parental_screen; + int darken_top_screen; + u32 filter_flags; + u32 save_state_flags; + u16 max_text_len; + u16 dict_word_count; + u16 max_digits; + u16 button_text[3][SWKBD_MAX_BUTTON_TEXT_LEN+1]; + u16 numpad_keys[2]; + u16 hint_text[SWKBD_MAX_HINT_TEXT_LEN+1]; + bool predictive_input; + bool multiline; + bool fixed_width; + bool allow_home; + bool allow_reset; + bool allow_power; + bool unknown; // XX: what is this supposed to do? "communicateWithOtherRegions" + bool default_qwerty; + bool button_submits_text[4]; + u16 language; // XX: not working? supposedly 0 = use system language, CFG_Language+1 = pick language + int initial_text_offset; + int dict_offset; + int initial_status_offset; + int initial_learning_offset; + size_t shared_memory_size; + u32 version; + SwkbdResult result; + int status_offset; + int learning_offset; + int text_offset; + u16 text_length; + int callback_result; + u16 callback_msg[SWKBD_MAX_CALLBACK_MSG_LEN+1]; + bool skip_at_check; + union + { + u8 reserved[171]; + SwkbdExtra extra; + }; +} SwkbdState; + +/** + * @brief Initializes software keyboard status. + * @param swkbd Pointer to swkbd state. + * @param type Keyboard type. + * @param numButtons Number of dialog buttons to display (1, 2 or 3). + * @param maxTextLength Maximum number of UTF-16 code units that input text can have (or -1 to let libctru use a big default). + */ +void swkbdInit(SwkbdState* swkbd, SwkbdType type, int numButtons, int maxTextLength); + +/** + * @brief Configures password mode in a software keyboard. + * @param swkbd Pointer to swkbd state. + * @param mode Password mode. + */ +static inline void swkbdSetPasswordMode(SwkbdState* swkbd, SwkbdPasswordMode mode) +{ + swkbd->password_mode = mode; +} + +/** + * @brief Configures input validation in a software keyboard. + * @param swkbd Pointer to swkbd state. + * @param validInput Specifies which inputs are valid. + * @param filterFlags Bitmask specifying which characters are disallowed (filtered). + * @param maxDigits In case digits are disallowed, specifies how many digits are allowed at maximum in input strings (0 completely restricts digit input). + */ +static inline void swkbdSetValidation(SwkbdState* swkbd, SwkbdValidInput validInput, u32 filterFlags, int maxDigits) +{ + swkbd->valid_input = validInput; + swkbd->filter_flags = filterFlags; + swkbd->max_digits = (filterFlags & SWKBD_FILTER_DIGITS) ? maxDigits : 0; +} + +/** + * @brief Configures what characters will the two bottom keys in a numpad produce. + * @param swkbd Pointer to swkbd state. + * @param left Unicode codepoint produced by the leftmost key in the bottom row (0 hides the key). + * @param left Unicode codepoint produced by the rightmost key in the bottom row (0 hides the key). + */ +static inline void swkbdSetNumpadKeys(SwkbdState* swkbd, int left, int right) +{ + swkbd->numpad_keys[0] = left; + swkbd->numpad_keys[1] = right; +} + +/** + * @brief Specifies which special features are enabled in a software keyboard. + * @param swkbd Pointer to swkbd state. + * @param features Feature bitmask. + */ +void swkbdSetFeatures(SwkbdState* swkbd, u32 features); + +/** + * @brief Sets the hint text of a software keyboard (that is, the help text that is displayed when the textbox is empty). + * @param swkbd Pointer to swkbd state. + * @param text Hint text. + */ +void swkbdSetHintText(SwkbdState* swkbd, const char* text); + +/** + * @brief Configures a dialog button in a software keyboard. + * @param swkbd Pointer to swkbd state. + * @param button Specifies which button to configure. + * @param text Button text. + * @param submit Specifies whether pushing the button will submit the text or discard it. + */ +void swkbdSetButton(SwkbdState* swkbd, SwkbdButton button, const char* text, bool submit); + +/** + * @brief Sets the initial text that a software keyboard will display on launch. + * @param swkbd Pointer to swkbd state. + * @param text Initial text. + */ +void swkbdSetInitialText(SwkbdState* swkbd, const char* text); + +/** + * @brief Configures a word in a predictive dictionary for use with a software keyboard. + * @param word Pointer to dictionary word structure. + * @param reading Reading of the word, that is, the sequence of characters that need to be typed to trigger the word in the predictive input system. + * @param text Spelling of the word, that is, the actual characters that will be produced when the user decides to select the word. + */ +void swkbdSetDictWord(SwkbdDictWord* word, const char* reading, const char* text); + +/** + * @brief Sets the custom word dictionary to be used with the predictive input system of a software keyboard. + * @param swkbd Pointer to swkbd state. + * @param dict Pointer to dictionary words. + * @param wordCount Number of words in the dictionary. + */ +void swkbdSetDictionary(SwkbdState* swkbd, const SwkbdDictWord* dict, int wordCount); + +/** + * @brief Configures software keyboard internal status management. + * @param swkbd Pointer to swkbd state. + * @param data Pointer to internal status structure (can be in, out or both depending on the other parameters). + * @param in Specifies whether the data should be read from the structure when the keyboard is launched. + * @param out Specifies whether the data should be written to the structure when the keyboard is closed. + */ +void swkbdSetStatusData(SwkbdState* swkbd, SwkbdStatusData* data, bool in, bool out); + +/** + * @brief Configures software keyboard predictive input learning data management. + * @param swkbd Pointer to swkbd state. + * @param data Pointer to learning data structure (can be in, out or both depending on the other parameters). + * @param in Specifies whether the data should be read from the structure when the keyboard is launched. + * @param out Specifies whether the data should be written to the structure when the keyboard is closed. + */ +void swkbdSetLearningData(SwkbdState* swkbd, SwkbdLearningData* data, bool in, bool out); + +/** + * @brief Configures a custom function to be used to check the validity of input when it is submitted in a software keyboard. + * @param swkbd Pointer to swkbd state. + * @param callback Filter callback function. + * @param user Custom data to be passed to the callback function. + */ +void swkbdSetFilterCallback(SwkbdState* swkbd, SwkbdCallbackFn callback, void* user); + +/** + * @brief Launches a software keyboard in order to input text. + * @param swkbd Pointer to swkbd state. + * @param buf Pointer to output buffer which will hold the inputted text. + * @param bufsize Maximum number of UTF-8 code units that the buffer can hold (including null terminator). + * @return The identifier of the dialog button that was pressed, or SWKBD_BUTTON_NONE if a different condition was triggered - in that case use swkbdGetResult to check the condition. + */ +SwkbdButton swkbdInputText(SwkbdState* swkbd, char* buf, size_t bufsize); + +/** + * @brief Retrieves the result condition of a software keyboard after it has been used. + * @param swkbd Pointer to swkbd state. + * @return The result value. + */ +static inline SwkbdResult swkbdGetResult(SwkbdState* swkbd) +{ + return swkbd->result; +} diff --git a/libctru/include/3ds/errf.h b/libctru/include/3ds/errf.h new file mode 100644 index 0000000..70123a9 --- /dev/null +++ b/libctru/include/3ds/errf.h @@ -0,0 +1,118 @@ +/** + * @file errf.h + * @brief Error Display API + */ + +#pragma once + +#include <3ds/types.h> + +/// Used for register dumps. +typedef struct { + u32 r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, cpsr; +} ERRF_ExceptionContext; + +/// Types of errors that can be thrown by err:f. +typedef enum { + ERRF_ERRTYPE_GENERIC = 0, ///< For generic errors. Shows miscellaneous info. + ERRF_ERRTYPE_MEM_CORRUPT = 1, ///< Same output as generic, but informs the user that "the System Memory has been damaged". + ERRF_ERRTYPE_CARD_REMOVED = 2, ///< Displays the "The Game Card was removed." message. + ERRF_ERRTYPE_EXCEPTION = 3, ///< For exceptions, or more specifically 'crashes'. union data should be exception_data. + ERRF_ERRTYPE_FAILURE = 4, ///< For general failure. Shows a message. union data should have a string set in failure_mesg + ERRF_ERRTYPE_LOGGED = 5 ///< Outputs logs to NAND in some cases. +} ERRF_ErrType; + +/// Types of 'Exceptions' thrown for ERRF_ERRTYPE_EXCEPTION +typedef enum { + ERRF_EXCEPTION_PREFETCH_ABORT = 0, ///< Prefetch Abort + ERRF_EXCEPTION_DATA_ABORT = 1, ///< Data abort + ERRF_EXCEPTION_UNDEFINED = 2, ///< Undefined instruction + ERRF_EXCEPTION_VFP = 3 ///< VFP (floating point) exception. +} ERRF_ExceptionType; + +typedef struct { + ERRF_ExceptionType type; ///< Type of the exception. One of the ERRF_EXCEPTION_* values. + u8 reserved[3]; + u32 reg1; ///< If type is prefetch, this should be ifsr, and on data abort dfsr + u32 reg2; ///< If type is prefetch, this should be r15, and dfar on data abort + u32 fpexc; + u32 fpinst; + u32 fpint2; +} ERRF_ExceptionInfo; + +typedef struct { + ERRF_ExceptionInfo excep; ///< Exception info struct + ERRF_ExceptionContext regs; ///< Register dump. + u8 pad[4]; +} ERRF_ExceptionData; + +typedef struct { + ERRF_ErrType type; ///< Type, one of the ERRF_ERRTYPE_* enum + u8 revHigh; ///< High revison ID + u16 revLow; ///< Low revision ID + u32 resCode; ///< Result code + u32 pcAddr; ///< PC address at exception + u32 procId; ///< Process ID. + u64 titleId; ///< Title ID. + u64 appTitleId; ///< Application Title ID. + union { + ERRF_ExceptionData exception_data; ///< Data for when type is ERRF_ERRTYPE_EXCEPTION + char failure_mesg[60]; ///< String for when type is ERRF_ERRTYPE_FAILURE + } data; ///< The different types of data for errors. +} ERRF_FatalErrInfo; + +/// Initializes ERR:f. Unless you plan to call ERRF_Throw yourself, do not use this. +Result errfInit(void); + +/// Exits ERR:f. Unless you plan to call ERRF_Throw yourself, do not use this. +void errfExit(void); + +/** + * @brief Gets the current err:f API session handle. + * @return The current err:f API session handle. + */ +Handle *errfGetSessionHandle(void); + +/** + * @brief Throws a system error and possibly results in ErrDisp triggering. + * @param[in] error Error to throw. + * + * After performing this, the system may panic and need to be rebooted. Extra information will be displayed on the + * top screen with a developer console or the proper patches in a CFW applied. + * + * The error may not be shown and execution aborted until errfExit(void) is called. + * + * You may wish to use ERRF_ThrowResult() or ERRF_ThrowResultWithMessage() instead of + * constructing the ERRF_FatalErrInfo struct yourself. + */ +Result ERRF_Throw(const ERRF_FatalErrInfo* error); + +/** + * @brief Throws a system error with the given Result code. + * @param[in] failure Result code to throw. + * + * This calls ERRF_Throw() with error type ERRF_ERRTYPE_GENERIC and fills in the required data. + * + * This function \em does fill in the address where this function was called from. + * + * See https://3dbrew.org/wiki/ERR:Throw#Generic for expected top screen output + * on development units/patched ErrDisp. + */ +Result ERRF_ThrowResult(Result failure); + +/** + * @brief Throws a system error with the given Result code and message. + * @param[in] failure Result code to throw. + * @param[in] message The message to display. + * + * This calls ERRF_Throw() with error type ERRF_ERRTYPE_FAILURE and fills in the required data. + * + * This function does \em not fill in the address where this function was called from because it + * would not be displayed. + * + * The message is only displayed on development units/patched ErrDisp. + * + * See https://3dbrew.org/wiki/ERR:Throw#Result_Failure for expected top screen output + * on development units/patched ErrDisp. + */ +Result ERRF_ThrowResultWithMessage(Result failure, const char* message); diff --git a/libctru/include/3ds/gpu/gpu-old.h b/libctru/include/3ds/gpu/gpu-old.h deleted file mode 100644 index cb7ba5d..0000000 --- a/libctru/include/3ds/gpu/gpu-old.h +++ /dev/null @@ -1,235 +0,0 @@ -/** - * @file gpu-old.h - * @brief Deprecated GPU functions which should not be used in new code. - * @description These functions have been superseeded by direct GPU register writes, or external GPU libraries. - * @deprecated - */ -#pragma once - -#include "gpu.h" - -/** - * @brief Initializes the GPU. - * @param gsphandle GSP handle to use. - * @deprecated - */ -void GPU_Init(Handle *gsphandle) DEPRECATED; - -/** - * @brief Resets the GPU. - * @param gxbuf GX command buffer to use. - * @param gpuBuf GPU command buffer to use. - * @param gpuBufSize GPU command buffer size. - * @deprecated - */ -void GPU_Reset(u32* gxbuf, u32* gpuBuf, u32 gpuBufSize) DEPRECATED; - -/** - * @brief Sets a shader float uniform. - * @param type Type of shader to set the uniform of. - * @param startreg Start of the uniform register to set. - * @param data Data to set. - * @param numreg Number of registers to set. - * @deprecated - */ -void GPU_SetFloatUniform(GPU_SHADER_TYPE type, u32 startreg, u32* data, u32 numreg) DEPRECATED; - -/** - * @brief Sets the viewport. - * @param depthBuffer Buffer to output depth data to. - * @param colorBuffer Buffer to output color data to. - * @param x X of the viewport. - * @param y Y of the viewport. - * @param w Width of the viewport. - * @param h Height of the viewport. - * @deprecated - */ -void GPU_SetViewport(u32* depthBuffer, u32* colorBuffer, u32 x, u32 y, u32 w, u32 h) DEPRECATED; - -/** - * @brief Sets the current scissor test mode. - * @param mode Scissor test mode to use. - * @param x X of the scissor region. - * @param y Y of the scissor region. - * @param w Width of the scissor region. - * @param h Height of the scissor region. - * @deprecated - */ -void GPU_SetScissorTest(GPU_SCISSORMODE mode, u32 left, u32 bottom, u32 right, u32 top) DEPRECATED; - -/** - * @brief Sets the depth map. - * @param zScale Z scale to use. - * @param zOffset Z offset to use. - * @deprecated - */ -void GPU_DepthMap(float zScale, float zOffset) DEPRECATED; - -/** - * @brief Sets the alpha test parameters. - * @param enable Whether to enable alpha testing. - * @param function Test function to use. - * @param ref Reference value to use. - * @deprecated - */ -void GPU_SetAlphaTest(bool enable, GPU_TESTFUNC function, u8 ref) DEPRECATED; - -/** - * @brief Sets the depth test parameters and pixel write mask. - * @note GPU_WRITEMASK values can be ORed together. - * @param enable Whether to enable depth testing. - * @param function Test function to use. - * @param writemask Pixel write mask to use. - * @deprecated - */ -void GPU_SetDepthTestAndWriteMask(bool enable, GPU_TESTFUNC function, GPU_WRITEMASK writemask) DEPRECATED; - -/** - * @brief Sets the stencil test parameters. - * @param enable Whether to enable stencil testing. - * @param function Test function to use. - * @param ref Reference value to use. - * @param input_mask Input mask to use. - * @param write_mask Write mask to use. - * @deprecated - */ -void GPU_SetStencilTest(bool enable, GPU_TESTFUNC function, u8 ref, u8 input_mask, u8 write_mask) DEPRECATED; - -/** - * @brief Sets the stencil test operators. - * @param sfail Operator to use on source test failure. - * @param dfail Operator to use on destination test failure. - * @param pass Operator to use on test passing. - * @deprecated - */ -void GPU_SetStencilOp(GPU_STENCILOP sfail, GPU_STENCILOP dfail, GPU_STENCILOP pass) DEPRECATED; - -/** - * @brief Sets the face culling mode. - * @param mode Face culling mode to use. - * @deprecated - */ -void GPU_SetFaceCulling(GPU_CULLMODE mode) DEPRECATED; - -/** - * @brief Sets the combiner buffer write parameters. - * @note Use GPU_TEV_BUFFER_WRITE_CONFIG to build the parameters. - * @note Only the first four TEV stages can write to the combiner buffer. - * @param rgb_config RGB configuration to use. - * @param alpha_config Alpha configuration to use. - * @deprecated - */ -void GPU_SetCombinerBufferWrite(u8 rgb_config, u8 alpha_config) DEPRECATED; - -/** - * @brief Sets the alpha blending parameters. - * @note Cannot be used with GPU_SetColorLogicOp. - * @param colorEquation Blend equation to use for color components. - * @param alphaEquation Blend equation to use for the alpha component. - * @param colorSrc Source factor of color components. - * @param colorDst Destination factor of color components. - * @param alphaSrc Source factor of the alpha component. - * @param alphaDst Destination factor of the alpha component. - * @deprecated - */ -void GPU_SetAlphaBlending(GPU_BLENDEQUATION colorEquation, GPU_BLENDEQUATION alphaEquation, - GPU_BLENDFACTOR colorSrc, GPU_BLENDFACTOR colorDst, - GPU_BLENDFACTOR alphaSrc, GPU_BLENDFACTOR alphaDst) DEPRECATED; - -/** - * @brief Sets the color logic operator. - * @note Cannot be used with GPU_SetAlphaBlending. - * @param op Operator to set. - * @deprecated - */ -void GPU_SetColorLogicOp(GPU_LOGICOP op) DEPRECATED; - -/** - * @brief Sets the blending color. - * @param r Red component. - * @param g Green component. - * @param b Blue component. - * @param a Alpha component. - * @deprecated - */ -void GPU_SetBlendingColor(u8 r, u8 g, u8 b, u8 a) DEPRECATED; - -/** - * @brief Sets the VBO attribute buffers. - * @param totalAttributes Total number of attributes. - * @param baseAddress Base address of the VBO. - * @param attributeFormats Attribute format data. - * @param attributeMask Attribute mask. - * @param attributePermutation Attribute permutations. - * @param numBuffers Number of buffers. - * @param bufferOffsets Offsets of the buffers. - * @param bufferPermutations Buffer permutations. - * @param bufferNumAttributes Numbers of attributes of the buffers. - * @deprecated - */ -void GPU_SetAttributeBuffers(u8 totalAttributes, u32* baseAddress, u64 attributeFormats, u16 attributeMask, u64 attributePermutation, u8 numBuffers, u32 bufferOffsets[], u64 bufferPermutations[], u8 bufferNumAttributes[]) DEPRECATED; - -/** - * @brief Sets the enabled texture units. - * @param units Units to enable. OR texture unit values together to create this value. - * @deprecated - */ -void GPU_SetTextureEnable(GPU_TEXUNIT units) DEPRECATED; - -/** - * @brief Sets the texture data of a texture unit. - * @param unit Texture unit to use. - * @param data Data to load. Must be in linear memory or VRAM. - * @param width Width of the texture. - * @param height Height of the texture. - * @param Parameters of the texture, such as filters and wrap modes. - * @param colorType Color type of the texture. - * @deprecated - */ -void GPU_SetTexture(GPU_TEXUNIT unit, u32* data, u16 width, u16 height, u32 param, GPU_TEXCOLOR colorType) DEPRECATED; - -/** - * @brief Sets the border color of a texture unit. - * @param unit Texture unit to use. - * @param borderColor The color used for the border when using the @ref GPU_CLAMP_TO_BORDER wrap mode. - * @deprecated - */ -void GPU_SetTextureBorderColor(GPU_TEXUNIT unit,u32 borderColor) DEPRECATED; - -/** - * @brief Sets the parameters of a texture combiner. - * @param id ID of the combiner. - * @param rgbSources RGB source configuration. - * @param alphaSources Alpha source configuration. - * @param rgbOperands RGB operand configuration. - * @param alphaOperands Alpha operand configuration. - * @param rgbCombine RGB combiner function. - * @param alphaCombine Alpha combiner function. - * @param constantColor Constant color to provide. - * @deprecated - */ -void GPU_SetTexEnv(u8 id, u16 rgbSources, u16 alphaSources, u16 rgbOperands, u16 alphaOperands, GPU_COMBINEFUNC rgbCombine, GPU_COMBINEFUNC alphaCombine, u32 constantColor) DEPRECATED; - -/** - * @brief Draws an array of vertex data. - * @param primitive Primitive to draw. - * @param first First vertex to draw. - * @param count Number of vertices to draw. - * @deprecated - */ -void GPU_DrawArray(GPU_Primitive_t primitive, u32 first, u32 count) DEPRECATED; - -/** - * @brief Draws vertex elements. - * @param primitive Primitive to draw. - * @param indexArray Array of vertex indices to use. - * @param n Number of vertices to draw. - * @deprecated - */ -void GPU_DrawElements(GPU_Primitive_t primitive, u32* indexArray, u32 n) DEPRECATED; - -/** - * @brief Finishes drawing. - * @deprecated - */ -void GPU_FinishDrawing() DEPRECATED; diff --git a/libctru/include/3ds/gpu/gpu.h b/libctru/include/3ds/gpu/gpu.h index aefdef2..38d0fca 100644 --- a/libctru/include/3ds/gpu/gpu.h +++ b/libctru/include/3ds/gpu/gpu.h @@ -41,7 +41,7 @@ void GPUCMD_GetBuffer(u32** adr, u32* size, u32* offset); * @param cmd Buffer containing commands to add. * @param size Size of the buffer. */ -void GPUCMD_AddRawCommands(u32* cmd, u32 size); +void GPUCMD_AddRawCommands(const u32* cmd, u32 size); /// Executes the GPU command buffer. void GPUCMD_Run(void); @@ -55,7 +55,7 @@ void GPUCMD_FlushAndRun(void); * @param param Parameters of the command. * @param paramlength Size of the parameter buffer. */ -void GPUCMD_Add(u32 header, u32* param, u32 paramlength); +void GPUCMD_Add(u32 header, const u32* param, u32 paramlength); /// Finalizes the GPU command buffer. void GPUCMD_Finalize(void); diff --git a/libctru/include/3ds/gpu/shaderProgram.h b/libctru/include/3ds/gpu/shaderProgram.h index 90f1692..e0d5302 100644 --- a/libctru/include/3ds/gpu/shaderProgram.h +++ b/libctru/include/3ds/gpu/shaderProgram.h @@ -33,18 +33,8 @@ typedef struct shaderInstance_s* geometryShader; ///< Geometry shader. u32 geoShaderInputPermutation[2]; ///< Geometry shader input permutation. u8 geoShaderInputStride; ///< Geometry shader input stride. - u8 geoShaderMode; ///< Geometry shader operation mode. }shaderProgram_s; -/// Geometry shader operation modes. -typedef enum -{ - GSH_NORMAL = 0, ///< Normal operation. - GSH_PARTICLE = 1, ///< Particle system. - GSH_SUBDIVISION_LOOP = 2, ///< Loop subdivision surface. - GSH_SUBDIVISION_CATMULL_CLARK = 3, ///< Catmull-Clark subdivision surface. -} geoShaderMode; - /** * @brief Initializes a shader instance. * @param si Shader instance to initialize. @@ -104,7 +94,7 @@ Result shaderProgramSetVsh(shaderProgram_s* sp, DVLE_s* dvle); * @brief Sets the geometry shader of a shader program. * @param sp Shader program to use. * @param dvle Geometry shader to set. - * @param stride Stride of the geometry shader. + * @param stride Input stride of the shader (pass 0 to match the number of outputs of the vertex shader). */ Result shaderProgramSetGsh(shaderProgram_s* sp, DVLE_s* dvle, u8 stride); @@ -115,13 +105,6 @@ Result shaderProgramSetGsh(shaderProgram_s* sp, DVLE_s* dvle, u8 stride); */ Result shaderProgramSetGshInputPermutation(shaderProgram_s* sp, u64 permutation); -/** - * @brief Configures the operation mode of the geometry shader of a shader program. - * @param sp Shader program to use. - * @param mode Operation mode to use. - */ -Result shaderProgramSetGshMode(shaderProgram_s* sp, geoShaderMode mode); - /** * @brief Configures the shader units to use the specified shader program. * @param sp Shader program to use. diff --git a/libctru/include/3ds/gpu/shbin.h b/libctru/include/3ds/gpu/shbin.h index c53b272..e331beb 100644 --- a/libctru/include/3ds/gpu/shbin.h +++ b/libctru/include/3ds/gpu/shbin.h @@ -28,9 +28,18 @@ typedef enum{ RESULT_TEXCOORD0W = 0x4, ///< Texture coordinate 0 W. RESULT_TEXCOORD1 = 0x5, ///< Texture coordinate 1. RESULT_TEXCOORD2 = 0x6, ///< Texture coordinate 2. - RESULT_VIEW = 0x8 ///< View. + RESULT_VIEW = 0x8, ///< View. + RESULT_DUMMY = 0x9, ///< Dummy attribute (used as passthrough for geometry shader input). }DVLE_outputAttribute_t; +/// Geometry shader operation modes. +typedef enum +{ + GSH_POINT = 0, ///< Point processing mode. + GSH_VARIABLE_PRIM = 1, ///< Variable-size primitive processing mode. + GSH_FIXED_PRIM = 2, ///< Fixed-size primitive processing mode. +} DVLE_geoShaderMode; + /// DVLP data. typedef struct{ u32 codeSize; ///< Code size. @@ -64,6 +73,11 @@ typedef struct{ /// DVLE data. typedef struct{ DVLE_type type; ///< DVLE type. + bool mergeOutmaps; ///< true = merge vertex/geometry shader outmaps ('dummy' output attribute is present). + DVLE_geoShaderMode gshMode; ///< Geometry shader operation mode. + u8 gshFixedVtxStart; ///< Starting float uniform register number for storing the fixed-size primitive vertex array. + u8 gshVariableVtxNum; ///< Number of fully-defined vertices in the variable-size primitive vertex array. + u8 gshFixedVtxNum; ///< Number of vertices in the fixed-size primitive vertex array. DVLP_s* dvlp; ///< Contained DVLPs. u32 mainOffset; ///< Offset of the start of the main function. u32 endmainOffset; ///< Offset of the end of the main function. diff --git a/libctru/include/3ds/os.h b/libctru/include/3ds/os.h index e9b26c5..f3983ce 100644 --- a/libctru/include/3ds/os.h +++ b/libctru/include/3ds/os.h @@ -161,7 +161,6 @@ void osSetSpeedupEnable(bool enable); /** * @brief Gets the NAND system-version stored in NVer/CVer. - * The romfs device must not be already initialized(via romfsInit*()) at the time this function is called, since this code uses the romfs device. * @param nver_versionbin Output OS_VersionBin structure for the data read from NVer. * @param cver_versionbin Output OS_VersionBin structure for the data read from CVer. * @return The result-code. This value can be positive if opening "romfs:/version.bin" fails with stdio, since errno would be returned in that case. In some cases the error can be special negative values as well. diff --git a/libctru/include/3ds/romfs.h b/libctru/include/3ds/romfs.h index e031cf7..7deec55 100644 --- a/libctru/include/3ds/romfs.h +++ b/libctru/include/3ds/romfs.h @@ -45,15 +45,36 @@ typedef struct u16 name[]; ///< Name. (UTF-16) } romfs_file; -/// Initializes the RomFS driver. -Result romfsInit(void); +struct romfs_mount; /** - * @brief Initializes the RomFS driver from a RomFS file. + * @brief Mounts the Application's RomFS. + * @param mount Output mount handle + */ +Result romfsMount(struct romfs_mount **mount); +static inline Result romfsInit(void) +{ + return romfsMount(NULL); +} + +/** + * @brief Mounts RomFS from an open file. * @param file Handle of the RomFS file. * @param offset Offset of the RomFS within the file. + * @param mount Output mount handle */ -Result romfsInitFromFile(Handle file, u32 offset); +Result romfsMountFromFile(Handle file, u32 offset, struct romfs_mount **mount); +static inline Result romfsInitFromFile(Handle file, u32 offset) +{ + return romfsMountFromFile(file, offset, NULL); +} -/// Exits the RomFS driver. -Result romfsExit(void); +/// Bind the RomFS mount +Result romfsBind(struct romfs_mount *mount); + +/// Unmounts the RomFS device. +Result romfsUnmount(struct romfs_mount *mount); +static inline Result romfsExit(void) +{ + return romfsUnmount(NULL); +} diff --git a/libctru/include/3ds/services/am.h b/libctru/include/3ds/services/am.h index dc301b1..d02650b 100644 --- a/libctru/include/3ds/services/am.h +++ b/libctru/include/3ds/services/am.h @@ -56,9 +56,12 @@ typedef struct { u64 titlesFreeSpace; ///< Free space for titles. } AM_TWLPartitionInfo; -/// Initializes AM. +/// Initializes AM. This doesn't initialize with "am:app", see amAppInit(). Result amInit(void); +/// Initializes AM with a service which has access to the amapp-commands. This should only be used when using the amapp commands, not non-amapp AM commands. +Result amAppInit(void); + /// Exits AM. void amExit(void); diff --git a/libctru/include/3ds/services/apt.h b/libctru/include/3ds/services/apt.h index ae961e8..1dad9de 100644 --- a/libctru/include/3ds/services/apt.h +++ b/libctru/include/3ds/services/apt.h @@ -10,6 +10,7 @@ * Retrieved from http://3dbrew.org/wiki/NS_and_APT_Services#AppIDs */ typedef enum { + APPID_NONE = 0, APPID_HOMEMENU = 0x101, ///< Home Menu APPID_CAMERA = 0x110, ///< Camera applet APPID_FRIENDS_LIST = 0x112, ///< Friends List applet @@ -32,34 +33,82 @@ typedef enum { APPID_MEMOLIB = 0x409, ///< memolib } NS_APPID; -/// App status values. +/// APT applet position. typedef enum { - APP_NOTINITIALIZED, ///< App not initialized. - APP_RUNNING, ///< App running. - APP_SUSPENDED, ///< App suspended. - APP_EXITING, ///< App exiting. - APP_SUSPENDING, ///< App suspending. - APP_SLEEPMODE, ///< App in sleep mode. - APP_PREPARE_SLEEPMODE, ///< App preparing to enter sleep mode. - APP_APPLETSTARTED, ///< Applet started. - APP_APPLETCLOSED ///< Applet closed. -} APT_AppStatus; + APTPOS_NONE = -1, ///< No position specified. + APTPOS_APP = 0, ///< Application. + APTPOS_APPLIB = 1, ///< Application library (?). + APTPOS_SYS = 2, ///< System applet. + APTPOS_SYSLIB = 3, ///< System library (?). + APTPOS_RESIDENT = 4, ///< Resident applet. +} APT_AppletPos; + +typedef u8 APT_AppletAttr; + +/// Create an APT_AppletAttr bitfield from its components. +static inline APT_AppletAttr aptMakeAppletAttr(APT_AppletPos pos, bool manualGpuRights, bool manualDspRights) +{ + return (pos&7) | (manualGpuRights ? BIT(3) : 0) | (manualDspRights ? BIT(4) : 0); +} + +/// APT query reply. +typedef enum { + APTREPLY_REJECT = 0, + APTREPLY_ACCEPT = 1, + APTREPLY_LATER = 2, +} APT_QueryReply; /// APT signals. typedef enum { - APTSIGNAL_HOMEBUTTON = 1, ///< Home button pressed. - // 2: sleep-mode related? - APTSIGNAL_PREPARESLEEP = 3, ///< Prepare to enter sleep mode. - // 4: triggered when ptm:s GetShellStatus() returns 5. - APTSIGNAL_ENTERSLEEP = 5, ///< Enter sleep mode. - APTSIGNAL_WAKEUP = 6, ///< Wake from sleep mode. - APTSIGNAL_ENABLE = 7, ///< Enable. - APTSIGNAL_POWERBUTTON = 8, ///< Power button pressed. - APTSIGNAL_UTILITY = 9, ///< Utility called. - APTSIGNAL_SLEEPSYSTEM = 10, ///< System sleeping. - APTSIGNAL_ERROR = 11 ///< Error occurred. + APTSIGNAL_NONE = 0, ///< No signal received. + APTSIGNAL_HOMEBUTTON = 1, ///< HOME button pressed. + APTSIGNAL_HOMEBUTTON2 = 2, ///< HOME button pressed (again?). + APTSIGNAL_SLEEP_QUERY = 3, ///< Prepare to enter sleep mode. + APTSIGNAL_SLEEP_CANCEL = 4, ///< Triggered when ptm:s GetShellStatus() returns 5. + APTSIGNAL_SLEEP_ENTER = 5, ///< Enter sleep mode. + APTSIGNAL_SLEEP_WAKEUP = 6, ///< Wake from sleep mode. + APTSIGNAL_SHUTDOWN = 7, ///< Shutdown. + APTSIGNAL_POWERBUTTON = 8, ///< POWER button pressed. + APTSIGNAL_POWERBUTTON2 = 9, ///< POWER button cleared (?). + APTSIGNAL_TRY_SLEEP = 10, ///< System sleeping (?). + APTSIGNAL_ORDERTOCLOSE = 11, ///< Order to close (such as when an error happens?). } APT_Signal; +/// APT commands. +typedef enum { + APTCMD_NONE = 0, ///< No command received. + APTCMD_WAKEUP = 1, ///< Applet should wake up. + APTCMD_REQUEST = 2, ///< Source applet sent us a parameter. + APTCMD_RESPONSE = 3, ///< Target applet replied to our parameter. + APTCMD_EXIT = 4, ///< Exit (??) + APTCMD_MESSAGE = 5, ///< Message (??) + APTCMD_HOMEBUTTON_ONCE = 6, ///< HOME button pressed once. + APTCMD_HOMEBUTTON_TWICE = 7, ///< HOME button pressed twice (double-pressed). + APTCMD_DSP_SLEEP = 8, ///< DSP should sleep (manual DSP rights related?). + APTCMD_DSP_WAKEUP = 9, ///< DSP should wake up (manual DSP rights related?). + APTCMD_WAKEUP_EXIT = 10, ///< Applet wakes up due to a different applet exiting. + APTCMD_WAKEUP_PAUSE = 11, ///< Applet wakes up after being paused through HOME menu. + APTCMD_WAKEUP_CANCEL = 12, ///< Applet wakes up due to being cancelled. + APTCMD_WAKEUP_CANCELALL = 13, ///< Applet wakes up due to all applets being cancelled. + APTCMD_WAKEUP_POWERBUTTON = 14, ///< Applet wakes up due to POWER button being pressed (?). + APTCMD_WAKEUP_JUMPTOHOME = 15, ///< Applet wakes up and is instructed to jump to HOME menu (?). + APTCMD_SYSAPPLET_REQUEST = 16, ///< Request for sysapplet (?). + APTCMD_WAKEUP_LAUNCHAPP = 17, ///< Applet wakes up and is instructed to launch another applet (?). +} APT_Command; + +/// APT capture buffer information. +typedef struct +{ + u32 size; + u32 is3D; + struct + { + u32 leftOffset; + u32 rightOffset; + u32 format; + } top, bottom; +} aptCaptureBufInfo; + /// APT hook types. typedef enum { APTHOOK_ONSUSPEND = 0, ///< App suspended. @@ -82,8 +131,8 @@ typedef struct tag_aptHookCookie void* param; ///< Callback parameter. } aptHookCookie; -/// APT events. -extern Handle aptEvents[3]; +/// APT message callback. +typedef void (*aptMessageCb)(void* user, NS_APPID sender, void* msg, size_t msgsize); /// Initializes APT. Result aptInit(void); @@ -91,55 +140,17 @@ Result aptInit(void); /// Exits APT. void aptExit(void); -/// Opens an APT session. -void aptOpenSession(void); - -/// Closes an APT session. -void aptCloseSession(void); - /** - * @brief Sets the app's status. - * @param status Status to set. + * @brief Sends an APT command through IPC, taking care of locking, opening and closing an APT session. + * @param aptcmdbuf Pointer to command buffer (should have capacity for at least 16 words). */ -void aptSetStatus(APT_AppStatus status); - -/** - * @brief Gets the app's status. - * @return The app's status. - */ -APT_AppStatus aptGetStatus(void); - -/** - * @brief Gets the app's power status. - * When the status is APT_SUSPEND, this can be used to check what triggered a return-to-menu. - * @return The app's power status. (0 = normal, 1 = power button pressed) - */ -u32 aptGetStatusPower(void); - -/** - * @brief Sets the app's power status. - * @param status Power status to set. - */ -void aptSetStatusPower(u32 status); - -/** - * @brief Triggers a return to the home menu. - * - * This should be called by the user application when aptGetStatus() returns APP_SUSPENDING, not calling this will result in return-to-menu being disabled with the status left at APP_SUSPENDING. This function will not return until the system returns to the application, or when the status was changed to APP_EXITING. - */ -void aptReturnToMenu(void); - -/// Waits for an APT status event. -void aptWaitStatusEvent(void); - -/// Signals that the app is ready to sleep. -void aptSignalReadyForSleep(void); +Result aptSendCommand(u32* aptcmdbuf); /** * @brief Gets whether to allow the system to enter sleep mode. * @return Whether sleep mode is allowed. */ -bool aptIsSleepAllowed(); +bool aptIsSleepAllowed(void); /** * @brief Sets whether to allow the system to enter sleep mode. @@ -147,15 +158,9 @@ bool aptIsSleepAllowed(); */ void aptSetSleepAllowed(bool allowed); -/** - * @brief Gets the menu's app ID. - * @return The menu's app ID. - */ -NS_APPID aptGetMenuAppID(void); - /** * @brief Processes the current APT status. Generally used within a main loop. - * @return Whether the application is closing. + * @return Whether the application should continue running. */ bool aptMainLoop(void); @@ -173,6 +178,23 @@ void aptHook(aptHookCookie* cookie, aptHookFn callback, void* param); */ void aptUnhook(aptHookCookie* cookie); +/** + * @brief Sets the function to be called when an APT message from another applet is received. + * @param callback Callback function. + * @param user User-defined data to be passed to the callback. + */ +void aptSetMessageCallback(aptMessageCb callback, void* user); + +/** + * @brief Launches a library applet. + * @param appId ID of the applet to launch. + * @param buf Input/output buffer that contains launch parameters on entry and result data on exit. + * @param bufsize Size of the buffer. + * @param handle Handle to pass to the library applet. + * @return Whether the application should continue running after the library applet launch. + */ +bool aptLaunchLibraryApplet(NS_APPID appId, void* buf, size_t bufsize, Handle handle); + /** * @brief Gets an APT lock handle. * @param flags Flags to use. @@ -183,10 +205,11 @@ Result APT_GetLockHandle(u16 flags, Handle* lockHandle); /** * @brief Initializes an application's registration with APT. * @param appId ID of the application. - * @param eventHandle1 Pointer to output the signal event handle to. - * @param eventHandle2 Pointer to output the launch and exit event handle to. + * @param attr Attributes of the application. + * @param signalEvent Pointer to output the signal event handle to. + * @param resumeEvent Pointer to output the resume event handle to. */ -Result APT_Initialize(NS_APPID appId, Handle* eventHandle1, Handle* eventHandle2); +Result APT_Initialize(NS_APPID appId, APT_AppletAttr attr, Handle* signalEvent, Handle* resumeEvent); /** * @brief Terminates an application's registration with APT. @@ -199,31 +222,41 @@ Result APT_HardwareResetAsync(void); /** * @brief Enables APT. - * @param a Parameter to enable with. + * @param attr Attributes of the application. */ -Result APT_Enable(u32 a); +Result APT_Enable(APT_AppletAttr attr); /** * @brief Gets applet management info. - * @param inval Requested applet type. - * @param outval8 Pointer to output the current applet type to. - * @param outval32 Pointer to output the requested app ID to. - * @param menu_appid Pointer to output the home menu app ID to. - * @param active_appid Pointer to output the currently active app ID to. - * @param pAttributes Pointer to output the atrributes to. + * @param inpos Requested applet position. + * @param outpos Pointer to output the position of the current applet to. + * @param req_appid Pointer to output the AppID of the applet at the requested position to. + * @param menu_appid Pointer to output the HOME menu AppID to. + * @param active_appid Pointer to output the AppID of the currently active applet to. */ -Result APT_GetAppletManInfo(u8 inval, u8 *outval8, u32 *outval32, NS_APPID *menu_appid, NS_APPID *active_appid); +Result APT_GetAppletManInfo(APT_AppletPos inpos, APT_AppletPos* outpos, NS_APPID* req_appid, NS_APPID* menu_appid, NS_APPID* active_appid); + +/** + * @brief Gets the menu's app ID. + * @return The menu's app ID. + */ +static inline NS_APPID aptGetMenuAppID(void) +{ + NS_APPID menu_appid = APPID_NONE; + APT_GetAppletManInfo(APTPOS_NONE, NULL, NULL, &menu_appid, NULL); + return menu_appid; +} /** * @brief Gets an applet's information. - * @param appID ID of the applet. + * @param appID AppID of the applet. * @param pProgramID Pointer to output the program ID to. * @param pMediaType Pointer to output the media type to. * @param pRegistered Pointer to output the registration status to. * @param pLoadState Pointer to output the load state to. - * @param pAttributes Pointer to output the atrributes to. + * @param pAttributes Pointer to output the applet atrributes to. */ -Result APT_GetAppletInfo(NS_APPID appID, u64* pProgramID, u8* pMediaType, u8* pRegistered, u8* pLoadState, u32* pAttributes); +Result APT_GetAppletInfo(NS_APPID appID, u64* pProgramID, u8* pMediaType, bool* pRegistered, bool* pLoadState, APT_AppletAttr* pAttributes); /** * @brief Gets an applet's program information. @@ -256,13 +289,13 @@ Result APT_PrepareToJumpToHomeMenu(void); * @param Size of the parameter buffer. * @param handle Handle to pass. */ -Result APT_JumpToHomeMenu(const u8 *param, size_t paramSize, Handle handle); +Result APT_JumpToHomeMenu(const void* param, size_t paramSize, Handle handle); /** * @brief Prepares to jump to an application. - * @param a Application to jump to. + * @param exiting Specifies whether the applet is exiting. */ -Result APT_PrepareToJumpToApplication(u32 a); +Result APT_PrepareToJumpToApplication(bool exiting); /** * @brief Jumps to an application. @@ -270,14 +303,14 @@ Result APT_PrepareToJumpToApplication(u32 a); * @param Size of the parameter buffer. * @param handle Handle to pass. */ -Result APT_JumpToApplication(const u8 *param, size_t paramSize, Handle handle); +Result APT_JumpToApplication(const void* param, size_t paramSize, Handle handle); /** * @brief Gets whether an application is registered. * @param appID ID of the application. * @param out Pointer to output the registration state to. */ -Result APT_IsRegistered(NS_APPID appID, u8* out); +Result APT_IsRegistered(NS_APPID appID, bool* out); /** * @brief Inquires as to whether a signal has been received. @@ -294,59 +327,85 @@ Result APT_NotifyToWait(NS_APPID appID); /** * @brief Calls an applet utility function. + * @param id Utility function to call. * @param out Pointer to write output data to. - * @param a Utility function to call. - * @param size1 Size of the first buffer. - * @param buf1 First buffer. - * @param size2 Size of the second buffer. - * @param buf2 Second buffer. + * @param outSize Size of the output buffer. + * @param in Pointer to the input data. + * @param inSize Size of the input buffer. */ -Result APT_AppletUtility(u32* out, u32 a, u32 size1, u8* buf1, u32 size2, u8* buf2); +Result APT_AppletUtility(int id, void* out, size_t outSize, const void* in, size_t inSize); + +/// Sleeps if shell is closed (?). +Result APT_SleepIfShellClosed(void); + +/** + * @brief Tries to lock a transition (?). + * @param transition Transition ID. + * @param succeeded Pointer to output whether the lock was successfully applied. + */ +Result APT_TryLockTransition(u32 transition, bool* succeeded); + +/** + * @brief Unlocks a transition (?). + * @param transition Transition ID. + */ +Result APT_UnlockTransition(u32 transition); /** * @brief Glances at a receieved parameter without removing it from the queue. - * @param appID ID of the application. - * @param bufferSize Size of the buffer. + * @param appID AppID of the application. * @param buffer Buffer to receive to. + * @param bufferSize Size of the buffer. + * @param sender Pointer to output the sender's AppID to. + * @param command Pointer to output the command ID to. * @param actualSize Pointer to output the actual received data size to. - * @param signalType Pointer to output the signal type to. + * @param parameter Pointer to output the parameter handle to. */ -Result APT_GlanceParameter(NS_APPID appID, u32 bufferSize, u32* buffer, u32* actualSize, u8* signalType); +Result APT_GlanceParameter(NS_APPID appID, void* buffer, size_t bufferSize, NS_APPID* sender, APT_Command* command, size_t* actualSize, Handle* parameter); /** * @brief Receives a parameter. - * @param appID ID of the application. - * @param bufferSize Size of the buffer. + * @param appID AppID of the application. * @param buffer Buffer to receive to. + * @param bufferSize Size of the buffer. + * @param sender Pointer to output the sender's AppID to. + * @param command Pointer to output the command ID to. * @param actualSize Pointer to output the actual received data size to. - * @param signalType Pointer to output the signal type to. + * @param parameter Pointer to output the parameter handle to. */ -Result APT_ReceiveParameter(NS_APPID appID, u32 bufferSize, u32* buffer, u32* actualSize, u8* signalType); +Result APT_ReceiveParameter(NS_APPID appID, void* buffer, size_t bufferSize, NS_APPID* sender, APT_Command* command, size_t* actualSize, Handle* parameter); /** * @brief Sends a parameter. - * @param src_appID ID of the source application. - * @param dst_appID ID of the destination application. - * @param bufferSize Size of the buffer. + * @param source AppID of the source application. + * @param dest AppID of the destination application. + * @param command Command to send. * @param buffer Buffer to send. - * @param paramhandle Handle to pass. - * @param signalType Signal type to send. + * @param bufferSize Size of the buffer. + * @param parameter Parameter handle to pass. */ -Result APT_SendParameter(NS_APPID src_appID, NS_APPID dst_appID, u32 bufferSize, u32* buffer, Handle paramhandle, u8 signalType); +Result APT_SendParameter(NS_APPID source, NS_APPID dest, APT_Command command, const void* buffer, u32 bufferSize, Handle parameter); + +/** + * @brief Cancels a parameter which matches the specified source and dest AppIDs. + * @param source AppID of the source application (use APPID_NONE to disable the check). + * @param dest AppID of the destination application (use APPID_NONE to disable the check). + * @param success Pointer to output true if a parameter was cancelled, or false otherwise. + */ +Result APT_CancelParameter(NS_APPID source, NS_APPID dest, bool* success); /** * @brief Sends capture buffer information. - * @param bufferSize Size of the buffer to send. - * @param buffer Buffer to send. + * @param captureBuf Capture buffer information to send. */ -Result APT_SendCaptureBufferInfo(u32 bufferSize, u32* buffer); +Result APT_SendCaptureBufferInfo(const aptCaptureBufInfo* captureBuf); /** * @brief Replies to a sleep query. * @param appID ID of the application. - * @param a Parameter to reply with. + * @param reply Query reply value. */ -Result APT_ReplySleepQuery(NS_APPID appID, u32 a); +Result APT_ReplySleepQuery(NS_APPID appID, APT_QueryReply reply); /** * @brief Replies that a sleep notification has been completed. @@ -356,9 +415,9 @@ Result APT_ReplySleepNotificationComplete(NS_APPID appID); /** * @brief Prepares to close the application. - * @param a Whether the jump is to the home menu. + * @param cancelPreload Whether applet preloads should be cancelled. */ -Result APT_PrepareToCloseApplication(u8 a); +Result APT_PrepareToCloseApplication(bool cancelPreload); /** * @brief Closes the application. @@ -366,7 +425,7 @@ Result APT_PrepareToCloseApplication(u8 a); * @param paramSize Size of param. * @param handle Handle to pass. */ -Result APT_CloseApplication(const u8 *param, size_t paramSize, Handle handle); +Result APT_CloseApplication(const void* param, size_t paramSize, Handle handle); /** * @brief Sets the application's CPU time limit. @@ -382,22 +441,9 @@ Result APT_GetAppCpuTimeLimit(u32 *percent); /** * @brief Checks whether the system is a New 3DS. - * Note: this function is unreliable, see: http://3dbrew.org/wiki/APT:PrepareToStartApplication * @param out Pointer to write the New 3DS flag to. */ -Result APT_CheckNew3DS_Application(u8 *out); - -/** - * @brief Checks whether the system is a New 3DS. - * @param out Pointer to write the New 3DS flag to. - */ -Result APT_CheckNew3DS_System(u8 *out); - -/** - * @brief Checks whether the system is a New 3DS. - * @param out Pointer to write the New 3DS flag to. - */ -Result APT_CheckNew3DS(u8 *out); +Result APT_CheckNew3DS(bool* out); /** * @brief Prepares for an applicaton jump. @@ -405,56 +451,45 @@ Result APT_CheckNew3DS(u8 *out); * @param programID ID of the program to jump to. * @param mediatype Media type of the program to jump to. */ -Result APT_PrepareToDoAppJump(u8 flags, u64 programID, u8 mediatype); +Result APT_PrepareToDoApplicationJump(u8 flags, u64 programID, u8 mediatype); /** * @brief Performs an application jump. - * @param NSbuf0Size Size of NSbuf0Ptr. - * @param NSbuf1Size Size of NSbuf1Ptr. - * @param NSbuf0Ptr Launch buffer 0. - * @param NSbuf1Ptr Launch buffer 1. + * @param param Parameter buffer. + * @param paramSize Size of parameter buffer. + * @param hmac HMAC buffer (should be 0x20 bytes long). */ -Result APT_DoAppJump(u32 NSbuf0Size, u32 NSbuf1Size, u8 *NSbuf0Ptr, u8 *NSbuf1Ptr); +Result APT_DoApplicationJump(const void* param, size_t paramSize, const void* hmac); /** * @brief Prepares to start a library applet. - * @param appID ID of the applet to start. + * @param appID AppID of the applet to start. */ Result APT_PrepareToStartLibraryApplet(NS_APPID appID); /** * @brief Starts a library applet. - * @param appID ID of the applet to launch. - * @param inhandle Handle to pass to the applet. - * @param parambuf Buffer containing applet parameters. - * @param parambufsize Size of parambuf. + * @param appID AppID of the applet to launch. + * @param param Buffer containing applet parameters. + * @param paramsize Size of the buffer. + * @param handle Handle to pass to the applet. */ -Result APT_StartLibraryApplet(NS_APPID appID, Handle inhandle, u32 *parambuf, u32 parambufsize); - -/** - * @brief Launches a library applet. - * Note: This is not usable from the homebrew launcher. This is broken: when the applet does get launched at all, the applet process doesn't actually get terminated when the applet gets closed. - * @param appID ID of the applet to launch. - * @param inhandle Handle to pass to the applet. - * @param parambuf Buffer containing applet parameters. - * @param parambufsize Size of parambuf. - */ -Result APT_LaunchLibraryApplet(NS_APPID appID, Handle inhandle, u32 *parambuf, u32 parambufsize); +Result APT_StartLibraryApplet(NS_APPID appID, const void* param, size_t paramSize, Handle handle); /** * @brief Prepares to start a system applet. - * @param appID ID of the applet to start. + * @param appID AppID of the applet to start. */ Result APT_PrepareToStartSystemApplet(NS_APPID appID); /** * @brief Starts a system applet. - * @param appID ID of the applet to launch. - * @param bufSize Size of the parameter buffer. - * @param applHandle Handle to pass to the applet. - * @param buf Buffer containing applet parameters. + * @param appID AppID of the applet to launch. + * @param param Buffer containing applet parameters. + * @param paramSize Size of the parameter buffer. + * @param handle Handle to pass to the applet. */ -Result APT_StartSystemApplet(NS_APPID appID, u32 bufSize, Handle applHandle, u8 *buf); +Result APT_StartSystemApplet(NS_APPID appID, const void* param, size_t paramSize, Handle handle); /** * @brief Retrieves the shared system font. diff --git a/libctru/include/3ds/services/httpc.h b/libctru/include/3ds/services/httpc.h index 01d2990..6123b34 100644 --- a/libctru/include/3ds/services/httpc.h +++ b/libctru/include/3ds/services/httpc.h @@ -25,12 +25,21 @@ typedef enum { HTTPC_STATUS_DOWNLOAD_READY = 0x7 ///< Download ready. } HTTPC_RequestStatus; +/// HTTP KeepAlive option. +typedef enum { + HTTPC_KEEPALIVE_DISABLED = 0x0, + HTTPC_KEEPALIVE_ENABLED = 0x1 +} HTTPC_KeepAlive; + /// Result code returned when a download is pending. #define HTTPC_RESULTCODE_DOWNLOADPENDING 0xd840a02b -// Result code returned when asked about a non-existing header +// Result code returned when asked about a non-existing header. #define HTTPC_RESULTCODE_NOTFOUND 0xd840a028 +// Result code returned when any timeout function times out. +#define HTTPC_RESULTCODE_TIMEDOUT 0xd820a069 + /// Initializes HTTPC. For HTTP GET the sharedmem_size can be zero. The sharedmem contains data which will be later uploaded for HTTP POST. sharedmem_size should be aligned to 0x1000-bytes. Result httpcInit(u32 sharedmem_size); @@ -43,7 +52,7 @@ void httpcExit(void); * @param url URL to connect to. * @param use_defaultproxy Whether the default proxy should be used (0 for default) */ -Result httpcOpenContext(httpcContext *context, HTTPC_RequestMethod method, char* url, u32 use_defaultproxy); +Result httpcOpenContext(httpcContext *context, HTTPC_RequestMethod method, const char* url, u32 use_defaultproxy); /** * @brief Closes a HTTP context. @@ -51,13 +60,19 @@ Result httpcOpenContext(httpcContext *context, HTTPC_RequestMethod method, char* */ Result httpcCloseContext(httpcContext *context); +/** + * @brief Cancels a HTTP connection. + * @param context Context to close. + */ +Result httpcCancelConnection(httpcContext *context); + /** * @brief Adds a request header field to a HTTP context. * @param context Context to use. * @param name Name of the field. * @param value Value of the field. */ -Result httpcAddRequestHeaderField(httpcContext *context, char* name, char* value); +Result httpcAddRequestHeaderField(httpcContext *context, const char* name, const char* value); /** * @brief Adds a POST form field to a HTTP context. @@ -65,7 +80,7 @@ Result httpcAddRequestHeaderField(httpcContext *context, char* name, char* value * @param name Name of the field. * @param value Value of the field. */ -Result httpcAddPostDataAscii(httpcContext *context, char* name, char* value); +Result httpcAddPostDataAscii(httpcContext *context, const char* name, const char* value); /** * @brief Adds a POST body to a HTTP context. @@ -73,7 +88,7 @@ Result httpcAddPostDataAscii(httpcContext *context, char* name, char* value); * @param data The data to be passed as raw into the body of the post request. * @param len Length of data passed by data param. */ -Result httpcAddPostDataRaw(httpcContext *context, u32* data, u32 len); +Result httpcAddPostDataRaw(httpcContext *context, const u32* data, u32 len); /** * @brief Begins a HTTP request. @@ -89,6 +104,15 @@ Result httpcBeginRequest(httpcContext *context); */ Result httpcReceiveData(httpcContext *context, u8* buffer, u32 size); +/** + * @brief Receives data from a HTTP context with a timeout value. + * @param context Context to use. + * @param buffer Buffer to receive data to. + * @param size Size of the buffer. + * @param timeout Maximum time in nanoseconds to wait for a reply. + */ +Result httpcReceiveDataTimeout(httpcContext *context, u8* buffer, u32 size, u64 timeout); + /** * @brief Gets the request state of a HTTP context. * @param context Context to use. @@ -108,9 +132,16 @@ Result httpcGetDownloadSizeState(httpcContext *context, u32* downloadedsize, u32 * @brief Gets the response code of the HTTP context. * @param context Context to get the response code of. * @param out Pointer to write the response code to. - * @param delay Delay to wait for the status code. Not used yet. */ -Result httpcGetResponseStatusCode(httpcContext *context, u32* out, u64 delay); +Result httpcGetResponseStatusCode(httpcContext *context, u32* out); + +/** + * @brief Gets the response code of the HTTP context with a timeout value. + * @param context Context to get the response code of. + * @param out Pointer to write the response code to. + * @param timeout Maximum time in nanoseconds to wait for a reply. + */ +Result httpcGetResponseStatusCodeTimeout(httpcContext *context, u32* out, u64 timeout); /** * @brief Gets a response header field from a HTTP context. @@ -119,7 +150,7 @@ Result httpcGetResponseStatusCode(httpcContext *context, u32* out, u64 delay); * @param value Pointer to output the value of the field to. * @param valuebuf_maxsize Maximum size of the value buffer. */ -Result httpcGetResponseHeader(httpcContext *context, char* name, char* value, u32 valuebuf_maxsize); +Result httpcGetResponseHeader(httpcContext *context, const char* name, char* value, u32 valuebuf_maxsize); /** * @brief Adds a trusted RootCA cert to a HTTP context. @@ -127,7 +158,7 @@ Result httpcGetResponseHeader(httpcContext *context, char* name, char* value, u3 * @param cert Pointer to DER cert. * @param certsize Size of the DER cert. */ -Result httpcAddTrustedRootCA(httpcContext *context, u8 *cert, u32 certsize); +Result httpcAddTrustedRootCA(httpcContext *context, const u8 *cert, u32 certsize); /** * @brief Adds a default RootCA cert to a HTTP context. @@ -151,7 +182,7 @@ Result httpcSelectRootCertChain(httpcContext *context, u32 RootCertChain_context * @param privk Pointer to the DER private key. * @param privk_size Size of the privk. */ -Result httpcSetClientCert(httpcContext *context, u8 *cert, u32 certsize, u8 *privk, u32 privk_size); +Result httpcSetClientCert(httpcContext *context, const u8 *cert, u32 certsize, const u8 *privk, u32 privk_size); /** * @brief Sets the default clientcert for a HTTP context. @@ -202,7 +233,7 @@ Result httpcDestroyRootCertChain(u32 RootCertChain_contexthandle); * @param certsize Size of the DER cert. * @param cert_contexthandle Optional output ptr for the cert contexthandle(this can be NULL). */ -Result httpcRootCertChainAddCert(u32 RootCertChain_contexthandle, u8 *cert, u32 certsize, u32 *cert_contexthandle); +Result httpcRootCertChainAddCert(u32 RootCertChain_contexthandle, const u8 *cert, u32 certsize, u32 *cert_contexthandle); /** * @brief Adds a default RootCA cert to a RootCertChain. @@ -227,7 +258,7 @@ Result httpcRootCertChainRemoveCert(u32 RootCertChain_contexthandle, u32 cert_co * @param privk_size Size of the privk. * @param ClientCert_contexthandle Output ClientCert context handle. */ -Result httpcOpenClientCertContext(u8 *cert, u32 certsize, u8 *privk, u32 privk_size, u32 *ClientCert_contexthandle); +Result httpcOpenClientCertContext(const u8 *cert, u32 certsize, const u8 *privk, u32 privk_size, u32 *ClientCert_contexthandle); /** * @brief Opens a ClientCert-context with a default clientclient. Up to 2 ClientCert-contexts can be open under this user-process. @@ -252,3 +283,10 @@ Result httpcCloseClientCertContext(u32 ClientCert_contexthandle); */ Result httpcDownloadData(httpcContext *context, u8* buffer, u32 size, u32 *downloadedsize); +/** + * @brief Sets Keep-Alive for the context. + * @param context Context to set the KeepAlive flag on. + * @param option HTTPC_KeepAlive option. + */ +Result httpcSetKeepAlive(httpcContext *context, HTTPC_KeepAlive option); + diff --git a/libctru/include/3ds/services/ps.h b/libctru/include/3ds/services/ps.h index 398e7fd..4d17fee 100644 --- a/libctru/include/3ds/services/ps.h +++ b/libctru/include/3ds/services/ps.h @@ -74,3 +74,10 @@ Result PS_GetLocalFriendCodeSeed(u64* seed); * @param device_id Pointer to write the device ID to. */ Result PS_GetDeviceId(u32* device_id); + +/** + * @brief Generates cryptographically secure random bytes. + * @param out Pointer to the buffer to write the bytes to. + * @param len Number of bytes to write. + */ +Result PS_GenerateRandomBytes(void* out, size_t len); diff --git a/libctru/include/3ds/services/pxidev.h b/libctru/include/3ds/services/pxidev.h new file mode 100644 index 0000000..96144e8 --- /dev/null +++ b/libctru/include/3ds/services/pxidev.h @@ -0,0 +1,79 @@ +/** + * @file pxidev.h + * @brief Gamecard PXI service. + */ +#pragma once + +#include <3ds/services/fs.h> + +/// Card SPI wait operation type. +typedef enum { + WAIT_NONE = 0, ///< Do not wait. + WAIT_SLEEP = 1, ///< Sleep for the specified number of nanoseconds. + WAIT_IREQ_RETURN = 2, ///< Wait for IREQ, return if timeout. + WAIT_IREQ_CONTINUE = 3 ///< Wait for IREQ, continue if timeout. +} PXIDEV_WaitType; + +/// Card SPI register deassertion type. +typedef enum { + DEASSERT_NONE = 0, ///< Do not deassert. + DEASSERT_BEFORE_WAIT = 1, ///< Deassert before waiting. + DEASSERT_AFTER_WAIT = 2 ///< Deassert after waiting. +} PXIDEV_DeassertType; + +/// Card SPI transfer buffer. +typedef struct { + void* ptr; ///< Data pointer. + u32 size; ///< Data size. + u8 transferOption; ///< Transfer options. See @ref pxiDevMakeTransferOption + u64 waitOperation; ///< Wait operation. See @ref pxiDevMakeWaitOperation +} PXIDEV_SPIBuffer; + +/// Initializes pxi:dev. +Result pxiDevInit(void); + +/// Shuts down pxi:dev. +void pxiDevExit(void); + +/** + * @brief Creates a packed card SPI transfer option value. + * @param baudRate Baud rate to use when transferring. + * @param busMode Bus mode to use when transferring. + * @return A packed card SPI transfer option value. + */ +static inline u8 pxiDevMakeTransferOption(FS_CardSpiBaudRate baudRate, FS_CardSpiBusMode busMode) +{ + return (baudRate & 0x3F) | ((busMode & 0x3) << 6); +} + +/** + * @brief Creates a packed card SPI wait operation value. + * @param waitType Type of wait to perform. + * @param deassertType Type of register deassertion to perform. + * @param timeout Timeout, in nanoseconds, to wait, if applicable. + * @return A packed card SPI wait operation value. + */ +static inline u64 pxiDevMakeWaitOperation(PXIDEV_WaitType waitType, PXIDEV_DeassertType deassertType, u64 timeout) +{ + return (waitType & 0xF) | ((deassertType & 0xF) << 4) | ((timeout & 0xFFFFFFFFFFFFFF) << 8); +} + +/** + * @brief Performs multiple card SPI writes and reads. + * @param header Header to lead the transfers with. Must be, at most, 8 bytes in size. + * @param writeBuffer1 Buffer to make first transfer from. + * @param readBuffer1 Buffer to receive first response to. + * @param writeBuffer2 Buffer to make second transfer from. + * @param readBuffer2 Buffer to receive second response to. + * @param footer Footer to follow the transfers with. Must be, at most, 8 bytes in size. Wait operation is unused. + */ +Result PXIDEV_SPIMultiWriteRead(PXIDEV_SPIBuffer* header, PXIDEV_SPIBuffer* writeBuffer1, PXIDEV_SPIBuffer* readBuffer1, PXIDEV_SPIBuffer* writeBuffer2, PXIDEV_SPIBuffer* readBuffer2, PXIDEV_SPIBuffer* footer); + +/** + * @brief Performs a single card SPI write and read. + * @param bytesRead Pointer to output the number of bytes received to. + * @param initialWaitOperation Wait operation to perform before transferring data. + * @param writeBuffer Buffer to transfer data from. + * @param readBuffer Buffer to receive data to. + */ +Result PXIDEV_SPIWriteRead(u32* bytesRead, u64 initialWaitOperation, PXIDEV_SPIBuffer* writeBuffer, PXIDEV_SPIBuffer* readBuffer); diff --git a/libctru/include/3ds/services/soc.h b/libctru/include/3ds/services/soc.h index b84f924..fe49f54 100644 --- a/libctru/include/3ds/services/soc.h +++ b/libctru/include/3ds/services/soc.h @@ -14,16 +14,17 @@ /// Options to be used with @ref SOCU_GetNetworkOpt typedef enum { - NETOPT_MAC_ADDRESS = 0x1004, ///< The mac address of the interface (u32 mac[6]) - NETOPT_ARP_TABLE = 0x3002, ///< The ARP table @see SOCU_ARPTableEntry - NETOPT_IP_INFO = 0x4003, ///< The cureent IP setup @see SOCU_IPInfo - NETOPT_IP_MTU = 0x4004, ///< The value of the IP MTU (u32) - NETOPT_ROUTING_TABLE = 0x4006, ///< The routing table @see SOCU_RoutingTableEntry - NETOPT_UDP_NUMBER = 0x8002, ///< The number of sockets in the UDP table (u32) - NETOPT_UDP_TABLE = 0x8003, ///< The table of opened UDP sockets @see SOCU_UDPTableEntry - NETOPT_TCP_NUMBER = 0x9002, ///< The number of sockets in the TCP table (u32) - NETOPT_TCP_TABLE = 0x9003, ///< The table of opened TCP sockets @see SOCU_TCPTableEntry - NETOPT_DNS_TABLE = 0xB003, ///< The table of the DNS servers @see SOCU_DNSTableEntry -- Returns a buffer of size 336 but only 2 entries are set ? + NETOPT_MAC_ADDRESS = 0x1004, ///< The mac address of the interface (u32 mac[6]) + NETOPT_ARP_TABLE = 0x3002, ///< The ARP table @see SOCU_ARPTableEntry + NETOPT_IP_INFO = 0x4003, ///< The cureent IP setup @see SOCU_IPInfo + NETOPT_IP_MTU = 0x4004, ///< The value of the IP MTU (u32) + NETOPT_ROUTING_TABLE = 0x4006, ///< The routing table @see SOCU_RoutingTableEntry + NETOPT_UDP_NUMBER = 0x8002, ///< The number of sockets in the UDP table (u32) + NETOPT_UDP_TABLE = 0x8003, ///< The table of opened UDP sockets @see SOCU_UDPTableEntry + NETOPT_TCP_NUMBER = 0x9002, ///< The number of sockets in the TCP table (u32) + NETOPT_TCP_TABLE = 0x9003, ///< The table of opened TCP sockets @see SOCU_TCPTableEntry + NETOPT_DNS_TABLE = 0xB003, ///< The table of the DNS servers @see SOCU_DNSTableEntry -- Returns a buffer of size 336 but only 2 entries are set ? + NETOPT_DHCP_LEASE_TIME = 0xC001, ///< The DHCP lease time remaining, in seconds } NetworkOpt; /// One entry of the ARP table retrieved by using @ref SOCU_GetNetworkOpt and @ref NETOPT_ARP_TABLE diff --git a/libctru/include/3ds/services/sslc.h b/libctru/include/3ds/services/sslc.h index 4324f25..9a86f2a 100644 --- a/libctru/include/3ds/services/sslc.h +++ b/libctru/include/3ds/services/sslc.h @@ -60,7 +60,7 @@ Result sslcDestroyRootCertChain(u32 RootCertChain_contexthandle); * @param cert Pointer to the DER cert. * @param certsize Size of the DER cert. */ -Result sslcAddTrustedRootCA(u32 RootCertChain_contexthandle, u8 *cert, u32 certsize, u32 *cert_contexthandle); +Result sslcAddTrustedRootCA(u32 RootCertChain_contexthandle, const u8 *cert, u32 certsize, u32 *cert_contexthandle); /** * @brief Adds a default RootCA cert to a RootCertChain. @@ -95,7 +95,7 @@ Result sslcDestroy8CertChain(u32 CertChain_contexthandle); * @param cert Pointer to the cert. * @param certsize Size of the cert. */ -Result sslc8CertChainAddCert(u32 CertChain_contexthandle, u8 *cert, u32 certsize, u32 *cert_contexthandle); +Result sslc8CertChainAddCert(u32 CertChain_contexthandle, const u8 *cert, u32 certsize, u32 *cert_contexthandle); /** * @brief Adds a default cert to a CertChain from sslcCreate8CertChain(). Not actually usable since no certIDs are implemented in SSL-module for this. @@ -120,7 +120,7 @@ Result sslc8CertChainRemoveCert(u32 CertChain_contexthandle, u32 cert_contexthan * @param keysize Size of the DER key. * @param ClientCert_contexthandle Output contexthandle. */ -Result sslcOpenClientCertContext(u8 *cert, u32 certsize, u8 *key, u32 keysize, u32 *ClientCert_contexthandle); +Result sslcOpenClientCertContext(const u8 *cert, u32 certsize, const u8 *key, u32 keysize, u32 *ClientCert_contexthandle); /** * @brief Opens a ClientCert-context with a default certID. @@ -154,7 +154,7 @@ Result sslcGenerateRandomData(u8 *buf, u32 size); * @param input_opt Input sslc options bitmask. * @param hostname Server hostname. */ -Result sslcCreateContext(sslcContext *context, int sockfd, u32 input_opt, char *hostname); +Result sslcCreateContext(sslcContext *context, int sockfd, u32 input_opt, const char *hostname); /* * @brief Destroys a sslc context. The associated sockfd must be closed manually. @@ -187,7 +187,7 @@ Result sslcRead(sslcContext *context, void *buf, size_t len, bool peek); * @param len Size to send. * @return When this isn't an error-code, this is the total transferred data size. */ -Result sslcWrite(sslcContext *context, void *buf, size_t len); +Result sslcWrite(sslcContext *context, const void *buf, size_t len); /* * @brief Set the RootCertChain for the specified sslc context. @@ -247,5 +247,5 @@ Result sslcContextInitSharedmem(sslcContext *context, u8 *buf, u32 size); * @param buf Input cert. * @param size Cert size. */ -Result sslcAddCert(sslcContext *context, u8 *buf, u32 size); +Result sslcAddCert(sslcContext *context, const u8 *buf, u32 size); diff --git a/libctru/include/3ds/svc.h b/libctru/include/3ds/svc.h index 9667963..6dfb6fa 100644 --- a/libctru/include/3ds/svc.h +++ b/libctru/include/3ds/svc.h @@ -90,6 +90,13 @@ typedef enum { ///@name Multithreading ///@{ +/// Reset types (for use with events and timers) +typedef enum { + RESET_ONESHOT = 0, ///< When the primitive is signaled, it will wake up exactly one thread and will clear itself automatically. + RESET_STICKY = 1, ///< When the primitive is signaled, it will wake up all threads and it won't clear itself automatically. + RESET_PULSE = 2, ///< Only meaningful for timers: same as ONESHOT but it will periodically signal the timer instead of just once. +} ResetType; + /// Types of thread info. typedef enum { THREADINFO_TYPE_UNKNOWN ///< Unknown. @@ -741,9 +748,9 @@ Result svcReleaseSemaphore(s32* count, Handle semaphore, s32 release_count); /** * @brief Creates an event handle. * @param[out] event Pointer to output the created event handle to. - * @param reset_type Type of reset the event uses. + * @param reset_type Type of reset the event uses (RESET_ONESHOT/RESET_STICKY). */ -Result svcCreateEvent(Handle* event, u8 reset_type); +Result svcCreateEvent(Handle* event, ResetType reset_type); /** * @brief Signals an event. @@ -821,6 +828,22 @@ Result svcAcceptSession(Handle* session, Handle port); * @param replyTarget Handle of the session to reply to. */ Result svcReplyAndReceive(s32* index, Handle* handles, s32 handleCount, Handle replyTarget); + +/** + * @brief Binds an event handle to an ARM11 interrupt. + * @param interruptId Interrupt identfier (see https://www.3dbrew.org/wiki/ARM11_Interrupts). + * @param event Event handle to bind to the given interrupt. + * @param priority Priority of the interrupt for the current process. + * @param isManualClear Indicates whether the interrupt has to be manually cleared or not. + */ +Result svcBindInterrupt(u32 interruptId, Handle event, s32 priority, bool isManualClear); + +/** + * @brief Unbinds an event handle from an ARM11 interrupt. + * @param interruptId Interrupt identfier, see (see https://www.3dbrew.org/wiki/ARM11_Interrupts). + * @param event Event handle to unbind from the given interrupt. + */ +Result svcUnbindInterrupt(u32 interruptId, Handle event); ///@} ///@name Time @@ -830,7 +853,7 @@ Result svcReplyAndReceive(s32* index, Handle* handles, s32 handleCount, Handle r * @param[out] timer Pointer to output the handle of the created timer to. * @param reset_type Type of reset to perform on the timer. */ -Result svcCreateTimer(Handle* timer, u8 reset_type); +Result svcCreateTimer(Handle* timer, ResetType reset_type); /** * @brief Sets a timer. diff --git a/libctru/include/3ds/synchronization.h b/libctru/include/3ds/synchronization.h index 0307745..f23594b 100644 --- a/libctru/include/3ds/synchronization.h +++ b/libctru/include/3ds/synchronization.h @@ -11,6 +11,13 @@ typedef _LOCK_T LightLock; /// A recursive lock. typedef _LOCK_RECURSIVE_T RecursiveLock; +/// A light event. +typedef struct +{ + s32 state; ///< State of the event: -2=cleared sticky, -1=cleared oneshot, 0=signaled oneshot, 1=signaled sticky + LightLock lock; ///< Lock used for sticky timer operation +} LightEvent; + /// Performs a Data Synchronization Barrier operation. static inline void __dsb(void) { @@ -114,3 +121,41 @@ int RecursiveLock_TryLock(RecursiveLock* lock); * @param lock Pointer to the lock. */ void RecursiveLock_Unlock(RecursiveLock* lock); + +/** + * @brief Initializes a light event. + * @param event Pointer to the event. + * @param reset_type Type of reset the event uses (RESET_ONESHOT/RESET_STICKY). + */ +void LightEvent_Init(LightEvent* event, ResetType reset_type); + +/** + * @brief Clears a light event. + * @param event Pointer to the event. + */ +void LightEvent_Clear(LightEvent* event); + +/** + * @brief Wakes up threads waiting on a sticky light event without signaling it. If the event had been signaled before, it is cleared instead. + * @param event Pointer to the event. + */ +void LightEvent_Pulse(LightEvent* event); + +/** + * @brief Signals a light event, waking up threads waiting on it. + * @param event Pointer to the event. + */ +void LightEvent_Signal(LightEvent* event); + +/** + * @brief Attempts to wait on a light event. + * @param event Pointer to the event. + * @return Non-zero if the event was signaled, zero otherwise. + */ +int LightEvent_TryWait(LightEvent* event); + +/** + * @brief Waits on a light event. + * @param event Pointer to the event. + */ +void LightEvent_Wait(LightEvent* event); diff --git a/libctru/source/applets/swkbd.c b/libctru/source/applets/swkbd.c new file mode 100644 index 0000000..8e712ae --- /dev/null +++ b/libctru/source/applets/swkbd.c @@ -0,0 +1,276 @@ +#include +#include +#include +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/svc.h> +#include <3ds/synchronization.h> +#include <3ds/services/apt.h> +#include <3ds/applets/swkbd.h> +#include <3ds/ipc.h> +#include <3ds/env.h> +#include <3ds/util/utf.h> + +static char* swkbdSharedMem; +static Handle swkbdSharedMemHandle; + +void swkbdInit(SwkbdState* swkbd, SwkbdType type, int numButtons, int maxTextLength) +{ + memset(swkbd, 0, sizeof(*swkbd)); + swkbd->type = type; + swkbd->num_buttons_m1 = numButtons-1; + swkbd->max_text_len = maxTextLength > 0 ? maxTextLength : 0xFDE8; + swkbd->button_submits_text[SWKBD_BUTTON_CONFIRM] = true; + swkbd->initial_text_offset = -1; + swkbd->dict_offset = -1; + swkbd->initial_status_offset = -1; + swkbd->initial_learning_offset = -1; + swkbd->version = 5; + swkbd->result = SWKBD_NONE; + swkbd->status_offset = -1; + swkbd->learning_offset = -1; + swkbd->text_offset = -1; +} + +static void swkbdConvertToUTF8(char* out, const u16* in, int max) +{ + if (!in || !*in) + { + out[0] = 0; + return; + } + + ssize_t units = utf16_to_utf8((uint8_t*)out, in, max); + if (units < 0) + { + out[0] = 0; + return; + } + + out[units] = 0; +} + +static void swkbdConvertToUTF16(u16* out, const char* in, int max) +{ + if (!in || !*in) + { + out[0] = 0; + return; + } + + ssize_t units = utf8_to_utf16(out, (const uint8_t*)in, max); + if (units < 0) + { + out[0] = 0; + return; + } + + out[units] = 0; +} + +static const u16 swkbdFeatures[] = +{ + offsetof(SwkbdState, is_parental_screen), + offsetof(SwkbdState, darken_top_screen), + offsetof(SwkbdState, predictive_input), + offsetof(SwkbdState, multiline), + offsetof(SwkbdState, fixed_width), + offsetof(SwkbdState, allow_home), + offsetof(SwkbdState, allow_reset), + offsetof(SwkbdState, allow_power), + offsetof(SwkbdState, unknown), + offsetof(SwkbdState, default_qwerty), +}; + +void swkbdSetFeatures(SwkbdState* swkbd, u32 features) +{ + int i; + for (i = 0; i < (sizeof(swkbdFeatures)/sizeof(u16)); i ++) + *((u8*)swkbd + swkbdFeatures[i]) = (features & BIT(i)) ? 1 : 0; +} + +void swkbdSetHintText(SwkbdState* swkbd, const char* text) +{ + swkbdConvertToUTF16(swkbd->hint_text, text, SWKBD_MAX_HINT_TEXT_LEN); +} + +void swkbdSetButton(SwkbdState* swkbd, SwkbdButton button, const char* text, bool submit) +{ + swkbdConvertToUTF16(swkbd->button_text[button], text, SWKBD_MAX_BUTTON_TEXT_LEN); + swkbd->button_submits_text[button] = submit; +} + +void swkbdSetInitialText(SwkbdState* swkbd, const char* text) +{ + swkbd->extra.initial_text = text; +} + +void swkbdSetDictWord(SwkbdDictWord* word, const char* reading, const char* text) +{ + swkbdConvertToUTF16(word->reading, reading, SWKBD_MAX_WORD_LEN); + swkbdConvertToUTF16(word->word, text, SWKBD_MAX_WORD_LEN); + word->language = 0; + word->all_languages = true; +} + +void swkbdSetDictionary(SwkbdState* swkbd, const SwkbdDictWord* dict, int wordCount) +{ + swkbd->dict_word_count = dict ? wordCount : 0; + swkbd->extra.dict = dict; +} + +void swkbdSetStatusData(SwkbdState* swkbd, SwkbdStatusData* data, bool in, bool out) +{ + swkbd->extra.status_data = data; + swkbd->initial_status_offset = (data&&in) ? 0 : -1; + if (data&&out) swkbd->save_state_flags |= BIT(0); + else swkbd->save_state_flags &= ~BIT(0); +} + +void swkbdSetLearningData(SwkbdState* swkbd, SwkbdLearningData* data, bool in, bool out) +{ + swkbd->extra.learning_data = data; + swkbd->initial_learning_offset = (data&&in) ? 0 : -1; + if (data&&out) swkbd->save_state_flags |= BIT(1); + else swkbd->save_state_flags &= ~BIT(1); +} + +void swkbdSetFilterCallback(SwkbdState* swkbd, SwkbdCallbackFn callback, void* user) +{ + swkbd->extra.callback = callback; + swkbd->extra.callback_user = callback ? user : NULL; +} + +static void swkbdMessageCallback(void* user, NS_APPID sender, void* msg, size_t msgsize) +{ + SwkbdExtra* extra = (SwkbdExtra*)user; + SwkbdState* swkbd = (SwkbdState*)msg; + + if (sender != APPID_SOFTWARE_KEYBOARD || msgsize != sizeof(SwkbdState)) + return; + + u16* text16 = (u16*)(swkbdSharedMem + swkbd->text_offset); + ssize_t units = utf16_to_utf8(NULL, text16, 0); + if (units < 0) svcBreak(USERBREAK_PANIC); // Shouldn't happen. + char* text8 = (char*)malloc(units+1); + if (!text8) svcBreak(USERBREAK_PANIC); // Shouldn't happen. + swkbdConvertToUTF8(text8, text16, units); + + const char* retmsg = NULL; + swkbd->callback_result = extra->callback(extra->callback_user, &retmsg, text8, units); + if (swkbd->callback_result > SWKBD_CALLBACK_OK) + swkbdConvertToUTF16(swkbd->callback_msg, retmsg, SWKBD_MAX_CALLBACK_MSG_LEN); + else + swkbd->callback_msg[0] = 0; + + free(text8); + APT_SendParameter(envGetAptAppId(), sender, APTCMD_MESSAGE, swkbd, sizeof(*swkbd), 0); +} + +SwkbdButton swkbdInputText(SwkbdState* swkbd, char* buf, size_t bufsize) +{ + SwkbdExtra extra = swkbd->extra; // Struct copy + + // Calculate sharedmem size + size_t sharedMemSize = 0; + sharedMemSize += (sizeof(u16)*(swkbd->max_text_len+1) + 3) &~ 3; + size_t dictOff = sharedMemSize; + sharedMemSize += (sizeof(SwkbdDictWord)*swkbd->dict_word_count + 3) &~ 3; + size_t statusOff = sharedMemSize; + sharedMemSize += swkbd->initial_status_offset >= 0 ? sizeof(SwkbdStatusData) : 0; + size_t learningOff = sharedMemSize; + sharedMemSize += swkbd->initial_learning_offset >= 0 ? sizeof(SwkbdLearningData) : 0; + if (swkbd->save_state_flags & BIT(0)) + { + swkbd->status_offset = sharedMemSize; + sharedMemSize += sizeof(SwkbdStatusData); + } + if (swkbd->save_state_flags & BIT(1)) + { + swkbd->learning_offset = sharedMemSize; + sharedMemSize += sizeof(SwkbdLearningData); + } + sharedMemSize = (sharedMemSize + 0xFFF) &~ 0xFFF; + swkbd->shared_memory_size = sharedMemSize; + + // Allocate sharedmem + swkbdSharedMem = (char*)memalign(0x1000, sharedMemSize); + if (!swkbdSharedMem) + { + swkbd->result = SWKBD_OUTOFMEM; + return SWKBD_BUTTON_NONE; + } + + // Create sharedmem block + Result res = svcCreateMemoryBlock(&swkbdSharedMemHandle, (u32)swkbdSharedMem, sharedMemSize, MEMPERM_READ|MEMPERM_WRITE, MEMPERM_READ|MEMPERM_WRITE); + if (R_FAILED(res)) + { + free(swkbdSharedMem); + swkbd->result = SWKBD_OUTOFMEM; + return SWKBD_BUTTON_NONE; + } + + // Copy stuff to shared mem + if (extra.initial_text) + { + swkbd->initial_text_offset = 0; + swkbdConvertToUTF16((u16*)swkbdSharedMem, extra.initial_text, swkbd->max_text_len); + } + if (extra.dict) + { + swkbd->dict_offset = dictOff; + memcpy(swkbdSharedMem+dictOff, extra.dict, sizeof(SwkbdDictWord)*swkbd->dict_word_count); + } + if (swkbd->initial_status_offset >= 0) + { + swkbd->initial_status_offset = statusOff; + memcpy(swkbdSharedMem+statusOff, extra.status_data, sizeof(SwkbdStatusData)); + } + if (swkbd->initial_learning_offset >= 0) + { + swkbd->initial_learning_offset = learningOff; + memcpy(swkbdSharedMem+learningOff, extra.learning_data, sizeof(SwkbdLearningData)); + } + + if (extra.callback) swkbd->filter_flags |= SWKBD_FILTER_CALLBACK; + else swkbd->filter_flags &= ~SWKBD_FILTER_CALLBACK; + + // Launch swkbd + memset(swkbd->reserved, 0, sizeof(swkbd->reserved)); + if (extra.callback) aptSetMessageCallback(swkbdMessageCallback, &extra); + bool ret = aptLaunchLibraryApplet(APPID_SOFTWARE_KEYBOARD, swkbd, sizeof(*swkbd), swkbdSharedMemHandle); + if (extra.callback) aptSetMessageCallback(NULL, NULL); + svcCloseHandle(swkbdSharedMemHandle); + + SwkbdButton button = SWKBD_BUTTON_NONE; + + if (ret) + { + u16* text16 = (u16*)(swkbdSharedMem+swkbd->text_offset); + text16[swkbd->text_length] = 0; + swkbdConvertToUTF8(buf, text16, bufsize-1); + if (swkbd->save_state_flags & BIT(0)) memcpy(extra.status_data, swkbdSharedMem+swkbd->status_offset, sizeof(SwkbdStatusData)); + if (swkbd->save_state_flags & BIT(1)) memcpy(extra.learning_data, swkbdSharedMem+swkbd->learning_offset, sizeof(SwkbdLearningData)); + + switch (swkbd->result) + { + case SWKBD_D1_CLICK0: + case SWKBD_D2_CLICK0: + button = SWKBD_BUTTON_LEFT; + break; + case SWKBD_D2_CLICK1: + button = SWKBD_BUTTON_MIDDLE; + break; + case SWKBD_D0_CLICK: + case SWKBD_D1_CLICK1: + case SWKBD_D2_CLICK2: + button = SWKBD_BUTTON_RIGHT; + break; + default: + break; + } + } + + free(swkbdSharedMem); + return button; +} diff --git a/libctru/source/errf.c b/libctru/source/errf.c new file mode 100644 index 0000000..e9ad6b1 --- /dev/null +++ b/libctru/source/errf.c @@ -0,0 +1,108 @@ +#include +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/svc.h> +#include <3ds/synchronization.h> +#include <3ds/errf.h> +#include <3ds/ipc.h> +#include <3ds/env.h> +#include "internal.h" + +static Handle errfHandle; +static int errfRefCount; + +Result errfInit(void) +{ + Result rc = 0; + + if (AtomicPostIncrement(&errfRefCount)) return 0; + + rc = svcConnectToPort(&errfHandle, "err:f"); + if (R_FAILED(rc)) goto end; + +end: + if (R_FAILED(rc)) errfExit(); + return rc; +} + +void errfExit(void) +{ + if (AtomicDecrement(&errfRefCount)) + return; + svcCloseHandle(errfHandle); +} + +Handle* errfGetSessionHandle(void) +{ + return &errfHandle; +} + +Result ERRF_Throw(const ERRF_FatalErrInfo* error) +{ + uint32_t *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x1,32,0); // 0x10800 + memcpy(&cmdbuf[1], error, sizeof(ERRF_FatalErrInfo)); + + Result ret = 0; + if (R_FAILED(ret = svcSendSyncRequest(errfHandle))) + return ret; + + return cmdbuf[1]; +} + +static inline void getCommonErrorData(ERRF_FatalErrInfo* error, Result failure) +{ + error->resCode = failure; + svcGetProcessId(&error->procId, 0xFFFF8001); +} + +Result ERRF_ThrowResult(Result failure) +{ + ERRF_FatalErrInfo error; + Result ret; + + if (R_FAILED(ret = errfInit())) + return ret; + + memset(&error, 0, sizeof(error)); + + error.type = ERRF_ERRTYPE_GENERIC; + + // pcAddr is not used by ErrDisp for ERRF_ERRTYPE_FAILURE + error.pcAddr = (u32)__builtin_extract_return_addr(__builtin_return_address(0)); + getCommonErrorData(&error, failure); + + ret = ERRF_Throw(&error); + + errfExit(); + + return ret; +} + +Result ERRF_ThrowResultWithMessage(Result failure, const char* message) +{ + ERRF_FatalErrInfo error; + Result ret; + size_t msglen; + + if (R_FAILED(ret = errfInit())) + return ret; + + memset(&error, 0, sizeof(error)); + + error.type = ERRF_ERRTYPE_FAILURE; + getCommonErrorData(&error, failure); + + if ((msglen = strlen(message)) > sizeof(error.data.failure_mesg) - 1) + msglen = sizeof(error.data.failure_mesg) - 1; + + memcpy(error.data.failure_mesg, message, msglen); + error.data.failure_mesg[msglen] = '\0'; + + ret = ERRF_Throw(&error); + + errfExit(); + + return ret; +} diff --git a/libctru/source/font.c b/libctru/source/font.c index 8f3f675..e7d3f55 100644 --- a/libctru/source/font.c +++ b/libctru/source/font.c @@ -16,9 +16,7 @@ Result fontEnsureMapped(void) Result res = 0; Handle hSharedFont = 0; - aptOpenSession(); res = APT_GetSharedFont(&hSharedFont, &sharedFontAddr); - aptCloseSession(); if (R_FAILED(res)) return res; // Map the shared font if it's not already mapped. diff --git a/libctru/source/gfx.c b/libctru/source/gfx.c index f16ce27..6fa27fb 100644 --- a/libctru/source/gfx.c +++ b/libctru/source/gfx.c @@ -56,7 +56,7 @@ void gfxSetDoubleBuffering(gfxScreen_t screen, bool doubleBuffering) { doubleBuf[screen] = doubleBuffering ? 1 : 0; // make sure they're the integer values '1' and '0' } -static u32 __get_bytes_per_pixel(GSPGPU_FramebufferFormats format) { +u32 __get_bytes_per_pixel(GSPGPU_FramebufferFormats format) { switch(format) { case GSP_RGBA8_OES: return 4; @@ -129,7 +129,7 @@ void gfxInit(GSPGPU_FramebufferFormats topFormat, GSPGPU_FramebufferFormats bott GSPGPU_AcquireRight(0x0); //setup our gsp shared mem section - svcCreateEvent(&gspEvent, 0x0); + svcCreateEvent(&gspEvent, RESET_ONESHOT); GSPGPU_RegisterInterruptRelayQueue(gspEvent, 0x1, &gspSharedMemHandle, &gfxThreadID); svcMapMemoryBlock(gspSharedMemHandle, (u32)gfxSharedMemory, 0x3, 0x10000000); diff --git a/libctru/source/gpu/gpu-old.c b/libctru/source/gpu/gpu-old.c deleted file mode 100644 index 5e8f47b..0000000 --- a/libctru/source/gpu/gpu-old.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - gpu-old.c _ Legacy GPU commands. -*/ - -#define LIBCTRU_NO_DEPRECATION -#include -#include -#include <3ds/types.h> -#include <3ds/gpu/gpu.h> -#include <3ds/gpu/gpu-old.h> -#include <3ds/gpu/gx.h> -#include <3ds/gpu/shbin.h> - - -void GPU_Init(Handle *gsphandle) -{ - gpuCmdBuf=NULL; - gpuCmdBufSize=0; - gpuCmdBufOffset=0; -} - -void GPU_Reset(u32* gxbuf, u32* gpuBuf, u32 gpuBufSize) -{ - GPUCMD_SetBuffer(gpuBuf, gpuBufSize, 0); -} - -void GPU_SetFloatUniform(GPU_SHADER_TYPE type, u32 startreg, u32* data, u32 numreg) -{ - if(!data)return; - - int regOffset=(type==GPU_GEOMETRY_SHADER)?(-0x30):(0x0); - - GPUCMD_AddWrite(GPUREG_VSH_FLOATUNIFORM_CONFIG+regOffset, 0x80000000|startreg); - GPUCMD_AddWrites(GPUREG_VSH_FLOATUNIFORM_DATA+regOffset, data, numreg*4); -} - - -//takes PAs as arguments -void GPU_SetViewport(u32* depthBuffer, u32* colorBuffer, u32 x, u32 y, u32 w, u32 h) -{ - u32 param[0x4]; - float fw=(float)w; - float fh=(float)h; - - GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 0x00000001); - GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 0x00000001); - - u32 f116e=0x01000000|(((h-1)&0xFFF)<<12)|(w&0xFFF); - - param[0x0]=((u32)depthBuffer)>>3; - param[0x1]=((u32)colorBuffer)>>3; - param[0x2]=f116e; - GPUCMD_AddIncrementalWrites(GPUREG_DEPTHBUFFER_LOC, param, 0x00000003); - - GPUCMD_AddWrite(GPUREG_RENDERBUF_DIM, f116e); - GPUCMD_AddWrite(GPUREG_DEPTHBUFFER_FORMAT, 0x00000003); //depth buffer format - GPUCMD_AddWrite(GPUREG_COLORBUFFER_FORMAT, 0x00000002); //color buffer format - GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_BLOCK32, 0x00000000); //? - - param[0x0]=f32tof24(fw/2); - param[0x1]=f32tof31(2.0f / fw) << 1; - param[0x2]=f32tof24(fh/2); - param[0x3]=f32tof31(2.0f / fh) << 1; - GPUCMD_AddIncrementalWrites(GPUREG_VIEWPORT_WIDTH, param, 0x00000004); - - GPUCMD_AddWrite(GPUREG_VIEWPORT_XY, (y<<16)|(x&0xFFFF)); - - param[0x0]=0x00000000; - param[0x1]=0x00000000; - param[0x2]=((h-1)<<16)|((w-1)&0xFFFF); - GPUCMD_AddIncrementalWrites(GPUREG_SCISSORTEST_MODE, param, 0x00000003); - - //enable depth buffer - param[0x0]=0x0000000F; - param[0x1]=0x0000000F; - param[0x2]=0x00000002; - param[0x3]=0x00000002; - GPUCMD_AddIncrementalWrites(GPUREG_COLORBUFFER_READ, param, 0x00000004); -} - -void GPU_SetScissorTest(GPU_SCISSORMODE mode, u32 left, u32 bottom, u32 right, u32 top) -{ - u32 param[3]; - - param[0x0] = mode; - param[0x1] = (bottom<<16)|(left&0xFFFF); - param[0x2] = ((top-1)<<16)|((right-1)&0xFFFF); - GPUCMD_AddIncrementalWrites(GPUREG_SCISSORTEST_MODE, param, 0x00000003); -} - -void GPU_DepthMap(float zScale, float zOffset) -{ - GPUCMD_AddWrite(GPUREG_DEPTHMAP_ENABLE, 0x00000001); - GPUCMD_AddWrite(GPUREG_DEPTHMAP_SCALE, f32tof24(zScale)); - GPUCMD_AddWrite(GPUREG_DEPTHMAP_OFFSET, f32tof24(zOffset)); -} - -void GPU_SetAlphaTest(bool enable, GPU_TESTFUNC function, u8 ref) -{ - GPUCMD_AddWrite(GPUREG_FRAGOP_ALPHA_TEST, (enable&1)|((function&7)<<4)|(ref<<8)); -} - -void GPU_SetStencilTest(bool enable, GPU_TESTFUNC function, u8 ref, u8 input_mask, u8 write_mask) -{ - GPUCMD_AddWrite(GPUREG_STENCIL_TEST, (enable&1)|((function&7)<<4)|(write_mask<<8)|(ref<<16)|(input_mask<<24)); -} - -void GPU_SetStencilOp(GPU_STENCILOP sfail, GPU_STENCILOP dfail, GPU_STENCILOP pass) -{ - GPUCMD_AddWrite(GPUREG_STENCIL_OP, sfail | (dfail << 4) | (pass << 8)); -} - -void GPU_SetDepthTestAndWriteMask(bool enable, GPU_TESTFUNC function, GPU_WRITEMASK writemask) -{ - GPUCMD_AddWrite(GPUREG_DEPTH_COLOR_MASK, (enable&1)|((function&7)<<4)|(writemask<<8)); -} - -void GPU_SetAlphaBlending(GPU_BLENDEQUATION colorEquation, GPU_BLENDEQUATION alphaEquation, - GPU_BLENDFACTOR colorSrc, GPU_BLENDFACTOR colorDst, - GPU_BLENDFACTOR alphaSrc, GPU_BLENDFACTOR alphaDst) -{ - GPUCMD_AddWrite(GPUREG_BLEND_FUNC, colorEquation | (alphaEquation<<8) | (colorSrc<<16) | (colorDst<<20) | (alphaSrc<<24) | (alphaDst<<28)); - GPUCMD_AddMaskedWrite(GPUREG_COLOR_OPERATION, 0x2, 0x00000100); -} - -void GPU_SetColorLogicOp(GPU_LOGICOP op) -{ - GPUCMD_AddWrite(GPUREG_LOGIC_OP, op); - GPUCMD_AddMaskedWrite(GPUREG_COLOR_OPERATION, 0x2, 0x00000000); -} - -void GPU_SetBlendingColor(u8 r, u8 g, u8 b, u8 a) -{ - GPUCMD_AddWrite(GPUREG_BLEND_COLOR, r | (g << 8) | (b << 16) | (a << 24)); -} - -void GPU_SetTextureEnable(GPU_TEXUNIT units) -{ - GPUCMD_AddMaskedWrite(GPUREG_SH_OUTATTR_CLOCK, 0x2, units<<8); // enables texcoord outputs - GPUCMD_AddWrite(GPUREG_TEXUNIT_CONFIG, 0x00011000|units); // enables texture units -} - -void GPU_SetTexture(GPU_TEXUNIT unit, u32* data, u16 width, u16 height, u32 param, GPU_TEXCOLOR colorType) -{ - switch (unit) - { - case GPU_TEXUNIT0: - GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, colorType); - GPUCMD_AddWrite(GPUREG_TEXUNIT0_ADDR1, ((u32)data)>>3); - GPUCMD_AddWrite(GPUREG_TEXUNIT0_DIM, (width<<16)|height); - GPUCMD_AddWrite(GPUREG_TEXUNIT0_PARAM, param); - break; - - case GPU_TEXUNIT1: - GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, colorType); - GPUCMD_AddWrite(GPUREG_TEXUNIT1_ADDR, ((u32)data)>>3); - GPUCMD_AddWrite(GPUREG_TEXUNIT1_DIM, (width<<16)|height); - GPUCMD_AddWrite(GPUREG_TEXUNIT1_PARAM, param); - break; - - case GPU_TEXUNIT2: - GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, colorType); - GPUCMD_AddWrite(GPUREG_TEXUNIT2_ADDR, ((u32)data)>>3); - GPUCMD_AddWrite(GPUREG_TEXUNIT2_DIM, (width<<16)|height); - GPUCMD_AddWrite(GPUREG_TEXUNIT2_PARAM, param); - break; - } -} - -void GPU_SetTextureBorderColor(GPU_TEXUNIT unit,u32 borderColor) -{ - switch (unit) - { - case GPU_TEXUNIT0: - GPUCMD_AddWrite(GPUREG_TEXUNIT0_BORDER_COLOR, borderColor); - break; - - case GPU_TEXUNIT1: - GPUCMD_AddWrite(GPUREG_TEXUNIT1_BORDER_COLOR, borderColor); - break; - - case GPU_TEXUNIT2: - GPUCMD_AddWrite(GPUREG_TEXUNIT2_BORDER_COLOR, borderColor); - break; - } -} - -const u8 GPU_FORMATSIZE[4]={1,1,2,4}; - -void GPU_SetAttributeBuffers(u8 totalAttributes, u32* baseAddress, u64 attributeFormats, u16 attributeMask, u64 attributePermutation, u8 numBuffers, u32 bufferOffsets[], u64 bufferPermutations[], u8 bufferNumAttributes[]) -{ - u32 param[0x28]; - - memset(param, 0x00, 0x28*4); - - param[0x0]=((u32)baseAddress)>>3; - param[0x1]=attributeFormats&0xFFFFFFFF; - param[0x2]=((totalAttributes-1)<<28)|((attributeMask&0xFFF)<<16)|((attributeFormats>>32)&0xFFFF); - - int i, j; - u8 sizeTable[0xC]; - for(i=0;i>2)+1); - attributeFormats>>=4; - } - - for(i=0;i>(4*j))&0xF]; - param[3*(i+1)+2]=(bufferNumAttributes[i]<<28)|((stride&0xFFF)<<16)|((bufferPermutations[i]>>32)&0xFFFF); - } - - GPUCMD_AddIncrementalWrites(GPUREG_ATTRIBBUFFERS_LOC, param, 0x00000027); - - GPUCMD_AddMaskedWrite(GPUREG_VSH_INPUTBUFFER_CONFIG, 0xB, 0xA0000000|(totalAttributes-1)); - GPUCMD_AddWrite(GPUREG_VSH_NUM_ATTR, (totalAttributes-1)); - - GPUCMD_AddIncrementalWrites(GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW, ((u32[]){attributePermutation&0xFFFFFFFF, (attributePermutation>>32)&0xFFFF}), 2); -} - -void GPU_SetAttributeBuffersAddress(u32* baseAddress) -{ - GPUCMD_AddWrite(GPUREG_ATTRIBBUFFERS_LOC, ((u32)baseAddress)>>3); -} - -void GPU_SetFaceCulling(GPU_CULLMODE mode) -{ - GPUCMD_AddWrite(GPUREG_FACECULLING_CONFIG, mode&0x3); -} - -void GPU_SetCombinerBufferWrite(u8 rgb_config, u8 alpha_config) -{ - GPUCMD_AddMaskedWrite(GPUREG_TEXENV_UPDATE_BUFFER, 0x2, (rgb_config << 8) | (alpha_config << 12)); -} - -const u8 GPU_TEVID[]={0xC0,0xC8,0xD0,0xD8,0xF0,0xF8}; - -void GPU_SetTexEnv(u8 id, u16 rgbSources, u16 alphaSources, u16 rgbOperands, u16 alphaOperands, GPU_COMBINEFUNC rgbCombine, GPU_COMBINEFUNC alphaCombine, u32 constantColor) -{ - if(id>6)return; - u32 param[0x5]; - memset(param, 0x00, 5*4); - - param[0x0]=(alphaSources<<16)|(rgbSources); - param[0x1]=(alphaOperands<<12)|(rgbOperands); - param[0x2]=(alphaCombine<<16)|(rgbCombine); - param[0x3]=constantColor; - param[0x4]=0x00000000; // ? - - GPUCMD_AddIncrementalWrites(GPUREG_0000|GPU_TEVID[id], param, 0x00000005); -} - -void GPU_DrawArray(GPU_Primitive_t primitive, u32 first, u32 count) -{ - //set primitive type - GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 0x2, primitive); - GPUCMD_AddMaskedWrite(GPUREG_RESTART_PRIMITIVE, 0x2, 0x00000001); - //index buffer address register should be cleared (except bit 31) before drawing - GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, 0x80000000); - //pass number of vertices - GPUCMD_AddWrite(GPUREG_NUMVERTICES, count); - //set first vertex - GPUCMD_AddWrite(GPUREG_VERTEX_OFFSET, first); - - //all the following except 0x000F022E might be useless - GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 0x1, 0x00000001); - GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 0x1, 0x00000000); - GPUCMD_AddWrite(GPUREG_DRAWARRAYS, 0x00000001); - GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 0x1, 0x00000001); - GPUCMD_AddWrite(GPUREG_VTX_FUNC, 0x00000001); - GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 0x00000001); -} - -void GPU_DrawElements(GPU_Primitive_t primitive, u32* indexArray, u32 n) -{ - //set primitive type - GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 0x2, primitive); - GPUCMD_AddMaskedWrite(GPUREG_RESTART_PRIMITIVE, 0x2, 0x00000001); - //index buffer (TODO : support multiple types) - GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, 0x80000000|((u32)indexArray)); - //pass number of vertices - GPUCMD_AddWrite(GPUREG_NUMVERTICES, n); - - GPUCMD_AddWrite(GPUREG_VERTEX_OFFSET, 0x00000000); - - GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 0x2, 0x00000100); - GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 0x2, 0x00000100); - - GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 0x1, 0x00000000); - GPUCMD_AddWrite(GPUREG_DRAWELEMENTS, 0x00000001); - GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 0x1, 0x00000001); - GPUCMD_AddWrite(GPUREG_VTX_FUNC, 0x00000001); - - // CHECKME: does this one also require GPUREG_FRAMEBUFFER_FLUSH at the end? -} - -void GPU_FinishDrawing() -{ - GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 0x00000001); - GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 0x00000001); - GPUCMD_AddWrite(GPUREG_EARLYDEPTH_CLEAR, 0x00000001); -} diff --git a/libctru/source/gpu/gpu.c b/libctru/source/gpu/gpu.c index 9146c10..7029622 100644 --- a/libctru/source/gpu/gpu.c +++ b/libctru/source/gpu/gpu.c @@ -32,7 +32,7 @@ void GPUCMD_GetBuffer(u32** adr, u32* size, u32* offset) if(offset)*offset=gpuCmdBufOffset; } -void GPUCMD_AddRawCommands(u32* cmd, u32 size) +void GPUCMD_AddRawCommands(const u32* cmd, u32 size) { if(!cmd || !size)return; @@ -55,7 +55,7 @@ void GPUCMD_FlushAndRun(void) GX_ProcessCommandList(gpuCmdBuf, gpuCmdBufOffset*4, 0x0); } -void GPUCMD_Add(u32 header, u32* param, u32 paramlength) +void GPUCMD_Add(u32 header, const u32* param, u32 paramlength) { u32 zero=0x0; diff --git a/libctru/source/gpu/shaderProgram.c b/libctru/source/gpu/shaderProgram.c index 8b2c34e..edfcf98 100644 --- a/libctru/source/gpu/shaderProgram.c +++ b/libctru/source/gpu/shaderProgram.c @@ -5,7 +5,7 @@ #include <3ds/gpu/registers.h> #include <3ds/gpu/shaderProgram.h> -static void GPU_SetShaderOutmap(u32 outmapData[8]); +static void GPU_SetShaderOutmap(const u32 outmapData[8]); static void GPU_SendShaderCode(GPU_SHADER_TYPE type, u32* data, u16 offset, u16 length); static void GPU_SendOperandDescriptors(GPU_SHADER_TYPE type, u32* data, u16 offset, u16 length); @@ -168,7 +168,6 @@ Result shaderProgramSetGsh(shaderProgram_s* sp, DVLE_s* dvle, u8 stride) sp->geoShaderInputPermutation[0] = 0x76543210; sp->geoShaderInputPermutation[1] = 0xFEDCBA98; sp->geoShaderInputStride = stride; - sp->geoShaderMode = GSH_NORMAL; return shaderInstanceInit(sp->geometryShader, dvle); } @@ -182,82 +181,149 @@ Result shaderProgramSetGshInputPermutation(shaderProgram_s* sp, u64 permutation) return 0; } -Result shaderProgramSetGshMode(shaderProgram_s* sp, geoShaderMode mode) +static inline void shaderProgramUploadDvle(const DVLE_s* dvle) { - if(!sp || !sp->geometryShader)return -1; + const DVLP_s* dvlp = dvle->dvlp; + // Limit vertex shader code size to the first 512 instructions + int codeSize = dvle->type == GEOMETRY_SHDR ? dvlp->codeSize : (dvlp->codeSize < 512 ? dvlp->codeSize : 512); + GPU_SendShaderCode(dvle->type, dvlp->codeData, 0, codeSize); + GPU_SendOperandDescriptors(dvle->type, dvlp->opcdescData, 0, dvlp->opdescSize); +} - sp->geoShaderMode = mode & 3; - return 0; +static inline void shaderProgramMergeOutmaps(u32* outmapData, const u32* vshOutmap, const u32* gshOutmap) +{ + int i, j; + + // Find and copy attributes common to both vertex and geometry shader + u32 vsh_common = 0, gsh_common = 0; + for (i = 1; i < 8; i ++) + { + u32 mask = gshOutmap[i]; + if (mask == 0x1F1F1F1F) + break; + for (j = 1; j < 8; j ++) + { + if (vshOutmap[j] == mask) + { + outmapData[++outmapData[0]] = mask; + vsh_common |= BIT(j); + gsh_common |= BIT(i); + break; + } + } + } + + // Find and copy attributes that are exclusive to the geometry shader + for (i = 1; i < 8; i ++) + { + u32 mask = gshOutmap[i]; + if (mask == 0x1F1F1F1F) + break; + if (!(gsh_common & BIT(i))) + outmapData[++outmapData[0]] = mask; + } + + // Find and copy attributes that are exclusive to the vertex shader + for (i = 1; i < 8; i ++) + { + u32 mask = vshOutmap[i]; + if (mask == 0x1F1F1F1F) + break; + if (!(vsh_common & BIT(i))) + outmapData[++outmapData[0]] = mask; + } } Result shaderProgramConfigure(shaderProgram_s* sp, bool sendVshCode, bool sendGshCode) { - if(!sp)return -1; + if (!sp || !sp->vertexShader) return -1; - if(!sp->vertexShader)return -2; - - // configure geostage - // has to be done first or else VSH registers might only reconfigure 3 of the 4 shader units ! - if(!sp->geometryShader) - { - GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 0x1, 0x00000000); - GPUCMD_AddMaskedWrite(GPUREG_VSH_COM_MODE, 0x1, 0x00000000); - }else{ - GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 0x1, 0x00000002); - GPUCMD_AddMaskedWrite(GPUREG_VSH_COM_MODE, 0x1, 0x00000001); - } - - // setup vertex shader stuff no matter what + // Get pointers to relevant structures const DVLE_s* vshDvle = sp->vertexShader->dvle; - const DVLP_s* vshDvlp = vshDvle->dvlp; + const DVLE_s* gshDvle = sp->geometryShader ? sp->geometryShader->dvle : NULL; + const DVLE_s* mainDvle = gshDvle ? gshDvle : vshDvle; + + // Variables for working with the outmap + u32 outmapData[8]; + u32 outmapMode = mainDvle->outmapMode; + u32 outmapClock = mainDvle->outmapClock; + + // Initialize geometry engine - do this early in order to ensure all 4 units are correctly initialized + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 0x3, gshDvle ? 2 : 0); + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 0x3, 0); + GPUCMD_AddMaskedWrite(GPUREG_VSH_COM_MODE, 0x1, gshDvle ? 1 : 0); + + // Set up vertex shader code blob (if necessary) if (sendVshCode) - { - GPU_SendShaderCode(vshDvle->type, vshDvlp->codeData, 0, vshDvlp->codeSize); - GPU_SendOperandDescriptors(vshDvle->type, vshDvlp->opcdescData, 0, vshDvlp->opdescSize); - } + shaderProgramUploadDvle(vshDvle); + + // Set up vertex shader entrypoint & outmap mask GPUCMD_AddWrite(GPUREG_VSH_ENTRYPOINT, 0x7FFF0000|(vshDvle->mainOffset&0xFFFF)); GPUCMD_AddWrite(GPUREG_VSH_OUTMAP_MASK, vshDvle->outmapMask); + GPUCMD_AddWrite(GPUREG_VSH_OUTMAP_TOTAL1, vshDvle->outmapData[0]-1); + GPUCMD_AddWrite(GPUREG_VSH_OUTMAP_TOTAL2, vshDvle->outmapData[0]-1); - GPUCMD_AddWrite(GPUREG_VSH_OUTMAP_TOTAL1, vshDvle->outmapData[0]-1); // ? - GPUCMD_AddWrite(GPUREG_VSH_OUTMAP_TOTAL2, vshDvle->outmapData[0]-1); // ? - - bool subdivision = sp->geoShaderMode >= GSH_SUBDIVISION_LOOP; - GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 0x8, subdivision ? 0x80000000 : 0); // Enable or disable subdivision - u32 gshMisc = 0; - if (subdivision) - gshMisc = 1; - else if (sp->geoShaderMode == GSH_PARTICLE) - gshMisc = 0x01004302; - GPUCMD_AddWrite(GPUREG_GSH_MISC0, gshMisc); - GPUCMD_AddWrite(GPUREG_GSH_MISC1, sp->geoShaderMode); - - if(!sp->geometryShader) + // Set up geometry shader (if present) + if (gshDvle) { - // finish setting up vertex shader alone - GPU_SetShaderOutmap((u32*)vshDvle->outmapData); - - GPUCMD_AddWrite(GPUREG_SH_OUTATTR_MODE, vshDvle->outmapMode); - GPUCMD_AddWrite(GPUREG_SH_OUTATTR_CLOCK, vshDvle->outmapClock); - }else{ - // setup both vertex and geometry shader - const DVLE_s* gshDvle = sp->geometryShader->dvle; - const DVLP_s* gshDvlp = gshDvle->dvlp; + // Set up geometry shader code blob (if necessary) if (sendGshCode) - { - GPU_SendShaderCode(gshDvle->type, gshDvlp->codeData, 0, gshDvlp->codeSize); - GPU_SendOperandDescriptors(gshDvle->type, gshDvlp->opcdescData, 0, gshDvlp->opdescSize); - } + shaderProgramUploadDvle(gshDvle); + + // Set up geometry shader entrypoint & outmap mask GPUCMD_AddWrite(GPUREG_GSH_ENTRYPOINT, 0x7FFF0000|(gshDvle->mainOffset&0xFFFF)); GPUCMD_AddWrite(GPUREG_GSH_OUTMAP_MASK, gshDvle->outmapMask); + } - GPU_SetShaderOutmap((u32*)gshDvle->outmapData); + // Merge vertex shader & geometry shader outmaps if requested + if (gshDvle && gshDvle->mergeOutmaps) + { + // Clear outmap + memset(outmapData, 0x1F, sizeof(outmapData)); + outmapData[0] = 0; - //GSH input attributes stuff - GPUCMD_AddWrite(GPUREG_GSH_INPUTBUFFER_CONFIG, 0x08000000|(sp->geoShaderInputStride-1)|(subdivision?0x100:0)); + // Merge outmaps + shaderProgramMergeOutmaps(outmapData, vshDvle->outmapData, gshDvle->outmapData); + outmapMode |= vshDvle->outmapMode; + outmapClock |= vshDvle->outmapClock; + } else + memcpy(outmapData, mainDvle->outmapData, sizeof(outmapData)); + + // Upload and configure outmap + GPU_SetShaderOutmap(outmapData); + GPUCMD_AddWrite(GPUREG_SH_OUTATTR_MODE, outmapMode); + GPUCMD_AddWrite(GPUREG_SH_OUTATTR_CLOCK, outmapClock); + + // Configure geostage + if (gshDvle) + { + // Input stride: use value if specified, otherwise use number of outputs in vertex shader + int stride = sp->geoShaderInputStride ? sp->geoShaderInputStride : vshDvle->outmapData[0]; + + // Enable or disable variable-size primitive processing + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 0xA, gshDvle->gshMode == GSH_VARIABLE_PRIM ? 0x80000000 : 0); + + // Set up geoshader processing mode + u32 misc = gshDvle->gshMode; + if (misc == GSH_FIXED_PRIM) + misc |= 0x01000000 | ((u32)gshDvle->gshFixedVtxStart<<16) | ((stride-1)<<12) | ((u32)(gshDvle->gshFixedVtxNum-1)<<8); + GPUCMD_AddWrite(GPUREG_GSH_MISC0, misc); + + // Set up variable-size primitive mode parameters + GPUCMD_AddWrite(GPUREG_GSH_MISC1, gshDvle->gshMode == GSH_VARIABLE_PRIM ? (gshDvle->gshVariableVtxNum-1) : 0); + + // Set up geoshader input + GPUCMD_AddWrite(GPUREG_GSH_INPUTBUFFER_CONFIG, 0x08000000 | (gshDvle->gshMode ? 0x0100 : 0) | (stride-1)); + + // Set up geoshader permutation GPUCMD_AddIncrementalWrites(GPUREG_GSH_ATTRIBUTES_PERMUTATION_LOW, sp->geoShaderInputPermutation, 2); - - GPUCMD_AddWrite(GPUREG_SH_OUTATTR_MODE, gshDvle->outmapMode); - GPUCMD_AddWrite(GPUREG_SH_OUTATTR_CLOCK, gshDvle->outmapClock); + } else + { + // Defaults for when geostage is disabled + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 0xA, 0); + GPUCMD_AddWrite(GPUREG_GSH_MISC0, 0); + GPUCMD_AddWrite(GPUREG_GSH_MISC1, 0); + GPUCMD_AddWrite(GPUREG_GSH_INPUTBUFFER_CONFIG, 0xA0000000); } return 0; @@ -271,12 +337,12 @@ Result shaderProgramUse(shaderProgram_s* sp) int i; // Set up uniforms - GPUCMD_AddWrite(GPUREG_VSH_BOOLUNIFORM, 0x7FFF0000|~sp->vertexShader->boolUniforms); + GPUCMD_AddWrite(GPUREG_VSH_BOOLUNIFORM, 0x7FFF0000|sp->vertexShader->boolUniforms); GPUCMD_AddIncrementalWrites(GPUREG_VSH_INTUNIFORM_I0, sp->vertexShader->intUniforms, 4); for(i=0; ivertexShader->numFloat24Uniforms; i++) GPUCMD_AddIncrementalWrites(GPUREG_VSH_FLOATUNIFORM_CONFIG, (u32*)&sp->vertexShader->float24Uniforms[i], 4); if (sp->geometryShader) { - GPUCMD_AddWrite(GPUREG_GSH_BOOLUNIFORM, 0x7FFF0000|~sp->geometryShader->boolUniforms); + GPUCMD_AddWrite(GPUREG_GSH_BOOLUNIFORM, 0x7FFF0000|sp->geometryShader->boolUniforms); GPUCMD_AddIncrementalWrites(GPUREG_GSH_INTUNIFORM_I0, sp->geometryShader->intUniforms, 4); for(i=0; igeometryShader->numFloat24Uniforms; i++) GPUCMD_AddIncrementalWrites(GPUREG_GSH_FLOATUNIFORM_CONFIG, (u32*)&sp->geometryShader->float24Uniforms[i], 4); } @@ -284,7 +350,7 @@ Result shaderProgramUse(shaderProgram_s* sp) return 0; } -void GPU_SetShaderOutmap(u32 outmapData[8]) +void GPU_SetShaderOutmap(const u32 outmapData[8]) { GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 0x1, outmapData[0]-1); GPUCMD_AddIncrementalWrites(GPUREG_SH_OUTMAP_TOTAL, outmapData, 8); diff --git a/libctru/source/gpu/shbin.c b/libctru/source/gpu/shbin.c index 5209c3c..7f60150 100644 --- a/libctru/source/gpu/shbin.c +++ b/libctru/source/gpu/shbin.c @@ -38,9 +38,18 @@ DVLB_s* DVLB_ParseFile(u32* shbinData, u32 shbinSize) dvle->dvlp=&ret->DVLP; dvle->type=(dvleData[1]>>16)&0xFF; + dvle->mergeOutmaps=(dvleData[1]>>24)&1; dvle->mainOffset=dvleData[2]; dvle->endmainOffset=dvleData[3]; + if(dvle->type==GEOMETRY_SHDR) + { + dvle->gshMode=dvleData[5]&0xFF; + dvle->gshFixedVtxStart=(dvleData[5]>>8)&0xFF; + dvle->gshVariableVtxNum=(dvleData[5]>>16)&0xFF; + dvle->gshFixedVtxNum=(dvleData[5]>>24)&0xFF; + } + dvle->constTableSize=dvleData[7]; dvle->constTableData=(DVLE_constEntry_s*)&dvleData[dvleData[6]/4]; @@ -89,80 +98,53 @@ s8 DVLE_GetUniformRegister(DVLE_s* dvle, const char* name) void DVLE_GenerateOutmap(DVLE_s* dvle) { - if(!dvle)return; + if (!dvle) return; + // Initialize outmap data memset(dvle->outmapData, 0x1F, sizeof(dvle->outmapData)); + dvle->outmapData[0] = 0; + dvle->outmapMask = 0; + dvle->outmapMode = 0; + dvle->outmapClock = 0; - int i; - u8 numAttr=0; - u8 maxAttr=0; - u8 attrMask=0; - u32 attrMode=0; - u32 attrClock=0; - - for(i=0;ioutTableSize;i++) + int i, j, k; + for (i = 0; i < dvle->outTableSize; i ++) { - u32* out=&dvle->outmapData[dvle->outTableData[i].regID+1]; - u32 mask=0x00000000; - u8 tmpmask=dvle->outTableData[i].mask; - mask=(mask<<8)|((tmpmask&8)?0xFF:0x00);tmpmask<<=1; - mask=(mask<<8)|((tmpmask&8)?0xFF:0x00);tmpmask<<=1; - mask=(mask<<8)|((tmpmask&8)?0xFF:0x00);tmpmask<<=1; - mask=(mask<<8)|((tmpmask&8)?0xFF:0x00);tmpmask<<=1; + int type = dvle->outTableData[i].type; + int mask = dvle->outTableData[i].mask; + int regID = dvle->outTableData[i].regID; + u32* out = &dvle->outmapData[regID+1]; - if(*out==0x1F1F1F1F)numAttr++; - - u32 val=0x1F1F1F1F; - switch(dvle->outTableData[i].type) + if (!(dvle->outmapMask & BIT(regID))) { - case RESULT_POSITION: val=0x03020100; break; - case RESULT_NORMALQUAT: val=0x07060504; break; - case RESULT_COLOR: val=0x0B0A0908; break; - case RESULT_TEXCOORD0: val=0x1F1F0D0C; break; - case RESULT_TEXCOORD0W: val=0x10101010; break; - case RESULT_TEXCOORD1: val=0x1F1F0F0E; break; - case RESULT_TEXCOORD2: val=0x1F1F1716; break; - case RESULT_VIEW: val=0x1F141312; break; - } - *out=((*out)&~mask)|(val&mask); - - switch(dvle->outTableData[i].type) - { - case RESULT_POSITION: - if ((*out & 0xFF0000)==0x020000) - attrClock |= BIT(0); - break; - case RESULT_COLOR: - attrClock |= BIT(1); - break; - case RESULT_TEXCOORD0: - attrMode = 1; - attrClock |= BIT(8); - break; - case RESULT_TEXCOORD1: - attrMode = 1; - attrClock |= BIT(9); - break; - case RESULT_TEXCOORD2: - attrMode = 1; - attrClock |= BIT(10); - break; - case RESULT_TEXCOORD0W: - attrMode = 1; - attrClock |= BIT(16); - break; - case RESULT_NORMALQUAT: - case RESULT_VIEW: - attrClock |= BIT(24); - break; + dvle->outmapMask |= BIT(regID); + dvle->outmapData[0] ++; } - attrMask|=1<outTableData[i].regID; - if(dvle->outTableData[i].regID+1>maxAttr)maxAttr=dvle->outTableData[i].regID+1; + int sem = 0x1F, num = 0; + switch (type) + { + case RESULT_POSITION: sem = 0x00; num = 4; break; + case RESULT_NORMALQUAT: sem = 0x04; num = 4; dvle->outmapClock |= BIT(24); break; + case RESULT_COLOR: sem = 0x08; num = 4; dvle->outmapClock |= BIT(1); break; + case RESULT_TEXCOORD0: sem = 0x0C; num = 2; dvle->outmapClock |= BIT(8); dvle->outmapMode = 1; break; + case RESULT_TEXCOORD0W: sem = 0x10; num = 1; dvle->outmapClock |= BIT(16); dvle->outmapMode = 1; break; + case RESULT_TEXCOORD1: sem = 0x0E; num = 2; dvle->outmapClock |= BIT(9); dvle->outmapMode = 1; break; + case RESULT_TEXCOORD2: sem = 0x16; num = 2; dvle->outmapClock |= BIT(10); dvle->outmapMode = 1; break; + case RESULT_VIEW: sem = 0x12; num = 3; dvle->outmapClock |= BIT(24); break; + default: continue; + } + + for (j = 0, k = 0; j < 4 && k < num; j ++) + { + if (mask & BIT(j)) + { + *out &= ~(0xFF << (j*8)); + *out |= (sem++) << (j*8); + k ++; + if (type==RESULT_POSITION && k==3) + dvle->outmapClock |= BIT(0); + } + } } - - dvle->outmapData[0]=numAttr; - dvle->outmapMask=attrMask; - dvle->outmapMode=attrMode; - dvle->outmapClock=attrClock; } diff --git a/libctru/source/ndsp/ndsp.c b/libctru/source/ndsp/ndsp.c index 867eeb5..00b3cdc 100644 --- a/libctru/source/ndsp/ndsp.c +++ b/libctru/source/ndsp/ndsp.c @@ -9,7 +9,7 @@ u16 ndspFrameId, ndspBufferCurId, ndspBufferId; void* ndspVars[16][2]; -static bool bComponentLoaded = false, bDspReady = false, bSleeping = false, bNeedsSync = false; +static bool bComponentLoaded = false, bDspReady = false, bSleeping = false, bActuallySleeping = false, bNeedsSync = false; static u32 droppedFrames, frameCount; static const void* componentBin; @@ -19,7 +19,8 @@ static bool componentFree; static aptHookCookie aptCookie; -static Handle irqEvent, dspSem, sleepEvent; +static Handle irqEvent, dspSem; +static LightEvent sleepEvent; static LightLock ndspMutex; static u8 dspVar5Backup[0x1080]; @@ -211,7 +212,7 @@ static Result ndspInitialize(bool resume) rc = ndspLoadComponent(); if (R_FAILED(rc)) return rc; - rc = svcCreateEvent(&irqEvent, 1); + rc = svcCreateEvent(&irqEvent, RESET_STICKY); if (R_FAILED(rc)) goto _fail1; rc = DSP_RegisterInterruptEvents(irqEvent, 2, 2); @@ -305,7 +306,11 @@ static void ndspAptHook(APT_HookType hook, void* param) case APTHOOK_ONWAKEUP: bSleeping = false; ndspInitialize(true); - svcSignalEvent(sleepEvent); + if (bActuallySleeping) + { + bActuallySleeping = false; + LightEvent_Signal(&sleepEvent); + } break; case APTHOOK_ONSUSPEND: @@ -323,8 +328,8 @@ static void ndspSync(void) { if (bSleeping) { - svcWaitSynchronization(sleepEvent, U64_MAX); - svcClearEvent(sleepEvent); + bActuallySleeping = true; + LightEvent_Wait(&sleepEvent); } ndspWaitForIrq(); @@ -455,7 +460,7 @@ Result ndspInit(void) if (!componentBin && !ndspFindAndLoadComponent()) { - rc = MAKERESULT(RL_PERMANENT, RS_NOTFOUND, 41, RD_NOT_FOUND); + rc = MAKERESULT(RL_PERMANENT, RS_NOTFOUND, RM_DSP, RD_NOT_FOUND); goto _fail0; } @@ -479,17 +484,14 @@ Result ndspInit(void) rc = ndspInitialize(false); if (R_FAILED(rc)) goto _fail1; - rc = svcCreateEvent(&sleepEvent, 0); - if (R_FAILED(rc)) goto _fail2; + LightEvent_Init(&sleepEvent, RESET_ONESHOT); ndspThread = threadCreate(ndspThreadMain, 0x0, NDSP_THREAD_STACK_SIZE, 0x18, -2, true); - if (!ndspThread) goto _fail3; + if (!ndspThread) goto _fail2; aptHook(&aptCookie, ndspAptHook, NULL); return 0; -_fail3: - svcCloseHandle(sleepEvent); _fail2: ndspFinalize(false); _fail1: @@ -509,10 +511,12 @@ void ndspExit(void) if (AtomicDecrement(&ndspRefCount)) return; if (!bDspReady) return; ndspThreadRun = false; - if (bSleeping) - svcSignalEvent(sleepEvent); + if (bActuallySleeping) + { + bActuallySleeping = false; + LightEvent_Signal(&sleepEvent); + } threadJoin(ndspThread, U64_MAX); - svcCloseHandle(sleepEvent); aptUnhook(&aptCookie); if (!bSleeping) ndspFinalize(false); diff --git a/libctru/source/os-versionbin.c b/libctru/source/os-versionbin.c index a7fe0a5..5856aa7 100644 --- a/libctru/source/os-versionbin.c +++ b/libctru/source/os-versionbin.c @@ -41,11 +41,12 @@ static Result __read_versionbin(FS_ArchiveID archiveId, FS_Path archivePath, FS_ Result ret = 0; Handle filehandle = 0; FILE *f = NULL; + struct romfs_mount *mount; ret = FSUSER_OpenFileDirectly(&filehandle, archiveId, archivePath, fileLowPath, FS_OPEN_READ, 0x0); if(R_FAILED(ret))return ret; - ret = romfsInitFromFile(filehandle, 0x0); + ret = romfsMountFromFile(filehandle, 0x0, &mount); if(R_FAILED(ret))return ret; f = fopen("romfs:/version.bin", "r"); @@ -59,7 +60,7 @@ static Result __read_versionbin(FS_ArchiveID archiveId, FS_Path archivePath, FS_ fclose(f); } - romfsExit(); + romfsUnmount(mount); return ret; } diff --git a/libctru/source/romfs_dev.c b/libctru/source/romfs_dev.c index a7d0e69..072f24c 100644 --- a/libctru/source/romfs_dev.c +++ b/libctru/source/romfs_dev.c @@ -16,15 +16,17 @@ #include <3ds/util/utf.h> #include <3ds/env.h> -static bool romFS_active; -static Handle romFS_file; -static time_t romFS_mtime; -static u32 romFS_offset; -static romfs_header romFS_header; -static romfs_dir* romFS_cwd; - -static u32 *dirHashTable, *fileHashTable; -static void *dirTable, *fileTable; +typedef struct romfs_mount +{ + Handle fd; + time_t mtime; + u32 offset; + romfs_header header; + romfs_dir *cwd; + u32 *dirHashTable, *fileHashTable; + void *dirTable, *fileTable; + struct romfs_mount *next; +} romfs_mount; extern int __system_argc; extern char** __system_argv; @@ -32,25 +34,25 @@ extern char** __system_argv; static char __component[PATH_MAX+1]; static uint16_t __utf16path[PATH_MAX+1]; -#define romFS_root ((romfs_dir*)dirTable) -#define romFS_dir(x) ((romfs_dir*) ((u8*)dirTable + (x))) -#define romFS_file(x) ((romfs_file*)((u8*)fileTable + (x))) +#define romFS_root(m) ((romfs_dir*)(m)->dirTable) +#define romFS_dir(m,x) ((romfs_dir*) ((u8*)(m)->dirTable + (x))) +#define romFS_file(m,x) ((romfs_file*)((u8*)(m)->fileTable + (x))) #define romFS_none ((u32)~0) #define romFS_dir_mode (S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH) #define romFS_file_mode (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH) -static ssize_t _romfs_read(u64 offset, void* buffer, u32 size) +static ssize_t _romfs_read(romfs_mount *mount, u64 offset, void* buffer, u32 size) { - u64 pos = (u64)romFS_offset + offset; + u64 pos = (u64)mount->offset + offset; u32 read = 0; - Result rc = FSFILE_Read(romFS_file, &read, pos, buffer, size); + Result rc = FSFILE_Read(mount->fd, &read, pos, buffer, size); if (R_FAILED(rc)) return -1; return read; } -static bool _romfs_read_chk(u64 offset, void* buffer, u32 size) +static bool _romfs_read_chk(romfs_mount *mount, u64 offset, void* buffer, u32 size) { - return _romfs_read(offset, buffer, size) == size; + return _romfs_read(mount, offset, buffer, size) == size; } //----------------------------------------------------------------------------- @@ -69,12 +71,14 @@ static int romfs_dirclose(struct _reent *r, DIR_ITER *dirState); typedef struct { - romfs_file *file; - u64 offset, pos; + romfs_mount *mount; + romfs_file *file; + u64 offset, pos; } romfs_fileobj; typedef struct { + romfs_mount *mount; romfs_dir* dir; u32 state; u32 childDir; @@ -120,21 +124,68 @@ typedef struct u32 fsOffset; } _3DSX_Header; -static Result romfsInitCommon(void); -static void romfsInitMtime(FS_ArchiveID archId, FS_Path archPath, FS_Path filePath); +static Result romfsMountCommon(romfs_mount *mount); +static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath); __attribute__((weak)) const char* __romfs_path = NULL; -Result romfsInit(void) +static romfs_mount *romfs_mount_list = NULL; + +static void romfs_insert(romfs_mount *mount) { - if (romFS_active) return 0; + mount->next = romfs_mount_list; + romfs_mount_list = mount; +} + +static void romfs_remove(romfs_mount *mount) +{ + for(romfs_mount **it = &romfs_mount_list; *it; it = &(*it)->next) + { + if(*it == mount) + { + *it = mount->next; + return; + } + } +} + +static romfs_mount* romfs_alloc(void) +{ + romfs_mount *mount = (romfs_mount*)calloc(1, sizeof(romfs_mount)); + + if(mount) + romfs_insert(mount); + + return mount; +} + +static void romfs_free(romfs_mount *mount) +{ + romfs_remove(mount); + free(mount->fileTable); + free(mount->fileHashTable); + free(mount->dirTable); + free(mount->dirHashTable); + free(mount); +} + +Result romfsMount(struct romfs_mount **p) +{ + romfs_mount *mount = romfs_alloc(); + if(mount == NULL) + return 99; + if (envIsHomebrew()) { // RomFS appended to a 3DSX file const char* filename = __romfs_path; if (__system_argc > 0 && __system_argv[0]) filename = __system_argv[0]; - if (!filename) return 1; + if (!filename) + { + romfs_free(mount); + return 1; + } if (strncmp(filename, "sdmc:/", 6) == 0) filename += 5; @@ -144,29 +195,46 @@ Result romfsInit(void) strncat(__component, filename+8, PATH_MAX); __component[PATH_MAX] = 0; filename = __component; - } else + } + else + { + romfs_free(mount); return 2; + } ssize_t units = utf8_to_utf16(__utf16path, (const uint8_t*)filename, PATH_MAX); - if (units < 0) return 3; - if (units >= PATH_MAX) return 4; + if (units < 0) + { + romfs_free(mount); + return 3; + } + if (units >= PATH_MAX) + { + romfs_free(mount); + return 4; + } __utf16path[units] = 0; FS_Path archPath = { PATH_EMPTY, 1, (u8*)"" }; FS_Path filePath = { PATH_UTF16, (units+1)*2, (u8*)__utf16path }; - Result rc = FSUSER_OpenFileDirectly(&romFS_file, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0); - if (R_FAILED(rc)) return rc; + Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0); + if (R_FAILED(rc)) + { + romfs_free(mount); + return rc; + } - romfsInitMtime(ARCHIVE_SDMC, archPath, filePath); + romfsInitMtime(mount, ARCHIVE_SDMC, archPath, filePath); _3DSX_Header hdr; - if (!_romfs_read_chk(0, &hdr, sizeof(hdr))) goto _fail0; + if (!_romfs_read_chk(mount, 0, &hdr, sizeof(hdr))) goto _fail0; if (hdr.magic != _3DSX_MAGIC) goto _fail0; if (hdr.headerSize < sizeof(hdr)) goto _fail0; - romFS_offset = hdr.fsOffset; - if (!romFS_offset) goto _fail0; - } else + mount->offset = hdr.fsOffset; + if (!mount->offset) goto _fail0; + } + else { // Regular RomFS u8 zeros[0xC]; @@ -175,75 +243,94 @@ Result romfsInit(void) FS_Path archPath = { PATH_EMPTY, 1, (u8*)"" }; FS_Path filePath = { PATH_BINARY, sizeof(zeros), zeros }; - Result rc = FSUSER_OpenFileDirectly(&romFS_file, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0); - if (R_FAILED(rc)) return rc; + Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0); + if (R_FAILED(rc)) + { + romfs_free(mount); + return rc; + } - romfsInitMtime(ARCHIVE_ROMFS, archPath, filePath); + romfsInitMtime(mount, ARCHIVE_ROMFS, archPath, filePath); } - return romfsInitCommon(); + Result ret = romfsMountCommon(mount); + if(R_SUCCEEDED(ret) && p) + *p = mount; + + return ret; _fail0: - FSFILE_Close(romFS_file); + FSFILE_Close(mount->fd); + romfs_free(mount); return 10; } -Result romfsInitFromFile(Handle file, u32 offset) +Result romfsMountFromFile(Handle file, u32 offset, struct romfs_mount **p) { - if (romFS_active) return 0; - romFS_file = file; - romFS_offset = offset; - return romfsInitCommon(); + romfs_mount *mount = romfs_alloc(); + if(mount == NULL) + return 99; + + mount->fd = file; + mount->offset = offset; + + Result ret = romfsMountCommon(mount); + if(R_SUCCEEDED(ret) && p) + *p = mount; + + return ret; } -Result romfsInitCommon(void) +Result romfsMountCommon(romfs_mount *mount) { - if (_romfs_read(0, &romFS_header, sizeof(romFS_header)) != sizeof(romFS_header)) - goto _fail0; + if (_romfs_read(mount, 0, &mount->header, sizeof(mount->header)) != sizeof(mount->header)) + goto fail; - dirHashTable = (u32*)malloc(romFS_header.dirHashTableSize); - if (!dirHashTable) goto _fail0; - if (!_romfs_read_chk(romFS_header.dirHashTableOff, dirHashTable, romFS_header.dirHashTableSize)) goto _fail1; + mount->dirHashTable = (u32*)malloc(mount->header.dirHashTableSize); + if (!mount->dirHashTable) + goto fail; + if (!_romfs_read_chk(mount, mount->header.dirHashTableOff, mount->dirHashTable, mount->header.dirHashTableSize)) + goto fail; - dirTable = malloc(romFS_header.dirTableSize); - if (!dirTable) goto _fail1; - if (!_romfs_read_chk(romFS_header.dirTableOff, dirTable, romFS_header.dirTableSize)) goto _fail2; + mount->dirTable = malloc(mount->header.dirTableSize); + if (!mount->dirTable) + goto fail; + if (!_romfs_read_chk(mount, mount->header.dirTableOff, mount->dirTable, mount->header.dirTableSize)) + goto fail; - fileHashTable = (u32*)malloc(romFS_header.fileHashTableSize); - if (!fileHashTable) goto _fail2; - if (!_romfs_read_chk(romFS_header.fileHashTableOff, fileHashTable, romFS_header.fileHashTableSize)) goto _fail3; + mount->fileHashTable = (u32*)malloc(mount->header.fileHashTableSize); + if (!mount->fileHashTable) + goto fail; + if (!_romfs_read_chk(mount, mount->header.fileHashTableOff, mount->fileHashTable, mount->header.fileHashTableSize)) + goto fail; - fileTable = malloc(romFS_header.fileTableSize); - if (!fileTable) goto _fail3; - if (!_romfs_read_chk(romFS_header.fileTableOff, fileTable, romFS_header.fileTableSize)) goto _fail4; + mount->fileTable = malloc(mount->header.fileTableSize); + if (!mount->fileTable) + goto fail; + if (!_romfs_read_chk(mount, mount->header.fileTableOff, mount->fileTable, mount->header.fileTableSize)) + goto fail; - romFS_cwd = romFS_root; - romFS_active = true; + mount->cwd = romFS_root(mount); - AddDevice(&romFS_devoptab); + // add device if this is the first one + if(mount->next == NULL && AddDevice(&romFS_devoptab) < 0) + goto fail; return 0; -_fail4: - free(fileTable); -_fail3: - free(fileHashTable); -_fail2: - free(dirTable); -_fail1: - free(dirHashTable); -_fail0: - FSFILE_Close(romFS_file); +fail: + FSFILE_Close(mount->fd); + romfs_free(mount); return 10; } -static void romfsInitMtime(FS_ArchiveID archId, FS_Path archPath, FS_Path filePath) +static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath) { u64 mtime; FS_Archive arch; Result rc; - romFS_mtime = time(NULL); + mount->mtime = time(NULL); rc = FSUSER_OpenArchive(&arch, archId, archPath); if (R_FAILED(rc)) return; @@ -259,21 +346,45 @@ static void romfsInitMtime(FS_ArchiveID archId, FS_Path archPath, FS_Path filePa mtime /= 1000; /* convert from 2000-based timestamp to UNIX timestamp */ mtime += 946684800; - romFS_mtime = mtime; + mount->mtime = mtime; } -Result romfsExit(void) +Result romfsBind(struct romfs_mount *mount) { - if (!romFS_active) return 0; - romFS_active = false; + for(romfs_mount **it = &romfs_mount_list; *it; it = &(*it)->next) + { + if(*it == mount) + { + *it = mount->next; + romfs_insert(mount); + return 0; + } + } - RemoveDevice("romfs:"); - FSFILE_Close(romFS_file); - free(dirHashTable); - free(fileHashTable); - free(dirTable); - free(fileTable); - romFS_offset = 0; + return 99; +} + +Result romfsUnmount(struct romfs_mount *mount) +{ + if(mount) + { + // unmount specific + FSFILE_Close(mount->fd); + romfs_free(mount); + } + else + { + // unmount everything + while(romfs_mount_list) + { + FSFILE_Close(romfs_mount_list->fd); + romfs_free(romfs_mount_list); + } + } + + // if no more mounts, remove device + if(romfs_mount_list == NULL) + RemoveDevice("romfs:"); return 0; } @@ -292,15 +403,15 @@ static u32 calcHash(u32 parent, u16* name, u32 namelen, u32 total) return hash % total; } -static romfs_dir* searchForDir(romfs_dir* parent, u16* name, u32 namelen) +static romfs_dir* searchForDir(romfs_mount *mount, romfs_dir* parent, u16* name, u32 namelen) { - u32 parentOff = (u32)parent - (u32)dirTable; - u32 hash = calcHash(parentOff, name, namelen, romFS_header.dirHashTableSize/4); + u32 parentOff = (u32)parent - (u32)mount->dirTable; + u32 hash = calcHash(parentOff, name, namelen, mount->header.dirHashTableSize/4); romfs_dir* curDir = NULL; u32 curOff; - for (curOff = dirHashTable[hash]; curOff != romFS_none; curOff = curDir->nextHash) + for (curOff = mount->dirHashTable[hash]; curOff != romFS_none; curOff = curDir->nextHash) { - curDir = romFS_dir(curOff); + curDir = romFS_dir(mount, curOff); if (curDir->parent != parentOff) continue; if (curDir->nameLen != namelen*2) continue; if (memcmp(curDir->name, name, namelen*2) != 0) continue; @@ -309,15 +420,15 @@ static romfs_dir* searchForDir(romfs_dir* parent, u16* name, u32 namelen) return NULL; } -static romfs_file* searchForFile(romfs_dir* parent, u16* name, u32 namelen) +static romfs_file* searchForFile(romfs_mount *mount, romfs_dir* parent, u16* name, u32 namelen) { - u32 parentOff = (u32)parent - (u32)dirTable; - u32 hash = calcHash(parentOff, name, namelen, romFS_header.fileHashTableSize/4); + u32 parentOff = (u32)parent - (u32)mount->dirTable; + u32 hash = calcHash(parentOff, name, namelen, mount->header.fileHashTableSize/4); romfs_file* curFile = NULL; u32 curOff; - for (curOff = fileHashTable[hash]; curOff != romFS_none; curOff = curFile->nextHash) + for (curOff = mount->fileHashTable[hash]; curOff != romFS_none; curOff = curFile->nextHash) { - curFile = romFS_file(curOff); + curFile = romFS_file(mount, curOff); if (curFile->parent != parentOff) continue; if (curFile->nameLen != namelen*2) continue; if (memcmp(curFile->name, name, namelen*2) != 0) continue; @@ -326,7 +437,7 @@ static romfs_file* searchForFile(romfs_dir* parent, u16* name, u32 namelen) return NULL; } -static int navigateToDir(romfs_dir** ppDir, const char** pPath, bool isDir) +static int navigateToDir(romfs_mount *mount, romfs_dir** ppDir, const char** pPath, bool isDir) { ssize_t units; @@ -335,10 +446,10 @@ static int navigateToDir(romfs_dir** ppDir, const char** pPath, bool isDir) if (!**pPath) return EILSEQ; - *ppDir = romFS_cwd; + *ppDir = mount->cwd; if (**pPath == '/') { - *ppDir = romFS_root; + *ppDir = romFS_root(mount); (*pPath)++; } @@ -370,7 +481,7 @@ static int navigateToDir(romfs_dir** ppDir, const char** pPath, bool isDir) if (!component[1]) continue; if (component[1]=='.' && !component[2]) { - *ppDir = romFS_dir((*ppDir)->parent); + *ppDir = romFS_dir(mount, (*ppDir)->parent); continue; } } @@ -381,7 +492,7 @@ static int navigateToDir(romfs_dir** ppDir, const char** pPath, bool isDir) if (units >= PATH_MAX) return ENAMETOOLONG; - *ppDir = searchForDir(*ppDir, __utf16path, units); + *ppDir = searchForDir(mount, *ppDir, __utf16path, units); if (!*ppDir) return EEXIST; } @@ -392,9 +503,9 @@ static int navigateToDir(romfs_dir** ppDir, const char** pPath, bool isDir) return 0; } -static ino_t dir_inode(romfs_dir *dir) +static ino_t dir_inode(romfs_mount *mount, romfs_dir *dir) { - return (uint32_t*)dir - (uint32_t*)dirTable; + return (uint32_t*)dir - (uint32_t*)mount->dirTable; } static off_t dir_size(romfs_dir *dir) @@ -402,14 +513,14 @@ static off_t dir_size(romfs_dir *dir) return sizeof(romfs_dir) + (dir->nameLen+3)/4; } -static nlink_t dir_nlink(romfs_dir *dir) +static nlink_t dir_nlink(romfs_mount *mount, romfs_dir *dir) { nlink_t count = 2; // one for self, one for parent u32 offset = dir->childDir; while(offset != romFS_none) { - romfs_dir *tmp = romFS_dir(offset); + romfs_dir *tmp = romFS_dir(mount, offset); ++count; offset = tmp->sibling; } @@ -417,7 +528,7 @@ static nlink_t dir_nlink(romfs_dir *dir) offset = dir->childFile; while(offset != romFS_none) { - romfs_file *tmp = romFS_file(offset); + romfs_file *tmp = romFS_file(mount, offset); ++count; offset = tmp->sibling; } @@ -425,9 +536,9 @@ static nlink_t dir_nlink(romfs_dir *dir) return count; } -static ino_t file_inode(romfs_file *file) +static ino_t file_inode(romfs_mount *mount, romfs_file *file) { - return ((uint32_t*)file - (uint32_t*)fileTable) + romFS_header.dirTableSize/4; + return ((uint32_t*)file - (uint32_t*)mount->fileTable) + mount->header.dirTableSize/4; } //----------------------------------------------------------------------------- @@ -436,6 +547,8 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, { romfs_fileobj* fileobj = (romfs_fileobj*)fileStruct; + fileobj->mount = romfs_mount_list; + if ((flags & O_ACCMODE) != O_RDONLY) { r->_errno = EROFS; @@ -443,7 +556,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, } romfs_dir* curDir = NULL; - r->_errno = navigateToDir(&curDir, &path, false); + r->_errno = navigateToDir(fileobj->mount, &curDir, &path, false); if (r->_errno != 0) return -1; @@ -459,7 +572,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, return -1; } - romfs_file* file = searchForFile(curDir, __utf16path, units); + romfs_file* file = searchForFile(fileobj->mount, curDir, __utf16path, units); if (!file) { if(flags & O_CREAT) @@ -475,7 +588,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, } fileobj->file = file; - fileobj->offset = (u64)romFS_header.fileDataOff + file->dataOff; + fileobj->offset = (u64)fileobj->mount->header.fileDataOff + file->dataOff; fileobj->pos = 0; return 0; @@ -500,7 +613,7 @@ ssize_t romfs_read(struct _reent *r, int fd, char *ptr, size_t len) endPos = file->file->dataSize; len = endPos - file->pos; - ssize_t adv = _romfs_read(file->offset + file->pos, ptr, len); + ssize_t adv = _romfs_read(file->mount, file->offset + file->pos, ptr, len); if(adv >= 0) { file->pos += adv; @@ -558,21 +671,22 @@ int romfs_fstat(struct _reent *r, int fd, struct stat *st) { romfs_fileobj* file = (romfs_fileobj*)fd; memset(st, 0, sizeof(struct stat)); - st->st_ino = file_inode(file->file); + st->st_ino = file_inode(file->mount, file->file); st->st_mode = romFS_file_mode; st->st_nlink = 1; st->st_size = (off_t)file->file->dataSize; st->st_blksize = 512; st->st_blocks = (st->st_blksize + 511) / 512; - st->st_atime = st->st_mtime = st->st_ctime = romFS_mtime; + st->st_atime = st->st_mtime = st->st_ctime = file->mount->mtime; return 0; } int romfs_stat(struct _reent *r, const char *path, struct stat *st) { + romfs_mount* mount = romfs_mount_list; romfs_dir* curDir = NULL; - r->_errno = navigateToDir(&curDir, &path, false); + r->_errno = navigateToDir(mount, &curDir, &path, false); if(r->_errno != 0) return -1; @@ -588,32 +702,32 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st) return -1; } - romfs_dir* dir = searchForDir(curDir, __utf16path, units); + romfs_dir* dir = searchForDir(mount, curDir, __utf16path, units); if(dir) { memset(st, 0, sizeof(*st)); - st->st_ino = dir_inode(dir); + st->st_ino = dir_inode(mount, dir); st->st_mode = romFS_dir_mode; - st->st_nlink = dir_nlink(dir); + st->st_nlink = dir_nlink(mount, dir); st->st_size = dir_size(dir); st->st_blksize = 512; st->st_blocks = (st->st_blksize + 511) / 512; - st->st_atime = st->st_mtime = st->st_ctime = romFS_mtime; + st->st_atime = st->st_mtime = st->st_ctime = mount->mtime; return 0; } - romfs_file* file = searchForFile(curDir, __utf16path, units); + romfs_file* file = searchForFile(mount, curDir, __utf16path, units); if(file) { memset(st, 0, sizeof(*st)); - st->st_ino = file_inode(file); + st->st_ino = file_inode(mount, file); st->st_mode = romFS_file_mode; st->st_nlink = 1; st->st_size = file->dataSize; st->st_blksize = 512; st->st_blocks = (st->st_blksize + 511) / 512; - st->st_atime = st->st_mtime = st->st_ctime = romFS_mtime; + st->st_atime = st->st_mtime = st->st_ctime = mount->mtime; return 0; } @@ -624,12 +738,13 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st) int romfs_chdir(struct _reent *r, const char *path) { + romfs_mount* mount = romfs_mount_list; romfs_dir* curDir = NULL; - r->_errno = navigateToDir(&curDir, &path, true); + r->_errno = navigateToDir(mount, &curDir, &path, true); if (r->_errno != 0) return -1; - romFS_cwd = curDir; + mount->cwd = curDir; return 0; } @@ -637,8 +752,9 @@ DIR_ITER* romfs_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) { romfs_diriter* iter = (romfs_diriter*)(dirState->dirStruct); romfs_dir* curDir = NULL; + iter->mount = romfs_mount_list; - r->_errno = navigateToDir(&curDir, &path, true); + r->_errno = navigateToDir(iter->mount, &curDir, &path, true); if(r->_errno != 0) return NULL; @@ -670,7 +786,7 @@ int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct s { /* '.' entry */ memset(filestat, 0, sizeof(*filestat)); - filestat->st_ino = dir_inode(iter->dir); + filestat->st_ino = dir_inode(iter->mount, iter->dir); filestat->st_mode = romFS_dir_mode; strcpy(filename, "."); @@ -680,10 +796,10 @@ int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct s else if(iter->state == 1) { /* '..' entry */ - romfs_dir* dir = romFS_dir(iter->dir->parent); + romfs_dir* dir = romFS_dir(iter->mount, iter->dir->parent); memset(filestat, 0, sizeof(*filestat)); - filestat->st_ino = dir_inode(dir); + filestat->st_ino = dir_inode(iter->mount, dir); filestat->st_mode = romFS_dir_mode; strcpy(filename, ".."); @@ -693,11 +809,11 @@ int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct s if(iter->childDir != romFS_none) { - romfs_dir* dir = romFS_dir(iter->childDir); + romfs_dir* dir = romFS_dir(iter->mount, iter->childDir); iter->childDir = dir->sibling; memset(filestat, 0, sizeof(*filestat)); - filestat->st_ino = dir_inode(dir); + filestat->st_ino = dir_inode(iter->mount, dir); filestat->st_mode = romFS_dir_mode; /* convert name from UTF-16 to UTF-8 */ @@ -724,11 +840,11 @@ int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct s } else if(iter->childFile != romFS_none) { - romfs_file* file = romFS_file(iter->childFile); + romfs_file* file = romFS_file(iter->mount, iter->childFile); iter->childFile = file->sibling; memset(filestat, 0, sizeof(*filestat)); - filestat->st_ino = file_inode(file); + filestat->st_ino = file_inode(iter->mount, file); filestat->st_mode = romFS_file_mode; /* convert name from UTF-16 to UTF-8 */ diff --git a/libctru/source/services/am.c b/libctru/source/services/am.c index 6e3f452..4d8e640 100644 --- a/libctru/source/services/am.c +++ b/libctru/source/services/am.c @@ -20,6 +20,18 @@ Result amInit(void) ret = srvGetServiceHandle(&amHandle, "am:net"); if (R_FAILED(ret)) ret = srvGetServiceHandle(&amHandle, "am:u"); if (R_FAILED(ret)) ret = srvGetServiceHandle(&amHandle, "am:sys"); + if (R_FAILED(ret)) AtomicDecrement(&amRefCount); + + return ret; +} + +Result amAppInit(void) +{ + Result ret; + + if (AtomicPostIncrement(&amRefCount)) return 0; + + ret = srvGetServiceHandle(&amHandle, "am:sys"); if (R_FAILED(ret)) ret = srvGetServiceHandle(&amHandle, "am:app"); if (R_FAILED(ret)) AtomicDecrement(&amRefCount); @@ -548,7 +560,7 @@ Result AM_QueryAvailableExternalTitleDatabase(bool* available) if(R_FAILED(ret = (Result)cmdbuf[1])) return ret; // Only accept this if the command was a success - if(available) *available = cmdbuf[2]; + if(available) *available = cmdbuf[2] & 0xFF; return ret; } diff --git a/libctru/source/services/apt.c b/libctru/source/services/apt.c index 688c16e..ec84449 100644 --- a/libctru/source/services/apt.c +++ b/libctru/source/services/apt.c @@ -17,38 +17,50 @@ #define APT_HANDLER_STACKSIZE (0x1000) -NS_APPID currentAppId; - -static const char *__apt_servicestr; -static const char * const __apt_servicenames[3] = {"APT:U", "APT:S", "APT:A"}; - -static u32 __apt_new3dsflag_initialized; -static u8 __apt_new3dsflag; - -Handle aptLockHandle; -Handle aptuHandle; -Handle aptEvents[3]; - -Thread aptEventHandlerThread; - -LightLock aptStatusMutex; -Handle aptStatusEvent; -APT_AppStatus aptStatus = APP_NOTINITIALIZED; -APT_AppStatus aptStatusBeforeSleep = APP_NOTINITIALIZED; -u32 aptStatusPower; -Handle aptSleepSync; -static bool aptSleepAllowed = true; - -u32 aptParameters[0x1000/4]; //TEMP - -static u32 __ns_capinfo[0x20>>2]; - -static NS_APPID __apt_launchapplet_appID; -static Handle __apt_launchapplet_inhandle; -static u32 *__apt_launchapplet_parambuf; -static u32 __apt_launchapplet_parambufsize; - +static int aptRefCount = 0; +static Handle aptLockHandle; +static Handle aptEvents[3]; +static LightEvent aptSleepEvent; +static Thread aptEventHandlerThread; static aptHookCookie aptFirstHook; +static aptMessageCb aptMessageFunc; +static void* aptMessageFuncData; + +enum +{ + FLAG_ACTIVE = BIT(0), + FLAG_ALLOWSLEEP = BIT(1), + FLAG_ORDERTOCLOSE = BIT(2), + FLAG_SHUTDOWN = BIT(3), + FLAG_POWERBUTTON = BIT(4), + FLAG_WKUPBYCANCEL = BIT(5), + FLAG_WANTSTOSLEEP = BIT(6), + FLAG_SLEEPING = BIT(7), + FLAG_EXITED = BIT(31), +}; + +static u8 aptHomeButtonState; +static u32 aptFlags = FLAG_ALLOWSLEEP; +static u32 aptParameters[0x1000/4]; + +typedef enum +{ + TR_ENABLE = 0x62, + TR_JUMPTOMENU = 0x0E, + TR_SYSAPPLET = 0x05, + TR_LIBAPPLET = 0x04, + TR_CANCELLIB = 0x03, + TR_CLOSEAPP = 0x09, + TR_APPJUMP = 0x12, +} APT_Transition; + +static void aptEventHandler(void *arg); +static APT_Command aptWaitForWakeUp(APT_Transition transition); + +// The following function can be overriden in order to log APT signals and notifications for debugging purposes +__attribute__((weak)) void _aptDebug(int a, int b) +{ +} static void aptCallHook(APT_HookType hookType) { @@ -57,15 +69,6 @@ static void aptCallHook(APT_HookType hookType) c->callback(hookType, c->param); } -// The following function can be overriden in order to log APT signals and notifications for debugging purposes -__attribute__((weak)) void _aptDebug(int a, int b) -{ -} - -void __ctru_speedup_config(void); - -static void aptAppStarted(void); - static bool aptIsReinit(void) { return (envGetSystemRunFlags() & RUNFLAG_APTREINIT) != 0; @@ -76,537 +79,423 @@ static bool aptIsCrippled(void) return (envGetSystemRunFlags() & RUNFLAG_APTWORKAROUND) != 0 && !aptIsReinit(); } -static Result __apt_initservicehandle(void) +static Result aptGetServiceHandle(Handle* aptuHandle) { - Result ret=0; - u32 i; + static const char* serviceName; + static const char* const serviceNameTable[3] = {"APT:S", "APT:A", "APT:U"}; - if(__apt_servicestr) - { - return srvGetServiceHandle(&aptuHandle, __apt_servicestr); - } + if (serviceName) + return srvGetServiceHandleDirect(aptuHandle, serviceName); - for(i=0; i<3; i++) + Result ret; + int i; + for (i = 0; i < 3; i ++) { - ret = srvGetServiceHandle(&aptuHandle, __apt_servicenames[i]); - if(R_SUCCEEDED(ret)) + ret = srvGetServiceHandleDirect(aptuHandle, serviceNameTable[i]); + if (R_SUCCEEDED(ret)) { - __apt_servicestr = __apt_servicenames[i]; - return ret; + serviceName = serviceNameTable[i]; + break; } } return ret; } -void aptInitCaptureInfo(u32 *ns_capinfo) +static inline int countPrmWords(u32 hdr) { - u32 tmp=0; - u32 main_pixsz, sub_pixsz; - GSPGPU_CaptureInfo gspcapinfo; + return 1 + (hdr&0x3F) + ((hdr>>6)&0x3F); +} - memset(&gspcapinfo, 0, sizeof(GSPGPU_CaptureInfo)); +Result aptSendCommand(u32* aptcmdbuf) +{ + Handle aptuHandle; + + if (aptLockHandle) svcWaitSynchronization(aptLockHandle, U64_MAX); + Result res = aptGetServiceHandle(&aptuHandle); + if (R_SUCCEEDED(res)) + { + u32* cmdbuf = getThreadCommandBuffer(); + memcpy(cmdbuf, aptcmdbuf, 4*countPrmWords(aptcmdbuf[0])); + res = svcSendSyncRequest(aptuHandle); + if (R_SUCCEEDED(res)) + { + memcpy(aptcmdbuf, cmdbuf, 4*16);//4*countPrmWords(cmdbuf[0])); // Workaround for Citra failing to emulate response cmdheaders + res = aptcmdbuf[1]; + } + svcCloseHandle(aptuHandle); + } + if (aptLockHandle) svcReleaseMutex(aptLockHandle); + return res; +} + +static void aptClearParamQueue(void) +{ + // Check for parameters? + for (;;) + { + APT_Command cmd; + Result res = APT_GlanceParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), NULL, &cmd, NULL, NULL); + if (R_FAILED(res) || cmd==APTCMD_NONE) break; + _aptDebug(2, cmd); + svcClearEvent(aptEvents[2]); + APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL); + } +} + +static void aptInitCaptureInfo(aptCaptureBufInfo* capinfo) +{ + GSPGPU_CaptureInfo gspcapinfo; + memset(&gspcapinfo, 0, sizeof(gspcapinfo)); // Get display-capture info from GSP. GSPGPU_ImportDisplayCaptureInfo(&gspcapinfo); // Fill in display-capture info for NS. - if(gspcapinfo.screencapture[0].framebuf0_vaddr != gspcapinfo.screencapture[0].framebuf1_vaddr)ns_capinfo[1] = 1; + capinfo->is3D = gspcapinfo.screencapture[0].framebuf0_vaddr != gspcapinfo.screencapture[0].framebuf1_vaddr; - ns_capinfo[4] = gspcapinfo.screencapture[0].format & 0x7; - ns_capinfo[7] = gspcapinfo.screencapture[1].format & 0x7; + capinfo->top.format = gspcapinfo.screencapture[0].format & 0x7; + capinfo->bottom.format = gspcapinfo.screencapture[1].format & 0x7; - main_pixsz = (ns_capinfo[4] < 2) ? 3 : 2; - sub_pixsz = (ns_capinfo[7] < 2) ? 3 : 2; + u32 __get_bytes_per_pixel(u32 format); + u32 main_pixsz = __get_bytes_per_pixel(capinfo->top.format); + u32 sub_pixsz = __get_bytes_per_pixel(capinfo->bottom.format); - ns_capinfo[2] = sub_pixsz * 0x14000; - ns_capinfo[3] = ns_capinfo[2]; + capinfo->bottom.leftOffset = 0; + capinfo->bottom.rightOffset = 0; + capinfo->top.leftOffset = sub_pixsz * 0x14000; + capinfo->top.rightOffset = capinfo->top.leftOffset; - if(ns_capinfo[1])ns_capinfo[3] = main_pixsz * 0x19000 + ns_capinfo[2]; + if (capinfo->is3D) + capinfo->top.rightOffset += main_pixsz * 0x19000; - tmp = main_pixsz * 0x19000 + ns_capinfo[3]; - ns_capinfo[0] = main_pixsz * 0x7000 + tmp; + capinfo->size = main_pixsz * 0x7000 + main_pixsz * 0x19000 + capinfo->top.rightOffset; } -void aptWaitStatusEvent(void) -{ - svcWaitSynchronization(aptStatusEvent, U64_MAX); - svcClearEvent(aptStatusEvent); -} - -void aptAppletUtility_Exit_RetToApp(u32 type) -{ - u8 buf1[4], buf2[4]; - - memset(buf1, 0, 4); - - buf1[0]=0x10; - aptOpenSession(); - APT_AppletUtility(NULL, 0x7, 0x4, buf1, 0x1, buf2); - aptCloseSession(); - - buf1[0]=0x00; - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, buf1, 0x1, buf2); - aptCloseSession(); - - buf1[0]=0x01; - aptOpenSession(); - APT_AppletUtility(NULL, 0x7, 0x4, buf1, 0x1, buf2); - aptCloseSession(); - - buf1[0]=0x00; - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, buf1, 0x1, buf2); - aptCloseSession(); - - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, buf1, 0x1, buf2); - aptCloseSession(); - - if(type) - { - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, buf1, 0x1, buf2); - aptCloseSession(); - } -} - -NS_APPID aptGetMenuAppID(void) -{ - NS_APPID menu_appid = 0; - - aptOpenSession(); - APT_GetAppletManInfo(0xff, NULL, NULL, &menu_appid, NULL); - aptCloseSession(); - - return menu_appid; -} - -void aptReturnToMenu(void) -{ - NS_APPID menu_appid; - u32 tmp0 = 1, tmp1 = 0; - - if(aptIsCrippled()) - { - svcClearEvent(aptStatusEvent); - aptSetStatus(APP_EXITING); - return; - } - - // This is only executed when ret-to-menu was triggered via the home-button, not the power-button. - if(aptGetStatusPower() == 0) - { - aptOpenSession(); - APT_AppletUtility(NULL, 0x6, 0x4, (u8*)&tmp0, 0x1, (u8*)&tmp1); - aptCloseSession(); - } - - // Set status to SUSPENDED. - __apt_launchapplet_appID = 0; - svcClearEvent(aptStatusEvent); - aptSetStatus(APP_SUSPENDED); - - // Prepare for return to menu - aptOpenSession(); - APT_PrepareToJumpToHomeMenu(); - aptCloseSession(); - - // Save Vram - GSPGPU_SaveVramSysArea(); - - // Capture screen. - memset(__ns_capinfo, 0, 0x20); - - aptInitCaptureInfo(__ns_capinfo); - - menu_appid = aptGetMenuAppID(); - - // Send capture-screen info to menu. - aptOpenSession(); - APT_SendParameter(currentAppId, menu_appid, 0x20, __ns_capinfo, 0x0, 0x10); - aptCloseSession(); - - aptOpenSession(); - APT_SendCaptureBufferInfo(0x20, __ns_capinfo); - aptCloseSession(); - - // Release GSP module. - GSPGPU_ReleaseRight(); - - // Jump to menu! - aptOpenSession(); - APT_JumpToHomeMenu(0x0, 0x0, 0x0); - aptCloseSession(); - - // Wait for return to application. - aptOpenSession(); - APT_NotifyToWait(currentAppId); - aptCloseSession(); - - // This is only executed when ret-to-menu was triggered via the home-button, not the power-button. - if(aptGetStatusPower() == 0) - { - tmp0 = 0; - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, (u8*)&tmp0, 0x1, (u8*)&tmp1); - aptCloseSession(); - } - - aptWaitStatusEvent(); -} - -void aptAppletStarted(void) -{ - u8 buf1[4], buf2[4]; - - memset(buf1, 0, 4); - - // Set status to SUSPENDED. - svcClearEvent(aptStatusEvent); - aptSetStatus(APP_SUSPENDED); - - aptOpenSession(); - APT_SendCaptureBufferInfo(0x20, __ns_capinfo); - aptCloseSession(); - - aptOpenSession(); - APT_ReplySleepQuery(currentAppId, 0x0); - aptCloseSession(); - - aptOpenSession(); - APT_StartLibraryApplet(__apt_launchapplet_appID, __apt_launchapplet_inhandle, __apt_launchapplet_parambuf, __apt_launchapplet_parambufsize); - aptCloseSession(); - - buf1[0]=0x00; - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, buf1, 0x1, buf2); - aptCloseSession(); - - aptOpenSession(); - APT_NotifyToWait(currentAppId); - aptCloseSession(); - - buf1[0]=0x00; - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, buf1, 0x1, buf2); - aptCloseSession(); -} - -void aptAppletClosed(void) -{ - aptAppletUtility_Exit_RetToApp(1); - - GSPGPU_AcquireRight(0x0); - GSPGPU_RestoreVramSysArea(); - - svcClearEvent(aptStatusEvent); - aptSetStatus(APP_RUNNING); - svcClearEvent(aptStatusEvent); -} - -static void __handle_notification(void) { - APT_Signal type; - Result ret=0; - - // Get notification type. - aptOpenSession(); - ret = APT_InquireNotification(currentAppId, &type); - aptCloseSession(); - if(R_FAILED(ret)) return; - - _aptDebug(1, type); - - switch(type) - { - case APTSIGNAL_HOMEBUTTON: - case APTSIGNAL_POWERBUTTON: - // The main thread should call aptReturnToMenu() when the status gets set to this. - if(aptGetStatus() == APP_RUNNING) - { - aptOpenSession(); - APT_ReplySleepQuery(currentAppId, 0x0); - aptCloseSession(); - - if(type == APTSIGNAL_HOMEBUTTON) aptSetStatusPower(0); - if(type == APTSIGNAL_POWERBUTTON) aptSetStatusPower(1); - aptSetStatus(APP_SUSPENDING); - } - break; - - case APTSIGNAL_PREPARESLEEP: - // Reply to sleep-request. - if(aptIsSleepAllowed()) - { - aptStatusBeforeSleep = aptGetStatus(); - aptSetStatus(APP_PREPARE_SLEEPMODE); - svcWaitSynchronization(aptSleepSync, U64_MAX); - svcClearEvent(aptSleepSync); - - aptOpenSession(); - APT_ReplySleepQuery(currentAppId, 0x1); - aptCloseSession(); - } else - { - aptOpenSession(); - APT_ReplySleepQuery(currentAppId, 0x0); - aptCloseSession(); - } - break; - - case APTSIGNAL_ENTERSLEEP: - if(aptGetStatus() == APP_PREPARE_SLEEPMODE) - { - // Report into sleep-mode. - aptSetStatus(APP_SLEEPMODE); - - aptOpenSession(); - APT_ReplySleepNotificationComplete(currentAppId); - aptCloseSession(); - } - break; - - // Leaving sleep-mode. - case APTSIGNAL_WAKEUP: - if(aptGetStatus() == APP_SLEEPMODE) - { - if(aptStatusBeforeSleep == APP_RUNNING)GSPGPU_SetLcdForceBlack(0); - - // Restore old aptStatus. - aptSetStatus(aptStatusBeforeSleep); - } - break; - - default: - break; - } -} - -static bool __handle_incoming_parameter(void) { - u8 type; - - aptOpenSession(); - APT_ReceiveParameter(currentAppId, 0x1000, aptParameters, NULL, &type); - aptCloseSession(); - - _aptDebug(2, type); - - switch(type) - { - case 0x1: // Application just started. - aptAppStarted(); - return true; - - case 0x3: // "Launched library applet finished loading" - if (aptGetStatus() != APP_SUSPENDED || __apt_launchapplet_appID==0) return true; - aptSetStatus(APP_APPLETSTARTED); - return true; - case 0xA: // "Launched library applet closed" - if (aptGetStatus() != APP_SUSPENDED || __apt_launchapplet_appID==0) return true; - if(__apt_launchapplet_parambuf && __apt_launchapplet_parambufsize)memcpy(__apt_launchapplet_parambuf, aptParameters, __apt_launchapplet_parambufsize); - aptSetStatus(APP_APPLETCLOSED); - return true; - case 0xB: // Just returned from menu. - if (aptGetStatus() != APP_NOTINITIALIZED) - { - GSPGPU_AcquireRight(0x0); - GSPGPU_RestoreVramSysArea(); - aptAppletUtility_Exit_RetToApp(0); - aptSetStatus(APP_RUNNING); - } else - aptAppStarted(); - return true; - - case 0xC: // Exiting application. - aptSetStatus(APP_EXITING); - return false; - } - - return true; -} - -void aptEventHandler(void *arg) -{ - bool runThread = true; - - while(runThread) - { - s32 syncedID = 0; - svcWaitSynchronizationN(&syncedID, aptEvents, 3, 0, U64_MAX); - svcClearEvent(aptEvents[syncedID]); - switch(syncedID) - { - // Event 0 means we got a signal from NS (home button, power button etc). - case 0x0: - __handle_notification(); - break; - // Event 1 means we got an incoming parameter. - case 0x1: - runThread = __handle_incoming_parameter(); - break; - // Event 2 means we should exit the thread. - case 0x2: - runThread = false; - break; - } - } -} - -static int aptRefCount = 0; - Result aptInit(void) { Result ret=0; if (AtomicPostIncrement(&aptRefCount)) return 0; - // Initialize APT stuff, escape load screen. - ret = __apt_initservicehandle(); - if(R_FAILED(ret)) goto _fail; - if(R_FAILED(ret=APT_GetLockHandle(0x0, &aptLockHandle))) goto _fail; - svcCloseHandle(aptuHandle); + // Retrieve APT lock + ret = APT_GetLockHandle(0x0, &aptLockHandle); + if (R_FAILED(ret)) goto _fail; + if (aptIsCrippled()) return 0; - currentAppId = envGetAptAppId(); + // Initialize APT + APT_AppletAttr attr = aptMakeAppletAttr(APTPOS_APP, false, false); + ret = APT_Initialize(envGetAptAppId(), attr, &aptEvents[1], &aptEvents[2]); + if (R_FAILED(ret)) goto _fail2; - svcCreateEvent(&aptStatusEvent, 0); - svcCreateEvent(&aptSleepSync, 0); - LightLock_Init(&aptStatusMutex); - aptStatus=0; + // Enable APT + ret = APT_Enable(attr); + if (R_FAILED(ret)) goto _fail3; - if(!aptIsCrippled()) + // Create APT close event + ret = svcCreateEvent(&aptEvents[0], RESET_STICKY); + if (R_FAILED(ret)) goto _fail3; + + // Initialize APT sleep event + LightEvent_Init(&aptSleepEvent, RESET_ONESHOT); + + // Create APT event handler thread + aptEventHandlerThread = threadCreate(aptEventHandler, 0x0, APT_HANDLER_STACKSIZE, 0x31, -2, true); + if (!aptEventHandlerThread) goto _fail4; + + // Special handling for aptReinit (aka hax) + APT_Transition transition = TR_ENABLE; + if (aptIsReinit()) { - aptOpenSession(); - if(R_FAILED(ret=APT_Initialize(currentAppId, &aptEvents[0], &aptEvents[1])))return ret; - aptCloseSession(); - - aptOpenSession(); - if(R_FAILED(ret=APT_Enable(0x0))) goto _fail; - aptCloseSession(); - - // create APT close event - svcCreateEvent(&aptEvents[2], 0); + transition = TR_JUMPTOMENU; - // After a cycle of APT_Finalize+APT_Initialize APT thinks the - // application is suspended, so we need to tell it to unsuspend us. - if (aptIsReinit()) - { - aptOpenSession(); - APT_PrepareToJumpToApplication(0x0); - aptCloseSession(); + // Clear out any pending parameters + bool success = false; + do + ret = APT_CancelParameter(APPID_NONE, envGetAptAppId(), &success); + while (success); - aptOpenSession(); - APT_JumpToApplication(0x0, 0x0, 0x0); - aptCloseSession(); - } - - aptOpenSession(); - if(R_FAILED(ret=APT_NotifyToWait(currentAppId)))goto _fail; - aptCloseSession(); - - // create APT event handler thread - aptEventHandlerThread = threadCreate(aptEventHandler, 0x0, APT_HANDLER_STACKSIZE, 0x31, -2, true); - - // Wait for the state to become APT_RUNNING - aptWaitStatusEvent(); - } else - aptAppStarted(); + // APT thinks the application is suspended, so we need to tell it to unsuspend us. + APT_PrepareToJumpToApplication(false); + APT_JumpToApplication(NULL, 0, 0); + } + // Wait for wakeup + aptWaitForWakeUp(transition); return 0; +_fail4: + svcCloseHandle(aptEvents[0]); +_fail3: + svcCloseHandle(aptEvents[1]); + svcCloseHandle(aptEvents[2]); +_fail2: + svcCloseHandle(aptLockHandle); _fail: AtomicDecrement(&aptRefCount); return ret; } +bool aptIsSleepAllowed(void) +{ + return (aptFlags & FLAG_ALLOWSLEEP) != 0; +} + +void aptSetSleepAllowed(bool allowed) +{ + bool cur = aptIsSleepAllowed(); + if (allowed && !cur) + { + aptFlags |= FLAG_ALLOWSLEEP; + APT_SleepIfShellClosed(); + } + else if (!allowed && cur) + { + aptFlags &= ~FLAG_ALLOWSLEEP; + APT_ReplySleepQuery(envGetAptAppId(), APTREPLY_REJECT); + } +} + +static void aptExitProcess(void) +{ + APT_CloseApplication(NULL, 0, 0); + svcExitProcess(); +} + void aptExit(void) { if (AtomicDecrement(&aptRefCount)) return; - if(!aptIsCrippled())aptAppletUtility_Exit_RetToApp(0); + bool closeAptLock = true; - // This is only executed when application-termination was triggered via the home-menu power-off screen. - if(aptGetStatusPower() == 1) + if (!aptIsCrippled()) { - aptOpenSession(); - APT_ReplySleepQuery(currentAppId, 0x0); - aptCloseSession(); + if ((aptFlags & FLAG_EXITED) || !aptIsReinit()) + { + APT_PrepareToCloseApplication(true); + + extern void (*__system_retAddr)(void); + __system_retAddr = aptExitProcess; + closeAptLock = false; + srvInit(); // Keep srv initialized + } else + { + APT_Finalize(envGetAptAppId()); + aptClearParamQueue(); + } + + svcSignalEvent(aptEvents[0]); + threadJoin(aptEventHandlerThread, U64_MAX); + int i; + for (i = 0; i < 3; i ++) + svcCloseHandle(aptEvents[i]); } - if(!aptIsCrippled()) - { - bool isReinit = aptIsReinit(); - if (aptGetStatus() == APP_EXITING || !isReinit) - { - aptOpenSession(); - APT_PrepareToCloseApplication(0x1); - aptCloseSession(); - - aptOpenSession(); - APT_CloseApplication(0x0, 0x0, 0x0); - aptCloseSession(); + if (closeAptLock) + svcCloseHandle(aptLockHandle); +} - if (isReinit) - { - extern void (*__system_retAddr)(void); - __system_retAddr = NULL; - } - } else if (isReinit) +void aptEventHandler(void *arg) +{ + for (;;) + { + s32 id = 0; + svcWaitSynchronizationN(&id, aptEvents, 2, 0, U64_MAX); + svcClearEvent(aptEvents[id]); + if (id != 1) break; + + APT_Signal signal; + Result res = APT_InquireNotification(envGetAptAppId(), &signal); + if (R_FAILED(res)) break; + switch (signal) { - aptOpenSession(); - APT_Finalize(currentAppId); - aptCloseSession(); + case APTSIGNAL_HOMEBUTTON: + if (!aptHomeButtonState) aptHomeButtonState = 1; + break; + case APTSIGNAL_HOMEBUTTON2: + if (!aptHomeButtonState) aptHomeButtonState = 2; + break; + case APTSIGNAL_SLEEP_QUERY: + APT_ReplySleepQuery(envGetAptAppId(), aptIsSleepAllowed() ? APTREPLY_ACCEPT : APTREPLY_REJECT); + break; + case APTSIGNAL_SLEEP_CANCEL: + // Do something maybe? + break; + case APTSIGNAL_SLEEP_ENTER: + aptFlags |= FLAG_WANTSTOSLEEP; + break; + case APTSIGNAL_SLEEP_WAKEUP: + if (aptFlags & FLAG_SLEEPING) + LightEvent_Signal(&aptSleepEvent); + else + aptFlags &= ~FLAG_WANTSTOSLEEP; + break; + case APTSIGNAL_SHUTDOWN: + aptFlags |= FLAG_ORDERTOCLOSE | FLAG_SHUTDOWN; + break; + case APTSIGNAL_POWERBUTTON: + aptFlags |= FLAG_POWERBUTTON; + break; + case APTSIGNAL_POWERBUTTON2: + aptFlags &= ~FLAG_POWERBUTTON; + break; + case APTSIGNAL_TRY_SLEEP: + break; + case APTSIGNAL_ORDERTOCLOSE: + aptFlags |= FLAG_ORDERTOCLOSE; + break; + default: + break; + } + } +} + +static Result aptReceiveParameter(APT_Command* cmd, size_t* actualSize, Handle* handle) +{ + NS_APPID sender; + size_t temp_actualSize; + if (!actualSize) actualSize = &temp_actualSize; + + svcWaitSynchronization(aptEvents[2], U64_MAX); + svcClearEvent(aptEvents[2]); + Result res = APT_ReceiveParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), &sender, cmd, actualSize, handle); + if (R_SUCCEEDED(res) && *cmd == APTCMD_MESSAGE && aptMessageFunc) + aptMessageFunc(aptMessageFuncData, sender, aptParameters, *actualSize); + return res; +} + +APT_Command aptWaitForWakeUp(APT_Transition transition) +{ + APT_Command cmd; + APT_NotifyToWait(envGetAptAppId()); + if (transition != TR_ENABLE) + APT_SleepIfShellClosed(); + aptFlags &= ~FLAG_ACTIVE; + for (;;) + { + Result res = aptReceiveParameter(&cmd, NULL, NULL); + if (R_SUCCEEDED(res) + && (cmd==APTCMD_WAKEUP || cmd==APTCMD_WAKEUP_PAUSE || cmd==APTCMD_WAKEUP_EXIT || cmd==APTCMD_WAKEUP_CANCEL + || cmd==APTCMD_WAKEUP_CANCELALL || cmd==APTCMD_WAKEUP_POWERBUTTON || cmd==APTCMD_WAKEUP_JUMPTOHOME + || cmd==APTCMD_WAKEUP_LAUNCHAPP)) break; + } + aptFlags |= FLAG_ACTIVE; + + void __ctru_speedup_config(); + __ctru_speedup_config(); + + if (transition != TR_CANCELLIB && cmd != APTCMD_WAKEUP_CANCEL && cmd != APTCMD_WAKEUP) + { + GSPGPU_AcquireRight(0); + GSPGPU_RestoreVramSysArea(); + aptCallHook(APTHOOK_ONRESTORE); + } + + if (cmd == APTCMD_WAKEUP_CANCEL) + aptFlags |= FLAG_WKUPBYCANCEL; + + if (cmd != APTCMD_WAKEUP_JUMPTOHOME) + { + APT_UnlockTransition(0x10); + APT_SleepIfShellClosed(); + } else + { + bool dummy; + APT_TryLockTransition(0x01, &dummy); + } + + if (transition == TR_JUMPTOMENU || transition == TR_LIBAPPLET || transition == TR_SYSAPPLET || transition == TR_APPJUMP) + { + if (cmd != APTCMD_WAKEUP_JUMPTOHOME) + { + aptHomeButtonState = 0; + APT_UnlockTransition(0x01); + APT_SleepIfShellClosed(); } } - svcSignalEvent(aptEvents[2]); - threadJoin(aptEventHandlerThread, U64_MAX); - svcCloseHandle(aptEvents[2]); - - svcCloseHandle(aptSleepSync); + return cmd; +} - svcCloseHandle(aptLockHandle); - svcCloseHandle(aptStatusEvent); +static void aptScreenTransfer(NS_APPID appId, bool sysApplet) +{ + aptCallHook(APTHOOK_ONSUSPEND); + GSPGPU_SaveVramSysArea(); + + aptCaptureBufInfo capinfo; + aptInitCaptureInfo(&capinfo); + + for (;;) + { + bool tmp; + Result res = APT_IsRegistered(appId, &tmp); + if (R_SUCCEEDED(res) && tmp) break; + svcSleepThread(10000000); + } + + for (;;) + { + Result res = APT_SendParameter(envGetAptAppId(), appId, sysApplet ? APTCMD_SYSAPPLET_REQUEST : APTCMD_REQUEST, &capinfo, sizeof(capinfo), 0); + if (R_SUCCEEDED(res)) break; + svcSleepThread(10000000); + } + + for (;;) + { + APT_Command cmd; + Result res = aptReceiveParameter(&cmd, NULL, NULL); + if (R_SUCCEEDED(res) && cmd==APTCMD_RESPONSE) + break; + } + + APT_SendCaptureBufferInfo(&capinfo); + GSPGPU_ReleaseRight(); +} + +static void aptProcessJumpToMenu(void) +{ + bool sleep = aptIsSleepAllowed(); + aptSetSleepAllowed(false); + + aptClearParamQueue(); + APT_PrepareToJumpToHomeMenu(); + aptScreenTransfer(aptGetMenuAppID(), true); + + APT_JumpToHomeMenu(NULL, 0, 0); + aptFlags &= ~FLAG_ACTIVE; + + aptWaitForWakeUp(TR_JUMPTOMENU); + aptSetSleepAllowed(sleep); } bool aptMainLoop(void) { - while(1) - { - //if(aptIsCrippled())__handle_notification(); + if (aptIsCrippled()) return true; + if (aptFlags & FLAG_EXITED) return false; - switch(aptGetStatus()) - { - case APP_RUNNING: - return true; - case APP_EXITING: - aptCallHook(APTHOOK_ONEXIT); - return false; - case APP_SUSPENDING: - aptCallHook(APTHOOK_ONSUSPEND); - aptReturnToMenu(); - if (aptGetStatus() == APP_RUNNING) - aptCallHook(APTHOOK_ONRESTORE); - break; - case APP_APPLETSTARTED: - aptAppletStarted(); - break; - case APP_APPLETCLOSED: - aptAppletClosed(); - aptCallHook(APTHOOK_ONRESTORE); - break; - case APP_PREPARE_SLEEPMODE: - aptCallHook(APTHOOK_ONSLEEP); - aptSignalReadyForSleep(); - // Fall through - default: - //case APP_NOTINITIALIZED: - //case APP_SLEEPMODE: - aptWaitStatusEvent(); - aptCallHook(APTHOOK_ONWAKEUP); - break; - } + if (aptFlags & FLAG_WANTSTOSLEEP) + { + aptFlags = (aptFlags &~ FLAG_WANTSTOSLEEP) | FLAG_SLEEPING; + aptCallHook(APTHOOK_ONSLEEP); + APT_ReplySleepNotificationComplete(envGetAptAppId()); + LightEvent_Wait(&aptSleepEvent); + aptFlags &= ~FLAG_SLEEPING; + + if (aptFlags & FLAG_ACTIVE) + GSPGPU_SetLcdForceBlack(0); + aptCallHook(APTHOOK_ONWAKEUP); } + else if ((aptFlags & FLAG_POWERBUTTON) || aptHomeButtonState) + aptProcessJumpToMenu(); + + if (aptFlags & (FLAG_ORDERTOCLOSE|FLAG_WKUPBYCANCEL)) + { + aptFlags |= FLAG_EXITED; + aptCallHook(APTHOOK_ONEXIT); + return false; + } + + return true; } void aptHook(aptHookCookie* cookie, aptHookFn callback, void* param) @@ -633,744 +522,560 @@ void aptUnhook(aptHookCookie* cookie) } } -void aptAppStarted(void) +void aptSetMessageCallback(aptMessageCb callback, void* user) { - u8 buf1[4], buf2[4]; - - aptSetStatus(APP_RUNNING); - svcClearEvent(aptStatusEvent); - - if(!aptIsCrippled()) - { - memset(buf1, 0, 4); - - buf1[0] = 0x10; - aptOpenSession(); - APT_AppletUtility(NULL, 0x7, 0x4, buf1, 0x1, buf2); - aptCloseSession(); - - buf1[0] = 0x00; - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, buf1, 0x1, buf2); - aptCloseSession(); - - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, buf1, 0x1, buf2); - aptCloseSession(); - } + aptMessageFunc = callback; + aptMessageFuncData = user; } -APT_AppStatus aptGetStatus(void) +bool aptLaunchLibraryApplet(NS_APPID appId, void* buf, size_t bufsize, Handle handle) { - APT_AppStatus ret; - LightLock_Lock(&aptStatusMutex); - ret = aptStatus; - LightLock_Unlock(&aptStatusMutex); - return ret; -} + bool sleep = aptIsSleepAllowed(); -void aptSetStatus(APT_AppStatus status) -{ - LightLock_Lock(&aptStatusMutex); + aptSetSleepAllowed(false); + aptClearParamQueue(); + APT_PrepareToStartLibraryApplet(appId); + aptSetSleepAllowed(sleep); - aptStatus = status; + aptScreenTransfer(appId, false); - //if(prevstatus != APP_NOTINITIALIZED) - //{ - if(status == APP_RUNNING) - __ctru_speedup_config(); - if(status == APP_RUNNING || status == APP_EXITING || status == APP_APPLETSTARTED || status == APP_APPLETCLOSED) - svcSignalEvent(aptStatusEvent); - //} + aptSetSleepAllowed(false); + APT_StartLibraryApplet(appId, buf, bufsize, handle); + aptFlags &= ~FLAG_ACTIVE; - LightLock_Unlock(&aptStatusMutex); -} + aptWaitForWakeUp(TR_LIBAPPLET); + memcpy(buf, aptParameters, bufsize); + aptSetSleepAllowed(sleep); -u32 aptGetStatusPower(void) -{ - u32 ret; - LightLock_Lock(&aptStatusMutex); - ret = aptStatusPower; - LightLock_Unlock(&aptStatusMutex); - return ret; -} - -void aptSetStatusPower(u32 status) -{ - LightLock_Lock(&aptStatusMutex); - aptStatusPower = status; - LightLock_Unlock(&aptStatusMutex); -} - -void aptOpenSession(void) -{ - LightLock_Lock(&aptStatusMutex); - __apt_initservicehandle(); -} - -void aptCloseSession(void) -{ - svcCloseHandle(aptuHandle); - LightLock_Unlock(&aptStatusMutex); -} - -void aptSignalReadyForSleep(void) -{ - svcSignalEvent(aptSleepSync); -} - -bool aptIsSleepAllowed() -{ - return aptSleepAllowed; -} - -void aptSetSleepAllowed(bool allowed) -{ - aptSleepAllowed = allowed; + return aptMainLoop(); } Result APT_GetLockHandle(u16 flags, Handle* lockHandle) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x1,1,0); // 0x10040 cmdbuf[1]=flags; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - if(lockHandle)*lockHandle=cmdbuf[5]; - - return cmdbuf[1]; + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + *lockHandle = cmdbuf[5]; + + return ret; } -Result APT_Initialize(NS_APPID appId, Handle* eventHandle1, Handle* eventHandle2) +Result APT_Initialize(NS_APPID appId, APT_AppletAttr attr, Handle* signalEvent, Handle* resumeEvent) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x2,2,0); // 0x20080 cmdbuf[1]=appId; - cmdbuf[2]=0x0; + cmdbuf[2]=attr; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - if(eventHandle1)*eventHandle1=cmdbuf[3]; //return to menu event ? - if(eventHandle2)*eventHandle2=cmdbuf[4]; - - return cmdbuf[1]; -} - -Result APT_Finalize(NS_APPID appId) -{ - u32* cmdbuf=getThreadCommandBuffer(); - cmdbuf[0]=IPC_MakeHeader(0x4,1,0); // 0x40040 - cmdbuf[1]=appId; - - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - return cmdbuf[1]; -} - -Result APT_HardwareResetAsync() -{ - u32* cmdbuf=getThreadCommandBuffer(); - cmdbuf[0]=IPC_MakeHeader(0x4E,0,0); // 0x4E0000 - - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; -} - -Result APT_Enable(u32 a) -{ - u32* cmdbuf=getThreadCommandBuffer(); - cmdbuf[0]=IPC_MakeHeader(0x3,1,0); // 0x30040 - cmdbuf[1]=a; - - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; -} - -Result APT_GetAppletManInfo(u8 inval, u8 *outval8, u32 *outval32, NS_APPID *menu_appid, NS_APPID *active_appid) -{ - u32* cmdbuf=getThreadCommandBuffer(); - cmdbuf[0]=IPC_MakeHeader(0x5,1,0); // 0x50040 - cmdbuf[1]=inval; - - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - if(outval8)*outval8=cmdbuf[2]; - if(outval32)*outval32=cmdbuf[3]; - if(menu_appid)*menu_appid=cmdbuf[4]; - if(active_appid)*active_appid=cmdbuf[5]; - - return cmdbuf[1]; -} - -Result APT_GetAppletInfo(NS_APPID appID, u64* pProgramID, u8* pMediaType, u8* pRegistered, u8* pLoadState, u32* pAttributes) -{ - u32* cmdbuf=getThreadCommandBuffer(); - cmdbuf[0]=IPC_MakeHeader(0x6,1,0); // 0x60040 - cmdbuf[1]=appID; - - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - if(pProgramID)*pProgramID=(u64)cmdbuf[2]|((u64)cmdbuf[3]<<32); - if(pMediaType)*pMediaType=cmdbuf[4]; - if(pRegistered)*pRegistered=cmdbuf[5]; - if(pLoadState)*pLoadState=cmdbuf[6]; - if(pAttributes)*pAttributes=cmdbuf[7]; - - return cmdbuf[1]; -} - -Result APT_GetAppletProgramInfo(u32 id, u32 flags, u16 *titleversion) -{ - u32* cmdbuf=getThreadCommandBuffer(); - cmdbuf[0]=IPC_MakeHeader(0x4D,2,0); // 0x4D0080 - cmdbuf[1]=id; - cmdbuf[2]=flags; - - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - if(titleversion)*titleversion=cmdbuf[2]; - - return cmdbuf[1]; -} - -Result APT_GetProgramID(u64* pProgramID) -{ - u32* cmdbuf=getThreadCommandBuffer(); - cmdbuf[0] = IPC_MakeHeader(0x58,0,2); // 0x580002 - cmdbuf[1] = IPC_Desc_CurProcessHandle(); - - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - if(ret==0)ret = cmdbuf[1]; - - if(pProgramID) + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) { - if(ret==0) *pProgramID=((u64)cmdbuf[3]<<32)|cmdbuf[2]; + if(signalEvent) *signalEvent=cmdbuf[3]; + if(resumeEvent) *resumeEvent=cmdbuf[4]; } return ret; } -Result APT_IsRegistered(NS_APPID appID, u8* out) +Result APT_Finalize(NS_APPID appId) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; + cmdbuf[0]=IPC_MakeHeader(0x4,1,0); // 0x40040 + cmdbuf[1]=appId; + + return aptSendCommand(cmdbuf); +} + +Result APT_HardwareResetAsync(void) +{ + u32 cmdbuf[16]; + cmdbuf[0]=IPC_MakeHeader(0x4E,0,0); // 0x4E0000 + + return aptSendCommand(cmdbuf); +} + +Result APT_Enable(APT_AppletAttr attr) +{ + u32 cmdbuf[16]; + cmdbuf[0]=IPC_MakeHeader(0x3,1,0); // 0x30040 + cmdbuf[1]=attr; + + return aptSendCommand(cmdbuf); +} + +Result APT_GetAppletManInfo(APT_AppletPos inpos, APT_AppletPos* outpos, NS_APPID* req_appid, NS_APPID* menu_appid, NS_APPID* active_appid) +{ + u32 cmdbuf[16]; + cmdbuf[0]=IPC_MakeHeader(0x5,1,0); // 0x50040 + cmdbuf[1]=inpos; + + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + { + if (outpos) *outpos =cmdbuf[2]; + if (req_appid) *req_appid =cmdbuf[3]; + if (menu_appid) *menu_appid =cmdbuf[4]; + if (active_appid) *active_appid=cmdbuf[5]; + } + + return ret; +} + +Result APT_GetAppletInfo(NS_APPID appID, u64* pProgramID, u8* pMediaType, bool* pRegistered, bool* pLoadState, APT_AppletAttr* pAttributes) +{ + u32 cmdbuf[16]; + cmdbuf[0]=IPC_MakeHeader(0x6,1,0); // 0x60040 + cmdbuf[1]=appID; + + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + { + if (pProgramID) *pProgramID =(u64)cmdbuf[2]|((u64)cmdbuf[3]<<32); + if (pMediaType) *pMediaType =cmdbuf[4]; + if (pRegistered) *pRegistered=cmdbuf[5] & 0xFF; + if (pLoadState) *pLoadState =cmdbuf[6] & 0xFF; + if (pAttributes) *pAttributes=cmdbuf[7]; + } + + return ret; +} + +Result APT_GetAppletProgramInfo(u32 id, u32 flags, u16 *titleversion) +{ + u32 cmdbuf[16]; + cmdbuf[0]=IPC_MakeHeader(0x4D,2,0); // 0x4D0080 + cmdbuf[1]=id; + cmdbuf[2]=flags; + + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + *titleversion=cmdbuf[2]; + + return ret; +} + +Result APT_GetProgramID(u64* pProgramID) +{ + u32 cmdbuf[16]; + cmdbuf[0] = IPC_MakeHeader(0x58,0,2); // 0x580002 + cmdbuf[1] = IPC_Desc_CurProcessHandle(); + + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + *pProgramID=((u64)cmdbuf[3]<<32)|cmdbuf[2]; + + return ret; +} + +Result APT_IsRegistered(NS_APPID appID, bool* out) +{ + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x9,1,0); // 0x90040 cmdbuf[1]=appID; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + *out=cmdbuf[2] & 0xFF; - if(out)*out=cmdbuf[2]; - - return cmdbuf[1]; + return ret; } Result APT_InquireNotification(u32 appID, APT_Signal* signalType) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0xB,1,0); // 0xB0040 cmdbuf[1]=appID; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + { + _aptDebug(1, cmdbuf[2]); + *signalType=cmdbuf[2]; + } - if(signalType)*signalType=cmdbuf[2]; - - return cmdbuf[1]; + return ret; } Result APT_PrepareToJumpToHomeMenu(void) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x2B,0,0); // 0x2B0000 - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_JumpToHomeMenu(const u8 *param, size_t paramSize, Handle handle) +Result APT_JumpToHomeMenu(const void* param, size_t paramSize, Handle handle) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x2C,1,4); // 0x2C0044 cmdbuf[1]=paramSize; cmdbuf[2]=IPC_Desc_SharedHandles(1); cmdbuf[3]=handle; - cmdbuf[4]=IPC_Desc_StaticBuffer(paramSize,0); - cmdbuf[5]= (u32) param; + cmdbuf[4]=IPC_Desc_StaticBuffer(cmdbuf[1],0); + cmdbuf[5]=(u32) param; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_PrepareToJumpToApplication(u32 a) +Result APT_PrepareToJumpToApplication(bool exiting) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x23,1,0); // 0x230040 - cmdbuf[1]=a; + cmdbuf[1]=exiting; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_JumpToApplication(const u8 *param, size_t paramSize, Handle handle) +Result APT_JumpToApplication(const void* param, size_t paramSize, Handle handle) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x24,1,4); // 0x240044 cmdbuf[1]=paramSize; cmdbuf[2]=IPC_Desc_SharedHandles(1); cmdbuf[3]=handle; - cmdbuf[4]=IPC_Desc_StaticBuffer(paramSize,0); + cmdbuf[4]=IPC_Desc_StaticBuffer(cmdbuf[1],0); cmdbuf[5]= (u32) param; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } Result APT_NotifyToWait(NS_APPID appID) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x43,1,0); // 0x430040 cmdbuf[1]=appID; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_AppletUtility(u32* out, u32 a, u32 size1, u8* buf1, u32 size2, u8* buf2) +Result APT_AppletUtility(int id, void* out, size_t outSize, const void* in, size_t inSize) { - u32 saved_threadstorage[2]; - - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x4B,3,2); // 0x4B00C2 - cmdbuf[1]=a; - cmdbuf[2]=size1; - cmdbuf[3]=size2; - cmdbuf[4]=IPC_Desc_StaticBuffer(size1,1); - cmdbuf[5]=(u32)buf1; + cmdbuf[1]=id; + cmdbuf[2]=inSize; + cmdbuf[3]=outSize; + cmdbuf[4]=IPC_Desc_StaticBuffer(cmdbuf[2],1); + cmdbuf[5]=(u32)in; - u32 *staticbufs = getThreadStaticBuffers(); + u32 saved_threadstorage[2]; + u32* staticbufs = getThreadStaticBuffers(); saved_threadstorage[0]=staticbufs[0]; saved_threadstorage[1]=staticbufs[1]; + staticbufs[0]=IPC_Desc_StaticBuffer(cmdbuf[3],0); + staticbufs[1]=(u32)out; - staticbufs[0]=IPC_Desc_StaticBuffer(size2,0); - staticbufs[1]=(u32)buf2; - - Result ret=svcSendSyncRequest(aptuHandle); - + Result ret = aptSendCommand(cmdbuf); staticbufs[0]=saved_threadstorage[0]; staticbufs[1]=saved_threadstorage[1]; - - if(R_FAILED(ret))return ret; - - if(out)*out=cmdbuf[2]; - return cmdbuf[1]; + return R_SUCCEEDED(ret) ? cmdbuf[2] : ret; } -Result APT_GlanceParameter(NS_APPID appID, u32 bufferSize, u32* buffer, u32* actualSize, u8* signalType) +Result APT_SleepIfShellClosed(void) { - u32* cmdbuf=getThreadCommandBuffer(); + u8 out, in = 0; + return APT_AppletUtility(4, &out, sizeof(out), &in, sizeof(in)); +} + +Result APT_TryLockTransition(u32 transition, bool* succeeded) +{ + return APT_AppletUtility(6, &succeeded, sizeof(succeeded), &transition, sizeof(transition)); +} + +Result APT_UnlockTransition(u32 transition) +{ + u8 out; + return APT_AppletUtility(7, &out, sizeof(out), &transition, sizeof(transition)); +} + +Result APT_GlanceParameter(NS_APPID appID, void* buffer, size_t bufferSize, NS_APPID* sender, APT_Command* command, size_t* actualSize, Handle* parameter) +{ + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0xE,2,0); // 0xE0080 cmdbuf[1]=appID; cmdbuf[2]=bufferSize; - u32 *staticbufs = getThreadStaticBuffers(); - staticbufs[0]=IPC_Desc_StaticBuffer(bufferSize,0); + u32 saved_threadstorage[2]; + u32* staticbufs = getThreadStaticBuffers(); + saved_threadstorage[0]=staticbufs[0]; + saved_threadstorage[1]=staticbufs[1]; + staticbufs[0]=IPC_Desc_StaticBuffer(cmdbuf[2],0); staticbufs[1]=(u32)buffer; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; + Result ret = aptSendCommand(cmdbuf); + staticbufs[0]=saved_threadstorage[0]; + staticbufs[1]=saved_threadstorage[1]; - if(signalType)*signalType=cmdbuf[3]; - if(actualSize)*actualSize=cmdbuf[4]; + if (R_SUCCEEDED(ret)) + { + if (sender) *sender =cmdbuf[2]; + if (command) *command =cmdbuf[3]; + if (actualSize) *actualSize=cmdbuf[4]; + if (parameter) *parameter =cmdbuf[6]; + else if (cmdbuf[6]) svcCloseHandle(cmdbuf[6]); + } - return cmdbuf[1]; + return ret; } -Result APT_ReceiveParameter(NS_APPID appID, u32 bufferSize, u32* buffer, u32* actualSize, u8* signalType) +Result APT_ReceiveParameter(NS_APPID appID, void* buffer, size_t bufferSize, NS_APPID* sender, APT_Command* command, size_t* actualSize, Handle* parameter) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0xD,2,0); // 0xD0080 cmdbuf[1]=appID; cmdbuf[2]=bufferSize; - u32 *staticbufs = getThreadStaticBuffers(); - staticbufs[0]=IPC_Desc_StaticBuffer(bufferSize,0); + u32 saved_threadstorage[2]; + u32* staticbufs = getThreadStaticBuffers(); + saved_threadstorage[0]=staticbufs[0]; + saved_threadstorage[1]=staticbufs[1]; + staticbufs[0]=IPC_Desc_StaticBuffer(cmdbuf[2],0); staticbufs[1]=(u32)buffer; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; + Result ret = aptSendCommand(cmdbuf); + staticbufs[0]=saved_threadstorage[0]; + staticbufs[1]=saved_threadstorage[1]; - if(signalType)*signalType=cmdbuf[3]; - if(actualSize)*actualSize=cmdbuf[4]; + if (R_SUCCEEDED(ret)) + { + _aptDebug(2, cmdbuf[3]); + if (sender) *sender =cmdbuf[2]; + if (command) *command =cmdbuf[3]; + if (actualSize) *actualSize=cmdbuf[4]; + if (parameter) *parameter =cmdbuf[6]; + else if (cmdbuf[6]) svcCloseHandle(cmdbuf[6]); + } - return cmdbuf[1]; + return ret; } -Result APT_SendParameter(NS_APPID src_appID, NS_APPID dst_appID, u32 bufferSize, u32* buffer, Handle paramhandle, u8 signalType) +Result APT_SendParameter(NS_APPID source, NS_APPID dest, APT_Command command, const void* buffer, u32 bufferSize, Handle parameter) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0] = IPC_MakeHeader(0xC,4,4); // 0xC0104 - cmdbuf[1] = src_appID; - cmdbuf[2] = dst_appID; - cmdbuf[3] = signalType; + cmdbuf[1] = source; + cmdbuf[2] = dest; + cmdbuf[3] = command; cmdbuf[4] = bufferSize; cmdbuf[5] = IPC_Desc_SharedHandles(1); - cmdbuf[6] = paramhandle; + cmdbuf[6] = parameter; - cmdbuf[7] = IPC_Desc_StaticBuffer(bufferSize,0); + cmdbuf[7] = IPC_Desc_StaticBuffer(cmdbuf[4],0); cmdbuf[8] = (u32)buffer; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_SendCaptureBufferInfo(u32 bufferSize, u32* buffer) +Result APT_CancelParameter(NS_APPID source, NS_APPID dest, bool* success) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; + + cmdbuf[0] = IPC_MakeHeader(0xF,4,0); // 0xF0100 + cmdbuf[1] = source != APPID_NONE; + cmdbuf[2] = source; + cmdbuf[3] = dest != APPID_NONE; + cmdbuf[4] = dest; + + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret) && success) + *success = cmdbuf[2] & 0xFF; + + return ret; +} + +Result APT_SendCaptureBufferInfo(const aptCaptureBufInfo* captureBuf) +{ + u32 cmdbuf[16]; cmdbuf[0] = IPC_MakeHeader(0x40,1,2); // 0x400042 - cmdbuf[1] = bufferSize; - cmdbuf[2] = IPC_Desc_StaticBuffer(bufferSize,0); - cmdbuf[3] = (u32)buffer; + cmdbuf[1] = sizeof(*captureBuf); + cmdbuf[2] = IPC_Desc_StaticBuffer(cmdbuf[1],0); + cmdbuf[3] = (u32)captureBuf; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_ReplySleepQuery(NS_APPID appID, u32 a) +Result APT_ReplySleepQuery(NS_APPID appID, APT_QueryReply reply) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x3E,2,0); // 0x3E0080 cmdbuf[1]=appID; - cmdbuf[2]=a; + cmdbuf[2]=reply; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } Result APT_ReplySleepNotificationComplete(NS_APPID appID) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x3F,1,0); // 0x3F0040 cmdbuf[1]=appID; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_PrepareToCloseApplication(u8 a) +Result APT_PrepareToCloseApplication(bool cancelPreload) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x22,1,0); // 0x220040 - cmdbuf[1]=a; + cmdbuf[1]=cancelPreload; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_CloseApplication(const u8 *param, size_t paramSize, Handle handle) +Result APT_CloseApplication(const void* param, size_t paramSize, Handle handle) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x27,1,4); // 0x270044 cmdbuf[1]=paramSize; cmdbuf[2]=IPC_Desc_SharedHandles(1); cmdbuf[3]=handle; - cmdbuf[4]=IPC_Desc_StaticBuffer(paramSize,0); + cmdbuf[4]=IPC_Desc_StaticBuffer(cmdbuf[1],0); cmdbuf[5]= (u32) param; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } //See http://3dbrew.org/APT:SetApplicationCpuTimeLimit Result APT_SetAppCpuTimeLimit(u32 percent) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x4F,2,0); // 0x4F0080 cmdbuf[1]=1; cmdbuf[2]=percent; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } Result APT_GetAppCpuTimeLimit(u32 *percent) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x50,1,0); // 0x500040 cmdbuf[1]=1; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - if(percent)*percent=cmdbuf[2]; - - return cmdbuf[1]; -} - -// Note: this function is unreliable, see: http://3dbrew.org/wiki/APT:PrepareToStartApplication -Result APT_CheckNew3DS_Application(u8 *out) -{ - u32* cmdbuf=getThreadCommandBuffer(); - cmdbuf[0]=IPC_MakeHeader(0x101,0,0); // 0x1010000 - - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - if(ret==0)ret = cmdbuf[1]; - - if(out) - { - *out = 0; - if(ret==0)*out=cmdbuf[2] & 0xFF; - } + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + *percent=cmdbuf[2]; return ret; } -Result APT_CheckNew3DS_System(u8 *out) +static Result APT_CheckNew3DS_System(bool* out) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x102,0,0); // 0x1020000 - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - if(ret==0)ret = cmdbuf[1]; - - if(out) - { - *out = 0; - if(ret==0)*out=cmdbuf[2] & 0xFF; - } + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + *out = cmdbuf[2] & 0xFF; return ret; } -Result APT_CheckNew3DS(u8 *out) +Result APT_CheckNew3DS(bool* out) { - Result ret=0; - - if(out==NULL)return -1; - - *out = 0; - - if(__apt_new3dsflag_initialized) + static bool flagInit, flagValue; + if (!flagInit) { - *out = __apt_new3dsflag; - return 0; + *out = false; + Result ret = APT_CheckNew3DS_System(&flagValue); + if (R_FAILED(ret)) return ret; + flagInit = true; } - aptOpenSession(); - ret = APT_CheckNew3DS_System(out); - aptCloseSession(); - - __apt_new3dsflag_initialized = 1; - __apt_new3dsflag = *out; - - return ret; + *out = flagValue; + return 0; } -Result APT_PrepareToDoAppJump(u8 flags, u64 programID, u8 mediatype) +Result APT_PrepareToDoApplicationJump(u8 flags, u64 programID, u8 mediatype) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x31,4,0); // 0x310100 cmdbuf[1]=flags; cmdbuf[2]=(u32)programID; cmdbuf[3]=(u32)(programID>>32); cmdbuf[4]=mediatype; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_DoAppJump(u32 NSbuf0Size, u32 NSbuf1Size, u8 *NSbuf0Ptr, u8 *NSbuf1Ptr) +Result APT_DoApplicationJump(const void* param, size_t paramSize, const void* hmac) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x32,2,4); // 0x320084 - cmdbuf[1]=NSbuf0Size; - cmdbuf[2]=NSbuf1Size; - cmdbuf[3]=IPC_Desc_StaticBuffer(NSbuf0Size,0); - cmdbuf[4]=(u32)NSbuf0Ptr; - cmdbuf[5]=IPC_Desc_StaticBuffer(NSbuf1Size,2); - cmdbuf[6]=(u32)NSbuf1Ptr; + cmdbuf[1]=paramSize; + cmdbuf[2]=hmac ? 0x20 : 0; + cmdbuf[3]=IPC_Desc_StaticBuffer(cmdbuf[1],0); + cmdbuf[4]=(u32)param; + cmdbuf[5]=IPC_Desc_StaticBuffer(cmdbuf[2],2); + cmdbuf[6]=(u32)hmac; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } Result APT_PrepareToStartLibraryApplet(NS_APPID appID) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x18,1,0); // 0x180040 cmdbuf[1]=appID; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_StartLibraryApplet(NS_APPID appID, Handle inhandle, u32 *parambuf, u32 parambufsize) +Result APT_StartLibraryApplet(NS_APPID appID, const void* param, size_t paramSize, Handle handle) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x1E,2,4); // 0x1E0084 cmdbuf[1]=appID; - cmdbuf[2]=parambufsize; + cmdbuf[2]=paramSize; cmdbuf[3]=IPC_Desc_SharedHandles(1); - cmdbuf[4]=inhandle; - cmdbuf[5]=IPC_Desc_StaticBuffer(parambufsize,0); - cmdbuf[6]=(u32)parambuf; + cmdbuf[4]=handle; + cmdbuf[5]=IPC_Desc_StaticBuffer(cmdbuf[2],0); + cmdbuf[6]=(u32)param; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; -} - -Result APT_LaunchLibraryApplet(NS_APPID appID, Handle inhandle, u32 *parambuf, u32 parambufsize) -{ - Result ret=0; - u8 tmp=0; - - u8 buf1[4]; - u8 buf2[4]; - - aptOpenSession(); - APT_ReplySleepQuery(currentAppId, 0); - aptCloseSession(); - - aptOpenSession(); - ret=APT_PrepareToStartLibraryApplet(appID); - aptCloseSession(); - if(R_FAILED(ret))return ret; - - memset(buf1, 0, 4); - aptOpenSession(); - APT_AppletUtility(NULL, 0x4, 0x1, buf1, 0x1, buf2); - aptCloseSession(); - - while(1) - { - aptOpenSession(); - ret=APT_IsRegistered(appID, &tmp); - aptCloseSession(); - if(R_FAILED(ret))return ret; - - if(tmp!=0)break; - } - - aptCallHook(APTHOOK_ONSUSPEND); - - __apt_launchapplet_appID = appID; - __apt_launchapplet_inhandle = inhandle; - __apt_launchapplet_parambuf = parambuf; - __apt_launchapplet_parambufsize = parambufsize; - - // Set status to SUSPENDED. - svcClearEvent(aptStatusEvent); - aptSetStatus(APP_SUSPENDED); - - // Save Vram - GSPGPU_SaveVramSysArea(); - - // Capture screen. - memset(__ns_capinfo, 0, 0x20); - - aptInitCaptureInfo(__ns_capinfo); - - // Send capture-screen info to the library applet. - aptOpenSession(); - APT_SendParameter(currentAppId, appID, 0x20, __ns_capinfo, 0x0, 0x2); - aptCloseSession(); - - // Release GSP module. - GSPGPU_ReleaseRight(); - - return 0; + return aptSendCommand(cmdbuf); } Result APT_PrepareToStartSystemApplet(NS_APPID appID) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0]=IPC_MakeHeader(0x19,1,0); // 0x190040 cmdbuf[1]=appID; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } -Result APT_StartSystemApplet(NS_APPID appID, u32 bufSize, Handle applHandle, u8 *buf) +Result APT_StartSystemApplet(NS_APPID appID, const void* param, size_t paramSize, Handle handle) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0] = IPC_MakeHeader(0x1F,2,4); // 0x001F0084 cmdbuf[1] = appID; - cmdbuf[2] = bufSize; + cmdbuf[2] = paramSize; cmdbuf[3] = IPC_Desc_SharedHandles(1); - cmdbuf[4] = applHandle; - cmdbuf[5] = IPC_Desc_StaticBuffer(bufSize,0); - cmdbuf[6] = (u32)buf; + cmdbuf[4] = handle; + cmdbuf[5] = IPC_Desc_StaticBuffer(cmdbuf[2],0); + cmdbuf[6] = (u32)param; - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; - - return cmdbuf[1]; + return aptSendCommand(cmdbuf); } Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr) { - u32* cmdbuf=getThreadCommandBuffer(); + u32 cmdbuf[16]; cmdbuf[0] = IPC_MakeHeader(0x44,0,0); // 0x00440000 - Result ret=0; - if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; + Result ret = aptSendCommand(cmdbuf); + if (R_SUCCEEDED(ret)) + { + if(fontHandle) *fontHandle = cmdbuf[4]; + if(mapAddr) *mapAddr = cmdbuf[2]; + } - if(fontHandle) *fontHandle = cmdbuf[4]; - if(mapAddr) *mapAddr = cmdbuf[2]; - - return cmdbuf[1]; + return ret; } diff --git a/libctru/source/services/cam.c b/libctru/source/services/cam.c index 996d544..3b76bd0 100644 --- a/libctru/source/services/cam.c +++ b/libctru/source/services/cam.c @@ -457,7 +457,7 @@ Result CAMU_SetLensCorrection(u32 select, CAMU_LensCorrection lensCorrection) { Result CAMU_SetOutputFormat(u32 select, CAMU_OutputFormat format, CAMU_Context context) { Result ret = 0; u32* cmdbuf = getThreadCommandBuffer(); - cmdbuf[0] = IPC_MakeHeader(0x25,2,0); // 0x2500C0 + cmdbuf[0] = IPC_MakeHeader(0x25,3,0); // 0x2500C0 cmdbuf[1] = select; cmdbuf[2] = format; cmdbuf[3] = context; diff --git a/libctru/source/services/fs.c b/libctru/source/services/fs.c index 3470283..8894cf8 100644 --- a/libctru/source/services/fs.c +++ b/libctru/source/services/fs.c @@ -1137,8 +1137,8 @@ Result FSUSER_ExportIntegrityVerificationSeed(FS_IntegrityVerificationSeed* seed u32 *cmdbuf = getThreadCommandBuffer(); cmdbuf[0] = IPC_MakeHeader(0x84A,0,2); // 0x84A0002 - cmdbuf[2] = IPC_Desc_Buffer(sizeof(FS_IntegrityVerificationSeed), IPC_BUFFER_W); - cmdbuf[3] = (u32) seed; + cmdbuf[1] = IPC_Desc_Buffer(sizeof(FS_IntegrityVerificationSeed), IPC_BUFFER_W); + cmdbuf[2] = (u32) seed; Result ret = 0; if(R_FAILED(ret = svcSendSyncRequest(fsSession()))) return ret; @@ -1151,8 +1151,8 @@ Result FSUSER_ImportIntegrityVerificationSeed(FS_IntegrityVerificationSeed* seed u32 *cmdbuf = getThreadCommandBuffer(); cmdbuf[0] = IPC_MakeHeader(0x84B,0,2); // 0x84B0002 - cmdbuf[2] = IPC_Desc_Buffer(sizeof(FS_IntegrityVerificationSeed), IPC_BUFFER_R); - cmdbuf[3] = (u32) seed; + cmdbuf[1] = IPC_Desc_Buffer(sizeof(FS_IntegrityVerificationSeed), IPC_BUFFER_R); + cmdbuf[2] = (u32) seed; Result ret = 0; if(R_FAILED(ret = svcSendSyncRequest(fsSession()))) return ret; diff --git a/libctru/source/services/gspgpu.c b/libctru/source/services/gspgpu.c index a4b3c0d..af602d8 100644 --- a/libctru/source/services/gspgpu.c +++ b/libctru/source/services/gspgpu.c @@ -14,19 +14,22 @@ Handle gspGpuHandle; static int gspRefCount; -Handle gspEvents[GSPGPU_EVENT_MAX]; -vu32 gspEventCounts[GSPGPU_EVENT_MAX]; -ThreadFunc gspEventCb[GSPGPU_EVENT_MAX]; -void* gspEventCbData[GSPGPU_EVENT_MAX]; -bool gspEventCbOneShot[GSPGPU_EVENT_MAX]; -volatile bool gspRunEvents; -Thread gspEventThread; +static s32 gspLastEvent = -1; +static LightEvent gspEvents[GSPGPU_EVENT_MAX]; +static vu32 gspEventCounts[GSPGPU_EVENT_MAX]; +static ThreadFunc gspEventCb[GSPGPU_EVENT_MAX]; +static void* gspEventCbData[GSPGPU_EVENT_MAX]; +static bool gspEventCbOneShot[GSPGPU_EVENT_MAX]; +static volatile bool gspRunEvents; +static Thread gspEventThread; static Handle gspEvent; static vu8* gspEventData; static void gspEventThreadMain(void *arg); +Handle __sync_get_arbiter(void); + Result gspInit(void) { Result res=0; @@ -53,20 +56,10 @@ void gspSetEventCallback(GSPGPU_Event id, ThreadFunc cb, void* data, bool oneSho Result gspInitEventHandler(Handle _gspEvent, vu8* _gspSharedMem, u8 gspThreadId) { - // Create events + // Initialize events int i; for (i = 0; i < GSPGPU_EVENT_MAX; i ++) - { - Result rc = svcCreateEvent(&gspEvents[i], 0); - if (rc != 0) - { - // Destroy already created events due to failure - int j; - for (j = 0; j < i; j ++) - svcCloseHandle(gspEvents[j]); - return rc; - } - } + LightEvent_Init(&gspEvents[i], RESET_STICKY); // Start event thread gspEvent = _gspEvent; @@ -82,11 +75,6 @@ void gspExitEventHandler(void) gspRunEvents = false; svcSignalEvent(gspEvent); threadJoin(gspEventThread, U64_MAX); - - // Free events - int i; - for (i = 0; i < GSPGPU_EVENT_MAX; i ++) - svcCloseHandle(gspEvents[i]); } void gspWaitForEvent(GSPGPU_Event id, bool nextEvent) @@ -94,19 +82,30 @@ void gspWaitForEvent(GSPGPU_Event id, bool nextEvent) if(id>= GSPGPU_EVENT_MAX)return; if (nextEvent) - svcClearEvent(gspEvents[id]); - svcWaitSynchronization(gspEvents[id], U64_MAX); + LightEvent_Clear(&gspEvents[id]); + LightEvent_Wait(&gspEvents[id]); if (!nextEvent) - svcClearEvent(gspEvents[id]); + LightEvent_Clear(&gspEvents[id]); } GSPGPU_Event gspWaitForAnyEvent(void) { - s32 which = 0; - Result rc = svcWaitSynchronizationN(&which, gspEvents, GSPGPU_EVENT_MAX, false, U64_MAX); - if (R_FAILED(rc)) return -1; - svcClearEvent(gspEvents[which]); - return which; + s32 x; + do + { + do + { + x = __ldrex(&gspLastEvent); + if (x < 0) + { + __clrex(); + break; + } + } while (__strex(&gspLastEvent, -1)); + if (x < 0) + svcArbitrateAddress(__sync_get_arbiter(), (u32)&gspLastEvent, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0); + } while (x < 0); + return (GSPGPU_Event)x; } static int popInterrupt() @@ -168,7 +167,11 @@ void gspEventThreadMain(void *arg) gspEventCb[curEvt] = NULL; func(gspEventCbData[curEvt]); } - svcSignalEvent(gspEvents[curEvt]); + LightEvent_Signal(&gspEvents[curEvt]); + do + __ldrex(&gspLastEvent); + while (__strex(&gspLastEvent, curEvt)); + svcArbitrateAddress(__sync_get_arbiter(), (u32)&gspLastEvent, ARBITRATION_SIGNAL, 1, 0); gspEventCounts[curEvt]++; } } diff --git a/libctru/source/services/hid.c b/libctru/source/services/hid.c index 711bf9f..f371df5 100644 --- a/libctru/source/services/hid.c +++ b/libctru/source/services/hid.c @@ -31,7 +31,7 @@ static int hidRefCount; Result hidInit(void) { - u8 val=0; + bool val=false; Result ret=0; if (AtomicPostIncrement(&hidRefCount)) return 0; @@ -84,7 +84,7 @@ void hidExit(void) if (AtomicDecrement(&hidRefCount)) return; // Unmap HID sharedmem and close handles. - u8 val=0; + bool val=false; int i; for(i=0; i<5; i++)svcCloseHandle(hidEvents[i]); svcUnmapMemoryBlock(hidMemHandle, (u32)hidSharedMem); svcCloseHandle(hidMemHandle); diff --git a/libctru/source/services/httpc.c b/libctru/source/services/httpc.c index d5c7a83..aa954c3 100644 --- a/libctru/source/services/httpc.c +++ b/libctru/source/services/httpc.c @@ -20,7 +20,7 @@ static Handle __httpc_sharedmem_handle; static Result HTTPC_Initialize(Handle handle, u32 sharedmem_size, Handle sharedmem_handle); static Result HTTPC_Finalize(Handle handle); -static Result HTTPC_CreateContext(Handle handle, HTTPC_RequestMethod method, char* url, Handle* contextHandle); +static Result HTTPC_CreateContext(Handle handle, HTTPC_RequestMethod method, const char* url, Handle* contextHandle); static Result HTTPC_CloseContext(Handle handle, Handle contextHandle); static Result HTTPC_InitializeConnectionSession(Handle handle, Handle contextHandle); @@ -87,7 +87,7 @@ void httpcExit(void) } } -Result httpcOpenContext(httpcContext *context, HTTPC_RequestMethod method, char* url, u32 use_defaultproxy) +Result httpcOpenContext(httpcContext *context, HTTPC_RequestMethod method, const char* url, u32 use_defaultproxy) { Result ret=0; @@ -129,6 +129,19 @@ Result httpcCloseContext(httpcContext *context) return ret; } +Result httpcCancelConnection(httpcContext *context) +{ + u32* cmdbuf=getThreadCommandBuffer(); + + cmdbuf[0]=IPC_MakeHeader(0x4,1,0); // 0x40040 + cmdbuf[1]=context->httphandle; + + Result ret=0; + if(R_FAILED(ret=svcSendSyncRequest(__httpc_servhandle)))return ret; + + return cmdbuf[1]; +} + Result httpcDownloadData(httpcContext *context, u8* buffer, u32 size, u32 *downloadedsize) { Result ret=0; @@ -187,7 +200,7 @@ static Result HTTPC_Finalize(Handle handle) return cmdbuf[1]; } -static Result HTTPC_CreateContext(Handle handle, HTTPC_RequestMethod method, char* url, Handle* contextHandle) +static Result HTTPC_CreateContext(Handle handle, HTTPC_RequestMethod method, const char* url, Handle* contextHandle) { u32* cmdbuf=getThreadCommandBuffer(); u32 l=strlen(url)+1; @@ -246,7 +259,7 @@ static Result HTTPC_CloseContext(Handle handle, Handle contextHandle) return cmdbuf[1]; } -Result httpcAddRequestHeaderField(httpcContext *context, char* name, char* value) +Result httpcAddRequestHeaderField(httpcContext *context, const char* name, const char* value) { u32* cmdbuf=getThreadCommandBuffer(); @@ -268,7 +281,7 @@ Result httpcAddRequestHeaderField(httpcContext *context, char* name, char* value return cmdbuf[1]; } -Result httpcAddPostDataAscii(httpcContext *context, char* name, char* value) +Result httpcAddPostDataAscii(httpcContext *context, const char* name, const char* value) { u32* cmdbuf=getThreadCommandBuffer(); @@ -290,7 +303,7 @@ Result httpcAddPostDataAscii(httpcContext *context, char* name, char* value) return cmdbuf[1]; } -Result httpcAddPostDataRaw(httpcContext *context, u32* data, u32 len) +Result httpcAddPostDataRaw(httpcContext *context, const u32* data, u32 len) { u32* cmdbuf=getThreadCommandBuffer(); @@ -337,6 +350,24 @@ Result httpcReceiveData(httpcContext *context, u8* buffer, u32 size) return cmdbuf[1]; } +Result httpcReceiveDataTimeout(httpcContext *context, u8* buffer, u32 size, u64 timeout) +{ + u32* cmdbuf=getThreadCommandBuffer(); + + cmdbuf[0]=IPC_MakeHeader(0xC,4,2); // 0xC0102 + cmdbuf[1]=context->httphandle; + cmdbuf[2]=size; + cmdbuf[3]=timeout & 0xffffffff; + cmdbuf[4]=(timeout >> 32) & 0xffffffff; + cmdbuf[5]=IPC_Desc_Buffer(size,IPC_BUFFER_W); + cmdbuf[6]=(u32)buffer; + + Result ret=0; + if(R_FAILED(ret=svcSendSyncRequest(context->servhandle)))return ret; + + return cmdbuf[1]; +} + Result httpcGetRequestState(httpcContext *context, HTTPC_RequestStatus* out) { u32* cmdbuf=getThreadCommandBuffer(); @@ -367,7 +398,7 @@ Result httpcGetDownloadSizeState(httpcContext *context, u32* downloadedsize, u32 return cmdbuf[1]; } -Result httpcGetResponseHeader(httpcContext *context, char* name, char* value, u32 valuebuf_maxsize) +Result httpcGetResponseHeader(httpcContext *context, const char* name, char* value, u32 valuebuf_maxsize) { u32* cmdbuf=getThreadCommandBuffer(); @@ -388,7 +419,7 @@ Result httpcGetResponseHeader(httpcContext *context, char* name, char* value, u3 return cmdbuf[1]; } -Result httpcGetResponseStatusCode(httpcContext *context, u32* out, u64 delay) +Result httpcGetResponseStatusCode(httpcContext *context, u32* out) { u32* cmdbuf=getThreadCommandBuffer(); @@ -403,7 +434,25 @@ Result httpcGetResponseStatusCode(httpcContext *context, u32* out, u64 delay) return cmdbuf[1]; } -Result httpcAddTrustedRootCA(httpcContext *context, u8 *cert, u32 certsize) + +Result httpcGetResponseStatusCodeTimeout(httpcContext *context, u32* out, u64 timeout) +{ + u32* cmdbuf=getThreadCommandBuffer(); + + cmdbuf[0]=IPC_MakeHeader(0x23,3,0); // 0x2300C0 + cmdbuf[1]=context->httphandle; + cmdbuf[2]=timeout & 0xffffffff; + cmdbuf[3]=(timeout >> 32) & 0xffffffff; + + Result ret=0; + if(R_FAILED(ret=svcSendSyncRequest(context->servhandle)))return ret; + + *out = cmdbuf[2]; + + return cmdbuf[1]; +} + +Result httpcAddTrustedRootCA(httpcContext *context, const u8 *cert, u32 certsize) { u32* cmdbuf=getThreadCommandBuffer(); @@ -447,7 +496,7 @@ Result httpcSelectRootCertChain(httpcContext *context, u32 RootCertChain_context return cmdbuf[1]; } -Result httpcSetClientCert(httpcContext *context, u8 *cert, u32 certsize, u8 *privk, u32 privk_size) +Result httpcSetClientCert(httpcContext *context, const u8 *cert, u32 certsize, const u8 *privk, u32 privk_size) { u32* cmdbuf=getThreadCommandBuffer(); @@ -550,7 +599,7 @@ Result httpcDestroyRootCertChain(u32 RootCertChain_contexthandle) return cmdbuf[1]; } -Result httpcRootCertChainAddCert(u32 RootCertChain_contexthandle, u8 *cert, u32 certsize, u32 *cert_contexthandle) +Result httpcRootCertChainAddCert(u32 RootCertChain_contexthandle, const u8 *cert, u32 certsize, u32 *cert_contexthandle) { u32* cmdbuf=getThreadCommandBuffer(); @@ -600,7 +649,7 @@ Result httpcRootCertChainRemoveCert(u32 RootCertChain_contexthandle, u32 cert_co return cmdbuf[1]; } -Result httpcOpenClientCertContext(u8 *cert, u32 certsize, u8 *privk, u32 privk_size, u32 *ClientCert_contexthandle) +Result httpcOpenClientCertContext(const u8 *cert, u32 certsize, const u8 *privk, u32 privk_size, u32 *ClientCert_contexthandle) { u32* cmdbuf=getThreadCommandBuffer(); @@ -649,3 +698,17 @@ Result httpcCloseClientCertContext(u32 ClientCert_contexthandle) return cmdbuf[1]; } + +Result httpcSetKeepAlive(httpcContext *context, HTTPC_KeepAlive option) +{ + u32* cmdbuf=getThreadCommandBuffer(); + + cmdbuf[0]=IPC_MakeHeader(0x37,2,0); // 0x370080 + cmdbuf[1]=context->httphandle; + cmdbuf[2]=option; + + Result ret=0; + if(R_FAILED(ret=svcSendSyncRequest(context->servhandle)))return ret; + + return cmdbuf[1]; +} diff --git a/libctru/source/services/nfc.c b/libctru/source/services/nfc.c index d32832a..044d449 100644 --- a/libctru/source/services/nfc.c +++ b/libctru/source/services/nfc.c @@ -60,12 +60,12 @@ Handle nfcGetSessionHandle(void) Result nfcStartScanning(u16 inval) { Result ret, ret2; - u8 new3ds_flag = 0; + bool new3ds_flag = false; u8 status; APT_CheckNew3DS(&new3ds_flag); - if(new3ds_flag==0) + if(!new3ds_flag) { ret = NFC_StartCommunication(); if(R_FAILED(ret))return ret; @@ -102,13 +102,13 @@ Result nfcStartScanning(u16 inval) void nfcStopScanning(void) { - u8 new3ds_flag = 0; + bool new3ds_flag = false; APT_CheckNew3DS(&new3ds_flag); NFC_StopTagScanning(); - if(new3ds_flag==0) + if(!new3ds_flag) { NFC_StopCommunication(); } diff --git a/libctru/source/services/ps.c b/libctru/source/services/ps.c index 53ae0eb..ab8520e 100644 --- a/libctru/source/services/ps.c +++ b/libctru/source/services/ps.c @@ -1,4 +1,5 @@ #include +#include #include <3ds/types.h> #include <3ds/result.h> #include <3ds/svc.h> @@ -34,10 +35,7 @@ Result PS_EncryptDecryptAes(u32 size, u8* in, u8* out, PS_AESAlgorithm aes_algo, cmdbuf[0] = IPC_MakeHeader(0x4,7,4); // 0x401C4 cmdbuf[1] = size; - cmdbuf[2] = _iv[0]; - cmdbuf[3] = _iv[1]; - cmdbuf[4] = _iv[2]; - cmdbuf[5] = _iv[3]; + memcpy(&cmdbuf[2], _iv, 16); cmdbuf[6] = aes_algo; cmdbuf[7] = key_type; cmdbuf[8] = IPC_Desc_PXIBuffer(size,0,false); @@ -47,10 +45,7 @@ Result PS_EncryptDecryptAes(u32 size, u8* in, u8* out, PS_AESAlgorithm aes_algo, if(R_FAILED(ret = svcSendSyncRequest(psHandle)))return ret; - _iv[0] = cmdbuf[2] & 0xFF; - _iv[1] = cmdbuf[3] & 0xFF; - _iv[2] = cmdbuf[4] & 0xFF; - _iv[3] = cmdbuf[5] & 0xFF; + memcpy(_iv, &cmdbuf[2], 16); return (Result)cmdbuf[1]; } @@ -68,15 +63,13 @@ Result PS_EncryptSignDecryptVerifyAesCcm(u8* in, u32 in_size, u8* out, u32 out_s cmdbuf[3] = mac_data_len; cmdbuf[4] = data_len; cmdbuf[5] = mac_len; - cmdbuf[6] = _nonce[0]; - cmdbuf[7] = _nonce[1]; - cmdbuf[8] = _nonce[2]; + memcpy(&cmdbuf[6], _nonce, 12); cmdbuf[9] = aes_algo; cmdbuf[10] = key_type; - cmdbuf[8] = IPC_Desc_PXIBuffer(in_size,0,false); - cmdbuf[9] = (u32)in; - cmdbuf[10] = IPC_Desc_PXIBuffer(out_size,1,false); - cmdbuf[11] = (u32)out; + cmdbuf[11] = IPC_Desc_PXIBuffer(in_size,0,false); + cmdbuf[12] = (u32)in; + cmdbuf[13] = IPC_Desc_PXIBuffer(out_size,1,false); + cmdbuf[14] = (u32)out; if(R_FAILED(ret = svcSendSyncRequest(psHandle)))return ret; @@ -110,3 +103,18 @@ Result PS_GetDeviceId(u32* device_id) return (Result)cmdbuf[1]; } + +Result PS_GenerateRandomBytes(void* out, size_t len) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xD,1,2); // 0xD0042 + cmdbuf[1] = len; + cmdbuf[2] = IPC_Desc_Buffer(len, IPC_BUFFER_W); + cmdbuf[3] = (u32)out; + + if(R_FAILED(ret = svcSendSyncRequest(psHandle)))return ret; + + return (Result)cmdbuf[1]; +} diff --git a/libctru/source/services/pxidev.c b/libctru/source/services/pxidev.c new file mode 100644 index 0000000..55d608c --- /dev/null +++ b/libctru/source/services/pxidev.c @@ -0,0 +1,101 @@ +#include +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/svc.h> +#include <3ds/srv.h> +#include <3ds/synchronization.h> +#include <3ds/services/pxidev.h> +#include <3ds/ipc.h> + +static Handle pxiDevHandle; +static int pxiDevRefCount; + +Result pxiDevInit(void) +{ + Result ret; + + if (AtomicPostIncrement(&pxiDevRefCount)) return 0; + + ret = srvGetServiceHandle(&pxiDevHandle, "pxi:dev"); + if (R_FAILED(ret)) AtomicDecrement(&pxiDevRefCount); + + return ret; +} + +void pxiDevExit(void) +{ + if (AtomicDecrement(&pxiDevRefCount)) return; + svcCloseHandle(pxiDevHandle); +} + +Result PXIDEV_SPIMultiWriteRead(PXIDEV_SPIBuffer* header, PXIDEV_SPIBuffer* writeBuffer1, PXIDEV_SPIBuffer* readBuffer1, PXIDEV_SPIBuffer* writeBuffer2, PXIDEV_SPIBuffer* readBuffer2, PXIDEV_SPIBuffer* footer) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xD,26,8); // 0x000D0688 + memcpy(&cmdbuf[1], header->ptr, header->size); + cmdbuf[3] = header->size; + cmdbuf[4] = header->transferOption; + cmdbuf[5] = (u32) (header->waitOperation & 0xFFFFFFFF); + cmdbuf[6] = (u32) ((header->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[7] = writeBuffer1->size; + cmdbuf[8] = writeBuffer1->transferOption; + cmdbuf[9] = (u32) (writeBuffer1->waitOperation & 0xFFFFFFFF); + cmdbuf[10] = (u32) ((writeBuffer1->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[11] = readBuffer1->size; + cmdbuf[12] = readBuffer1->transferOption; + cmdbuf[13] = (u32) (readBuffer1->waitOperation & 0xFFFFFFFF); + cmdbuf[14] = (u32) ((readBuffer1->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[15] = writeBuffer2->size; + cmdbuf[16] = writeBuffer2->transferOption; + cmdbuf[17] = (u32) (writeBuffer2->waitOperation & 0xFFFFFFFF); + cmdbuf[18] = (u32) ((writeBuffer2->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[19] = readBuffer2->size; + cmdbuf[20] = readBuffer2->transferOption; + cmdbuf[21] = (u32) (readBuffer2->waitOperation & 0xFFFFFFFF); + cmdbuf[22] = (u32) ((readBuffer2->waitOperation >> 32) & 0xFFFFFFFF); + memcpy(&cmdbuf[23], footer->ptr, footer->size); + cmdbuf[25] = footer->size; + cmdbuf[26] = footer->transferOption; + cmdbuf[27] = IPC_Desc_PXIBuffer(writeBuffer1->size, 0, true); + cmdbuf[28] = (u32) writeBuffer1->ptr; + cmdbuf[29] = IPC_Desc_PXIBuffer(writeBuffer2->size, 1, true); + cmdbuf[30] = (u32) writeBuffer2->ptr; + cmdbuf[31] = IPC_Desc_PXIBuffer(readBuffer1->size, 2, false); + cmdbuf[32] = (u32) readBuffer1->ptr; + cmdbuf[33] = IPC_Desc_PXIBuffer(readBuffer2->size, 3, false); + cmdbuf[34] = (u32) readBuffer2->ptr; + + if (R_FAILED(ret = svcSendSyncRequest(pxiDevHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result PXIDEV_SPIWriteRead(u32* bytesRead, u64 initialWaitOperation, PXIDEV_SPIBuffer* writeBuffer, PXIDEV_SPIBuffer* readBuffer) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xE,10,4); // 0x000E0284 + cmdbuf[1] = (u32) (initialWaitOperation & 0xFFFFFFFF); + cmdbuf[2] = (u32) ((initialWaitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[3] = writeBuffer->size; + cmdbuf[4] = writeBuffer->transferOption; + cmdbuf[5] = (u32) (writeBuffer->waitOperation & 0xFFFFFFFF); + cmdbuf[6] = (u32) ((writeBuffer->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[7] = readBuffer->size; + cmdbuf[8] = readBuffer->transferOption; + cmdbuf[9] = (u32) (readBuffer->waitOperation & 0xFFFFFFFF); + cmdbuf[10] = (u32) ((readBuffer->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[11] = IPC_Desc_PXIBuffer(writeBuffer->size, 0, true); + cmdbuf[12] = (u32) writeBuffer->ptr; + cmdbuf[13] = IPC_Desc_PXIBuffer(readBuffer->size, 1, false); + cmdbuf[14] = (u32) readBuffer->ptr; + + if (R_FAILED(ret = svcSendSyncRequest(pxiDevHandle))) return ret; + + if (bytesRead) *bytesRead = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} diff --git a/libctru/source/services/sslc.c b/libctru/source/services/sslc.c index 83cd704..facc533 100644 --- a/libctru/source/services/sslc.c +++ b/libctru/source/services/sslc.c @@ -52,7 +52,7 @@ static Result sslcipc_Initialize(void) return cmdbuf[1]; } -static Result sslcipc_CreateContext(sslcContext *context, int sockfd, u32 input_opt, char *hostname) +static Result sslcipc_CreateContext(sslcContext *context, int sockfd, u32 input_opt, const char *hostname) { u32* cmdbuf=getThreadCommandBuffer(); u32 size = strlen(hostname)+1; @@ -101,7 +101,7 @@ static Result sslcipc_DestroyCertChain(u32 type, u32 contexthandle) return cmdbuf[1]; } -static Result sslcipc_CertChainAddCert(u32 type, u32 contexthandle, u8 *cert, u32 certsize, u32 *cert_contexthandle) +static Result sslcipc_CertChainAddCert(u32 type, u32 contexthandle, const u8 *cert, u32 certsize, u32 *cert_contexthandle) { u32* cmdbuf=getThreadCommandBuffer(); @@ -151,7 +151,7 @@ static Result sslcipc_CertChainRemoveCert(u32 type, u32 contexthandle, u32 cert_ return cmdbuf[1]; } -Result sslcOpenClientCertContext(u8 *cert, u32 certsize, u8 *key, u32 keysize, u32 *ClientCert_contexthandle) +Result sslcOpenClientCertContext(const u8 *cert, u32 certsize, const u8 *key, u32 keysize, u32 *ClientCert_contexthandle) { u32* cmdbuf=getThreadCommandBuffer(); @@ -275,7 +275,7 @@ static Result sslcipc_StartConnectionGetOut(sslcContext *context, int *internal_ return ret; } -static Result sslcipc_DataTransfer(sslcContext *context, void *buf, size_t len, u32 type) +static Result sslcipc_DataTransfer(sslcContext *context, const void *buf, size_t len, u32 type) { u32* cmdbuf=getThreadCommandBuffer(); @@ -378,7 +378,7 @@ static Result sslcipc_ContextInitSharedmem(sslcContext *context, u32 size) return cmdbuf[1]; } -Result sslcAddCert(sslcContext *context, u8 *buf, u32 size) +Result sslcAddCert(sslcContext *context, const u8 *buf, u32 size) { u32* cmdbuf=getThreadCommandBuffer(); @@ -405,7 +405,7 @@ Result sslcDestroyRootCertChain(u32 RootCertChain_contexthandle) return sslcipc_DestroyCertChain(0, RootCertChain_contexthandle); } -Result sslcAddTrustedRootCA(u32 RootCertChain_contexthandle, u8 *cert, u32 certsize, u32 *cert_contexthandle) +Result sslcAddTrustedRootCA(u32 RootCertChain_contexthandle, const u8 *cert, u32 certsize, u32 *cert_contexthandle) { return sslcipc_CertChainAddCert(0, RootCertChain_contexthandle, cert, certsize, cert_contexthandle); } @@ -430,7 +430,7 @@ Result sslcDestroy8CertChain(u32 PinnedCertChain_contexthandle) return sslcipc_DestroyCertChain(1, PinnedCertChain_contexthandle); } -Result sslc8CertChainAddCert(u32 PinnedCertChain_contexthandle, u8 *cert, u32 certsize, u32 *cert_contexthandle) +Result sslc8CertChainAddCert(u32 PinnedCertChain_contexthandle, const u8 *cert, u32 certsize, u32 *cert_contexthandle) { return sslcipc_CertChainAddCert(1, PinnedCertChain_contexthandle, cert, certsize, cert_contexthandle); } @@ -445,7 +445,7 @@ Result sslc8CertChainRemoveCert(u32 PinnedCertChain_contexthandle, u32 cert_cont return sslcipc_CertChainRemoveCert(1, PinnedCertChain_contexthandle, cert_contexthandle); } -Result sslcCreateContext(sslcContext *context, int sockfd, u32 input_opt, char *hostname) +Result sslcCreateContext(sslcContext *context, int sockfd, u32 input_opt, const char *hostname) { Result ret=0; @@ -507,7 +507,7 @@ Result sslcRead(sslcContext *context, void *buf, size_t len, bool peek) return sslcipc_DataTransfer(context, buf, len, type); } -Result sslcWrite(sslcContext *context, void *buf, size_t len) +Result sslcWrite(sslcContext *context, const void *buf, size_t len) { return sslcipc_DataTransfer(context, buf, len, 2); } diff --git a/libctru/source/services/uds.c b/libctru/source/services/uds.c index cf25708..5a12366 100644 --- a/libctru/source/services/uds.c +++ b/libctru/source/services/uds.c @@ -481,7 +481,7 @@ Result udsScanBeacons(void *buf, size_t maxsize, udsNetworkScanInfo **networks, if(maxsize < sizeof(nwmBeaconDataReplyHeader))return -2; - ret = svcCreateEvent(&event, 0); + ret = svcCreateEvent(&event, RESET_ONESHOT); if(R_FAILED(ret))return ret; if(!connected)ret = udsipc_RecvBeaconBroadcastData(outbuf, maxsize, &scaninput, wlancommID, id8, event); diff --git a/libctru/source/svc.s b/libctru/source/svc.s index 309487b..d3815e3 100644 --- a/libctru/source/svc.s +++ b/libctru/source/svc.s @@ -352,6 +352,14 @@ SVC_BEGIN svcReplyAndReceive add sp, sp, #4 bx lr +SVC_BEGIN svcBindInterrupt + svc 0x50 + bx lr + +SVC_BEGIN svcUnbindInterrupt + svc 0x51 + bx lr + SVC_BEGIN svcInvalidateProcessDataCache svc 0x52 bx lr diff --git a/libctru/source/synchronization.c b/libctru/source/synchronization.c index ecbae16..15bc35c 100644 --- a/libctru/source/synchronization.c +++ b/libctru/source/synchronization.c @@ -127,3 +127,92 @@ void RecursiveLock_Unlock(RecursiveLock* lock) LightLock_Unlock(&lock->lock); } } + +static inline void LightEvent_SetState(LightEvent* event, int state) +{ + do + __ldrex(&event->state); + while (__strex(&event->state, state)); +} + +static inline int LightEvent_TryReset(LightEvent* event) +{ + do + { + if (__ldrex(&event->state)) + { + __clrex(); + return 0; + } + } while (__strex(&event->state, -1)); + return 1; +} + +void LightEvent_Init(LightEvent* event, ResetType reset_type) +{ + LightLock_Init(&event->lock); + LightEvent_SetState(event, reset_type == RESET_STICKY ? -2 : -1); +} + +void LightEvent_Clear(LightEvent* event) +{ + if (event->state == 1) + { + LightLock_Lock(&event->lock); + LightEvent_SetState(event, -2); + LightLock_Unlock(&event->lock); + } else if (event->state == 0) + LightEvent_SetState(event, -1); +} + +void LightEvent_Pulse(LightEvent* event) +{ + if (event->state == -2) + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, -1, 0); + else if (event->state == -1) + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, 1, 0); + else + LightEvent_Clear(event); +} + +void LightEvent_Signal(LightEvent* event) +{ + if (event->state == -1) + { + LightEvent_SetState(event, 0); + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, 1, 0); + } else if (event->state == -2) + { + LightLock_Lock(&event->lock); + LightEvent_SetState(event, 1); + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, -1, 0); + LightLock_Unlock(&event->lock); + } +} + +int LightEvent_TryWait(LightEvent* event) +{ + if (event->state == 1) + return 1; + return LightEvent_TryReset(event); +} + +void LightEvent_Wait(LightEvent* event) +{ + for (;;) + { + if (event->state == -2) + { + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0); + return; + } + if (event->state != -1) + { + if (event->state == 1) + return; + if (event->state == 0 && LightEvent_TryReset(event)) + return; + } + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0); + } +}