Skip to content
Permalink
Browse files

Switch homebrew port (#173)

* Initial port

* Switch specific video modes

* Handle clean exit

* Hide option to disable gamepads, force it enabled

* Match buttons layout with Switch controllers

* Hide player name setting (to avoid getting stuck)

* Switch specific VFS setup instead of env bootstrap

* Clean up, get rid of warnings and a few ifdefs

* Add Switch specific build script and assets

* Make vfs setup mount packages

* Re-enable packaging on Switch

* Transpile shaders to es and install them

* Add applet warning and shader deps to README

* Remove script; instead using meson build scripts

* Strict prototypes

* Build script compat with project minimum meson ver

Refactor of pack.py exclude option

* Uniformise header inclusion on arch_switch.c

* Allow input for any dev; hide the option on Switch

* Silence unsused function warnings
  • Loading branch information...
p-sam authored and Akaricchi committed Aug 2, 2019
1 parent c6c83a7 commit 469d6e2f4889dfe779e0cb7be62cf3f92bad5f86
@@ -117,7 +117,7 @@ if sm_check.stderr() != ''
warning('Submodule check completed with errors:\n@0@'.format(sm_check.stderr()))
endif

static = get_option('static') or (host_machine.system() == 'emscripten')
static = get_option('static') or ['emscripten', 'nx'].contains(host_machine.system())

dep_freetype = dependency('freetype2', required : true, static : static, fallback : ['freetype', 'freetype_dep'])
dep_opusfile = dependency('opusfile', required : false, static : static, fallback : ['opusfile', 'opusfile_dep'])
@@ -197,6 +197,7 @@ prefer_relpath_systems = [

force_relpath_systems = [
'emscripten',
'nx'
]

if macos_app_bundle
@@ -268,7 +269,7 @@ config.set('TAISEI_BUILDCONF_LOG_FATAL_MSGBOX', (
))
config.set('TAISEI_BUILDCONF_DEBUG_OPENGL', get_option('debug_opengl'))

install_docs = get_option('docs') and host_machine.system() != 'emscripten'
install_docs = get_option('docs') and not ['emscripten', 'nx'].contains(host_machine.system())

if host_machine.system() == 'windows'
if install_docs
@@ -310,6 +311,7 @@ bindist_deps = []

subdir('misc')
subdir('emscripten')
subdir('switch')
subdir('external')
subdir('resources')
subdir('doc')
@@ -363,7 +365,7 @@ Summary:
', '.join(enabled_audio_backends),
get_option('a_default'),
', '.join(enabled_renderers),
get_option('r_default'),
default_renderer,
get_option('shader_transpiler'),
enable_zip,
package_data,
@@ -140,7 +140,7 @@ spvc_vert_args = [
'--stage', 'vert',
]

if host_machine.system() == 'emscripten'
if ['emscripten', 'nx'].contains(host_machine.system())
validate_glsl = 'true'
transpile_glsl = true
else
@@ -81,6 +81,7 @@ foreach pkg : packages
pkg_path,
'@OUTPUT@',
'--depfile', '@DEPFILE@',
'--exclude', '**/meson.build',
],
output : pkg_zip,
depfile : '@0@.d'.format(pkg_zip),
@@ -94,6 +95,31 @@ foreach pkg : packages
endif
endforeach

if host_machine.system() == 'nx'
# Package shaders that were transpiled
shader_pkg_zip = '01-es-shaders.zip'
shader_pkg_path = join_paths(shaders_build_dir, '..')
if package_data
bindist_deps += custom_target(shader_pkg_zip,
command : [pack_command,
shader_pkg_path,
'@OUTPUT@',
'--depfile', '@DEPFILE@',
'--exclude', '**/*.spv',
'--exclude', '**/meson.build',
],
output : shader_pkg_zip,
depfile : '@0@.d'.format(shader_pkg_zip),
install : true,
install_dir : data_path,
)
else
glob_result = run_command(glob_command, shaders_build_dir, '**/*.spv', '**/meson.build')
assert(glob_result.returncode() == 0, 'Glob script failed')
install_subdir(shaders_build_dir, install_dir : data_path, exclude_files : glob_result.stdout().split('\n'))
endif
endif

if host_machine.system() == 'emscripten'
# First add some stuff that isn't sourced from resources/

@@ -24,7 +24,6 @@
write_depfile,
)


def pack(args):
nocompress_file = args.directory / '.nocompress'

@@ -40,7 +39,7 @@ def pack(args):

with ZipFile(str(args.output), 'w', ZIP_DEFLATED, **zkwargs) as zf:
for path in sorted(args.directory.glob('**/*')):
if path.name[0] == '.' or path.name == 'meson.build':
if path.name[0] == '.' or any(path.match(x) for x in args.exclude):
continue

relpath = path.relative_to(args.directory)
@@ -82,6 +81,12 @@ def main(args):
help='the output archive path'
)

parser.add_argument('--exclude',
action='append',
default=[],
help='file exclusion pattern'
)

add_common_args(parser, depfile=True)

args = parser.parse_args(args[1:])
@@ -0,0 +1,107 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2019, p-sam <p-sam@d3vs.net>.
*/

#include "taisei.h"

#include "arch_switch.h"

#include <switch/runtime/devices/socket.h>
#include <switch/runtime/nxlink.h>
#include <switch/services/applet.h>
#include <switch/services/fs.h>

#define NX_LOG_FMT(fmt, ...) tsfprintf(stdout, "[NX] " fmt "\n", ##__VA_ARGS__)
#define NX_LOG(str) NX_LOG_FMT("%s", str)
#define NX_SETENV(name, val) NX_LOG_FMT("Setting env var %s to %s", name, val);env_set_string(name, val, true)

static nxAtExitFn g_nxAtExitFn = NULL;
static char g_programDir[FS_MAX_PATH] = {0};
static AppletHookCookie g_hookCookie;

static void onAppletHook(AppletHookType hook, void *param) {
switch (hook) {
case AppletHookType_OnExitRequest:
NX_LOG("Got AppletHook OnExitRequest, exiting.\n");
taisei_quit();
break;

default:
break;
}
}

attr_used
void userAppInit(void) {
socketInitializeDefault();
appletLockExit();
appletHook(&g_hookCookie, onAppletHook, NULL);

#ifdef DEBUG
dup2(1, 2);
NX_LOG("stderr -> stdout");
nxlinkStdio();
NX_LOG("nxlink enabled");
NX_SETENV("TAISEI_NOASYNC", "1");
#endif

appletInitializeGamePlayRecording();
appletSetGamePlayRecordingState(1);

getcwd(g_programDir, FS_MAX_PATH);

#if defined(DEBUG) && defined(TAISEI_BUILDCONF_DEBUG_OPENGL)
// enable Mesa logging:
NX_SETENV("EGL_LOG_LEVEL", "debug");
NX_SETENV("MESA_VERBOSE", "all");
NX_SETENV("MESA_DEBUG", "1");
NX_SETENV("NOUVEAU_MESA_DEBUG", "1");

// enable shader debugging in Nouveau:
NX_SETENV("NV50_PROG_OPTIMIZE", "0");
NX_SETENV("NV50_PROG_DEBUG", "1");
NX_SETENV("NV50_PROG_CHIPSET", "0x120");
#else
// disable error checking and save CPU time
NX_SETENV("MESA_NO_ERROR", "1");
#endif
}

attr_used
void userAppExit(void) {
if(g_nxAtExitFn != NULL) {
NX_LOG("calling exit callback");
g_nxAtExitFn();
g_nxAtExitFn = NULL;
}
socketExit();
appletUnlockExit();
}

int nxAtExit(nxAtExitFn fn) {
if(g_nxAtExitFn == NULL) {
NX_LOG("got exit callback");
g_nxAtExitFn = fn;
return 0;
}
return -1;
}

void __attribute__((weak)) noreturn __libnx_exit(int rc);

void noreturn nxExit(int rc) {
__libnx_exit(rc);
}

void noreturn nxAbort(void) {
/* Using abort would not give us correct offsets in crash reports,
* nor code region name, so we use __builtin_trap instead */
__builtin_trap();
}

const char* nxGetProgramDir(void) {
return g_programDir;
}
@@ -0,0 +1,22 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2019, p-sam <p-sam@d3vs.net>.
*/

#ifndef IGUARD_arch_switch_h
#define IGUARD_arch_switch_h

#include "taisei.h"

typedef void (*nxAtExitFn)(void);

void userAppInit(void);
void userAppExit(void);
int nxAtExit(nxAtExitFn fn);
void noreturn nxExit(int rc);
void noreturn nxAbort(void);
const char* nxGetProgramDir(void);

#endif // IGUARD_arch_switch_h
@@ -476,4 +476,9 @@ void config_load(void) {

// set config version to the latest
config_set_int(CONFIG_VERSION, sizeof(config_upgrades) / sizeof(ConfigUpgradeFunc));

#ifdef __SWITCH__
config_set_int(CONFIG_GAMEPAD_ENABLED, true);
config_set_str(CONFIG_GAMEPAD_DEVICE, "any");
#endif
}
@@ -44,10 +44,17 @@ typedef enum GamepadEmulatedButton {
typedef enum GamepadButton {
// must match SDL_GameControllerButton
GAMEPAD_BUTTON_INVALID = -1,
#ifdef __SWITCH__
GAMEPAD_BUTTON_B,
GAMEPAD_BUTTON_A,
GAMEPAD_BUTTON_Y,
GAMEPAD_BUTTON_X,
#else
GAMEPAD_BUTTON_A,
GAMEPAD_BUTTON_B,
GAMEPAD_BUTTON_X,
GAMEPAD_BUTTON_Y,
#endif
GAMEPAD_BUTTON_BACK,
GAMEPAD_BUTTON_GUIDE,
GAMEPAD_BUTTON_START,
@@ -50,8 +50,13 @@

enum {
// defaults
#ifdef __SWITCH__
RESX = 1280,
RESY = 720,
#else
RESX = 800,
RESY = 600,
#endif

VIEWPORT_X = 40,
VIEWPORT_Y = 20,
@@ -91,8 +91,9 @@ noreturn static void log_abort(const char *msg) {
}
#endif

// abort() doesn't clean up, but it lets us get a backtrace, which is more useful
log_shutdown();

// abort() doesn't clean up, but it lets us get a backtrace, which is more useful
abort();
}

@@ -164,6 +164,7 @@ static int bind_gpdev_set(OptionBinding *b, int v) {
return b->selected;
}

#ifndef __SWITCH__
// BT_GamepadDevice: dynamic device list
static OptionBinding* bind_gpdevice(int cfgentry) {
OptionBinding *bind = bind_new();
@@ -191,6 +192,7 @@ static OptionBinding* bind_stroption(ConfigIndex cfgentry) {

return bind;
}
#endif

// BT_Resolution: super-special binding type for the resolution setting
static void bind_resolution_update(OptionBinding *bind) {
@@ -590,9 +592,11 @@ static void bind_setvaluerange_fancy(OptionBinding *b, int ma) {
}
}

#ifndef __SWITCH__
static bool gamepad_enabled_depencence(void) {
return config_get_int(CONFIG_GAMEPAD_ENABLED);
}
#endif

static MenuData* create_options_menu_gamepad_controls(MenuData *parent) {
MenuData *m = create_options_menu_base("Gamepad Controls");
@@ -662,13 +666,15 @@ static MenuData* create_options_menu_gamepad(MenuData *parent) {

OptionBinding *b;

#ifndef __SWITCH__
add_menu_entry(m, "Enable Gamepad/Joystick support", do_nothing,
b = bind_option(CONFIG_GAMEPAD_ENABLED, bind_common_onoff_get, bind_common_onoff_set)
); bind_onoff(b);

add_menu_entry(m, "Device", do_nothing,
b = bind_gpdevice(CONFIG_GAMEPAD_DEVICE)
); b->dependence = gamepad_enabled_depencence;
#endif

add_menu_separator(m);
add_menu_entry(m, "Customize controls…", enter_options_menu_gamepad_controls, NULL);
@@ -811,11 +817,13 @@ MenuData* create_options_menu(void) {
MenuData *m = create_options_menu_base("Options");
OptionBinding *b;

#ifndef __SWITCH__
add_menu_entry(m, "Player name", do_nothing,
b = bind_stroption(CONFIG_PLAYERNAME)
);

add_menu_separator(m);
#endif

add_menu_entry(m, "Save replays", do_nothing,
b = bind_option(CONFIG_SAVE_RPY, bind_common_onoffplus_get, bind_common_onoffplus_set)

0 comments on commit 469d6e2

Please sign in to comment.
You can’t perform that action at this time.