File access refactoring for multiple library directories
This commit is contained in:
parent
8facf97440
commit
914b00f50d
|
@ -17,6 +17,8 @@ _global_script_class_icons={
|
|||
|
||||
config/name="Rhythm"
|
||||
run/main_scene="res://main.tscn"
|
||||
config/use_custom_user_dir=true
|
||||
config/custom_user_dir_name="RhythmGame"
|
||||
config/icon="res://assets/spritesheet.png"
|
||||
|
||||
[audio]
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
#extends Object
|
||||
extends Node
|
||||
|
||||
var userroot := 'user://' if OS.get_name() != 'Android' else '/storage/emulated/0/RhythmGame/'
|
||||
const ERROR_CODES := [
|
||||
'OK', 'FAILED', 'ERR_UNAVAILABLE', 'ERR_UNCONFIGURED', 'ERR_UNAUTHORIZED', 'ERR_PARAMETER_RANGE_ERROR',
|
||||
'ERR_OUT_OF_MEMORY', 'ERR_FILE_NOT_FOUND', 'ERR_FILE_BAD_DRIVE', 'ERR_FILE_BAD_PATH','ERR_FILE_NO_PERMISSION',
|
||||
'ERR_FILE_ALREADY_IN_USE', 'ERR_FILE_CANT_OPEN', 'ERR_FILE_CANT_WRITE', 'ERR_FILE_CANT_READ', 'ERR_FILE_UNRECOGNIZED',
|
||||
'ERR_FILE_CORRUPT', 'ERR_FILE_MISSING_DEPENDENCIES', 'ERR_FILE_EOF', 'ERR_CANT_OPEN', 'ERR_CANT_CREATE', 'ERR_QUERY_FAILED',
|
||||
'ERR_ALREADY_IN_USE', 'ERR_LOCKED', 'ERR_TIMEOUT', 'ERR_CANT_CONNECT', 'ERR_CANT_RESOLVE', 'ERR_CONNECTION_ERROR',
|
||||
'ERR_CANT_ACQUIRE_RESOURCE', 'ERR_CANT_FORK', 'ERR_INVALID_DATA', 'ERR_INVALID_PARAMETER', 'ERR_ALREADY_EXISTS',
|
||||
'ERR_DOES_NOT_EXIST', 'ERR_DATABASE_CANT_READ', 'ERR_DATABASE_CANT_WRITE', 'ERR_COMPILATION_FAILED', 'ERR_METHOD_NOT_FOUND',
|
||||
'ERR_LINK_FAILED', 'ERR_SCRIPT_FAILED', 'ERR_CYCLIC_LINK', 'ERR_INVALID_DECLARATION', 'ERR_DUPLICATE_SYMBOL',
|
||||
'ERR_PARSE_ERROR', 'ERR_BUSY', 'ERR_SKIP', 'ERR_HELP', 'ERR_BUG'
|
||||
]
|
||||
|
||||
var userroot := OS.get_user_data_dir().rstrip('/')+'/' if OS.get_name() != 'Android' else '/storage/emulated/0/RhythmGame/'
|
||||
var PATHS := [userroot]
|
||||
# The following would probably work. One huge caveat is that permission needs to be manually granted by the user in app settings as we can't use OS.request_permission('WRITE_EXTERNAL_STORAGE')
|
||||
# '/storage/emulated/0/Android/data/au.ufeff.rhythmgame/'
|
||||
# '/sdcard/Android/data/au.ufeff.rhythmgame/'
|
||||
func _ready() -> void:
|
||||
print('Library paths: ', PATHS)
|
||||
|
||||
func directory_list(directory: String, hidden: bool, sort:=true) -> Dictionary:
|
||||
# Sadly there's no filelist sugar so we make our own
|
||||
|
@ -79,7 +94,7 @@ func scan_library():
|
|||
# Our format
|
||||
song_defs[key] = FileLoader.load_folder('%s/%s' % [rootdir, key])
|
||||
print('Loaded song directory: %s' % key)
|
||||
song_images[key] = FileLoader.load_image('%s/%s/%s' % [rootdir, key, song_defs[key]['tile_filename']])
|
||||
# song_images[key] = FileLoader.load_image('%s/%s' % [key, song_defs[key]['tile_filename']])
|
||||
if song_defs[key]['genre'] in genres:
|
||||
genres[song_defs[key]['genre']].append(key)
|
||||
else:
|
||||
|
@ -90,24 +105,29 @@ func scan_library():
|
|||
for i in min(len(diffs), len(default_difficulty_keys)):
|
||||
chart_difficulties[default_difficulty_keys[i]] = diffs[i]
|
||||
song_defs[key]['chart_difficulties'] = chart_difficulties
|
||||
|
||||
elif dir.file_exists(key + '/collection.json'):
|
||||
var collection = FileLoader.load_folder('%s/%s' % [rootdir, key], 'collection')
|
||||
var dir_collection = '%s/%s' % [rootdir, key]
|
||||
var collection = FileLoader.load_folder(dir_collection, 'collection')
|
||||
collections[key] = collection
|
||||
var base_dict = {} # Top level of the collection dict contains defaults for every song in it
|
||||
var base_dict = {'filepath': key+'/'} # Top level of the collection dict contains defaults for every song in it
|
||||
for key in collection.keys():
|
||||
if key != 'songs':
|
||||
base_dict[key] = collection[key]
|
||||
for song_key in collection['songs'].keys():
|
||||
var song_dict = collection['songs'][song_key]
|
||||
var song_def = base_dict.duplicate()
|
||||
song_defs[song_key] = song_def
|
||||
for key in song_dict.keys():
|
||||
song_def[key] = song_dict[key]
|
||||
song_images[song_key] = FileLoader.load_image('%s/%s/%s.png' % [rootdir, key, song_key])
|
||||
Library.add_song(song_key, song_def)
|
||||
# Legacy compat stuff
|
||||
song_defs[song_key] = song_def
|
||||
# song_images[song_key] = FileLoader.load_image('%s/%s/%s.png' % [rootdir, key, song_key])
|
||||
if song_defs[song_key]['genre'] in genres:
|
||||
genres[song_defs[song_key]['genre']].append(song_key)
|
||||
else:
|
||||
genres[song_defs[song_key]['genre']] = [song_key]
|
||||
|
||||
else:
|
||||
var files_by_ext = find_by_extensions(directory_list(rootdir + '/' + key, false).files)
|
||||
if 'sm' in files_by_ext:
|
||||
|
@ -545,11 +565,12 @@ func load_folder(folder, filename='song'):
|
|||
result.directory = folder
|
||||
return result
|
||||
|
||||
func load_filelist(filelist: Array):
|
||||
func load_filelist(filelist: Array, directory=''):
|
||||
var charts = {}
|
||||
var key := 1
|
||||
for filename in filelist:
|
||||
var extension: String = filename.rsplit('.', true, 1)[-1]
|
||||
filename = directory.rstrip('/') + '/' + filename
|
||||
match extension:
|
||||
'rgtm': # multiple charts
|
||||
var res = RGT.load_file(filename)
|
||||
|
@ -570,7 +591,7 @@ func load_filelist(filelist: Array):
|
|||
return charts
|
||||
|
||||
|
||||
func load_ogg(filename) -> AudioStreamOGGVorbis:
|
||||
func direct_load_ogg(filename) -> AudioStreamOGGVorbis:
|
||||
var audiostream = AudioStreamOGGVorbis.new()
|
||||
var oggfile = File.new()
|
||||
oggfile.open(filename, File.READ)
|
||||
|
@ -578,9 +599,74 @@ func load_ogg(filename) -> AudioStreamOGGVorbis:
|
|||
oggfile.close()
|
||||
return audiostream
|
||||
|
||||
func load_image(filename) -> ImageTexture:
|
||||
var tex = ImageTexture.new()
|
||||
var img = Image.new()
|
||||
var fallback_audiostream = AudioStreamOGGVorbis.new()
|
||||
func load_ogg(filename) -> AudioStreamOGGVorbis:
|
||||
var file = File.new()
|
||||
for root in PATHS:
|
||||
var filename1 = root + filename
|
||||
if file.file_exists(filename1):
|
||||
return direct_load_ogg(filename1)
|
||||
return fallback_audiostream
|
||||
|
||||
func direct_load_image(filename) -> ImageTexture:
|
||||
var tex := ImageTexture.new()
|
||||
var img := Image.new()
|
||||
img.load(filename)
|
||||
tex.create_from_image(img)
|
||||
return tex
|
||||
|
||||
var fallback_texture := ImageTexture.new()
|
||||
func load_image(filename) -> ImageTexture:
|
||||
var file = File.new()
|
||||
for root in PATHS:
|
||||
var filename1 = root + filename
|
||||
if file.file_exists(filename1):
|
||||
return direct_load_image(filename1)
|
||||
print('File not found: ', filename)
|
||||
return fallback_texture
|
||||
|
||||
|
||||
func init_directory(directory: String):
|
||||
var dir = Directory.new()
|
||||
var err = dir.make_dir_recursive(directory)
|
||||
if err != OK:
|
||||
print('An error occurred while trying to create the scores directory: ', err, ERROR_CODES[err])
|
||||
return err
|
||||
|
||||
func save_json(filename: String, data: Dictionary):
|
||||
filename = userroot + filename
|
||||
var dir = filename.rsplit('/', true, 1)[0]
|
||||
var err = FileLoader.init_directory(dir)
|
||||
if err != OK:
|
||||
print('Error making directory for JSON file: ', err, ERROR_CODES[err])
|
||||
return err
|
||||
var json = JSON.print(data)
|
||||
var file = File.new()
|
||||
err = file.open(filename, File.WRITE)
|
||||
if err != OK:
|
||||
print('Error saving JSON file: ', err, ERROR_CODES[err])
|
||||
return err
|
||||
file.store_string(json)
|
||||
file.close()
|
||||
return OK
|
||||
|
||||
func load_json(filename: String):
|
||||
var file = File.new()
|
||||
var err
|
||||
for root in PATHS:
|
||||
var filename1 = root + filename
|
||||
if file.file_exists(filename1):
|
||||
err = file.open(filename1, File.READ)
|
||||
if err != OK:
|
||||
print('An error occurred while trying to open file: ', filename1, err, ERROR_CODES[err])
|
||||
continue # return err
|
||||
var result_json = JSON.parse(file.get_as_text())
|
||||
file.close()
|
||||
if result_json.error != OK:
|
||||
print('Error: ', result_json.error)
|
||||
print('Error Line: ', result_json.error_line)
|
||||
print('Error String: ', result_json.error_string)
|
||||
return result_json.error
|
||||
return result_json.result
|
||||
print('File not found in any libraries: ', filename)
|
||||
return ERR_FILE_NOT_FOUND
|
||||
|
|
|
@ -9,9 +9,9 @@ class MultilangStr:
|
|||
var en := '' setget set_english
|
||||
func _init(native='', translit='', english=''):
|
||||
self.n = native
|
||||
if not translit.empty():
|
||||
if translit and not translit.empty():
|
||||
self.tl = translit
|
||||
if not english.empty:
|
||||
if english and not english.empty():
|
||||
self.en = english
|
||||
# func get_native() -> String:
|
||||
# return n
|
||||
|
@ -39,6 +39,7 @@ class Song:
|
|||
var bpm_values: Array
|
||||
var dynamic_bpm: bool
|
||||
var genre: String
|
||||
var filepath: String # For now this excludes the 'songs/' bit.
|
||||
var tile_filename: String
|
||||
var audio_filelist: Array
|
||||
var video_filelist: Array
|
||||
|
@ -64,6 +65,7 @@ class Song:
|
|||
if 'bpm' in values:
|
||||
BPM = values['bpm']
|
||||
|
||||
filepath = values.get('filepath', '')
|
||||
tile_filename = values.get('tile_filename', '%s.png'%values.get('index', 'tile'))
|
||||
audio_filelist = values.get('audio_filelist', ['%s.ogg'%values.get('index', 'audio')])
|
||||
video_filelist = values.get('video_filelist', ['%s.webm'%values.get('index', 'video')])
|
||||
|
@ -92,18 +94,29 @@ class Song:
|
|||
|
||||
|
||||
var all_songs = {}
|
||||
var genre_ids = {}
|
||||
var genre_titles = []
|
||||
var genre_songs = []
|
||||
var genre_ids = {} # String: int
|
||||
var genre_titles = [] # Strings
|
||||
var genre_songs = [] # Dictionaries of key: Song
|
||||
|
||||
var tile_tex_cache = {} # We'll need some way of managing this later since holding all the tiles in memory might be expensive
|
||||
var charts_cache = {}
|
||||
|
||||
func add_song(key: String, data: Dictionary):
|
||||
if not data.has('index'):
|
||||
data['index'] = key
|
||||
var song = Song.new(data)
|
||||
all_songs[key] = song
|
||||
if not genre_ids.has(song.genre):
|
||||
genre_ids[song.genre] = len(genre_titles)
|
||||
genre_titles.append(song.genre)
|
||||
genre_songs.append({})
|
||||
genre_songs[genre_ids[song.genre]][key] = song
|
||||
|
||||
func get_song_tile_texture(song_key):
|
||||
if song_key in tile_tex_cache:
|
||||
return tile_tex_cache[song_key]
|
||||
elif song_key in all_songs:
|
||||
tile_tex_cache[song_key] = load(all_songs[song_key].tile_filename)
|
||||
tile_tex_cache[song_key] = FileLoader.load_image('songs/' + all_songs[song_key].filepath.rstrip('/') + '/' + all_songs[song_key].tile_filename)
|
||||
return tile_tex_cache[song_key]
|
||||
else:
|
||||
print_debug('Invalid song_key: ', song_key)
|
||||
|
@ -112,7 +125,7 @@ func get_song_charts(song_key):
|
|||
if song_key in charts_cache:
|
||||
return charts_cache[song_key]
|
||||
elif song_key in all_songs:
|
||||
charts_cache[song_key] = FileLoader.load_filelist(all_songs[song_key].chart_filelist)
|
||||
charts_cache[song_key] = FileLoader.load_filelist(all_songs[song_key].chart_filelist, all_songs[song_key].filepath)
|
||||
return charts_cache[song_key]
|
||||
else:
|
||||
print_debug('Invalid song_key: ', song_key)
|
||||
|
|
|
@ -37,60 +37,26 @@ var GenreFont := preload('res://assets/MenuGenreFont.tres')
|
|||
var ScoreFont := preload('res://assets/MenuScoreFont.tres')
|
||||
var snd_interact := preload('res://assets/softclap.wav')
|
||||
|
||||
var userroot : String = FileLoader.userroot
|
||||
|
||||
func scan_library():
|
||||
var results = FileLoader.scan_library()
|
||||
song_defs = results.song_defs
|
||||
song_images = results.song_images
|
||||
genres = results.genres
|
||||
|
||||
|
||||
func save_score():
|
||||
var rootdir = userroot + 'scores'
|
||||
var dir = Directory.new()
|
||||
var err = dir.make_dir_recursive(rootdir)
|
||||
if err != OK:
|
||||
print('An error occurred while trying to create the scores directory: ', err)
|
||||
return err
|
||||
var data = {}
|
||||
data.score_data = scorescreen_score_data
|
||||
data.song_key = scorescreen_song_key
|
||||
var json = JSON.print(data)
|
||||
var file = File.new()
|
||||
# var filename = rootdir + '/{year}{month}{day}T{hour}{minute}{second}.json'.format(scorescreen_datetime)
|
||||
# So uh. Can't zero-pad using the string.format() method. This sucks.
|
||||
func save_score() -> int:
|
||||
var data = {'score_data': scorescreen_score_data, 'song_key': scorescreen_song_key}
|
||||
var dt = scorescreen_datetime
|
||||
var filename = rootdir + '/%04d%02d%02dT%02d%02d%02d.json'%[dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second]
|
||||
err = file.open(filename, File.WRITE)
|
||||
if err != OK:
|
||||
print(err)
|
||||
return err
|
||||
file.store_string(json)
|
||||
file.close()
|
||||
var filename = 'scores/%04d%02d%02dT%02d%02d%02d.json'%[dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second]
|
||||
var err = FileLoader.save_json(filename, data)
|
||||
if err == OK:
|
||||
scorescreen_saved = true
|
||||
return err
|
||||
|
||||
func load_score(filename):
|
||||
var rootdir = userroot + 'scores'
|
||||
var dir = Directory.new()
|
||||
var err = dir.make_dir_recursive(rootdir)
|
||||
if err != OK:
|
||||
print('An error occurred while trying to create the scores directory: ', err)
|
||||
return err
|
||||
|
||||
var file = File.new()
|
||||
err = file.open(rootdir + '/' + filename, File.READ)
|
||||
if err != OK:
|
||||
print('An error occurred while trying to access the chosen score file: ', err)
|
||||
return err
|
||||
var result_json = JSON.parse(file.get_as_text())
|
||||
file.close()
|
||||
if result_json.error != OK:
|
||||
print('Error: ', result_json.error)
|
||||
print('Error Line: ', result_json.error_line)
|
||||
print('Error String: ', result_json.error_string)
|
||||
return result_json.error
|
||||
var result = result_json.result
|
||||
var result = FileLoader.load_json('scores/%s'%filename)
|
||||
if not (result is Dictionary):
|
||||
print('An error occurred while trying to access the chosen score file: ', result)
|
||||
return result
|
||||
var data = {}
|
||||
for key in result.score_data:
|
||||
var value = {}
|
||||
|
@ -106,11 +72,8 @@ func load_score(filename):
|
|||
set_menu_mode(MenuMode.SCORE_SCREEN)
|
||||
|
||||
func _ready():
|
||||
print('user:// root is: ', OS.get_user_data_dir())
|
||||
print('Root for songs and scores is: ', userroot)
|
||||
scan_library()
|
||||
NoteHandler.connect('finished_song', self, 'finished_song')
|
||||
# load_score('20191211T234131.json') # For testing purposes
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta):
|
||||
|
@ -141,7 +104,8 @@ func draw_songtile(song_key, position, size, title_text:=false, difficulty=selec
|
|||
var diff_color := GameTheme.COLOR_DIFFICULTY[difficulty*2]
|
||||
var rect := Rect2(position.x, position.y, size, size)
|
||||
draw_rect(Rect2(position.x - outline_px, position.y - outline_px, size + outline_px*2, size + outline_px*2), diff_color)
|
||||
draw_texture_rect(song_images[song_key], rect, false)
|
||||
# draw_texture_rect(song_images[song_key], rect, false)
|
||||
draw_texture_rect(Library.get_song_tile_texture(song_key), rect, false)
|
||||
# Draw track difficulty rating
|
||||
draw_string_centered(GenreFont, Vector2(position.x+size-24, position.y+size-56), song_defs[song_key]['chart_difficulties'].get(Library.Song.default_difficulty_keys[difficulty], 0), diff_color)
|
||||
if title_text:
|
||||
|
|
|
@ -575,7 +575,7 @@ func load_track(data: Dictionary, difficulty_idx: int):
|
|||
bpm = data.bpm_values[0]
|
||||
sync_offset_audio = data.audio_offsets[0]
|
||||
sync_offset_video = data.video_offsets[0]
|
||||
var audiostream = FileLoader.load_ogg(data.directory + '/' + data.audio_filelist[0])
|
||||
var audiostream = FileLoader.direct_load_ogg(data.directory + '/' + data.audio_filelist[0])
|
||||
var videostream = load(data.directory + '/' + data.video_filelist[0])
|
||||
|
||||
MusicPlayer.set_stream(audiostream)
|
||||
|
|
Loading…
Reference in New Issue