diff --git a/README.md b/README.md
index 3f7e36a6..6bd77f0e 100644
--- a/README.md
+++ b/README.md
@@ -237,6 +237,39 @@ schroot --chroot jessie -- cmake --build build-in-chroot
TODO
+## Nintendo Switch
+
+### Prerequisites
+
+1. Set up [`dkp-pacman`](https://devkitpro.org/wiki/devkitPro_pacman).
+2. Install dependency packages:
+```
+sudo dkp-pacman -S switch-dev dkp-toolchain-vars switch-mesa switch-libdrm_nouveau switch-sdl2
+```
+3. Make sure the `DEVKITPRO` environment variable is set to the devkitPro SDK root:
+```
+export DEVKITPRO=/opt/devkitpro
+```
+4. Install libsolder:
+```
+source $DEVKITPRO/switchvars.sh
+git clone https://github.com/fgsfdsfgs/libsolder.git
+make -C libsolder install
+```
+
+### Building using CMake
+```
+mkdir build && cd build
+aarch64-none-elf-cmake -G"Unix Makefiles" -DCMAKE_PROJECT_HLSDK-PORTABLE_INCLUDE="$DEVKITPRO/portlibs/switch/share/SolderShim.cmake" ..
+make -j
+```
+
+### Building using waf
+```
+./waf configure -T release --nswitch
+./waf build
+```
+
## Other platforms
Building on other Unix-like platforms (e.g. FreeBSD) is supported.
diff --git a/cmake/LibraryNaming.cmake b/cmake/LibraryNaming.cmake
index c97c92f7..156ab672 100644
--- a/cmake/LibraryNaming.cmake
+++ b/cmake/LibraryNaming.cmake
@@ -42,6 +42,7 @@ check_symbol_exists(XASH_SERENITY "build.h" XASH_SERENITY)
check_symbol_exists(XASH_WIN32 "build.h" XASH_WIN32)
check_symbol_exists(XASH_WIN64 "build.h" XASH_WIN64)
check_symbol_exists(XASH_X86 "build.h" XASH_X86)
+check_symbol_exists(XASH_NSWITCH "build.h" XASH_NSWITCH)
unset(CMAKE_REQUIRED_INCLUDES)
# engine/common/build.c
@@ -63,6 +64,8 @@ elseif(XASH_HAIKU)
set(BUILDOS "haiku")
elseif(XASH_SERENITY)
set(BUILDOS "serenityos")
+elseif(XASH_NSWITCH)
+ set(BUILDOS "nswitch")
else()
message(SEND_ERROR "Place your operating system name here! If this is a mistake, try to fix conditions above and report a bug")
endif()
diff --git a/dlls/weapons.h b/dlls/weapons.h
index 616d1d8f..9e0fc901 100644
--- a/dlls/weapons.h
+++ b/dlls/weapons.h
@@ -147,7 +147,6 @@ public:
#define GLOCK_MAX_CLIP 17
#define PYTHON_MAX_CLIP 6
#define MP5_MAX_CLIP 50
-#define MP5_DEFAULT_AMMO 25
#define SHOTGUN_MAX_CLIP 8
#define CROSSBOW_MAX_CLIP 5
#define RPG_MAX_CLIP 1
@@ -169,7 +168,6 @@ public:
#define GLOCK_DEFAULT_GIVE 17
#define PYTHON_DEFAULT_GIVE 6
#define MP5_DEFAULT_GIVE 50
-#define MP5_DEFAULT_AMMO 25
#define MP5_M203_DEFAULT_GIVE 0
#define SHOTGUN_DEFAULT_GIVE 12
#define CROSSBOW_DEFAULT_GIVE 5
diff --git a/public/build.h b/public/build.h
index 5b6bcc36..521e4675 100644
--- a/public/build.h
+++ b/public/build.h
@@ -79,6 +79,7 @@ For more information, please refer to
#undef XASH_WIN32
#undef XASH_WIN64
#undef XASH_X86
+#undef XASH_NSWITCH
//================================================================
//
@@ -96,6 +97,10 @@ For more information, please refer to
#if defined(_WIN64)
#define XASH_WIN64 1
#endif
+#elif defined __SWITCH__
+ #define XASH_NSWITCH 1
+ #define XASH_LITTLE_ENDIAN 1
+ #define XASH_POSIX 1
#elif defined(__linux__)
#define XASH_LINUX 1
#if defined(__ANDROID__)
@@ -134,7 +139,7 @@ For more information, please refer to
#error "Place your operating system name here! If this is a mistake, try to fix conditions above and report a bug"
#endif
-#if defined XASH_ANDROID || defined XASH_IOS
+#if defined XASH_ANDROID || defined XASH_IOS || defined XASH_NSWITCH
#define XASH_MOBILE_PLATFORM 1
#endif
diff --git a/scripts/waifulib/library_naming.py b/scripts/waifulib/library_naming.py
index f3b9762a..3709bc76 100644
--- a/scripts/waifulib/library_naming.py
+++ b/scripts/waifulib/library_naming.py
@@ -61,6 +61,7 @@ DEFINES = [
'XASH_WIN32',
'XASH_WIN64',
'XASH_X86',
+'XASH_NSWITCH',
]
def configure(conf):
@@ -92,6 +93,8 @@ def configure(conf):
buildos = "haiku"
elif conf.env.XASH_SERENITY:
buildos = "serenityos"
+ elif conf.env.XASH_NSWITCH:
+ buildos = "nswitch"
else:
conf.fatal("Place your operating system name in build.h and library_naming.py!\n"
"If this is a mistake, try to fix conditions above and report a bug")
diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py
index b9ce398f..51d8ec87 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,14 +350,100 @@ class Android:
ldflags += ['-march=armv5te']
return ldflags
+class NintendoSwitch:
+ ctx = None # waf context
+ arch = "arm64"
+ 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 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]
+ if cxx:
+ # while these are supported, they could fuck up the crappy dynamic linker
+ cflags += ['-fno-exceptions', '-fno-rtti']
+ # the game wants GNU extensions
+ 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):
+ # NOTE: shared libraries should be built without standard libs, so that they could import their contents from the NRO,
+ # but executables, including the SDL2 sanity check, will generally require libstdc++ and libm, which we will add manually
+ ldflags = [] # ['-lm', '-lstdc++']
+ return ldflags
+
def options(opt):
- android = opt.add_option_group('Android options')
- android.add_option('--android', action='store', dest='ANDROID_OPTS', default=None,
+ xc = opt.add_option_group('Cross compile options')
+ xc.add_option('--android', action='store', dest='ANDROID_OPTS', default=None,
help='enable building for android, format: --android=,,, example: --android=armeabi-v7a-hard,4.9,9')
-
- magx = opt.add_option_group('MotoMAGX options')
- magx.add_option('--enable-magx', action = 'store_true', dest = 'MAGX', default = False,
- help = 'enable targetting for MotoMAGX phones [default: %default]')
+ xc.add_option('--enable-magx', action='store_true', dest='MAGX', default=False,
+ 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:
@@ -389,18 +477,42 @@ def configure(conf):
conf.msg('... C/C++ flags', ' '.join(android.cflags()).replace(android.ndk_home, '$NDK/'))
conf.msg('... link flags', ' '.join(android.linkflags()).replace(android.ndk_home, '$NDK/'))
conf.msg('... ld flags', ' '.join(android.ldflags()).replace(android.ndk_home, '$NDK/'))
-
- # conf.env.ANDROID_OPTS = android
- conf.env.DEST_OS2 = 'android'
elif conf.options.MAGX:
# useless to change toolchain path, as toolchain meant to be placed in this path
toolchain_path = '/opt/toolchains/motomagx/arm-eabi2/lib/'
conf.env.INCLUDES_MAGX = [toolchain_path + i for i in ['ezx-z6/include', 'qt-2.3.8/include']]
conf.env.LIBPATH_MAGX = [toolchain_path + i for i in ['ezx-z6/lib', 'qt-2.3.8/lib']]
conf.env.LINKFLAGS_MAGX = ['-Wl,-rpath-link=' + i for i in conf.env.LIBPATH_MAGX]
+ elif conf.options.MSVC_WINE:
+ try:
+ toolchain_path = conf.environ['MSVC_WINE_PATH']
+ except KeyError:
+ conf.fatal('Set MSVC_WINE_PATH environment variable to the MSVC toolchain root!')
+
+ conf.environ['CC'] = conf.environ['CXX'] = os.path.join(toolchain_path, 'bin', conf.env.MSVC_TARGETS[0], 'cl')
+ conf.environ['LINK_CXX'] = os.path.join(toolchain_path, 'bin', conf.env.MSVC_TARGETS[0], 'link')
+ conf.environ['AR'] = os.path.join(toolchain_path, 'bin', conf.env.MSVC_TARGETS[0], 'lib')
+ conf.environ['WINRC'] = os.path.join(toolchain_path, 'bin', conf.env.MSVC_TARGETS[0], 'rc')
+ 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
- MACRO_TO_DESTOS = OrderedDict({ '__ANDROID__' : 'android' })
+ conf.env.MSVC_WINE = conf.options.MSVC_WINE
+ 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
@@ -433,11 +545,17 @@ compiler_cxx_configure = getattr(compiler_cxx, 'configure')
compiler_c_configure = getattr(compiler_c, 'configure')
def patch_compiler_cxx_configure(conf):
- compiler_cxx_configure(conf)
+ if not conf.env.MSVC_WINE:
+ compiler_cxx_configure(conf)
+ else:
+ conf.load('msvc', funs='no_autodetect')
post_compiler_cxx_configure(conf)
def patch_compiler_c_configure(conf):
- compiler_c_configure(conf)
+ if not conf.env.MSVC_WINE:
+ compiler_c_configure(conf)
+ else:
+ conf.load('msvc', funs='no_autodetect')
post_compiler_c_configure(conf)
setattr(compiler_cxx, 'configure', patch_compiler_cxx_configure)
diff --git a/wscript b/wscript
index e5aabeda..1ee8c148 100644
--- a/wscript
+++ b/wscript
@@ -74,8 +74,10 @@ def configure(conf):
conf.load('msvs msdev strip_on_install')
if conf.env.DEST_OS == 'android':
- conf.options.GOLDSRC = False
+ conf.options.GOLDSRC = conf.env.GOLDSRC = False
conf.env.SERVER_NAME = 'server' # can't be any other name, until specified
+ elif conf.env.DEST_OS == 'nswitch':
+ conf.options.GOLDSRC = conf.env.GOLDSRC = False
if conf.env.MAGX:
enforce_pic = False
@@ -148,6 +150,13 @@ def configure(conf):
cflags += conf.filter_cflags(compiler_optional_flags + c_compiler_optional_flags, cflags)
cxxflags += conf.filter_cxxflags(compiler_optional_flags, cflags)
+ # on the Switch, allow undefined symbols by default, which is needed for libsolder to work
+ # 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'])
+
conf.env.append_unique('CFLAGS', cflags)
conf.env.append_unique('CXXFLAGS', cxxflags)
conf.env.append_unique('LINKFLAGS', linkflags)