diff --git a/common/backends.h b/common/backends.h index be3cdaa6..77bcced8 100644 --- a/common/backends.h +++ b/common/backends.h @@ -47,6 +47,7 @@ GNU General Public License for more details. #define MSGBOX_SDL 1 #define MSGBOX_ANDROID 2 #define MSGBOX_WIN32 3 +#define MSGBOX_NSWITCH 4 // library loading (XASH_LIB) diff --git a/common/defaults.h b/common/defaults.h index e992ef4d..2144b3be 100644 --- a/common/defaults.h +++ b/common/defaults.h @@ -47,7 +47,9 @@ SETUP BACKENDS DEFINITIONS #endif // XASH_TIMER #ifndef XASH_MESSAGEBOX - #define XASH_MESSAGEBOX MSGBOX_SDL + #if !XASH_NSWITCH // SDL2 messageboxes not available + #define XASH_MESSAGEBOX MSGBOX_SDL + #endif #endif // XASH_MESSAGEBOX #endif #elif XASH_ANDROID @@ -105,6 +107,8 @@ SETUP BACKENDS DEFINITIONS #ifndef XASH_MESSAGEBOX #if XASH_WIN32 #define XASH_MESSAGEBOX MSGBOX_WIN32 + #elif XASH_NSWITCH + #define XASH_MESSAGEBOX MSGBOX_NSWITCH #else // !XASH_WIN32 #define XASH_MESSAGEBOX MSGBOX_STDERR #endif // !XASH_WIN32 @@ -177,4 +181,9 @@ Default build-depended cvar and constant values #define DEFAULT_FULLSCREEN 1 #endif // DEFAULT_FULLSCREEN +#if XASH_NSWITCH + #define DEFAULT_MODE_WIDTH 1280 + #define DEFAULT_MODE_HEIGHT 720 +#endif // XASH_NSWITCH + #endif // DEFAULTS_H diff --git a/common/port.h b/common/port.h index 05a732da..bf55e171 100644 --- a/common/port.h +++ b/common/port.h @@ -39,8 +39,13 @@ GNU General Public License for more details. #if XASH_POSIX #include - #include - #define HAVE_DUP + #ifdef XASH_NSWITCH + #define SOLDER_LIBDL_COMPAT + #include + #else + #include + #define HAVE_DUP + #endif #define O_BINARY 0 #define O_TEXT 0 #define _mkdir( x ) mkdir( x, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) diff --git a/engine/common/host.c b/engine/common/host.c index c0515eae..e558b285 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1013,7 +1013,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha #if TARGET_OS_IOS const char *IOS_GetDocsDir(); Q_strncpy( host.rootdir, IOS_GetDocsDir(), sizeof(host.rootdir) ); -#elif XASH_SDL == 2 +#elif (XASH_SDL == 2) && !XASH_NSWITCH // GetBasePath not impl'd in switch-sdl2 char *szBasePath; if( !( szBasePath = SDL_GetBasePath() ) ) diff --git a/engine/common/imagelib/img_png.c b/engine/common/imagelib/img_png.c index 8056ac7c..2a4c742d 100644 --- a/engine/common/imagelib/img_png.c +++ b/engine/common/imagelib/img_png.c @@ -20,6 +20,9 @@ GNU General Public License for more details. #if defined(XASH_NO_NETWORK) #include "platform/stub/net_stub.h" +#elif XASH_NSWITCH + // our ntohl is here + #include #elif !XASH_WIN32 #include #endif diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index b0a241a6..6a0c3dd2 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -264,6 +264,11 @@ static void Sys_PrintStdout( const char *logtime, const char *msg ) void IOS_Log( const char * ); IOS_Log( buf ); #endif // TARGET_OS_IOS + +#if XASH_NSWITCH && NSWITCH_DEBUG + // just spew it to stderr normally in debug mode + fprintf( stderr, "%s %s", logtime, buf ); +#endif // XASH_NSWITCH && NSWITCH_DEBUG #elif !XASH_WIN32 // Wcon does the job Sys_PrintLogfile( STDOUT_FILENO, logtime, msg, XASH_COLORIZE_CONSOLE ); Sys_FlushStdout(); diff --git a/engine/common/system.c b/engine/common/system.c index 6df8ffe6..d7d08165 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -26,7 +26,6 @@ GNU General Public License for more details. #if XASH_POSIX #include #include -#include #if !XASH_ANDROID #include @@ -37,6 +36,10 @@ GNU General Public License for more details. #include #endif +#if XASH_NSWITCH +#include +#endif + #include "menu_int.h" // _UPDATE_PAGE macro #include "library.h" @@ -126,7 +129,7 @@ const char *Sys_GetCurrentUser( void ) if( GetUserName( s_userName, &size )) return s_userName; -#elif XASH_POSIX && !XASH_ANDROID +#elif XASH_POSIX && !XASH_ANDROID && !XASH_NSWITCH uid_t uid = geteuid(); struct passwd *pw = getpwuid( uid ); @@ -566,6 +569,19 @@ it explicitly doesn't use internal allocation or string copy utils */ qboolean Sys_NewInstance( const char *gamedir ) { +#if XASH_NSWITCH + char newargs[4096]; + const char *exe = host.argv[0]; // arg 0 is always the full NRO path + + // TODO: carry over the old args (assuming you can even pass any) + Q_snprintf( newargs, sizeof( newargs ), "%s -game %s", exe, gamedir ); + // just restart the entire thing + printf( "envSetNextLoad exe: `%s`\n", exe ); + printf( "envSetNextLoad argv:\n`%s`\n", newargs ); + Host_Shutdown( ); + envSetNextLoad( exe, newargs ); + exit( 0 ); +#else int i = 0; qboolean replacedArg = false; size_t exelen; @@ -613,6 +629,7 @@ qboolean Sys_NewInstance( const char *gamedir ) free( newargs[i] ); free( newargs ); free( exe ); +#endif return false; } diff --git a/engine/common/whereami.c b/engine/common/whereami.c index e7958363..0a0fb570 100644 --- a/engine/common/whereami.c +++ b/engine/common/whereami.c @@ -795,7 +795,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) return length; } -#elif defined(__sgi) +#elif defined(__sgi) || defined(__SWITCH__) /* * These functions are stubbed for now to get the code compiling. diff --git a/engine/platform/nswitch/icon.jpg b/engine/platform/nswitch/icon.jpg new file mode 100644 index 00000000..3bb62ec2 Binary files /dev/null and b/engine/platform/nswitch/icon.jpg differ diff --git a/engine/platform/nswitch/sys_nswitch.c b/engine/platform/nswitch/sys_nswitch.c new file mode 100644 index 00000000..8165f211 --- /dev/null +++ b/engine/platform/nswitch/sys_nswitch.c @@ -0,0 +1,140 @@ +/* +switch.c - switch backend +Copyright (C) 2021-2023 fgsfds + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "platform/platform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int nxlink_sock = -1; + +/* HACKHACK: force-export stuff required by the dynamic libs */ + +// libunwind stuff for C++ exceptions, required by filesystem_stdio +extern void *_Unwind_GetIPInfo; +extern void *_Unwind_Resume_or_Rethrow; +extern void *_Unwind_GetRegionStart; +extern void *_Unwind_Resume; +extern void *_Unwind_DeleteException; +extern void *_Unwind_RaiseException; +extern void *_Unwind_SetIP; +extern void *_Unwind_GetTextRelBase; +extern void *_Unwind_GetLanguageSpecificData; +extern void *_Unwind_SetGR; +extern void *_Unwind_GetDataRelBase; + +// these are macros in our libc, so we need to wrap them +static int tolower_fn(int c) { return tolower(c); } +static int toupper_fn(int c) { return toupper(c); } +static int isalnum_fn(int c) { return isalnum(c); } +static int isalpha_fn(int c) { return isalpha(c); } + +static const solder_export_t aux_exports[] = +{ + SOLDER_EXPORT("tolower", tolower_fn), + SOLDER_EXPORT("toupper", toupper_fn), + SOLDER_EXPORT("isalnum", isalnum_fn), + SOLDER_EXPORT("isalpha", isalpha_fn), + SOLDER_EXPORT_SYMBOL(mkdir), + SOLDER_EXPORT_SYMBOL(remove), + SOLDER_EXPORT_SYMBOL(rename), + SOLDER_EXPORT_SYMBOL(fsync), + SOLDER_EXPORT_SYMBOL(strchrnul), + SOLDER_EXPORT_SYMBOL(stpcpy), + SOLDER_EXPORT_SYMBOL(_Unwind_GetIPInfo), + SOLDER_EXPORT_SYMBOL(_Unwind_Resume_or_Rethrow), + SOLDER_EXPORT_SYMBOL(_Unwind_GetRegionStart), + SOLDER_EXPORT_SYMBOL(_Unwind_Resume), + SOLDER_EXPORT_SYMBOL(_Unwind_DeleteException), + SOLDER_EXPORT_SYMBOL(_Unwind_RaiseException), + SOLDER_EXPORT_SYMBOL(_Unwind_SetIP), + SOLDER_EXPORT_SYMBOL(_Unwind_GetTextRelBase), + SOLDER_EXPORT_SYMBOL(_Unwind_GetLanguageSpecificData), + SOLDER_EXPORT_SYMBOL(_Unwind_SetGR), + SOLDER_EXPORT_SYMBOL(_Unwind_GetDataRelBase), +}; + +const solder_export_t *__solder_aux_exports = aux_exports; +const size_t __solder_num_aux_exports = sizeof(aux_exports) / sizeof(*aux_exports); + +/* end of export crap */ + +void Platform_ShellExecute( const char *path, const char *parms ) +{ + Con_Reportf( S_WARN "Tried to shell execute ;%s; -- not supported\n", path ); +} + +#if XASH_MESSAGEBOX == MSGBOX_NSWITCH +void Platform_MessageBox( const char *title, const char *message, qboolean unused ) +{ + // TODO: maybe figure out how to show an actual messagebox or an on-screen console + // without murdering the renderer + // assume this is a fatal error + FILE *f = fopen( "fatal.log", "w" ); + if ( f ) + { + fprintf( f, "%s:\n%s\n", title, message ); + fclose( f ); + } + // dump to nxlink as well + fprintf( stderr, "%s:\n%s\n", title, message ); +} +#endif // XASH_MESSAGEBOX == MSGBOX_NSWITCH + +// this gets executed before main(), do not delete +void userAppInit( void ) +{ + socketInitializeDefault( ); +#ifdef NSWITCH_DEBUG + nxlink_sock = nxlinkStdio( ); +#endif + if ( solder_init( 0 ) < 0 ) + { + fprintf( stderr, "solder_init() failed: %s\n", solder_dlerror() ); + fflush( stderr ); + exit( 1 ); + } +} + +// this gets executed on exit(), do not delete +void userAppExit( void ) +{ + solder_quit( ); + if ( nxlink_sock >= 0 ) + { + close( nxlink_sock ); + nxlink_sock = -1; + } + socketExit( ); +} + +void NSwitch_Init( void ) +{ + printf( "NSwitch_Init\n" ); +} + +void NSwitch_Shutdown( void ) +{ + printf( "NSwitch_Shutdown\n" ); + // force deinit everything SDL-related to avoid issues with changing games + if ( SDL_WasInit( 0 ) ) + SDL_Quit( ); +} diff --git a/engine/platform/nswitch/xash3d-fwgs.nacp b/engine/platform/nswitch/xash3d-fwgs.nacp new file mode 100644 index 00000000..7f8c9c53 Binary files /dev/null and b/engine/platform/nswitch/xash3d-fwgs.nacp differ diff --git a/engine/platform/platform.h b/engine/platform/platform.h index 0aaee2b5..ccf53067 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -51,6 +51,11 @@ void Platform_UpdateStatusLine( void ); static inline void Platform_UpdateStatusLine( void ) { } #endif +#if XASH_NSWITCH +void NSwitch_Init( void ); +void NSwitch_Shutdown( void ); +#endif + /* ============================================================================== diff --git a/engine/platform/posix/lib_posix.c b/engine/platform/posix/lib_posix.c index 746b73e7..ad97130e 100644 --- a/engine/platform/posix/lib_posix.c +++ b/engine/platform/posix/lib_posix.c @@ -15,7 +15,6 @@ GNU General Public License for more details. #define _GNU_SOURCE #include "platform/platform.h" #if XASH_LIB == LIB_POSIX -#include #ifdef XASH_IRIX #include "platform/irix/dladdr.h" #endif diff --git a/engine/platform/posix/sys_posix.c b/engine/platform/posix/sys_posix.c index c5371f98..3872f8da 100644 --- a/engine/platform/posix/sys_posix.c +++ b/engine/platform/posix/sys_posix.c @@ -67,7 +67,7 @@ static qboolean Sys_FindExecutable( const char *baseName, char *buf, size_t size return false; } -#if !XASH_ANDROID +#if !XASH_ANDROID && !XASH_NSWITCH void Platform_ShellExecute( const char *path, const char *parms ) { char xdgOpen[128]; diff --git a/engine/platform/sdl/sys_sdl.c b/engine/platform/sdl/sys_sdl.c index 0194c291..8c6fc13f 100644 --- a/engine/platform/sdl/sys_sdl.c +++ b/engine/platform/sdl/sys_sdl.c @@ -68,6 +68,9 @@ void Platform_Init( void ) #ifdef XASH_WIN32 Wcon_CreateConsole(); // system console used by dedicated server or show fatal errors #endif +#ifdef XASH_NSWITCH + NSwitch_Init(); +#endif SDLash_InitCursors(); } @@ -79,4 +82,7 @@ void Platform_Shutdown( void ) #ifdef XASH_WIN32 Wcon_DestroyConsole(); #endif +#ifdef XASH_NSWITCH + NSwitch_Shutdown(); +#endif } diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index d3d953dc..c2d84e01 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -3100,7 +3100,7 @@ void SV_SetStringArrayMode( qboolean dynamic ) #endif } -#if XASH_64BIT && !XASH_WIN32 && !XASH_APPLE +#if XASH_64BIT && !XASH_WIN32 && !XASH_APPLE && !XASH_NSWITCH #define USE_MMAP #include #endif diff --git a/engine/wscript b/engine/wscript index 8195d772..1639147e 100644 --- a/engine/wscript +++ b/engine/wscript @@ -48,6 +48,14 @@ def configure(conf): conf.options.NO_ASYNC_RESOLVE = True if not conf.check_cc( fragment='int main(){ int i = socket();}', lib = 'wattcpwl', mandatory=False ): conf.define('XASH_NO_NETWORK',1) + elif conf.env.DEST_OS == 'nswitch': + conf.load('sdl2') + if not conf.env.HAVE_SDL2: + conf.fatal('SDL2 not availiable! Install switch-sdl2!') + conf.define('XASH_SDL', 2) + # disallow undefined symbols + conf.env.append_unique('CXXFLAGS', '-Wl,--no-undefined') + conf.env.append_unique('CFLAGS', '-Wl,--no-undefined') elif conf.options.FBDEV_SW: # unused, XASH_LINUX without XASH_SDL gives fbdev & alsa support # conf.define('XASH_FBDEV', 1) @@ -123,7 +131,7 @@ def build(bld): if bld.env.DEST_OS == 'win32': libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP', 'PSAPI', 'WS2_32' ] source += bld.path.ant_glob(['platform/win32/*.c']) - elif bld.env.DEST_OS != 'dos': #posix + elif bld.env.DEST_OS != 'dos' and bld.env.DEST_OS != 'nswitch': #posix libs += [ 'M', 'RT', 'PTHREAD', 'ASOUND'] if not bld.env.STATIC: libs += ['DL'] @@ -162,6 +170,11 @@ def build(bld): libs += ['LOG'] source += bld.path.ant_glob(['platform/android/*.cpp', 'platform/android/*.c', 'platform/linux/*.c']) + if bld.env.DEST_OS == 'nswitch': + libs += [ 'SOLDER' ] + source += bld.path.ant_glob(['platform/posix/*.c']) + source += bld.path.ant_glob(['platform/nswitch/*.c']) + # add client files if not bld.env.DEDICATED: source += bld.path.ant_glob([ @@ -172,22 +185,34 @@ def build(bld): includes = ['server', 'client', 'client/vgui' ] - if bld.env.SINGLE_BINARY: - install_path = bld.env.BINDIR - program = 'cxxprogram' if is_cxx_link else 'cprogram' - if bld.env.STATIC: - program += '_static' - features = ['c', program] + # Switch has custom parameters + if bld.env.DEST_OS == 'nswitch': + bld(source = source, + target = 'xash', + features = 'c cxxprogram', + includes = includes, + use = libs, + install_path = None, + nro_install_path = bld.env.BINDIR, + nacp = 'platform/nswitch/xash3d-fwgs.nacp', + icon = 'platform/nswitch/icon.jpg') else: - install_path = bld.env.LIBDIR - features = ['c', 'cxxshlib' if is_cxx_link else 'cshlib'] - - bld(source = source, - target = 'xash', - features = features, - includes = includes, - use = libs, - install_path = install_path, - subsystem = bld.env.MSVC_SUBSYSTEM, - rpath = '$ORIGIN' - ) + if bld.env.SINGLE_BINARY: + install_path = bld.env.BINDIR + program = 'cxxprogram' if is_cxx_link else 'cprogram' + if bld.env.STATIC: + program += '_static' + features = ['c', program] + else: + install_path = bld.env.LIBDIR + features = ['c', 'cxxshlib' if is_cxx_link else 'cshlib'] + + bld(source = source, + target = 'xash', + features = features, + includes = includes, + use = libs, + install_path = install_path, + subsystem = bld.env.MSVC_SUBSYSTEM, + rpath = '$ORIGIN' + ) diff --git a/public/build.c b/public/build.c index 758ef738..08827800 100644 --- a/public/build.c +++ b/public/build.c @@ -109,6 +109,8 @@ const char *Q_PlatformStringByID( const int platform ) return "serenity"; case PLATFORM_IRIX: return "irix"; + case PLATFORM_NSWITCH: + return "nswitch"; } assert( 0 ); diff --git a/public/build.h b/public/build.h index b2ff2c2d..8a4db711 100644 --- a/public/build.h +++ b/public/build.h @@ -82,6 +82,7 @@ Then you can use another oneliner to query all variables: #undef XASH_SERENITY #undef XASH_WIN32 #undef XASH_X86 +#undef XASH_NSWITCH //================================================================ // @@ -119,12 +120,14 @@ Then you can use another oneliner to query all variables: #if TARGET_OS_IOS #define XASH_IOS 1 #endif // TARGET_OS_IOS + #elif defined __SWITCH__ + #define XASH_NSWITCH 1 #else #error #endif #endif -#if XASH_ANDROID || defined XASH_IOS +#if XASH_ANDROID || defined XASH_IOS || defined XASH_NSWITCH #define XASH_MOBILE_PLATFORM 1 #endif diff --git a/public/buildenums.h b/public/buildenums.h index 872975ad..ed7a0c58 100644 --- a/public/buildenums.h +++ b/public/buildenums.h @@ -39,6 +39,7 @@ GNU General Public License for more details. #define PLATFORM_HAIKU 10 #define PLATFORM_SERENITY 11 #define PLATFORM_IRIX 12 +#define PLATFORM_NSWITCH 13 #if XASH_WIN32 #define XASH_PLATFORM PLATFORM_WIN32 @@ -64,6 +65,8 @@ GNU General Public License for more details. #define XASH_PLATFORM PLATFORM_SERENITY #elif XASH_IRIX #define XASH_PLATFORM PLATFORM_IRIX +#elif XASH_NSWITCH + #define XASH_PLATFORM PLATFORM_NSWITCH #else #error #endif diff --git a/scripts/waifulib/compiler_optimizations.py b/scripts/waifulib/compiler_optimizations.py index a0fd9b87..110b0762 100644 --- a/scripts/waifulib/compiler_optimizations.py +++ b/scripts/waifulib/compiler_optimizations.py @@ -169,4 +169,8 @@ def get_optimization_flags(conf): if conf.options.POLLY: cflags += conf.get_flags_by_compiler(POLLY_CFLAGS, conf.env.COMPILER_CC) + if conf.env.DEST_OS == 'nswitch' and conf.options.BUILD_TYPE == 'debug': + # enable remote debugger + cflags.append('-DNSWITCH_DEBUG') + return cflags, linkflags diff --git a/scripts/waifulib/nswitch.py b/scripts/waifulib/nswitch.py new file mode 100644 index 00000000..63391768 --- /dev/null +++ b/scripts/waifulib/nswitch.py @@ -0,0 +1,64 @@ +# encoding: utf-8 +# nswitch.py -- switch NRO task +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +from waflib.Tools import ccroot +from waflib import * + +def configure(conf): + conf.find_program('elf2nro') + + v = conf.env + + v.ELF2NRO_NACP_F = ['--nacp='] + v.ELF2NRO_ICON_F = ['--icon='] + +class elf2nro(Task.Task): + color = 'RED' + run_str = '${ELF2NRO} ${ELFFILE} ${TGT} ${ELF2NRO_NACP_F?NACP}${NACP} ${ELF2NRO_ICON_F?ICON}${ICON}' + + def keyword(self): + if Logs.colors_lst['USE']: # red/blue switch colors :) + return '%sConverting to NRO' % Logs.colors_lst['CYAN'] + return 'Converting to NRO' + +@TaskGen.feature('cxxprogram') +@TaskGen.after_method('apply_link') +def apply_nro(self): + elffile = self.link_task.outputs[0] + + nodes = [elffile] + + def add_source_file(ctx, nodes, f): + if f: + if isinstance(f, str): + node = ctx.path.make_node(f) + elif isinstance(f, Node.Node): + node = f + + nodes += [node] + return node + return None + + nacpfile = add_source_file(self, nodes, getattr(self, 'nacp', None)) + iconfile = add_source_file(self, nodes, getattr(self, 'icon', None)) + self.env.ELFFILE = str(elffile) + if nacpfile: self.env.NACP = str(nacpfile) + if iconfile: self.env.ICON = str(iconfile) + + tsk = self.nro_task = self.create_task('elf2nro', nodes) + self.nro_task.set_outputs(nodes[0].change_ext('.nro')) + + inst_to = getattr(self, 'nro_install_path', None) + if inst_to: + self.add_install_files(install_to=inst_to, + install_from=tsk.outputs[:], chmod=Utils.O755, task=tsk) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 981a081d..f2fa4091 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -30,6 +30,8 @@ ANDROID_NDK_API_MIN = { 10: 3, 19: 16, 20: 16, 23: 16, 25: 19 } # minimal API le ANDROID_STPCPY_API_MIN = 21 # stpcpy() introduced in SDK 21 ANDROID_64BIT_API_MIN = 21 # minimal API level that supports 64-bit targets +NSWITCH_ENVVARS = ['DEVKITPRO'] + # This class does support ONLY r10e and r19c/r20 NDK class Android: ctx = None # waf context @@ -348,6 +350,87 @@ class Android: ldflags += ['-march=armv5te'] return ldflags +class NintendoSwitch: + ctx = None # waf context + arch = "aarch64" + dkp_dir = None + portlibs_dir = None + dka64_dir = None + libnx_dir = None + + def __init__(self, ctx): + self.ctx = ctx + + for i in NSWITCH_ENVVARS: + self.dkp_dir = os.getenv(i) + if self.dkp_dir != None: + break + else: + ctx.fatal('Set %s environment variable pointing to the DEVKITPRO home!' % + ' or '.join(NSWITCH_ENVVARS)) + + self.dkp_dir = os.path.abspath(self.dkp_dir) + + self.dka64_dir = os.path.join(self.dkp_dir, 'devkitA64') + if not os.path.exists(self.dka64_dir): + ctx.fatal('devkitA64 not found in `%s`. Install devkitA64!' % self.dka64_dir) + + self.libnx_dir = os.path.join(self.dkp_dir, 'libnx') + if not os.path.exists(self.libnx_dir): + ctx.fatal('libnx not found in `%s`. Install libnx!' % self.libnx_dir) + + self.portlibs_dir = os.path.join(self.dkp_dir, 'portlibs', 'switch') + if not os.path.exists(self.portlibs_dir): + ctx.fatal('No Switch libraries found in `%s`!' % self.portlibs_dir) + + def gen_toolchain_prefix(self): + return 'aarch64-none-elf-' + + def gen_gcc_toolchain_path(self): + return os.path.join(self.dka64_dir, 'bin', self.gen_toolchain_prefix()) + + def cc(self): + return self.gen_gcc_toolchain_path() + 'gcc' + + def cxx(self): + return self.gen_gcc_toolchain_path() + 'g++' + + def strip(self): + return self.gen_gcc_toolchain_path() + 'strip' + + def pkgconfig(self): + # counter-intuitively, this motherfucker is in $DEVKITPRO/portlibs/switch/bin + return os.path.join(self.portlibs_dir, 'bin', self.gen_toolchain_prefix() + 'pkg-config') + + def cflags(self, cxx = False): + cflags = [] + # arch flags + cflags += ['-D__SWITCH__', '-march=armv8-a+crc+crypto', '-mtune=cortex-a57', '-mtp=soft', '-ftls-model=local-exec', '-fPIE'] + # help the linker out + cflags += ['-ffunction-sections', '-fdata-sections'] + # base include dirs + cflags += ['-isystem %s/include' % self.libnx_dir, '-I%s/include' % self.portlibs_dir] + # the game wants GNU extensions + if cxx: + cflags += ['-std=gnu++17', '-D_GNU_SOURCE'] + else: + cflags += ['-std=gnu11', '-D_GNU_SOURCE'] + return cflags + + # they go before object list + def linkflags(self): + linkflags = ['-fPIE', '-specs=%s/switch.specs' % self.libnx_dir] + # libsolder only supports sysv hashes and we need to build everything with -rdynamic + linkflags += ['-Wl,--hash-style=sysv', '-rdynamic'] + # avoid pulling in and exposing mesa's internals, that crashes it for some god forsaken reason + linkflags += ['-Wl,--exclude-libs=libglapi.a', '-Wl,--exclude-libs=libdrm_nouveau.a'] + return linkflags + + def ldflags(self): + # system libraries implicitly require math and C++ standard library + ldflags = ['-lm', '-lstdc++'] + return ldflags + def options(opt): xc = opt.add_option_group('Cross compile options') xc.add_option('--android', action='store', dest='ANDROID_OPTS', default=None, @@ -356,6 +439,8 @@ def options(opt): help='enable building for Motorola MAGX [default: %default]') xc.add_option('--enable-msvc-wine', action='store_true', dest='MSVC_WINE', default=False, help='enable building with MSVC using Wine [default: %default]') + xc.add_option('--nswitch', action='store_true', dest='NSWITCH', default = False, + help ='enable building for Nintendo Switch [default: %default]') def configure(conf): if conf.options.ANDROID_OPTS: @@ -408,10 +493,23 @@ def configure(conf): conf.env.DEST_OS = 'win32' conf.env.DEST_CPU = conf.env.MSVC_TARGETS[0] conf.env.COMPILER_CXX = conf.env.COMPILER_CC = 'msvc' + elif conf.options.NSWITCH: + conf.nswitch = nswitch = NintendoSwitch(conf) + conf.environ['CC'] = nswitch.cc() + conf.environ['CXX'] = nswitch.cxx() + conf.environ['STRIP'] = nswitch.strip() + conf.env.PKGCONFIG = nswitch.pkgconfig() + conf.env.CFLAGS += nswitch.cflags() + conf.env.CXXFLAGS += nswitch.cflags(True) + conf.env.LINKFLAGS += nswitch.linkflags() + conf.env.LDFLAGS += nswitch.ldflags() + conf.env.HAVE_M = True + conf.env.LIB_M = ['m'] + conf.env.DEST_OS = 'nswitch' conf.env.MAGX = conf.options.MAGX conf.env.MSVC_WINE = conf.options.MSVC_WINE - MACRO_TO_DESTOS = OrderedDict({ '__ANDROID__' : 'android' }) + MACRO_TO_DESTOS = OrderedDict({ '__ANDROID__' : 'android', '__SWITCH__' : 'nswitch' }) for k in c_config.MACRO_TO_DESTOS: MACRO_TO_DESTOS[k] = c_config.MACRO_TO_DESTOS[k] # ordering is important c_config.MACRO_TO_DESTOS = MACRO_TO_DESTOS diff --git a/wscript b/wscript index 4931ad6a..be23b686 100644 --- a/wscript +++ b/wscript @@ -132,6 +132,9 @@ def configure(conf): # Load compilers early conf.load('xshlib xcompile compiler_c compiler_cxx') + if conf.options.NSWITCH: + conf.load('nswitch') + # HACKHACK: override msvc DEST_CPU value by something that we understand if conf.env.DEST_CPU == 'amd64': conf.env.DEST_CPU = 'x86_64' @@ -170,6 +173,12 @@ def configure(conf): enforce_pic = False elif conf.env.DEST_OS == 'dos': conf.options.SINGLE_BINARY = True + elif conf.env.DEST_OS == 'nswitch': + conf.options.NO_VGUI = True + conf.options.GL = True + conf.options.SINGLE_BINARY = True + conf.options.NO_ASYNC_RESOLVE = True + conf.options.USE_STBTT = True if conf.env.STATIC_LINKING: enforce_pic = False # PIC may break full static builds @@ -233,6 +242,14 @@ def configure(conf): cflags, linkflags = conf.get_optimization_flags() + # on the Switch, allow undefined symbols by default, which is needed for libsolder to work + # we'll specifically disallow for the engine executable + # additionally, shared libs are linked without libc + if conf.env.DEST_OS == 'nswitch': + linkflags.remove('-Wl,--no-undefined') + conf.env.append_unique('LINKFLAGS_cshlib', ['-nostdlib', '-nostartfiles']) + conf.env.append_unique('LINKFLAGS_cxxshlib', ['-nostdlib', '-nostartfiles']) + # And here C++ flags starts to be treated separately cxxflags = list(cflags) if conf.env.COMPILER_CC != 'msvc' and not conf.options.DISABLE_WERROR: @@ -282,7 +299,10 @@ def configure(conf): conf.define('ALLOCA_H', 'malloc.h') if conf.env.DEST_OS != 'win32': - conf.check_cc(lib='dl', mandatory=False) + if conf.env.DEST_OS == 'nswitch': + conf.check_cfg(package='solder', args='--cflags --libs', uselib_store='SOLDER') + else: + conf.check_cc(lib='dl', mandatory=False) if not conf.env.LIB_M: # HACK: already added in xcompile! conf.check_cc(lib='m') @@ -354,7 +374,7 @@ int main(int argc, char **argv) { strchrnul(argv[1], 'x'); return 0; }''' conf.env.LIBDIR = conf.env.BINDIR = conf.env.LIBDIR + '/xash3d' conf.env.SHAREDIR = '${PREFIX}/share/xash3d' else: - if sys.platform != 'win32' and not conf.env.DEST_OS == 'android': + if sys.platform != 'win32' and conf.env.DEST_OS != 'android': conf.env.PREFIX = '/' conf.env.SHAREDIR = conf.env.LIBDIR = conf.env.BINDIR = conf.env.PREFIX