mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-11-23 05:33:48 +00:00
Fixes for quicksave
This commit is contained in:
parent
818ae6ddd9
commit
33a1b81737
2
assets
2
assets
|
|
@ -1 +1 @@
|
||||||
Subproject commit fd745fcb16c6a0de73449ae833ce1d92f022d9d6
|
Subproject commit 8e8aeb06472ca294c569818cbefb1bb3dfce7854
|
||||||
|
|
@ -218,6 +218,18 @@ class ChartEditorAudioHandler
|
||||||
snd.play();
|
snd.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function wipeInstrumentalData(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
state.audioInstTrackData.clear();
|
||||||
|
stopExistingInstrumental(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function wipeVocalData(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
state.audioVocalTrackData.clear();
|
||||||
|
stopExistingVocals(state);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert byte data into a playable sound.
|
* Convert byte data into a playable sound.
|
||||||
*
|
*
|
||||||
|
|
@ -238,18 +250,27 @@ class ChartEditorAudioHandler
|
||||||
{
|
{
|
||||||
var zipEntries = [];
|
var zipEntries = [];
|
||||||
|
|
||||||
for (key in state.audioInstTrackData.keys())
|
var instTrackIds = state.audioInstTrackData.keys().array();
|
||||||
|
for (key in instTrackIds)
|
||||||
{
|
{
|
||||||
if (key == 'default')
|
if (key == 'default')
|
||||||
{
|
{
|
||||||
var data:Null<Bytes> = state.audioInstTrackData.get('default');
|
var data:Null<Bytes> = state.audioInstTrackData.get('default');
|
||||||
if (data == null) continue;
|
if (data == null)
|
||||||
|
{
|
||||||
|
trace('[WARN] Failed to access inst track ($key)');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst.ogg', data));
|
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst.ogg', data));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var data:Null<Bytes> = state.audioInstTrackData.get(key);
|
var data:Null<Bytes> = state.audioInstTrackData.get(key);
|
||||||
if (data == null) continue;
|
if (data == null)
|
||||||
|
{
|
||||||
|
trace('[WARN] Failed to access inst track ($key)');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst-${key}.ogg', data));
|
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst-${key}.ogg', data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -261,10 +282,15 @@ class ChartEditorAudioHandler
|
||||||
{
|
{
|
||||||
var zipEntries = [];
|
var zipEntries = [];
|
||||||
|
|
||||||
|
var vocalTrackIds = state.audioVocalTrackData.keys().array();
|
||||||
for (key in state.audioVocalTrackData.keys())
|
for (key in state.audioVocalTrackData.keys())
|
||||||
{
|
{
|
||||||
var data:Null<Bytes> = state.audioVocalTrackData.get(key);
|
var data:Null<Bytes> = state.audioVocalTrackData.get(key);
|
||||||
if (data == null) continue;
|
if (data == null)
|
||||||
|
{
|
||||||
|
trace('[WARN] Failed to access vocal track ($key)');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Voices-${key}.ogg', data));
|
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Voices-${key}.ogg', data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,31 @@ class ChartEditorDialogHandler
|
||||||
state.stopWelcomeMusic();
|
state.stopWelcomeMusic();
|
||||||
|
|
||||||
// Load chart from file
|
// Load chart from file
|
||||||
ChartEditorImportExportHandler.loadFromFNFCPath(state, chartPath);
|
var result:Null<Array<String>> = ChartEditorImportExportHandler.loadFromFNFCPath(state, chartPath);
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
#if !mac
|
||||||
|
NotificationManager.instance.addNotification(
|
||||||
|
{
|
||||||
|
title: 'Success',
|
||||||
|
body: result.length == 0 ? 'Loaded chart (${chartPath.toString()})' : 'Loaded chart (${chartPath.toString()})\n${result.join("\n")}',
|
||||||
|
type: result.length == 0 ? NotificationType.Success : NotificationType.Warning,
|
||||||
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||||
|
});
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if !mac
|
||||||
|
NotificationManager.instance.addNotification(
|
||||||
|
{
|
||||||
|
title: 'Failure',
|
||||||
|
body: 'Failed to load chart (${chartPath.toString()})',
|
||||||
|
type: NotificationType.Error,
|
||||||
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||||
|
});
|
||||||
|
#end
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FileUtil.doesFileExist(chartPath))
|
if (!FileUtil.doesFileExist(chartPath))
|
||||||
|
|
@ -260,7 +284,8 @@ class ChartEditorDialogHandler
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (ChartEditorImportExportHandler.loadFromFNFC(state, selectedFile.bytes))
|
var result:Null<Array<String>> = ChartEditorImportExportHandler.loadFromFNFC(state, selectedFile.bytes);
|
||||||
|
if (result != null)
|
||||||
{
|
{
|
||||||
#if !mac
|
#if !mac
|
||||||
NotificationManager.instance.addNotification(
|
NotificationManager.instance.addNotification(
|
||||||
|
|
@ -299,21 +324,33 @@ class ChartEditorDialogHandler
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (ChartEditorImportExportHandler.loadFromFNFCPath(state, path.toString()))
|
var result:Null<Array<String>> = ChartEditorImportExportHandler.loadFromFNFCPath(state, path.toString());
|
||||||
|
if (result != null)
|
||||||
{
|
{
|
||||||
#if !mac
|
#if !mac
|
||||||
NotificationManager.instance.addNotification(
|
NotificationManager.instance.addNotification(
|
||||||
{
|
{
|
||||||
title: 'Success',
|
title: 'Success',
|
||||||
body: 'Loaded chart (${path.toString()})',
|
body: result.length == 0 ? 'Loaded chart (${path.toString()})' : 'Loaded chart (${path.toString()})\n${result.join("\n")}',
|
||||||
type: NotificationType.Success,
|
type: result.length == 0 ? NotificationType.Success : NotificationType.Warning,
|
||||||
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||||
});
|
});
|
||||||
#end
|
#end
|
||||||
|
|
||||||
dialog.hideDialog(DialogButton.APPLY);
|
dialog.hideDialog(DialogButton.APPLY);
|
||||||
removeDropHandler(onDropFile);
|
removeDropHandler(onDropFile);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if !mac
|
||||||
|
NotificationManager.instance.addNotification(
|
||||||
|
{
|
||||||
|
title: 'Failure',
|
||||||
|
body: 'Failed to load chart (${path.toString()})',
|
||||||
|
type: NotificationType.Error,
|
||||||
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||||
|
});
|
||||||
|
#end
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (err)
|
catch (err)
|
||||||
{
|
{
|
||||||
|
|
@ -359,6 +396,8 @@ class ChartEditorDialogHandler
|
||||||
var uploadVocalsDialog:Dialog = openUploadVocalsDialog(state, closable); // var uploadVocalsDialog:Dialog
|
var uploadVocalsDialog:Dialog = openUploadVocalsDialog(state, closable); // var uploadVocalsDialog:Dialog
|
||||||
uploadVocalsDialog.onDialogClosed = function(_event) {
|
uploadVocalsDialog.onDialogClosed = function(_event) {
|
||||||
state.isHaxeUIDialogOpen = false;
|
state.isHaxeUIDialogOpen = false;
|
||||||
|
state.currentWorkingFilePath = null; // Built from parts, so no .fnfc to save to.
|
||||||
|
state.switchToCurrentInstrumental();
|
||||||
state.postLoadInstrumental();
|
state.postLoadInstrumental();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -398,6 +437,8 @@ class ChartEditorDialogHandler
|
||||||
var uploadVocalsDialog:Dialog = openUploadVocalsDialog(state, closable); // var uploadVocalsDialog:Dialog
|
var uploadVocalsDialog:Dialog = openUploadVocalsDialog(state, closable); // var uploadVocalsDialog:Dialog
|
||||||
uploadVocalsDialog.onDialogClosed = function(_event) {
|
uploadVocalsDialog.onDialogClosed = function(_event) {
|
||||||
state.isHaxeUIDialogOpen = false;
|
state.isHaxeUIDialogOpen = false;
|
||||||
|
state.currentWorkingFilePath = null; // New file, so no path.
|
||||||
|
state.switchToCurrentInstrumental();
|
||||||
state.postLoadInstrumental();
|
state.postLoadInstrumental();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -848,6 +889,7 @@ class ChartEditorDialogHandler
|
||||||
var dialogContinue:Null<Button> = dialog.findComponent('dialogContinue', Button);
|
var dialogContinue:Null<Button> = dialog.findComponent('dialogContinue', Button);
|
||||||
if (dialogContinue == null) throw 'Could not locate dialogContinue button in Song Metadata dialog';
|
if (dialogContinue == null) throw 'Could not locate dialogContinue button in Song Metadata dialog';
|
||||||
dialogContinue.onClick = (_event) -> {
|
dialogContinue.onClick = (_event) -> {
|
||||||
|
state.songMetadata.clear();
|
||||||
state.songMetadata.set(targetVariation, newSongMetadata);
|
state.songMetadata.set(targetVariation, newSongMetadata);
|
||||||
|
|
||||||
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import haxe.io.Path;
|
||||||
import funkin.util.SerializerUtil;
|
import funkin.util.SerializerUtil;
|
||||||
import haxe.ui.notifications.NotificationManager;
|
import haxe.ui.notifications.NotificationManager;
|
||||||
import funkin.util.FileUtil;
|
import funkin.util.FileUtil;
|
||||||
import funkin.util.FileUtil;
|
import funkin.util.FileUtil.FileWriteMode;
|
||||||
import haxe.io.Bytes;
|
import haxe.io.Bytes;
|
||||||
import funkin.play.song.Song;
|
import funkin.play.song.Song;
|
||||||
import funkin.data.song.SongData.SongChartData;
|
import funkin.data.song.SongData.SongChartData;
|
||||||
|
|
@ -53,7 +53,8 @@ class ChartEditorImportExportHandler
|
||||||
|
|
||||||
state.sortChartData();
|
state.sortChartData();
|
||||||
|
|
||||||
state.clearVocals();
|
ChartEditorAudioHandler.wipeInstrumentalData(state);
|
||||||
|
ChartEditorAudioHandler.wipeVocalData(state);
|
||||||
|
|
||||||
var variations:Array<String> = state.availableVariations;
|
var variations:Array<String> = state.availableVariations;
|
||||||
for (variation in variations)
|
for (variation in variations)
|
||||||
|
|
@ -91,7 +92,10 @@ class ChartEditorImportExportHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.isHaxeUIDialogOpen = false;
|
||||||
|
state.currentWorkingFilePath = null; // New file, so no path.
|
||||||
state.switchToCurrentInstrumental();
|
state.switchToCurrentInstrumental();
|
||||||
|
state.postLoadInstrumental();
|
||||||
|
|
||||||
state.refreshMetadataToolbox();
|
state.refreshMetadataToolbox();
|
||||||
|
|
||||||
|
|
@ -138,31 +142,40 @@ class ChartEditorImportExportHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function loadFromFNFCPath(state:ChartEditorState, path:String):Bool
|
/**
|
||||||
|
* Load a chart's metadata, chart data, and audio from an FNFC file path.
|
||||||
|
* @param state
|
||||||
|
* @param path
|
||||||
|
* @return `null` on failure, `[]` on success, `[warnings]` on success with warnings.
|
||||||
|
*/
|
||||||
|
public static function loadFromFNFCPath(state:ChartEditorState, path:String):Null<Array<String>>
|
||||||
{
|
{
|
||||||
var bytes:Null<Bytes> = FileUtil.readBytesFromPath(path);
|
var bytes:Null<Bytes> = FileUtil.readBytesFromPath(path);
|
||||||
if (bytes == null) return false;
|
if (bytes == null) return null;
|
||||||
|
|
||||||
trace('Loaded ${bytes.length} bytes from $path');
|
trace('Loaded ${bytes.length} bytes from $path');
|
||||||
|
|
||||||
var result:Bool = loadFromFNFC(state, bytes);
|
var result:Null<Array<String>> = loadFromFNFC(state, bytes);
|
||||||
if (result)
|
if (result != null)
|
||||||
{
|
{
|
||||||
state.currentWorkingFilePath = path;
|
state.currentWorkingFilePath = path;
|
||||||
|
state.saveDataDirty = false; // Just loaded file!
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a chart's metadata, chart data, and audio from an FNFC archive..
|
* Load a chart's metadata, chart data, and audio from an FNFC archive.
|
||||||
* @param state
|
* @param state
|
||||||
* @param bytes
|
* @param bytes
|
||||||
* @param instId
|
* @param instId
|
||||||
* @return Bool
|
* @return `null` on failure, `[]` on success, `[warnings]` on success with warnings.
|
||||||
*/
|
*/
|
||||||
public static function loadFromFNFC(state:ChartEditorState, bytes:Bytes):Bool
|
public static function loadFromFNFC(state:ChartEditorState, bytes:Bytes):Null<Array<String>>
|
||||||
{
|
{
|
||||||
|
var warnings:Array<String> = [];
|
||||||
|
|
||||||
var songMetadatas:Map<String, SongMetadata> = [];
|
var songMetadatas:Map<String, SongMetadata> = [];
|
||||||
var songChartDatas:Map<String, SongChartData> = [];
|
var songChartDatas:Map<String, SongChartData> = [];
|
||||||
|
|
||||||
|
|
@ -231,8 +244,8 @@ class ChartEditorImportExportHandler
|
||||||
songChartDatas.set(variation, variChartData);
|
songChartDatas.set(variation, variChartData);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChartEditorAudioHandler.stopExistingInstrumental(state);
|
ChartEditorAudioHandler.wipeInstrumentalData(state);
|
||||||
ChartEditorAudioHandler.stopExistingVocals(state);
|
ChartEditorAudioHandler.wipeVocalData(state);
|
||||||
|
|
||||||
// Load instrumentals
|
// Load instrumentals
|
||||||
for (variation in [Constants.DEFAULT_VARIATION].concat(variationList))
|
for (variation in [Constants.DEFAULT_VARIATION].concat(variationList))
|
||||||
|
|
@ -264,12 +277,14 @@ class ChartEditorImportExportHandler
|
||||||
{
|
{
|
||||||
if (!ChartEditorAudioHandler.loadVocalsFromBytes(state, playerVocalsFileBytes, playerCharId, instId))
|
if (!ChartEditorAudioHandler.loadVocalsFromBytes(state, playerVocalsFileBytes, playerCharId, instId))
|
||||||
{
|
{
|
||||||
throw 'Could not load vocals ($playerCharId).';
|
warnings.push('Could not parse vocals ($playerCharId).');
|
||||||
|
// throw 'Could not parse vocals ($playerCharId).';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw 'Could not find vocals ($playerVocalsFileName).';
|
warnings.push('Could not find vocals ($playerVocalsFileName).');
|
||||||
|
// throw 'Could not find vocals ($playerVocalsFileName).';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opponentCharId != null)
|
if (opponentCharId != null)
|
||||||
|
|
@ -280,12 +295,14 @@ class ChartEditorImportExportHandler
|
||||||
{
|
{
|
||||||
if (!ChartEditorAudioHandler.loadVocalsFromBytes(state, opponentVocalsFileBytes, opponentCharId, instId))
|
if (!ChartEditorAudioHandler.loadVocalsFromBytes(state, opponentVocalsFileBytes, opponentCharId, instId))
|
||||||
{
|
{
|
||||||
throw 'Could not load vocals ($opponentCharId).';
|
warnings.push('Could not parse vocals ($opponentCharId).');
|
||||||
|
// throw 'Could not parse vocals ($opponentCharId).';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw 'Could not load vocals ($playerCharId-$instId).';
|
warnings.push('Could not find vocals ($opponentVocalsFileName).');
|
||||||
|
// throw 'Could not find vocals ($opponentVocalsFileName).';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -297,7 +314,7 @@ class ChartEditorImportExportHandler
|
||||||
|
|
||||||
state.switchToCurrentInstrumental();
|
state.switchToCurrentInstrumental();
|
||||||
|
|
||||||
return true;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -345,8 +362,10 @@ class ChartEditorImportExportHandler
|
||||||
|
|
||||||
if (force)
|
if (force)
|
||||||
{
|
{
|
||||||
|
var targetMode:FileWriteMode = Force;
|
||||||
if (targetPath == null)
|
if (targetPath == null)
|
||||||
{
|
{
|
||||||
|
targetMode = Skip;
|
||||||
targetPath = Path.join([
|
targetPath = Path.join([
|
||||||
'./backups/',
|
'./backups/',
|
||||||
'chart-editor-${DateUtil.generateTimestamp()}.${Constants.EXT_CHART}'
|
'chart-editor-${DateUtil.generateTimestamp()}.${Constants.EXT_CHART}'
|
||||||
|
|
@ -355,7 +374,8 @@ class ChartEditorImportExportHandler
|
||||||
|
|
||||||
// We have to force write because the program will die before the save dialog is closed.
|
// We have to force write because the program will die before the save dialog is closed.
|
||||||
trace('Force exporting to $targetPath...');
|
trace('Force exporting to $targetPath...');
|
||||||
FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath);
|
FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode);
|
||||||
|
state.saveDataDirty = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -364,9 +384,11 @@ class ChartEditorImportExportHandler
|
||||||
if (paths.length != 1)
|
if (paths.length != 1)
|
||||||
{
|
{
|
||||||
trace('[WARN] Could not get save path.');
|
trace('[WARN] Could not get save path.');
|
||||||
|
state.applyWindowTitle();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
trace('Saved to "${paths[0]}"');
|
||||||
state.currentWorkingFilePath = paths[0];
|
state.currentWorkingFilePath = paths[0];
|
||||||
state.applyWindowTitle();
|
state.applyWindowTitle();
|
||||||
}
|
}
|
||||||
|
|
@ -380,6 +402,7 @@ class ChartEditorImportExportHandler
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileUtil.saveChartAsFNFC(zipEntries, onSave, onCancel, '${state.currentSongId}.${Constants.EXT_CHART}');
|
FileUtil.saveChartAsFNFC(zipEntries, onSave, onCancel, '${state.currentSongId}.${Constants.EXT_CHART}');
|
||||||
|
state.saveDataDirty = false;
|
||||||
}
|
}
|
||||||
catch (e) {}
|
catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -644,7 +644,9 @@ class ChartEditorState extends HaxeUIState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return saveDataDirty = value;
|
saveDataDirty = value;
|
||||||
|
applyWindowTitle();
|
||||||
|
return saveDataDirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -882,7 +884,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
var result:Null<SongMetadata> = songMetadata.get(selectedVariation);
|
var result:Null<SongMetadata> = songMetadata.get(selectedVariation);
|
||||||
if (result == null)
|
if (result == null)
|
||||||
{
|
{
|
||||||
result = new SongMetadata('Dad Battle', 'Kawai Sprite', selectedVariation);
|
result = new SongMetadata('DadBattle', 'Kawai Sprite', selectedVariation);
|
||||||
songMetadata.set(selectedVariation, result);
|
songMetadata.set(selectedVariation, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1170,7 +1172,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
/**
|
/**
|
||||||
* The item in the menubar to save the currently opened chart.
|
* The item in the menubar to save the currently opened chart.
|
||||||
*/
|
*/
|
||||||
var menubarItemSave:Null<FunkinMenuItem> = null;
|
var menubarItemSaveChart:Null<FunkinMenuItem> = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The playbar head slider.
|
* The playbar head slider.
|
||||||
|
|
@ -1229,10 +1231,11 @@ class ChartEditorState extends HaxeUIState
|
||||||
/**
|
/**
|
||||||
* A list of previous working file paths.
|
* A list of previous working file paths.
|
||||||
* Also known as the "recent files" list.
|
* Also known as the "recent files" list.
|
||||||
|
* The first element is [null] if the current working file has not been saved anywhere yet.
|
||||||
*/
|
*/
|
||||||
public var previousWorkingFilePaths(default, set):Array<String> = [];
|
public var previousWorkingFilePaths(default, set):Array<Null<String>> = [null];
|
||||||
|
|
||||||
function set_previousWorkingFilePaths(value:Array<String>):Array<String>
|
function set_previousWorkingFilePaths(value:Array<Null<String>>):Array<Null<String>>
|
||||||
{
|
{
|
||||||
// Called only when the WHOLE LIST is overridden.
|
// Called only when the WHOLE LIST is overridden.
|
||||||
previousWorkingFilePaths = value;
|
previousWorkingFilePaths = value;
|
||||||
|
|
@ -1260,7 +1263,9 @@ class ChartEditorState extends HaxeUIState
|
||||||
if (previousWorkingFilePaths.contains(null))
|
if (previousWorkingFilePaths.contains(null))
|
||||||
{
|
{
|
||||||
// Filter all instances of `null` from the array.
|
// Filter all instances of `null` from the array.
|
||||||
previousWorkingFilePaths = previousWorkingFilePaths.filter((x) -> x != null);
|
previousWorkingFilePaths = previousWorkingFilePaths.filter(function(x:Null<String>):Bool {
|
||||||
|
return x != null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previousWorkingFilePaths.contains(value))
|
if (previousWorkingFilePaths.contains(value))
|
||||||
|
|
@ -1340,22 +1345,31 @@ class ChartEditorState extends HaxeUIState
|
||||||
if (params != null && params.fnfcTargetPath != null)
|
if (params != null && params.fnfcTargetPath != null)
|
||||||
{
|
{
|
||||||
// Chart editor was opened from the command line. Open the FNFC file now!
|
// Chart editor was opened from the command line. Open the FNFC file now!
|
||||||
if (ChartEditorImportExportHandler.loadFromFNFCPath(this, params.fnfcTargetPath))
|
var result:Null<Array<String>> = ChartEditorImportExportHandler.loadFromFNFCPath(this, params.fnfcTargetPath);
|
||||||
|
if (result != null)
|
||||||
{
|
{
|
||||||
// Don't open the welcome dialog!
|
|
||||||
|
|
||||||
#if !mac
|
#if !mac
|
||||||
NotificationManager.instance.addNotification(
|
NotificationManager.instance.addNotification(
|
||||||
{
|
{
|
||||||
title: 'Success',
|
title: 'Success',
|
||||||
body: 'Loaded chart (${params.fnfcTargetPath})',
|
body: result.length == 0 ? 'Loaded chart (${params.fnfcTargetPath})' : 'Loaded chart (${params.fnfcTargetPath})\n${result.join("\n")}',
|
||||||
type: NotificationType.Success,
|
type: result.length == 0 ? NotificationType.Success : NotificationType.Warning,
|
||||||
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||||
});
|
});
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if !mac
|
||||||
|
NotificationManager.instance.addNotification(
|
||||||
|
{
|
||||||
|
title: 'Failure',
|
||||||
|
body: 'Failed to load chart (${params.fnfcTargetPath})',
|
||||||
|
type: NotificationType.Error,
|
||||||
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||||
|
});
|
||||||
|
#end
|
||||||
|
|
||||||
// Song failed to load, open the Welcome dialog so we aren't in a broken state.
|
// Song failed to load, open the Welcome dialog so we aren't in a broken state.
|
||||||
ChartEditorDialogHandler.openWelcomeDialog(this, false);
|
ChartEditorDialogHandler.openWelcomeDialog(this, false);
|
||||||
}
|
}
|
||||||
|
|
@ -1376,7 +1390,14 @@ class ChartEditorState extends HaxeUIState
|
||||||
{
|
{
|
||||||
var save:Save = Save.get();
|
var save:Save = Save.get();
|
||||||
|
|
||||||
previousWorkingFilePaths = save.chartEditorPreviousFiles;
|
if (previousWorkingFilePaths[0] == null)
|
||||||
|
{
|
||||||
|
previousWorkingFilePaths = [null].concat(save.chartEditorPreviousFiles);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
previousWorkingFilePaths = [currentWorkingFilePath].concat(save.chartEditorPreviousFiles);
|
||||||
|
}
|
||||||
noteSnapQuantIndex = save.chartEditorNoteQuant;
|
noteSnapQuantIndex = save.chartEditorNoteQuant;
|
||||||
currentLiveInputStyle = save.chartEditorLiveInputStyle;
|
currentLiveInputStyle = save.chartEditorLiveInputStyle;
|
||||||
isViewDownscroll = save.chartEditorDownscroll;
|
isViewDownscroll = save.chartEditorDownscroll;
|
||||||
|
|
@ -1396,7 +1417,12 @@ class ChartEditorState extends HaxeUIState
|
||||||
{
|
{
|
||||||
var save:Save = Save.get();
|
var save:Save = Save.get();
|
||||||
|
|
||||||
save.chartEditorPreviousFiles = previousWorkingFilePaths;
|
// Can't use filter() because of null safety checking!
|
||||||
|
var filteredWorkingFilePaths:Array<String> = [];
|
||||||
|
for (chartPath in previousWorkingFilePaths)
|
||||||
|
if (chartPath != null) filteredWorkingFilePaths.push(chartPath);
|
||||||
|
|
||||||
|
save.chartEditorPreviousFiles = filteredWorkingFilePaths;
|
||||||
save.chartEditorNoteQuant = noteSnapQuantIndex;
|
save.chartEditorNoteQuant = noteSnapQuantIndex;
|
||||||
save.chartEditorLiveInputStyle = currentLiveInputStyle;
|
save.chartEditorLiveInputStyle = currentLiveInputStyle;
|
||||||
save.chartEditorDownscroll = isViewDownscroll;
|
save.chartEditorDownscroll = isViewDownscroll;
|
||||||
|
|
@ -1428,7 +1454,31 @@ class ChartEditorState extends HaxeUIState
|
||||||
stopWelcomeMusic();
|
stopWelcomeMusic();
|
||||||
|
|
||||||
// Load chart from file
|
// Load chart from file
|
||||||
ChartEditorImportExportHandler.loadFromFNFCPath(this, chartPath);
|
var result:Null<Array<String>> = ChartEditorImportExportHandler.loadFromFNFCPath(this, chartPath);
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
#if !mac
|
||||||
|
NotificationManager.instance.addNotification(
|
||||||
|
{
|
||||||
|
title: 'Success',
|
||||||
|
body: result.length == 0 ? 'Loaded chart (${chartPath.toString()})' : 'Loaded chart (${chartPath.toString()})\n${result.join("\n")}',
|
||||||
|
type: result.length == 0 ? NotificationType.Success : NotificationType.Warning,
|
||||||
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||||
|
});
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if !mac
|
||||||
|
NotificationManager.instance.addNotification(
|
||||||
|
{
|
||||||
|
title: 'Failure',
|
||||||
|
body: 'Failed to load chart (${chartPath.toString()})',
|
||||||
|
type: NotificationType.Error,
|
||||||
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||||
|
});
|
||||||
|
#end
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FileUtil.doesFileExist(chartPath))
|
if (!FileUtil.doesFileExist(chartPath))
|
||||||
|
|
@ -1774,8 +1824,8 @@ class ChartEditorState extends HaxeUIState
|
||||||
menubarOpenRecent = findComponent('menubarOpenRecent', Menu);
|
menubarOpenRecent = findComponent('menubarOpenRecent', Menu);
|
||||||
if (menubarOpenRecent == null) throw "Could not find menubarOpenRecent!";
|
if (menubarOpenRecent == null) throw "Could not find menubarOpenRecent!";
|
||||||
|
|
||||||
menubarItemSave = findComponent('menubarItemSave', FunkinMenuItem);
|
menubarItemSaveChart = findComponent('menubarItemSaveChart', FunkinMenuItem);
|
||||||
if (menubarItemSave == null) throw "Could not find menubarItemSave!";
|
if (menubarItemSaveChart == null) throw "Could not find menubarItemSaveChart!";
|
||||||
|
|
||||||
// Setup notifications.
|
// Setup notifications.
|
||||||
@:privateAccess
|
@:privateAccess
|
||||||
|
|
@ -3340,12 +3390,24 @@ class ChartEditorState extends HaxeUIState
|
||||||
ChartEditorDialogHandler.openBrowseFNFC(this, true);
|
ChartEditorDialogHandler.openBrowseFNFC(this, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.S)
|
||||||
|
{
|
||||||
|
if (currentWorkingFilePath == null || FlxG.keys.pressed.SHIFT)
|
||||||
|
{
|
||||||
// CTRL + SHIFT + S = Save As
|
// CTRL + SHIFT + S = Save As
|
||||||
|
ChartEditorImportExportHandler.exportAllSongData(this, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// CTRL + S = Save Chart
|
||||||
|
ChartEditorImportExportHandler.exportAllSongData(this, true, currentWorkingFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.S)
|
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.S)
|
||||||
{
|
{
|
||||||
ChartEditorImportExportHandler.exportAllSongData(this, false);
|
ChartEditorImportExportHandler.exportAllSongData(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CTRL + Q = Quit to Menu
|
// CTRL + Q = Quit to Menu
|
||||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.Q)
|
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.Q)
|
||||||
{
|
{
|
||||||
|
|
@ -3361,6 +3423,8 @@ class ChartEditorState extends HaxeUIState
|
||||||
// TODO: PR Flixel to make onComplete nullable.
|
// TODO: PR Flixel to make onComplete nullable.
|
||||||
if (audioInstTrack != null) audioInstTrack.onComplete = null;
|
if (audioInstTrack != null) audioInstTrack.onComplete = null;
|
||||||
FlxG.switchState(new MainMenuState());
|
FlxG.switchState(new MainMenuState());
|
||||||
|
|
||||||
|
resetWindowTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -4534,22 +4598,36 @@ class ChartEditorState extends HaxeUIState
|
||||||
|
|
||||||
function applyCanQuickSave():Void
|
function applyCanQuickSave():Void
|
||||||
{
|
{
|
||||||
if (currentWorkingFilePath == null) {}
|
if (menubarItemSaveChart == null) return;
|
||||||
else {}
|
|
||||||
|
if (currentWorkingFilePath == null)
|
||||||
|
{
|
||||||
|
menubarItemSaveChart.disabled = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
menubarItemSaveChart.disabled = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyWindowTitle():Void
|
function applyWindowTitle():Void
|
||||||
{
|
{
|
||||||
var inner:String = (currentSongMetadata.songName != null) ? currentSongMetadata.songName : 'Untitled';
|
var inner:String = 'New Chart';
|
||||||
if (currentWorkingFilePath == null)
|
var cwfp:Null<String> = currentWorkingFilePath;
|
||||||
|
if (cwfp != null)
|
||||||
|
{
|
||||||
|
inner = cwfp;
|
||||||
|
}
|
||||||
|
if (currentWorkingFilePath == null || saveDataDirty)
|
||||||
{
|
{
|
||||||
inner += '*';
|
inner += '*';
|
||||||
}
|
}
|
||||||
else
|
WindowUtil.setWindowTitle('Friday Night Funkin\' Chart Editor - ${inner}');
|
||||||
{
|
|
||||||
inner += ' (${currentWorkingFilePath})';
|
|
||||||
}
|
}
|
||||||
WindowUtil.setWindowTitle('FNF Chart Editor - ${inner}');
|
|
||||||
|
function resetWindowTitle():Void
|
||||||
|
{
|
||||||
|
WindowUtil.setWindowTitle('Friday Night Funkin\'');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ class FileUtil
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Browses for a file location to save to, then calls `onSelect(path)` when a path chosen.
|
* 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.
|
* 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?
|
* @param typeFilter TODO What does this do?
|
||||||
|
|
@ -183,7 +183,7 @@ class FileUtil
|
||||||
var filter:String = convertTypeFilter(typeFilter);
|
var filter:String = convertTypeFilter(typeFilter);
|
||||||
|
|
||||||
var fileDialog:FileDialog = new FileDialog();
|
var fileDialog:FileDialog = new FileDialog();
|
||||||
if (onSave != null) fileDialog.onSelect.add(onSave);
|
if (onSave != null) fileDialog.onSave.add(onSave);
|
||||||
if (onCancel != null) fileDialog.onCancel.add(onCancel);
|
if (onCancel != null) fileDialog.onCancel.add(onCancel);
|
||||||
|
|
||||||
fileDialog.save(data, filter, defaultFileName, dialogTitle);
|
fileDialog.save(data, filter, defaultFileName, dialogTitle);
|
||||||
|
|
@ -268,7 +268,8 @@ class FileUtil
|
||||||
var zipBytes:Bytes = createZIPFromEntries(resources);
|
var zipBytes:Bytes = createZIPFromEntries(resources);
|
||||||
|
|
||||||
var onSave:String->Void = function(path:String) {
|
var onSave:String->Void = function(path:String) {
|
||||||
onSave([path]);
|
trace('Saved ${resources.length} files to ZIP at "$path".');
|
||||||
|
if (onSave != null) onSave([path]);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prompt the user to save the ZIP file.
|
// Prompt the user to save the ZIP file.
|
||||||
|
|
@ -287,7 +288,8 @@ class FileUtil
|
||||||
var zipBytes:Bytes = createZIPFromEntries(resources);
|
var zipBytes:Bytes = createZIPFromEntries(resources);
|
||||||
|
|
||||||
var onSave:String->Void = function(path:String) {
|
var onSave:String->Void = function(path:String) {
|
||||||
onSave([path]);
|
trace('Saved FNF file to "$path"');
|
||||||
|
if (onSave != null) onSave([path]);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prompt the user to save the ZIP file.
|
// Prompt the user to save the ZIP file.
|
||||||
|
|
@ -302,14 +304,14 @@ class FileUtil
|
||||||
* Use `saveFilesAsZIP` instead.
|
* Use `saveFilesAsZIP` instead.
|
||||||
* @param force Whether to force overwrite an existing file.
|
* @param force Whether to force overwrite an existing file.
|
||||||
*/
|
*/
|
||||||
public static function saveFilesAsZIPToPath(resources:Array<Entry>, path:String, force:Bool = false):Bool
|
public static function saveFilesAsZIPToPath(resources:Array<Entry>, path:String, mode:FileWriteMode = Skip):Bool
|
||||||
{
|
{
|
||||||
#if desktop
|
#if desktop
|
||||||
// Create a ZIP file.
|
// Create a ZIP file.
|
||||||
var zipBytes:Bytes = createZIPFromEntries(resources);
|
var zipBytes:Bytes = createZIPFromEntries(resources);
|
||||||
|
|
||||||
// Write the ZIP.
|
// Write the ZIP.
|
||||||
writeBytesToPath(path, zipBytes, force ? Force : Skip);
|
writeBytesToPath(path, zipBytes, mode);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
|
|
@ -449,12 +451,14 @@ class FileUtil
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw 'File already exists: $path';
|
// Do nothing.
|
||||||
|
// throw 'File already exists: $path';
|
||||||
}
|
}
|
||||||
case Ask:
|
case Ask:
|
||||||
if (doesFileExist(path))
|
if (doesFileExist(path))
|
||||||
{
|
{
|
||||||
// TODO: We don't have the technology to use native popups yet.
|
// TODO: We don't have the technology to use native popups yet.
|
||||||
|
throw 'File already exists: $path';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -490,12 +494,14 @@ class FileUtil
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw 'File already exists: $path';
|
// Do nothing.
|
||||||
|
// throw 'File already exists: $path';
|
||||||
}
|
}
|
||||||
case Ask:
|
case Ask:
|
||||||
if (doesFileExist(path))
|
if (doesFileExist(path))
|
||||||
{
|
{
|
||||||
// TODO: We don't have the technology to use native popups yet.
|
// TODO: We don't have the technology to use native popups yet.
|
||||||
|
throw 'File already exists: $path';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue