Skip to content

Commit 38368c0

Browse files
committed
#75 wip: xboxビルドテスト v56
1 parent 11984e1 commit 38368c0

4 files changed

Lines changed: 101 additions & 7 deletions

File tree

src/xbox.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import fs from "fs";
66
import os from "os";
77
import path from "path";
88
import cp from "child_process";
9+
import { minifySync } from "vite";
910
import { ctx } from "./context.js";
1011
import { getTemplateDir } from "./utils.js";
1112
import {
@@ -194,6 +195,35 @@ export const renderAssetsRc = (pakAbsPath: string): string =>
194195
return `N2DASSETS RCDATA "${forward}"\n`;
195196
};
196197

198+
/**
199+
* @description ホストスクリプト(js/bootstrap.js 等)を minify + 難読化する純関数。
200+
* Oxc(vite 同梱)でコメント除去・空白圧縮・ローカル識別子マングルを行う。
201+
* `globalThis.*` 等のグローバル名はゲームが参照するため保持される。
202+
* 失敗時は元コードをそのまま返す(埋め込み自体は継続)。
203+
* Minify/obfuscate a host script; returns the original on failure.
204+
*
205+
* @param {string} name ファイル名 (拡張子で構文判定)
206+
* @param {string} code 元の JavaScript
207+
* @return {string}
208+
* @method
209+
* @public
210+
*/
211+
export const minifyJs = (name: string, code: string): string =>
212+
{
213+
try {
214+
const result = minifySync(name, code);
215+
if (result.errors && result.errors.length > 0) {
216+
const msg = (result.errors[0] as any)?.message ?? String(result.errors[0]);
217+
console.log(pc.yellow(`Xbox: minify skipped for ${name} (${msg}); embedding as-is.`));
218+
return code;
219+
}
220+
return result.code;
221+
} catch (error) {
222+
console.log(pc.yellow(`Xbox: minify failed for ${name}; embedding as-is. ${error}`));
223+
return code;
224+
}
225+
};
226+
197227
/**
198228
* @description Xbox ホストの assets/app とホストスクリプト(js/bootstrap.js 等)を
199229
* 単一の pak バイナリへまとめ、`assets.pak` と RCDATA 参照用の
@@ -247,10 +277,26 @@ const embedXboxAssets = (): Promise<void> =>
247277
}
248278

249279
// (key, データ) を読み込み、pak バイナリへ直列化する。
280+
// js/ のホストスクリプト(bootstrap.js/selftest.js)は minify + 難読化する。
281+
// assets/app の JS は vite が本番ビルドで minify 済みのためそのまま。
282+
let minified = 0;
250283
const pakEntries: [string, Buffer][] = entries.map(
251-
([key, abs]): [string, Buffer] => [key, fs.readFileSync(abs)]
284+
([key, abs]): [string, Buffer] => {
285+
if (key.startsWith("js/") && key.endsWith(".js")) {
286+
const src = fs.readFileSync(abs, { "encoding": "utf8" });
287+
const out = minifyJs(key, src);
288+
if (out.length < src.length) {
289+
++minified;
290+
}
291+
return [key, Buffer.from(out, "utf8")];
292+
}
293+
return [key, fs.readFileSync(abs)];
294+
}
252295
);
253296
const pak: Buffer = buildPak(pakEntries);
297+
if (minified > 0) {
298+
console.log(pc.green(`Minified ${minified} host script(s) before embedding.`));
299+
}
254300
fs.writeFileSync(pakPath, pak);
255301

256302
// rc.exe が assets.pak を RCDATA "N2DASSETS" として取り込む .rc を生成する。

templates/xbox/src/bindings/Console.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ void Log(const v8::FunctionCallbackInfo<v8::Value>& args)
3030

3131
void Error(const v8::FunctionCallbackInfo<v8::Value>& args)
3232
{
33-
std::cerr << FormatArgs(args.GetIsolate(), args) << std::endl;
33+
// console.error / console.warn は GUI 実行では stderr が見えないため、
34+
// next2d-error.log にもミラーする (player 側のエラー・警告や WebGPU の
35+
// error scope 結果などを掴む)。
36+
const std::string msg = FormatArgs(args.GetIsolate(), args);
37+
std::cerr << msg << std::endl;
38+
v8util::AppendErrorLog("[console] " + msg);
3439
}
3540

3641
} // namespace

templates/xbox/src/gpu/DawnContext.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,44 @@
11
#include "DawnContext.h"
22

33
#include <Windows.h>
4+
#include <fstream>
45
#include <iostream>
6+
#include <string>
57

68
namespace next2d {
79

810
namespace {
911

12+
// GUI 実行では stderr が見えないため、GPU の致命的エラー/検証エラーを
13+
// next2d-error.log にも残す。描画が黒くなる/表示されない等の原因(検証エラー・
14+
// device lost・リソース枯渇)を掴む唯一の手段。氾濫防止に総数を制限する。
15+
void AppendGpuLog(const std::string& line)
16+
{
17+
static int count = 0;
18+
if (count >= 60) {
19+
return;
20+
}
21+
++count;
22+
std::ofstream ofs("next2d-error.log", std::ios::app);
23+
if (ofs) {
24+
ofs << line << std::endl;
25+
}
26+
}
27+
1028
void HandleDeviceLost(const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView message)
1129
{
12-
std::cerr << "[Dawn] Device lost (" << static_cast<int>(reason) << "): "
13-
<< std::string(message.data, message.length) << std::endl;
30+
const std::string line = "[Dawn] Device lost (" + std::to_string(static_cast<int>(reason))
31+
+ "): " + std::string(message.data, message.length);
32+
std::cerr << line << std::endl;
33+
AppendGpuLog(line);
1434
}
1535

1636
void HandleUncapturedError(const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message)
1737
{
18-
std::cerr << "[Dawn] Uncaptured error (" << static_cast<int>(type) << "): "
19-
<< std::string(message.data, message.length) << std::endl;
38+
const std::string line = "[Dawn] Uncaptured error (" + std::to_string(static_cast<int>(type))
39+
+ "): " + std::string(message.data, message.length);
40+
std::cerr << line << std::endl;
41+
AppendGpuLog(line);
2042
}
2143

2244
} // namespace

tests/xbox.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import path from "node:path";
77
import {
88
buildPak,
99
renderAssetsRc,
10-
walkFiles
10+
walkFiles,
11+
minifyJs
1112
} from "../dist/xbox.js";
1213

1314
// host 側 EmbeddedAssets.cpp の ParseEmbeddedPak と同じ手順で pak を復号する検証用デコーダ。
@@ -80,3 +81,23 @@ test("walkFiles: 入れ子を posix 相対キーで収集", () => {
8081
test("walkFiles: 存在しないディレクトリは空配列", () => {
8182
assert.deepEqual(walkFiles(path.join(os.tmpdir(), "n2d-does-not-exist-xyz")), []);
8283
});
84+
85+
test("minifyJs: 圧縮しコメント除去、グローバル名は保持", () => {
86+
const src = `
87+
// このコメントは除去される
88+
globalThis.__next2d_boot = function () {
89+
var longLocalVariableName = 1 + 2;
90+
return longLocalVariableName;
91+
};
92+
`;
93+
const out = minifyJs("js/bootstrap.js", src);
94+
assert.ok(out.length < src.length, "縮む");
95+
assert.ok(!out.includes("除去される"), "コメント除去");
96+
assert.ok(out.includes("__next2d_boot"), "グローバル名は保持 (ゲームが参照)");
97+
assert.ok(!/\blongLocalVariableName\b/.test(out), "ローカル名はマングル");
98+
});
99+
100+
test("minifyJs: 不正な JS は元コードのまま返す (埋め込み継続)", () => {
101+
const broken = "globalThis.x = (((;";
102+
assert.equal(minifyJs("js/bootstrap.js", broken), broken);
103+
});

0 commit comments

Comments
 (0)