extends PanelContainer #warning-ignore-all:return_value_discarded signal continue_pressed const FOLDER_ICON := preload('res://theme/icons/file_folder.tres') const ALLOWED_EXTS := PoolStringArray(['bin', 'iso', 'sfc', 'srm', 'gba']) const CD_EXTS := PoolStringArray(['bin', 'iso']) # If you have a weird disc image format, you can mount it yourself, leave me out of it var EXT_ICONS := { # Dicts can't be const 'bin': preload('res://theme/icons/file_binary.tres'), 'iso': preload('res://theme/icons/file_disc.tres'), 'sfc': preload('res://theme/icons/file_cart.tres'), 'gba': preload('res://theme/icons/file_cart.tres'), } var TYPE_DESCS := { # Dicts can't be const 'bin': 'Binary', 'iso': 'CD-ROM Image', 'sfc': 'SNES ROM', 'gba': 'GBA ROM', 'srm': 'SNES Savefile' } var uploaded_files := {} # filename: data var uploaded_files_types := {} # filename: type var cached_cd_bin_paths := {} onready var itemlist: ItemList = $VBoxContainer/HBoxContainer/PanelContainer/ItemList onready var vscroller: VScrollBar = itemlist.get_child(0) onready var folder_buttons_scroller: ScrollContainer = $VBoxContainer/ScrollContainer onready var folder_buttons: HBoxContainer = $VBoxContainer/ScrollContainer/folder_buttons onready var btn_ok: Button = $'%btn_ok' onready var btn_continue: Button = $'%btn_continue' onready var lbl_filename_header: Label = $'%lbl_filename_header' onready var lbl_filename_content: Label = $'%lbl_filename_content' onready var lbl_filetype_header: Label = $'%lbl_filetype_header' onready var lbl_filetype_content: Label = $'%lbl_filetype_content' onready var lbl_filesize_header: Label = $'%lbl_filesize_header' onready var lbl_filesize_content: Label = $'%lbl_filesize_content' onready var lbl_fileinfo_header: Label = $'%lbl_fileinfo_header' onready var lbl_fileinfo_content: Label = $'%lbl_fileinfo_content' const inactive_modulate_color := Color(0.5, 0.5, 0.5) const active_modulate_color := Color.white var last_dir := '' static func get_human_size(n: int) -> String: if n > 0x100000000: return '%.2f GiB' % (n/float(0x40000000)) if n > 0x400000: return '%.1f MiB' % (n/float(0x100000)) if n > 0x1000: return '%.1f KiB' % (n/float(0x400)) return '%d B' % n static func tokenize_path(path: String) -> PoolStringArray: return path.rstrip('/').split('/', true) # Allow empty means '/' will return [''] static func join_path(tokens) -> String: var path = '/'.join(tokens) return path if path else '/' func update_view(): for child in folder_buttons.get_children(): child.queue_free() itemlist.clear() var files := [] for entry in uploaded_files.keys(): if entry in cached_cd_bin_paths: files.append([entry, EXT_ICONS.iso]) elif '.' in entry: var extension = entry.rsplit('.', true, 1)[1] if extension in ALLOWED_EXTS: files.append([entry, EXT_ICONS.get(extension)]) files.sort() for entry in files: itemlist.add_item(entry[0], entry[1]) func load_file(entry: String): var ext = entry.rsplit('.', true, 1)[1].to_lower() match ext: 'sfc': RomLoader.load_snes_rom_from_bytes(uploaded_files[entry]) func activate_entry(entry: String, _index: int = -1): load_file(entry) func activate_current_entry(): var selected := itemlist.get_selected_items() if len(selected) > 0: activate_entry(itemlist.get_item_text(selected[0])) func view_entry(entry: String, _index: int = -1): init_labels() lbl_filename_header.modulate = active_modulate_color lbl_filename_content.text = entry lbl_filetype_header.modulate = active_modulate_color var size = len(uploaded_files[entry]) var human_size = get_human_size(size) var ext = entry.rsplit('.', true, 1)[1].to_lower() var type = TYPE_DESCS.get(ext, 'Unknown') var prodcode: String = uploaded_files_types[entry] if ext in CD_EXTS: pass # TODO: Update cd image loader to accept PoolByteArray or a buffer view # var cd := RomLoader.loader_cd_image.new(uploaded_files[entry]) # for key in cd.directory: # var s = key.trim_prefix('./') # var re_match := RomLoader.psx_productcode_regex.search(s) # if re_match: # prodcode = '%s\n%s\n%s' % [cd.pvd.system_identifier.strip_edges(), cd.pvd.volume_identifier.strip_edges(), re_match.get_string(1)] # type = 'PSX CD-ROM Image' # cached_cd_bin_paths[entry] = prodcode # if index >= 0: # itemlist.set_item_icon(index, EXT_ICONS.iso) # break var info = ('Info:\n %s' % prodcode) if prodcode else '' lbl_filetype_content.text = type lbl_filesize_header.modulate = active_modulate_color lbl_filesize_content.text = human_size lbl_fileinfo_header.modulate = active_modulate_color if prodcode else inactive_modulate_color lbl_fileinfo_content.text = prodcode btn_ok.disabled = (ext != 'sfc') # Only allow opening snes roms for now func init_labels(): lbl_filename_header.modulate = inactive_modulate_color lbl_filename_content.text = '' lbl_filetype_header.modulate = inactive_modulate_color lbl_filetype_content.text = '' lbl_filesize_header.modulate = inactive_modulate_color lbl_filesize_content.text = '' lbl_fileinfo_header.modulate = inactive_modulate_color lbl_fileinfo_content.text = '' func _upload_file(filename: String, file_type: String, data): # data is Javascript ArrayBuffer, which should be castable to PoolByteArray uploaded_files[filename] = data uploaded_files_types[filename] = file_type update_view() func _ready() -> void: if OS.get_name() != 'HTML5' or !OS.has_feature('JavaScript'): return init_labels() update_view() btn_ok.connect('pressed', self, 'activate_current_entry') btn_continue.connect('pressed', self, '_on_continue_pressed') RomLoader.connect('rom_loaded', self, '_on_rom_loaded') HTML5.connect('uploaded_file', self, '_upload_file') func _on_ItemList_item_activated(index: int) -> void: activate_entry(itemlist.get_item_text(index), index) func _on_ItemList_item_selected(index: int) -> void: view_entry(itemlist.get_item_text(index), index) func _on_btn_upload_pressed() -> void: HTML5.upload_file() func _on_rom_loaded() -> void: $'%lbl_prompt'.text = "Select a save file from your device's storage" btn_continue.disabled = false func _on_continue_pressed() -> void: emit_signal('continue_pressed')