From cc21a7a66cf46fa0f2551a55f66fb5468f2c58a4 Mon Sep 17 00:00:00 2001 From: Varun Patil <radialapps@gmail.com> Date: Tue, 20 Oct 2020 11:32:31 +0530 Subject: [PATCH] Save preload to FS directly --- extra/js/dpad.js | 84 ++++++++++++++ extra/js/drive.js | 168 ++++++++++++++++++++++++++-- extra/shell.html | 265 ++++----------------------------------------- src/emscripten.cpp | 4 +- 4 files changed, 270 insertions(+), 251 deletions(-) create mode 100644 extra/js/dpad.js diff --git a/extra/js/dpad.js b/extra/js/dpad.js new file mode 100644 index 0000000..0ab1e6a --- /dev/null +++ b/extra/js/dpad.js @@ -0,0 +1,84 @@ +function simulateKeyEvent(eventType, keyCode, charCode) { + var e = document.createEventObject ? document.createEventObject() : document.createEvent("Events"); + if (e.initEvent) e.initEvent(eventType, true, true); + + e.keyCode = keyCode; + e.which = keyCode; + e.charCode = charCode; + + // Dispatch directly to Emscripten's html5.h API (use this if page uses emscripten/html5.h event handling): + if (typeof JSEvents !== 'undefined' && JSEvents.eventHandlers && JSEvents.eventHandlers.length > 0) { + for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if ((JSEvents.eventHandlers[i].target == Module['canvas'] || JSEvents.eventHandlers[i].target == window) + && JSEvents.eventHandlers[i].eventTypeString == eventType) { + JSEvents.eventHandlers[i].handlerFunc(e); + } + } + } else { + // Dispatch to browser for real (use this if page uses SDL or something else for event handling): + Module['canvas'].dispatchEvent ? Module['canvas'].dispatchEvent(e) : Module['canvas'].fireEvent("on" + eventType, e); + } +} + +// Mappings +const keyMap = {}; +const keysDown = {}; + +/** Add virtual key binding */ +function bindKey(elem, key) { + keyMap[elem] = key; + const ne = document.getElementById(elem); + + ne.addEventListener('touchstart', function(e) { + e.preventDefault(); + simulateKeyEvent('keydown', key); + keysDown[e.target.id] = elem; + }); + ne.addEventListener('touchend', function(e) { + e.preventDefault(); + if (keysDown[e.target.id] && keyMap[keysDown[e.target.id]]) { + simulateKeyEvent('keyup', keyMap[keysDown[e.target.id]]); + } + keysDown[e.target.id] = 0; + }); + + ne.addEventListener('touchmove', function(event) { + const myLocation = event.changedTouches[0]; + const realTarget = document.elementFromPoint(myLocation.clientX, myLocation.clientY).id; + const origTarget = keysDown[myLocation.target.id]; + + if (origTarget !== realTarget) { + if (origTarget) { + simulateKeyEvent('keyup', keyMap[origTarget]); + keysDown[myLocation.target.id] = 0; + } + if (keyMap[realTarget]) { + simulateKeyEvent('keydown', keyMap[realTarget]); + keysDown[myLocation.target.id] = realTarget; + } + } + }); +} + +function is_touch_device() { + try { + document.createEvent("TouchEvent"); + return true; + } catch (e) { + return false; + } +} + +const resize = function() { + const el = document.getElementById('canvas'); + if (window.innerHeight > window.innerWidth) { + el.style.height = 'unset'; + el.style.width = '100%'; + } else { + el.style.width = 'unset'; + el.style.height = '100%'; + } +} + +window.addEventListener('resize', resize); +window.addEventListener('load', resize); diff --git a/extra/js/drive.js b/extra/js/drive.js index 4f2a546..5afbe24 100644 --- a/extra/js/drive.js +++ b/extra/js/drive.js @@ -21,24 +21,27 @@ function _base64ToBytes(base64) { // Canvas used for image generation var generationCanvas = document.createElement('canvas') +window.fileAsyncCache = {}; + +window.getMappingKey = function(file) { + return file.toLowerCase().replace(new RegExp("\\.[^/.]+$"), "") +} window.loadFileAsync = function(fullPath, bitmap, callback) { // noop callback = callback || (() => {}); - // Make cache object - if (!window.fileAsyncCache) window.fileAsyncCache = {}; + // Get mapping key + const mappingKey = getMappingKey(fullPath); + const mappingValue = mapping[mappingKey]; // Check if already loaded - if (window.fileAsyncCache.hasOwnProperty(fullPath)) return callback(); + if (window.fileAsyncCache.hasOwnProperty(mappingKey)) return callback(); + console.log('load', mappingKey); // Show spinner if (!bitmap && window.setBusy) window.setBusy(); - // Get mapping key - const mappingKey = fullPath.toLowerCase().replace(new RegExp("\\.[^/.]+$"), ""); - const mappingValue = mapping[mappingKey]; - // Check if this is a folder if (!mappingValue || mappingValue.endsWith("h=")) { console.error("Skipping loading", fullPath, mappingValue); @@ -56,7 +59,7 @@ window.loadFileAsync = function(fullPath, bitmap, callback) { const load = (cb1) => { getLazyAsset(iurl, filename, (data) => { FS.createPreloadedFile(path, filename, new Uint8Array(data), true, true, function() { - window.fileAsyncCache[fullPath] = 1; + window.fileAsyncCache[mappingKey] = 1; if (!bitmap && window.setNotBusy) window.setNotBusy(); if (window.fileLoadedAsync) window.fileLoadedAsync(fullPath); callback(); @@ -103,3 +106,152 @@ window.loadFileAsync = function(fullPath, bitmap, callback) { load(); } } + + +window.saveFile = function(filename) { + const buf = FS.readFile('/game/' + filename); + const b64 = _bytesToBase64(buf); + localforage.setItem(namespace + filename, b64); + + localforage.getItem(namespace, function(err, res) { + if (err || !res) res = {}; + res[filename] = 1; + localforage.setItem(namespace, res); + }); +}; + +var loadFiles = function() { + localforage.getItem(namespace, function(err, res) { + if (err || !res) return; + + const keys = Object.keys(res); + + console.log('Locally stored savefiles', keys); + + keys.forEach((key) => { + localforage.getItem(namespace + key, (err, res) => { + if (err) return; + + const buf = _base64ToBytes(res); + FS.writeFile('/game/' + key, buf); + }); + }); + }); +} + +var createDummies = function() { + // Base directory + FS.mkdir('/game'); + + // Create dummy objects + Object.values(mapping).forEach((file) => { + // Get filename + const filename = '/game/' + file.split("?")[0]; + + // Check if folder + if (file.endsWith('h=')) { + return FS.mkdir(filename); + } + + // Create dummy file + FS.writeFile(filename, '1'); + }); +}; + +window.setBusy = function() { + document.getElementById('spinner').style.opacity = "0.5"; +}; + +window.setNotBusy = function() { + document.getElementById('spinner').style.opacity = "0"; +}; + +window.onerror = function() { + console.error("An error occured!") +}; + +function preloadList(jsonArray) { + jsonArray.forEach((file) => { + const mappingKey = getMappingKey(file); + const mappingValue = mapping[mappingKey]; + if (!mappingValue || window.fileAsyncCache[mappingKey]) return; + + // Get path and filename + const path = "/game/" + mappingValue.substring(0, mappingValue.lastIndexOf("/")); + const filename = mappingValue.substring(mappingValue.lastIndexOf("/") + 1).split("?")[0]; + + // Preload the asset + FS.createPreloadedFile(path, filename, "gameasync/" + mappingValue, true, true, function() { + window.fileAsyncCache[mappingKey] = 1; + console.log('preload', mappingKey); + }, console.error, false, false, () => { + try { FS.unlink(path + "/" + filename); } catch (err) {} + }); + }); +} + +window.fileLoadedAsync = function(file) { + document.title = wTitle; + + if (!(/.*Map.*rxdata/i.test(file))) return; + + fetch('preload/' + file + '.json') + .then(function(response) { + return response.json(); + }) + .then(function(jsonResponse) { + setTimeout(() => { + preloadList(jsonResponse); + }, 200); + }); +}; + +var hideTimer = 0; +function getLazyAsset(url, filename, callback) { + const xhr = new XMLHttpRequest(); + xhr.responseType = "arraybuffer"; + const pdiv = document.getElementById("progress"); + let showTimer = 0; + let abortTimer = 0; + + const retry = () => { + xhr.abort(); + getLazyAsset(url, filename, callback); + } + + xhr.onreadystatechange = function() { + if (xhr.readyState == XMLHttpRequest.DONE && xhr.status >= 200 && xhr.status < 400) { + pdiv.innerHTML = `${filename} - done`; + hideTimer = setTimeout(() => { + pdiv.style.opacity = '0'; + hideTimer = 0; + }, 500); + clearTimeout(showTimer); + clearTimeout(abortTimer); + callback(xhr.response); + } + } + xhr.onprogress = function (event) { + const loaded = Math.round(event.loaded / 1024); + const total = Math.round(event.total / 1024); + pdiv.innerHTML = `${filename} - ${loaded}KB / ${total}KB`; + + clearTimeout(abortTimer); + abortTimer = setTimeout(retry, 3000); + }; + xhr.open('GET', url); + xhr.send(); + + pdiv.innerHTML = `${filename} - starting`; + + showTimer = setTimeout(() => { + pdiv.style.opacity = '0.5'; + }, 100); + + abortTimer = setTimeout(retry, 3000); + + if (hideTimer) { + clearTimeout(hideTimer); + hideTimer = 0; + } +} diff --git a/extra/shell.html b/extra/shell.html index acfd3a5..2493e9f 100644 --- a/extra/shell.html +++ b/extra/shell.html @@ -7,6 +7,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"> <script src="js/localforage.min.js"></script> <script src="js/drive.js"></script> + <script src="js/dpad.js"></script> <script src="gameasync/mapping.js"></script> <script src="gameasync/bitmap-map.js"></script> @@ -180,60 +181,11 @@ <script type='text/javascript'> var namespace = 'kne'; var wTitle = 'KN_E' - - window.saveFile = function(filename) { - const buf = FS.readFile('/game/' + filename); - const b64 = _bytesToBase64(buf); - localforage.setItem(namespace + filename, b64); - - localforage.getItem(namespace, function(err, res) { - if (err || !res) res = {}; - res[filename] = 1; - localforage.setItem(namespace, res); - }); - }; - - var loadFiles = function() { - localforage.getItem(namespace, function(err, res) { - if (err || !res) return; - - const keys = Object.keys(res); - - console.log('Locally stored savefiles', keys); - - keys.forEach((key) => { - localforage.getItem(namespace + key, (err, res) => { - if (err) return; - - const buf = _base64ToBytes(res); - FS.writeFile('/game/' + key, buf); - }); - }); - }); - } - - var createDummies = function() { - // Base directory - FS.mkdir('/game'); - - // Create dummy objects - Object.values(mapping).forEach((file) => { - // Get filename - const filename = '/game/' + file.split("?")[0]; - - // Check if folder - if (file.endsWith('h=')) { - return FS.mkdir(filename); - } - - // Create dummy file - FS.writeFile(filename, '1'); - }); - }; + document.title = wTitle; var Module = { preRun: [createDummies], - postRun: [loadFiles], + postRun: [loadFiles, preloadInit], noAudioDecoding: true, print: (function() { return function(text) { @@ -253,121 +205,33 @@ setStatus: function(text) {} }; - window.setBusy = function() { - document.getElementById('spinner').style.opacity = "0.5"; - }; - - window.setNotBusy = function() { - document.getElementById('spinner').style.opacity = "0"; - }; - function fullscreen() { document.getElementById('main').requestFullscreen(); screen.orientation.lock("landscape") } - window.onerror = function() { - console.error("An error occured!") - }; - - function preloadList(jsonArray) { - jsonArray.forEach((f) => { - const url = mapping[f.toLowerCase().replace(new RegExp("\\.[^/.]+$"), "")]; - if (!url) return; - - // Preload the asset - fetch('gameasync/' + url).then().catch(); - }); + function preloadInit() { + // Load important files + preloadList([ + 'rgss.rb', + 'data/scripts', + 'data/actors', + 'data/classes', + 'data/skills', + 'data/items', + 'data/weapons', + 'data/armors', + 'data/enemies', + 'data/troops', + 'data/states', + 'data/animations', + 'data/tilesets', + 'data/commonevents', + 'data/system', + 'data/mapinfos', + ]); } - window.fileLoadedAsync = function(file) { - document.title = wTitle; - - if (!(/.*Map.*rxdata/i.test(file))) return; - - fetch('preload/' + file + '.json') - .then(function(response) { - return response.json(); - }) - .then(function(jsonResponse) { - setTimeout(() => { - preloadList(jsonResponse); - }, 200); - }); - }; - - var hideTimer = 0; - function getLazyAsset(url, filename, callback) { - const xhr = new XMLHttpRequest(); - xhr.responseType = "arraybuffer"; - const pdiv = document.getElementById("progress"); - let showTimer = 0; - let abortTimer = 0; - - const retry = () => { - xhr.abort(); - getLazyAsset(url, filename, callback); - } - - xhr.onreadystatechange = function() { - if (xhr.readyState == XMLHttpRequest.DONE && xhr.status >= 200 && xhr.status < 400) { - pdiv.innerHTML = `${filename} - done`; - hideTimer = setTimeout(() => { - pdiv.style.opacity = '0'; - hideTimer = 0; - }, 500); - clearTimeout(showTimer); - clearTimeout(abortTimer); - callback(xhr.response); - } - } - xhr.onprogress = function (event) { - const loaded = Math.round(event.loaded / 1024); - const total = Math.round(event.total / 1024); - pdiv.innerHTML = `${filename} - ${loaded}KB / ${total}KB`; - - clearTimeout(abortTimer); - abortTimer = setTimeout(retry, 3000); - }; - xhr.open('GET', url); - xhr.send(); - - pdiv.innerHTML = `${filename} - starting`; - - showTimer = setTimeout(() => { - pdiv.style.opacity = '0.5'; - }, 100); - - abortTimer = setTimeout(retry, 3000); - - if (hideTimer) { - clearTimeout(hideTimer); - hideTimer = 0; - } - } - - document.title = wTitle; - - // Load important files - preloadList([ - 'rgss.rb', - 'data/scripts', - 'data/actors', - 'data/classes', - 'data/skills', - 'data/items', - 'data/weapons', - 'data/armors', - 'data/enemies', - 'data/troops', - 'data/states', - 'data/animations', - 'data/tilesets', - 'data/commonevents', - 'data/system', - 'data/mapinfos', - ]); - // Load wasm then initialize setTimeout(() => { getLazyAsset('mkxp.wasm', 'Game engine', () => { @@ -377,77 +241,6 @@ }); }, 200); - function simulateKeyEvent(eventType, keyCode, charCode) { - var e = document.createEventObject ? document.createEventObject() : document.createEvent("Events"); - if (e.initEvent) e.initEvent(eventType, true, true); - - e.keyCode = keyCode; - e.which = keyCode; - e.charCode = charCode; - - // Dispatch directly to Emscripten's html5.h API (use this if page uses emscripten/html5.h event handling): - if (typeof JSEvents !== 'undefined' && JSEvents.eventHandlers && JSEvents.eventHandlers.length > 0) { - for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { - if ((JSEvents.eventHandlers[i].target == Module['canvas'] || JSEvents.eventHandlers[i].target == window) - && JSEvents.eventHandlers[i].eventTypeString == eventType) { - JSEvents.eventHandlers[i].handlerFunc(e); - } - } - } else { - // Dispatch to browser for real (use this if page uses SDL or something else for event handling): - Module['canvas'].dispatchEvent ? Module['canvas'].dispatchEvent(e) : Module['canvas'].fireEvent("on" + eventType, e); - } - } - - // Mappings - const keyMap = {}; - const keysDown = {}; - - /** Add virtual key binding */ - function bindKey(elem, key) { - keyMap[elem] = key; - const ne = document.getElementById(elem); - - ne.addEventListener('touchstart', function(e) { - e.preventDefault(); - simulateKeyEvent('keydown', key); - keysDown[e.target.id] = elem; - }); - ne.addEventListener('touchend', function(e) { - e.preventDefault(); - if (keysDown[e.target.id] && keyMap[keysDown[e.target.id]]) { - simulateKeyEvent('keyup', keyMap[keysDown[e.target.id]]); - } - keysDown[e.target.id] = 0; - }); - - ne.addEventListener('touchmove', function(event) { - const myLocation = event.changedTouches[0]; - const realTarget = document.elementFromPoint(myLocation.clientX, myLocation.clientY).id; - const origTarget = keysDown[myLocation.target.id]; - - if (origTarget !== realTarget) { - if (origTarget) { - simulateKeyEvent('keyup', keyMap[origTarget]); - keysDown[myLocation.target.id] = 0; - } - if (keyMap[realTarget]) { - simulateKeyEvent('keydown', keyMap[realTarget]); - keysDown[myLocation.target.id] = realTarget; - } - } - }); - } - - function is_touch_device() { - try { - document.createEvent("TouchEvent"); - return true; - } catch (e) { - return false; - } - } - if (!is_touch_device()) { document.getElementById('dpad').style.display = 'none'; document.getElementById('apad').style.display = 'none'; @@ -463,18 +256,6 @@ bindKey('ap-a', 90); bindKey('ap-ka', 65); - const resize = function() { - const el = document.getElementById('canvas'); - if (window.innerHeight > window.innerWidth) { - el.style.height = 'unset'; - el.style.width = '100%'; - } else { - el.style.width = 'unset'; - el.style.height = '100%'; - } - } - - window.addEventListener('resize', resize); resize(); </script> diff --git a/src/emscripten.cpp b/src/emscripten.cpp index 1061047..6194fa0 100644 --- a/src/emscripten.cpp +++ b/src/emscripten.cpp @@ -13,7 +13,9 @@ EM_JS(void, save_file_async_js, (const char* fullPathC), { }); EM_JS(int, file_is_cached, (const char* fullPathC), { - return window.fileAsyncCache.hasOwnProperty(UTF8ToString(fullPathC)) ? 1 : 0; + const fullPath = UTF8ToString(fullPathC); + const mappingKey = getMappingKey(fullPath); + return window.fileAsyncCache.hasOwnProperty(mappingKey) ? 1 : 0; }); #endif