@next2d/builder 0.1.8 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +59 -0
- package/dist/cli.js +141 -0
- package/dist/constants.d.ts +53 -0
- package/dist/constants.js +64 -0
- package/dist/context.d.ts +29 -0
- package/dist/context.js +19 -0
- package/dist/electron.d.ts +10 -0
- package/dist/electron.js +142 -0
- package/dist/index.js +18 -790
- package/dist/native.d.ts +27 -0
- package/dist/native.js +123 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.js +40 -0
- package/dist/web.d.ts +18 -0
- package/dist/web.js +70 -0
- package/dist/xbox.d.ts +62 -0
- package/dist/xbox.js +426 -0
- package/package.json +11 -9
- package/templates/xbox/CMakeLists.txt +47 -11
- package/templates/xbox/README.md +18 -0
- package/templates/xbox/js/bootstrap.js +120 -0
- package/templates/xbox/src/AssetLoader.cpp +115 -12
- package/templates/xbox/src/AssetLoader.h +4 -0
- package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
- package/templates/xbox/src/EmbeddedAssets.h +60 -0
- package/templates/xbox/src/EventLoop.cpp +0 -15
- package/templates/xbox/src/bindings/Audio.cpp +118 -22
- package/templates/xbox/src/bindings/Canvas.cpp +32 -2
- package/templates/xbox/src/bindings/Canvas2D.cpp +495 -39
- package/templates/xbox/src/bindings/Console.cpp +6 -1
- package/templates/xbox/src/bindings/DomShims.cpp +18 -2
- package/templates/xbox/src/bindings/EventTarget.cpp +9 -0
- package/templates/xbox/src/bindings/Image.cpp +83 -57
- package/templates/xbox/src/bindings/Polyfills.cpp +671 -236
- package/templates/xbox/src/bindings/RasterCore.h +105 -1
- package/templates/xbox/src/bindings/Video.cpp +57 -3
- package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +29 -3
- package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +4 -1
- package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
- package/templates/xbox/src/gpu/DawnContext.cpp +167 -16
- package/templates/xbox/src/main.cpp +385 -16
- package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
- package/templates/xbox/src/platform/AudioDecoder.h +18 -0
- package/templates/xbox/src/platform/AudioEngine.cpp +28 -3
- package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
- package/templates/xbox/src/platform/DecodeQueue.h +27 -0
- package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
- package/templates/xbox/src/platform/ImageDecoder.h +18 -0
- package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
- package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
- package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
- package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
- package/templates/xbox/src/v8/CodeCache.h +223 -0
- package/templates/xbox/src/v8/V8Runtime.cpp +34 -33
- package/templates/xbox/src/v8/V8Util.h +54 -4
- package/templates/xbox/src/worker/WorkerRuntime.cpp +165 -65
- package/templates/xbox/src/worker/WorkerRuntime.h +23 -3
- package/templates/xbox/tests/check_local.sh +5 -1
- package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
- package/templates/xbox/tests/raster_test.cpp +50 -0
- package/templates/xbox/tests/windows_platform_test.cpp +75 -3
- package/templates/xbox/third_party/LICENSES.md +14 -0
- package/templates/xbox/third_party/dr_mp3.h +5412 -0
- package/templates/xbox/third_party/dr_wav.h +9131 -0
- package/templates/xbox/third_party/stb_image.h +7988 -0
- package/templates/xbox/third_party/stb_truetype.h +5079 -0
- package/templates/xbox/third_party/stb_vorbis.c +5584 -0
|
@@ -110,6 +110,126 @@
|
|
|
110
110
|
global.URL.revokeObjectURL = function () {};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
// --- URLSearchParams の最小補完 ----------------------------------------
|
|
114
|
+
// V8 単体には URLSearchParams が存在しないため補完する。
|
|
115
|
+
// Next2D アプリがクエリ文字列 (?mode=0 等) を解析する際に使用する。
|
|
116
|
+
if (typeof global.URLSearchParams === "undefined") {
|
|
117
|
+
global.URLSearchParams = class URLSearchParams {
|
|
118
|
+
constructor(init) {
|
|
119
|
+
this._list = [];
|
|
120
|
+
if (typeof init === "string") {
|
|
121
|
+
let query = init;
|
|
122
|
+
if (query.charAt(0) === "?") {
|
|
123
|
+
query = query.slice(1);
|
|
124
|
+
}
|
|
125
|
+
if (query.length) {
|
|
126
|
+
const pairs = query.split("&");
|
|
127
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
128
|
+
const pair = pairs[i];
|
|
129
|
+
if (!pair) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const index = pair.indexOf("=");
|
|
133
|
+
const decode = (value) => {
|
|
134
|
+
try {
|
|
135
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
136
|
+
} catch {
|
|
137
|
+
return value;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
if (index === -1) {
|
|
141
|
+
this._list.push([decode(pair), ""]);
|
|
142
|
+
} else {
|
|
143
|
+
this._list.push([decode(pair.slice(0, index)), decode(pair.slice(index + 1))]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} else if (init && typeof init === "object") {
|
|
148
|
+
if (Array.isArray(init)) {
|
|
149
|
+
for (let i = 0; i < init.length; i++) {
|
|
150
|
+
this._list.push([String(init[i][0]), String(init[i][1])]);
|
|
151
|
+
}
|
|
152
|
+
} else if (typeof init.forEach === "function") {
|
|
153
|
+
init.forEach((value, key) => this._list.push([String(key), String(value)]));
|
|
154
|
+
} else {
|
|
155
|
+
for (const key in init) {
|
|
156
|
+
if (Object.prototype.hasOwnProperty.call(init, key)) {
|
|
157
|
+
this._list.push([key, String(init[key])]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
get(name) {
|
|
164
|
+
for (let i = 0; i < this._list.length; i++) {
|
|
165
|
+
if (this._list[i][0] === name) {
|
|
166
|
+
return this._list[i][1];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
getAll(name) {
|
|
172
|
+
const result = [];
|
|
173
|
+
for (let i = 0; i < this._list.length; i++) {
|
|
174
|
+
if (this._list[i][0] === name) {
|
|
175
|
+
result.push(this._list[i][1]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
has(name) {
|
|
181
|
+
return this.get(name) !== null;
|
|
182
|
+
}
|
|
183
|
+
set(name, value) {
|
|
184
|
+
const text = String(value);
|
|
185
|
+
let done = false;
|
|
186
|
+
const next = [];
|
|
187
|
+
for (let i = 0; i < this._list.length; i++) {
|
|
188
|
+
if (this._list[i][0] === name) {
|
|
189
|
+
if (!done) {
|
|
190
|
+
next.push([name, text]);
|
|
191
|
+
done = true;
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
next.push(this._list[i]);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!done) {
|
|
198
|
+
next.push([name, text]);
|
|
199
|
+
}
|
|
200
|
+
this._list = next;
|
|
201
|
+
}
|
|
202
|
+
append(name, value) {
|
|
203
|
+
this._list.push([String(name), String(value)]);
|
|
204
|
+
}
|
|
205
|
+
delete(name) {
|
|
206
|
+
this._list = this._list.filter((pair) => pair[0] !== name);
|
|
207
|
+
}
|
|
208
|
+
forEach(callback, thisArg) {
|
|
209
|
+
for (let i = 0; i < this._list.length; i++) {
|
|
210
|
+
callback.call(thisArg, this._list[i][1], this._list[i][0], this);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
keys() {
|
|
214
|
+
return this._list.map((pair) => pair[0])[Symbol.iterator]();
|
|
215
|
+
}
|
|
216
|
+
values() {
|
|
217
|
+
return this._list.map((pair) => pair[1])[Symbol.iterator]();
|
|
218
|
+
}
|
|
219
|
+
entries() {
|
|
220
|
+
return this._list.map((pair) => [pair[0], pair[1]])[Symbol.iterator]();
|
|
221
|
+
}
|
|
222
|
+
toString() {
|
|
223
|
+
return this._list
|
|
224
|
+
.map((pair) => encodeURIComponent(pair[0]) + "=" + encodeURIComponent(pair[1]))
|
|
225
|
+
.join("&");
|
|
226
|
+
}
|
|
227
|
+
[Symbol.iterator]() {
|
|
228
|
+
return this.entries();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
113
233
|
// --- location (Next2D が参照する場合の最小値) ---------------------------
|
|
114
234
|
if (typeof global.location === "undefined") {
|
|
115
235
|
global.location = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "AssetLoader.h"
|
|
2
|
+
#include "EmbeddedAssets.h"
|
|
2
3
|
|
|
3
4
|
#include <filesystem>
|
|
4
5
|
#include <fstream>
|
|
@@ -14,9 +15,10 @@ AssetLoader::AssetLoader(std::string root)
|
|
|
14
15
|
{
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
std::string AssetLoader::
|
|
18
|
+
std::string AssetLoader::RelativeKey(const std::string& url) const
|
|
18
19
|
{
|
|
19
|
-
// 先頭の "/" や "./"、絶対 URL のオリジンを剥がして assets/app
|
|
20
|
+
// 先頭の "/" や "./"、絶対 URL のオリジンを剥がして assets/app 基準の相対パスにする。
|
|
21
|
+
// 埋め込み pak のキー(フォワードスラッシュ相対)と一致させるためのもの。
|
|
20
22
|
std::string path = url;
|
|
21
23
|
|
|
22
24
|
// スキーム付き (file://, http://localhost/...) はパス部だけ取り出す
|
|
@@ -39,11 +41,101 @@ std::string AssetLoader::Resolve(const std::string& url) const
|
|
|
39
41
|
path = path.substr(2);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
// バックスラッシュはフォワードスラッシュへ正規化 (pak キーと一致させる)。
|
|
45
|
+
for (char& c : path) {
|
|
46
|
+
if (c == '\\') c = '/';
|
|
47
|
+
}
|
|
48
|
+
return path;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
std::string AssetLoader::Resolve(const std::string& url) const
|
|
52
|
+
{
|
|
53
|
+
return (fs::path(root_) / RelativeKey(url)).string();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// base64 デコード (data: URI 用)。Image.cpp の同等実装と同じ仕様。
|
|
57
|
+
static bool DecodeBase64(const std::string& in, std::vector<uint8_t>* out)
|
|
58
|
+
{
|
|
59
|
+
auto val = [](char c) -> int {
|
|
60
|
+
if (c >= 'A' && c <= 'Z') return c - 'A';
|
|
61
|
+
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
|
62
|
+
if (c >= '0' && c <= '9') return c - '0' + 52;
|
|
63
|
+
if (c == '+') return 62;
|
|
64
|
+
if (c == '/') return 63;
|
|
65
|
+
return -1;
|
|
66
|
+
};
|
|
67
|
+
out->clear();
|
|
68
|
+
out->reserve(in.size() / 4 * 3);
|
|
69
|
+
int buf = 0, bits = 0;
|
|
70
|
+
for (char c : in) {
|
|
71
|
+
if (c == '=' || c == '\r' || c == '\n' || c == ' ') continue;
|
|
72
|
+
const int v = val(c);
|
|
73
|
+
if (v < 0) return false;
|
|
74
|
+
buf = buf << 6 | v;
|
|
75
|
+
bits += 6;
|
|
76
|
+
if (bits >= 8) {
|
|
77
|
+
bits -= 8;
|
|
78
|
+
out->push_back(static_cast<uint8_t>(buf >> bits & 0xFF));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// data: URI を「%xx」percent-decode する (base64 でない稀なケース用)。
|
|
85
|
+
static std::vector<uint8_t> PercentDecode(const std::string& in)
|
|
86
|
+
{
|
|
87
|
+
std::vector<uint8_t> out;
|
|
88
|
+
out.reserve(in.size());
|
|
89
|
+
auto hex = [](char c) -> int {
|
|
90
|
+
if (c >= '0' && c <= '9') return c - '0';
|
|
91
|
+
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
|
92
|
+
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
|
93
|
+
return -1;
|
|
94
|
+
};
|
|
95
|
+
for (size_t i = 0; i < in.size(); ++i) {
|
|
96
|
+
if (in[i] == '%' && i + 2 < in.size()) {
|
|
97
|
+
const int hi = hex(in[i + 1]);
|
|
98
|
+
const int lo = hex(in[i + 2]);
|
|
99
|
+
if (hi >= 0 && lo >= 0) {
|
|
100
|
+
out.push_back(static_cast<uint8_t>(hi << 4 | lo));
|
|
101
|
+
i += 2;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
out.push_back(static_cast<uint8_t>(in[i]));
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
43
108
|
}
|
|
44
109
|
|
|
45
110
|
std::optional<std::vector<uint8_t>> AssetLoader::ReadBinary(const std::string& url) const
|
|
46
111
|
{
|
|
112
|
+
// data: URI (Vite の ?inline はアセットを data:<mime>;base64,<payload> に埋め込む)。
|
|
113
|
+
// このゲームは全サウンドを ?inline で取り込むため実体は data URI。画像は Image.cpp が
|
|
114
|
+
// 対応済みだが、音声 (new Audio / Sound の XHR fetch) もこの ReadBinary を通るので
|
|
115
|
+
// ここで一括デコードする。未対応だと data: をファイルパス扱いして開けず、全 SFX/BGM が
|
|
116
|
+
// 無音になる (XAudio2 の可否とは無関係に実機でも無音)。
|
|
117
|
+
if (url.rfind("data:", 0) == 0) {
|
|
118
|
+
const auto comma = url.find(',');
|
|
119
|
+
if (comma == std::string::npos) {
|
|
120
|
+
return std::nullopt;
|
|
121
|
+
}
|
|
122
|
+
const std::string meta = url.substr(5, comma - 5); // 例: "audio/mpeg;base64"
|
|
123
|
+
const std::string payload = url.substr(comma + 1);
|
|
124
|
+
if (meta.find("base64") != std::string::npos) {
|
|
125
|
+
std::vector<uint8_t> out;
|
|
126
|
+
if (!DecodeBase64(payload, &out)) {
|
|
127
|
+
return std::nullopt;
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
return PercentDecode(payload);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 埋め込みモード優先: exe 内 pak に同じ相対キーがあれば実ファイルより先に返す。
|
|
135
|
+
if (const auto* embedded = GetEmbeddedAsset(RelativeKey(url))) {
|
|
136
|
+
return *embedded;
|
|
137
|
+
}
|
|
138
|
+
|
|
47
139
|
const std::string path = Resolve(url);
|
|
48
140
|
std::ifstream ifs(path, std::ios::binary | std::ios::ate);
|
|
49
141
|
if (!ifs) {
|
|
@@ -61,6 +153,10 @@ std::optional<std::vector<uint8_t>> AssetLoader::ReadBinary(const std::string& u
|
|
|
61
153
|
|
|
62
154
|
std::optional<std::string> AssetLoader::ReadText(const std::string& url) const
|
|
63
155
|
{
|
|
156
|
+
if (const auto* embedded = GetEmbeddedAsset(RelativeKey(url))) {
|
|
157
|
+
return std::string(embedded->begin(), embedded->end());
|
|
158
|
+
}
|
|
159
|
+
|
|
64
160
|
const std::string path = Resolve(url);
|
|
65
161
|
std::ifstream ifs(path, std::ios::binary);
|
|
66
162
|
if (!ifs) {
|
|
@@ -74,22 +170,29 @@ std::optional<std::string> AssetLoader::ReadText(const std::string& url) const
|
|
|
74
170
|
std::optional<std::string> AssetLoader::ResolveEntryModule() const
|
|
75
171
|
{
|
|
76
172
|
// vite が出力する index.html から <script type="module" src="..."> を抽出する。
|
|
77
|
-
|
|
78
|
-
std::
|
|
79
|
-
if (
|
|
80
|
-
|
|
173
|
+
// 埋め込みモードでは pak から、無ければ実ファイルから読む。
|
|
174
|
+
std::string html;
|
|
175
|
+
if (const auto* embedded = GetEmbeddedAsset("index.html")) {
|
|
176
|
+
html.assign(embedded->begin(), embedded->end());
|
|
177
|
+
} else {
|
|
178
|
+
const fs::path index = fs::path(root_) / "index.html";
|
|
179
|
+
std::ifstream ifs(index, std::ios::binary);
|
|
180
|
+
if (!ifs) {
|
|
181
|
+
return std::nullopt;
|
|
182
|
+
}
|
|
183
|
+
std::ostringstream oss;
|
|
184
|
+
oss << ifs.rdbuf();
|
|
185
|
+
html = oss.str();
|
|
81
186
|
}
|
|
82
|
-
std::ostringstream oss;
|
|
83
|
-
oss << ifs.rdbuf();
|
|
84
|
-
const std::string html = oss.str();
|
|
85
187
|
|
|
86
188
|
// type="module" を含む <script ... src="..."> を優先して拾う。
|
|
189
|
+
// 返す値は assets/app 基準の絶対パス。モジュールローダ側で埋め込み/実ファイルを解決する。
|
|
87
190
|
std::regex module_re(
|
|
88
191
|
R"(<script[^>]*type=["']module["'][^>]*src=["']([^"']+)["'])",
|
|
89
192
|
std::regex::icase);
|
|
90
193
|
std::smatch m;
|
|
91
194
|
if (std::regex_search(html, m, module_re)) {
|
|
92
|
-
return
|
|
195
|
+
return Resolve(m[1].str());
|
|
93
196
|
}
|
|
94
197
|
|
|
95
198
|
// 逆順 (src が先) も試す
|
|
@@ -97,7 +200,7 @@ std::optional<std::string> AssetLoader::ResolveEntryModule() const
|
|
|
97
200
|
R"(<script[^>]*src=["']([^"']+)["'][^>]*type=["']module["'])",
|
|
98
201
|
std::regex::icase);
|
|
99
202
|
if (std::regex_search(html, m, module_re2)) {
|
|
100
|
-
return
|
|
203
|
+
return Resolve(m[1].str());
|
|
101
204
|
}
|
|
102
205
|
|
|
103
206
|
return std::nullopt;
|
|
@@ -20,6 +20,10 @@ public:
|
|
|
20
20
|
// 相対 URL/パスを assets/app 基準の絶対パスへ解決する。
|
|
21
21
|
std::string Resolve(const std::string& url) const;
|
|
22
22
|
|
|
23
|
+
// URL を assets/app 基準の相対キー(フォワードスラッシュ)へ正規化する。
|
|
24
|
+
// 埋め込み pak のキーと一致させるために使う。
|
|
25
|
+
std::string RelativeKey(const std::string& url) const;
|
|
26
|
+
|
|
23
27
|
// バイナリ読み込み。存在しなければ nullopt。
|
|
24
28
|
std::optional<std::vector<uint8_t>> ReadBinary(const std::string& url) const;
|
|
25
29
|
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#include "EmbeddedAssets.h"
|
|
2
|
+
|
|
3
|
+
#include <filesystem>
|
|
4
|
+
#include <map>
|
|
5
|
+
#include <utility>
|
|
6
|
+
|
|
7
|
+
#ifdef _WIN32
|
|
8
|
+
#ifndef WIN32_LEAN_AND_MEAN
|
|
9
|
+
#define WIN32_LEAN_AND_MEAN
|
|
10
|
+
#endif
|
|
11
|
+
#include <Windows.h>
|
|
12
|
+
#endif
|
|
13
|
+
|
|
14
|
+
namespace fs = std::filesystem;
|
|
15
|
+
|
|
16
|
+
namespace next2d {
|
|
17
|
+
|
|
18
|
+
namespace {
|
|
19
|
+
|
|
20
|
+
std::map<std::string, std::vector<uint8_t>> g_assets;
|
|
21
|
+
std::string g_root; // assets/app の正規化済み絶対パス
|
|
22
|
+
bool g_loaded = false;
|
|
23
|
+
bool g_has = false;
|
|
24
|
+
|
|
25
|
+
inline uint32_t Read32LE(const uint8_t* p)
|
|
26
|
+
{
|
|
27
|
+
return static_cast<uint32_t>(p[0])
|
|
28
|
+
| static_cast<uint32_t>(p[1]) << 8
|
|
29
|
+
| static_cast<uint32_t>(p[2]) << 16
|
|
30
|
+
| static_cast<uint32_t>(p[3]) << 24;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
} // namespace
|
|
34
|
+
|
|
35
|
+
bool ParseEmbeddedPak(const uint8_t* data, std::size_t size,
|
|
36
|
+
std::vector<std::pair<std::string, std::vector<uint8_t>>>* out)
|
|
37
|
+
{
|
|
38
|
+
if (!data || size < 12) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (data[0] != 'N' || data[1] != '2' || data[2] != 'D' || data[3] != 'A') {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
// data[4..7] = version (現状 1 のみ。将来の互換のため値は検査しない)
|
|
45
|
+
const uint32_t count = Read32LE(data + 8);
|
|
46
|
+
std::size_t off = 12;
|
|
47
|
+
for (uint32_t i = 0; i < count; ++i) {
|
|
48
|
+
if (off + 4 > size) return false;
|
|
49
|
+
const uint32_t key_len = Read32LE(data + off);
|
|
50
|
+
off += 4;
|
|
51
|
+
if (key_len > size || off + key_len > size) return false;
|
|
52
|
+
std::string key(reinterpret_cast<const char*>(data + off), key_len);
|
|
53
|
+
off += key_len;
|
|
54
|
+
|
|
55
|
+
if (off + 4 > size) return false;
|
|
56
|
+
const uint32_t data_len = Read32LE(data + off);
|
|
57
|
+
off += 4;
|
|
58
|
+
if (data_len > size || off + data_len > size) return false;
|
|
59
|
+
std::vector<uint8_t> bytes(data + off, data + off + data_len);
|
|
60
|
+
off += data_len;
|
|
61
|
+
|
|
62
|
+
if (out) {
|
|
63
|
+
out->emplace_back(std::move(key), std::move(bytes));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
bool InitEmbeddedAssets()
|
|
70
|
+
{
|
|
71
|
+
if (g_loaded) {
|
|
72
|
+
return g_has;
|
|
73
|
+
}
|
|
74
|
+
g_loaded = true;
|
|
75
|
+
|
|
76
|
+
#ifdef _WIN32
|
|
77
|
+
// 実行ファイル自身のリソースから "N2DASSETS" (RCDATA) を取り出す。
|
|
78
|
+
HRSRC res = FindResourceW(nullptr, L"N2DASSETS", RT_RCDATA);
|
|
79
|
+
if (res) {
|
|
80
|
+
HGLOBAL handle = LoadResource(nullptr, res);
|
|
81
|
+
const DWORD sz = SizeofResource(nullptr, res);
|
|
82
|
+
const void* ptr = handle ? LockResource(handle) : nullptr;
|
|
83
|
+
if (ptr && sz >= 12) {
|
|
84
|
+
std::vector<std::pair<std::string, std::vector<uint8_t>>> entries;
|
|
85
|
+
if (ParseEmbeddedPak(static_cast<const uint8_t*>(ptr), sz, &entries)) {
|
|
86
|
+
for (auto& e : entries) {
|
|
87
|
+
g_assets.emplace(std::move(e.first), std::move(e.second));
|
|
88
|
+
}
|
|
89
|
+
g_has = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
#endif
|
|
94
|
+
|
|
95
|
+
return g_has;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
bool HasEmbeddedAssets()
|
|
99
|
+
{
|
|
100
|
+
return g_has;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
void SetEmbeddedAssetsRoot(const std::string& abs_root)
|
|
104
|
+
{
|
|
105
|
+
std::error_code ec;
|
|
106
|
+
fs::path canon = fs::weakly_canonical(fs::path(abs_root), ec);
|
|
107
|
+
g_root = ec ? abs_root : canon.string();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const std::vector<uint8_t>* GetEmbeddedAsset(const std::string& key)
|
|
111
|
+
{
|
|
112
|
+
if (!g_has) {
|
|
113
|
+
return nullptr;
|
|
114
|
+
}
|
|
115
|
+
auto it = g_assets.find(key);
|
|
116
|
+
return it == g_assets.end() ? nullptr : &it->second;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
void ForEachEmbeddedAsset(
|
|
120
|
+
const std::function<void(const std::string& key,
|
|
121
|
+
const std::vector<uint8_t>& data)>& callback)
|
|
122
|
+
{
|
|
123
|
+
if (!g_has) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
for (const auto& [key, data] : g_assets) {
|
|
127
|
+
callback(key, data);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const std::vector<uint8_t>* GetEmbeddedAssetByAbsPath(const std::string& abs)
|
|
132
|
+
{
|
|
133
|
+
if (!g_has || g_root.empty()) {
|
|
134
|
+
return nullptr;
|
|
135
|
+
}
|
|
136
|
+
// 純粋に字句的な相対化 (FS 参照なし)。abs も root も weakly_canonical 済みを前提。
|
|
137
|
+
const fs::path rel = fs::path(abs).lexically_relative(fs::path(g_root));
|
|
138
|
+
if (rel.empty()) {
|
|
139
|
+
return nullptr;
|
|
140
|
+
}
|
|
141
|
+
const std::string key = rel.generic_string();
|
|
142
|
+
// root 外 ("../..") は埋め込み対象外。
|
|
143
|
+
if (key.rfind("..", 0) == 0) {
|
|
144
|
+
return nullptr;
|
|
145
|
+
}
|
|
146
|
+
return GetEmbeddedAsset(key);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
} // namespace next2d
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// EmbeddedAssets: exe に埋め込んだ assets pak (Windows RCDATA) を解析して提供する。
|
|
2
|
+
//
|
|
3
|
+
// 目的: 書き出した *.exe の隣に平文の JS/HTML を置かず、ゲーム資産(assets/app 一式 +
|
|
4
|
+
// js/bootstrap.js)を exe 内リソースへ格納する。埋め込みが無い(開発ビルド等)場合は
|
|
5
|
+
// 全 API が「見つからない」を返し、呼び出し側はファイルシステムへフォールバックする。
|
|
6
|
+
// これにより埋め込みは純粋に加算的で、既存のファイル読み経路を壊さない。
|
|
7
|
+
//
|
|
8
|
+
// pak フォーマット (リトルエンディアン uint32):
|
|
9
|
+
// [4] magic "N2DA"
|
|
10
|
+
// [4] uint32 version (=1)
|
|
11
|
+
// [4] uint32 count
|
|
12
|
+
// count 回:
|
|
13
|
+
// [4] uint32 key_len
|
|
14
|
+
// [key_len] key (UTF-8, フォワードスラッシュ相対パス)
|
|
15
|
+
// [4] uint32 data_len
|
|
16
|
+
// [data_len] data
|
|
17
|
+
//
|
|
18
|
+
// key は次のいずれか:
|
|
19
|
+
// - assets/app 基準の相対パス (例 "app.js", "index.html", "assets/xxx.js")
|
|
20
|
+
// - "js/bootstrap.js" / "js/selftest.js" (exe_dir 基準)
|
|
21
|
+
#pragma once
|
|
22
|
+
|
|
23
|
+
#include <cstddef>
|
|
24
|
+
#include <cstdint>
|
|
25
|
+
#include <functional>
|
|
26
|
+
#include <string>
|
|
27
|
+
#include <vector>
|
|
28
|
+
|
|
29
|
+
namespace next2d {
|
|
30
|
+
|
|
31
|
+
// exe リソースから pak をロードして解析する (プロセスで一度だけ)。
|
|
32
|
+
// 埋め込みが存在し解析に成功したとき true。以後の Has/Get はこの結果に従う。
|
|
33
|
+
bool InitEmbeddedAssets();
|
|
34
|
+
|
|
35
|
+
// 埋め込みが利用可能か。
|
|
36
|
+
bool HasEmbeddedAssets();
|
|
37
|
+
|
|
38
|
+
// assets/app のルート絶対パスを登録する。GetEmbeddedAssetByAbsPath の
|
|
39
|
+
// 相対キー算出に使う。main で AssetLoader の root と同じ値を渡す。
|
|
40
|
+
void SetEmbeddedAssetsRoot(const std::string& abs_root);
|
|
41
|
+
|
|
42
|
+
// 相対キー直接指定で検索する。見つかればバイト列ポインタ(プロセス生存中有効)、無ければ nullptr。
|
|
43
|
+
const std::vector<uint8_t>* GetEmbeddedAsset(const std::string& key);
|
|
44
|
+
|
|
45
|
+
// 全エントリを列挙する (フォント等、キーが事前に分からない資材の収集用)。
|
|
46
|
+
// callback には key とデータ (プロセス生存中有効) が渡る。
|
|
47
|
+
void ForEachEmbeddedAsset(
|
|
48
|
+
const std::function<void(const std::string& key,
|
|
49
|
+
const std::vector<uint8_t>& data)>& callback);
|
|
50
|
+
|
|
51
|
+
// モジュールローダ用: assets/app 配下の絶対パスを相対キーへ変換して検索する。
|
|
52
|
+
// root 未登録 / abs が root 配下でない場合は nullptr。
|
|
53
|
+
const std::vector<uint8_t>* GetEmbeddedAssetByAbsPath(const std::string& abs);
|
|
54
|
+
|
|
55
|
+
// テスト用: 生の pak バッファを解析して (key -> data) を out へ格納する。
|
|
56
|
+
// プラットフォーム非依存で macOS 上でも検証できる。成功時 true。
|
|
57
|
+
bool ParseEmbeddedPak(const uint8_t* data, std::size_t size,
|
|
58
|
+
std::vector<std::pair<std::string, std::vector<uint8_t>>>* out);
|
|
59
|
+
|
|
60
|
+
} // namespace next2d
|
|
@@ -64,21 +64,6 @@ uint32_t EventLoop::RequestAnimationFrame(v8::Local<v8::Function> callback)
|
|
|
64
64
|
{
|
|
65
65
|
const uint32_t id = next_raf_id_++;
|
|
66
66
|
|
|
67
|
-
// 診断: 誰が rAF を登録しているか (関数名 + minify 後スクリプト位置)。
|
|
68
|
-
// 最初の 40 件 + 以後 300 件毎にサンプルし、凍結後も生きている登録元を特定する。
|
|
69
|
-
static uint64_t reg_count = 0;
|
|
70
|
-
++reg_count;
|
|
71
|
-
if (reg_count <= 40 || reg_count % 300 == 0) {
|
|
72
|
-
const std::string name =
|
|
73
|
-
v8util::ToStdString(isolate_, callback->GetDebugName());
|
|
74
|
-
v8::Local<v8::Value> res = callback->GetScriptOrigin().ResourceName();
|
|
75
|
-
std::cerr << "[Loop] rAF register #" << reg_count
|
|
76
|
-
<< " fn=" << (name.empty() ? "(anon)" : name)
|
|
77
|
-
<< " at " << v8util::ToStdString(isolate_, res)
|
|
78
|
-
<< ":" << callback->GetScriptLineNumber() + 1
|
|
79
|
-
<< ":" << callback->GetScriptColumnNumber() + 1 << std::endl;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
67
|
v8::Global<v8::Function> global;
|
|
83
68
|
global.Reset(isolate_, callback);
|
|
84
69
|
raf_callbacks_.emplace_back(id, std::move(global));
|