Support for Nintendo Switch (#4491)
* WIP support for Nintendo Switch * OpenAL is usable * Remove some debug code, add manual crash when DEBUG_NXLINK is on * Remove more debug logs * Support touch, account name detection, language detection. Remove resolution settings * Stylistic changes * SFXManager: update on main thread * Add build script, remove crypto.hpp changes, disable opengl recorder on switch * make: use sudo where needed, libs=>lib, portlib=>portlibs * make: build harfbuzz with freetype * main: remove DEBUG_NXLINK * socket_address: this comment is no longer relevant * Fix indentation in SDL, remove extra debug logs from InputManager * InputManager: make log debug, not info * CMakeLists: add mbedtls include dirs on switch * main: deinitialize stuff * main_loop: fix format
This commit is contained in:
parent
d1e73f550d
commit
d9b8b7acad
@ -25,21 +25,25 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "STKRelease")
|
||||
endif()
|
||||
|
||||
option(USE_SWITCH "Build targetting switch" OFF)
|
||||
|
||||
option(SERVER_ONLY "Create a server only (i.e. no graphics or sound)" OFF)
|
||||
option(CHECK_ASSETS "Check if assets are installed in ../stk-assets" ON)
|
||||
option(USE_SYSTEM_ANGELSCRIPT "Use system angelscript instead of built-in angelscript. If you enable this option, make sure to use a compatible version." OFF)
|
||||
option(USE_SYSTEM_ENET "Use system ENet instead of the built-in version, when available." ON)
|
||||
option(USE_IPV6 "Allow create or connect to game server with IPv6 address, system enet will not be used." ON)
|
||||
CMAKE_DEPENDENT_OPTION(USE_IPV6 "Allow create or connect to game server with IPv6 address, system enet will not be used." ON
|
||||
"NOT USE_SWITCH" OFF)
|
||||
option(USE_SYSTEM_WIIUSE "Use system WiiUse instead of the built-in version, when available." OFF)
|
||||
option(USE_SQLITE3 "Use sqlite to manage server stats and ban list." ON)
|
||||
|
||||
option(USE_CRYPTO_OPENSSL "Use OpenSSL instead of MbedTLS for cryptography in STK." ON)
|
||||
CMAKE_DEPENDENT_OPTION(USE_CRYPTO_OPENSSL "Use OpenSSL instead of MbedTLS for cryptography in STK." ON
|
||||
"NOT USE_SWITCH" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(BUILD_RECORDER "Build opengl recorder" ON
|
||||
"NOT SERVER_ONLY;NOT APPLE" OFF)
|
||||
"NOT SERVER_ONLY;NOT APPLE;NOT USE_SWITCH" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(USE_SYSTEM_SQUISH "Use system Squish library instead of the built-in version, when available." ON
|
||||
"NOT SERVER_ONLY" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(USE_WIIUSE "Support for wiimote input devices" ON
|
||||
"NOT SERVER_ONLY;NOT CYGWIN" OFF)
|
||||
"NOT SERVER_ONLY;NOT CYGWIN;NOT USE_SWITCH" OFF)
|
||||
|
||||
if(APPLE AND NOT IOS)
|
||||
if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm")
|
||||
@ -185,7 +189,7 @@ endif()
|
||||
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/bullet")
|
||||
include_directories(BEFORE "${PROJECT_SOURCE_DIR}/lib/bullet/src")
|
||||
|
||||
if(WIN32 OR CMAKE_SYSTEM_NAME MATCHES "BSD" OR CMAKE_SYSTEM_NAME MATCHES "DragonFly")
|
||||
if(WIN32 OR CMAKE_SYSTEM_NAME MATCHES "BSD" OR CMAKE_SYSTEM_NAME MATCHES "DragonFly" OR USE_SWITCH)
|
||||
set(LIBRESOLV_LIBRARY)
|
||||
elseif (HAIKU)
|
||||
find_library(LIBRESOLV_LIBRARY NAMES network socket)
|
||||
@ -370,7 +374,13 @@ if (NOT SERVER_ONLY)
|
||||
SET(SHEENBIDI_LIBRARY sheenbidi)
|
||||
|
||||
# Freetype
|
||||
find_package(Freetype)
|
||||
if (USE_SWITCH)
|
||||
SET(FREETYPE_INCLUDE_DIRS /opt/devkitpro/portlibs/switch/include/freetype2)
|
||||
SET(FREETYPE_LIBRARY freetype)
|
||||
SET(FREETYPE_FOUND YES)
|
||||
else()
|
||||
find_package(Freetype)
|
||||
endif()
|
||||
if(FREETYPE_FOUND)
|
||||
include_directories(${FREETYPE_INCLUDE_DIRS})
|
||||
else()
|
||||
@ -605,6 +615,29 @@ target_link_libraries(supertuxkart
|
||||
${MCPP_LIBRARY}
|
||||
)
|
||||
|
||||
if (USE_SWITCH)
|
||||
include(cmake/FindEGL.cmake)
|
||||
find_library(NX_LIBRARY NAMES nx libnx REQUIRED)
|
||||
find_library(DRM_LIBRARY NAMES drm_nouveau libdrm_nouveau REQUIRED)
|
||||
find_library(GLAPI_LIBRARY NAMES glapi libglapi REQUIRED)
|
||||
target_link_libraries(supertuxkart
|
||||
${NX_LIBRARY}
|
||||
-lz
|
||||
-lfreetype
|
||||
-lSDL2
|
||||
-lpng
|
||||
-lbz2
|
||||
-lharfbuzz
|
||||
-logg
|
||||
-lvorbis
|
||||
-lopenal
|
||||
${EGL_LIBRARY}
|
||||
${DRM_LIBRARY}
|
||||
${GLAPI_LIBRARY}
|
||||
${NX_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
|
||||
if (USE_SQLITE3)
|
||||
target_link_libraries(supertuxkart ${SQLITE3_LIBRARY})
|
||||
endif()
|
||||
|
BIN
data/supertuxkart_256.jpg
Normal file
BIN
data/supertuxkart_256.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
@ -10,8 +10,12 @@ check_function_exists("fcntl" HAS_FCNTL)
|
||||
check_function_exists("poll" HAS_POLL)
|
||||
check_function_exists("getaddrinfo" HAS_GETADDRINFO)
|
||||
check_function_exists("getnameinfo" HAS_GETNAMEINFO)
|
||||
check_function_exists("gethostbyname_r" HAS_GETHOSTBYNAME_R)
|
||||
check_function_exists("gethostbyaddr_r" HAS_GETHOSTBYADDR_R)
|
||||
# Switch doesn't support these and cmake freaks out checking them
|
||||
# because they exist in headers but not libs
|
||||
if (NOT USE_SWITCH)
|
||||
check_function_exists("gethostbyname_r" HAS_GETHOSTBYNAME_R)
|
||||
check_function_exists("gethostbyaddr_r" HAS_GETHOSTBYADDR_R)
|
||||
endif()
|
||||
check_function_exists("inet_pton" HAS_INET_PTON)
|
||||
check_function_exists("inet_ntop" HAS_INET_NTOP)
|
||||
check_struct_has_member("struct msghdr" "msg_flags" "sys/types.h;sys/socket.h" HAS_MSGHDR_FLAGS)
|
||||
|
@ -587,7 +587,7 @@ bool CFileSystem::changeWorkingDirectoryTo(const io::path& newDirectory)
|
||||
|
||||
io::path CFileSystem::getAbsolutePath(const io::path& filename) const
|
||||
{
|
||||
#if defined(_IRR_WINDOWS_CE_PLATFORM_)
|
||||
#if defined(_IRR_WINDOWS_CE_PLATFORM_) || defined(__SWITCH__)
|
||||
return filename;
|
||||
#elif defined(_IRR_WINDOWS_API_)
|
||||
wchar_t *p=0;
|
||||
@ -958,6 +958,12 @@ bool CFileSystem::existFile(const io::path& filename) const
|
||||
#if defined(_IRR_WINDOWS_API_)
|
||||
return (_waccess(StringUtils::utf8ToWide(filename.c_str()).c_str(), F_OK) != -1);
|
||||
#else
|
||||
#ifdef __SWITCH__
|
||||
if(filename.size() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return (access(filename.c_str(), F_OK) != -1);
|
||||
#endif
|
||||
#else
|
||||
|
@ -68,6 +68,9 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters& param)
|
||||
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
|
||||
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
|
||||
|
||||
// Switch SDL disables this hint by default: https://github.com/devkitPro/SDL/pull/55#issuecomment-633775255
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "1");
|
||||
|
||||
#ifndef MOBILE_STK
|
||||
// Prevent fullscreen minimizes when losing focus
|
||||
if (CreationParams.Fullscreen)
|
||||
@ -100,8 +103,11 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters& param)
|
||||
{
|
||||
SDL_VERSION(&Info.version);
|
||||
|
||||
// Switch doesn't support GetWindowWMInfo
|
||||
#ifndef __SWITCH__
|
||||
if (!SDL_GetWindowWMInfo(Window, &Info))
|
||||
return;
|
||||
#endif
|
||||
#ifdef IOS_STK
|
||||
init_objc(&Info, &TopPadding, &BottomPadding, &LeftPadding, &RightPadding);
|
||||
#endif
|
||||
@ -127,7 +133,7 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters& param)
|
||||
else
|
||||
return;
|
||||
}
|
||||
#ifndef ANDROID
|
||||
#if !defined(ANDROID) && !defined(__SWITCH__)
|
||||
else if (!GUIEngine::isNoGraphics())
|
||||
{
|
||||
// Get highdpi native scale using renderer so it will work with any
|
||||
|
@ -95,7 +95,13 @@ CMountPointReader::CMountPointReader(IFileSystem * parent, const io::path& basen
|
||||
|
||||
const io::path& work = Parent->getWorkingDirectory();
|
||||
|
||||
Parent->changeWorkingDirectoryTo(basename);
|
||||
if(!Parent->changeWorkingDirectoryTo(basename))
|
||||
{
|
||||
#ifdef __SWITCH__
|
||||
printf("Failed changing directory to %s\n", basename.c_str());
|
||||
perror("Why couldn't we change working directory?");
|
||||
#endif
|
||||
}
|
||||
buildDirectory();
|
||||
Parent->changeWorkingDirectoryTo(work);
|
||||
|
||||
@ -119,6 +125,19 @@ void CMountPointReader::buildDirectory()
|
||||
for (u32 i=0; i < size; ++i)
|
||||
{
|
||||
io::path full = list->getFullFileName(i);
|
||||
#ifdef __SWITCH__
|
||||
// Real hardware gets sdmc: into the path somehow
|
||||
auto sdmc = "sdmc:";
|
||||
auto romfs = "romfs:";
|
||||
if (full.find(sdmc, 0) == 0)
|
||||
{
|
||||
full = full.subString(5, full.size() - 5);
|
||||
}
|
||||
else if (full.find(romfs, 0) == 0)
|
||||
{
|
||||
full = full.subString(6, full.size() - 6);
|
||||
}
|
||||
#endif
|
||||
full = full.subString(Path.size(), full.size() - Path.size());
|
||||
|
||||
if (full == "")
|
||||
|
@ -54,6 +54,9 @@
|
||||
#endif
|
||||
|
||||
#include "sys/types.h"
|
||||
#ifdef __SWITCH__
|
||||
#define readlink(path,buf,length) 0
|
||||
#endif
|
||||
#include "sys/stat.h" /* For stat() */
|
||||
#if ! defined( S_ISREG)
|
||||
#define S_ISREG( mode) (mode & S_IFREG)
|
||||
|
@ -94,7 +94,9 @@ SFXManager::SFXManager()
|
||||
{
|
||||
// The thread is created even if there atm sfx are disabled
|
||||
// (since the user might enable it later).
|
||||
#ifndef __SWITCH__
|
||||
m_thread = std::thread(std::bind(mainLoop, this));
|
||||
#endif
|
||||
setMasterSFXVolume( UserConfigParams::m_sfx_volume );
|
||||
m_sfx_commands.lock();
|
||||
m_sfx_commands.getData().clear();
|
||||
@ -108,7 +110,7 @@ SFXManager::SFXManager()
|
||||
*/
|
||||
SFXManager::~SFXManager()
|
||||
{
|
||||
#ifdef ENABLE_SOUND
|
||||
#if defined(ENABLE_SOUND) && !defined(__SWITCH__)
|
||||
if (UserConfigParams::m_enable_sound)
|
||||
{
|
||||
m_thread.join();
|
||||
@ -345,14 +347,25 @@ void SFXManager::mainLoop(void *obj)
|
||||
if (!UserConfigParams::m_enable_sound)
|
||||
return;
|
||||
|
||||
#ifndef __SWITCH__
|
||||
VS::setThreadName("SFXManager");
|
||||
#endif
|
||||
SFXManager *me = (SFXManager*)obj;
|
||||
|
||||
std::unique_lock<std::mutex> ul = me->m_sfx_commands.acquireMutex();
|
||||
|
||||
#ifdef __SWITCH__
|
||||
int iterCount = 0;
|
||||
#endif
|
||||
// Wait till we have an empty sfx in the queue
|
||||
while (me->m_sfx_commands.getData().empty() ||
|
||||
me->m_sfx_commands.getData().front()->m_command!=SFX_EXIT)
|
||||
while (
|
||||
#ifdef __SWITCH__
|
||||
// Don't spend too much time working on audio
|
||||
++iterCount != 30 && !me->m_sfx_commands.getData().empty()
|
||||
#else
|
||||
me->m_sfx_commands.getData().empty() || me->m_sfx_commands.getData().front()->m_command!=SFX_EXIT
|
||||
#endif
|
||||
)
|
||||
{
|
||||
PROFILER_PUSH_CPU_MARKER("Wait", 255, 0, 0);
|
||||
bool empty = me->m_sfx_commands.getData().empty();
|
||||
@ -360,18 +373,24 @@ void SFXManager::mainLoop(void *obj)
|
||||
// Wait in cond_wait for a request to arrive. The 'while' is necessary
|
||||
// since "spurious wakeups from the pthread_cond_wait ... may occur"
|
||||
// (pthread_cond_wait man page)!
|
||||
#ifndef __SWITCH__
|
||||
while (empty)
|
||||
{
|
||||
me->m_condition_variable.wait(ul);
|
||||
empty = me->m_sfx_commands.getData().empty();
|
||||
}
|
||||
#endif
|
||||
SFXCommand *current = me->m_sfx_commands.getData().front();
|
||||
me->m_sfx_commands.getData().erase(me->m_sfx_commands.getData().begin());
|
||||
|
||||
if (current->m_command == SFX_EXIT)
|
||||
{
|
||||
delete current;
|
||||
#ifdef __SWITCH__
|
||||
return;
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
ul.unlock();
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
@ -460,13 +479,15 @@ void SFXManager::mainLoop(void *obj)
|
||||
// need to keep the user waiting for STK to exit.
|
||||
me->setCanBeDeleted();
|
||||
|
||||
#ifndef __SWITCH__
|
||||
// Clean up memory to avoid leak detection
|
||||
while(!me->m_sfx_commands.getData().empty())
|
||||
{
|
||||
delete me->m_sfx_commands.getData().front();
|
||||
me->m_sfx_commands.getData().erase(me->m_sfx_commands.getData().begin());
|
||||
}
|
||||
#endif
|
||||
#endif // __SWITCH__
|
||||
#endif // ENABLE_SOUD
|
||||
return;
|
||||
} // mainLoop
|
||||
|
||||
@ -790,6 +811,10 @@ void SFXManager::update()
|
||||
queue(SFX_UPDATE, (SFXBase*)NULL);
|
||||
// Wake up the sfx thread to handle all queued up audio commands.
|
||||
m_condition_variable.notify_one();
|
||||
|
||||
#ifdef __SWITCH__
|
||||
mainLoop(this);
|
||||
#endif
|
||||
#endif
|
||||
} // update
|
||||
|
||||
|
@ -221,8 +221,10 @@ private:
|
||||
/** Master gain value, taken from the user config value. */
|
||||
float m_master_gain;
|
||||
|
||||
#ifndef __SWITCH__
|
||||
/** Thread id of the thread running in this object. */
|
||||
std::thread m_thread;
|
||||
#endif
|
||||
|
||||
uint64_t m_last_update_time;
|
||||
|
||||
|
@ -38,7 +38,9 @@
|
||||
#include <string>
|
||||
#ifndef WIN32
|
||||
# include <sys/param.h> // To get BSD macro
|
||||
# include <sys/utsname.h>
|
||||
# ifndef __SWITCH__
|
||||
# include <sys/utsname.h>
|
||||
# endif
|
||||
#endif
|
||||
#if defined(__APPLE__) || defined(BSD)
|
||||
# include <sys/sysctl.h>
|
||||
|
@ -161,9 +161,17 @@ IrrDriver::IrrDriver()
|
||||
m_resolution_changing = RES_CHANGE_NONE;
|
||||
|
||||
struct irr::SIrrlichtCreationParameters p;
|
||||
#ifdef __SWITCH__
|
||||
// Switch doesn't like multiple window create/closes, so we hardcode it
|
||||
// Aforementioned chicken and egg problem isn't an issue because switch's SDL only supports two resolutions
|
||||
p.DriverType = video::EDT_OPENGL;
|
||||
p.Bits = 24U;
|
||||
p.WindowSize = core::dimension2d<u32>(1280,720);
|
||||
#else
|
||||
p.DriverType = video::EDT_NULL;
|
||||
p.WindowSize = core::dimension2d<u32>(640,480);
|
||||
p.Bits = 16U;
|
||||
p.WindowSize = core::dimension2d<u32>(640,480);
|
||||
#endif
|
||||
p.Fullscreen = false;
|
||||
p.SwapInterval = 0;
|
||||
p.EventReceiver = NULL;
|
||||
@ -366,6 +374,7 @@ void IrrDriver::initDevice()
|
||||
{
|
||||
SIrrlichtCreationParameters params;
|
||||
|
||||
#ifndef __SWITCH__
|
||||
// If --no-graphics option was used, the null device can still be used.
|
||||
if (!GUIEngine::isNoGraphics())
|
||||
{
|
||||
@ -452,7 +461,11 @@ void IrrDriver::initDevice()
|
||||
UserConfigParams::m_gamepad_visualisation);
|
||||
|
||||
// Try 32 and, upon failure, 24 then 16 bit per pixels
|
||||
#ifdef __SWITCH__
|
||||
int bits=24;
|
||||
#else
|
||||
for (int bits=32; bits>15; bits -=8)
|
||||
#endif
|
||||
{
|
||||
if(UserConfigParams::logMisc())
|
||||
Log::verbose("irr_driver", "Trying to create device with "
|
||||
@ -509,8 +522,12 @@ void IrrDriver::initDevice()
|
||||
*/
|
||||
m_device = createDeviceEx(params);
|
||||
|
||||
#ifdef __SWITCH__
|
||||
assert(m_device != NULL);
|
||||
#else
|
||||
if(m_device)
|
||||
break;
|
||||
#endif
|
||||
} // for bits=32, 24, 16
|
||||
|
||||
|
||||
@ -541,6 +558,7 @@ void IrrDriver::initDevice()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // __SWITCH__
|
||||
|
||||
if(!m_device)
|
||||
{
|
||||
|
@ -83,6 +83,7 @@ using GUIEngine::EVENT_BLOCK;
|
||||
InputManager::InputManager() : m_mode(BOOTSTRAP),
|
||||
m_mouse_val_x(-1), m_mouse_val_y(-1)
|
||||
{
|
||||
Log::debug("InputManager", "Initialising InputManager!");
|
||||
m_device_manager = new DeviceManager();
|
||||
m_device_manager->initialize();
|
||||
|
||||
@ -93,6 +94,10 @@ InputManager::InputManager() : m_mode(BOOTSTRAP),
|
||||
Log::error("InputManager", "Failed to init SDL game controller: %s",
|
||||
SDL_GetError());
|
||||
}
|
||||
#ifdef __SWITCH__
|
||||
// Otherwise we report 'B' as 'A' (like Xbox controller)
|
||||
SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -119,7 +124,7 @@ void InputManager::addJoystick()
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Log::error("SDLController", "%s", e.what());
|
||||
Log::error("SDLController", "Error in addJoystick %s", e.what());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -189,7 +194,7 @@ void InputManager::handleJoystick(SDL_Event& event)
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Log::error("SDLController", "%s", e.what());
|
||||
Log::error("SDLController", "Error in handleJoystick() %s", e.what());
|
||||
}
|
||||
} // handleJoystick
|
||||
#endif
|
||||
|
@ -175,8 +175,14 @@ FileManager::FileManager()
|
||||
#ifdef __APPLE__
|
||||
else if( macSetBundlePathIfRelevant( root_dir ) ) { root_dir = root_dir + "data/"; }
|
||||
#endif
|
||||
else if(fileExists("data/", version))
|
||||
root_dir = "data/" ;
|
||||
#ifdef __SWITCH__
|
||||
else if(fileExists("sdmc:/stk-data/", version))
|
||||
root_dir = "sdmc:/stk-data/";
|
||||
else if(fileExists("romfs:/data/", version))
|
||||
root_dir = "romfs:/data/";
|
||||
#endif
|
||||
else if(fileExists("./data/", version))
|
||||
root_dir = "./data/" ;
|
||||
else if(fileExists("../data/", version))
|
||||
root_dir = "../data/" ;
|
||||
else if(fileExists("../../data/", version))
|
||||
|
92
src/main.cpp
92
src/main.cpp
@ -166,6 +166,25 @@
|
||||
#include <jni.h>
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
extern "C" {
|
||||
#include <sys/iosupport.h>
|
||||
#include <switch/kernel/svc.h>
|
||||
#include <switch/runtime/nxlink.h>
|
||||
#include <switch/runtime/diag.h>
|
||||
#include <switch/services/ssl.h>
|
||||
#define Event libnx_Event
|
||||
#include <switch/services/set.h>
|
||||
#include <switch/runtime/pad.h>
|
||||
#undef Event
|
||||
#include <switch/runtime/devices/socket.h>
|
||||
}
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
@ -1793,6 +1812,9 @@ void initRest()
|
||||
GUIEngine::setSkin(NULL);
|
||||
|
||||
input_manager = new InputManager();
|
||||
#ifdef __SWITCH__
|
||||
input_manager->addJoystick();
|
||||
#endif
|
||||
// Get into menu mode initially.
|
||||
input_manager->setMode(InputManager::MENU);
|
||||
// Input manager set first so it recieves SDL joystick event
|
||||
@ -1985,6 +2007,33 @@ void main_abort()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
ssize_t dotab_stdout_fn(struct _reent *r,void *fd,const char *ptr, size_t len)
|
||||
{
|
||||
svcOutputDebugString(ptr, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
void debugLoop()
|
||||
{
|
||||
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||
|
||||
PadState pad;
|
||||
padInitializeDefault(&pad);
|
||||
|
||||
while(1)
|
||||
{
|
||||
padUpdate(&pad);
|
||||
uint64_t down = padGetButtons(&pad);
|
||||
if(down & HidNpadButton_Up)
|
||||
{
|
||||
diagAbortWithResult(0);
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
#if defined(ANDROID)
|
||||
int android_main(int argc, char *argv[])
|
||||
@ -1994,6 +2043,43 @@ int ios_main(int argc, char *argv[])
|
||||
int main(int argc, char *argv[])
|
||||
#endif
|
||||
{
|
||||
#ifdef __SWITCH__
|
||||
constexpr devoptab_t dotab_stdout = {
|
||||
.name = "con",
|
||||
.write_r = dotab_stdout_fn,
|
||||
};
|
||||
|
||||
devoptab_list[STD_OUT] = &dotab_stdout;
|
||||
devoptab_list[STD_ERR] = &dotab_stdout;
|
||||
|
||||
// Up number of maximum concurrent sockets, otherwise we can fail while loading with nxlink
|
||||
const SocketInitConfig socketConfig = {
|
||||
.bsdsockets_version = 1,
|
||||
.tcp_tx_buf_size = 0x8000,
|
||||
.tcp_rx_buf_size = 0x10000,
|
||||
.tcp_tx_buf_max_size = 0x40000,
|
||||
.tcp_rx_buf_max_size = 0x40000,
|
||||
|
||||
.udp_tx_buf_size = 0x2400,
|
||||
.udp_rx_buf_size = 0xA500,
|
||||
|
||||
.sb_efficiency = 4,
|
||||
.num_bsd_sessions = 16,
|
||||
.bsd_service_type = BsdServiceType_User,
|
||||
};
|
||||
|
||||
// Initialize socket, needed for networking and nxlink stdio
|
||||
socketInitialize(&socketConfig);
|
||||
|
||||
// Initialize settings, needed to grab language
|
||||
setInitialize();
|
||||
// Crashes on Reujinx
|
||||
#ifdef DEBUG_NXLINK
|
||||
nxlinkStdio();
|
||||
std::thread debugThread = std::thread(debugLoop);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
clearGlobalVariables();
|
||||
CommandLine::init(argc, argv);
|
||||
|
||||
@ -2458,6 +2544,12 @@ int main(int argc, char *argv[])
|
||||
|
||||
delete file_manager;
|
||||
|
||||
#ifdef __SWITCH__
|
||||
// De-initialize stuff!
|
||||
setExit();
|
||||
socketExit();
|
||||
#endif
|
||||
|
||||
#ifdef IOS_STK
|
||||
// App store may not like this, but this can happen if player uses keyboard to quit stk
|
||||
exit(0);
|
||||
|
@ -61,6 +61,22 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
extern "C" {
|
||||
#define Event libnx_Event
|
||||
#define u64 uint64_t
|
||||
#define u32 uint32_t
|
||||
#define s64 int64_t
|
||||
#define s32 int32_t
|
||||
#include <switch/services/applet.h>
|
||||
#undef Event
|
||||
#undef u64
|
||||
#undef u32
|
||||
#undef s64
|
||||
#undef s32
|
||||
}
|
||||
#endif
|
||||
|
||||
MainLoop* main_loop = 0;
|
||||
|
||||
#ifdef WIN32
|
||||
@ -427,6 +443,14 @@ void MainLoop::run()
|
||||
|
||||
while (!m_abort)
|
||||
{
|
||||
#ifdef __SWITCH__
|
||||
// This feeds us messages (like when the Switch sleeps or requests an exit)
|
||||
m_abort = !appletMainLoop();
|
||||
if (m_abort)
|
||||
{
|
||||
Log::info("MainLoop", "Aborting main loop because Switch told us to!");
|
||||
}
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
if (parent != 0 && parent != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -446,7 +470,7 @@ void MainLoop::run()
|
||||
m_request_abort = true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#elif !defined( __SWITCH__ )
|
||||
// POSIX equivalent
|
||||
if (m_parent_pid != 0 && getppid() != (int)m_parent_pid)
|
||||
{
|
||||
|
@ -37,7 +37,9 @@
|
||||
#else
|
||||
# include <arpa/inet.h>
|
||||
# include <errno.h>
|
||||
#ifndef __SWITCH__
|
||||
# include <ifaddrs.h>
|
||||
#endif
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
|
@ -49,12 +49,16 @@
|
||||
# pragma comment(lib, "dnsapi.lib")
|
||||
#endif
|
||||
#else
|
||||
#ifndef __SWITCH__
|
||||
# include <arpa/nameser.h>
|
||||
# include <arpa/nameser_compat.h>
|
||||
#endif
|
||||
# include <netdb.h>
|
||||
# include <netinet/in.h>
|
||||
#ifndef __SWITCH__
|
||||
# include <resolv.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <jni.h>
|
||||
@ -567,7 +571,9 @@ void NetworkConfig::fillStunList(std::vector<std::pair<std::string, int> >* l,
|
||||
env->DeleteLocalRef(class_native_activity);
|
||||
env->DeleteLocalRef(native_activity);
|
||||
g_list = NULL;
|
||||
|
||||
#elif defined(__SWITCH__)
|
||||
// TODO: Unclear how to get SRV records from libnx...
|
||||
return;
|
||||
#elif !defined(__CYGWIN__)
|
||||
#define SRV_WEIGHT (RRFIXEDSZ+2)
|
||||
#define SRV_PORT (RRFIXEDSZ+4)
|
||||
|
@ -55,12 +55,16 @@
|
||||
# pragma comment(lib, "dnsapi.lib")
|
||||
#endif
|
||||
#else
|
||||
#ifndef __SWITCH__
|
||||
# include <arpa/nameser.h>
|
||||
# include <arpa/nameser_compat.h>
|
||||
#endif
|
||||
# include <netdb.h>
|
||||
# include <netinet/in.h>
|
||||
#ifndef __SWITCH__
|
||||
# include <resolv.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
// ============================================================================
|
||||
@ -405,7 +409,7 @@ int ConnectToServer::interceptCallback(ENetHost* host, ENetEvent* event)
|
||||
host->receivedData[8] == '-' && host->receivedData[9] == 's' &&
|
||||
host->receivedData[10] == 't' && host->receivedData[11] == 'k')
|
||||
{
|
||||
#ifdef ENABLE_IPV6
|
||||
#if defined(ENABLE_IPV6) || defined(__SWITCH__)
|
||||
if (enet_ip_not_equal(host->receivedAddress.host, m_server_address.host) ||
|
||||
#else
|
||||
if (host->receivedAddress.host != m_server_address.host ||
|
||||
@ -462,7 +466,8 @@ bool ConnectToServer::tryConnect(int timeout, int retry, bool another_port,
|
||||
Log::info("ConnectToServer", "Trying connecting to %s from port %d, "
|
||||
"retry remain: %d", connecting_address.c_str(),
|
||||
nw->getPort(), m_retry_count);
|
||||
while (enet_host_service(nw->getENetHost(), &event, timeout) != 0)
|
||||
int res;
|
||||
while ((res = enet_host_service(nw->getENetHost(), &event, timeout)) != 0)
|
||||
{
|
||||
if (event.type == ENET_EVENT_TYPE_CONNECT)
|
||||
{
|
||||
@ -684,6 +689,8 @@ bool ConnectToServer::detectPort()
|
||||
if (port_from_dns != 0)
|
||||
break;
|
||||
}
|
||||
#elif defined(__SWITCH__)
|
||||
// TODO: Unclear how to use libnx in this case
|
||||
#elif !defined(__CYGWIN__)
|
||||
unsigned char response[512] = {};
|
||||
const std::string& utf8name = StringUtils::wideToUtf8(m_server->getName());
|
||||
|
@ -44,7 +44,9 @@
|
||||
# define _WIN32_WINNT 0x600
|
||||
# include <iphlpapi.h>
|
||||
#else
|
||||
#ifndef __SWITCH__
|
||||
# include <ifaddrs.h>
|
||||
#endif
|
||||
# include <net/if.h>
|
||||
#endif
|
||||
|
||||
@ -388,7 +390,9 @@ void ServersManager::addAllBroadcastAddresses(const SocketAddress &a, int len,
|
||||
std::vector<SocketAddress> ServersManager::getBroadcastAddresses(bool ipv6)
|
||||
{
|
||||
std::vector<SocketAddress> result;
|
||||
#ifndef WIN32
|
||||
#ifdef __SWITCH__
|
||||
return result;
|
||||
#elif !defined(WIN32)
|
||||
struct ifaddrs *addresses, *p;
|
||||
|
||||
if (getifaddrs(&addresses) == -1)
|
||||
|
@ -26,7 +26,21 @@
|
||||
# define _WIN32_WINNT 0x600
|
||||
# include <iphlpapi.h>
|
||||
#else
|
||||
#ifndef __SWITCH__
|
||||
# include <ifaddrs.h>
|
||||
#else
|
||||
extern "C" {
|
||||
#define u64 uint64_t
|
||||
#define u32 uint32_t
|
||||
#define s64 int64_t
|
||||
#define s32 int32_t
|
||||
#include <switch/services/nifm.h>
|
||||
#undef u64
|
||||
#undef u32
|
||||
#undef s32
|
||||
#undef s64
|
||||
}
|
||||
#endif
|
||||
# include <sys/ioctl.h>
|
||||
# include <net/if.h>
|
||||
# include <string.h>
|
||||
@ -87,7 +101,11 @@ SocketAddress::SocketAddress(const ENetAddress& ea)
|
||||
}
|
||||
#else
|
||||
m_family = AF_INET;
|
||||
#ifdef __SWITCH__
|
||||
setIP(htonl(ea.host.p0));
|
||||
#else
|
||||
setIP(htonl(ea.host));
|
||||
#endif
|
||||
setPort(ea.port);
|
||||
#endif
|
||||
} // SocketAddress(const ENetAddress&)
|
||||
@ -136,6 +154,7 @@ void SocketAddress::init(const std::string& str, uint16_t port_number,
|
||||
else
|
||||
port_str = StringUtils::toString(port_number);
|
||||
}
|
||||
#ifdef ENABLE_IPV6
|
||||
else if (str[0] == '[')
|
||||
{
|
||||
// Handle [IPv6 address]:port format
|
||||
@ -151,6 +170,14 @@ void SocketAddress::init(const std::string& str, uint16_t port_number,
|
||||
else
|
||||
port_str = StringUtils::toString(port_number);
|
||||
}
|
||||
#else
|
||||
// Ignore ipv6, otherwise we end up querying for them which can be slow
|
||||
else if (colon_pos != std::string::npos)
|
||||
{
|
||||
Log::debug("SocketAddress", "Ignoring ipv6 address: %s", str.c_str());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
addr_str = str;
|
||||
@ -302,7 +329,17 @@ bool SocketAddress::isPublicAddressLocalhost() const
|
||||
return false;
|
||||
if (isLoopback())
|
||||
return true;
|
||||
#ifndef WIN32
|
||||
#ifdef __SWITCH__
|
||||
if (m_family == AF_INET)
|
||||
{
|
||||
uint32_t currentIp = 0;
|
||||
nifmGetCurrentIpAddress(¤tIp);
|
||||
// Unsure how Result works so this is the best I have
|
||||
if(currentIp)
|
||||
return htonl(currentIp) == getIP();
|
||||
}
|
||||
return false;
|
||||
#elif !defined(WIN32)
|
||||
struct ifaddrs *addresses, *p;
|
||||
|
||||
if (getifaddrs(&addresses) == -1)
|
||||
@ -715,7 +752,7 @@ ENetAddress SocketAddress::toENetAddress() const
|
||||
{
|
||||
ENetAddress ea = {};
|
||||
uint32_t ip = getIP();
|
||||
#ifdef ENABLE_IPV6
|
||||
#if defined(ENABLE_IPV6) || defined(__SWITCH__)
|
||||
if (isIPv6Socket())
|
||||
{
|
||||
struct sockaddr_in6* in6 = (struct sockaddr_in6*)m_sockaddr.data();
|
||||
|
@ -1008,7 +1008,7 @@ void STKHost::mainLoop(ProcessType pt)
|
||||
// Enet will reuse a disconnected peer so we check here to avoid
|
||||
// sending to wrong peer
|
||||
if (peer->state != ENET_PEER_STATE_CONNECTED ||
|
||||
#ifdef ENABLE_IPV6
|
||||
#if defined(ENABLE_IPV6) || defined(__SWITCH__)
|
||||
(enet_ip_not_equal(ea_peer_now.host, ea.host) &&
|
||||
ea_peer_now.port != ea.port))
|
||||
#else
|
||||
|
@ -34,7 +34,9 @@
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#ifndef __SWITCH__
|
||||
#include <err.h>
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -36,6 +36,20 @@
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
#ifdef __SWITCH__
|
||||
extern "C" {
|
||||
#define u64 uint64_t
|
||||
#define u32 uint32_t
|
||||
#define s64 int64_t
|
||||
#define s32 int32_t
|
||||
#include <switch/services/acc.h>
|
||||
#undef u64
|
||||
#undef u32
|
||||
#undef s64
|
||||
#undef s32
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace GUIEngine;
|
||||
using namespace Online;
|
||||
using namespace irr;
|
||||
@ -100,6 +114,22 @@ void RegisterScreen::init()
|
||||
DWORD length = GetEnvironmentVariable(L"USERNAME", env.data(), 32767);
|
||||
if (length != 0)
|
||||
username = env.data();
|
||||
#elif defined(__SWITCH__)
|
||||
AccountUid uid;
|
||||
// It's possible the user is using an app that doesn't need a user selection
|
||||
// We try the last opened user as well
|
||||
if(!accountInitialize(AccountServiceType_Application))
|
||||
if(!accountGetPreselectedUser(&uid) || !accountGetLastOpenedUser(&uid))
|
||||
{
|
||||
AccountProfile profile;
|
||||
if(!accountGetProfile(&profile, uid))
|
||||
{
|
||||
AccountProfileBase profileBase;
|
||||
if(!accountProfileGet(&profile, NULL, &profileBase))
|
||||
username = profileBase.nickname;
|
||||
accountProfileClose(&profile);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (getenv("USER") != NULL) // Linux, Macs
|
||||
username = getenv("USER");
|
||||
|
@ -359,7 +359,7 @@ void OptionsScreenVideo::init()
|
||||
}
|
||||
} // next found resolution
|
||||
|
||||
#ifndef MOBILE_STK
|
||||
#if !defined(MOBILE_STK) && !defined(__SWITCH__)
|
||||
// Add default resolutions that were not found by irrlicht
|
||||
if (!found_1024_768)
|
||||
{
|
||||
@ -447,7 +447,7 @@ void OptionsScreenVideo::init()
|
||||
if (getWidget<SpinnerWidget>("scale_rtts")->isActivated())
|
||||
getWidget<SpinnerWidget>("scale_rtts")->setActive(!in_game);
|
||||
|
||||
#if defined(MOBILE_STK)
|
||||
#if defined(MOBILE_STK) || defined(__SWITCH__)
|
||||
applyBtn->setVisible(false);
|
||||
full->setVisible(false);
|
||||
getWidget<LabelWidget>("fullscreenText")->setVisible(false);
|
||||
|
@ -49,6 +49,20 @@
|
||||
#include "SDL_locale.h"
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
extern "C" {
|
||||
#define u64 uint64_t
|
||||
#define u32 uint32_t
|
||||
#define s64 int64_t
|
||||
#define s32 int32_t
|
||||
#include <switch/services/set.h>
|
||||
#undef u64
|
||||
#undef u32
|
||||
#undef s64
|
||||
#undef s32
|
||||
}
|
||||
#endif
|
||||
|
||||
// set to 1 to debug i18n
|
||||
#define TRANSLATE_VERBOSE 0
|
||||
// Define TEST_BIDI to force right-to-left style for all languages
|
||||
@ -353,7 +367,14 @@ Translations::Translations() //: m_dictionary_manager("UTF-16")
|
||||
language = p_lang;
|
||||
else
|
||||
{
|
||||
#ifdef MOBILE_STK
|
||||
#ifdef __SWITCH__
|
||||
uint64_t languageStr = 0;
|
||||
if(!setGetLanguageCode(&languageStr))
|
||||
{
|
||||
language = (char*)&languageStr;
|
||||
language = StringUtils::findAndReplace(language, "-", "_");
|
||||
}
|
||||
#elif defined(MOBILE_STK)
|
||||
SDL_Locale* locale = SDL_GetPreferredLocales();
|
||||
if (locale)
|
||||
{
|
||||
|
24
switch/decode.js
Normal file
24
switch/decode.js
Normal file
@ -0,0 +1,24 @@
|
||||
const child_process = require("child_process");
|
||||
const fs = require("fs");
|
||||
|
||||
const file = fs.readFileSync(process.argv[2], "utf8");
|
||||
console.log(
|
||||
file.replace(
|
||||
/ \(supertuxkart \+ (0x[0-9a-f]{1,16})\)/g,
|
||||
(existing, offset) =>
|
||||
existing +
|
||||
" => " +
|
||||
child_process
|
||||
.execFileSync("aarch64-none-elf-addr2line", [
|
||||
"-e",
|
||||
"./bin/supertuxkart",
|
||||
"-f",
|
||||
"-p",
|
||||
"-C",
|
||||
"-a",
|
||||
offset,
|
||||
])
|
||||
.toString("utf8")
|
||||
.trim()
|
||||
)
|
||||
);
|
116
switch/make.sh
Executable file
116
switch/make.sh
Executable file
@ -0,0 +1,116 @@
|
||||
#!/bin/bash
|
||||
|
||||
OLD_PWD="$(pwd)"
|
||||
|
||||
# Mac can prefix tool names with 'dkp-'
|
||||
if which dkp-pacman &>/dev/null; then
|
||||
PACMAN=dkp-pacman
|
||||
elif which pacman &>/dev/null; then
|
||||
PACMAN=pacman
|
||||
else
|
||||
echo "Couldn't find pacman in PATH! Is it installed?"
|
||||
echo "Please see https://devkitpro.org/wiki/devkitPro_pacman#Installing_devkitPro_Pacman for instructions to install it!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install deps. --needed means don't reinstall if already installed
|
||||
sudo $PACMAN -S --needed switch-dev \
|
||||
devkitpro-pkgbuild-helpers \
|
||||
switch-curl switch-mbedtls \
|
||||
switch-freetype switch-libfribidi \
|
||||
switch-libogg switch-libvorbis \
|
||||
switch-libjpeg-turbo switch-libpng \
|
||||
switch-zlib switch-bzip2 \
|
||||
switch-physfs \
|
||||
switch-pkg-config \
|
||||
switch-sdl2 switch-mesa switch-libdrm_nouveau \
|
||||
libnx \
|
||||
switch-tools # elf2nro
|
||||
|
||||
# Users of MSYS2 or Arch Linux will already have Pacman installed but may not have the DKP repos on their system:
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to install packages! Did you add the repositories?"
|
||||
echo "Please see https://devkitpro.org/wiki/devkitPro_pacman#Customising_Existing_Pacman_Install for instructions!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Unclear why this isn't in lib path
|
||||
if [ ! -f "${DEVKITPRO}/portlibs/switch/lib/libpthread.a" ]; then
|
||||
sudo ln -s "${DEVKITPRO}/devkitA64/aarch64-none-elf/lib/libpthread.a" \
|
||||
"${DEVKITPRO}/portlibs/switch/lib/libpthread.a"
|
||||
fi
|
||||
|
||||
SWITCH_DIR=$(realpath "$(dirname "$0")")
|
||||
STK_DIR=$(dirname "${SWITCH_DIR}")
|
||||
|
||||
# Some shells don't set BASH_SOURCE. Let's set it just in case:
|
||||
BASH_SOURCE="${DEVKITPRO}/switchvars.sh"
|
||||
source "${DEVKITPRO}/switchvars.sh" # Sets environment variables needed for cross-compiling
|
||||
|
||||
if [ ! -d "${STK_DIR}/lib/harfbuzz/cmake_build" ]; then
|
||||
# Harfbuzz
|
||||
echo "Compiling Harfbuzz"
|
||||
mkdir "${STK_DIR}/lib/harfbuzz/cmake_build"
|
||||
cd "${STK_DIR}/lib/harfbuzz/cmake_build"
|
||||
cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE="${DEVKITPRO}/switch.cmake" \
|
||||
-DUSE_SWITCH=ON -DCMAKE_INSTALL_PREFIX="${PORTLIBS_PREFIX}" \
|
||||
-DHB_HAVE_FREETYPE=ON \
|
||||
../
|
||||
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
fi
|
||||
|
||||
if [ ! -d "${STK_DIR}/lib/openal/cmake_build" ]; then
|
||||
# OpenAL
|
||||
echo "Compiling OpenAL"
|
||||
mkdir "${STK_DIR}/lib/openal/cmake_build"
|
||||
cd "${STK_DIR}/lib/openal/cmake_build"
|
||||
cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE="${DEVKITPRO}/switch.cmake" \
|
||||
-DUSE_SWITCH=ON -DALSOFT_UTILS=OFF -DLIBTYPE=STATIC -DALSOFT_EXAMPLES=OFF \
|
||||
-DALSOFT_REQUIRE_SDL2=ON -DALSOFT_BACKEND_SDL2=ON \
|
||||
-DSDL2_INCLUDE_DIR="${PORTLIBS_PREFIX}/include" \
|
||||
-DCMAKE_INSTALL_PREFIX="${PORTLIBS_PREFIX}" \
|
||||
../
|
||||
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
fi
|
||||
|
||||
echo "Compiling STK"
|
||||
|
||||
mkdir "${STK_DIR}/cmake_build"
|
||||
cd "${STK_DIR}/cmake_build"
|
||||
|
||||
cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE="${DEVKITPRO}/switch.cmake" \
|
||||
-DUSE_SWITCH=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=/ \
|
||||
../
|
||||
|
||||
make -j$(nproc)
|
||||
make install DESTDIR=./install
|
||||
|
||||
# Build nro (executable for switch)
|
||||
"${SWITCH_DIR}/package.sh"
|
||||
|
||||
echo "Building package"
|
||||
|
||||
rm -rf sdcard
|
||||
mkdir sdcard
|
||||
# Move data over
|
||||
mv install/share/supertuxkart/data sdcard/stk-data
|
||||
# Add executable
|
||||
mkdir sdcard/switch
|
||||
mv bin/stk.nro sdcard/switch/stk.nro
|
||||
|
||||
echo "Compressing"
|
||||
|
||||
# Zip up actual release:
|
||||
cd sdcard
|
||||
ZIP_PATH="${STK_DIR}/cmake_build/bin/SuperTuxKart-Switch-${PROJECT_VERSION}.zip"
|
||||
zip -r "${ZIP_PATH}" .
|
||||
|
||||
# Recover old pwd
|
||||
cd $OLD_PWD
|
||||
|
||||
echo "Done. Package available at ${ZIP_PATH}"
|
2
switch/package.sh
Executable file
2
switch/package.sh
Executable file
@ -0,0 +1,2 @@
|
||||
nacptool --create "SuperTuxKart" "Many People" "${PROJECT_VERSION}" control.nacp
|
||||
elf2nro bin/supertuxkart bin/stk.nro --nacp=control.nacp --icon=../switch/supertuxkart_256.jpg
|
BIN
switch/supertuxkart_256.jpg
Normal file
BIN
switch/supertuxkart_256.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
Loading…
Reference in New Issue
Block a user