2023-12-05 23:18:27 +10:30
|
|
|
extends PanelContainer
|
|
|
|
#warning-ignore-all:return_value_discarded
|
|
|
|
|
2023-12-08 21:39:44 +10:30
|
|
|
signal debug_pressed
|
2023-12-06 16:32:40 +10:30
|
|
|
signal savefile_loaded(dicts)
|
2023-12-05 23:18:27 +10:30
|
|
|
|
2023-12-06 16:32:40 +10:30
|
|
|
const globals = preload('res://globals.gd') # Make LSP shut up about non-const
|
2023-12-06 18:19:19 +10:30
|
|
|
const FOLDER_ICON := globals.FOLDER_ICON
|
|
|
|
const ALLOWED_EXTS := globals.ALLOWED_EXTS
|
|
|
|
const CD_EXTS := globals.CD_EXTS
|
|
|
|
const EXT_ICONS := globals.EXT_ICONS
|
|
|
|
const TYPE_DESCS := globals.TYPE_DESCS
|
2023-12-05 23:18:27 +10:30
|
|
|
|
2023-12-06 16:32:40 +10:30
|
|
|
enum {SELECT_ROM, SELECT_SAVE, COMPLETE}
|
|
|
|
var current_mode = SELECT_ROM
|
|
|
|
|
2023-12-05 23:18:27 +10:30
|
|
|
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
|
2023-12-08 21:39:44 +10:30
|
|
|
onready var btn_load: Button = $'%btn_load'
|
|
|
|
onready var btn_debug: Button = $'%btn_debug'
|
2023-12-05 23:18:27 +10:30
|
|
|
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'
|
2023-12-11 00:21:12 +10:30
|
|
|
onready var splash_filter := $'%splash_filter'
|
|
|
|
onready var splash_label := $'%splash_label'
|
2023-12-05 23:18:27 +10:30
|
|
|
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():
|
2023-12-08 21:39:44 +10:30
|
|
|
# for child in folder_buttons.get_children():
|
|
|
|
# child.queue_free()
|
2023-12-05 23:18:27 +10:30
|
|
|
|
|
|
|
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:
|
2023-12-06 18:19:19 +10:30
|
|
|
'sfc', 'smc':
|
2024-07-08 16:55:53 +09:30
|
|
|
if true: # current_mode == SELECT_ROM:
|
2023-12-11 00:21:12 +10:30
|
|
|
splash_filter.visible = true
|
|
|
|
splash_label.text = 'Loading ROM: %s'%filename
|
2024-07-08 16:55:53 +09:30
|
|
|
var scenetree := get_tree()
|
|
|
|
if scenetree:
|
|
|
|
yield(scenetree, 'idle_frame')
|
2023-12-06 16:32:40 +10:30
|
|
|
RomLoader.load_snes_rom_from_bytes(uploaded_files[entry])
|
|
|
|
'srm':
|
|
|
|
if current_mode != SELECT_ROM:
|
|
|
|
emit_signal('savefile_loaded', SaveLoader.load_save_dicts_from_bytes(uploaded_files[entry]))
|
|
|
|
_set_mode(COMPLETE)
|
2023-12-05 23:18:27 +10:30
|
|
|
|
|
|
|
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
|
2023-12-06 16:32:40 +10:30
|
|
|
match current_mode:
|
|
|
|
SELECT_ROM:
|
2023-12-08 21:39:44 +10:30
|
|
|
btn_load.disabled = (ext != 'sfc' and ext != 'smc') # Only allow opening snes roms for now
|
2023-12-06 16:32:40 +10:30
|
|
|
SELECT_SAVE, COMPLETE:
|
2023-12-08 21:39:44 +10:30
|
|
|
btn_load.disabled = (ext != 'srm')
|
2023-12-05 23:18:27 +10:30
|
|
|
|
|
|
|
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
|
2023-12-06 16:32:40 +10:30
|
|
|
print('uploaded "%s" with filesize %d bytes'%[filename, len(data)])
|
2023-12-05 23:18:27 +10:30
|
|
|
update_view()
|
|
|
|
|
2024-07-08 15:35:38 +09:30
|
|
|
func _files_dropped(filenames: PoolStringArray, screen_idx: int) -> void:
|
|
|
|
print('Files Dropped signal: screen=%d, files='%screen_idx, filenames)
|
|
|
|
for filename in filenames:
|
2024-07-26 22:54:05 +09:30
|
|
|
var dir := Directory.new()
|
|
|
|
dir.open('user://')
|
|
|
|
var new_filename = 'user://' + filename.rsplit('/', true, 1)[1]
|
|
|
|
var err = dir.copy(filename, new_filename)
|
|
|
|
print('Attempted to copy %s to %s - error code %s' % [filename, new_filename, globals.ERROR_CODE_STRINGS[err]])
|
2024-07-08 15:35:38 +09:30
|
|
|
var ext: String = filename.rsplit('.', true, 1)[1]
|
|
|
|
if ext in ALLOWED_EXTS:
|
|
|
|
var file := File.new()
|
|
|
|
match file.open(filename, File.READ):
|
|
|
|
OK:
|
|
|
|
var rom_size := file.get_len()
|
|
|
|
var data := file.get_buffer(rom_size)
|
|
|
|
uploaded_files[filename] = data
|
|
|
|
uploaded_files_types[filename] = ext
|
|
|
|
print('uploaded "%s" with filesize %d bytes'%[filename, len(data)])
|
|
|
|
update_view()
|
|
|
|
load_file(filename)
|
|
|
|
var error:
|
|
|
|
print_debug('Error %d loading filename:'%error, filename)
|
|
|
|
|
2023-12-05 23:18:27 +10:30
|
|
|
func _ready() -> void:
|
|
|
|
if OS.get_name() != 'HTML5' or !OS.has_feature('JavaScript'):
|
|
|
|
return
|
2024-07-26 22:54:05 +09:30
|
|
|
print('is_userfs_persistent() == %s'%OS.is_userfs_persistent())
|
2023-12-05 23:18:27 +10:30
|
|
|
init_labels()
|
|
|
|
update_view()
|
2023-12-08 21:39:44 +10:30
|
|
|
btn_load.connect('pressed', self, 'activate_current_entry')
|
|
|
|
btn_debug.connect('pressed', self, '_on_debug_pressed')
|
2023-12-05 23:18:27 +10:30
|
|
|
RomLoader.connect('rom_loaded', self, '_on_rom_loaded')
|
2023-12-11 00:21:12 +10:30
|
|
|
RomLoader.connect('loading_stage_updated', self, '_on_loading_stage_updated')
|
2023-12-05 23:18:27 +10:30
|
|
|
HTML5.connect('uploaded_file', self, '_upload_file')
|
2024-07-08 15:35:38 +09:30
|
|
|
get_tree().connect('files_dropped', self, '_files_dropped')
|
2023-12-05 23:18:27 +10:30
|
|
|
|
2023-12-06 16:32:40 +10:30
|
|
|
func _set_mode(new_mode) -> void:
|
|
|
|
match new_mode:
|
|
|
|
SELECT_ROM:
|
|
|
|
$'%lbl_prompt'.text = "Select a ROM from your device's storage"
|
2023-12-08 21:39:44 +10:30
|
|
|
btn_debug.disabled = true
|
2023-12-06 16:32:40 +10:30
|
|
|
SELECT_SAVE:
|
|
|
|
$'%lbl_prompt'.text = "Select a save file from your device's storage"
|
2023-12-08 21:39:44 +10:30
|
|
|
btn_debug.disabled = false
|
2023-12-06 16:32:40 +10:30
|
|
|
COMPLETE:
|
2023-12-08 21:39:44 +10:30
|
|
|
$'%lbl_prompt'.text = "Save file loaded"
|
|
|
|
btn_debug.disabled = false
|
2023-12-06 16:32:40 +10:30
|
|
|
current_mode = new_mode
|
|
|
|
update_view()
|
|
|
|
init_labels()
|
2023-12-05 23:18:27 +10:30
|
|
|
|
|
|
|
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()
|
|
|
|
|
2023-12-11 00:21:12 +10:30
|
|
|
func _on_loading_stage_updated(stage: String) -> void:
|
|
|
|
splash_label.text += '\n' + stage
|
|
|
|
|
2023-12-05 23:18:27 +10:30
|
|
|
func _on_rom_loaded() -> void:
|
2023-12-11 00:21:12 +10:30
|
|
|
splash_filter.visible = false
|
2023-12-06 16:32:40 +10:30
|
|
|
_set_mode(SELECT_SAVE)
|
2023-12-05 23:18:27 +10:30
|
|
|
|
2023-12-08 21:39:44 +10:30
|
|
|
func _on_debug_pressed() -> void:
|
|
|
|
emit_signal('debug_pressed')
|