Files
crtr/Assets/Cryville/Crtr/ChartCompatibilityHandler.cs

124 lines
4.4 KiB
C#

using Cryville.Common;
using Cryville.Common.Pdt;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using System;
using System.Text.RegularExpressions;
namespace Cryville.Crtr {
public class ChartCompatibilityHandler : CustomCreationConverter<Chart> {
public static IMotionStringParser MotionStringParser { get; private set; }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
var obj = JToken.ReadFrom(reader);
MotionStringParser = obj["format"].ToObject<int>() switch {
2 => MotionStringParser2.Instance,
3 => MotionStringParser3.Instance,
_ => throw new FormatException("Unsupported chart format"),
};
return base.ReadJson(obj.CreateReader(), objectType, existingValue, serializer);
}
public override Chart Create(Type objectType) {
return new Chart();
}
public interface IMotionStringParser {
void Parse(string str, out Identifier name, out MotionNode node);
}
class MotionStringParser2 : IMotionStringParser {
static MotionStringParser2 _instance;
public static MotionStringParser2 Instance {
get {
_instance ??= new MotionStringParser2();
return _instance;
}
}
static readonly PdtFragmentInterpreter _itor = new();
static readonly PropOp _vecop = new VectorOp(v => _vecbuf = v);
static float[] _vecbuf;
public void Parse(string str, out Identifier name, out MotionNode node) {
Match m = Regex.Match(str, @"^(.+?)(#(\d+))?(@(.+?))?(\^(.+?))?(\*(.+?))?(:(.+))?$");
if (!m.Success) throw new ArgumentException("Invalid motion string format");
name = new Identifier(m.Groups[1].Value);
var registry = ChartPlayer.motionRegistry[name];
if (m.Groups[3].Success) {
short id = short.Parse(m.Groups[3].Value);
if (id < 0) throw new ArgumentException("Got negative motion node index");
Vec1 time = m.Groups[5].Success ? new Vec1(float.Parse(m.Groups[5].Value)) : null;
/*byte? trs = m.Groups[7].Success ? byte.Parse(m.Groups[7].Value) : null;
Vec1 rate = m.Groups[9].Success ? new Vec1(float.Parse(m.Groups[9].Value)) : null;*/
Vector value = m.Groups[11].Success ? Vector.Construct(registry.Type, ParseVector(m.Groups[11].Value)) : null;
node = new MotionNode {
Id = id,
Time = time,
Value = value
};
}
else {
node = new MotionNode {
Value = Vector.Construct(registry.Type, ParseVector(m.Groups[11].Value))
};
}
}
float[] ParseVector(string str) {
var comps = str.Split(',');
for (int i = 0; i < comps.Length; i++) {
if (comps[i] == "") comps[i] = "0";
}
_itor.SetSource(string.Format("({0});", string.Join(',', comps)));
PdtEvaluator.Instance.Evaluate(_vecop, _itor.GetExp());
return _vecbuf;
}
}
class MotionStringParser3 : IMotionStringParser {
static MotionStringParser3 _instance;
public static MotionStringParser3 Instance {
get {
_instance ??= new MotionStringParser3();
return _instance;
}
}
static readonly PdtFragmentInterpreter _itor = new();
static readonly PropOp _vecop = new VectorOp(v => _vecbuf = v);
static float[] _vecbuf;
public void Parse(string str, out Identifier name, out MotionNode node) {
Match m = Regex.Match(str, @"^([0-9A-Za-z_]+)(#(\d+))?(.+)?$");
if (!m.Success) throw new ArgumentException("Invalid motion string format");
name = new Identifier(m.Groups[1].Value);
node = new MotionNode();
if (m.Groups[3].Success) {
short id = short.Parse(m.Groups[3].Value);
if (id < 0) throw new ArgumentException("Got negative motion node index");
node.Id = id;
}
if (m.Groups[4].Success) {
_itor.SetSource(m.Groups[4].Value);
while (_itor.Position < _itor.Source.Length) {
var c = _itor.GetChar();
var exp = _itor.GetExp();
switch (c) {
case '@':
PdtEvaluator.Instance.Evaluate(_vecop, exp);
node.Time = new Vec1(_vecbuf);
break;
case '~':
PdtEvaluator.Instance.Evaluate(_vecop, exp);
node.EndTime = new Vec1(_vecbuf);
break;
case '^':
node.Transition = exp;
break;
case ':':
PdtEvaluator.Instance.Evaluate(_vecop, exp);
node.Value = Vector.Construct(ChartPlayer.motionRegistry[name].Type, _vecbuf);
break;
default:
throw new ArgumentException("Invalid motion string format");
}
}
}
else node.Reset = true;
}
}
}
}