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:
Mary 2021-03-03 20:23:23 -05:00 committed by GitHub
parent d1e73f550d
commit d9b8b7acad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 528 additions and 32 deletions

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 == "")

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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>

View File

@ -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)
{

View File

@ -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

View File

@ -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))

View File

@ -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);

View File

@ -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)
{

View File

@ -37,7 +37,9 @@
#else
# include <arpa/inet.h>
# include <errno.h>
#ifndef __SWITCH__
# include <ifaddrs.h>
#endif
# include <sys/socket.h>
#endif

View File

@ -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)

View File

@ -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());

View File

@ -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)

View File

@ -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(&currentIp);
// 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();

View File

@ -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

View File

@ -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>

View File

@ -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");

View File

@ -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);

View File

@ -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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB