Skip to content

Commit 5b0d4c0

Browse files
committed
#75 wip: xboxビルドテスト v71
1 parent 9fa4984 commit 5b0d4c0

19 files changed

Lines changed: 28503 additions & 17 deletions

.npmrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# npm 12 は git 依存の取得を既定で無効化する (EALLOWGIT)。
2+
# 依存ツリー内の @electron/node-gyp (electron の推移的依存) が git 参照のため、
3+
# プロジェクト単位でオプトインする。npm 11 以前はこの設定を無視する (無害)。
4+
allow-git=all

templates/xbox/CMakeLists.txt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ add_executable(Next2DXboxHost WIN32 ${NEXT2D_HOST_SOURCES})
106106

107107
target_include_directories(Next2DXboxHost PRIVATE
108108
"${CMAKE_CURRENT_SOURCE_DIR}/src"
109+
"${CMAKE_CURRENT_SOURCE_DIR}" # third_party/ (stb_image, dr_mp3 等)
109110
)
110111

111112
# 展開レイアウト (GDK_ROOT) 使用時: include/lib を明示指定し、実行時 DLL を並べる
@@ -162,15 +163,25 @@ if(TARGET webgpu_dawn)
162163
COMMENT "Staging webgpu_dawn.dll next to the executable")
163164
endif()
164165

165-
# WebGPU / D3D12 / GameInput / WIC / XAudio2 / GDK
166+
# WebGPU / D3D12 / GameInput / XAudio2 / GDK
166167
target_link_libraries(Next2DXboxHost PRIVATE
167168
d3d12
168169
dxgi
169170
dxguid
170-
windowscodecs # WIC: 画像デコード
171-
dbghelp # クラッシュハンドラのスタックトレース (StackWalk64/SymFromAddr)
172171
)
173172

173+
if(NOT NEXT2D_IS_CONSOLE)
174+
# デスクトップ専用 (Game Core OS に存在しない):
175+
# windowscodecs = WIC (stb_image で扱えない形式の画像デコードフォールバック)
176+
# dbghelp = クラッシュハンドラのスタックトレース (StackWalk64/SymFromAddr)
177+
# MF (mfplat 等)/d2d1/dwrite/shlwapi/winmm はソース側の #pragma comment(lib) が
178+
# 同じ NEXT2D_XBOX_CONSOLE ガード内でリンク指示する。
179+
target_link_libraries(Next2DXboxHost PRIVATE
180+
windowscodecs
181+
dbghelp
182+
)
183+
endif()
184+
174185
if(NEXT2D_IS_CONSOLE)
175186
# コンソール用ライブラリ (GDK が提供)
176187
target_link_libraries(Next2DXboxHost PRIVATE

templates/xbox/src/bindings/Image.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "EventTarget.h"
66
#include "ImageSource.h"
77
#include "platform/DecodeQueue.h"
8-
#include "platform/WicDecoder.h"
8+
#include "platform/ImageDecoder.h"
99
#include "v8/V8Util.h"
1010
#include "v8/WeakHandle.h"
1111

@@ -85,7 +85,7 @@ void LoadImageFromSrc(v8::Isolate* isolate, v8::Local<v8::Object> self, const st
8585
decodequeue::Submit(
8686
// プールスレッド: WIC デコードのみ (V8 に触れない)
8787
[input = std::move(input), decoded, ok]() {
88-
*ok = !input.empty() && DecodeImageWithWIC(input, *decoded);
88+
*ok = DecodeImage(input, *decoded);
8989
},
9090
// メインスレッド: 結果を image オブジェクトへ反映し load を発火
9191
[isolate, image_ref, decoded, ok]() {
@@ -273,7 +273,7 @@ void CreateImageBitmap(const v8::FunctionCallbackInfo<v8::Value>& args)
273273

274274
decodequeue::Submit(
275275
[input = std::move(input), decoded, ok]() {
276-
*ok = DecodeImageWithWIC(input, *decoded);
276+
*ok = DecodeImage(input, *decoded);
277277
},
278278
[isolate, resolver_ref, decoded, ok]() {
279279
v8::HandleScope hs(isolate);

templates/xbox/src/bindings/Video.cpp

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@
1515
#include "v8/V8Util.h"
1616
#include "v8/WeakHandle.h"
1717

18+
#include <string>
19+
#include <vector>
20+
21+
// Media Foundation はコンソール (Game Core OS) に無いデスクトップ専用 API。
22+
// コンソールでは動画デコード未対応 (末尾の «CONSOLE» スタブ)。
23+
// 実機での動画対応は devkit 段階の課題。
24+
#if !NEXT2D_XBOX_CONSOLE
25+
1826
#include <mfapi.h>
1927
#include <mfidl.h>
2028
#include <mfreadwrite.h>
2129
#include <shlwapi.h>
2230
#include <wrl/client.h>
2331

24-
#include <string>
25-
#include <vector>
26-
2732
#pragma comment(lib, "mfplat.lib")
2833
#pragma comment(lib, "mfreadwrite.lib")
2934
#pragma comment(lib, "mfuuid.lib")
@@ -322,3 +327,52 @@ v8::Local<v8::Object> CreateVideoElement(v8::Isolate* isolate, HostContext* /*ho
322327
}
323328

324329
} // namespace next2d
330+
331+
#else // NEXT2D_XBOX_CONSOLE
332+
333+
// «CONSOLE» 動画デコード未対応スタブ。
334+
// createElement('video') がクラッシュしないよう API 形状だけ提供する
335+
// (フレーム取得は常に false、canPlayType は空 = 非対応を正しく通知)。
336+
namespace next2d {
337+
338+
using v8util::Str;
339+
using v8util::SetMethod;
340+
using v8util::SetValue;
341+
342+
bool GetVideoFramePixels(v8::Isolate*, v8::Local<v8::Object>,
343+
const uint8_t** out_rgba, uint32_t* out_width, uint32_t* out_height)
344+
{
345+
*out_rgba = nullptr;
346+
*out_width = 0;
347+
*out_height = 0;
348+
return false;
349+
}
350+
351+
v8::Local<v8::Object> CreateVideoElement(v8::Isolate* isolate, HostContext* /*host*/)
352+
{
353+
v8::Local<v8::Object> self = v8::Object::New(isolate);
354+
SetValue(isolate, self, "__isVideoElement", v8::Boolean::New(isolate, true));
355+
SetValue(isolate, self, "tagName", Str(isolate, "VIDEO"));
356+
SetValue(isolate, self, "localName", Str(isolate, "video"));
357+
SetValue(isolate, self, "videoWidth", v8::Integer::New(isolate, 0));
358+
SetValue(isolate, self, "videoHeight", v8::Integer::New(isolate, 0));
359+
SetValue(isolate, self, "paused", v8::Boolean::New(isolate, true));
360+
SetValue(isolate, self, "ended", v8::Boolean::New(isolate, false));
361+
InstallEventTarget(isolate, self);
362+
SetMethod(isolate, self, "load", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
363+
SetMethod(isolate, self, "play", [](const v8::FunctionCallbackInfo<v8::Value>& a) {
364+
v8::Isolate* iso = a.GetIsolate();
365+
auto r = v8::Promise::Resolver::New(iso->GetCurrentContext()).ToLocalChecked();
366+
r->Resolve(iso->GetCurrentContext(), v8::Undefined(iso)).Check();
367+
a.GetReturnValue().Set(r->GetPromise());
368+
});
369+
SetMethod(isolate, self, "pause", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
370+
SetMethod(isolate, self, "canPlayType", [](const v8::FunctionCallbackInfo<v8::Value>& a) {
371+
a.GetReturnValue().Set(Str(a.GetIsolate(), ""));
372+
});
373+
return self;
374+
}
375+
376+
} // namespace next2d
377+
378+
#endif // !NEXT2D_XBOX_CONSOLE

templates/xbox/src/main.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44
// bootstrap.js とアプリ本体(ESM)の読み込み -> ゲームループ、という流れ。
55
#include <Windows.h>
66
#include <windowsx.h>
7-
#include <timeapi.h>
8-
#include <DbgHelp.h>
97
#include <XGameRuntime.h>
108

9+
// winmm (timeBeginPeriod) と DbgHelp (StackWalk64) はコンソール (Game Core OS) に
10+
// 存在しないデスクトップ専用 API。コンソールは全画面 VSync 固定でペーシング不要、
11+
// クラッシュ解析はプラットフォーム側の仕組みに任せる。
12+
#if !NEXT2D_XBOX_CONSOLE
13+
#include <timeapi.h>
14+
#include <DbgHelp.h>
1115
#pragma comment(lib, "winmm.lib")
16+
#endif
1217

1318
#include "HostContext.h"
1419
#include "AssetLoader.h"
@@ -340,14 +345,17 @@ HWND CreateHostWindow(HINSTANCE instance, int width, int height)
340345
// ウィンドウが画面(作業領域)より大きいと画面外にはみ出し、提示が
341346
// 見切れる/非等倍でスケールされて歪む (CI の低解像度ディスプレイで
342347
// 1920x1080 ウィンドウ → 描画が縦長になっていた)。作業領域に収まるよう
343-
// クランプする。実機コンソールでは作業領域=全画面のため 1920x1080 のまま。
348+
// クランプする。コンソールは常に全画面のためクランプ不要
349+
// (SystemParametersInfo は Game Core に無い)。
350+
#if !NEXT2D_XBOX_CONSOLE
344351
RECT wa = {};
345352
if (SystemParametersInfoW(SPI_GETWORKAREA, 0, &wa, 0)) {
346353
const int maxW = wa.right - wa.left;
347354
const int maxH = wa.bottom - wa.top;
348355
if (maxW > 0 && width > maxW) { width = maxW; }
349356
if (maxH > 0 && height > maxH) { height = maxH; }
350357
}
358+
#endif
351359

352360
// コンソールでは全画面相当。PC(GDK) 検証ではウィンドウ。
353361
const DWORD style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
@@ -363,6 +371,8 @@ HWND CreateHostWindow(HINSTANCE instance, int width, int height)
363371
// GUI 実行では stderr が見えないため、C++ クラッシュ地点を掴む唯一の手段。
364372
// DbgHelp の StackWalk64 で例外コンテキストからフレームを辿り、可能なら
365373
// シンボル名 (関数+行) とモジュールを添える (PDB があれば関数/行まで解決)。
374+
// コンソールには DbgHelp が無いためデスクトップ専用 (クラッシュ解析は実機ツール側)。
375+
#if !NEXT2D_XBOX_CONSOLE
366376
static LONG WINAPI CrashHandler(EXCEPTION_POINTERS* ep)
367377
{
368378
std::ofstream ofs("next2d-error.log", std::ios::app);
@@ -443,15 +453,18 @@ static LONG WINAPI CrashHandler(EXCEPTION_POINTERS* ep)
443453
SymCleanup(process);
444454
return EXCEPTION_EXECUTE_HANDLER; // プロセスを終了させる (無限再入回避)
445455
}
456+
#endif // !NEXT2D_XBOX_CONSOLE
446457

447458
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR cmd_line, int)
448459
{
460+
#if !NEXT2D_XBOX_CONSOLE
449461
// 最初にクラッシュハンドラを設置し、以降の segfault 等でスタックを記録する。
450462
SetUnhandledExceptionFilter(CrashHandler);
451463

452464
// Sleep() の分解能を 1ms に上げる (フレームペーシング用。既定 15.6ms では
453-
// 60Hz を刻めない)
465+
// 60Hz を刻めない)。コンソールは全画面 VSync 提示でペーシングされるため不要。
454466
timeBeginPeriod(1);
467+
#endif
455468

456469
// --selftest: アプリの代わりに js/selftest.js を実行し、全バインディングを
457470
// 実機/PC(GDK) 上で検証して終了する (テスト完了で自動終了)。
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#include "AudioDecoder.h"
2+
3+
// dr_mp3 / dr_wav / stb_vorbis: public domain のヘッダオンリーデコーダ (third_party/)。
4+
// メモリ入力のみ使用する。実装は本 TU に集約する (ODR 一意)。
5+
#define DR_MP3_IMPLEMENTATION
6+
#define DR_MP3_NO_STDIO
7+
#include "third_party/dr_mp3.h"
8+
9+
#define DR_WAV_IMPLEMENTATION
10+
#define DR_WAV_NO_STDIO
11+
#include "third_party/dr_wav.h"
12+
13+
// stb_vorbis は .c 配布のためここで取り込む (STB_VORBIS_HEADER_ONLY は使わない)。
14+
#define STB_VORBIS_NO_STDIO
15+
#define STB_VORBIS_NO_PUSHDATA_API
16+
#include "third_party/stb_vorbis.c"
17+
18+
#include <cstring>
19+
20+
namespace next2d {
21+
22+
namespace {
23+
24+
bool DecodeMp3(const std::vector<uint8_t>& input, PcmBuffer& out)
25+
{
26+
drmp3_config config = {};
27+
drmp3_uint64 total_frames = 0;
28+
float* frames = drmp3_open_memory_and_read_pcm_frames_f32(
29+
input.data(), input.size(), &config, &total_frames, nullptr);
30+
if (!frames || total_frames == 0 || config.channels == 0) {
31+
if (frames) {
32+
drmp3_free(frames, nullptr);
33+
}
34+
return false;
35+
}
36+
out.channels = config.channels;
37+
out.sample_rate = config.sampleRate;
38+
out.samples.assign(frames, frames + total_frames * config.channels);
39+
drmp3_free(frames, nullptr);
40+
return true;
41+
}
42+
43+
bool DecodeWav(const std::vector<uint8_t>& input, PcmBuffer& out)
44+
{
45+
unsigned int channels = 0, sample_rate = 0;
46+
drwav_uint64 total_frames = 0;
47+
float* frames = drwav_open_memory_and_read_pcm_frames_f32(
48+
input.data(), input.size(), &channels, &sample_rate, &total_frames, nullptr);
49+
if (!frames || total_frames == 0 || channels == 0) {
50+
if (frames) {
51+
drwav_free(frames, nullptr);
52+
}
53+
return false;
54+
}
55+
out.channels = channels;
56+
out.sample_rate = sample_rate;
57+
out.samples.assign(frames, frames + total_frames * channels);
58+
drwav_free(frames, nullptr);
59+
return true;
60+
}
61+
62+
bool DecodeOgg(const std::vector<uint8_t>& input, PcmBuffer& out)
63+
{
64+
int err = 0;
65+
stb_vorbis* vorbis = stb_vorbis_open_memory(
66+
input.data(), static_cast<int>(input.size()), &err, nullptr);
67+
if (!vorbis) {
68+
return false;
69+
}
70+
const stb_vorbis_info info = stb_vorbis_get_info(vorbis);
71+
const unsigned int total_frames = stb_vorbis_stream_length_in_samples(vorbis);
72+
if (info.channels <= 0 || total_frames == 0) {
73+
stb_vorbis_close(vorbis);
74+
return false;
75+
}
76+
out.channels = static_cast<uint32_t>(info.channels);
77+
out.sample_rate = info.sample_rate;
78+
out.samples.resize(static_cast<size_t>(total_frames) * info.channels);
79+
const int read = stb_vorbis_get_samples_float_interleaved(
80+
vorbis, info.channels, out.samples.data(),
81+
static_cast<int>(out.samples.size()));
82+
stb_vorbis_close(vorbis);
83+
if (read <= 0) {
84+
out.samples.clear();
85+
return false;
86+
}
87+
out.samples.resize(static_cast<size_t>(read) * info.channels);
88+
return true;
89+
}
90+
91+
} // namespace
92+
93+
bool DecodeAudio(const std::vector<uint8_t>& input, PcmBuffer& out)
94+
{
95+
if (input.size() < 12) {
96+
return false;
97+
}
98+
const uint8_t* d = input.data();
99+
100+
// マジックナンバーで形式を判定して該当デコーダを先に試し、
101+
// 外れたら残りをカスケード (誤判定・ヘッダ無し MP3 の保険)。
102+
const bool looks_wav = std::memcmp(d, "RIFF", 4) == 0 && std::memcmp(d + 8, "WAVE", 4) == 0;
103+
const bool looks_ogg = std::memcmp(d, "OggS", 4) == 0;
104+
const bool looks_mp3 = std::memcmp(d, "ID3", 3) == 0
105+
|| (d[0] == 0xFF && (d[1] & 0xE0) == 0xE0);
106+
107+
if (looks_wav && DecodeWav(input, out)) {
108+
return true;
109+
}
110+
if (looks_ogg && DecodeOgg(input, out)) {
111+
return true;
112+
}
113+
if (looks_mp3 && DecodeMp3(input, out)) {
114+
return true;
115+
}
116+
117+
// 判定が外れた場合のカスケード
118+
return DecodeWav(input, out) || DecodeOgg(input, out) || DecodeMp3(input, out);
119+
}
120+
121+
} // namespace next2d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// 音声デコード (エンコード済みバイト列 -> float32 interleaved PCM)。
2+
//
3+
// 第一候補は dr_mp3 / dr_wav / stb_vorbis (MP3/WAV/OGG、全プラットフォーム共通、
4+
// public domain)。コンソール (Game Core OS) にはデスクトップの Media Foundation が
5+
// 無いため、MF 依存を第一経路から外して PC 検証と実機の挙動を一致させる。
6+
// デスクトップのみ AAC 等の保険として AudioEngine::Decode 側に MF フォールバックが残る。
7+
#pragma once
8+
9+
#include "AudioEngine.h" // PcmBuffer
10+
11+
#include <cstdint>
12+
#include <vector>
13+
14+
namespace next2d {
15+
16+
bool DecodeAudio(const std::vector<uint8_t>& input, PcmBuffer& out);
17+
18+
} // namespace next2d

0 commit comments

Comments
 (0)