1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-12-08 13:08:26 +00:00

Osu!Mania Importing

This commit is contained in:
Furo 2025-09-27 20:59:29 +02:00 committed by Kade
parent ba31f3611a
commit 0be42bf047
5 changed files with 379 additions and 52 deletions

View file

@ -0,0 +1,81 @@
package funkin.data.song.importer;
/**
* Structure of a parsed Osu!Mania .osu file
* Stuctured like a INI file format by CSV for HitObjects and more
*/
typedef OsuManiaData =
{
var General:
{
var PreviewTime:Int;
};
var Editor:
{
var DistanceSpacing:Float;
var BeatDivisor:Int;
var GridSize:Int;
};
var Metadata:
{
var Title:String;
var TitleUnicode:String;
var Artist:String;
var ArtistUnicode:String;
var Creator:String;
var Version:String;
};
var Difficulty:
{
var OverallDifficulty:Float;
var SliderMultiplier:Float;
var SliderTickRate:Float;
};
var HitObjects:Array<ManiaHitObject>;
var TimingPoints:Array<TimingPoint>;
var Events:Array<Any>;
}
class TimingPoint
{
public var time:Float;
public var beatLength:Float;
public var meter:Int;
public var sampleSet:Int;
public var sampleIndex:Int;
public var volume:Int;
public var uninherited:Int;
public var effects:Int;
public var bpm:Null<Float>;
public var sv:Null<Float>;
public function new(time:Float, beatLength:Float, meter:Int, sampleSet:Int, sampleIndex:Int, volume:Int, uninherited:Int, effects:Int)
{
this.time = time;
this.beatLength = beatLength;
this.meter = meter;
this.sampleSet = sampleSet;
this.sampleIndex = sampleIndex;
this.volume = volume;
this.uninherited = uninherited;
this.effects = effects;
// Derived values
this.bpm = (uninherited == 1) ? (Math.round((60000 / beatLength) * 10) / 10) : null;
this.sv = (uninherited == 0) ? (beatLength / 100) : null; // Just incase someone wants to add Scroll Velocity Support
}
}
class ManiaHitObject
{
public var time:Int;
public var column:Int;
public var holdDuration:Int;
public function new(time:Int, column:Int, holdDuration:Int)
{
this.time = time;
this.column = column;
this.holdDuration = holdDuration;
}
}

View file

@ -0,0 +1,185 @@
package funkin.data.song.importer;
import funkin.data.song.SongData.SongMetadata;
import funkin.data.song.SongData.SongChartData;
import funkin.data.song.SongData.SongCharacterData;
import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongData.SongTimeChange;
import funkin.data.song.importer.OsuManiaData;
import funkin.data.song.importer.OsuManiaData.TimingPoint;
import funkin.data.song.importer.OsuManiaData.ManiaHitObject;
class OsuManiaImporter
{
public static function parseOsuFile(osuContent:String):OsuManiaData
{
var lines:Array<String> = osuContent.split("\n");
var result:Dynamic = {};
var currentSection:String = null;
var nonCSVLikeSections = ["General", "Editor", "Metadata", "Difficulty"];
for (line in lines)
{
line = StringTools.trim(line);
if (line == "" || StringTools.startsWith(line, "//")) continue;
// Section header like [General]
var sectionRegex = ~/^\[(.+)\]$/;
if (sectionRegex.match(line))
{
currentSection = sectionRegex.matched(1);
if (nonCSVLikeSections.contains(currentSection))
{
Reflect.setField(result, currentSection, {});
}
else
{
Reflect.setField(result, currentSection, []);
}
continue;
}
// Key-value pairs (INI style)
if (currentSection != null && nonCSVLikeSections.contains(currentSection))
{
var parts:Array<String> = line.split(":");
var key:String = StringTools.trim(parts.shift());
var value:String = StringTools.trim(parts.join(":"));
if (Reflect.field(result, currentSection) == null) Reflect.setField(result, currentSection, {});
Reflect.setField(Reflect.field(result, currentSection), key, value);
}
// For CSV-like sections
else if (currentSection != null)
{
var theArray:Array<String> = cast Reflect.field(result, currentSection);
theArray.push(line);
Reflect.setField(result, currentSection, theArray);
}
}
return result;
}
/**
* @param data The raw parsed JSON data to migrate, as a Dynamic.
* @param difficulty
* @return SongMetadata
*/
public static function migrateMetadata(songData:Dynamic, difficulty:String = 'normal'):SongMetadata
{
trace('Migrating song metadata from Osu!Mania.');
var songMetadata:SongMetadata = new SongMetadata('Import', songData.Metadata.ArtistUnicode ?? songData.Metadata.Artist ?? Constants.DEFAULT_ARTIST,
songData.Metadata.Creator ?? Constants.DEFAULT_CHARTER, Constants.DEFAULT_VARIATION);
// Set generatedBy string for debugging.
songMetadata.generatedBy = 'Chart Editor Import (Osu!Mania)';
songMetadata.playData.stage = 'mainStage';
songMetadata.songName = songData.Metadata.TitleUnicode ?? songData.Metadata.Title ?? 'Import';
songMetadata.playData.difficulties = [difficulty];
songMetadata.playData.songVariations = [];
songMetadata.timeChanges = rebuildTimeChanges(songData);
songMetadata.playData.characters = new SongCharacterData('bf', 'gf', 'dad');
songMetadata.playData.ratings.set(difficulty, songData.Difficulty.OverallDifficulty ?? 0);
return songMetadata;
}
static function rebuildTimeChanges(songData:Dynamic):Array<SongTimeChange>
{
var timings:Array<TimingPoint> = parseTimingPoints(songData.TimingPoints);
var bpmPoints:Array<TimingPoint> = timings.filter((tp) -> tp.uninherited == 1);
var result:Array<SongTimeChange> = [];
if (bpmPoints.length >= 1)
{
result.push(new SongTimeChange(0, bpmPoints[0].bpm ?? Constants.DEFAULT_BPM));
for (i in 1...bpmPoints.length)
{
var bpmPoint:TimingPoint = bpmPoints[i];
result.push(new SongTimeChange(bpmPoint.time, bpmPoint.bpm ?? Constants.DEFAULT_BPM));
}
}
if (result.length == 0)
{
result.push(new SongTimeChange(0, Constants.DEFAULT_BPM));
trace("[WARN] No BPM points found, resulting to default BPM...");
}
return result;
}
public static function migrateChartData(songData:Dynamic, difficulty:String = 'normal'):SongChartData
{
trace('Migrating song chart data from Osu!Mania.');
// Osu!Mania doesn't have a scroll speed variable as its controlled by the player
var songChartData:SongChartData = new SongChartData([difficulty => Constants.DEFAULT_SCROLLSPEED], [], [difficulty => []]);
var osuNotes:Array<ManiaHitObject> = parseManiaHitObjects(songData.HitObjects);
songChartData.notes.set(difficulty, convertNotes(osuNotes));
songChartData.events = [];
return songChartData;
}
static final STRUMLINE_SIZE = 4;
static function convertNotes(hitObjects:Array<ManiaHitObject>):Array<SongNoteData>
{
var result:Array<SongNoteData> = [];
for (hitObject in hitObjects)
{
result.push(new SongNoteData(hitObject.time, hitObject.column, hitObject.holdDuration ?? 0, ''));
result.push(new SongNoteData(hitObject.time, hitObject.column + STRUMLINE_SIZE, hitObject.holdDuration ?? 0, ''));
}
return result;
}
static function parseTimingPoints(timingLines:Array<String>):Array<TimingPoint>
{
return timingLines.map(function(line:String):TimingPoint {
var parts = line.split(",");
var time = Std.parseFloat(parts[0]);
var beatLength = Std.parseFloat(parts[1]);
var meter = Std.parseInt(parts[2]);
var sampleSet = Std.parseInt(parts[3]);
var sampleIndex = Std.parseInt(parts[4]);
var volume = Std.parseInt(parts[5]);
var uninherited = Std.parseInt(parts[6]);
var effects = Std.parseInt(parts[7]);
return new TimingPoint(time, beatLength, meter, sampleSet, sampleIndex, volume, uninherited, effects);
});
}
static function parseManiaHitObjects(hitObjectsLines:Array<String>, ?columns:Int = 4):Array<ManiaHitObject>
{
return hitObjectsLines.map(function(line:String):ManiaHitObject {
var parts = line.split(",");
var x:Int = Std.parseInt(parts[0]);
var time:Int = Std.parseInt(parts[2]);
var type:Int = Std.parseInt(parts[3]);
var hasHold:Bool = (type & 128) == 128;
var noteD:Int = Std.int(x / (512 / columns));
var holdEndTime:Null<Int> = hasHold ? Std.parseInt(parts[5].split(":")[0]) : null;
var holdDuration:Int = (holdEndTime != null) ? (holdEndTime - time) : 0;
return new ManiaHitObject(time, noteD, holdDuration);
});
}
}

View file

@ -79,7 +79,7 @@ class ChartEditorUploadVocalsDialog extends ChartEditorBaseDialog
this.hasClearedVocals = true;
// Tell the user the load was successful.
chartEditorState.success('Loaded Vocals', 'Loaded vocals for $charName (${path.file}.${path.ext}), variation ${chartEditorState.selectedVariation}');
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
vocalsEntry.vocalsEntryLabel.text = 'Voices for $charName (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}';
#else
vocalsEntry.vocalsEntryLabel.text = 'Voices for $charName (click to browse)\n${path.file}.${path.ext}';
@ -95,7 +95,7 @@ class ChartEditorUploadVocalsDialog extends ChartEditorBaseDialog
chartEditorState.error('Failed to Load Vocals',
'Failed to load vocal track (${path.file}.${path.ext}) for variation (${chartEditorState.selectedVariation})');
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
vocalsEntry.vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.';
#else
vocalsEntry.vocalsEntryLabel.text = 'Click to browse for vocals for $charName.';
@ -117,7 +117,7 @@ class ChartEditorUploadVocalsDialog extends ChartEditorBaseDialog
chartEditorState.success('Loaded Vocals',
'Loaded vocals for $charName (${selectedFile.name}), variation ${chartEditorState.selectedVariation}');
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
vocalsEntry.vocalsEntryLabel.text = 'Voices for $charName (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}';
#else
vocalsEntry.vocalsEntryLabel.text = 'Voices for $charName (click to browse)\n${selectedFile.name}';
@ -132,7 +132,7 @@ class ChartEditorUploadVocalsDialog extends ChartEditorBaseDialog
chartEditorState.error('Failed to Load Vocals',
'Failed to load vocal track (${selectedFile.name}) for variation (${chartEditorState.selectedVariation})');
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
vocalsEntry.vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.';
#else
vocalsEntry.vocalsEntryLabel.text = 'Click to browse for vocals for $charName.';
@ -145,7 +145,7 @@ class ChartEditorUploadVocalsDialog extends ChartEditorBaseDialog
dropHandler.handler = onDropFile;
// onDropFile
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
dropHandlers.push(dropHandler);
#end
@ -290,7 +290,7 @@ class ChartEditorUploadVocalsEntry extends Box
this.charName = charName;
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.';
#else
vocalsEntryLabel.text = 'Click to browse for vocals for $charName.';

View file

@ -34,6 +34,7 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
this.splashCreateFromSongErectOnly.onClick = _ -> onClickLinkCreateErectOnly();
this.splashCreateFromSongBasicErect.onClick = _ -> onClickLinkCreateBasicErect();
this.splashImportChartLegacy.onClick = _ -> onClickLinkImportChartLegacy();
this.splashImportChartOsuMania.onClick = _ -> onClickLinkImportOsuMania();
// Add items to the Recent Charts list
#if sys
@ -242,5 +243,18 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
// Open the "Import Chart" dialog
chartEditorState.openImportChartWizard('legacy', false);
}
/**
* Called when the user clicks the "Import Chart: Osu! Mania" link in the dialog.
* Reassign this function to change the behavior.
*/
public function onClickLinkImportOsuMania():Void
{
// Hide the welcome dialog
this.hideDialog(DialogButton.CANCEL);
// Open the "Import Chart" dialog
chartEditorState.openImportChartWizard('osumania', false);
}
}
#end

View file

@ -4,6 +4,8 @@ package funkin.ui.debug.charting.handlers;
import flixel.util.FlxTimer;
import funkin.data.song.importer.FNFLegacyData;
import funkin.data.song.importer.FNFLegacyImporter;
import funkin.data.song.importer.OsuManiaData;
import funkin.data.song.importer.OsuManiaImporter;
import funkin.data.song.SongData.SongCharacterData;
import funkin.data.song.SongData.SongChartData;
import funkin.data.song.SongData.SongMetadata;
@ -780,7 +782,7 @@ class ChartEditorDialogHandler
var songDefaultChartDataEntry:Component = RuntimeComponentBuilder.fromAsset(CHART_EDITOR_DIALOG_OPEN_CHART_PARTS_ENTRY_LAYOUT);
var songDefaultChartDataEntryLabel:Null<Label> = songDefaultChartDataEntry.findComponent('chartEntryLabel', Label);
if (songDefaultChartDataEntryLabel == null) throw 'Could not locate chartEntryLabel in Open Chart dialog';
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
songDefaultChartDataEntryLabel.text = 'Drag and drop <song>-chart.json file, or click to browse.';
#else
songDefaultChartDataEntryLabel.text = 'Click to browse for <song>-chart.json file.';
@ -800,7 +802,7 @@ class ChartEditorDialogHandler
var songVariationMetadataEntry:Component = RuntimeComponentBuilder.fromAsset(CHART_EDITOR_DIALOG_OPEN_CHART_PARTS_ENTRY_LAYOUT);
var songVariationMetadataEntryLabel:Null<Label> = songVariationMetadataEntry.findComponent('chartEntryLabel', Label);
if (songVariationMetadataEntryLabel == null) throw 'Could not locate chartEntryLabel in Open Chart dialog';
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
songVariationMetadataEntryLabel.text = 'Drag and drop <song>-metadata-${variation}.json file, or click to browse.';
#else
songVariationMetadataEntryLabel.text = 'Click to browse for <song>-metadata-${variation}.json file.';
@ -815,7 +817,7 @@ class ChartEditorDialogHandler
Cursor.cursorMode = Default;
}
songVariationMetadataEntry.onClick = onClickMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel);
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
state.addDropHandler(
{
component: songVariationMetadataEntry,
@ -828,7 +830,7 @@ class ChartEditorDialogHandler
var songVariationChartDataEntry:Component = RuntimeComponentBuilder.fromAsset(CHART_EDITOR_DIALOG_OPEN_CHART_PARTS_ENTRY_LAYOUT);
var songVariationChartDataEntryLabel:Null<Label> = songVariationChartDataEntry.findComponent('chartEntryLabel', Label);
if (songVariationChartDataEntryLabel == null) throw 'Could not locate chartEntryLabel in Open Chart dialog';
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
songVariationChartDataEntryLabel.text = 'Drag and drop <song>-chart-${variation}.json file, or click to browse.';
#else
songVariationChartDataEntryLabel.text = 'Click to browse for <song>-chart-${variation}.json file.';
@ -843,7 +845,7 @@ class ChartEditorDialogHandler
Cursor.cursorMode = Default;
}
songVariationChartDataEntry.onClick = onClickChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel);
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
state.addDropHandler(
{
component: songVariationChartDataEntry,
@ -883,7 +885,7 @@ class ChartEditorDialogHandler
// Tell the user the load was successful.
state.success('Loaded Metadata', 'Loaded metadata file (${path.file}.${path.ext})');
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}';
#else
label.text = 'Metadata file (click to browse)\n${path.file}.${path.ext}';
@ -919,7 +921,7 @@ class ChartEditorDialogHandler
// Tell the user the load was successful.
state.success('Loaded Metadata', 'Loaded metadata file (${selectedFile.name})');
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}';
#else
label.text = 'Metadata file (click to browse)\n${selectedFile.name}';
@ -963,7 +965,7 @@ class ChartEditorDialogHandler
// Tell the user the load was successful.
state.success('Loaded Chart Data', 'Loaded chart data file (${path.file}.${path.ext})');
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}';
#else
label.text = 'Chart data file (click to browse)\n${path.file}.${path.ext}';
@ -1006,7 +1008,7 @@ class ChartEditorDialogHandler
// Tell the user the load was successful.
state.success('Loaded Chart Data', 'Loaded chart data file (${selectedFile.name})');
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}';
#else
label.text = 'Chart data file (click to browse)\n${selectedFile.name}';
@ -1020,7 +1022,7 @@ class ChartEditorDialogHandler
var metadataEntryLabel:Null<Label> = metadataEntry.findComponent('chartEntryLabel', Label);
if (metadataEntryLabel == null) throw 'Could not locate chartEntryLabel in Open Chart dialog';
#if FILE_DROP_SUPPORTED
#if FEATURE_FILE_DROP
metadataEntryLabel.text = 'Drag and drop <song>-metadata.json file, or click to browse.';
#else
metadataEntryLabel.text = 'Click to browse for <song>-metadata.json file.';
@ -1057,27 +1059,45 @@ class ChartEditorDialogHandler
var prettyFormat:String = switch (format)
{
case 'legacy': 'FNF Legacy';
case 'osumania': 'Osu!Mania';
default: 'Unknown';
}
var fileFilter = switch (format)
{
case 'legacy':
// TODO / BUG: File filtering not working on mac finder dialog, so we don't use it for now
#if !mac
[
{label: 'JSON Data File (.json)', extension: 'json'}];
#else
[];
#end
case 'osumania':
[
{label: 'OSU! Beatmap File (.osu)', extension: 'osu'}];
default: null;
}
var fileExt = switch (format)
{
case 'osumania':
"osu";
default: "json";
}
dialog.title = 'Import Chart - ${prettyFormat}';
var buttonCancel:Null<Button> = dialog.findComponent('dialogCancel', Button);
if (buttonCancel == null) throw 'Could not locate dialogCancel button in Import Chart dialog';
var importExtLabel:Null<Label> = dialog.findComponent('importLabel', Label);
if (importExtLabel != null)
{
#if FEATURE_FILE_DROP
importExtLabel.text = 'Drag and drop a chart.$fileExt file, or click to browse.';
#else
importExtLabel.text = 'Click to browse for a chart.$fileExt file.';
#end
}
state.isHaxeUIDialogOpen = true;
buttonCancel.onClick = function(_) {
state.isHaxeUIDialogOpen = false;
@ -1098,49 +1118,76 @@ class ChartEditorDialogHandler
var onDropFile:String->Void;
importBox.onClick = function(_) {
Dialogs.openBinaryFile('Import Chart - ${prettyFormat}', fileFilter ?? [], function(selectedFile:SelectedFileInfo) {
if (selectedFile != null && selectedFile.bytes != null)
{
trace('Selected file: ' + selectedFile.fullPath);
var selectedFileTxt:String = selectedFile.bytes.toString();
var fnfLegacyData:Null<FNFLegacyData> = FNFLegacyImporter.parseLegacyDataRaw(selectedFileTxt, selectedFile.fullPath);
var onFileSelected:String->String->Void = (pathStr:String, content:String) -> {
var path:Path = new Path(pathStr ?? "");
trace('Selected file: ' + path.toString());
var songMetadata:Null<SongMetadata> = null;
var songChartData:Null<SongChartData> = null;
if (path.ext != fileExt)
{
state.error('Failure', 'Given file extension ".${path.ext}" was not the requested extension ".$fileExt"');
return;
}
var loadedText = '';
switch (format)
{
case 'legacy':
var fnfLegacyData:Null<FNFLegacyData> = FNFLegacyImporter.parseLegacyDataRaw(content, path.toString());
if (fnfLegacyData == null)
{
state.error('Failure', 'Failed to parse FNF chart file (${selectedFile.name})');
state.error('Failure', 'Failed to parse FNF chart file (${path.file}.${path.ext})');
return;
}
var songMetadata:SongMetadata = FNFLegacyImporter.migrateMetadata(fnfLegacyData);
var songChartData:SongChartData = FNFLegacyImporter.migrateChartData(fnfLegacyData);
songMetadata = FNFLegacyImporter.migrateMetadata(fnfLegacyData);
songChartData = FNFLegacyImporter.migrateChartData(fnfLegacyData);
state.loadSong([Constants.DEFAULT_VARIATION => songMetadata], [Constants.DEFAULT_VARIATION => songChartData]);
loadedText = 'Loaded chart file';
dialog.hideDialog(DialogButton.APPLY);
state.success('Success', 'Loaded chart file (${selectedFile.name})');
case 'osumania':
var osuManiaData:Null<OsuManiaData> = OsuManiaImporter.parseOsuFile(content);
if (osuManiaData == null)
{
state.error('Failure', 'Failed to parse Osu!Mania beatmap file (${path.file}.${path.ext})');
return;
}
songMetadata = OsuManiaImporter.migrateMetadata(osuManiaData);
songChartData = OsuManiaImporter.migrateChartData(osuManiaData);
loadedText = 'Loaded beatmap file';
}
if (songMetadata == null || songMetadata == null)
{
state.error('Failure', 'Failed to load song (${path.file}.${path.ext})');
return;
}
state.loadSong([Constants.DEFAULT_VARIATION => songMetadata], [Constants.DEFAULT_VARIATION => songChartData]);
dialog.hideDialog(DialogButton.APPLY);
state.success('Success', '$loadedText (${path.file}.${path.ext})');
};
importBox.onClick = function(_) {
// TODO / BUG: File filtering not working on mac finder dialog, so we don't use it for now
Dialogs.openBinaryFile('Import Chart - ${prettyFormat}', #if !mac fileFilter ?? [] #else [] #end, function(selectedFile:SelectedFileInfo) {
if (selectedFile != null && selectedFile.bytes != null)
{
@:nullSafety(Off)
onFileSelected(selectedFile.fullPath, selectedFile.bytes.toString());
}
});
}
onDropFile = function(pathStr:String) {
var path:Path = new Path(pathStr);
var selectedFileText:String = FileUtil.readStringFromPath(path.toString());
var selectedFileData:Null<FNFLegacyData> = FNFLegacyImporter.parseLegacyDataRaw(selectedFileText, path.toString());
if (selectedFileData == null)
{
state.error('Failure', 'Failed to parse FNF chart file (${path.file}.${path.ext})');
return;
}
var songMetadata:SongMetadata = FNFLegacyImporter.migrateMetadata(selectedFileData);
var songChartData:SongChartData = FNFLegacyImporter.migrateChartData(selectedFileData);
state.loadSong([Constants.DEFAULT_VARIATION => songMetadata], [Constants.DEFAULT_VARIATION => songChartData]);
dialog.hideDialog(DialogButton.APPLY);
state.success('Success', 'Loaded chart file (${path.file}.${path.ext})');
var selectedFileText:String = FileUtil.readStringFromPath(pathStr);
onFileSelected(pathStr, selectedFileText);
};
state.addDropHandler({component: importBox, handler: onDropFile});
@ -1326,11 +1373,11 @@ class ChartEditorDialogHandler
{
var dialog:Null<Dialog> = Dialogs.messageBox("You are about to leave the editor without saving.\n\nAre you sure?", "Leave Editor",
MessageBoxType.TYPE_YESNO, true, function(button:DialogButton) {
state.isHaxeUIDialogOpen = false;
if (button == DialogButton.YES)
{
state.quitChartEditor();
}
state.isHaxeUIDialogOpen = false;
});
dialog.destroyOnClose = true;