diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e26f272c2..1ffc19606 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -356,6 +356,35 @@ jobs: name: "tic80-nintendo-3ds" path: build/bin/tic80.3dsx + # === Nintendo Switch build === + nintendo-switch: + runs-on: ubuntu-latest + container: devkitpro/devkita64:20250728 + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Install Host toolchain + run: | + apt-get update + apt-get install --assume-yes build-essential ruby-full + + - name: Build + run: | + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=$DEVKITPRO/cmake/Switch.cmake \ + -DCMAKE_BUILD_TYPE=MinSizeRel -DBUILD_WITH_ALL=ON -DBUILD_SDLGPU=ON \ + -DBUILD_WITH_YUE=OFF + cmake --build build --parallel + + - name: Deploy + uses: actions/upload-artifact@v4 + with: + name: "tic80-nintendo-switch" + path: build/bin/tic80.nro + # === MacOS 14 / arm64 === macos-arm64: runs-on: macos-14 diff --git a/.gitignore b/.gitignore index 2efee802c..4bbed67d6 100644 --- a/.gitignore +++ b/.gitignore @@ -179,6 +179,11 @@ CMakeSettings.json tic_mruby_build_config.rb.lock tic_mruby_wasm_build_config.rb.lock build/mruby_vendor-prefix/ +# switch +build/*.nro +build/*.nacp +build/*.lst +build/*.map **/.zig-cache **/zig-out .cache diff --git a/CMakeLists.txt b/CMakeLists.txt index 780f0223e..426fd7abf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ message("VERSION_HASH: ${VERSION_HASH}") configure_file("${PROJECT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h") -if(ANDROID OR EMSCRIPTEN OR NINTENDO_3DS OR BAREMETALPI) +if(ANDROID OR EMSCRIPTEN OR NINTENDO_3DS OR NINTENDO_SWITCH OR BAREMETALPI) set(BUILD_STATIC_DEFAULT ON) else() set(BUILD_STATIC_DEFAULT OFF) @@ -32,7 +32,11 @@ endif() set(BUILD_PLAYER_DEFAULT OFF) set(BUILD_LIBRETRO_DEFAULT OFF) -set(BUILD_TOUCH_INPUT_DEFAULT ${ANDROID}) +if(ANDROID OR NINTENDO_SWITCH) + set(BUILD_TOUCH_INPUT_DEFAULT ON) +else() + set(BUILD_TOUCH_INPUT_DEFAULT OFF) +endif() set(BUILD_WITH_ALL_DEFAULT OFF) option(BUILD_STATIC "Static runtime" ${BUILD_STATIC_DEFAULT}) @@ -60,6 +64,10 @@ if(NOT BUILD_SDL) set(BUILD_SDLGPU OFF) endif() +if(NINTENDO_SWITCH) + set(PREFER_SYSTEM_LIBRARIES ON) +endif() + add_library(runtime INTERFACE) if(BUILD_STATIC) @@ -77,13 +85,12 @@ message("BUILD_SDLGPU: ${BUILD_SDLGPU}") message("BUILD_TOUCH_INPUT: ${BUILD_TOUCH_INPUT}") message("BUILD_WITH_ALL: ${BUILD_WITH_ALL}") -if (NINTENDO_3DS) +if(NINTENDO_3DS OR BAREMETALPI) set(BUILD_SDL OFF) - set(PREFER_SYSTEM_LIBRARIES ON) endif() -if (BAREMETALPI) - set(BUILD_SDL OFF) +if (NINTENDO_3DS) + set(PREFER_SYSTEM_LIBRARIES ON) endif() if(UNIX AND NOT APPLE AND NOT EMSCRIPTEN AND NOT ANDROID) @@ -178,5 +185,6 @@ include(cmake/studio.cmake) include(cmake/sdl.cmake) include(cmake/libretro.cmake) include(cmake/n3ds.cmake) +include(cmake/nswitch.cmake) include(cmake/install.cmake) diff --git a/README.md b/README.md index 91da8fe47..90601462e 100644 --- a/README.md +++ b/README.md @@ -424,3 +424,4 @@ You can find the compiled version ready download and install [on F-Droid](https: * NuSan — [Github @TheNuSan](https://github.com/thenusan) * Li Jin — [Github @pigpigyyy](https://github.com/pigpigyyy) * Dania Rifki — [Github @Kaleidosium](https://github.com/Kaleidosium) +* Carsten Teibes — [GitHub @carstene1ns](https://github.com/carstene1ns) diff --git a/build/nswitch/README.md b/build/nswitch/README.md new file mode 100644 index 000000000..b3189b7cc --- /dev/null +++ b/build/nswitch/README.md @@ -0,0 +1,19 @@ +# Switch build + +## Requirements + +* devkitA64 + the switch-dev meta package +* the following additional packages: + * switch-libpng + * switch-zlib + * switch-sdl2 + * switch-curl + +## Building instructions + +``` +cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=$DEVKITPRO/cmake/Switch.cmake +cmake --build build +``` + +You should now be able to find `tic80.nro` in build/bin. diff --git a/build/nswitch/icon.jpg b/build/nswitch/icon.jpg new file mode 100644 index 000000000..6ba6a5e4d Binary files /dev/null and b/build/nswitch/icon.jpg differ diff --git a/cmake/naett.cmake b/cmake/naett.cmake index fe878e00d..d100af041 100644 --- a/cmake/naett.cmake +++ b/cmake/naett.cmake @@ -2,7 +2,9 @@ # naett ################################ -if(NOT RPI AND NOT NINTENDO_3DS AND NOT EMSCRIPTEN AND NOT BAREMETALPI) +if(RPI OR NINTENDO_3DS OR NINTENDO_SWITCH OR EMSCRIPTEN OR BAREMETALPI) + set(USE_NAETT FALSE) +else() set(USE_NAETT TRUE) endif() diff --git a/cmake/nswitch.cmake b/cmake/nswitch.cmake new file mode 100644 index 000000000..ff7f4dc86 --- /dev/null +++ b/cmake/nswitch.cmake @@ -0,0 +1,32 @@ +################################ +# TIC-80 app (Switch) +################################ + +if(NINTENDO_SWITCH) + if(BUILD_EDITORS) + target_sources(tic80studio PRIVATE + src/system/nswitch/net.c) + target_include_directories(tic80studio + PRIVATE ${TIC80LIB_DIR}/studio) + endif() + + target_sources(${TIC80_TARGET} PRIVATE + src/system/nswitch/runtime.c) + + find_package(PkgConfig) + pkg_search_module(CURL libcurl IMPORTED_TARGET) + target_link_libraries(tic80 PkgConfig::CURL) + + nx_generate_nacp(tic80.nacp + NAME "TIC-80 tiny computer" + AUTHOR "Nesbox, carstene1ns" + VERSION ${PROJECT_VERSION}) + + nx_create_nro(${TIC80_TARGET} + NACP tic80.nacp + ICON ${CMAKE_SOURCE_DIR}/build/nswitch/icon.jpg + #ROMFS ${CMAKE_SOURCE_DIR}/build/nswitch/romfs + OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/tic80.nro) + + dkp_target_generate_symbol_list(${TIC80_TARGET}) +endif() diff --git a/cmake/quickjs.cmake b/cmake/quickjs.cmake index aa0348f3f..1d0c8f66a 100644 --- a/cmake/quickjs.cmake +++ b/cmake/quickjs.cmake @@ -49,7 +49,7 @@ if(BUILD_WITH_JS) target_compile_definitions(quickjs PRIVATE DUMP_LEAKS) endif() - if(BAREMETALPI OR NINTENDO_3DS) + if(BAREMETALPI OR NINTENDO_3DS OR NINTENDO_SWITCH) target_compile_definitions(quickjs PRIVATE POOR_CLIB) endif() diff --git a/cmake/scheme.cmake b/cmake/scheme.cmake index c95ceccd5..d696e81c5 100644 --- a/cmake/scheme.cmake +++ b/cmake/scheme.cmake @@ -34,6 +34,10 @@ if(BUILD_WITH_SCHEME) target_compile_definitions(scheme PRIVATE S7_N3DS) endif() + if (NINTENDO_SWITCH) + target_compile_definitions(scheme PRIVATE S7_SWITCH) + endif() + if (BAREMETALPI) target_compile_definitions(scheme PRIVATE S7_BAREMETALPI) endif() diff --git a/cmake/sdl.cmake b/cmake/sdl.cmake index 513c0ee2d..5c5627d7f 100644 --- a/cmake/sdl.cmake +++ b/cmake/sdl.cmake @@ -4,8 +4,13 @@ if(PREFER_SYSTEM_LIBRARIES) find_package(SDL2) if(SDL2_FOUND) - add_library(SDL2 ALIAS SDL2::SDL2) - add_library(SDL2-static ALIAS SDL2::SDL2) + if(NINTENDO_SWITCH) + add_library(SDL2 ALIAS SDL2::SDL2-static) + add_library(SDL2-static ALIAS SDL2::SDL2-static) + else() + add_library(SDL2 ALIAS SDL2::SDL2) + add_library(SDL2-static ALIAS SDL2::SDL2) + endif() message(STATUS "Use system library: SDL2") else() message(WARNING "System library SDL2 not found") @@ -89,7 +94,7 @@ set(SDLGPU_SRC ${SDLGPU_DIR}/externals/stb_image_write/stb_image_write.c ) -if(NOT ANDROID) +if(NOT ANDROID AND NOT NINTENDO_SWITCH) list(APPEND SDLGPU_SRC ${SDLGPU_DIR}/renderer_GLES_1.c ${SDLGPU_DIR}/renderer_GLES_3.c @@ -105,7 +110,7 @@ endif() add_library(sdlgpu STATIC ${SDLGPU_SRC}) -if(EMSCRIPTEN OR ANDROID) +if(EMSCRIPTEN OR ANDROID OR NINTENDO_SWITCH) target_compile_definitions(sdlgpu PRIVATE GLEW_STATIC SDL_GPU_DISABLE_GLES_1 SDL_GPU_DISABLE_GLES_3 SDL_GPU_DISABLE_OPENGL) else() target_compile_definitions(sdlgpu PRIVATE GLEW_STATIC SDL_GPU_DISABLE_GLES SDL_GPU_DISABLE_OPENGL_3 SDL_GPU_DISABLE_OPENGL_4) @@ -141,6 +146,12 @@ if(ANDROID) ) endif() +if(NINTENDO_SWITCH) + find_package(PkgConfig REQUIRED) + pkg_search_module(GLES2 glesv2 REQUIRED IMPORTED_TARGET) + target_link_libraries(sdlgpu PkgConfig::GLES2) +endif() + if(NOT EMSCRIPTEN) if(BUILD_STATIC) target_link_libraries(sdlgpu SDL2-static) diff --git a/include/tic80_config.h b/include/tic80_config.h index 57ede847c..2e1cc2775 100644 --- a/include/tic80_config.h +++ b/include/tic80_config.h @@ -75,6 +75,6 @@ # endif #endif -#if defined(ANDROID) || defined(__ANDROID__) || defined(BAREMETALPI) || defined(__3DS__) +#if defined(ANDROID) || defined(__ANDROID__) || defined(BAREMETALPI) || defined(__3DS__) || defined(__SWITCH__) # define TIC80_FFT_UNSUPPORTED 1 #endif diff --git a/src/ext/miniaudio.h b/src/ext/miniaudio.h index 11107db90..856cdef13 100644 --- a/src/ext/miniaudio.h +++ b/src/ext/miniaudio.h @@ -16137,7 +16137,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority int result; pthread_attr_t* pAttr = NULL; -#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) +#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) && !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) { diff --git a/src/studio/net.c b/src/studio/net.c index d20e73819..b78ba9730 100644 --- a/src/studio/net.c +++ b/src/studio/net.c @@ -272,6 +272,10 @@ void tic_net_end(tic_net *net) } } +#elif defined(__SWITCH__) + +// See net.c in src/system + #else tic_net* tic_net_create(const char* host) {return NULL;} diff --git a/src/studio/studio.h b/src/studio/studio.h index 3b3055547..70eb2b012 100644 --- a/src/studio/studio.h +++ b/src/studio/studio.h @@ -42,6 +42,8 @@ #ifdef BAREMETALPI #define TIC_LOCAL "../.tic80/" +#elif defined(__SWITCH__) +#define TIC_LOCAL "./data/" #else #define TIC_LOCAL ".local/" #endif diff --git a/src/system/nswitch/net.c b/src/system/nswitch/net.c new file mode 100644 index 000000000..693bcacb2 --- /dev/null +++ b/src/system/nswitch/net.c @@ -0,0 +1,342 @@ +// MIT License + +// Copyright (c) 2025 Carsten Teibes + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "net.h" +#include "defines.h" +#include "version.h" + +#include +#include +#include +#include +#include + +#define STR(s) TO_STRING(s) +#define TO_STRING(s) #s +#define TIC_VERSION_STRING STR(TIC_VERSION_MAJOR) "." STR(TIC_VERSION_MINOR) "." STR(TIC_VERSION_REVISION) + +#define URL_SIZE 2048 + +typedef enum { + RS_RUNNING = 0, + RS_OK, + RS_ERROR, +} req_state; + +typedef struct { + tic_net *net; + + char url[URL_SIZE]; + volatile req_state state; + + // for callback + net_get_callback callback; + void *calldata; + + // for progress function + curl_off_t lastruntime; + CURL *curl; + + // for write function + char *buffer; + size_t size; +} net_ctx; + +struct tic_net { + char host[URL_SIZE]; + Mutex lock; + Thread thread; + UEvent poll_event, exit_event; + CURLM *curl_multi; + bool close_sockets; + + net_ctx **requests; + size_t count; +}; + +static size_t switch_write_cb(void *contents, size_t size, size_t nmemb, void *userp) { + net_ctx *ctx = (net_ctx *)userp; + + size_t realsize = size * nmemb; + size_t old_size = ctx->size; + + ctx->buffer = realloc(ctx->buffer, ctx->size + realsize + 1); + if(!ctx->buffer) { + printf("out of memory!\n"); + return 0; // signal error + } + + // append data + memcpy(&(ctx->buffer[old_size]), contents, realsize); + ctx->size += realsize; + ctx->buffer[ctx->size] = '\0'; + + return realsize; +} + +static int switch_xferinfo_cb(void *userp, curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow) { + net_ctx *ctx = (net_ctx *)userp; + + // no size yet + if (!dltotal) return 0; + + curl_off_t curtime; + curl_easy_getinfo(ctx->curl, CURLINFO_TOTAL_TIME_T, &curtime); + if((curtime - ctx->lastruntime) >= 100000U) { // every 100ms + ctx->lastruntime = curtime; + if (ctx->callback != NULL) { + if (mutexTryLock(&ctx->net->lock)) { + // send progress + net_get_data getData = { + .type = net_get_progress, + .progress = { + .size = dlnow, + .total = dltotal, + }, + .calldata = ctx->calldata, + .url = ctx->url, + }; + ctx->callback(&getData); + mutexUnlock(&ctx->net->lock); + } + } + } + + return 0; +} + +static void switch_worker_thread(void *userp) { + tic_net* net = (tic_net *)userp; + + Result rc; + int idx; + while(1) { + // wait until something to do + rc = waitMulti(&idx, -1, waiterForUEvent(&net->poll_event), waiterForUEvent(&net->exit_event)); + if(R_FAILED(rc)) { + continue; + } + + if(idx == 1) { // exit_event + break; + } + + ueventClear(&net->poll_event); + + int still_running = 1; + while(still_running) { + CURLMcode mc = curl_multi_perform(net->curl_multi, &still_running); + + if(still_running) { + mc = curl_multi_poll(net->curl_multi, NULL, 0, 1000, NULL); + if(mc != CURLM_OK) { + printf("curl_multi failed, code %d.\n", mc); + break; + } + } + + CURLMsg *msg; + int msgs_left; + while((msg = curl_multi_info_read(net->curl_multi, &msgs_left)) != NULL) { + if(msg->msg == CURLMSG_DONE) { // request done + CURL *curl = msg->easy_handle; + + net_ctx *ctx = NULL; + curl_easy_getinfo(curl, CURLINFO_PRIVATE, &ctx); + if(ctx) { + if(msg->data.result == CURLE_OK) { // all ok + ctx->state = RS_OK; + } else { // some error happened + // TODO: maybe publish msg->data.result + ctx->state = RS_ERROR; + } + } + + curl_multi_remove_handle(net->curl_multi, curl); + curl_easy_cleanup(curl); + } + } + } + } +} + +tic_net* tic_net_create(const char* host) { + tic_net* net = NEW(tic_net); + memset(net, 0, sizeof(tic_net)); + + strncpy(net->host, host, URL_SIZE - 1); + net->host[URL_SIZE - 1] = '\0'; + + mutexInit(&net->lock); + ueventCreate(&net->poll_event, false); + ueventCreate(&net->exit_event, false); + + Result rc = socketInitializeDefault(); + if (R_SUCCEEDED(rc)) { + net->close_sockets = true; + } else { + if(R_VALUE(rc) == MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized)) { + net->close_sockets = false; // we use nxlink currently + } else { + printf("socket init fail!\n"); + return NULL; + } + } + + curl_global_init(CURL_GLOBAL_DEFAULT); + net->curl_multi = curl_multi_init(); + if(!net->curl_multi) { + printf("curl init fail!\n"); + return NULL; + } + curl_multi_setopt(net->curl_multi, CURLMOPT_MAXCONNECTS, 8L); + + rc = threadCreate(&net->thread, switch_worker_thread, (void*)net, NULL, 0x10000, 0x2B, -2); + if (R_SUCCEEDED(rc)) { + rc = threadStart(&net->thread); + if (R_FAILED(rc)) { + printf("thread init fail!\n"); + return NULL; + } + } + + return net; +} + +void tic_net_get(tic_net* net, const char* url, net_get_callback callback, void* calldata) { + net_ctx *ctx = NEW(net_ctx); + memset(ctx, 0, sizeof(*ctx)); + + ctx->net = net; + + snprintf(ctx->url, URL_SIZE - 1, "%s%s", net->host, url); + ctx->state = RS_RUNNING; + + ctx->callback = callback; + ctx->calldata = calldata; + + ctx->lastruntime = 0; + + ctx->buffer = NULL; + ctx->size = 0; + + // queue transfer + ctx->curl = curl_easy_init(); + if (ctx->curl) { + curl_easy_setopt(ctx->curl, CURLOPT_URL, ctx->url); + curl_easy_setopt(ctx->curl, CURLOPT_PRIVATE, ctx); + curl_easy_setopt(ctx->curl, CURLOPT_USERAGENT, "tic-80-switch/" TIC_VERSION_STRING); + curl_easy_setopt(ctx->curl, CURLOPT_CONNECTTIMEOUT_MS, 3000L); // 3s + curl_easy_setopt(ctx->curl, CURLOPT_FOLLOWLOCATION, 1L); // CURLFOLLOW_ALL + curl_easy_setopt(ctx->curl, CURLOPT_WRITEFUNCTION, switch_write_cb); + curl_easy_setopt(ctx->curl, CURLOPT_WRITEDATA, ctx); + curl_easy_setopt(ctx->curl, CURLOPT_XFERINFOFUNCTION, switch_xferinfo_cb); + curl_easy_setopt(ctx->curl, CURLOPT_XFERINFODATA, ctx); + curl_easy_setopt(ctx->curl, CURLOPT_NOPROGRESS, 0L); + //curl_easy_setopt(ctx->curl, CURLOPT_VERBOSE, 1L); + curl_multi_add_handle(net->curl_multi, ctx->curl); + ueventSignal(&net->poll_event); + } + + // add request to list + net->requests = realloc(net->requests, sizeof(*net->requests) * (net->count + 1)); + net->requests[net->count++] = ctx; +} + +void tic_net_close(tic_net* net) { + // close thread + ueventSignal(&net->exit_event); + curl_multi_wakeup(net->curl_multi); + threadWaitForExit(&net->thread); + threadClose(&net->thread); + + curl_multi_cleanup(net->curl_multi); + curl_global_cleanup(); + + for(size_t i = 0; i < net->count; i++) { + net_ctx *ctx = net->requests[i]; + if(ctx) { + // reset buffer + if(ctx->buffer != NULL) { + free(ctx->buffer); + ctx->buffer = NULL; + ctx->size = 0; + } + free(ctx); + } + } + if(net->requests) + free(net->requests); + + if(net->close_sockets) { + socketExit(); + } + free(net); +} + +void tic_net_start(tic_net *net) { + mutexLock(&net->lock); +} + +void tic_net_end(tic_net *net) { + if(!net->requests) { + mutexUnlock(&net->lock); + return; + } + + // handle finished requests + for(size_t i = 0; i < net->count; i++) { + net_ctx *ctx = net->requests[i]; + if(ctx && ctx->state != RS_RUNNING) { + if(ctx->callback != NULL) { + net_get_data getData = { + .calldata = ctx->calldata, + .url = ctx->url, + }; + + if(ctx->state == RS_OK) { + getData.type = net_get_done; + getData.done.data = ctx->buffer; + getData.done.size = ctx->size; + } else { + getData.type = net_get_error; + getData.error.code = -1; + } + ctx->callback(&getData); + } + + // reset buffer + if(ctx->buffer != NULL) { + free(ctx->buffer); + ctx->buffer = NULL; + ctx->size = 0; + } + + // delete context + free(net->requests[i]); + net->requests[i] = NULL; + } + } + + mutexUnlock(&net->lock); +} diff --git a/src/system/nswitch/runtime.c b/src/system/nswitch/runtime.c new file mode 100644 index 000000000..d53b277ec --- /dev/null +++ b/src/system/nswitch/runtime.c @@ -0,0 +1,59 @@ +// MIT License + +// Copyright (c) 2025 Carsten Teibes + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include + +#ifdef DEBUG +static int nxlink_sock = -1; + +static void nxlink_init() { + socketInitializeDefault(); + nxlink_sock = nxlinkStdio(); + if (nxlink_sock >= 0) + printf("nxlink activated...\n"); +} + +static void nxlink_exit() { + if (nxlink_sock >= 0) { + close(nxlink_sock); + nxlink_sock = -1; + } + socketExit(); +} +#else +static void nxlink_init() {} +static void nxlink_exit() {} +#endif + +// these hooks are called by libnx + +void userAppInit() { + nxlink_init(); // enable debug log over network + romfsInit(); // can be used for engine data or a shipped game +} + +void userAppExit() { + romfsExit(); + nxlink_exit(); +} diff --git a/src/system/sdl/main.c b/src/system/sdl/main.c index bb0b97cad..1e9353859 100644 --- a/src/system/sdl/main.c +++ b/src/system/sdl/main.c @@ -29,6 +29,11 @@ #include #include +#ifdef __SWITCH__ +// from studio/studio.h +extern void gotoMenu(Studio* studio); +#endif + #if defined(__TIC_LINUX__) #include #endif @@ -57,7 +62,7 @@ #include #endif -#if defined(__TIC_ANDROID__) +#if defined(__TIC_ANDROID__) || defined(__SWITCH__) #include #endif @@ -982,11 +987,26 @@ static void processGamepad() || getAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX, +1) || getButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); +#ifdef __SWITCH__ + // nintendo layout + gamepad->a = getButton(controller, SDL_CONTROLLER_BUTTON_B); + gamepad->b = getButton(controller, SDL_CONTROLLER_BUTTON_A); + gamepad->x = getButton(controller, SDL_CONTROLLER_BUTTON_Y); + gamepad->y = getButton(controller, SDL_CONTROLLER_BUTTON_X); + + // "+" is a common way to quit homebrew, let's show the menu + if(getButton(controller, SDL_CONTROLLER_BUTTON_START)) + { + //studio_exit(platform.studio); + gotoMenu(platform.studio); + } +#else + // xbox layout gamepad->a = getButton(controller, SDL_CONTROLLER_BUTTON_A); gamepad->b = getButton(controller, SDL_CONTROLLER_BUTTON_B); gamepad->x = getButton(controller, SDL_CONTROLLER_BUTTON_X); gamepad->y = getButton(controller, SDL_CONTROLLER_BUTTON_Y); - +#endif // !TODO: We have to find a better way to handle gamepad MENU button // atm we show game menu for only Pause Menu button on XBox one controller // issue #1220 @@ -1370,6 +1390,11 @@ static const char* getAppFolder() strcat(appFolder, AppFolder); mkdir(appFolder, 0777); +#elif defined(__SWITCH__) + + strcpy(appFolder, "/switch/tic80"); + mkdir(appFolder, 0777); + #else char* path = SDL_GetPrefPath(TIC_PACKAGE, TIC_NAME); @@ -1506,7 +1531,7 @@ void tic_sys_preseed() static void loadCrtShader() { static const char VertextShader[] = -#if !defined (EMSCRIPTEN) +#if !defined (EMSCRIPTEN) && !defined(__SWITCH__) "#version 110" "\n" #endif "attribute vec3 gpu_Vertex;" "\n" @@ -1524,7 +1549,7 @@ static void loadCrtShader() ; static const char PixelShader[] = -#if !defined (EMSCRIPTEN) +#if !defined (EMSCRIPTEN) && !defined(__SWITCH__) "#version 110" "\n" #else "precision highp float;" "\n" @@ -1894,6 +1919,11 @@ static s32 start(s32 argc, char **argv, const char* folder) #if defined(__MACOSX__) SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); #endif + +#ifdef __SWITCH__ + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "1"); +#endif + int result = SDL_Init(SDL_INIT_VIDEO); if (result != 0) { diff --git a/vendor/s7/s7.c b/vendor/s7/s7.c index dcf8d8bd9..fac24a222 100644 --- a/vendor/s7/s7.c +++ b/vendor/s7/s7.c @@ -375,7 +375,7 @@ #define MS_WINDOWS 0 #endif -#if defined(_MSC_VER) || defined(__MINGW32__) || defined(S7_BAREMETALPI) || defined(S7_N3DS) +#if defined(_MSC_VER) || defined(__MINGW32__) || defined(S7_BAREMETALPI) || defined(S7_N3DS) || defined(S7_SWITCH) #define Jmp_Buf jmp_buf #define SetJmp(A, B) setjmp(A) #define LongJmp(A, B) longjmp(A, B) @@ -36582,7 +36582,7 @@ system captures the output as a string and returns it." } -#if (!MS_WINDOWS) && !defined(S7_BAREMETALPI) && !defined(S7_N3DS) +#if (!MS_WINDOWS) && !defined(S7_BAREMETALPI) && !defined(S7_N3DS) && !defined(S7_SWITCH) #include /* -------------------------------- directory->list -------------------------------- */ @@ -92351,7 +92351,7 @@ static s7_pointer memory_usage(s7_scheme *sc) s7_pointer mu_let = s7_inlet(sc, sc->nil); s7_int gc_loc = gc_protect_1(sc, mu_let); -#if !defined(S7_BAREMETALPI) && !defined(S7_N3DS) +#if !defined(S7_BAREMETALPI) && !defined(S7_N3DS) && !defined(S7_SWITCH) #if (!_WIN32) /* (!MS_WINDOWS) */ getrusage(RUSAGE_SELF, &info); ut = info.ru_utime; @@ -92365,7 +92365,7 @@ static s7_pointer memory_usage(s7_scheme *sc) #endif add_slot_unchecked_with_id(sc, mu_let, make_symbol(sc, "IO", 2), cons(sc, make_integer(sc, info.ru_inblock), make_integer(sc, info.ru_oublock))); #endif -#endif // !defined(S7_BAREMETALPI) && !defined(S7_N3DS) +#endif // !defined(S7_BAREMETALPI) && !defined(S7_N3DS) && !defined(S7_SWITCH) add_slot_unchecked_with_id(sc, mu_let, make_symbol(sc, "rootlet-size", 12), make_integer(sc, sc->rootlet_entries)); add_slot_unchecked_with_id(sc, mu_let, make_symbol(sc, "heap-size", 9),