diff --git a/.gitignore b/.gitignore index 66e3d83..f68da9c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Xmake cache .xmake/ build/ +local_packages/ +*.log +PLAN.md # MacOS Cache .DS_Store diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..e65713b --- /dev/null +++ b/BUILD.md @@ -0,0 +1,269 @@ +# Frostbite2D 编译说明 + +本文档记录当前 Windows PC 和 Nintendo Switch 版本的编译方法。命令默认在项目根目录执行: + +```powershell +cd X:\NS_unknown_game\Frostbite2D-Engine +``` + +## 1. Windows PC 编译 + +### 1.1 前置环境 + +当前机器已验证可用: + +- Visual Studio 2026 MSVC x64 工具链 +- XMake:`C:\Program Files\xmake\xmake.exe` +- CMake:`C:\Program Files\CMake\bin\cmake.exe` +- Python 3.13,供 XMake 构建部分依赖包使用 + +XMake 会自动拉取这些 PC 依赖: + +- SDL2 +- SDL2_image +- SDL2_mixer +- SDL2_ttf +- GLM +- zlib + +### 1.2 编译引擎静态库 + +```powershell +$env:PATH='C:\Program Files\CMake\bin;C:\Program Files\xmake;' + $env:PATH + +& 'C:\Program Files\xmake\xmake.exe' f -c -p windows -a x64 -m release -y +& 'C:\Program Files\xmake\xmake.exe' build Frostbite2D +``` + +产物: + +```text +build\windows\x64\release\Frostbite2D.lib +``` + +### 1.3 编译 Windows 测试 EXE + +项目内有一个最小 Windows 测试程序: + +```text +examples\windows_smoke\main.cpp +``` + +编译命令: + +```powershell +$env:PATH='C:\Program Files\CMake\bin;C:\Program Files\xmake;' + $env:PATH + +& 'C:\Program Files\xmake\xmake.exe' f -c -p windows -a x64 -m release -y +& 'C:\Program Files\xmake\xmake.exe' build Frostbite2DWindowsSmoke +``` + +产物: + +```text +build\windows\x64\release\Frostbite2DWindowsSmoke.exe +``` + +运行 EXE 时,需要把依赖 DLL 放在 EXE 同目录。当前已验证至少需要: + +```text +SDL2.dll +SDL2_image.dll +SDL2_mixer.dll +SDL2_ttf.dll +FLAC.dll +``` + +如果重新清理构建目录后 DLL 丢失,可以从 XMake 包缓存复制。当前机器路径示例: + +```powershell +$out='build\windows\x64\release' + +Copy-Item 'C:\Users\11655\AppData\Local\.xmake\packages\l\libsdl2\2.32.10\9cf157962d784bab85f45cbb66d68046\bin\SDL2.dll' $out -Force +Copy-Item 'C:\Users\11655\AppData\Local\.xmake\packages\l\libsdl2_image\2.8.8\026ef5efba044dc0824fc0125a4307c9\bin\SDL2_image.dll' $out -Force +Copy-Item 'C:\Users\11655\AppData\Local\.xmake\packages\l\libsdl2_mixer\2.8.1\03da77434a3f43dfa9c6e52b7ea5dafd\bin\SDL2_mixer.dll' $out -Force +Copy-Item 'C:\Users\11655\AppData\Local\.xmake\packages\l\libsdl2_ttf\2.24.0\56f7684f28e04a3eb194f8f4a4ed4c2b\bin\SDL2_ttf.dll' $out -Force +Copy-Item 'C:\Users\11655\AppData\Local\.xmake\packages\l\libflac\1.5.0\96b3cdc9f5834a3c9ac2a033627c794b\bin\FLAC.dll' $out -Force +``` + +运行测试: + +```powershell +& build\windows\x64\release\Frostbite2DWindowsSmoke.exe +``` + +正常输出中应包含: + +```text +Frostbite2D Windows smoke init ok. +Renderer initialized +TaskSystem initialized +Application stopped +``` + +## 2. Nintendo Switch 编译 + +### 2.1 前置环境 + +当前机器已验证可用: + +- devkitPro:`C:\devkitPro` +- devkitA64:`C:\devkitPro\devkitA64` +- libnx:`C:\devkitPro\libnx` +- Switch portlibs:`C:\devkitPro\portlibs\switch` +- XMake:`C:\Program Files\xmake\xmake.exe` + +Switch 版依赖不走 XMake 的桌面包,而是使用 devkitPro 的 portlibs: + +- SDL2 +- SDL2_image +- SDL2_mixer +- SDL2_ttf +- GLM +- zlib +- libnx + +### 2.2 编译 Switch 引擎静态库 + +```powershell +$env:PATH='C:\devkitPro\devkitA64\bin;C:\devkitPro\tools\bin;C:\devkitPro\msys2\usr\bin;C:\Program Files\CMake\bin;C:\Program Files\xmake;' + $env:PATH +$env:DEVKITPRO='C:\devkitPro' +$env:DEVKITA64='C:\devkitPro\devkitA64' + +& 'C:\Program Files\xmake\xmake.exe' f -c -p switch -a aarch64 -m release --toolchain=cross --sdk='C:\devkitPro\devkitA64' --bin='C:\devkitPro\devkitA64\bin' --cross='aarch64-none-elf-' -y +& 'C:\Program Files\xmake\xmake.exe' build Frostbite2D +``` + +产物: + +```text +build\switch\aarch64\release\libFrostbite2D.a +``` + +### 2.3 编译 Switch NRO 测试程序 + +项目内有一个最小 Switch 测试程序: + +```text +examples\switch_smoke\main.cpp +``` + +它会调用: + +```cpp +frostbite2D::switchInit(); +frostbite2D::switchShutdown(); +``` + +先确保 `libFrostbite2D.a` 已经生成,然后执行: + +```powershell +$env:PATH='C:\devkitPro\devkitA64\bin;C:\devkitPro\tools\bin;C:\devkitPro\msys2\usr\bin;' + $env:PATH +$env:DEVKITPRO='C:\devkitPro' +$env:DEVKITA64='C:\devkitPro\devkitA64' + +$out='build\switch\aarch64\release\switch_smoke' +New-Item -ItemType Directory -Force -Path $out | Out-Null + +$common=@( + '-g', + '-O2', + '-D__SWITCH__', + '-march=armv8-a+crc+crypto', + '-mtune=cortex-a57', + '-mtp=soft', + '-ffunction-sections', + '-fdata-sections', + '-fPIE', + '-IFrostbite2D\include', + '-IFrostbite2D\third_party\quickjspp', + '-IC:\devkitPro\libnx\include', + '-IC:\devkitPro\portlibs\switch\include', + '-IC:\devkitPro\portlibs\switch\include\SDL2', + '-specs=C:\devkitPro\libnx\switch.specs' +) + +$compileArgs=@('-c','-std=c++17') + $common + @( + '-o', "$out\main.o", + 'examples\switch_smoke\main.cpp' +) +& C:\devkitPro\devkitA64\bin\aarch64-none-elf-g++.exe @compileArgs +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +$linkArgs=@( + '-specs=C:\devkitPro\libnx\switch.specs', + '-g', + '-march=armv8-a+crc+crypto', + '-mtune=cortex-a57', + '-mtp=soft', + '-fPIE', + '-Lbuild\switch\aarch64\release', + '-LC:\devkitPro\libnx\lib', + '-o', "$out\Frostbite2D_smoke.elf", + "$out\main.o", + '-lFrostbite2D', + '-lnx' +) +& C:\devkitPro\devkitA64\bin\aarch64-none-elf-g++.exe @linkArgs +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +& C:\devkitPro\tools\bin\nacptool.exe --create 'Frostbite2D Smoke' 'Frostbite2D' '1.0.0' "$out\control.nacp" +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +& C:\devkitPro\tools\bin\elf2nro.exe "$out\Frostbite2D_smoke.elf" "$out\Frostbite2D_smoke_meta.nro" --nacp="$out\control.nacp" --icon=C:\devkitPro\libnx\default_icon.jpg +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +``` + +产物: + +```text +build\switch\aarch64\release\switch_smoke\Frostbite2D_smoke.elf +build\switch\aarch64\release\switch_smoke\Frostbite2D_smoke_meta.nro +build\switch\aarch64\release\switch_smoke\control.nacp +``` + +推荐运行带元数据的版本: + +```text +Frostbite2D_smoke_meta.nro +``` + +不带 NACP/icon 的 NRO 在 Ryujinx 1.3.3 中可能触发 `OpenNroAssetSection` 相关加载错误。 + +正常运行后屏幕显示: + +```text +Frostbite2D Switch smoke test +Press PLUS to exit. +``` + +## 3. 常见问题 + +### 3.1 切换平台后编译异常 + +XMake 会缓存当前平台配置。Windows 和 Switch 来回切换时,建议使用 `f -c` 重新配置: + +```powershell +& 'C:\Program Files\xmake\xmake.exe' f -c -p windows -a x64 -m release -y +``` + +或: + +```powershell +& 'C:\Program Files\xmake\xmake.exe' f -c -p switch -a aarch64 -m release --toolchain=cross --sdk='C:\devkitPro\devkitA64' --bin='C:\devkitPro\devkitA64\bin' --cross='aarch64-none-elf-' -y +``` + +### 3.2 Windows EXE 启动时报 `0xC0000135` + +这是缺 DLL。把 SDL 相关 DLL 复制到 EXE 同目录即可。 + +### 3.3 Switch NRO 在 Ryujinx 中加载失败 + +优先使用带 NACP/icon 的: + +```text +Frostbite2D_smoke_meta.nro +``` + +不要优先使用裸的 `Frostbite2D_smoke.nro`。 + diff --git a/Frostbite2D/include/frostbite2D/base/object_registry.h b/Frostbite2D/include/frostbite2D/base/object_registry.h index dc8c529..aae17de 100644 --- a/Frostbite2D/include/frostbite2D/base/object_registry.h +++ b/Frostbite2D/include/frostbite2D/base/object_registry.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace frostbite2D { diff --git a/Frostbite2D/include/frostbite2D/platform/windows.h b/Frostbite2D/include/frostbite2D/platform/windows.h index 57e8812..a650bb9 100644 --- a/Frostbite2D/include/frostbite2D/platform/windows.h +++ b/Frostbite2D/include/frostbite2D/platform/windows.h @@ -2,6 +2,14 @@ #ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifndef NOMINMAX +#define NOMINMAX +#endif + #include namespace frostbite2D { diff --git a/Frostbite2D/third_party/quickjspp/quickjs/cutils.h b/Frostbite2D/third_party/quickjspp/quickjs/cutils.h index 31f7cd8..6ce4e9f 100644 --- a/Frostbite2D/third_party/quickjspp/quickjs/cutils.h +++ b/Frostbite2D/third_party/quickjspp/quickjs/cutils.h @@ -27,15 +27,32 @@ #include #include +#if defined(_MSC_VER) +#include +#endif /* set if CPU is big endian */ #undef WORDS_BIGENDIAN +#if defined(_MSC_VER) +#define likely(x) (x) +#define unlikely(x) (x) +#define force_inline __forceinline +#define no_inline __declspec(noinline) +#define __maybe_unused +#ifndef __attribute__ +#define __attribute__(x) +#endif +#ifndef __attribute +#define __attribute(x) +#endif +#else #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #define force_inline inline __attribute__((always_inline)) #define no_inline __attribute__((noinline)) #define __maybe_unused __attribute__((unused)) +#endif #define xglue(x, y) x ## y #define glue(x, y) xglue(x, y) @@ -114,27 +131,69 @@ static inline int64_t min_int64(int64_t a, int64_t b) /* WARNING: undefined if a = 0 */ static inline int clz32(unsigned int a) { +#if defined(_MSC_VER) + unsigned long r; + _BitScanReverse(&r, a); + return 31 - (int)r; +#else return __builtin_clz(a); +#endif } /* WARNING: undefined if a = 0 */ static inline int clz64(uint64_t a) { +#if defined(_MSC_VER) +#if defined(_M_X64) || defined(_M_ARM64) + unsigned long r; + _BitScanReverse64(&r, a); + return 63 - (int)r; +#else + uint32_t h = (uint32_t)(a >> 32); + if (h != 0) + return clz32(h); + return 32 + clz32((uint32_t)a); +#endif +#else return __builtin_clzll(a); +#endif } /* WARNING: undefined if a = 0 */ static inline int ctz32(unsigned int a) { +#if defined(_MSC_VER) + unsigned long r; + _BitScanForward(&r, a); + return (int)r; +#else return __builtin_ctz(a); +#endif } /* WARNING: undefined if a = 0 */ static inline int ctz64(uint64_t a) { +#if defined(_MSC_VER) +#if defined(_M_X64) || defined(_M_ARM64) + unsigned long r; + _BitScanForward64(&r, a); + return (int)r; +#else + uint32_t l = (uint32_t)a; + if (l != 0) + return ctz32(l); + return 32 + ctz32((uint32_t)(a >> 32)); +#endif +#else return __builtin_ctzll(a); +#endif } +#if defined(_MSC_VER) +#pragma pack(push, 1) +#endif + struct __attribute__((packed)) packed_u64 { uint64_t v; }; @@ -147,6 +206,10 @@ struct __attribute__((packed)) packed_u16 { uint16_t v; }; +#if defined(_MSC_VER) +#pragma pack(pop) +#endif + static inline uint64_t get_u64(const uint8_t *tab) { return ((const struct packed_u64 *)tab)->v; diff --git a/Frostbite2D/third_party/quickjspp/quickjs/quickjs.c b/Frostbite2D/third_party/quickjspp/quickjs/quickjs.c index 461e492..8275d6c 100644 --- a/Frostbite2D/third_party/quickjspp/quickjs/quickjs.c +++ b/Frostbite2D/third_party/quickjspp/quickjs/quickjs.c @@ -28,7 +28,11 @@ #include #include #include +#if defined(_WIN32) +#include +#else #include +#endif #include #include #include @@ -50,7 +54,7 @@ #define OPTIMIZE 1 #define SHORT_OPCODES 1 -#if defined(EMSCRIPTEN) +#if defined(EMSCRIPTEN) || defined(_MSC_VER) #define DIRECT_DISPATCH 0 #else #define DIRECT_DISPATCH 1 @@ -69,11 +73,11 @@ /* define to include Atomics.* operations which depend on the OS threads */ -#if !defined(EMSCRIPTEN) +#if !defined(EMSCRIPTEN) && !defined(_WIN32) #define CONFIG_ATOMICS #endif -#if !defined(EMSCRIPTEN) +#if !defined(EMSCRIPTEN) && !defined(_WIN32) /* enable stack limitation */ #define CONFIG_STACK_CHECK #endif @@ -115,6 +119,43 @@ #include #endif +#if defined(_WIN32) +static int gettimeofday(struct timeval *tv, void *tz) +{ + FILETIME ft; + uint64_t t; + (void)tz; + + GetSystemTimeAsFileTime(&ft); + t = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + t -= UINT64_C(116444736000000000); + tv->tv_sec = (long)(t / 10000000); + tv->tv_usec = (long)((t % 10000000) / 10); + return 0; +} + +#ifndef ssize_t +typedef intptr_t ssize_t; +#endif + +static struct tm *localtime_r(const time_t *timep, struct tm *result) +{ + return localtime_s(result, timep) == 0 ? result : NULL; +} + +static double js_get_infinity(int sign) +{ + volatile double zero = 0.0; + double inf = 1.0 / zero; + return sign < 0 ? -inf : inf; +} +#else +static double js_get_infinity(int sign) +{ + return sign < 0 ? -1.0 / 0.0 : 1.0 / 0.0; +} +#endif + enum { /* classid tag */ /* union usage | properties */ JS_CLASS_OBJECT = 1, /* must be first */ @@ -7249,7 +7290,7 @@ static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, JS_ThrowTypeErrorNotASymbol(ctx); goto fail; } - prop = js_symbol_to_atom(ctx, (JSValue)name); + prop = js_symbol_to_atom(ctx, name); p = JS_VALUE_GET_OBJ(obj); prs = find_own_property(&pr, p, prop); if (prs) { @@ -7280,7 +7321,7 @@ static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, /* safety check */ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) return JS_ThrowTypeErrorNotASymbol(ctx); - prop = js_symbol_to_atom(ctx, (JSValue)name); + prop = js_symbol_to_atom(ctx, name); p = JS_VALUE_GET_OBJ(obj); prs = find_own_property(&pr, p, prop); if (!prs) { @@ -7307,7 +7348,7 @@ static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, JS_ThrowTypeErrorNotASymbol(ctx); goto fail; } - prop = js_symbol_to_atom(ctx, (JSValue)name); + prop = js_symbol_to_atom(ctx, name); p = JS_VALUE_GET_OBJ(obj); prs = find_own_property(&pr, p, prop); if (!prs) { @@ -7397,7 +7438,7 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) goto not_obj; p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand)); if (!prs) { JS_ThrowTypeError(ctx, "invalid brand on object"); return -1; @@ -9051,7 +9092,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, return -1; } /* this code relies on the fact that Uint32 are never allocated */ - val = (JSValueConst)JS_NewUint32(ctx, array_length); + val = JS_NewUint32(ctx, array_length); /* prs may have been modified */ prs = find_own_property(&pr, p, prop); assert(prs != NULL); @@ -10246,7 +10287,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, } else #endif { - double d = 1.0 / 0.0; + double d = js_get_infinity(1); if (is_neg) d = -d; val = JS_NewFloat64(ctx, d); @@ -16052,7 +16093,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, #else sf->js_mode = 0; #endif - sf->cur_func = (JSValue)func_obj; + sf->cur_func = func_obj; sf->arg_count = argc; arg_buf = argv; @@ -16296,7 +16337,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sf->js_mode = b->js_mode; arg_buf = argv; sf->arg_count = argc; - sf->cur_func = (JSValue)func_obj; + sf->cur_func = func_obj; init_list_head(&sf->var_ref_list); var_refs = p->u.func.var_refs; @@ -39292,8 +39333,8 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, if (!JS_IsUndefined(mapperFunction)) { JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source }; element = JS_Call(ctx, mapperFunction, thisArg, 3, args); - JS_FreeValue(ctx, (JSValue)args[0]); - JS_FreeValue(ctx, (JSValue)args[1]); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); if (JS_IsException(element)) return -1; } @@ -39839,9 +39880,9 @@ static const JSCFunctionListEntry js_number_funcs[] = { JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ), JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ), JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ), - JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), - JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ), - JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ), + JS_PROP_DOUBLE_DEF("NaN", JS_FLOAT64_NAN, 0 ), + JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -1.0 / 0.0, 0 ), + JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", 1.0 / 0.0, 0 ), JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */ JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */ JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */ @@ -40710,7 +40751,7 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, str = JS_NewString(ctx, "g"); if (JS_IsException(str)) goto fail; - args[args_len++] = (JSValueConst)str; + args[args_len++] = str; } rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); JS_FreeValue(ctx, str); @@ -41759,6 +41800,32 @@ static double js_fmax(double a, double b) } } +static double js_math_fabs_wrap(double x) { return fabs(x); } +static double js_math_floor_wrap(double x) { return floor(x); } +static double js_math_ceil_wrap(double x) { return ceil(x); } +static double js_math_sqrt_wrap(double x) { return sqrt(x); } +static double js_math_acos_wrap(double x) { return acos(x); } +static double js_math_asin_wrap(double x) { return asin(x); } +static double js_math_atan_wrap(double x) { return atan(x); } +static double js_math_atan2_wrap(double x, double y) { return atan2(x, y); } +static double js_math_cos_wrap(double x) { return cos(x); } +static double js_math_exp_wrap(double x) { return exp(x); } +static double js_math_log_wrap(double x) { return log(x); } +static double js_math_sin_wrap(double x) { return sin(x); } +static double js_math_tan_wrap(double x) { return tan(x); } +static double js_math_trunc_wrap(double x) { return trunc(x); } +static double js_math_cosh_wrap(double x) { return cosh(x); } +static double js_math_sinh_wrap(double x) { return sinh(x); } +static double js_math_tanh_wrap(double x) { return tanh(x); } +static double js_math_acosh_wrap(double x) { return acosh(x); } +static double js_math_asinh_wrap(double x) { return asinh(x); } +static double js_math_atanh_wrap(double x) { return atanh(x); } +static double js_math_expm1_wrap(double x) { return expm1(x); } +static double js_math_log1p_wrap(double x) { return log1p(x); } +static double js_math_log2_wrap(double x) { return log2(x); } +static double js_math_log10_wrap(double x) { return log10(x); } +static double js_math_cbrt_wrap(double x) { return cbrt(x); } + static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { @@ -41768,7 +41835,7 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, uint32_t tag; if (unlikely(argc == 0)) { - return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); + return __JS_NewFloat64(ctx, js_get_infinity(is_max ? -1 : 1)); } tag = JS_VALUE_GET_TAG(argv[0]); @@ -41943,36 +42010,36 @@ static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, static const JSCFunctionListEntry js_math_funcs[] = { JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), - JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ), - JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ), - JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ), + JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, js_math_fabs_wrap ), + JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, js_math_floor_wrap ), + JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, js_math_ceil_wrap ), JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), - JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ), + JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, js_math_sqrt_wrap ), - JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ), - JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ), - JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ), - JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ), - JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ), - JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ), - JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ), + JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, js_math_acos_wrap ), + JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, js_math_asin_wrap ), + JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, js_math_atan_wrap ), + JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, js_math_atan2_wrap ), + JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, js_math_cos_wrap ), + JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, js_math_exp_wrap ), + JS_CFUNC_SPECIAL_DEF("log", 1, f_f, js_math_log_wrap ), JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ), - JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ), - JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ), + JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, js_math_sin_wrap ), + JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, js_math_tan_wrap ), /* ES6 */ - JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ), + JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, js_math_trunc_wrap ), JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ), - JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ), - JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ), - JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ), - JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ), - JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ), - JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ), - JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ), - JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ), - JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ), - JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ), - JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ), + JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, js_math_cosh_wrap ), + JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, js_math_sinh_wrap ), + JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, js_math_tanh_wrap ), + JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, js_math_acosh_wrap ), + JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, js_math_asinh_wrap ), + JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, js_math_atanh_wrap ), + JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, js_math_expm1_wrap ), + JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, js_math_log1p_wrap ), + JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, js_math_log2_wrap ), + JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, js_math_log10_wrap ), + JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, js_math_cbrt_wrap ), JS_CFUNC_DEF("hypot", 2, js_math_hypot ), JS_CFUNC_DEF("random", 0, js_math_random ), JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), @@ -45738,7 +45805,7 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, } else { JS_DupValue(ctx, key); } - mr->key = (JSValue)key; + mr->key = key; h = map_hash_key(ctx, key) & (s->hash_size - 1); list_add_tail(&mr->hash_link, &s->hash_table[h]); list_add_tail(&mr->link, &s->records); @@ -45960,7 +46027,7 @@ static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, args[0] = args[1]; else args[0] = JS_DupValue(ctx, mr->value); - args[2] = (JSValue)this_val; + args[2] = this_val; ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args); JS_FreeValue(ctx, args[0]); if (!magic) @@ -46956,7 +47023,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, goto fail_reject; } resolve_element_data[0] = JS_NewBool(ctx, FALSE); - resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index); + resolve_element_data[1] = JS_NewInt32(ctx, index); resolve_element_data[2] = values; resolve_element_data[3] = resolving_funcs[is_promise_any]; resolve_element_data[4] = resolve_element_env; @@ -47315,7 +47382,7 @@ static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, { JSValueConst func_data[1]; - func_data[0] = (JSValueConst)JS_NewBool(ctx, done); + func_data[0] = JS_NewBool(ctx, done); return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, 1, 0, 1, func_data); } @@ -47894,7 +47961,7 @@ static const JSCFunctionListEntry js_global_funcs[] = { JS_CFUNC_DEF("escape", 1, js_global_escape ), JS_CFUNC_DEF("unescape", 1, js_global_unescape ), JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ), - JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), + JS_PROP_DOUBLE_DEF("NaN", JS_FLOAT64_NAN, 0 ), JS_PROP_UNDEFINED_DEF("undefined", 0 ), /* for the 'Date' implementation */ @@ -52744,8 +52811,8 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { psc->exception = 1; } done: - JS_FreeValue(ctx, (JSValue)argv[0]); - JS_FreeValue(ctx, (JSValue)argv[1]); + JS_FreeValue(ctx, argv[0]); + JS_FreeValue(ctx, argv[1]); } return cmp; } diff --git a/Frostbite2D/third_party/quickjspp/quickjs/quickjs.h b/Frostbite2D/third_party/quickjspp/quickjs/quickjs.h index 215e7b2..689f870 100644 --- a/Frostbite2D/third_party/quickjspp/quickjs/quickjs.h +++ b/Frostbite2D/third_party/quickjspp/quickjs/quickjs.h @@ -215,12 +215,33 @@ typedef struct JSValue { #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) #define JS_VALUE_GET_PTR(v) ((v).u.ptr) -#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } -#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } +static inline JSValue JS_MKVAL(int tag, int32_t val) +{ + JSValue v; + v.tag = tag; + v.u.int32 = val; + return v; +} + +static inline JSValue JS_MKPTR(int tag, void *ptr) +{ + JSValue v; + v.tag = tag; + v.u.ptr = ptr; + return v; +} #define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) -#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } +static inline JSValue JS_MKFLOAT64_NAN(void) +{ + JSValue v; + v.tag = JS_TAG_FLOAT64; + v.u.float64 = JS_FLOAT64_NAN; + return v; +} + +#define JS_NAN JS_MKFLOAT64_NAN() static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) { @@ -667,7 +688,7 @@ static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); p->ref_count++; } - return (JSValue)v; + return v; } static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) @@ -676,7 +697,7 @@ static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); p->ref_count++; } - return (JSValue)v; + return v; } int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ diff --git a/engine.lua b/engine.lua index 2f43580..1e54dfa 100644 --- a/engine.lua +++ b/engine.lua @@ -6,31 +6,56 @@ function add_frostbite2d_target() target("Frostbite2D") set_kind("static") add_files(path.join(engine_root, "Frostbite2D/src/**.cpp")) - add_files(path.join(engine_root, "Frostbite2D/src/**.c")) + add_files(path.join(engine_root, "Frostbite2D/src/glad/*.c")) add_includedirs(path.join(engine_root, "Frostbite2D/include"), {public = true}) add_includedirs(path.join(engine_root, "Frostbite2D/third_party/quickjspp"), {public = true}) local quickjs_dir = path.join(engine_root, "Frostbite2D/third_party/quickjspp/quickjs") local quickjs_defines = { "CONFIG_VERSION=\"2024-01-13\"", - "CONFIG_BIGNUM", "_GNU_SOURCE" } + local enable_quickjs_bignum = not is_plat("windows") + if enable_quickjs_bignum then + table.insert(quickjs_defines, "CONFIG_BIGNUM") + end if is_plat("mingw") or is_plat("windows") then table.insert(quickjs_defines, "__USE_MINGW_ANSI_STDIO") end add_files(path.join(quickjs_dir, "quickjs.c"), {defines = quickjs_defines}) add_files(path.join(quickjs_dir, "cutils.c"), {defines = quickjs_defines}) - add_files(path.join(quickjs_dir, "libbf.c"), {defines = quickjs_defines}) + if enable_quickjs_bignum then + add_files(path.join(quickjs_dir, "libbf.c"), {defines = quickjs_defines}) + end add_files(path.join(quickjs_dir, "libregexp.c"), {defines = quickjs_defines}) add_files(path.join(quickjs_dir, "libunicode.c"), {defines = quickjs_defines}) - add_packages("libsdl2", {public = true}) - add_packages("libsdl2_image", {public = true}) - add_packages("libsdl2_mixer", {public = true}) - add_packages("libsdl2_ttf", {public = true}) - add_packages("glm", {public = true}) - add_packages("zlib", {public = true}) + if is_plat("switch") then + local devkitpro = os.getenv("DEVKITPRO") or "C:/devkitPro" + local devkita64 = os.getenv("DEVKITA64") or path.join(devkitpro, "devkitA64") + local portlibs = path.join(devkitpro, "portlibs/switch") + local libnx = path.join(devkitpro, "libnx") + + add_cxflags("-D__SWITCH__", "-march=armv8-a+crc+crypto", "-mtune=cortex-a57", "-mtp=soft", {public = true}) + add_cxflags("-ffunction-sections", "-fdata-sections", {public = true}) + add_cxflags("-specs=" .. path.join(libnx, "switch.specs"), {public = true, force = true}) + add_ldflags("-specs=" .. path.join(libnx, "switch.specs"), {public = true, force = true}) + + add_includedirs(path.join(portlibs, "include"), {public = true}) + add_includedirs(path.join(portlibs, "include/SDL2"), {public = true}) + add_includedirs(path.join(portlibs, "include/freetype2"), {public = true}) + add_includedirs(path.join(libnx, "include"), {public = true}) + add_linkdirs(path.join(portlibs, "lib"), path.join(libnx, "lib"), {public = true}) + add_links("SDL2_ttf", "freetype", "SDL2_mixer", "vorbisidec", "opusfile", "opus", "modplug", "mpg123", "mikmod", "FLAC", "ogg", {public = true}) + add_links("SDL2_image", "png", "jpeg", "webp", "z", "SDL2", "EGL", "GLESv2", "glapi", "drm_nouveau", "nx", "m", "pthread", {public = true}) + else + add_packages("libsdl2", {public = true}) + add_packages("libsdl2_image", {public = true}) + add_packages("libsdl2_mixer", {public = true}) + add_packages("libsdl2_ttf", {public = true}) + add_packages("glm", {public = true}) + add_packages("zlib", {public = true}) + end if is_plat("mingw") or is_plat("windows") then add_syslinks("imm32", {public = true}) diff --git a/examples/switch_smoke/main.cpp b/examples/switch_smoke/main.cpp new file mode 100644 index 0000000..2201074 --- /dev/null +++ b/examples/switch_smoke/main.cpp @@ -0,0 +1,34 @@ +#include + +#include + +#include + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + consoleInit(nullptr); + padConfigureInput(1, HidNpadStyleSet_NpadStandard); + + PadState pad; + padInitializeDefault(&pad); + + frostbite2D::switchInit(); + + std::printf("Frostbite2D Switch smoke test\n"); + std::printf("Press PLUS to exit.\n"); + + while (appletMainLoop()) { + padUpdate(&pad); + if (padGetButtonsDown(&pad) & HidNpadButton_Plus) { + break; + } + consoleUpdate(nullptr); + } + + frostbite2D::switchShutdown(); + consoleExit(nullptr); + return 0; +} diff --git a/examples/windows_smoke/main.cpp b/examples/windows_smoke/main.cpp new file mode 100644 index 0000000..a3cefa4 --- /dev/null +++ b/examples/windows_smoke/main.cpp @@ -0,0 +1,32 @@ +#include + +#define SDL_MAIN_HANDLED +#include + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + auto config = frostbite2D::AppConfig::createDefault(); + config.appName = "Frostbite2D Windows Smoke"; + config.windowConfig.title = "Frostbite2D Windows Smoke"; + config.windowConfig.width = 640; + config.windowConfig.height = 360; + config.windowConfig.resizable = false; + config.windowConfig.vsync = false; + config.maxFps = 60; + + auto &app = frostbite2D::Application::get(); + if (!app.init(config)) { + std::puts("Frostbite2D Windows smoke init failed."); + return 1; + } + + std::puts("Frostbite2D Windows smoke init ok."); + app.run([&app]() { + app.quit(); + }); + app.shutdown(); + return 0; +} diff --git a/xmake.lua b/xmake.lua index 688850c..93d4877 100644 --- a/xmake.lua +++ b/xmake.lua @@ -7,11 +7,28 @@ set_encodings("utf-8") add_rules("mode.debug", "mode.release") -add_requires("libsdl2", {configs = {shared = true}}) -add_requires("libsdl2_image", {configs = {shared = true}}) -add_requires("libsdl2_mixer", {configs = {shared = true}}) -add_requires("libsdl2_ttf", {configs = {shared = true}}) -add_requires("glm") -add_requires("zlib") +if not is_plat("switch") then + add_requires("libsdl2", {configs = {shared = true}}) + add_requires("libsdl2_image", {configs = {shared = true}}) + add_requires("libsdl2_mixer", {configs = {shared = true}}) + add_requires("libsdl2_ttf", {configs = {shared = true}}) + add_requires("glm") + add_requires("zlib") +end includes("engine.lua") + +if is_plat("windows") then + target("Frostbite2DWindowsSmoke") + set_kind("binary") + add_files("examples/windows_smoke/main.cpp") + add_deps("Frostbite2D") + add_packages("libsdl2") + add_packages("libsdl2_image") + add_packages("libsdl2_mixer") + add_packages("libsdl2_ttf") + add_packages("glm") + add_packages("zlib") + add_syslinks("imm32") + target_end() +end