17bool g_filesystem_ready =
false;
18std::function<void(std::string)> g_rom_load_handler;
19std::queue<std::string> g_pending_rom_loads;
20std::mutex g_rom_load_mutex;
27void SetFileSystemReady() {
28 g_filesystem_ready =
true;
29 LOG_INFO(
"Wasm",
"Filesystem sync complete.");
33 if (Module.onFileSystemReady) {
34 Module.onFileSystemReady();
40void SyncFilesystem() {
43 if (typeof FS !==
'undefined' && FS.syncfs) {
44 FS.syncfs(false, function(err) {
46 console.error(
'[WASM] Failed to sync filesystem:', err);
48 console.log(
'[WASM] Filesystem synced successfully');
56void LoadRomFromWeb(
const char* filename) {
58 LOG_ERROR(
"Wasm",
"LoadRomFromWeb called with null filename");
62 std::string path(filename);
66 LOG_ERROR(
"Wasm",
"LoadRomFromWeb called with empty filename");
71 if (path.find(
"..") != std::string::npos) {
72 LOG_ERROR(
"Wasm",
"LoadRomFromWeb: path traversal not allowed: %s", filename);
77 if (path.length() > 512) {
78 LOG_ERROR(
"Wasm",
"LoadRomFromWeb: path too long (%zu chars)", path.length());
82 yaze::app::wasm::TriggerRomLoad(path);
87EM_JS(
void, MountFilesystems, (), {
88 if (typeof FS ===
'undefined') {
89 if (typeof Module !==
'undefined') {
90 Module.YAZE_FS_MOUNT_ATTEMPTS = (Module.YAZE_FS_MOUNT_ATTEMPTS || 0) + 1;
91 if (Module.YAZE_FS_MOUNT_ATTEMPTS < 50) {
92 console.warn(
'[WASM] FS not ready, retrying mount (' + Module.YAZE_FS_MOUNT_ATTEMPTS +
')');
93 setTimeout(MountFilesystems, 50);
97 console.error(
'[WASM] FS unavailable, skipping filesystem mounts');
98 if (typeof Module !==
'undefined' && Module._SetFileSystemReady) {
99 Module._SetFileSystemReady();
104 function isErrno(err, code) {
105 if (!err)
return false;
106 if (err.code === code)
return true;
107 if (typeof err.errno ===
'number' && FS.ERRNO_CODES && FS.ERRNO_CODES[code] === err.errno) {
113 function pathExists(path) {
122 function ensureDir(dir) {
124 var stat = FS.stat(dir);
125 if (FS.isDir(stat.mode))
return true;
127 var backup = dir +
'.bak';
128 if (pathExists(backup)) {
130 while (suffix < 5 && pathExists(backup + suffix)) {
133 backup = backup + suffix;
136 FS.rename(dir, backup);
137 console.warn(
'[WASM] Renamed file at ' + dir +
' to ' + backup);
138 }
catch (renameErr) {
139 console.warn(
'[WASM] Failed to rename file at ' + dir +
': ' + renameErr);
143 if (!isErrno(e,
'ENOENT')) {
144 console.warn(
'[WASM] Failed to stat directory ' + dir +
': ' + e);
152 if (isErrno(e,
'EEXIST'))
return true;
153 console.warn(
'[WASM] Failed to create directory ' + dir +
': ' + e);
177 '/.yaze/screenshots',
181 directories.forEach(function(dir) {
187 FS.mount(MEMFS, {},
'/.yaze/temp');
189 if (!isErrno(e,
'EBUSY') && !isErrno(e,
'EEXIST')) {
190 console.warn(
'[WASM] Failed to mount MEMFS for /.yaze/temp: ' + e);
196 if (typeof IDBFS !==
'undefined') {
198 }
else if (typeof Module !==
'undefined' && typeof Module.IDBFS !==
'undefined') {
199 idbfs = Module.IDBFS;
200 }
else if (typeof FS !==
'undefined' && typeof FS.filesystems !==
'undefined' && FS.filesystems.IDBFS) {
201 idbfs = FS.filesystems.IDBFS;
205 var persistentDirs = [
224 var mountedCount = 0;
225 var totalToMount = persistentDirs.length;
227 if (idbfs !== null) {
228 persistentDirs.forEach(function(dir) {
230 FS.mount(idbfs, {}, dir);
233 console.error(
"Error mounting IDBFS for " + dir +
": " + e);
236 FS.mount(MEMFS, {}, dir);
245 FS.syncfs(
true, function(err) {
247 console.error(
"Failed to sync IDBFS: " + err);
249 console.log(
"IDBFS synced successfully");
252 Module._SetFileSystemReady();
256 console.warn(
"IDBFS not available, using MEMFS for all directories (no persistence)");
257 persistentDirs.forEach(function(dir) {
259 FS.mount(MEMFS, {}, dir);
264 Module._SetFileSystemReady();
268EM_JS(
void, SetupYazeGlobalApi, (), {
269 if (typeof Module ===
'undefined')
return;
273 execute: function(cmd) {
274 if (Module.executeCommand) {
275 return Module.executeCommand(cmd);
277 return "Error: bindings not ready";
280 getState: function() {
281 if (Module.getFullDebugState) {
282 try {
return JSON.parse(Module.getFullDebugState()); }
catch(e) {
return {}; }
288 if (Module.getEditorState) {
289 try {
return JSON.parse(Module.getEditorState()); }
catch(e) {
return {}; }
294 loadRom: function(filename) {
295 return this.execute(
"rom load " + filename);
299 console.log(
"[yaze] window.yazeApp API initialized for agents");
302namespace yaze::app::wasm {
304bool IsFileSystemReady() {
305 return g_filesystem_ready;
308void SetRomLoadHandler(std::function<
void(std::string)> handler) {
309 std::lock_guard<std::mutex> lock(g_rom_load_mutex);
310 g_rom_load_handler = handler;
313 while (!g_pending_rom_loads.empty()) {
314 std::string path = g_pending_rom_loads.front();
315 g_pending_rom_loads.pop();
316 LOG_INFO(
"Wasm",
"Flushing pending ROM load: %s", path.c_str());
321void TriggerRomLoad(
const std::string& path) {
322 std::lock_guard<std::mutex> lock(g_rom_load_mutex);
323 if (g_rom_load_handler) {
324 g_rom_load_handler(path);
326 LOG_INFO(
"Wasm",
"Queuing ROM load (handler not ready): %s", path.c_str());
327 g_pending_rom_loads.push(path);
331void InitializeWasmPlatform() {
333 app::platform::WasmConfig::Get().LoadFromJavaScript();
336 SetupYazeGlobalApi();
339 auto& drop_handler = yaze::platform::WasmDropHandler::GetInstance();
340 drop_handler.Initialize(
"",
341 [](
const std::string& filename,
const std::vector<uint8_t>& data) {
343 std::string ext = filename.substr(filename.find_last_of(
".") + 1);
344 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
346 if (ext ==
"sfc" || ext ==
"smc" || ext ==
"zip") {
348 std::string path =
"/.yaze/roms/" + filename;
349 std::ofstream file(path, std::ios::binary);
350 file.write(
reinterpret_cast<const char*
>(data.data()), data.size());
353 LOG_INFO(
"Wasm",
"Wrote dropped ROM to %s (%zu bytes)", path.c_str(), data.size());
354 LoadRomFromWeb(path.c_str());
356 else if (ext ==
"pal" || ext ==
"tpl") {
357 LOG_INFO(
"Wasm",
"Palette drop detected: %s. Feature pending UI integration.", filename.c_str());
360 [](
const std::string& error) {
361 LOG_ERROR(
"Wasm",
"Drop Handler Error: %s", error.c_str());
#define LOG_ERROR(category, format,...)
#define LOG_INFO(category, format,...)
EM_JS(void, CallJsAiDriver,(const char *history_json), { if(window.yaze &&window.yaze.ai &&window.yaze.ai.processAgentRequest) { window.yaze.ai.processAgentRequest(UTF8ToString(history_json));} else { console.error("AI Driver not found in window.yaze.ai.processAgentRequest");} })
std::string getEditorState()