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
This commit is contained in:
parent
c6c83a7c03
commit
469d6e2f48
24 changed files with 477 additions and 46 deletions
|
@ -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 @@ from taiseilib.common import (
|
|||
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:])
|
||||
|
|
107
src/arch_switch.c
Normal file
107
src/arch_switch.c
Normal file
|
@ -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;
|
||||
}
|
22
src/arch_switch.h
Normal file
22
src/arch_switch.h
Normal file
|
@ -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,6 +666,7 @@ 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);
|
||||
|
@ -669,6 +674,7 @@ static MenuData* create_options_menu_gamepad(MenuData *parent) {
|
|||
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)
|
||||
|
|
|
@ -97,6 +97,12 @@ else
|
|||
)
|
||||
endif
|
||||
|
||||
if host_machine.system() == 'nx'
|
||||
taisei_src += files(
|
||||
'arch_switch.c',
|
||||
)
|
||||
endif
|
||||
|
||||
sse42_src = []
|
||||
|
||||
subdir('audio')
|
||||
|
@ -242,6 +248,53 @@ if host_machine.system() == 'emscripten'
|
|||
install : true,
|
||||
install_dir : bindir,
|
||||
)
|
||||
elif host_machine.system() == 'nx'
|
||||
taisei_elf_name = '@0@.elf'.format(taisei_basename)
|
||||
taisei_elf = executable(taisei_elf_name, taisei_src, version_deps,
|
||||
dependencies : taisei_deps,
|
||||
c_args : taisei_c_args,
|
||||
c_pch : 'pch/taisei_pch.h',
|
||||
install : true,
|
||||
install_dir : bindir,
|
||||
override_options: ['strip=false'],
|
||||
)
|
||||
bindist_deps += taisei_elf
|
||||
|
||||
taisei_nacp_name = '@0@.nacp'.format(taisei_basename)
|
||||
taisei_nacp = custom_target(taisei_nacp_name,
|
||||
command : [
|
||||
find_program('nacptool'),
|
||||
'--create',
|
||||
nx_app_title,
|
||||
nx_app_author,
|
||||
taisei_version_string,
|
||||
'@OUTPUT@',
|
||||
],
|
||||
build_by_default : true,
|
||||
install : false,
|
||||
output : taisei_nacp_name,
|
||||
)
|
||||
|
||||
taisei_nro_name = '@0@.nro'.format(taisei_basename)
|
||||
taisei_nro = custom_target(taisei_nro_name,
|
||||
# NOTE: Unfortunately we can't just put 'taisei_elf' directly into the command array.
|
||||
# Meson then makes an invalid assumption that we are going to execute it ("use as a generator"),
|
||||
# and aborts because there's no exe wrapper in the cross file (which wouldn't make sense to have).
|
||||
|
||||
command : [
|
||||
find_program('elf2nro'),
|
||||
taisei_elf.full_path(), # workaround for the above issue
|
||||
'@OUTPUT@',
|
||||
'--nacp=@0@'.format(taisei_nacp.full_path()), # if we could pass the path in a standalone argument, we could have meson generate an implicit dependency here...
|
||||
'--icon=@0@'.format(nx_icon_path),
|
||||
],
|
||||
build_by_default : true,
|
||||
depends : [taisei_elf, taisei_nacp],
|
||||
install : true,
|
||||
install_dir : bindir,
|
||||
output : taisei_nro_name,
|
||||
)
|
||||
bindist_deps += taisei_nro
|
||||
else
|
||||
taisei = executable(taisei_basename, taisei_src, version_deps,
|
||||
dependencies : taisei_deps,
|
||||
|
|
|
@ -1,8 +1,22 @@
|
|||
modules = [
|
||||
'gl33',
|
||||
'gles20',
|
||||
'gles30',
|
||||
'null',
|
||||
]
|
||||
|
||||
default_backend = get_option('r_default')
|
||||
if host_machine.system() == 'nx'
|
||||
has_forced_renderer = true
|
||||
forced_renderer = 'gles30'
|
||||
else
|
||||
has_forced_renderer = false
|
||||
forced_renderer = ''
|
||||
endif
|
||||
|
||||
if not get_option('r_@0@'.format(default_backend))
|
||||
error('Default renderer \'@0@\' is not enabled. Enable it with -Dr_@0@=true, or set r_default to something else.'.format(default_backend))
|
||||
default_renderer = has_forced_renderer ? forced_renderer : get_option('r_default')
|
||||
|
||||
if not has_forced_renderer and not get_option('r_@0@'.format(default_renderer))
|
||||
error('Default renderer \'@0@\' is not enabled. Enable it with -Dr_@0@=true, or set r_default to something else.'.format(default_renderer))
|
||||
endif
|
||||
|
||||
renderer_src = files(
|
||||
|
@ -21,19 +35,13 @@ subdir('glescommon')
|
|||
subdir('gles20')
|
||||
subdir('gles30')
|
||||
|
||||
modules = [
|
||||
'gl33',
|
||||
'gles20',
|
||||
'gles30',
|
||||
'null',
|
||||
]
|
||||
|
||||
included_deps = []
|
||||
needed_deps = ['common']
|
||||
r_macro = []
|
||||
|
||||
foreach m : modules
|
||||
if get_option('r_@0@'.format(m))
|
||||
should_include = has_forced_renderer ? m == forced_renderer : get_option('r_@0@'.format(m))
|
||||
if should_include
|
||||
renderer_src += get_variable('r_@0@_src'.format(m))
|
||||
r_macro += ['R(@0@)'.format(m)]
|
||||
enabled_renderers += [m]
|
||||
|
@ -53,4 +61,4 @@ endforeach
|
|||
|
||||
r_macro = ' '.join(r_macro)
|
||||
config.set('TAISEI_BUILDCONF_RENDERER_BACKENDS', r_macro)
|
||||
config.set_quoted('TAISEI_BUILDCONF_RENDERER_DEFAULT', default_backend)
|
||||
config.set_quoted('TAISEI_BUILDCONF_RENDERER_DEFAULT', default_renderer)
|
||||
|
|
|
@ -278,4 +278,11 @@ typedef complex max_align_t;
|
|||
|
||||
#define CASTPTR_ASSUME_ALIGNED(expr, type) ((type*)ASSUME_ALIGNED((expr), alignof(type)))
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include "../arch_switch.h"
|
||||
#define atexit nxAtExit
|
||||
#define exit nxExit
|
||||
#define abort nxAbort
|
||||
#endif
|
||||
|
||||
#endif // IGUARD_util_compat_h
|
||||
|
|
|
@ -37,6 +37,11 @@ if host_machine.system() == 'emscripten'
|
|||
'setup_emscripten.c',
|
||||
'sync_emscripten.c',
|
||||
)
|
||||
elif host_machine.system() == 'nx'
|
||||
vfs_src += files(
|
||||
'setup_switch.c',
|
||||
'sync_noop.c',
|
||||
)
|
||||
else
|
||||
vfs_src += files(
|
||||
'setup_generic.c',
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
* Copyright (c) 2019, p-sam <p-sam@d3vs.net>.
|
||||
*/
|
||||
|
||||
#ifndef IGUARD_vfs_setup_h
|
||||
|
@ -12,7 +13,50 @@
|
|||
#include "taisei.h"
|
||||
|
||||
#include "public.h"
|
||||
#include "loadpacks.h"
|
||||
|
||||
typedef struct VfsSetupFixedPaths {
|
||||
const char* res_path;
|
||||
const char* storage_path;
|
||||
const char* cache_path;
|
||||
} VfsSetupFixedPaths;
|
||||
|
||||
void vfs_setup(CallChain onready);
|
||||
|
||||
static inline void vfs_setup_fixedpaths_onsync(CallChainResult ccr, VfsSetupFixedPaths* paths) {
|
||||
assume(paths != NULL);
|
||||
assume(paths->res_path != NULL);
|
||||
assume(paths->storage_path != NULL);
|
||||
assume(paths->cache_path != NULL);
|
||||
|
||||
log_info("Resource path: %s", paths->res_path);
|
||||
log_info("Storage path: %s", paths->storage_path);
|
||||
log_info("Cache path: %s", paths->cache_path);
|
||||
|
||||
vfs_create_union_mountpoint("/res");
|
||||
|
||||
if(!vfs_mount_syspath("/res-dir", paths->res_path, VFS_SYSPATH_MOUNT_READONLY)) {
|
||||
log_fatal("Failed to mount '%s': %s", paths->res_path, vfs_get_error());
|
||||
}
|
||||
|
||||
if(!vfs_mount_syspath("/storage", paths->storage_path, VFS_SYSPATH_MOUNT_MKDIR)) {
|
||||
log_fatal("Failed to mount '%s': %s", paths->storage_path, vfs_get_error());
|
||||
}
|
||||
|
||||
if(!vfs_mount_syspath("/cache", paths->cache_path, VFS_SYSPATH_MOUNT_MKDIR)) {
|
||||
log_fatal("Failed to mount '%s': %s", paths->cache_path, vfs_get_error());
|
||||
}
|
||||
|
||||
vfs_load_packages("/res-dir", "/res");
|
||||
vfs_mount_alias("/res", "/res-dir");
|
||||
vfs_unmount("/res-dir");
|
||||
|
||||
vfs_mkdir_required("storage/replays");
|
||||
vfs_mkdir_required("storage/screenshots");
|
||||
|
||||
CallChain *next = ccr.ctx;
|
||||
run_call_chain(next, NULL);
|
||||
free(next);
|
||||
}
|
||||
|
||||
#endif // IGUARD_vfs_setup_h
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
* Copyright (c) 2019, p-sam <p-sam@d3vs.net>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
@ -13,32 +14,13 @@
|
|||
#include "util.h"
|
||||
|
||||
static void vfs_setup_onsync(CallChainResult ccr) {
|
||||
const char *res_path = "/" TAISEI_BUILDCONF_DATA_PATH;
|
||||
const char *storage_path = "/persistent/storage";
|
||||
const char *cache_path = "/persistent/cache";
|
||||
VfsSetupFixedPaths paths = {
|
||||
.res_path = "/" TAISEI_BUILDCONF_DATA_PATH,
|
||||
.storage_path = "/persistent/storage",
|
||||
.cache_path ="/persistent/cache",
|
||||
};
|
||||
|
||||
log_info("Resource path: %s", res_path);
|
||||
log_info("Storage path: %s", storage_path);
|
||||
log_info("Cache path: %s", cache_path);
|
||||
|
||||
if(!vfs_mount_syspath("/res", res_path, VFS_SYSPATH_MOUNT_READONLY)) {
|
||||
log_fatal("Failed to mount '%s': %s", res_path, vfs_get_error());
|
||||
}
|
||||
|
||||
if(!vfs_mount_syspath("/storage", storage_path, VFS_SYSPATH_MOUNT_MKDIR)) {
|
||||
log_fatal("Failed to mount '%s': %s", storage_path, vfs_get_error());
|
||||
}
|
||||
|
||||
if(!vfs_mount_syspath("/cache", cache_path, VFS_SYSPATH_MOUNT_MKDIR)) {
|
||||
log_fatal("Failed to mount '%s': %s", cache_path, vfs_get_error());
|
||||
}
|
||||
|
||||
vfs_mkdir_required("storage/replays");
|
||||
vfs_mkdir_required("storage/screenshots");
|
||||
|
||||
CallChain *next = ccr.ctx;
|
||||
run_call_chain(next, NULL);
|
||||
free(next);
|
||||
vfs_setup_fixedpaths_onsync(ccr, &paths);
|
||||
}
|
||||
|
||||
void vfs_setup(CallChain next) {
|
||||
|
|
39
src/vfs/setup_switch.c
Normal file
39
src/vfs/setup_switch.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
* Copyright (c) 2019, p-sam <p-sam@d3vs.net>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "public.h"
|
||||
#include "setup.h"
|
||||
#include "util.h"
|
||||
|
||||
static void vfs_setup_onsync(CallChainResult ccr) {
|
||||
const char *program_dir = nxGetProgramDir();
|
||||
char *res_path = strfmt("%s/%s", program_dir, TAISEI_BUILDCONF_DATA_PATH);
|
||||
char *storage_path = strfmt("%s/storage", program_dir);
|
||||
char *cache_path = strfmt("%s/cache", program_dir);
|
||||
|
||||
VfsSetupFixedPaths paths = {
|
||||
.res_path = res_path,
|
||||
.storage_path = storage_path,
|
||||
.cache_path = cache_path,
|
||||
};
|
||||
|
||||
vfs_setup_fixedpaths_onsync(ccr, &paths);
|
||||
|
||||
free(res_path);
|
||||
free(storage_path);
|
||||
free(cache_path);
|
||||
}
|
||||
|
||||
void vfs_setup(CallChain next) {
|
||||
vfs_init();
|
||||
CallChain *cc = memdup(&next, sizeof(next));
|
||||
vfs_sync(VFS_SYNC_LOAD, CALLCHAIN(vfs_setup_onsync, cc));
|
||||
}
|
17
src/video.c
17
src/video.c
|
@ -66,6 +66,7 @@ static VideoCapabilityState video_query_capability_alwaysfullscreen(VideoCapabil
|
|||
UNREACHABLE;
|
||||
}
|
||||
|
||||
#ifndef __SWITCH__
|
||||
static VideoCapabilityState video_query_capability_webcanvas(VideoCapability cap) {
|
||||
switch(cap) {
|
||||
case VIDEO_CAP_EXTERNAL_RESIZE:
|
||||
|
@ -78,6 +79,7 @@ static VideoCapabilityState video_query_capability_webcanvas(VideoCapability cap
|
|||
return video_query_capability_generic(cap);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void video_add_mode(int width, int height) {
|
||||
if(video.modes) {
|
||||
|
@ -622,6 +624,10 @@ void video_init(void) {
|
|||
const char *driver = SDL_GetCurrentVideoDriver();
|
||||
log_info("Using driver '%s'", driver);
|
||||
|
||||
#ifdef __SWITCH__
|
||||
video.backend = VIDEO_BACKEND_SWITCH;
|
||||
video_query_capability = video_query_capability_alwaysfullscreen;
|
||||
#else
|
||||
video_query_capability = video_query_capability_generic;
|
||||
|
||||
if(!strcmp(driver, "x11")) {
|
||||
|
@ -642,11 +648,12 @@ void video_init(void) {
|
|||
} else {
|
||||
video.backend = VIDEO_BACKEND_OTHER;
|
||||
}
|
||||
#endif
|
||||
|
||||
r_init();
|
||||
|
||||
// Register all resolutions that are available in fullscreen
|
||||
|
||||
#ifndef __SWITCH__
|
||||
for(int s = 0; s < video_num_displays(); ++s) {
|
||||
log_info("Found display #%i: %s", s, video_display_name(s));
|
||||
for(int i = 0; i < SDL_GetNumDisplayModes(s); ++i) {
|
||||
|
@ -660,6 +667,7 @@ void video_init(void) {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!fullscreen_available) {
|
||||
log_warn("No available fullscreen modes");
|
||||
|
@ -669,6 +677,11 @@ void video_init(void) {
|
|||
// Then, add some common 4:3 modes for the windowed mode if they are not there yet.
|
||||
// This is required for some multihead setups.
|
||||
VideoMode common_modes[] = {
|
||||
#ifdef __SWITCH__
|
||||
{640, 480},
|
||||
{1280, 720},
|
||||
{1920, 1080},
|
||||
#else
|
||||
{RESX, RESY},
|
||||
{SCREEN_W, SCREEN_H},
|
||||
|
||||
|
@ -679,7 +692,7 @@ void video_init(void) {
|
|||
{1152, 864},
|
||||
{1400, 1050},
|
||||
{1440, 1080},
|
||||
|
||||
#endif
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ typedef enum VideoBackend {
|
|||
VIDEO_BACKEND_EMSCRIPTEN,
|
||||
VIDEO_BACKEND_KMSDRM,
|
||||
VIDEO_BACKEND_RPI,
|
||||
VIDEO_BACKEND_SWITCH,
|
||||
} VideoBackend;
|
||||
|
||||
typedef struct {
|
||||
|
|
43
switch/README.md
Normal file
43
switch/README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
Taisei Switch Port
|
||||
==================
|
||||
|
||||
<p align="center"><img src="icon.jpg"></p>
|
||||
|
||||
## Installation
|
||||
|
||||
### Grabbing Binaries
|
||||
|
||||
Download the latest release,
|
||||
and extract the archive in the `/switch` folder on your SD Card.
|
||||
Then, run the game from the [hbmenu](https://github.com/switchbrew/nx-hbmenu)
|
||||
using [hbl](https://github.com/switchbrew/nx-hbloader).
|
||||
|
||||
**WARNING:** This will crash if executed from an applet such as the Photo/Library applet,
|
||||
be sure to launch it from hbmenu on top of the game of your choice,
|
||||
which can be done by holding R over any installed title on latest Atmosphère, with default settings.
|
||||
|
||||
### Build dependencies
|
||||
|
||||
For building, you need the devkitA64 from devkitPro setup, along with switch portlibs and libnx.
|
||||
Documentation to setup that can be found [here](https://switchbrew.org/wiki/Setting_up_Development_Environment).
|
||||
|
||||
Other dependencies common to the main targets include:
|
||||
|
||||
* meson >= 0.45.0 (build system; >=0.48.0 recommended)
|
||||
* Python >= 3.5
|
||||
* ninja
|
||||
* glslc
|
||||
* spirv-cross
|
||||
|
||||
### Compiling from source
|
||||
|
||||
Run one of the following commands from the project root:
|
||||
|
||||
```
|
||||
mkdir -p ./build/nx
|
||||
./switch/crossfile.sh > ./build/nx/crossfile.txt
|
||||
meson --cross-file="./build/nx/crossfile.txt" . ./build/nx
|
||||
ninja -C ./build/nx
|
||||
```
|
||||
|
||||
**Note:** You can optionally set a custom prefix and `ninja install` NRO and assets into that folder.
|
44
switch/crossfile.sh
Executable file
44
switch/crossfile.sh
Executable file
|
@ -0,0 +1,44 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
source "$DEVKITPRO/switchvars.sh"
|
||||
|
||||
function meson_arg_list() {
|
||||
while (( "$#" )); do
|
||||
echo -n "'$1'";
|
||||
if [ $# -gt 1 ]; then
|
||||
echo -n ",";
|
||||
fi
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
function bin_path() {
|
||||
which "${TOOL_PREFIX}$1"
|
||||
}
|
||||
|
||||
ADDITIONAL_LINK_FLAGS="-specs=$DEVKITPRO/libnx/switch.specs"
|
||||
|
||||
cat <<DOCEND
|
||||
[binaries]
|
||||
c = '$(bin_path gcc)'
|
||||
cpp = '$(bin_path g++)'
|
||||
ar = '$(bin_path gcc-ar)'
|
||||
strip = '$(bin_path strip)'
|
||||
pkgconfig = '$(bin_path pkg-config)'
|
||||
elf2nro = '$(which elf2nro)'
|
||||
nacptool = '$(which nacptool)'
|
||||
|
||||
[properties]
|
||||
c_args = [$(meson_arg_list $CPPFLAGS $CFLAGS)]
|
||||
c_link_args = [$(meson_arg_list $LDFLAGS $LIBS $ADDITIONAL_LINK_FLAGS)]
|
||||
cpp_args = [$(meson_arg_list $CPPFLAGS $CXXFLAGS)]
|
||||
cpp_link_args = [$(meson_arg_list $LDFLAGS $LIBS $ADDITIONAL_LINK_FLAGS)]
|
||||
|
||||
[host_machine]
|
||||
system = 'nx'
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'cortex-a57'
|
||||
endian = 'little'
|
||||
DOCEND
|
BIN
switch/icon.jpg
Normal file
BIN
switch/icon.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
4
switch/meson.build
Normal file
4
switch/meson.build
Normal file
|
@ -0,0 +1,4 @@
|
|||
nx_app_title = 'Taisei Project'
|
||||
nx_app_author = 'Taisei Project Team, port by p-sam'
|
||||
|
||||
nx_icon_path = join_paths(meson.current_source_dir(), 'icon.jpg')
|
Loading…
Reference in a new issue