Funkin/source/funkin/data/DataParse.hx

226 lines
7 KiB
Haxe
Raw Normal View History

2023-09-08 17:45:47 -04:00
package funkin.data;
import funkin.data.song.importer.FNFLegacyData.LegacyNote;
import funkin.data.song.importer.FNFLegacyData.LegacyNoteData;
2023-10-18 01:02:10 -04:00
import funkin.data.song.importer.FNFLegacyData.LegacyNoteSection;
import funkin.data.song.importer.FNFLegacyData.LegacyScrollSpeeds;
2023-10-18 01:02:10 -04:00
import haxe.ds.Either;
import hxjsonast.Json;
import hxjsonast.Json.JObjectField;
import hxjsonast.Tools;
import thx.semver.Version;
import thx.semver.VersionRule;
2023-09-08 17:45:47 -04:00
/**
* `json2object` has an annotation `@:jcustomparse` which allows for mutation of parsed values.
*
* It also allows for validation, since throwing an error in this function will cause the issue to be properly caught.
* Parsing will fail and `parser.errors` will contain the thrown exception.
*
* Functions must be of the signature `(hxjsonast.Json, String) -> T`, where the String is the property name and `T` is the type of the property.
*/
class DataParse
{
/**
* `@:jcustomparse(funkin.data.DataParse.stringNotEmpty)`
* @param json Contains the `pos` and `value` of the property.
* @param name The name of the property.
2023-10-18 01:02:10 -04:00
* @throws Error If the property is not a string or is empty.
* @return The string value.
2023-09-08 17:45:47 -04:00
*/
public static function stringNotEmpty(json:Json, name:String):String
{
switch (json.value)
{
case JString(s):
if (s == "") throw 'Expected property $name to be non-empty.';
return s;
default:
throw 'Expected property $name to be a string, but it was ${json.value}.';
}
}
2023-10-18 01:02:10 -04:00
/**
* `@:jcustomparse(funkin.data.DataParse.semverVersion)`
* @param json Contains the `pos` and `value` of the property.
* @param name The name of the property.
* @return The value of the property as a `thx.semver.Version`.
*/
public static function semverVersion(json:Json, name:String):Version
{
switch (json.value)
{
case JString(s):
if (s == "") throw 'Expected version property $name to be non-empty.';
return s;
default:
throw 'Expected version property $name to be a string, but it was ${json.value}.';
}
}
/**
* `@:jcustomparse(funkin.data.DataParse.semverVersionRule)`
* @param json Contains the `pos` and `value` of the property.
* @param name The name of the property.
* @return The value of the property as a `thx.semver.VersionRule`.
*/
public static function semverVersionRule(json:Json, name:String):VersionRule
{
switch (json.value)
{
case JString(s):
if (s == "") throw 'Expected version rule property $name to be non-empty.';
return s;
default:
throw 'Expected version rule property $name to be a string, but it was ${json.value}.';
}
}
2023-09-08 17:45:47 -04:00
/**
* Parser which outputs a Dynamic value, either a object or something else.
* @param json
* @param name
* @return The value of the property.
*/
public static function dynamicValue(json:Json, name:String):Dynamic
{
return Tools.getValue(json);
2023-09-08 17:45:47 -04:00
}
/**
* Parser which outputs a `Either<Array<LegacyNoteSection>, LegacyNoteData>`.
* Used by the FNF legacy JSON importer.
2023-09-08 17:45:47 -04:00
*/
public static function eitherLegacyNoteData(json:Json, name:String):Either<Array<LegacyNoteSection>, LegacyNoteData>
2023-09-08 17:45:47 -04:00
{
switch (json.value)
{
case JArray(values):
return Either.Left(legacyNoteSectionArray(json, name));
2023-09-08 17:45:47 -04:00
case JObject(fields):
return Either.Right(cast Tools.getValue(json));
2023-09-08 17:45:47 -04:00
default:
throw 'Expected property $name to be note data, but it was ${json.value}.';
2023-09-08 17:45:47 -04:00
}
}
/**
* Parser which outputs a `Either<Float, Array<Float>>`.
*/
public static function eitherFloatOrFloats(json:Json, name:String):Null<Either<Float, Array<Float>>>
{
switch (json.value)
{
case JNumber(f):
return Either.Left(Std.parseFloat(f));
case JArray(fields):
return Either.Right(fields.map((field) -> cast Tools.getValue(field)));
default:
throw 'Expected property $name to be one or multiple floats, but it was ${json.value}.';
}
}
/**
* Parser which outputs a `Either<Float, LegacyScrollSpeeds>`.
* Used by the FNF legacy JSON importer.
*/
public static function eitherLegacyScrollSpeeds(json:Json, name:String):Either<Float, LegacyScrollSpeeds>
2023-09-08 17:45:47 -04:00
{
switch (json.value)
2023-09-08 17:45:47 -04:00
{
case JNumber(f):
return Either.Left(Std.parseFloat(f));
case JObject(fields):
return Either.Right(cast Tools.getValue(json));
default:
throw 'Expected property $name to be scroll speeds, but it was ${json.value}.';
2023-09-08 17:45:47 -04:00
}
}
/**
* Array of JSON fields `[{key, value}, {key, value}]` to a Dynamic object `{key:value, key:value}`.
* @param fields
* @return Dynamic
*/
static function jsonFieldsToDynamicObject(fields:Array<JObjectField>):Dynamic
{
var result:Dynamic = {};
for (field in fields)
{
Reflect.setField(result, field.name, Tools.getValue(field.value));
2023-09-08 17:45:47 -04:00
}
return result;
}
/**
* Array of JSON elements `[Json, Json, Json]` to a Dynamic array `[String, Object, Int, Array]`
* @param jsons
* @return Array<Dynamic>
*/
static function jsonArrayToDynamicArray(jsons:Array<Json>):Array<Null<Dynamic>>
{
return [for (json in jsons) Tools.getValue(json)];
}
static function legacyNoteSectionArray(json:Json, name:String):Array<LegacyNoteSection>
{
switch (json.value)
{
case JArray(values):
return [for (value in values) legacyNoteSection(value, name)];
default:
throw 'Expected property to be an array, but it was ${json.value}.';
}
}
static function legacyNoteSection(json:Json, name:String):LegacyNoteSection
{
switch (json.value)
{
case JObject(fields):
return cast Tools.getValue(json);
default:
throw 'Expected property $name to be an object, but it was ${json.value}.';
}
}
public static function legacyNoteData(json:Json, name:String):LegacyNoteData
{
switch (json.value)
{
case JObject(fields):
return cast Tools.getValue(json);
default:
throw 'Expected property $name to be an object, but it was ${json.value}.';
}
}
public static function legacyNotes(json:Json, name:String):Array<LegacyNote>
{
switch (json.value)
{
case JArray(values):
return [for (value in values) legacyNote(value, name)];
default:
throw 'Expected property $name to be an array of notes, but it was ${json.value}.';
}
}
public static function legacyNote(json:Json, name:String):LegacyNote
{
switch (json.value)
{
case JArray(values):
// var time:Null<Float> = values[0] == null ? null : Tools.getValue(values[0]);
// var data:Null<Int> = values[1] == null ? null : Tools.getValue(values[1]);
// var length:Null<Float> = values[2] == null ? null : Tools.getValue(values[2]);
// var alt:Null<Bool> = values[3] == null ? null : Tools.getValue(values[3]);
// return new LegacyNote(time, data, length, alt);
return null;
default:
throw 'Expected property $name to be a note, but it was ${json.value}.';
}
2023-09-08 17:45:47 -04:00
}
}