Add the switch port first code

This commit is contained in:
Luiz Pestana 2021-07-24 20:32:55 -03:00
parent 981f360371
commit dd152f6232
9 changed files with 1172 additions and 19 deletions

8
.gitignore vendored
View File

@ -58,6 +58,14 @@ packages/
*.bc
*.so
# Ignore switch files
examples/*/*.d
examples/*/*.elf
examples/*/*.lst
examples/*/*.map
examples/*/*.nacp
examples/*/*.nro
# Ignore files build by xcode
*.mode*v*
*.pbxuser

View File

@ -30,7 +30,7 @@ RAYLIB_PATH ?= ..
# Define default options
# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB, PLATFORM_NX
PLATFORM ?= PLATFORM_DESKTOP
# Locations of your newly installed library and associated headers. See ../src/Makefile
@ -101,6 +101,14 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
PLATFORM_OS=LINUX
endif
endif
ifeq ($(PLATFORM),PLATFORM_NX)
UNAMEOS = $(shell uname)
ifeq ($(UNAMEOS),Linux)
PLATFORM_OS = LINUX
endif
RAYLIB_LIBTYPE = STATIC
USE_EXTERNAL_GLFW = TRUE
endif
# RAYLIB_PATH adjustment for different platforms.
# If using GNU make, we can get the full path to the top of the tree. Windows? BSD?
@ -131,6 +139,11 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH)
endif
ifeq ($(PLATFORM),PLATFORM_NX)
RAYLIB_PREFIX ?= ..
RAYLIB_PATH = $(realpath $(RAYLIB_PREFIX))
endif
# Define raylib release directory for compiled library.
# RAYLIB_RELEASE_PATH points to provided binaries or your freshly built version
RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src
@ -176,6 +189,12 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
# to use emscripten.h and emscripten_set_main_loop()
CC = emcc
endif
ifeq ($(PLATFORM),PLATFORM_NX)
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
include $(DEVKITPRO)/libnx/switch_rules
endif
# Define default make program
MAKE = make
@ -205,6 +224,8 @@ ifeq ($(BUILD_MODE),DEBUG)
else
ifeq ($(PLATFORM),PLATFORM_WEB)
CFLAGS += -Os
else ifeq ($(PLATFORM),PLATFORM_NX)
CFLAGS += -O2
else
CFLAGS += -s -O1
endif
@ -260,6 +281,12 @@ endif
# NOTE: Some external/extras libraries could be required (stb, physac, easings...)
INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external -I$(RAYLIB_PATH)/src/extras
ifeq ($(PLATFORM),PLATFORM_NX)
LIBDIRS := $(PORTLIBS) $(LIBNX)
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS += -ffunction-sections $(ARCH) $(foreach dir,$(LIBDIRS),-I$(dir)/include) -D__SWITCH__
endif
# Define additional directories containing required header files
ifeq ($(PLATFORM),PLATFORM_RPI)
# RPI required libraries
@ -308,6 +335,10 @@ ifeq ($(PLATFORM),PLATFORM_RPI)
LDFLAGS += -L/opt/vc/lib
endif
ifeq ($(PLATFORM),PLATFORM_NX)
LDFLAGS += -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$*.map $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
endif
# Define any libraries required on linking
# if you want to link libraries (libname.so or libname.a), use the -lname
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
@ -369,6 +400,10 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
# Libraries for web (HTML5) compiling
LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.a
endif
ifeq ($(PLATFORM),PLATFORM_NX)
LDLIBS = -lraylib -lEGL -lGLESv2 -lglapi -ldrm_nouveau -lnx -lm
LDLIBS += `$(PREFIX)pkg-config --libs sdl2`
endif
# Define all object files required
CORE = \
@ -521,6 +556,12 @@ physics: $(PHYSICS)
%: %.c
ifeq ($(PLATFORM),PLATFORM_ANDROID)
$(MAKE) -f Makefile.Android PROJECT_NAME=$@ PROJECT_SOURCE_FILES=$<
else ifeq ($(PLATFORM),PLATFORM_NX)
$(CC) -MMD -MP -MF $@.d -o $@.o -c $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
$(CXX) $@.o $(LDFLAGS) $(LDLIBS) -o $@.elf -D$(PLATFORM)
$(NM) -CSn $@.elf > $@.lst
nacptool --create "$(notdir $@)" "Raylib Example" "1.0.0" $@.nacp
elf2nro $@.elf $@.nro --icon=textures/resources/raylib_logo.jpg --nacp=$@.nacp
else
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
endif
@ -550,6 +591,16 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
endif
ifeq ($(PLATFORM),PLATFORM_WEB)
del *.o *.html *.js
endif
ifeq ($(PLATFORM),PLATFORM_NX)
find . -type f \( \
-name "*.o" -o \
-name "*.d" -o \
-name "*.elf" -o \
-name "*.lst" -o \
-name "*.map" -o \
-name "*.nacp" -o \
-name "*.nro" \) -delete
endif
@echo Cleaning done

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@ -11,6 +11,7 @@
# PLATFORM_RPI: Raspberry Pi (Raspbian)
# PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver
# PLATFORM_WEB: HTML5 (Chrome, Firefox)
# PLATFORM_NX: Switch LibNX
#
# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline.
# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline.
@ -66,7 +67,7 @@ RAYLIB_LIB_NAME ?= raylib
RAYLIB_RES_FILE ?= ./raylib.dll.rc.data
# Define raylib platform
# Options: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
# Options: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB, PLATFORM_NX
PLATFORM ?= PLATFORM_DESKTOP
# Include raylib modules on compilation
@ -144,6 +145,14 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
PLATFORM_OS = LINUX
endif
endif
ifeq ($(PLATFORM),PLATFORM_NX)
UNAMEOS = $(shell uname)
ifeq ($(UNAMEOS),Linux)
PLATFORM_OS = LINUX
endif
RAYLIB_LIBTYPE = STATIC
USE_EXTERNAL_GLFW = TRUE
endif
# RAYLIB_SRC_PATH adjustment for different platforms.
# If using GNU make, we can get the full path to the top of the tree. Windows? BSD?
@ -223,6 +232,9 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID)
# By default use OpenGL ES 2.0 on Android
GRAPHICS = GRAPHICS_API_OPENGL_ES2
endif
ifeq ($(PLATFORM),PLATFORM_NX)
GRAPHICS = GRAPHICS_API_OPENGL_ES2
endif
# Define default C compiler and archiver to pack library
CC = gcc
@ -271,6 +283,12 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID)
AR = $(ANDROID_TOOLCHAIN)/bin/x86_64-linux-android-ar
endif
endif
ifeq ($(PLATFORM),PLATFORM_NX)
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
include $(DEVKITPRO)/libnx/switch_rules
endif
# Define compiler flags:
# -O1 defines optimization level
@ -309,6 +327,9 @@ ifeq ($(RAYLIB_BUILD_MODE),RELEASE)
ifeq ($(PLATFORM),PLATFORM_ANDROID)
CFLAGS += -O2
endif
ifeq ($(PLATFORM),PLATFORM_NX)
CFLAGS += -O2
endif
endif
# Additional flags for compiler (if desired)
@ -387,6 +408,13 @@ endif
# NOTE: Several external required libraries (stb and others)
INCLUDE_PATHS = -I. -Iexternal/glfw/include -Iexternal/glfw/deps/mingw
ifeq ($(PLATFORM),PLATFORM_NX)
LIBDIRS := $(PORTLIBS) $(LIBNX)
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec
CFLAGS += -ffunction-sections -fdata-sections $(ARCH) $(foreach dir,$(LIBDIRS),-I$(dir)/include) -D__SWITCH__
INCLUDE_PATHS = -I.
endif
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS),BSD)
INCLUDE_PATHS += -I/usr/local/include
@ -475,7 +503,9 @@ endif
ifeq ($(RAYLIB_MODULE_PHYSAC),TRUE)
OBJS += physac.o
endif
ifeq ($(PLATFORM),PLATFORM_NX)
OBJS += nxusb.o
endif
ifeq ($(PLATFORM),PLATFORM_ANDROID)
OBJS += android_native_app_glue.o
endif
@ -550,7 +580,7 @@ else
else
# Compile raylib static library version $(RAYLIB_VERSION)
# WARNING: You should type "make clean" before doing this target.
$(AR) rcs $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(OBJS)
$(AR) rc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(OBJS)
@echo "raylib static library generated (lib$(RAYLIB_LIB_NAME).a) in $(RAYLIB_RELEASE_PATH)!"
endif
endif
@ -589,6 +619,10 @@ models.o : models.c raylib.h rlgl.h raymath.h
raudio.o : raudio.c raylib.h
$(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM)
# Compile switch usb module
nxusb.o : nxusb.c nxusb.h raylib.h
$(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM)
# Compile raygui module
# NOTE: raygui header should be distributed with raylib.h
raygui.o : raygui.c raygui.h gui_textbox_extended.h ricons.h
@ -621,6 +655,10 @@ android_native_app_glue.o : $(NATIVE_APP_GLUE)/android_native_app_glue.c
# See below and ../examples/Makefile for more information.
# TODO: Add other platforms. Remove sudo requirement, i.e. add USER mode.
ifeq ($(PLATFORM),PLATFORM_NX)
DESTDIR ?= $(PORTLIBS)
endif
# RAYLIB_INSTALL_PATH should be the desired full path to libraylib. No relative paths.
DESTDIR ?= /usr/local
RAYLIB_INSTALL_PATH ?= $(DESTDIR)/lib

View File

@ -61,6 +61,10 @@
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents()
// Enabling this flag allows manual control of the frame processes, use at your own risk
//#define SUPPORT_CUSTOM_FRAME_CONTROL 1
// Enabling this flag allows gamepad to set keyboard and mouse states
#define NX_SUPPORT_GAMEPAD_EMULATION 1
// Enabling this flag allows debugging by USB, the application will wait for an USB connection to start
//#define NX_USB_DEBUGGER 1
// core: Configuration values
//------------------------------------------------------------------------------------

View File

@ -11,6 +11,7 @@
* - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian, native mode)
* - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver
* - PLATFORM_WEB: HTML5 with WebAssembly
* - PLATFORM_NX: Switch LibNX
*
* CONFIGURATION:
*
@ -34,6 +35,10 @@
* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js
* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code.
*
* #define PLATFORM_NX
* Windowing and input system configured for libnx (Nintendo Switch)
* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
*
* #define SUPPORT_DEFAULT_FONT (default)
* Default font is loaded on window initialization to be available for the user to render simple text.
* NOTE: If enabled, uses external module functions to load default raylib font (module: text)
@ -264,6 +269,16 @@
#include <emscripten/html5.h> // Emscripten HTML5 library
#endif
#if defined(PLATFORM_NX)
#include <switch.h>
#include <EGL/egl.h> // EGL library
#include <EGL/eglext.h> // EGL extensions
#include <GLES2/gl2.h> // OpenGL ES 2.0 library
#if defined(NX_USB_DEBUGGER)
#include "nxusb.h"
#endif
#endif
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
@ -353,7 +368,7 @@ typedef struct CoreData {
#if defined(PLATFORM_RPI)
EGL_DISPMANX_WINDOW_T handle; // Native window handle (graphic device)
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
#if defined(PLATFORM_DRM)
int fd; // File descriptor for /dev/dri/...
drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector
@ -364,6 +379,9 @@ typedef struct CoreData {
struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping)
uint32_t prevFB; // Previous GBM framebufer (during frame swapping)
#endif // PLATFORM_DRM
#if defined(PLATFORM_NX)
NWindow *gbmSurface; // GBM surface
#endif
EGLDisplay device; // Native display device (physical screen connection)
EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
EGLContext context; // Graphic context, mode in which drawing can be done
@ -458,6 +476,9 @@ typedef struct CoreData {
pthread_t threadId; // Gamepad reading thread id
int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor
char name[64]; // Gamepad name holder
#endif
#if defined(PLATFORM_NX)
PadState nxPad[MAX_GAMEPADS]; // Gamepad state holder
#endif
} Gamepad;
} Input;
@ -468,7 +489,7 @@ typedef struct CoreData {
double draw; // Time measure for frame draw
double frame; // Time measure for one frame
double target; // Desired time for one frame, if 0 not applied
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
unsigned long long base; // Base time measure for hi-res timer
#endif
unsigned int frameCounter; // Frame counter
@ -691,6 +712,9 @@ struct android_app *GetAndroidApp(void)
// NOTE: data parameter could be used to pass any kind of required data to the initialization
void InitWindow(int width, int height, const char *title)
{
#if defined(PLATFORM_NX) && defined(NX_USB_DEBUGGER)
NxUsbDebuggerInit();
#endif
TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
@ -765,7 +789,7 @@ void InitWindow(int width, int height, const char *title)
}
}
#endif
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
// Initialize graphics device (display device and OpenGL context)
// NOTE: returns true if window and graphic device has been initialized successfully
CORE.Window.ready = InitGraphicsDevice(width, height);
@ -812,6 +836,13 @@ void InitWindow(int width, int height, const char *title)
InitKeyboard(); // Keyboard init (stdin)
#endif
#if defined(PLATFORM_NX)
// Configure our supported input layout
padConfigureInput(MAX_GAMEPADS, HidNpadStyleSet_NpadStandard);
// Initialize the gamepads
padInitializeDefault(&CORE.Input.Gamepad.nxPad[0]);
#endif
#if defined(PLATFORM_WEB)
// Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
//emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
@ -872,7 +903,7 @@ void CloseWindow(void)
timeEndPeriod(1); // Restore time period
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_NX)
// Close surface, context and display
if (CORE.Window.device != EGL_NO_DISPLAY)
{
@ -991,6 +1022,9 @@ void CloseWindow(void)
CORE.Window.ready = false;
TRACELOG(LOG_INFO, "Window closed successfully");
#if defined(PLATFORM_NX) && defined(NX_USB_DEBUGGER)
NxUsbDebuggerEnd();
#endif
}
// Check if KEY_ESCAPE pressed or Close icon pressed
@ -1022,7 +1056,10 @@ bool WindowShouldClose(void)
else return true;
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
#if defined(PLATFORM_NX)
if (!appletMainLoop()) return true;
#endif
if (CORE.Window.ready) return CORE.Window.shouldClose;
else return true;
#endif
@ -2601,7 +2638,7 @@ double GetTime(void)
return glfwGetTime(); // Elapsed time since glfwInit()
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
struct timespec ts = { 0 };
clock_gettime(CLOCK_MONOTONIC, &ts);
unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
@ -3835,7 +3872,7 @@ static bool InitGraphicsDevice(int width, int height)
}
#endif // PLATFORM_DESKTOP || PLATFORM_WEB
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
CORE.Window.fullscreen = true;
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
@ -4025,8 +4062,10 @@ static bool InitGraphicsDevice(int width, int height)
//EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
//EGL_STENCIL_SIZE, 8, // Stencil buffer size
#if !defined(PLATFORM_NX)
EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
#endif
EGL_NONE
};
@ -4036,7 +4075,7 @@ static bool InitGraphicsDevice(int width, int height)
EGL_NONE
};
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
EGLint numConfigs = 0;
// Get an EGL device connection
@ -4203,7 +4242,21 @@ static bool InitGraphicsDevice(int width, int height)
//---------------------------------------------------------------------------------
#endif // PLATFORM_RPI
#if defined(PLATFORM_DRM)
#if defined(PLATFORM_NX)
CORE.Window.gbmSurface = nwindowGetDefault();
if (appletGetOperationMode() == AppletOperationMode_Console)
{
CORE.Window.display.width = 1920;
CORE.Window.display.height = 1080;
}
else if (appletGetOperationMode() == AppletOperationMode_Handheld)
{
CORE.Window.display.width = 1280;
CORE.Window.display.height = 720;
}
#endif
#if defined(PLATFORM_DRM) || defined(PLATFORM_NX)
CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL);
if (EGL_NO_SURFACE == CORE.Window.surface)
{
@ -4470,7 +4523,7 @@ void SwapScreenBuffer(void)
glfwSwapBuffers(CORE.Window.handle);
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
eglSwapBuffers(CORE.Window.device, CORE.Window.surface);
#if defined(PLATFORM_DRM)
@ -4522,7 +4575,7 @@ void SwapScreenBuffer(void)
CORE.Window.prevBO = bo;
#endif // PLATFORM_DRM
#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM
#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM || PLATFORM_NX
}
// Register all input events
@ -4773,6 +4826,206 @@ void PollInputEvents(void)
}
#endif
#if defined(PLATFORM_NX)
int nxGamepadIndex = 0;
// Scan the gamepad. This should be done once for each frame
padUpdate(&CORE.Input.Gamepad.nxPad[nxGamepadIndex]);
CORE.Input.Gamepad.ready[nxGamepadIndex] = padIsConnected(&CORE.Input.Gamepad.nxPad[nxGamepadIndex]);
if (CORE.Input.Gamepad.ready[nxGamepadIndex]) {
// Returns the set of buttons that are currently pressed
u64 kHeld = padGetButtons(&CORE.Input.Gamepad.nxPad[nxGamepadIndex]);
u64 kButton;
for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
{
// Register previous gamepad states
CORE.Input.Gamepad.previousButtonState[nxGamepadIndex][k] = CORE.Input.Gamepad.currentButtonState[nxGamepadIndex][k];
// Check digital buttons
kButton = 0;
switch (k)
{
case GAMEPAD_BUTTON_LEFT_FACE_UP: kButton = HidNpadButton_Up; break;
case GAMEPAD_BUTTON_LEFT_FACE_RIGHT: kButton = HidNpadButton_Right; break;
case GAMEPAD_BUTTON_LEFT_FACE_DOWN: kButton = HidNpadButton_Down; break;
case GAMEPAD_BUTTON_LEFT_FACE_LEFT: kButton = HidNpadButton_Left; break;
case GAMEPAD_BUTTON_RIGHT_FACE_UP: kButton = HidNpadButton_X; break;
case GAMEPAD_BUTTON_RIGHT_FACE_RIGHT: kButton = HidNpadButton_A; break;
case GAMEPAD_BUTTON_RIGHT_FACE_DOWN: kButton = HidNpadButton_B; break;
case GAMEPAD_BUTTON_RIGHT_FACE_LEFT: kButton = HidNpadButton_Y; break;
case GAMEPAD_BUTTON_LEFT_TRIGGER_1: kButton = HidNpadButton_L; break;
case GAMEPAD_BUTTON_LEFT_TRIGGER_2: kButton = HidNpadButton_ZL; break;
case GAMEPAD_BUTTON_RIGHT_TRIGGER_1: kButton = HidNpadButton_R; break;
case GAMEPAD_BUTTON_RIGHT_TRIGGER_2: kButton = HidNpadButton_ZR; break;
case GAMEPAD_BUTTON_MIDDLE_LEFT: kButton = HidNpadButton_Minus; break;
case GAMEPAD_BUTTON_MIDDLE_RIGHT: kButton = HidNpadButton_Plus; break;
case GAMEPAD_BUTTON_LEFT_THUMB: kButton = HidNpadButton_StickL; break;
case GAMEPAD_BUTTON_RIGHT_THUMB: kButton = HidNpadButton_StickR; break;
}
if (kHeld & kButton) {
CORE.Input.Gamepad.currentButtonState[nxGamepadIndex][k] = 1;
CORE.Input.Gamepad.lastButtonPressed = k;
} else {
CORE.Input.Gamepad.currentButtonState[nxGamepadIndex][k] = 0;
}
}
// Check analogic axis and buttons
HidAnalogStickState kAxisL = padGetStickPos(&CORE.Input.Gamepad.nxPad[nxGamepadIndex], 0);
HidAnalogStickState kAxisR = padGetStickPos(&CORE.Input.Gamepad.nxPad[nxGamepadIndex], 1);
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_LEFT_X] = (float)kAxisL.x / 32767.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_LEFT_Y] = (float)kAxisL.y / 32767.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_X] = (float)kAxisR.x / 32767.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_Y] = (float)kAxisR.y / 32767.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_LEFT_TRIGGER] = (kHeld & HidNpadButton_ZL) ? 1.0f : 0.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_TRIGGER] = (kHeld & HidNpadButton_ZR) ? 1.0f : 0.0f;
#if defined(NX_SUPPORT_GAMEPAD_EMULATION)
CORE.Input.Keyboard.previousKeyState[KEY_RIGHT] = CORE.Input.Keyboard.currentKeyState[KEY_RIGHT];
CORE.Input.Keyboard.previousKeyState[KEY_D] = CORE.Input.Keyboard.currentKeyState[KEY_D];
if (kHeld & HidNpadButton_Right || kHeld & HidNpadButton_StickLRight) {
CORE.Input.Keyboard.currentKeyState[KEY_RIGHT] = 1;
CORE.Input.Keyboard.currentKeyState[KEY_D] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_RIGHT] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_D] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_LEFT] = CORE.Input.Keyboard.currentKeyState[KEY_LEFT];
CORE.Input.Keyboard.previousKeyState[KEY_A] = CORE.Input.Keyboard.currentKeyState[KEY_A];
if (kHeld & HidNpadButton_Left || kHeld & HidNpadButton_StickLLeft) {
CORE.Input.Keyboard.currentKeyState[KEY_LEFT] = 1;
CORE.Input.Keyboard.currentKeyState[KEY_A] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_LEFT] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_A] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_DOWN] = CORE.Input.Keyboard.currentKeyState[KEY_DOWN];
CORE.Input.Keyboard.previousKeyState[KEY_S] = CORE.Input.Keyboard.currentKeyState[KEY_S];
if (kHeld & HidNpadButton_Down || kHeld & HidNpadButton_StickLDown) {
CORE.Input.Keyboard.currentKeyState[KEY_DOWN] = 1;
CORE.Input.Keyboard.currentKeyState[KEY_S] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_DOWN] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_S] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_UP] = CORE.Input.Keyboard.currentKeyState[KEY_UP];
CORE.Input.Keyboard.previousKeyState[KEY_W] = CORE.Input.Keyboard.currentKeyState[KEY_W];
if (kHeld & HidNpadButton_Up || kHeld & HidNpadButton_StickLUp) {
CORE.Input.Keyboard.currentKeyState[KEY_UP] = 1;
CORE.Input.Keyboard.currentKeyState[KEY_W] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_UP] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_W] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_Q] = CORE.Input.Keyboard.currentKeyState[KEY_Q];
if (kHeld & HidNpadButton_Y) {
CORE.Input.Keyboard.currentKeyState[KEY_Q] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_Q] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_E] = CORE.Input.Keyboard.currentKeyState[KEY_E];
if (kHeld & HidNpadButton_A) {
CORE.Input.Keyboard.currentKeyState[KEY_E] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_E] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_R] = CORE.Input.Keyboard.currentKeyState[KEY_R];
if (kHeld & HidNpadButton_X) {
CORE.Input.Keyboard.currentKeyState[KEY_R] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_R] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_F] = CORE.Input.Keyboard.currentKeyState[KEY_F];
if (kHeld & HidNpadButton_B) {
CORE.Input.Keyboard.currentKeyState[KEY_F] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_F] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_ENTER] = CORE.Input.Keyboard.currentKeyState[KEY_ENTER];
CORE.Input.Keyboard.previousKeyState[KEY_SPACE] = CORE.Input.Keyboard.currentKeyState[KEY_SPACE];
CORE.Input.Keyboard.previousKeyState[KEY_ESCAPE] = CORE.Input.Keyboard.currentKeyState[KEY_ESCAPE];
if (kHeld & HidNpadButton_Plus && kHeld & HidNpadButton_Minus) {
CORE.Input.Keyboard.currentKeyState[KEY_ENTER] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_SPACE] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_ESCAPE] = 1;
} else {
if (kHeld & HidNpadButton_Plus) {
CORE.Input.Keyboard.currentKeyState[KEY_ENTER] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_ENTER] = 0;
}
if (kHeld & HidNpadButton_Minus) {
CORE.Input.Keyboard.currentKeyState[KEY_SPACE] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_SPACE] = 0;
}
}
CORE.Input.Keyboard.previousKeyState[KEY_LEFT_SHIFT] = CORE.Input.Keyboard.currentKeyState[KEY_LEFT_SHIFT];
if (kHeld & GAMEPAD_BUTTON_LEFT_THUMB) {
CORE.Input.Keyboard.currentKeyState[KEY_LEFT_SHIFT] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_LEFT_SHIFT] = 0;
}
CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_LEFT] = CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT];
if (kHeld & HidNpadButton_ZL) {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 1;
} else {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 0;
}
CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_MIDDLE] = CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE];
if (kHeld & GAMEPAD_BUTTON_RIGHT_THUMB) {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 1;
} else {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 0;
}
CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_RIGHT] = CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT];
if (kHeld & HidNpadButton_ZR) {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 1;
} else {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 0;
}
CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
if (kHeld & HidNpadButton_L) {
CORE.Input.Mouse.currentWheelMove = -1.0f;
} else if (kHeld & HidNpadButton_R) {
CORE.Input.Mouse.currentWheelMove = 1.0f;
} else {
CORE.Input.Mouse.currentWheelMove = 0.0f;
}
CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.currentPosition.x;
CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.currentPosition.y;
CORE.Input.Mouse.currentPosition.x += CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_X] * 10;
CORE.Input.Mouse.currentPosition.y -= CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_Y] * 10;
if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
else if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x;
if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
else if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y;
if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
#endif
}
#endif
#if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI)
// NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
// stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console

View File

@ -1556,6 +1556,10 @@ extern "C" {
#ifdef __EMSCRIPTEN__
#define MA_EMSCRIPTEN
#endif
#ifdef __SWITCH__
#define MA_SWITCH
#define MA_NO_RUNTIME_LINKING
#endif
#endif
#include <stddef.h> /* For size_t. */
@ -2988,6 +2992,9 @@ This section contains the APIs for device playback and capture. Here is where yo
#if defined(MA_EMSCRIPTEN)
#define MA_SUPPORT_WEBAUDIO
#endif
#if defined(MA_SWITCH)
#define MA_SUPPORT_SDL
#endif
/* All platforms should support custom backends. */
#define MA_SUPPORT_CUSTOM
@ -3037,6 +3044,9 @@ This section contains the APIs for device playback and capture. Here is where yo
#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
#define MA_HAS_WEBAUDIO
#endif
#if defined(MA_SUPPORT_SDL) && !defined(MA_NO_SDL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SDL))
#define MA_HAS_SDL
#endif
#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
#define MA_HAS_CUSTOM
#endif
@ -3076,6 +3086,7 @@ typedef enum
ma_backend_aaudio,
ma_backend_opensl,
ma_backend_webaudio,
ma_backend_sdl,
ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */
ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
} ma_backend;
@ -3306,6 +3317,7 @@ typedef union
ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
int sdl; /* SDL devices are identified with an index. */
union
{
int i;
@ -3960,6 +3972,19 @@ struct ma_context
int _unused;
} webaudio;
#endif
#ifdef MA_SUPPORT_SDL
struct
{
ma_handle hSDL; // SDL
ma_proc SDL_InitSubSystem;
ma_proc SDL_QuitSubSystem;
ma_proc SDL_GetNumAudioDevices;
ma_proc SDL_GetAudioDeviceName;
ma_proc SDL_CloseAudioDevice;
ma_proc SDL_OpenAudioDevice;
ma_proc SDL_PauseAudioDevice;
} sdl;
#endif
#ifdef MA_SUPPORT_NULL
struct
{
@ -4247,6 +4272,12 @@ struct ma_device
int indexCapture;
} webaudio;
#endif
#ifdef MA_SUPPORT_SDL
struct
{
ma_uint32 deviceID;
} sdl;
#endif
#ifdef MA_SUPPORT_NULL
struct
{
@ -10391,7 +10422,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority
int result;
pthread_attr_t* pAttr = NULL;
#if !defined(__EMSCRIPTEN__)
#if !defined(__EMSCRIPTEN__) && !defined(__SWITCH__)
/* Try setting the thread priority. It's not critical if anything fails here. */
pthread_attr_t attr;
if (pthread_attr_init(&attr) == 0) {
@ -10900,7 +10931,9 @@ DEVICE I/O
#ifdef MA_POSIX
#include <sys/types.h>
#include <unistd.h>
#ifndef MA_NO_RUNTIME_LINKING
#include <dlfcn.h>
#endif
#endif
/*
@ -10919,6 +10952,24 @@ not officially supporting this, but I'm leaving it here in case it's useful for
#endif
#endif
#ifdef MA_ENABLE_SDL
#define ML_HAS_SDL
// SDL headers are necessary if using compile-time linking.
#ifdef MA_NO_RUNTIME_LINKING
#ifdef __has_include
#ifdef MA_EMSCRIPTEN
#if !__has_include(<SDL/SDL_audio.h>)
#undef MA_HAS_SDL
#endif
#else
#if !__has_include(<SDL2/SDL_audio.h>)
#undef MA_HAS_SDL
#endif
#endif
#endif
#endif
#endif
MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags)
{
@ -10953,6 +11004,7 @@ MA_API const char* ma_get_backend_name(ma_backend backend)
case ma_backend_aaudio: return "AAudio";
case ma_backend_opensl: return "OpenSL|ES";
case ma_backend_webaudio: return "Web Audio";
case ma_backend_sdl: return "SDL";
case ma_backend_custom: return "Custom";
case ma_backend_null: return "Null";
default: return "Unknown";
@ -11045,6 +11097,12 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
#else
return MA_FALSE;
#endif
case ma_backend_sdl:
#if defined(MA_HAS_SDL)
return MA_TRUE;
#else
return MA_FALSE;
#endif
case ma_backend_custom:
#if defined(MA_HAS_CUSTOM)
return MA_TRUE;
@ -11113,6 +11171,7 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
case ma_backend_aaudio: return MA_FALSE;
case ma_backend_opensl: return MA_FALSE;
case ma_backend_webaudio: return MA_FALSE;
case ma_backend_sdl: return MA_FALSE;
case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */
case ma_backend_null: return MA_FALSE;
default: return MA_FALSE;
@ -11596,6 +11655,7 @@ Timing
Dynamic Linking
*******************************************************************************/
#ifndef MA_NO_RUNTIME_LINKING
MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
{
ma_handle handle;
@ -11687,7 +11747,7 @@ MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symb
(void)pContext; /* It's possible for pContext to be unused. */
return proc;
}
#endif
#if 0
static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
@ -31985,6 +32045,227 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
#endif /* Web Audio */
/******************************************************************************
SDL Backend
******************************************************************************/
#ifdef MA_HAS_SDL
#define MA_SDL_INIT_AUDIO 0x00000010
#define MA_AUDIO_U8 0x0008
#define MA_AUDIO_S16 0x8010
#define MA_AUDIO_S32 0x8020
#define MA_AUDIO_F32 0x8120
#define MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001
#define MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002
#define MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004
#define MA_SDL_AUDIO_ALLOW_ANY_CHANGE (MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE | MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE)
#include <SDL2/SDL.h>
typedef int (* MA_PFN_SDL_InitSubSystem)(ma_uint32 flags);
typedef void (* MA_PFN_SDL_QuitSubSystem)(ma_uint32 flags);
typedef int (* MA_PFN_SDL_GetNumAudioDevices)(int iscapture);
typedef const char* (* MA_PFN_SDL_GetAudioDeviceName)(int index, int iscapture);
typedef void (* MA_PFN_SDL_CloseAudioDevice)(SDL_AudioDeviceID dev);
typedef SDL_AudioDeviceID (* MA_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const SDL_AudioSpec* desired, SDL_AudioSpec* obtained, int allowed_changes);
typedef void (* MA_PFN_SDL_PauseAudioDevice)(SDL_AudioDeviceID dev, int pause_on);
static SDL_AudioFormat ma_format_to_sdl(ma_format format)
{
switch (format)
{
case ma_format_unknown: return 0;
case ma_format_u8: return MA_AUDIO_U8;
case ma_format_s16: return MA_AUDIO_S16;
case ma_format_s24: return MA_AUDIO_S32; // Closest match.
case ma_format_s32: return MA_AUDIO_S32;
case ma_format_f32: return MA_AUDIO_F32;
default: return 0;
}
}
static ma_format ma_format_from_sdl(SDL_AudioFormat format)
{
switch (format)
{
case MA_AUDIO_U8: return ma_format_u8;
case MA_AUDIO_S16: return ma_format_s16;
case MA_AUDIO_S32: return ma_format_s32;
case MA_AUDIO_F32: return ma_format_f32;
default: return ma_format_unknown;
}
}
static ma_result ma_context_get_device_info__sdl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(deviceType == ma_device_type_playback);
pDeviceInfo->id.sdl = 0;
pDeviceInfo->isDefault = MA_TRUE;
SDL_AudioSpec desiredSpec, obtainedSpec;
MA_ZERO_OBJECT(&desiredSpec);
SDL_AudioDeviceID tempDeviceID = ((MA_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(NULL, 0, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
((MA_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID);
ma_format format = ma_format_from_sdl(obtainedSpec.format);
if (format == ma_format_unknown) {
format = ma_format_f32;
}
pDeviceInfo->nativeDataFormatCount = 0;
ma_device_info_add_native_data_format(pDeviceInfo, format, obtainedSpec.channels, obtainedSpec.freq, 0);
return MA_SUCCESS;
}
static ma_result ma_device_uninit__sdl(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
((MA_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID);
return MA_SUCCESS;
}
static void ma_audio_callback__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
{
ma_device* pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
ma_uint32 bytesPerFrame = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
ma_uint32 frameCount = (ma_uint32)bufferSizeInBytes / bytesPerFrame;
ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, frameCount);
}
static ma_result ma_device_init__sdl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
{
MA_ASSERT(pDevice != NULL);
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pConfig->deviceType == ma_device_type_playback);
if (pDescriptorPlayback->sampleRate == 0) {
pDescriptorPlayback->sampleRate = MA_DEFAULT_SAMPLE_RATE;
}
pDescriptorPlayback->periodSizeInFrames = pDescriptorPlayback->periodCount * ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
/* SDL wants the buffer size to be a power of 2 for some reason. */
if (pDescriptorPlayback->periodSizeInFrames > 32768) {
pDescriptorPlayback->periodSizeInFrames = 32768;
} else {
pDescriptorPlayback->periodSizeInFrames = ma_next_power_of_2(pDescriptorPlayback->periodSizeInFrames);
}
MA_ASSERT(pDescriptorPlayback->periodSizeInFrames <= 32768);
/* We now have enough information to set up the device. */
SDL_AudioSpec desiredSpec, obtainedSpec;
MA_ZERO_OBJECT(&desiredSpec);
desiredSpec.freq = (int)pDescriptorPlayback->sampleRate;
desiredSpec.format = ma_format_to_sdl(pDescriptorPlayback->format);
desiredSpec.channels = (ma_uint8)pDescriptorPlayback->channels;
desiredSpec.samples = (ma_uint16)pDescriptorPlayback->periodSizeInFrames;
desiredSpec.callback = ma_audio_callback__sdl;
desiredSpec.userdata = pDevice;
/* We'll fall back to f32 if we don't have an appropriate mapping between SDL and miniaudio. */
if (desiredSpec.format == ma_format_unknown) {
desiredSpec.format = MA_AUDIO_F32;
}
pDevice->sdl.deviceID = ((MA_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(NULL, 0, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
/* The descriptor needs to be updated with our actual settings. */
pDescriptorPlayback->format = ma_format_from_sdl(obtainedSpec.format);
pDescriptorPlayback->channels = obtainedSpec.channels;
pDescriptorPlayback->sampleRate = (ma_uint32)obtainedSpec.freq;
ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
pDescriptorPlayback->periodSizeInFrames = obtainedSpec.samples;
pDescriptorPlayback->periodCount = 1; /* SDL doesn't use the notion of period counts, so just set to 1. */
return MA_SUCCESS;
}
static ma_result ma_device_start__sdl(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
((MA_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0);
return MA_SUCCESS;
}
static ma_result ma_device_stop__sdl(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
((MA_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1);
ma_device__set_state(pDevice, MA_STATE_STOPPED);
ma_stop_proc onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
return MA_SUCCESS;
}
static ma_result ma_context_uninit__sdl(ma_context* pContext)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pContext->backend == ma_backend_sdl);
((MA_PFN_SDL_QuitSubSystem)pContext->sdl.SDL_QuitSubSystem)(MA_SDL_INIT_AUDIO);
return MA_SUCCESS;
}
static ma_result ma_context_init__sdl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
{
MA_ASSERT(pContext != NULL);
(void)pConfig; /* Unused. */
pContext->sdl.SDL_InitSubSystem = (ma_proc)SDL_InitSubSystem;
pContext->sdl.SDL_QuitSubSystem = (ma_proc)SDL_QuitSubSystem;
pContext->sdl.SDL_GetNumAudioDevices = (ma_proc)SDL_GetNumAudioDevices;
pContext->sdl.SDL_GetAudioDeviceName = (ma_proc)SDL_GetAudioDeviceName;
pContext->sdl.SDL_CloseAudioDevice = (ma_proc)SDL_CloseAudioDevice;
pContext->sdl.SDL_OpenAudioDevice = (ma_proc)SDL_OpenAudioDevice;
pContext->sdl.SDL_PauseAudioDevice = (ma_proc)SDL_PauseAudioDevice;
int resultSDL = ((MA_PFN_SDL_InitSubSystem)pContext->sdl.SDL_InitSubSystem)(MA_SDL_INIT_AUDIO);
if (resultSDL != 0) {
return MA_ERROR;
}
pCallbacks->onContextInit = ma_context_init__sdl;
pCallbacks->onContextUninit = ma_context_uninit__sdl;
pCallbacks->onContextEnumerateDevices = NULL;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sdl;
pCallbacks->onDeviceInit = ma_device_init__sdl;
pCallbacks->onDeviceUninit = ma_device_uninit__sdl;
pCallbacks->onDeviceStart = ma_device_start__sdl;
pCallbacks->onDeviceStop = ma_device_stop__sdl;
pCallbacks->onDeviceRead = NULL;
pCallbacks->onDeviceWrite = NULL;
pCallbacks->onDeviceDataLoop = NULL;
return MA_SUCCESS;
}
#endif /* SDL */
static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels)
{
@ -32340,7 +32621,7 @@ static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
#if !defined(__EMSCRIPTEN__)
#if !defined(__EMSCRIPTEN__) && !defined(__SWITCH__)
pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
@ -32536,6 +32817,12 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC
pContext->callbacks.onContextInit = ma_context_init__webaudio;
} break;
#endif
#ifdef MA_HAS_SDL
case ma_backend_sdl:
{
pContext->callbacks.onContextInit = ma_context_init__sdl;
} break;
#endif
#ifdef MA_HAS_CUSTOM
case ma_backend_custom:
{

502
src/nxusb.c Normal file
View File

@ -0,0 +1,502 @@
#include "nxusb.h"
#include <switch.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <sys/iosupport.h>
#define U64_MAX ((u64)~0ULL)
#define TOTAL_INTERFACES 4
#define TOTAL_ENDPOINTS 4
#define EP_IN 0
#define EP_OUT 1
typedef enum {
UsbDirectionRead = 0,
UsbDirectionWrite = 1,
} UsbDirection;
typedef struct {
struct usb_interface_descriptor *interface_desc;
struct usb_endpoint_descriptor *endpoint_desc[4];
const char *string_descriptor;
} UsbInterfaceDesc;
typedef struct {
UsbDsEndpoint *endpoint;
u8 *buffer;
RwLock lock;
} UsbCommsEndpoint;
typedef struct {
RwLock lock;
bool initialized;
UsbDsInterface* interface;
u32 endpoint_number;
UsbCommsEndpoint endpoint[TOTAL_ENDPOINTS];
} UsbCommsInterface;
static bool g_usbCommsInitialized = false;
static UsbCommsInterface g_usbCommsInterfaces[TOTAL_INTERFACES];
static RwLock g_usbCommsLock;
static int ep_in = 1;
static int ep_out = 1;
static Result UsbCommsInterfaceInit(u32 intf_ind, const UsbInterfaceDesc *info)
{
Result rc = 0;
UsbCommsInterface *interface = &g_usbCommsInterfaces[intf_ind];
u8 index = 0;
if (info->string_descriptor != NULL)
{
usbDsAddUsbStringDescriptor(&index, info->string_descriptor);
}
info->interface_desc->iInterface = index;
struct usb_ss_endpoint_companion_descriptor endpoint_companion = {
.bLength = sizeof(struct usb_ss_endpoint_companion_descriptor),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMPANION,
.bMaxBurst = 0x0F,
.bmAttributes = 0x00,
.wBytesPerInterval = 0x00,
};
interface->initialized = 1;
//The buffer for PostBufferAsync commands must be 0x1000-byte aligned.
for (u32 i = 0; i < interface->endpoint_number; i++)
{
interface->endpoint[i].buffer = (u8*)memalign(0x1000, 0x1000);
if (interface->endpoint[i].buffer == NULL)
{
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
break;
}
memset(interface->endpoint[i].buffer, 0, 0x1000);
}
if (R_FAILED(rc)) return rc;
rc = usbDsRegisterInterface(&interface->interface);
if (R_FAILED(rc)) return rc;
info->interface_desc->bInterfaceNumber = interface->interface->interface_index;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
if((info->endpoint_desc[i]->bEndpointAddress & USB_ENDPOINT_IN) != 0)
{
info->endpoint_desc[i]->bEndpointAddress |= ep_in;
ep_in++;
}
else
{
info->endpoint_desc[i]->bEndpointAddress |= ep_out;
ep_out++;
}
}
// Full Speed Config
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Full, info->interface_desc, USB_DT_INTERFACE_SIZE);
if (R_FAILED(rc)) return rc;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
if(info->endpoint_desc[i]->bmAttributes == USB_TRANSFER_TYPE_BULK)
info->endpoint_desc[i]->wMaxPacketSize = 0x40;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Full, info->endpoint_desc[i], USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
}
// High Speed Config
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_High, info->interface_desc, USB_DT_INTERFACE_SIZE);
if (R_FAILED(rc)) return rc;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
if(info->endpoint_desc[i]->bmAttributes == USB_TRANSFER_TYPE_BULK)
info->endpoint_desc[i]->wMaxPacketSize = 0x200;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_High, info->endpoint_desc[i], USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
}
// Super Speed Config
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, info->interface_desc, USB_DT_INTERFACE_SIZE);
if (R_FAILED(rc)) return rc;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
if(info->endpoint_desc[i]->bmAttributes == USB_TRANSFER_TYPE_BULK)
info->endpoint_desc[i]->wMaxPacketSize = 0x400;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, info->endpoint_desc[i], USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE);
if (R_FAILED(rc)) return rc;
}
//Setup endpoints.
for (u32 i = 0; i < interface->endpoint_number; i++)
{
rc = usbDsInterface_RegisterEndpoint(interface->interface, &interface->endpoint[i].endpoint, info->endpoint_desc[i]->bEndpointAddress);
if (R_FAILED(rc)) return rc;
}
rc = usbDsInterface_EnableInterface(interface->interface);
if (R_FAILED(rc)) return rc;
return rc;
}
static Result UsbCommsTransfer(UsbCommsEndpoint *ep, UsbDirection dir, const void* buffer, size_t size, u64 timeout, size_t *transferredSize)
{
Result rc=0;
u32 urbId=0;
u32 chunksize=0;
u8 transfer_type=0;
u8 *bufptr = (u8*)buffer;
u8 *transfer_buffer = NULL;
u32 tmp_transferredSize = 0;
size_t total_transferredSize=0;
UsbDsReportData reportdata;
//Makes sure endpoints are ready for data-transfer / wait for init if needed.
rc = usbDsWaitReady(U64_MAX);
if (R_FAILED(rc)) return rc;
while(size)
{
//When bufptr isn't page-aligned copy the data into g_usbComms_endpoint_in_buffer and transfer that, otherwise use the bufptr directly.
if(((u64)bufptr) & 0xfff)
{
transfer_buffer = ep->buffer;
memset(ep->buffer, 0, 0x1000);
chunksize = 0x1000;
//After this transfer, bufptr will be page-aligned (if size is large enough for another transfer).
chunksize-= ((u64)bufptr) & 0xfff;
if (size<chunksize) chunksize = size;
if(dir == UsbDirectionWrite)
memcpy(ep->buffer, bufptr, chunksize);
transfer_type = 0;
}
else
{
transfer_buffer = bufptr;
chunksize = size;
transfer_type = 1;
}
//Start transfer.
rc = usbDsEndpoint_PostBufferAsync(ep->endpoint, transfer_buffer, chunksize, &urbId);
if(R_FAILED(rc)) return rc;
//Wait for the transfer to finish.
rc = eventWait(&ep->endpoint->CompletionEvent, timeout);
if (R_FAILED(rc))
{
usbDsEndpoint_Cancel(ep->endpoint);
eventWait(&ep->endpoint->CompletionEvent, U64_MAX);
eventClear(&ep->endpoint->CompletionEvent);
return rc;
}
eventClear(&ep->endpoint->CompletionEvent);
rc = usbDsEndpoint_GetReportData(ep->endpoint, &reportdata);
if (R_FAILED(rc)) return rc;
rc = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize);
if (R_FAILED(rc)) return rc;
if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize;
total_transferredSize+= (size_t)tmp_transferredSize;
if ((transfer_type==0) && (dir == UsbDirectionRead))
memcpy(bufptr, transfer_buffer, tmp_transferredSize);
bufptr+= tmp_transferredSize;
size-= tmp_transferredSize;
if (tmp_transferredSize < chunksize) break;
}
if (transferredSize) *transferredSize = total_transferredSize;
return rc;
}
static void UsbCommsInterfaceFree(UsbCommsInterface *interface)
{
rwlockWriteLock(&interface->lock);
if (!interface->initialized)
{
rwlockWriteUnlock(&interface->lock);
return;
}
interface->initialized = 0;
interface->interface = NULL;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
rwlockWriteLock(&interface->endpoint[i].lock);
interface->endpoint[i].endpoint = NULL;
free(interface->endpoint[i].buffer);
interface->endpoint[i].buffer = NULL;
rwlockWriteUnlock(&interface->endpoint[i].lock);
}
rwlockWriteUnlock(&interface->lock);
}
static size_t UsbTransfer(u32 interface, u32 endpoint, UsbDirection dir, void* buffer, size_t size, u64 timeout)
{
size_t transferredSize=-1;
u32 state=0;
Result rc, rc2;
bool initialized;
UsbCommsInterface *inter = &g_usbCommsInterfaces[interface];
UsbCommsEndpoint *ep = &inter->endpoint[endpoint];
rwlockReadLock(&inter->lock);
initialized = inter->initialized;
rwlockReadUnlock(&inter->lock);
if (!initialized) return 0;
rwlockWriteLock(&ep->lock);
rc = UsbCommsTransfer(ep, dir, buffer, size, timeout, &transferredSize);
rwlockWriteUnlock(&ep->lock);
if (R_FAILED(rc))
{
rc2 = usbDsGetState(&state);
if (R_SUCCEEDED(rc2))
{
if (state!=5)
{
rwlockWriteLock(&ep->lock);
// If state changed during transfer, try again. usbDsWaitReady() will be called from this.
rc = UsbCommsTransfer(ep, dir, buffer, size, timeout, &transferredSize);
rwlockWriteUnlock(&ep->lock);
}
}
if (R_FAILED(rc))
{
transferredSize = 0;
}
}
return transferredSize;
}
/*
* REDIRECT IO FUNCTIONS
*/
static ssize_t _write_stdout(struct _reent *r,void *fd,const char *ptr, size_t len)
{
return UsbTransfer(0, EP_IN, UsbDirectionWrite, (void*)ptr, len, U64_MAX);
}
static const devoptab_t dotab_stdout = {
"usb",
0,
NULL,
NULL,
_write_stdout,
NULL,
NULL,
NULL
};
static void RedirectOutput(void)
{
devoptab_list[STD_OUT] = &dotab_stdout;
devoptab_list[STD_ERR] = &dotab_stdout;
setvbuf(stdout, NULL , _IONBF, 0);
setvbuf(stderr, NULL , _IONBF, 0);
}
/*
* PUBLIC FUNCTIONS
*/
bool NxUsbDebuggerInit(void)
{
u32 num_interfaces = 1;
struct usb_device_descriptor device_descriptor = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0110,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = 0x40,
.idVendor = 0x057e,
.idProduct = 0x4000,
.bcdDevice = 0x0100,
.bNumConfigurations = 0x01
};
struct usb_interface_descriptor serial_interface_descriptor = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceProtocol = USB_CLASS_VENDOR_SPEC,
};
struct usb_endpoint_descriptor serial_endpoint_descriptor_in = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_ENDPOINT_IN,
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 0x200,
};
struct usb_endpoint_descriptor serial_endpoint_descriptor_out = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_ENDPOINT_OUT,
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 0x200,
};
UsbInterfaceDesc infos[1];
infos[0].interface_desc = &serial_interface_descriptor;
infos[0].endpoint_desc[EP_IN] = &serial_endpoint_descriptor_in;
infos[0].endpoint_desc[EP_OUT] = &serial_endpoint_descriptor_out;
infos[0].string_descriptor = NULL;
Result rc = 0;
rwlockWriteLock(&g_usbCommsLock);
if (g_usbCommsInitialized)
{
rc = MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
}
else if (num_interfaces > TOTAL_INTERFACES)
{
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
}
else
{
rc = usbDsInitialize();
if (R_SUCCEEDED(rc)) {
if (hosversionAtLeast(5,0,0))
{
u8 iManufacturer, iProduct, iSerialNumber;
static const u16 supported_langs[1] = {0x0409};
// Send language descriptor
rc = usbDsAddUsbLanguageStringDescriptor(NULL, supported_langs, sizeof(supported_langs) / sizeof(u16));
// Send manufacturer
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iManufacturer, "Nintendo");
// Send product
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iProduct, "Nintendo Switch");
// Send serial number
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iSerialNumber, "SerialNumber");
// Send device descriptors
device_descriptor.iManufacturer = iManufacturer;
device_descriptor.iProduct = iProduct;
device_descriptor.iSerialNumber = iSerialNumber;
// Full Speed is USB 1.1
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Full, &device_descriptor);
// High Speed is USB 2.0
device_descriptor.bcdUSB = 0x0200;
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, &device_descriptor);
// Super Speed is USB 3.0
device_descriptor.bcdUSB = 0x0300;
// Upgrade packet size to 512
device_descriptor.bMaxPacketSize0 = 0x09;
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Super, &device_descriptor);
// Define Binary Object Store
u8 bos[0x16] = {
0x05, // .bLength
USB_DT_BOS, // .bDescriptorType
0x16, 0x00, // .wTotalLength
0x02, // .bNumDeviceCaps
// USB 2.0
0x07, // .bLength
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
0x02, // .bDevCapabilityType
0x02, 0x00, 0x00, 0x00, // dev_capability_data
// USB 3.0
0x0A, // .bLength
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
0x03, // .bDevCapabilityType
0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00
};
if (R_SUCCEEDED(rc)) rc = usbDsSetBinaryObjectStore(bos, sizeof(bos));
}
if (R_SUCCEEDED(rc))
{
for (u32 i = 0; i < num_interfaces; i++)
{
UsbCommsInterface *intf = &g_usbCommsInterfaces[i];
const UsbInterfaceDesc *info = &infos[i];
intf->endpoint_number = info->interface_desc->bNumEndpoints;
rwlockWriteLock(&intf->lock);
for (u32 j = 0; j < intf->endpoint_number; j++)
{
rwlockWriteLock(&intf->endpoint[j].lock);
}
rc = UsbCommsInterfaceInit(i, info);
for (u32 j = 0; j < intf->endpoint_number; j++)
{
rwlockWriteUnlock(&intf->endpoint[j].lock);
}
rwlockWriteUnlock(&intf->lock);
if (R_FAILED(rc)) break;
}
}
}
if (R_SUCCEEDED(rc) && hosversionAtLeast(5,0,0))
{
rc = usbDsEnable();
}
if (R_FAILED(rc))
{
NxUsbDebuggerEnd();
}
}
if (R_SUCCEEDED(rc))
{
g_usbCommsInitialized = true;
}
rwlockWriteUnlock(&g_usbCommsLock);
if (g_usbCommsInitialized) RedirectOutput();
return g_usbCommsInitialized;
}
void NxUsbDebuggerEnd(void)
{
rwlockWriteLock(&g_usbCommsLock);
usbDsExit();
g_usbCommsInitialized = false;
rwlockWriteUnlock(&g_usbCommsLock);
for (u32 i = 0; i < TOTAL_INTERFACES; i++)
{
UsbCommsInterfaceFree(&g_usbCommsInterfaces[i]);
}
}

10
src/nxusb.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef NXUSB_H
#define NXUSB_H
#include <stdbool.h>
#include <stddef.h>
bool NxUsbDebuggerInit(void);
void NxUsbDebuggerEnd(void);
#endif // NXUSB_H