From 060ccd22e3f284b243585dea8c996f46b665aa75 Mon Sep 17 00:00:00 2001 From: Luke Hubmayer-Werner Date: Tue, 5 Dec 2023 20:23:55 +1030 Subject: [PATCH] Add file picking function for web build --- data/html5_file_picker.js | 27 +++++++++++++++++++++++++++ project.godot | 2 ++ scripts/managers/HTML5.gd | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 data/html5_file_picker.js create mode 100644 scripts/managers/HTML5.gd diff --git a/data/html5_file_picker.js b/data/html5_file_picker.js new file mode 100644 index 0000000..06b8378 --- /dev/null +++ b/data/html5_file_picker.js @@ -0,0 +1,27 @@ +var _FilePicker = {}; +var _FilePickerData = {}; +_FilePicker.upload = function(gd_callback) { + // canceled = true; + var input = document.createElement('INPUT'); + input.setAttribute("type", "file"); + input.setAttribute("accept", ".sfc,.srm,.gba,.bin,.iso"); + input.setAttribute("multiple", ""); + input.click(); + input.addEventListener('change', event => { + for (const file of event.target.files) { + var reader = new FileReader(); + // this.filename = file.name; + // this.file_type = file.type; + reader.readAsArrayBuffer(file); + reader.onloadend = (evt) => { + if (evt.target.readyState == FileReader.DONE) { + // Godot's JavaScriptObject API is very very poorly documented. + // It might be possible to unwrap JavaScriptObject to PoolByteArray + // without a bytewise for loop, but I can't work it out by trial and error. + _FilePickerData[file.name] = evt.target.result; + gd_callback(file.name, file.type); + } + } + } + }); +} diff --git a/project.godot b/project.godot index 03b72b3..48c2564 100644 --- a/project.godot +++ b/project.godot @@ -31,6 +31,7 @@ RomLoader="*res://scripts/loaders/RomLoader.gd" SaveLoader="*res://scripts/loaders/SaveLoader.gd" ThemeManager="*res://scripts/managers/ThemeManager.gd" StringLoader="*res://scripts/loaders/StringLoader.gd" +HTML5="*res://scripts/managers/HTML5.gd" [debug] @@ -63,6 +64,7 @@ texture={ [rendering] +quality/driver/driver_name="GLES2" 2d/snapping/use_gpu_pixel_snap=true environment/default_clear_color=Color( 0, 0, 0, 1 ) environment/default_environment="res://default_env.tres" diff --git a/scripts/managers/HTML5.gd b/scripts/managers/HTML5.gd new file mode 100644 index 0000000..d212152 --- /dev/null +++ b/scripts/managers/HTML5.gd @@ -0,0 +1,36 @@ +extends Node + +signal uploaded_file(filename, file_type, data) + +var js_callback: JavaScriptObject +var js_interface: JavaScriptObject + +func _ready() -> void: + if OS.get_name() != 'HTML5' or !OS.has_feature('JavaScript'): + return + + var file := File.new() + match file.open('res://data/html5_file_picker.js', File.READ): + OK: + JavaScript.eval(file.get_as_text(), true) + var error: + print_stack() + return + + js_callback = JavaScript.create_callback(self, '_upload_handler') + js_interface = JavaScript.get_interface('_FilePicker') + +func _upload_handler(_args): + var filename = _args[0] + var file_type = _args[1] + # Workaround for JavaScriptObject being seemingly impossible to unwrap (no docs) + var data_key: String = '_FilePickerData["%s"]' % filename + var data = JavaScript.eval(data_key, true) + JavaScript.eval('delete '+data_key, true) + print(_args) + emit_signal('uploaded_file', filename, file_type, data) + +func upload_file() -> void: + if OS.get_name() != 'HTML5' or !OS.has_feature('JavaScript'): + return + js_interface.upload(js_callback)