N3DS port (squashed)

A dedicated renderer using Citro3D would likely allow for better
much better graphical performances.
This commit is contained in:
Pierre Wendling 2021-03-30 04:32:39 -04:00 committed by Sam Lantinga
parent 61b5360e17
commit 655275378d
50 changed files with 2663 additions and 5 deletions

40
.github/workflows/n3ds.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: Build (Nintendo 3DS)
on: [push, pull_request]
jobs:
n3ds:
runs-on: ubuntu-latest
container:
image: devkitpro/devkitarm:latest
steps:
- uses: actions/checkout@v2
- name: Install build requirements
run: |
apt update
apt install ninja-build
- name: Configure CMake
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/3DS.cmake \
-DSDL_TESTS=ON \
-DSDL_INSTALL_TESTS=ON \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=prefix
- name: Build
run: cmake --build build --verbose
- name: Install CMake
run: |
echo "SDL2_DIR=$(pwd)/prefix" >> $GITHUB_ENV
cmake --install build/
( cd prefix; find ) | LC_ALL=C sort -u
- name: Verify CMake configuration files
run: |
cmake -S cmake/test -B cmake_config_build -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/3DS.cmake \
-DTEST_SHARED=FALSE \
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }} \
-DCMAKE_BUILD_TYPE=Release
cmake --build cmake_config_build --verbose
# Not running test_pkgconfig.sh and test_sdlconfig.sh
# as invoking the compiler manually is not supported

View file

@ -196,6 +196,8 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "BeOS.*")
message_error("BeOS support has been removed as of SDL 2.0.2.")
elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku.*")
set(HAIKU TRUE)
elseif(NINTENDO_3DS)
set(N3DS TRUE)
endif()
# Don't mistake osx for unix
@ -277,7 +279,7 @@ if(APPLE OR ARCH_64 OR MSVC_CLANG)
set(OPT_DEF_SSEMATH ON)
endif()
endif()
if(UNIX OR MINGW OR MSYS OR (USE_CLANG AND NOT WINDOWS) OR VITA OR PSP OR PS2)
if(UNIX OR MINGW OR MSYS OR (USE_CLANG AND NOT WINDOWS) OR VITA OR PSP OR PS2 OR N3DS)
set(OPT_DEF_LIBC ON)
endif()
@ -381,7 +383,7 @@ if(EMSCRIPTEN)
set(SDL_TEST_ENABLED_BY_DEFAULT OFF)
endif()
if(VITA OR PSP OR PS2)
if(VITA OR PSP OR PS2 OR N3DS)
set(SDL_SHARED_ENABLED_BY_DEFAULT OFF)
set(SDL_LOADSO_ENABLED_BY_DEFAULT OFF)
endif()
@ -2734,6 +2736,74 @@ elseif(OS2)
if(SDL_HIDAPI)
CheckHIDAPI()
endif()
elseif(N3DS)
file(GLOB N3DS_MAIN_SOURCES ${SDL2_SOURCE_DIR}/src/main/n3ds/*.c)
set(SDLMAIN_SOURCES ${SDLMAIN_SOURCES} ${N3DS_MAIN_SOURCES})
if(SDL_AUDIO)
set(SDL_AUDIO_DRIVER_N3DS 1)
file(GLOB N3DS_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/n3ds/*.c)
list(APPEND SOURCE_FILES ${N3DS_AUDIO_SOURCES})
set(HAVE_SDL_AUDIO TRUE)
endif()
if(SDL_FILESYSTEM)
set(SDL_FILESYSTEM_N3DS 1)
file(GLOB N3DS_FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/n3ds/*.c)
list(APPEND SOURCE_FILES ${N3DS_FILESYSTEM_SOURCES})
set(HAVE_SDL_FILESYSTEM TRUE)
endif()
if(SDL_JOYSTICK)
set(SDL_JOYSTICK_N3DS 1)
file(GLOB N3DS_JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/n3ds/*.c)
list(APPEND SOURCE_FILES ${N3DS_JOYSTICK_SOURCES})
set(HAVE_SDL_JOYSTICK TRUE)
endif()
if(SDL_POWER)
set(SDL_POWER_N3DS 1)
file(GLOB N3DS_POWER_SOURCES ${SDL2_SOURCE_DIR}/src/power/n3ds/*.c)
list(APPEND SOURCE_FILES ${N3DS_POWER_SOURCES})
set(HAVE_SDL_POWER TRUE)
endif()
if(SDL_THREADS)
set(SDL_THREAD_N3DS 1)
file(GLOB N3DS_THREAD_SOURCES ${SDL2_SOURCE_DIR}/src/thread/n3ds/*.c)
list(APPEND SOURCE_FILES ${N3DS_THREAD_SOURCES} ${SDL2_SOURCE_DIR}/src/thread/generic/SDL_systls.c)
set(HAVE_SDL_THREADS TRUE)
endif()
if(SDL_TIMERS)
set(SDL_TIMER_N3DS 1)
file(GLOB TIMER_SOURCES ${SDL2_SOURCE_DIR}/src/timer/n3ds/*.c)
list(APPEND SOURCE_FILES ${TIMER_SOURCES})
set(HAVE_SDL_TIMERS TRUE)
endif()
if(SDL_VIDEO)
set(SDL_VIDEO_DRIVER_N3DS 1)
file(GLOB N3DS_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/n3ds/*.c)
list(APPEND SOURCE_FILES ${N3DS_VIDEO_SOURCES})
set(HAVE_SDL_VIDEO TRUE)
endif()
if(SDL_LOCALE)
file(GLOB N3DS_LOCALE_SOURCES ${SDL2_SOURCE_DIR}/src/locale/n3ds/*.c)
list(APPEND SOURCE_FILES ${N3DS_LOCALE_SOURCES})
set(HAVE_SDL_LOCALE TRUE)
endif()
# Requires the n3ds file implementation
if(SDL_FILE)
file(GLOB N3DS_FILE_SOURCES ${SDL2_SOURCE_DIR}/src/file/n3ds/*.c)
list(APPEND SOURCE_FILES ${N3DS_FILE_SOURCES})
set(HAVE_SDL_FILE TRUE)
else()
message_error("SDL_FILE must be enabled to build on N3DS")
endif()
endif()
if(HAVE_VULKAN AND NOT SDL_LOADSO)

27
docs/README-n3ds.md Normal file
View file

@ -0,0 +1,27 @@
# Nintendo 3DS
SDL port for the Nintendo 3DS [Homebrew toolchain](https://devkitpro.org/) contributed by:
- [Pierre Wendling](https://github.com/FtZPetruska)
Credits to:
- The awesome people who ported SDL to other homebrew platforms.
- The Devkitpro team for making all the tools necessary to achieve this.
## Building
To build for the Nintendo 3DS, make sure you have devkitARM and cmake installed and run:
```bash
cmake -S. -Bbuild -DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/3DS.cmake" -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install build
```
## Notes
- Currently only software rendering is supported.
- Window are created on the top screen by default, use the `SDL_WINDOW_N3DS_BOTTOM` flag to put them on the bottom screen.
- SDL2main should be used to ensure all the necessary services are initialised.
- By default, the extra L2 cache and higher clock speeds of the New 2/3DS lineup are enabled. If you wish to turn it off, [use the PTMSYSM service](https://libctru.devkitpro.org/ptmsysm_8h.html#ae3a437bfd0de05fbc5ba9a460d148430) to turn it off in your program.

View file

@ -325,6 +325,7 @@
#cmakedefine SDL_AUDIO_DRIVER_VITA @SDL_AUDIO_DRIVER_VITA@
#cmakedefine SDL_AUDIO_DRIVER_PSP @SDL_AUDIO_DRIVER_PSP@
#cmakedefine SDL_AUDIO_DRIVER_PS2 @SDL_AUDIO_DRIVER_PS2@
#cmakedefine SDL_AUDIO_DRIVER_N3DS @SDL_AUDIO_DRIVER_N3DS@
/* Enable various input drivers */
#cmakedefine SDL_INPUT_LINUXEV @SDL_INPUT_LINUXEV@
@ -349,6 +350,7 @@
#cmakedefine SDL_JOYSTICK_VITA @SDL_JOYSTICK_VITA@
#cmakedefine SDL_JOYSTICK_PSP @SDL_JOYSTICK_PSP@
#cmakedefine SDL_JOYSTICK_PS2 @SDL_JOYSTICK_PS2@
#cmakedefine SDL_JOYSTICK_N3DS @SDL_JOYSTICK_N3DS@
#cmakedefine SDL_HAPTIC_DUMMY @SDL_HAPTIC_DUMMY@
#cmakedefine SDL_HAPTIC_LINUX @SDL_HAPTIC_LINUX@
#cmakedefine SDL_HAPTIC_IOKIT @SDL_HAPTIC_IOKIT@
@ -381,6 +383,7 @@
#cmakedefine SDL_THREAD_VITA @SDL_THREAD_VITA@
#cmakedefine SDL_THREAD_PSP @SDL_THREAD_PSP@
#cmakedefine SDL_THREAD_PS2 @SDL_THREAD_PS2@
#cmakedefine SDL_THREAD_N3DS @SDL_THREAD_N3DS@
/* Enable various timer systems */
#cmakedefine SDL_TIMER_HAIKU @SDL_TIMER_HAIKU@
@ -391,6 +394,7 @@
#cmakedefine SDL_TIMER_VITA @SDL_TIMER_VITA@
#cmakedefine SDL_TIMER_PSP @SDL_TIMER_PSP@
#cmakedefine SDL_TIMER_PS2 @SDL_TIMER_PS2@
#cmakedefine SDL_TIMER_N3DS @SDL_TIMER_N3DS@
/* Enable various video drivers */
#cmakedefine SDL_VIDEO_DRIVER_ANDROID @SDL_VIDEO_DRIVER_ANDROID@
@ -444,6 +448,7 @@
#cmakedefine SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS @SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS@
#cmakedefine SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM @SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM@
#cmakedefine SDL_VIDEO_DRIVER_VITA @SDL_VIDEO_DRIVER_VITA@
#cmakedefine SDL_VIDEO_DRIVER_N3DS @SDL_VIDEO_DRIVER_N3DS@
#cmakedefine SDL_VIDEO_RENDER_D3D @SDL_VIDEO_RENDER_D3D@
#cmakedefine SDL_VIDEO_RENDER_D3D11 @SDL_VIDEO_RENDER_D3D11@
@ -487,6 +492,7 @@
#cmakedefine SDL_POWER_HARDWIRED @SDL_POWER_HARDWIRED@
#cmakedefine SDL_POWER_VITA @SDL_POWER_VITA@
#cmakedefine SDL_POWER_PSP @SDL_POWER_PSP@
#cmakedefine SDL_POWER_N3DS @SDL_POWER_N3DS@
/* Enable system filesystem support */
#cmakedefine SDL_FILESYSTEM_ANDROID @SDL_FILESYSTEM_ANDROID@
@ -501,6 +507,7 @@
#cmakedefine SDL_FILESYSTEM_VITA @SDL_FILESYSTEM_VITA@
#cmakedefine SDL_FILESYSTEM_PSP @SDL_FILESYSTEM_PSP@
#cmakedefine SDL_FILESYSTEM_PS2 @SDL_FILESYSTEM_PS2@
#cmakedefine SDL_FILESYSTEM_N3DS @SDL_FILESYSTEM_N3DS@
/* Enable misc subsystem */
#cmakedefine SDL_MISC_DUMMY @SDL_MISC_DUMMY@

View file

@ -108,6 +108,15 @@
void reset_IOP(); \
void reset_IOP() {}
#elif defined(__3DS__)
/*
On N3DS, SDL provides a main function that sets up the screens
and storage.
If you provide this yourself, you may define SDL_MAIN_HANDLED
*/
#define SDL_MAIN_AVAILABLE
#endif
#endif /* SDL_MAIN_HANDLED */

View file

@ -221,6 +221,11 @@
#define __VITA__ 1
#endif
#if defined(__3DS__)
#undef __3DS__
#define __3DS__ 1
#endif
#include "begin_code.h"
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus

View file

@ -410,7 +410,7 @@ SDL_COMPILE_TIME_ASSERT(sint64, sizeof(Sint64) == 8);
/** \cond */
#ifndef DOXYGEN_SHOULD_IGNORE_THIS
#if !defined(__ANDROID__) && !defined(__VITA__)
#if !defined(__ANDROID__) && !defined(__VITA__) && !defined(__3DS__)
/* TODO: include/SDL_stdinc.h:174: error: size of array 'SDL_dummy_enum' is negative */
typedef enum
{

View file

@ -126,6 +126,7 @@ typedef enum
SDL_WINDOW_KEYBOARD_GRABBED = 0x00100000, /**< window has grabbed keyboard input */
SDL_WINDOW_VULKAN = 0x10000000, /**< window usable for Vulkan surface */
SDL_WINDOW_METAL = 0x20000000, /**< window usable for Metal view */
SDL_WINDOW_N3DS_BOTTOM = 0x40000000, /**< window should be on the bottom screen (N3DS only) */
SDL_WINDOW_INPUT_GRABBED = SDL_WINDOW_MOUSE_GRABBED /**< equivalent to SDL_WINDOW_MOUSE_GRABBED for compatibility */
} SDL_WindowFlags;

View file

@ -609,6 +609,8 @@ SDL_GetPlatform(void)
return "PlayStation Vita";
#elif __NGAGE__
return "Nokia N-Gage";
#elif __3DS__
return "Nintendo 3DS";
#else
return "Unknown (see SDL_platform.h)";
#endif

View file

@ -485,6 +485,13 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
fclose (pFile);
}
#elif defined(__3DS__)
{
FILE* pFile;
pFile = fopen ("/SDL_Log.txt", "a");
fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
fclose (pFile);
}
#endif
#if HAVE_STDIO_H && \
!(defined(__APPLE__) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT)))

View file

@ -105,6 +105,9 @@ static const AudioBootStrap *const bootstrap[] = {
#if SDL_AUDIO_DRIVER_VITA
&VITAAUD_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_N3DS
&N3DSAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_EMSCRIPTEN
&EMSCRIPTENAUDIO_bootstrap,
#endif

View file

@ -209,6 +209,7 @@ extern AudioBootStrap ANDROIDAUDIO_bootstrap;
extern AudioBootStrap PS2AUDIO_bootstrap;
extern AudioBootStrap PSPAUDIO_bootstrap;
extern AudioBootStrap VITAAUD_bootstrap;
extern AudioBootStrap N3DSAUDIO_bootstrap;
extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
extern AudioBootStrap OS2AUDIO_bootstrap;

View file

@ -0,0 +1,363 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_AUDIO_DRIVER_N3DS
#include "SDL_audio.h"
/* N3DS Audio driver */
#include "../SDL_sysaudio.h"
#include "SDL_n3dsaudio.h"
#include "SDL_timer.h"
#define N3DSAUDIO_DRIVER_NAME "n3ds"
static dspHookCookie dsp_hook;
static SDL_AudioDevice *audio_device;
static void FreePrivateData(_THIS);
static int FindAudioFormat(_THIS);
static SDL_INLINE void
contextLock(_THIS)
{
LightLock_Lock(&this->hidden->lock);
}
static SDL_INLINE void
contextUnlock(_THIS)
{
LightLock_Unlock(&this->hidden->lock);
}
static void
N3DSAUD_LockAudio(_THIS)
{
contextLock(this);
}
static void
N3DSAUD_UnlockAudio(_THIS)
{
contextUnlock(this);
}
static void
N3DSAUD_DspHook(DSP_HookType hook)
{
if (hook == DSPHOOK_ONCANCEL) {
contextLock(audio_device);
audio_device->hidden->isCancelled = SDL_TRUE;
SDL_AtomicSet(&audio_device->enabled, SDL_FALSE);
CondVar_Broadcast(&audio_device->hidden->cv);
contextUnlock(audio_device);
}
}
static void
AudioFrameFinished(void *device)
{
bool shouldBroadcast = false;
unsigned i;
SDL_AudioDevice *this = (SDL_AudioDevice *) device;
contextLock(this);
for (i = 0; i < NUM_BUFFERS; i++) {
if (this->hidden->waveBuf[i].status == NDSP_WBUF_DONE) {
this->hidden->waveBuf[i].status = NDSP_WBUF_FREE;
shouldBroadcast = SDL_TRUE;
}
}
if (shouldBroadcast) {
CondVar_Broadcast(&this->hidden->cv);
}
contextUnlock(this);
}
static int
N3DSAUDIO_OpenDevice(_THIS, const char *devname)
{
Result ndsp_init_res;
Uint8 *data_vaddr;
float mix[12];
this->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof *this->hidden);
if (this->hidden == NULL) {
return SDL_OutOfMemory();
}
/* Initialise the DSP service */
ndsp_init_res = ndspInit();
if (R_FAILED(ndsp_init_res)) {
if ((R_SUMMARY(ndsp_init_res) == RS_NOTFOUND) && (R_MODULE(ndsp_init_res) == RM_DSP)) {
SDL_SetError("DSP init failed: dspfirm.cdc missing!");
} else {
SDL_SetError("DSP init failed. Error code: 0x%lX", ndsp_init_res);
}
return -1;
}
/* Initialise internal state */
LightLock_Init(&this->hidden->lock);
CondVar_Init(&this->hidden->cv);
if (this->spec.channels > 2) {
this->spec.channels = 2;
}
/* Should not happen but better be safe. */
if (FindAudioFormat(this) < 0) {
return SDL_SetError("No supported audio format found.");
}
/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&this->spec);
/* Allocate mixing buffer */
if (this->spec.size >= SDL_MAX_UINT32 / 2) {
return SDL_SetError("Mixing buffer is too large.");
}
this->hidden->mixlen = this->spec.size;
this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->spec.size);
if (this->hidden->mixbuf == NULL) {
return SDL_OutOfMemory();
}
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
data_vaddr = (Uint8 *) linearAlloc(this->hidden->mixlen * NUM_BUFFERS);
if (data_vaddr == NULL) {
return SDL_OutOfMemory();
}
SDL_memset(data_vaddr, 0, this->hidden->mixlen * NUM_BUFFERS);
DSP_FlushDataCache(data_vaddr, this->hidden->mixlen * NUM_BUFFERS);
this->hidden->nextbuf = 0;
this->hidden->channels = this->spec.channels;
this->hidden->samplerate = this->spec.freq;
ndspChnReset(0);
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetRate(0, this->spec.freq);
ndspChnSetFormat(0, this->hidden->format);
SDL_memset(mix, 0, sizeof(mix));
mix[0] = 1.0;
mix[1] = 1.0;
ndspChnSetMix(0, mix);
SDL_memset(this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
for (unsigned i = 0; i < NUM_BUFFERS; i++) {
this->hidden->waveBuf[i].data_vaddr = data_vaddr;
this->hidden->waveBuf[i].nsamples = this->hidden->mixlen / this->hidden->bytePerSample;
data_vaddr += this->hidden->mixlen;
}
/* Setup callback */
audio_device = this;
ndspSetCallback(AudioFrameFinished, this);
dspHook(&dsp_hook, N3DSAUD_DspHook);
return 0;
}
static int
N3DSAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
{
/* Delay to make this sort of simulate real audio input. */
SDL_Delay((this->spec.samples * 1000) / this->spec.freq);
/* always return a full buffer of silence. */
SDL_memset(buffer, this->spec.silence, buflen);
return buflen;
}
static void
N3DSAUDIO_PlayDevice(_THIS)
{
size_t nextbuf;
size_t sampleLen;
contextLock(this);
nextbuf = this->hidden->nextbuf;
sampleLen = this->hidden->mixlen;
if (this->hidden->isCancelled ||
this->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) {
contextUnlock(this);
return;
}
this->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS;
contextUnlock(this);
memcpy((void *) this->hidden->waveBuf[nextbuf].data_vaddr,
this->hidden->mixbuf, sampleLen);
DSP_FlushDataCache(this->hidden->waveBuf[nextbuf].data_vaddr, sampleLen);
ndspChnWaveBufAdd(0, &this->hidden->waveBuf[nextbuf]);
}
static void
N3DSAUDIO_WaitDevice(_THIS)
{
contextLock(this);
while (!this->hidden->isCancelled &&
this->hidden->waveBuf[this->hidden->nextbuf].status != NDSP_WBUF_FREE) {
CondVar_Wait(&this->hidden->cv, &this->hidden->lock);
}
contextUnlock(this);
}
static Uint8 *
N3DSAUDIO_GetDeviceBuf(_THIS)
{
return this->hidden->mixbuf;
}
static void
N3DSAUDIO_CloseDevice(_THIS)
{
contextLock(this);
dspUnhook(&dsp_hook);
ndspSetCallback(NULL, NULL);
if (!this->hidden->isCancelled) {
ndspChnReset(0);
memset(this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
CondVar_Broadcast(&this->hidden->cv);
}
contextUnlock(this);
ndspExit();
FreePrivateData(this);
}
static void
N3DSAUDIO_ThreadInit(_THIS)
{
s32 current_priority;
svcGetThreadPriority(&current_priority, CUR_THREAD_HANDLE);
current_priority--;
/* 0x18 is reserved for video, 0x30 is the default for main thread */
current_priority = SDL_clamp(current_priority, 0x19, 0x2F);
svcSetThreadPriority(CUR_THREAD_HANDLE, current_priority);
}
static SDL_bool
N3DSAUDIO_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->OpenDevice = N3DSAUDIO_OpenDevice;
impl->PlayDevice = N3DSAUDIO_PlayDevice;
impl->WaitDevice = N3DSAUDIO_WaitDevice;
impl->GetDeviceBuf = N3DSAUDIO_GetDeviceBuf;
impl->CloseDevice = N3DSAUDIO_CloseDevice;
impl->ThreadInit = N3DSAUDIO_ThreadInit;
impl->LockDevice = N3DSAUD_LockAudio;
impl->UnlockDevice = N3DSAUD_UnlockAudio;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
/* Should be possible, but micInit would fail */
impl->HasCaptureSupport = SDL_FALSE;
impl->CaptureFromDevice = N3DSAUDIO_CaptureFromDevice;
return SDL_TRUE; /* this audio target is available. */
}
AudioBootStrap N3DSAUDIO_bootstrap = {
N3DSAUDIO_DRIVER_NAME,
"SDL N3DS audio driver",
N3DSAUDIO_Init,
0
};
/**
* Cleans up all allocated memory, safe to call with null pointers
*/
static void
FreePrivateData(_THIS)
{
if (!this->hidden) {
return;
}
if (this->hidden->waveBuf[0].data_vaddr) {
linearFree((void *) this->hidden->waveBuf[0].data_vaddr);
}
if (this->hidden->mixbuf) {
SDL_free(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
static int
FindAudioFormat(_THIS)
{
SDL_bool found_valid_format = SDL_FALSE;
Uint16 test_format = SDL_FirstAudioFormat(this->spec.format);
while (!found_valid_format && test_format) {
this->spec.format = test_format;
switch (test_format) {
case AUDIO_S8:
/* Signed 8-bit audio supported */
this->hidden->format = (this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM8 : NDSP_FORMAT_MONO_PCM8;
this->hidden->isSigned = 1;
this->hidden->bytePerSample = this->spec.channels;
found_valid_format = SDL_TRUE;
break;
case AUDIO_S16:
/* Signed 16-bit audio supported */
this->hidden->format = (this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16;
this->hidden->isSigned = 1;
this->hidden->bytePerSample = this->spec.channels * 2;
found_valid_format = SDL_TRUE;
break;
default:
test_format = SDL_NextAudioFormat();
break;
}
}
return found_valid_format ? 0 : -1;
}
#endif /* SDL_AUDIO_DRIVER_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,50 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef _SDL_n3dsaudio_h_
#define _SDL_n3dsaudio_h_
#include <3ds.h>
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
#define NUM_BUFFERS 2 /* -- Don't lower this! */
struct SDL_PrivateAudioData
{
/* Speaker data */
Uint8 *mixbuf;
Uint32 mixlen;
Uint32 format;
Uint32 samplerate;
Uint32 channels;
Uint8 bytePerSample;
Uint32 isSigned;
Uint32 nextbuf;
ndspWaveBuf waveBuf[NUM_BUFFERS];
LightLock lock;
CondVar cv;
SDL_bool isCancelled;
};
#endif /* _SDL_n3dsaudio_h_ */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -468,6 +468,8 @@ CPU_haveNEON(void)
return 1; /* ARMv8 always has non-optional NEON support. */
#elif __VITA__
return 1;
#elif __3DS__
return 1;
#elif defined(__APPLE__) && defined(__ARM_ARCH) && (__ARM_ARCH >= 7)
/* (note that sysctlbyname("hw.optional.neon") doesn't work!) */
return 1; /* all Apple ARMv7 chips and later have NEON. */

View file

@ -63,6 +63,8 @@
#define SDL_DYNAMIC_API 0 /* vitasdk doesn't support dynamic linking */
#elif defined(__NGAGE__)
#define SDL_DYNAMIC_API 0 /* The N-Gage doesn't support dynamic linking either */
#elif defined(__3DS__)
#define SDL_DYNAMIC_API 0 /* devkitARM doesn't support dynamic linking */
#elif defined(DYNAPI_NEEDS_DLOPEN) && !defined(HAVE_DLOPEN)
#define SDL_DYNAMIC_API 0 /* we need dlopen(), but don't have it.... */
#endif

View file

@ -53,6 +53,10 @@
#include "cocoa/SDL_rwopsbundlesupport.h"
#endif /* __APPLE__ */
#ifdef __3DS__
#include "n3ds/SDL_rwopsromfs.h"
#endif /* __3DS__ */
#ifdef __ANDROID__
#include "../core/android/SDL_android.h"
#include "SDL_system.h"
@ -601,6 +605,8 @@ SDL_RWFromFile(const char *file, const char *mode)
#elif __WINRT__
FILE *fp = NULL;
fopen_s(&fp, file, mode);
#elif __3DS__
FILE *fp = N3DS_FileOpen(file, mode);
#else
FILE *fp = fopen(file, mode);
#endif

View file

@ -0,0 +1,56 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_rwopsromfs.h"
/* Nintendo 3DS applications may embed resources in the executable. The
resources are stored in a special read-only partition prefixed with
'romfs:/'. As such, when opening a file, we should first try the romfs
unless sdmc is specifically mentionned.
*/
FILE *
N3DS_FileOpen(const char *file, const char *mode)
{
FILE *fp = NULL;
char romfs_path[4096];
/* romfs are read-only */
if (SDL_strchr(mode, 'r') == NULL) {
return fopen(file, mode);
}
/* If the path has an explicit prefix, we skip the guess work */
if (SDL_strncmp("romfs:/", file, 7) == 0 ||
SDL_strncmp("sdmc:/", file, 6) == 0) {
return fopen(file, mode);
}
SDL_snprintf(romfs_path, 4096, "romfs:/%s", file);
fp = fopen(romfs_path, mode);
if (fp == NULL) {
fp = fopen(file, mode);
}
return fp;
}
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,30 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef SDL_rwopsromfs_h_
#define SDL_rwopsromfs_h_
FILE *N3DS_FileOpen(const char *file, const char *mode);
#endif /* SDL_rwopsromfs_h_ */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,86 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_FILESYSTEM_N3DS
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* System dependent filesystem routines */
#include <3ds.h>
#include <dirent.h>
#include <errno.h>
#include "SDL_error.h"
#include "SDL_filesystem.h"
SDL_FORCE_INLINE char *MakePrefPath(const char *app);
SDL_FORCE_INLINE int CreatePrefPathDir(const char *pref);
char *
SDL_GetBasePath(void)
{
char *base_path = SDL_strdup("romfs:/");
return base_path;
}
char *
SDL_GetPrefPath(const char *org, const char *app)
{
char *pref_path = NULL;
if (app == NULL) {
SDL_InvalidParamError("app");
return NULL;
}
pref_path = MakePrefPath(app);
if (CreatePrefPathDir(pref_path) < 0) {
SDL_free(pref_path);
return NULL;
}
return pref_path;
}
SDL_FORCE_INLINE char *
MakePrefPath(const char *app)
{
static const char *FMT = "/3ds/%s/";
size_t length = SDL_snprintf(NULL, 0, FMT, app) + 1;
char *pref_path = (char *) SDL_calloc(length, sizeof(char));
SDL_snprintf(pref_path, length, FMT, app);
return pref_path;
}
SDL_FORCE_INLINE int
CreatePrefPathDir(const char *pref)
{
int result = mkdir(pref, 0666);
if (result == -1 && errno != EEXIST) {
return SDL_SetError("Failed to create '%s' (%s)", pref, strerror(errno));
}
return 0;
}
#endif /* SDL_FILESYSTEM_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -977,6 +977,9 @@ static const char *s_ControllerMappings [] =
#endif
#if SDL_JOYSTICK_VITA
"0000000050535669746120436f6e7400,PSVita Controller,crc:d598,a:b2,b:b1,back:b10,dpdown:b6,dpleft:b7,dpright:b9,dpup:b8,leftshoulder:b4,leftstick:b14,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,",
#endif
#if SDL_JOYSTICK_N3DS
"4e696e74656e646f20334453206d6170,Nintendo 3DS,a:b0,b:b1,back:b2,dpdown:b7,dpleft:b5,dpright:b4,dpup:b6,leftshoulder:b9,leftstick:b14,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,rightx:a2,righty:a3,start:b3,x:b10,y:b11,",
#endif
"hidapi,*,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
NULL

View file

@ -102,6 +102,9 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = {
#ifdef SDL_JOYSTICK_VITA
&SDL_VITA_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_N3DS
&SDL_N3DS_JoystickDriver
#endif
#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
&SDL_DUMMY_JoystickDriver
#endif

View file

@ -238,6 +238,7 @@ extern SDL_JoystickDriver SDL_OS2_JoystickDriver;
extern SDL_JoystickDriver SDL_PS2_JoystickDriver;
extern SDL_JoystickDriver SDL_PSP_JoystickDriver;
extern SDL_JoystickDriver SDL_VITA_JoystickDriver;
extern SDL_JoystickDriver SDL_N3DS_JoystickDriver;
/* Ends C function definitions when using C++ */
#ifdef __cplusplus

View file

@ -0,0 +1,367 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_N3DS
/* This is the N3DS implementation of the SDL joystick API */
#include <3ds.h>
#include "../SDL_sysjoystick.h"
#include "SDL_events.h"
#define NB_BUTTONS 23
/*
N3DS sticks values are roughly within +/-160
which is too small to pass the jitter tolerance.
This correction factor is applied to axis values
so they fit better in SDL's value range.
*/
#define CORRECTION_FACTOR_X SDL_JOYSTICK_AXIS_MAX / 160
/*
The Y axis needs to be flipped because SDL's "up"
is reversed compared to libctru's "up"
*/
#define CORRECTION_FACTOR_Y -CORRECTION_FACTOR_X
/*
Factors used to convert touchscreen coordinates to
SDL's 0-1 values. Note that the N3DS's screen is
internally in a portrait disposition so the
GSP_SCREEN constants are flipped.
*/
#define TOUCHPAD_SCALE_X 1.0f / GSP_SCREEN_HEIGHT_BOTTOM
#define TOUCHPAD_SCALE_Y 1.0f / GSP_SCREEN_WIDTH
typedef struct N3DSJoystickState
{
u32 kDown;
u32 kUp;
circlePosition circlePos;
circlePosition cStickPos;
accelVector acceleration;
angularRate rate;
} N3DSJoystickState;
SDL_FORCE_INLINE void UpdateAxis(SDL_Joystick *joystick, N3DSJoystickState *previous_state);
SDL_FORCE_INLINE void UpdateButtons(SDL_Joystick *joystick, N3DSJoystickState *previous_state);
SDL_FORCE_INLINE void UpdateSensors(SDL_Joystick *joystick, N3DSJoystickState *previous_state);
SDL_FORCE_INLINE void UpdateTouch(SDL_Joystick *joystick);
static N3DSJoystickState current_state;
static SDL_bool sensors_enabled = SDL_FALSE;
static int
N3DS_JoystickInit(void)
{
hidInit();
HIDUSER_EnableAccelerometer();
HIDUSER_EnableGyroscope();
return 0;
}
static const char *
N3DS_JoystickGetDeviceName(int device_index)
{
return "Nintendo 3DS";
}
static int
N3DS_JoystickGetCount(void)
{
return 1;
}
static SDL_JoystickGUID
N3DS_JoystickGetDeviceGUID(int device_index)
{
/* GUID corresponds to the name "Nintendo 3DS map" */
SDL_JoystickGUID guid = { { 0x4e, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x6f, 0x20, 0x33, 0x44, 0x53, 0x20, 0x6d, 0x61, 0x70 } };
return guid;
}
static SDL_JoystickID
N3DS_JoystickGetDeviceInstanceID(int device_index)
{
return device_index;
}
static int
N3DS_JoystickOpen(SDL_Joystick *joystick, int device_index)
{
joystick->nbuttons = NB_BUTTONS;
joystick->naxes = 4;
joystick->nhats = 0;
joystick->instance_id = device_index;
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);
SDL_PrivateJoystickAddTouchpad(joystick, 1);
return 0;
}
static int
N3DS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
{
sensors_enabled = enabled;
return 0;
}
static void
N3DS_JoystickUpdate(SDL_Joystick *joystick)
{
N3DSJoystickState previous_state = current_state;
hidScanInput();
UpdateAxis(joystick, &previous_state);
UpdateButtons(joystick, &previous_state);
UpdateTouch(joystick);
if (sensors_enabled) {
UpdateSensors(joystick, &previous_state);
}
}
SDL_FORCE_INLINE void
UpdateAxis(SDL_Joystick *joystick, N3DSJoystickState *previous_state)
{
hidCircleRead(&current_state.circlePos);
if (previous_state->circlePos.dx != current_state.circlePos.dx) {
SDL_PrivateJoystickAxis(joystick,
0,
current_state.circlePos.dx * CORRECTION_FACTOR_X);
}
if (previous_state->circlePos.dy != current_state.circlePos.dy) {
SDL_PrivateJoystickAxis(joystick,
1,
current_state.circlePos.dy * CORRECTION_FACTOR_Y);
}
hidCstickRead(&current_state.cStickPos);
if (previous_state->cStickPos.dx != current_state.cStickPos.dx) {
SDL_PrivateJoystickAxis(joystick,
2,
current_state.cStickPos.dx * CORRECTION_FACTOR_X);
}
if (previous_state->cStickPos.dy != current_state.cStickPos.dy) {
SDL_PrivateJoystickAxis(joystick,
3,
current_state.cStickPos.dy * CORRECTION_FACTOR_Y);
}
}
SDL_FORCE_INLINE void
UpdateButtons(SDL_Joystick *joystick, N3DSJoystickState *previous_state)
{
u32 updated_down, updated_up;
current_state.kDown = hidKeysDown();
updated_down = previous_state->kDown ^ current_state.kDown;
if (updated_down) {
for (Uint8 i = 0; i < joystick->nbuttons; i++) {
if (current_state.kDown & BIT(i) & updated_down) {
SDL_PrivateJoystickButton(joystick, i, SDL_PRESSED);
}
}
}
current_state.kUp = hidKeysUp();
updated_up = previous_state->kUp ^ current_state.kUp;
if (updated_up) {
for (Uint8 i = 0; i < joystick->nbuttons; i++) {
if (current_state.kUp & BIT(i) & updated_up) {
SDL_PrivateJoystickButton(joystick, i, SDL_RELEASED);
}
}
}
}
SDL_FORCE_INLINE void
UpdateTouch(SDL_Joystick *joystick)
{
touchPosition touch;
Uint8 state;
hidTouchRead(&touch);
state = (touch.px == 0 && touch.py == 0) ? SDL_RELEASED : SDL_PRESSED;
SDL_PrivateJoystickTouchpad(joystick,
0,
0,
state,
touch.px * TOUCHPAD_SCALE_X,
touch.py * TOUCHPAD_SCALE_Y,
state == SDL_PRESSED ? 1.0f : 0.0f);
}
SDL_FORCE_INLINE void
UpdateSensors(SDL_Joystick *joystick, N3DSJoystickState *previous_state)
{
float data[3];
hidAccelRead(&current_state.acceleration);
if (SDL_memcmp(&previous_state->acceleration, &current_state.acceleration, sizeof(accelVector)) != 0) {
SDL_memcpy(&previous_state->acceleration, &current_state.acceleration, sizeof(accelVector));
data[0] = (float) current_state.acceleration.x * SDL_STANDARD_GRAVITY;
data[1] = (float) current_state.acceleration.y * SDL_STANDARD_GRAVITY;
data[2] = (float) current_state.acceleration.z * SDL_STANDARD_GRAVITY;
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, data, sizeof data);
}
hidGyroRead(&current_state.rate);
if (SDL_memcmp(&previous_state->rate, &current_state.rate, sizeof(angularRate)) != 0) {
SDL_memcpy(&previous_state->rate, &current_state.rate, sizeof(angularRate));
data[0] = (float) current_state.rate.y;
data[1] = (float) current_state.rate.z;
data[2] = (float) current_state.rate.x;
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO, data, sizeof data);
}
}
static void
N3DS_JoystickClose(SDL_Joystick *joystick)
{
}
static void
N3DS_JoystickQuit(void)
{
HIDUSER_DisableGyroscope();
HIDUSER_DisableAccelerometer();
hidExit();
}
static SDL_bool
N3DS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
{
/* There is only one possible mapping. */
*out = (SDL_GamepadMapping){
.a = { EMappingKind_Button, 0 },
.b = { EMappingKind_Button, 1 },
.x = { EMappingKind_Button, 10 },
.y = { EMappingKind_Button, 11 },
.back = { EMappingKind_Button, 2 },
.guide = { EMappingKind_None, 255 },
.start = { EMappingKind_Button, 3 },
.leftstick = { EMappingKind_None, 255 },
.rightstick = { EMappingKind_None, 255 },
.leftshoulder = { EMappingKind_Button, 9 },
.rightshoulder = { EMappingKind_Button, 8 },
.dpup = { EMappingKind_Button, 6 },
.dpdown = { EMappingKind_Button, 7 },
.dpleft = { EMappingKind_Button, 5 },
.dpright = { EMappingKind_Button, 4 },
.misc1 = { EMappingKind_None, 255 },
.paddle1 = { EMappingKind_None, 255 },
.paddle2 = { EMappingKind_None, 255 },
.paddle3 = { EMappingKind_None, 255 },
.paddle4 = { EMappingKind_None, 255 },
.leftx = { EMappingKind_Axis, 0 },
.lefty = { EMappingKind_Axis, 1 },
.rightx = { EMappingKind_Axis, 2 },
.righty = { EMappingKind_Axis, 3 },
.lefttrigger = { EMappingKind_Button, 14 },
.righttrigger = { EMappingKind_Button, 15 },
};
return SDL_TRUE;
}
static void
N3DS_JoystickDetect(void)
{
}
static const char *
N3DS_JoystickGetDevicePath(int device_index)
{
return NULL;
}
static int
N3DS_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
}
static void
N3DS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
{
}
static Uint32
N3DS_JoystickGetCapabilities(SDL_Joystick *joystick)
{
return 0;
}
static int
N3DS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
return SDL_Unsupported();
}
static int
N3DS_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
{
return SDL_Unsupported();
}
static int
N3DS_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
{
return SDL_Unsupported();
}
static int
N3DS_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
{
return SDL_Unsupported();
}
SDL_JoystickDriver SDL_N3DS_JoystickDriver = {
N3DS_JoystickInit,
N3DS_JoystickGetCount,
N3DS_JoystickDetect,
N3DS_JoystickGetDeviceName,
N3DS_JoystickGetDevicePath,
N3DS_JoystickGetDevicePlayerIndex,
N3DS_JoystickSetDevicePlayerIndex,
N3DS_JoystickGetDeviceGUID,
N3DS_JoystickGetDeviceInstanceID,
N3DS_JoystickOpen,
N3DS_JoystickRumble,
N3DS_JoystickRumbleTriggers,
N3DS_JoystickGetCapabilities,
N3DS_JoystickSetLED,
N3DS_JoystickSendEffect,
N3DS_JoystickSetSensorsEnabled,
N3DS_JoystickUpdate,
N3DS_JoystickClose,
N3DS_JoystickQuit,
N3DS_JoystickGetGamepadMapping
};
#endif /* SDL_JOYSTICK_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -27,7 +27,7 @@
#define libm_hidden_def(x)
#define strong_alias(x, y)
#if !defined(__HAIKU__) && !defined(__PSP__) && !defined(__PS2__) /* already defined in a system header. */
#if !defined(__HAIKU__) && !defined(__PSP__) && !defined(__3DS__) && !defined(__PS2__) /* already defined in a system header. */
typedef unsigned int u_int32_t;
#endif

View file

@ -0,0 +1,59 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../SDL_syslocale.h"
#include "../../SDL_internal.h"
#include <3ds.h>
/* Used when the CFGU fails to work. */
#define BAD_LOCALE 255
SDL_FORCE_INLINE u8 GetLocaleIndex(void);
void
SDL_SYS_GetPreferredLocales(char *buf, size_t buflen)
{
/* The 3DS only supports these 12 languages, only one can be active at a time */
static const char AVAILABLE_LOCALES[][6] = { "ja_JP", "en_US", "fr_FR", "de_DE",
"it_IT", "es_ES", "zn_CN", "ko_KR",
"nl_NL", "pt_PT", "ru_RU", "zh_TW" };
u8 current_locale = GetLocaleIndex();
if (current_locale != BAD_LOCALE) {
SDL_strlcpy(buf, AVAILABLE_LOCALES[current_locale], buflen);
}
}
SDL_FORCE_INLINE u8
GetLocaleIndex(void)
{
u8 current_locale;
if (R_FAILED(cfguInit())) {
return BAD_LOCALE;
}
if (R_FAILED(CFGU_GetSystemLanguage(&current_locale))) {
return BAD_LOCALE;
}
cfguExit();
return current_locale;
}
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,82 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef __3DS__
#include "SDL_main.h"
#include <3ds.h>
#ifdef main
#undef main
#endif
SDL_FORCE_INLINE void N3DS_Init(void);
SDL_FORCE_INLINE void N3DS_SetCPUSpeed(void);
SDL_FORCE_INLINE void N3DS_Quit(void);
#define HIGH_CLOCK 1
#define L2_CACHE 2
int
main(int argc, char *argv[])
{
int result;
N3DS_Init();
SDL_SetMainReady();
result = SDL_main(argc, argv);
N3DS_Quit();
return result;
}
SDL_FORCE_INLINE void
N3DS_Init(void)
{
N3DS_SetCPUSpeed();
romfsInit();
gfxInit(GSP_RGBA8_OES, GSP_RGBA8_OES, false);
hidInit();
}
/* If available, enable L2 cache and high CPU clock */
SDL_FORCE_INLINE void
N3DS_SetCPUSpeed(void)
{
if (R_SUCCEEDED(ptmSysmInit())) {
if (R_SUCCEEDED(PTMSYSM_CheckNew3DS())) {
PTMSYSM_ConfigureNew3DSCPU(HIGH_CLOCK | L2_CACHE);
}
ptmSysmExit();
}
}
SDL_FORCE_INLINE void
N3DS_Quit(void)
{
hidExit();
gfxExit();
romfsExit();
}
#endif /* __3DS__ */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -71,6 +71,9 @@ static SDL_GetPowerInfo_Impl implementations[] = {
#ifdef SDL_POWER_VITA /* handles PSVita. */
SDL_GetPowerInfo_VITA,
#endif
#ifdef SDL_POWER_N3DS /* handles N3DS. */
SDL_GetPowerInfo_N3DS,
#endif
#ifdef SDL_POWER_WINRT /* handles WinRT */
SDL_GetPowerInfo_WinRT,
#endif

View file

@ -39,6 +39,7 @@ SDL_bool SDL_GetPowerInfo_Haiku(SDL_PowerState *, int *, int *);
SDL_bool SDL_GetPowerInfo_Android(SDL_PowerState *, int *, int *);
SDL_bool SDL_GetPowerInfo_PSP(SDL_PowerState *, int *, int *);
SDL_bool SDL_GetPowerInfo_VITA(SDL_PowerState *, int *, int *);
SDL_bool SDL_GetPowerInfo_N3DS(SDL_PowerState *, int *, int *);
SDL_bool SDL_GetPowerInfo_WinRT(SDL_PowerState *, int *, int *);
SDL_bool SDL_GetPowerInfo_Emscripten(SDL_PowerState *, int *, int *);

View file

@ -0,0 +1,111 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if !defined(SDL_POWER_DISABLED) && defined(SDL_POWER_N3DS)
#include <3ds.h>
#include "SDL_error.h"
#include "SDL_power.h"
SDL_FORCE_INLINE SDL_PowerState GetPowerState(void);
SDL_FORCE_INLINE int ReadStateFromPTMU(bool *is_plugged, u8 *is_charging);
SDL_FORCE_INLINE int GetBatteryPercentage(void);
#define BATTERY_PERCENT_REG 0xB
#define BATTERY_PERCENT_REG_SIZE 2
SDL_bool
SDL_GetPowerInfo_N3DS(SDL_PowerState *state, int *seconds, int *percent)
{
*state = GetPowerState();
*percent = GetBatteryPercentage();
*seconds = -1; /* libctru doesn't provide a way to estimate battery life */
return SDL_TRUE;
}
SDL_FORCE_INLINE SDL_PowerState
GetPowerState(void)
{
bool is_plugged;
u8 is_charging;
if (ReadStateFromPTMU(&is_plugged, &is_charging) < 0) {
return SDL_POWERSTATE_UNKNOWN;
}
if (is_charging) {
return SDL_POWERSTATE_CHARGING;
}
if (is_plugged) {
return SDL_POWERSTATE_CHARGED;
}
return SDL_POWERSTATE_ON_BATTERY;
}
SDL_FORCE_INLINE int
ReadStateFromPTMU(bool *is_plugged, u8 *is_charging)
{
if (R_FAILED(ptmuInit())) {
return SDL_SetError("Failed to initialise PTMU service");
}
if (R_FAILED(PTMU_GetAdapterState(is_plugged))) {
ptmuExit();
return SDL_SetError("Failed to read adapter state");
}
if (R_FAILED(PTMU_GetBatteryChargeState(is_charging))) {
ptmuExit();
return SDL_SetError("Failed to read battery charge state");
}
ptmuExit();
return 0;
}
SDL_FORCE_INLINE int
GetBatteryPercentage(void)
{
u8 data[BATTERY_PERCENT_REG_SIZE];
if (R_FAILED(mcuHwcInit())) {
return SDL_SetError("Failed to initialise mcuHwc service");
}
if (R_FAILED(MCUHWC_ReadRegister(BATTERY_PERCENT_REG, data, BATTERY_PERCENT_REG_SIZE))) {
mcuHwcExit();
return SDL_SetError("Failed to read battery register");
}
mcuHwcExit();
return (int) SDL_round(data[0] + data[1] / 256.0);
}
#endif /* !SDL_POWER_DISABLED && SDL_POWER_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -38,6 +38,8 @@
#include "psp/SDL_systhread_c.h"
#elif SDL_THREAD_VITA
#include "vita/SDL_systhread_c.h"
#elif SDL_THREAD_N3DS
#include "n3ds/SDL_systhread_c.h"
#elif SDL_THREAD_STDCPP
#include "stdcpp/SDL_systhread_c.h"
#elif SDL_THREAD_OS2

View file

@ -0,0 +1,133 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_THREAD_N3DS
/* An implementation of condition variables using libctru's CondVar */
#include "SDL_sysmutex_c.h"
struct SDL_cond
{
CondVar cond_variable;
};
/* Create a condition variable */
SDL_cond *
SDL_CreateCond(void)
{
SDL_cond *cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
if (cond) {
CondVar_Init(&cond->cond_variable);
} else {
SDL_OutOfMemory();
}
return cond;
}
/* Destroy a condition variable */
void
SDL_DestroyCond(SDL_cond *cond)
{
if (cond) {
SDL_free(cond);
}
}
/* Restart one of the threads that are waiting on the condition variable */
int
SDL_CondSignal(SDL_cond *cond)
{
if (!cond) {
return SDL_SetError("Passed a NULL condition variable");
}
CondVar_Signal(&cond->cond_variable);
return 0;
}
/* Restart all threads that are waiting on the condition variable */
int
SDL_CondBroadcast(SDL_cond *cond)
{
if (!cond) {
return SDL_SetError("Passed a NULL condition variable");
}
CondVar_Broadcast(&cond->cond_variable);
return 0;
}
/* Wait on the condition variable for at most 'ms' milliseconds.
The mutex must be locked before entering this function!
The mutex is unlocked during the wait, and locked again after the wait.
Typical use:
Thread A:
SDL_LockMutex(lock);
while ( ! condition ) {
SDL_CondWait(cond, lock);
}
SDL_UnlockMutex(lock);
Thread B:
SDL_LockMutex(lock);
...
condition = true;
...
SDL_CondSignal(cond);
SDL_UnlockMutex(lock);
*/
int
SDL_CondWaitTimeout(SDL_cond *cond, SDL_mutex *mutex, Uint32 ms)
{
Result res;
if (!cond) {
return SDL_SetError("Passed a NULL condition variable");
}
if (!mutex) {
return SDL_SetError("Passed a NULL mutex");
}
res = 0;
if (ms == SDL_MUTEX_MAXWAIT) {
CondVar_Wait(&cond->cond_variable, &mutex->lock.lock);
} else {
res = CondVar_WaitTimeout(&cond->cond_variable, &mutex->lock.lock,
(s64) ms * 1000000LL);
}
return R_SUCCEEDED(res) ? 0 : SDL_MUTEX_TIMEDOUT;
}
/* Wait on the condition variable forever */
int
SDL_CondWait(SDL_cond *cond, SDL_mutex *mutex)
{
return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT);
}
#endif /* SDL_THREAD_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,93 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_THREAD_N3DS
/* An implementation of mutexes using libctru's RecursiveLock */
#include "SDL_sysmutex_c.h"
/* Create a mutex */
SDL_mutex *
SDL_CreateMutex(void)
{
SDL_mutex *mutex;
/* Allocate mutex memory */
mutex = (SDL_mutex *) SDL_malloc(sizeof(*mutex));
if (mutex) {
RecursiveLock_Init(&mutex->lock);
} else {
SDL_OutOfMemory();
}
return mutex;
}
/* Free the mutex */
void
SDL_DestroyMutex(SDL_mutex *mutex)
{
if (mutex) {
SDL_free(mutex);
}
}
/* Lock the mutex */
int
SDL_LockMutex(SDL_mutex *mutex)
{
if (mutex == NULL) {
return SDL_SetError("Passed a NULL mutex");
}
RecursiveLock_Lock(&mutex->lock);
return 0;
}
/* try Lock the mutex */
int
SDL_TryLockMutex(SDL_mutex *mutex)
{
if (mutex == NULL) {
return SDL_SetError("Passed a NULL mutex");
}
return RecursiveLock_TryLock(&mutex->lock);
}
/* Unlock the mutex */
int
SDL_mutexV(SDL_mutex *mutex)
{
if (mutex == NULL) {
return SDL_SetError("Passed a NULL mutex");
}
RecursiveLock_Unlock(&mutex->lock);
return 0;
}
#endif /* SDL_THREAD_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,37 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef SDL_sysmutex_c_h_
#define SDL_sysmutex_c_h_
#include <3ds.h>
#include "SDL_mutex.h"
struct SDL_mutex
{
RecursiveLock lock;
};
#endif /* SDL_sysmutex_c_h */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,134 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_THREAD_N3DS
/* An implementation of semaphores using libctru's LightSemaphore */
#include <3ds.h>
#include "SDL_thread.h"
struct SDL_semaphore
{
LightSemaphore semaphore;
};
SDL_sem *
SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_sem *sem;
if (initial_value > SDL_MAX_SINT16) {
SDL_SetError("Initial semaphore value too high for this platform");
return NULL;
}
sem = (SDL_sem *) SDL_malloc(sizeof(*sem));
if (!sem) {
SDL_OutOfMemory();
return NULL;
}
LightSemaphore_Init(&sem->semaphore, initial_value, SDL_MAX_SINT16);
return sem;
}
/* WARNING:
You cannot call this function when another thread is using the semaphore.
*/
void
SDL_DestroySemaphore(SDL_sem *sem)
{
if (sem) {
SDL_free(sem);
}
}
int
SDL_SemTryWait(SDL_sem *sem)
{
if (!sem) {
return SDL_SetError("Passed a NULL semaphore");
}
return SDL_SemWaitTimeout(sem, 0);
}
int
SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout)
{
int retval;
if (!sem) {
return SDL_SetError("Passed a NULL semaphore");
}
if (timeout == SDL_MUTEX_MAXWAIT) {
LightSemaphore_Acquire(&sem->semaphore, 1);
retval = 0;
} else {
int return_code = LightSemaphore_TryAcquire(&sem->semaphore, 1);
if (return_code != 0) {
for (u32 i = 0; i < timeout; i++) {
svcSleepThread(1000000LL);
return_code = LightSemaphore_TryAcquire(&sem->semaphore, 1);
if (return_code == 0) {
break;
}
}
}
retval = return_code != 0 ? SDL_MUTEX_TIMEDOUT : 0;
}
return retval;
}
int
SDL_SemWait(SDL_sem *sem)
{
return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT);
}
Uint32
SDL_SemValue(SDL_sem *sem)
{
if (!sem) {
return SDL_SetError("Passed a NULL semaphore");
}
return sem->semaphore.current_count;
}
int
SDL_SemPost(SDL_sem *sem)
{
if (!sem) {
return SDL_SetError("Passed a NULL semaphore");
}
LightSemaphore_Release(&sem->semaphore, 1);
return 0;
}
#endif /* SDL_THREAD_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,148 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_THREAD_N3DS
/* Thread management routines for SDL */
#include "../SDL_systhread.h"
/* N3DS has very limited RAM (128MB), so we put a limit on thread stack size. */
#define N3DS_THREAD_STACK_SIZE_MAX (16 * 1024)
#define N3DS_THREAD_STACK_SIZE_DEFAULT (4 * 1024)
#define N3DS_THREAD_PRIORITY_LOW 0x3F /**< Minimum priority */
#define N3DS_THREAD_PRIORITY_MEDIUM 0x2F /**< Slightly higher than main thread (0x30) */
#define N3DS_THREAD_PRIORITY_HIGH 0x19 /**< High priority for non-video work */
#define N3DS_THREAD_PRIORITY_TIME_CRITICAL 0x18 /**< Highest priority */
static size_t GetStackSize(size_t requested_size);
static void
ThreadEntry(void *arg)
{
SDL_RunThread((SDL_Thread *) arg);
threadExit(0);
}
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
#error "SDL_PASSED_BEGINTHREAD_ENDTHREAD is not supported on N3DS"
#endif
int
SDL_SYS_CreateThread(SDL_Thread *thread)
{
s32 priority = N3DS_THREAD_PRIORITY_MEDIUM;
size_t stack_size = GetStackSize(thread->stacksize);
thread->handle = threadCreate(ThreadEntry,
thread,
stack_size,
priority,
-1,
false);
if (thread->handle == NULL) {
return SDL_SetError("Couldn't create thread");
}
return 0;
}
static size_t
GetStackSize(size_t requested_size)
{
if (requested_size == 0) {
return N3DS_THREAD_STACK_SIZE_DEFAULT;
}
if (requested_size > N3DS_THREAD_STACK_SIZE_MAX) {
SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM,
"Requested a thread size of %zu,"
" falling back to the maximum supported of %zu\n",
requested_size,
N3DS_THREAD_STACK_SIZE_MAX);
requested_size = N3DS_THREAD_STACK_SIZE_MAX;
}
return requested_size;
}
void
SDL_SYS_SetupThread(const char *name)
{
return;
}
SDL_threadID
SDL_ThreadID(void)
{
u32 thread_ID = 0;
svcGetThreadId(&thread_ID, CUR_THREAD_HANDLE);
return (SDL_threadID) thread_ID;
}
int
SDL_SYS_SetThreadPriority(SDL_ThreadPriority sdl_priority)
{
s32 svc_priority;
switch (sdl_priority) {
case SDL_THREAD_PRIORITY_LOW:
svc_priority = N3DS_THREAD_PRIORITY_LOW;
break;
case SDL_THREAD_PRIORITY_NORMAL:
svc_priority = N3DS_THREAD_PRIORITY_MEDIUM;
break;
case SDL_THREAD_PRIORITY_HIGH:
svc_priority = N3DS_THREAD_PRIORITY_HIGH;
break;
case SDL_THREAD_PRIORITY_TIME_CRITICAL:
svc_priority = N3DS_THREAD_PRIORITY_TIME_CRITICAL;
break;
default:
svc_priority = N3DS_THREAD_PRIORITY_MEDIUM;
}
return (int) svcSetThreadPriority(CUR_THREAD_HANDLE, svc_priority);
}
void
SDL_SYS_WaitThread(SDL_Thread *thread)
{
Result res = threadJoin(thread->handle, U64_MAX);
/*
Detached threads can be waited on, but should NOT be cleaned manually
as it would result in a fatal error.
*/
if (R_SUCCEEDED(res) && SDL_AtomicGet(&thread->state) != SDL_THREAD_STATE_DETACHED) {
threadFree(thread->handle);
}
}
void
SDL_SYS_DetachThread(SDL_Thread *thread)
{
threadDetach(thread->handle);
}
#endif /* SDL_THREAD_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,32 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef SDL_systhread_c_h_
#define SDL_systhread_c_h_
#include <3ds.h>
typedef Thread SYS_ThreadHandle;
#endif /* SDL_systhread_c_h_ */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,81 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_TIMER_N3DS
#include <3ds.h>
static SDL_bool ticks_started = SDL_FALSE;
static u64 start_tick;
#define NSEC_PER_MSEC 1000000ULL
void
SDL_TicksInit(void)
{
if (ticks_started) {
return;
}
ticks_started = SDL_TRUE;
start_tick = svcGetSystemTick();
}
void
SDL_TicksQuit(void)
{
ticks_started = SDL_FALSE;
}
Uint64
SDL_GetTicks64(void)
{
u64 elapsed;
if (!ticks_started) {
SDL_TicksInit();
}
elapsed = svcGetSystemTick() - start_tick;
return elapsed / CPU_TICKS_PER_MSEC;
}
Uint64
SDL_GetPerformanceCounter(void)
{
return svcGetSystemTick();
}
Uint64
SDL_GetPerformanceFrequency(void)
{
return SYSCLOCK_ARM11;
}
void
SDL_Delay(Uint32 ms)
{
svcSleepThread(ms * NSEC_PER_MSEC);
}
#endif /* SDL_TIMER_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -461,6 +461,7 @@ extern VideoBootStrap PS2_bootstrap;
extern VideoBootStrap PSP_bootstrap;
extern VideoBootStrap VITA_bootstrap;
extern VideoBootStrap RISCOS_bootstrap;
extern VideoBootStrap N3DS_bootstrap;
extern VideoBootStrap RPI_bootstrap;
extern VideoBootStrap KMSDRM_bootstrap;
extern VideoBootStrap KMSDRM_LEGACY_bootstrap;

View file

@ -100,6 +100,9 @@ static VideoBootStrap *bootstrap[] = {
#if SDL_VIDEO_DRIVER_VITA
&VITA_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_N3DS
&N3DS_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_KMSDRM
&KMSDRM_bootstrap,
#endif
@ -1498,7 +1501,7 @@ SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen)
}
#define CREATE_FLAGS \
(SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL)
(SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL | SDL_WINDOW_N3DS_BOTTOM)
static SDL_INLINE SDL_bool
IsAcceptingDragAndDrop(void)

View file

@ -0,0 +1,45 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_N3DS
/* Pumping the events for the Home and Power buttons. */
#include <3ds.h>
#include "../../events/SDL_events_c.h"
#include "SDL_n3dsevents_c.h"
void
N3DS_PumpEvents(_THIS)
{
if (!aptMainLoop()) {
SDL_Event ev;
ev.type = SDL_QUIT;
SDL_PushEvent(&ev);
return;
}
}
#endif /* SDL_VIDEO_DRIVER_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,31 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_n3dsevents_c_h_
#define SDL_n3dsevents_c_h_
#include "../../SDL_internal.h"
extern void N3DS_PumpEvents(_THIS);
#endif /* SDL_n3dsevents_c_h_ */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,148 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_N3DS
#include "../SDL_sysvideo.h"
#include "SDL_n3dsframebuffer_c.h"
#include "SDL_n3dsvideo.h"
#define N3DS_SURFACE "_SDL_N3DSSurface"
typedef struct
{
int width, height;
} Dimensions;
SDL_FORCE_INLINE void FreePreviousWindowFramebuffer(SDL_Window *window);
SDL_FORCE_INLINE SDL_Surface *CreateNewWindowFramebuffer(SDL_Window *window);
SDL_FORCE_INLINE void CopyFramebuffertoN3DS(u32 *dest, const Dimensions dest_dim, const u32 *source, const Dimensions source_dim);
SDL_FORCE_INLINE int GetDestOffset(int x, int y, int dest_width);
SDL_FORCE_INLINE int GetSourceOffset(int x, int y, int source_width);
SDL_FORCE_INLINE void FlushN3DSBuffer(const void *buffer, u32 bufsize, gfxScreen_t screen);
int
SDL_N3DS_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, void **pixels, int *pitch)
{
SDL_Surface *framebuffer;
FreePreviousWindowFramebuffer(window);
framebuffer = CreateNewWindowFramebuffer(window);
if (!framebuffer) {
return SDL_OutOfMemory();
}
SDL_SetWindowData(window, N3DS_SURFACE, framebuffer);
*format = FRAMEBUFFER_FORMAT;
*pixels = framebuffer->pixels;
*pitch = framebuffer->pitch;
return 0;
}
SDL_FORCE_INLINE void
FreePreviousWindowFramebuffer(SDL_Window *window)
{
SDL_Surface *surface = (SDL_Surface *) SDL_GetWindowData(window, N3DS_SURFACE);
SDL_FreeSurface(surface);
}
SDL_FORCE_INLINE SDL_Surface *
CreateNewWindowFramebuffer(SDL_Window *window)
{
int w, h, bpp;
Uint32 Rmask, Gmask, Bmask, Amask;
SDL_PixelFormatEnumToMasks(FRAMEBUFFER_FORMAT, &bpp, &Rmask, &Gmask, &Bmask, &Amask);
SDL_GetWindowSize(window, &w, &h);
return SDL_CreateRGBSurface(0, w, h, bpp, Rmask, Gmask, Bmask, Amask);
}
int
SDL_N3DS_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects)
{
SDL_WindowData *drv_data = (SDL_WindowData *) window->driverdata;
SDL_Surface *surface;
u16 width, height;
u32 *framebuffer;
u32 bufsize;
surface = (SDL_Surface *) SDL_GetWindowData(window, N3DS_SURFACE);
if (!surface) {
return SDL_SetError("%s: Unable to get the window surface.", __func__);
}
/* Get the N3DS internal framebuffer and its size */
framebuffer = (u32 *) gfxGetFramebuffer(drv_data->screen, GFX_LEFT, &width, &height);
bufsize = width * height * 4;
CopyFramebuffertoN3DS(framebuffer, (Dimensions){ width, height },
surface->pixels, (Dimensions){ surface->w, surface->h });
FlushN3DSBuffer(framebuffer, bufsize, drv_data->screen);
return 0;
}
SDL_FORCE_INLINE void
CopyFramebuffertoN3DS(u32 *dest, const Dimensions dest_dim, const u32 *source, const Dimensions source_dim)
{
int rows = SDL_min(dest_dim.width, source_dim.height);
int cols = SDL_min(dest_dim.height, source_dim.width);
for (int y = 0; y < rows; ++y) {
for (int x = 0; x < cols; ++x) {
SDL_memcpy(
dest + GetDestOffset(x, y, dest_dim.width),
source + GetSourceOffset(x, y, source_dim.width),
4);
}
}
}
SDL_FORCE_INLINE int
GetDestOffset(int x, int y, int dest_width)
{
return dest_width - y - 1 + dest_width * x;
}
SDL_FORCE_INLINE int
GetSourceOffset(int x, int y, int source_width)
{
return x + y * source_width;
}
SDL_FORCE_INLINE void
FlushN3DSBuffer(const void *buffer, u32 bufsize, gfxScreen_t screen)
{
GSPGPU_FlushDataCache(buffer, bufsize);
gfxScreenSwapBuffers(screen, false);
}
void
SDL_N3DS_DestroyWindowFramebuffer(_THIS, SDL_Window *window)
{
SDL_Surface *surface;
surface = (SDL_Surface *) SDL_SetWindowData(window, N3DS_SURFACE, NULL);
SDL_FreeSurface(surface);
}
#endif /* SDL_VIDEO_DRIVER_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,33 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_n3dsframebuffer_c_h_
#define SDL_n3dsframebuffer_c_h_
#include "../../SDL_internal.h"
int SDL_N3DS_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, void **pixels, int *pitch);
int SDL_N3DS_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects);
void SDL_N3DS_DestroyWindowFramebuffer(_THIS, SDL_Window *window);
#endif /* SDL_n3dsframebuffer_c_h_ */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,76 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_N3DS
#include <3ds.h>
#include "SDL_n3dsswkb.h"
static SwkbdState sw_keyboard;
const static size_t BUFFER_SIZE = 256;
void
N3DS_SwkbInit()
{
swkbdInit(&sw_keyboard, SWKBD_TYPE_NORMAL, 2, -1);
}
void
N3DS_SwkbPoll()
{
return;
}
void
N3DS_SwkbQuit()
{
return;
}
SDL_bool
N3DS_HasScreenKeyboardSupport(_THIS)
{
return SDL_TRUE;
}
void
N3DS_StartTextInput(_THIS)
{
char buffer[BUFFER_SIZE];
SwkbdButton button_pressed;
button_pressed = swkbdInputText(&sw_keyboard, buffer, BUFFER_SIZE);
if (button_pressed == SWKBD_BUTTON_CONFIRM) {
SDL_SendKeyboardText(buffer);
}
}
void
N3DS_StopTextInput(_THIS)
{
return;
}
#endif /* SDL_VIDEO_DRIVER_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_n3dskeyboard_h_
#define SDL_n3dskeyboard_h_
#include "../../events/SDL_events_c.h"
void N3DS_SwkbInit();
void N3DS_SwkbPoll();
void N3DS_SwkbQuit();
SDL_bool N3DS_HasScreenKeyboardSupport(_THIS);
void N3DS_StartTextInput(_THIS);
void N3DS_StopTextInput(_THIS);
#endif /* SDL_n3dskeyboard_h_ */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,163 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_N3DS
#include "../SDL_sysvideo.h"
#include "SDL_n3dsevents_c.h"
#include "SDL_n3dsframebuffer_c.h"
#include "SDL_n3dsswkb.h"
#include "SDL_n3dsvideo.h"
#define N3DSVID_DRIVER_NAME "n3ds"
SDL_FORCE_INLINE void AddN3DSDisplay(gfxScreen_t screen);
static int N3DS_VideoInit(_THIS);
static void N3DS_VideoQuit(_THIS);
static void N3DS_GetDisplayModes(_THIS, SDL_VideoDisplay *display);
static int N3DS_CreateWindow(_THIS, SDL_Window *window);
static void N3DS_DestroyWindow(_THIS, SDL_Window *window);
/* N3DS driver bootstrap functions */
static void
N3DS_DeleteDevice(SDL_VideoDevice *device)
{
SDL_free(device->displays);
SDL_free(device->driverdata);
SDL_free(device);
}
static SDL_VideoDevice *
N3DS_CreateDevice(void)
{
SDL_VideoDevice *device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
if (!device) {
SDL_OutOfMemory();
return (0);
}
device->VideoInit = N3DS_VideoInit;
device->VideoQuit = N3DS_VideoQuit;
device->GetDisplayModes = N3DS_GetDisplayModes;
device->CreateSDLWindow = N3DS_CreateWindow;
device->DestroyWindow = N3DS_DestroyWindow;
device->HasScreenKeyboardSupport = N3DS_HasScreenKeyboardSupport;
device->StartTextInput = N3DS_StartTextInput;
device->StopTextInput = N3DS_StopTextInput;
device->PumpEvents = N3DS_PumpEvents;
device->CreateWindowFramebuffer = SDL_N3DS_CreateWindowFramebuffer;
device->UpdateWindowFramebuffer = SDL_N3DS_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = SDL_N3DS_DestroyWindowFramebuffer;
device->num_displays = 2;
device->free = N3DS_DeleteDevice;
return device;
}
VideoBootStrap N3DS_bootstrap = { N3DSVID_DRIVER_NAME, "N3DS Video Driver", N3DS_CreateDevice };
static int
N3DS_VideoInit(_THIS)
{
AddN3DSDisplay(GFX_TOP);
AddN3DSDisplay(GFX_BOTTOM);
N3DS_SwkbInit();
return 0;
}
SDL_FORCE_INLINE void
AddN3DSDisplay(gfxScreen_t screen)
{
SDL_DisplayMode mode;
SDL_VideoDisplay display;
SDL_zero(mode);
SDL_zero(display);
mode.w = (screen == GFX_TOP) ? GSP_SCREEN_HEIGHT_TOP : GSP_SCREEN_HEIGHT_BOTTOM;
mode.h = GSP_SCREEN_WIDTH;
mode.refresh_rate = 60;
mode.format = FRAMEBUFFER_FORMAT;
mode.driverdata = NULL;
display.desktop_mode = mode;
display.current_mode = mode;
display.driverdata = NULL;
SDL_AddVideoDisplay(&display, SDL_FALSE);
}
static void
N3DS_VideoQuit(_THIS)
{
N3DS_SwkbQuit();
return;
}
static void
N3DS_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
{
/* Each display only has a single mode */
SDL_AddDisplayMode(display, &display->current_mode);
}
static int
N3DS_CreateWindow(_THIS, SDL_Window *window)
{
SDL_WindowData *drv_data = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
if (drv_data == NULL) {
return SDL_OutOfMemory();
}
if (window->flags & SDL_WINDOW_N3DS_BOTTOM) {
drv_data->screen = GFX_BOTTOM;
} else {
drv_data->screen = GFX_TOP;
}
window->driverdata = drv_data;
return 0;
}
static void
N3DS_DestroyWindow(_THIS, SDL_Window *window)
{
if (window == NULL) {
return;
}
SDL_free(window->driverdata);
}
#endif /* SDL_VIDEO_DRIVER_N3DS */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef SDL_n3dsvideo_h_
#define SDL_n3dsvideo_h_
#include <3ds.h>
#include "../SDL_sysvideo.h"
typedef struct SDL_WindowData
{
gfxScreen_t screen; /**< Keeps track of which N3DS screen is targetted */
} SDL_WindowData;
#define FRAMEBUFFER_FORMAT SDL_PIXELFORMAT_RGBA8888
#endif /* SDL_n3dsvideo_h_ */
/* vi: set sts=4 ts=4 sw=4 expandtab: */

View file

@ -14,6 +14,10 @@ if(SDL_INSTALL_TESTS)
include(GNUInstallDirs)
endif()
if(N3DS)
link_libraries(SDL2::SDL2main)
endif()
if(PSP)
link_libraries(
SDL2::SDL2main
@ -430,6 +434,27 @@ if(PSP)
endforeach()
endif()
if(N3DS)
set(ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/romfs")
file(COPY ${RESOURCE_FILES} DESTINATION "${ROMFS_DIR}")
foreach(APP IN LISTS ALL_TESTS)
get_target_property(TARGET_BINARY_DIR ${APP} BINARY_DIR)
set(SMDH_FILE "${TARGET_BINARY_DIR}/${APP}.smdh")
ctr_generate_smdh("${SMDH_FILE}"
NAME "SDL-${APP}"
DESCRIPTION "SDL2 Test suite"
AUTHOR "SDL2 Contributors"
ICON "${CMAKE_CURRENT_SOURCE_DIR}/n3ds/logo48x48.png"
)
ctr_create_3dsx(
${APP}
ROMFS "${ROMFS_DIR}"
SMDH "${SMDH_FILE}"
)
endforeach()
endif()
if(RISCOS)
set(ALL_TESTS_AIF "")
foreach(APP IN LISTS ALL_TESTS)

BIN
test/n3ds/logo48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB