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);
169 directories.forEach(function(dir) {
175 FS.mount(MEMFS, {},
'/temp');
177 if (!isErrno(e,
'EBUSY') && !isErrno(e,
'EEXIST')) {
178 console.warn(
'[WASM] Failed to mount MEMFS for /temp: ' + e);
184 if (typeof IDBFS !==
'undefined') {
186 }
else if (typeof Module !==
'undefined' && typeof Module.IDBFS !==
'undefined') {
187 idbfs = Module.IDBFS;
188 }
else if (typeof FS !==
'undefined' && typeof FS.filesystems !==
'undefined' && FS.filesystems.IDBFS) {
189 idbfs = FS.filesystems.IDBFS;
193 var persistentDirs = [
'/roms',
'/saves',
'/config',
'/projects',
'/prompts',
'/recent'];
194 var mountedCount = 0;
195 var totalToMount = persistentDirs.length;
197 if (idbfs !== null) {
198 persistentDirs.forEach(function(dir) {
200 FS.mount(idbfs, {}, dir);
203 console.error(
"Error mounting IDBFS for " + dir +
": " + e);
206 FS.mount(MEMFS, {}, dir);
215 FS.syncfs(
true, function(err) {
217 console.error(
"Failed to sync IDBFS: " + err);
219 console.log(
"IDBFS synced successfully");
222 Module._SetFileSystemReady();
226 console.warn(
"IDBFS not available, using MEMFS for all directories (no persistence)");
227 persistentDirs.forEach(function(dir) {
229 FS.mount(MEMFS, {}, dir);
234 Module._SetFileSystemReady();
238EM_JS(
void, SetupYazeGlobalApi, (), {
239 if (typeof Module ===
'undefined')
return;
243 execute: function(cmd) {
244 if (Module.executeCommand) {
245 return Module.executeCommand(cmd);
247 return "Error: bindings not ready";
250 getState: function() {
251 if (Module.getFullDebugState) {
252 try {
return JSON.parse(Module.getFullDebugState()); }
catch(e) {
return {}; }
258 if (Module.getEditorState) {
259 try {
return JSON.parse(Module.getEditorState()); }
catch(e) {
return {}; }
264 loadRom: function(filename) {
265 return this.execute(
"rom load " + filename);
269 console.log(
"[yaze] window.yazeApp API initialized for agents");
272namespace yaze::app::wasm {
274bool IsFileSystemReady() {
275 return g_filesystem_ready;
278void SetRomLoadHandler(std::function<
void(std::string)> handler) {
279 std::lock_guard<std::mutex> lock(g_rom_load_mutex);
280 g_rom_load_handler = handler;
283 while (!g_pending_rom_loads.empty()) {
284 std::string path = g_pending_rom_loads.front();
285 g_pending_rom_loads.pop();
286 LOG_INFO(
"Wasm",
"Flushing pending ROM load: %s", path.c_str());
291void TriggerRomLoad(
const std::string& path) {
292 std::lock_guard<std::mutex> lock(g_rom_load_mutex);
293 if (g_rom_load_handler) {
294 g_rom_load_handler(path);
296 LOG_INFO(
"Wasm",
"Queuing ROM load (handler not ready): %s", path.c_str());
297 g_pending_rom_loads.push(path);
301void InitializeWasmPlatform() {
303 app::platform::WasmConfig::Get().LoadFromJavaScript();
306 SetupYazeGlobalApi();
309 auto& drop_handler = yaze::platform::WasmDropHandler::GetInstance();
310 drop_handler.Initialize(
"",
311 [](
const std::string& filename,
const std::vector<uint8_t>& data) {
313 std::string ext = filename.substr(filename.find_last_of(
".") + 1);
314 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
316 if (ext ==
"sfc" || ext ==
"smc" || ext ==
"zip") {
318 std::string path =
"/roms/" + filename;
319 std::ofstream file(path, std::ios::binary);
320 file.write(
reinterpret_cast<const char*
>(data.data()), data.size());
323 LOG_INFO(
"Wasm",
"Wrote dropped ROM to %s (%zu bytes)", path.c_str(), data.size());
324 LoadRomFromWeb(path.c_str());
326 else if (ext ==
"pal" || ext ==
"tpl") {
327 LOG_INFO(
"Wasm",
"Palette drop detected: %s. Feature pending UI integration.", filename.c_str());
330 [](
const std::string& error) {
331 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()