This commit is contained in:
parent
f442caa888
commit
b8dc380c33
2 changed files with 49 additions and 34 deletions
|
|
@ -1098,24 +1098,39 @@ void Crafter::EnableWasiBrowserRuntime(Configuration& cfg) {
|
|||
// Walk the dep graph again for non-JS assets — these get pre-loaded by
|
||||
// runtime.js into an in-memory VFS so the wasm's std::ifstream et al.
|
||||
// can actually read them (the wasi-runtime in this repo otherwise
|
||||
// stubs every fd syscall to zero). The manifest lists basenames only;
|
||||
// each file lives next to the .wasm in the bin dir. wasi-runtime
|
||||
// reduces every path_open path to its basename, so subdir layouts
|
||||
// collapse — that's fine for our flat-bin convention.
|
||||
auto compressedBasename = [](const fs::path& src) -> std::optional<std::string> {
|
||||
// stubs every fd syscall to zero).
|
||||
//
|
||||
// The manifest lists *relative paths* (e.g. "assets/Inter.ttf") so
|
||||
// runtime.js's fetch() resolves against the bin-dir layout the asset
|
||||
// copy step actually emits. The VFS is keyed by basename — path_open
|
||||
// strips to basename on lookup, so subdir layouts collapse on the
|
||||
// wasm side. Basename collisions across subdirs aren't supported on
|
||||
// the wasi runtime today; if two assets share a basename, the last
|
||||
// one preloaded wins. Avoid collisions in the source tree.
|
||||
auto compressedExt = [](const fs::path& src) -> std::optional<std::string> {
|
||||
std::string ext = src.extension().string();
|
||||
for (char& c : ext) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
fs::path out = src.filename();
|
||||
if (ext == ".png" || ext == ".tga" || ext == ".jpg" || ext == ".jpeg" || ext == ".bmp") {
|
||||
out.replace_extension(".ctex");
|
||||
} else if (ext == ".obj") {
|
||||
out.replace_extension(".cmesh");
|
||||
} else {
|
||||
return std::nullopt;
|
||||
return std::string(".ctex");
|
||||
}
|
||||
return out.string();
|
||||
if (ext == ".obj") return std::string(".cmesh");
|
||||
return std::nullopt;
|
||||
};
|
||||
auto compressedRel = [&](const fs::path& rel) -> fs::path {
|
||||
if (auto ext = compressedExt(rel)) {
|
||||
fs::path out = rel;
|
||||
out.replace_extension(*ext);
|
||||
return out;
|
||||
}
|
||||
return rel;
|
||||
};
|
||||
std::vector<std::string> assetFiles;
|
||||
auto pushUnique = [&](std::string name) {
|
||||
if (name.empty()) return;
|
||||
if (std::find(assetFiles.begin(), assetFiles.end(), name) == assetFiles.end()) {
|
||||
assetFiles.push_back(std::move(name));
|
||||
}
|
||||
};
|
||||
seen.clear();
|
||||
std::function<void(Configuration*)> walkAssets = [&](Configuration* c) {
|
||||
if (!c || !seen.insert(c).second) return;
|
||||
|
|
@ -1123,35 +1138,25 @@ void Crafter::EnableWasiBrowserRuntime(Configuration& cfg) {
|
|||
std::string ext = f.extension().string();
|
||||
if (ext == ".js" || ext == ".html") continue;
|
||||
if (f.filename() == "runtime.js") continue;
|
||||
std::string name = f.filename().string();
|
||||
if (name.empty()) continue;
|
||||
if (std::find(assetFiles.begin(), assetFiles.end(), name) == assetFiles.end()) {
|
||||
assetFiles.push_back(std::move(name));
|
||||
// cfg.files lands flat next to the .wasm by `name = filename()`.
|
||||
pushUnique(f.filename().string());
|
||||
}
|
||||
}
|
||||
// cfg.assets — emit the *compressed* output basename. Directory
|
||||
// entries get the same compressed-basename treatment per file;
|
||||
// unrecognized extensions are treated as passthrough (kept under
|
||||
// their original name, matching the build-side passthrough copy).
|
||||
// cfg.assets — mirror the bin-dir layout the build emits: a
|
||||
// directory entry becomes <topName>/<rel inside dir>, single
|
||||
// files land flat at the bin root. .png/.obj are compressed in
|
||||
// place; everything else passes through under its original name.
|
||||
for (const fs::path& a : c->assets) {
|
||||
if (fs::is_directory(a)) {
|
||||
const fs::path topName = a.filename();
|
||||
std::error_code ec;
|
||||
for (const auto& entry : fs::recursive_directory_iterator(a, ec)) {
|
||||
if (ec) break;
|
||||
if (!entry.is_regular_file()) continue;
|
||||
std::string name = compressedBasename(entry.path())
|
||||
.value_or(entry.path().filename().string());
|
||||
if (name.empty()) continue;
|
||||
if (std::find(assetFiles.begin(), assetFiles.end(), name) == assetFiles.end()) {
|
||||
assetFiles.push_back(std::move(name));
|
||||
}
|
||||
fs::path rel = fs::relative(entry.path(), a);
|
||||
pushUnique((topName / compressedRel(rel)).generic_string());
|
||||
}
|
||||
} else {
|
||||
std::optional<std::string> name = compressedBasename(a);
|
||||
if (!name) continue;
|
||||
if (std::find(assetFiles.begin(), assetFiles.end(), *name) == assetFiles.end()) {
|
||||
assetFiles.push_back(std::move(*name));
|
||||
}
|
||||
pushUnique(compressedRel(a.filename()).generic_string());
|
||||
}
|
||||
}
|
||||
for (Configuration* dep : c->dependencies) walkAssets(dep);
|
||||
|
|
|
|||
|
|
@ -301,6 +301,12 @@ if (!wasmUrl) {
|
|||
// EnableWasiBrowserRuntime) into an in-memory VFS so wasi-libc's file
|
||||
// syscalls work against them. Browser builds otherwise can't open assets
|
||||
// shipped alongside the .wasm — sync XHR is too deprecated to rely on.
|
||||
//
|
||||
// Manifest entries are relative paths under the bin dir (the layout
|
||||
// `cfg.assets` produces — e.g. "assets/Inter.ttf",
|
||||
// "mods/3DForts_Base/foo.cmesh"). We fetch each at its full path but
|
||||
// key the VFS by basename so `path_open`'s basename-reduction can find
|
||||
// it regardless of the C++ side's cwd or prefix.
|
||||
const vfs = new Map();
|
||||
try {
|
||||
const manifestResp = await fetch("files.json");
|
||||
|
|
@ -308,8 +314,12 @@ try {
|
|||
const names = await manifestResp.json();
|
||||
await Promise.all(names.map(async (name) => {
|
||||
const r = await fetch(name);
|
||||
if (r.ok) vfs.set(name, new Uint8Array(await r.arrayBuffer()));
|
||||
else console.warn(`[wasi] failed to preload ${name}: HTTP ${r.status}`);
|
||||
if (!r.ok) {
|
||||
console.warn(`[wasi] failed to preload ${name}: HTTP ${r.status}`);
|
||||
return;
|
||||
}
|
||||
const base = name.split(/[\\/]/).filter(Boolean).pop() || name;
|
||||
vfs.set(base, new Uint8Array(await r.arrayBuffer()));
|
||||
}));
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue