71 Commits

Author SHA1 Message Date
054b17811d Preserve Convert. 2023-03-22 20:04:32 +08:00
94d5f7f82e Preserve BinaryReader and StreamReader. 2023-03-19 14:13:36 +08:00
0e4445e52b Detects assembly references and determines loading order automatically. 2023-03-18 13:46:13 +08:00
ed2c216cf4 Implement extension. 2023-03-17 18:03:59 +08:00
609645c108 Import HybridCLR. 2023-03-17 18:03:22 +08:00
d280d27a0a Improve error log on resource import failure. 2023-03-17 18:00:46 +08:00
5b727065f3 Adapt conversion for RawChartResource. 2023-03-17 17:59:59 +08:00
7aa2577059 Move UMG importers to extensions. 2023-03-17 17:58:45 +08:00
c1b7e9ab55 Separate extension classes into external assembly. 2023-03-17 17:56:12 +08:00
99384397ed Remove extensions. 2023-03-17 17:49:54 +08:00
a2391aeb22 Cleanup. (Skin editor) 2023-03-15 15:44:32 +08:00
2207c80951 Fix incorrect style for single-statement blocks in expression. 2023-03-15 15:43:30 +08:00
bf578d7cb9 Make statements in expression not sortable. 2023-03-15 15:41:02 +08:00
0bc57c368f Improve scaling logic of popup list in skin editor.. 2023-03-15 15:39:31 +08:00
b64f85aaa2 Add category for expression list in skin editor. 2023-03-15 15:36:45 +08:00
93fa2f2d7e Sync some constants, operators, and functions for skin editor. (3) 2023-03-15 15:34:33 +08:00
d72216de8b Sync some constants, operators, and functions for skin editor. (2) 2023-03-15 10:02:09 +08:00
df5133a91a Sync some constants, operators, and functions for skin editor. 2023-03-15 00:46:19 +08:00
88b959a118 Modify border color of expression statement. 2023-03-15 00:44:44 +08:00
310bf91fbd Always collapses delete button in skin editor. 2023-03-15 00:43:49 +08:00
699f47f98d Fix the height of popup list in skin editor. 2023-03-15 00:43:07 +08:00
24e881b138 Sync component and property list for skin editor. 2023-03-14 23:32:35 +08:00
2eef1b5c4e Update translations for skin editor. 2023-03-14 17:27:05 +08:00
c18ceb50d4 Update skin structure in editor. 2023-03-14 17:24:32 +08:00
27ca1a7292 Implement expression in skin editor. 2023-03-14 17:22:48 +08:00
dc59176eac Fix encoding for skin editor. 2023-03-14 16:59:37 +08:00
aec7470ff8 Optimize css for skin editor. 2023-03-14 16:58:03 +08:00
983cba6843 Detects user chart path for Malody chart finder. 2023-03-11 21:41:54 +08:00
871782e73f Remove potentially buggy syncing logic. 2023-03-11 21:41:22 +08:00
9aaa96fe10 Optimize GC for input proxy. 2023-03-11 21:40:34 +08:00
22190a29c1 Fix incorrect array length for vector operator. 2023-03-11 21:39:26 +08:00
613ca467d0 Fix offset error on function anim. 2023-03-09 22:02:34 +08:00
536a3066b2 Fix array operator creating array of type error. 2023-03-09 16:27:16 +08:00
43488cd002 Add "emit effect on self" annotation. 2023-03-09 16:26:43 +08:00
a11ccbd39c Cleanup variable name. 2023-03-09 11:41:25 +08:00
76df4929a7 Implement set variable annotation. 2023-03-09 11:38:49 +08:00
505b826627 Move up non-generic collection types and add debug view. 2023-03-09 11:37:23 +08:00
26a8675922 Make animation time of subspan relative. 2023-03-09 10:07:24 +08:00
b89e1f983e Code cleanup. 2023-03-08 20:36:18 +08:00
717e77b47e Fix inaccurate beat snapping for Quaver charts. 2023-03-06 08:16:42 +08:00
2f10c79dee Adjust offset for osu chart converter. 2023-03-05 23:06:18 +08:00
55f7790f89 Fix potential error on vector property source. 2023-03-05 23:05:48 +08:00
ae5f4a8c16 (Amend to 1851bd3c) 2023-03-05 21:53:30 +08:00
1851bd3c54 Fix inaccurate beat snapping for osu charts. Fix osu charts with too low BPM not able to be imported. 2023-03-05 20:40:02 +08:00
6d74685cb7 Add anim.iteration, anim.direction, and anim.delay properties. 2023-03-03 15:10:52 +08:00
03fd7f6d01 Add inf constant. 2023-03-03 15:09:08 +08:00
28c878f3e5 Code cleanup. 2023-03-03 13:34:12 +08:00
7736eba14d Supplement generic PairCollection. 2023-03-03 13:26:11 +08:00
2e54b38d2b Supplement Insert methods for pair list. 2023-03-03 11:49:32 +08:00
da60dc0903 Fix potential order inconsistency of element and property lists. 2023-03-03 11:45:16 +08:00
215f72b3b5 Add cubic bezier functions. 2023-03-02 10:40:18 +08:00
a93c081dd8 Add easing function parameter to anim function. 2023-03-02 10:39:39 +08:00
456782930a Fix incorrect zero format specifier range. 2023-03-01 17:55:09 +08:00
ba3cbbd64c Add state-based effect. 2023-03-01 00:37:22 +08:00
dfc3e9ca06 Refactor OpenedAnchorName in ISkinnableGroup. (Amend) 2023-03-01 00:36:09 +08:00
f567a2b78e Fix error on setting anim.name to null. 2023-03-01 00:35:19 +08:00
c7e7bd8a77 Add Identifier.Empty. 2023-03-01 00:33:28 +08:00
67720fd0e1 Refactor OpenedAnchorName in ISkinnableGroup. 2023-03-01 00:33:01 +08:00
1a30149942 Prevents infinite propagation on input proxy and judge. 2023-02-28 13:47:38 +08:00
a755cc13bd Implement animation. 2023-02-27 00:17:14 +08:00
9a51cf1b56 Add rewind and tick mechanism for skin component. 2023-02-27 00:16:44 +08:00
256c656e9c Fix once annotation not working on certain properties. 2023-02-27 00:13:38 +08:00
2ac7f05316 Add contextual function anim. 2023-02-27 00:11:37 +08:00
28d46dbabe Disallows multiple meshes in one object. 2023-02-26 16:24:58 +08:00
18b3e0bc84 Code cleanup. 2023-02-26 16:24:28 +08:00
23f2e7c1f2 Fix multiple flags ignored in enum property operator. 2023-02-26 16:23:17 +08:00
998713b41f Pull some init logic in enum property operator to static constructor. 2023-02-26 16:22:37 +08:00
3561354231 Implement execute once annotation on properties. 2023-02-26 16:21:33 +08:00
0cccb170c1 Support extra annotations on skin property key. Code cleanup. 2023-02-26 16:20:25 +08:00
d2480b8a6f Update global suppressions. 2023-02-26 16:16:46 +08:00
6837d3f7ee Make the group in skin container readonly. 2023-02-24 15:25:04 +08:00
577 changed files with 2050 additions and 29990 deletions

1
.gitignore vendored
View File

@@ -68,3 +68,4 @@ crashlytics-build.properties
/UserSettings
/*.zip
*.lnk
/HybridCLRData

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5335612e48e4c3947808c99fb99411d5
guid: c4ef48e4a4983de4e9c31483df2a918e
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6823ead66b33bc048bbad48719feb25d
guid: 9ec674235c0dd6744af2dab2b58dd53c
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Cryville.Common.Collections.Generic {
public interface IPairList<TKey, TValue> : IList<KeyValuePair<TKey, TValue>> {
void Add(TKey key, TValue value);
void Insert(int index, TKey key, TValue value);
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f5b3f3294f679f14f8ec1195b0def630
guid: 73fb17b484b343242bcce27c15ed7d44
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Cryville.Common.Collections.Generic {
[DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(PairCollectionDebugView<,>))]
public struct PairCollection<TKey, TValue> : IDisposable {
public void Dispose() { }
readonly IPairList<TKey, TValue> _pairList;
readonly IDictionary<TKey, TValue> _dictionary;
public PairCollection(object collection) : this() {
var type = collection.GetType();
if (typeof(IPairList<TKey, TValue>).IsAssignableFrom(type)) _pairList = (IPairList<TKey, TValue>)collection;
else if (typeof(IDictionary<TKey, TValue>).IsAssignableFrom(type)) _dictionary = (IDictionary<TKey, TValue>)collection;
else throw new ArgumentException("Parameter is not a pair collection");
}
public int Count {
get {
if (_pairList != null) return _pairList.Count;
else return _dictionary.Count;
}
}
public void Add(TKey key, TValue value) {
if (_pairList != null) _pairList.Add(key, value);
else _dictionary.Add(key, value);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index) {
if (_pairList != null) _pairList.CopyTo(array, index);
else _dictionary.CopyTo(array, index);
}
}
internal class PairCollectionDebugView<TKey, TValue> {
readonly PairCollection<TKey, TValue> _self;
public PairCollectionDebugView(PairCollection<TKey, TValue> self) {
_self = self;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public KeyValuePair<TKey, TValue>[] Items {
get {
KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[_self.Count];
_self.CopyTo(array, 0);
return array;
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 23377bf2926d93a4b8e3f3ab6040c7f2
guid: 2517e8f040bd36f46948e5fafaf5335c
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Cryville.Common.Collections.Generic {
[DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(PairListDebugView<,>))]
public class PairList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IPairList<TKey, TValue>, IPairList {
public void Add(TKey key, TValue value) {
Add(new KeyValuePair<TKey, TValue>(key, value));
}
public void Add(object key, object value) {
try {
Add((TKey)key, (TValue)value);
}
catch (InvalidCastException) {
throw new ArgumentException("Wrong key type or value type");
}
}
public void Insert(int index, TKey key, TValue value) {
Insert(index, new KeyValuePair<TKey, TValue>(key, value));
}
public void Insert(int index, object key, object value) {
try {
Insert(index, (TKey)key, (TValue)value);
}
catch (InvalidCastException) {
throw new ArgumentException("Wrong key type or value type");
}
}
}
internal class PairListDebugView<TKey, TValue> {
readonly PairList<TKey, TValue> _self;
public PairListDebugView(PairList<TKey, TValue> self) {
_self = self;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public KeyValuePair<TKey, TValue>[] Items {
get {
KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[_self.Count];
_self.CopyTo(array, 0);
return array;
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4ffe72fef6ebb9e4da3571b4117f0d6d
guid: d9ed5ea8b7b1a934287e7ec5971166c0
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,8 @@
using System.Collections;
namespace Cryville.Common.Collections {
public interface IPairList : IList {
void Add(object key, object value);
void Insert(int index, object key, object value);
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: e3c5a8bf05d5e284ba498e91cb0dd35e
guid: 046617672d437de4ab7e644a55defd3b
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace Cryville.Common.Collections {
[DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(PairCollectionDebugView))]
public struct PairCollection : IDisposable {
public void Dispose() { }
readonly IPairList _pairList;
readonly IDictionary _dictionary;
public PairCollection(object collection) : this() {
var type = collection.GetType();
if (typeof(IPairList).IsAssignableFrom(type)) _pairList = (IPairList)collection;
else if (typeof(IDictionary).IsAssignableFrom(type)) _dictionary = (IDictionary)collection;
else throw new ArgumentException("Parameter is not a pair collection");
}
public int Count {
get {
if (_pairList != null) return _pairList.Count;
else return _dictionary.Count;
}
}
public void Add(object key, object value) {
if (_pairList != null) _pairList.Add(key, value);
else _dictionary.Add(key, value);
}
public void CopyTo(KeyValuePair<object, object>[] array, int index) {
if (_pairList != null) _pairList.CopyTo(array, index);
else _dictionary.CopyTo(array, index);
}
}
internal class PairCollectionDebugView {
readonly PairCollection _self;
public PairCollectionDebugView(PairCollection self) {
_self = self;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public KeyValuePair<object, object>[] Items {
get {
KeyValuePair<object, object>[] array = new KeyValuePair<object, object>[_self.Count];
_self.CopyTo(array, 0);
return array;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1f87dfb8f6a1f5640b6deae741cd715c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Diagnostics;
namespace Cryville.Common.Collections {
[DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(PairListDebugView))]
public class PairList : List<KeyValuePair<object, object>>, IPairList {
public void Add(object key, object value) {
Add(new KeyValuePair<object, object>(key, value));
}
public void Insert(int index, object key, object value) {
Insert(index, new KeyValuePair<object, object>(key, value));
}
}
internal class PairListDebugView {
readonly PairList _self;
public PairListDebugView(PairList self) {
_self = self;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public KeyValuePair<object, object>[] Items {
get {
KeyValuePair<object, object>[] array = new KeyValuePair<object, object>[_self.Count];
_self.CopyTo(array, 0);
return array;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57fc9f037c1fda5449e2a365a835c82c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -45,7 +45,7 @@ namespace Cryville.Common.Font {
return tableDirectoryOffsets;
}
public override TableDirectory GetSubTable(UInt32 item) {
var i = (UInt32)item;
var i = item;
return new TableDirectory(Reader, i);
}
}

View File

@@ -2,6 +2,7 @@
namespace Cryville.Common {
public struct Identifier : IEquatable<Identifier> {
public static Identifier Empty = new Identifier(0);
public int Key { get; private set; }
public object Name { get { return IdentifierManager.SharedInstance.Retrieve(Key); } }
public Identifier(int key) {

View File

@@ -0,0 +1,44 @@
using SMath = System.Math;
namespace Cryville.Common.Math {
// Ported from https://github.com/arian/cubic-bezier/blob/master/index.js
public static class CubicBezier {
public static float Evaluate(float t, float x1, float y1, float x2, float y2, float epsilon) {
float x = t, t0, t1, t2, tx, d2, i;
for (t2 = x, i = 0; i < 8; i++) {
tx = CurveX(t2, x1, x2) - x;
if (SMath.Abs(tx) < epsilon) return CurveY(t2, y1, y2);
d2 = DerivativeCurveX(t2, x1, x2);
if (SMath.Abs(d2) < 1e-6) break;
t2 -= tx / d2;
}
t0 = 0; t1 = 1; t2 = x;
if (t2 < t0) return CurveY(t0, y1, y2);
if (t2 > t1) return CurveY(t1, y1, y2);
while (t0 < t1) {
tx = CurveX(t2, x1, x2);
if (SMath.Abs(tx - x) < epsilon) return CurveY(t2, y1, y2);
if (x > tx) t0 = t2;
else t1 = t2;
t2 = (t1 - t0) * .5f + t0;
}
return CurveY(t2, y1, y2);
}
static float CurveX(float t, float x1, float x2) {
float v = 1 - t;
return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t;
}
static float CurveY(float t, float y1, float y2) {
float v = 1 - t;
return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t;
}
static float DerivativeCurveX(float t, float x1, float x2) {
float v = 1 - t;
return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (-t * t * t + 2 * v * t) * x2;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17dd6f775fc965f43960da7166b55b87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -12,12 +12,12 @@ namespace Cryville.Common.Math {
/// <param name="error">The error.</param>
/// <param name="n">The numerator.</param>
/// <param name="d">The denominator.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="value" /> is less than 0 or <paramref name="error" /> is not greater than 0 or not less than 1.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="value" /> is less than 0 or <paramref name="error" /> is not greater than 0 or greater than 1.</exception>
public static void ToFraction(double value, double error, out int n, out int d) {
if (value < 0.0)
throw new ArgumentOutOfRangeException("value", "Must be >= 0.");
if (error <= 0.0 || error >= 1.0)
throw new ArgumentOutOfRangeException("accuracy", "Must be > 0 and < 1.");
if (error <= 0.0 || error > 1.0)
throw new ArgumentOutOfRangeException("error", "Must be > 0 and <= 1.");
int num = (int)System.Math.Floor(value);
value -= num;

View File

@@ -5,7 +5,7 @@ namespace Cryville.Common.Pdt {
/// Indicates that the attributed member is an element list.
/// </summary>
/// <remarks>
/// <para>An element list is a <see cref="System.Collections.IDictionary" /> that represents a collection of PDT elements. There must be at most one element list in a class.</para>
/// <para>An element list is a <see cref="System.Collections.IDictionary" /> or <see cref="Cryville.Common.Collections.Generic.IPairList" /> that represents a collection of PDT elements. There must be at most one element list in a class.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ElementListAttribute : Attribute { }
@@ -14,7 +14,7 @@ namespace Cryville.Common.Pdt {
/// Indicates that the attributed member is a property list.
/// </summary>
/// <remarks>
/// <para>A property list is a <see cref="System.Collections.IDictionary" /> that represents a collection of PDT properties. There must be at most one property list in a class.</para>
/// <para>A property list is a <see cref="System.Collections.IDictionary" /> or <see cref="Cryville.Common.Collections.Generic.IPairList" /> that represents a collection of PDT properties. There must be at most one property list in a class.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class PropertyListAttribute : Attribute { }

View File

@@ -92,7 +92,7 @@ namespace Cryville.Common.Pdt {
}
}
public partial class PdtInterpreter {
readonly static Dictionary<char, int> OP_PRIORITY = new Dictionary<char, int> {
static readonly Dictionary<char, int> OP_PRIORITY = new Dictionary<char, int> {
{ '@', 7 },
{ '*', 6 }, { '/', 6 }, { '%', 6 },
{ '+', 5 }, { '-', 5 },
@@ -103,7 +103,7 @@ namespace Cryville.Common.Pdt {
{ ',', 0 },
{ '$', -1 },
};
readonly static Dictionary<char, int> OP_TYPE = new Dictionary<char, int> {
static readonly Dictionary<char, int> OP_TYPE = new Dictionary<char, int> {
{ '@', 0 },
{ '*', 0 }, { '/', 0 }, { '%', 0 },
{ '+', 0 }, { '-', 0 },
@@ -115,7 +115,7 @@ namespace Cryville.Common.Pdt {
{ '$', -1 },
};
readonly static PdtExpression _emptyexp;
static readonly PdtExpression _emptyexp;
static PdtInterpreter() {
var ins = new LinkedList<PdtInstruction>();
ins.AddLast(new PdtInstruction.PushConstant(
@@ -143,7 +143,7 @@ namespace Cryville.Common.Pdt {
public override string ToString() {
return string.Format("0x{0:x4}: {1}", Type, Value);
}
public readonly static PdtExpToken EmptyOperator = new PdtExpToken {
public static readonly PdtExpToken EmptyOperator = new PdtExpToken {
Type = 0x0080,
Value = "$",
};

View File

@@ -6,30 +6,30 @@
/// <summary>
/// Error type.
/// </summary>
public readonly static int Error = 0x00525245;
public const int Error = 0x00525245;
/// <summary>
/// Array of a same variable-length type, with a suffix indicating the element count and the element type.
/// </summary>
public readonly static int Array = 0x00525241;
public const int Array = 0x00525241;
/// <summary>
/// Null type.
/// </summary>
public readonly static int Null = 0x4c4c554e;
public const int Null = 0x4c4c554e;
/// <summary>
/// IEEE 754 32-bit floating-point number.
/// </summary>
public readonly static int Number = 0x004d554e;
public const int Number = 0x004d554e;
/// <summary>
/// A sequence of UTF-16 code units, with a prefix indicating the number of the code units.
/// </summary>
public readonly static int String = 0x00525453;
public const int String = 0x00525453;
/// <summary>
/// A sequence of UTF-16 code units, with a prefix indicating the number of the code units, representing the name of an undefined variable.
/// </summary>
public readonly static int Undefined = 0x00444e55;
public const int Undefined = 0x00444e55;
/// <summary>
/// Vector of a same constant-length type, with a suffix indicating the element type.
/// </summary>
public readonly static int Vector = 0x00434556;
public const int Vector = 0x00434556;
}
}

View File

@@ -1,4 +1,5 @@
using System;
using Cryville.Common.Collections;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -27,7 +28,7 @@ namespace Cryville.Common.Pdt {
/// <item><term><c>0x1000</c></term><description>End of Key</description></item>
/// </list>
/// </remarks>
readonly static int[] cm = new int[] {
static readonly int[] cm = new int[] {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -255,13 +256,13 @@ namespace Cryville.Common.Pdt {
if (prop == null) throw new MissingMemberException(string.Format("The property \"{0}\" is not found", pkey));
Type ptype = ReflectionHelper.GetMemberType(prop);
if (flag) {
if (!typeof(IDictionary).IsAssignableFrom(ptype))
throw new InvalidOperationException("Internal error: Element list is not a dictionary");
var ktype = ptype.GetGenericArguments()[0];
var vtype = ptype.GetGenericArguments()[1];
object key = _binder.ChangeType(pkey, ktype, null);
object value = InterpretObject(vtype);
((IDictionary)ReflectionHelper.GetValue(prop, result)).Add(key, value);
using (var collection = new PairCollection(ReflectionHelper.GetValue(prop, result))) {
var ktype = ptype.GetGenericArguments()[0];
var vtype = ptype.GetGenericArguments()[1];
object key = _binder.ChangeType(pkey, ktype, null);
object value = InterpretObject(vtype);
collection.Add(key, value);
}
}
else ReflectionHelper.SetValue(prop, result, InterpretObject(ptype));
}
@@ -284,13 +285,13 @@ namespace Cryville.Common.Pdt {
if (prop == null) throw new MissingMemberException(string.Format("The property \"{0}\" is not found", pkey));
var ptype = ReflectionHelper.GetMemberType(prop);
if (flag) {
if (!typeof(IDictionary).IsAssignableFrom(ptype))
throw new InvalidOperationException("Internal error: Property list is not a dictionary");
var ktype = ptype.GetGenericArguments()[0];
var vtype = ptype.GetGenericArguments()[1];
object key = _binder.ChangeType(pkey, ktype, null);
object value = _binder.ChangeType(exp, vtype, null);
((IDictionary)ReflectionHelper.GetValue(prop, result)).Add(key, value);
using (var collection = new PairCollection(ReflectionHelper.GetValue(prop, result))) {
var ktype = ptype.GetGenericArguments()[0];
var vtype = ptype.GetGenericArguments()[1];
object key = _binder.ChangeType(pkey, ktype, null);
object value = _binder.ChangeType(exp, vtype, null);
collection.Add(key, value);
}
}
else {
object value = _binder.ChangeType(exp, ptype, null);

View File

@@ -4,7 +4,7 @@ namespace Cryville.Common.Pdt {
/// <summary>
/// PDT operator.
/// </summary>
public unsafe abstract class PdtOperator {
public abstract unsafe class PdtOperator {
byte* _prmem;
int _loadindex;
readonly PdtVariableMemory[] _operands;

View File

@@ -28,8 +28,8 @@ namespace Cryville.Common.Unity {
set { m_filter = value; }
}
public Dictionary<string, string> m_presetPaths = new Dictionary<string, string>();
public Dictionary<string, string> PresetPaths {
public IReadOnlyDictionary<string, string> m_presetPaths = new Dictionary<string, string>();
public IReadOnlyDictionary<string, string> PresetPaths {
get { return m_presetPaths; }
set { m_presetPaths = value; }
}

View File

@@ -3,6 +3,6 @@ using UnityEngine;
namespace Cryville.Common.Unity.UI {
public abstract class SetParameterBehaviour : StateMachineBehaviour {
[SerializeField] protected string m_name;
public override abstract void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex);
public abstract override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex);
}
}

View File

@@ -1,8 +0,0 @@
using System.Collections.Generic;
namespace Cryville.Crtr.Browsing {
public abstract class ExtensionInterface {
public abstract IEnumerable<ResourceConverter> GetResourceConverters();
public abstract IEnumerable<LocalResourceFinder> GetResourceFinders();
}
}

View File

@@ -0,0 +1,138 @@
using Cryville.Common;
using Cryville.Crtr.Extension;
using Mono.Cecil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace Cryville.Crtr.Browsing {
internal static class ExtensionManager {
static readonly Dictionary<string, List<ResourceConverter>> _converters
= new Dictionary<string, List<ResourceConverter>>();
public static IEnumerable<string> GetSupportedFormats() {
return _converters.Keys;
}
public static bool TryGetConverters(string extension, out IEnumerable<ResourceConverter> converters) {
List<ResourceConverter> outResult;
bool result = _converters.TryGetValue(extension, out outResult);
converters = outResult;
return result;
}
static readonly Dictionary<string, string> _localRes
= new Dictionary<string, string>();
public static IReadOnlyDictionary<string, string> GetLocalResourcePaths() {
return _localRes;
}
public static void Init(string rootPath) {
LoadExtension(typeof(Extensions.Umg.Extension));
var asms = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToHashSet();
var modules = new Queue<ModuleItem>();
var extensionDir = new DirectoryInfo(Path.Combine(rootPath, "extensions"));
if (extensionDir.Exists) {
foreach (var extension in extensionDir.EnumerateFiles("*.dll")) {
try {
modules.Enqueue(new ModuleItem(extension.OpenRead()));
}
catch (Exception ex) {
Logger.Log("main", 4, "Extension", "Failed to load DLL {0}: {1}", extension, ex);
}
}
}
int refCounter = 0;
while (modules.Count > 0 && refCounter < modules.Count) {
var module = modules.Dequeue();
bool flag = false;
foreach (var reference in module.Definition.AssemblyReferences) {
if (!asms.Contains(reference.Name)) {
flag = true;
break;
}
}
if (flag) {
modules.Enqueue(module);
refCounter++;
}
else {
try {
var stream = module.Stream;
stream.Seek(0, SeekOrigin.Begin);
var buf = new byte[stream.Length];
stream.Read(buf, 0, buf.Length);
var asm = Assembly.Load(buf);
if (asm == null) throw new TypeLoadException("Failed to load the module");
asms.Add(asm.GetName().Name);
foreach (var type in asm.GetTypes()) {
if (typeof(ExtensionInterface).IsAssignableFrom(type)) {
LoadExtension(type);
}
}
Logger.Log("main", 1, "Extension", "Loaded module {0}", module.Definition.Name);
}
catch (Exception ex) {
Logger.Log("main", 4, "Extension", "An error occured while trying to load module {0}: {1}", module.Definition.Name, ex);
}
finally {
module.Definition.Dispose();
module.Stream.Dispose();
refCounter = 0;
}
}
}
var missingList = new List<string>();
while (modules.Count > 0) {
missingList.Clear();
var module = modules.Dequeue();
foreach (var reference in module.Definition.AssemblyReferences) {
if (!asms.Contains(reference.Name)) {
missingList.Add(reference.Name);
}
}
Logger.Log("main", 4, "Extension", "Could not load the module {0} because the following dependencies were missing: {1}", module.Definition.Name, missingList.Aggregate((current, next) => current + ", " + next));
module.Definition.Dispose();
module.Stream.Dispose();
}
}
struct ModuleItem {
public ModuleDefinition Definition { get; set; }
public FileStream Stream { get; set; }
public ModuleItem(FileStream stream) {
Stream = stream;
Definition = ModuleDefinition.ReadModule(stream);
}
}
static void LoadExtension(Type type) {
try {
var extension = (ExtensionInterface)Activator.CreateInstance(type);
var l1 = extension.GetResourceConverters();
if (l1 != null) {
foreach (var c in l1) {
var fs = c.GetSupportedFormats();
if (fs == null) continue;
foreach (var f in fs) {
if (f == null) continue;
if (!_converters.ContainsKey(f))
_converters.Add(f, new List<ResourceConverter> { c });
else _converters[f].Add(c);
}
}
}
var l2 = extension.GetResourceFinders();
if (l2 != null) {
foreach (var f in l2) {
var name = f.Name;
var path = f.GetRootPath();
if (name != null && path != null) _localRes.Add(name, path);
}
}
Logger.Log("main", 1, "Extension", "Loaded extension {0}", type);
}
catch (Exception ex) {
Logger.Log("main", 4, "Extension", "Failed to load extension {0}: {1}", type, ex);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a9ea11165e6269b488f916982507a2af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -12,6 +12,6 @@ namespace Cryville.Crtr.Browsing {
bool ImportItemFrom(string path);
string[] GetSupportedFormats();
Dictionary<string, string> GetPresetPaths();
IReadOnlyDictionary<string, string> GetPresetPaths();
}
}

View File

@@ -1,5 +1,7 @@
using Cryville.Common;
using Cryville.Common.Unity;
using Cryville.Crtr.Extension;
using Cryville.Crtr.Extensions.Umg;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -15,49 +17,13 @@ namespace Cryville.Crtr.Browsing {
private DirectoryInfo[] items = new DirectoryInfo[0];
public string[] CurrentDirectory { get; private set; }
static readonly Dictionary<string, List<ResourceConverter>> converters
= new Dictionary<string, List<ResourceConverter>>();
static readonly Dictionary<string, string> localRes
= new Dictionary<string, string>();
static bool _init;
public LegacyResourceManager(string rootPath) {
_rootPath = rootPath;
}
static LegacyResourceManager() {
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) {
foreach (var type in asm.GetTypes()) {
if (!type.IsSubclassOf(typeof(ExtensionInterface))) continue;
var ext = (ExtensionInterface)Activator.CreateInstance(type);
try {
var cs = ext.GetResourceConverters();
if (cs != null) {
foreach (var c in cs) {
var fs = c.GetSupportedFormats();
if (fs == null) continue;
foreach (var f in fs) {
if (f == null) continue;
if (!converters.ContainsKey(f))
converters.Add(f, new List<ResourceConverter> { c });
else converters[f].Add(c);
}
}
}
var fs2 = ext.GetResourceFinders();
if (fs2 != null) {
foreach (var f in fs2) {
var name = f.Name;
var path = f.GetRootPath();
if (name != null && path != null)
localRes.Add(name, path);
}
}
Logger.Log("main", 1, "Resource", "Loaded extension {0}", ReflectionHelper.GetNamespaceQualifiedName(type));
}
catch (Exception ex) {
Logger.Log("main", 4, "Resource", "Failed to initialize extension {0}: {1}", ReflectionHelper.GetNamespaceQualifiedName(type), ex);
}
}
if (!_init) {
_init = true;
ExtensionManager.Init(rootPath);
}
}
@@ -149,14 +115,15 @@ namespace Cryville.Crtr.Browsing {
public bool ImportItemFrom(string path) {
var file = new FileInfo(path);
if (!converters.ContainsKey(file.Extension)) return false;
foreach (var converter in converters[file.Extension]) {
IEnumerable<ResourceConverter> converters;
if (!ExtensionManager.TryGetConverters(file.Extension, out converters)) return false;
foreach (var converter in converters) {
IEnumerable<Resource> resources = null;
try {
resources = converter.ConvertFrom(file);
}
catch (Exception ex) {
LogAndPopup(4, ex.Message);
LogAndPopupExtra(4, ex, "Failed to import resource: {0}", ex.Message);
return false;
}
foreach (var res in resources) {
@@ -168,7 +135,7 @@ namespace Cryville.Crtr.Browsing {
var dir = new DirectoryInfo(_rootPath + "/charts/" + res.Name);
if (!dir.Exists) dir.Create();
using (var writer = new StreamWriter(dir.FullName + "/.json")) {
writer.Write(JsonConvert.SerializeObject(tres.Main, Game.GlobalJsonSerializerSettings));
writer.Write(JsonConvert.SerializeObject(ConvertChartData(tres.Main), Game.GlobalJsonSerializerSettings));
}
using (var writer = new StreamWriter(dir.FullName + "/.umgc")) {
tres.Meta.data = "";
@@ -219,12 +186,89 @@ namespace Cryville.Crtr.Browsing {
Popup.Create(msg);
}
public string[] GetSupportedFormats() {
return converters.Keys.ToArray();
void LogAndPopupExtra(int level, object extraLog, string format, params object[] args) {
var msg = string.Format(format, args);
Logger.Log("main", level, "Resource", "{0}\n{1}", msg, extraLog);
Popup.Create(msg);
}
public Dictionary<string, string> GetPresetPaths() {
return localRes;
public string[] GetSupportedFormats() {
return ExtensionManager.GetSupportedFormats().ToArray();
}
public IReadOnlyDictionary<string, string> GetPresetPaths() {
return ExtensionManager.GetLocalResourcePaths();
}
static Chart ConvertChartData(ChartData i) {
return new Chart {
endtime = ConvertBeatTime(i.endtime),
format = i.format,
groups = ConvertGroups(i.groups),
motions = ConvertMotions(i.motions),
ruleset = i.ruleset,
sigs = ConvertSignatures(i.sigs),
sounds = ConvertSounds(i.sounds),
time = ConvertBeatTime(i.time),
};
}
static BeatTime? ConvertBeatTime(Extension.BeatTime? value) {
if (value == null) return null;
return new BeatTime(value.Value.b, value.Value.n, value.Value.d);
}
static List<Chart.Group> ConvertGroups(List<ChartData.Group> l) {
return l.Select(i => new Chart.Group {
endtime = ConvertBeatTime(i.endtime),
motions = ConvertMotions(i.motions),
notes = ConvertNotes(i.notes),
time = ConvertBeatTime(i.time),
tracks = ConvertTracks(i.tracks),
}).ToList();
}
static List<Chart.Judge> ConvertJudges(List<ChartData.Judge> l) {
return l.Select(i => new Chart.Judge {
endtime = ConvertBeatTime(i.endtime),
name = i.name,
time = ConvertBeatTime(i.time),
}).ToList();
}
static List<Chart.Motion> ConvertMotions(List<ChartData.Motion> l) {
return l.Select(i => new Chart.Motion {
endtime = ConvertBeatTime(i.endtime),
motion = i.motion,
sumfix = i.sumfix,
time = ConvertBeatTime(i.time),
}).ToList();
}
static List<Chart.Note> ConvertNotes(List<ChartData.Note> l) {
return l.Select(i => new Chart.Note {
endtime = ConvertBeatTime(i.endtime),
judges = ConvertJudges(i.judges),
motions = ConvertMotions(i.motions),
time = ConvertBeatTime(i.time),
}).ToList();
}
static List<Chart.Signature> ConvertSignatures(List<ChartData.Signature> l) {
return l.Select(i => new Chart.Signature {
endtime = ConvertBeatTime(i.endtime),
tempo = i.tempo,
time = ConvertBeatTime(i.time),
}).ToList();
}
static List<Chart.Sound> ConvertSounds(List<ChartData.Sound> l) {
return l.Select(i => new Chart.Sound {
endtime = ConvertBeatTime(i.endtime),
id = i.id,
offset = i.offset,
time = ConvertBeatTime(i.time),
}).ToList();
}
static List<Chart.Track> ConvertTracks(List<ChartData.Track> l) {
return l.Select(i => new Chart.Track {
endtime = ConvertBeatTime(i.endtime),
motions = ConvertMotions(i.motions),
time = ConvertBeatTime(i.time),
}).ToList();
}
}
}

View File

@@ -1,6 +0,0 @@
namespace Cryville.Crtr.Browsing {
public abstract class LocalResourceFinder {
public abstract string Name { get; }
public abstract string GetRootPath();
}
}

View File

@@ -9,11 +9,11 @@ namespace Cryville.Crtr.Browsing {
public override object Value {
get {
float s_value = GetDisplayValue();
return IntegerMode ? (object)(int)s_value : (object)s_value;
return IntegerMode ? (int)s_value : (object)s_value;
}
set {
if (value is double) m_value = (double)value;
else m_value = IntegerMode ? (double)(int)value : (double)(float)value;
else m_value = IntegerMode ? (int)value : (double)(float)value;
float s_value = GetDisplayValue();
m_text.text = s_value.ToString();
if (Range != null && MaxStep == 0) {

View File

@@ -1,7 +1,7 @@
using Cryville.Common;
using Cryville.Common.Unity.UI;
using Cryville.Crtr.Config;
using Newtonsoft.Json;
using Cryville.Crtr.Extension;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
@@ -94,24 +94,4 @@ namespace Cryville.Crtr.Browsing {
public AsyncDelivery<Texture2D> Cover { get; set; }
public ChartMeta Meta { get; set; }
}
#pragma warning disable IDE1006
public class MetaInfo {
public string name { get; set; }
public string author { get; set; }
[JsonRequired]
public string data { get; set; }
}
public class SongMetaInfo {
public string name { get; set; }
public string author { get; set; }
}
public class ChartMeta : MetaInfo {
public SongMetaInfo song { get; set; }
public float length { get; set; }
public string ruleset { get; set; }
public int note_count { get; set; }
public string cover { get; set; }
}
#pragma warning restore IDE1006
}

View File

@@ -1,79 +0,0 @@
using Cryville.Common;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
namespace Cryville.Crtr.Browsing {
public abstract class ResourceConverter {
public abstract string[] GetSupportedFormats();
public abstract IEnumerable<Resource> ConvertFrom(FileInfo file);
}
public abstract class Resource {
protected Resource(string name) {
Name = StringUtils.EscapeFileName(name);
}
public string Name { get; private set; }
public abstract bool Valid { get; }
public override string ToString() {
return string.Format("{0} ({1})", Name, ReflectionHelper.GetSimpleName(GetType()));
}
}
public class RawChartResource : Resource {
public RawChartResource(string name, Chart main, ChartMeta meta) : base(name) {
Main = main; Meta = meta;
}
public Chart Main { get; private set; }
public ChartMeta Meta { get; private set; }
public override bool Valid { get { return true; } }
}
public abstract class FileResource : Resource {
public FileResource(string name, FileInfo master) : base(name) {
Master = master;
Attachments = new List<FileInfo>();
}
public FileInfo Master { get; private set; }
public List<FileInfo> Attachments { get; private set; }
public override bool Valid {
get {
if (!Master.Exists) return false;
foreach (var file in Attachments) {
if (!file.Exists) return false;
}
return true;
}
}
}
public class ChartResource : FileResource {
public ChartResource(string name, FileInfo master) : base(name, master) {
using (var reader = new StreamReader(master.FullName)) {
var meta = JsonConvert.DeserializeObject<ChartMeta>(reader.ReadToEnd());
Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, meta.data + ".json")));
if (meta.cover != null) Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, meta.cover)));
}
}
}
public class SongResource : FileResource {
public SongResource(string name, FileInfo master) : base(name, master) { }
}
public class RulesetResource : FileResource {
public RulesetResource(string name, FileInfo master) : base(name, master) {
using (var reader = new StreamReader(master.FullName)) {
var meta = JsonConvert.DeserializeObject<Ruleset>(reader.ReadToEnd());
Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, meta.data + ".pdt")));
}
}
}
public class SkinResource : FileResource {
public string RulesetName { get; private set; }
public SkinResource(string name, FileInfo master) : base(name, master) {
using (var reader = new StreamReader(master.FullName)) {
var meta = JsonConvert.DeserializeObject<Skin>(reader.ReadToEnd());
RulesetName = meta.ruleset;
Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, meta.data + ".pdt")));
foreach (var frame in meta.frames) {
Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, frame)));
}
}
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 2546fb1d514348842a14a03531be192d
timeCreated: 1637982768
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,20 +0,0 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Cryville.Crtr.Browsing {
public class SkinResourceImporter : ResourceConverter {
static readonly string[] SUPPORTED_FORMATS = { ".umgs" };
public override string[] GetSupportedFormats() {
return SUPPORTED_FORMATS;
}
public override IEnumerable<Resource> ConvertFrom(FileInfo file) {
using (StreamReader reader = new StreamReader(file.FullName, Encoding.UTF8)) {
var data = JsonConvert.DeserializeObject<Skin>(reader.ReadToEnd());
return new Resource[] { new SkinResource(data.name, file) };
}
}
}
}

View File

@@ -24,7 +24,7 @@ namespace Cryville.Crtr {
#region Fields
Chart chart;
Skin skin;
PdtSkin pskin;
public static PdtSkin pskin;
Ruleset ruleset;
PdtRuleset pruleset;
Dictionary<string, Texture2D> texs;
@@ -485,7 +485,6 @@ namespace Cryville.Crtr {
}
Game.AudioSequencer.Playing = true;
atime0 = Game.AudioClient.BufferPosition;
Thread.Sleep((int)((atime0 - Game.AudioClient.Position) * 1000));
inputProxy.SyncTime(cbus.Time);
started = true;
}

View File

@@ -2,6 +2,7 @@
using UnityEngine;
namespace Cryville.Crtr.Components {
[DisallowMultipleComponent]
public abstract class MeshBase : SkinComponent {
public MeshBase() {
SubmitProperty("color", new PropOp.Color(v => Color = v));

View File

@@ -72,7 +72,7 @@ namespace Cryville.Crtr.Components {
public op_set_shape(PolygonSGO self) : base(1) {
_self = self;
}
protected unsafe override void Execute() {
protected override unsafe void Execute() {
var o = GetOperand(0);
if (o.Type != PdtInternalType.Vector) throw new ArgumentException("Not a vector");
_self._shapeLength = (o.Length - sizeof(int)) / sizeof(Vector2);

View File

@@ -1,5 +1,89 @@
namespace Cryville.Crtr.Components {
using Cryville.Common;
using System;
using UnityEngine;
using Logger = Cryville.Common.Logger;
namespace Cryville.Crtr.Components {
public class SkinAnimation : SkinComponent {
public SkinAnimation() {
SubmitProperty("name", new PropOp.Identifier(v => Name = v));
SubmitProperty("duration", new PropOp.Float(v => Duration = v));
SubmitProperty("iteration", new PropOp.Float(v => Iteration = v));
SubmitProperty("direction", new PropOp.Enum<AnimationDirection>(v => Direction = v, v => (AnimationDirection)v));
SubmitProperty("delay", new PropOp.Float(v => Delay = v));
Iteration = 1;
}
SkinContext _skinContext;
Transform _writeTransform;
AnimationSpan _anim;
int _name;
public int Name {
set {
if (_name == value) return;
_name = value;
if (value == 0) {
_anim = null;
}
else {
var id = new Identifier(value);
AnimationSpan anim;
if (!ChartPlayer.pskin.animations.TryGetValue(id, out anim)) {
Logger.Log("main", 4, "Skin", "Animation {0} not found", id.Name);
_anim = null;
return;
}
_anim = anim;
}
}
}
public float Duration { get; private set; }
public float Iteration { get; private set; }
public AnimationDirection Direction { get; private set; }
public float Delay { get; private set; }
double _startTime;
public override void Init() {
_skinContext = new SkinContext(transform);
}
public override void Rewind(double time, Transform target) {
_startTime = time;
if (target == null) target = transform;
_writeTransform = target;
}
public override void Tick(SkinContainer c, double time) {
float _rtime = (float)(time - _startTime - Delay) / Duration;
if (_rtime < 0) _rtime = 0;
else if (_rtime > Iteration) {
if (Direction.HasFlag(AnimationDirection.alternate)) {
_rtime = Iteration % 2;
if (_rtime > 1) _rtime = 2 - _rtime;
}
else {
_rtime = Iteration % 1;
if (_rtime == 0) _rtime = 1;
}
}
else {
if (Direction.HasFlag(AnimationDirection.alternate)) {
_rtime %= 2;
if (_rtime > 1) _rtime = 2 - _rtime;
}
else {
_rtime %= 1;
}
}
if (Direction.HasFlag(AnimationDirection.reverse)) _rtime = 1 - _rtime;
if (_anim != null) c.MatchAnimation(_anim, _rtime, new RuntimeSkinContext(_skinContext, _writeTransform));
}
protected override void OnDestroy() { }
}
[Flags]
public enum AnimationDirection {
normal = 0,
reverse = 1,
alternate = 2,
}
}

View File

@@ -26,6 +26,8 @@ namespace Cryville.Crtr.Components {
}
public virtual void Init() { }
public virtual void Rewind(double time, Transform target) { }
public virtual void Tick(SkinContainer c, double time) { }
protected abstract void OnDestroy();
}
public struct SkinProperty {

View File

@@ -8,12 +8,12 @@ namespace Cryville.Crtr.Components {
SubmitProperty("border", new PropOp.Vector2(v => Border = v));
}
readonly static Dictionary<float, int> uvrefl
static readonly Dictionary<float, int> uvrefl
= new Dictionary<float, int>() {
{-0.5f, 0}, {-0.4f, 1}, {0.4f, 2}, {0.5f, 3},
};
static Vector2[] _origuv;
protected new static Vector2[] OriginalUV {
protected static new Vector2[] OriginalUV {
get {
if (_origuv == null) {
var m = GenericResources.Meshes["quad_scale3h"];

View File

@@ -19,7 +19,7 @@ namespace Cryville.Crtr.Components {
public op_set_frames(SpriteText self) : base(2) {
_self = self;
}
protected unsafe override void Execute() {
protected override unsafe void Execute() {
var keys = GetOperand(0).AsString();
var values = GetOperand(1);
int arrtype; int len;

View File

@@ -1,5 +1,6 @@
using Cryville.Common.Buffers;
using System.Collections.Generic;
using UnityEngine;
namespace Cryville.Crtr {
public class EffectGroup {
@@ -35,28 +36,37 @@ namespace Cryville.Crtr {
while (_endQueue.Count > 0) {
var item = _endQueue[0];
if (item.EndTime > _time) break;
item.OnDone();
_instances.Remove(item.Index);
_pool.Return(item);
_endQueue.RemoveAt(0);
if (item.OnStateDone()) {
QueueInstance(item);
}
else {
item.Tick(time);
_instances.Remove(item.Index);
_pool.Return(item);
}
}
foreach (var instance in _instances) {
instance.Value.Tick();
instance.Value.Tick(time);
}
}
public void Emit(float index) {
public void Emit(float index, Transform target = null) {
EffectInstance instance;
if (_instances.TryGetValue(index, out instance)) {
var i = _endQueue.BinarySearch(instance);
_endQueue.RemoveAt(i);
}
else {
_instances.Add(index, instance = _pool.Rent());
}
bool flag = _instances.TryGetValue(index, out instance);
if (!flag) _instances.Add(index, instance = _pool.Rent());
instance.Index = index;
instance.OnEmit(_time);
var i2 = ~_endQueue.BinarySearch(instance);
_endQueue.Insert(i2, instance);
if (instance.CanEmit()) {
if (flag) {
var i = _endQueue.BinarySearch(instance);
_endQueue.RemoveAt(i);
}
instance.OnEmit(_time, target);
QueueInstance(instance);
}
}
void QueueInstance(EffectInstance i) {
var index = ~_endQueue.BinarySearch(i);
_endQueue.Insert(index, i);
}
public void Dispose() {
_pool.DisposeAll();

View File

@@ -1,5 +1,6 @@
using Cryville.Common;
using Cryville.Crtr.Components;
using Cryville.Crtr.Event;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -10,19 +11,24 @@ namespace Cryville.Crtr {
readonly EffectDefinition _def;
readonly SkinContainer _skinContainer;
public Transform RootTransform { get; private set; }
readonly SkinComponent[] _comps;
public EffectInstance(EffectDefinition def) {
_def = def;
_skinContainer = new SkinContainer(_def.elements);
_skinContainer = new SkinContainer(this, _def.elements);
RootTransform = new GameObject("effect:" + GetHashCode().ToString(CultureInfo.InvariantCulture)).transform;
SkinContext = new SkinContext(RootTransform);
ChartPlayer.etor.ContextCascadeInsertBlock();
_skinContainer.MatchStatic(this);
_skinContainer.MatchStatic();
ChartPlayer.etor.ContextCascadeDiscardBlock();
foreach (var i in RootTransform.GetComponentsInChildren<SkinComponent>())
i.Init();
_comps = RootTransform.GetComponentsInChildren<SkinComponent>();
foreach (var i in _comps) i.Init();
_indexSrc = new PropSrc.Float(() => Index);
_durationOp = new PropOp.Float(v => _duration = v);
}
public void Rewind(double time, Transform target) {
_startTime = time;
foreach (var i in _comps) i.Rewind(time, target);
}
private float m_index;
public float Index {
get { return m_index; }
@@ -32,26 +38,62 @@ namespace Cryville.Crtr {
_indexSrc.Invalidate();
}
}
Transform _currentTarget;
Identifier _currentStateName = Identifier.Empty;
EffectState _currentState;
ChartEvent _ctxev;
ContainerState _ctxstate;
internal static readonly int _VAR_EFFECT_INDEX = IdentifierManager.SharedInstance.Request("effect_index");
readonly PropSrc _indexSrc;
double _startTime;
float _duration;
readonly PropOp _durationOp;
public double EndTime { get { return _startTime + _duration; } }
public void Tick() {
_skinContainer.MatchDynamic(this, 1);
public void Tick(double time) {
foreach (var i in _comps) i.Tick(_skinContainer, time);
}
public void OnEmit(double time) {
_startTime = time;
public bool CanEmit() {
return _currentStateName.Key == 0 || _currentState.rewind.Key != 0;
}
public void OnEmit(double time, Transform target) {
_currentTarget = target;
_ctxev = ChartPlayer.etor.ContextEvent;
_ctxstate = ChartPlayer.etor.ContextState;
if (_currentStateName.Key == 0) {
EnterState(_def.init, time, _currentTarget, true);
}
else {
if (_currentState.rewind.Key == 0) throw new InvalidOperationException("Cannot rewind");
EnterState(_currentState.rewind, time, _currentTarget, true);
}
}
void EnterState(Identifier name, double time, Transform target, bool emitting) {
_currentStateName = name;
_currentState = _def.states[name];
Rewind(time, target);
RootTransform.gameObject.SetActive(true);
ChartPlayer.etor.ContextCascadeInsert();
ChartPlayer.etor.ContextCascadeUpdate(_VAR_EFFECT_INDEX, _indexSrc);
ChartPlayer.etor.Evaluate(_durationOp, _def.duration);
_skinContainer.MatchDynamic(this, 0);
ChartPlayer.etor.Evaluate(_durationOp, _currentState.duration);
if (emitting) _skinContainer.MatchDynamic(0, true);
_skinContainer.MatchDynamic(1, emitting);
ChartPlayer.etor.ContextCascadeDiscard();
}
public void OnDone() {
RootTransform.gameObject.SetActive(false);
public bool OnStateDone() {
if (_currentState.next.Key == 0) {
RootTransform.gameObject.SetActive(false);
_currentStateName = Identifier.Empty;
_currentState = null;
return false;
}
else {
ChartPlayer.etor.ContextEvent = _ctxev;
ChartPlayer.etor.ContextState = _ctxstate;
EnterState(_currentState.next, EndTime, _currentTarget, false);
ChartPlayer.etor.ContextEvent = null;
ChartPlayer.etor.ContextState = null;
return true;
}
}
public void Dispose() {
GameObject.Destroy(RootTransform.gameObject);
@@ -59,7 +101,7 @@ namespace Cryville.Crtr {
public string TypeName { get { throw new InvalidOperationException("Type name undefined"); } }
public SkinContext SkinContext { get; private set; }
public Anchor OpenedAnchor { get { throw new InvalidOperationException("Anchor not supported"); } }
public int OpenedAnchorName { get { return _currentStateName.Key; } }
public void PushAnchorEvent(double time, int name) {
throw new InvalidOperationException("Anchor not supported");
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using UnityEngine;
namespace Cryville.Crtr {
public class EffectManager {
@@ -15,6 +16,9 @@ namespace Cryville.Crtr {
public void Emit(int id, float index) {
_groups[id].Emit(index);
}
public void EmitSelf(int id, float index, Transform target) {
_groups[id].Emit(index, target);
}
public void Dispose() {
foreach (var g in _groups) g.Value.Dispose();
}

View File

@@ -37,10 +37,9 @@ namespace Cryville.Crtr.Event {
/// </summary>
public ContainerState ts;
/// <summary>
/// <see cref="GameObject"/> group, the <see cref="Transform"/> containing all the generated elements in the <see cref="ContainerHandler"/>.
/// </summary>
protected Transform gogroup;
protected Transform RootTransform;
SkinComponent[] _comps;
public Vector3 Position { get; protected set; }
public Quaternion Rotation { get; protected set; }
@@ -70,7 +69,7 @@ namespace Cryville.Crtr.Event {
SkinContainer skinContainer;
protected Judge judge;
public void AttachSystems(PdtSkin skin, Judge judge) {
skinContainer = new SkinContainer(skin.elements);
skinContainer = new SkinContainer(this, skin.elements);
this.judge = judge;
}
@@ -80,15 +79,15 @@ namespace Cryville.Crtr.Event {
Anchor a_cur;
Anchor a_head;
Anchor a_tail;
readonly static int _a_cur = IdentifierManager.SharedInstance.Request("cur");
readonly static int _a_head = IdentifierManager.SharedInstance.Request("head");
readonly static int _a_tail = IdentifierManager.SharedInstance.Request("tail");
static readonly int _a_cur = IdentifierManager.SharedInstance.Request("cur");
static readonly int _a_head = IdentifierManager.SharedInstance.Request("head");
static readonly int _a_tail = IdentifierManager.SharedInstance.Request("tail");
double atime_head;
double atime_tail;
public Anchor RegisterAnchor(int name, bool dyn = false, int propSrcCount = 0) {
var strname = IdentifierManager.SharedInstance.Retrieve(name);
var go = new GameObject("." + strname).transform;
go.SetParent(gogroup, false);
go.SetParent(RootTransform, false);
var result = new Anchor(name, go, propSrcCount);
if (dyn) {
if (DynamicAnchors.ContainsKey(name))
@@ -103,22 +102,22 @@ namespace Cryville.Crtr.Event {
return result;
}
protected void OpenAnchor(Anchor anchor) {
if (OpenedAnchor != null) throw new InvalidOperationException("An anchor has been opened");
if (_openedAnchor != null) throw new InvalidOperationException("An anchor has been opened");
anchor.Transform.gameObject.SetActive(true);
OpenedAnchor = anchor;
_openedAnchor = anchor;
}
protected void CloseAnchor() {
OpenedAnchor = null;
_openedAnchor = null;
}
#endregion
#region Logic
#region Init methods: Called on prehandle
public virtual void PreInit() {
gogroup = new GameObject(TypeName + ":" + Container.GetHashCode().ToString(CultureInfo.InvariantCulture)).transform;
SkinContext = new SkinContext(gogroup);
RootTransform = new GameObject(TypeName + ":" + Container.GetHashCode().ToString(CultureInfo.InvariantCulture)).transform;
SkinContext = new SkinContext(RootTransform);
if (cs.Parent != null)
gogroup.SetParent(cs.Parent.Handler.gogroup, false);
RootTransform.SetParent(cs.Parent.Handler.RootTransform, false);
a_cur = RegisterAnchor(_a_cur);
a_head = RegisterAnchor(_a_head, true);
a_tail = RegisterAnchor(_a_tail, true);
@@ -126,16 +125,16 @@ namespace Cryville.Crtr.Event {
public virtual void Init() {
ChartPlayer.etor.ContextState = ps;
ChartPlayer.etor.ContextEvent = Container;
skinContainer.MatchStatic(this);
skinContainer.MatchStatic();
ChartPlayer.etor.ContextEvent = null;
ChartPlayer.etor.ContextState = null;
foreach (var i in gogroup.GetComponentsInChildren<SkinComponent>())
i.Init();
_comps = RootTransform.GetComponentsInChildren<SkinComponent>();
foreach (var i in _comps) i.Init();
}
public virtual void PostInit() {
PropSrcs.Add(_var_current_time, new PropSrc.Float(() => (float)cs.rootPrototype.Time));
PropSrcs.Add(_var_invisible_bounds, new PropSrc.Boolean(() => atime_head > atime_tail));
gogroup.gameObject.SetActive(false);
RootTransform.gameObject.SetActive(false);
}
#endregion
#region Start methods
@@ -146,14 +145,14 @@ namespace Cryville.Crtr.Event {
public virtual void StartLogicalUpdate(ContainerState s) { }
protected virtual void StartPreGraphicalUpdate(ContainerState s) { }
protected virtual void StartGraphicalUpdate(ContainerState s) {
if (gogroup) gogroup.gameObject.SetActive(true);
if (RootTransform) RootTransform.gameObject.SetActive(true);
}
#endregion
public virtual void Update(ContainerState s, StampedEvent ev) {
if (s.CloneType == 3) SetPreGraphicalActive(true, s);
else if (ev is StampedEvent.Anchor) {
var tev = (StampedEvent.Anchor)ev;
if (gogroup) {
if (RootTransform) {
OpenAnchor(tev.Target);
#if UNITY_5_6_OR_NEWER
tev.Target.Transform.SetPositionAndRotation(Position, Rotation);
@@ -172,7 +171,7 @@ namespace Cryville.Crtr.Event {
}
anchorEvPool.Return(tev);
}
else if (gogroup && s.CloneType == 2) MatchDynamic(s, 1);
else if (RootTransform && s.CloneType == 2) MatchDynamic(s, 1);
}
#region End methods
protected virtual void EndGraphicalUpdate(ContainerState s) { }
@@ -180,8 +179,8 @@ namespace Cryville.Crtr.Event {
public virtual void EndLogicalUpdate(ContainerState s) { }
public virtual void EndPhysicalUpdate(ContainerState s) { }
public virtual void Dispose() {
if (gogroup)
GameObject.Destroy(gogroup.gameObject);
if (RootTransform)
GameObject.Destroy(RootTransform.gameObject);
Alive = false;
}
public virtual void DisposeAll() { }
@@ -193,7 +192,7 @@ namespace Cryville.Crtr.Event {
void MatchDynamic(ContainerState s, int dl) {
ChartPlayer.etor.ContextState = s;
ChartPlayer.etor.ContextEvent = Container;
skinContainer.MatchDynamic(this, dl);
skinContainer.MatchDynamic(dl);
ChartPlayer.etor.ContextEvent = null;
ChartPlayer.etor.ContextState = null;
}
@@ -205,6 +204,7 @@ namespace Cryville.Crtr.Event {
atime_head = cs.StampedContainer.Time;
atime_tail = atime_head + cs.StampedContainer.Duration;
MatchDynamic(cs, 0);
foreach (var i in _comps) i.Tick(skinContainer, cs.Time);
if (cs.Active) PushAnchorEvent(cs.Time, a_cur);
if (double.IsNaN(DynamicAnchorSetTime[_a_head])) DynamicAnchorSetTime[_a_head] = atime_head;
if (double.IsNaN(DynamicAnchorSetTime[_a_tail])) DynamicAnchorSetTime[_a_tail] = atime_tail;
@@ -241,7 +241,8 @@ namespace Cryville.Crtr.Event {
#region ISkinnableGroup
public abstract string TypeName { get; }
public SkinContext SkinContext { get; private set; }
public Anchor OpenedAnchor { get; private set; }
Anchor _openedAnchor;
public int OpenedAnchorName { get { return _openedAnchor == null ? 0 : _openedAnchor.Name; } }
public bool TryGetAnchorsByName(int name, out IReadOnlyCollection<Anchor> result) {
List<Anchor> anchors;
var ret = Anchors.TryGetValue(name, out anchors);

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: dbc046e7cabacbb4fbf74520399a7340
guid: b35ffffce02252548a66e18cf98050e2
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using UnityEngine.Scripting;
namespace Cryville.Crtr.Extension {
[Preserve]
public static class RefTypes {
[Preserve]
public static void PreserveEnumerable() {
IEnumerable<object> p = Enumerable.Empty<object>();
p.All(i => false);
p.Any();
p.Any(i => false);
p.Cast<object>();
p.Concat(Enumerable.Empty<object>());
p.Contains(null);
p.Count();
p.Count(i => false);
p.DefaultIfEmpty();
p.DefaultIfEmpty(null);
p.Distinct();
p.ElementAt(0);
p.ElementAtOrDefault(0);
p.First();
p.First(i => false);
p.FirstOrDefault();
p.FirstOrDefault(i => false);
p.GetEnumerator();
p.Last();
p.Last(i => false);
p.LastOrDefault();
p.LastOrDefault(i => false);
p.LongCount();
p.LongCount(i => false);
p.OrderBy(i => i).ThenBy(i => i);
p.OrderByDescending(i => i).ThenByDescending(i => i);
p.Reverse();
p.Select(i => i);
p.Select((i, j) => false);
p.SequenceEqual(p);
p.Single();
p.Single(i => false);
p.SingleOrDefault();
p.SingleOrDefault(i => false);
p.Skip(0);
p.SkipLast(0);
p.SkipWhile(i => false);
p.SkipWhile((i, j) => false);
p.Take(0);
p.TakeLast(0);
p.TakeWhile(i => false);
p.TakeWhile((i, j) => false);
p.ToArray();
p.ToDictionary(i => new object());
p.ToDictionary(i => new object(), i => new object());
p.ToHashSet();
p.ToList();
p.Where(i => false);
p.Where((i, j) => false);
}
[Preserve]
public static void PreserveBinaryReader() {
BinaryReader p = new BinaryReader(null);
p.Close();
p.Dispose();
p.PeekChar();
p.Read();
p.ReadBoolean();
p.ReadByte();
p.ReadBytes(0);
p.ReadChar();
p.ReadChars(0);
p.ReadDouble();
p.ReadInt16();
p.ReadInt32();
p.ReadInt64();
p.ReadSByte();
p.ReadSingle();
p.ReadUInt16();
p.ReadUInt32();
p.ReadUInt64();
}
[Preserve]
public static void PreserveStreamReader() {
object _;
StreamReader p = new StreamReader((Stream)null);
p.Close();
_ = p.CurrentEncoding;
p.DiscardBufferedData();
p.Dispose();
_ = p.EndOfStream;
p.Peek();
p.Read();
p.ReadBlock(null, 0, 0);
p.ReadLine();
p.ReadToEnd();
}
[Preserve]
public static void PreserveConvert() {
Convert.ChangeType(0, typeof(byte));
Convert.ChangeType(0, typeof(byte), CultureInfo.InvariantCulture);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 29a6376ce10b77e4099d2613876f9549
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,189 +0,0 @@
using Cryville.Common;
using Cryville.Common.Math;
using Cryville.Crtr.Browsing;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
namespace Cryville.Crtr.Extensions.Bestdori {
public class BestdoriChartConverter : ResourceConverter {
static readonly string[] SUPPORTED_FORMATS = { ".bestdori" };
public override string[] GetSupportedFormats() {
return SUPPORTED_FORMATS;
}
public override IEnumerable<Resource> ConvertFrom(FileInfo file) {
List<Resource> result = new List<Resource>();
List<BestdoriChartEvent> src;
using (var reader = new StreamReader(file.FullName)) {
src = JsonConvert.DeserializeObject<List<BestdoriChartEvent>>(reader.ReadToEnd());
}
var group = new Chart.Group() {
tracks = new List<Chart.Track>(),
notes = new List<Chart.Note>(),
motions = new List<Chart.Motion>(),
};
Chart chart = new Chart {
format = 2,
time = new BeatTime(0, 0, 1),
ruleset = "bang_dream_girls_band_party",
sigs = new List<Chart.Signature>(),
sounds = new List<Chart.Sound>(),
motions = new List<Chart.Motion>(),
groups = new List<Chart.Group> { group },
};
var tm = new BeatTimeTimingModel();
string bgm = null;
double endbeat = 0;
foreach (var ev in src) {
tm.ForwardTo(ev.StartBeat);
if (ev.StartBeat > endbeat) endbeat = ev.StartBeat;
if (ev is BestdoriChartEvent.System) {
if (bgm != null) continue;
var tev = (BestdoriChartEvent.System)ev;
bgm = StringUtils.TrimExt(tev.data);
var name = "bang_dream_girls_band_party__" + bgm;
result.Add(new SongResource(name, new FileInfo(Path.Combine(file.Directory.FullName, tev.data))));
chart.sounds.Add(new Chart.Sound { time = ToBeatTime(tev.beat), id = name });
}
else if (ev is BestdoriChartEvent.BPM) {
var tev = (BestdoriChartEvent.BPM)ev;
tm.BPM = tev.bpm;
chart.sigs.Add(new Chart.Signature { time = ToBeatTime(tev.beat), tempo = (float)tev.bpm });
}
else if (ev is BestdoriChartEvent.Single) {
var tev = (BestdoriChartEvent.Single)ev;
group.notes.Add(new Chart.Note {
time = ToBeatTime(tev.beat),
judges = new List<Chart.Judge> { new Chart.Judge { name = tev.flick ? "single_flick" : "single" } },
motions = new List<Chart.Motion> { new Chart.Motion { motion = "track:" + tev.lane.ToString(CultureInfo.InvariantCulture) } },
});
}
else if (ev is BestdoriChartEvent.Long) {
var tev = (BestdoriChartEvent.Long)ev;
var c1 = tev.connections[tev.connections.Count - 1];
var note = new Chart.Note {
time = ToBeatTime(tev.connections[0].beat),
endtime = ToBeatTime(c1.beat),
judges = new List<Chart.Judge>(),
};
for (int i = 0; i < tev.connections.Count; i++) {
BestdoriChartEvent.Connection c = tev.connections[i];
var motion = new Chart.Motion { motion = "track:" + c.lane.ToString(CultureInfo.InvariantCulture) };
if (i == 0) {
note.judges.Add(new Chart.Judge { name = "single" });
note.motions.Add(motion);
}
else {
var cbeat = motion.endtime = ToBeatTime(c.beat);
if (i > 1) motion.time = ToBeatTime(tev.connections[i - 1].beat);
note.motions.Add(motion);
if (i == tev.connections.Count - 1)
note.judges.Add(new Chart.Judge { time = cbeat, name = c.flick ? "longend_flick" : "longend" });
else if (!c.hidden)
note.judges.Add(new Chart.Judge { time = cbeat, name = "longnode" });
}
}
if (c1.beat > endbeat) endbeat = c1.beat;
group.notes.Add(note);
}
else throw new NotImplementedException("Unsupported event: " + ev.type);
}
if (bgm == null) throw new FormatException("Chart contains no song");
chart.endtime = ToBeatTime(endbeat + 4);
if (endbeat > tm.BeatTime) tm.ForwardTo(endbeat);
result.Add(new RawChartResource(string.Format("bang_dream_girls_band_party__{0}__{1}", bgm, StringUtils.TrimExt(file.Name)), chart, new ChartMeta {
name = string.Format("Bandori {0} {1}", bgm, StringUtils.TrimExt(file.Name)),
author = "©BanG Dream! Project ©Craft Egg Inc. ©bushiroad",
ruleset = "bang_dream_girls_band_party",
note_count = group.notes.Count,
length = (float)tm.Time,
song = new SongMetaInfo {
name = bgm,
author = "©BanG Dream! Project ©Craft Egg Inc. ©bushiroad",
}
}));
return result;
}
BeatTime ToBeatTime(double beat, double error = 1e-4) {
int i, n, d;
FractionUtils.ToFraction(beat, error, out n, out d);
i = n / d; n %= d;
return new BeatTime(i, n, d);
}
#pragma warning disable IDE1006
[JsonConverter(typeof(BestdoriChartEventCreator))]
abstract class BestdoriChartEvent {
public abstract string type { get; }
public abstract double StartBeat { get; }
public abstract class InstantEvent : BestdoriChartEvent {
public double beat;
public override double StartBeat { get { return beat; } }
}
public class BPM : InstantEvent {
public override string type { get { return "BPM"; } }
public double bpm;
}
public class System : InstantEvent {
public override string type { get { return "System"; } }
public string data;
}
public abstract class SingleBase : InstantEvent {
public double lane;
public bool skill;
public bool flick;
}
public class Single : SingleBase {
public override string type { get { return "Single"; } }
}
public class Directional : SingleBase {
public override string type { get { return "Directional"; } }
public string direction;
public int width;
}
public class Connection : SingleBase {
public override string type { get { return null; } }
public bool hidden;
}
public class Long : BestdoriChartEvent {
public override string type { get { return "Long"; } }
public List<Connection> connections;
public override double StartBeat { get { return connections[0].beat; } }
}
public class Slide : Long {
public override string type { get { return "Slide"; } }
}
}
#pragma warning restore IDE1006
class BestdoriChartEventCreator : CustomCreationConverter<BestdoriChartEvent> {
string _currentType;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
var obj = JObject.ReadFrom(reader);
var type = obj["type"];
if (type == null) _currentType = null;
else _currentType = obj["type"].ToObject<string>();
return base.ReadJson(obj.CreateReader(), objectType, existingValue, serializer);
}
public override BestdoriChartEvent Create(Type objectType) {
switch (_currentType) {
case "BPM": return new BestdoriChartEvent.BPM();
case "System": return new BestdoriChartEvent.System();
case "Single": return new BestdoriChartEvent.Single();
case "Directional": return new BestdoriChartEvent.Directional();
case null: return new BestdoriChartEvent.Connection();
case "Long": return new BestdoriChartEvent.Long();
case "Slide": return new BestdoriChartEvent.Slide();
default: throw new ArgumentException("Unknown event type: " + _currentType);
}
}
}
}
}

View File

@@ -1,27 +0,0 @@
using Cryville.Crtr.Browsing;
using Cryville.Crtr.Extensions.Bestdori;
using Cryville.Crtr.Extensions.Malody;
using Cryville.Crtr.Extensions.osu;
using Cryville.Crtr.Extensions.Quaver;
using System.Collections.Generic;
namespace Cryville.Crtr.Extensions {
public class Extensions : ExtensionInterface {
public override IEnumerable<ResourceConverter> GetResourceConverters() {
return new ResourceConverter[] {
new BestdoriChartConverter(),
new MalodyChartConverter(),
new osuChartConverter(),
new QuaverChartConverter(),
};
}
public override IEnumerable<LocalResourceFinder> GetResourceFinders() {
return new LocalResourceFinder[] {
new MalodyChartFinder(),
new osuChartFinder(),
new QuaverChartFinder(),
};
}
}
}

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: dfd44d1c0681e4842a1f031556519042
folderAsset: yes
timeCreated: 1637936550
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,225 +0,0 @@
using Cryville.Crtr.Browsing;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
namespace Cryville.Crtr.Extensions.Malody {
public class MalodyChartConverter : ResourceConverter {
static readonly string[] SUPPORTED_FORMATS = { ".mc", ".mcz" };
static readonly string[] MODES = { "key", "step", "dj", "catch", "pad", "taiko", "ring", "slide", "live" };
public override string[] GetSupportedFormats() {
return SUPPORTED_FORMATS;
}
public override IEnumerable<Resource> ConvertFrom(FileInfo file) {
List<Resource> result = new List<Resource>();
MalodyChart src;
if (file.Extension != ".mc") throw new NotImplementedException("mcz file is not supported yet");
using (var reader = new StreamReader(file.FullName)) {
src = JsonConvert.DeserializeObject<MalodyChart>(reader.ReadToEnd());
}
if (src.meta.mode != 0) throw new NotImplementedException(string.Format("{0} mode is not supported yet", MODES[src.meta.mode]));
var ruleset = "malody!" + MODES[src.meta.mode];
if (src.meta.mode == 0) {
ruleset += "." + src.meta.mode_ext.column.ToString(CultureInfo.InvariantCulture) + "k";
}
ChartMeta meta = new ChartMeta() {
name = src.meta.version,
author = src.meta.creator,
song = new SongMetaInfo() {
name = src.meta.song.titleorg != null ? src.meta.song.titleorg : src.meta.song.title,
author = src.meta.song.artistorg != null ? src.meta.song.artistorg : src.meta.song.artist,
},
ruleset = ruleset,
};
Chart chart = new Chart {
format = 2,
time = new BeatTime(-4, 0, 1),
ruleset = ruleset,
sigs = new List<Chart.Signature>(),
sounds = new List<Chart.Sound>(),
motions = new List<Chart.Motion>(),
groups = new List<Chart.Group>(),
};
var group = new Chart.Group() {
tracks = new List<Chart.Track>(),
notes = new List<Chart.Note>(),
motions = new List<Chart.Motion>(),
};
chart.groups.Add(group);
int col = src.meta.mode_ext.column;
IEnumerable<MalodyChart.IEvent> events = src.time.Cast<MalodyChart.IEvent>();
if (src.effect != null) events = events.Concat(src.effect.Cast<MalodyChart.IEvent>());
events = events.Concat(src.note.Cast<MalodyChart.IEvent>());
List<MalodyChart.IEvent> endEvents = new List<MalodyChart.IEvent>();
foreach (var ev in events)
if (ev.endbeat != null)
endEvents.Add(new MalodyChart.EndEvent {
beat = ev.endbeat,
StartEvent = ev,
});
events = events.Concat(endEvents)
.OrderBy(e => e.beat[0] + (float)e.beat[1] / e.beat[2])
.ThenBy(e => e.Priority);
Dictionary<MalodyChart.IEvent, StartEventState> longEvents
= new Dictionary<MalodyChart.IEvent, StartEventState>();
float? baseBpm = null;
var tm = new FractionalBeatTimeTimingModel();
foreach (var ev in events) {
var beat = new BeatTime(ev.beat[0], ev.beat[1], ev.beat[2]);
tm.ForwardTo(beat);
if (ev is MalodyChart.Time) {
var tev = (MalodyChart.Time)ev;
if (baseBpm == null) baseBpm = tev.bpm;
tm.BPM = tev.bpm;
chart.sigs.Add(new Chart.Signature {
time = beat,
tempo = tev.bpm,
});
chart.motions.Add(new Chart.Motion {
time = beat,
motion = "svm:" + (tev.bpm / baseBpm.Value).ToString(CultureInfo.InvariantCulture)
});
}
else if (ev is MalodyChart.Effect) {
var tev = (MalodyChart.Effect)ev;
if (tev.scroll != null) group.motions.Add(new Chart.Motion {
time = beat,
motion = "svm:" + tev.scroll.Value.ToString(CultureInfo.InvariantCulture)
});
}
else if (ev is MalodyChart.Note) {
var tev = (MalodyChart.Note)ev;
if (tev.type == 1) {
if (tev.beat[0] == 0 && tev.beat[1] == 0) {
var res = new SongResource(meta.song.name, new FileInfo(file.DirectoryName + "/" + tev.sound));
result.Add(res);
chart.sounds.Add(new Chart.Sound {
time = new BeatTime(0, 0, 1),
id = res.Name,
offset = -tev.offset / 1000f,
});
}
else throw new NotImplementedException("Key sounds are not supported yet");
}
else {
var rn = new Chart.Note() {
time = beat,
motions = new List<Chart.Motion> {
new Chart.Motion() { motion = "track:" + tev.column.ToString(CultureInfo.InvariantCulture) }
},
};
if (tev.endbeat != null) {
rn.endtime = new BeatTime(tev.endbeat[0], tev.endbeat[1], tev.endbeat[2]);
longEvents.Add(ev, new StartEventState {
Destination = rn,
Time = tm.Time,
});
}
group.notes.Add(rn);
}
}
else if (ev is MalodyChart.EndEvent) {
var tev = (MalodyChart.EndEvent)ev;
if (tev.StartEvent is MalodyChart.Note) {
var sev = tev.StartEvent;
longEvents.Remove(sev);
}
else throw new NotSupportedException("Unrecognized long event");
}
else throw new NotSupportedException("Unrecognized event");
}
var endbeat = tm.FractionalBeatTime;
endbeat.b += 4;
chart.endtime = endbeat;
meta.length = (float)tm.Time;
meta.note_count = group.notes.Count;
string chartName = string.Format("{0} - {1}", meta.song.name, meta.name);
if (src.meta.background != null) {
meta.cover = src.meta.background;
}
result.Add(new RawChartResource(chartName, chart, meta));
return result;
}
struct StartEventState {
public double Time { get; set; }
public ChartEvent Destination { get; set; }
}
#pragma warning disable IDE1006
struct MalodyChart {
public interface IEvent {
int[] beat { get; set; }
int[] endbeat { get; set; }
int Priority { get; }
}
public struct EndEvent : IEvent {
public int[] beat { get; set; }
public int[] endbeat { get; set; }
public IEvent StartEvent { get; set; }
public int Priority { get { return StartEvent.Priority - 1; } }
}
public Meta meta;
public struct Meta {
public SongInfo song;
public struct SongInfo {
public string title;
public string artist;
public string titleorg;
public string artistorg;
}
public string background;
public string creator;
public string version;
public int mode;
public ModeExt mode_ext;
public struct ModeExt {
public int column;
}
}
public List<Time> time;
public struct Time : IEvent {
public int[] beat { get; set; }
public int[] endbeat { get; set; }
public float bpm;
public int Priority { get { return -2; } }
}
public List<Effect> effect;
public struct Effect : IEvent {
public int[] beat { get; set; }
public int[] endbeat { get; set; }
public float? scroll;
public int Priority { get { return 0; } }
}
public List<Note> note;
public struct Note : IEvent {
public int[] beat { get; set; }
public int[] endbeat { get; set; }
public int column;
public string sound;
public int offset;
public int type;
public int Priority { get { return 0; } }
}
}
#pragma warning restore IDE1006
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: b3624013a6911ba45933085332724ff1
timeCreated: 1637936498
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,26 +0,0 @@
using Cryville.Common;
using Cryville.Crtr.Browsing;
using Microsoft.Win32;
using System;
using System.IO;
namespace Cryville.Crtr.Extensions.Malody {
public class MalodyChartFinder : LocalResourceFinder {
public override string Name { get { return "Malody beatmaps"; } }
public override string GetRootPath() {
switch (Environment.OSVersion.Platform) {
case PlatformID.Unix:
return "/storage/emulated/0/data/malody/beatmap";
case PlatformID.Win32NT:
var reg = Registry.ClassesRoot.OpenSubKey(@"malody\Shell\Open\Command");
if (reg == null) return null;
var pathObj = reg.GetValue(null);
if (pathObj == null) return null;
var path = (string)pathObj;
return Path.Combine(new FileInfo(StringUtils.GetProcessPathFromCommand(path)).Directory.FullName, "beatmap");
default: return null;
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3c9beaff62143a2468e18ad4642232c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,153 +0,0 @@
using Cryville.Crtr.Browsing;
using Quaver.API.Maps;
using Quaver.API.Maps.Structures;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
namespace Cryville.Crtr.Extensions.Quaver {
public class QuaverChartConverter : ResourceConverter {
static readonly string[] SUPPORTED_FORMATS = { ".qua" };
const double OFFSET = 0.05;
public override string[] GetSupportedFormats() {
return SUPPORTED_FORMATS;
}
public override IEnumerable<Resource> ConvertFrom(FileInfo file) {
List<Resource> result = new List<Resource>();
var src = Qua.Parse(file.FullName);
var ruleset = "quaver!" + src.Mode.ToString().ToLower();
var meta = new ChartMeta {
name = src.DifficultyName,
author = src.Creator,
song = new SongMetaInfo {
name = src.Title,
author = src.Artist,
},
ruleset = ruleset,
cover = src.BackgroundFile,
note_count = src.HitObjects.Count,
};
var chart = new Chart {
format = 2,
time = new BeatTime(-4, 0, 1),
ruleset = ruleset,
sigs = new List<Chart.Signature>(),
sounds = new List<Chart.Sound> {
new Chart.Sound { time = new BeatTime(0, 0, 1), id = src.Title, offset = (float)(src.TimingPoints[0].StartTime / 1e3 + OFFSET) }
},
motions = new List<Chart.Motion>(),
groups = new List<Chart.Group>(),
};
var group = new Chart.Group() {
tracks = new List<Chart.Track>(),
notes = new List<Chart.Note>(),
motions = new List<Chart.Motion>(),
};
chart.groups.Add(group);
result.Add(new RawChartResource(string.Format("{0} - {1}", meta.song.name, meta.name), chart, meta));
result.Add(new SongResource(meta.song.name, new FileInfo(Path.Combine(file.DirectoryName, src.AudioFile))));
var evs = new List<EventWrapper>();
foreach (var e in src.HitObjects) evs.Add(new EventWrapper.HitObject(e));
foreach (var e in src.SliderVelocities) evs.Add(new EventWrapper.SliderVelocity(e));
foreach (var e in src.SoundEffects) evs.Add(new EventWrapper.SoundEffect(e));
foreach (var e in src.TimingPoints) evs.Add(new EventWrapper.TimingPoint(e));
var evc = evs.Count;
for (int i = 0; i < evc; i++) if (evs[i].IsLong) evs.Add(new EventWrapper.EndEvent(evs[i]));
evs.Sort();
var longevs = new Dictionary<EventWrapper, ChartEvent>();
var tm = new TimeTimingModel(src.TimingPoints[0].StartTime / 1e3);
foreach (var ev in evs) {
tm.ForwardTo(ev.StartTime / 1e3);
if (ev is EventWrapper.HitObject) {
var tev = (EventWrapper.HitObject)ev;
var rn = new Chart.Note {
time = tm.FractionalBeatTime,
motions = new List<Chart.Motion> {
new Chart.Motion { motion = string.Format(CultureInfo.InvariantCulture, "track:{0}", tev.Event.Lane - 1) }
},
};
if (ev.IsLong) longevs.Add(ev, rn);
group.notes.Add(rn);
}
else if (ev is EventWrapper.SliderVelocity) {
var tev = (EventWrapper.SliderVelocity)ev;
group.motions.Add(new Chart.Motion {
time = tm.FractionalBeatTime,
motion = string.Format(CultureInfo.InvariantCulture, "svm:{0}", tev.Event.Multiplier),
});
}
else if (ev is EventWrapper.TimingPoint) {
var tev = (EventWrapper.TimingPoint)ev;
tm.BPM = tev.Event.Bpm;
chart.sigs.Add(new Chart.Signature {
time = tm.FractionalBeatTime,
tempo = tev.Event.Bpm,
});
}
else if (ev is EventWrapper.EndEvent) {
var tev = (EventWrapper.EndEvent)ev;
var oev = tev.Original;
longevs[oev].endtime = tm.FractionalBeatTime;
}
else throw new NotSupportedException("Sound effects are not supported yet");
}
var endbeat = tm.FractionalBeatTime;
endbeat.b += 4;
chart.endtime = endbeat;
meta.length = (float)tm.Time;
return result;
}
abstract class EventWrapper : IComparable<EventWrapper> {
public abstract int StartTime { get; }
public abstract int EndTime { get; }
public bool IsLong { get { return EndTime > 0; } }
public abstract int Priority { get; }
public int CompareTo(EventWrapper other) {
var c = StartTime.CompareTo(other.StartTime);
if (c != 0) return c;
return Priority.CompareTo(other.Priority);
}
public class HitObject : EventWrapper {
public HitObjectInfo Event;
public HitObject(HitObjectInfo ev) { Event = ev; }
public override int StartTime { get { return Event.StartTime; } }
public override int EndTime { get { return Event.EndTime; } }
public override int Priority { get { return 0; } }
}
public class SliderVelocity : EventWrapper {
public SliderVelocityInfo Event;
public SliderVelocity(SliderVelocityInfo ev) { Event = ev; }
public override int StartTime { get { return (int)Event.StartTime; } }
public override int EndTime { get { return 0; } }
public override int Priority { get { return 0; } }
}
public class SoundEffect : EventWrapper {
public SoundEffectInfo Event;
public SoundEffect(SoundEffectInfo ev) { Event = ev; }
public override int StartTime { get { return (int)Event.StartTime; } }
public override int EndTime { get { return 0; } }
public override int Priority { get { return 0; } }
}
public class TimingPoint : EventWrapper {
public TimingPointInfo Event;
public TimingPoint(TimingPointInfo ev) { Event = ev; }
public override int StartTime { get { return (int)Event.StartTime; } }
public override int EndTime { get { return 0; } }
public override int Priority { get { return -2; } }
}
public class EndEvent : EventWrapper {
public EventWrapper Original;
public EndEvent(EventWrapper ev) { if (!ev.IsLong) throw new ArgumentException("Event is not long"); Original = ev; }
public override int StartTime { get { return Original.EndTime; } }
public override int EndTime { get { return 0; } }
public override int Priority { get { return Original.Priority - 1; } }
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: b073cac7ce0d41a4f8ca589845678aa2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,24 +0,0 @@
using Cryville.Common;
using Cryville.Crtr.Browsing;
using Microsoft.Win32;
using System;
using System.IO;
namespace Cryville.Crtr.Extensions.Quaver {
public class QuaverChartFinder : LocalResourceFinder {
public override string Name { get { return "Quaver beatmaps"; } }
public override string GetRootPath() {
switch (Environment.OSVersion.Platform) {
case PlatformID.Win32NT:
var reg = Registry.ClassesRoot.OpenSubKey(@"quaver\Shell\Open\Command");
if (reg == null) return null;
var pathObj = reg.GetValue(null);
if (pathObj == null) return null;
var path = (string)pathObj;
return Path.Combine(new FileInfo(StringUtils.GetProcessPathFromCommand(path)).Directory.FullName, "Songs");
default: return null;
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 68bacf7746cbeea42a78a7d55cfdbea0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,56 +0,0 @@
using Cryville.Common.Math;
using System;
namespace Cryville.Crtr.Extensions {
public abstract class TimingModel {
public double Time { get; protected set; }
public double BeatTime { get; protected set; }
public BeatTime FractionalBeatTime { get; protected set; }
double m_bpm;
public double BPM { get { return m_bpm; } set { m_bpm = value; } }
public double BeatLength { get { return 60 / m_bpm; } set { m_bpm = 60 / value; } }
public TimingModel(double offset) {
Time = offset;
FractionalBeatTime = new BeatTime(0, 0, 1);
}
}
public class FractionalBeatTimeTimingModel : TimingModel {
public FractionalBeatTimeTimingModel(double offset = 0) : base(offset) { }
public void ForwardTo(BeatTime t) {
if (t == FractionalBeatTime) return;
if (BPM == 0) throw new InvalidOperationException("BPM not determined");
FractionalBeatTime = t;
var nt = t.Decimal;
Time += (nt - BeatTime) / BPM * 60;
BeatTime = nt;
}
}
public class BeatTimeTimingModel : TimingModel {
public BeatTimeTimingModel(double offset = 0) : base(offset) { }
public void ForwardTo(double t) {
if (t == BeatTime) return;
if (BPM == 0) throw new InvalidOperationException("BPM not determined");
Time += (t - BeatTime) / BPM * 60;
BeatTime = t;
FractionalBeatTime = ToBeatTime(t);
}
static BeatTime ToBeatTime(double beat, double error = 1e-4) {
int i, n, d;
FractionUtils.ToFraction(beat, error, out n, out d);
i = n / d; n %= d;
return new BeatTime(i, n, d);
}
}
public class TimeTimingModel : TimingModel {
public TimeTimingModel(double offset = 0) : base(offset) { }
public void ForwardTo(double t) {
if (t == Time) return;
if (BPM == 0) throw new InvalidOperationException("BPM not determined");
BeatTime += (t - Time) * BPM / 60;
int n, d;
FractionUtils.ToFraction(BeatTime, 1f / 48 / BPM * 60, out n, out d);
FractionalBeatTime = new BeatTime(n, d);
Time = t;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 5c4a1fab8f53dd742ba6501d682eb7f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b9bd9e24d7c553341a2a12391843542f
guid: 3fe9f91db8da80f459bcf70ff680644f
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -1,9 +1,10 @@
using Newtonsoft.Json;
using Cryville.Crtr.Extension;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Cryville.Crtr.Browsing {
namespace Cryville.Crtr.Extensions.Umg {
public class ChartResourceImporter : ResourceConverter {
static readonly string[] SUPPORTED_FORMATS = { ".umgc" };
public override string[] GetSupportedFormats() {
@@ -19,4 +20,13 @@ namespace Cryville.Crtr.Browsing {
}
}
}
public class ChartResource : FileResource {
public ChartResource(string name, FileInfo master) : base(name, master) {
using (var reader = new StreamReader(master.FullName)) {
var meta = JsonConvert.DeserializeObject<ChartMeta>(reader.ReadToEnd());
Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, meta.data + ".json")));
if (meta.cover != null) Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, meta.cover)));
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 168366bb891392b42a1d0a6bfa068ff3
guid: bfcd614ec96fbe543aa2b2f1630aac73
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,18 @@
using Cryville.Crtr.Extension;
using System.Collections.Generic;
namespace Cryville.Crtr.Extensions.Umg {
public class Extension : ExtensionInterface {
public override IEnumerable<ResourceConverter> GetResourceConverters() {
return new ResourceConverter[] {
new ChartResourceImporter(),
new RulesetResourceImporter(),
new SkinResourceImporter(),
};
}
public override IEnumerable<LocalResourceFinder> GetResourceFinders() {
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 323b670cbdea58644ac9ba20fc4c1a89
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,9 +1,10 @@
using Newtonsoft.Json;
using Cryville.Crtr.Extension;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Cryville.Crtr.Browsing {
namespace Cryville.Crtr.Extensions.Umg {
public class RulesetResourceImporter : ResourceConverter {
static readonly string[] SUPPORTED_FORMATS = { ".umgr" };
public override string[] GetSupportedFormats() {
@@ -17,4 +18,12 @@ namespace Cryville.Crtr.Browsing {
}
}
}
public class RulesetResource : FileResource {
public RulesetResource(string name, FileInfo master) : base(name, master) {
using (var reader = new StreamReader(master.FullName)) {
var meta = JsonConvert.DeserializeObject<Ruleset>(reader.ReadToEnd());
Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, meta.data + ".pdt")));
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f2c1531e76f19a647865f7ec335561cd
guid: 9e856b78a468f644191d62ab489ae089
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,34 @@
using Cryville.Crtr.Extension;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Cryville.Crtr.Extensions.Umg {
public class SkinResourceImporter : ResourceConverter {
static readonly string[] SUPPORTED_FORMATS = { ".umgs" };
public override string[] GetSupportedFormats() {
return SUPPORTED_FORMATS;
}
public override IEnumerable<Resource> ConvertFrom(FileInfo file) {
using (StreamReader reader = new StreamReader(file.FullName, Encoding.UTF8)) {
var data = JsonConvert.DeserializeObject<Skin>(reader.ReadToEnd());
return new Resource[] { new SkinResource(data.name, file) };
}
}
}
public class SkinResource : FileResource {
public string RulesetName { get; private set; }
public SkinResource(string name, FileInfo master) : base(name, master) {
using (var reader = new StreamReader(master.FullName)) {
var meta = JsonConvert.DeserializeObject<Skin>(reader.ReadToEnd());
RulesetName = meta.ruleset;
Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, meta.data + ".pdt")));
foreach (var frame in meta.frames) {
Attachments.Add(new FileInfo(Path.Combine(master.Directory.FullName, frame)));
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9904b4c21758c5046afc341fe2fa8845
guid: 2afd6abf146c2ee45ab749477b8c7fda
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,383 +0,0 @@
using Cryville.Crtr.Browsing;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
namespace Cryville.Crtr.Extensions.osu {
#pragma warning disable IDE1006
public class osuChartConverter : ResourceConverter {
#pragma warning restore IDE1006
static readonly string[] SUPPORTED_FORMATS = { ".osu" };
const double OFFSET = 0.05;
public override string[] GetSupportedFormats() {
return SUPPORTED_FORMATS;
}
public override IEnumerable<Resource> ConvertFrom(FileInfo file) {
List<Resource> result = new List<Resource>();
var meta = new ChartMeta { song = new SongMetaInfo() };
var group = new Chart.Group() {
tracks = new List<Chart.Track>(),
notes = new List<Chart.Note>(),
motions = new List<Chart.Motion>(),
};
var chart = new Chart {
format = 2,
time = new BeatTime(-4, 0, 1),
sigs = new List<Chart.Signature>(),
sounds = new List<Chart.Sound>(),
motions = new List<Chart.Motion>(),
groups = new List<Chart.Group> { group },
};
var diff = new DifficultyInfo();
var evs = new List<osuEvent>();
bool ftc = false;
using (var reader = new StreamReader(file.FullName, Encoding.UTF8)) {
Section section = Section.General;
int version;
bool flag = false;
string line;
while ((line = reader.ReadLine()) != null) {
if (!flag) {
if (line.StartsWith("osu file format v")) {
version = int.Parse(line.Substring(17), CultureInfo.InvariantCulture);
if (version > 14) throw new NotSupportedException("osu! chart format version too high");
else if (version < 5) throw new NotImplementedException("osu! chart format version too low"); // TODO apply offset
}
else throw new NotSupportedException("Unrecognized osu! chart format");
flag = true;
}
if (ShouldSkipLine(line)) continue;
if (section != Section.Metadata) line = StripComments(line);
line = line.TrimEnd();
if (line.StartsWith('[') && line.EndsWith(']')) {
Enum.TryParse(line.Substring(1, line.Length - 2), out section);
continue;
}
ParseLine(meta, chart, diff, evs, ref ftc, section, line);
}
}
if (meta.ruleset == "osu!mania") {
chart.ruleset = meta.ruleset += "." + diff.CircleSize.ToString(CultureInfo.InvariantCulture) + "k";
}
if (!ftc) throw new InvalidOperationException("Unconvertible chart: no timing point is present in this beatmap");
result.Add(new RawChartResource(string.Format("{0} - {1}", meta.song.name, meta.name), chart, meta));
var evc = evs.Count;
for (int i = 0; i < evc; i++) if (evs[i].IsLong) evs.Add(new osuEvent.EndEvent(evs[i]));
evs.Sort();
var longevs = new Dictionary<osuEvent, ChartEvent>();
Chart.Sound bgmEv = null;
TimeTimingModel tm = null;
foreach (var ev in evs) {
if (tm != null) tm.ForwardTo(ev.StartTime / 1e3);
if (ev is osuEvent.Audio) {
var tev = (osuEvent.Audio)ev;
chart.sounds.Add(bgmEv = new Chart.Sound { time = new BeatTime(0, 0, 1), id = meta.song.name });
result.Add(new SongResource(meta.song.name, new FileInfo(Path.Combine(file.DirectoryName, tev.AudioFile))));
}
else if (ev is osuEvent.Background) {
meta.cover = ((osuEvent.Background)ev).FileName;
}
else if (ev is osuEvent.EffectPoint) {
var tev = (osuEvent.EffectPoint)ev;
group.motions.Add(new Chart.Motion { time = tm.FractionalBeatTime, motion = string.Format(CultureInfo.InvariantCulture, "svm:{0}", tev.ScrollSpeed) });
}
else if (ev is osuEvent.EndEvent) {
if (tm == null) throw new InvalidOperationException("Unconvertible chart: timed event before first timing point");
var tev = (osuEvent.EndEvent)ev;
longevs[tev.Original].endtime = tm.FractionalBeatTime;
}
else if (ev is osuEvent.HOMania) {
if (tm == null) throw new InvalidOperationException("Unconvertible chart: timed event before first timing point");
var tev = (osuEvent.HOMania)ev;
var rn = new Chart.Note {
time = tm.FractionalBeatTime,
motions = new List<Chart.Motion> {
new Chart.Motion{ motion = string.Format(CultureInfo.InvariantCulture, "track:{0}", (int)(tev.X * diff.CircleSize / 512)) }
},
};
group.notes.Add(rn);
if (tev.IsLong) longevs.Add(tev, rn);
}
else if (ev is osuEvent.TimingChange) {
var tev = (osuEvent.TimingChange)ev;
if (tm == null) {
tm = new TimeTimingModel(tev.StartTime / 1e3);
bgmEv.offset = (float)(tev.StartTime / 1e3 + OFFSET);
}
tm.BeatLength = tev.BeatLength / 1e3;
chart.sigs.Add(new Chart.Signature {
time = tm.FractionalBeatTime,
tempo = (float)tm.BPM,
});
}
else throw new NotSupportedException("Unsupported event detected");
}
var endbeat = tm.FractionalBeatTime;
endbeat.b += 4;
chart.endtime = endbeat;
meta.length = (float)tm.Time;
meta.note_count = group.notes.Count;
return result;
}
void ParseLine(ChartMeta meta, Chart chart, DifficultyInfo diff, List<osuEvent> evs, ref bool ftc, Section section, string line) {
switch (section) {
case Section.General: HandleGeneral(meta, chart, evs, line); return;
case Section.Metadata: HandleMetadata(meta, line); return;
case Section.Difficulty: HandleDifficulty(diff, line); return;
case Section.Events: HandleEvent(evs, line); return;
case Section.TimingPoints: HandleTimingPoint(chart, evs, ref ftc, line); return;
case Section.HitObjects: HandleHitObject(evs, line); return;
}
}
void HandleGeneral(ChartMeta meta, Chart chart, List<osuEvent> evs, string line) {
var pair = SplitKeyVal(line);
switch (pair.Key) {
case @"AudioFilename":
evs.Add(new osuEvent.Audio { StartTime = double.NegativeInfinity, AudioFile = pair.Value });
break;
case @"Mode":
int rulesetID = int.Parse(pair.Value, CultureInfo.InvariantCulture);
var ruleset = "osu!";
switch (rulesetID) {
case 0: /*ruleset += "standard";*/ throw new NotImplementedException("osu!standard mode is not supported yet");
case 1: /*ruleset += "taiko";*/ throw new NotImplementedException("osu!taiko mode is not supported yet");
case 2: /*ruleset += "catch";*/ throw new NotImplementedException("osu!catch mode is not supported yet");
case 3: ruleset += "mania"; break;
}
meta.ruleset = chart.ruleset = ruleset;
break;
}
}
void HandleMetadata(ChartMeta meta, string line) {
var pair = SplitKeyVal(line);
switch (pair.Key) {
case @"Title": if (meta.song.name == null) meta.song.name = pair.Value; break;
case @"TitleUnicode": meta.song.name = pair.Value; break;
case @"Artist": if (meta.song.author == null) meta.song.author = pair.Value; break;
case @"ArtistUnicode": meta.song.author = pair.Value; break;
case @"Creator": meta.author = pair.Value; break;
case @"Version": meta.name = pair.Value; break;
}
}
void HandleDifficulty(DifficultyInfo diff, string line) {
var pair = SplitKeyVal(line);
switch (pair.Key) {
case @"CircleSize":
diff.CircleSize = float.Parse(pair.Value, CultureInfo.InvariantCulture);
break;
case @"SliderMultiplier":
diff.SliderMultiplier = double.Parse(pair.Value, CultureInfo.InvariantCulture);
break;
}
}
void HandleEvent(List<osuEvent> evs, string line) {
string[] split = line.Split(',');
if (!Enum.TryParse(split[0], out LegacyEventType type))
throw new InvalidDataException($@"Unknown event type: {split[0]}");
switch (type) {
case LegacyEventType.Sprite:
if (evs.Count == 0 || !(evs[evs.Count - 1] is osuEvent.Background))
evs.Add(new osuEvent.Background { StartTime = double.NegativeInfinity, FileName = CleanFilename(split[3]) });
break;
case LegacyEventType.Background:
evs.Add(new osuEvent.Background { StartTime = double.NegativeInfinity, FileName = CleanFilename(split[2]) });
break;
}
}
enum LegacyEventType {
Background = 0,
Video = 1,
Break = 2,
Colour = 3,
Sprite = 4,
Sample = 5,
Animation = 6
}
void HandleTimingPoint(Chart chart, List<osuEvent> evs, ref bool ftc, string line) {
string[] split = line.Split(',');
double time = double.Parse(split[0].Trim(), CultureInfo.InvariantCulture)/* + offset*/;
// beatLength is allowed to be NaN to handle an edge case in which some beatmaps use NaN slider velocity to disable slider tick generation (see LegacyDifficultyControlPoint).
double beatLength = double.Parse(split[1].Trim(), CultureInfo.InvariantCulture);
// If beatLength is NaN, speedMultiplier should still be 1 because all comparisons against NaN are false.
double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;
int timeSignature = 4;
if (split.Length >= 3)
timeSignature = split[2][0] == '0' ? 4 : int.Parse(split[2], CultureInfo.InvariantCulture);
bool timingChange = true;
if (split.Length >= 7)
timingChange = split[6][0] == '1';
//bool omitFirstBarSignature = false;
//if (split.Length >= 8) {
// int effectFlags = int.Parse(split[7], CultureInfo.InvariantCulture);
// omitFirstBarSignature = (effectFlags & 0x8) != 0;
//}
if (timingChange) {
if (double.IsNaN(beatLength))
throw new InvalidDataException("Beat length cannot be NaN in a timing control point");
var ev = new osuEvent.TimingChange { StartTime = time, BeatLength = beatLength, TimeSignature = timeSignature };
if (!ftc) {
ftc = true;
ev.StartTime = time % beatLength - beatLength;
}
evs.Add(ev);
}
// osu!taiko and osu!mania use effect points rather than difficulty points for scroll speed adjustments.
if (chart.ruleset == "osu!taiko" || chart.ruleset == "osu!mania")
evs.Add(new osuEvent.EffectPoint { StartTime = time, ScrollSpeed = speedMultiplier });
}
void HandleHitObject(List<osuEvent> evs, string line) {
string[] split = line.Split(',');
int posx = (int)float.Parse(split[0], CultureInfo.InvariantCulture);
// int posy = (int)float.Parse(split[1], CultureInfo.InvariantCulture);
double startTime = double.Parse(split[2], CultureInfo.InvariantCulture)/* + Offset*/;
LegacyHitObjectType type = (LegacyHitObjectType)int.Parse(split[3], CultureInfo.InvariantCulture);
// int comboOffset = (int)(type & LegacyHitObjectType.ComboOffset) >> 4;
type &= ~LegacyHitObjectType.ComboOffset;
// bool combo = type.HasFlag(LegacyHitObjectType.NewCombo);
type &= ~LegacyHitObjectType.NewCombo;
osuEvent.HitObject result;
if (type.HasFlag(LegacyHitObjectType.Circle)) {
result = new osuEvent.HOManiaHit { X = posx };
}
else if (type.HasFlag(LegacyHitObjectType.Hold)) {
double endTime = Math.Max(startTime, double.Parse(split[2], CultureInfo.InvariantCulture));
if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) {
string[] ss = split[5].Split(':');
endTime = Math.Max(startTime, double.Parse(ss[0], CultureInfo.InvariantCulture));
}
result = new osuEvent.HOManiaHold { X = posx, EndTime = endTime };
}
else throw new NotSupportedException(string.Format("Hit objects of type {0} is not supported yet", type));
if (result == null) throw new InvalidDataException($"Unknown hit object type: {split[3]}");
result.StartTime = startTime;
if (result != null) evs.Add(result);
}
[Flags]
enum LegacyHitObjectType {
Circle = 1,
Slider = 1 << 1,
NewCombo = 1 << 2,
Spinner = 1 << 3,
ComboOffset = (1 << 4) | (1 << 5) | (1 << 6),
Hold = 1 << 7
}
static bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith("//", StringComparison.Ordinal)
|| line.StartsWith(' ') || line.StartsWith('_');
protected string StripComments(string line) {
int index = line.IndexOf("//");
if (index > 0) return line.Substring(0, index);
return line;
}
KeyValuePair<string, string> SplitKeyVal(string line, char separator = ':', bool shouldTrim = true) {
string[] split = line.Split(separator, 2);
if (shouldTrim) {
for (int i = 0; i < split.Length; i++)
split[i] = split[i].Trim();
}
return new KeyValuePair<string, string> (
split[0],
split.Length > 1 ? split[1] : string.Empty
);
}
static string CleanFilename(string path) => path.Replace(@"\\", @"\").Trim('"');
enum Section {
General,
Editor,
Metadata,
Difficulty,
Events,
TimingPoints,
Colours,
HitObjects,
Variables,
Fonts,
CatchTheBeat,
Mania,
}
class DifficultyInfo {
public float CircleSize { get; internal set; }
public double SliderMultiplier { get; set; }
}
#pragma warning disable IDE1006 // Naming Styles
abstract class osuEvent : IComparable<osuEvent> {
public virtual double StartTime { get; set; }
public virtual double EndTime { get; set; }
public bool IsLong { get { return EndTime - StartTime > 0 && EndTime > 0; } }
public abstract int Priority { get; }
public int CompareTo(osuEvent other) {
var c = StartTime.CompareTo(other.StartTime);
if (c != 0) return c;
return Priority.CompareTo(other.Priority);
}
public class EndEvent : osuEvent {
public osuEvent Original;
public EndEvent(osuEvent ev) { if (!ev.IsLong) throw new ArgumentException("Event is not long"); Original = ev; }
public override double StartTime { get { return Original.EndTime; } }
public override double EndTime { get { return 0; } }
public override int Priority { get { return Original.Priority - 1; } }
}
public class Audio : osuEvent {
public string AudioFile { get; set; }
public override int Priority { get { return 0; } }
}
public class TimingChange : osuEvent {
public double BeatLength { get; set; }
public int TimeSignature { get; set; }
public override int Priority { get { return -4; } }
}
public class EffectPoint : osuEvent {
public double ScrollSpeed { get; set; }
public override int Priority { get { return -2; } }
}
public class Background : osuEvent {
public string FileName { get; set; }
public override int Priority { get { return 0; } }
}
public class HitObject : osuEvent {
public sealed override int Priority { get { return 0; } }
}
public class HOMania : HitObject {
public float X { get; set; }
}
public class HOManiaHit : HOMania { }
public class HOManiaHold : HOMania { }
}
#pragma warning restore IDE1006 // Naming Styles
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 82838dd8639c2244caf3c830edfbc59c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,24 +0,0 @@
using Cryville.Common;
using Cryville.Crtr.Browsing;
using Microsoft.Win32;
using System;
using System.IO;
namespace Cryville.Crtr.Extensions.osu {
public class osuChartFinder : LocalResourceFinder {
public override string Name { get { return "osu! beatmaps"; } }
public override string GetRootPath() {
switch (Environment.OSVersion.Platform) {
case PlatformID.Win32NT:
var reg = Registry.ClassesRoot.OpenSubKey(@"osu!\Shell\Open\Command");
if (reg == null) return null;
var pathObj = reg.GetValue(null);
if (pathObj == null) return null;
var path = (string)pathObj;
return Path.Combine(new FileInfo(StringUtils.GetProcessPathFromCommand(path)).Directory.FullName, "Songs");
default: return null;
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 365d879536c05284fa2335a7676c6cf4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -24,7 +24,7 @@ namespace Cryville.Crtr {
get;
private set;
}
public readonly static string FileProtocolPrefix
public static readonly string FileProtocolPrefix
#if UNITY_STANDALONE_WIN
= "file:///";
#elif UNITY_ANDROID
@@ -37,9 +37,9 @@ namespace Cryville.Crtr {
public static SimpleSequencerSource AudioSequencer;
public static SimpleSequencerSession AudioSession;
public static InputManager InputManager;
public readonly static NetworkTaskWorker NetworkTaskWorker = new NetworkTaskWorker();
public static readonly NetworkTaskWorker NetworkTaskWorker = new NetworkTaskWorker();
public readonly static JsonSerializerSettings GlobalJsonSerializerSettings
public static readonly JsonSerializerSettings GlobalJsonSerializerSettings
= new JsonSerializerSettings() {
DefaultValueHandling = DefaultValueHandling.Ignore,
};

View File

@@ -24,6 +24,7 @@ namespace Cryville.Crtr {
Components.Add("polysec", typeof(PolygonSGO));
Components.Add("rect", typeof(SpriteRect));
Components.Add("scale3", typeof(SpriteScale3));
Components.Add("sec", typeof(SectionalGameObject));
Components.Add("sprite", typeof(SpriteBase));
Components.Add("text", typeof(SpriteText));

View File

@@ -4,7 +4,8 @@ using Cryville.Common.Unity.Input;
using Cryville.Crtr.Config;
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.Serialization;
using RVector3 = UnityEngine.Vector3;
namespace Cryville.Crtr {
public class InputProxy : IDisposable {
@@ -12,12 +13,11 @@ namespace Cryville.Crtr {
readonly PdtRuleset _ruleset;
readonly Judge _judge;
public InputProxy(PdtRuleset ruleset, Judge judge) {
unsafe {
fixed (byte* ptr = _vecbuf) {
*(int*)(ptr + 3 * sizeof(float)) = PdtInternalType.Number;
}
for (int i = 0; i <= MAX_DEPTH; i++) {
var vecsrc = new InputVectorSrc();
_vecsrcs[i] = vecsrc;
_vecops[i] = new InputVectorOp(vecsrc);
}
_vecsrc = new PropSrc.Arbitrary(PdtInternalType.Vector, _vecbuf);
_etor = ChartPlayer.etor;
_ruleset = ruleset;
_judge = judge;
@@ -174,14 +174,58 @@ namespace Cryville.Crtr {
readonly object _lock = new object();
static readonly int _var_value = IdentifierManager.SharedInstance.Request("value");
static readonly PropOp.Arbitrary _arbop = new PropOp.Arbitrary();
readonly byte[] _vecbuf = new byte[3 * sizeof(float) + sizeof(int)];
readonly PropSrc.Arbitrary _vecsrc;
const int MAX_DEPTH = 15;
const int MAX_DIMENSION = 3;
readonly InputVectorSrc[] _vecsrcs = new InputVectorSrc[MAX_DEPTH + 1];
readonly InputVectorOp[] _vecops = new InputVectorOp[MAX_DEPTH + 1];
unsafe class InputVectorSrc : PropSrc.FixedBuffer {
public InputVectorSrc() : base(PdtInternalType.Vector, MAX_DIMENSION * sizeof(float) + sizeof(int)) {
fixed (byte* ptr = buf) {
*(int*)(ptr + MAX_DIMENSION * sizeof(float)) = PdtInternalType.Number;
}
}
public bool IsNull { get; set; }
public void Set(RVector3 vec) {
fixed (byte* _ptr = buf) {
*(RVector3*)_ptr = vec;
}
Invalidate();
}
}
class InputVectorOp : PropOp {
readonly InputVectorSrc _src;
public InputVectorOp(InputVectorSrc src) {
_src = src;
}
protected override void Execute() {
var op = GetOperand(0);
if (op.Type == PdtInternalType.Null) {
_src.IsNull = true;
}
else {
var vec = new RVector3();
int dim;
if (op.Type == PdtInternalType.Number) dim = 1;
else if (op.Type == PdtInternalType.Vector) {
int arrtype, _;
op.GetArraySuffix(out arrtype, out _);
if (arrtype != PdtInternalType.Number)
throw new InvalidCastException("Not a vector of numbers");
dim = Math.Min(3, (op.Length - sizeof(int)) / sizeof(float));
}
else throw new InvalidCastException("Invalid vector");
for (int i = 0; i < dim; i++) {
vec[i] = op.AsNumber(i * sizeof(float));
}
_src.IsNull = false;
_src.Set(vec);
}
}
}
readonly Dictionary<InputHandler, double> _timeOrigins = new Dictionary<InputHandler, double>();
readonly Dictionary<InputSource, int> _activeCounts = new Dictionary<InputSource, int>();
readonly Dictionary<InputIdentifier, float> _vect = new Dictionary<InputIdentifier, float>();
readonly Dictionary<ProxiedInputIdentifier, PropSrc> _vecs = new Dictionary<ProxiedInputIdentifier, PropSrc>();
static readonly PropSrc.Arbitrary _nullsrc = new PropSrc.Arbitrary(PdtInternalType.Null, new byte[0]);
double? _lockTime = null;
unsafe void OnInput(InputIdentifier id, InputVector vec) {
lock (_lock) {
@@ -191,15 +235,12 @@ namespace Cryville.Crtr {
float ft, tt = (float)(_lockTime != null ? _lockTime.Value : (vec.Time - _timeOrigins[id.Source.Handler]));
if (!_vect.TryGetValue(id, out ft)) ft = tt;
if (vec.IsNull) {
_etor.ContextCascadeUpdate(_var_value, _nullsrc);
_etor.ContextCascadeUpdate(_var_value, PropSrc.Null);
OnInput(id, proxy.Target, ft, tt, true);
}
else {
fixed (byte* ptr = _vecbuf) {
*(Vector3*)ptr = vec.Vector;
}
_vecsrc.Invalidate();
_etor.ContextCascadeUpdate(_var_value, _vecsrc);
_vecsrcs[0].Set(vec.Vector);
_etor.ContextCascadeUpdate(_var_value, _vecsrcs[0]);
OnInput(id, proxy.Target, ft, tt, false);
}
_vect[id] = tt;
@@ -209,21 +250,27 @@ namespace Cryville.Crtr {
}
static readonly int _var_fv = IdentifierManager.SharedInstance.Request("fv");
static readonly int _var_tv = IdentifierManager.SharedInstance.Request("tv");
unsafe void OnInput(InputIdentifier id, Identifier target, float ft, float tt, bool nullflag) {
unsafe void OnInput(InputIdentifier id, Identifier target, float ft, float tt, bool nullflag, int depth = 0) {
if (depth >= MAX_DEPTH) throw new InputProxyException("Input propagation limit reached\nThe ruleset has invalid input definitions");
var def = _ruleset.inputs[target];
if (def.pass != null) {
foreach (var p in def.pass) {
_etor.ContextCascadeInsert();
_arbop.Name = _var_value;
if (!nullflag) _etor.Evaluate(_arbop, p.Value);
OnInput(id, p.Key, ft, tt, nullflag);
bool newNullFlag = nullflag;
if (!newNullFlag) {
ChartPlayer.etor.Evaluate(_vecops[depth + 1], p.Value);
newNullFlag = _vecsrcs[depth + 1].IsNull;
if (newNullFlag) ChartPlayer.etor.ContextCascadeUpdate(_var_value, PropSrc.Null);
else ChartPlayer.etor.ContextCascadeUpdate(_var_value, _vecsrcs[depth + 1]);
}
OnInput(id, p.Key, ft, tt, newNullFlag, depth + 1);
_etor.ContextCascadeDiscard();
}
}
else {
var pid = new ProxiedInputIdentifier { Source = id, Target = target };
PropSrc fv, tv = _etor.ContextCascadeLookup(_var_value);
if (!_vecs.TryGetValue(pid, out fv)) fv = _nullsrc;
if (!_vecs.TryGetValue(pid, out fv)) fv = PropSrc.Null;
if (fv.Type != PdtInternalType.Null || tv.Type != PdtInternalType.Null) {
if (fv.Type == PdtInternalType.Null) _activeCounts[id.Source]++;
_etor.ContextCascadeInsert();
@@ -306,4 +353,12 @@ namespace Cryville.Crtr {
return !lhs.Equals(rhs);
}
}
[Serializable]
public class InputProxyException : Exception {
public InputProxyException() { }
public InputProxyException(string message) : base(message) { }
public InputProxyException(string message, Exception inner) : base(message, inner) { }
protected InputProxyException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
}

View File

@@ -4,6 +4,7 @@ using Cryville.Common.Pdt;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.Serialization;
using System.Text.Formatting;
namespace Cryville.Crtr {
@@ -134,7 +135,7 @@ namespace Cryville.Crtr {
if (actlist.Count > 0) {
_numbuf3 = ft; _numsrc3.Invalidate(); _etor.ContextCascadeUpdate(_var_ft, _numsrc3);
_numbuf4 = tt; _numsrc4.Invalidate(); _etor.ContextCascadeUpdate(_var_tt, _numsrc4);
var index = 0;
int index = 0, iter = 0;
while (index >= 0 && index < actlist.Count) {
var ev = actlist[index];
_numbuf1 = (float)ev.StartTime; _numsrc1.Invalidate(); _etor.ContextCascadeUpdate(_var_fn, _numsrc1);
@@ -154,19 +155,21 @@ namespace Cryville.Crtr {
if (index < 0) index = ~index;
}
else index++;
if (iter++ >= 16) throw new JudgePropagationException();
}
else index++;
}
}
}
bool Pass(JudgeEvent ev, float time, Identifier[] ids) {
bool Pass(JudgeEvent ev, float time, Identifier[] ids, int depth = 0) {
if (depth >= 16) throw new JudgePropagationException();
foreach (var i in ids) {
var def = _rs.judges[i];
if (def.hit != null) _etor.Evaluate(_flagop, def.hit);
else _flag = true;
if (_flag) {
if (def.scores != null) UpdateScore(def.scores);
if (def.pass != null) Pass(ev, time, def.pass);
if (def.pass != null) Pass(ev, time, def.pass, depth + 1);
ev.Handler.ReportJudge(ev, time, i);
return true;
}
@@ -323,4 +326,12 @@ namespace Cryville.Crtr {
public string format = "";
}
#endregion
[Serializable]
public class JudgePropagationException : Exception {
public JudgePropagationException() : base("Judge propagation limit reached\nThe ruleset has invalid judge definitions") { }
public JudgePropagationException(string message) : base(message) { }
public JudgePropagationException(string message, Exception inner) : base(message, inner) { }
protected JudgePropagationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
}

View File

@@ -360,7 +360,7 @@ namespace Cryville.Crtr {
public abstract void LerpWith(Vector start, float lerpedTime, ref Vector result);
public abstract float DelerpWith(Vector start, Vector value);
public abstract bool IsZero();
public override abstract string ToString();
public abstract override string ToString();
public abstract unsafe void ToArray(float* arr);
public Vector Clone() {
@@ -610,8 +610,8 @@ namespace Cryville.Crtr {
}
}
}
w = aw ? rw : (float?)null;
h = ah ? rh : (float?)null;
w = aw ? rw : null;
h = ah ? rh : null;
}
public static VecPtComp Parse(string s) {
@@ -951,16 +951,22 @@ namespace Cryville.Crtr {
}
}
public class VectorSrc : PropSrc.FixedBuffer {
public unsafe class VectorSrc : PropSrc.FixedBuffer {
const int MAX_DIMENSION = 8;
protected readonly Func<Vector> _cb;
public VectorSrc(Func<Vector> cb) : base(PdtInternalType.Vector, 8 * sizeof(float) + sizeof(int)) { _cb = cb; }
protected override unsafe void InternalGet() {
public VectorSrc(Func<Vector> cb) : base(PdtInternalType.Vector, MAX_DIMENSION * sizeof(float) + sizeof(int)) {
_cb = cb;
fixed (byte* rptr = buf) {
var ptr = (float*)rptr;
*(int*)(ptr + MAX_DIMENSION) = PdtInternalType.Number;
}
}
protected override void InternalGet() {
var v = _cb();
if (v.Dimension > 8) throw new NotSupportedException("Vector dimension too large");
if (v.Dimension > MAX_DIMENSION) throw new NotSupportedException("Vector dimension too large");
fixed (byte* rptr = buf) {
var ptr = (float*)rptr;
v.ToArray(ptr);
*(int*)(ptr + v.Dimension) = PdtInternalType.Number;
}
}
}
@@ -968,7 +974,7 @@ namespace Cryville.Crtr {
public class VectorOp : PropOp {
readonly Action<float[]> _cb;
public VectorOp(Action<float[]> cb) { _cb = cb; }
protected unsafe override void Execute() {
protected override unsafe void Execute() {
var op = GetOperand(0);
float[] values;
if (op.Type == PdtInternalType.Number) {
@@ -979,7 +985,7 @@ namespace Cryville.Crtr {
op.GetArraySuffix(out type, out _);
if (type != PdtInternalType.Number)
throw new InvalidOperationException("Not a vector of numbers");
values = new float[op.Length - sizeof(int)];
values = new float[(op.Length - sizeof(int)) / sizeof(float)];
fixed (float* ptr = values) {
op.CopyTo((byte*)ptr, 0, op.Length - sizeof(int));
}

View File

@@ -57,7 +57,7 @@ namespace Cryville.Crtr {
}
public override void Init() {
base.Init();
sgos = gogroup.GetComponentsInChildren<SectionalGameObject>();
sgos = RootTransform.GetComponentsInChildren<SectionalGameObject>();
foreach (var judge in judges) judge.Value.InitPropSrcs();
}
@@ -70,7 +70,7 @@ namespace Cryville.Crtr {
protected override void StartGraphicalUpdate(ContainerState s) {
base.StartGraphicalUpdate(s);
TransformAwake(s);
if (gogroup) {
if (RootTransform) {
if (Event.IsLong) {
foreach (var i in sgos) {
i.Reset();
@@ -79,10 +79,10 @@ namespace Cryville.Crtr {
}
else {
#if UNITY_5_6_OR_NEWER
gogroup.SetPositionAndRotation(Position, Rotation);
RootTransform.SetPositionAndRotation(Position, Rotation);
#else
gogroup.position = Position;
gogroup.rotation = Rotation;
RootTransform.position = Position;
RootTransform.rotation = Rotation;
#endif
}
}
@@ -96,7 +96,7 @@ namespace Cryville.Crtr {
if (s.CloneType <= 2) {
Position = GetFramePoint(s.Parent, s.Track);
Rotation = GetFrameRotation(s.Parent, s.Track);
if (s.CloneType == 2 && gogroup && Event.IsLong) {
if (s.CloneType == 2 && RootTransform && Event.IsLong) {
foreach (var i in sgos)
i.AppendPoint(Position, Rotation);
}
@@ -128,7 +128,7 @@ namespace Cryville.Crtr {
}
protected override void EndGraphicalUpdate(ContainerState s) {
if (gogroup) {
if (RootTransform) {
foreach (var i in sgos) i.Seal();
}
base.EndGraphicalUpdate(s);

View File

@@ -1,4 +1,5 @@
using Cryville.Common;
using Cryville.Common.Math;
using Cryville.Common.Pdt;
using Cryville.Crtr.Event;
using System;
@@ -17,19 +18,21 @@ namespace Cryville.Crtr {
Vector _vec;
static readonly int _var_w = IdentifierManager.SharedInstance.Request("w");
static readonly int _var_h = IdentifierManager.SharedInstance.Request("h");
static readonly int _var_current_time = IdentifierManager.SharedInstance.Request("current_time");
static readonly int _var_inf = IdentifierManager.SharedInstance.Request("inf");
static readonly int _var_true = IdentifierManager.SharedInstance.Request("true");
static readonly int _var_false = IdentifierManager.SharedInstance.Request("false");
static readonly int _var_null = IdentifierManager.SharedInstance.Request("null");
static readonly int _var_current_time = IdentifierManager.SharedInstance.Request("current_time");
protected override void GetVariable(int name, bool forced, out int type, out byte[] value) {
if (name == _var_w) { LoadNum(ChartPlayer.hitRect.width); type = PdtInternalType.Number; value = _numbuf; }
else if (name == _var_h) { LoadNum(ChartPlayer.hitRect.height); type = PdtInternalType.Number; value = _numbuf; }
else if (name == _var_inf) { LoadNum(float.PositiveInfinity); type = PdtInternalType.Number; value = _numbuf; }
else if (name == _var_true) { LoadNum(1); type = PdtInternalType.Number; value = _numbuf; }
else if (name == _var_false) { LoadNum(0); type = PdtInternalType.Number; value = _numbuf; }
else if (name == _var_null) { LoadIdent(0); type = PdtInternalType.Undefined; value = _numbuf; }
else {
var id = new Identifier(name);
PropSrc prop;
PropSrc prop; SkinVariable variable;
if (ContextEvent != null && ContextEvent.PropSrcs.TryGetValue(name, out prop)) {
prop.Get(out type, out value);
}
@@ -42,6 +45,9 @@ namespace Cryville.Crtr {
prop.Get(out type, out value);
RevokePotentialConstant();
}
else if (ContextSkinContainer != null && ContextSkinContainer.Variables.TryGetValue(name, out variable)) {
variable.Src.Get(out type, out value);
}
else if (ContextJudge != null && ContextJudge.TryGetScoreSrc(name, out prop)) {
prop.Get(out type, out value);
RevokePotentialConstant();
@@ -101,11 +107,12 @@ namespace Cryville.Crtr {
else throw new KeyNotFoundException(string.Format("Undefined collapse operator {0}", IdentifierManager.SharedInstance.Retrieve(name)));
}
public ChartEvent ContextEvent { private get; set; }
public ContainerState ContextState { private get; set; }
public Transform ContextTransform { private get; set; }
public Judge ContextJudge { private get; set; }
public PropSrc ContextSelfValue { private get; set; }
public ChartEvent ContextEvent { get; set; }
public ContainerState ContextState { get; set; }
public SkinContainer ContextSkinContainer { get; set; }
public Transform ContextTransform { get; set; }
public Judge ContextJudge { get; set; }
public PropSrc ContextSelfValue { get; set; }
readonly Stack<int> ContextCascadeBlocks = new Stack<int>();
public void ContextCascadeInsertBlock() {
@@ -151,6 +158,12 @@ namespace Cryville.Crtr {
_ctxops.Add(IdentifierManager.SharedInstance.Request("min"), new func_min(() => ContextSelfValue));
_ctxops.Add(IdentifierManager.SharedInstance.Request("max"), new func_max(() => ContextSelfValue));
_ctxops.Add(IdentifierManager.SharedInstance.Request("abs"), new func_abs(() => ContextSelfValue));
_ctxops.Add(IdentifierManager.SharedInstance.Request("anim"), new func_anim(() => ContextSelfValue));
_ctxops.Add(IdentifierManager.SharedInstance.Request("cubic_bezier"), new func_cubic_bezier(() => ContextSelfValue));
_ctxops.Add(IdentifierManager.SharedInstance.Request("ease"), new func_cubic_bezier_fixed(0.25f, 0.1f, 0.25f, 1f, () => ContextSelfValue));
_ctxops.Add(IdentifierManager.SharedInstance.Request("ease_in"), new func_cubic_bezier_fixed(0.42f, 0f, 1f, 1f, () => ContextSelfValue));
_ctxops.Add(IdentifierManager.SharedInstance.Request("ease_out"), new func_cubic_bezier_fixed(0f, 0f, 0.58f, 1f, () => ContextSelfValue));
_ctxops.Add(IdentifierManager.SharedInstance.Request("ease_in_out"), new func_cubic_bezier_fixed(0.42f, 0f, 0.58f, 1f, () => ContextSelfValue));
Func<int, PropSrc> cccb = k => ContextCascadeLookup(k);
_ctxops.Add(IdentifierManager.SharedInstance.Request("attack_timing"), new func_attack_timing(cccb));
@@ -267,6 +280,7 @@ namespace Cryville.Crtr {
protected override void Execute() {
var o0 = GetOperand(0);
int type = o0.Type;
if (type == PdtInternalType.Error) throw new InvalidOperationException("Error");
int len = o0.Length;
bool blit = !IsBlittable(type);
for (var i = 1; i < LoadedOperandCount; i++) {
@@ -490,6 +504,85 @@ namespace Cryville.Crtr {
ret.SetNumber(Mathf.Abs(arg));
}
}
class func_anim : PdtOperator {
readonly Func<PropSrc> _ctxcb;
public func_anim(Func<PropSrc> ctxcb) : base(3) {
_ctxcb = ctxcb;
}
protected override unsafe void Execute() {
var op1 = GetOperand(0);
var op2 = GetOperand(1);
var dim1 = GetDimension(op1);
var dim2 = GetDimension(op2);
var dim0 = Math.Min(dim1, dim2);
float time;
switch (LoadedOperandCount) {
case 2: time = oputil.AsNumber(_ctxcb()); break;
case 3: time = GetOperand(2).AsNumber(); break;
default: throw new ArgumentException("Argument count not 2 or 3");
}
if (dim0 == 1) {
GetReturnFrame(PdtInternalType.Number, sizeof(float))
.SetNumber(op1.AsNumber() * (1 - time) + op2.AsNumber() * time);
}
else {
var ret = GetReturnFrame(PdtInternalType.Vector, dim0 * sizeof(float) + sizeof(int));
for (int i = 0; i < dim0 * sizeof(float); i += sizeof(float)) {
ret.SetNumber(op1.AsNumber(i) * (1 - time) + op2.AsNumber(i) * time, i);
}
ret.SetArraySuffix(PdtInternalType.Number);
}
}
static int GetDimension(PdtVariableMemory op) {
switch (op.Type) {
case PdtInternalType.Number: return 1;
case PdtInternalType.Vector:
int arrtype, _;
op.GetArraySuffix(out arrtype, out _);
if (arrtype != PdtInternalType.Number)
throw new ArgumentException("Not animatable");
return (op.Length - sizeof(int)) / sizeof(float);
default: throw new ArgumentException("Not animatable");
}
}
}
class func_cubic_bezier : PdtOperator {
readonly Func<PropSrc> _ctxcb;
public func_cubic_bezier(Func<PropSrc> ctxcb) : base(5) {
_ctxcb = ctxcb;
}
protected override unsafe void Execute() {
float x1 = GetOperand(0).AsNumber(), y1 = GetOperand(1).AsNumber();
float x2 = GetOperand(2).AsNumber(), y2 = GetOperand(3).AsNumber();
float time;
switch (LoadedOperandCount) {
case 4: time = oputil.AsNumber(_ctxcb()); break;
case 5: time = GetOperand(4).AsNumber(); break;
default: throw new ArgumentException("Argument count not 4 or 5");
}
GetReturnFrame(PdtInternalType.Number, sizeof(float))
.SetNumber(CubicBezier.Evaluate(time, x1, y1, x2, y2, 1e-5f));
}
}
class func_cubic_bezier_fixed : PdtOperator {
readonly float x1, y1, x2, y2;
readonly Func<PropSrc> _ctxcb;
public func_cubic_bezier_fixed(float x1, float y1, float x2, float y2, Func<PropSrc> ctxcb) : base(1) {
this.x1 = x1; this.y1 = y1;
this.x2 = x2; this.y2 = y2;
_ctxcb = ctxcb;
}
protected override void Execute() {
float time;
switch (LoadedOperandCount) {
case 0: time = oputil.AsNumber(_ctxcb()); break;
case 1: time = GetOperand(0).AsNumber(); break;
default: throw new ArgumentException("Argument count not 0 or 1");
}
GetReturnFrame(PdtInternalType.Number, sizeof(float))
.SetNumber(CubicBezier.Evaluate(time, x1, y1, x2, y2, 1e-5f));
}
}
#endregion
#region Judge Functions
static readonly int _var_fn = IdentifierManager.SharedInstance.Request("fn");
@@ -558,7 +651,7 @@ namespace Cryville.Crtr {
}
}
#endregion
unsafe static class oputil {
static unsafe class oputil {
public static float AsNumber(PropSrc src) {
if (src == null) throw new ArgumentNullException("src");
int type; byte[] value;

View File

@@ -63,7 +63,7 @@ namespace Cryville.Crtr {
public class StringArray : PropOp {
readonly Action<string[]> _cb;
public StringArray(Action<string[]> cb) { _cb = cb; }
protected unsafe override void Execute() {
protected override unsafe void Execute() {
var op = GetOperand(0);
int arrtype; int len;
op.GetArraySuffix(out arrtype, out len);
@@ -100,22 +100,25 @@ namespace Cryville.Crtr {
}
}
public class Enum<T> : PropOp {
readonly static Dictionary<int, int> _cache = new Dictionary<int, int>();
static readonly Dictionary<int, int> _cache = new Dictionary<int, int>();
readonly Action<T> _cb;
readonly Func<int, T> _caster;
public Enum(Action<T> cb, Func<int, T> caster) {
static Enum() {
if (!typeof(T).IsEnum)
throw new ArgumentException("Type is not enum");
var names = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);
for (int i = 0; i < names.Length; i++)
_cache[IdentifierManager.SharedInstance.Request(names[i].Name)] = Convert.ToInt32(names[i].GetValue(null));
}
public Enum(Action<T> cb, Func<int, T> caster) {
_cb = cb;
_caster = caster;
}
protected override void Execute() {
int result = 0;
for (int i = 0; i < LoadedOperandCount; i++)
result |= _cache[GetOperand(0).AsIdentifier()];
result |= _cache[GetOperand(i).AsIdentifier()];
_cb(_caster(result));
}
}

View File

@@ -54,6 +54,7 @@ namespace Cryville.Crtr {
}
}
}
public static readonly PropSrc Null = new Arbitrary(PdtInternalType.Null, new byte[0]);
public class String : PropSrc {
readonly Func<string> _cb;
public String(Func<string> cb) : base(PdtInternalType.String) { _cb = cb; }

View File

@@ -1,6 +1,7 @@
using Cryville.Common;
using Cryville.Common.Collections.Generic;
using Cryville.Common.Pdt;
using Cryville.Crtr.Browsing;
using Cryville.Crtr.Extension;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -64,9 +65,9 @@ namespace Cryville.Crtr {
public class Constraint {
static readonly PropOp.Arbitrary _arbop = new PropOp.Arbitrary();
[ElementList]
public Dictionary<RulesetSelectors, Constraint> Elements = new Dictionary<RulesetSelectors, Constraint>();
public PairList<RulesetSelectors, Constraint> Elements = new PairList<RulesetSelectors, Constraint>();
[PropertyList]
public Dictionary<PropertyKey, PdtExpression> Properties = new Dictionary<PropertyKey, PdtExpression>();
public PairList<PropertyKey, PdtExpression> Properties = new PairList<PropertyKey, PdtExpression>();
public void Optimize(PdtEvaluatorBase etor) {
foreach (var e in Properties) {
etor.Optimize(e.Value);

View File

@@ -1,6 +1,7 @@
using Cryville.Common;
using Cryville.Common.Collections.Generic;
using Cryville.Common.Pdt;
using Cryville.Crtr.Browsing;
using Cryville.Crtr.Extension;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -60,7 +61,9 @@ namespace Cryville.Crtr {
var effect = e.Value;
etor.ContextCascadeInsert();
etor.ContextCascadeUpdate(EffectInstance._VAR_EFFECT_INDEX, PropSrc.Error);
etor.Optimize(effect.duration);
foreach(var s in effect.states) {
etor.Optimize(s.Value.duration);
}
effect.elements.Optimize(etor);
etor.ContextCascadeDiscard();
}
@@ -70,12 +73,12 @@ namespace Cryville.Crtr {
public class SkinElement {
[ElementList]
public Dictionary<SkinSelectors, SkinElement> elements
= new Dictionary<SkinSelectors, SkinElement>();
public PairList<SkinSelectors, SkinElement> elements
= new PairList<SkinSelectors, SkinElement>();
[PropertyList]
public Dictionary<SkinPropertyKey, PdtExpression> properties
= new Dictionary<SkinPropertyKey, PdtExpression>();
public PairList<SkinPropertyKey, PdtExpression> properties
= new PairList<SkinPropertyKey, PdtExpression>();
public bool IsDynamic {
get;
@@ -99,18 +102,38 @@ namespace Cryville.Crtr {
}
public class EffectDefinition {
public PdtExpression duration;
static Identifier _ident_init = new Identifier("init");
#pragma warning disable IDE1006
public PdtExpression duration {
set {
EffectState s;
if (!states.TryGetValue(_ident_init, out s))
throw new InvalidOperationException("Cannot set duration and states at the same time");
s.duration = value;
}
}
#pragma warning restore IDE1006
public Identifier init = _ident_init;
public Dictionary<Identifier, EffectState> states = new Dictionary<Identifier, EffectState> {
{ _ident_init, new EffectState() { rewind = _ident_init } }
};
public SkinElement elements;
}
public class EffectState {
public PdtExpression duration;
public Identifier rewind;
public Identifier next;
}
public class AnimationSpan {
[ElementList]
public Dictionary<Clip, AnimationSpan> spans
= new Dictionary<Clip, AnimationSpan>();
public PairList<Clip, AnimationSpan> spans
= new PairList<Clip, AnimationSpan>();
[PropertyList]
public Dictionary<SkinPropertyKey, PdtExpression> properties
= new Dictionary<SkinPropertyKey, PdtExpression>();
public PairList<SkinPropertyKey, PdtExpression> properties
= new PairList<SkinPropertyKey, PdtExpression>();
public void Optimize(PdtEvaluator etor) {
foreach (var p in properties) {

View File

@@ -5,8 +5,12 @@ using UnityEngine.Profiling;
namespace Cryville.Crtr {
public class SkinContainer {
readonly ISkinnableGroup _group;
readonly SkinElement _rootElement;
readonly DynamicStack[] _stacks = new DynamicStack[2];
readonly HashSet<SkinPropertyKey> _once = new HashSet<SkinPropertyKey>();
public readonly Dictionary<int, SkinVariable> Variables = new Dictionary<int, SkinVariable>();
class DynamicStack {
public readonly List<DynamicProperty> Properties = new List<DynamicProperty>();
public readonly List<DynamicElement> Elements = new List<DynamicElement>();
@@ -25,22 +29,24 @@ namespace Cryville.Crtr {
public SkinSelectors Selectors { get; set; }
public SkinElement Element { get; set; }
}
public SkinContainer(SkinElement rootElement) {
public SkinContainer(ISkinnableGroup group, SkinElement rootElement) {
_group = group;
_rootElement = rootElement;
for (int i = 0; i < _stacks.Length; i++) _stacks[i] = new DynamicStack();
_rtimeSrc = new PropSrc.Float(() => _rtime);
}
public void MatchStatic(ISkinnableGroup group) {
public void MatchStatic() {
var stack = _stacks[0];
stack.Clear();
MatchStatic(_rootElement, group, stack, new RuntimeSkinContext(group.SkinContext));
MatchStatic(_rootElement, stack, new RuntimeSkinContext(_group.SkinContext));
}
void MatchStatic(SkinElement rel, ISkinnableGroup group, DynamicStack stack, RuntimeSkinContext ctx) {
void MatchStatic(SkinElement rel, DynamicStack stack, RuntimeSkinContext ctx) {
var rc = ctx.ReadContext;
ChartPlayer.etor.ContextTransform = rc.Transform;
if (rc.PropSrcs != null) ChartPlayer.etor.ContextCascadeInsert(rc.PropSrcs);
foreach (var p in rel.properties) {
try {
p.Key.ExecuteStatic(group, ctx, p.Value);
p.Key.ExecuteStatic(_group, ctx, p.Value, Variables);
}
catch (EvaluationFailureException) { }
if (p.Key.IsValueRequired && !p.Value.IsConstant) stack.Properties.Add(
@@ -50,13 +56,13 @@ namespace Cryville.Crtr {
ChartPlayer.etor.ContextTransform = null;
foreach (var e in rel.elements) {
try {
var nctxs = e.Key.MatchStatic(group, rc);
var nctxs = e.Key.MatchStatic(_group, rc);
if (nctxs != null) {
var roflag = e.Key.annotations.Contains("if");
var woflag = e.Key.annotations.Contains("then");
foreach (var nctx in nctxs) {
var nrctx = new RuntimeSkinContext(nctx, ctx, roflag, woflag);
MatchStatic(e.Value, group, stack, nrctx);
MatchStatic(e.Value, stack, nrctx);
}
}
}
@@ -68,15 +74,19 @@ namespace Cryville.Crtr {
}
if (rc.PropSrcs != null) ChartPlayer.etor.ContextCascadeDiscard();
}
public void MatchDynamic(ISkinnableGroup group, int dl) {
public void MatchDynamic(int dl, bool recursive = false) {
var stack = _stacks[dl];
if (stack.Properties.Count == 0 && stack.Elements.Count == 0) return;
var nstack = dl + 1 < _stacks.Length ? _stacks[dl + 1] : null;
if (nstack != null) nstack.Clear();
Profiler.BeginSample("SkinContainer.MatchDynamic");
if (!recursive) ChartPlayer.etor.ContextSkinContainer = this;
for (int i = 0; i < stack.Properties.Count; i++) {
DynamicProperty p = stack.Properties[i];
p.Key.ExecuteDynamic(group, p.Context, p.Value, dl);
p.Key.ExecuteDynamic(_group, p.Context, p.Value, Variables, dl);
if (p.Key.annotations.Contains("once")) {
stack.Properties.RemoveAt(i--);
}
}
for (int i = 0; i < stack.Elements.Count; i++) {
DynamicElement e = stack.Elements[i];
@@ -84,14 +94,14 @@ namespace Cryville.Crtr {
if (psrcs != null) ChartPlayer.etor.ContextCascadeInsert(psrcs);
SkinContext nctx = null;
try {
nctx = e.Selectors.MatchDynamic(group, e.Context.ReadContext);
nctx = e.Selectors.MatchDynamic(_group, e.Context.ReadContext);
}
catch (SelectorNotAvailableException) {
if (nstack == null) throw;
nstack.Elements.Add(e);
}
if (nctx != null) {
MatchDynamic(e.Element, group, dl, nstack, new RuntimeSkinContext(
MatchDynamic(e.Element, dl, nstack, new RuntimeSkinContext(
nctx, e.Context, e.Selectors.annotations.Contains("if"), e.Selectors.annotations.Contains("then")
));
if (e.Selectors.annotations.Contains("once")) {
@@ -100,20 +110,28 @@ namespace Cryville.Crtr {
}
if (psrcs != null) ChartPlayer.etor.ContextCascadeDiscard();
}
if (!recursive) ChartPlayer.etor.ContextSkinContainer = null;
Profiler.EndSample();
}
void MatchDynamic(SkinElement rel, ISkinnableGroup group, int dl, DynamicStack stack, RuntimeSkinContext ctx) {
void MatchDynamic(SkinElement rel, int dl, DynamicStack stack, RuntimeSkinContext ctx) {
var rc = ctx.ReadContext;
ChartPlayer.etor.ContextTransform = rc.Transform;
if (rc.PropSrcs != null) ChartPlayer.etor.ContextCascadeInsert(rc.PropSrcs);
foreach (var p in rel.properties) {
p.Key.ExecuteDynamic(group, ctx, p.Value, dl);
if (p.Key.annotations.Contains("once")) {
if (_once.Contains(p.Key)) continue;
p.Key.ExecuteDynamic(_group, ctx, p.Value, Variables, dl);
_once.Add(p.Key);
}
else {
p.Key.ExecuteDynamic(_group, ctx, p.Value, Variables, dl);
}
}
ChartPlayer.etor.ContextTransform = null;
foreach (var e in rel.elements) {
if (e.Key.IsUpdatable(group, dl)) {
SkinContext nctx = e.Key.MatchDynamic(group, rc);
if (nctx != null) MatchDynamic(e.Value, group, dl, stack, new RuntimeSkinContext(
if (e.Key.IsUpdatable(_group, dl)) {
SkinContext nctx = e.Key.MatchDynamic(_group, rc);
if (nctx != null) MatchDynamic(e.Value, dl, stack, new RuntimeSkinContext(
nctx, ctx, e.Key.annotations.Contains("if"), e.Key.annotations.Contains("then")
));
}
@@ -126,6 +144,26 @@ namespace Cryville.Crtr {
}
if (rc.PropSrcs != null) ChartPlayer.etor.ContextCascadeDiscard();
}
float _rtime;
readonly PropSrc _rtimeSrc;
public void MatchAnimation(AnimationSpan span, float rtime, RuntimeSkinContext ctx) {
ChartPlayer.etor.ContextSkinContainer = this;
ChartPlayer.etor.ContextSelfValue = _rtimeSrc;
MatchAnimationInternal(span, rtime, ctx);
ChartPlayer.etor.ContextSelfValue = null;
ChartPlayer.etor.ContextSkinContainer = null;
}
void MatchAnimationInternal(AnimationSpan span, float rtime, RuntimeSkinContext ctx) {
_rtime = rtime;
_rtimeSrc.Invalidate();
foreach (var p in span.properties) {
p.Key.ExecuteDynamic(_group, ctx, p.Value, Variables, 0);
}
foreach (var s in span.spans) {
if (rtime < s.Key.Behind || rtime >= s.Key.Ahead) continue;
MatchAnimationInternal(s.Value, (rtime - s.Key.Behind) / (s.Key.Ahead - s.Key.Behind), ctx);
}
}
}
public class SkinContext {
public Transform Transform { get; private set; }
@@ -164,9 +202,22 @@ namespace Cryville.Crtr {
public interface ISkinnableGroup {
string TypeName { get; }
SkinContext SkinContext { get; }
Anchor OpenedAnchor { get; }
int OpenedAnchorName { get; }
bool TryGetAnchorsByName(int name, out IReadOnlyCollection<Anchor> result);
void RegisterAnchor(int name);
void PushAnchorEvent(double time, int name);
}
public class SkinVariable {
float _value;
public PropOp Op { get; private set; }
public PropSrc Src { get; private set; }
public SkinVariable() {
Op = new PropOp.Float(Set);
Src = new PropSrc.Float(() => _value);
}
void Set(float value) {
_value = value;
Src.Invalidate();
}
}
}

View File

@@ -1,6 +1,4 @@
using Cryville.Common;
using Cryville.Common.Pdt;
using Cryville.Crtr.Components;
using Cryville.Common.Pdt;
using System;
using System.Collections.Generic;
using System.Reflection;
@@ -58,41 +56,7 @@ namespace Cryville.Crtr {
case ';':
case ':':
if (invalidKeyFlag) throw new FormatException("Invalid key format");
if (a.Contains("has")) {
if (k.Count != 1) throw new FormatException("Invalid anchor name");
return new SkinPropertyKey.CreateAnchor {
Name = IdentifierManager.SharedInstance.Request(k[0])
};
}
else if (a.Contains("at")) {
if (k.Count != 1) throw new FormatException("Invalid anchor name");
return new SkinPropertyKey.SetAnchor {
Name = IdentifierManager.SharedInstance.Request(k[0])
};
}
else if (a.Contains("emit")) {
if (k.Count != 1) throw new FormatException("Invalid effect name");
return new SkinPropertyKey.EmitEffect {
Name = IdentifierManager.SharedInstance.Request(k[0])
};
}
switch (k.Count) {
case 1:
if (compKeyFlag) return new SkinPropertyKey.CreateComponent {
Component = GetComponentByName(k[0])
};
else return new SkinPropertyKey.SetProperty {
Component = typeof(TransformInterface),
Name = IdentifierManager.SharedInstance.Request(k[0])
};
case 2:
return new SkinPropertyKey.SetProperty {
Component = GetComponentByName(k[0]),
Name = IdentifierManager.SharedInstance.Request(k[1])
};
default:
throw new FormatException("Unknown error"); // Unreachable
}
return SkinPropertyKey.Construct(a, k, compKeyFlag);
case '{':
return new SkinSelectors(s, a);
case '}':
@@ -127,7 +91,7 @@ namespace Cryville.Crtr {
if (cc != '{') throw new FormatException("Invalid span format");
return new Clip(start, end);
}
k.Clear();
a.Clear(); k.Clear();
while (true) {
int pp = Position;
switch (cc) {
@@ -139,20 +103,7 @@ namespace Cryville.Crtr {
break;
case ';':
case ':':
switch (k.Count) {
case 1:
return new SkinPropertyKey.SetProperty {
Component = typeof(TransformInterface),
Name = IdentifierManager.SharedInstance.Request(k[0])
};
case 2:
return new SkinPropertyKey.SetProperty {
Component = GetComponentByName(k[0]),
Name = IdentifierManager.SharedInstance.Request(k[1])
};
default:
throw new FormatException("Unknown error"); // Unreachable
}
return SkinPropertyKey.Construct(a, k, false);
case '{':
throw new FormatException("Invalid token");
case '}':
@@ -167,10 +118,5 @@ namespace Cryville.Crtr {
if (Position == pp) throw new FormatException("Invalid selector or key format");
}
}
static Type GetComponentByName(string name) {
Type result;
if (GenericResources.Components.TryGetValue(name, out result)) return result;
throw new ArgumentException(string.Format("Component type \"{0}\" not found", name));
}
}
}

View File

@@ -2,38 +2,87 @@
using Cryville.Common.Pdt;
using Cryville.Crtr.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Cryville.Crtr {
public abstract class SkinPropertyKey {
public static SkinPropertyKey Construct(HashSet<string> a, IReadOnlyList<string> k, bool compKeyFlag) {
if (a.Remove("has")) {
if (k.Count != 1) throw new FormatException("Invalid anchor name");
return new CreateAnchor(a, IdentifierManager.SharedInstance.Request(k[0]));
}
else if (a.Remove("at")) {
if (k.Count != 1) throw new FormatException("Invalid anchor name");
return new SetAnchor(a, IdentifierManager.SharedInstance.Request(k[0]));
}
else if (a.Remove("emit")) {
if (k.Count != 1) throw new FormatException("Invalid effect name");
return new EmitEffect(a, IdentifierManager.SharedInstance.Request(k[0]));
}
else if (a.Remove("emit_self")) {
if (k.Count != 1) throw new FormatException("Invalid effect name");
return new EmitEffect(a, IdentifierManager.SharedInstance.Request(k[0]), true);
}
else if (a.Remove("var")) {
if (k.Count != 1) throw new FormatException("Invalid variable name");
return new SetVariable(a, IdentifierManager.SharedInstance.Request(k[0]));
}
switch (k.Count) {
case 1:
if (compKeyFlag) return new CreateComponent(a, GetComponentByName(k[0]));
else return new SetProperty(a, typeof(TransformInterface), IdentifierManager.SharedInstance.Request(k[0]));
case 2:
return new SetProperty(a, GetComponentByName(k[0]), IdentifierManager.SharedInstance.Request(k[1]));
default:
throw new FormatException("Unknown error");
}
static Type GetComponentByName(string name) {
Type result;
if (GenericResources.Components.TryGetValue(name, out result)) return result;
throw new ArgumentException(string.Format("Component type \"{0}\" not found", name));
}
}
public readonly HashSet<string> annotations;
public SkinPropertyKey(IEnumerable<string> a) {
annotations = a.ToHashSet();
}
public abstract override string ToString();
public abstract bool IsValueRequired { get; }
public abstract void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp);
public abstract void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, int dl);
public abstract void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars);
public abstract void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars, int dl);
public class CreateComponent : SkinPropertyKey {
public Type Component { get; set; }
public Type Component { get; private set; }
public CreateComponent(IEnumerable<string> a, Type component) : base(a) {
Component = component;
}
public override string ToString() {
return string.Format("*{0}", Component.Name);
}
public override bool IsValueRequired { get { return false; } }
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp) {
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars) {
ctx.WriteTransform.gameObject.AddComponent(Component);
}
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, int dl) {
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars, int dl) {
throw new InvalidOperationException("Component creation in dynamic context is not allowed");
}
}
public class SetProperty : SkinPropertyKey {
public Type Component { get; set; }
public int Name { get; set; }
public Type Component { get; private set; }
public int Name { get; private set; }
public SetProperty(IEnumerable<string> a, Type component, int name) : base(a) {
Component = component;
Name = name;
}
public override string ToString() {
return string.Format("{0}.{1}", Component.Name, IdentifierManager.SharedInstance.Retrieve(Name));
}
public override bool IsValueRequired { get { return true; } }
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp) {
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars) {
Execute(ctx, GetPropOp(ctx.WriteTransform).Operator, exp);
}
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, int dl) {
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars, int dl) {
var prop = GetPropOp(ctx.WriteTransform);
if (dl > prop.UpdateDynamicLevel) return;
Execute(ctx, prop.Operator, exp);
@@ -61,33 +110,37 @@ namespace Cryville.Crtr {
}
}
public class CreateAnchor : SkinPropertyKey {
public int Name { get; set; }
public int Name { get; private set; }
public CreateAnchor(IEnumerable<string> a, int name) : base(a) {
Name = name;
}
public override string ToString() {
return string.Format("@has {0}", IdentifierManager.SharedInstance.Retrieve(Name));
}
public override bool IsValueRequired { get { return false; } }
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp) {
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars) {
group.RegisterAnchor(Name);
}
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, int dl) {
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars, int dl) {
throw new InvalidOperationException("Anchor creation in dynamic context is not allowed");
}
}
public class SetAnchor : SkinPropertyKey {
public int Name { get; set; }
public SetAnchor() {
public int Name { get; private set; }
public SetAnchor(IEnumerable<string> a, int name) : base(a) {
Name = name;
_timeOp = new PropOp.Float(v => _time = v);
}
public override string ToString() {
return string.Format("@at {0}", IdentifierManager.SharedInstance.Retrieve(Name));
}
public override bool IsValueRequired { get { return true; } }
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp) {
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars) {
throw new InvalidOperationException("Setting anchor in static context is not allowed");
}
float _time;
readonly PropOp _timeOp;
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, int dl) {
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars, int dl) {
if (dl > 0) return;
var psrcs = ctx.ReadContext.PropSrcs;
if (psrcs != null) ChartPlayer.etor.ContextCascadeInsert(psrcs);
@@ -97,22 +150,48 @@ namespace Cryville.Crtr {
}
}
public class EmitEffect : SkinPropertyKey {
public int Name { get; set; }
public EmitEffect() {
public int Name { get; private set; }
public bool IsSelf { get; private set; }
public EmitEffect(IEnumerable<string> a, int name, bool isSelf = false) : base(a) {
Name = name;
IsSelf = isSelf;
_op = new PropOp.Float(v => _index = v);
}
public override string ToString() {
return string.Format("@emit {0}", IdentifierManager.SharedInstance.Retrieve(Name));
return string.Format(IsSelf ? "@emit_self {0}" : "@emit {0}", IdentifierManager.SharedInstance.Retrieve(Name));
}
public override bool IsValueRequired { get { return true; } }
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp) {
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars) {
throw new InvalidOperationException("Emitting effect in static context is not allowed");
}
float _index;
readonly PropOp _op;
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, int dl) {
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars, int dl) {
ChartPlayer.etor.Evaluate(_op, exp);
ChartPlayer.effectManager.Emit(Name, _index);
if (IsSelf) ChartPlayer.effectManager.EmitSelf(Name, _index, ctx.WriteTransform);
else ChartPlayer.effectManager.Emit(Name, _index);
}
}
public class SetVariable : SkinPropertyKey {
public int Name { get; private set; }
public SetVariable(IEnumerable<string> a, int name) : base(a) {
Name = name;
}
public override string ToString() {
return string.Format("@var {0}", IdentifierManager.SharedInstance.Retrieve(Name));
}
public override bool IsValueRequired { get { return true; } }
public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars) {
SkinVariable v;
if (!vars.TryGetValue(Name, out v))
vars.Add(Name, v = new SkinVariable());
ChartPlayer.etor.Evaluate(v.Op, exp);
}
public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary<int, SkinVariable> vars, int dl) {
SkinVariable v;
if (!vars.TryGetValue(Name, out v))
throw new InvalidOperationException(string.Format("Variable \"{0}\" not defined", IdentifierManager.SharedInstance.Retrieve(Name)));
ChartPlayer.etor.Evaluate(v.Op, exp);
}
}
}

View File

@@ -101,7 +101,7 @@ namespace Cryville.Crtr {
public override string ToString() { return string.Format("..{0}", IdentifierManager.SharedInstance.Retrieve(Name)); }
public override SkinContext MatchDynamic(ISkinnableGroup g, SkinContext c) {
return g.OpenedAnchor != null && g.OpenedAnchor.Name == Name ? c : null;
return g.OpenedAnchorName == Name ? c : null;
}
public override bool IsUpdatable(ISkinnableGroup g, int dl) {
return dl >= 1;

View File

@@ -16,7 +16,7 @@ namespace Cryville.Crtr {
public override void Init() {
base.Init();
sgos = gogroup.GetComponentsInChildren<SectionalGameObject>();
sgos = RootTransform.GetComponentsInChildren<SectionalGameObject>();
}
SectionalGameObject[] sgos;
Vector3 bpos; Quaternion brot;
@@ -33,7 +33,7 @@ namespace Cryville.Crtr {
}
protected override void StartGraphicalUpdate(ContainerState s) {
base.StartGraphicalUpdate(s);
if (gogroup) {
if (RootTransform) {
TransformAwake(s);
var p = GetCurrentWorldPoint();
foreach (var i in sgos) {
@@ -82,7 +82,7 @@ namespace Cryville.Crtr {
ptime = s.Time;
if (!gogroup || s.CloneType == 3) return;
if (!RootTransform || s.CloneType == 3) return;
var p = GetCurrentWorldPoint();
foreach (var i in sgos)
i.AppendPoint(p, s.QuatDir);

View File

@@ -2,8 +2,8 @@
"name": "Cryville.Crtr",
"rootNamespace": "",
"references": [
"GUID:d8ea0e0da3ad53a45b65c912ffcacab0",
"GUID:5686e5ee69d0e084c843d61c240d7fdb",
"GUID:13ba8ce62aa80c74598530029cb2d649",
"GUID:2922aa74af3b2854e81b8a8b286d8206",
"GUID:da293eebbcb9a4947a212534c52d1a32"
],

View File

@@ -15,6 +15,7 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE0016")]
[assembly: SuppressMessage("Style", "IDE0029")]
[assembly: SuppressMessage("Style", "IDE0031")]
[assembly: SuppressMessage("Style", "IDE0270")]
[assembly: SuppressMessage("Style", "IDE1005")]
// Null compound assignment not supported

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 82458af35dc0eff4f9f5bcfc7ffd9482
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 81c3fbb9e3606dd40908ceb861f822ea
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,21 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Copyright (c) 2017-2018 Swan & The Quaver Team <support@quavergame.com>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Quaver.API.Enums
{
public enum GameMode
{
Keys4 = 1,
Keys7 = 2
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e80eaf8c181728041aefa03ada870aed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More