From 8ad98ad81c5c734c3e36aa9e3b97ffa3691ae5dc Mon Sep 17 00:00:00 2001 From: cyn Date: Sun, 14 Jul 2024 18:55:49 -0700 Subject: [PATCH 1/9] fileutil additions --- source/funkin/util/FileUtil.hx | 260 +++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) diff --git a/source/funkin/util/FileUtil.hx b/source/funkin/util/FileUtil.hx index 00a0a14b7..69d95e938 100644 --- a/source/funkin/util/FileUtil.hx +++ b/source/funkin/util/FileUtil.hx @@ -522,6 +522,90 @@ class FileUtil #end } + /** + * Moves a file from one location to another. + * Only works on desktop. + * + * @param path The path to the file. + * @param destination The path to move the file to. + */ + public static function moveFile(path:String, destination:String):Void + { + #if sys + if (doesFileExist(path)) + { + if (isDirectory(path)) + { + trace('WARNING: Path is a directory: $path - attempting to move as a directory instead'); + moveDir(path, destination); + return; + } + + var destinationFolder:String = destination; + if (Path.extension(destination) != '') + { + destinationFolder = Path.directory(destination); + } + + createDirIfNotExists(destinationFolder); + sys.FileSystem.rename(path, Path.join([destinationFolder, Path.withoutDirectory(path)])); + } + else + { + throw 'File does not exist: $path'; + } + #else + throw 'Direct file moving by path not supported on this platform.'; + #end + } + + /** + * Delete a file at the given path. + * Only works on desktop. + * + * @param path The path to the file. + */ + public static function deleteFile(path:String):Void + { + #if sys + sys.FileSystem.deleteFile(path); + #else + throw 'Direct file deletion by path not supported on this platform.'; + #end + } + + /** + * Get a file's size in bytes. Max representable size is ~2.147 GB. + * Only works on desktop. + * + * @param path The path to the file. + * @return The size of the file in bytes. + */ + public static function getFileSize(path:String):Int + { + #if sys + return sys.FileSystem.stat(path).size; + #else + return -1; + #end + } + + /** + * Check if a path is a directory. + * Only works on desktop. + * + * @param path The path to the (potential) directory. + * @return Whether the path is a directory or not. + */ + public static function isDirectory(path:String):Bool + { + #if sys + return sys.FileSystem.isDirectory(path); + #else + return false; + #end + } + /** * Create a directory if it doesn't already exist. * Only works on desktop. @@ -538,6 +622,145 @@ class FileUtil #end } + /** + * List all entries in a directory. + * Only works on desktop. + * + * @param path The path to the directory. + * @return An array of file and directory names in the directory. + */ + public static function readDir(path:String):Array + { + #if sys + return sys.FileSystem.readDirectory(path); + #else + return []; + #end + } + + /** + * Move a directory from one location to another, optionally ignoring some paths. + * Only works on desktop. + * + * @param path The path to the directory. + * @param destination The path to move the directory to. + * @param ignore A list of file names to ignore. + */ + public static function moveDir(path:String, destination:String, ?ignore:Array):Void + { + #if sys + if (!isDirectory(path)) + { + trace('WARNING: Path is not a directory: $path, moving as a file instead'); + moveFile(path, destination); + return; + } + + createDirIfNotExists(destination); + + ignore = ignore ?? []; + for (entry in readDir(path)) + { + if (ignore.indexOf(Path.join([path, entry])) != -1) continue; + if (isDirectory(Path.join([path, entry]))) + { + moveDir(Path.join([path, entry]), Path.join([destination, entry]), ignore); + } + else + { + moveFile(Path.join([path, entry]), Path.join([destination, entry])); + } + } + + if (readDir(path).length == 0) + { + deleteDir(path); + } + #else + throw 'Direct directory moving by path not supported on this platform.'; + #end + } + + /** + * Delete a directory, optionally including its contents, and optionally ignoring some paths. + * Only works on desktop. + * + * @param path The path to the directory. + * @param recursive Whether to delete all contents of the directory. + * @param ignore A list of file names to ignore. + */ + public static function deleteDir(path:String, recursive:Bool = false, ?ignore:Array):Void + { + #if sys + if (!isDirectory(path)) + { + throw 'Path is not a directory: $path'; + } + + if (recursive) + { + ignore = ignore ?? []; + for (entry in readDir(path)) + { + if (ignore.indexOf(Path.join([path, entry])) != -1) continue; + var entryPath = Path.join([path, entry]); + if (isDirectory(entryPath)) + { + deleteDir(entryPath, true, ignore); + } + else + { + deleteFile(entryPath); + } + } + + if (readDir(path).length == 0) + { + sys.FileSystem.deleteDirectory(path); + } + } else { + sys.FileSystem.deleteDirectory(path); + } + #else + throw 'Direct directory deletion by path not supported on this platform.'; + #end + } + + /** + * Get a directory's total size in bytes. Max representable size is ~2.147 GB. + * Only works on desktop. + * + * @param path The path to the directory. + * @return The total size of the directory in bytes. + */ + public static function getDirSize(path:String):Int + { + #if sys + if (!isDirectory(path)) + { + throw 'Path is not a directory: $path'; + } + + var total:Int = 0; + for (entry in readDir(path)) + { + var entryPath = Path.join([path, entry]); + if (isDirectory(entryPath)) + { + total += getDirSize(entryPath); + } + else + { + total += getFileSize(entryPath); + } + } + + return total; + #else + return -1; + #end + } + static var tempDir:String = null; static final TEMP_ENV_VARS:Array = ['TEMP', 'TMPDIR', 'TEMPDIR', 'TMP']; @@ -570,6 +793,43 @@ class FileUtil #end } + /** + * Rename a file or directory. + * Only works on desktop. + * + * @param path The path to the file or directory. + * @param newName The new name of the file or directory. + * @param keepExtension Whether to keep the extension the same, if applicable. + */ + public static function rename(path:String, newName:String, keepExtension:Bool = true):Void + { + #if sys + if (doesFileExist(path)) + { + newName = Path.withoutDirectory(newName); + if (!isDirectory(path)) + { + if (keepExtension) + { + newName = Path.withExtension(Path.withoutExtension(newName), Path.extension(path)); + } + else if (Path.extension(newName) == '') + { + newName = Path.withExtension(newName, Path.extension(path)); + } + } + + sys.FileSystem.rename(path, Path.join([Path.directory(path), newName])); + } + else + { + throw 'Path does not exist: $path'; + } + #else + throw 'Direct file renaming by path not supported on this platform.'; + #end + } + /** * Create a Bytes object containing a ZIP file, containing the provided entries. * From d4092f4d28be7c903568a1490056da875802f26f Mon Sep 17 00:00:00 2001 From: cyn Date: Sun, 14 Jul 2024 19:12:32 -0700 Subject: [PATCH 2/9] potential misinterpretation --- source/funkin/util/FileUtil.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/util/FileUtil.hx b/source/funkin/util/FileUtil.hx index 69d95e938..2900085e0 100644 --- a/source/funkin/util/FileUtil.hx +++ b/source/funkin/util/FileUtil.hx @@ -644,7 +644,7 @@ class FileUtil * * @param path The path to the directory. * @param destination The path to move the directory to. - * @param ignore A list of file names to ignore. + * @param ignore A list of paths to ignore. */ public static function moveDir(path:String, destination:String, ?ignore:Array):Void { @@ -687,7 +687,7 @@ class FileUtil * * @param path The path to the directory. * @param recursive Whether to delete all contents of the directory. - * @param ignore A list of file names to ignore. + * @param ignore A list of paths to ignore. */ public static function deleteDir(path:String, recursive:Bool = false, ?ignore:Array):Void { From a5b100b954e3d933c56e1e16bcd9ceb7d1a60f91 Mon Sep 17 00:00:00 2001 From: cyn Date: Tue, 16 Jul 2024 22:19:00 -0700 Subject: [PATCH 3/9] sandbox FileUtil + small fixes --- source/funkin/modding/PolymodHandler.hx | 8 +- source/funkin/play/song/SongSerializer.hx | 10 +- source/funkin/save/Save.hx | 4 +- source/funkin/ui/debug/DebugMenuSubState.hx | 4 +- .../ui/debug/charting/ChartEditorState.hx | 4 +- .../dialogs/ChartEditorUploadChartDialog.hx | 6 +- .../dialogs/ChartEditorUploadVocalsDialog.hx | 6 +- .../dialogs/ChartEditorWelcomeDialog.hx | 4 +- .../handlers/ChartEditorAudioHandler.hx | 8 +- .../handlers/ChartEditorDialogHandler.hx | 15 +- .../ChartEditorImportExportHandler.hx | 24 +- .../handlers/ChartEditorToolboxHandler.hx | 2 +- .../toolboxes/ChartEditorDifficultyToolbox.hx | 6 +- source/funkin/util/FileUtil.hx | 336 +++++++++++++++++- source/funkin/util/logging/CrashHandler.hx | 3 +- .../funkin/util/plugins/ScreenshotPlugin.hx | 7 +- tests/unit/source/TestMain.hx | 4 +- 17 files changed, 386 insertions(+), 65 deletions(-) diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index ae754b780..a406e2aa2 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -13,7 +13,7 @@ import funkin.data.freeplay.album.AlbumRegistry; import funkin.modding.module.ModuleHandler; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.save.Save; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import funkin.util.macro.ClassMacro; import polymod.backends.PolymodAssets.PolymodAssetType; import polymod.format.ParseRules.TextFileFormat; @@ -63,7 +63,7 @@ class PolymodHandler */ public static function createModRoot():Void { - FileUtil.createDirIfNotExists(MOD_FOLDER); + FileUtilBase.createDirIfNotExists(MOD_FOLDER); } /** @@ -266,6 +266,10 @@ class PolymodHandler var className:String = Type.getClassName(cls); Polymod.blacklistImport(className); } + + // `FileUtilBase` + // has unrestricted filesystem access + Polymod.blacklistImport('funkin.util.FileUtilBase'); } static function buildParseRules():polymod.format.ParseRules diff --git a/source/funkin/play/song/SongSerializer.hx b/source/funkin/play/song/SongSerializer.hx index 10296e5b4..3f719b6f8 100644 --- a/source/funkin/play/song/SongSerializer.hx +++ b/source/funkin/play/song/SongSerializer.hx @@ -3,7 +3,7 @@ package funkin.play.song; import funkin.data.song.SongData.SongChartData; import funkin.data.song.SongData.SongMetadata; import funkin.util.SerializerUtil; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import lime.utils.Bytes; import openfl.events.Event; import openfl.events.IOErrorEvent; @@ -20,7 +20,7 @@ class SongSerializer */ public static function importSongChartDataSync(path:String):SongChartData { - var fileData = FileUtil.readStringFromPath(path); + var fileData = FileUtilBase.readStringFromPath(path); if (fileData == null) return null; @@ -35,7 +35,7 @@ class SongSerializer */ public static function importSongMetadataSync(path:String):SongMetadata { - var fileData = FileUtil.readStringFromPath(path); + var fileData = FileUtilBase.readStringFromPath(path); if (fileData == null) return null; @@ -50,7 +50,7 @@ class SongSerializer */ public static function importSongChartDataAsync(callback:SongChartData->Void):Void { - FileUtil.browseFileReference(function(fileReference:FileReference) { + FileUtilBase.browseFileReference(function(fileReference:FileReference) { var data = fileReference.data.toString(); if (data == null) return; @@ -67,7 +67,7 @@ class SongSerializer */ public static function importSongMetadataAsync(callback:SongMetadata->Void):Void { - FileUtil.browseFileReference(function(fileReference:FileReference) { + FileUtilBase.browseFileReference(function(fileReference:FileReference) { var data = fileReference.data.toString(); if (data == null) return; diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 2900ce2be..9f370475c 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -1,7 +1,7 @@ package funkin.save; import flixel.util.FlxSave; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import funkin.input.Controls.Device; import funkin.play.scoring.Scoring; import funkin.play.scoring.Scoring.ScoringRank; @@ -925,7 +925,7 @@ class Save public function debug_dumpSave():Void { - FileUtil.saveFile(haxe.io.Bytes.ofString(this.serialize()), [FileUtil.FILE_FILTER_JSON], null, null, './save.json', 'Write save data as JSON...'); + FileUtilBase.saveFile(haxe.io.Bytes.ofString(this.serialize()), [FileUtilBase.FILE_FILTER_JSON], null, null, './save.json', 'Write save data as JSON...'); } } diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index f8b1be9d2..81eb929d9 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -10,7 +10,7 @@ import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.MusicBeatSubState; import funkin.util.logging.CrashHandler; import flixel.addons.transition.FlxTransitionableState; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; class DebugMenuSubState extends MusicBeatSubState { @@ -122,7 +122,7 @@ class DebugMenuSubState extends MusicBeatSubState #if sys function openLogFolder() { - FileUtil.openFolder(CrashHandler.LOG_FOLDER); + FileUtilBase.openFolder(CrashHandler.LOG_FOLDER); } #end diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f72cca77f..15685d2b3 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -92,7 +92,7 @@ import funkin.ui.haxeui.HaxeUIState; import funkin.ui.mainmenu.MainMenuState; import funkin.ui.transition.LoadingState; import funkin.util.Constants; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import funkin.util.logging.CrashHandler; import funkin.util.SortUtil; import funkin.util.WindowUtil; @@ -2348,7 +2348,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } } - if (!FileUtil.doesFileExist(chartPath)) + if (!FileUtilBase.doesFileExist(chartPath)) { trace('Previously loaded chart file (${chartPath.toString()}) does not exist, disabling link...'); menuItemRecentChart.disabled = true; diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx index 17f047106..8744a3759 100644 --- a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx +++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx @@ -3,7 +3,7 @@ package funkin.ui.debug.charting.dialogs; import funkin.input.Cursor; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogDropTarget; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import haxe.io.Path; import haxe.ui.containers.dialogs.Dialog.DialogButton; import haxe.ui.containers.dialogs.Dialog.DialogEvent; @@ -96,9 +96,9 @@ class ChartEditorUploadChartDialog extends ChartEditorBaseDialog this.lock(); // TODO / BUG: File filtering not working on mac finder dialog, so we don't use it for now #if !mac - FileUtil.browseForBinaryFile('Open Chart', [FileUtil.FILE_EXTENSION_INFO_FNFC], onSelectFile, onCancelBrowse); + FileUtilBase.browseForBinaryFile('Open Chart', [FileUtilBase.FILE_EXTENSION_INFO_FNFC], onSelectFile, onCancelBrowse); #else - FileUtil.browseForBinaryFile('Open Chart', null, onSelectFile, onCancelBrowse); + FileUtilBase.browseForBinaryFile('Open Chart', null, onSelectFile, onCancelBrowse); #end } diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadVocalsDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadVocalsDialog.hx index 537c7c36e..407422510 100644 --- a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadVocalsDialog.hx +++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadVocalsDialog.hx @@ -3,7 +3,7 @@ package funkin.ui.debug.charting.dialogs; import funkin.input.Cursor; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogDropTarget; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import funkin.play.character.CharacterData; import haxe.io.Path; import haxe.ui.components.Button; @@ -210,9 +210,9 @@ class ChartEditorUploadVocalsDialog extends ChartEditorBaseDialog this.lock(); // TODO / BUG: File filtering not working on mac finder dialog, so we don't use it for now #if !mac - FileUtil.browseForBinaryFile('Open Chart', [FileUtil.FILE_EXTENSION_INFO_FNFC], onSelectFile, onCancelBrowse); + FileUtilBase.browseForBinaryFile('Open Chart', [FileUtilBase.FILE_EXTENSION_INFO_FNFC], onSelectFile, onCancelBrowse); #else - FileUtil.browseForBinaryFile('Open Chart', null, onSelectFile, onCancelBrowse); + FileUtilBase.browseForBinaryFile('Open Chart', null, onSelectFile, onCancelBrowse); #end } diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx index 7539b9725..f93cf86ac 100644 --- a/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx +++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx @@ -5,7 +5,7 @@ import funkin.play.song.Song; import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import funkin.util.SortUtil; import haxe.ui.components.Label; import haxe.ui.components.Link; @@ -111,7 +111,7 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog } } - if (!FileUtil.doesFileExist(chartPath)) + if (!FileUtilBase.doesFileExist(chartPath)) { trace('Previously loaded chart file (${chartPath}) does not exist, disabling link...'); linkRecentChart.disabled = true; diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx index 26e246371..aa455cb1f 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx @@ -4,7 +4,7 @@ import flixel.system.FlxAssets.FlxSoundAsset; import funkin.audio.VoicesGroup; import funkin.audio.FunkinSound; import funkin.play.character.BaseCharacter.CharacterType; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import funkin.util.assets.SoundUtil; import funkin.util.TimerUtil; import funkin.audio.waveform.WaveformData; @@ -340,7 +340,7 @@ class ChartEditorAudioHandler trace('[WARN] Failed to access inst track ($key)'); continue; } - zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst.ogg', data)); + zipEntries.push(FileUtilBase.makeZIPEntryFromBytes('Inst.ogg', data)); } else { @@ -350,7 +350,7 @@ class ChartEditorAudioHandler trace('[WARN] Failed to access inst track ($key)'); continue; } - zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst-${key}.ogg', data)); + zipEntries.push(FileUtilBase.makeZIPEntryFromBytes('Inst-${key}.ogg', data)); } } @@ -375,7 +375,7 @@ class ChartEditorAudioHandler trace('[WARN] Failed to access vocal track ($key)'); continue; } - zipEntries.push(FileUtil.makeZIPEntryFromBytes('Voices-${key}.ogg', data)); + zipEntries.push(FileUtilBase.makeZIPEntryFromBytes('Voices-${key}.ogg', data)); } return zipEntries; diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx index b84c68f8d..4f47f5f3e 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx @@ -23,7 +23,7 @@ import funkin.ui.debug.charting.dialogs.ChartEditorUploadVocalsDialog; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import funkin.util.Constants; import funkin.util.DateUtil; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import funkin.util.SerializerUtil; import funkin.util.SortUtil; import funkin.util.VersionUtil; @@ -808,8 +808,11 @@ class ChartEditorDialogHandler } songVariationMetadataEntry.onClick = onClickMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel); #if FILE_DROP_SUPPORTED - state.addDropHandler({component: songVariationMetadataEntry, handler: onDropFileMetadataVariation.bind(variation) - .bind(songVariationMetadataEntryLabel)}); + state.addDropHandler( + { + component: songVariationMetadataEntry, + handler: onDropFileMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel) + }); #end chartContainerB.addComponent(songVariationMetadataEntry); @@ -847,7 +850,7 @@ class ChartEditorDialogHandler var path:Path = new Path(pathStr); trace('Dropped JSON file (${path})'); - var songMetadataTxt:String = FileUtil.readStringFromPath(path.toString()); + var songMetadataTxt:String = FileUtilBase.readStringFromPath(path.toString()); var songMetadataVersion:Null = VersionUtil.getVersionFromJSON(songMetadataTxt); if (songMetadataVersion == null) @@ -929,7 +932,7 @@ class ChartEditorDialogHandler var path:Path = new Path(pathStr); trace('Dropped JSON file (${path})'); - var songChartDataTxt:String = FileUtil.readStringFromPath(path.toString()); + var songChartDataTxt:String = FileUtilBase.readStringFromPath(path.toString()); var songChartDataVersion:Null = VersionUtil.getVersionFromJSON(songChartDataTxt); if (songChartDataVersion == null) @@ -1107,7 +1110,7 @@ class ChartEditorDialogHandler onDropFile = function(pathStr:String) { var path:Path = new Path(pathStr); - var selectedFileText:String = FileUtil.readStringFromPath(path.toString()); + var selectedFileText:String = FileUtilBase.readStringFromPath(path.toString()); var selectedFileData:FNFLegacyData = FNFLegacyImporter.parseLegacyDataRaw(selectedFileText, path.toString()); var songMetadata:SongMetadata = FNFLegacyImporter.migrateMetadata(selectedFileData); var songChartData:SongChartData = FNFLegacyImporter.migrateChartData(selectedFileData); diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx index e84f7ec43..caf71734c 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx @@ -5,7 +5,7 @@ import funkin.util.DateUtil; import haxe.io.Path; import funkin.util.SerializerUtil; import funkin.util.SortUtil; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import funkin.util.FileUtil.FileWriteMode; import haxe.io.Bytes; import funkin.play.song.Song; @@ -149,7 +149,7 @@ class ChartEditorImportExportHandler */ public static function loadFromFNFCPath(state:ChartEditorState, path:String):Null> { - var bytes:Null = FileUtil.readBytesFromPath(path); + var bytes:Null = FileUtilBase.readBytesFromPath(path); if (bytes == null) return null; trace('Loaded ${bytes.length} bytes from $path'); @@ -178,8 +178,8 @@ class ChartEditorImportExportHandler var songMetadatas:Map = []; var songChartDatas:Map = []; - var fileEntries:Array = FileUtil.readZIPFromBytes(bytes); - var mappedFileEntries:Map = FileUtil.mapZIPEntriesByName(fileEntries); + var fileEntries:Array = FileUtilBase.readZIPFromBytes(bytes); + var mappedFileEntries:Map = FileUtilBase.mapZIPEntriesByName(fileEntries); var manifestBytes:Null = mappedFileEntries.get('manifest.json')?.data; if (manifestBytes == null) throw 'Could not locate manifest.'; @@ -388,14 +388,14 @@ class ChartEditorImportExportHandler { variationMetadata.version = funkin.data.song.SongRegistry.SONG_METADATA_VERSION; variationMetadata.generatedBy = funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY; - zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata.json', variationMetadata.serialize())); + zipEntries.push(FileUtilBase.makeZIPEntry('${state.currentSongId}-metadata.json', variationMetadata.serialize())); } var variationChart:Null = state.songChartData.get(variation); if (variationChart != null) { variationChart.version = funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION; variationChart.generatedBy = funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY; - zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart.json', variationChart.serialize())); + zipEntries.push(FileUtilBase.makeZIPEntry('${state.currentSongId}-chart.json', variationChart.serialize())); } } else @@ -403,14 +403,14 @@ class ChartEditorImportExportHandler var variationMetadata:Null = state.songMetadata.get(variation); if (variationMetadata != null) { - zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata-$variationId.json', variationMetadata.serialize())); + zipEntries.push(FileUtilBase.makeZIPEntry('${state.currentSongId}-metadata-$variationId.json', variationMetadata.serialize())); } var variationChart:Null = state.songChartData.get(variation); if (variationChart != null) { variationChart.version = funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION; variationChart.generatedBy = funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY; - zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart-$variationId.json', variationChart.serialize())); + zipEntries.push(FileUtilBase.makeZIPEntry('${state.currentSongId}-chart-$variationId.json', variationChart.serialize())); } } } @@ -419,7 +419,7 @@ class ChartEditorImportExportHandler if (state.audioVocalTrackData != null) zipEntries = zipEntries.concat(state.makeZIPEntriesFromVocals()); var manifest:ChartManifestData = new ChartManifestData(state.currentSongId); - zipEntries.push(FileUtil.makeZIPEntry('manifest.json', manifest.serialize())); + zipEntries.push(FileUtilBase.makeZIPEntry('manifest.json', manifest.serialize())); trace('Exporting ${zipEntries.length} files to ZIP...'); @@ -438,7 +438,7 @@ class ChartEditorImportExportHandler trace('Force exporting to $targetPath...'); try { - FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode); + FileUtilBase.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode); // On success. if (onSaveCb != null) onSaveCb(targetPath); } @@ -455,7 +455,7 @@ class ChartEditorImportExportHandler try { // On success. - FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode); + FileUtilBase.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode); state.saveDataDirty = false; if (onSaveCb != null) onSaveCb(targetPath); } @@ -492,7 +492,7 @@ class ChartEditorImportExportHandler trace('Exporting to user-defined location...'); try { - FileUtil.saveChartAsFNFC(zipEntries, onSave, onCancel, '${state.currentSongId}.${Constants.EXT_CHART}'); + FileUtilBase.saveChartAsFNFC(zipEntries, onSave, onCancel, '${state.currentSongId}.${Constants.EXT_CHART}'); state.saveDataDirty = false; } catch (e) {} diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx index f82bc3c1f..e75d9a764 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx @@ -19,7 +19,7 @@ import funkin.data.stage.StageData; import haxe.ui.RuntimeComponentBuilder; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import funkin.ui.haxeui.components.CharacterPlayer; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import haxe.ui.components.Button; import haxe.ui.data.ArrayDataSource; import haxe.ui.components.CheckBox; diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx index 1163c1b96..7312eddaa 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx @@ -13,7 +13,7 @@ import haxe.ui.containers.dialogs.Dialog.DialogButton; import funkin.data.song.SongData.SongMetadata; import haxe.ui.components.DropDown; import haxe.ui.components.HorizontalSlider; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; import haxe.ui.containers.dialogs.MessageBox.MessageBoxType; import funkin.play.song.SongSerializer; import haxe.ui.components.Label; @@ -97,12 +97,12 @@ class ChartEditorDifficultyToolbox extends ChartEditorBaseToolbox difficultyToolboxSaveMetadata.onClick = function(_:UIEvent) { var vari:String = chartEditorState.selectedVariation != Constants.DEFAULT_VARIATION ? '-${chartEditorState.selectedVariation}' : ''; - FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-metadata.json', chartEditorState.currentSongMetadata.serialize()); + FileUtilBase.writeFileReference('${chartEditorState.currentSongId}$vari-metadata.json', chartEditorState.currentSongMetadata.serialize()); }; difficultyToolboxSaveChart.onClick = function(_:UIEvent) { var vari:String = chartEditorState.selectedVariation != Constants.DEFAULT_VARIATION ? '-${chartEditorState.selectedVariation}' : ''; - FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-chart.json', chartEditorState.currentSongChartData.serialize()); + FileUtilBase.writeFileReference('${chartEditorState.currentSongId}$vari-chart.json', chartEditorState.currentSongChartData.serialize()); }; difficultyToolboxLoadMetadata.onClick = function(_:UIEvent) { diff --git a/source/funkin/util/FileUtil.hx b/source/funkin/util/FileUtil.hx index 2900085e0..93c7c1705 100644 --- a/source/funkin/util/FileUtil.hx +++ b/source/funkin/util/FileUtil.hx @@ -13,10 +13,11 @@ import haxe.ui.containers.dialogs.Dialogs; import haxe.ui.containers.dialogs.Dialogs.SelectedFileInfo; import haxe.ui.containers.dialogs.Dialogs.FileDialogExtensionInfo; +using StringTools; /** * Utilities for reading and writing files on various platforms. */ -class FileUtil +class FileUtilBase { public static final FILE_FILTER_FNFC:FileFilter = new FileFilter("Friday Night Funkin' Chart (.fnfc)", "*.fnfc"); public static final FILE_FILTER_JSON:FileFilter = new FileFilter("JSON Data File (.json)", "*.json"); @@ -536,9 +537,7 @@ class FileUtil { if (isDirectory(path)) { - trace('WARNING: Path is a directory: $path - attempting to move as a directory instead'); - moveDir(path, destination); - return; + throw 'Path is a directory: $path'; } var destinationFolder:String = destination; @@ -651,17 +650,19 @@ class FileUtil #if sys if (!isDirectory(path)) { - trace('WARNING: Path is not a directory: $path, moving as a file instead'); - moveFile(path, destination); - return; + throw 'Path is not a directory: $path'; } createDirIfNotExists(destination); + if (!isDirectory(destination)) + { + throw 'Destination is not a directory: $destination'; + } ignore = ignore ?? []; for (entry in readDir(path)) { - if (ignore.indexOf(Path.join([path, entry])) != -1) continue; + if (ignore.contains(Path.join([path, entry]))) continue; if (isDirectory(Path.join([path, entry]))) { moveDir(Path.join([path, entry]), Path.join([destination, entry]), ignore); @@ -702,7 +703,7 @@ class FileUtil ignore = ignore ?? []; for (entry in readDir(path)) { - if (ignore.indexOf(Path.join([path, entry])) != -1) continue; + if (ignore.contains(Path.join([path, entry]))) continue; var entryPath = Path.join([path, entry]); if (isDirectory(entryPath)) { @@ -718,7 +719,9 @@ class FileUtil { sys.FileSystem.deleteDirectory(path); } - } else { + } + else + { sys.FileSystem.deleteDirectory(path); } #else @@ -819,7 +822,15 @@ class FileUtil } } - sys.FileSystem.rename(path, Path.join([Path.directory(path), newName])); + newName = Path.join([Path.directory(path), newName]); + if (doesFileExist(newName)) + { + throw 'File already exists: $newName'; + } + else + { + sys.FileSystem.rename(path, newName); + } } else { @@ -931,7 +942,7 @@ class FileUtil var filters:Array = []; for (type in typeFilter) { - filters.push(StringTools.replace(StringTools.replace(type.extension, '*.', ''), ';', ',')); + filters.push(type.extension.replace('*.', '').replace(';', ',')); } filter = filters.join(';'); } @@ -939,6 +950,307 @@ class FileUtil } } +/** + * Utilities for reading and writing files on various platforms. + * Wrapper for `FileUtilBase` that sanitizes paths for script safety. + */ +class FileUtil +{ + /** + * Prevent paths from exiting the root, preventing directory traversal attacks. + * + * @param path The path to sanitize. + * @return The sanitized path. + */ + public static function sanitizePath(path:String):String + { + path = path.trim().replace('\\', '/'); + + if (path.contains(':')) + { + path = path.substring(path.lastIndexOf(':') + 1); + } + + while (path.charAt(0) == '/') + { + path = path.substring(1); + } + + var parts:Array = path.split('/'); + var sanitized:Array = []; + for (part in parts) + { + switch (part) + { + case '.': + case '': + continue; + case '..': + if (sanitized.length > 0) sanitized.pop(); + default: + sanitized.push(part); + } + } + + return sanitized.join('/'); + } + + /** + * Paths which should not be deleted or modified by scripts. + */ + private static final PROTECTED:Array = [ + '', + 'assets', + 'manifest', + 'manifest/*', + 'plugins', + 'plugins/*', + 'Funkin.exe', + 'Funkin', + 'libvlc.dll', + 'libvlccore.dll', + 'lime.ndll' + ]; + + /** + * Check against protected paths. + * @param path The path to check. + * @return Whether the path is protected. + */ + public static function isProtected(path:String):Bool + { + path = sanitizePath(path); + for (protectedPath in PROTECTED) + { + if (path == protectedPath || (protectedPath.contains('*') && path.startsWith(protectedPath.replace('*', '')))) + { + return true; + } + } + + return false; + } + + public static final FILE_FILTER_FNFC:FileFilter = FileUtilBase.FILE_FILTER_FNFC; + public static final FILE_FILTER_JSON:FileFilter = FileUtilBase.FILE_FILTER_JSON; + public static final FILE_FILTER_ZIP:FileFilter = FileUtilBase.FILE_FILTER_ZIP; + public static final FILE_FILTER_PNG:FileFilter = FileUtilBase.FILE_FILTER_PNG; + + public static final FILE_EXTENSION_INFO_FNFC:FileDialogExtensionInfo = FileUtilBase.FILE_EXTENSION_INFO_FNFC; + public static final FILE_EXTENSION_INFO_ZIP:FileDialogExtensionInfo = FileUtilBase.FILE_EXTENSION_INFO_ZIP; + public static final FILE_EXTENSION_INFO_PNG:FileDialogExtensionInfo = FileUtilBase.FILE_EXTENSION_INFO_PNG; + + public static function browseForBinaryFile(dialogTitle:String, ?typeFilter:Array, ?onSelect:SelectedFileInfo->Void, + ?onCancel:Void->Void) + { + FileUtilBase.browseForBinaryFile(dialogTitle, typeFilter, onSelect, onCancel); + } + + public static function browseForTextFile(dialogTitle:String, ?typeFilter:Array, ?onSelect:SelectedFileInfo->Void, + ?onCancel:Void->Void) + { + FileUtilBase.browseForTextFile(dialogTitle, typeFilter, onSelect, onCancel); + } + + public static function browseForDirectory(?typeFilter:Array, ?onSelect:String->Void, ?onCancel:Void->Void, ?defaultPath:String, + ?dialogTitle:String):Bool + { + return FileUtilBase.browseForDirectory(typeFilter, onSelect, onCancel, defaultPath, dialogTitle); + } + + public static function browseForMultipleFiles(?typeFilter:Array, ?onSelect:Array->Void, ?onCancel:Void->Void, ?defaultPath:String, + ?dialogTitle:String):Bool + { + return FileUtilBase.browseForMultipleFiles(typeFilter, onSelect, onCancel, defaultPath, dialogTitle); + } + + public static function browseForSaveFile(?typeFilter:Array, ?onSelect:String->Void, ?onCancel:Void->Void, ?defaultPath:String, + ?dialogTitle:String):Bool + { + return FileUtilBase.browseForSaveFile(typeFilter, onSelect, onCancel, defaultPath, dialogTitle); + } + + public static function saveFile(data:Bytes, ?typeFilter:Array, ?onSave:String->Void, ?onCancel:Void->Void, ?defaultFileName:String, + ?dialogTitle:String):Bool + { + return FileUtilBase.saveFile(data, typeFilter, onSave, onCancel, defaultFileName, dialogTitle); + } + + public static function saveMultipleFiles(resources:Array, ?onSaveAll:Array->Void, ?onCancel:Void->Void, ?defaultPath:String, + force:Bool = false):Bool + { + return FileUtilBase.saveMultipleFiles(resources, onSaveAll, onCancel, defaultPath, force); + } + + public static function saveFilesAsZIP(resources:Array, ?onSave:Array->Void, ?onCancel:Void->Void, ?defaultPath:String, force:Bool = false):Bool + { + return FileUtilBase.saveFilesAsZIP(resources, onSave, onCancel, defaultPath, force); + } + + public static function saveChartAsFNFC(resources:Array, ?onSave:Array->Void, ?onCancel:Void->Void, ?defaultPath:String, + force:Bool = false):Bool + { + return FileUtilBase.saveChartAsFNFC(resources, onSave, onCancel, defaultPath, force); + } + + public static function saveFilesAsZIPToPath(resources:Array, path:String, mode:FileWriteMode = Skip):Bool + { + if (isProtected(path = sanitizePath(path))) return false; + return FileUtilBase.saveFilesAsZIPToPath(resources, path, mode); + } + + public static function readStringFromPath(path:String):String + { + path = sanitizePath(path); + return FileUtilBase.readStringFromPath(path); + } + + public static function readBytesFromPath(path:String):Bytes + { + path = sanitizePath(path); + return FileUtilBase.readBytesFromPath(path); + } + + public static function doesFileExist(path:String):Bool + { + path = sanitizePath(path); + return FileUtilBase.doesFileExist(path); + } + + public static function browseFileReference(callback:FileReference->Void) + { + FileUtilBase.browseFileReference(callback); + } + + public static function writeFileReference(path:String, data:String) + { + FileUtilBase.writeFileReference(path, data); + } + + public static function readJSONFromPath(path:String):Dynamic + { + path = sanitizePath(path); + return FileUtilBase.readJSONFromPath(path); + } + + public static function writeStringToPath(path:String, data:String, mode:FileWriteMode = Skip):Void + { + if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path'; + FileUtilBase.writeStringToPath(path, data, mode); + } + + public static function writeBytesToPath(path:String, data:Bytes, mode:FileWriteMode = Skip):Void + { + if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path'; + FileUtilBase.writeBytesToPath(path, data, mode); + } + + public static function appendStringToPath(path:String, data:String):Void + { + if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path'; + FileUtilBase.appendStringToPath(path, data); + } + + public static function moveFile(path:String, destination:String):Void + { + if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: $path'; + if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: $destination'; + FileUtilBase.moveFile(path, destination); + } + + public static function deleteFile(path:String):Void + { + if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: $path'; + FileUtilBase.deleteFile(path); + } + + public static function getFileSize(path:String):Int + { + path = sanitizePath(path); + return FileUtilBase.getFileSize(path); + } + + public static function isDirectory(path:String):Bool + { + path = sanitizePath(path); + return FileUtilBase.isDirectory(path); + } + + public static function createDirIfNotExists(dir:String):Void + { + dir = sanitizePath(dir); + FileUtilBase.createDirIfNotExists(dir); + } + + public static function readDir(path:String):Array + { + path = sanitizePath(path); + return FileUtilBase.readDir(path); + } + + public static function moveDir(path:String, destination:String, ?ignore:Array):Void + { + if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: $path'; + if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: $destination'; + FileUtilBase.moveDir(path, destination, ignore); + } + + public static function deleteDir(path:String, recursive:Bool = false, ?ignore:Array):Void + { + if (path.trim().replace('\\', '/').endsWith(':/')) throw 'Absolutely not.'; + if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: $path'; + FileUtilBase.deleteDir(path, recursive, ignore); + } + + public static function getDirSize(path:String):Int + { + path = sanitizePath(path); + return FileUtilBase.getDirSize(path); + } + + public static function getTempDir():String + { + return FileUtilBase.getTempDir(); + } + + public static function rename(path:String, newName:String, keepExtension:Bool = true):Void + { + if (isProtected(path = sanitizePath(path))) throw 'Cannot rename protected path: $path'; + newName = sanitizePath(newName); + FileUtilBase.rename(path, newName, keepExtension); + } + + public static function createZIPFromEntries(entries:Array):Bytes + { + return FileUtilBase.createZIPFromEntries(entries); + } + + public static function readZIPFromBytes(input:Bytes):Array + { + return FileUtilBase.readZIPFromBytes(input); + } + + public static function mapZIPEntriesByName(input:Array):Map + { + return FileUtilBase.mapZIPEntriesByName(input); + } + + public static function makeZIPEntry(name:String, content:String):Entry + { + return FileUtilBase.makeZIPEntry(name, content); + } + + public static function makeZIPEntryFromBytes(name:String, data:haxe.io.Bytes):Entry + { + return FileUtilBase.makeZIPEntryFromBytes(name, data); + } + + public static function openFolder(pathFolder:String) + { + FileUtilBase.openFolder(pathFolder); + } +} + enum FileWriteMode { /** diff --git a/source/funkin/util/logging/CrashHandler.hx b/source/funkin/util/logging/CrashHandler.hx index 71d1ad394..3d2c06b3d 100644 --- a/source/funkin/util/logging/CrashHandler.hx +++ b/source/funkin/util/logging/CrashHandler.hx @@ -4,6 +4,7 @@ import openfl.Lib; import openfl.events.UncaughtErrorEvent; import flixel.util.FlxSignal.FlxTypedSignal; import flixel.FlxG.FlxRenderMethod; +import funkin.util.FileUtil.FileUtilBase; /** * A custom crash handler that writes to a log file and displays a message box. @@ -106,7 +107,7 @@ class CrashHandler static function logErrorMessage(message:String, critical:Bool = false):Void { - FileUtil.createDirIfNotExists(LOG_FOLDER); + FileUtilBase.createDirIfNotExists(LOG_FOLDER); sys.io.File.saveContent('$LOG_FOLDER/crash${critical ? '-critical' : ''}-${DateUtil.generateTimestamp()}.log', buildCrashReport(message)); } diff --git a/source/funkin/util/plugins/ScreenshotPlugin.hx b/source/funkin/util/plugins/ScreenshotPlugin.hx index c859710de..cc9b9a12a 100644 --- a/source/funkin/util/plugins/ScreenshotPlugin.hx +++ b/source/funkin/util/plugins/ScreenshotPlugin.hx @@ -14,6 +14,7 @@ import flixel.util.FlxTimer; import funkin.graphics.FunkinSprite; import funkin.input.Cursor; import funkin.audio.FunkinSound; +import funkin.util.FileUtil.FileUtilBase; import openfl.display.Bitmap; import openfl.display.Sprite; import openfl.display.BitmapData; @@ -267,7 +268,7 @@ class ScreenshotPlugin extends FlxBasic function openScreenshotsFolder(e:MouseEvent):Void { - FileUtil.openFolder(SCREENSHOT_FOLDER); + FileUtilBase.openFolder(SCREENSHOT_FOLDER); } static function getCurrentState():FlxState @@ -287,7 +288,7 @@ class ScreenshotPlugin extends FlxBasic static function makeScreenshotPath():Void { - FileUtil.createDirIfNotExists(SCREENSHOT_FOLDER); + FileUtilBase.createDirIfNotExists(SCREENSHOT_FOLDER); } /** @@ -318,7 +319,7 @@ class ScreenshotPlugin extends FlxBasic { trace('Saving screenshot to: ' + targetPath); // TODO: Make this work on browser. - FileUtil.writeBytesToPath(targetPath, pngData); + FileUtilBase.writeBytesToPath(targetPath, pngData); } } } diff --git a/tests/unit/source/TestMain.hx b/tests/unit/source/TestMain.hx index 11eb87b33..ee52a1649 100644 --- a/tests/unit/source/TestMain.hx +++ b/tests/unit/source/TestMain.hx @@ -7,7 +7,7 @@ import massive.munit.TestRunner; import massive.munit.client.HTTPClient; import massive.munit.client.SummaryReportClient; import funkin.util.logging.CrashHandler; -import funkin.util.FileUtil; +import funkin.util.FileUtil.FileUtilBase; /** * Auto generated Test Application. @@ -50,7 +50,7 @@ class TestMain // NOTE: You can also create a custom ICoverageTestResultClient implementation // Output coverage in LCOV format. - FileUtil.createDirIfNotExists(COVERAGE_FOLDER); + FileUtilBase.createDirIfNotExists(COVERAGE_FOLDER); mcover.coverage.MCoverage.getLogger().addClient(new mcover.coverage.client.LcovPrintClient("Funkin' Coverage Report", '${COVERAGE_FOLDER}/lcov.info')); #else // Print individual test results. From fb93398f8a6b8b735fb5394ca54d18e41e31dc26 Mon Sep 17 00:00:00 2001 From: cyn Date: Tue, 16 Jul 2024 22:47:05 -0700 Subject: [PATCH 4/9] im stupid thats not what that means --- source/funkin/util/FileUtil.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/util/FileUtil.hx b/source/funkin/util/FileUtil.hx index 93c7c1705..537c49ff0 100644 --- a/source/funkin/util/FileUtil.hx +++ b/source/funkin/util/FileUtil.hx @@ -957,7 +957,7 @@ class FileUtilBase class FileUtil { /** - * Prevent paths from exiting the root, preventing directory traversal attacks. + * Prevent paths from exiting the root. * * @param path The path to sanitize. * @return The sanitized path. From dd6889d58deeb529953f27608cba9d3244ef674c Mon Sep 17 00:00:00 2001 From: cyn Date: Fri, 26 Jul 2024 18:08:37 -0700 Subject: [PATCH 5/9] change class names --- source/funkin/modding/PolymodHandler.hx | 11 +-- source/funkin/play/song/SongSerializer.hx | 2 +- source/funkin/save/Save.hx | 4 +- source/funkin/ui/debug/DebugMenuSubState.hx | 4 +- .../ui/debug/charting/ChartEditorState.hx | 4 +- .../dialogs/ChartEditorUploadChartDialog.hx | 6 +- .../dialogs/ChartEditorUploadVocalsDialog.hx | 6 +- .../dialogs/ChartEditorWelcomeDialog.hx | 4 +- .../handlers/ChartEditorAudioHandler.hx | 8 +- .../handlers/ChartEditorDialogHandler.hx | 8 +- .../ChartEditorImportExportHandler.hx | 24 ++--- .../handlers/ChartEditorToolboxHandler.hx | 1 - .../toolboxes/ChartEditorDifficultyToolbox.hx | 6 +- source/funkin/util/FileUtil.hx | 93 ++++++++++--------- source/funkin/util/logging/CrashHandler.hx | 4 +- .../funkin/util/plugins/ScreenshotPlugin.hx | 8 +- tests/unit/source/TestMain.hx | 4 +- 17 files changed, 98 insertions(+), 99 deletions(-) diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index a406e2aa2..fb8f8f56c 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -13,7 +13,7 @@ import funkin.data.freeplay.album.AlbumRegistry; import funkin.modding.module.ModuleHandler; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.save.Save; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import funkin.util.macro.ClassMacro; import polymod.backends.PolymodAssets.PolymodAssetType; import polymod.format.ParseRules.TextFileFormat; @@ -63,7 +63,7 @@ class PolymodHandler */ public static function createModRoot():Void { - FileUtilBase.createDirIfNotExists(MOD_FOLDER); + FileUtil.createDirIfNotExists(MOD_FOLDER); } /** @@ -230,8 +230,11 @@ class PolymodHandler // Add import aliases for certain classes. // NOTE: Scripted classes are automatically aliased to their parent class. + Polymod.addImportAlias('flixel.math.FlxPoint', flixel.math.FlxPoint.FlxBasePoint); + Polymod.addImportAlias('funkin.util.FileUtil', funkin.util.FileUtil.FileUtilSandboxed); + // Add blacklisting for prohibited classes and packages. // `Sys` @@ -266,10 +269,6 @@ class PolymodHandler var className:String = Type.getClassName(cls); Polymod.blacklistImport(className); } - - // `FileUtilBase` - // has unrestricted filesystem access - Polymod.blacklistImport('funkin.util.FileUtilBase'); } static function buildParseRules():polymod.format.ParseRules diff --git a/source/funkin/play/song/SongSerializer.hx b/source/funkin/play/song/SongSerializer.hx index 3f719b6f8..74fcc253a 100644 --- a/source/funkin/play/song/SongSerializer.hx +++ b/source/funkin/play/song/SongSerializer.hx @@ -3,7 +3,7 @@ package funkin.play.song; import funkin.data.song.SongData.SongChartData; import funkin.data.song.SongData.SongMetadata; import funkin.util.SerializerUtil; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import lime.utils.Bytes; import openfl.events.Event; import openfl.events.IOErrorEvent; diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 9f370475c..2900ce2be 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -1,7 +1,7 @@ package funkin.save; import flixel.util.FlxSave; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import funkin.input.Controls.Device; import funkin.play.scoring.Scoring; import funkin.play.scoring.Scoring.ScoringRank; @@ -925,7 +925,7 @@ class Save public function debug_dumpSave():Void { - FileUtilBase.saveFile(haxe.io.Bytes.ofString(this.serialize()), [FileUtilBase.FILE_FILTER_JSON], null, null, './save.json', 'Write save data as JSON...'); + FileUtil.saveFile(haxe.io.Bytes.ofString(this.serialize()), [FileUtil.FILE_FILTER_JSON], null, null, './save.json', 'Write save data as JSON...'); } } diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index 81eb929d9..f8b1be9d2 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -10,7 +10,7 @@ import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.MusicBeatSubState; import funkin.util.logging.CrashHandler; import flixel.addons.transition.FlxTransitionableState; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; class DebugMenuSubState extends MusicBeatSubState { @@ -122,7 +122,7 @@ class DebugMenuSubState extends MusicBeatSubState #if sys function openLogFolder() { - FileUtilBase.openFolder(CrashHandler.LOG_FOLDER); + FileUtil.openFolder(CrashHandler.LOG_FOLDER); } #end diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 15685d2b3..f72cca77f 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -92,7 +92,7 @@ import funkin.ui.haxeui.HaxeUIState; import funkin.ui.mainmenu.MainMenuState; import funkin.ui.transition.LoadingState; import funkin.util.Constants; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import funkin.util.logging.CrashHandler; import funkin.util.SortUtil; import funkin.util.WindowUtil; @@ -2348,7 +2348,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } } - if (!FileUtilBase.doesFileExist(chartPath)) + if (!FileUtil.doesFileExist(chartPath)) { trace('Previously loaded chart file (${chartPath.toString()}) does not exist, disabling link...'); menuItemRecentChart.disabled = true; diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx index 8744a3759..17f047106 100644 --- a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx +++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx @@ -3,7 +3,7 @@ package funkin.ui.debug.charting.dialogs; import funkin.input.Cursor; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogDropTarget; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import haxe.io.Path; import haxe.ui.containers.dialogs.Dialog.DialogButton; import haxe.ui.containers.dialogs.Dialog.DialogEvent; @@ -96,9 +96,9 @@ class ChartEditorUploadChartDialog extends ChartEditorBaseDialog this.lock(); // TODO / BUG: File filtering not working on mac finder dialog, so we don't use it for now #if !mac - FileUtilBase.browseForBinaryFile('Open Chart', [FileUtilBase.FILE_EXTENSION_INFO_FNFC], onSelectFile, onCancelBrowse); + FileUtil.browseForBinaryFile('Open Chart', [FileUtil.FILE_EXTENSION_INFO_FNFC], onSelectFile, onCancelBrowse); #else - FileUtilBase.browseForBinaryFile('Open Chart', null, onSelectFile, onCancelBrowse); + FileUtil.browseForBinaryFile('Open Chart', null, onSelectFile, onCancelBrowse); #end } diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadVocalsDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadVocalsDialog.hx index 407422510..537c7c36e 100644 --- a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadVocalsDialog.hx +++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadVocalsDialog.hx @@ -3,7 +3,7 @@ package funkin.ui.debug.charting.dialogs; import funkin.input.Cursor; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogDropTarget; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import funkin.play.character.CharacterData; import haxe.io.Path; import haxe.ui.components.Button; @@ -210,9 +210,9 @@ class ChartEditorUploadVocalsDialog extends ChartEditorBaseDialog this.lock(); // TODO / BUG: File filtering not working on mac finder dialog, so we don't use it for now #if !mac - FileUtilBase.browseForBinaryFile('Open Chart', [FileUtilBase.FILE_EXTENSION_INFO_FNFC], onSelectFile, onCancelBrowse); + FileUtil.browseForBinaryFile('Open Chart', [FileUtil.FILE_EXTENSION_INFO_FNFC], onSelectFile, onCancelBrowse); #else - FileUtilBase.browseForBinaryFile('Open Chart', null, onSelectFile, onCancelBrowse); + FileUtil.browseForBinaryFile('Open Chart', null, onSelectFile, onCancelBrowse); #end } diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx index f93cf86ac..7539b9725 100644 --- a/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx +++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx @@ -5,7 +5,7 @@ import funkin.play.song.Song; import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog; import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import funkin.util.SortUtil; import haxe.ui.components.Label; import haxe.ui.components.Link; @@ -111,7 +111,7 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog } } - if (!FileUtilBase.doesFileExist(chartPath)) + if (!FileUtil.doesFileExist(chartPath)) { trace('Previously loaded chart file (${chartPath}) does not exist, disabling link...'); linkRecentChart.disabled = true; diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx index aa455cb1f..26e246371 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx @@ -4,7 +4,7 @@ import flixel.system.FlxAssets.FlxSoundAsset; import funkin.audio.VoicesGroup; import funkin.audio.FunkinSound; import funkin.play.character.BaseCharacter.CharacterType; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import funkin.util.assets.SoundUtil; import funkin.util.TimerUtil; import funkin.audio.waveform.WaveformData; @@ -340,7 +340,7 @@ class ChartEditorAudioHandler trace('[WARN] Failed to access inst track ($key)'); continue; } - zipEntries.push(FileUtilBase.makeZIPEntryFromBytes('Inst.ogg', data)); + zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst.ogg', data)); } else { @@ -350,7 +350,7 @@ class ChartEditorAudioHandler trace('[WARN] Failed to access inst track ($key)'); continue; } - zipEntries.push(FileUtilBase.makeZIPEntryFromBytes('Inst-${key}.ogg', data)); + zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst-${key}.ogg', data)); } } @@ -375,7 +375,7 @@ class ChartEditorAudioHandler trace('[WARN] Failed to access vocal track ($key)'); continue; } - zipEntries.push(FileUtilBase.makeZIPEntryFromBytes('Voices-${key}.ogg', data)); + zipEntries.push(FileUtil.makeZIPEntryFromBytes('Voices-${key}.ogg', data)); } return zipEntries; diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx index 4f47f5f3e..ab13da1d9 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx @@ -23,7 +23,7 @@ import funkin.ui.debug.charting.dialogs.ChartEditorUploadVocalsDialog; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import funkin.util.Constants; import funkin.util.DateUtil; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import funkin.util.SerializerUtil; import funkin.util.SortUtil; import funkin.util.VersionUtil; @@ -850,7 +850,7 @@ class ChartEditorDialogHandler var path:Path = new Path(pathStr); trace('Dropped JSON file (${path})'); - var songMetadataTxt:String = FileUtilBase.readStringFromPath(path.toString()); + var songMetadataTxt:String = FileUtil.readStringFromPath(path.toString()); var songMetadataVersion:Null = VersionUtil.getVersionFromJSON(songMetadataTxt); if (songMetadataVersion == null) @@ -932,7 +932,7 @@ class ChartEditorDialogHandler var path:Path = new Path(pathStr); trace('Dropped JSON file (${path})'); - var songChartDataTxt:String = FileUtilBase.readStringFromPath(path.toString()); + var songChartDataTxt:String = FileUtil.readStringFromPath(path.toString()); var songChartDataVersion:Null = VersionUtil.getVersionFromJSON(songChartDataTxt); if (songChartDataVersion == null) @@ -1110,7 +1110,7 @@ class ChartEditorDialogHandler onDropFile = function(pathStr:String) { var path:Path = new Path(pathStr); - var selectedFileText:String = FileUtilBase.readStringFromPath(path.toString()); + var selectedFileText:String = FileUtil.readStringFromPath(path.toString()); var selectedFileData:FNFLegacyData = FNFLegacyImporter.parseLegacyDataRaw(selectedFileText, path.toString()); var songMetadata:SongMetadata = FNFLegacyImporter.migrateMetadata(selectedFileData); var songChartData:SongChartData = FNFLegacyImporter.migrateChartData(selectedFileData); diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx index caf71734c..e84f7ec43 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx @@ -5,7 +5,7 @@ import funkin.util.DateUtil; import haxe.io.Path; import funkin.util.SerializerUtil; import funkin.util.SortUtil; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import funkin.util.FileUtil.FileWriteMode; import haxe.io.Bytes; import funkin.play.song.Song; @@ -149,7 +149,7 @@ class ChartEditorImportExportHandler */ public static function loadFromFNFCPath(state:ChartEditorState, path:String):Null> { - var bytes:Null = FileUtilBase.readBytesFromPath(path); + var bytes:Null = FileUtil.readBytesFromPath(path); if (bytes == null) return null; trace('Loaded ${bytes.length} bytes from $path'); @@ -178,8 +178,8 @@ class ChartEditorImportExportHandler var songMetadatas:Map = []; var songChartDatas:Map = []; - var fileEntries:Array = FileUtilBase.readZIPFromBytes(bytes); - var mappedFileEntries:Map = FileUtilBase.mapZIPEntriesByName(fileEntries); + var fileEntries:Array = FileUtil.readZIPFromBytes(bytes); + var mappedFileEntries:Map = FileUtil.mapZIPEntriesByName(fileEntries); var manifestBytes:Null = mappedFileEntries.get('manifest.json')?.data; if (manifestBytes == null) throw 'Could not locate manifest.'; @@ -388,14 +388,14 @@ class ChartEditorImportExportHandler { variationMetadata.version = funkin.data.song.SongRegistry.SONG_METADATA_VERSION; variationMetadata.generatedBy = funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY; - zipEntries.push(FileUtilBase.makeZIPEntry('${state.currentSongId}-metadata.json', variationMetadata.serialize())); + zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata.json', variationMetadata.serialize())); } var variationChart:Null = state.songChartData.get(variation); if (variationChart != null) { variationChart.version = funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION; variationChart.generatedBy = funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY; - zipEntries.push(FileUtilBase.makeZIPEntry('${state.currentSongId}-chart.json', variationChart.serialize())); + zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart.json', variationChart.serialize())); } } else @@ -403,14 +403,14 @@ class ChartEditorImportExportHandler var variationMetadata:Null = state.songMetadata.get(variation); if (variationMetadata != null) { - zipEntries.push(FileUtilBase.makeZIPEntry('${state.currentSongId}-metadata-$variationId.json', variationMetadata.serialize())); + zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata-$variationId.json', variationMetadata.serialize())); } var variationChart:Null = state.songChartData.get(variation); if (variationChart != null) { variationChart.version = funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION; variationChart.generatedBy = funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY; - zipEntries.push(FileUtilBase.makeZIPEntry('${state.currentSongId}-chart-$variationId.json', variationChart.serialize())); + zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart-$variationId.json', variationChart.serialize())); } } } @@ -419,7 +419,7 @@ class ChartEditorImportExportHandler if (state.audioVocalTrackData != null) zipEntries = zipEntries.concat(state.makeZIPEntriesFromVocals()); var manifest:ChartManifestData = new ChartManifestData(state.currentSongId); - zipEntries.push(FileUtilBase.makeZIPEntry('manifest.json', manifest.serialize())); + zipEntries.push(FileUtil.makeZIPEntry('manifest.json', manifest.serialize())); trace('Exporting ${zipEntries.length} files to ZIP...'); @@ -438,7 +438,7 @@ class ChartEditorImportExportHandler trace('Force exporting to $targetPath...'); try { - FileUtilBase.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode); + FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode); // On success. if (onSaveCb != null) onSaveCb(targetPath); } @@ -455,7 +455,7 @@ class ChartEditorImportExportHandler try { // On success. - FileUtilBase.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode); + FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode); state.saveDataDirty = false; if (onSaveCb != null) onSaveCb(targetPath); } @@ -492,7 +492,7 @@ class ChartEditorImportExportHandler trace('Exporting to user-defined location...'); try { - FileUtilBase.saveChartAsFNFC(zipEntries, onSave, onCancel, '${state.currentSongId}.${Constants.EXT_CHART}'); + FileUtil.saveChartAsFNFC(zipEntries, onSave, onCancel, '${state.currentSongId}.${Constants.EXT_CHART}'); state.saveDataDirty = false; } catch (e) {} diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx index e75d9a764..396547ed2 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx @@ -19,7 +19,6 @@ import funkin.data.stage.StageData; import haxe.ui.RuntimeComponentBuilder; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import funkin.ui.haxeui.components.CharacterPlayer; -import funkin.util.FileUtil.FileUtilBase; import haxe.ui.components.Button; import haxe.ui.data.ArrayDataSource; import haxe.ui.components.CheckBox; diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx index 7312eddaa..1163c1b96 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx @@ -13,7 +13,7 @@ import haxe.ui.containers.dialogs.Dialog.DialogButton; import funkin.data.song.SongData.SongMetadata; import haxe.ui.components.DropDown; import haxe.ui.components.HorizontalSlider; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import haxe.ui.containers.dialogs.MessageBox.MessageBoxType; import funkin.play.song.SongSerializer; import haxe.ui.components.Label; @@ -97,12 +97,12 @@ class ChartEditorDifficultyToolbox extends ChartEditorBaseToolbox difficultyToolboxSaveMetadata.onClick = function(_:UIEvent) { var vari:String = chartEditorState.selectedVariation != Constants.DEFAULT_VARIATION ? '-${chartEditorState.selectedVariation}' : ''; - FileUtilBase.writeFileReference('${chartEditorState.currentSongId}$vari-metadata.json', chartEditorState.currentSongMetadata.serialize()); + FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-metadata.json', chartEditorState.currentSongMetadata.serialize()); }; difficultyToolboxSaveChart.onClick = function(_:UIEvent) { var vari:String = chartEditorState.selectedVariation != Constants.DEFAULT_VARIATION ? '-${chartEditorState.selectedVariation}' : ''; - FileUtilBase.writeFileReference('${chartEditorState.currentSongId}$vari-chart.json', chartEditorState.currentSongChartData.serialize()); + FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-chart.json', chartEditorState.currentSongChartData.serialize()); }; difficultyToolboxLoadMetadata.onClick = function(_:UIEvent) { diff --git a/source/funkin/util/FileUtil.hx b/source/funkin/util/FileUtil.hx index 537c49ff0..700cb3af2 100644 --- a/source/funkin/util/FileUtil.hx +++ b/source/funkin/util/FileUtil.hx @@ -14,10 +14,11 @@ import haxe.ui.containers.dialogs.Dialogs.SelectedFileInfo; import haxe.ui.containers.dialogs.Dialogs.FileDialogExtensionInfo; using StringTools; + /** * Utilities for reading and writing files on various platforms. */ -class FileUtilBase +class FileUtil { public static final FILE_FILTER_FNFC:FileFilter = new FileFilter("Friday Night Funkin' Chart (.fnfc)", "*.fnfc"); public static final FILE_FILTER_JSON:FileFilter = new FileFilter("JSON Data File (.json)", "*.json"); @@ -952,9 +953,9 @@ class FileUtilBase /** * Utilities for reading and writing files on various platforms. - * Wrapper for `FileUtilBase` that sanitizes paths for script safety. + * Wrapper for `FileUtil` that sanitizes paths for script safety. */ -class FileUtil +class FileUtilSandboxed { /** * Prevent paths from exiting the root. @@ -1031,223 +1032,223 @@ class FileUtil return false; } - public static final FILE_FILTER_FNFC:FileFilter = FileUtilBase.FILE_FILTER_FNFC; - public static final FILE_FILTER_JSON:FileFilter = FileUtilBase.FILE_FILTER_JSON; - public static final FILE_FILTER_ZIP:FileFilter = FileUtilBase.FILE_FILTER_ZIP; - public static final FILE_FILTER_PNG:FileFilter = FileUtilBase.FILE_FILTER_PNG; + public static final FILE_FILTER_FNFC:FileFilter = FileUtil.FILE_FILTER_FNFC; + public static final FILE_FILTER_JSON:FileFilter = FileUtil.FILE_FILTER_JSON; + public static final FILE_FILTER_ZIP:FileFilter = FileUtil.FILE_FILTER_ZIP; + public static final FILE_FILTER_PNG:FileFilter = FileUtil.FILE_FILTER_PNG; - public static final FILE_EXTENSION_INFO_FNFC:FileDialogExtensionInfo = FileUtilBase.FILE_EXTENSION_INFO_FNFC; - public static final FILE_EXTENSION_INFO_ZIP:FileDialogExtensionInfo = FileUtilBase.FILE_EXTENSION_INFO_ZIP; - public static final FILE_EXTENSION_INFO_PNG:FileDialogExtensionInfo = FileUtilBase.FILE_EXTENSION_INFO_PNG; + public static final FILE_EXTENSION_INFO_FNFC:FileDialogExtensionInfo = FileUtil.FILE_EXTENSION_INFO_FNFC; + public static final FILE_EXTENSION_INFO_ZIP:FileDialogExtensionInfo = FileUtil.FILE_EXTENSION_INFO_ZIP; + public static final FILE_EXTENSION_INFO_PNG:FileDialogExtensionInfo = FileUtil.FILE_EXTENSION_INFO_PNG; public static function browseForBinaryFile(dialogTitle:String, ?typeFilter:Array, ?onSelect:SelectedFileInfo->Void, ?onCancel:Void->Void) { - FileUtilBase.browseForBinaryFile(dialogTitle, typeFilter, onSelect, onCancel); + FileUtil.browseForBinaryFile(dialogTitle, typeFilter, onSelect, onCancel); } public static function browseForTextFile(dialogTitle:String, ?typeFilter:Array, ?onSelect:SelectedFileInfo->Void, ?onCancel:Void->Void) { - FileUtilBase.browseForTextFile(dialogTitle, typeFilter, onSelect, onCancel); + FileUtil.browseForTextFile(dialogTitle, typeFilter, onSelect, onCancel); } public static function browseForDirectory(?typeFilter:Array, ?onSelect:String->Void, ?onCancel:Void->Void, ?defaultPath:String, ?dialogTitle:String):Bool { - return FileUtilBase.browseForDirectory(typeFilter, onSelect, onCancel, defaultPath, dialogTitle); + return FileUtil.browseForDirectory(typeFilter, onSelect, onCancel, defaultPath, dialogTitle); } public static function browseForMultipleFiles(?typeFilter:Array, ?onSelect:Array->Void, ?onCancel:Void->Void, ?defaultPath:String, ?dialogTitle:String):Bool { - return FileUtilBase.browseForMultipleFiles(typeFilter, onSelect, onCancel, defaultPath, dialogTitle); + return FileUtil.browseForMultipleFiles(typeFilter, onSelect, onCancel, defaultPath, dialogTitle); } public static function browseForSaveFile(?typeFilter:Array, ?onSelect:String->Void, ?onCancel:Void->Void, ?defaultPath:String, ?dialogTitle:String):Bool { - return FileUtilBase.browseForSaveFile(typeFilter, onSelect, onCancel, defaultPath, dialogTitle); + return FileUtil.browseForSaveFile(typeFilter, onSelect, onCancel, defaultPath, dialogTitle); } public static function saveFile(data:Bytes, ?typeFilter:Array, ?onSave:String->Void, ?onCancel:Void->Void, ?defaultFileName:String, ?dialogTitle:String):Bool { - return FileUtilBase.saveFile(data, typeFilter, onSave, onCancel, defaultFileName, dialogTitle); + return FileUtil.saveFile(data, typeFilter, onSave, onCancel, defaultFileName, dialogTitle); } public static function saveMultipleFiles(resources:Array, ?onSaveAll:Array->Void, ?onCancel:Void->Void, ?defaultPath:String, force:Bool = false):Bool { - return FileUtilBase.saveMultipleFiles(resources, onSaveAll, onCancel, defaultPath, force); + return FileUtil.saveMultipleFiles(resources, onSaveAll, onCancel, defaultPath, force); } public static function saveFilesAsZIP(resources:Array, ?onSave:Array->Void, ?onCancel:Void->Void, ?defaultPath:String, force:Bool = false):Bool { - return FileUtilBase.saveFilesAsZIP(resources, onSave, onCancel, defaultPath, force); + return FileUtil.saveFilesAsZIP(resources, onSave, onCancel, defaultPath, force); } public static function saveChartAsFNFC(resources:Array, ?onSave:Array->Void, ?onCancel:Void->Void, ?defaultPath:String, force:Bool = false):Bool { - return FileUtilBase.saveChartAsFNFC(resources, onSave, onCancel, defaultPath, force); + return FileUtil.saveChartAsFNFC(resources, onSave, onCancel, defaultPath, force); } public static function saveFilesAsZIPToPath(resources:Array, path:String, mode:FileWriteMode = Skip):Bool { if (isProtected(path = sanitizePath(path))) return false; - return FileUtilBase.saveFilesAsZIPToPath(resources, path, mode); + return FileUtil.saveFilesAsZIPToPath(resources, path, mode); } public static function readStringFromPath(path:String):String { path = sanitizePath(path); - return FileUtilBase.readStringFromPath(path); + return FileUtil.readStringFromPath(path); } public static function readBytesFromPath(path:String):Bytes { path = sanitizePath(path); - return FileUtilBase.readBytesFromPath(path); + return FileUtil.readBytesFromPath(path); } public static function doesFileExist(path:String):Bool { path = sanitizePath(path); - return FileUtilBase.doesFileExist(path); + return FileUtil.doesFileExist(path); } public static function browseFileReference(callback:FileReference->Void) { - FileUtilBase.browseFileReference(callback); + FileUtil.browseFileReference(callback); } public static function writeFileReference(path:String, data:String) { - FileUtilBase.writeFileReference(path, data); + FileUtil.writeFileReference(path, data); } public static function readJSONFromPath(path:String):Dynamic { path = sanitizePath(path); - return FileUtilBase.readJSONFromPath(path); + return FileUtil.readJSONFromPath(path); } public static function writeStringToPath(path:String, data:String, mode:FileWriteMode = Skip):Void { if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path'; - FileUtilBase.writeStringToPath(path, data, mode); + FileUtil.writeStringToPath(path, data, mode); } public static function writeBytesToPath(path:String, data:Bytes, mode:FileWriteMode = Skip):Void { if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path'; - FileUtilBase.writeBytesToPath(path, data, mode); + FileUtil.writeBytesToPath(path, data, mode); } public static function appendStringToPath(path:String, data:String):Void { if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path'; - FileUtilBase.appendStringToPath(path, data); + FileUtil.appendStringToPath(path, data); } public static function moveFile(path:String, destination:String):Void { if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: $path'; if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: $destination'; - FileUtilBase.moveFile(path, destination); + FileUtil.moveFile(path, destination); } public static function deleteFile(path:String):Void { if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: $path'; - FileUtilBase.deleteFile(path); + FileUtil.deleteFile(path); } public static function getFileSize(path:String):Int { path = sanitizePath(path); - return FileUtilBase.getFileSize(path); + return FileUtil.getFileSize(path); } public static function isDirectory(path:String):Bool { path = sanitizePath(path); - return FileUtilBase.isDirectory(path); + return FileUtil.isDirectory(path); } public static function createDirIfNotExists(dir:String):Void { dir = sanitizePath(dir); - FileUtilBase.createDirIfNotExists(dir); + FileUtil.createDirIfNotExists(dir); } public static function readDir(path:String):Array { path = sanitizePath(path); - return FileUtilBase.readDir(path); + return FileUtil.readDir(path); } public static function moveDir(path:String, destination:String, ?ignore:Array):Void { if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: $path'; if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: $destination'; - FileUtilBase.moveDir(path, destination, ignore); + FileUtil.moveDir(path, destination, ignore); } public static function deleteDir(path:String, recursive:Bool = false, ?ignore:Array):Void { if (path.trim().replace('\\', '/').endsWith(':/')) throw 'Absolutely not.'; if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: $path'; - FileUtilBase.deleteDir(path, recursive, ignore); + FileUtil.deleteDir(path, recursive, ignore); } public static function getDirSize(path:String):Int { path = sanitizePath(path); - return FileUtilBase.getDirSize(path); + return FileUtil.getDirSize(path); } public static function getTempDir():String { - return FileUtilBase.getTempDir(); + return FileUtil.getTempDir(); } public static function rename(path:String, newName:String, keepExtension:Bool = true):Void { if (isProtected(path = sanitizePath(path))) throw 'Cannot rename protected path: $path'; newName = sanitizePath(newName); - FileUtilBase.rename(path, newName, keepExtension); + FileUtil.rename(path, newName, keepExtension); } public static function createZIPFromEntries(entries:Array):Bytes { - return FileUtilBase.createZIPFromEntries(entries); + return FileUtil.createZIPFromEntries(entries); } public static function readZIPFromBytes(input:Bytes):Array { - return FileUtilBase.readZIPFromBytes(input); + return FileUtil.readZIPFromBytes(input); } public static function mapZIPEntriesByName(input:Array):Map { - return FileUtilBase.mapZIPEntriesByName(input); + return FileUtil.mapZIPEntriesByName(input); } public static function makeZIPEntry(name:String, content:String):Entry { - return FileUtilBase.makeZIPEntry(name, content); + return FileUtil.makeZIPEntry(name, content); } public static function makeZIPEntryFromBytes(name:String, data:haxe.io.Bytes):Entry { - return FileUtilBase.makeZIPEntryFromBytes(name, data); + return FileUtil.makeZIPEntryFromBytes(name, data); } public static function openFolder(pathFolder:String) { - FileUtilBase.openFolder(pathFolder); + FileUtil.openFolder(pathFolder); } } diff --git a/source/funkin/util/logging/CrashHandler.hx b/source/funkin/util/logging/CrashHandler.hx index 3d2c06b3d..23c6f57f7 100644 --- a/source/funkin/util/logging/CrashHandler.hx +++ b/source/funkin/util/logging/CrashHandler.hx @@ -4,7 +4,7 @@ import openfl.Lib; import openfl.events.UncaughtErrorEvent; import flixel.util.FlxSignal.FlxTypedSignal; import flixel.FlxG.FlxRenderMethod; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; /** * A custom crash handler that writes to a log file and displays a message box. @@ -107,7 +107,7 @@ class CrashHandler static function logErrorMessage(message:String, critical:Bool = false):Void { - FileUtilBase.createDirIfNotExists(LOG_FOLDER); + FileUtil.createDirIfNotExists(LOG_FOLDER); sys.io.File.saveContent('$LOG_FOLDER/crash${critical ? '-critical' : ''}-${DateUtil.generateTimestamp()}.log', buildCrashReport(message)); } diff --git a/source/funkin/util/plugins/ScreenshotPlugin.hx b/source/funkin/util/plugins/ScreenshotPlugin.hx index cc9b9a12a..7088a581a 100644 --- a/source/funkin/util/plugins/ScreenshotPlugin.hx +++ b/source/funkin/util/plugins/ScreenshotPlugin.hx @@ -14,7 +14,7 @@ import flixel.util.FlxTimer; import funkin.graphics.FunkinSprite; import funkin.input.Cursor; import funkin.audio.FunkinSound; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; import openfl.display.Bitmap; import openfl.display.Sprite; import openfl.display.BitmapData; @@ -268,7 +268,7 @@ class ScreenshotPlugin extends FlxBasic function openScreenshotsFolder(e:MouseEvent):Void { - FileUtilBase.openFolder(SCREENSHOT_FOLDER); + FileUtil.openFolder(SCREENSHOT_FOLDER); } static function getCurrentState():FlxState @@ -288,7 +288,7 @@ class ScreenshotPlugin extends FlxBasic static function makeScreenshotPath():Void { - FileUtilBase.createDirIfNotExists(SCREENSHOT_FOLDER); + FileUtil.createDirIfNotExists(SCREENSHOT_FOLDER); } /** @@ -319,7 +319,7 @@ class ScreenshotPlugin extends FlxBasic { trace('Saving screenshot to: ' + targetPath); // TODO: Make this work on browser. - FileUtilBase.writeBytesToPath(targetPath, pngData); + FileUtil.writeBytesToPath(targetPath, pngData); } } } diff --git a/tests/unit/source/TestMain.hx b/tests/unit/source/TestMain.hx index ee52a1649..11eb87b33 100644 --- a/tests/unit/source/TestMain.hx +++ b/tests/unit/source/TestMain.hx @@ -7,7 +7,7 @@ import massive.munit.TestRunner; import massive.munit.client.HTTPClient; import massive.munit.client.SummaryReportClient; import funkin.util.logging.CrashHandler; -import funkin.util.FileUtil.FileUtilBase; +import funkin.util.FileUtil; /** * Auto generated Test Application. @@ -50,7 +50,7 @@ class TestMain // NOTE: You can also create a custom ICoverageTestResultClient implementation // Output coverage in LCOV format. - FileUtilBase.createDirIfNotExists(COVERAGE_FOLDER); + FileUtil.createDirIfNotExists(COVERAGE_FOLDER); mcover.coverage.MCoverage.getLogger().addClient(new mcover.coverage.client.LcovPrintClient("Funkin' Coverage Report", '${COVERAGE_FOLDER}/lcov.info')); #else // Print individual test results. From 31e9140d67b6596d7216ce9b6205d2c6776450c1 Mon Sep 17 00:00:00 2001 From: cyn Date: Fri, 26 Jul 2024 18:11:49 -0700 Subject: [PATCH 6/9] vscode is dumb --- source/funkin/play/song/SongSerializer.hx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/funkin/play/song/SongSerializer.hx b/source/funkin/play/song/SongSerializer.hx index 74fcc253a..10296e5b4 100644 --- a/source/funkin/play/song/SongSerializer.hx +++ b/source/funkin/play/song/SongSerializer.hx @@ -20,7 +20,7 @@ class SongSerializer */ public static function importSongChartDataSync(path:String):SongChartData { - var fileData = FileUtilBase.readStringFromPath(path); + var fileData = FileUtil.readStringFromPath(path); if (fileData == null) return null; @@ -35,7 +35,7 @@ class SongSerializer */ public static function importSongMetadataSync(path:String):SongMetadata { - var fileData = FileUtilBase.readStringFromPath(path); + var fileData = FileUtil.readStringFromPath(path); if (fileData == null) return null; @@ -50,7 +50,7 @@ class SongSerializer */ public static function importSongChartDataAsync(callback:SongChartData->Void):Void { - FileUtilBase.browseFileReference(function(fileReference:FileReference) { + FileUtil.browseFileReference(function(fileReference:FileReference) { var data = fileReference.data.toString(); if (data == null) return; @@ -67,7 +67,7 @@ class SongSerializer */ public static function importSongMetadataAsync(callback:SongMetadata->Void):Void { - FileUtilBase.browseFileReference(function(fileReference:FileReference) { + FileUtil.browseFileReference(function(fileReference:FileReference) { var data = fileReference.data.toString(); if (data == null) return; From 596d696e8482449bfb451b79fbd50bf566c54ae1 Mon Sep 17 00:00:00 2001 From: cyn Date: Sat, 27 Jul 2024 20:57:00 +0000 Subject: [PATCH 7/9] remove unnecessary changes --- .../ui/debug/charting/handlers/ChartEditorDialogHandler.hx | 7 ++----- .../debug/charting/handlers/ChartEditorToolboxHandler.hx | 1 + source/funkin/util/logging/CrashHandler.hx | 1 - source/funkin/util/plugins/ScreenshotPlugin.hx | 1 - 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx index ab13da1d9..b84c68f8d 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx @@ -808,11 +808,8 @@ class ChartEditorDialogHandler } songVariationMetadataEntry.onClick = onClickMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel); #if FILE_DROP_SUPPORTED - state.addDropHandler( - { - component: songVariationMetadataEntry, - handler: onDropFileMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel) - }); + state.addDropHandler({component: songVariationMetadataEntry, handler: onDropFileMetadataVariation.bind(variation) + .bind(songVariationMetadataEntryLabel)}); #end chartContainerB.addComponent(songVariationMetadataEntry); diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx index 396547ed2..f82bc3c1f 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx @@ -19,6 +19,7 @@ import funkin.data.stage.StageData; import haxe.ui.RuntimeComponentBuilder; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import funkin.ui.haxeui.components.CharacterPlayer; +import funkin.util.FileUtil; import haxe.ui.components.Button; import haxe.ui.data.ArrayDataSource; import haxe.ui.components.CheckBox; diff --git a/source/funkin/util/logging/CrashHandler.hx b/source/funkin/util/logging/CrashHandler.hx index 23c6f57f7..71d1ad394 100644 --- a/source/funkin/util/logging/CrashHandler.hx +++ b/source/funkin/util/logging/CrashHandler.hx @@ -4,7 +4,6 @@ import openfl.Lib; import openfl.events.UncaughtErrorEvent; import flixel.util.FlxSignal.FlxTypedSignal; import flixel.FlxG.FlxRenderMethod; -import funkin.util.FileUtil; /** * A custom crash handler that writes to a log file and displays a message box. diff --git a/source/funkin/util/plugins/ScreenshotPlugin.hx b/source/funkin/util/plugins/ScreenshotPlugin.hx index 7088a581a..c859710de 100644 --- a/source/funkin/util/plugins/ScreenshotPlugin.hx +++ b/source/funkin/util/plugins/ScreenshotPlugin.hx @@ -14,7 +14,6 @@ import flixel.util.FlxTimer; import funkin.graphics.FunkinSprite; import funkin.input.Cursor; import funkin.audio.FunkinSound; -import funkin.util.FileUtil; import openfl.display.Bitmap; import openfl.display.Sprite; import openfl.display.BitmapData; From c28ce60738c89f03c3b8e8f705a467477b846505 Mon Sep 17 00:00:00 2001 From: cyn Date: Fri, 11 Oct 2024 11:23:19 -0700 Subject: [PATCH 8/9] prevent DCE --- source/funkin/util/FileUtil.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/util/FileUtil.hx b/source/funkin/util/FileUtil.hx index ce6f1339e..c5c65609c 100644 --- a/source/funkin/util/FileUtil.hx +++ b/source/funkin/util/FileUtil.hx @@ -18,7 +18,7 @@ using StringTools; /** * Utilities for reading and writing files on various platforms. */ -class FileUtil +@:keep class FileUtil { public static final FILE_FILTER_FNFC:FileFilter = new FileFilter("Friday Night Funkin' Chart (.fnfc)", "*.fnfc"); public static final FILE_FILTER_JSON:FileFilter = new FileFilter("JSON Data File (.json)", "*.json"); @@ -962,7 +962,7 @@ class FileUtil * Utilities for reading and writing files on various platforms. * Wrapper for `FileUtil` that sanitizes paths for script safety. */ -class FileUtilSandboxed +@:keep class FileUtilSandboxed { /** * Prevent paths from exiting the root. From 8d64fe0340808294b03ca60e940dbd23858b2f41 Mon Sep 17 00:00:00 2001 From: cyn Date: Wed, 6 Nov 2024 23:42:15 -0800 Subject: [PATCH 9/9] revert "prevent DCE" --- source/funkin/util/FileUtil.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/util/FileUtil.hx b/source/funkin/util/FileUtil.hx index c5c65609c..ce6f1339e 100644 --- a/source/funkin/util/FileUtil.hx +++ b/source/funkin/util/FileUtil.hx @@ -18,7 +18,7 @@ using StringTools; /** * Utilities for reading and writing files on various platforms. */ -@:keep class FileUtil +class FileUtil { public static final FILE_FILTER_FNFC:FileFilter = new FileFilter("Friday Night Funkin' Chart (.fnfc)", "*.fnfc"); public static final FILE_FILTER_JSON:FileFilter = new FileFilter("JSON Data File (.json)", "*.json"); @@ -962,7 +962,7 @@ using StringTools; * Utilities for reading and writing files on various platforms. * Wrapper for `FileUtil` that sanitizes paths for script safety. */ -@:keep class FileUtilSandboxed +class FileUtilSandboxed { /** * Prevent paths from exiting the root.