mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-07-22 12:21:07 +00:00
1468 lines
40 KiB
Haxe
1468 lines
40 KiB
Haxe
package funkin.util;
|
|
|
|
import haxe.zip.Entry;
|
|
import lime.utils.Bytes;
|
|
import lime.ui.FileDialog;
|
|
import openfl.net.FileFilter;
|
|
import haxe.io.Path;
|
|
import openfl.net.FileReference;
|
|
import openfl.events.Event;
|
|
import openfl.events.IOErrorEvent;
|
|
import haxe.ui.containers.dialogs.Dialog.DialogButton;
|
|
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.
|
|
*/
|
|
@:nullSafety
|
|
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");
|
|
public static final FILE_FILTER_ZIP:FileFilter = new FileFilter("ZIP Archive (.zip)", "*.zip");
|
|
public static final FILE_FILTER_PNG:FileFilter = new FileFilter("PNG Image (.png)", "*.png");
|
|
public static final FILE_FILTER_FNFS:FileFilter = new FileFilter("Friday Night Funkin' Stage (.fnfs)", "*.fnfs");
|
|
|
|
public static final FILE_EXTENSION_INFO_FNFC:FileDialogExtensionInfo =
|
|
{
|
|
extension: 'fnfc',
|
|
label: 'Friday Night Funkin\' Chart',
|
|
};
|
|
public static final FILE_EXTENSION_INFO_ZIP:FileDialogExtensionInfo =
|
|
{
|
|
extension: 'zip',
|
|
label: 'ZIP Archive',
|
|
};
|
|
public static final FILE_EXTENSION_INFO_PNG:FileDialogExtensionInfo =
|
|
{
|
|
extension: 'png',
|
|
label: 'PNG Image',
|
|
};
|
|
|
|
public static final FILE_EXTENSION_INFO_FNFS:FileDialogExtensionInfo =
|
|
{
|
|
extension: 'fnfs',
|
|
label: 'Friday Night Funkin\' Stage',
|
|
};
|
|
|
|
/**
|
|
* Paths which should not be deleted or modified by scripts.
|
|
*/
|
|
private static final PROTECTED_PATHS:Array<String> = [
|
|
'',
|
|
'assets',
|
|
'manifest',
|
|
'manifest/*',
|
|
'plugins',
|
|
'plugins/*',
|
|
'Funkin.exe',
|
|
'Funkin',
|
|
'libvlc.dll',
|
|
'libvlccore.dll',
|
|
'lime.ndll'
|
|
];
|
|
|
|
/**
|
|
* Invalid filesystem characters.
|
|
*/
|
|
private static final INVALID_CHARS:Array<String> = [':', '*', '?', '"', '<', '>', '|'];
|
|
|
|
#if sys
|
|
private static var _gameDirectory:Null<String> = null;
|
|
public static var gameDirectory(get, never):String;
|
|
|
|
public static function get_gameDirectory():String
|
|
{
|
|
if (_gameDirectory != null)
|
|
{
|
|
return _gameDirectory;
|
|
}
|
|
|
|
return _gameDirectory = Path.directory(Path.normalize(sys.FileSystem.fullPath(Sys.programPath())));
|
|
}
|
|
#end
|
|
|
|
/**
|
|
* Browses for a single file, then calls `onSelect(fileInfo)` when a file is selected.
|
|
* Powered by HaxeUI, so it works on all platforms.
|
|
* File contents will be binary, not String.
|
|
*
|
|
* @param typeFilter
|
|
* @param onSelect A callback that provides a `SelectedFileInfo` object when a file is selected.
|
|
* @param onCancel A callback that is called when the user closes the dialog without selecting a file.
|
|
*/
|
|
public static function browseForBinaryFile(dialogTitle:String, ?typeFilter:Array<FileDialogExtensionInfo>, onSelect:(SelectedFileInfo) -> Void,
|
|
?onCancel:() -> Void)
|
|
{
|
|
var onComplete = function(button, selectedFiles) {
|
|
if (button == DialogButton.OK && selectedFiles.length > 0)
|
|
{
|
|
onSelect(selectedFiles[0]);
|
|
}
|
|
else if (onCancel != null)
|
|
{
|
|
onCancel();
|
|
}
|
|
};
|
|
|
|
Dialogs.openFile(onComplete,
|
|
{
|
|
readContents: true,
|
|
readAsBinary: true, // Binary
|
|
multiple: false,
|
|
extensions: typeFilter ?? new Array<FileDialogExtensionInfo>(),
|
|
title: dialogTitle,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Browses for a single file, then calls `onSelect(fileInfo)` when a file is selected.
|
|
* Powered by HaxeUI, so it works on all platforms.
|
|
* File contents will be a String, not binary.
|
|
*
|
|
* @param typeFilter
|
|
* @param onSelect A callback that provides a `SelectedFileInfo` object when a file is selected.
|
|
* @param onCancel A callback that is called when the user closes the dialog without selecting a file.
|
|
*/
|
|
public static function browseForTextFile(dialogTitle:String, ?typeFilter:Array<FileDialogExtensionInfo>, onSelect:(SelectedFileInfo) -> Void,
|
|
?onCancel:() -> Void):Void
|
|
{
|
|
var onComplete = function(button, selectedFiles) {
|
|
if (button == DialogButton.OK && selectedFiles.length > 0)
|
|
{
|
|
onSelect(selectedFiles[0]);
|
|
}
|
|
else if (onCancel != null)
|
|
{
|
|
onCancel();
|
|
}
|
|
};
|
|
|
|
Dialogs.openFile(onComplete,
|
|
{
|
|
readContents: true,
|
|
readAsBinary: false, // Text
|
|
multiple: false,
|
|
extensions: typeFilter ?? new Array<FileDialogExtensionInfo>(),
|
|
title: dialogTitle,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Browses for a directory, then calls `onSelect(path)` when a path chosen.
|
|
* Note that on HTML5 this will immediately fail.
|
|
*
|
|
* @param typeFilter TODO What does this do?
|
|
* @return Whether the file dialog was opened successfully.
|
|
*/
|
|
public static function browseForDirectory(?typeFilter:Array<FileFilter>, onSelect:(String) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
?dialogTitle:String):Bool
|
|
{
|
|
#if desktop
|
|
var filter:Null<String> = convertTypeFilter(typeFilter);
|
|
var fileDialog:FileDialog = new FileDialog();
|
|
fileDialog.onSelect.add(onSelect);
|
|
if (onCancel != null)
|
|
{
|
|
fileDialog.onCancel.add(onCancel);
|
|
}
|
|
|
|
fileDialog.browse(OPEN_DIRECTORY, filter, defaultPath, dialogTitle);
|
|
|
|
return true;
|
|
#else
|
|
trace('WARNING: browseForDirectory not implemented for this platform');
|
|
|
|
if (onCancel != null)
|
|
{
|
|
onCancel();
|
|
}
|
|
|
|
return false;
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Browses for multiple file, then calls `onSelect(paths)` when a path chosen.
|
|
* Note that on HTML5 this will immediately fail.
|
|
*
|
|
* @return Whether the file dialog was opened successfully.
|
|
*/
|
|
public static function browseForMultipleFiles(?typeFilter:Array<FileFilter>, onSelect:(Array<String>) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
?dialogTitle:String):Bool
|
|
{
|
|
#if desktop
|
|
var filter:Null<String> = convertTypeFilter(typeFilter);
|
|
var fileDialog:FileDialog = new FileDialog();
|
|
fileDialog.onSelectMultiple.add(onSelect);
|
|
if (onCancel != null)
|
|
{
|
|
fileDialog.onCancel.add(onCancel);
|
|
}
|
|
|
|
fileDialog.browse(OPEN_MULTIPLE, filter, defaultPath, dialogTitle);
|
|
|
|
return true;
|
|
#else
|
|
trace('WARNING: browseForMultipleFiles not implemented for this platform');
|
|
|
|
if (onCancel != null)
|
|
{
|
|
onCancel();
|
|
}
|
|
|
|
return false;
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Browses for a file location to save to, then calls `onSave(path)` when a path chosen.
|
|
* Note that on HTML5 you can't do much with this, you should call `saveFile(resource:haxe.io.Bytes)` instead.
|
|
*
|
|
* @param typeFilter TODO What does this do?
|
|
* @return Whether the file dialog was opened successfully.
|
|
*/
|
|
public static function browseForSaveFile(?typeFilter:Array<FileFilter>, onSelect:(String) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
?dialogTitle:String):Bool
|
|
{
|
|
#if desktop
|
|
var filter:Null<String> = convertTypeFilter(typeFilter);
|
|
var fileDialog:FileDialog = new FileDialog();
|
|
fileDialog.onSelect.add(onSelect);
|
|
if (onCancel != null)
|
|
{
|
|
fileDialog.onCancel.add(onCancel);
|
|
}
|
|
|
|
fileDialog.browse(SAVE, filter, defaultPath, dialogTitle);
|
|
|
|
return true;
|
|
#else
|
|
trace('WARNING: browseForSaveFile not implemented for this platform');
|
|
|
|
if (onCancel != null)
|
|
{
|
|
onCancel();
|
|
}
|
|
|
|
return false;
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Browses for a single file location, then writes the provided `haxe.io.Bytes` data and calls `onSave(path)` when done.
|
|
* Works great on desktop and HTML5.
|
|
*
|
|
* @return Whether the file dialog was opened successfully.
|
|
*/
|
|
public static function saveFile(data:Bytes, ?typeFilter:Array<FileFilter>, ?onSave:(String) -> Void, ?onCancel:() -> Void, ?defaultFileName:String,
|
|
?dialogTitle:String):Bool
|
|
{
|
|
#if desktop
|
|
var filter:Null<String> = convertTypeFilter(typeFilter);
|
|
var fileDialog:FileDialog = new FileDialog();
|
|
if (onSave != null)
|
|
{
|
|
fileDialog.onSave.add(onSave);
|
|
}
|
|
|
|
if (onCancel != null)
|
|
{
|
|
fileDialog.onCancel.add(onCancel);
|
|
}
|
|
|
|
fileDialog.save(data, filter, defaultFileName, dialogTitle);
|
|
|
|
return true;
|
|
#elseif html5
|
|
var filter:Null<String> = defaultFileName != null ? Path.extension(defaultFileName) : null;
|
|
var fileDialog:FileDialog = new FileDialog();
|
|
if (onSave != null)
|
|
{
|
|
fileDialog.onSave.add(onSave);
|
|
}
|
|
|
|
if (onCancel != null)
|
|
{
|
|
fileDialog.onCancel.add(onCancel);
|
|
}
|
|
|
|
fileDialog.save(data, filter, defaultFileName, dialogTitle);
|
|
|
|
return true;
|
|
#else
|
|
trace('WARNING: saveFile not implemented for this platform');
|
|
|
|
if (onCancel != null)
|
|
{
|
|
onCancel();
|
|
}
|
|
|
|
return false;
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Prompts the user to save multiple files.
|
|
* On desktop, this will prompt the user for a directory, then write all of the files to there.
|
|
* On HTML5, this will zip the files up and prompt the user to save that.
|
|
*
|
|
* @param typeFilter TODO What does this do?
|
|
* @return Whether the file dialog was opened successfully.
|
|
*/
|
|
public static function saveMultipleFiles(resources:Array<Entry>, ?onSaveAll:(Array<String>) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
force:Bool = false):Bool
|
|
{
|
|
#if desktop
|
|
// Prompt the user for a directory, then write all of the files to there.
|
|
var onSelectDir:(String) -> Void = function(targetPath:String):Void {
|
|
var paths:Array<String> = new Array<String>();
|
|
for (resource in resources)
|
|
{
|
|
/*
|
|
var filePath:String = Path.join([targetPath, resource.fileName]);
|
|
try
|
|
{
|
|
if (resource.data == null)
|
|
{
|
|
trace('WARNING: File $filePath has no data or content. Skipping.');
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
writeBytesToPath(filePath, resource.data, force ? Force : Skip);
|
|
}
|
|
}
|
|
catch (e:Dynamic)
|
|
{
|
|
throw 'Failed to write file (probably already exists): $filePath';
|
|
}
|
|
*/
|
|
if (resource.data == null)
|
|
{
|
|
trace('WARNING: File ${resource.fileName} has no data or content. Skipping.');
|
|
continue;
|
|
}
|
|
|
|
var filePath:String = Path.join([targetPath, resource.fileName]);
|
|
|
|
paths.push(filePath);
|
|
}
|
|
|
|
if (onSaveAll != null)
|
|
{
|
|
onSaveAll(paths);
|
|
}
|
|
}
|
|
|
|
trace('Browsing for directory to save individual files to...');
|
|
|
|
#if mac
|
|
defaultPath = null;
|
|
#end
|
|
|
|
browseForDirectory(null, onSelectDir, onCancel, defaultPath, 'Choose directory to save all files to...');
|
|
|
|
return true;
|
|
#elseif html5
|
|
saveFilesAsZIP(resources, onSaveAll, onCancel, defaultPath, force);
|
|
|
|
return true;
|
|
#else
|
|
trace('WARNING: saveMultipleFiles not implemented for this platform');
|
|
|
|
if (onCancel != null)
|
|
{
|
|
onCancel();
|
|
}
|
|
|
|
return false;
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Takes an array of file entries and prompts the user to save them as a ZIP file.
|
|
*/
|
|
public static function saveFilesAsZIP(resources:Array<Entry>, ?onSave:(Array<String>) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
force:Bool = false):Bool
|
|
{
|
|
// Create a ZIP file.
|
|
var zipBytes:Bytes = createZIPFromEntries(resources);
|
|
var onSave:(String) -> Void = function(path:String) {
|
|
trace('Saved ${resources.length} files to ZIP at "$path"');
|
|
|
|
if (onSave != null)
|
|
{
|
|
onSave([path]);
|
|
}
|
|
};
|
|
|
|
// Prompt the user to save the ZIP file.
|
|
saveFile(zipBytes, [FILE_FILTER_ZIP], onSave, onCancel, defaultPath, 'Save files as ZIP...');
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Takes an array of file entries and prompts the user to save them as a FNFC file.
|
|
*/
|
|
public static function saveChartAsFNFC(resources:Array<Entry>, ?onSave:(Array<String>) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
force:Bool = false):Bool
|
|
{
|
|
// Create a ZIP file.
|
|
var zipBytes:Bytes = createZIPFromEntries(resources);
|
|
var onSave:(String) -> Void = function(path:String) {
|
|
trace('Saved FNFC file to "$path"');
|
|
|
|
if (onSave != null)
|
|
{
|
|
onSave([path]);
|
|
}
|
|
};
|
|
// Prompt the user to save the ZIP file.
|
|
saveFile(zipBytes, [FILE_FILTER_FNFC], onSave, onCancel, defaultPath, 'Save chart as FNFC...');
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Takes an array of file entries and forcibly writes a ZIP to the given path.
|
|
* Only works on desktop, because HTML5 doesn't allow you to write files to arbitrary paths.
|
|
* Use `saveFilesAsZIP` instead.
|
|
* @param force Whether to force overwrite an existing file.
|
|
*/
|
|
public static function saveFilesAsZIPToPath(resources:Array<Entry>, path:String, mode:FileWriteMode = Skip):Bool
|
|
{
|
|
#if desktop
|
|
// Create a ZIP file.
|
|
var zipBytes:Bytes = createZIPFromEntries(resources);
|
|
// Write the ZIP.
|
|
writeBytesToPath(path, zipBytes, mode);
|
|
return true;
|
|
#else
|
|
return false;
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Read string file contents directly from a given path.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the file.
|
|
* @return The file contents.
|
|
*/
|
|
public static function readStringFromPath(path:String):String
|
|
{
|
|
#if sys
|
|
return sys.io.File.getContent(path);
|
|
#else
|
|
throw 'Direct file reading by path is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Read bytes file contents directly from a given path.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the file.
|
|
* @return The file contents.
|
|
*/
|
|
public static function readBytesFromPath(path:String):Bytes
|
|
{
|
|
#if sys
|
|
return sys.io.File.getBytes(path);
|
|
#else
|
|
throw 'Direct file reading by path is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Browse for a file to read and execute a callback once we have a file reference.
|
|
* Works great on HTML5 or desktop.
|
|
*
|
|
* @param callback The function to call when the file is loaded.
|
|
*/
|
|
public static function browseFileReference(callback:(FileReference) -> Void):Void
|
|
{
|
|
var file = new FileReference();
|
|
file.addEventListener(Event.SELECT, function(e) {
|
|
var selectedFileRef:FileReference = e.target;
|
|
trace('Selected file: ' + selectedFileRef.name);
|
|
|
|
selectedFileRef.addEventListener(Event.COMPLETE, function(e) {
|
|
var loadedFileRef:FileReference = e.target;
|
|
trace('Loaded file: ' + loadedFileRef.name);
|
|
|
|
callback(loadedFileRef);
|
|
});
|
|
|
|
selectedFileRef.load();
|
|
});
|
|
|
|
file.browse();
|
|
}
|
|
|
|
/**
|
|
* Prompts the user to save a file to their computer.
|
|
*/
|
|
public static function writeFileReference(path:String, data:String):Void
|
|
{
|
|
var file = new FileReference();
|
|
|
|
file.addEventListener(Event.COMPLETE, function(e:Event) {
|
|
trace('Successfully wrote file: "$path"');
|
|
});
|
|
|
|
file.addEventListener(Event.CANCEL, function(e:Event) {
|
|
trace('Cancelled writing file: "$path"');
|
|
});
|
|
|
|
file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent) {
|
|
trace('IO error writing file: "$path"');
|
|
});
|
|
|
|
file.save(data, path);
|
|
}
|
|
|
|
/**
|
|
* Read JSON file contents directly from a given path.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the file.
|
|
* @return The JSON data.
|
|
*/
|
|
public static function readJSONFromPath(path:String):Dynamic
|
|
{
|
|
#if sys
|
|
return SerializerUtil.fromJSON(sys.io.File.getContent(path));
|
|
#else
|
|
throw 'Direct file reading by path is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Write string file contents directly to a given path.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the file.
|
|
* @param data The string to write.
|
|
* @param mode Whether to Force, Skip, or Ask to overwrite an existing file.
|
|
*/
|
|
public static function writeStringToPath(path:String, data:String, mode:FileWriteMode = Skip):Void
|
|
{
|
|
#if sys
|
|
if (directoryExists(path))
|
|
{
|
|
throw 'Target path is a directory, not a file: "$path"';
|
|
}
|
|
|
|
createDirIfNotExists(Path.directory(path));
|
|
|
|
switch (mode)
|
|
{
|
|
case Force:
|
|
sys.io.File.saveContent(path, data);
|
|
case Skip:
|
|
if (!pathExists(path))
|
|
{
|
|
sys.io.File.saveContent(path, data);
|
|
}
|
|
case Ask:
|
|
if (pathExists(path))
|
|
{
|
|
// TODO: We don't have the technology to use native popups yet.
|
|
throw 'Entry at path already exists: $path';
|
|
}
|
|
else
|
|
{
|
|
sys.io.File.saveContent(path, data);
|
|
}
|
|
}
|
|
#else
|
|
throw 'Direct file writing by path is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Write byte file contents directly to a given path.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the file.
|
|
* @param data The bytes to write.
|
|
* @param mode Whether to Force, Skip, or Ask to overwrite an existing file.
|
|
*/
|
|
public static function writeBytesToPath(path:String, data:Bytes, mode:FileWriteMode = Skip):Void
|
|
{
|
|
#if sys
|
|
if (directoryExists(path))
|
|
{
|
|
throw 'Target path is a directory, not a file: "$path"';
|
|
}
|
|
|
|
createDirIfNotExists(Path.directory(path));
|
|
|
|
var shouldWrite:Bool = true;
|
|
switch (mode)
|
|
{
|
|
case Force:
|
|
shouldWrite = true;
|
|
case Skip:
|
|
if (!pathExists(path))
|
|
{
|
|
shouldWrite = true;
|
|
}
|
|
case Ask:
|
|
if (pathExists(path))
|
|
{
|
|
// TODO: We don't have the technology to use native popups yet.
|
|
throw 'Entry at path already exists: "$path"';
|
|
}
|
|
else
|
|
{
|
|
shouldWrite = true;
|
|
}
|
|
}
|
|
|
|
if (shouldWrite)
|
|
{
|
|
createDirIfNotExists(Path.directory(path));
|
|
sys.io.File.saveBytes(path, data);
|
|
}
|
|
#else
|
|
throw 'Direct file writing by path is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Write string file contents directly to the end of a file at the given path.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the file.
|
|
* @param data The string to append.
|
|
*/
|
|
public static function appendStringToPath(path:String, data:String):Void
|
|
{
|
|
#if sys
|
|
if (!pathExists(path))
|
|
{
|
|
writeStringToPath(path, data, Force);
|
|
return;
|
|
}
|
|
else if (directoryExists(path))
|
|
{
|
|
throw 'Target path is a directory, not a file: "$path"';
|
|
}
|
|
|
|
var output:Null<sys.io.FileOutput> = null;
|
|
try
|
|
{
|
|
output = sys.io.File.append(path, false);
|
|
output.writeString(data);
|
|
output.close();
|
|
}
|
|
catch (e:Dynamic)
|
|
{
|
|
if (output != null)
|
|
{
|
|
output.close();
|
|
}
|
|
|
|
throw 'Failed to append to file: "$path"';
|
|
}
|
|
#else
|
|
throw 'Direct file writing by path is not supported on this platform.';
|
|
#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 (Path.extension(destination) != '')
|
|
{
|
|
destination = Path.directory(destination);
|
|
}
|
|
|
|
sys.FileSystem.rename(path, Path.join([destination, Path.withoutDirectory(path)]));
|
|
#else
|
|
throw 'File moving is 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 'File deletion is 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
|
|
throw 'File size calculation is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Check if a path exists on the filesystem.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the potential file or directory.
|
|
* @return Whether the path exists.
|
|
*/
|
|
public static function pathExists(path:String):Bool
|
|
{
|
|
#if sys
|
|
return sys.FileSystem.exists(path);
|
|
#else
|
|
return false;
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Check if a path is a file on the filesystem.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the potential file.
|
|
* @return Whether the path exists and is a file.
|
|
*/
|
|
public static function fileExists(path:String):Bool
|
|
{
|
|
#if sys
|
|
return pathExists(path) && !directoryExists(path);
|
|
#else
|
|
throw 'Filesystem check is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Check if a path is a directory on the filesystem.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the potential directory.
|
|
* @return Whether the path exists and is a directory.
|
|
*/
|
|
public static function directoryExists(path:String):Bool
|
|
{
|
|
#if sys
|
|
try
|
|
{
|
|
return sys.FileSystem.isDirectory(path);
|
|
}
|
|
catch (e:Dynamic)
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
throw 'Filesystem check is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Create a directory if it doesn't already exist.
|
|
* Only works on desktop.
|
|
*
|
|
* @param dir The path to the directory.
|
|
*/
|
|
public static function createDirIfNotExists(dir:String):Void
|
|
{
|
|
#if sys
|
|
if (!directoryExists(dir))
|
|
{
|
|
sys.FileSystem.createDirectory(dir);
|
|
}
|
|
#else
|
|
throw 'Directory creation is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* List all entries in a directory.
|
|
* Only works on desktop.
|
|
*
|
|
* @param path The path to the directory.
|
|
* @return An array of entries in the directory.
|
|
*/
|
|
public static function readDir(path:String):Array<String>
|
|
{
|
|
#if sys
|
|
return sys.FileSystem.readDirectory(path);
|
|
#else
|
|
throw 'Directory reading is not supported on this platform.';
|
|
#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 paths to ignore.
|
|
* @param strict Fails if the destination directory is not empty.
|
|
*/
|
|
public static function moveDir(path:String, destination:String, ?ignore:Array<String>, strict:Bool = true):Void
|
|
{
|
|
#if sys
|
|
if (!directoryExists(path))
|
|
{
|
|
throw 'Path is not a directory: "$path"';
|
|
}
|
|
|
|
createDirIfNotExists(destination);
|
|
if (strict)
|
|
{
|
|
// Ensure the destination is empty if strict mode is enabled.
|
|
var entries:Array<String> = readDir(destination);
|
|
if (entries.length > 0)
|
|
{
|
|
throw 'Destination directory "$destination" is not empty.';
|
|
}
|
|
}
|
|
|
|
var stack:Array<String> = [path];
|
|
while (stack.length > 0)
|
|
{
|
|
var currentPath:Null<String> = stack.pop();
|
|
if (currentPath == null) continue;
|
|
|
|
var entries:Array<String> = readDir(currentPath);
|
|
for (entry in entries)
|
|
{
|
|
var entryPath:String = Path.join([currentPath, entry]);
|
|
if (ignore != null && ignore.contains(entryPath)) continue;
|
|
if (directoryExists(entryPath))
|
|
{
|
|
stack.push(entryPath);
|
|
}
|
|
else
|
|
{
|
|
moveFile(entryPath, Path.join([destination, entry]));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (readDir(path)?.length == 0)
|
|
{
|
|
deleteDir(path);
|
|
}
|
|
#else
|
|
throw 'Directory moving is 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 paths to ignore.
|
|
*/
|
|
public static function deleteDir(path:String, recursive:Bool = false, ?ignore:Array<String>):Void
|
|
{
|
|
#if sys
|
|
if (!directoryExists(path))
|
|
{
|
|
throw 'Path is not a valid directory: "$path"';
|
|
}
|
|
|
|
if (recursive)
|
|
{
|
|
var stack:Array<String> = [path];
|
|
while (stack.length > 0)
|
|
{
|
|
var currentPath:Null<String> = stack.pop();
|
|
if (currentPath == null) continue;
|
|
|
|
var entries:Array<String> = readDir(currentPath);
|
|
for (entry in entries)
|
|
{
|
|
var entryPath:String = Path.join([currentPath, entry]);
|
|
if (ignore != null && ignore.contains(entryPath)) continue;
|
|
if (directoryExists(entryPath))
|
|
{
|
|
stack.push(entryPath);
|
|
}
|
|
else
|
|
{
|
|
deleteFile(entryPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sys.FileSystem.deleteDirectory(path);
|
|
}
|
|
#else
|
|
throw 'Directory deletion is 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 (!directoryExists(path))
|
|
{
|
|
throw 'Path is not a valid directory path: $path';
|
|
}
|
|
|
|
var stack:Array<String> = [path];
|
|
var total:Int = 0;
|
|
while (stack.length > 0)
|
|
{
|
|
var currentPath:Null<String> = stack.pop();
|
|
if (currentPath == null) continue;
|
|
|
|
for (entry in readDir(currentPath))
|
|
{
|
|
var entryPath:String = Path.join([currentPath, entry]);
|
|
if (directoryExists(entryPath))
|
|
{
|
|
stack.push(entryPath);
|
|
}
|
|
else
|
|
{
|
|
total += getFileSize(entryPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
return total;
|
|
#else
|
|
throw 'Directory size calculation not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
static var tempDir:Null<String> = null;
|
|
static final TEMP_ENV_VARS:Array<String> = ['TEMP', 'TMPDIR', 'TEMPDIR', 'TMP'];
|
|
|
|
/**
|
|
* Get the path to a temporary directory we can use for writing files.
|
|
* Only works on desktop.
|
|
*
|
|
* @return The path to the temporary directory.
|
|
*/
|
|
public static function getTempDir():Null<String>
|
|
{
|
|
if (tempDir != null) return tempDir;
|
|
#if sys
|
|
#if windows
|
|
var path:Null<String> = null;
|
|
for (envName in TEMP_ENV_VARS)
|
|
{
|
|
path = Sys.getEnv(envName);
|
|
if (path == '') path = null;
|
|
if (path != null) break;
|
|
}
|
|
tempDir = Path.join([path ?? '', 'funkin/']);
|
|
return tempDir;
|
|
#else
|
|
tempDir = '/tmp/funkin/';
|
|
return tempDir;
|
|
#end
|
|
#else
|
|
return null;
|
|
#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 (!pathExists(path))
|
|
{
|
|
throw 'Path does not exist: "$path"';
|
|
}
|
|
|
|
final isDirectory:Bool = directoryExists(path);
|
|
newName = Path.withoutDirectory(newName);
|
|
if (isDirectory)
|
|
{
|
|
newName = Path.withoutExtension(newName);
|
|
}
|
|
else if (keepExtension)
|
|
{
|
|
newName = Path.withExtension(newName, Path.extension(path));
|
|
}
|
|
|
|
newName = Path.join([Path.directory(path), newName]);
|
|
if (newName == path)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pathExists(newName))
|
|
{
|
|
// Prevent overwriting something.
|
|
throw 'Destination path already exists: "$newName"';
|
|
}
|
|
|
|
sys.FileSystem.rename(path, newName);
|
|
#else
|
|
throw 'File renaming by path is not supported on this platform.';
|
|
#end
|
|
}
|
|
|
|
/**
|
|
* Create a Bytes object containing a ZIP file, containing the provided entries.
|
|
*
|
|
* @param entries The entries to add to the ZIP file.
|
|
* @return The ZIP file as a Bytes object.
|
|
*/
|
|
public static function createZIPFromEntries(entries:Array<Entry>):Bytes
|
|
{
|
|
var o:haxe.io.BytesOutput = new haxe.io.BytesOutput();
|
|
var zipWriter:haxe.zip.Writer = new haxe.zip.Writer(o);
|
|
zipWriter.write(entries.list());
|
|
return o.getBytes();
|
|
}
|
|
|
|
public static function readZIPFromBytes(input:Bytes):Array<Entry>
|
|
{
|
|
var bytesInput = new haxe.io.BytesInput(input);
|
|
var zippedEntries = haxe.zip.Reader.readZip(bytesInput);
|
|
var results:Array<Entry> = new Array<Entry>();
|
|
for (entry in zippedEntries)
|
|
{
|
|
if (entry.compressed)
|
|
{
|
|
entry.data = haxe.zip.Reader.unzip(entry);
|
|
}
|
|
|
|
results.push(entry);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
public static function mapZIPEntriesByName(input:Array<Entry>):Map<String, Entry>
|
|
{
|
|
var results:Map<String, Entry> = new Map<String, Entry>();
|
|
for (entry in input)
|
|
{
|
|
results.set(entry.fileName, entry);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Create a ZIP file entry from a file name and its string contents.
|
|
*
|
|
* @param name The name of the file. You can use slashes to create subdirectories.
|
|
* @param content The string contents of the file.
|
|
* @return The resulting entry.
|
|
*/
|
|
public static function makeZIPEntry(name:String, content:String):Entry
|
|
{
|
|
var data:Bytes = haxe.io.Bytes.ofString(content, UTF8);
|
|
return makeZIPEntryFromBytes(name, data);
|
|
}
|
|
|
|
/**
|
|
* Create a ZIP file entry from a file name and its string contents.
|
|
*
|
|
* @param name The name of the file. You can use slashes to create subdirectories.
|
|
* @param data The byte data of the file.
|
|
* @return The resulting entry.
|
|
*/
|
|
public static function makeZIPEntryFromBytes(name:String, data:haxe.io.Bytes):Entry
|
|
{
|
|
return {
|
|
fileName: name,
|
|
fileSize: data.length,
|
|
data: data,
|
|
dataSize: data.length,
|
|
compressed: false,
|
|
fileTime: Date.now(),
|
|
crc32: null,
|
|
extraFields: null,
|
|
};
|
|
}
|
|
|
|
public static function openFolder(pathFolder:String) // what is this for??
|
|
WindowUtil.openFolder(pathFolder);
|
|
|
|
private static function convertTypeFilter(?typeFilter:Array<FileFilter>):Null<String>
|
|
{
|
|
var filter:Null<String> = null;
|
|
if (typeFilter != null)
|
|
{
|
|
var filters:Array<String> = new Array<String>();
|
|
for (type in typeFilter)
|
|
{
|
|
filters.push(type.extension.replace('*.', '').replace(';', ','));
|
|
}
|
|
|
|
filter = filters.join(';');
|
|
}
|
|
|
|
return filter;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utilities for reading and writing files on various platforms.
|
|
* Wrapper for `FileUtil` that sanitizes paths for script safety.
|
|
*/
|
|
@:nullSafety
|
|
class FileUtilSandboxed
|
|
{
|
|
/**
|
|
* Prevent paths from exiting the root.
|
|
*
|
|
* @param path The path to sanitize.
|
|
* @return The sanitized path.
|
|
*/
|
|
public static function sanitizePath(path:String):String
|
|
{
|
|
if (path == null)
|
|
{
|
|
#if sys
|
|
return FileUtil.gameDirectory;
|
|
#else
|
|
return '';
|
|
#end
|
|
}
|
|
|
|
if (path.contains(':'))
|
|
{
|
|
path = path.substring(path.lastIndexOf(':') + 1);
|
|
}
|
|
|
|
path = path.trim().replace('\\', '/');
|
|
while (path.contains('//'))
|
|
{
|
|
path = path.replace('//', '/');
|
|
}
|
|
|
|
@:privateAccess for (char in FileUtil.INVALID_CHARS)
|
|
{
|
|
path = path.replace(char, '');
|
|
}
|
|
|
|
var parts:Array<String> = path.split('/');
|
|
var sanitized:Array<String> = new Array<String>();
|
|
for (part in parts)
|
|
{
|
|
switch (part)
|
|
{
|
|
case '.' | '':
|
|
continue;
|
|
case '..':
|
|
sanitized.pop();
|
|
default:
|
|
sanitized.push(part);
|
|
}
|
|
}
|
|
|
|
var ret:String = sanitized.join('/');
|
|
|
|
#if sys
|
|
var realPath:String = Path.normalize(sys.FileSystem.fullPath(Path.join([FileUtil.gameDirectory, ret])));
|
|
// ^ TODO: figure out how to get "real" path of symlinked paths
|
|
|
|
if (!realPath.startsWith(FileUtil.gameDirectory.toString()))
|
|
{
|
|
return FileUtil.gameDirectory;
|
|
}
|
|
|
|
ret = realPath;
|
|
#end
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
@:privateAccess for (protected in FileUtil.PROTECTED_PATHS)
|
|
{
|
|
if (path == protected || (protected.contains('*') && path.startsWith(protected.substring(0, protected.indexOf('*')))))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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 = 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<FileDialogExtensionInfo>, onSelect:(SelectedFileInfo) -> Void,
|
|
?onCancel:() -> Void)
|
|
{
|
|
FileUtil.browseForBinaryFile(dialogTitle, typeFilter, onSelect, onCancel);
|
|
}
|
|
|
|
public static function browseForTextFile(dialogTitle:String, ?typeFilter:Array<FileDialogExtensionInfo>, onSelect:(SelectedFileInfo) -> Void,
|
|
?onCancel:() -> Void):Void
|
|
{
|
|
FileUtil.browseForTextFile(dialogTitle, typeFilter, onSelect, onCancel);
|
|
}
|
|
|
|
public static function browseForDirectory(?typeFilter:Array<FileFilter>, onSelect:(String) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
?dialogTitle:String):Bool
|
|
{
|
|
return FileUtil.browseForDirectory(typeFilter, onSelect, onCancel, defaultPath, dialogTitle);
|
|
}
|
|
|
|
public static function browseForMultipleFiles(?typeFilter:Array<FileFilter>, onSelect:(Array<String>) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
?dialogTitle:String):Bool
|
|
{
|
|
return FileUtil.browseForMultipleFiles(typeFilter, onSelect, onCancel, defaultPath, dialogTitle);
|
|
}
|
|
|
|
public static function browseForSaveFile(?typeFilter:Array<FileFilter>, onSelect:(String) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
?dialogTitle:String):Bool
|
|
{
|
|
return FileUtil.browseForSaveFile(typeFilter, onSelect, onCancel, defaultPath, dialogTitle);
|
|
}
|
|
|
|
public static function saveFile(data:Bytes, ?typeFilter:Array<FileFilter>, ?onSave:(String) -> Void, ?onCancel:() -> Void, ?defaultFileName:String,
|
|
?dialogTitle:String):Bool
|
|
{
|
|
return FileUtil.saveFile(data, typeFilter, onSave, onCancel, defaultFileName, dialogTitle);
|
|
}
|
|
|
|
public static function saveMultipleFiles(resources:Array<Entry>, ?onSaveAll:(Array<String>) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
force:Bool = false):Bool
|
|
{
|
|
return FileUtil.saveMultipleFiles(resources, onSaveAll, onCancel, defaultPath, force);
|
|
}
|
|
|
|
public static function saveFilesAsZIP(resources:Array<Entry>, ?onSave:(Array<String>) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
force:Bool = false):Bool
|
|
{
|
|
return FileUtil.saveFilesAsZIP(resources, onSave, onCancel, defaultPath, force);
|
|
}
|
|
|
|
public static function saveChartAsFNFC(resources:Array<Entry>, ?onSave:(Array<String>) -> Void, ?onCancel:() -> Void, ?defaultPath:String,
|
|
force:Bool = false):Bool
|
|
{
|
|
return FileUtil.saveChartAsFNFC(resources, onSave, onCancel, defaultPath, force);
|
|
}
|
|
|
|
public static function saveFilesAsZIPToPath(resources:Array<Entry>, path:String, mode:FileWriteMode = Skip):Bool
|
|
{
|
|
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
|
|
return FileUtil.saveFilesAsZIPToPath(resources, path, mode);
|
|
}
|
|
|
|
public static function readStringFromPath(path:String):String
|
|
{
|
|
return FileUtil.readStringFromPath(sanitizePath(path));
|
|
}
|
|
|
|
public static function readBytesFromPath(path:String):Bytes
|
|
{
|
|
return FileUtil.readBytesFromPath(sanitizePath(path));
|
|
}
|
|
|
|
public static function browseFileReference(callback:(FileReference) -> Void):Void
|
|
{
|
|
FileUtil.browseFileReference(callback);
|
|
}
|
|
|
|
public static function writeFileReference(path:String, data:String):Void
|
|
{
|
|
FileUtil.writeFileReference(path, data);
|
|
}
|
|
|
|
public static function readJSONFromPath(path:String):Dynamic
|
|
{
|
|
return FileUtil.readJSONFromPath(sanitizePath(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';
|
|
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';
|
|
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';
|
|
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';
|
|
FileUtil.moveFile(path, destination);
|
|
}
|
|
|
|
public static function deleteFile(path:String):Void
|
|
{
|
|
if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: $path';
|
|
FileUtil.deleteFile(path);
|
|
}
|
|
|
|
public static function getFileSize(path:String):Int
|
|
{
|
|
return FileUtil.getFileSize(sanitizePath(path));
|
|
}
|
|
|
|
public static function pathExists(path:String):Bool
|
|
{
|
|
return FileUtil.pathExists(sanitizePath(path));
|
|
}
|
|
|
|
public static function fileExists(path:String):Bool
|
|
{
|
|
return FileUtil.fileExists(sanitizePath(path));
|
|
}
|
|
|
|
public static function directoryExists(path:String):Bool
|
|
{
|
|
return FileUtil.directoryExists(sanitizePath(path));
|
|
}
|
|
|
|
public static function createDirIfNotExists(dir:String):Void
|
|
{
|
|
FileUtil.createDirIfNotExists(sanitizePath(dir));
|
|
}
|
|
|
|
public static function readDir(path:String):Array<String>
|
|
{
|
|
return FileUtil.readDir(sanitizePath(path));
|
|
}
|
|
|
|
public static function moveDir(path:String, destination:String, ?ignore:Array<String>, strict:Bool = true):Void
|
|
{
|
|
if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: "$path"';
|
|
if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: "$destination"';
|
|
FileUtil.moveDir(path, destination, ignore, strict);
|
|
}
|
|
|
|
public static function deleteDir(path:String, recursive:Bool = false, ?ignore:Array<String>):Void
|
|
{
|
|
if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: "$path"';
|
|
FileUtil.deleteDir(path, recursive, ignore);
|
|
}
|
|
|
|
public static function getDirSize(path:String):Int
|
|
{
|
|
return FileUtil.getDirSize(sanitizePath(path));
|
|
}
|
|
|
|
public static function getTempDir():Null<String>
|
|
{
|
|
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"';
|
|
FileUtil.rename(path, sanitizePath(newName), keepExtension);
|
|
}
|
|
|
|
public static function createZIPFromEntries(entries:Array<Entry>):Bytes
|
|
{
|
|
return FileUtil.createZIPFromEntries(entries);
|
|
}
|
|
|
|
public static function readZIPFromBytes(input:Bytes):Array<Entry>
|
|
{
|
|
return FileUtil.readZIPFromBytes(input);
|
|
}
|
|
|
|
public static function mapZIPEntriesByName(input:Array<Entry>):Map<String, Entry>
|
|
{
|
|
return FileUtil.mapZIPEntriesByName(input);
|
|
}
|
|
|
|
public static function makeZIPEntry(name:String, content:String):Entry
|
|
{
|
|
return FileUtil.makeZIPEntry(name, content);
|
|
}
|
|
|
|
public static function makeZIPEntryFromBytes(name:String, data:haxe.io.Bytes):Entry
|
|
{
|
|
return FileUtil.makeZIPEntryFromBytes(name, data);
|
|
}
|
|
|
|
public static function openFolder(pathFolder:String)
|
|
{
|
|
FileUtil.openFolder(pathFolder);
|
|
}
|
|
}
|
|
|
|
enum FileWriteMode
|
|
{
|
|
/**
|
|
* Forcibly overwrite the file if it already exists.
|
|
*/
|
|
Force;
|
|
|
|
/**
|
|
* Ask the user if they want to overwrite the file if it already exists.
|
|
*/
|
|
Ask;
|
|
|
|
/**
|
|
* Skip the file if it already exists.
|
|
*/
|
|
Skip;
|
|
}
|