Funkin/source/funkin/data/song/importer/FNFLegacyImporter.hx

202 lines
6.7 KiB
Haxe

package funkin.data.song.importer; // import is a reserved word dumbass
import funkin.data.song.SongData.SongMetadata;
import funkin.data.song.SongData.SongChartData;
import funkin.data.song.SongData.SongCharacterData;
import funkin.data.song.SongData.SongEventData;
import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongData.SongTimeChange;
import funkin.data.song.importer.FNFLegacyData;
import funkin.data.song.importer.FNFLegacyData.LegacyNoteSection;
class FNFLegacyImporter
{
public static function parseLegacyDataRaw(input:String, fileName:String = 'raw'):FNFLegacyData
{
var parser = new json2object.JsonParser<FNFLegacyData>();
parser.fromJson(input, fileName);
if (parser.errors.length > 0)
{
trace('[FNFLegacyImporter] Error parsing JSON data from ' + fileName + ':');
for (error in parser.errors)
DataError.printError(error);
return null;
}
return parser.value;
}
/**
* @param data The raw parsed JSON data to migrate, as a Dynamic.
* @param difficulty
* @return SongMetadata
*/
public static function migrateMetadata(songData:FNFLegacyData, difficulty:String = 'normal'):SongMetadata
{
trace('Migrating song metadata from FNF Legacy.');
var songMetadata:SongMetadata = new SongMetadata('Import', 'Kawai Sprite', 'default');
var hadError:Bool = false;
// Set generatedBy string for debugging.
songMetadata.generatedBy = 'Chart Editor Import (FNF Legacy)';
songMetadata.playData.stage = songData?.song?.stageDefault ?? 'mainStage';
songMetadata.songName = songData?.song?.song ?? 'Import';
songMetadata.playData.difficulties = [];
if (songData?.song?.notes != null)
{
switch (songData.song.notes)
{
case Left(notes):
// One difficulty of notes.
songMetadata.playData.difficulties.push(difficulty);
case Right(difficulties):
if (difficulties.easy != null) songMetadata.playData.difficulties.push('easy');
if (difficulties.normal != null) songMetadata.playData.difficulties.push('normal');
if (difficulties.hard != null) songMetadata.playData.difficulties.push('hard');
}
}
songMetadata.playData.songVariations = [];
songMetadata.timeChanges = rebuildTimeChanges(songData);
songMetadata.playData.characters = new SongCharacterData(songData?.song?.player1 ?? 'bf', 'gf', songData?.song?.player2 ?? 'dad', 'mom');
return songMetadata;
}
public static function migrateChartData(songData:FNFLegacyData, difficulty:String = 'normal'):SongChartData
{
trace('Migrating song chart data from FNF Legacy.');
var songChartData:SongChartData = new SongChartData([difficulty => 1.0], [], [difficulty => []]);
if (songData?.song?.notes != null)
{
switch (songData.song.notes)
{
case Left(notes):
// One difficulty of notes.
songChartData.notes.set(difficulty, migrateNoteSections(notes));
case Right(difficulties):
var baseDifficulty = null;
if (difficulties.easy != null) songChartData.notes.set('easy', migrateNoteSections(difficulties.easy));
if (difficulties.normal != null) songChartData.notes.set('normal', migrateNoteSections(difficulties.normal));
if (difficulties.hard != null) songChartData.notes.set('hard', migrateNoteSections(difficulties.hard));
}
}
// Import event data.
songChartData.events = rebuildEventData(songData);
switch (songData.song.speed)
{
case Left(speed):
// All difficulties will use the one scroll speed.
songChartData.scrollSpeed.set('default', speed);
case Right(speeds):
if (speeds.easy != null) songChartData.scrollSpeed.set('easy', speeds.easy);
if (speeds.normal != null) songChartData.scrollSpeed.set('normal', speeds.normal);
if (speeds.hard != null) songChartData.scrollSpeed.set('hard', speeds.hard);
}
return songChartData;
}
/**
* FNF Legacy doesn't have song events, but without them the song won't look right,
* so we insert camera events when the character changes.
*/
static function rebuildEventData(songData:FNFLegacyData):Array<SongEventData>
{
var result:Array<SongEventData> = [];
var noteSections = [];
switch (songData.song.notes)
{
case Left(notes):
// All difficulties will use the one scroll speed.
noteSections = notes;
case Right(difficulties):
if (difficulties.normal != null) noteSections = difficulties.normal;
if (difficulties.hard != null) noteSections = difficulties.normal;
if (difficulties.easy != null) noteSections = difficulties.normal;
}
if (noteSections == null || noteSections.length == 0) return result;
// Add camera events.
var lastSectionWasMustHit:Null<Bool> = null;
for (section in noteSections)
{
// Skip empty sections.
if (section.sectionNotes.length == 0) continue;
if (section.mustHitSection != lastSectionWasMustHit)
{
lastSectionWasMustHit = section.mustHitSection;
var firstNote:LegacyNote = section.sectionNotes[0];
result.push(new SongEventData(firstNote.time, 'FocusCamera', {char: section.mustHitSection ? 0 : 1}));
}
}
return result;
}
/**
* Port over time changes from FNF Legacy.
* If a section contains a BPM change, it will be applied at the timestamp of the first note in that section.
*/
static function rebuildTimeChanges(songData:FNFLegacyData):Array<SongTimeChange>
{
var result:Array<SongTimeChange> = [];
result.push(new SongTimeChange(0, songData?.song?.bpm ?? Constants.DEFAULT_BPM));
var noteSections = [];
switch (songData.song.notes)
{
case Left(notes):
// All difficulties will use the one scroll speed.
noteSections = notes;
case Right(difficulties):
if (difficulties.normal != null) noteSections = difficulties.normal;
if (difficulties.hard != null) noteSections = difficulties.normal;
if (difficulties.easy != null) noteSections = difficulties.normal;
}
if (noteSections == null || noteSections.length == 0) return result;
for (noteSection in noteSections)
{
if (noteSection.changeBPM ?? false)
{
var firstNote:LegacyNote = noteSection.sectionNotes[0];
if (firstNote != null) result.push(new SongTimeChange(firstNote.time, noteSection.bpm));
}
}
return result;
}
static function migrateNoteSections(input:Array<LegacyNoteSection>):Array<SongNoteData>
{
var result:Array<SongNoteData> = [];
for (section in input)
{
for (note in section.sectionNotes)
{
result.push(new SongNoteData(note.time, note.data, note.length, note.getKind()));
}
}
return result;
}
}