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)