Compare commits
73 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1b1762f872 | ||
![]() |
04b9418dd9 | ||
![]() |
30dc65121d | ||
![]() |
8cbcce773b | ||
![]() |
a88b5af7a8 | ||
![]() |
8e55cdf05d | ||
![]() |
5f06a037d7 | ||
![]() |
cc5b884d7d | ||
![]() |
c5ecf54d51 | ||
![]() |
faf5162b60 | ||
![]() |
a6a6548348 | ||
![]() |
0da8705527 | ||
![]() |
d0936b879b | ||
![]() |
c579a65655 | ||
![]() |
bf4a24a4e7 | ||
![]() |
bd69c96b7d | ||
![]() |
41e31d3921 | ||
![]() |
687aaf6cfc | ||
![]() |
4de90c3436 | ||
![]() |
5b2a9f6136 | ||
![]() |
0f098853f2 | ||
![]() |
a4634c0290 | ||
![]() |
e9fa000e94 | ||
![]() |
0cbae436c8 | ||
![]() |
a9a9d9ab69 | ||
![]() |
6714c04806 | ||
![]() |
39a53c4fe5 | ||
![]() |
8d90551306 | ||
![]() |
032f3dad40 | ||
![]() |
b18f04d887 | ||
![]() |
98324d412f | ||
![]() |
cd78fb05cb | ||
![]() |
a30628058c | ||
![]() |
cb9d682f65 | ||
![]() |
1de86ea38a | ||
![]() |
e253c2c005 | ||
![]() |
08b76e2e17 | ||
![]() |
da323fa50b | ||
![]() |
af5321c78e | ||
![]() |
58719f32ac | ||
![]() |
ef806d5bd8 | ||
![]() |
e6fcbef36c | ||
![]() |
b20ac22ab7 | ||
![]() |
59cf50c201 | ||
![]() |
607f1d1fb6 | ||
![]() |
10cf9bb95a | ||
![]() |
813d28ddc4 | ||
![]() |
82f821156a | ||
![]() |
59e0d7596f | ||
![]() |
d4e08aa2cc | ||
![]() |
b76a245b79 | ||
![]() |
11686876ac | ||
![]() |
c36d9cc4a6 | ||
![]() |
cf538b1fa8 | ||
![]() |
ebb5305188 | ||
![]() |
4815537048 | ||
![]() |
4d36c74be4 | ||
![]() |
8ad7b5a71a | ||
![]() |
bb9c49b84d | ||
![]() |
2564d708e6 | ||
![]() |
c83c12357e | ||
![]() |
982be53414 | ||
![]() |
5f13628dac | ||
![]() |
6360f4bdb1 | ||
![]() |
48967dc417 | ||
![]() |
1f4669fa4c | ||
![]() |
2a9f86fcbc | ||
![]() |
bf06f27a0e | ||
![]() |
4e25fb1d6c | ||
![]() |
858ef9408c | ||
![]() |
1fb16e21c2 | ||
![]() |
75b4d1f563 | ||
![]() |
4fdc40228a |
21
.github/workflows/build.yaml
vendored
Normal file
21
.github/workflows/build.yaml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: Build libctru
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Test build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: 'devkitpro/devkitarm'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
run: make -C libctru
|
40
.github/workflows/doxygen.yaml
vendored
Normal file
40
.github/workflows/doxygen.yaml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
name: Build documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: [ v* ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Get current tag
|
||||||
|
id: vars
|
||||||
|
run: echo ::set-output name=tag::${GITHUB_REF/refs\/tags\//}
|
||||||
|
|
||||||
|
- name: Set up Doxygen
|
||||||
|
run: sudo apt-get install -y doxygen
|
||||||
|
|
||||||
|
- name: Display Doxygen version
|
||||||
|
run: echo "Doxygen version $(doxygen -v)"
|
||||||
|
|
||||||
|
- name: Build documentation
|
||||||
|
run: |
|
||||||
|
git clone --branch=master --single-branch --depth 1 https://github.com/devkitPro/3ds-examples examples
|
||||||
|
cd libctru
|
||||||
|
CTRU_VERSION=${{ steps.vars.outputs.tag }} doxygen Doxyfile
|
||||||
|
|
||||||
|
- name: Deploy 🚀
|
||||||
|
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||||
|
with:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
BRANCH: gh-pages
|
||||||
|
FOLDER: libctru/docs/html
|
||||||
|
CLEAN: true
|
||||||
|
SINGLE_COMMIT: true
|
41
.travis.yml
41
.travis.yml
@ -1,41 +0,0 @@
|
|||||||
language: c
|
|
||||||
|
|
||||||
os: linux
|
|
||||||
dist: bionic
|
|
||||||
|
|
||||||
services: docker
|
|
||||||
|
|
||||||
#Cache doxygen
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- /home/travis/doxygen/doxygen-1.8.18/bin
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
# Travis has an OLD doxygen build, so we fetch a recent one
|
|
||||||
- export DOXY_BINPATH=/home/travis/doxygen/doxygen-1.8.18/bin
|
|
||||||
- export PATH=$PATH:$DOXY_BINPATH
|
|
||||||
- if [ -n "$TRAVIS_TAG" ]; then bash .travis/doxyprep.sh; fi
|
|
||||||
- if [ -n "$TRAVIS_TAG" ]; then openssl aes-256-cbc -k $deploy_password -in .travis/id_travis_deploy.enc -out .travis/id_travis_deploy -d; fi
|
|
||||||
|
|
||||||
install:
|
|
||||||
- docker pull devkitpro/devkitarm
|
|
||||||
|
|
||||||
script:
|
|
||||||
- docker run -e ENABLE_COMPATIBILITY_REPORTING -v $TRAVIS_BUILD_DIR:/libctru devkitpro/devkitarm /bin/bash -ex /libctru/.travis/docker.sh
|
|
||||||
|
|
||||||
before_deploy:
|
|
||||||
- sh .travis/exportdoc.sh
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
provider: pages
|
|
||||||
deploy_key: .travis/id_travis_deploy
|
|
||||||
edge: true
|
|
||||||
keep_history: false
|
|
||||||
fqdn: libctru.devkitpro.org
|
|
||||||
local_dir: libctru/docs/html
|
|
||||||
on:
|
|
||||||
tags: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
secure: b3rLutBX7Nqp4DBh8IeBDfxcxlBsceLDhmAXcREk8jTAHPDZ+MncuuExTCxejFmZO7qnZsU4veXDa6l9DsvpY3vGHqxodICQlT5oTnIfZSrPG2GI5xVNLKske5olmR0taM0/BzdlqO8N2ML9uzSXPcbX1bdYdzVoNMuTblc44RM=
|
|
@ -1,5 +0,0 @@
|
|||||||
#!/bin/bash -ex
|
|
||||||
|
|
||||||
source /etc/profile.d/devkit-env.sh
|
|
||||||
|
|
||||||
make -C libctru/libctru
|
|
@ -1,8 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if [ ! -e "$DOXY_BINPATH/doxygen" ]; then
|
|
||||||
mkdir -p ~/doxygen && pushd ~/doxygen
|
|
||||||
wget http://doxygen.nl/files/doxygen-1.8.18.linux.bin.tar.gz
|
|
||||||
tar xzf doxygen-1.8.18.linux.bin.tar.gz
|
|
||||||
popd
|
|
||||||
fi
|
|
@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
git clone --branch=master --single-branch --depth 1 https://github.com/devkitPro/3ds-examples examples
|
|
||||||
cd libctru
|
|
||||||
doxygen Doxyfile
|
|
Binary file not shown.
94
Changelog.md
94
Changelog.md
@ -1,5 +1,99 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Version 2.4.0
|
||||||
|
|
||||||
|
- **Added full support of all QTM services**, with extensive documentation and technical details in `qtm.h` and `qtmc.h`** (breaking change); examples have been updated for this
|
||||||
|
- Added MCUHWC_SetInfoLedPattern
|
||||||
|
- Added ndspChnGetFormat
|
||||||
|
- Fixed PTMSYSM_CheckNew3DS
|
||||||
|
- Fixed NDMU_QueryStatus
|
||||||
|
- Fixed a few service commands improperly deserializing boolean output
|
||||||
|
- Fixed documentation of ACU_GetWifiStatus and ACU_SetAllowApType
|
||||||
|
- Fixed shaderInstanceInit to initialize all fields
|
||||||
|
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
|
||||||
|
|
||||||
|
## Version 2.3.0
|
||||||
|
|
||||||
|
- Fix typo in docs by @DeltaF1 in https://github.com/devkitPro/libctru/pull/525
|
||||||
|
- Add GPU_DOT3_RGBA texture combiner function by @oreo639 in https://github.com/devkitPro/libctru/pull/528
|
||||||
|
- FSUSER_GetLegacyBannerData: Fix documentation typo by @Tekito-256 in https://github.com/devkitPro/libctru/pull/526
|
||||||
|
- Add CTR_ prefix to ALIGN,PACKED,DEPRECATED macros by @glebm in https://github.com/devkitPro/libctru/pull/532
|
||||||
|
- Buffer console control sequences by @piepie62 in https://github.com/devkitPro/libctru/pull/522
|
||||||
|
- Prevent CPU from postponing threadOnException memory writes
|
||||||
|
- Add SSID-related ac:i functions
|
||||||
|
- ACI_LoadNetworkSetting, ACI_GetNetworkWirelessEssidSecuritySsid and acGetSessionHandle.
|
||||||
|
- Prevent double call of destructors on exit.
|
||||||
|
- Added experimental support for standard threading APIs (pthread, C threads, C++ std::thread)
|
||||||
|
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
|
||||||
|
|
||||||
|
## New Contributors
|
||||||
|
- @DeltaF1 made their first contribution in https://github.com/devkitPro/libctru/pull/525
|
||||||
|
- @Tekito-256 made their first contribution in https://github.com/devkitPro/libctru/pull/526
|
||||||
|
- @glebm made their first contribution in https://github.com/devkitPro/libctru/pull/532
|
||||||
|
|
||||||
|
## Version 2.2.2
|
||||||
|
|
||||||
|
- archive_dev: Ensure path separator for local path
|
||||||
|
- adjust struct hostent for compatibilty
|
||||||
|
|
||||||
|
## Version 2.2.1
|
||||||
|
|
||||||
|
- add `_SOCKLEN_T_DECLARED`
|
||||||
|
|
||||||
|
## Version 2.2.0
|
||||||
|
|
||||||
|
- apt: add deliver arg support to chainloader
|
||||||
|
|
||||||
|
## Version 2.1.2
|
||||||
|
|
||||||
|
- Added cdc:CHK service wrappers
|
||||||
|
- Added support for `clock_gettime` (#495)
|
||||||
|
- svc: Fixed svcGetDmaState writing out-of-bounds data
|
||||||
|
- svc: Changed svcCreateCodeSet address parameters to pointer-sized integers, improve documentation
|
||||||
|
- fspxi: Fixed FSPXI_CreateFile and FSPXI_WriteFile (#496)
|
||||||
|
- ndsp: Added various ndspGet\* and ndspChnGet\* methods (#505, #506, #507)
|
||||||
|
- ir: Added IRU_GetSend/RecvFinishedEvent (#513)
|
||||||
|
- errf: Added ERRF_SetUserString and clarify documentation
|
||||||
|
- mcuhwc: Added mcuHwcGetSessionHandle
|
||||||
|
- apt: Fixed dirty homebrew chainload bug (used when Home Menu hasn't been started)
|
||||||
|
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
|
||||||
|
|
||||||
|
## Version 2.1.1
|
||||||
|
|
||||||
|
- FPSCR is now initialized with a predictable value in all threads (including the main thread).
|
||||||
|
- Added gspGetSessionHandle and gspLcdGetSessionHandle.
|
||||||
|
- Fixed bugs related to uninitialized data in srv/errf service wrappers.
|
||||||
|
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
|
||||||
|
|
||||||
|
## Version 2.1.0
|
||||||
|
|
||||||
|
**The #define for the 3DS platform has been changed to `__3DS__` (previously it was `_3DS`) - please update your Makefiles and your code**
|
||||||
|
|
||||||
|
### graphics
|
||||||
|
|
||||||
|
- Refactored VRAM allocators:
|
||||||
|
- Added proper handling for VRAM banks (A and B, 3 MiB each)
|
||||||
|
- Allocations no longer cross VRAM bank boundaries
|
||||||
|
- Added vramAllocAt, vramMemAlignAt
|
||||||
|
- Add gspIsPresentPending.
|
||||||
|
- Add return value to gspPresentBuffer.
|
||||||
|
- libctru console now supports SGR 38 and 48 escape sequences (needed by fmtlib).
|
||||||
|
- Fixed GPU_TEXFACE enum.
|
||||||
|
|
||||||
|
### filesystem
|
||||||
|
|
||||||
|
- Changed rename to replace existing files, for better compatibility with POSIX (#483)
|
||||||
|
- Added SDMMC speed info types, and more clock rates (#480).
|
||||||
|
|
||||||
|
### miscellaneous
|
||||||
|
|
||||||
|
- Added support for 3dslink stdio redirection (#488).
|
||||||
|
- Added ptm:gets, ptm:sets and more ptm service commands.
|
||||||
|
- Added AM_ContentInfo, AM_ContentInfoFlags structs.
|
||||||
|
- Added AMAPP_GetDLCContentInfoCount, AMAPP_ListDLCContentInfos.
|
||||||
|
- Fixed bug in Huffman decoder.
|
||||||
|
- Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
|
||||||
|
|
||||||
## Version 2.0.1
|
## Version 2.0.1
|
||||||
|
|
||||||
- Added CFG_SystemModel enum.
|
- Added CFG_SystemModel enum.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# libctru - CTR User Library
|
# libctru - CTR User Library
|
||||||
|
|
||||||
[](https://travis-ci.org/devkitPro/libctru)
|

|
||||||
|
|
||||||
Library for writing user mode ARM11 code for the 3DS (CTR)
|
Library for writing user mode ARM11 code for the 3DS (CTR)
|
||||||
|
|
||||||
|
1
libctru/.gitignore
vendored
1
libctru/.gitignore
vendored
@ -7,3 +7,4 @@ docs
|
|||||||
internal_docs
|
internal_docs
|
||||||
*.bz2
|
*.bz2
|
||||||
.*/
|
.*/
|
||||||
|
*.tag
|
||||||
|
@ -38,7 +38,7 @@ PROJECT_NAME = "libctru"
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = "$(TRAVIS_TAG)"
|
PROJECT_NUMBER = "$(CTRU_VERSION)"
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
@ -2023,7 +2023,7 @@ INCLUDE_FILE_PATTERNS =
|
|||||||
# recursively expanded use the := operator instead of the = operator.
|
# recursively expanded use the := operator instead of the = operator.
|
||||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||||
|
|
||||||
PREDEFINED = PACKED
|
PREDEFINED = CTR_PACKED
|
||||||
|
|
||||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||||
# tag can be used to specify a list of macro names that should be expanded. The
|
# tag can be used to specify a list of macro names that should be expanded. The
|
||||||
@ -2067,7 +2067,7 @@ TAGFILES =
|
|||||||
# tag file that is based on the input files it reads. See section "Linking to
|
# tag file that is based on the input files it reads. See section "Linking to
|
||||||
# external documentation" for more information about the usage of tag files.
|
# external documentation" for more information about the usage of tag files.
|
||||||
|
|
||||||
GENERATE_TAGFILE =
|
GENERATE_TAGFILE = libctru.tag
|
||||||
|
|
||||||
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
|
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
|
||||||
# the class index. If set to NO, only the inherited external classes will be
|
# the class index. If set to NO, only the inherited external classes will be
|
||||||
|
@ -9,8 +9,8 @@ endif
|
|||||||
include $(DEVKITARM)/base_rules
|
include $(DEVKITARM)/base_rules
|
||||||
|
|
||||||
export LIBCTRU_MAJOR := 2
|
export LIBCTRU_MAJOR := 2
|
||||||
export LIBCTRU_MINOR := 0
|
export LIBCTRU_MINOR := 4
|
||||||
export LIBCTRU_PATCH := 1
|
export LIBCTRU_PATCH := 0
|
||||||
|
|
||||||
|
|
||||||
VERSION := $(LIBCTRU_MAJOR).$(LIBCTRU_MINOR).$(LIBCTRU_PATCH)
|
VERSION := $(LIBCTRU_MAJOR).$(LIBCTRU_MINOR).$(LIBCTRU_PATCH)
|
||||||
@ -50,7 +50,7 @@ CFLAGS := -g -Wall -Werror -mword-relocations \
|
|||||||
$(ARCH) \
|
$(ARCH) \
|
||||||
$(BUILD_CFLAGS)
|
$(BUILD_CFLAGS)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
|
CFLAGS += $(INCLUDE) -D__3DS__
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ lib/libctrud.a : lib debug $(SOURCES) $(INCLUDES)
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
clean:
|
clean:
|
||||||
@echo clean ...
|
@echo clean ...
|
||||||
@rm -fr release debug lib docs
|
@rm -fr release debug lib docs libctru.tag
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
else
|
else
|
||||||
|
@ -8,6 +8,11 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(_3DS) && !defined(__3DS__)
|
||||||
|
#warning "Please update your Makefile and replace -DARM11 -D_3DS with -D__3DS__"
|
||||||
|
#define __3DS__
|
||||||
|
#endif
|
||||||
|
|
||||||
//might be missing some
|
//might be missing some
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/result.h>
|
#include <3ds/result.h>
|
||||||
@ -60,6 +65,8 @@ extern "C" {
|
|||||||
#include <3ds/services/ps.h>
|
#include <3ds/services/ps.h>
|
||||||
#include <3ds/services/ptmu.h>
|
#include <3ds/services/ptmu.h>
|
||||||
#include <3ds/services/ptmsysm.h>
|
#include <3ds/services/ptmsysm.h>
|
||||||
|
#include <3ds/services/ptmgets.h>
|
||||||
|
#include <3ds/services/ptmsets.h>
|
||||||
#include <3ds/services/pxidev.h>
|
#include <3ds/services/pxidev.h>
|
||||||
#include <3ds/services/pxipm.h>
|
#include <3ds/services/pxipm.h>
|
||||||
#include <3ds/services/soc.h>
|
#include <3ds/services/soc.h>
|
||||||
@ -68,10 +75,12 @@ extern "C" {
|
|||||||
#include <3ds/services/nfc.h>
|
#include <3ds/services/nfc.h>
|
||||||
#include <3ds/services/news.h>
|
#include <3ds/services/news.h>
|
||||||
#include <3ds/services/qtm.h>
|
#include <3ds/services/qtm.h>
|
||||||
|
#include <3ds/services/qtmc.h>
|
||||||
#include <3ds/services/srvpm.h>
|
#include <3ds/services/srvpm.h>
|
||||||
#include <3ds/services/loader.h>
|
#include <3ds/services/loader.h>
|
||||||
#include <3ds/services/y2r.h>
|
#include <3ds/services/y2r.h>
|
||||||
#include <3ds/services/mcuhwc.h>
|
#include <3ds/services/mcuhwc.h>
|
||||||
|
#include <3ds/services/cdcchk.h>
|
||||||
|
|
||||||
#include <3ds/gpu/gx.h>
|
#include <3ds/gpu/gx.h>
|
||||||
#include <3ds/gpu/gpu.h>
|
#include <3ds/gpu/gpu.h>
|
||||||
@ -92,6 +101,7 @@ extern "C" {
|
|||||||
#include <3ds/mii.h>
|
#include <3ds/mii.h>
|
||||||
|
|
||||||
#include <3ds/gdbhio_dev.h>
|
#include <3ds/gdbhio_dev.h>
|
||||||
|
#include <3ds/3dslink.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
34
libctru/include/3ds/3dslink.h
Normal file
34
libctru/include/3ds/3dslink.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @file 3dslink.h
|
||||||
|
* @brief Netloader (3dslink) utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct in_addr;
|
||||||
|
|
||||||
|
/// Address of the host connected through 3dslink
|
||||||
|
extern struct in_addr __3dslink_host;
|
||||||
|
|
||||||
|
#define LINK3DS_COMM_PORT 17491 ///< 3dslink TCP server port
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connects to the 3dslink host, setting up an output stream.
|
||||||
|
* @param[in] redirStdout Whether to redirect stdout to nxlink output.
|
||||||
|
* @param[in] redirStderr Whether to redirect stderr to nxlink output.
|
||||||
|
* @return Socket fd on success, negative number on failure.
|
||||||
|
* @note The socket should be closed with close() during application cleanup.
|
||||||
|
*/
|
||||||
|
int link3dsConnectToHost(bool redirStdout, bool redirStderr);
|
||||||
|
|
||||||
|
/// Same as \ref link3dsConnectToHost but redirecting both stdout/stderr.
|
||||||
|
static inline int link3dsStdio(void) {
|
||||||
|
return link3dsConnectToHost(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as \ref link3dsConnectToHost but redirecting only stderr.
|
||||||
|
static inline int link3dsStdioForDebug(void) {
|
||||||
|
return link3dsConnectToHost(false, true);
|
||||||
|
}
|
@ -4,6 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Allocates a 0x80-byte aligned buffer.
|
* @brief Allocates a 0x80-byte aligned buffer.
|
||||||
* @param size Size of the buffer to allocate.
|
* @param size Size of the buffer to allocate.
|
||||||
|
@ -4,6 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum vramAllocPos
|
||||||
|
{
|
||||||
|
VRAM_ALLOC_A = BIT(0),
|
||||||
|
VRAM_ALLOC_B = BIT(1),
|
||||||
|
VRAM_ALLOC_ANY = VRAM_ALLOC_A | VRAM_ALLOC_B,
|
||||||
|
} vramAllocPos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Allocates a 0x80-byte aligned buffer.
|
* @brief Allocates a 0x80-byte aligned buffer.
|
||||||
* @param size Size of the buffer to allocate.
|
* @param size Size of the buffer to allocate.
|
||||||
@ -11,6 +18,14 @@
|
|||||||
*/
|
*/
|
||||||
void* vramAlloc(size_t size);
|
void* vramAlloc(size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocates a 0x80-byte aligned buffer in the given VRAM bank.
|
||||||
|
* @param size Size of the buffer to allocate.
|
||||||
|
* @param pos VRAM bank to use (see \ref vramAllocPos).
|
||||||
|
* @return The allocated buffer.
|
||||||
|
*/
|
||||||
|
void* vramAllocAt(size_t size, vramAllocPos pos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Allocates a buffer aligned to the given size.
|
* @brief Allocates a buffer aligned to the given size.
|
||||||
* @param size Size of the buffer to allocate.
|
* @param size Size of the buffer to allocate.
|
||||||
@ -19,6 +34,15 @@ void* vramAlloc(size_t size);
|
|||||||
*/
|
*/
|
||||||
void* vramMemAlign(size_t size, size_t alignment);
|
void* vramMemAlign(size_t size, size_t alignment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocates a buffer aligned to the given size in the given VRAM bank.
|
||||||
|
* @param size Size of the buffer to allocate.
|
||||||
|
* @param alignment Alignment to use.
|
||||||
|
* @param pos VRAM bank to use (see \ref vramAllocPos).
|
||||||
|
* @return The allocated buffer.
|
||||||
|
*/
|
||||||
|
void* vramMemAlignAt(size_t size, size_t alignment, vramAllocPos pos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reallocates a buffer.
|
* @brief Reallocates a buffer.
|
||||||
* Note: Not implemented yet.
|
* Note: Not implemented yet.
|
||||||
|
@ -94,8 +94,8 @@ typedef struct PrintConsole
|
|||||||
int windowHeight; ///< Window height in characters (not implemented)
|
int windowHeight; ///< Window height in characters (not implemented)
|
||||||
|
|
||||||
int tabSize; ///< Size of a tab
|
int tabSize; ///< Size of a tab
|
||||||
int fg; ///< Foreground color
|
u16 fg; ///< Foreground color
|
||||||
int bg; ///< Background color
|
u16 bg; ///< Background color
|
||||||
int flags; ///< Reverse/bright flags
|
int flags; ///< Reverse/bright flags
|
||||||
|
|
||||||
ConsolePrint PrintChar; ///< Callback for printing a character. Should return true if it has handled rendering the graphics (else the print engine will attempt to render via tiles).
|
ConsolePrint PrintChar; ///< Callback for printing a character. Should return true if it has handled rendering the graphics (else the print engine will attempt to render via tiles).
|
||||||
@ -112,6 +112,8 @@ typedef struct PrintConsole
|
|||||||
#define CONSOLE_COLOR_REVERSE (1<<6) ///< Reversed color text
|
#define CONSOLE_COLOR_REVERSE (1<<6) ///< Reversed color text
|
||||||
#define CONSOLE_CONCEAL (1<<7) ///< Concealed text
|
#define CONSOLE_CONCEAL (1<<7) ///< Concealed text
|
||||||
#define CONSOLE_CROSSED_OUT (1<<8) ///< Crossed out text
|
#define CONSOLE_CROSSED_OUT (1<<8) ///< Crossed out text
|
||||||
|
#define CONSOLE_FG_CUSTOM (1<<9) ///< Foreground custom color
|
||||||
|
#define CONSOLE_BG_CUSTOM (1<<10) ///< Background custom color
|
||||||
|
|
||||||
/// Console debug devices supported by libnds.
|
/// Console debug devices supported by libnds.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -9,12 +9,12 @@
|
|||||||
|
|
||||||
/// Types of errors that can be thrown by err:f.
|
/// Types of errors that can be thrown by err:f.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ERRF_ERRTYPE_GENERIC = 0, ///< For generic errors. Shows miscellaneous info.
|
ERRF_ERRTYPE_GENERIC = 0, ///< Generic fatal error. Shows miscellaneous info, including the address of the caller
|
||||||
ERRF_ERRTYPE_MEM_CORRUPT = 1, ///< Same output as generic, but informs the user that "the System Memory has been damaged".
|
ERRF_ERRTYPE_NAND_DAMAGED = 1, ///< Damaged NAND (CC_ERROR after reading CSR)
|
||||||
ERRF_ERRTYPE_CARD_REMOVED = 2, ///< Displays the "The Game Card was removed." message.
|
ERRF_ERRTYPE_CARD_REMOVED = 2, ///< Game content storage medium (cartridge and/or SD card) ejected. Not logged
|
||||||
ERRF_ERRTYPE_EXCEPTION = 3, ///< For exceptions, or more specifically 'crashes'. union data should be exception_data.
|
ERRF_ERRTYPE_EXCEPTION = 3, ///< CPU or VFP exception
|
||||||
ERRF_ERRTYPE_FAILURE = 4, ///< For general failure. Shows a message. union data should have a string set in failure_mesg
|
ERRF_ERRTYPE_FAILURE = 4, ///< Fatal error with a message instead of the caller's address
|
||||||
ERRF_ERRTYPE_LOGGED = 5, ///< Outputs logs to NAND in some cases.
|
ERRF_ERRTYPE_LOG_ONLY = 5, ///< Log-level failure. Does not display the exception and does not force the system to reboot
|
||||||
} ERRF_ErrType;
|
} ERRF_ErrType;
|
||||||
|
|
||||||
/// Types of 'Exceptions' thrown for ERRF_ERRTYPE_EXCEPTION
|
/// Types of 'Exceptions' thrown for ERRF_ERRTYPE_EXCEPTION
|
||||||
@ -46,12 +46,12 @@ typedef struct {
|
|||||||
u16 revLow; ///< Low revision ID
|
u16 revLow; ///< Low revision ID
|
||||||
u32 resCode; ///< Result code
|
u32 resCode; ///< Result code
|
||||||
u32 pcAddr; ///< PC address at exception
|
u32 pcAddr; ///< PC address at exception
|
||||||
u32 procId; ///< Process ID.
|
u32 procId; ///< Process ID of the caller
|
||||||
u64 titleId; ///< Title ID.
|
u64 titleId; ///< Title ID of the caller
|
||||||
u64 appTitleId; ///< Application Title ID.
|
u64 appTitleId; ///< Title ID of the running application
|
||||||
union {
|
union {
|
||||||
ERRF_ExceptionData exception_data; ///< Data for when type is ERRF_ERRTYPE_EXCEPTION
|
ERRF_ExceptionData exception_data; ///< Data for when type is ERRF_ERRTYPE_EXCEPTION
|
||||||
char failure_mesg[0x60]; ///< String for when type is ERRF_ERRTYPE_FAILURE
|
char failure_mesg[0x60]; ///< String for when type is ERRF_ERRTYPE_FAILURE
|
||||||
} data; ///< The different types of data for errors.
|
} data; ///< The different types of data for errors.
|
||||||
} ERRF_FatalErrInfo;
|
} ERRF_FatalErrInfo;
|
||||||
|
|
||||||
@ -68,13 +68,19 @@ void errfExit(void);
|
|||||||
Handle *errfGetSessionHandle(void);
|
Handle *errfGetSessionHandle(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Throws a system error and possibly results in ErrDisp triggering.
|
* @brief Throws a system error and possibly logs it.
|
||||||
* @param[in] error Error to throw.
|
* @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
|
* ErrDisp may convert the error info to \ref ERRF_ERRTYPE_NAND_DAMAGED or \ref ERRF_ERRTYPE_CARD_REMOVED
|
||||||
* top screen with a developer console or the proper patches in a CFW applied.
|
* depending on the error code.
|
||||||
*
|
*
|
||||||
* The error may not be shown and execution aborted until errfExit(void) is called.
|
* Except with \ref ERRF_ERRTYPE_LOG_ONLY, the system will panic and will need to be rebooted.
|
||||||
|
* Fatal error information will also be logged into a file, unless the type either \ref ERRF_ERRTYPE_NAND_DAMAGED
|
||||||
|
* or \ref ERRF_ERRTYPE_CARD_REMOVED.
|
||||||
|
*
|
||||||
|
* No error will be shown if the system is asleep.
|
||||||
|
*
|
||||||
|
* On retail units with vanilla firmware, no detailed information will be displayed on screen.
|
||||||
*
|
*
|
||||||
* You may wish to use ERRF_ThrowResult() or ERRF_ThrowResultWithMessage() instead of
|
* You may wish to use ERRF_ThrowResult() or ERRF_ThrowResultWithMessage() instead of
|
||||||
* constructing the ERRF_FatalErrInfo struct yourself.
|
* constructing the ERRF_FatalErrInfo struct yourself.
|
||||||
@ -82,35 +88,44 @@ Handle *errfGetSessionHandle(void);
|
|||||||
Result ERRF_Throw(const ERRF_FatalErrInfo* error);
|
Result ERRF_Throw(const ERRF_FatalErrInfo* error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Throws a system error with the given Result code.
|
* @brief Throws (and logs) a system error with the given Result code.
|
||||||
* @param[in] failure Result code to throw.
|
* @param[in] failure Result code to throw.
|
||||||
*
|
*
|
||||||
* This calls ERRF_Throw() with error type ERRF_ERRTYPE_GENERIC and fills in the required data.
|
* This calls \ref ERRF_Throw with error type \ref ERRF_ERRTYPE_GENERIC and fills in the required data.
|
||||||
*
|
*
|
||||||
* This function \em does fill in the address where this function was called from.
|
* This function \em does fill in the address where this function was called from.
|
||||||
*
|
|
||||||
* See https://3dbrew.org/wiki/ERR:Throw#Generic for expected top screen output
|
|
||||||
* on development units/patched ErrDisp.
|
|
||||||
*/
|
*/
|
||||||
Result ERRF_ThrowResult(Result failure);
|
Result ERRF_ThrowResult(Result failure);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Logs a system error with the given Result code.
|
||||||
|
* @param[in] failure Result code to log.
|
||||||
|
*
|
||||||
|
* Similar to \ref ERRF_Throw, except that it does not display anything on the screen,
|
||||||
|
* nor does it force the system to reboot.
|
||||||
|
*
|
||||||
|
* This function \em does fill in the address where this function was called from.
|
||||||
|
*/
|
||||||
|
Result ERRF_LogResult(Result failure);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Throws a system error with the given Result code and message.
|
* @brief Throws a system error with the given Result code and message.
|
||||||
* @param[in] failure Result code to throw.
|
* @param[in] failure Result code to throw.
|
||||||
* @param[in] message The message to display.
|
* @param[in] message The message to display.
|
||||||
*
|
*
|
||||||
* This calls ERRF_Throw() with error type ERRF_ERRTYPE_FAILURE and fills in the required data.
|
* This calls \ref ERRF_Throw with error type \ref ERRF_ERRTYPE_FAILURE and fills in the required data.
|
||||||
*
|
*
|
||||||
* This function does \em not fill in the address where this function was called from because it
|
* This function does \em not fill in the address where this function was called from because it
|
||||||
* would not be displayed.
|
* would not be displayed.
|
||||||
*
|
|
||||||
* The message is only displayed on development units/patched ErrDisp.
|
|
||||||
*
|
|
||||||
* See https://3dbrew.org/wiki/ERR:Throw#Result_Failure for expected top screen output
|
|
||||||
* on development units/patched ErrDisp.
|
|
||||||
*/
|
*/
|
||||||
Result ERRF_ThrowResultWithMessage(Result failure, const char* message);
|
Result ERRF_ThrowResultWithMessage(Result failure, const char* message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Specify an additional user string to use for error reporting.
|
||||||
|
* @param[in] user_string User string (up to 256 bytes, not including NUL byte)
|
||||||
|
*/
|
||||||
|
Result ERRF_SetUserString(const char* user_string);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handles an exception using ErrDisp.
|
* @brief Handles an exception using ErrDisp.
|
||||||
* @param excep Exception information
|
* @param excep Exception information
|
||||||
|
@ -166,7 +166,7 @@ void gfxScreenSwapBuffers(gfxScreen_t scr, bool hasStereo);
|
|||||||
* @param immediate This parameter no longer has any effect and is thus ignored.
|
* @param immediate This parameter no longer has any effect and is thus ignored.
|
||||||
* @deprecated This function has been superseded by \ref gfxScreenSwapBuffers, please use that instead.
|
* @deprecated This function has been superseded by \ref gfxScreenSwapBuffers, please use that instead.
|
||||||
*/
|
*/
|
||||||
DEPRECATED void gfxConfigScreen(gfxScreen_t scr, bool immediate);
|
CTR_DEPRECATED void gfxConfigScreen(gfxScreen_t scr, bool immediate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Updates the configuration of both screens.
|
* @brief Updates the configuration of both screens.
|
||||||
|
@ -78,16 +78,16 @@ typedef enum
|
|||||||
GPU_ETC1A4 = 0xD, ///< ETC1 texture compression + 4-bit Alpha
|
GPU_ETC1A4 = 0xD, ///< ETC1 texture compression + 4-bit Alpha
|
||||||
} GPU_TEXCOLOR;
|
} GPU_TEXCOLOR;
|
||||||
|
|
||||||
// Texture faces.
|
/// Texture faces.
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
GPU_TEXFACE_2D = 0, // 2D face
|
GPU_TEXFACE_2D = 0, ///< 2D face
|
||||||
GPU_POSITIVE_X = 0, // +X face
|
GPU_POSITIVE_X = 0, ///< +X face
|
||||||
GPU_NEGATIVE_X = 1, // -X face
|
GPU_NEGATIVE_X = 1, ///< -X face
|
||||||
GPU_POSITIVE_Y = 2, // +Y face
|
GPU_POSITIVE_Y = 2, ///< +Y face
|
||||||
GPU_NEGATIVE_Y = 3, // -Y face
|
GPU_NEGATIVE_Y = 3, ///< -Y face
|
||||||
GPU_POSITIVE_Z = 4, // +Z face
|
GPU_POSITIVE_Z = 4, ///< +Z face
|
||||||
GPU_NEGATIVE_Z = 5, // -Z face
|
GPU_NEGATIVE_Z = 5, ///< -Z face
|
||||||
} GPU_TEXFACE;
|
} GPU_TEXFACE;
|
||||||
|
|
||||||
/// Procedural texture clamp modes.
|
/// Procedural texture clamp modes.
|
||||||
@ -368,7 +368,8 @@ typedef enum
|
|||||||
GPU_ADD_SIGNED = 0x03, ///< Signed add.
|
GPU_ADD_SIGNED = 0x03, ///< Signed add.
|
||||||
GPU_INTERPOLATE = 0x04, ///< Interpolate.
|
GPU_INTERPOLATE = 0x04, ///< Interpolate.
|
||||||
GPU_SUBTRACT = 0x05, ///< Subtract.
|
GPU_SUBTRACT = 0x05, ///< Subtract.
|
||||||
GPU_DOT3_RGB = 0x06, ///< Dot3. RGB only.
|
GPU_DOT3_RGB = 0x06, ///< Dot3. Scalar result is written to RGB only.
|
||||||
|
GPU_DOT3_RGBA = 0x07, ///< Dot3. Scalar result is written to RGBA.
|
||||||
GPU_MULTIPLY_ADD = 0x08, ///< Multiply then add.
|
GPU_MULTIPLY_ADD = 0x08, ///< Multiply then add.
|
||||||
GPU_ADD_MULTIPLY = 0x09, ///< Add then multiply.
|
GPU_ADD_MULTIPLY = 0x09, ///< Add then multiply.
|
||||||
} GPU_COMBINEFUNC;
|
} GPU_COMBINEFUNC;
|
||||||
|
@ -70,7 +70,7 @@ static inline u32 IPC_Desc_CurProcessId(void)
|
|||||||
return 0x20;
|
return 0x20;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline DEPRECATED u32 IPC_Desc_CurProcessHandle(void)
|
static inline CTR_DEPRECATED u32 IPC_Desc_CurProcessHandle(void)
|
||||||
{
|
{
|
||||||
return IPC_Desc_CurProcessId();
|
return IPC_Desc_CurProcessId();
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ typedef struct
|
|||||||
} glasses_details;
|
} glasses_details;
|
||||||
|
|
||||||
/// Mole details
|
/// Mole details
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
bool enable : 1;
|
bool enable : 1;
|
||||||
u16 scale : 5;
|
u16 scale : 5;
|
||||||
@ -156,4 +156,4 @@ typedef struct
|
|||||||
} mole_details;
|
} mole_details;
|
||||||
|
|
||||||
u16 author_name[10]; ///< Name of Mii's author (Encoded using UTF16)
|
u16 author_name[10]; ///< Name of Mii's author (Encoded using UTF16)
|
||||||
} PACKED MiiData;
|
} CTR_PACKED MiiData;
|
||||||
|
@ -107,6 +107,14 @@ void ndspChnSetPaused(int id, bool paused);
|
|||||||
*/
|
*/
|
||||||
void ndspChnSetFormat(int id, u16 format);
|
void ndspChnSetFormat(int id, u16 format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @brief Gets the format of a channel.
|
||||||
|
* @param id ID of the channel (0..23).
|
||||||
|
* @return The format of the channel.
|
||||||
|
*/
|
||||||
|
u16 ndspChnGetFormat(int id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the interpolation type of a channel.
|
* @brief Sets the interpolation type of a channel.
|
||||||
* @param id ID of the channel (0..23).
|
* @param id ID of the channel (0..23).
|
||||||
@ -114,6 +122,13 @@ void ndspChnSetFormat(int id, u16 format);
|
|||||||
*/
|
*/
|
||||||
void ndspChnSetInterp(int id, ndspInterpType type);
|
void ndspChnSetInterp(int id, ndspInterpType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the interpolation type of a channel.
|
||||||
|
* @param id ID of the channel (0..23).
|
||||||
|
* @return The interpolation type of the channel.
|
||||||
|
*/
|
||||||
|
ndspInterpType ndspChnGetInterp(int id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the sample rate of a channel.
|
* @brief Sets the sample rate of a channel.
|
||||||
* @param id ID of the channel (0..23).
|
* @param id ID of the channel (0..23).
|
||||||
@ -121,6 +136,13 @@ void ndspChnSetInterp(int id, ndspInterpType type);
|
|||||||
*/
|
*/
|
||||||
void ndspChnSetRate(int id, float rate);
|
void ndspChnSetRate(int id, float rate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the sample rate of a channel.
|
||||||
|
* @param id ID of the channel (0..23).
|
||||||
|
* @return The sample rate of the channel.
|
||||||
|
*/
|
||||||
|
float ndspChnGetRate(int id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the mix parameters (volumes) of a channel.
|
* @brief Sets the mix parameters (volumes) of a channel.
|
||||||
* @param id ID of the channel (0..23).
|
* @param id ID of the channel (0..23).
|
||||||
@ -134,6 +156,13 @@ void ndspChnSetRate(int id, float rate);
|
|||||||
*/
|
*/
|
||||||
void ndspChnSetMix(int id, float mix[12]);
|
void ndspChnSetMix(int id, float mix[12]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the mix parameters (volumes) of a channel.
|
||||||
|
* @param id ID of the channel (0..23)
|
||||||
|
* @param mix Mix parameters to write out to. See \ref ndspChnSetMix.
|
||||||
|
*/
|
||||||
|
void ndspChnGetMix(int id, float mix[12]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the DSPADPCM coefficients of a channel.
|
* @brief Sets the DSPADPCM coefficients of a channel.
|
||||||
* @param id ID of the channel (0..23).
|
* @param id ID of the channel (0..23).
|
||||||
|
@ -118,24 +118,48 @@ u32 ndspGetFrameCount(void);
|
|||||||
*/
|
*/
|
||||||
void ndspSetMasterVol(float volume);
|
void ndspSetMasterVol(float volume);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the master volume.
|
||||||
|
* @return The master volume.
|
||||||
|
*/
|
||||||
|
float ndspGetMasterVol(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the output mode.
|
* @brief Sets the output mode.
|
||||||
* @param mode Output mode to set. Defaults to NDSP_OUTPUT_STEREO.
|
* @param mode Output mode to set. Defaults to NDSP_OUTPUT_STEREO.
|
||||||
*/
|
*/
|
||||||
void ndspSetOutputMode(ndspOutputMode mode);
|
void ndspSetOutputMode(ndspOutputMode mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the output mode.
|
||||||
|
* @return The output mode.
|
||||||
|
*/
|
||||||
|
ndspOutputMode ndspGetOutputMode(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the clipping mode.
|
* @brief Sets the clipping mode.
|
||||||
* @param mode Clipping mode to set. Defaults to NDSP_CLIP_SOFT.
|
* @param mode Clipping mode to set. Defaults to NDSP_CLIP_SOFT.
|
||||||
*/
|
*/
|
||||||
void ndspSetClippingMode(ndspClippingMode mode);
|
void ndspSetClippingMode(ndspClippingMode mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the clipping mode.
|
||||||
|
* @return The clipping mode.
|
||||||
|
*/
|
||||||
|
ndspClippingMode ndspGetClippingMode(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the output count.
|
* @brief Sets the output count.
|
||||||
* @param count Output count to set. Defaults to 2.
|
* @param count Output count to set. Defaults to 2.
|
||||||
*/
|
*/
|
||||||
void ndspSetOutputCount(int count);
|
void ndspSetOutputCount(int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the output count.
|
||||||
|
* @return The output count.
|
||||||
|
*/
|
||||||
|
int ndspGetOutputCount(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the wave buffer to capture audio to.
|
* @brief Sets the wave buffer to capture audio to.
|
||||||
* @param capture Wave buffer to capture to.
|
* @param capture Wave buffer to capture to.
|
||||||
@ -158,17 +182,35 @@ void ndspSetCallback(ndspCallback callback, void* data);
|
|||||||
*/
|
*/
|
||||||
void ndspSurroundSetDepth(u16 depth);
|
void ndspSurroundSetDepth(u16 depth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the surround sound depth.
|
||||||
|
* @return The surround sound depth.
|
||||||
|
*/
|
||||||
|
u16 ndspSurroundGetDepth(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the surround sound position.
|
* @brief Sets the surround sound position.
|
||||||
* @param pos Position to set. Defaults to NDSP_SPKPOS_SQUARE.
|
* @param pos Position to set. Defaults to NDSP_SPKPOS_SQUARE.
|
||||||
*/
|
*/
|
||||||
void ndspSurroundSetPos(ndspSpeakerPos pos);
|
void ndspSurroundSetPos(ndspSpeakerPos pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the surround sound position.
|
||||||
|
* @return The surround sound speaker position.
|
||||||
|
*/
|
||||||
|
ndspSpeakerPos ndspSurroundGetPos(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the surround sound rear ratio.
|
* @brief Sets the surround sound rear ratio.
|
||||||
* @param ratio Rear ratio to set. Defaults to 0x8000.
|
* @param ratio Rear ratio to set. Defaults to 0x8000.
|
||||||
*/
|
*/
|
||||||
void ndspSurroundSetRearRatio(u16 ratio);
|
void ndspSurroundSetRearRatio(u16 ratio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the surround sound rear ratio.
|
||||||
|
* @return The rear ratio.
|
||||||
|
*/
|
||||||
|
u16 ndspSurroundGetRearRatio(void);
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
///@name Auxiliary output
|
///@name Auxiliary output
|
||||||
@ -180,6 +222,13 @@ void ndspSurroundSetRearRatio(u16 ratio);
|
|||||||
*/
|
*/
|
||||||
void ndspAuxSetEnable(int id, bool enable);
|
void ndspAuxSetEnable(int id, bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets whether auxiliary output is enabled.
|
||||||
|
* @param id ID of the auxiliary output.
|
||||||
|
* @return Whether auxiliary output is enabled.
|
||||||
|
*/
|
||||||
|
bool ndspAuxIsEnabled(int id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Configures whether an auxiliary output should use front bypass.
|
* @brief Configures whether an auxiliary output should use front bypass.
|
||||||
* @param id ID of the auxiliary output.
|
* @param id ID of the auxiliary output.
|
||||||
@ -187,6 +236,13 @@ void ndspAuxSetEnable(int id, bool enable);
|
|||||||
*/
|
*/
|
||||||
void ndspAuxSetFrontBypass(int id, bool bypass);
|
void ndspAuxSetFrontBypass(int id, bool bypass);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets whether auxiliary output front bypass is enabled.
|
||||||
|
* @param id ID of the auxiliary output.
|
||||||
|
* @return Whether auxiliary output front bypass is enabled.
|
||||||
|
*/
|
||||||
|
bool ndspAuxGetFrontBypass(int id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the volume of an auxiliary output.
|
* @brief Sets the volume of an auxiliary output.
|
||||||
* @param id ID of the auxiliary output.
|
* @param id ID of the auxiliary output.
|
||||||
@ -194,6 +250,13 @@ void ndspAuxSetFrontBypass(int id, bool bypass);
|
|||||||
*/
|
*/
|
||||||
void ndspAuxSetVolume(int id, float volume);
|
void ndspAuxSetVolume(int id, float volume);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the volume of an auxiliary output.
|
||||||
|
* @param id ID of the auxiliary output.
|
||||||
|
* @return Volume of the auxiliary output.
|
||||||
|
*/
|
||||||
|
float ndspAuxGetVolume(int id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the callback of an auxiliary output.
|
* @brief Sets the callback of an auxiliary output.
|
||||||
* @param id ID of the auxiliary output.
|
* @param id ID of the auxiliary output.
|
||||||
|
@ -5,10 +5,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
#define SYSCLOCK_SOC (16756991)
|
///< The external clock rate for the SoC.
|
||||||
#define SYSCLOCK_ARM9 (SYSCLOCK_SOC * 8)
|
#define SYSCLOCK_SOC (16756991u)
|
||||||
#define SYSCLOCK_ARM11 (SYSCLOCK_ARM9 * 2)
|
///< The base system clock rate (for I2C, NDMA, etc.).
|
||||||
#define SYSCLOCK_ARM11_NEW (SYSCLOCK_ARM11 * 3)
|
#define SYSCLOCK_SYS (SYSCLOCK_SOC * 2)
|
||||||
|
///< The base clock rate for the SDMMC controller (and some other peripherals).
|
||||||
|
#define SYSCLOCK_SDMMC (SYSCLOCK_SYS * 2)
|
||||||
|
///< The clock rate for the Arm9.
|
||||||
|
#define SYSCLOCK_ARM9 (SYSCLOCK_SYS * 4)
|
||||||
|
///< The clock rate for the Arm11 in CTR mode and in \ref svcGetSystemTick.
|
||||||
|
#define SYSCLOCK_ARM11 (SYSCLOCK_ARM9 * 2)
|
||||||
|
///< The clock rate for the Arm11 in LGR1 mode.
|
||||||
|
#define SYSCLOCK_ARM11_LGR1 (SYSCLOCK_ARM11 * 2)
|
||||||
|
///< The clock rate for the Arm11 in LGR2 mode.
|
||||||
|
#define SYSCLOCK_ARM11_LGR2 (SYSCLOCK_ARM11 * 3)
|
||||||
|
///< The highest possible clock rate for the Arm11 on known New 3DS units.
|
||||||
|
#define SYSCLOCK_ARM11_NEW SYSCLOCK_ARM11_LGR2
|
||||||
|
|
||||||
#define CPU_TICKS_PER_MSEC (SYSCLOCK_ARM11 / 1000.0)
|
#define CPU_TICKS_PER_MSEC (SYSCLOCK_ARM11 / 1000.0)
|
||||||
#define CPU_TICKS_PER_USEC (SYSCLOCK_ARM11 / 1000000.0)
|
#define CPU_TICKS_PER_USEC (SYSCLOCK_ARM11 / 1000000.0)
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
/// Wifi security modes.
|
/// Wifi security modes.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
AC_OPEN = 0, ///< Open authentication.
|
AC_OPEN = 0, ///< Open authentication.
|
||||||
@ -16,6 +18,16 @@ typedef enum {
|
|||||||
AC_WPA2_AES = 7, ///< WPA2 AES authentication.
|
AC_WPA2_AES = 7, ///< WPA2 AES authentication.
|
||||||
} acSecurityMode;
|
} acSecurityMode;
|
||||||
|
|
||||||
|
/// Wifi access point types (bitfield).
|
||||||
|
enum {
|
||||||
|
AC_AP_TYPE_NONE = 0, ///< No access point/none allowed.
|
||||||
|
AC_AP_TYPE_SLOT1 = BIT(1), ///< Slot 1 in System Settings.
|
||||||
|
AC_AP_TYPE_SLOT2 = BIT(2), ///< Slot 2 in System Settings.
|
||||||
|
AC_AP_TYPE_SLOT3 = BIT(3), ///< Slot 3 in System Settings.
|
||||||
|
|
||||||
|
AC_AP_TYPE_ALL = 0x7FFFFFFF, ///< All access point types allowed.
|
||||||
|
};
|
||||||
|
|
||||||
/// Struct to contain the data for connecting to a Wifi network from a stored slot.
|
/// Struct to contain the data for connecting to a Wifi network from a stored slot.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u8 reserved[0x200];
|
u8 reserved[0x200];
|
||||||
@ -27,12 +39,15 @@ Result acInit(void);
|
|||||||
/// Exits AC.
|
/// Exits AC.
|
||||||
void acExit(void);
|
void acExit(void);
|
||||||
|
|
||||||
|
/// Gets the current AC session handle.
|
||||||
|
Handle *acGetSessionHandle(void);
|
||||||
|
|
||||||
/// Waits for the system to connect to the internet.
|
/// Waits for the system to connect to the internet.
|
||||||
Result acWaitInternetConnection(void);
|
Result acWaitInternetConnection(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the connected Wifi status.
|
* @brief Describes the access point the console is currently connected to with AC_AP_TYPE_* flags.
|
||||||
* @param out Pointer to output the connected Wifi status to. (0 = not connected, 1 = O3DS Internet, 2 = N3DS Internet)
|
* @param out Pointer to output the combination of AC_AP_TYPE_* flags describing the AP to.
|
||||||
*/
|
*/
|
||||||
Result ACU_GetWifiStatus(u32 *out);
|
Result ACU_GetWifiStatus(u32 *out);
|
||||||
|
|
||||||
@ -112,7 +127,7 @@ Result ACU_SetNetworkArea(acuConfig* config, u8 area);
|
|||||||
/**
|
/**
|
||||||
* @brief Sets the slot to use when connecting.
|
* @brief Sets the slot to use when connecting.
|
||||||
* @param config Pointer to an acuConfig struct used with ACU_CreateDefaultConfig previously.
|
* @param config Pointer to an acuConfig struct used with ACU_CreateDefaultConfig previously.
|
||||||
* @param type Allowed slots flag. BIT(0) for slot 1, BIT(1) for slot 2, BIT(2) for slot 3.
|
* @param type Allowed AP types bitmask, a combination of AC_AP_TYPE_* flags.
|
||||||
*/
|
*/
|
||||||
Result ACU_SetAllowApType(acuConfig* config, u8 type);
|
Result ACU_SetAllowApType(acuConfig* config, u8 type);
|
||||||
|
|
||||||
@ -128,3 +143,15 @@ Result ACU_SetRequestEulaVersion(acuConfig* config);
|
|||||||
* @param connectionHandle Handle created with svcCreateEvent to wait on until the connection succeeds or fails.
|
* @param connectionHandle Handle created with svcCreateEvent to wait on until the connection succeeds or fails.
|
||||||
*/
|
*/
|
||||||
Result ACU_ConnectAsync(const acuConfig* config, Handle connectionHandle);
|
Result ACU_ConnectAsync(const acuConfig* config, Handle connectionHandle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Selects the WiFi configuration slot for further ac:i operations.
|
||||||
|
* @param slot WiFi slot (0, 1 or 2).
|
||||||
|
*/
|
||||||
|
Result ACI_LoadNetworkSetting(u32 slot);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fetches the SSID of the previously selected WiFi configuration slot.
|
||||||
|
* @param[out] ssid Pointer to the output buffer of size 32B the SSID will be stored in.
|
||||||
|
*/
|
||||||
|
Result ACI_GetNetworkWirelessEssidSecuritySsid(void *ssid);
|
||||||
|
@ -56,6 +56,23 @@ typedef struct {
|
|||||||
u64 titlesFreeSpace; ///< Free space for titles.
|
u64 titlesFreeSpace; ///< Free space for titles.
|
||||||
} AM_TWLPartitionInfo;
|
} AM_TWLPartitionInfo;
|
||||||
|
|
||||||
|
/// Contains information about a title's content.
|
||||||
|
typedef struct {
|
||||||
|
u16 index; ///< Index of the content in the title.
|
||||||
|
u16 type; ///< ?
|
||||||
|
u32 contentId; ///< ID of the content in the title.
|
||||||
|
u64 size; ///< Size of the content in the title.
|
||||||
|
u8 flags; ///< @ref AM_ContentInfoFlags
|
||||||
|
u8 padding[7]; ///< Padding
|
||||||
|
} AM_ContentInfo;
|
||||||
|
|
||||||
|
/// Title ContentInfo flags.
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
AM_CONTENT_DOWNLOADED = BIT(0), ///< ?
|
||||||
|
AM_CONTENT_OWNED = BIT(1) ///< ?
|
||||||
|
} AM_ContentInfoFlags;
|
||||||
|
|
||||||
/// Initializes AM. This doesn't initialize with "am:app", see amAppInit().
|
/// Initializes AM. This doesn't initialize with "am:app", see amAppInit().
|
||||||
Result amInit(void);
|
Result amInit(void);
|
||||||
|
|
||||||
@ -500,3 +517,22 @@ Result AM_DeleteAllExpiredTitles(FS_MediaType mediatype);
|
|||||||
|
|
||||||
/// Deletes all TWL titles.
|
/// Deletes all TWL titles.
|
||||||
Result AM_DeleteAllTwlTitles(void);
|
Result AM_DeleteAllTwlTitles(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the number of content index installed under the specified DLC title.
|
||||||
|
* @param[out] count Pointer to output the number of content indices to.
|
||||||
|
* @param mediatype Media type of the title.
|
||||||
|
* @param titleID Title ID to retrieve the count for (high-id is 0x0004008C).
|
||||||
|
*/
|
||||||
|
Result AMAPP_GetDLCContentInfoCount(u32* count, FS_MediaType mediatype, u64 titleID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets content infos installed under the specified DLC title.
|
||||||
|
* @param[out] contentInfoRead Pointer to output the number of content infos read to.
|
||||||
|
* @param mediatype Media type of the title.
|
||||||
|
* @param titleID Title ID to retrieve the content infos for (high-id is 0x0004008C).
|
||||||
|
* @param contentInfoCount Number of content infos to retrieve.
|
||||||
|
* @param offset Offset from the first content index the count starts at.
|
||||||
|
* @param[out] contentInfos Pointer to output the content infos read to.
|
||||||
|
*/
|
||||||
|
Result AMAPP_ListDLCContentInfos(u32* contentInfoRead, FS_MediaType mediatype, u64 titleID, u32 contentInfoCount, u32 offset, AM_ContentInfo* contentInfos);
|
||||||
|
@ -176,7 +176,7 @@ bool aptShouldJumpToHome(void);
|
|||||||
bool aptCheckHomePressRejected(void);
|
bool aptCheckHomePressRejected(void);
|
||||||
|
|
||||||
/// \deprecated Alias for \ref aptCheckHomePressRejected.
|
/// \deprecated Alias for \ref aptCheckHomePressRejected.
|
||||||
static inline DEPRECATED bool aptIsHomePressed(void)
|
static inline CTR_DEPRECATED bool aptIsHomePressed(void)
|
||||||
{
|
{
|
||||||
return aptCheckHomePressRejected();
|
return aptCheckHomePressRejected();
|
||||||
}
|
}
|
||||||
@ -237,9 +237,22 @@ void aptClearChainloader(void);
|
|||||||
*/
|
*/
|
||||||
void aptSetChainloader(u64 programID, u8 mediatype);
|
void aptSetChainloader(u64 programID, u8 mediatype);
|
||||||
|
|
||||||
|
/// Configures the chainloader to launch the previous application.
|
||||||
|
void aptSetChainloaderToCaller(void);
|
||||||
|
|
||||||
/// Configures the chainloader to relaunch the current application (i.e. soft-reset)
|
/// Configures the chainloader to relaunch the current application (i.e. soft-reset)
|
||||||
void aptSetChainloaderToSelf(void);
|
void aptSetChainloaderToSelf(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the "deliver arg" and HMAC for the chainloader, which will
|
||||||
|
* be passed to the target 3DS/DS(i) application. The meaning of each
|
||||||
|
* parameter varies on a per-application basis.
|
||||||
|
* @param deliverArg Deliver arg to pass to the target application.
|
||||||
|
* @param deliverArgSize Size of the deliver arg, maximum 0x300 bytes.
|
||||||
|
* @param hmac HMAC buffer, 32 bytes. Use NULL to pass an all-zero dummy HMAC.
|
||||||
|
*/
|
||||||
|
void aptSetChainloaderArgs(const void *deliverArg, size_t deliverArgSize, const void *hmac);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets an APT lock handle.
|
* @brief Gets an APT lock handle.
|
||||||
* @param flags Flags to use.
|
* @param flags Flags to use.
|
||||||
@ -565,4 +578,4 @@ Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr);
|
|||||||
* @param sender Pointer to output the sender's AppID to.
|
* @param sender Pointer to output the sender's AppID to.
|
||||||
* @param received Pointer to output whether an argument was received to.
|
* @param received Pointer to output whether an argument was received to.
|
||||||
*/
|
*/
|
||||||
Result APT_ReceiveDeliverArg(const void* param, size_t paramSize, const void* hmac, u64* sender, bool* received);
|
Result APT_ReceiveDeliverArg(void* param, size_t paramSize, void* hmac, u64* sender, bool* received);
|
||||||
|
90
libctru/include/3ds/services/cdcchk.h
Normal file
90
libctru/include/3ds/services/cdcchk.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* @file cdcchk.h
|
||||||
|
* @brief CODEC Hardware Check service.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
/// I2S line enumeration
|
||||||
|
typedef enum {
|
||||||
|
CODEC_I2S_LINE_1, ///< Primary I2S line, used by DSP/Mic (configurable)/GBA sound controller.
|
||||||
|
CODEC_I2S_LINE_2, ///< Secondary I2S line, used by CSND hardware.
|
||||||
|
} CodecI2sLine;
|
||||||
|
|
||||||
|
/// Initializes CDCCHK.
|
||||||
|
Result cdcChkInit(void);
|
||||||
|
|
||||||
|
/// Exits CDCCHK.
|
||||||
|
void cdcChkExit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a pointer to the current cdc:CHK session handle.
|
||||||
|
* @return A pointer to the current cdc:CHK session handle.
|
||||||
|
*/
|
||||||
|
Handle *cdcChkGetSessionHandle(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads multiple registers from the CODEC, using the old
|
||||||
|
* SPI hardware interface and a 4MHz baudrate.
|
||||||
|
* @param pageId CODEC Page ID.
|
||||||
|
* @param initialRegAddr Address of the CODEC register to start with.
|
||||||
|
* @param[out] outData Where to write the read data to.
|
||||||
|
* @param size Number of registers to read (bytes to read, max. 64).
|
||||||
|
*/
|
||||||
|
Result CDCCHK_ReadRegisters1(u8 pageId, u8 initialRegAddr, void *outData, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads multiple registers from the CODEC, using the new
|
||||||
|
* SPI hardware interface and a 16MHz baudrate.
|
||||||
|
* @param pageId CODEC Page ID.
|
||||||
|
* @param initialRegAddr Address of the CODEC register to start with.
|
||||||
|
* @param[out] outData Where to read the data to.
|
||||||
|
* @param size Number of registers to read (bytes to read, max. 64).
|
||||||
|
*/
|
||||||
|
Result CDCCHK_ReadRegisters2(u8 pageId, u8 initialRegAddr, void *outData, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes multiple registers to the CODEC, using the old
|
||||||
|
* SPI hardware interface and a 4MHz baudrate.
|
||||||
|
* @param pageId CODEC Page ID.
|
||||||
|
* @param initialRegAddr Address of the CODEC register to start with.
|
||||||
|
* @param data Where to read the data to write from.
|
||||||
|
* @param size Number of registers to write (bytes to read, max. 64).
|
||||||
|
*/
|
||||||
|
Result CDCCHK_WriteRegisters1(u8 pageId, u8 initialRegAddr, const void *data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes multiple registers to the CODEC, using the new
|
||||||
|
* SPI hardware interface and a 16MHz baudrate.
|
||||||
|
* @param pageId CODEC Page ID.
|
||||||
|
* @param initialRegAddr Address of the CODEC register to start with.
|
||||||
|
* @param data Where to read the data to write from.
|
||||||
|
* @param size Number of registers to write (bytes to read, max. 64).
|
||||||
|
*/
|
||||||
|
Result CDCCHK_WriteRegisters2(u8 pageId, u8 initialRegAddr, const void *data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads a single register from the NTR PMIC.
|
||||||
|
* @param[out] outData Where to read the data to (1 byte).
|
||||||
|
* @param regAddr Register address.
|
||||||
|
* @note The NTR PMIC is emulated by the CODEC hardware and sends
|
||||||
|
* IRQs to the MCU when relevant.
|
||||||
|
*/
|
||||||
|
Result CDCCHK_ReadNtrPmicRegister(u8 *outData, u8 regAddr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes a single register from the NTR PMIC.
|
||||||
|
* @param regAddr Register address.
|
||||||
|
* @param data Data to write (1 byte).
|
||||||
|
* @note The NTR PMIC is emulated by the CODEC hardware and sends
|
||||||
|
* IRQs to the MCU when relevant.
|
||||||
|
*/
|
||||||
|
Result CDCCHK_WriteNtrPmicRegister(u8 regAddr, u8 data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the DAC volume level for the specified I2S line.
|
||||||
|
* @param i2sLine I2S line to set the volume for.
|
||||||
|
* @param volume Volume level (-128 to 0).
|
||||||
|
*/
|
||||||
|
Result CDCCHK_SetI2sVolume(CodecI2sLine i2sLine, s8 volume);
|
@ -20,18 +20,19 @@ typedef enum
|
|||||||
/// Configuration language values.
|
/// Configuration language values.
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
CFG_LANGUAGE_JP = 0, ///< Japanese
|
CFG_LANGUAGE_DEFAULT = -1, ///< Use system language in errorInit
|
||||||
CFG_LANGUAGE_EN = 1, ///< English
|
CFG_LANGUAGE_JP, ///< Japanese
|
||||||
CFG_LANGUAGE_FR = 2, ///< French
|
CFG_LANGUAGE_EN, ///< English
|
||||||
CFG_LANGUAGE_DE = 3, ///< German
|
CFG_LANGUAGE_FR, ///< French
|
||||||
CFG_LANGUAGE_IT = 4, ///< Italian
|
CFG_LANGUAGE_DE, ///< German
|
||||||
CFG_LANGUAGE_ES = 5, ///< Spanish
|
CFG_LANGUAGE_IT, ///< Italian
|
||||||
CFG_LANGUAGE_ZH = 6, ///< Simplified Chinese
|
CFG_LANGUAGE_ES, ///< Spanish
|
||||||
CFG_LANGUAGE_KO = 7, ///< Korean
|
CFG_LANGUAGE_ZH, ///< Simplified Chinese
|
||||||
CFG_LANGUAGE_NL = 8, ///< Dutch
|
CFG_LANGUAGE_KO, ///< Korean
|
||||||
CFG_LANGUAGE_PT = 9, ///< Portugese
|
CFG_LANGUAGE_NL, ///< Dutch
|
||||||
CFG_LANGUAGE_RU = 10, ///< Russian
|
CFG_LANGUAGE_PT, ///< Portugese
|
||||||
CFG_LANGUAGE_TW = 11, ///< Traditional Chinese
|
CFG_LANGUAGE_RU, ///< Russian
|
||||||
|
CFG_LANGUAGE_TW, ///< Traditional Chinese
|
||||||
} CFG_Language;
|
} CFG_Language;
|
||||||
|
|
||||||
// Configuration system model values.
|
// Configuration system model values.
|
||||||
|
@ -141,7 +141,7 @@ Result DSP_RegisterInterruptEvents(Handle handle, u32 interrupt, u32 channel);
|
|||||||
Result DSP_ReadPipeIfPossible(u32 channel, u32 peer, void* buffer, u16 length, u16* length_read);
|
Result DSP_ReadPipeIfPossible(u32 channel, u32 peer, void* buffer, u16 length, u16* length_read);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Writes to a pipe.
|
* @brief Writes to a pipe.
|
||||||
* @param channel unknown. Usually 2
|
* @param channel unknown. Usually 2
|
||||||
* @param buffer The message to send to the DSP process
|
* @param buffer The message to send to the DSP process
|
||||||
* @param length Length of the message
|
* @param length Length of the message
|
||||||
|
@ -202,7 +202,7 @@ typedef struct
|
|||||||
} FS_IntegrityVerificationSeed;
|
} FS_IntegrityVerificationSeed;
|
||||||
|
|
||||||
/// Ext save data information.
|
/// Ext save data information.
|
||||||
typedef struct PACKED
|
typedef struct CTR_PACKED
|
||||||
{
|
{
|
||||||
FS_MediaType mediaType : 8; ///< Media type.
|
FS_MediaType mediaType : 8; ///< Media type.
|
||||||
u8 unknown; ///< Unknown.
|
u8 unknown; ///< Unknown.
|
||||||
@ -235,6 +235,14 @@ typedef struct
|
|||||||
const void* data; ///< Pointer to FS path data.
|
const void* data; ///< Pointer to FS path data.
|
||||||
} FS_Path;
|
} FS_Path;
|
||||||
|
|
||||||
|
/// SDMC/NAND speed information
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool highSpeedModeEnabled; ///< Whether or not High Speed Mode is enabled.
|
||||||
|
bool usesHighestClockRate; ///< Whether or not a clock divider of 2 is being used.
|
||||||
|
u16 sdClkCtrl; ///< The value of the SD_CLK_CTRL register.
|
||||||
|
} FS_SdMmcSpeedInfo;
|
||||||
|
|
||||||
/// Filesystem archive handle, providing access to a filesystem's contents.
|
/// Filesystem archive handle, providing access to a filesystem's contents.
|
||||||
typedef u64 FS_Archive;
|
typedef u64 FS_Archive;
|
||||||
|
|
||||||
@ -467,13 +475,13 @@ Result FSUSER_GetNandCid(u8* out, u32 length);
|
|||||||
* @brief Gets the SDMC speed info.
|
* @brief Gets the SDMC speed info.
|
||||||
* @param speedInfo Pointer to output the speed info to.
|
* @param speedInfo Pointer to output the speed info to.
|
||||||
*/
|
*/
|
||||||
Result FSUSER_GetSdmcSpeedInfo(u32 *speedInfo);
|
Result FSUSER_GetSdmcSpeedInfo(FS_SdMmcSpeedInfo *speedInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the NAND speed info.
|
* @brief Gets the NAND speed info.
|
||||||
* @param speedInfo Pointer to output the speed info to.
|
* @param speedInfo Pointer to output the speed info to.
|
||||||
*/
|
*/
|
||||||
Result FSUSER_GetNandSpeedInfo(u32 *speedInfo);
|
Result FSUSER_GetNandSpeedInfo(FS_SdMmcSpeedInfo *speedInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the SDMC log.
|
* @brief Gets the SDMC log.
|
||||||
@ -639,7 +647,7 @@ Result FSUSER_GetLegacyRomHeader(FS_MediaType mediaType, u64 programId, void* he
|
|||||||
* @brief Gets the legacy banner data of a program.
|
* @brief Gets the legacy banner data of a program.
|
||||||
* @param mediaType Media type of the program.
|
* @param mediaType Media type of the program.
|
||||||
* @param programId ID of the program.
|
* @param programId ID of the program.
|
||||||
* @param header Pointer to output the legacy banner data to. (size = 0x23C0)
|
* @param banner Pointer to output the legacy banner data to. (size = 0x23C0)
|
||||||
*/
|
*/
|
||||||
Result FSUSER_GetLegacyBannerData(FS_MediaType mediaType, u64 programId, void* banner);
|
Result FSUSER_GetLegacyBannerData(FS_MediaType mediaType, u64 programId, void* banner);
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ Result FSPXI_CloseArchive(Handle serviceHandle, FSPXI_Archive archive);
|
|||||||
/**
|
/**
|
||||||
* @brief Unknown 0x17. Appears to be an "is archive handle valid" command?
|
* @brief Unknown 0x17. Appears to be an "is archive handle valid" command?
|
||||||
* @param archive Archive handle to check validity of.
|
* @param archive Archive handle to check validity of.
|
||||||
* @param out Pointer to output validity to.
|
* @param out Pointer to output validity to.
|
||||||
*/
|
*/
|
||||||
Result FSPXI_Unknown0x17(Handle serviceHandle, FSPXI_Archive archive, bool* out);
|
Result FSPXI_Unknown0x17(Handle serviceHandle, FSPXI_Archive archive, bool* out);
|
||||||
|
|
||||||
@ -251,13 +251,13 @@ Result FSPXI_GetNandCid(Handle serviceHandle, void* out, u32 size);
|
|||||||
* @brief Gets the SDMC speed info
|
* @brief Gets the SDMC speed info
|
||||||
* @param out Buffer to output the speed info to.
|
* @param out Buffer to output the speed info to.
|
||||||
*/
|
*/
|
||||||
Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, u32* out);
|
Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, FS_SdMmcSpeedInfo* out);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the NAND speed info
|
* @brief Gets the NAND speed info
|
||||||
* @param out Buffer to output the speed info to.
|
* @param out Buffer to output the speed info to.
|
||||||
*/
|
*/
|
||||||
Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, u32* out);
|
Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, FS_SdMmcSpeedInfo* out);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the SDMC log
|
* @brief Gets the SDMC log
|
||||||
|
@ -89,6 +89,12 @@ Result gspInit(void);
|
|||||||
/// Exits GSPGPU.
|
/// Exits GSPGPU.
|
||||||
void gspExit(void);
|
void gspExit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a pointer to the current gsp::Gpu session handle.
|
||||||
|
* @return A pointer to the current gsp::Gpu session handle.
|
||||||
|
*/
|
||||||
|
Handle *gspGetSessionHandle(void);
|
||||||
|
|
||||||
/// Returns true if the application currently has GPU rights.
|
/// Returns true if the application currently has GPU rights.
|
||||||
bool gspHasGpuRight(void);
|
bool gspHasGpuRight(void);
|
||||||
|
|
||||||
@ -100,9 +106,16 @@ bool gspHasGpuRight(void);
|
|||||||
* @param fb_b Pointer to the secondary framebuffer (only used in stereo mode for the right eye, otherwise pass the same as fb_a)
|
* @param fb_b Pointer to the secondary framebuffer (only used in stereo mode for the right eye, otherwise pass the same as fb_a)
|
||||||
* @param stride Stride in bytes between scanlines
|
* @param stride Stride in bytes between scanlines
|
||||||
* @param mode Mode configuration to be written to LCD register
|
* @param mode Mode configuration to be written to LCD register
|
||||||
|
* @return true if a buffer had already been presented to the screen but not processed yet by GSP, false otherwise.
|
||||||
* @note The most recently presented buffer is processed and configured during the specified screen's next VBlank event.
|
* @note The most recently presented buffer is processed and configured during the specified screen's next VBlank event.
|
||||||
*/
|
*/
|
||||||
void gspPresentBuffer(unsigned screen, unsigned swap, const void* fb_a, const void* fb_b, u32 stride, u32 mode);
|
bool gspPresentBuffer(unsigned screen, unsigned swap, const void* fb_a, const void* fb_b, u32 stride, u32 mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if a prior \ref gspPresentBuffer command is still pending to be processed by GSP.
|
||||||
|
* @param screen Screen ID (see \ref GSP_SCREEN_TOP and \ref GSP_SCREEN_BOTTOM)
|
||||||
|
*/
|
||||||
|
bool gspIsPresentPending(unsigned screen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Configures a callback to run when a GSPGPU event occurs.
|
* @brief Configures a callback to run when a GSPGPU event occurs.
|
||||||
|
@ -20,6 +20,12 @@ Result gspLcdInit(void);
|
|||||||
/// Exits GSPLCD.
|
/// Exits GSPLCD.
|
||||||
void gspLcdExit(void);
|
void gspLcdExit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a pointer to the current gsp::Lcd session handle.
|
||||||
|
* @return A pointer to the current gsp::Lcd session handle.
|
||||||
|
*/
|
||||||
|
Handle *gspLcdGetSessionHandle(void);
|
||||||
|
|
||||||
/// Powers on both backlights.
|
/// Powers on both backlights.
|
||||||
Result GSPLCD_PowerOnAllBacklights(void);
|
Result GSPLCD_PowerOnAllBacklights(void);
|
||||||
|
|
||||||
@ -69,4 +75,4 @@ Result GSPLCD_SetBrightness(u32 screen, u32 brightness);
|
|||||||
* @param screen Screen to set the brightness value of.
|
* @param screen Screen to set the brightness value of.
|
||||||
* @param brightness Brightness value set.
|
* @param brightness Brightness value set.
|
||||||
*/
|
*/
|
||||||
Result GSPLCD_SetBrightnessRaw(u32 screen, u32 brightness);
|
Result GSPLCD_SetBrightnessRaw(u32 screen, u32 brightness);
|
||||||
|
@ -91,3 +91,15 @@ Result IRU_SetIRLEDState(u32 value);
|
|||||||
* @param out Pointer to write the IR LED state to.
|
* @param out Pointer to write the IR LED state to.
|
||||||
*/
|
*/
|
||||||
Result IRU_GetIRLEDRecvState(u32 *out);
|
Result IRU_GetIRLEDRecvState(u32 *out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets an event which is signaled once a send finishes.
|
||||||
|
* @param out Pointer to write the event handle to.
|
||||||
|
*/
|
||||||
|
Result IRU_GetSendFinishedEvent(Handle *out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets an event which is signaled once a receive finishes.
|
||||||
|
* @param out Pointer to write the event handle to.
|
||||||
|
*/
|
||||||
|
Result IRU_GetRecvFinishedEvent(Handle *out);
|
||||||
|
@ -4,15 +4,25 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
typedef enum
|
typedef enum {
|
||||||
|
LED_NORMAL = 1, ///< The normal mode of the led
|
||||||
|
LED_SLEEP_MODE, ///< The led pulses slowly as it does in the sleep mode
|
||||||
|
LED_OFF, ///< Switch off power led
|
||||||
|
LED_RED, ///< Red state of the led
|
||||||
|
LED_BLUE, ///< Blue state of the led
|
||||||
|
LED_BLINK_RED, ///< Blinking red state of power led and notification led
|
||||||
|
} powerLedState;
|
||||||
|
|
||||||
|
typedef struct InfoLedPattern
|
||||||
{
|
{
|
||||||
LED_NORMAL = 1, ///< The normal mode of the led
|
u8 delay; ///< Delay between pattern values, 1/16th of a second (1 second = 0x10)
|
||||||
LED_SLEEP_MODE, ///< The led pulses slowly as it does in the sleep mode
|
u8 smoothing; ///< Smoothing between pattern values (higher = smoother)
|
||||||
LED_OFF, ///< Switch off power led
|
u8 loopDelay; ///< Delay between pattern loops, 1/16th of a second (1 second = 0x10, 0xFF = pattern is played only once)
|
||||||
LED_RED, ///< Red state of the led
|
u8 blinkSpeed; ///< Blink speed, when smoothing == 0x00
|
||||||
LED_BLUE, ///< Blue state of the led
|
u8 redPattern[32]; ///< Pattern for red component
|
||||||
LED_BLINK_RED, ///< Blinking red state of power led and notification led
|
u8 greenPattern[32]; ///< Pattern for green component
|
||||||
}powerLedState;
|
u8 bluePattern[32]; ///< Pattern for blue component
|
||||||
|
} InfoLedPattern;
|
||||||
|
|
||||||
/// Initializes mcuHwc.
|
/// Initializes mcuHwc.
|
||||||
Result mcuHwcInit(void);
|
Result mcuHwcInit(void);
|
||||||
@ -20,6 +30,12 @@ Result mcuHwcInit(void);
|
|||||||
/// Exits mcuHwc.
|
/// Exits mcuHwc.
|
||||||
void mcuHwcExit(void);
|
void mcuHwcExit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the current mcuHwc session handle.
|
||||||
|
* @return A pointer to the current mcuHwc session handle.
|
||||||
|
*/
|
||||||
|
Handle* mcuHwcGetSessionHandle(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reads data from an i2c device3 register
|
* @brief Reads data from an i2c device3 register
|
||||||
* @param reg Register number. See https://www.3dbrew.org/wiki/I2C_Registers#Device_3 for more info
|
* @param reg Register number. See https://www.3dbrew.org/wiki/I2C_Registers#Device_3 for more info
|
||||||
@ -60,6 +76,12 @@ Result MCUHWC_GetSoundSliderLevel(u8 *level);
|
|||||||
*/
|
*/
|
||||||
Result MCUHWC_SetWifiLedState(bool state);
|
Result MCUHWC_SetWifiLedState(bool state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the notification LED pattern
|
||||||
|
* @param pattern Pattern for the notification LED.
|
||||||
|
*/
|
||||||
|
Result MCUHWC_SetInfoLedPattern(const InfoLedPattern* pattern);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets Power LED state
|
* @brief Sets Power LED state
|
||||||
* @param state powerLedState State of power LED.
|
* @param state powerLedState State of power LED.
|
||||||
|
@ -111,10 +111,11 @@ Result NDMU_ResumeScheduler(void);
|
|||||||
Result NDMU_GetCurrentState(ndmState *state);
|
Result NDMU_GetCurrentState(ndmState *state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the daemon state.
|
* @brief Returns a daemon state.
|
||||||
* @param state Pointer to write the daemons state to.
|
* @param daemon The specified daemon.
|
||||||
|
* @param state Pointer to write the daemon state to.
|
||||||
*/
|
*/
|
||||||
Result NDMU_QueryStatus(ndmDaemonStatus *status);
|
Result NDMU_QueryStatus(ndmDaemon daemon, ndmDaemonStatus *status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the scan interval.
|
* @brief Sets the scan interval.
|
||||||
|
25
libctru/include/3ds/services/ptmgets.h
Normal file
25
libctru/include/3ds/services/ptmgets.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @file ptmgets.h
|
||||||
|
* @brief PTMGETS service.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
/// Initializes PTMGETS.
|
||||||
|
Result ptmGetsInit(void);
|
||||||
|
|
||||||
|
/// Exits PTMGETS.
|
||||||
|
void ptmGetsExit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a pointer to the current ptm:gets session handle.
|
||||||
|
* @return A pointer to the current ptm:gets session handle.
|
||||||
|
*/
|
||||||
|
Handle *ptmGetsGetSessionHandle(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the system time.
|
||||||
|
* @param[out] outMsY2k The pointer to write the number of milliseconds since 01/01/2000 to.
|
||||||
|
*/
|
||||||
|
Result PTMGETS_GetSystemTime(s64 *outMsY2k);
|
25
libctru/include/3ds/services/ptmsets.h
Normal file
25
libctru/include/3ds/services/ptmsets.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @file ptmsets.h
|
||||||
|
* @brief PTMSETS service.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
/// Initializes PTMSETS.
|
||||||
|
Result ptmSetsInit(void);
|
||||||
|
|
||||||
|
/// Exits PTMSETS.
|
||||||
|
void ptmSetsExit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a pointer to the current ptm:sets session handle.
|
||||||
|
* @return A pointer to the current ptm:sets session handle.
|
||||||
|
*/
|
||||||
|
Handle *ptmSetsGetSessionHandle(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the system time.
|
||||||
|
* @param msY2k The number of milliseconds since 01/01/2000.
|
||||||
|
*/
|
||||||
|
Result PTMSETS_SetSystemTime(s64 msY2k);
|
@ -48,6 +48,12 @@ Result ptmSysmInit(void);
|
|||||||
/// Exits ptm:sysm.
|
/// Exits ptm:sysm.
|
||||||
void ptmSysmExit(void);
|
void ptmSysmExit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a pointer to the current ptm:sysm session handle.
|
||||||
|
* @return A pointer to the current ptm:sysm session handle.
|
||||||
|
*/
|
||||||
|
Handle *ptmSysmGetSessionHandle(void);
|
||||||
|
|
||||||
/// Requests to enter sleep mode.
|
/// Requests to enter sleep mode.
|
||||||
Result PTMSYSM_RequestSleep(void);
|
Result PTMSYSM_RequestSleep(void);
|
||||||
|
|
||||||
@ -81,12 +87,18 @@ Result PTMSYSM_GetWakeReason(PtmSleepConfig *outSleepConfig);
|
|||||||
/// Cancels the "half-awake" state and fully wakes up the 3DS after some delay.
|
/// Cancels the "half-awake" state and fully wakes up the 3DS after some delay.
|
||||||
Result PTMSYSM_Awaken(void);
|
Result PTMSYSM_Awaken(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the user time by updating the user time offset.
|
||||||
|
* @param msY2k The number of milliseconds since 01/01/2000.
|
||||||
|
*/
|
||||||
|
Result PTMSYSM_SetUserTime(s64 msY2k);
|
||||||
|
|
||||||
/// Invalidates the "system time" (cfg block 0x30002)
|
/// Invalidates the "system time" (cfg block 0x30002)
|
||||||
Result PTMSYSM_InvalidateSystemTime(void);
|
Result PTMSYSM_InvalidateSystemTime(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reads the time and date coming from the RTC and converts the result.
|
* @brief Reads the time and date coming from the RTC and converts the result.
|
||||||
* @returns The number of milliseconds since 01/01/2000.
|
* @param[out] outMsY2k The pointer to write the number of milliseconds since 01/01/2000 to.
|
||||||
*/
|
*/
|
||||||
Result PTMSYSM_GetRtcTime(s64 *outMsY2k);
|
Result PTMSYSM_GetRtcTime(s64 *outMsY2k);
|
||||||
|
|
||||||
@ -97,9 +109,10 @@ Result PTMSYSM_GetRtcTime(s64 *outMsY2k);
|
|||||||
Result PTMSYSM_SetRtcTime(s64 msY2k);
|
Result PTMSYSM_SetRtcTime(s64 msY2k);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns 1 if it's a New 3DS, otherwise 0.
|
* @brief Checks whether the system is a New 3DS.
|
||||||
|
* @param[out] out Pointer to write the New 3DS flag to.
|
||||||
*/
|
*/
|
||||||
Result PTMSYSM_CheckNew3DS(void);
|
Result PTMSYSM_CheckNew3DS(bool *out);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Configures the New 3DS' CPU clock speed and L2 cache.
|
* @brief Configures the New 3DS' CPU clock speed and L2 cache.
|
||||||
|
@ -10,6 +10,12 @@ Result ptmuInit(void);
|
|||||||
/// Exits PTMU.
|
/// Exits PTMU.
|
||||||
void ptmuExit(void);
|
void ptmuExit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a pointer to the current ptm:u session handle.
|
||||||
|
* @return A pointer to the current ptm:u session handle.
|
||||||
|
*/
|
||||||
|
Handle *ptmuGetSessionHandle(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the system's current shell state.
|
* @brief Gets the system's current shell state.
|
||||||
* @param out Pointer to write the current shell state to. (0 = closed, 1 = open)
|
* @param out Pointer to write the current shell state to. (0 = closed, 1 = open)
|
||||||
|
@ -1,57 +1,559 @@
|
|||||||
/**
|
/**
|
||||||
* @file qtm.h
|
* @file qtm.h
|
||||||
* @brief QTM service.
|
* @brief QTM services.
|
||||||
|
*
|
||||||
|
* QTM is responsible for the following:
|
||||||
|
* - tracking and predicting the position of the user's eyes. This is done by using the inner
|
||||||
|
* camera sampling at 320x240px at 30 FPS, and by using the gyroscope to predict the position
|
||||||
|
* of the eyes between two camera samples (effectively doubling the tracking rate).
|
||||||
|
* The API reporting eye tracking data is actually *console-agnostic*. This concept is most
|
||||||
|
* likely covered by patent US9098112B2
|
||||||
|
* - automatically managing the IR LED emitter used for eye tracking in low-light conditions
|
||||||
|
* - managing the state of the parallax barrier by controlling the positions of the barrier's
|
||||||
|
* mask (opaque/transparent state by repeating pattern of 12 units, plus polarity). This is
|
||||||
|
* done via a TI TCA6416A I2C->Parallel expander (highlighted in yellow in this teardown photo:
|
||||||
|
* https://guide-images.cdn.ifixit.com/igi/IKlW6WTZKKmapYkt.full, with the expected 12 traces
|
||||||
|
* being clearly visible near the ribbon cable slot)
|
||||||
|
* - updating the barrier position according to eye tracking data relative to an optimal setting
|
||||||
|
* stored in calibration data: this is what Nintendo calls "Super Stable 3D" (SS3D); not done
|
||||||
|
* when in 2D mode
|
||||||
|
*
|
||||||
|
* Head-tracking can be used even if SS3D is disabled.
|
||||||
|
*
|
||||||
|
* SS3D works as follows:
|
||||||
|
* - compute the raw X and Y pixel coordinates for the eye midpoint:
|
||||||
|
* `rawX = (leftEyeRawPxCoords.x + rightEyeRawPxCoords.x) / 2`
|
||||||
|
* `rawY = (leftEyeRawPxCoords.y + rightEyeRawPxCoords.y) / 2`
|
||||||
|
* - rotate the value around the optical Z-axis using the optimal eye-to-camera angle from
|
||||||
|
* calibration data, with a rotation matrix
|
||||||
|
* - normalize the X value:
|
||||||
|
* `xC = (rawX / 320) - 0.5`
|
||||||
|
* - transform into world space coordinate, using fovX from calibration (convert to radians first).
|
||||||
|
* Note that this fovX doesn't take lens distortion into account and is slightly different from
|
||||||
|
* the hardcoded angle used in \ref QTMU_GetTrackingData
|
||||||
|
* `x = xC * tan(fovX/2)`
|
||||||
|
* - multiply by length of adjacent side (eye-to-camera distance) to get the length of the opposite
|
||||||
|
* side. This is the eye horizontal deviation to camera lens in mm, which is then converted to
|
||||||
|
* number of iod/12 units:
|
||||||
|
* `delta = x * optimalDistance / (iod/12)`
|
||||||
|
* - we then obtain the new target position of the parallax barrier (expressed in iod/12 units,
|
||||||
|
* mod iod/12):
|
||||||
|
* `pos = centerPos + delta`
|
||||||
|
* - the value is then rounded to nearest integer. To avoid artifacts, if the rounded value is
|
||||||
|
* going to increase, then 0.01 is subtracted, and if it is going to decrease, then 0.01 is added.
|
||||||
|
* The value is rounded again to compute the final discrete value written to the expander (barrier
|
||||||
|
* position).
|
||||||
|
* - note: all calculation in QTM and otherwise assume interocular distance to be 62mm (the average).
|
||||||
|
* Assumedly, if the user's IOD is different, then "optimal distance to screen" effectively changes
|
||||||
|
* for that user.
|
||||||
|
*
|
||||||
|
* QTM services are not present on O3DS, thus caller must call \ref qtmCheckServicesRegistered to check
|
||||||
|
* if the services are registered. Moreover, since QTM functionality might not always be available (due
|
||||||
|
* to blacklist or console being N2DSXL), `qtm:u` users should check Result return values, and `qtm:s`
|
||||||
|
* users can call \ref QTMS_SetQtmStatus to check the actual availability status.
|
||||||
|
*
|
||||||
|
* Considering that the eye tracking data reporting API is hardware-agnostic, it is advisable to
|
||||||
|
* hardcode neither camera aspect ratio (even if it is 4/3 on real hardware) and resolution nor
|
||||||
|
* field-of-view angle.
|
||||||
|
*
|
||||||
|
* There is a separate QTM service, `qtm:c` ("hardware check"), that lets you manipulate parallax barrier
|
||||||
|
* pattern directly. It is covered in \ref qtmc.h instead.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
//See also: http://3dbrew.org/wiki/QTM_Services
|
#include <3ds/types.h>
|
||||||
|
|
||||||
/// Head tracking coordinate pair.
|
/// ID of QTM status data (fully enabled/SS3D disabled) in `cfg`
|
||||||
typedef struct {
|
#define QTM_STATUS_CFG_BLK_ID 0x180000u
|
||||||
float x; ///< X coordinate.
|
|
||||||
float y; ///< Y coordinate.
|
|
||||||
} QTM_HeadTrackingInfoCoord;
|
|
||||||
|
|
||||||
/// Head tracking info.
|
/// ID of QTM calibration data in `cfg`
|
||||||
typedef struct {
|
#define QTM_CAL_CFG_BLK_ID 0x180001u
|
||||||
u8 flags[5]; ///< Flags.
|
|
||||||
u8 padding[3]; ///< Padding.
|
|
||||||
float floatdata_x08; ///< Unknown. Not used by System_Settings.
|
|
||||||
QTM_HeadTrackingInfoCoord coords0[4]; ///< Head coordinates.
|
|
||||||
u32 unk_x2c[5]; ///< Unknown. Not used by System_Settings.
|
|
||||||
} QTM_HeadTrackingInfo;
|
|
||||||
|
|
||||||
/// Initializes QTM.
|
/**
|
||||||
Result qtmInit(void);
|
* @brief QTM enablement status (when cameras not in use by user), set by `qtm:s`.
|
||||||
|
* @note Manual IR LED control, camera lux, and `qtm:c` commands remain available
|
||||||
|
* for use on N3DS and N3DSXL regardless.
|
||||||
|
*/
|
||||||
|
typedef enum QtmStatus {
|
||||||
|
QTM_STATUS_ENABLED = 0, ///< QTM is fully enabled.
|
||||||
|
QTM_STATUS_SS3D_DISABLED = 1, ///< QTM "super stable 3D" feature is disabled. Parallax barrier hardware state is configured to match O3DS.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief QTM is unavailable: either "blacklisted" (usually by NS) for the current title, **or console is a N2DSXL**.
|
||||||
|
*
|
||||||
|
* In this state, all QTM functionality is disabled. This includes "super-stable 3D"
|
||||||
|
* (ie. auto barrier adjustment) including `qtm:s` manual barrier position setting functions,
|
||||||
|
* head tracking, IR LED control and camera luminance reporting (400.0 is returned instead).
|
||||||
|
*
|
||||||
|
* @note `qtm:c` barrier hardware state setting function (\ref blah) bypasses this state.
|
||||||
|
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
|
||||||
|
* to be done, and is in fact never done by official software.
|
||||||
|
*/
|
||||||
|
QTM_STATUS_UNAVAILABLE = 2,
|
||||||
|
} QtmStatus;
|
||||||
|
|
||||||
|
/// QTM status data (fully enabled/SS3D disabled) in `cfg`. Usually all-zero on N2DSXL.
|
||||||
|
typedef struct QtmStatusCfgData {
|
||||||
|
QtmStatus defaultStats; ///< QTM status at boot (fully enabled or SS3D disabled).
|
||||||
|
/**
|
||||||
|
* @brief "Global variable" (.data) section load mode? Unused.
|
||||||
|
* From CTRAging:
|
||||||
|
* - 0: "normal"
|
||||||
|
* - 1: "single reacq"
|
||||||
|
* - 2: "double reacq"
|
||||||
|
- 3/4/5: "w2w copy 1/10/100"
|
||||||
|
*/
|
||||||
|
u8 gvLoadMode;
|
||||||
|
u8 _padding[2]; ///< Padding.
|
||||||
|
} QtmStatusCfgData;
|
||||||
|
|
||||||
|
/// QTM calibration data (fully enabled/SS3D disabled) in `cfg`. Usually all-zero on N2DSXL.
|
||||||
|
typedef struct QtmCalibrationData {
|
||||||
|
/**
|
||||||
|
* @brief Neutral (center) barrier position/offset (with slit width of 6 units), when the user is
|
||||||
|
* facing directly facing the camera, that is to say, their eye midpoint normalized X coord
|
||||||
|
* in the camera's plane is 0, assuming the user's head is located at the expected viewing distance
|
||||||
|
* and at the expected eye-to-camera angle (as per the rest of this structure).
|
||||||
|
* This is expressed in terms of iod/12 units modulo iod/12 (thus, range is 0 to 11 included),
|
||||||
|
* with IOD (interocular distance) assumed to be 62mm.
|
||||||
|
* @note This field is floating-point for QTM auto-adjustment purposes, however the actual barrier
|
||||||
|
* position in hardware is an integer.
|
||||||
|
* @note This is the field that System Settings lets you add -1.0 to +1.0 to.
|
||||||
|
* @note Moreover, this field can be directly changed through \ref QTMS_SetCenterBarrierPosition.
|
||||||
|
*/
|
||||||
|
float centerBarrierPosition;
|
||||||
|
|
||||||
|
float translationX; ///< Lens X coord in inner camera space? Very low value and seems to be unused.
|
||||||
|
float translationY; ///< Lens Y coord in inner camera space? Very low value and seems to be unused.
|
||||||
|
float rotationZ; ///< Optimal eye-to-camera angle, in radians, without accounting for lens distortion.
|
||||||
|
float fovX; ///< Camera's horizontal FoV in degrees, without accounting for lens distortion.
|
||||||
|
float viewingDistance; ///< Optimal viewing distance between user and top screen, assuming iod to be 62mm.
|
||||||
|
} QtmCalibrationData;
|
||||||
|
|
||||||
|
/// Left eye or right eye, for \ref QtmTrackingData and \ref QtmRawTrackingData
|
||||||
|
typedef enum QtmEyeSide {
|
||||||
|
QTM_EYE_LEFT = 0, ///< Left eye.
|
||||||
|
QTM_EYE_RIGHT = 1, ///< Right eye.
|
||||||
|
|
||||||
|
QTM_EYE_NUM, ///< Number of eyes.
|
||||||
|
} QtmEyeSide;
|
||||||
|
|
||||||
|
/// QTM raw eye tracking data
|
||||||
|
typedef struct QtmRawTrackingData {
|
||||||
|
/**
|
||||||
|
* @brief Eye position detected or predicted, equals (confidenceLevel > 0).
|
||||||
|
* If false, QTM will attempt to make a guess based on gyro data.
|
||||||
|
* If the console isn't moving either, then QTM will assume the user's eyes are progressively
|
||||||
|
* moving back to face the screen.
|
||||||
|
*/
|
||||||
|
bool eyesTracked; ///< Eye position detected or predicted, equals (confidenceLevel > 0).
|
||||||
|
u8 _padding[3]; ///< Padding.
|
||||||
|
u32 singletonQtmPtr; ///< Pointer to eye-tracking singleton pointer, in QTM's .bss, located in N3DS extra memory.
|
||||||
|
float confidenceLevel; ///< Eye tracking confidence level (0 to 1).
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Raw predicted or detected eye coordinates. Each eye is represented as one point.
|
||||||
|
* Fractional part is *not* necessarily zero.
|
||||||
|
* @note X coord is within 0 to 320.
|
||||||
|
* @note Y coord is within 0 to 240.
|
||||||
|
*/
|
||||||
|
float rawEyeCameraCoordinates[QTM_EYE_NUM][2];
|
||||||
|
|
||||||
|
float dPitch; ///< Difference in gyro pitch from position at console boot.
|
||||||
|
float dYaw; ///< Difference in gyro yaw from position at console boot.
|
||||||
|
float dRoll; ///< Difference in gyro roll from position at console boot.
|
||||||
|
|
||||||
|
s64 samplingTick; ///< Time point the current measurements were made.
|
||||||
|
} QtmRawTrackingData;
|
||||||
|
|
||||||
|
/// QTM processed eye tracking data, suitable for 3D programming
|
||||||
|
typedef struct QtmTrackingData {
|
||||||
|
bool eyesTracked; ///< Eye position detected or tracked with some confidence, equals (confidenceLevel > 0). Even if false, QTM may make a guess
|
||||||
|
bool faceDetected; ///< Whether or not the entirety of the user's face has been detected with good confidence.
|
||||||
|
bool eyesDetected; ///< Whether or not the user's eyes have actually been detected with full confidence.
|
||||||
|
u8 _unused; ///< Unused.
|
||||||
|
bool clamped; ///< Whether or not the normalized eye coordinates have been clamped after accounting for lens distortion.
|
||||||
|
u8 _padding[3]; ///< Padding.
|
||||||
|
|
||||||
|
float confidenceLevel; ///< Eye tracking confidence level (0 to 1).
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Normalized eye coordinates, for each eye, after accounting for lens distortion, centered around camera.
|
||||||
|
* X coord is in the -1 to 1 range, and Y coord range depends on inverse aspect ratio (-0.75 to 0.75 on real hardware).
|
||||||
|
* @note On real hardware, X coord equals `((rawX / 160.0) - 1.00) * 1.0639` before clamping.
|
||||||
|
* @note On real hardware, Y coord equals `((rawY / 160.0) - 0.75) * 1.0637` before clamping.
|
||||||
|
*/
|
||||||
|
float eyeCameraCoordinates[QTM_EYE_NUM][2];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Normalized eye coordinates, for each eye, in world space.
|
||||||
|
* Corresponds to \ref eyeCameraCoordinates multiplied by tangent of field of view.
|
||||||
|
* @note On real hardware, X coord equals `eyeCameraCoordinates.x * tan(64.9 deg / 2)`.
|
||||||
|
* @note On real hardware, Y coord equals `eyeCameraCoordinates.x * tan(51.0 deg / 2)`.
|
||||||
|
*/
|
||||||
|
float eyeWorldCoordinates[QTM_EYE_NUM][2];
|
||||||
|
|
||||||
|
float dPitch; ///< Difference in gyro pitch from position at console boot.
|
||||||
|
float dYaw; ///< Difference in gyro yaw from position at console boot.
|
||||||
|
float dRoll; ///< Difference in gyro roll from position at console boot.
|
||||||
|
|
||||||
|
|
||||||
|
s64 samplingTick; ///< Time point the current measurements were made.
|
||||||
|
} QtmTrackingData;
|
||||||
|
|
||||||
|
/// QTM service name enum, excluding `qtm:c`
|
||||||
|
typedef enum QtmServiceName {
|
||||||
|
/**
|
||||||
|
* @brief `qtm:u`: has eye-tracking commands and IR LED control commands, but for some
|
||||||
|
* reason cannot fetch ambiant lux data from the camera's luminosity sensor.
|
||||||
|
*/
|
||||||
|
QTM_SERVICE_USER = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief `qtm:s`: has access to all `qtm:u` commands, plus luminosity sensor, plus
|
||||||
|
* manual barrier position setting and calibration adjustment commands.
|
||||||
|
* Automatic barrier control is reenabled on session exit.
|
||||||
|
*/
|
||||||
|
QTM_SERVICE_SYSTEM = 1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief `qtm:sp`: has access to all `qtm:s` (and `qtm:u`) commands, and merely has a
|
||||||
|
* few more commands that GSP uses to notify QTM of 2D<>3D mode switches and
|
||||||
|
* power events. Automatic barrier control is reenabled on session exit.
|
||||||
|
* GSP always keeps a `qtm:sp` sessions open (at least on latest system version),
|
||||||
|
* whereas NS opens then immediately closes a `qtm:sp` sessions only when dealing
|
||||||
|
* with a "blacklisted" application (that is, almost never).
|
||||||
|
*/
|
||||||
|
QTM_SERVICE_SYSTEM_PROCESS = 2,
|
||||||
|
} QtmServiceName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether or not QTM services are registered.
|
||||||
|
* @return True on O3DS systems, false on N3DS systems.
|
||||||
|
*/
|
||||||
|
bool qtmCheckServicesRegistered(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes QTM (except `qtm:c`).
|
||||||
|
* Excluding `qtm:c`, QTM has three main services.
|
||||||
|
* Only 3 sessions (2 until 9.3.0 sysupdate) for ALL services COMBINED, including `qtm:c`,
|
||||||
|
* can be open at a time.
|
||||||
|
* Refer to \ref QtmServiceName enum value descriptions to see which service to choose.
|
||||||
|
*
|
||||||
|
* @param serviceName QTM service name enum value (corresponding to `qtm:u`, `qtm:s` and `qtm:sp`
|
||||||
|
* respectively).
|
||||||
|
* @note Result of \ref qtmCheckServicesRegistered should be checked before calling this function.
|
||||||
|
*/
|
||||||
|
Result qtmInit(QtmServiceName serviceName);
|
||||||
|
|
||||||
/// Exits QTM.
|
/// Exits QTM.
|
||||||
void qtmExit(void);
|
void qtmExit(void);
|
||||||
|
|
||||||
/**
|
/// Checks whether or not a `qtm:u`, `qtm:s` or `qtm:sp` session is active.
|
||||||
* @brief Checks whether QTM is initialized.
|
bool qtmIsInitialized(void);
|
||||||
* @return Whether QTM is initialized.
|
|
||||||
*/
|
/// Returns a pointer to the current `qtm:u` / `qtm:s` / `qtm:sp` session handle.
|
||||||
bool qtmCheckInitialized(void);
|
Handle *qtmGetSessionHandle(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether a head is fully detected.
|
* @brief Gets the current raw eye tracking data, with an optional prediction made for predictionTimePointOrZero = t+dt,
|
||||||
* @param info Tracking info to check.
|
* or for the current time point (QTM makes predictions based on gyro data since inner camera runs at 30 FPS).
|
||||||
|
*
|
||||||
|
* @param[out] outData Where to write the raw tracking data to. Cleared to all-zero on failure (instead of being left uninitialized).
|
||||||
|
* @param predictionTimePointOrZero Either zero, or the time point (in system ticks) for which to make a prediction for.
|
||||||
|
* Maximum 1 frame (at 30 FPS) in the past, and up to 5 frames in the future.
|
||||||
|
* @return `0xC8A18008` if camera is in use by user, or `0xC8A183EF` if QTM is unavailable (in particular, QTM is always
|
||||||
|
* unavailable on N2DSXL), Otherwise, 0 (success). Return value should be checked by caller.
|
||||||
|
* @note Consider using \ref QTMU_GetTrackingDataEx instead.
|
||||||
*/
|
*/
|
||||||
bool qtmCheckHeadFullyDetected(QTM_HeadTrackingInfo *info);
|
Result QTMU_GetRawTrackingDataEx(QtmRawTrackingData *outData, s64 predictionTimePointOrZero);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converts QTM coordinates to screen coordinates.
|
* @brief Gets the current raw eye tracking data.
|
||||||
* @param coord Coordinates to convert.
|
*
|
||||||
* @param screen_width Width of the screen. Can be NULL to use the default value for the top screen.
|
* @param[out] outData Where to write the raw tracking data to. Cleared to all-zero on failure (instead of being left uninitialized).
|
||||||
* @param screen_height Height of the screen. Can be NULL to use the default value for the top screen.
|
* @return `0xC8A18008` if camera is in use by user, or `0xC8A183EF` if QTM is unavailable (in particular, QTM is always
|
||||||
* @param x Pointer to output the screen X coordinate to.
|
* unavailable on N2DSXL), Otherwise, 0 (success). Return value should be checked by caller.
|
||||||
* @param y Pointer to output the screen Y coordinate to.
|
* @note Consider using \ref QTMU_GetTrackingData instead.
|
||||||
*/
|
*/
|
||||||
Result qtmConvertCoordToScreen(QTM_HeadTrackingInfoCoord *coord, float *screen_width, float *screen_height, u32 *x, u32 *y);
|
static inline Result QTMU_GetRawTrackingData(QtmRawTrackingData *outData)
|
||||||
|
{
|
||||||
|
return QTMU_GetRawTrackingDataEx(outData, 0LL);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the current head tracking info.
|
* @brief Gets the current normalized eye tracking data, made suitable for 3D programming with an optional prediction made
|
||||||
* @param val Normally 0.
|
* for predictionTimePointOrZero = t+dt, or for the current time point (QTM makes predictions based on gyro data since
|
||||||
* @param out Pointer to write head tracking info to.
|
* inner camera runs at 30 FPS).
|
||||||
|
*
|
||||||
|
* @param[out] outData Where to write the raw tracking data to. Cleared to all-zero on failure (instead of being left uninitialized).
|
||||||
|
* @param predictionTimePointOrZero Either zero, or the time point (in system ticks) for which to make a prediction for.
|
||||||
|
* Maximum 1 frame (at 30 FPS) in the past, and up to 5 frames in the future.
|
||||||
|
* @return `0xC8A18008` if camera is in use by user, or `0xC8A183EF` if QTM is unavailable (in particular, QTM is always
|
||||||
|
* unavailable on N2DSXL). Otherwise, 0 (success). Return value should be checked by caller.
|
||||||
|
* @note This can, for example, be used in games to allow the user to control the scene's camera with their own face.
|
||||||
*/
|
*/
|
||||||
Result QTM_GetHeadTrackingInfo(u64 val, QTM_HeadTrackingInfo* out);
|
Result QTMU_GetTrackingDataEx(QtmTrackingData *outData, s64 predictionTimePointOrZero);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the current normalized eye tracking data, made suitable for 3D programming.
|
||||||
|
*
|
||||||
|
* @param[out] outData Where to write the raw tracking data to. Cleared to all-zero on failure (instead of being left uninitialized).
|
||||||
|
* @return `0xC8A18008` if camera is in use by user, or `0xC8A183EF` if QTM is unavailable (in particular, QTM is always
|
||||||
|
* unavailable on N2DSXL). Otherwise, 0 (success). Return value should be checked by caller.
|
||||||
|
* @note This can, for example, be used in games to allow the user to control the scene's camera with their own face.
|
||||||
|
*/
|
||||||
|
static inline Result QTMU_GetTrackingData(QtmTrackingData *outData)
|
||||||
|
{
|
||||||
|
return QTMU_GetTrackingDataEx(outData, 0LL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes an approximation of the horizontal angular field of view of the camera based on eye tracking data.
|
||||||
|
*
|
||||||
|
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
|
||||||
|
* @return Horizontal angular field of view in radians. Corresponds to 64.9 degrees on real hardware.
|
||||||
|
*/
|
||||||
|
float qtmComputeFovX(const QtmTrackingData *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes an approximation of the vertical angular field of view of the camera based on eye tracking data.
|
||||||
|
*
|
||||||
|
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
|
||||||
|
* @return Vertical angular field of view in radians. Corresponds to 51.0 degrees on real hardware.
|
||||||
|
*/
|
||||||
|
float qtmComputeFovY(const QtmTrackingData *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes a rough approximation of the inverse of the aspect ration of the camera based on eye tracking data.
|
||||||
|
*
|
||||||
|
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
|
||||||
|
* @return Rough approximation of the inverse of the aspect ratio of the camera. Aspect ratio is exactly 0.75 on real hardware.
|
||||||
|
*/
|
||||||
|
float qtmComputeInverseAspectRatio(const QtmTrackingData *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes the user's head tilt angle, that is, the angle between the line through both eyes and the camera's
|
||||||
|
* horizontal axis in camera space.
|
||||||
|
*
|
||||||
|
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
|
||||||
|
* @return Horizontal head angle relative to camera, in radians.
|
||||||
|
*/
|
||||||
|
float qtmComputeHeadTiltAngle(const QtmTrackingData *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Estimates the distance between the user's eyes and the camera, based on
|
||||||
|
* eye tracking data. This may be a little bit inaccurate, as this assumes
|
||||||
|
* interocular distance of 62mm (like all 3DS software does), and that both
|
||||||
|
* eyes are at the same distance from the screen.
|
||||||
|
*
|
||||||
|
* @param data Eye tracking data, obtained from \ref QTMU_GetTrackingData or \ref QTMU_GetTrackingDataEx.
|
||||||
|
* @return Eye-to-camera distance in millimeters.
|
||||||
|
*/
|
||||||
|
float qtmEstimateEyeToCameraDistance(const QtmTrackingData *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Temporarily enables manual control of the IR LED by user, disabling its automatic control.
|
||||||
|
* If not already done, this also turns off the IR LED. This setting is cleared when user closes the console's shell.
|
||||||
|
* @return Always 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMU_EnableManualIrLedControl(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Temporarily disables manual control of the IR LED by user, re-enabling its automatic control.
|
||||||
|
* If not already done, this also turns off the IR LED.
|
||||||
|
* @return Always 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMU_DisableManualIrLedControl(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Turns the IR LED on or off during manual control. \ref QTMU_EnableManualIrLedControl must have been called.
|
||||||
|
*
|
||||||
|
* @param on Whether to turn the IR LED on or off.
|
||||||
|
* @return `0xC8A18005` if manual control was not enabled or if the operation failed, `0xC8A18008` if camera is in use
|
||||||
|
* by user, or `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL).
|
||||||
|
* Otherwise, 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMU_SetIrLedStatus(bool on);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Attempts to clear IR LED overrides from any of the relevant commands in `qtm:u`, `qtm:s` (and `qtm:c`) commands
|
||||||
|
* by calling \ref QTMU_EnableManualIrLedControl followed by \ref QTMU_DisableManualIrLedControl, so that auto IR LED
|
||||||
|
* management takes place again.
|
||||||
|
* @return The value returned by \ref QTMU_DisableManualIrLedControl.
|
||||||
|
*/
|
||||||
|
Result qtmClearIrLedOverrides(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether or not QTM has been blacklisted, ie. that it has been made unavailable.
|
||||||
|
* In detail, this means that the last call to \ref QTMS_SetQtmStatus was made with argument \ref QTM_STATUS_UNAVAILABLE,
|
||||||
|
* usually by NS. This feature seems to only be used for some internal test titles.
|
||||||
|
*
|
||||||
|
* @param[out] outBlacklisted Whether or not QTM is unavailable. Always true on N2DSXL.
|
||||||
|
* @return Always 0 (success).
|
||||||
|
* @note On N2DSXL, even though status is always supposed to be \ref QTM_STATUS_UNAVAILABLE, this function often returns true
|
||||||
|
* (because NS doesn't change QTM's status if title isn't blacklisted). Do not rely on this for N2DSXL detection.
|
||||||
|
* @note Refer to https://www.3dbrew.org/wiki/NS_CFA for a list of title UIDs this is used for.
|
||||||
|
*/
|
||||||
|
Result QTMU_IsCurrentAppBlacklisted(bool *outBlacklisted);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the neutral (center) barrier position/offset in calibration, _without_ saving it to `cfg`.
|
||||||
|
* Takes effect immediately. SS3D works by calculating the position of the eye midpoint, rotated
|
||||||
|
* by the ideal eye-to-camera angle, expressed in (iod/12 units, iod assumed to be 62mm).
|
||||||
|
*
|
||||||
|
* @param position Center barrier position, in terms of iod/12 units modulo iod/12.
|
||||||
|
* @note This field is floating-point for QTM auto-adjustment purposes, however the actual barrier position
|
||||||
|
* in hardware is an integer.
|
||||||
|
* @note This is the field that System Settings lets you add -1.0 to +1.0 to.
|
||||||
|
* @note There is no "get" counterpart for this.
|
||||||
|
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
|
||||||
|
0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMS_SetCenterBarrierPosition(float position);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the average ambient luminance as perceived by the inner camera (in lux).
|
||||||
|
* If QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), returns 400.0 instead
|
||||||
|
* of the actual luminance.
|
||||||
|
*
|
||||||
|
* @param[out] outLuminanceLux Where to write the luminance to. Always 400.0 on N2DSXL.
|
||||||
|
* @note Camera exposure, and in particular auto-exposure affects the returned luminance value. This must be
|
||||||
|
* taken into consideration, because this value can thus surge when user covers the inner camera.
|
||||||
|
* @return Always 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMS_GetCameraLuminance(float *outLuminanceLux);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enables automatic barrier control when in 3D mode with "super stable 3D" enabled.
|
||||||
|
*
|
||||||
|
* @note This is automatically called upon `qtm:s` and `qtm:sp` session exit.
|
||||||
|
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
|
||||||
|
0 (success).
|
||||||
|
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
|
||||||
|
* to be done, and is in fact never done by official software. If that is regardless the case,
|
||||||
|
* this function here does nothing.
|
||||||
|
*/
|
||||||
|
Result QTMS_EnableAutoBarrierControl(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Temporarily disables automatic barrier control (when in 3D mode with "super stable 3D" enabled).
|
||||||
|
*
|
||||||
|
* @note This is automatically called upon `qtm:s` and `qtm:sp` session exit.
|
||||||
|
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
|
||||||
|
0 (success).
|
||||||
|
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
|
||||||
|
* to be done, and is in fact never done by official software. If that is regardless the case,
|
||||||
|
* this function here does nothing.
|
||||||
|
*/
|
||||||
|
Result QTMS_DisableAutoBarrierControl(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Temporarily sets the parallax barrier's position (offset in iod/12 units, assuming slit width of 6 units).
|
||||||
|
* Does nothing in 2D mode and/or if "super stable 3D" is disabled.
|
||||||
|
*
|
||||||
|
* @param position Parallax barrier position (offset in units), must be between 0 and 11 (both included)
|
||||||
|
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), 0xE0E18002
|
||||||
|
* if \p position is not in range, otherwise 0 (success).
|
||||||
|
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
|
||||||
|
* to be done, and is in fact never done by official software. If that is regardless the case,
|
||||||
|
* this function here does nothing.
|
||||||
|
* @note No effect when the screen is in 2D mode.
|
||||||
|
* @see QTMC_SetBarrierPattern
|
||||||
|
*/
|
||||||
|
Result QTMS_SetBarrierPosition(u8 position);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the current position of the parallax barrier (offset in iod/12 units, slit width of 6 units).
|
||||||
|
* When "super stable 3D" is disabled, returns 13 instead.
|
||||||
|
*
|
||||||
|
* @param[out] outPosition Where to write the barrier's position to.
|
||||||
|
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
|
||||||
|
0 (success).
|
||||||
|
* @note When SS3D is disabled, this returns 13 to \p outPosition . When in 2D mode, the returned position is not
|
||||||
|
updated.
|
||||||
|
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
|
||||||
|
* to be done, and is in fact never done by official software. If that is regardless the case,
|
||||||
|
* this function here returns 13 to \p outPosition .
|
||||||
|
* @see QTMC_SetBarrierPattern
|
||||||
|
*/
|
||||||
|
Result QTMS_GetCurrentBarrierPosition(u8 *outPosition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Temporarily overrides IR LED state. Requires "manual control" from `qtm:u` to be disabled, and has
|
||||||
|
* lower priority than it.
|
||||||
|
*
|
||||||
|
* @param on Whether to turn the IR LED on or off.
|
||||||
|
* @return `0xC8A18005` if manual control was enabled or if the operation failed, `0xC8A18008` if camera is in use
|
||||||
|
* by user (unless "hardware check" API enabled), or `0xC8A18009` if QTM is unavailable (in particular,
|
||||||
|
* QTM is always unavailable on N2DSXL). Otherwise, 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMS_SetIrLedStatusOverride(bool on);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets calibration data, taking effect immediately, and optionally saves it to `cfg`.
|
||||||
|
*
|
||||||
|
* @param cal Pointer to calibration data.
|
||||||
|
* @param saveCalToCfg Whether or not to persist the calibration data in `cfg`.
|
||||||
|
* @return `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL), otherwise
|
||||||
|
whatever `cfg:s` commands return (if used), or 0 (success).
|
||||||
|
* @note There is no "get" counterpart for this function, and there is no way to see the current calibration data
|
||||||
|
in use unless it has been saved to `cfg`.
|
||||||
|
* @note Due to an oversight, \ref QTMS_SetQtmStatus allows changing QTM state on N2DSXL. This is not intended
|
||||||
|
* to be done, and is in fact never done by official software. If that is regardless the case,
|
||||||
|
* this function here doesn't apply calibrations parameters (they may still be saved, however,
|
||||||
|
* even though QTM calibration blocks are always normally 0 on N2DSXL).
|
||||||
|
*/
|
||||||
|
Result QTMS_SetCalibrationData(const QtmCalibrationData *cal, bool saveCalToCfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the current QTM status (enabled/ss3d disabled/unavailable).
|
||||||
|
*
|
||||||
|
* @param[out] outQtmStatus Where to write the QTM status to.
|
||||||
|
* @return Always 0.
|
||||||
|
*/
|
||||||
|
Result QTMS_GetQtmStatus(QtmStatus *outQtmStatus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the current QTM status (enabled/ss3d disabled/unavailable). Also sets or clear the
|
||||||
|
* "blacklisted" flag returned by \ref QTMU_IsCurrentAppBlacklisted.
|
||||||
|
*
|
||||||
|
* @param qtmStatus QTM status to set. If equal to \ref QTM_STATUS_UNAVAILABLE, sets the "blacklisted" flag,
|
||||||
|
* otherwise clears it.
|
||||||
|
* @return `0xE0E18002` if enum value is invalid, otherwise 0 (success).
|
||||||
|
* @note System settings uses this to disable super-stable 3D, and NS to "blacklist" (make QTM unavailable)
|
||||||
|
* specific applications.
|
||||||
|
*/
|
||||||
|
Result QTMS_SetQtmStatus(QtmStatus qtmStatus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called by GSP's LCD driver to signal 2D<>3D mode change
|
||||||
|
* @param newMode 0 for 2D, 1 for 800px 2D (unused for this function, same as 0), 2 for 3D
|
||||||
|
* @return Always 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMSP_NotifyTopLcdModeChange(u8 newMode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called by GSP's LCD driver during top LCD power-on to signal to QTM that it may power on
|
||||||
|
* and/or reconfigure then use the TI TCA6416A expander. In the process, QTM re-creates its
|
||||||
|
* expander thread.
|
||||||
|
* @return Always 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMSP_NotifyTopLcdPowerOn(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called by GSP's LCD driver to know whether or not QTM's expander thread is using
|
||||||
|
* the TI TCA6416A expander; it is waiting for this to become true/false during LCD
|
||||||
|
* power on/power off to proceed. Always false on N2DSXL.
|
||||||
|
* @param[out] outActive Where to write the "in use" status to.
|
||||||
|
* @return Always 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMSP_IsExpanderInUse(bool *outActive);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called by GSP's LCD driver during top LCD power-on to signal to QTM that it needs to
|
||||||
|
* switch the parallax barrier state to a 2D state (all-transparent mask). Causes QTM's
|
||||||
|
* expander thread to exit, relinquishing its `i2c::QTM` session with it.
|
||||||
|
* @return Always 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMSP_NotifyTopLcdPowerOff(void);
|
||||||
|
117
libctru/include/3ds/services/qtmc.h
Normal file
117
libctru/include/3ds/services/qtmc.h
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* @file qtmc.h
|
||||||
|
* @brief QTM Hardware Check service.
|
||||||
|
*
|
||||||
|
* Allows direct control over the parallax barrier's pattern and polarity phase through the
|
||||||
|
* TI TCA6416A I2C->Parallel expander, as well as control over IR LED state even when camera
|
||||||
|
* is used by user.
|
||||||
|
*
|
||||||
|
* TI TCA6416A I2C->Parallel expander is located on bus I2C1 (PA 0x10161000) device ID 0x40.
|
||||||
|
*
|
||||||
|
* The top screen parallax barrier was covered by patent US20030234980A1.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes `qtm:c`.
|
||||||
|
* Only 3 sessions (2 until 9.3.0 sysupdate) for ALL services COMBINED, including the main
|
||||||
|
* services, can be open at a time.
|
||||||
|
*/
|
||||||
|
Result qtmcInit(void);
|
||||||
|
|
||||||
|
/// Exits `qtm:c`.
|
||||||
|
void qtmcExit(void);
|
||||||
|
|
||||||
|
/// Returns a pointer to the current `qtm:c` session handle.
|
||||||
|
Handle *qtmcGetSessionHandle(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Starts the QTM Hardware Check API. This must be called before using any other `qtm:c` command,
|
||||||
|
* and causes barrier pattern to be overriden by what was last set in \ref QTMC_SetBarrierPattern,
|
||||||
|
* **even in 2D mode**. Also allows IR LED state to be overridden even if user uses the inner camera.
|
||||||
|
* @return `0xD82183F9` if already started, otherwise 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMC_StartHardwareCheck(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stops the QTM Hardware Check API. Restore normal barrier and IR LED management behavior.
|
||||||
|
* @return `0xD82183F8` if API not started, otherwise 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMC_StopHardwareCheck(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the parallax barrier's mask pattern and polarity phase (12+1 bits).
|
||||||
|
*
|
||||||
|
* Bit11 to 0 correspond to a repeating barrier mask pattern, 0 meaning the corresponding mask unit is
|
||||||
|
* transparent and 1 that it is opaque. The direction is: left->right corresponds to MSB->LSB.
|
||||||
|
*
|
||||||
|
* Bit12 is the polarity bit.
|
||||||
|
*
|
||||||
|
* QTM's expander management thread repeatedly writes (on every loop iteration) the current mask pattern
|
||||||
|
* plus polarity bit, whether it is normally set or overridden by `qtm:c`, then on the following set,
|
||||||
|
* negates both (it writes pattern ^ 0x1FFF). This is done at all times, even it 2D mode.
|
||||||
|
*
|
||||||
|
* The register being written to are regId 0x02 and 0x03 (output ports).
|
||||||
|
* TI TCA6416A I2C->Parallel expander is located on bus I2C1 (PA 0x10161000) device ID 0x40.
|
||||||
|
*
|
||||||
|
* This function has no effect on N2DSXL.
|
||||||
|
*
|
||||||
|
* @param pattern Barrier mask pattern (bit12: polarity, bit11-0: 12-bit mask pattern)
|
||||||
|
* @return `0xD82183F8` if API not started, otherwise 0 (success).
|
||||||
|
* @see Patent US20030234980A1 for a description of parallax barriers.
|
||||||
|
* @example The mask pattern used for super-stable 3D are as follows (position 0 to 11):
|
||||||
|
*
|
||||||
|
* 000011111100
|
||||||
|
* 000001111110
|
||||||
|
* 000000111111
|
||||||
|
* 100000011111
|
||||||
|
* 110000001111
|
||||||
|
* 111000000111
|
||||||
|
* 111100000011
|
||||||
|
* 111110000001
|
||||||
|
* 111111000000
|
||||||
|
* 011111100000
|
||||||
|
* 001111110000
|
||||||
|
* 000111111000
|
||||||
|
*
|
||||||
|
* When SS3D is disabled (ie. it tries to match O3DS behavior), then pattern becomes:
|
||||||
|
* 111100000111
|
||||||
|
* Notice that the slit width is reduced from 6 to 5 units there.
|
||||||
|
*
|
||||||
|
* For 2D it is all-zero:
|
||||||
|
* 000000000000
|
||||||
|
*
|
||||||
|
* 2D pattern is automatically set on QTM process init and exit.
|
||||||
|
*/
|
||||||
|
Result QTMC_SetBarrierPattern(u32 pattern);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Waits for the expander management thread to (re)initalize the TI TCA6416A I2C->Parallel expander,
|
||||||
|
* then checks if that expander is behaving as expected (responds with the port direction config
|
||||||
|
* it has been configured with): it checks whether all ports have been configured as outputs.
|
||||||
|
*
|
||||||
|
* On N2DSXL, this function waits forever and never returns.
|
||||||
|
*
|
||||||
|
* In detail, the hardware init procedure for the expander is as follows (as done by the expander mgmt. thread):
|
||||||
|
* - configure enable expander pin on SoC: set GPIO3.bit11 to OUTPUT, then set to 1
|
||||||
|
* - on the expander (I2C1 deviceId 0x40), set all ports to OUTPUT (regId 0x06, 0x07)
|
||||||
|
* - on the expander, write 0 (all-transparent mask/2D) to the data registers (regId 0x02, 0x03)
|
||||||
|
*
|
||||||
|
* @param[out] outWorking Where to write the working status to. If true, expander is present working.
|
||||||
|
* If false, the expander is present but is misbehaving. If the function does not
|
||||||
|
* return, then expander is missing (e.g. on N2DSXL).
|
||||||
|
* @return `0xD82183F8` if API not started, otherwise 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMC_WaitAndCheckExpanderWorking(bool *outWorking);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Temporarily overrides IR LED state. Requires "manual control" from `qtm:u` to be disabled, and has
|
||||||
|
* lower priority than it. Same implementation as \ref QTMS_SetIrLedStatusOverride.
|
||||||
|
*
|
||||||
|
* @param on Whether to turn the IR LED on or off.
|
||||||
|
* @return `0xD82183F8` if API not started, `0xC8A18005` if manual control was enabled or if the operation failed,
|
||||||
|
* or `0xC8A18009` if QTM is unavailable (in particular, QTM is always unavailable on N2DSXL). Otherwise, 0 (success).
|
||||||
|
*/
|
||||||
|
Result QTMC_SetIrLedStatusOverride(bool on);
|
@ -16,7 +16,7 @@ typedef enum
|
|||||||
{
|
{
|
||||||
NETOPT_MAC_ADDRESS = 0x1004, ///< The mac address of the interface (u32 mac[6])
|
NETOPT_MAC_ADDRESS = 0x1004, ///< The mac address of the interface (u32 mac[6])
|
||||||
NETOPT_ARP_TABLE = 0x3002, ///< The ARP table @see SOCU_ARPTableEntry
|
NETOPT_ARP_TABLE = 0x3002, ///< The ARP table @see SOCU_ARPTableEntry
|
||||||
NETOPT_IP_INFO = 0x4003, ///< The cureent IP setup @see SOCU_IPInfo
|
NETOPT_IP_INFO = 0x4003, ///< The current IP setup @see SOCU_IPInfo
|
||||||
NETOPT_IP_MTU = 0x4004, ///< The value of the IP MTU (u32)
|
NETOPT_IP_MTU = 0x4004, ///< The value of the IP MTU (u32)
|
||||||
NETOPT_ROUTING_TABLE = 0x4006, ///< The routing table @see SOCU_RoutingTableEntry
|
NETOPT_ROUTING_TABLE = 0x4006, ///< The routing table @see SOCU_RoutingTableEntry
|
||||||
NETOPT_UDP_NUMBER = 0x8002, ///< The number of sockets in the UDP table (u32)
|
NETOPT_UDP_NUMBER = 0x8002, ///< The number of sockets in the UDP table (u32)
|
||||||
|
@ -497,9 +497,8 @@ typedef enum {
|
|||||||
/// Information on address space for process. All sizes are in pages (0x1000 bytes)
|
/// Information on address space for process. All sizes are in pages (0x1000 bytes)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u8 name[8]; ///< ASCII name of codeset
|
u8 name[8]; ///< ASCII name of codeset
|
||||||
u16 unk1;
|
u16 version; ///< Version field of codeset (unused)
|
||||||
u16 unk2;
|
u16 padding[3]; ///< Padding
|
||||||
u32 unk3;
|
|
||||||
u32 text_addr; ///< .text start address
|
u32 text_addr; ///< .text start address
|
||||||
u32 text_size; ///< .text number of pages
|
u32 text_size; ///< .text number of pages
|
||||||
u32 ro_addr; ///< .rodata start address
|
u32 ro_addr; ///< .rodata start address
|
||||||
@ -509,9 +508,9 @@ typedef struct {
|
|||||||
u32 text_size_total; ///< total pages for .text (aligned)
|
u32 text_size_total; ///< total pages for .text (aligned)
|
||||||
u32 ro_size_total; ///< total pages for .rodata (aligned)
|
u32 ro_size_total; ///< total pages for .rodata (aligned)
|
||||||
u32 rw_size_total; ///< total pages for .data, .bss (aligned)
|
u32 rw_size_total; ///< total pages for .data, .bss (aligned)
|
||||||
u32 unk4;
|
u32 padding2; ///< Padding
|
||||||
u64 program_id; ///< Program ID
|
u64 program_id; ///< Program ID
|
||||||
} CodeSetInfo;
|
} CodeSetHeader;
|
||||||
|
|
||||||
/// Information for the main thread of a process.
|
/// Information for the main thread of a process.
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -527,7 +526,7 @@ typedef struct
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the thread local storage buffer.
|
* @brief Gets the thread local storage buffer.
|
||||||
* @return The thread local storage bufger.
|
* @return The thread local storage buffer.
|
||||||
*/
|
*/
|
||||||
static inline void* getThreadLocalStorage(void)
|
static inline void* getThreadLocalStorage(void)
|
||||||
{
|
{
|
||||||
@ -538,7 +537,7 @@ static inline void* getThreadLocalStorage(void)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the thread command buffer.
|
* @brief Gets the thread command buffer.
|
||||||
* @return The thread command bufger.
|
* @return The thread command buffer.
|
||||||
*/
|
*/
|
||||||
static inline u32* getThreadCommandBuffer(void)
|
static inline u32* getThreadCommandBuffer(void)
|
||||||
{
|
{
|
||||||
@ -547,7 +546,7 @@ static inline u32* getThreadCommandBuffer(void)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the thread static buffer.
|
* @brief Gets the thread static buffer.
|
||||||
* @return The thread static bufger.
|
* @return The thread static buffer.
|
||||||
*/
|
*/
|
||||||
static inline u32* getThreadStaticBuffers(void)
|
static inline u32* getThreadStaticBuffers(void)
|
||||||
{
|
{
|
||||||
@ -699,7 +698,7 @@ Result svcQueryProcessMemory(MemInfo* info, PageInfo* out, Handle process, u32 a
|
|||||||
Result svcOpenProcess(Handle* process, u32 processId);
|
Result svcOpenProcess(Handle* process, u32 processId);
|
||||||
|
|
||||||
/// Exits the current process.
|
/// Exits the current process.
|
||||||
void __attribute__((noreturn)) svcExitProcess();
|
void svcExitProcess(void) __attribute__((noreturn));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Terminates a process.
|
* @brief Terminates a process.
|
||||||
@ -756,23 +755,24 @@ Result svcCreatePort(Handle* portServer, Handle* portClient, const char* name, s
|
|||||||
Result svcConnectToPort(volatile Handle* out, const char* portName);
|
Result svcConnectToPort(volatile Handle* out, const char* portName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets up virtual address space for a new process
|
* @brief Sets up virtual address space for a new process.
|
||||||
* @param[out] out Pointer to output the code set handle to.
|
* @param[out] out Pointer to output the codeset handle to.
|
||||||
* @param info Description for setting up the addresses
|
* @param info Codeset header, contains process name, titleId and segment info.
|
||||||
* @param code_ptr Pointer to .text in shared memory
|
* @param textSegmentLma Address of executable segment in caller's address space.
|
||||||
* @param ro_ptr Pointer to .rodata in shared memory
|
* @param roSegmentLma Address of read-only segment in caller's address space.
|
||||||
* @param data_ptr Pointer to .data in shared memory
|
* @param dataSegmentLma Address of read-write segment in caller's address space.
|
||||||
|
* @note On success, the provided segments are unmapped from the caller's address space.
|
||||||
*/
|
*/
|
||||||
Result svcCreateCodeSet(Handle* out, const CodeSetInfo *info, void* code_ptr, void* ro_ptr, void* data_ptr);
|
Result svcCreateCodeSet(Handle* out, const CodeSetHeader* info, u32 textSegmentLma, u32 roSegmentLma, u32 dataSegmentLma);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets up virtual address space for a new process
|
* @brief Create a new process.
|
||||||
* @param[out] out Pointer to output the process handle to.
|
* @param[out] out Pointer to output the process handle to.
|
||||||
* @param codeset Codeset created for this process
|
* @param codeset Codeset created for this process.
|
||||||
* @param arm11kernelcaps ARM11 Kernel Capabilities from exheader
|
* @param arm11KernelCaps Arm11 Kernel Capabilities from exheader.
|
||||||
* @param arm11kernelcaps_num Number of kernel capabilities
|
* @param numArm11KernelCaps Number of kernel capabilities.
|
||||||
*/
|
*/
|
||||||
Result svcCreateProcess(Handle* out, Handle codeset, const u32 *arm11kernelcaps, u32 arm11kernelcaps_num);
|
Result svcCreateProcess(Handle* out, Handle codeset, const u32* arm11KernelCaps, s32 numArm11KernelCaps);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets a process's affinity mask.
|
* @brief Gets a process's affinity mask.
|
||||||
|
@ -42,6 +42,12 @@ static inline void __dmb(void)
|
|||||||
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory");
|
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs an Instruction Synchronization Barrier (officially "flush prefetch buffer") operation.
|
||||||
|
static inline void __isb(void)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__("mcr p15, 0, %[val], c7, c5, 4" :: [val] "r" (0) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs a clrex operation.
|
/// Performs a clrex operation.
|
||||||
static inline void __clrex(void)
|
static inline void __clrex(void)
|
||||||
{
|
{
|
||||||
|
@ -117,4 +117,7 @@ static inline void threadOnException(ExceptionHandler handler, void* stack_top,
|
|||||||
*(u32*)(tls + 0x40) = (u32)handler;
|
*(u32*)(tls + 0x40) = (u32)handler;
|
||||||
*(u32*)(tls + 0x44) = (u32)stack_top;
|
*(u32*)(tls + 0x44) = (u32)stack_top;
|
||||||
*(u32*)(tls + 0x48) = (u32)exception_data;
|
*(u32*)(tls + 0x48) = (u32)exception_data;
|
||||||
|
|
||||||
|
__dsb();
|
||||||
|
__isb();
|
||||||
}
|
}
|
||||||
|
@ -47,16 +47,16 @@ typedef void (*voidfn)(void);
|
|||||||
#define BIT(n) (1U<<(n))
|
#define BIT(n) (1U<<(n))
|
||||||
|
|
||||||
/// Aligns a struct (and other types?) to m, making sure that the size of the struct is a multiple of m.
|
/// Aligns a struct (and other types?) to m, making sure that the size of the struct is a multiple of m.
|
||||||
#define ALIGN(m) __attribute__((aligned(m)))
|
#define CTR_ALIGN(m) __attribute__((aligned(m)))
|
||||||
/// Packs a struct (and other types?) so it won't include padding bytes.
|
/// Packs a struct (and other types?) so it won't include padding bytes.
|
||||||
#define PACKED __attribute__((packed))
|
#define CTR_PACKED __attribute__((packed))
|
||||||
|
|
||||||
#ifndef LIBCTRU_NO_DEPRECATION
|
#ifndef CTR_NO_DEPRECATION
|
||||||
/// Flags a function as deprecated.
|
/// Flags a function as deprecated.
|
||||||
#define DEPRECATED __attribute__ ((deprecated))
|
#define CTR_DEPRECATED __attribute__ ((deprecated))
|
||||||
#else
|
#else
|
||||||
/// Flags a function as deprecated.
|
/// Flags a function as deprecated.
|
||||||
#define DEPRECATED
|
#define CTR_DEPRECATED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Structure representing CPU registers
|
/// Structure representing CPU registers
|
||||||
@ -71,7 +71,7 @@ typedef struct {
|
|||||||
/// Structure representing FPU registers
|
/// Structure representing FPU registers
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
struct PACKED { double d[16]; }; ///< d0-d15.
|
struct CTR_PACKED { double d[16]; }; ///< d0-d15.
|
||||||
float s[32]; ///< s0-s31.
|
float s[32]; ///< s0-s31.
|
||||||
};
|
};
|
||||||
u32 fpscr; ///< fpscr.
|
u32 fpscr; ///< fpscr.
|
||||||
|
@ -9,14 +9,13 @@
|
|||||||
#define TRY_AGAIN 4
|
#define TRY_AGAIN 4
|
||||||
|
|
||||||
struct hostent {
|
struct hostent {
|
||||||
char *h_name;
|
char *h_name; /* official name of host */
|
||||||
char **h_aliases;
|
char **h_aliases; /* alias list */
|
||||||
int h_addrtype;
|
uint16_t h_addrtype; /* host address type */
|
||||||
int h_length;
|
uint16_t h_length; /* length of address */
|
||||||
char **h_addr_list;
|
char **h_addr_list; /* list of addresses from name server */
|
||||||
char *h_addr;
|
|
||||||
};
|
};
|
||||||
|
#define h_addr h_addr_list[0] /* for backward compatibility */
|
||||||
|
|
||||||
#define AI_PASSIVE 0x01
|
#define AI_PASSIVE 0x01
|
||||||
#define AI_CANONNAME 0x02
|
#define AI_CANONNAME 0x02
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <3ds/types.h>
|
|
||||||
|
|
||||||
#define POLLIN 0x01
|
#define POLLIN 0x01
|
||||||
#define POLLPRI 0x02
|
#define POLLPRI 0x02
|
||||||
#define POLLHUP 0x04 // unknown ???
|
#define POLLHUP 0x04 // unknown ???
|
||||||
@ -9,7 +7,7 @@
|
|||||||
#define POLLOUT 0x10
|
#define POLLOUT 0x10
|
||||||
#define POLLNVAL 0x20
|
#define POLLNVAL 0x20
|
||||||
|
|
||||||
typedef u32 nfds_t;
|
typedef unsigned int nfds_t;
|
||||||
|
|
||||||
struct pollfd
|
struct pollfd
|
||||||
{
|
{
|
||||||
|
@ -44,6 +44,8 @@
|
|||||||
|
|
||||||
#define SO_BROADCAST 0x0000 // unrequired, included for compatibility
|
#define SO_BROADCAST 0x0000 // unrequired, included for compatibility
|
||||||
|
|
||||||
|
#define _SOCKLEN_T_DECLARED
|
||||||
|
|
||||||
typedef uint32_t socklen_t;
|
typedef uint32_t socklen_t;
|
||||||
typedef uint16_t sa_family_t;
|
typedef uint16_t sa_family_t;
|
||||||
|
|
||||||
|
48
libctru/source/3dslink.c
Normal file
48
libctru/source/3dslink.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <3ds/3dslink.h>
|
||||||
|
|
||||||
|
struct in_addr __3dslink_host = {0};
|
||||||
|
|
||||||
|
static int sock = -1;
|
||||||
|
|
||||||
|
int link3dsConnectToHost(bool redirStdout, bool redirStderr)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
struct sockaddr_in srv_addr;
|
||||||
|
|
||||||
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (!sock) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero(&srv_addr, sizeof srv_addr);
|
||||||
|
srv_addr.sin_family = AF_INET;
|
||||||
|
srv_addr.sin_addr = __3dslink_host;
|
||||||
|
srv_addr.sin_port = htons(LINK3DS_COMM_PORT);
|
||||||
|
|
||||||
|
ret = connect(sock, (struct sockaddr *) &srv_addr, sizeof(srv_addr));
|
||||||
|
if (ret != 0) {
|
||||||
|
close(sock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirStdout) {
|
||||||
|
// redirect stdout
|
||||||
|
fflush(stdout);
|
||||||
|
dup2(sock, STDOUT_FILENO);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirStderr) {
|
||||||
|
// redirect stderr
|
||||||
|
fflush(stderr);
|
||||||
|
dup2(sock, STDERR_FILENO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
}
|
@ -27,18 +27,9 @@ static bool linearInit()
|
|||||||
|
|
||||||
void* linearMemAlign(size_t size, size_t alignment)
|
void* linearMemAlign(size_t size, size_t alignment)
|
||||||
{
|
{
|
||||||
// Enforce minimum alignment
|
// Convert alignment to shift
|
||||||
if (alignment < 16)
|
int shift = alignmentToShift(alignment);
|
||||||
alignment = 16;
|
if (shift < 0)
|
||||||
|
|
||||||
// Convert alignment to shift amount
|
|
||||||
int shift;
|
|
||||||
for (shift = 4; shift < 32; shift ++)
|
|
||||||
{
|
|
||||||
if ((1U<<shift) == alignment)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (shift == 32) // Invalid alignment
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Initialize the pool if it is not ready
|
// Initialize the pool if it is not ready
|
||||||
|
@ -2,6 +2,15 @@
|
|||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static inline int alignmentToShift(size_t alignment)
|
||||||
|
{
|
||||||
|
if (alignment < 16)
|
||||||
|
alignment = 16;
|
||||||
|
else if (alignment & (alignment - 1))
|
||||||
|
return -1; // Not a power of two
|
||||||
|
return __builtin_ffs(alignment)-1;
|
||||||
|
}
|
||||||
|
|
||||||
struct MemChunk
|
struct MemChunk
|
||||||
{
|
{
|
||||||
u8* addr;
|
u8* addr;
|
||||||
|
@ -9,60 +9,107 @@ extern "C"
|
|||||||
#include "mem_pool.h"
|
#include "mem_pool.h"
|
||||||
#include "addrmap.h"
|
#include "addrmap.h"
|
||||||
|
|
||||||
static MemPool sVramPool;
|
static MemPool sVramPoolA, sVramPoolB;
|
||||||
|
|
||||||
static bool vramInit()
|
static bool vramInit()
|
||||||
{
|
{
|
||||||
auto blk = MemBlock::Create((u8*)OS_VRAM_VADDR, OS_VRAM_SIZE);
|
if (sVramPoolA.Ready() || sVramPoolB.Ready())
|
||||||
if (blk)
|
|
||||||
{
|
|
||||||
sVramPool.AddBlock(blk);
|
|
||||||
rbtree_init(&sAddrMap, addrMapNodeComparator);
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
auto blkA = MemBlock::Create((u8*)OS_VRAM_VADDR, OS_VRAM_SIZE/2);
|
||||||
|
if (!blkA)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto blkB = MemBlock::Create((u8*)OS_VRAM_VADDR + OS_VRAM_SIZE/2, OS_VRAM_SIZE/2);
|
||||||
|
if (!blkB)
|
||||||
|
{
|
||||||
|
free(blkA);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
sVramPoolA.AddBlock(blkA);
|
||||||
|
sVramPoolB.AddBlock(blkB);
|
||||||
|
rbtree_init(&sAddrMap, addrMapNodeComparator);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemPool* vramPoolForAddr(void* addr)
|
||||||
|
{
|
||||||
|
uintptr_t addr_ = (uintptr_t)addr;
|
||||||
|
if (addr_ < OS_VRAM_VADDR)
|
||||||
|
return nullptr;
|
||||||
|
if (addr_ < OS_VRAM_VADDR + OS_VRAM_SIZE/2)
|
||||||
|
return &sVramPoolA;
|
||||||
|
if (addr_ < OS_VRAM_VADDR + OS_VRAM_SIZE)
|
||||||
|
return &sVramPoolB;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* vramAlloc(size_t size)
|
||||||
|
{
|
||||||
|
return vramMemAlignAt(size, 0x80, VRAM_ALLOC_ANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* vramAllocAt(size_t size, vramAllocPos pos)
|
||||||
|
{
|
||||||
|
return vramMemAlignAt(size, 0x80, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* vramMemAlign(size_t size, size_t alignment)
|
void* vramMemAlign(size_t size, size_t alignment)
|
||||||
{
|
{
|
||||||
// Enforce minimum alignment
|
return vramMemAlignAt(size, alignment, VRAM_ALLOC_ANY);
|
||||||
if (alignment < 16)
|
}
|
||||||
alignment = 16;
|
|
||||||
|
|
||||||
// Convert alignment to shift amount
|
void* vramMemAlignAt(size_t size, size_t alignment, vramAllocPos pos)
|
||||||
int shift;
|
{
|
||||||
for (shift = 4; shift < 32; shift ++)
|
// Convert alignment to shift
|
||||||
{
|
int shift = alignmentToShift(alignment);
|
||||||
if ((1U<<shift) == alignment)
|
if (shift < 0)
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (shift == 32) // Invalid alignment
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Initialize the pool if it is not ready
|
// Initialize the allocator if it is not ready
|
||||||
if (!sVramPool.Ready() && !vramInit())
|
if (!vramInit())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Allocate the chunk
|
// Allocate the chunk
|
||||||
MemChunk chunk;
|
MemChunk chunk;
|
||||||
if (!sVramPool.Allocate(chunk, size, shift))
|
bool didAlloc = false;
|
||||||
|
switch (pos & VRAM_ALLOC_ANY)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
case VRAM_ALLOC_A:
|
||||||
|
didAlloc = sVramPoolA.Allocate(chunk, size, shift);
|
||||||
|
break;
|
||||||
|
case VRAM_ALLOC_B:
|
||||||
|
didAlloc = sVramPoolB.Allocate(chunk, size, shift);
|
||||||
|
break;
|
||||||
|
case VRAM_ALLOC_ANY:
|
||||||
|
{
|
||||||
|
// Crude attempt at "load balancing" VRAM A and B
|
||||||
|
bool prefer_a = sVramPoolA.GetFreeSpace() >= sVramPoolB.GetFreeSpace();
|
||||||
|
MemPool& firstPool = prefer_a ? sVramPoolA : sVramPoolB;
|
||||||
|
MemPool& secondPool = prefer_a ? sVramPoolB : sVramPoolA;
|
||||||
|
|
||||||
|
didAlloc = firstPool.Allocate(chunk, size, shift);
|
||||||
|
if (!didAlloc) didAlloc = secondPool.Allocate(chunk, size, shift);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!didAlloc)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto node = newNode(chunk);
|
auto node = newNode(chunk);
|
||||||
if (!node)
|
if (!node)
|
||||||
{
|
{
|
||||||
sVramPool.Deallocate(chunk);
|
vramPoolForAddr(chunk.addr)->Deallocate(chunk);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (rbtree_insert(&sAddrMap, &node->node));
|
if (rbtree_insert(&sAddrMap, &node->node));
|
||||||
return chunk.addr;
|
return chunk.addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* vramAlloc(size_t size)
|
|
||||||
{
|
|
||||||
return vramMemAlign(size, 0x80);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* vramRealloc(void* mem, size_t size)
|
void* vramRealloc(void* mem, size_t size)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
@ -81,7 +128,7 @@ void vramFree(void* mem)
|
|||||||
if (!node) return;
|
if (!node) return;
|
||||||
|
|
||||||
// Free the chunk
|
// Free the chunk
|
||||||
sVramPool.Deallocate(node->chunk);
|
vramPoolForAddr(mem)->Deallocate(node->chunk);
|
||||||
|
|
||||||
// Free the node
|
// Free the node
|
||||||
delNode(node);
|
delNode(node);
|
||||||
@ -89,5 +136,5 @@ void vramFree(void* mem)
|
|||||||
|
|
||||||
u32 vramSpaceFree()
|
u32 vramSpaceFree()
|
||||||
{
|
{
|
||||||
return sVramPool.GetFreeSpace();
|
return sVramPoolA.GetFreeSpace() + sVramPoolB.GetFreeSpace();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#include <3ds.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/synchronization.h>
|
#include <3ds/synchronization.h>
|
||||||
@ -11,7 +10,7 @@ void errorInit(errorConf* err, errorType type, CFG_Language lang)
|
|||||||
{
|
{
|
||||||
memset(err, 0, sizeof(*err));
|
memset(err, 0, sizeof(*err));
|
||||||
err->type = type;
|
err->type = type;
|
||||||
err->useLanguage = lang;
|
err->useLanguage = lang + 1;
|
||||||
err->upperScreenFlag = ERROR_NORMAL;
|
err->upperScreenFlag = ERROR_NORMAL;
|
||||||
err->eulaVersion = 0;
|
err->eulaVersion = 0;
|
||||||
err->homeButton = true;
|
err->homeButton = true;
|
||||||
@ -40,7 +39,7 @@ static void errorConvertToUTF16(u16* out, const char* in, size_t max)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
out[units] = 0;
|
out[units] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void errorText(errorConf *err, const char* text)
|
void errorText(errorConf *err, const char* text)
|
||||||
@ -49,6 +48,6 @@ void errorText(errorConf *err, const char* text)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void errorDisp(errorConf* err)
|
void errorDisp(errorConf* err)
|
||||||
{
|
{
|
||||||
aptLaunchLibraryApplet(APPID_ERROR, err, sizeof(*err), 0);
|
aptLaunchLibraryApplet(APPID_ERROR, err, sizeof(*err), 0);
|
||||||
}
|
}
|
||||||
|
@ -205,9 +205,11 @@ archive_fixpath(struct _reent *r,
|
|||||||
strncpy(__ctru_dev_path_buf, path, PATH_MAX);
|
strncpy(__ctru_dev_path_buf, path, PATH_MAX);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
size_t cwdlen = strlen(dev->cwd);
|
||||||
strncpy(__ctru_dev_path_buf, dev->cwd, PATH_MAX);
|
strncpy(__ctru_dev_path_buf, dev->cwd, PATH_MAX);
|
||||||
__ctru_dev_path_buf[PATH_MAX] = '\0';
|
__ctru_dev_path_buf[PATH_MAX] = '\0';
|
||||||
strncat(__ctru_dev_path_buf, path, PATH_MAX);
|
strncat(__ctru_dev_path_buf, "/", PATH_MAX - cwdlen);
|
||||||
|
strncat(__ctru_dev_path_buf, path, PATH_MAX - cwdlen - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(__ctru_dev_path_buf[PATH_MAX] != 0)
|
if(__ctru_dev_path_buf[PATH_MAX] != 0)
|
||||||
@ -980,12 +982,29 @@ archive_rename(struct _reent *r,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rc = FSUSER_RenameFile(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
|
rc = FSUSER_RenameFile(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
|
||||||
if(R_SUCCEEDED(rc))
|
/* if the file at the target destination exists, overwrite it */
|
||||||
return 0;
|
if(R_FAILED(rc) && R_DESCRIPTION(rc) == RD_ALREADY_EXISTS) {
|
||||||
|
rc = FSUSER_DeleteFile(sourceDevice->archive, fs_path_new);
|
||||||
|
if(R_FAILED(rc)) {
|
||||||
|
r->_errno = archive_translate_error(rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
rc = FSUSER_RenameFile(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
|
||||||
|
if(R_SUCCEEDED(rc)) return 0;
|
||||||
|
} else if(R_SUCCEEDED(rc)) return 0;
|
||||||
|
|
||||||
rc = FSUSER_RenameDirectory(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
|
rc = FSUSER_RenameDirectory(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
|
||||||
if(R_SUCCEEDED(rc))
|
/* if the directory at the target destination exists, overwrite it */
|
||||||
return 0;
|
if(R_FAILED(rc) && R_DESCRIPTION(rc) == RD_ALREADY_EXISTS) {
|
||||||
|
/* only overwrite empty directories */
|
||||||
|
rc = FSUSER_DeleteDirectory(sourceDevice-> archive, fs_path_new);
|
||||||
|
if(R_FAILED(rc)) {
|
||||||
|
r->_errno = archive_translate_error(rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
rc = FSUSER_RenameDirectory(sourceDevice->archive, fs_path_old, sourceDevice->archive, fs_path_new);
|
||||||
|
if(R_SUCCEEDED(rc)) return 0;
|
||||||
|
} else if(R_SUCCEEDED(rc)) return 0;
|
||||||
|
|
||||||
r->_errno = archive_translate_error(rc);
|
r->_errno = archive_translate_error(rc);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -37,6 +37,16 @@ static u16 colorTable[] = {
|
|||||||
RGB8_to_565( 96, 96, 96), // faint white
|
RGB8_to_565( 96, 96, 96), // faint white
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const u8 colorCube[] = {
|
||||||
|
0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u8 grayScale[] = {
|
||||||
|
0x08, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e,
|
||||||
|
0x58, 0x62, 0x6c, 0x76, 0x80, 0x8a, 0x94, 0x9e,
|
||||||
|
0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee,
|
||||||
|
};
|
||||||
|
|
||||||
PrintConsole defaultConsole =
|
PrintConsole defaultConsole =
|
||||||
{
|
{
|
||||||
//Font:
|
//Font:
|
||||||
@ -72,7 +82,7 @@ void consolePrintChar(int c);
|
|||||||
void consoleDrawChar(int c);
|
void consoleDrawChar(int c);
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
static void consoleCls(char mode) {
|
static void consoleCls(int mode) {
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -80,8 +90,7 @@ static void consoleCls(char mode) {
|
|||||||
|
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case '[':
|
case 0:
|
||||||
case '0':
|
|
||||||
{
|
{
|
||||||
colTemp = currentConsole->cursorX ;
|
colTemp = currentConsole->cursorX ;
|
||||||
rowTemp = currentConsole->cursorY ;
|
rowTemp = currentConsole->cursorY ;
|
||||||
@ -93,7 +102,7 @@ static void consoleCls(char mode) {
|
|||||||
currentConsole->cursorY = rowTemp;
|
currentConsole->cursorY = rowTemp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '1':
|
case 1:
|
||||||
{
|
{
|
||||||
colTemp = currentConsole->cursorX ;
|
colTemp = currentConsole->cursorX ;
|
||||||
rowTemp = currentConsole->cursorY ;
|
rowTemp = currentConsole->cursorY ;
|
||||||
@ -108,7 +117,7 @@ static void consoleCls(char mode) {
|
|||||||
currentConsole->cursorY = rowTemp;
|
currentConsole->cursorY = rowTemp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '2':
|
case 2:
|
||||||
{
|
{
|
||||||
currentConsole->cursorY = 0;
|
currentConsole->cursorY = 0;
|
||||||
currentConsole->cursorX = 0;
|
currentConsole->cursorX = 0;
|
||||||
@ -124,7 +133,7 @@ static void consoleCls(char mode) {
|
|||||||
gfxFlushBuffers();
|
gfxFlushBuffers();
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
static void consoleClearLine(char mode) {
|
static void consoleClearLine(int mode) {
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -132,8 +141,7 @@ static void consoleClearLine(char mode) {
|
|||||||
|
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case '[':
|
case 0:
|
||||||
case '0':
|
|
||||||
{
|
{
|
||||||
colTemp = currentConsole->cursorX ;
|
colTemp = currentConsole->cursorX ;
|
||||||
|
|
||||||
@ -145,7 +153,7 @@ static void consoleClearLine(char mode) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '1':
|
case 1:
|
||||||
{
|
{
|
||||||
colTemp = currentConsole->cursorX ;
|
colTemp = currentConsole->cursorX ;
|
||||||
|
|
||||||
@ -159,7 +167,7 @@ static void consoleClearLine(char mode) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '2':
|
case 2:
|
||||||
{
|
{
|
||||||
colTemp = currentConsole->cursorX ;
|
colTemp = currentConsole->cursorX ;
|
||||||
|
|
||||||
@ -202,6 +210,273 @@ static inline void consolePosition(int x, int y) {
|
|||||||
currentConsole->cursorY = y - 1;
|
currentConsole->cursorY = y - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int movement;
|
||||||
|
} directional;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int y;
|
||||||
|
int x;
|
||||||
|
} absolute;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int type;
|
||||||
|
} clear;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int args[3];
|
||||||
|
int flags;
|
||||||
|
u16 fg;
|
||||||
|
u16 bg;
|
||||||
|
} color;
|
||||||
|
int rawBuf[5];
|
||||||
|
};
|
||||||
|
int argIdx;
|
||||||
|
bool hasArg[3];
|
||||||
|
enum ESC_STATE
|
||||||
|
{
|
||||||
|
ESC_NONE,
|
||||||
|
ESC_START,
|
||||||
|
ESC_BUILDING_UNKNOWN,
|
||||||
|
ESC_BUILDING_FORMAT_UNKNOWN,
|
||||||
|
ESC_BUILDING_FORMAT_FG,
|
||||||
|
ESC_BUILDING_FORMAT_BG,
|
||||||
|
ESC_BUILDING_FORMAT_FG_NONRGB,
|
||||||
|
ESC_BUILDING_FORMAT_BG_NONRGB,
|
||||||
|
ESC_BUILDING_FORMAT_FG_RGB,
|
||||||
|
ESC_BUILDING_FORMAT_BG_RGB,
|
||||||
|
} state;
|
||||||
|
} escapeSeq;
|
||||||
|
|
||||||
|
static void consoleHandleColorEsc(int code)
|
||||||
|
{
|
||||||
|
switch (escapeSeq.state)
|
||||||
|
{
|
||||||
|
case ESC_BUILDING_FORMAT_UNKNOWN:
|
||||||
|
switch (code)
|
||||||
|
{
|
||||||
|
case 0: // reset
|
||||||
|
escapeSeq.color.flags = 0;
|
||||||
|
escapeSeq.color.bg = 0;
|
||||||
|
escapeSeq.color.fg = 7;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // bold
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_COLOR_FAINT;
|
||||||
|
escapeSeq.color.flags |= CONSOLE_COLOR_BOLD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // faint
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_COLOR_BOLD;
|
||||||
|
escapeSeq.color.flags |= CONSOLE_COLOR_FAINT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: // italic
|
||||||
|
escapeSeq.color.flags |= CONSOLE_ITALIC;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: // underline
|
||||||
|
escapeSeq.color.flags |= CONSOLE_UNDERLINE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5: // blink slow
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_BLINK_FAST;
|
||||||
|
escapeSeq.color.flags |= CONSOLE_BLINK_SLOW;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6: // blink fast
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_BLINK_SLOW;
|
||||||
|
escapeSeq.color.flags |= CONSOLE_BLINK_FAST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7: // reverse video
|
||||||
|
escapeSeq.color.flags |= CONSOLE_COLOR_REVERSE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 8: // conceal
|
||||||
|
escapeSeq.color.flags |= CONSOLE_CONCEAL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 9: // crossed-out
|
||||||
|
escapeSeq.color.flags |= CONSOLE_CROSSED_OUT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 21: // bold off
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_COLOR_BOLD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 22: // normal color
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_COLOR_BOLD;
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_COLOR_FAINT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 23: // italic off
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_ITALIC;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 24: // underline off
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_UNDERLINE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 25: // blink off
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_BLINK_SLOW;
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_BLINK_FAST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 27: // reverse off
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_COLOR_REVERSE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 29: // crossed-out off
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_CROSSED_OUT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 30 ... 37: // writing color
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_FG_CUSTOM;
|
||||||
|
escapeSeq.color.fg = code - 30;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 38: // custom foreground color
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_FG;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 39: // reset foreground color
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_FG_CUSTOM;
|
||||||
|
escapeSeq.color.fg = 7;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 40 ... 47: // screen color
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_BG_CUSTOM;
|
||||||
|
escapeSeq.color.bg = code - 40;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 48: // custom background color
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_BG;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 49: // reset background color
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_BG_CUSTOM;
|
||||||
|
escapeSeq.color.fg = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ESC_BUILDING_FORMAT_FG:
|
||||||
|
if (escapeSeq.color.args[0] == 5)
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_FG_NONRGB;
|
||||||
|
else if (escapeSeq.color.args[0] == 2)
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_FG_RGB;
|
||||||
|
else
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
case ESC_BUILDING_FORMAT_BG:
|
||||||
|
if (escapeSeq.color.args[0] == 5)
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_BG_NONRGB;
|
||||||
|
else if (escapeSeq.color.args[0] == 2)
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_BG_RGB;
|
||||||
|
else
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
case ESC_BUILDING_FORMAT_FG_NONRGB:
|
||||||
|
if (code <= 15) {
|
||||||
|
escapeSeq.color.fg = code;
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_FG_CUSTOM;
|
||||||
|
} else if (code <= 231) {
|
||||||
|
code -= 16;
|
||||||
|
unsigned int r = code / 36;
|
||||||
|
unsigned int g = (code - r * 36) / 6;
|
||||||
|
unsigned int b = code - r * 36 - g * 6;
|
||||||
|
|
||||||
|
escapeSeq.color.fg = RGB8_to_565 (colorCube[r], colorCube[g], colorCube[b]);
|
||||||
|
escapeSeq.color.flags |= CONSOLE_FG_CUSTOM;
|
||||||
|
} else if (code <= 255) {
|
||||||
|
code -= 232;
|
||||||
|
|
||||||
|
escapeSeq.color.fg = RGB8_to_565 (grayScale[code], grayScale[code], grayScale[code]);
|
||||||
|
escapeSeq.color.flags |= CONSOLE_FG_CUSTOM;
|
||||||
|
}
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
case ESC_BUILDING_FORMAT_BG_NONRGB:
|
||||||
|
if (code <= 15) {
|
||||||
|
escapeSeq.color.bg = code;
|
||||||
|
escapeSeq.color.flags &= ~CONSOLE_BG_CUSTOM;
|
||||||
|
} else if (code <= 231) {
|
||||||
|
code -= 16;
|
||||||
|
unsigned int r = code / 36;
|
||||||
|
unsigned int g = (code - r * 36) / 6;
|
||||||
|
unsigned int b = code - r * 36 - g * 6;
|
||||||
|
|
||||||
|
escapeSeq.color.bg = RGB8_to_565 (colorCube[r], colorCube[g], colorCube[b]);
|
||||||
|
escapeSeq.color.flags |= CONSOLE_BG_CUSTOM;
|
||||||
|
} else if (code <= 255) {
|
||||||
|
code -= 232;
|
||||||
|
|
||||||
|
escapeSeq.color.bg = RGB8_to_565 (grayScale[code], grayScale[code], grayScale[code]);
|
||||||
|
escapeSeq.color.flags |= CONSOLE_BG_CUSTOM;
|
||||||
|
}
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
case ESC_BUILDING_FORMAT_FG_RGB:
|
||||||
|
escapeSeq.color.fg = RGB8_to_565((unsigned int)escapeSeq.color.args[0], (unsigned int)escapeSeq.color.args[1], (unsigned int)escapeSeq.color.args[2]);
|
||||||
|
escapeSeq.color.flags |= CONSOLE_FG_CUSTOM;
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
case ESC_BUILDING_FORMAT_BG_RGB:
|
||||||
|
escapeSeq.color.bg = RGB8_to_565((unsigned int)escapeSeq.color.args[0], (unsigned int)escapeSeq.color.args[1], (unsigned int)escapeSeq.color.args[2]);
|
||||||
|
escapeSeq.color.flags |= CONSOLE_BG_CUSTOM;
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
escapeSeq.argIdx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void consoleColorStateShift(void)
|
||||||
|
{
|
||||||
|
switch (escapeSeq.state)
|
||||||
|
{
|
||||||
|
case ESC_BUILDING_UNKNOWN:
|
||||||
|
escapeSeq.state = ESC_BUILDING_FORMAT_UNKNOWN;
|
||||||
|
if (escapeSeq.hasArg[0])
|
||||||
|
consoleHandleColorEsc(escapeSeq.color.args[0]);
|
||||||
|
if (escapeSeq.hasArg[1])
|
||||||
|
consoleHandleColorEsc(escapeSeq.color.args[1]);
|
||||||
|
escapeSeq.argIdx = 0;
|
||||||
|
escapeSeq.hasArg[0] = escapeSeq.hasArg[1] = false;
|
||||||
|
break;
|
||||||
|
case ESC_BUILDING_FORMAT_BG:
|
||||||
|
case ESC_BUILDING_FORMAT_FG:
|
||||||
|
case ESC_BUILDING_FORMAT_FG_NONRGB:
|
||||||
|
case ESC_BUILDING_FORMAT_BG_NONRGB:
|
||||||
|
consoleHandleColorEsc(escapeSeq.color.args[0]);
|
||||||
|
escapeSeq.argIdx = 0;
|
||||||
|
escapeSeq.hasArg[0] = escapeSeq.hasArg[1] = false;
|
||||||
|
break;
|
||||||
|
case ESC_BUILDING_FORMAT_FG_RGB:
|
||||||
|
case ESC_BUILDING_FORMAT_BG_RGB:
|
||||||
|
if (escapeSeq.argIdx < 3)
|
||||||
|
escapeSeq.argIdx++;
|
||||||
|
else
|
||||||
|
consoleHandleColorEsc(0); // Nothing passed here because three RGB items
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void consoleColorApply(void)
|
||||||
|
{
|
||||||
|
currentConsole->bg = escapeSeq.color.bg;
|
||||||
|
currentConsole->fg = escapeSeq.color.fg;
|
||||||
|
currentConsole->flags = escapeSeq.color.flags;
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
ssize_t con_write(struct _reent *r,void *fd,const char *ptr, size_t len) {
|
ssize_t con_write(struct _reent *r,void *fd,const char *ptr, size_t len) {
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
@ -220,260 +495,165 @@ ssize_t con_write(struct _reent *r,void *fd,const char *ptr, size_t len) {
|
|||||||
chr = *(tmp++);
|
chr = *(tmp++);
|
||||||
i++; count++;
|
i++; count++;
|
||||||
|
|
||||||
if ( chr == 0x1b && *tmp == '[' ) {
|
switch (escapeSeq.state)
|
||||||
bool escaping = true;
|
{
|
||||||
char *escapeseq = tmp++;
|
case ESC_NONE:
|
||||||
int escapelen = 1;
|
if (chr == 0x1b)
|
||||||
i++; count++;
|
escapeSeq.state = ESC_START;
|
||||||
|
else
|
||||||
do {
|
consolePrintChar(chr);
|
||||||
chr = *(tmp++);
|
break;
|
||||||
i++; count++; escapelen++;
|
case ESC_START:
|
||||||
int parameter, assigned, consumed;
|
if (chr == '[')
|
||||||
|
{
|
||||||
// make sure parameters are positive values and delimited by semicolon
|
escapeSeq.state = ESC_BUILDING_UNKNOWN;
|
||||||
if((chr >= '0' && chr <= '9') || chr == ';')
|
memset(escapeSeq.rawBuf, 0, sizeof(escapeSeq.rawBuf));
|
||||||
continue;
|
memset(escapeSeq.hasArg, 0, sizeof(escapeSeq.hasArg));
|
||||||
|
escapeSeq.color.bg = currentConsole->bg;
|
||||||
switch (chr) {
|
escapeSeq.color.fg = currentConsole->fg;
|
||||||
|
escapeSeq.color.flags = currentConsole->flags;
|
||||||
|
escapeSeq.argIdx = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
consolePrintChar(0x1b);
|
||||||
|
consolePrintChar(chr);
|
||||||
|
escapeSeq.state = ESC_NONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ESC_BUILDING_UNKNOWN:
|
||||||
|
switch (chr)
|
||||||
|
{
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
escapeSeq.hasArg[escapeSeq.argIdx] = true;
|
||||||
|
escapeSeq.rawBuf[escapeSeq.argIdx] = escapeSeq.rawBuf[escapeSeq.argIdx] * 10 + (chr - '0');
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
if (escapeSeq.argIdx < 2)
|
||||||
|
escapeSeq.argIdx++;
|
||||||
|
else
|
||||||
|
consoleColorStateShift();
|
||||||
|
break;
|
||||||
|
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
// Cursor directional movement
|
// Cursor directional movement
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
case 'A':
|
case 'A':
|
||||||
consumed = 0;
|
if (!escapeSeq.hasArg[0])
|
||||||
assigned = sscanf(escapeseq,"[%dA%n", ¶meter, &consumed);
|
escapeSeq.directional.movement = 1;
|
||||||
if (assigned==0) parameter = 1;
|
currentConsole->cursorY = (currentConsole->cursorY - escapeSeq.directional.movement) < 0 ? 0 : currentConsole->cursorY - escapeSeq.directional.movement;
|
||||||
if (consumed)
|
escapeSeq.state = ESC_NONE;
|
||||||
currentConsole->cursorY = (currentConsole->cursorY - parameter) < 0 ? 0 : currentConsole->cursorY - parameter;
|
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
consumed = 0;
|
if (!escapeSeq.hasArg[0])
|
||||||
assigned = sscanf(escapeseq,"[%dB%n", ¶meter, &consumed);
|
escapeSeq.directional.movement = 1;
|
||||||
if (assigned==0) parameter = 1;
|
currentConsole->cursorY = (currentConsole->cursorY + escapeSeq.directional.movement) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY + escapeSeq.directional.movement;
|
||||||
if (consumed)
|
escapeSeq.state = ESC_NONE;
|
||||||
currentConsole->cursorY = (currentConsole->cursorY + parameter) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY + parameter;
|
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
consumed = 0;
|
if (!escapeSeq.hasArg[0])
|
||||||
assigned = sscanf(escapeseq,"[%dC%n", ¶meter, &consumed);
|
escapeSeq.directional.movement = 1;
|
||||||
if (assigned==0) parameter = 1;
|
currentConsole->cursorX = (currentConsole->cursorX + escapeSeq.directional.movement) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX + escapeSeq.directional.movement;
|
||||||
if (consumed)
|
escapeSeq.state = ESC_NONE;
|
||||||
currentConsole->cursorX = (currentConsole->cursorX + parameter) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX + parameter;
|
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
consumed = 0;
|
if (!escapeSeq.hasArg[0])
|
||||||
assigned = sscanf(escapeseq,"[%dD%n", ¶meter, &consumed);
|
escapeSeq.directional.movement = 1;
|
||||||
if (assigned==0) parameter = 1;
|
currentConsole->cursorX = (currentConsole->cursorX - escapeSeq.directional.movement) < 0 ? 0 : currentConsole->cursorX - escapeSeq.directional.movement;
|
||||||
if (consumed)
|
escapeSeq.state = ESC_NONE;
|
||||||
currentConsole->cursorX = (currentConsole->cursorX - parameter) < 0 ? 0 : currentConsole->cursorX - parameter;
|
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
// Cursor position movement
|
// Cursor position movement
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
case 'H':
|
case 'H':
|
||||||
case 'f':
|
case 'f':
|
||||||
{
|
consolePosition(escapeSeq.hasArg[1] ? escapeSeq.absolute.x : 1, escapeSeq.hasArg[0] ? escapeSeq.absolute.y : 1);
|
||||||
int x, y;
|
escapeSeq.state = ESC_NONE;
|
||||||
char c;
|
|
||||||
if(sscanf(escapeseq,"[%d;%d%c", &y, &x, &c) == 3 && (c == 'f' || c == 'H')) {
|
|
||||||
consolePosition(x, y);
|
|
||||||
escaping = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
x = y = 1;
|
|
||||||
if(sscanf(escapeseq,"[%d;%c", &y, &c) == 2 && (c == 'f' || c == 'H')) {
|
|
||||||
consolePosition(x, y);
|
|
||||||
escaping = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
x = y = 1;
|
|
||||||
if(sscanf(escapeseq,"[;%d%c", &x, &c) == 2 && (c == 'f' || c == 'H')) {
|
|
||||||
consolePosition(x, y);
|
|
||||||
escaping = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
x = y = 1;
|
|
||||||
if(sscanf(escapeseq,"[;%c", &c) == 1 && (c == 'f' || c == 'H')) {
|
|
||||||
consolePosition(x, y);
|
|
||||||
escaping = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalid format
|
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
// Screen clear
|
// Screen clear
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
case 'J':
|
case 'J':
|
||||||
if(escapelen <= 3)
|
consoleCls(escapeSeq.hasArg[0] ? escapeSeq.clear.type : 0);
|
||||||
consoleCls(escapeseq[escapelen-2]);
|
escapeSeq.state = ESC_NONE;
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
// Line clear
|
// Line clear
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
case 'K':
|
case 'K':
|
||||||
if(escapelen <= 3)
|
consoleClearLine(escapeSeq.hasArg[0] ? escapeSeq.clear.type : 0);
|
||||||
consoleClearLine(escapeseq[escapelen-2]);
|
escapeSeq.state = ESC_NONE;
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
// Save cursor position
|
// Save cursor position
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
case 's':
|
case 's':
|
||||||
if(escapelen == 2) {
|
currentConsole->prevCursorX = currentConsole->cursorX ;
|
||||||
currentConsole->prevCursorX = currentConsole->cursorX ;
|
currentConsole->prevCursorY = currentConsole->cursorY ;
|
||||||
currentConsole->prevCursorY = currentConsole->cursorY ;
|
escapeSeq.state = ESC_NONE;
|
||||||
}
|
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
// Load cursor position
|
// Load cursor position
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
case 'u':
|
case 'u':
|
||||||
if(escapelen == 2) {
|
currentConsole->cursorX = currentConsole->prevCursorX ;
|
||||||
currentConsole->cursorX = currentConsole->prevCursorX ;
|
currentConsole->cursorY = currentConsole->prevCursorY ;
|
||||||
currentConsole->cursorY = currentConsole->prevCursorY ;
|
escapeSeq.state = ESC_NONE;
|
||||||
}
|
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
// Color scan codes
|
// Color scan codes
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
case 'm':
|
case 'm':
|
||||||
escapeseq++;
|
consoleColorStateShift();
|
||||||
escapelen--;
|
consoleColorApply();
|
||||||
|
escapeSeq.state = ESC_NONE;
|
||||||
do {
|
|
||||||
parameter = 0;
|
|
||||||
if (escapelen == 1) {
|
|
||||||
consumed = 1;
|
|
||||||
} else if (memchr(escapeseq,';',escapelen)) {
|
|
||||||
sscanf(escapeseq,"%d;%n", ¶meter, &consumed);
|
|
||||||
} else {
|
|
||||||
sscanf(escapeseq,"%dm%n", ¶meter, &consumed);
|
|
||||||
}
|
|
||||||
|
|
||||||
escapeseq += consumed;
|
|
||||||
escapelen -= consumed;
|
|
||||||
|
|
||||||
switch(parameter) {
|
|
||||||
case 0: // reset
|
|
||||||
currentConsole->flags = 0;
|
|
||||||
currentConsole->bg = 0;
|
|
||||||
currentConsole->fg = 7;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // bold
|
|
||||||
currentConsole->flags &= ~CONSOLE_COLOR_FAINT;
|
|
||||||
currentConsole->flags |= CONSOLE_COLOR_BOLD;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // faint
|
|
||||||
currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
|
|
||||||
currentConsole->flags |= CONSOLE_COLOR_FAINT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // italic
|
|
||||||
currentConsole->flags |= CONSOLE_ITALIC;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4: // underline
|
|
||||||
currentConsole->flags |= CONSOLE_UNDERLINE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5: // blink slow
|
|
||||||
currentConsole->flags &= ~CONSOLE_BLINK_FAST;
|
|
||||||
currentConsole->flags |= CONSOLE_BLINK_SLOW;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6: // blink fast
|
|
||||||
currentConsole->flags &= ~CONSOLE_BLINK_SLOW;
|
|
||||||
currentConsole->flags |= CONSOLE_BLINK_FAST;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7: // reverse video
|
|
||||||
currentConsole->flags |= CONSOLE_COLOR_REVERSE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8: // conceal
|
|
||||||
currentConsole->flags |= CONSOLE_CONCEAL;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 9: // crossed-out
|
|
||||||
currentConsole->flags |= CONSOLE_CROSSED_OUT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 21: // bold off
|
|
||||||
currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 22: // normal color
|
|
||||||
currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
|
|
||||||
currentConsole->flags &= ~CONSOLE_COLOR_FAINT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 23: // italic off
|
|
||||||
currentConsole->flags &= ~CONSOLE_ITALIC;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 24: // underline off
|
|
||||||
currentConsole->flags &= ~CONSOLE_UNDERLINE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 25: // blink off
|
|
||||||
currentConsole->flags &= ~CONSOLE_BLINK_SLOW;
|
|
||||||
currentConsole->flags &= ~CONSOLE_BLINK_FAST;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 27: // reverse off
|
|
||||||
currentConsole->flags &= ~CONSOLE_COLOR_REVERSE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 29: // crossed-out off
|
|
||||||
currentConsole->flags &= ~CONSOLE_CROSSED_OUT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 30 ... 37: // writing color
|
|
||||||
currentConsole->fg = parameter - 30;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 39: // reset foreground color
|
|
||||||
currentConsole->fg = 7;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 40 ... 47: // screen color
|
|
||||||
currentConsole->bg = parameter - 40;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 49: // reset background color
|
|
||||||
currentConsole->fg = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (escapelen > 0);
|
|
||||||
|
|
||||||
escaping = false;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// some sort of unsupported escape; just gloss over it
|
// some sort of unsupported escape; just gloss over it
|
||||||
escaping = false;
|
escapeSeq.state = ESC_NONE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (escaping);
|
break;
|
||||||
continue;
|
default:
|
||||||
}
|
switch (chr)
|
||||||
|
{
|
||||||
consolePrintChar(chr);
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
escapeSeq.hasArg[escapeSeq.argIdx] = true;
|
||||||
|
escapeSeq.rawBuf[escapeSeq.argIdx] = escapeSeq.rawBuf[escapeSeq.argIdx] * 10 + (chr - '0');
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
consoleColorStateShift();
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
consoleColorStateShift();
|
||||||
|
consoleColorApply();
|
||||||
|
escapeSeq.state = ESC_NONE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// some sort of unsupported escape; just gloss over it
|
||||||
|
escapeSeq.state = ESC_NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
@ -533,6 +713,8 @@ PrintConsole* consoleInit(gfxScreen_t screen, PrintConsole* console) {
|
|||||||
setvbuf(stdout, NULL , _IONBF, 0);
|
setvbuf(stdout, NULL , _IONBF, 0);
|
||||||
setvbuf(stderr, NULL , _IONBF, 0);
|
setvbuf(stderr, NULL , _IONBF, 0);
|
||||||
|
|
||||||
|
memset(&escapeSeq, 0, sizeof(escapeSeq));
|
||||||
|
|
||||||
firstConsoleInit = false;
|
firstConsoleInit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,7 +741,7 @@ PrintConsole* consoleInit(gfxScreen_t screen, PrintConsole* console) {
|
|||||||
console->windowWidth = isWide ? 100 : 50;
|
console->windowWidth = isWide ? 100 : 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
consoleCls('2');
|
consoleCls(2);
|
||||||
|
|
||||||
return currentConsole;
|
return currentConsole;
|
||||||
|
|
||||||
@ -571,18 +753,18 @@ void consoleDebugInit(debugDevice device){
|
|||||||
|
|
||||||
int buffertype = _IONBF;
|
int buffertype = _IONBF;
|
||||||
|
|
||||||
switch(device) {
|
switch(device)
|
||||||
|
{
|
||||||
case debugDevice_SVC:
|
case debugDevice_SVC:
|
||||||
devoptab_list[STD_ERR] = &dotab_svc;
|
devoptab_list[STD_ERR] = &dotab_svc;
|
||||||
buffertype = _IOLBF;
|
buffertype = _IOLBF;
|
||||||
break;
|
break;
|
||||||
case debugDevice_CONSOLE:
|
case debugDevice_CONSOLE:
|
||||||
devoptab_list[STD_ERR] = &dotab_stdout;
|
devoptab_list[STD_ERR] = &dotab_stdout;
|
||||||
break;
|
break;
|
||||||
case debugDevice_NULL:
|
case debugDevice_NULL:
|
||||||
devoptab_list[STD_ERR] = &dotab_null;
|
devoptab_list[STD_ERR] = &dotab_null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
setvbuf(stderr, NULL , buffertype, 0);
|
setvbuf(stderr, NULL , buffertype, 0);
|
||||||
|
|
||||||
@ -629,7 +811,7 @@ static void newRow() {
|
|||||||
src += 240;
|
src += 240;
|
||||||
}
|
}
|
||||||
|
|
||||||
consoleClearLine('2');
|
consoleClearLine(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
@ -640,24 +822,29 @@ void consoleDrawChar(int c) {
|
|||||||
|
|
||||||
u8 *fontdata = currentConsole->font.gfx + (8 * c);
|
u8 *fontdata = currentConsole->font.gfx + (8 * c);
|
||||||
|
|
||||||
int writingColor = currentConsole->fg;
|
u16 fg = currentConsole->fg;
|
||||||
int screenColor = currentConsole->bg;
|
u16 bg = currentConsole->bg;
|
||||||
|
|
||||||
if (currentConsole->flags & CONSOLE_COLOR_BOLD) {
|
if (!(currentConsole->flags & CONSOLE_FG_CUSTOM)) {
|
||||||
writingColor += 8;
|
if (currentConsole->flags & CONSOLE_COLOR_BOLD) {
|
||||||
} else if (currentConsole->flags & CONSOLE_COLOR_FAINT) {
|
fg = colorTable[fg + 8];
|
||||||
writingColor += 16;
|
} else if (currentConsole->flags & CONSOLE_COLOR_FAINT) {
|
||||||
|
fg = colorTable[fg + 16];
|
||||||
|
} else {
|
||||||
|
fg = colorTable[fg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(currentConsole->flags & CONSOLE_BG_CUSTOM)) {
|
||||||
|
bg = colorTable[bg];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentConsole->flags & CONSOLE_COLOR_REVERSE) {
|
if (currentConsole->flags & CONSOLE_COLOR_REVERSE) {
|
||||||
int tmp = writingColor;
|
u16 tmp = fg;
|
||||||
writingColor = screenColor;
|
fg = bg;
|
||||||
screenColor = tmp;
|
bg = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 bg = colorTable[screenColor];
|
|
||||||
u16 fg = colorTable[writingColor];
|
|
||||||
|
|
||||||
u8 b1 = *(fontdata++);
|
u8 b1 = *(fontdata++);
|
||||||
u8 b2 = *(fontdata++);
|
u8 b2 = *(fontdata++);
|
||||||
u8 b3 = *(fontdata++);
|
u8 b3 = *(fontdata++);
|
||||||
@ -755,7 +942,7 @@ void consolePrintChar(int c) {
|
|||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
void consoleClear(void) {
|
void consoleClear(void) {
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
iprintf("\x1b[2J");
|
consoleCls(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
@ -773,5 +960,3 @@ void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height
|
|||||||
console->cursorY = 0;
|
console->cursorY = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,10 +18,12 @@ Result errfInit(void)
|
|||||||
if (AtomicPostIncrement(&errfRefCount)) return 0;
|
if (AtomicPostIncrement(&errfRefCount)) return 0;
|
||||||
|
|
||||||
rc = svcConnectToPort(&errfHandle, "err:f");
|
rc = svcConnectToPort(&errfHandle, "err:f");
|
||||||
if (R_FAILED(rc)) goto end;
|
if (R_FAILED(rc))
|
||||||
|
{
|
||||||
|
errfHandle = 0;
|
||||||
|
errfExit();
|
||||||
|
}
|
||||||
|
|
||||||
end:
|
|
||||||
if (R_FAILED(rc)) errfExit();
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +31,8 @@ void errfExit(void)
|
|||||||
{
|
{
|
||||||
if (AtomicDecrement(&errfRefCount))
|
if (AtomicDecrement(&errfRefCount))
|
||||||
return;
|
return;
|
||||||
svcCloseHandle(errfHandle);
|
if (errfHandle != 0) svcCloseHandle(errfHandle);
|
||||||
|
errfHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle* errfGetSessionHandle(void)
|
Handle* errfGetSessionHandle(void)
|
||||||
@ -39,7 +42,7 @@ Handle* errfGetSessionHandle(void)
|
|||||||
|
|
||||||
Result ERRF_Throw(const ERRF_FatalErrInfo* error)
|
Result ERRF_Throw(const ERRF_FatalErrInfo* error)
|
||||||
{
|
{
|
||||||
uint32_t *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x1,32,0); // 0x10800
|
cmdbuf[0] = IPC_MakeHeader(0x1,32,0); // 0x10800
|
||||||
memcpy(&cmdbuf[1], error, sizeof(ERRF_FatalErrInfo));
|
memcpy(&cmdbuf[1], error, sizeof(ERRF_FatalErrInfo));
|
||||||
@ -80,11 +83,33 @@ Result ERRF_ThrowResult(Result failure)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ERRF_LogResult(Result failure)
|
||||||
|
{
|
||||||
|
ERRF_FatalErrInfo error;
|
||||||
|
Result ret;
|
||||||
|
|
||||||
|
if (R_FAILED(ret = errfInit()))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
memset(&error, 0, sizeof(error));
|
||||||
|
|
||||||
|
error.type = ERRF_ERRTYPE_LOG_ONLY;
|
||||||
|
|
||||||
|
// pcAddr is not used by ErrDisp for ERRF_ERRTYPE_FAILURE
|
||||||
|
error.pcAddr = (u32)__builtin_extract_return_addr(__builtin_return_address(0));
|
||||||
|
getCommonErrorData(&error, failure);
|
||||||
|
|
||||||
|
ret = ERRF_Throw(&error);
|
||||||
|
|
||||||
|
errfExit();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
Result ERRF_ThrowResultWithMessage(Result failure, const char* message)
|
Result ERRF_ThrowResultWithMessage(Result failure, const char* message)
|
||||||
{
|
{
|
||||||
ERRF_FatalErrInfo error;
|
ERRF_FatalErrInfo error;
|
||||||
Result ret;
|
Result ret;
|
||||||
size_t msglen;
|
|
||||||
|
|
||||||
if (R_FAILED(ret = errfInit()))
|
if (R_FAILED(ret = errfInit()))
|
||||||
return ret;
|
return ret;
|
||||||
@ -94,11 +119,12 @@ Result ERRF_ThrowResultWithMessage(Result failure, const char* message)
|
|||||||
error.type = ERRF_ERRTYPE_FAILURE;
|
error.type = ERRF_ERRTYPE_FAILURE;
|
||||||
getCommonErrorData(&error, failure);
|
getCommonErrorData(&error, failure);
|
||||||
|
|
||||||
if ((msglen = strlen(message)) > sizeof(error.data.failure_mesg) - 1)
|
#pragma GCC diagnostic push
|
||||||
msglen = sizeof(error.data.failure_mesg) - 1;
|
#pragma GCC diagnostic ignored "-Wstringop-truncation"
|
||||||
|
// Official client code always copies at most 95 bytes + NUL byte, but server codes uses %.96s
|
||||||
memcpy(error.data.failure_mesg, message, msglen);
|
// and explicitely handles 96 non-NUL bytes.
|
||||||
error.data.failure_mesg[msglen] = '\0';
|
strncpy(error.data.failure_mesg, message, sizeof(error.data.failure_mesg));
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
ret = ERRF_Throw(&error);
|
ret = ERRF_Throw(&error);
|
||||||
|
|
||||||
@ -107,6 +133,28 @@ Result ERRF_ThrowResultWithMessage(Result failure, const char* message)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ERRF_SetUserString(const char* user_string)
|
||||||
|
{
|
||||||
|
Result ret = errfInit();
|
||||||
|
size_t size = strnlen(user_string, 256);
|
||||||
|
|
||||||
|
if (R_FAILED(ret))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x2,1,2); // 0x20042
|
||||||
|
cmdbuf[1] = size; // unused
|
||||||
|
cmdbuf[2] = IPC_Desc_StaticBuffer(size, 0);
|
||||||
|
cmdbuf[3] = (u32)user_string;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret = svcSendSyncRequest(errfHandle)))
|
||||||
|
ret = cmdbuf[1];
|
||||||
|
|
||||||
|
errfExit();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void ERRF_ExceptionHandler(ERRF_ExceptionInfo* excep, CpuRegisters* regs)
|
void ERRF_ExceptionHandler(ERRF_ExceptionInfo* excep, CpuRegisters* regs)
|
||||||
{
|
{
|
||||||
ERRF_FatalErrInfo error;
|
ERRF_FatalErrInfo error;
|
||||||
|
@ -205,7 +205,7 @@ static int _gdbExportSeekFlag(int flag)
|
|||||||
// https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat
|
// https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat
|
||||||
typedef u32 gdbhio_time_t;
|
typedef u32 gdbhio_time_t;
|
||||||
|
|
||||||
struct PACKED ALIGN(4) gdbhio_stat {
|
struct CTR_PACKED CTR_ALIGN(4) gdbhio_stat {
|
||||||
u32 gst_dev; /* device */
|
u32 gst_dev; /* device */
|
||||||
u32 gst_ino; /* inode */
|
u32 gst_ino; /* inode */
|
||||||
gdbhio_mode_t gst_mode; /* protection */
|
gdbhio_mode_t gst_mode; /* protection */
|
||||||
|
@ -13,17 +13,9 @@ Result shaderInstanceInit(shaderInstance_s* si, DVLE_s* dvle)
|
|||||||
{
|
{
|
||||||
if(!si || !dvle)return -1;
|
if(!si || !dvle)return -1;
|
||||||
|
|
||||||
|
memset(si, 0, sizeof(*si));
|
||||||
si->dvle = dvle;
|
si->dvle = dvle;
|
||||||
|
|
||||||
si->boolUniforms = 0;
|
|
||||||
si->boolUniformMask = 0;
|
|
||||||
si->intUniforms[0] = 0x00000000;
|
|
||||||
si->intUniforms[1] = 0x00000000;
|
|
||||||
si->intUniforms[2] = 0x00000000;
|
|
||||||
si->intUniforms[3] = 0x00000000;
|
|
||||||
si->float24Uniforms = NULL;
|
|
||||||
si->intUniformMask = 0;
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
DVLE_constEntry_s* cnst = dvle->constTableData;
|
DVLE_constEntry_s* cnst = dvle->constTableData;
|
||||||
if(cnst)
|
if(cnst)
|
||||||
|
@ -8,6 +8,12 @@
|
|||||||
#define THREADVARS_MAGIC 0x21545624 // !TV$
|
#define THREADVARS_MAGIC 0x21545624 // !TV$
|
||||||
#define FS_OVERRIDE_MAGIC 0x21465324 // !FS$
|
#define FS_OVERRIDE_MAGIC 0x21465324 // !FS$
|
||||||
|
|
||||||
|
extern const size_t __tdata_align;
|
||||||
|
extern const u8 __tdata_lma[];
|
||||||
|
extern const u8 __tdata_lma_end[];
|
||||||
|
extern u8 __tls_start[];
|
||||||
|
extern u8 __tls_end[];
|
||||||
|
|
||||||
// Keep this structure under 0x80 bytes
|
// Keep this structure under 0x80 bytes
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -31,7 +37,24 @@ typedef struct
|
|||||||
bool srv_blocking_policy;
|
bool srv_blocking_policy;
|
||||||
} ThreadVars;
|
} ThreadVars;
|
||||||
|
|
||||||
|
struct Thread_tag
|
||||||
|
{
|
||||||
|
Handle handle;
|
||||||
|
ThreadFunc ep;
|
||||||
|
void* arg;
|
||||||
|
int rc;
|
||||||
|
bool detached, finished;
|
||||||
|
struct _reent reent;
|
||||||
|
void* stacktop;
|
||||||
|
};
|
||||||
|
|
||||||
static inline ThreadVars* getThreadVars(void)
|
static inline ThreadVars* getThreadVars(void)
|
||||||
{
|
{
|
||||||
return (ThreadVars*)getThreadLocalStorage();
|
return (ThreadVars*)getThreadLocalStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void initThreadVars(struct Thread_tag *thread);
|
||||||
|
|
||||||
|
static inline size_t alignTo(const size_t base, const size_t align) {
|
||||||
|
return (base + (align - 1)) & ~(align - 1);
|
||||||
|
}
|
||||||
|
@ -100,6 +100,11 @@ void ndspChnSetFormat(int id, u16 format)
|
|||||||
ndspChn[id].format = format;
|
ndspChn[id].format = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u16 ndspChnGetFormat(int id)
|
||||||
|
{
|
||||||
|
return ndspChn[id].format;
|
||||||
|
}
|
||||||
|
|
||||||
bool ndspChnIsPaused(int id)
|
bool ndspChnIsPaused(int id)
|
||||||
{
|
{
|
||||||
return ndspChn[id].paused;
|
return ndspChn[id].paused;
|
||||||
@ -123,6 +128,12 @@ void ndspChnSetInterp(int id, ndspInterpType type)
|
|||||||
LightLock_Unlock(&chn->lock);
|
LightLock_Unlock(&chn->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ndspInterpType ndspChnGetInterp(int id)
|
||||||
|
{
|
||||||
|
ndspChnSt* chn = &ndspChn[id];
|
||||||
|
return chn->interpType;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspChnSetRate(int id, float rate)
|
void ndspChnSetRate(int id, float rate)
|
||||||
{
|
{
|
||||||
ndspChnSt* chn = &ndspChn[id];
|
ndspChnSt* chn = &ndspChn[id];
|
||||||
@ -132,6 +143,12 @@ void ndspChnSetRate(int id, float rate)
|
|||||||
LightLock_Unlock(&chn->lock);
|
LightLock_Unlock(&chn->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float ndspChnGetRate(int id)
|
||||||
|
{
|
||||||
|
ndspChnSt* chn = &ndspChn[id];
|
||||||
|
return chn->rate;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspChnSetMix(int id, float mix[12])
|
void ndspChnSetMix(int id, float mix[12])
|
||||||
{
|
{
|
||||||
ndspChnSt* chn = &ndspChn[id];
|
ndspChnSt* chn = &ndspChn[id];
|
||||||
@ -141,6 +158,14 @@ void ndspChnSetMix(int id, float mix[12])
|
|||||||
LightLock_Unlock(&chn->lock);
|
LightLock_Unlock(&chn->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ndspChnGetMix(int id, float out_mix[12])
|
||||||
|
{
|
||||||
|
ndspChnSt* chn = &ndspChn[id];
|
||||||
|
LightLock_Lock(&chn->lock);
|
||||||
|
memcpy(out_mix, chn->mix, sizeof(ndspChn[id].mix));
|
||||||
|
LightLock_Unlock(&chn->lock);
|
||||||
|
}
|
||||||
|
|
||||||
void ndspChnSetAdpcmCoefs(int id, u16 coefs[16])
|
void ndspChnSetAdpcmCoefs(int id, u16 coefs[16])
|
||||||
{
|
{
|
||||||
ndspChnSt* chn = &ndspChn[id];
|
ndspChnSt* chn = &ndspChn[id];
|
||||||
|
@ -612,6 +612,11 @@ void ndspSetMasterVol(float volume)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float ndspGetMasterVol(void)
|
||||||
|
{
|
||||||
|
return ndspMaster.masterVol;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspSetOutputMode(ndspOutputMode mode)
|
void ndspSetOutputMode(ndspOutputMode mode)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&ndspMaster.lock);
|
LightLock_Lock(&ndspMaster.lock);
|
||||||
@ -620,6 +625,11 @@ void ndspSetOutputMode(ndspOutputMode mode)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ndspOutputMode ndspGetOutputMode(void)
|
||||||
|
{
|
||||||
|
return ndspMaster.outputMode;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspSetClippingMode(ndspClippingMode mode)
|
void ndspSetClippingMode(ndspClippingMode mode)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&ndspMaster.lock);
|
LightLock_Lock(&ndspMaster.lock);
|
||||||
@ -628,6 +638,11 @@ void ndspSetClippingMode(ndspClippingMode mode)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ndspClippingMode ndspGetClippingMode(void)
|
||||||
|
{
|
||||||
|
return ndspMaster.clippingMode;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspSetOutputCount(int count)
|
void ndspSetOutputCount(int count)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&ndspMaster.lock);
|
LightLock_Lock(&ndspMaster.lock);
|
||||||
@ -636,6 +651,11 @@ void ndspSetOutputCount(int count)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ndspGetOutputCount(void)
|
||||||
|
{
|
||||||
|
return ndspMaster.outputCount;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspSetCapture(ndspWaveBuf* capture)
|
void ndspSetCapture(ndspWaveBuf* capture)
|
||||||
{
|
{
|
||||||
ndspMaster.capture = capture;
|
ndspMaster.capture = capture;
|
||||||
@ -655,6 +675,11 @@ void ndspSurroundSetDepth(u16 depth)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u16 ndspSurroundGetDepth(void)
|
||||||
|
{
|
||||||
|
return ndspMaster.surround.depth;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspSurroundSetPos(ndspSpeakerPos pos)
|
void ndspSurroundSetPos(ndspSpeakerPos pos)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&ndspMaster.lock);
|
LightLock_Lock(&ndspMaster.lock);
|
||||||
@ -663,6 +688,11 @@ void ndspSurroundSetPos(ndspSpeakerPos pos)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ndspSpeakerPos ndspSurroundGetPos(void)
|
||||||
|
{
|
||||||
|
return ndspMaster.surround.pos;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspSurroundSetRearRatio(u16 ratio)
|
void ndspSurroundSetRearRatio(u16 ratio)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&ndspMaster.lock);
|
LightLock_Lock(&ndspMaster.lock);
|
||||||
@ -671,6 +701,11 @@ void ndspSurroundSetRearRatio(u16 ratio)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u16 ndspSurroundGetRearRatio(void)
|
||||||
|
{
|
||||||
|
return ndspMaster.surround.rearRatio;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspAuxSetEnable(int id, bool enable)
|
void ndspAuxSetEnable(int id, bool enable)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&ndspMaster.lock);
|
LightLock_Lock(&ndspMaster.lock);
|
||||||
@ -679,6 +714,11 @@ void ndspAuxSetEnable(int id, bool enable)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ndspAuxIsEnabled(int id)
|
||||||
|
{
|
||||||
|
return ndspMaster.aux[id].enable;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspAuxSetFrontBypass(int id, bool bypass)
|
void ndspAuxSetFrontBypass(int id, bool bypass)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&ndspMaster.lock);
|
LightLock_Lock(&ndspMaster.lock);
|
||||||
@ -687,6 +727,11 @@ void ndspAuxSetFrontBypass(int id, bool bypass)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ndspGetFrontBypass(int id)
|
||||||
|
{
|
||||||
|
return ndspMaster.aux[id].frontBypass;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspAuxSetVolume(int id, float volume)
|
void ndspAuxSetVolume(int id, float volume)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&ndspMaster.lock);
|
LightLock_Lock(&ndspMaster.lock);
|
||||||
@ -695,6 +740,11 @@ void ndspAuxSetVolume(int id, float volume)
|
|||||||
LightLock_Unlock(&ndspMaster.lock);
|
LightLock_Unlock(&ndspMaster.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float ndspAuxGetVolume(int id)
|
||||||
|
{
|
||||||
|
return ndspMaster.aux[id].volume;
|
||||||
|
}
|
||||||
|
|
||||||
void ndspAuxSetCallback(int id, ndspAuxCallback callback, void* data)
|
void ndspAuxSetCallback(int id, ndspAuxCallback callback, void* data)
|
||||||
{
|
{
|
||||||
ndspMaster.aux[id].callback = callback;
|
ndspMaster.aux[id].callback = callback;
|
||||||
|
@ -88,27 +88,6 @@ u64 osGetTime(void) {
|
|||||||
return tr.value_ms + elapsed_ms;
|
return tr.value_ms + elapsed_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------
|
|
||||||
int __libctru_gtod(struct _reent *ptr, struct timeval *tp, struct timezone *tz) {
|
|
||||||
//---------------------------------------------------------------------------------
|
|
||||||
if (tp != NULL) {
|
|
||||||
// Retrieve current time, adjusting epoch from 1900 to 1970
|
|
||||||
s64 now = osGetTime() - 2208988800000ULL;
|
|
||||||
|
|
||||||
// Convert to struct timeval
|
|
||||||
tp->tv_sec = now / 1000;
|
|
||||||
tp->tv_usec = (now - 1000*tp->tv_sec) * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tz != NULL) {
|
|
||||||
// Provide dummy information, as the 3DS does not have the concept of timezones
|
|
||||||
tz->tz_minuteswest = 0;
|
|
||||||
tz->tz_dsttime = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
double osTickCounterRead(const TickCounter* cnt) {
|
double osTickCounterRead(const TickCounter* cnt) {
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <3ds/synchronization.h>
|
#include <3ds/synchronization.h>
|
||||||
#include <3ds/services/ac.h>
|
#include <3ds/services/ac.h>
|
||||||
#include <3ds/ipc.h>
|
#include <3ds/ipc.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
static Handle acHandle;
|
static Handle acHandle;
|
||||||
static int acRefCount;
|
static int acRefCount;
|
||||||
@ -30,6 +31,11 @@ void acExit(void)
|
|||||||
svcCloseHandle(acHandle);
|
svcCloseHandle(acHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle *acGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &acHandle;
|
||||||
|
}
|
||||||
|
|
||||||
Result acWaitInternetConnection(void)
|
Result acWaitInternetConnection(void)
|
||||||
{
|
{
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
@ -284,3 +290,36 @@ Result ACU_GetProxyUserName(char *username)
|
|||||||
|
|
||||||
return (Result)cmdbuf[1];
|
return (Result)cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ACI_LoadNetworkSetting(u32 slot)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x401,1,0); // 0x04010040
|
||||||
|
cmdbuf[1] = slot;
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(acHandle))) return ret;
|
||||||
|
|
||||||
|
return (Result)cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ACI_GetNetworkWirelessEssidSecuritySsid(void *ssid)
|
||||||
|
{
|
||||||
|
u32* cmdbuf = getThreadCommandBuffer();
|
||||||
|
u32* staticbufs = getThreadStaticBuffers();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x40F,0,0); // 0x040F0000
|
||||||
|
|
||||||
|
u32 staticbufBackup[2];
|
||||||
|
memcpy(staticbufBackup, staticbufs, 8);
|
||||||
|
|
||||||
|
staticbufs[0] = IPC_Desc_StaticBuffer(0x20, 0); // at most 32 bytes
|
||||||
|
staticbufs[1] = (u32)ssid;
|
||||||
|
|
||||||
|
Result ret = svcSendSyncRequest(acHandle);
|
||||||
|
|
||||||
|
memcpy(staticbufs, staticbufBackup, 8);
|
||||||
|
|
||||||
|
return R_SUCCEEDED(ret) ? (Result)cmdbuf[1] : ret;
|
||||||
|
}
|
||||||
|
@ -1031,3 +1031,41 @@ Result AM_DeleteAllTwlTitles(void)
|
|||||||
|
|
||||||
return (Result)cmdbuf[1];
|
return (Result)cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result AMAPP_GetDLCContentInfoCount(u32* count, FS_MediaType mediatype, u64 titleID)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x1001,3,0); // 0x100100C0
|
||||||
|
cmdbuf[1] = (u32)mediatype;
|
||||||
|
cmdbuf[2] = titleID & 0xffffffff;
|
||||||
|
cmdbuf[3] = (u32)(titleID >> 32);
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(amHandle))) return ret;
|
||||||
|
|
||||||
|
*count = cmdbuf[2];
|
||||||
|
|
||||||
|
return (Result)cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AMAPP_ListDLCContentInfos(u32* contentInfoRead, FS_MediaType mediatype, u64 titleID, u32 contentInfoCount, u32 offset, AM_ContentInfo* contentInfos)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = (IPC_MakeHeader(0x1003,5,2)); // 0x10030142
|
||||||
|
cmdbuf[1] = contentInfoCount;
|
||||||
|
cmdbuf[2] = (u32)mediatype;
|
||||||
|
cmdbuf[3] = titleID & 0xffffffff;
|
||||||
|
cmdbuf[4] = (u32)(titleID >> 32);
|
||||||
|
cmdbuf[5] = offset;
|
||||||
|
cmdbuf[6] = IPC_Desc_Buffer(contentInfoCount * 0x18, IPC_BUFFER_W);
|
||||||
|
cmdbuf[7] = (u32)contentInfos;
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(amHandle))) return ret;
|
||||||
|
|
||||||
|
*contentInfoRead = cmdbuf[2];
|
||||||
|
|
||||||
|
return (Result)cmdbuf[1];
|
||||||
|
}
|
||||||
|
@ -63,9 +63,12 @@ enum
|
|||||||
static u8 aptHomeButtonState;
|
static u8 aptHomeButtonState;
|
||||||
static u32 aptFlags;
|
static u32 aptFlags;
|
||||||
static u32 aptParameters[0x1000/4];
|
static u32 aptParameters[0x1000/4];
|
||||||
static u8 aptChainloadFlags;
|
|
||||||
static u64 aptChainloadTid;
|
static u64 aptChainloadTid;
|
||||||
|
static u8 aptChainloadDeliverArg[0x300];
|
||||||
|
static u32 aptChainloadDeliverArgSize = sizeof(aptChainloadDeliverArg);
|
||||||
|
static u8 aptChainloadHmac[0x20];
|
||||||
static u8 aptChainloadMediatype;
|
static u8 aptChainloadMediatype;
|
||||||
|
static u8 aptChainloadFlags;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@ -315,6 +318,9 @@ static void aptClearJumpToHome(void)
|
|||||||
void aptClearChainloader(void)
|
void aptClearChainloader(void)
|
||||||
{
|
{
|
||||||
aptFlags &= ~FLAG_CHAINLOAD;
|
aptFlags &= ~FLAG_CHAINLOAD;
|
||||||
|
aptChainloadDeliverArgSize = sizeof(aptChainloadDeliverArg);
|
||||||
|
memset(aptChainloadDeliverArg, 0, sizeof(aptChainloadDeliverArg));
|
||||||
|
memset(aptChainloadHmac, 0, sizeof(aptChainloadHmac));
|
||||||
}
|
}
|
||||||
|
|
||||||
void aptSetChainloader(u64 programID, u8 mediatype)
|
void aptSetChainloader(u64 programID, u8 mediatype)
|
||||||
@ -325,6 +331,14 @@ void aptSetChainloader(u64 programID, u8 mediatype)
|
|||||||
aptChainloadMediatype = mediatype;
|
aptChainloadMediatype = mediatype;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void aptSetChainloaderToCaller(void)
|
||||||
|
{
|
||||||
|
aptFlags |= FLAG_CHAINLOAD;
|
||||||
|
aptChainloadFlags = 1;
|
||||||
|
aptChainloadTid = 0;
|
||||||
|
aptChainloadMediatype = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void aptSetChainloaderToSelf(void)
|
void aptSetChainloaderToSelf(void)
|
||||||
{
|
{
|
||||||
aptFlags |= FLAG_CHAINLOAD;
|
aptFlags |= FLAG_CHAINLOAD;
|
||||||
@ -333,6 +347,20 @@ void aptSetChainloaderToSelf(void)
|
|||||||
aptChainloadMediatype = 0;
|
aptChainloadMediatype = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void aptSetChainloaderArgs(const void *deliverArg, size_t deliverArgSize, const void *hmac)
|
||||||
|
{
|
||||||
|
if (deliverArgSize >= sizeof(aptChainloadDeliverArg))
|
||||||
|
deliverArgSize = sizeof(aptChainloadDeliverArg);
|
||||||
|
|
||||||
|
aptChainloadDeliverArgSize = deliverArgSize;
|
||||||
|
memcpy(aptChainloadDeliverArg, deliverArg, deliverArgSize);
|
||||||
|
|
||||||
|
if (hmac != NULL)
|
||||||
|
memcpy(aptChainloadHmac, hmac, sizeof(aptChainloadHmac));
|
||||||
|
else
|
||||||
|
memset(aptChainloadHmac, 0, sizeof(aptChainloadHmac));
|
||||||
|
}
|
||||||
|
|
||||||
extern void (*__system_retAddr)(void);
|
extern void (*__system_retAddr)(void);
|
||||||
|
|
||||||
static void aptExitProcess(void)
|
static void aptExitProcess(void)
|
||||||
@ -345,6 +373,7 @@ void aptExit(void)
|
|||||||
if (AtomicDecrement(&aptRefCount)) return;
|
if (AtomicDecrement(&aptRefCount)) return;
|
||||||
|
|
||||||
bool closeAptLock = true;
|
bool closeAptLock = true;
|
||||||
|
bool doDirtyChainload = false;
|
||||||
|
|
||||||
if (!aptIsCrippled())
|
if (!aptIsCrippled())
|
||||||
{
|
{
|
||||||
@ -369,16 +398,14 @@ void aptExit(void)
|
|||||||
if (R_SUCCEEDED(APT_IsRegistered(aptGetMenuAppID(), &hmRegistered)) && hmRegistered)
|
if (R_SUCCEEDED(APT_IsRegistered(aptGetMenuAppID(), &hmRegistered)) && hmRegistered)
|
||||||
{
|
{
|
||||||
// Normal, sane chainload
|
// Normal, sane chainload
|
||||||
u8 param[0x300] = {0};
|
|
||||||
u8 hmac[0x20] = {0};
|
|
||||||
APT_PrepareToDoApplicationJump(aptChainloadFlags, aptChainloadTid, aptChainloadMediatype);
|
APT_PrepareToDoApplicationJump(aptChainloadFlags, aptChainloadTid, aptChainloadMediatype);
|
||||||
APT_DoApplicationJump(param, sizeof(param), hmac);
|
APT_DoApplicationJump(aptChainloadDeliverArg, aptChainloadDeliverArgSize, aptChainloadHmac);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// XX: HOME menu doesn't exist, so we need to use a workaround provided by Luma3DS
|
// XX: HOME menu doesn't exist, so we need to use a workaround provided by Luma3DS
|
||||||
APT_Finalize(envGetAptAppId());
|
APT_Finalize(envGetAptAppId());
|
||||||
srvPublishToSubscriber(0x3000, 0);
|
doDirtyChainload = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// After a chainload has been applied, we don't need to manually close
|
// After a chainload has been applied, we don't need to manually close
|
||||||
@ -413,6 +440,30 @@ void aptExit(void)
|
|||||||
|
|
||||||
if (closeAptLock)
|
if (closeAptLock)
|
||||||
svcCloseHandle(aptLockHandle);
|
svcCloseHandle(aptLockHandle);
|
||||||
|
|
||||||
|
if (doDirtyChainload)
|
||||||
|
{
|
||||||
|
// Provided by Luma3DS
|
||||||
|
Handle notificationHandle = 0;
|
||||||
|
Result res = 0;
|
||||||
|
u32 notificationNumber = 0;
|
||||||
|
srvEnableNotification(¬ificationHandle);
|
||||||
|
|
||||||
|
// Not needed, but official (sysmodule) code does this:
|
||||||
|
srvSubscribe(0x100);
|
||||||
|
|
||||||
|
// Make PM modify our run flags and ask us to terminate
|
||||||
|
srvPublishToSubscriber(0x3000, 0);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Bail out after 3 seconds, we don't want to wait forever for this
|
||||||
|
res = svcWaitSynchronization(notificationHandle, 3 * 1000 * 1000LL);
|
||||||
|
res = res == 0 ? srvReceiveNotification(¬ificationNumber) : res;
|
||||||
|
} while(res == 0 && notificationNumber != 0x100);
|
||||||
|
|
||||||
|
svcCloseHandle(notificationHandle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void aptEventHandler(void *arg)
|
void aptEventHandler(void *arg)
|
||||||
@ -1405,7 +1456,7 @@ Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result APT_ReceiveDeliverArg(const void* param, size_t paramSize, const void* hmac, u64* sender, bool* received)
|
Result APT_ReceiveDeliverArg(void* param, size_t paramSize, void* hmac, u64* sender, bool* received)
|
||||||
{
|
{
|
||||||
u32 cmdbuf[16];
|
u32 cmdbuf[16];
|
||||||
cmdbuf[0]=IPC_MakeHeader(0x35,2,0); // 0x350080
|
cmdbuf[0]=IPC_MakeHeader(0x35,2,0); // 0x350080
|
||||||
|
162
libctru/source/services/cdcchk.c
Normal file
162
libctru/source/services/cdcchk.c
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <3ds/types.h>
|
||||||
|
#include <3ds/result.h>
|
||||||
|
#include <3ds/svc.h>
|
||||||
|
#include <3ds/srv.h>
|
||||||
|
#include <3ds/synchronization.h>
|
||||||
|
#include <3ds/ipc.h>
|
||||||
|
#include <3ds/services/cdcchk.h>
|
||||||
|
|
||||||
|
static Handle cdcChkHandle;
|
||||||
|
static int cdcChkRefCount;
|
||||||
|
|
||||||
|
Result cdcChkInit(void)
|
||||||
|
{
|
||||||
|
if (AtomicPostIncrement(&cdcChkRefCount))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Result res = srvGetServiceHandle(&cdcChkHandle, "cdc:CHK");
|
||||||
|
if (R_FAILED(res))
|
||||||
|
AtomicDecrement(&cdcChkRefCount);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cdcChkExit(void)
|
||||||
|
{
|
||||||
|
if (AtomicDecrement(&cdcChkRefCount))
|
||||||
|
return;
|
||||||
|
svcCloseHandle(cdcChkHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle *cdcChkGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &cdcChkHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CDCCHK_ReadRegisters1(u8 pageId, u8 initialRegAddr, void *outData, size_t size)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
u32 *staticbufs = getThreadStaticBuffers();
|
||||||
|
|
||||||
|
u32 staticbufBackup[2];
|
||||||
|
memcpy(staticbufBackup, staticbufs, 8);
|
||||||
|
|
||||||
|
staticbufs[0] = IPC_Desc_StaticBuffer(size, 0);
|
||||||
|
staticbufs[1] = (u32)outData;
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(1, 3, 0); // 0x100C0
|
||||||
|
cmdbuf[1] = pageId;
|
||||||
|
cmdbuf[2] = initialRegAddr;
|
||||||
|
cmdbuf[3] = size;
|
||||||
|
|
||||||
|
ret = svcSendSyncRequest(cdcChkHandle);
|
||||||
|
|
||||||
|
memcpy(staticbufs, staticbufBackup, 8);
|
||||||
|
|
||||||
|
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CDCCHK_ReadRegisters2(u8 pageId, u8 initialRegAddr, void *outData, size_t size)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
u32 *staticbufs = getThreadStaticBuffers();
|
||||||
|
|
||||||
|
u32 staticbufBackup[2];
|
||||||
|
memcpy(staticbufBackup, staticbufs, 8);
|
||||||
|
|
||||||
|
staticbufs[0] = IPC_Desc_StaticBuffer(size, 0);
|
||||||
|
staticbufs[1] = (u32)outData;
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(2, 3, 0); // 0x200C0
|
||||||
|
cmdbuf[1] = pageId;
|
||||||
|
cmdbuf[2] = initialRegAddr;
|
||||||
|
cmdbuf[3] = size;
|
||||||
|
|
||||||
|
ret = svcSendSyncRequest(cdcChkHandle);
|
||||||
|
|
||||||
|
memcpy(staticbufs, staticbufBackup, 8);
|
||||||
|
|
||||||
|
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CDCCHK_WriteRegisters1(u8 pageId, u8 initialRegAddr, const void *data, size_t size)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(3, 3, 2); // 0x300C2
|
||||||
|
cmdbuf[1] = pageId;
|
||||||
|
cmdbuf[2] = initialRegAddr;
|
||||||
|
cmdbuf[3] = size;
|
||||||
|
cmdbuf[4] = IPC_Desc_StaticBuffer(size, 0);
|
||||||
|
cmdbuf[5] = (u32)data;
|
||||||
|
|
||||||
|
ret = svcSendSyncRequest(cdcChkHandle);
|
||||||
|
|
||||||
|
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CDCCHK_WriteRegisters2(u8 pageId, u8 initialRegAddr, const void *data, size_t size)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(4, 3, 2); // 0x400C2
|
||||||
|
cmdbuf[1] = pageId;
|
||||||
|
cmdbuf[2] = initialRegAddr;
|
||||||
|
cmdbuf[3] = size;
|
||||||
|
cmdbuf[4] = IPC_Desc_StaticBuffer(size, 0);
|
||||||
|
cmdbuf[5] = (u32)data;
|
||||||
|
|
||||||
|
ret = svcSendSyncRequest(cdcChkHandle);
|
||||||
|
|
||||||
|
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CDCCHK_ReadNtrPmicRegister(u8 *outData, u8 regAddr)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(5, 1, 0); // 0x50040
|
||||||
|
cmdbuf[1] = regAddr;
|
||||||
|
|
||||||
|
ret = svcSendSyncRequest(cdcChkHandle);
|
||||||
|
if (R_SUCCEEDED(ret))
|
||||||
|
{
|
||||||
|
*outData = (u8)cmdbuf[2];
|
||||||
|
ret = cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CDCCHK_WriteNtrPmicRegister(u8 regAddr, u8 data)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(6, 2, 0); // 0x60080
|
||||||
|
cmdbuf[1] = regAddr;
|
||||||
|
cmdbuf[2] = data;
|
||||||
|
|
||||||
|
ret = svcSendSyncRequest(cdcChkHandle);
|
||||||
|
|
||||||
|
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CDCCHK_SetI2sVolume(CodecI2sLine i2sLine, s8 volume)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(7, 2, 0); // 0x70080
|
||||||
|
cmdbuf[1] = (u32)i2sLine;
|
||||||
|
cmdbuf[2] = volume;
|
||||||
|
|
||||||
|
ret = svcSendSyncRequest(cdcChkHandle);
|
||||||
|
|
||||||
|
return R_SUCCEEDED(ret) ? cmdbuf[1] : ret;
|
||||||
|
}
|
@ -566,7 +566,7 @@ Result FSUSER_GetNandCid(u8* out, u32 length)
|
|||||||
return cmdbuf[1];
|
return cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
Result FSUSER_GetSdmcSpeedInfo(u32 *speedInfo)
|
Result FSUSER_GetSdmcSpeedInfo(FS_SdMmcSpeedInfo *speedInfo)
|
||||||
{
|
{
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
@ -575,12 +575,11 @@ Result FSUSER_GetSdmcSpeedInfo(u32 *speedInfo)
|
|||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(fsSession()))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(fsSession()))) return ret;
|
||||||
|
|
||||||
if(speedInfo) *speedInfo = cmdbuf[2];
|
if(speedInfo) memcpy(speedInfo, &cmdbuf[2], sizeof(FS_SdMmcSpeedInfo));
|
||||||
|
|
||||||
return cmdbuf[1];
|
return cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
Result FSUSER_GetNandSpeedInfo(u32 *speedInfo)
|
Result FSUSER_GetNandSpeedInfo(FS_SdMmcSpeedInfo *speedInfo)
|
||||||
{
|
{
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
@ -589,7 +588,7 @@ Result FSUSER_GetNandSpeedInfo(u32 *speedInfo)
|
|||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(fsSession()))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(fsSession()))) return ret;
|
||||||
|
|
||||||
if(speedInfo) *speedInfo = cmdbuf[2];
|
if(speedInfo) memcpy(speedInfo, &cmdbuf[2], sizeof(FS_SdMmcSpeedInfo));
|
||||||
|
|
||||||
return cmdbuf[1];
|
return cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
@ -107,8 +107,8 @@ Result FSPXI_CreateFile(Handle serviceHandle, FSPXI_Archive archive, FS_Path pat
|
|||||||
cmdbuf[6] = attributes;
|
cmdbuf[6] = attributes;
|
||||||
cmdbuf[7] = (u32)fileSize;
|
cmdbuf[7] = (u32)fileSize;
|
||||||
cmdbuf[8] = (u32)(fileSize >> 32);
|
cmdbuf[8] = (u32)(fileSize >> 32);
|
||||||
cmdbuf[8] = IPC_Desc_PXIBuffer(path.size, 0, true);
|
cmdbuf[9] = IPC_Desc_PXIBuffer(path.size, 0, true);
|
||||||
cmdbuf[9] = (u32) path.data;
|
cmdbuf[10] = (u32) path.data;
|
||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
@ -229,8 +229,8 @@ Result FSPXI_WriteFile(Handle serviceHandle, FSPXI_File file, u32* bytesWritten,
|
|||||||
cmdbuf[2] = (u32)(file >> 32);
|
cmdbuf[2] = (u32)(file >> 32);
|
||||||
cmdbuf[3] = (u32) offset;
|
cmdbuf[3] = (u32) offset;
|
||||||
cmdbuf[4] = (u32) (offset >> 32);
|
cmdbuf[4] = (u32) (offset >> 32);
|
||||||
cmdbuf[5] = flags;
|
cmdbuf[5] = size;
|
||||||
cmdbuf[6] = size;
|
cmdbuf[6] = flags;
|
||||||
cmdbuf[7] = IPC_Desc_PXIBuffer(size, 0, true);
|
cmdbuf[7] = IPC_Desc_PXIBuffer(size, 0, true);
|
||||||
cmdbuf[8] = (u32) buffer;
|
cmdbuf[8] = (u32) buffer;
|
||||||
|
|
||||||
@ -376,7 +376,7 @@ Result FSPXI_HasFile(Handle serviceHandle, FSPXI_Archive archive, bool* out, FS_
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if (out) *out = (bool)cmdbuf[2];
|
if (out) *out = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -396,7 +396,7 @@ Result FSPXI_HasDirectory(Handle serviceHandle, FSPXI_Archive archive, bool* out
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if (out) *out = (bool)cmdbuf[2];
|
if (out) *out = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -441,7 +441,7 @@ Result FSPXI_Unknown0x17(Handle serviceHandle, FSPXI_Archive archive, bool* out)
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if (out) *out = (bool)cmdbuf[2];
|
if (out) *out = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -523,7 +523,7 @@ Result FSPXI_IsSdmcDetected(Handle serviceHandle, bool* out)
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if(out) *out = (bool)cmdbuf[2];
|
if(out) *out = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -537,7 +537,7 @@ Result FSPXI_IsSdmcWritable(Handle serviceHandle, bool* out)
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if(out) *out = (bool)cmdbuf[2];
|
if(out) *out = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -572,7 +572,7 @@ Result FSPXI_GetNandCid(Handle serviceHandle, void* out, u32 size)
|
|||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, u32* out)
|
Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, FS_SdMmcSpeedInfo* out)
|
||||||
{
|
{
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
u32* cmdbuf = getThreadCommandBuffer();
|
u32* cmdbuf = getThreadCommandBuffer();
|
||||||
@ -581,12 +581,12 @@ Result FSPXI_GetSdmcSpeedInfo(Handle serviceHandle, u32* out)
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if(out) *out = cmdbuf[2];
|
if(out) memcpy(out, &cmdbuf[2], sizeof(FS_SdMmcSpeedInfo));
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, u32* out)
|
Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, FS_SdMmcSpeedInfo* out)
|
||||||
{
|
{
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
u32* cmdbuf = getThreadCommandBuffer();
|
u32* cmdbuf = getThreadCommandBuffer();
|
||||||
@ -595,7 +595,7 @@ Result FSPXI_GetNandSpeedInfo(Handle serviceHandle, u32* out)
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if(out) *out = cmdbuf[2];
|
if(out) memcpy(out, &cmdbuf[2], sizeof(FS_SdMmcSpeedInfo));
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -663,7 +663,7 @@ Result FSPXI_CardSlotIsInserted(Handle serviceHandle, bool* inserted)
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if(inserted) *inserted = (bool)cmdbuf[2];
|
if(inserted) *inserted = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -677,7 +677,7 @@ Result FSPXI_CardSlotPowerOn(Handle serviceHandle, bool* status)
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if(status) *status = (bool)cmdbuf[2];
|
if(status) *status = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -691,7 +691,7 @@ Result FSPXI_CardSlotPowerOff(Handle serviceHandle, bool* status)
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if(status) *status = (bool)cmdbuf[2];
|
if(status) *status = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -705,7 +705,7 @@ Result FSPXI_CardSlotGetCardIFPowerStatus(Handle serviceHandle, bool* status)
|
|||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(serviceHandle))) return ret;
|
||||||
|
|
||||||
if(status) *status = (bool)cmdbuf[2];
|
if(status) *status = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
return (Result) cmdbuf[1];
|
return (Result) cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -1084,7 +1084,7 @@ Result FSPXI_GetArchiveResource(Handle serviceHandle, FS_ArchiveResource* archiv
|
|||||||
{
|
{
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x43, 1, 0); // 0x00430040
|
cmdbuf[0] = IPC_MakeHeader(0x43, 1, 0); // 0x00430040
|
||||||
cmdbuf[1] = mediaType;
|
cmdbuf[1] = mediaType;
|
||||||
|
|
||||||
@ -1179,7 +1179,7 @@ Result FSPXI_ReadSpecialFile(Handle serviceHandle, u32* bytesRead, u64 fileOffse
|
|||||||
{
|
{
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x49, 4, 2); // 0x00490102
|
cmdbuf[0] = IPC_MakeHeader(0x49, 4, 2); // 0x00490102
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
cmdbuf[2] = (u32) fileOffset;
|
cmdbuf[2] = (u32) fileOffset;
|
||||||
|
@ -184,6 +184,12 @@ Result gspInit(void)
|
|||||||
gspSharedMem = mappableAlloc(0x1000);
|
gspSharedMem = mappableAlloc(0x1000);
|
||||||
svcMapMemoryBlock(gspSharedMemHandle, (u32)gspSharedMem, MEMPERM_READWRITE, MEMPERM_DONTCARE);
|
svcMapMemoryBlock(gspSharedMemHandle, (u32)gspSharedMem, MEMPERM_READWRITE, MEMPERM_DONTCARE);
|
||||||
|
|
||||||
|
// Initialize interrupt queue header
|
||||||
|
s32* sharedGspCmdBuf = (s32*)((u8*)gspSharedMem + 0x800 + gspThreadId*0x200);
|
||||||
|
do {
|
||||||
|
__ldrex(sharedGspCmdBuf);
|
||||||
|
} while (__strex(sharedGspCmdBuf, 0));
|
||||||
|
|
||||||
// Start event handling thread
|
// Start event handling thread
|
||||||
gspRunEvents = true;
|
gspRunEvents = true;
|
||||||
gspLastEvent = -1;
|
gspLastEvent = -1;
|
||||||
@ -221,12 +227,17 @@ void gspExit(void)
|
|||||||
svcCloseHandle(gspGpuHandle);
|
svcCloseHandle(gspGpuHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle *gspGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &gspGpuHandle;
|
||||||
|
}
|
||||||
|
|
||||||
bool gspHasGpuRight(void)
|
bool gspHasGpuRight(void)
|
||||||
{
|
{
|
||||||
return gspGpuRight;
|
return gspGpuRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gspPresentBuffer(unsigned screen, unsigned swap, const void* fb_a, const void* fb_b, u32 stride, u32 mode)
|
bool gspPresentBuffer(unsigned screen, unsigned swap, const void* fb_a, const void* fb_b, u32 stride, u32 mode)
|
||||||
{
|
{
|
||||||
GSPGPU_FramebufferInfo info;
|
GSPGPU_FramebufferInfo info;
|
||||||
info.active_framebuf = swap;
|
info.active_framebuf = swap;
|
||||||
@ -249,12 +260,23 @@ void gspPresentBuffer(unsigned screen, unsigned swap, const void* fb_a, const vo
|
|||||||
struct { u8 swap, update; };
|
struct { u8 swap, update; };
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
|
bool ret;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
u.header = __ldrex(fbInfoHeader);
|
u.header = __ldrex(fbInfoHeader);
|
||||||
|
ret = u.update != 0;
|
||||||
|
|
||||||
u.swap = pos;
|
u.swap = pos;
|
||||||
u.update = 1;
|
u.update = 1;
|
||||||
} while (__strex(fbInfoHeader, u.header));
|
} while (__strex(fbInfoHeader, u.header));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gspIsPresentPending(unsigned screen)
|
||||||
|
{
|
||||||
|
s32* fbInfoHeader = (s32*)((u8*)gspSharedMem + 0x200 + gspThreadId*0x80 + screen*0x40);
|
||||||
|
return (*fbInfoHeader & 0xff00) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gspSetEventCallback(GSPGPU_Event id, ThreadFunc cb, void* data, bool oneShot)
|
void gspSetEventCallback(GSPGPU_Event id, ThreadFunc cb, void* data, bool oneShot)
|
||||||
|
@ -26,6 +26,11 @@ void gspLcdExit(void)
|
|||||||
svcCloseHandle(gspLcdHandle);
|
svcCloseHandle(gspLcdHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle *gspLcdGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &gspLcdHandle;
|
||||||
|
}
|
||||||
|
|
||||||
Result GSPLCD_PowerOnAllBacklights(void)
|
Result GSPLCD_PowerOnAllBacklights(void)
|
||||||
{
|
{
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
@ -144,4 +149,4 @@ Result GSPLCD_SetBrightnessRaw(u32 screen, u32 brightness)
|
|||||||
if (R_FAILED(ret = svcSendSyncRequest(gspLcdHandle))) return ret;
|
if (R_FAILED(ret = svcSendSyncRequest(gspLcdHandle))) return ret;
|
||||||
|
|
||||||
return cmdbuf[1];
|
return cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
@ -237,3 +237,33 @@ Result IRU_GetIRLEDRecvState(u32 *out)
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IRU_GetSendFinishedEvent(Handle *out)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0xD,0,0); // 0xD0000
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(iruHandle)))return ret;
|
||||||
|
ret = (Result)cmdbuf[1];
|
||||||
|
|
||||||
|
*out = (Handle)cmdbuf[3];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IRU_GetRecvFinishedEvent(Handle *out)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0xE,0,0); // 0xE0000
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(iruHandle)))return ret;
|
||||||
|
ret = (Result)cmdbuf[1];
|
||||||
|
|
||||||
|
*out = (Handle)cmdbuf[3];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <string.h>
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/svc.h>
|
#include <3ds/svc.h>
|
||||||
#include <3ds/synchronization.h>
|
#include <3ds/synchronization.h>
|
||||||
@ -23,6 +24,11 @@ void mcuHwcExit(void)
|
|||||||
svcCloseHandle(mcuHwcHandle);
|
svcCloseHandle(mcuHwcHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle* mcuHwcGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &mcuHwcHandle;
|
||||||
|
}
|
||||||
|
|
||||||
Result MCUHWC_ReadRegister(u8 reg, void* data, u32 size)
|
Result MCUHWC_ReadRegister(u8 reg, void* data, u32 size)
|
||||||
{
|
{
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
@ -109,6 +115,22 @@ Result MCUHWC_SetWifiLedState(bool state)
|
|||||||
return (Result)cmdbuf[1];
|
return (Result)cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result MCUHWC_SetInfoLedPattern(const InfoLedPattern* pattern)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x0A,25,0); // 0xA0640
|
||||||
|
cmdbuf[1] = ((u32)pattern->blinkSpeed << 24) | ((u32)pattern->loopDelay << 16) | ((u32)pattern->smoothing << 8) | pattern->delay;
|
||||||
|
memcpy(&cmdbuf[2], pattern->redPattern, sizeof(pattern->redPattern));
|
||||||
|
memcpy(&cmdbuf[10], pattern->greenPattern, sizeof(pattern->greenPattern));
|
||||||
|
memcpy(&cmdbuf[18], pattern->bluePattern, sizeof(pattern->bluePattern));
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(mcuHwcHandle))) return ret;
|
||||||
|
|
||||||
|
return (Result)cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
Result MCUHWC_GetSoundSliderLevel(u8 *level)
|
Result MCUHWC_GetSoundSliderLevel(u8 *level)
|
||||||
{
|
{
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
|
@ -165,11 +165,12 @@ Result NDMU_GetCurrentState(ndmState *state)
|
|||||||
return (Result)cmdbuf[1];
|
return (Result)cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NDMU_QueryStatus(ndmDaemonStatus *status)
|
Result NDMU_QueryStatus(ndmDaemon daemon, ndmDaemonStatus *status)
|
||||||
{
|
{
|
||||||
u32* cmdbuf=getThreadCommandBuffer();
|
u32* cmdbuf=getThreadCommandBuffer();
|
||||||
|
|
||||||
cmdbuf[0]=IPC_MakeHeader(0xD,1,0); // 0xD0000
|
cmdbuf[0]=IPC_MakeHeader(0xD,1,0); // 0xD0040
|
||||||
|
cmdbuf[1]=daemon;
|
||||||
|
|
||||||
Result ret=0;
|
Result ret=0;
|
||||||
if(R_FAILED(ret=svcSendSyncRequest(ndmuHandle)))return ret;
|
if(R_FAILED(ret=svcSendSyncRequest(ndmuHandle)))return ret;
|
||||||
|
@ -79,7 +79,7 @@ Result PMAPP_TerminateTitle(u64 titleId, s64 timeout)
|
|||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x3, 4, 0); // 0x40100
|
cmdbuf[0] = IPC_MakeHeader(0x4, 4, 0); // 0x40100
|
||||||
cmdbuf[1] = (u32)titleId;
|
cmdbuf[1] = (u32)titleId;
|
||||||
cmdbuf[2] = (u32)(titleId >> 32);
|
cmdbuf[2] = (u32)(titleId >> 32);
|
||||||
cmdbuf[3] = (u32)timeout;
|
cmdbuf[3] = (u32)timeout;
|
||||||
|
43
libctru/source/services/ptmgets.c
Normal file
43
libctru/source/services/ptmgets.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <3ds/types.h>
|
||||||
|
#include <3ds/result.h>
|
||||||
|
#include <3ds/svc.h>
|
||||||
|
#include <3ds/srv.h>
|
||||||
|
#include <3ds/synchronization.h>
|
||||||
|
#include <3ds/services/ptmgets.h>
|
||||||
|
#include <3ds/ipc.h>
|
||||||
|
|
||||||
|
static Handle ptmGetsHandle;
|
||||||
|
static int ptmGetsRefCount;
|
||||||
|
|
||||||
|
Result ptmGetsInit(void)
|
||||||
|
{
|
||||||
|
if (AtomicPostIncrement(&ptmGetsRefCount)) return 0;
|
||||||
|
Result res = srvGetServiceHandle(&ptmGetsHandle, "ptm:gets");
|
||||||
|
if (R_FAILED(res)) AtomicDecrement(&ptmGetsRefCount);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ptmGetsExit(void)
|
||||||
|
{
|
||||||
|
if (AtomicDecrement(&ptmGetsRefCount)) return;
|
||||||
|
svcCloseHandle(ptmGetsHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle *ptmGetsGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &ptmGetsHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PTMGETS_GetSystemTime(s64 *outMsY2k)
|
||||||
|
{
|
||||||
|
Result ret;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x401,0,0); // 0x04010000
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(ptmGetsHandle)))return ret;
|
||||||
|
|
||||||
|
memcpy(outMsY2k, &cmdbuf[2], 8);
|
||||||
|
|
||||||
|
return (Result)cmdbuf[1];
|
||||||
|
}
|
42
libctru/source/services/ptmsets.c
Normal file
42
libctru/source/services/ptmsets.c
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <3ds/types.h>
|
||||||
|
#include <3ds/result.h>
|
||||||
|
#include <3ds/svc.h>
|
||||||
|
#include <3ds/srv.h>
|
||||||
|
#include <3ds/synchronization.h>
|
||||||
|
#include <3ds/services/ptmsets.h>
|
||||||
|
#include <3ds/ipc.h>
|
||||||
|
|
||||||
|
static Handle ptmSetsHandle;
|
||||||
|
static int ptmSetsRefCount;
|
||||||
|
|
||||||
|
Result ptmSetsInit(void)
|
||||||
|
{
|
||||||
|
if (AtomicPostIncrement(&ptmSetsRefCount)) return 0;
|
||||||
|
Result res = srvGetServiceHandle(&ptmSetsHandle, "ptm:sets");
|
||||||
|
if (R_FAILED(res)) AtomicDecrement(&ptmSetsRefCount);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ptmSetsExit(void)
|
||||||
|
{
|
||||||
|
if (AtomicDecrement(&ptmSetsRefCount)) return;
|
||||||
|
svcCloseHandle(ptmSetsHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle *ptmSetsGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &ptmSetsHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PTMSETS_SetSystemTime(s64 msY2k)
|
||||||
|
{
|
||||||
|
Result ret;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x1,2,0); // 0x00010080
|
||||||
|
memcpy(&cmdbuf[1], &msY2k, 8);
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(ptmSetsHandle)))return ret;
|
||||||
|
|
||||||
|
return (Result)cmdbuf[1];
|
||||||
|
}
|
@ -24,6 +24,11 @@ void ptmSysmExit(void)
|
|||||||
svcCloseHandle(ptmSysmHandle);
|
svcCloseHandle(ptmSysmHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle *ptmSysmGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &ptmSysmHandle;
|
||||||
|
}
|
||||||
|
|
||||||
Result PTMSYSM_RequestSleep(void)
|
Result PTMSYSM_RequestSleep(void)
|
||||||
{
|
{
|
||||||
Result ret;
|
Result ret;
|
||||||
@ -93,13 +98,14 @@ Result PTMSYSM_Awaken(void)
|
|||||||
return (Result)cmdbuf[1];
|
return (Result)cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
Result PTMSYSM_CheckNew3DS(void)
|
Result PTMSYSM_CheckNew3DS(bool *out)
|
||||||
{
|
{
|
||||||
Result ret;
|
Result ret;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x040A,0,0); // 0x040A0000
|
cmdbuf[0] = IPC_MakeHeader(0x040A,0,0); // 0x040A0000
|
||||||
|
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return 0;
|
if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret;
|
||||||
|
*out = (bool)(cmdbuf[2] & 1); // if cmdbuf[1] is != 0 then this is uninitialized (this is fine)
|
||||||
|
|
||||||
return (Result)cmdbuf[1];
|
return (Result)cmdbuf[1];
|
||||||
}
|
}
|
||||||
@ -144,6 +150,18 @@ Result PTMSYSM_RebootAsync(u64 timeout)
|
|||||||
return (Result)cmdbuf[1];
|
return (Result)cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result PTMSYSM_SetUserTime(s64 msY2k)
|
||||||
|
{
|
||||||
|
Result ret;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x80C,2,0); // 0x080C0080
|
||||||
|
memcpy(&cmdbuf[1], &msY2k, 8);
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret;
|
||||||
|
|
||||||
|
return (Result)cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
Result PTMSYSM_InvalidateSystemTime(void)
|
Result PTMSYSM_InvalidateSystemTime(void)
|
||||||
{
|
{
|
||||||
Result ret;
|
Result ret;
|
||||||
|
@ -24,6 +24,11 @@ void ptmuExit(void)
|
|||||||
svcCloseHandle(ptmuHandle);
|
svcCloseHandle(ptmuHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle *ptmuGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &ptmuHandle;
|
||||||
|
}
|
||||||
|
|
||||||
Result PTMU_GetShellState(u8 *out)
|
Result PTMU_GetShellState(u8 *out)
|
||||||
{
|
{
|
||||||
Result ret=0;
|
Result ret=0;
|
||||||
|
@ -7,76 +7,389 @@
|
|||||||
#include <3ds/synchronization.h>
|
#include <3ds/synchronization.h>
|
||||||
#include <3ds/services/qtm.h>
|
#include <3ds/services/qtm.h>
|
||||||
#include <3ds/ipc.h>
|
#include <3ds/ipc.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
Handle qtmHandle;
|
Handle qtmHandle;
|
||||||
static int qtmRefCount;
|
static int qtmRefCount;
|
||||||
|
|
||||||
Result qtmInit(void)
|
bool qtmCheckServicesRegistered(void)
|
||||||
{
|
{
|
||||||
Result ret=0;
|
bool registered = false;
|
||||||
|
return R_SUCCEEDED(srvIsServiceRegistered(®istered, "qtm:u")) && registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result qtmInit(QtmServiceName serviceName)
|
||||||
|
{
|
||||||
|
const char *name = NULL;
|
||||||
|
switch (serviceName)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case QTM_SERVICE_USER:
|
||||||
|
name = "qtm:u";
|
||||||
|
break;
|
||||||
|
case QTM_SERVICE_SYSTEM:
|
||||||
|
name = "qtm:s";
|
||||||
|
break;
|
||||||
|
case QTM_SERVICE_SYSTEM_PROCESS:
|
||||||
|
name = "qtm:sp";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (AtomicPostIncrement(&qtmRefCount)) return 0;
|
if (AtomicPostIncrement(&qtmRefCount)) return 0;
|
||||||
|
Result res = srvGetServiceHandle(&qtmHandle, name);
|
||||||
|
|
||||||
ret = srvGetServiceHandle(&qtmHandle, "qtm:u");
|
if (R_FAILED(res))
|
||||||
if (R_FAILED(ret)) ret = srvGetServiceHandle(&qtmHandle, "qtm:s");
|
{
|
||||||
if (R_FAILED(ret)) ret = srvGetServiceHandle(&qtmHandle, "qtm:sp");
|
qtmHandle = 0;
|
||||||
if (R_FAILED(ret)) AtomicDecrement(&qtmRefCount);
|
AtomicDecrement(&qtmRefCount);
|
||||||
return ret;
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qtmExit(void)
|
void qtmExit(void)
|
||||||
{
|
{
|
||||||
if (AtomicDecrement(&qtmRefCount)) return;
|
if (AtomicDecrement(&qtmRefCount)) return;
|
||||||
svcCloseHandle(qtmHandle);
|
svcCloseHandle(qtmHandle);
|
||||||
|
qtmHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool qtmCheckInitialized(void)
|
bool qtmIsInitialized(void)
|
||||||
{
|
{
|
||||||
return qtmRefCount>0;
|
// Use qtmHandle instead of qtmRefCount in the event user
|
||||||
|
// wants to bypass qtmInit and use *qtmGetSessionHandle() instead
|
||||||
|
// (e.g. stolen session).
|
||||||
|
return qtmHandle != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool qtmCheckHeadFullyDetected(QTM_HeadTrackingInfo *info)
|
Handle *qtmGetSessionHandle(void)
|
||||||
{
|
{
|
||||||
if(info==NULL)return false;
|
return &qtmHandle;
|
||||||
|
|
||||||
if(info->flags[0] && info->flags[1] && info->flags[2])return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result qtmConvertCoordToScreen(QTM_HeadTrackingInfoCoord *coord, float *screen_width, float *screen_height, u32 *x, u32 *y)
|
Result QTMU_GetRawTrackingDataEx(QtmRawTrackingData *outData, s64 predictionTimePointOrZero)
|
||||||
{
|
{
|
||||||
float width = 200.0f;
|
Result res = 0;
|
||||||
float height = 160.0f;
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
if(coord==NULL || x==NULL || y==NULL)return -1;
|
cmdbuf[0] = IPC_MakeHeader(0x1, 2, 0); // 0x10080
|
||||||
|
memcpy(&cmdbuf[1], &predictionTimePointOrZero, 8);
|
||||||
|
|
||||||
if(screen_width)width = (*screen_width) / 2;
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
if(screen_height)height = (*screen_height) / 2;
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
if(coord->x > 1.0f || coord->x < -1.0f)return -2;
|
memcpy(outData, &cmdbuf[2], sizeof(QtmRawTrackingData));
|
||||||
if(coord->y > 1.0f || coord->y < -1.0f)return -2;
|
|
||||||
|
|
||||||
*x = (u32)((coord->x * width) + width);
|
|
||||||
*y = (u32)((coord->y * height) + height);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result QTM_GetHeadTrackingInfo(u64 val, QTM_HeadTrackingInfo* out)
|
|
||||||
{
|
|
||||||
if(!qtmCheckInitialized())return -1;
|
|
||||||
|
|
||||||
Result ret = 0;
|
|
||||||
u32* cmdbuf = getThreadCommandBuffer();
|
|
||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x2,2,0); // 0x20080
|
|
||||||
cmdbuf[1] = val&0xFFFFFFFF;
|
|
||||||
cmdbuf[2] = val>>32;
|
|
||||||
|
|
||||||
if(R_FAILED(ret=svcSendSyncRequest(qtmHandle)))return ret;
|
|
||||||
|
|
||||||
if(out) memcpy(out, &cmdbuf[2], sizeof(QTM_HeadTrackingInfo));
|
|
||||||
|
|
||||||
return cmdbuf[1];
|
return cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result QTMU_GetTrackingDataEx(QtmTrackingData *outData, s64 predictionTimePointOrZero)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x2, 2, 0); // 0x20080
|
||||||
|
memcpy(&cmdbuf[1], &predictionTimePointOrZero, 8);
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
memcpy(outData, &cmdbuf[2], sizeof(QtmTrackingData));
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
float qtmComputeFovX(const QtmTrackingData *data)
|
||||||
|
{
|
||||||
|
return 2.0f * atanf(data->eyeWorldCoordinates[QTM_EYE_LEFT][0] / data->eyeCameraCoordinates[QTM_EYE_LEFT][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
float qtmComputeFovY(const QtmTrackingData *data)
|
||||||
|
{
|
||||||
|
return 2.0f * atanf(data->eyeWorldCoordinates[QTM_EYE_LEFT][1] / data->eyeCameraCoordinates[QTM_EYE_LEFT][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
float qtmComputeInverseAspectRatio(const QtmTrackingData *data)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(data->eyeWorldCoordinates[QTM_EYE_LEFT][1] * data->eyeCameraCoordinates[QTM_EYE_LEFT][0]) /
|
||||||
|
(data->eyeCameraCoordinates[QTM_EYE_LEFT][1] * data->eyeWorldCoordinates[QTM_EYE_LEFT][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
float qtmComputeHeadTiltAngle(const QtmTrackingData *data)
|
||||||
|
{
|
||||||
|
return atan2f(
|
||||||
|
data->eyeCameraCoordinates[QTM_EYE_RIGHT][1] - data->eyeCameraCoordinates[QTM_EYE_LEFT][1],
|
||||||
|
data->eyeCameraCoordinates[QTM_EYE_RIGHT][0] - data->eyeCameraCoordinates[QTM_EYE_LEFT][0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
float qtmEstimateEyeToCameraDistance(const QtmTrackingData *data)
|
||||||
|
{
|
||||||
|
float fovX = qtmComputeFovX(data);
|
||||||
|
float pxDistNorm = hypotf(
|
||||||
|
data->eyeWorldCoordinates[QTM_EYE_RIGHT][0] - data->eyeWorldCoordinates[QTM_EYE_LEFT][0],
|
||||||
|
data->eyeWorldCoordinates[QTM_EYE_RIGHT][1] - data->eyeWorldCoordinates[QTM_EYE_LEFT][1]
|
||||||
|
);
|
||||||
|
|
||||||
|
return 31.0f / tanf(0.5f * (pxDistNorm * fovX));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMU_EnableManualIrLedControl(void)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x3, 0, 0); // 0x30000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMU_DisableManualIrLedControl(void)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x4, 0, 0); // 0x40000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMU_SetIrLedStatus(bool on)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x5, 1, 0); // 0x50040
|
||||||
|
cmdbuf[1] = on;
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result qtmClearIrLedOverrides(void)
|
||||||
|
{
|
||||||
|
QTMU_EnableManualIrLedControl();
|
||||||
|
return QTMU_DisableManualIrLedControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMU_IsCurrentAppBlacklisted(bool *outBlacklisted)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x6, 0, 0); // 0x60000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
*outBlacklisted = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_SetCentralBarrierPosition(float position)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x401, 1, 0); // 0x4010040
|
||||||
|
memcpy(&cmdbuf[1], &position, sizeof(float));
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_GetCameraLuminance(float *outLuminanceLux)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x402, 0, 0); // 0x4020000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
memcpy(outLuminanceLux, &cmdbuf[2], sizeof(float));
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_EnableAutoBarrierControl(void)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x403, 0, 0); // 0x4030000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_DisableAutoBarrierControl(void)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x404, 0, 0); // 0x4040000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_SetBarrierPosition(u8 position)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x405, 1, 0); // 0x4050040
|
||||||
|
cmdbuf[1] = position;
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_GetCurrentBarrierPosition(u8 *outPosition)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x406, 0, 0); // 0x4060000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
*outPosition = (u8)cmdbuf[2];
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_SetIrLedStatusOverride(bool on)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x407, 1, 0); // 0x4070040
|
||||||
|
cmdbuf[1] = on;
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_SetCalibrationData(const QtmCalibrationData *cal, bool saveCalToCfg)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x408, 7, 0); // 0x40801C0
|
||||||
|
|
||||||
|
memcpy(&cmdbuf[1], cal, sizeof(QtmCalibrationData));
|
||||||
|
cmdbuf[7] = saveCalToCfg;
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_GetQtmStatus(QtmStatus *outQtmStatus)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x409, 0, 0); // 0x4090000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
*outQtmStatus = (QtmStatus)cmdbuf[2];
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMS_SetQtmStatus(QtmStatus qtmStatus)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x40A, 1, 0); // 0x40A0040
|
||||||
|
cmdbuf[1] = qtmStatus;
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMSP_NotifyTopLcdModeChange(u8 newMode)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x801, 1, 0); // 0x8010040
|
||||||
|
cmdbuf[1] = newMode;
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMSP_NotifyTopLcdPowerOn(void)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x802, 0, 0); // 0x8020000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMSP_IsExpanderInUse(bool *outInUse)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x803, 0, 0); // 0x8040000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
*outInUse = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMSP_NotifyTopLcdPowerOff(void)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x804, 0, 0); // 0x8040000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
101
libctru/source/services/qtmc.c
Normal file
101
libctru/source/services/qtmc.c
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <3ds/types.h>
|
||||||
|
#include <3ds/result.h>
|
||||||
|
#include <3ds/svc.h>
|
||||||
|
#include <3ds/srv.h>
|
||||||
|
#include <3ds/synchronization.h>
|
||||||
|
#include <3ds/services/qtmc.h>
|
||||||
|
#include <3ds/ipc.h>
|
||||||
|
|
||||||
|
static Handle qtmcHandle;
|
||||||
|
static int qtmcRefCount;
|
||||||
|
|
||||||
|
Result qtmcInit(void)
|
||||||
|
{
|
||||||
|
if (AtomicPostIncrement(&qtmcRefCount)) return 0;
|
||||||
|
Result res = srvGetServiceHandle(&qtmcHandle, "qtm:c");
|
||||||
|
|
||||||
|
if (R_FAILED(res)) AtomicDecrement(&qtmcRefCount);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qtmcExit(void)
|
||||||
|
{
|
||||||
|
if (AtomicDecrement(&qtmcRefCount)) return;
|
||||||
|
svcCloseHandle(qtmcHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle *qtmcGetSessionHandle(void)
|
||||||
|
{
|
||||||
|
return &qtmcHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMC_StartHardwareCheck(void)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x1, 0, 0); // 0x10000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmcHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMC_StopHardwareCheck(void)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x2, 0, 0); // 0x20000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmcHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMC_SetBarrierPattern(u32 pattern)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x3, 1, 0); // 0x30040
|
||||||
|
cmdbuf[1] = pattern;
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmcHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMC_WaitAndCheckExpanderWorking(bool *outWorking)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x4, 0, 0); // 0x40000
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmcHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
*outWorking = (bool)(cmdbuf[2] & 1);
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QTMC_SetIrLedStatusOverride(bool on)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x5, 1, 0); // 0x50040
|
||||||
|
cmdbuf[1] = (u32)on;
|
||||||
|
|
||||||
|
res = svcSendSyncRequest(qtmcHandle);
|
||||||
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
@ -6,10 +6,10 @@
|
|||||||
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int tmp_addrlen = 0x1c;
|
int tmp_addrlen = ADDR_STORAGE_LEN;
|
||||||
int fd, dev;
|
int fd, dev;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u8 tmpaddr[0x1c];
|
u8 tmpaddr[ADDR_STORAGE_LEN];
|
||||||
u32 saved_threadstorage[2];
|
u32 saved_threadstorage[2];
|
||||||
|
|
||||||
sockfd = soc_get_fd(sockfd);
|
sockfd = soc_get_fd(sockfd);
|
||||||
@ -27,7 +27,7 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
fd = __alloc_handle(dev);
|
fd = __alloc_handle(dev);
|
||||||
if(fd < 0) return fd;
|
if(fd < 0) return fd;
|
||||||
|
|
||||||
memset(tmpaddr, 0, 0x1c);
|
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
|
||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x4,2,2); // 0x40082
|
cmdbuf[0] = IPC_MakeHeader(0x4,2,2); // 0x40082
|
||||||
cmdbuf[1] = (u32)sockfd;
|
cmdbuf[1] = (u32)sockfd;
|
||||||
@ -61,9 +61,14 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
|
|
||||||
if(ret >= 0 && addr != NULL) {
|
if(ret >= 0 && addr != NULL) {
|
||||||
addr->sa_family = tmpaddr[1];
|
addr->sa_family = tmpaddr[1];
|
||||||
if(*addrlen > tmpaddr[0])
|
|
||||||
*addrlen = tmpaddr[0];
|
socklen_t user_addrlen = tmpaddr[0];
|
||||||
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - 2);
|
if(addr->sa_family == AF_INET)
|
||||||
|
user_addrlen += 8; // Accounting for the 8 bytes of sin_zero padding, which must be written for compatibility.
|
||||||
|
|
||||||
|
if(*addrlen > user_addrlen)
|
||||||
|
*addrlen = user_addrlen;
|
||||||
|
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - sizeof(addr->sa_family));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ret < 0) {
|
if(ret < 0) {
|
||||||
|
@ -8,7 +8,7 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
int tmp_addrlen = 0;
|
int tmp_addrlen = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u8 tmpaddr[0x1c];
|
u8 tmpaddr[ADDR_STORAGE_LEN];
|
||||||
|
|
||||||
sockfd = soc_get_fd(sockfd);
|
sockfd = soc_get_fd(sockfd);
|
||||||
if(sockfd < 0) {
|
if(sockfd < 0) {
|
||||||
@ -16,12 +16,12 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(tmpaddr, 0, 0x1c);
|
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
|
||||||
|
|
||||||
if(addr->sa_family == AF_INET)
|
if(addr->sa_family == AF_INET)
|
||||||
tmp_addrlen = 8;
|
tmp_addrlen = 8;
|
||||||
else
|
else
|
||||||
tmp_addrlen = 0x1c;
|
tmp_addrlen = ADDR_STORAGE_LEN;
|
||||||
|
|
||||||
if(addrlen < tmp_addrlen) {
|
if(addrlen < tmp_addrlen) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <3ds/services/soc.h>
|
#include <3ds/services/soc.h>
|
||||||
|
|
||||||
#define SYNC_ERROR ENODEV
|
#define SYNC_ERROR ENODEV
|
||||||
|
#define ADDR_STORAGE_LEN sizeof(struct sockaddr_storage)
|
||||||
|
|
||||||
extern Handle SOCU_handle;
|
extern Handle SOCU_handle;
|
||||||
extern Handle socMemhandle;
|
extern Handle socMemhandle;
|
||||||
|
@ -8,7 +8,7 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
int tmp_addrlen = 0;
|
int tmp_addrlen = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u8 tmpaddr[0x1c];
|
u8 tmpaddr[ADDR_STORAGE_LEN];
|
||||||
|
|
||||||
sockfd = soc_get_fd(sockfd);
|
sockfd = soc_get_fd(sockfd);
|
||||||
if(sockfd < 0) {
|
if(sockfd < 0) {
|
||||||
@ -16,12 +16,12 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(tmpaddr, 0, 0x1c);
|
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
|
||||||
|
|
||||||
if(addr->sa_family == AF_INET)
|
if(addr->sa_family == AF_INET)
|
||||||
tmp_addrlen = 8;
|
tmp_addrlen = 8;
|
||||||
else
|
else
|
||||||
tmp_addrlen = 0x1c;
|
tmp_addrlen = ADDR_STORAGE_LEN;
|
||||||
|
|
||||||
if(addrlen < tmp_addrlen) {
|
if(addrlen < tmp_addrlen) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
|
@ -8,7 +8,7 @@ int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_
|
|||||||
int i,tmp_addrlen;
|
int i,tmp_addrlen;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u32 saved_threadstorage[4];
|
u32 saved_threadstorage[4];
|
||||||
u8 tmpaddr[0x1c]; // sockaddr size for the kernel is 0x1C (sockaddr_in6?)
|
u8 tmpaddr[ADDR_STORAGE_LEN]; // sockaddr size for the kernel is 0x1C (sockaddr_in6?)
|
||||||
|
|
||||||
if((host == NULL || hostlen == 0) && (serv == NULL || servlen == 0))
|
if((host == NULL || hostlen == 0) && (serv == NULL || servlen == 0))
|
||||||
{
|
{
|
||||||
@ -18,7 +18,7 @@ int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_
|
|||||||
if(sa->sa_family == AF_INET)
|
if(sa->sa_family == AF_INET)
|
||||||
tmp_addrlen = 8;
|
tmp_addrlen = 8;
|
||||||
else
|
else
|
||||||
tmp_addrlen = 0x1c;
|
tmp_addrlen = ADDR_STORAGE_LEN;
|
||||||
|
|
||||||
if(salen < tmp_addrlen) {
|
if(salen < tmp_addrlen) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
|
@ -8,7 +8,7 @@ int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u32 saved_threadstorage[2];
|
u32 saved_threadstorage[2];
|
||||||
u8 tmpaddr[0x1c];
|
u8 tmpaddr[ADDR_STORAGE_LEN];
|
||||||
|
|
||||||
sockfd = soc_get_fd(sockfd);
|
sockfd = soc_get_fd(sockfd);
|
||||||
if(sockfd < 0) {
|
if(sockfd < 0) {
|
||||||
@ -16,16 +16,18 @@ int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
|
||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x18,2,2); // 0x180082
|
cmdbuf[0] = IPC_MakeHeader(0x18,2,2); // 0x180082
|
||||||
cmdbuf[1] = (u32)sockfd;
|
cmdbuf[1] = (u32)sockfd;
|
||||||
cmdbuf[2] = 0x1c;
|
cmdbuf[2] = ADDR_STORAGE_LEN;
|
||||||
cmdbuf[3] = IPC_Desc_CurProcessId();
|
cmdbuf[3] = IPC_Desc_CurProcessId();
|
||||||
|
|
||||||
u32 * staticbufs = getThreadStaticBuffers();
|
u32 * staticbufs = getThreadStaticBuffers();
|
||||||
saved_threadstorage[0] = staticbufs[0];
|
saved_threadstorage[0] = staticbufs[0];
|
||||||
saved_threadstorage[1] = staticbufs[1];
|
saved_threadstorage[1] = staticbufs[1];
|
||||||
|
|
||||||
staticbufs[0] = IPC_Desc_StaticBuffer(0x1c,0);
|
staticbufs[0] = IPC_Desc_StaticBuffer(ADDR_STORAGE_LEN,0);
|
||||||
staticbufs[1] = (u32)tmpaddr;
|
staticbufs[1] = (u32)tmpaddr;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(SOCU_handle);
|
||||||
@ -47,11 +49,15 @@ int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*addrlen > tmpaddr[0])
|
|
||||||
*addrlen = tmpaddr[0];
|
|
||||||
memset(addr, 0, sizeof(struct sockaddr));
|
|
||||||
addr->sa_family = tmpaddr[1];
|
addr->sa_family = tmpaddr[1];
|
||||||
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - 2);
|
|
||||||
|
socklen_t user_addrlen = tmpaddr[0];
|
||||||
|
if(addr->sa_family == AF_INET)
|
||||||
|
user_addrlen += 8;
|
||||||
|
|
||||||
|
if(*addrlen > user_addrlen)
|
||||||
|
*addrlen = user_addrlen;
|
||||||
|
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - sizeof(addr->sa_family));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u32 saved_threadstorage[2];
|
u32 saved_threadstorage[2];
|
||||||
u8 tmpaddr[0x1c];
|
u8 tmpaddr[ADDR_STORAGE_LEN];
|
||||||
|
|
||||||
sockfd = soc_get_fd(sockfd);
|
sockfd = soc_get_fd(sockfd);
|
||||||
if(sockfd < 0) {
|
if(sockfd < 0) {
|
||||||
@ -16,16 +16,18 @@ int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
|
||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x17,2,2); // 0x170082
|
cmdbuf[0] = IPC_MakeHeader(0x17,2,2); // 0x170082
|
||||||
cmdbuf[1] = (u32)sockfd;
|
cmdbuf[1] = (u32)sockfd;
|
||||||
cmdbuf[2] = 0x1c;
|
cmdbuf[2] = ADDR_STORAGE_LEN;
|
||||||
cmdbuf[3] = IPC_Desc_CurProcessId();
|
cmdbuf[3] = IPC_Desc_CurProcessId();
|
||||||
|
|
||||||
u32 * staticbufs = getThreadStaticBuffers();
|
u32 * staticbufs = getThreadStaticBuffers();
|
||||||
saved_threadstorage[0] = staticbufs[0];
|
saved_threadstorage[0] = staticbufs[0];
|
||||||
saved_threadstorage[1] = staticbufs[1];
|
saved_threadstorage[1] = staticbufs[1];
|
||||||
|
|
||||||
staticbufs[0] = IPC_Desc_StaticBuffer(0x1c,0);
|
staticbufs[0] = IPC_Desc_StaticBuffer(ADDR_STORAGE_LEN,0);
|
||||||
staticbufs[1] = (u32)tmpaddr;
|
staticbufs[1] = (u32)tmpaddr;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(SOCU_handle);
|
||||||
@ -47,11 +49,15 @@ int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*addrlen > tmpaddr[0])
|
|
||||||
*addrlen = tmpaddr[0];
|
|
||||||
memset(addr, 0, sizeof(struct sockaddr));
|
|
||||||
addr->sa_family = tmpaddr[1];
|
addr->sa_family = tmpaddr[1];
|
||||||
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - 2);
|
|
||||||
|
socklen_t user_addrlen = tmpaddr[0];
|
||||||
|
if(addr->sa_family == AF_INET)
|
||||||
|
user_addrlen += 8;
|
||||||
|
|
||||||
|
if(*addrlen > user_addrlen)
|
||||||
|
*addrlen = user_addrlen;
|
||||||
|
memcpy(addr->sa_data, &tmpaddr[2], *addrlen - sizeof(addr->sa_family));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
static int inet_pton4(const char *restrict src, void *restrict dst)
|
static int inet_pton4(const char *restrict src, void *restrict dst)
|
||||||
{
|
{
|
||||||
u8 ip[4] ALIGN(4);
|
u8 ip[4] CTR_ALIGN(4);
|
||||||
if(sscanf(src,"%hhu.%hhu.%hhu.%hhu",&ip[0], &ip[1], &ip[2], &ip[3]) != 4) return 0;
|
if(sscanf(src,"%hhu.%hhu.%hhu.%hhu",&ip[0], &ip[1], &ip[2], &ip[3]) != 4) return 0;
|
||||||
|
|
||||||
memcpy(dst,ip,4);
|
memcpy(dst,ip,4);
|
||||||
|
@ -8,13 +8,13 @@ ssize_t socuipc_cmd7(int sockfd, void *buf, size_t len, int flags, struct sockad
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u32 tmp_addrlen = 0;
|
u32 tmp_addrlen = 0;
|
||||||
u8 tmpaddr[0x1c];
|
u8 tmpaddr[ADDR_STORAGE_LEN];
|
||||||
u32 saved_threadstorage[2];
|
u32 saved_threadstorage[2];
|
||||||
|
|
||||||
memset(tmpaddr, 0, 0x1c);
|
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
|
||||||
|
|
||||||
if(src_addr)
|
if(src_addr)
|
||||||
tmp_addrlen = 0x1c;
|
tmp_addrlen = ADDR_STORAGE_LEN;
|
||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x7,4,4); // 0x70104
|
cmdbuf[0] = IPC_MakeHeader(0x7,4,4); // 0x70104
|
||||||
cmdbuf[1] = (u32)sockfd;
|
cmdbuf[1] = (u32)sockfd;
|
||||||
@ -53,9 +53,14 @@ ssize_t socuipc_cmd7(int sockfd, void *buf, size_t len, int flags, struct sockad
|
|||||||
|
|
||||||
if(src_addr != NULL) {
|
if(src_addr != NULL) {
|
||||||
src_addr->sa_family = tmpaddr[1];
|
src_addr->sa_family = tmpaddr[1];
|
||||||
if(*addrlen > tmpaddr[0])
|
|
||||||
*addrlen = tmpaddr[0];
|
socklen_t user_addrlen = tmpaddr[0];
|
||||||
memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - 2);
|
if(src_addr->sa_family == AF_INET)
|
||||||
|
user_addrlen += 8;
|
||||||
|
|
||||||
|
if(*addrlen > user_addrlen)
|
||||||
|
*addrlen = user_addrlen;
|
||||||
|
memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - sizeof(src_addr->sa_family));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -66,13 +71,13 @@ ssize_t socuipc_cmd8(int sockfd, void *buf, size_t len, int flags, struct sockad
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u32 tmp_addrlen = 0;
|
u32 tmp_addrlen = 0;
|
||||||
u8 tmpaddr[0x1c];
|
u8 tmpaddr[ADDR_STORAGE_LEN];
|
||||||
u32 saved_threadstorage[4];
|
u32 saved_threadstorage[4];
|
||||||
|
|
||||||
if(src_addr)
|
if(src_addr)
|
||||||
tmp_addrlen = 0x1c;
|
tmp_addrlen = ADDR_STORAGE_LEN;
|
||||||
|
|
||||||
memset(tmpaddr, 0, 0x1c);
|
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
|
||||||
|
|
||||||
cmdbuf[0] = 0x00080102;
|
cmdbuf[0] = 0x00080102;
|
||||||
cmdbuf[1] = (u32)sockfd;
|
cmdbuf[1] = (u32)sockfd;
|
||||||
@ -113,9 +118,14 @@ ssize_t socuipc_cmd8(int sockfd, void *buf, size_t len, int flags, struct sockad
|
|||||||
|
|
||||||
if(src_addr != NULL) {
|
if(src_addr != NULL) {
|
||||||
src_addr->sa_family = tmpaddr[1];
|
src_addr->sa_family = tmpaddr[1];
|
||||||
if(*addrlen > tmpaddr[0])
|
|
||||||
*addrlen = tmpaddr[0];
|
socklen_t user_addrlen = tmpaddr[0];
|
||||||
memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - 2);
|
if(src_addr->sa_family == AF_INET)
|
||||||
|
user_addrlen += 8;
|
||||||
|
|
||||||
|
if(*addrlen > user_addrlen)
|
||||||
|
*addrlen = user_addrlen;
|
||||||
|
memcpy(src_addr->sa_data, &tmpaddr[2], *addrlen - sizeof(src_addr->sa_family));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -8,15 +8,15 @@ ssize_t socuipc_cmd9(int sockfd, const void *buf, size_t len, int flags, const s
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u32 tmp_addrlen = 0;
|
u32 tmp_addrlen = 0;
|
||||||
u8 tmpaddr[0x1c];
|
u8 tmpaddr[ADDR_STORAGE_LEN];
|
||||||
|
|
||||||
memset(tmpaddr, 0, 0x1c);
|
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
|
||||||
|
|
||||||
if(dest_addr) {
|
if(dest_addr) {
|
||||||
if(dest_addr->sa_family == AF_INET)
|
if(dest_addr->sa_family == AF_INET)
|
||||||
tmp_addrlen = 8;
|
tmp_addrlen = 8;
|
||||||
else
|
else
|
||||||
tmp_addrlen = 0x1c;
|
tmp_addrlen = ADDR_STORAGE_LEN;
|
||||||
|
|
||||||
if(addrlen < tmp_addrlen) {
|
if(addrlen < tmp_addrlen) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
@ -62,15 +62,15 @@ ssize_t socuipc_cmda(int sockfd, const void *buf, size_t len, int flags, const s
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
u32 tmp_addrlen = 0;
|
u32 tmp_addrlen = 0;
|
||||||
u8 tmpaddr[0x1c];
|
u8 tmpaddr[ADDR_STORAGE_LEN];
|
||||||
|
|
||||||
memset(tmpaddr, 0, 0x1c);
|
memset(tmpaddr, 0, ADDR_STORAGE_LEN);
|
||||||
|
|
||||||
if(dest_addr) {
|
if(dest_addr) {
|
||||||
if(dest_addr->sa_family == AF_INET)
|
if(dest_addr->sa_family == AF_INET)
|
||||||
tmp_addrlen = 8;
|
tmp_addrlen = 8;
|
||||||
else
|
else
|
||||||
tmp_addrlen = 0x1c;
|
tmp_addrlen = ADDR_STORAGE_LEN;
|
||||||
|
|
||||||
if(addrlen < tmp_addrlen) {
|
if(addrlen < tmp_addrlen) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
|
@ -35,10 +35,13 @@ Result srvInit(void)
|
|||||||
rc = svcDuplicateHandle(&srvHandle, *srvPmGetSessionHandle()); // Prior to system version 7.0 srv:pm was a superset of srv:
|
rc = svcDuplicateHandle(&srvHandle, *srvPmGetSessionHandle()); // Prior to system version 7.0 srv:pm was a superset of srv:
|
||||||
else
|
else
|
||||||
rc = svcConnectToPort(&srvHandle, "srv:");
|
rc = svcConnectToPort(&srvHandle, "srv:");
|
||||||
if (R_FAILED(rc)) goto end;
|
|
||||||
|
|
||||||
rc = srvRegisterClient();
|
if (R_SUCCEEDED(rc))
|
||||||
end:
|
rc = srvRegisterClient();
|
||||||
|
else
|
||||||
|
// Prior to system version 11.0, the kernel filled the resulting handle with junk in case of failure
|
||||||
|
srvHandle = 0;
|
||||||
|
|
||||||
if (R_FAILED(rc)) srvExit();
|
if (R_FAILED(rc)) srvExit();
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -96,11 +99,11 @@ Result srvEnableNotification(Handle* semaphoreOut)
|
|||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x2,0,0);
|
cmdbuf[0] = IPC_MakeHeader(0x2,0,0);
|
||||||
|
|
||||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
rc = svcSendSyncRequest(srvHandle);
|
||||||
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
||||||
|
if(semaphoreOut) *semaphoreOut = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
|
||||||
|
|
||||||
if(semaphoreOut) *semaphoreOut = cmdbuf[3];
|
return rc;
|
||||||
|
|
||||||
return cmdbuf[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result srvRegisterService(Handle* out, const char* name, int maxSessions)
|
Result srvRegisterService(Handle* out, const char* name, int maxSessions)
|
||||||
@ -113,11 +116,11 @@ Result srvRegisterService(Handle* out, const char* name, int maxSessions)
|
|||||||
cmdbuf[3] = strnlen(name, 8);
|
cmdbuf[3] = strnlen(name, 8);
|
||||||
cmdbuf[4] = maxSessions;
|
cmdbuf[4] = maxSessions;
|
||||||
|
|
||||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
rc = svcSendSyncRequest(srvHandle);
|
||||||
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
||||||
|
if(out) *out = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
|
||||||
|
|
||||||
if(out) *out = cmdbuf[3];
|
return rc;
|
||||||
|
|
||||||
return cmdbuf[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result srvUnregisterService(const char* name)
|
Result srvUnregisterService(const char* name)
|
||||||
@ -144,11 +147,11 @@ Result srvGetServiceHandleDirect(Handle* out, const char* name)
|
|||||||
cmdbuf[3] = strnlen(name, 8);
|
cmdbuf[3] = strnlen(name, 8);
|
||||||
cmdbuf[4] = (u32)srvGetBlockingPolicy(); // per-thread setting, default is blocking
|
cmdbuf[4] = (u32)srvGetBlockingPolicy(); // per-thread setting, default is blocking
|
||||||
|
|
||||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
rc = svcSendSyncRequest(srvHandle);
|
||||||
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
||||||
|
if(out) *out = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
|
||||||
|
|
||||||
if(out) *out = cmdbuf[3];
|
return rc;
|
||||||
|
|
||||||
return cmdbuf[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result srvRegisterPort(const char* name, Handle clientHandle)
|
Result srvRegisterPort(const char* name, Handle clientHandle)
|
||||||
@ -191,11 +194,11 @@ Result srvGetPort(Handle* out, const char* name)
|
|||||||
cmdbuf[3] = strnlen(name, 8);
|
cmdbuf[3] = strnlen(name, 8);
|
||||||
cmdbuf[4] = 0x0;
|
cmdbuf[4] = 0x0;
|
||||||
|
|
||||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
rc = svcSendSyncRequest(srvHandle);
|
||||||
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
||||||
|
if(out) *out = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
|
||||||
|
|
||||||
if(out) *out = cmdbuf[3];
|
return rc;
|
||||||
|
|
||||||
return cmdbuf[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result srvWaitForPortRegistered(const char* name)
|
Result srvWaitForPortRegistered(const char* name)
|
||||||
@ -245,11 +248,11 @@ Result srvReceiveNotification(u32* notificationIdOut)
|
|||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0xB,0,0); // 0xB0000
|
cmdbuf[0] = IPC_MakeHeader(0xB,0,0); // 0xB0000
|
||||||
|
|
||||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
rc = svcSendSyncRequest(srvHandle);
|
||||||
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
||||||
|
if(notificationIdOut) *notificationIdOut = R_SUCCEEDED(rc) ? cmdbuf[2] : 0;
|
||||||
|
|
||||||
if(notificationIdOut) *notificationIdOut = cmdbuf[2];
|
return rc;
|
||||||
|
|
||||||
return cmdbuf[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result srvPublishToSubscriber(u32 notificationId, u32 flags)
|
Result srvPublishToSubscriber(u32 notificationId, u32 flags)
|
||||||
@ -274,12 +277,18 @@ Result srvPublishAndGetSubscriber(u32* processIdCountOut, u32* processIdsOut, u3
|
|||||||
cmdbuf[0] = IPC_MakeHeader(0xD,1,0); // 0xD0040
|
cmdbuf[0] = IPC_MakeHeader(0xD,1,0); // 0xD0040
|
||||||
cmdbuf[1] = notificationId;
|
cmdbuf[1] = notificationId;
|
||||||
|
|
||||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
rc = svcSendSyncRequest(srvHandle);
|
||||||
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
||||||
|
|
||||||
if(processIdCountOut) *processIdCountOut = cmdbuf[2];
|
if (R_SUCCEEDED(rc))
|
||||||
if(processIdsOut) memcpy(processIdsOut, &cmdbuf[3], cmdbuf[2] * sizeof(u32));
|
{
|
||||||
|
if(processIdCountOut) *processIdCountOut = cmdbuf[2];
|
||||||
|
if(processIdsOut) memcpy(processIdsOut, &cmdbuf[3], cmdbuf[2] * sizeof(u32));
|
||||||
|
}
|
||||||
|
else if(processIdCountOut)
|
||||||
|
*processIdCountOut = 0;
|
||||||
|
|
||||||
return cmdbuf[1];
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result srvIsServiceRegistered(bool* registeredOut, const char* name)
|
Result srvIsServiceRegistered(bool* registeredOut, const char* name)
|
||||||
@ -291,11 +300,12 @@ Result srvIsServiceRegistered(bool* registeredOut, const char* name)
|
|||||||
strncpy((char*) &cmdbuf[1], name,8);
|
strncpy((char*) &cmdbuf[1], name,8);
|
||||||
cmdbuf[3] = strnlen(name, 8);
|
cmdbuf[3] = strnlen(name, 8);
|
||||||
|
|
||||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
rc = svcSendSyncRequest(srvHandle);
|
||||||
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
||||||
|
|
||||||
if(registeredOut) *registeredOut = cmdbuf[2] & 0xFF;
|
if(registeredOut) *registeredOut = R_SUCCEEDED(rc) && (cmdbuf[2] & 0xFF) != 0;
|
||||||
|
|
||||||
return cmdbuf[1];
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result srvIsPortRegistered(bool* registeredOut, const char* name)
|
Result srvIsPortRegistered(bool* registeredOut, const char* name)
|
||||||
|
@ -510,7 +510,7 @@ SVC_BEGIN svcGetDmaState
|
|||||||
str r0, [sp, #-4]!
|
str r0, [sp, #-4]!
|
||||||
svc 0x57
|
svc 0x57
|
||||||
ldr r3, [sp], #4
|
ldr r3, [sp], #4
|
||||||
str r1, [r3]
|
strb r1, [r3]
|
||||||
bx lr
|
bx lr
|
||||||
SVC_END
|
SVC_END
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/env.h>
|
#include <3ds/env.h>
|
||||||
|
#include <3ds/3dslink.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
// System globals we define here
|
// System globals we define here
|
||||||
int __system_argc;
|
int __system_argc;
|
||||||
@ -47,5 +50,14 @@ void __attribute__((weak)) __system_initArgv(void)
|
|||||||
for (; *temp; temp ++);
|
for (; *temp; temp ++);
|
||||||
temp ++;
|
temp ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grab 3dslink host address if avaliable
|
||||||
|
if ( __system_argc > 1 &&
|
||||||
|
strlen(__system_argv[__system_argc - 1]) == 17 &&
|
||||||
|
strncmp(&__system_argv[__system_argc - 1][8], "_3DSLINK_", 8) == 0 )
|
||||||
|
{
|
||||||
|
__system_argc--;
|
||||||
|
__3dslink_host.s_addr = strtoul(__system_argv[__system_argc], NULL, 16);
|
||||||
|
}
|
||||||
__system_argv[__system_argc] = NULL;
|
__system_argv[__system_argc] = NULL;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ BEGIN_ASM_FUNC initSystem, weak
|
|||||||
END_ASM_FUNC
|
END_ASM_FUNC
|
||||||
|
|
||||||
BEGIN_ASM_FUNC __ctru_exit, weak
|
BEGIN_ASM_FUNC __ctru_exit, weak
|
||||||
bl __libc_fini_array
|
|
||||||
bl __appExit
|
bl __appExit
|
||||||
|
|
||||||
ldr r2, =saved_stack
|
ldr r2, =saved_stack
|
||||||
|
@ -3,21 +3,20 @@
|
|||||||
#include <sys/lock.h>
|
#include <sys/lock.h>
|
||||||
#include <sys/reent.h>
|
#include <sys/reent.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/svc.h>
|
#include <3ds/svc.h>
|
||||||
#include <3ds/env.h>
|
#include <3ds/env.h>
|
||||||
|
#include <3ds/os.h>
|
||||||
#include <3ds/synchronization.h>
|
#include <3ds/synchronization.h>
|
||||||
|
|
||||||
#include "../internal.h"
|
#include "../internal.h"
|
||||||
|
|
||||||
void __ctru_exit(int rc);
|
void __ctru_exit(int rc);
|
||||||
int __libctru_gtod(struct _reent *ptr, struct timeval *tp, struct timezone *tz);
|
|
||||||
|
|
||||||
extern const u8 __tdata_lma[];
|
struct _reent* __SYSCALL(getreent)()
|
||||||
extern const u8 __tdata_lma_end[];
|
|
||||||
extern u8 __tls_start[];
|
|
||||||
|
|
||||||
static struct _reent* __libctru_get_reent()
|
|
||||||
{
|
{
|
||||||
ThreadVars* tv = getThreadVars();
|
ThreadVars* tv = getThreadVars();
|
||||||
if (tv->magic != THREADVARS_MAGIC)
|
if (tv->magic != THREADVARS_MAGIC)
|
||||||
@ -28,39 +27,239 @@ static struct _reent* __libctru_get_reent()
|
|||||||
return tv->reent;
|
return tv->reent;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __libctru_nanosleep(const struct timespec *req, struct timespec *rem)
|
int __SYSCALL(clock_gettime)(clockid_t clock_id, struct timespec *tp) {
|
||||||
|
|
||||||
|
if (clock_id == CLOCK_REALTIME)
|
||||||
|
{
|
||||||
|
if (tp != NULL)
|
||||||
|
{
|
||||||
|
// Retrieve current time, adjusting epoch from 1900 to 1970
|
||||||
|
s64 ms_since_epoch = osGetTime() - 2208988800000ULL;
|
||||||
|
tp->tv_sec = ms_since_epoch / 1000;
|
||||||
|
tp->tv_nsec = (ms_since_epoch % 1000) * 1000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (clock_id == CLOCK_MONOTONIC)
|
||||||
|
{
|
||||||
|
if (tp != NULL)
|
||||||
|
{
|
||||||
|
// Use the ticks directly, as it offer the highest precision
|
||||||
|
u64 ticks_since_boot = svcGetSystemTick();
|
||||||
|
|
||||||
|
tp->tv_sec = ticks_since_boot / SYSCLOCK_ARM11;
|
||||||
|
tp->tv_nsec = ((ticks_since_boot % SYSCLOCK_ARM11) * 1000000000ULL) / SYSCLOCK_ARM11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __SYSCALL(clock_getres)(clockid_t clock_id, struct timespec *res) {
|
||||||
|
if (clock_id == CLOCK_REALTIME)
|
||||||
|
{
|
||||||
|
if (res != NULL)
|
||||||
|
{
|
||||||
|
res->tv_sec = 0;
|
||||||
|
res->tv_nsec = 1000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (clock_id == CLOCK_MONOTONIC)
|
||||||
|
{
|
||||||
|
if (res != NULL)
|
||||||
|
{
|
||||||
|
res->tv_sec = 0;
|
||||||
|
res->tv_nsec = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
int __SYSCALL(gettod_r)(struct _reent *ptr, struct timeval *tp, struct timezone *tz) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
if (tp != NULL) {
|
||||||
|
// Retrieve current time, adjusting epoch from 1900 to 1970
|
||||||
|
s64 now = osGetTime() - 2208988800000ULL;
|
||||||
|
|
||||||
|
// Convert to struct timeval
|
||||||
|
tp->tv_sec = now / 1000;
|
||||||
|
tp->tv_usec = (now % 1000) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tz != NULL) {
|
||||||
|
// Provide dummy information, as the 3DS does not have the concept of timezones
|
||||||
|
tz->tz_minuteswest = 0;
|
||||||
|
tz->tz_dsttime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __SYSCALL(nanosleep)(const struct timespec *req, struct timespec *rem)
|
||||||
{
|
{
|
||||||
svcSleepThread(req->tv_sec * 1000000000ull + req->tv_nsec);
|
svcSleepThread(req->tv_sec * 1000000000ull + req->tv_nsec);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __system_initSyscalls(void)
|
void __SYSCALL(lock_init) (_LOCK_T *lock)
|
||||||
{
|
{
|
||||||
// Register newlib syscalls
|
LightLock_Init(lock);
|
||||||
__syscalls.exit = __ctru_exit;
|
}
|
||||||
__syscalls.gettod_r = __libctru_gtod;
|
|
||||||
__syscalls.getreent = __libctru_get_reent;
|
|
||||||
__syscalls.nanosleep = __libctru_nanosleep;
|
|
||||||
|
|
||||||
// Register locking syscalls
|
void __SYSCALL(lock_acquire) (_LOCK_T *lock)
|
||||||
__syscalls.lock_init = LightLock_Init;
|
{
|
||||||
__syscalls.lock_acquire = LightLock_Lock;
|
LightLock_Lock(lock);
|
||||||
__syscalls.lock_try_acquire = LightLock_TryLock;
|
}
|
||||||
__syscalls.lock_release = LightLock_Unlock;
|
|
||||||
__syscalls.lock_init_recursive = RecursiveLock_Init;
|
|
||||||
__syscalls.lock_acquire_recursive = RecursiveLock_Lock;
|
|
||||||
__syscalls.lock_try_acquire_recursive = RecursiveLock_TryLock;
|
|
||||||
__syscalls.lock_release_recursive = RecursiveLock_Unlock;
|
|
||||||
|
|
||||||
// Initialize thread vars for the main thread
|
int __SYSCALL(lock_try_acquire) (_LOCK_T *lock)
|
||||||
|
{
|
||||||
|
return LightLock_TryLock(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __SYSCALL(lock_release) (_LOCK_T *lock)
|
||||||
|
{
|
||||||
|
LightLock_Unlock(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __SYSCALL(lock_init_recursive) (_LOCK_RECURSIVE_T *lock)
|
||||||
|
{
|
||||||
|
RecursiveLock_Init(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __SYSCALL(lock_acquire_recursive) (_LOCK_RECURSIVE_T *lock)
|
||||||
|
{
|
||||||
|
RecursiveLock_Lock(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __SYSCALL(lock_try_acquire_recursive) (_LOCK_RECURSIVE_T *lock)
|
||||||
|
{
|
||||||
|
return RecursiveLock_TryLock(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __SYSCALL(lock_release_recursive) (_LOCK_RECURSIVE_T *lock)
|
||||||
|
{
|
||||||
|
RecursiveLock_Unlock(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __SYSCALL(exit)(int rc) {
|
||||||
|
__ctru_exit(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __SYSCALL(cond_signal)(_COND_T *cond)
|
||||||
|
{
|
||||||
|
CondVar_Signal((CondVar*)cond);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __SYSCALL(cond_broadcast)(_COND_T *cond)
|
||||||
|
{
|
||||||
|
CondVar_Broadcast((CondVar*)cond);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __SYSCALL(cond_wait)(_COND_T *cond, _LOCK_T *lock, uint64_t timeout_ns)
|
||||||
|
{
|
||||||
|
return CondVar_WaitTimeout((CondVar*)cond, lock, timeout_ns) ? ETIMEDOUT : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __SYSCALL(cond_wait_recursive)(_COND_T *cond, _LOCK_RECURSIVE_T *lock, uint64_t timeout_ns)
|
||||||
|
{
|
||||||
|
uint32_t thread_tag_backup = 0;
|
||||||
|
if (lock->counter != 1)
|
||||||
|
return EBADF;
|
||||||
|
|
||||||
|
thread_tag_backup = lock->thread_tag;
|
||||||
|
lock->thread_tag = 0;
|
||||||
|
lock->counter = 0;
|
||||||
|
|
||||||
|
int err = CondVar_WaitTimeout((CondVar*)cond, &lock->lock, timeout_ns);
|
||||||
|
|
||||||
|
lock->thread_tag = thread_tag_backup;
|
||||||
|
lock->counter = 1;
|
||||||
|
|
||||||
|
return err ? ETIMEDOUT : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __SYSCALL(thread_create)(struct __pthread_t **thread, void* (*func)(void*), void *arg, void *stack_addr, size_t stack_size)
|
||||||
|
{
|
||||||
|
if (stack_addr) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stack_size) {
|
||||||
|
stack_size = 32*1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread t = threadCreate((ThreadFunc)func, arg, stack_size, 0x3F, 0, false);
|
||||||
|
if (t) {
|
||||||
|
*thread = (struct __pthread_t*)t;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*__SYSCALL(thread_join)(struct __pthread_t *thread)
|
||||||
|
{
|
||||||
|
threadJoin((Thread)thread, U64_MAX);
|
||||||
|
void* rc = (void*)threadGetExitCode((Thread)thread);
|
||||||
|
threadFree((Thread)thread);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __SYSCALL(thread_detach)(struct __pthread_t *thread)
|
||||||
|
{
|
||||||
|
threadDetach((Thread)thread);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __SYSCALL(thread_exit)(void *value)
|
||||||
|
{
|
||||||
|
threadExit((int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct __pthread_t *__SYSCALL(thread_self)(void)
|
||||||
|
{
|
||||||
|
return (struct __pthread_t*)threadGetCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initThreadVars(struct Thread_tag *thread)
|
||||||
|
{
|
||||||
ThreadVars* tv = getThreadVars();
|
ThreadVars* tv = getThreadVars();
|
||||||
tv->magic = THREADVARS_MAGIC;
|
tv->magic = THREADVARS_MAGIC;
|
||||||
tv->reent = _impure_ptr;
|
tv->reent = thread != NULL ? &thread->reent : _impure_ptr;
|
||||||
tv->thread_ptr = NULL;
|
tv->thread_ptr = thread;
|
||||||
tv->tls_tp = __tls_start-8; // ARM ELF TLS ABI mandates an 8-byte header
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||||
|
tv->tls_tp = (thread != NULL ? (u8*)thread->stacktop : __tls_start) - 8; // Arm ELF TLS ABI mandates an 8-byte header
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
tv->srv_blocking_policy = false;
|
tv->srv_blocking_policy = false;
|
||||||
|
|
||||||
u32 tls_size = __tdata_lma_end - __tdata_lma;
|
// Kernel does not initialize fpscr at all, so we must do it ourselves
|
||||||
if (tls_size)
|
// https://developer.arm.com/documentation/ddi0360/f/vfp-programmers-model/vfp11-system-registers/floating-point-status-and-control-register--fpscr
|
||||||
memcpy(__tls_start, __tdata_lma, tls_size);
|
|
||||||
|
// All flags clear, all interrupts disabled, all instruction scalar.
|
||||||
|
// As for the 3 below fields: default NaN mode, flush-to-zero both enabled & round to nearest.
|
||||||
|
__builtin_arm_set_fpscr(BIT(25) | BIT(24) | (0u << 22));
|
||||||
|
}
|
||||||
|
|
||||||
|
void __system_initSyscalls(void)
|
||||||
|
{
|
||||||
|
// Initialize thread vars for the main thread
|
||||||
|
initThreadVars(NULL);
|
||||||
|
u32 tls_size = __tdata_lma_end - __tdata_lma;
|
||||||
|
size_t tdata_start = alignTo((size_t)__tls_start, __tdata_align);
|
||||||
|
if (tls_size)
|
||||||
|
memcpy((void*)tdata_start, __tdata_lma, tls_size);
|
||||||
}
|
}
|
||||||
|
@ -3,22 +3,6 @@
|
|||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
extern const u8 __tdata_lma[];
|
|
||||||
extern const u8 __tdata_lma_end[];
|
|
||||||
extern u8 __tls_start[];
|
|
||||||
extern u8 __tls_end[];
|
|
||||||
|
|
||||||
struct Thread_tag
|
|
||||||
{
|
|
||||||
Handle handle;
|
|
||||||
ThreadFunc ep;
|
|
||||||
void* arg;
|
|
||||||
int rc;
|
|
||||||
bool detached, finished;
|
|
||||||
struct _reent reent;
|
|
||||||
void* stacktop;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __panic(void)
|
static void __panic(void)
|
||||||
{
|
{
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
@ -28,30 +12,33 @@ static void __panic(void)
|
|||||||
static void _thread_begin(void* arg)
|
static void _thread_begin(void* arg)
|
||||||
{
|
{
|
||||||
Thread t = (Thread)arg;
|
Thread t = (Thread)arg;
|
||||||
ThreadVars* tv = getThreadVars();
|
initThreadVars(t);
|
||||||
tv->magic = THREADVARS_MAGIC;
|
|
||||||
tv->reent = &t->reent;
|
|
||||||
tv->thread_ptr = t;
|
|
||||||
tv->tls_tp = (u8*)t->stacktop-8; // ARM ELF TLS ABI mandates an 8-byte header
|
|
||||||
tv->srv_blocking_policy = false;
|
|
||||||
t->ep(t->arg);
|
t->ep(t->arg);
|
||||||
threadExit(0);
|
threadExit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int prio, int core_id, bool detached)
|
Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int prio, int core_id, bool detached)
|
||||||
{
|
{
|
||||||
size_t stackoffset = (sizeof(struct Thread_tag)+7)&~7;
|
// The stack must be 8-aligned at minimum.
|
||||||
size_t allocsize = stackoffset + ((stack_size+7)&~7);
|
size_t align = __tdata_align > 8 ? __tdata_align : 8;
|
||||||
|
|
||||||
|
size_t stackoffset = alignTo(sizeof(struct Thread_tag), align);
|
||||||
|
size_t allocsize = alignTo(stackoffset + stack_size, align);
|
||||||
|
|
||||||
size_t tlssize = __tls_end-__tls_start;
|
size_t tlssize = __tls_end-__tls_start;
|
||||||
size_t tlsloadsize = __tdata_lma_end-__tdata_lma;
|
size_t tlsloadsize = __tdata_lma_end-__tdata_lma;
|
||||||
size_t tbsssize = tlssize-tlsloadsize;
|
size_t tbsssize = tlssize - tlsloadsize;
|
||||||
|
|
||||||
|
// memalign seems to have an implicit requirement that (size % align) == 0.
|
||||||
|
// Without this, it seems to return NULL whenever (align > 8).
|
||||||
|
size_t size = alignTo(allocsize + tlssize, align);
|
||||||
|
|
||||||
// Guard against overflow
|
// Guard against overflow
|
||||||
if (allocsize < stackoffset) return NULL;
|
if (allocsize < stackoffset) return NULL;
|
||||||
if ((allocsize-stackoffset) < stack_size) return NULL;
|
if ((allocsize - stackoffset) < stack_size) return NULL;
|
||||||
if ((allocsize+tlssize) < allocsize) return NULL;
|
if (size < allocsize) return NULL;
|
||||||
|
|
||||||
Thread t = (Thread)memalign(8,allocsize+tlssize);
|
Thread t = (Thread)memalign(align, size);
|
||||||
if (!t) return NULL;
|
if (!t) return NULL;
|
||||||
|
|
||||||
t->ep = entrypoint;
|
t->ep = entrypoint;
|
||||||
@ -60,10 +47,14 @@ Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int pri
|
|||||||
t->finished = false;
|
t->finished = false;
|
||||||
t->stacktop = (u8*)t + allocsize;
|
t->stacktop = (u8*)t + allocsize;
|
||||||
|
|
||||||
|
// ThreadVars.tls_tp must be aligned correctly, so we bump tdata_start to
|
||||||
|
// ensure that after subtracting 8 bytes for the TLS header, it will be aligned.
|
||||||
|
size_t tdata_start = 8 + alignTo((size_t)t->stacktop - 8, align);
|
||||||
|
|
||||||
if (tlsloadsize)
|
if (tlsloadsize)
|
||||||
memcpy(t->stacktop, __tdata_lma, tlsloadsize);
|
memcpy((void*)tdata_start, __tdata_lma, tlsloadsize);
|
||||||
if (tbsssize)
|
if (tbsssize)
|
||||||
memset((u8*)t->stacktop+tlsloadsize, 0, tbsssize);
|
memset((void*)tdata_start + tlsloadsize, 0, tbsssize);
|
||||||
|
|
||||||
// Set up child thread's reent struct, inheriting standard file handles
|
// Set up child thread's reent struct, inheriting standard file handles
|
||||||
_REENT_INIT_PTR(&t->reent);
|
_REENT_INIT_PTR(&t->reent);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user