mirror of
https://github.com/xivdev/EXDSchema.git
synced 2025-06-06 16:17:46 +00:00
Commit first-pass of schemas and schema validator
This commit is contained in:
parent
4a605383ec
commit
7b6e016950
816 changed files with 13870 additions and 123 deletions
|
@ -226,7 +226,7 @@ public class SchemaConverter
|
||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
var condition = new Condition();
|
var condition = new Condition();
|
||||||
condition.Switch = oldLink.Links[0].When.Key;
|
condition.Switch = Util.StripDefinitionName(oldLink.Links[0].When.Key);
|
||||||
condition.Cases = new Dictionary<int, List<string>>();
|
condition.Cases = new Dictionary<int, List<string>>();
|
||||||
foreach (var oldLinkLink in oldLink.Links)
|
foreach (var oldLinkLink in oldLink.Links)
|
||||||
condition.Cases.Add(oldLinkLink.When.Value, oldLinkLink.LinkedSheet == null ? oldLinkLink.Sheets : new List<string> { oldLinkLink.LinkedSheet });
|
condition.Cases.Add(oldLinkLink.When.Value, oldLinkLink.LinkedSheet == null ? oldLinkLink.Sheets : new List<string> { oldLinkLink.LinkedSheet });
|
||||||
|
|
|
@ -1,48 +1,53 @@
|
||||||
// using YamlDotNet.Core;
|
using SharpYaml;
|
||||||
// using YamlDotNet.Core.Events;
|
using SharpYaml.Events;
|
||||||
// using YamlDotNet.Serialization;
|
using SharpYaml.Serialization;
|
||||||
// using YamlDotNet.Serialization.EventEmitters;
|
using SharpYaml.Serialization.Serializers;
|
||||||
// using YamlDotNet.Serialization.NamingConventions;
|
|
||||||
//
|
namespace SchemaConverter;
|
||||||
// namespace SchemaConverter;
|
|
||||||
//
|
public static class SerializeUtil
|
||||||
// public static class SerializeUtil
|
{
|
||||||
// {
|
private static readonly Serializer _serializer;
|
||||||
// private static readonly ISerializer _serializer;
|
|
||||||
//
|
static SerializeUtil()
|
||||||
// static SerializeUtil()
|
{
|
||||||
// {
|
var settings = new SerializerSettings
|
||||||
// _serializer = new SerializerBuilder()
|
{
|
||||||
// .WithIndentedSequences()
|
EmitAlias = false,
|
||||||
// .WithNamingConvention(CamelCaseNamingConvention.Instance)
|
EmitDefaultValues = false,
|
||||||
// .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults)
|
NamingConvention = new CamelCaseNamingConvention(),
|
||||||
// .DisableAliases()
|
IgnoreNulls = true,
|
||||||
// // .WithEventEmitter(nextEmitter => new FlowEverythingEmitter(nextEmitter))
|
};
|
||||||
// .Build();
|
settings.RegisterSerializer(typeof(Dictionary<int, List<string>>), new CustomDictionarySerializer());
|
||||||
// }
|
settings.RegisterSerializer(typeof(New.FieldType), new CustomFieldTypeSerializer());
|
||||||
//
|
|
||||||
// public static string Serialize(object o)
|
_serializer = new Serializer(settings);
|
||||||
// {
|
}
|
||||||
// return _serializer.Serialize(o);
|
|
||||||
// }
|
public static string Serialize(object o)
|
||||||
//
|
{
|
||||||
// public class FlowEverythingEmitter : ChainedEventEmitter
|
return _serializer.Serialize(o);
|
||||||
// {
|
}
|
||||||
// public FlowEverythingEmitter(IEventEmitter nextEmitter) : base(nextEmitter) { }
|
}
|
||||||
//
|
|
||||||
// public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter)
|
internal class CustomDictionarySerializer : DictionarySerializer
|
||||||
// {
|
{
|
||||||
// Console.WriteLine($"Type: {eventInfo.Source.Type} Style: {eventInfo.Source.StaticType} Value: {eventInfo.Source.Value}");
|
protected override void WriteDictionaryItem(ref ObjectContext objectContext, KeyValuePair<object, object?> keyValue, KeyValuePair<Type, Type> types)
|
||||||
//
|
{
|
||||||
// eventInfo.Style = MappingStyle.Flow;
|
objectContext.SerializerContext.WriteYaml(keyValue.Key, types.Key);
|
||||||
// base.Emit(eventInfo, emitter);
|
objectContext.SerializerContext.WriteYaml(keyValue.Value, types.Value, YamlStyle.Flow);
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter)
|
|
||||||
// {
|
internal class CustomFieldTypeSerializer : ScalarSerializerBase
|
||||||
// Console.WriteLine($"Type: {eventInfo.Source.Type} StaticType: {eventInfo.Source.StaticType} Value: {eventInfo.Source.Value}");
|
{
|
||||||
// eventInfo.Style = SequenceStyle.Flow;
|
public override object? ConvertFrom(ref ObjectContext context, Scalar fromScalar)
|
||||||
// nextEmitter.Emit(eventInfo, emitter);
|
{
|
||||||
// }
|
return Enum.Parse<New.FieldType>(new PascalNamingConvention().Convert(fromScalar.Value));
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
|
public override string ConvertTo(ref ObjectContext objectContext)
|
||||||
|
{
|
||||||
|
return objectContext.Settings.NamingConvention.Convert(objectContext.Instance.ToString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
// using YamlMap;
|
|
||||||
//
|
|
||||||
// namespace SchemaConverter;
|
|
||||||
//
|
|
||||||
// public static class SerializeUtil2
|
|
||||||
// {
|
|
||||||
// private static readonly YamlWriter _serializer;
|
|
||||||
//
|
|
||||||
// static SerializeUtil2()
|
|
||||||
// {
|
|
||||||
// _serializer = new YamlWriter();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public static string Serialize(object o)
|
|
||||||
// {
|
|
||||||
// return _serializer.Write(o);
|
|
||||||
// }
|
|
||||||
// }
|
|
|
@ -1,53 +0,0 @@
|
||||||
using SharpYaml;
|
|
||||||
using SharpYaml.Events;
|
|
||||||
using SharpYaml.Serialization;
|
|
||||||
using SharpYaml.Serialization.Serializers;
|
|
||||||
|
|
||||||
namespace SchemaConverter;
|
|
||||||
|
|
||||||
public static class SerializeUtil3
|
|
||||||
{
|
|
||||||
private static readonly Serializer _serializer;
|
|
||||||
|
|
||||||
static SerializeUtil3()
|
|
||||||
{
|
|
||||||
var settings = new SerializerSettings
|
|
||||||
{
|
|
||||||
EmitAlias = false,
|
|
||||||
EmitDefaultValues = false,
|
|
||||||
NamingConvention = new CamelCaseNamingConvention(),
|
|
||||||
IgnoreNulls = true,
|
|
||||||
};
|
|
||||||
settings.RegisterSerializer(typeof(Dictionary<int, List<string>>), new CustomDictionarySerializer());
|
|
||||||
settings.RegisterSerializer(typeof(New.FieldType), new CustomFieldTypeSerializer());
|
|
||||||
|
|
||||||
_serializer = new Serializer(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Serialize(object o)
|
|
||||||
{
|
|
||||||
return _serializer.Serialize(o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class CustomDictionarySerializer : DictionarySerializer
|
|
||||||
{
|
|
||||||
protected override void WriteDictionaryItem(ref ObjectContext objectContext, KeyValuePair<object, object?> keyValue, KeyValuePair<Type, Type> types)
|
|
||||||
{
|
|
||||||
objectContext.SerializerContext.WriteYaml(keyValue.Key, types.Key);
|
|
||||||
objectContext.SerializerContext.WriteYaml(keyValue.Value, types.Value, YamlStyle.Flow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class CustomFieldTypeSerializer : ScalarSerializerBase
|
|
||||||
{
|
|
||||||
public override object? ConvertFrom(ref ObjectContext context, Scalar fromScalar)
|
|
||||||
{
|
|
||||||
return Enum.Parse<New.FieldType>(new PascalNamingConvention().Convert(fromScalar.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ConvertTo(ref ObjectContext objectContext)
|
|
||||||
{
|
|
||||||
return objectContext.Settings.NamingConvention.Convert(objectContext.Instance.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
46
SchemaValidator/DefinedColumn.cs
Normal file
46
SchemaValidator/DefinedColumn.cs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
using Lumina.Data.Structs.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
|
||||||
|
namespace SchemaValidator;
|
||||||
|
|
||||||
|
public class DefinedColumn
|
||||||
|
{
|
||||||
|
public ExcelColumnDefinition Definition { get; set; }
|
||||||
|
public Field Field { get; set; }
|
||||||
|
|
||||||
|
private int? _bitOffset;
|
||||||
|
public int BitOffset {
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_bitOffset == null)
|
||||||
|
_bitOffset = CalculateBitOffset(Definition.Offset, Definition.Type);
|
||||||
|
return _bitOffset.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => $"{Field} @ 0x{BitOffset / 8:X}&{BitOffset % 8}";
|
||||||
|
|
||||||
|
public static int CalculateBitOffset(ExcelColumnDefinition def)
|
||||||
|
{
|
||||||
|
return CalculateBitOffset(def.Offset, def.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CalculateBitOffset(int offset, ExcelColumnDataType type)
|
||||||
|
{
|
||||||
|
var bitOffset = offset * 8;
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
ExcelColumnDataType.PackedBool0 => bitOffset + 0,
|
||||||
|
ExcelColumnDataType.PackedBool1 => bitOffset + 1,
|
||||||
|
ExcelColumnDataType.PackedBool2 => bitOffset + 2,
|
||||||
|
ExcelColumnDataType.PackedBool3 => bitOffset + 3,
|
||||||
|
ExcelColumnDataType.PackedBool4 => bitOffset + 4,
|
||||||
|
ExcelColumnDataType.PackedBool5 => bitOffset + 5,
|
||||||
|
ExcelColumnDataType.PackedBool6 => bitOffset + 6,
|
||||||
|
ExcelColumnDataType.PackedBool7 => bitOffset + 7,
|
||||||
|
_ => bitOffset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
60
SchemaValidator/NewSheetDefinition.cs
Normal file
60
SchemaValidator/NewSheetDefinition.cs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
|
namespace SchemaValidator.New;
|
||||||
|
|
||||||
|
public enum FieldType
|
||||||
|
{
|
||||||
|
Scalar,
|
||||||
|
Array,
|
||||||
|
Icon,
|
||||||
|
ModelId,
|
||||||
|
Color,
|
||||||
|
Link,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Sheet
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string? DisplayField { get; set; }
|
||||||
|
public List<Field> Fields { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Field
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public int? Count { get; set; }
|
||||||
|
[DefaultValue(FieldType.Scalar)]
|
||||||
|
[JsonConverter(typeof(StringEnumConverter), true)]
|
||||||
|
public FieldType Type { get; set; }
|
||||||
|
public string? Comment { get; set; }
|
||||||
|
public List<Field>? Fields { get; set; }
|
||||||
|
public Condition? Condition { get; set; }
|
||||||
|
public List<string>? Targets { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var arraySuffix = Count.HasValue ? $"[{Count}]" : "";
|
||||||
|
var name = Name != null ? $"{Name}{arraySuffix}" : "Unknown";
|
||||||
|
return $"{name} ({Type})";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is not Field other)
|
||||||
|
return false;
|
||||||
|
var fieldsEqual = (Fields == null && other.Fields == null) || (Fields != null && other.Fields != null && Fields.SequenceEqual(other.Fields));
|
||||||
|
var targetsEqual = (Targets == null && other.Targets == null) || (Targets != null && other.Targets != null && Targets.SequenceEqual(other.Targets));
|
||||||
|
return Name == other.Name && Count == other.Count && Type == other.Type && Comment == other.Comment && Condition == other.Condition && fieldsEqual && targetsEqual;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Condition
|
||||||
|
{
|
||||||
|
public string? Switch { get; set; }
|
||||||
|
public Dictionary<int, List<string>>? Cases { get; set; }
|
||||||
|
}
|
|
@ -1,3 +0,0 @@
|
||||||
// See https://aka.ms/new-console-template for more information
|
|
||||||
|
|
||||||
Console.WriteLine("Hello, World!");
|
|
213
SchemaValidator/SchemaSchema.json
Normal file
213
SchemaValidator/SchemaSchema.json
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"$ref": "#/definitions/Sheet",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"sheet": {
|
||||||
|
"$ref": "#/definitions/Sheet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"Sheet": {
|
||||||
|
"title": "Sheet",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "The name of the sheet.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"displayField": {
|
||||||
|
"description": "The name of the field to use for displaying a reference to this sheet in a cell. Useful only for UI-based consumption.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"description": "The fields of the sheet. Sheets must specify all fields present in the EXH file for that sheet, meaning they all must have at least one field.",
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Field"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"comment": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oneOf": [
|
||||||
|
{ "required": ["name", "fields"] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Field": {
|
||||||
|
"description": "A field in a sheet. Describes one or more columns.",
|
||||||
|
"title": "Field",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "The name of the field.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "Defines the type of the field. Scalar should be assumed by default, and has no meaning. The only other type that affects parsing is array.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["scalar", "link", "array", "icon", "modelId", "color"]
|
||||||
|
},
|
||||||
|
"count": {
|
||||||
|
"description": "Only valid for array types. Defines the number of elements in the array.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"targets": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"$ref": "#/definitions/Condition"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"description": "Only valid for array types. Defines the fields of the array. Fields are not available on non-array types because grouping non-array types is meaningless. They should be defined at the top-level.",
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Field"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"comment": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"description": "Arrays require a count.",
|
||||||
|
"if": {
|
||||||
|
"required": ["type"],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "array"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"required": ["count"]
|
||||||
|
},
|
||||||
|
"else": {
|
||||||
|
"not": {
|
||||||
|
"required": ["count"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Fields with a fields list must be an array.",
|
||||||
|
"if": {
|
||||||
|
"required": ["fields"]
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"required": ["type"],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "array"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Fields with a count must be an array.",
|
||||||
|
"if": {
|
||||||
|
"required": ["count"]
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"required": ["type"],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "array"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Fields can have only one of condition or targets.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"description": "Fields with targets cannot have a condition.",
|
||||||
|
"if": {
|
||||||
|
"required": ["targets"]
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"not": {
|
||||||
|
"required": ["condition"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Fields with a condition cannot have targets.",
|
||||||
|
"if": {
|
||||||
|
"required": ["condition"]
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"not": {
|
||||||
|
"required": ["targets"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Arrays can have neither condition or targets.",
|
||||||
|
"not": {
|
||||||
|
"required": ["condition", "targets"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Fields with a link type must have a condition or targets.",
|
||||||
|
"if": {
|
||||||
|
"required": ["type"],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "link"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"required": ["condition"],
|
||||||
|
"not": {
|
||||||
|
"required": ["targets"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"required": ["targets"],
|
||||||
|
"not": {
|
||||||
|
"required": ["condition"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Condition": {
|
||||||
|
"title": "Condition",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"switch": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cases": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["cases", "switch"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
103
SchemaValidator/SchemaValidator.cs
Normal file
103
SchemaValidator/SchemaValidator.cs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
using SchemaValidator.Validation;
|
||||||
|
using SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
namespace SchemaValidator;
|
||||||
|
|
||||||
|
public class SchemaValidator
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
// we need 3 args
|
||||||
|
if (args.Length != 3)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Usage: SchemaValidator.exe <game install directory> <json schema file> <schema directory>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gameDir = args[0];
|
||||||
|
var schemaFile = args[1];
|
||||||
|
var schemaDir = args[2];
|
||||||
|
|
||||||
|
var gameData = new GameData(gameDir);
|
||||||
|
var schemaText = File.ReadAllText(schemaFile);
|
||||||
|
|
||||||
|
var testDict = new Dictionary<string, List<int>>()
|
||||||
|
{
|
||||||
|
{"all", new() {0}},
|
||||||
|
};
|
||||||
|
|
||||||
|
var validators = new List<Validator>
|
||||||
|
{
|
||||||
|
new SchemaFileValidator(gameData, schemaText),
|
||||||
|
new ColumnCountValidator(gameData),
|
||||||
|
new IconTypeValidator(gameData),
|
||||||
|
new NamedInnerNamedOuterValidator(gameData),
|
||||||
|
new ModelIdTypeValidator(gameData),
|
||||||
|
new ColorTypeValidator(gameData),
|
||||||
|
new IconPathExistsValidator(gameData),
|
||||||
|
new SingleLinkRefValidator(gameData, testDict),
|
||||||
|
new MultiLinkRefValidator(gameData, testDict),
|
||||||
|
new ConditionValidator(gameData),
|
||||||
|
new ConditionRefValidator(gameData, testDict),
|
||||||
|
};
|
||||||
|
|
||||||
|
// var exl = gameData.GetFile<ExcelListFile>("exd/root.exl");
|
||||||
|
var results = new ValidationResults();
|
||||||
|
|
||||||
|
foreach (var schemaPath in Directory.GetFiles(schemaDir, "*.yml"))
|
||||||
|
{
|
||||||
|
var sheetName = Path.GetFileNameWithoutExtension(schemaPath);
|
||||||
|
var exh = gameData.GetFile<ExcelHeaderFile>($"exd/{sheetName}.exh");
|
||||||
|
if (exh == null)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Sheet {sheetName} does not exist!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sheet sheet;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sheet = SerializeUtil.Deserialize<Sheet>(File.ReadAllText(schemaPath));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Sheet {sheetName} encountered an exception when deserializing!");
|
||||||
|
Console.Error.WriteLine(e.Message);
|
||||||
|
Console.Error.WriteLine(e.StackTrace);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sheet == null)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Sheet {sheetName} could not be deserialized!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeUtil.EvaluateSchema(schemaPath);
|
||||||
|
|
||||||
|
foreach (var validator in validators)
|
||||||
|
results.Add(validator.Validate(exh, sheet));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var result in results.Results.Where(r => r.Status == ValidationStatus.Warning))
|
||||||
|
{
|
||||||
|
var msgfmt = result.Message == "" ? "" : $" - ";
|
||||||
|
Console.WriteLine($"{result.Status}: {result.SheetName} - {result.ValidatorName}{msgfmt}{result.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var result in results.Results.Where(r => r.Status == ValidationStatus.Error))
|
||||||
|
{
|
||||||
|
var msgfmt = result.Message == "" ? "" : $" - ";
|
||||||
|
Console.WriteLine($"{result.Status}: {result.SheetName} - {result.ValidatorName}{msgfmt}{result.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var successCount = results.Results.Count(r => r.Status == ValidationStatus.Success);
|
||||||
|
var warningCount = results.Results.Count(r => r.Status == ValidationStatus.Warning);
|
||||||
|
var errorCount = results.Results.Count(r => r.Status == ValidationStatus.Error);
|
||||||
|
Console.WriteLine($"{successCount} success, {warningCount} warnings, {errorCount} errors");
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,17 @@
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="JsonSchema.Net" Version="5.2.5" />
|
||||||
|
<PackageReference Include="Lumina" Version="3.11.0" />
|
||||||
|
<PackageReference Include="NJsonSchema" Version="10.9.0" />
|
||||||
|
<PackageReference Include="SharpYaml" Version="2.1.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
120
SchemaValidator/Util/SchemaUtil.cs
Normal file
120
SchemaValidator/Util/SchemaUtil.cs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Useful methods for working with the EXDSchema object model.
|
||||||
|
/// </summary>
|
||||||
|
public static class SchemaUtil
|
||||||
|
{
|
||||||
|
public static int GetColumnCount(Sheet sheet)
|
||||||
|
{
|
||||||
|
var total = 0;
|
||||||
|
foreach (var field in sheet.Fields)
|
||||||
|
total += GetFieldCount(field);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DefinedColumn> Flatten(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var fields = new List<DefinedColumn>();
|
||||||
|
foreach (var field in sheet.Fields)
|
||||||
|
Emit(fields, field);
|
||||||
|
|
||||||
|
var exhDefList = exh.ColumnDefinitions.ToList();
|
||||||
|
exhDefList.Sort((c1, c2) => DefinedColumn.CalculateBitOffset(c1).CompareTo(DefinedColumn.CalculateBitOffset(c2)));
|
||||||
|
|
||||||
|
var min = Math.Min(exhDefList.Count, fields.Count);
|
||||||
|
for(int i = 0; i < min; i++)
|
||||||
|
{
|
||||||
|
var field = fields[i];
|
||||||
|
field.Definition = exhDefList[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Emit(List<DefinedColumn> list, Field field, string nameOverride = "")
|
||||||
|
{
|
||||||
|
if (field.Type != FieldType.Array)
|
||||||
|
{
|
||||||
|
// Single field
|
||||||
|
list.Add(new DefinedColumn { Field = CreateField(field, nameOverride) });
|
||||||
|
}
|
||||||
|
else if (field.Type == FieldType.Array)
|
||||||
|
{
|
||||||
|
// We can have an array without fields, it's just scalars
|
||||||
|
if (field.Fields == null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < field.Count.Value; i++)
|
||||||
|
{
|
||||||
|
list.Add(new DefinedColumn { Field = CreateField(field, "") });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < field.Count.Value; i++)
|
||||||
|
{
|
||||||
|
foreach (var nestedField in field.Fields)
|
||||||
|
{
|
||||||
|
Emit(list, nestedField, field.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field CreateField(Field baseField, string nameOverride)
|
||||||
|
{
|
||||||
|
var addedField = new Field
|
||||||
|
{
|
||||||
|
Name = baseField.Name,
|
||||||
|
Comment = baseField.Comment,
|
||||||
|
Count = null,
|
||||||
|
Type = baseField.Type == FieldType.Array ? FieldType.Scalar : baseField.Type,
|
||||||
|
Fields = null,
|
||||||
|
Condition = baseField.Condition,
|
||||||
|
Targets = baseField.Targets,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is for unnamed inner fields of arrays such as arrays of links
|
||||||
|
// We don't want to override the name of unnamed scalars though
|
||||||
|
if (baseField.Name == null && baseField.Type != FieldType.Scalar && nameOverride != "")
|
||||||
|
addedField.Name = nameOverride;
|
||||||
|
return addedField;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetFieldCount(Field field)
|
||||||
|
{
|
||||||
|
if (field.Type == FieldType.Array)
|
||||||
|
{
|
||||||
|
var total = 0;
|
||||||
|
if (field.Fields != null)
|
||||||
|
{
|
||||||
|
foreach (var nestedField in field.Fields)
|
||||||
|
total += GetFieldCount(nestedField);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
total = 1;
|
||||||
|
}
|
||||||
|
return total * field.Count.Value;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Field GetFieldByIndex(Sheet schema, int index)
|
||||||
|
{
|
||||||
|
foreach (var field in schema.Fields)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Field GetFieldByIndex(Field field, int index, int baseIndex)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
93
SchemaValidator/Util/SerializeUtil.cs
Normal file
93
SchemaValidator/Util/SerializeUtil.cs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Json.Schema;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SharpYaml;
|
||||||
|
using SharpYaml.Events;
|
||||||
|
using SharpYaml.Serialization;
|
||||||
|
using SharpYaml.Serialization.Serializers;
|
||||||
|
using JsonSchema = Json.Schema.JsonSchema;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Util;
|
||||||
|
|
||||||
|
public static class SerializeUtil
|
||||||
|
{
|
||||||
|
private static readonly Serializer _serializer;
|
||||||
|
|
||||||
|
static SerializeUtil()
|
||||||
|
{
|
||||||
|
var settings = new SerializerSettings
|
||||||
|
{
|
||||||
|
EmitAlias = false,
|
||||||
|
EmitDefaultValues = false,
|
||||||
|
NamingConvention = new CamelCaseNamingConvention(),
|
||||||
|
IgnoreNulls = true,
|
||||||
|
};
|
||||||
|
settings.RegisterSerializer(typeof(Dictionary<int, List<string>>), new CustomDictionarySerializer());
|
||||||
|
settings.RegisterSerializer(typeof(FieldType), new CustomFieldTypeSerializer());
|
||||||
|
|
||||||
|
_serializer = new Serializer(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize(object o)
|
||||||
|
{
|
||||||
|
return _serializer.Serialize(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? Deserialize<T>(string s)
|
||||||
|
{
|
||||||
|
return _serializer.Deserialize<T>(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object? Deserialize(string s)
|
||||||
|
{
|
||||||
|
return _serializer.Deserialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EvaluationResults? EvaluateSchema(string filePath)
|
||||||
|
{
|
||||||
|
var yamlText = File.ReadAllText(filePath);
|
||||||
|
object? yamlObject;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
yamlObject = _serializer.Deserialize(yamlText);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (yamlObject == null) return null;
|
||||||
|
|
||||||
|
var json = JsonConvert.SerializeObject(yamlObject);
|
||||||
|
// Console.WriteLine(json);
|
||||||
|
// File.WriteAllText(@"C:\Users\Liam\Documents\repos\EXDSchema\SchemaValidator\test.json", json);
|
||||||
|
|
||||||
|
var schemaText = File.ReadAllText(@"C:\Users\Liam\Documents\repos\EXDSchema\SchemaValidator\SchemaSchema.json");
|
||||||
|
var schema = JsonSchema.FromText(schemaText);
|
||||||
|
var node = JsonNode.Parse(json);
|
||||||
|
return schema.Evaluate(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CustomDictionarySerializer : DictionarySerializer
|
||||||
|
{
|
||||||
|
protected override void WriteDictionaryItem(ref ObjectContext objectContext, KeyValuePair<object, object?> keyValue, KeyValuePair<Type, Type> types)
|
||||||
|
{
|
||||||
|
objectContext.SerializerContext.WriteYaml(keyValue.Key, types.Key);
|
||||||
|
objectContext.SerializerContext.WriteYaml(keyValue.Value, types.Value, YamlStyle.Flow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CustomFieldTypeSerializer : ScalarSerializerBase
|
||||||
|
{
|
||||||
|
public override object? ConvertFrom(ref ObjectContext context, Scalar fromScalar)
|
||||||
|
{
|
||||||
|
return Enum.Parse<FieldType>(new PascalNamingConvention().Convert(fromScalar.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ConvertTo(ref ObjectContext objectContext)
|
||||||
|
{
|
||||||
|
return objectContext.Settings.NamingConvention.Convert(objectContext.Instance.ToString());
|
||||||
|
}
|
||||||
|
}
|
8
SchemaValidator/Util/StringToEnumCamelCaseConverter.cs
Normal file
8
SchemaValidator/Util/StringToEnumCamelCaseConverter.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Util;
|
||||||
|
|
||||||
|
public class StringToEnumCamelCaseConverter : StringEnumConverter
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
92
SchemaValidator/Validation/ValidationResult.cs
Normal file
92
SchemaValidator/Validation/ValidationResult.cs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
namespace SchemaValidator.Validation;
|
||||||
|
|
||||||
|
public enum ValidationStatus
|
||||||
|
{
|
||||||
|
Success,
|
||||||
|
Error,
|
||||||
|
Warning,
|
||||||
|
Failed,
|
||||||
|
Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ValidationResults
|
||||||
|
{
|
||||||
|
public List<ValidationResult> Results { get; set; } = new();
|
||||||
|
|
||||||
|
public ValidationResults() { }
|
||||||
|
public ValidationResults(ValidationResult result) => Results.Add(result);
|
||||||
|
|
||||||
|
public void Add(ValidationResult result) => Results.Add(result);
|
||||||
|
public void Add(ValidationResults results) => Results.AddRange(results.Results);
|
||||||
|
|
||||||
|
public static ValidationResults Success(string sheetName, string validatorName, string message = "") => new(ValidationResult.Success(sheetName, validatorName, message));
|
||||||
|
public static ValidationResults Error(string sheetName, string validatorName, string message = "") => new(ValidationResult.Error(sheetName, validatorName, message));
|
||||||
|
public static ValidationResults Warning(string sheetName, string validatorName, string message = "") => new(ValidationResult.Warning(sheetName, validatorName, message));
|
||||||
|
public static ValidationResults Failed(string sheetName, string validatorName, string message = "") => new(ValidationResult.Failed(sheetName, validatorName, message));
|
||||||
|
public static ValidationResults Info(string sheetName, string validatorName, string message = "") => new(ValidationResult.Info(sheetName, validatorName, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ValidationResult
|
||||||
|
{
|
||||||
|
public ValidationStatus Status { get; set; }
|
||||||
|
public string SheetName { get; set; }
|
||||||
|
public string ValidatorName { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
private ValidationResult() {}
|
||||||
|
|
||||||
|
public static ValidationResult Success(string sheetName, string validatorName, string message = "")
|
||||||
|
{
|
||||||
|
return new ValidationResult
|
||||||
|
{
|
||||||
|
SheetName = sheetName,
|
||||||
|
ValidatorName = validatorName,
|
||||||
|
Status = ValidationStatus.Success,
|
||||||
|
Message = message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationResult Error(string sheetName, string validatorName, string message = "")
|
||||||
|
{
|
||||||
|
return new ValidationResult
|
||||||
|
{
|
||||||
|
SheetName = sheetName,
|
||||||
|
ValidatorName = validatorName,
|
||||||
|
Status = ValidationStatus.Error,
|
||||||
|
Message = message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationResult Warning(string sheetName, string validatorName, string message = "")
|
||||||
|
{
|
||||||
|
return new ValidationResult
|
||||||
|
{
|
||||||
|
SheetName = sheetName,
|
||||||
|
ValidatorName = validatorName,
|
||||||
|
Status = ValidationStatus.Warning,
|
||||||
|
Message = message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationResult Failed(string sheetName, string validatorName, string message = "")
|
||||||
|
{
|
||||||
|
return new ValidationResult
|
||||||
|
{
|
||||||
|
SheetName = sheetName,
|
||||||
|
ValidatorName = validatorName,
|
||||||
|
Status = ValidationStatus.Failed,
|
||||||
|
Message = message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationResult Info(string sheetName, string validatorName, string message = "")
|
||||||
|
{
|
||||||
|
return new ValidationResult
|
||||||
|
{
|
||||||
|
SheetName = sheetName,
|
||||||
|
ValidatorName = validatorName,
|
||||||
|
Status = ValidationStatus.Info,
|
||||||
|
Message = message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
42
SchemaValidator/Validation/Validator.cs
Normal file
42
SchemaValidator/Validation/Validator.cs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using Lumina.Data.Structs.Excel;
|
||||||
|
using Lumina.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation;
|
||||||
|
|
||||||
|
public abstract class Validator
|
||||||
|
{
|
||||||
|
protected GameData GameData;
|
||||||
|
|
||||||
|
public Validator(GameData gameData)
|
||||||
|
{
|
||||||
|
GameData = gameData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract string ValidatorName();
|
||||||
|
public abstract ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet);
|
||||||
|
|
||||||
|
protected long? ReadColumnIntegerValue(RawExcelSheet sheet, RowParser parser, DefinedColumn column)
|
||||||
|
{
|
||||||
|
var offset = column.Definition.Offset;
|
||||||
|
var type = column.Definition.Type;
|
||||||
|
Int128? value = type switch
|
||||||
|
{
|
||||||
|
ExcelColumnDataType.Int8 => parser.ReadOffset<sbyte>(offset),
|
||||||
|
ExcelColumnDataType.UInt8 => parser.ReadOffset<byte>(offset),
|
||||||
|
ExcelColumnDataType.Int16 => parser.ReadOffset<short>(offset),
|
||||||
|
ExcelColumnDataType.UInt16 => parser.ReadOffset<ushort>(offset),
|
||||||
|
ExcelColumnDataType.Int32 => parser.ReadOffset<int>(offset),
|
||||||
|
ExcelColumnDataType.UInt32 => parser.ReadOffset<uint>(offset),
|
||||||
|
ExcelColumnDataType.Int64 => parser.ReadOffset<long>(offset),
|
||||||
|
ExcelColumnDataType.UInt64 => parser.ReadOffset<ulong>(offset),
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (value != null)
|
||||||
|
return (long)value;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
32
SchemaValidator/Validation/Validators/ColorTypeValidator.cs
Normal file
32
SchemaValidator/Validation/Validators/ColorTypeValidator.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using Lumina.Data.Structs.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class ColorTypeValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "ColorTypeValidator";
|
||||||
|
|
||||||
|
public ColorTypeValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var results = new ValidationResults();
|
||||||
|
var fields = SchemaUtil.Flatten(exh, sheet);
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
if (field.Field.Type == FieldType.Color && field.Definition.Type != ExcelColumnDataType.UInt32)
|
||||||
|
{
|
||||||
|
var msg = $"Column {field.Field.Name}@0x{field.Definition.Offset:X} type {field.Definition.Type} is not valid for type 'color'.";
|
||||||
|
results.Results.Add(ValidationResult.Error(sheet.Name, ValidatorName(), msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.Results.Count == 0)
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class ColumnCountValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "ColumnCountValidator";
|
||||||
|
|
||||||
|
public ColumnCountValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var colCount = SchemaUtil.GetColumnCount(sheet);
|
||||||
|
if (colCount != exh.ColumnDefinitions.Length)
|
||||||
|
return ValidationResults.Error(sheet.Name, ValidatorName(), $"Column count mismatch! exh count {exh.ColumnDefinitions.Length} != schema count {colCount}");
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
}
|
||||||
|
}
|
161
SchemaValidator/Validation/Validators/ConditionRefValidator.cs
Normal file
161
SchemaValidator/Validation/Validators/ConditionRefValidator.cs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using Lumina.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class ConditionRefValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "ConditionRefValidator";
|
||||||
|
|
||||||
|
private Dictionary<string, List<int>> _ignoredValues = new();
|
||||||
|
|
||||||
|
public ConditionRefValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
public ConditionRefValidator(GameData gameData, Dictionary<string, List<int>> ignoredValues) : base(gameData)
|
||||||
|
{
|
||||||
|
_ignoredValues = ignoredValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LinkTargetData
|
||||||
|
{
|
||||||
|
public string SourceSheet { get; set; }
|
||||||
|
public DefinedColumn Source { get; set; }
|
||||||
|
|
||||||
|
public string SwitchField { get; set; }
|
||||||
|
public int SwitchValue { get; set; }
|
||||||
|
|
||||||
|
public HashSet<string> TargetSheets { get; set; }
|
||||||
|
public HashSet<long> TargetKeys { get; set; }
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is not LinkTargetData other)
|
||||||
|
return false;
|
||||||
|
return SourceSheet == other.SourceSheet && Source.Field.Equals(other.Source.Field) && TargetSheets == other.TargetSheets && TargetKeys.SetEquals(other.TargetKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var results = new ValidationResults();
|
||||||
|
var fields = SchemaUtil.Flatten(exh, sheet);
|
||||||
|
var dataFile = GameData.Excel.GetSheetRaw($"{sheet.Name}");
|
||||||
|
if (dataFile == null)
|
||||||
|
{
|
||||||
|
var msg = $"Failed to obtain sheet {sheet.Name} from game data.";
|
||||||
|
results.Results.Add(ValidationResult.Failed(sheet.Name, ValidatorName(), msg));
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all link fields with condition
|
||||||
|
var linkFields = fields.Where(f => f.Field is { Type: FieldType.Link, Condition: not null, Targets: null }).ToList();
|
||||||
|
|
||||||
|
var linkVals = new List<LinkTargetData>();
|
||||||
|
foreach (var field in linkFields)
|
||||||
|
{
|
||||||
|
var offset = field.Definition.Offset;
|
||||||
|
var switchOn = field.Field.Condition.Switch;
|
||||||
|
var switchField = fields.First(f => f.Field.Name == switchOn);
|
||||||
|
// var definedSwitchColumnValues = field.Field.Condition.Cases.Keys.ToHashSet();
|
||||||
|
|
||||||
|
// store the column values for each switch value
|
||||||
|
var columnValues = new Dictionary<long, HashSet<long>>();
|
||||||
|
|
||||||
|
foreach (var row in dataFile.GetRowParsers())
|
||||||
|
{
|
||||||
|
var columnValue = ReadColumnIntegerValue(dataFile, row, field);
|
||||||
|
var switchColumnValue = ReadColumnIntegerValue(dataFile, row, switchField);
|
||||||
|
if (columnValue == null || switchColumnValue == null)
|
||||||
|
{
|
||||||
|
var msg = $"Column {field.Field.Name}@0x{offset:X} type {field.Definition.Type} is not valid for type 'link' condition switch, failed to read.";
|
||||||
|
results.Results.Add(ValidationResult.Failed(sheet.Name, ValidatorName(), msg));
|
||||||
|
break; // don't spam the same error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (columnValues.TryGetValue(switchColumnValue.Value, out var values))
|
||||||
|
{
|
||||||
|
values.Add(columnValue.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
columnValues.Add(switchColumnValue.Value, new HashSet<long> { columnValue.Value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var valueSet in columnValues)
|
||||||
|
{
|
||||||
|
// Skip if we don't have a defined target set for this value of the switch
|
||||||
|
if (!field.Field.Condition.Cases.TryGetValue((int)valueSet.Key, out var switchTargets))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var targetData = new LinkTargetData
|
||||||
|
{
|
||||||
|
SourceSheet = sheet.Name,
|
||||||
|
Source = field,
|
||||||
|
TargetSheets = switchTargets.ToHashSet(),
|
||||||
|
TargetKeys = valueSet.Value,
|
||||||
|
SwitchField = switchField.Field.Name,
|
||||||
|
SwitchValue = (int)valueSet.Key,
|
||||||
|
};
|
||||||
|
linkVals.Add(targetData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var linkData in linkVals.Distinct())
|
||||||
|
{
|
||||||
|
var sheetNames = linkData.TargetSheets;
|
||||||
|
var sheetKeys = linkData.TargetKeys;
|
||||||
|
|
||||||
|
var dataFiles = new Dictionary<string, RawExcelSheet>();
|
||||||
|
foreach (var sheetName in sheetNames)
|
||||||
|
{
|
||||||
|
var tmpDataFile = GameData.Excel.GetSheetRaw(sheetName);
|
||||||
|
if (tmpDataFile == null)
|
||||||
|
{
|
||||||
|
var msg = $"Source {linkData.SourceSheet} field {linkData.Source.Field.Name}@0x{linkData.Source.Definition.Offset:X} references non-existent sheet {sheetName}.";
|
||||||
|
results.Add(ValidationResult.Error(sheet.Name, ValidatorName(), msg));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dataFiles.Add(sheetName, tmpDataFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var key in sheetKeys)
|
||||||
|
{
|
||||||
|
if (_ignoredValues.TryGetValue("all", out var ignoredKeys) && ignoredKeys.Contains((int)key))
|
||||||
|
{
|
||||||
|
sheetKeys.Remove(key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var linkedDataFile in dataFiles)
|
||||||
|
{
|
||||||
|
if (_ignoredValues.TryGetValue(linkedDataFile.Key, out ignoredKeys) && ignoredKeys.Contains((int)key))
|
||||||
|
{
|
||||||
|
sheetKeys.Remove(key);
|
||||||
|
}
|
||||||
|
else if (linkedDataFile.Value.GetRow((uint)key) != null)
|
||||||
|
{
|
||||||
|
// Console.WriteLine($"removing {key} because of {linkedDataFile.Key}");
|
||||||
|
sheetKeys.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sheetKeys.Count > 0)
|
||||||
|
{
|
||||||
|
var contents = string.Join(", ", sheetKeys);
|
||||||
|
var display = $"{linkData.Source.Field.Name}@0x{linkData.Source.Definition.Offset:X}";
|
||||||
|
var msg = $"Source {linkData.SourceSheet} field {display} contains key references {contents} for case {linkData.SwitchField} = {linkData.SwitchValue} which do not exist in any linked sheet.";
|
||||||
|
results.Add(ValidationResult.Warning(sheet.Name, ValidatorName(), msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.Results.Count == 0)
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
67
SchemaValidator/Validation/Validators/ConditionValidator.cs
Normal file
67
SchemaValidator/Validation/Validators/ConditionValidator.cs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class ConditionValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "ConditionValidator";
|
||||||
|
|
||||||
|
public ConditionValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var results = new ValidationResults();
|
||||||
|
var fields = SchemaUtil.Flatten(exh, sheet);
|
||||||
|
var dataFile = GameData.Excel.GetSheetRaw($"{sheet.Name}");
|
||||||
|
|
||||||
|
// Get all link fields with null targets (filters out target-based links)
|
||||||
|
var conditionFields = fields.Where(f => f.Field is { Type: FieldType.Link, Condition: not null, Targets: null }).ToList();
|
||||||
|
|
||||||
|
foreach (var field in conditionFields)
|
||||||
|
{
|
||||||
|
var offset = field.Definition.Offset;
|
||||||
|
var switchOn = field.Field.Condition.Switch;
|
||||||
|
var switchField = fields.First(f => f.Field.Name == switchOn);
|
||||||
|
var definedSwitchColumnValues = field.Field.Condition.Cases.Keys.ToHashSet();
|
||||||
|
var switchColumnValues = new HashSet<long>();
|
||||||
|
|
||||||
|
foreach (var row in dataFile.GetRowParsers())
|
||||||
|
{
|
||||||
|
var columnValue = ReadColumnIntegerValue(dataFile, row, switchField);
|
||||||
|
if (columnValue == null)
|
||||||
|
{
|
||||||
|
var msg = $"Column {field.Field.Name}@0x{offset:X} type {field.Definition.Type} is not valid for type 'link' condition switch, failed to read.";
|
||||||
|
results.Results.Add(ValidationResult.Failed(sheet.Name, ValidatorName(), msg));
|
||||||
|
break; // don't spam the same error
|
||||||
|
}
|
||||||
|
switchColumnValues.Add(columnValue.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var switchColumnValue in switchColumnValues)
|
||||||
|
{
|
||||||
|
if (!definedSwitchColumnValues.Contains((int)switchColumnValue))
|
||||||
|
{
|
||||||
|
var msg = $"Column {field.Field.Name}@0x{offset:X} type {field.Definition.Type} is not valid for type 'link' condition switch, switch column {switchOn} value {switchColumnValue} is not defined in the schema.";
|
||||||
|
results.Add(ValidationResult.Warning(sheet.Name, ValidatorName(), msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var definedSwitchColumnValue in definedSwitchColumnValues)
|
||||||
|
{
|
||||||
|
if (!switchColumnValues.Contains(definedSwitchColumnValue))
|
||||||
|
{
|
||||||
|
var msg = $"Column {field.Field.Name}@0x{offset:X} type {field.Definition.Type} is not valid for type 'link' condition switch, switch column {switchOn} value {definedSwitchColumnValue} is not present in the column values.";
|
||||||
|
results.Add(ValidationResult.Warning(sheet.Name, ValidatorName(), msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.Results.Count == 0)
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class IconPathExistsValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "IconPathExistsValidator";
|
||||||
|
|
||||||
|
public IconPathExistsValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var results = new ValidationResults();
|
||||||
|
var fields = SchemaUtil.Flatten(exh, sheet);
|
||||||
|
var dataFile = GameData.Excel.GetSheetRaw($"{sheet.Name}");
|
||||||
|
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
if (field.Field.Type == FieldType.Icon)
|
||||||
|
{
|
||||||
|
var offset = field.Definition.Offset;
|
||||||
|
foreach (var row in dataFile.GetRowParsers())
|
||||||
|
{
|
||||||
|
var columnValue = ReadColumnIntegerValue(dataFile, row, field);
|
||||||
|
if (columnValue == null)
|
||||||
|
{
|
||||||
|
var msg = $"Column {field.Field.Name}@0x{offset:X} type {field.Definition.Type} is not valid for type 'icon', failed to read.";
|
||||||
|
results.Results.Add(ValidationResult.Failed(sheet.Name, ValidatorName(), msg));
|
||||||
|
break; // don't spam the same error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IconPathExists(columnValue.Value))
|
||||||
|
{
|
||||||
|
var msg = $"Column {field.Field.Name}@0x{offset:X} type {field.Definition.Type} at row {row.RowId} icon '{columnValue}' does not exist.";
|
||||||
|
results.Results.Add(ValidationResult.Warning(sheet.Name, ValidatorName(), msg));
|
||||||
|
break; // don't spam the same error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.Results.Count == 0)
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IconPathExists(long iconId)
|
||||||
|
{
|
||||||
|
var path = $"ui/icon/{iconId / 1000 * 1000:000000}/{iconId:000000}.tex";
|
||||||
|
return GameData.FileExists(path);
|
||||||
|
}
|
||||||
|
}
|
39
SchemaValidator/Validation/Validators/IconTypeValidator.cs
Normal file
39
SchemaValidator/Validation/Validators/IconTypeValidator.cs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using Lumina.Data.Structs.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class IconTypeValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "IconTypeValidator";
|
||||||
|
|
||||||
|
private readonly HashSet<ExcelColumnDataType> _validTypes = new()
|
||||||
|
{
|
||||||
|
ExcelColumnDataType.UInt32,
|
||||||
|
ExcelColumnDataType.Int32,
|
||||||
|
ExcelColumnDataType.UInt16,
|
||||||
|
};
|
||||||
|
|
||||||
|
public IconTypeValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var results = new ValidationResults();
|
||||||
|
var fields = SchemaUtil.Flatten(exh, sheet);
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
if (field.Field.Type == FieldType.Icon && !_validTypes.Contains(field.Definition.Type))
|
||||||
|
{
|
||||||
|
var msg = $"Column {field.Field.Name}@0x{field.Definition.Offset:X} type {field.Definition.Type} is not valid for type 'icon'.";
|
||||||
|
results.Results.Add(ValidationResult.Error(sheet.Name, ValidatorName(), msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.Results.Count == 0)
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using Lumina.Data.Structs.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class ModelIdTypeValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "ModelIdTypeValidator";
|
||||||
|
|
||||||
|
private readonly HashSet<ExcelColumnDataType> _validTypes = new()
|
||||||
|
{
|
||||||
|
ExcelColumnDataType.UInt32,
|
||||||
|
ExcelColumnDataType.UInt64,
|
||||||
|
};
|
||||||
|
|
||||||
|
public ModelIdTypeValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var results = new ValidationResults();
|
||||||
|
var fields = SchemaUtil.Flatten(exh, sheet);
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
if (field.Field.Type == FieldType.ModelId && !_validTypes.Contains(field.Definition.Type))
|
||||||
|
{
|
||||||
|
var msg = $"Column {field.Field.Name}@0x{field.Definition.Offset:X} type {field.Definition.Type} is not valid for type 'modelId'.";
|
||||||
|
results.Results.Add(ValidationResult.Error(sheet.Name, ValidatorName(), msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.Results.Count == 0)
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
136
SchemaValidator/Validation/Validators/MultiLinkRefValidator.cs
Normal file
136
SchemaValidator/Validation/Validators/MultiLinkRefValidator.cs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using Lumina.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class MultiLinkRefValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "MultiLinkRefValidator";
|
||||||
|
|
||||||
|
private Dictionary<string, List<int>> _ignoredValues = new();
|
||||||
|
|
||||||
|
public MultiLinkRefValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
public MultiLinkRefValidator(GameData gameData, Dictionary<string, List<int>> ignoredValues) : base(gameData)
|
||||||
|
{
|
||||||
|
_ignoredValues = ignoredValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LinkTargetData
|
||||||
|
{
|
||||||
|
public string SourceSheet { get; set; }
|
||||||
|
public DefinedColumn Source { get; set; }
|
||||||
|
|
||||||
|
public HashSet<string> TargetSheets { get; set; }
|
||||||
|
public HashSet<long> TargetKeys { get; set; }
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is not LinkTargetData other)
|
||||||
|
return false;
|
||||||
|
return SourceSheet == other.SourceSheet && Source.Field.Equals(other.Source.Field) && TargetSheets == other.TargetSheets && TargetKeys.SetEquals(other.TargetKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var results = new ValidationResults();
|
||||||
|
var fields = SchemaUtil.Flatten(exh, sheet);
|
||||||
|
var dataFile = GameData.Excel.GetSheetRaw($"{sheet.Name}");
|
||||||
|
if (dataFile == null)
|
||||||
|
{
|
||||||
|
var msg = $"Failed to obtain sheet {sheet.Name} from game data.";
|
||||||
|
results.Results.Add(ValidationResult.Failed(sheet.Name, ValidatorName(), msg));
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all link fields with null condition, > 1 target
|
||||||
|
var linkFields = fields.Where(f => f.Field is { Type: FieldType.Link, Condition: null, Targets.Count: > 1 }).ToList();
|
||||||
|
|
||||||
|
var linkVals = new List<LinkTargetData>();
|
||||||
|
foreach (var field in linkFields)
|
||||||
|
{
|
||||||
|
var offset = field.Definition.Offset;
|
||||||
|
|
||||||
|
var columnValues = new HashSet<long>();
|
||||||
|
|
||||||
|
foreach (var row in dataFile.GetRowParsers())
|
||||||
|
{
|
||||||
|
var columnValue = ReadColumnIntegerValue(dataFile, row, field);
|
||||||
|
if (columnValue == null)
|
||||||
|
{
|
||||||
|
var msg = $"Row {row.RowId} column {field.Field.Name}@0x{offset:X} type {field.Definition.Type} failed to read properly.";
|
||||||
|
results.Results.Add(ValidationResult.Failed(sheet.Name, ValidatorName(), msg));
|
||||||
|
break; // don't spam the same error
|
||||||
|
}
|
||||||
|
columnValues.Add(columnValue.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetData = new LinkTargetData
|
||||||
|
{
|
||||||
|
SourceSheet = sheet.Name,
|
||||||
|
Source = field,
|
||||||
|
TargetSheets = field.Field.Targets.ToHashSet(),
|
||||||
|
TargetKeys = columnValues
|
||||||
|
};
|
||||||
|
linkVals.Add(targetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var linkData in linkVals.Distinct())
|
||||||
|
{
|
||||||
|
var sheetNames = linkData.TargetSheets;
|
||||||
|
var sheetKeys = linkData.TargetKeys;
|
||||||
|
|
||||||
|
var dataFiles = new Dictionary<string, RawExcelSheet>();
|
||||||
|
foreach (var sheetName in sheetNames)
|
||||||
|
{
|
||||||
|
var tmpDataFile = GameData.Excel.GetSheetRaw(sheetName);
|
||||||
|
if (tmpDataFile == null)
|
||||||
|
{
|
||||||
|
var msg = $"Source {linkData.SourceSheet} field {linkData.Source.Field.Name}@0x{linkData.Source.Definition.Offset:X} references non-existent sheet {sheetName}.";
|
||||||
|
results.Add(ValidationResult.Error(sheet.Name, ValidatorName(), msg));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dataFiles.Add(sheetName, tmpDataFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var key in sheetKeys)
|
||||||
|
{
|
||||||
|
if (_ignoredValues.TryGetValue("all", out var ignoredKeys) && ignoredKeys.Contains((int)key))
|
||||||
|
{
|
||||||
|
sheetKeys.Remove(key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var linkedDataFile in dataFiles)
|
||||||
|
{
|
||||||
|
if (_ignoredValues.TryGetValue(linkedDataFile.Key, out ignoredKeys) && ignoredKeys.Contains((int)key))
|
||||||
|
{
|
||||||
|
sheetKeys.Remove(key);
|
||||||
|
}
|
||||||
|
else if (linkedDataFile.Value.GetRow((uint)key) != null)
|
||||||
|
{
|
||||||
|
// Console.WriteLine($"removing {key} because of {linkedDataFile.Key}");
|
||||||
|
sheetKeys.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sheetKeys.Count > 0)
|
||||||
|
{
|
||||||
|
var contents = string.Join(", ", sheetKeys);
|
||||||
|
var display = $"{linkData.Source.Field.Name}@0x{linkData.Source.Definition.Offset:X}";
|
||||||
|
var msg = $"Source {linkData.SourceSheet} field {display} contains key references {contents} which do not exist in any linked sheet.";
|
||||||
|
results.Add(ValidationResult.Warning(sheet.Name, ValidatorName(), msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.Results.Count == 0)
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class NamedInnerNamedOuterValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "NamedInnerNamedOuterValidator";
|
||||||
|
private string _sheetName = "";
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
_sheetName = sheet.Name;
|
||||||
|
var results = new ValidationResults();
|
||||||
|
foreach (var field in sheet.Fields)
|
||||||
|
{
|
||||||
|
CheckNames(results, null, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.Results.Count == 0)
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NamedInnerNamedOuterValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
private void CheckNames(ValidationResults results, Field? parentField, Field field)
|
||||||
|
{
|
||||||
|
if (parentField != null)
|
||||||
|
{
|
||||||
|
if (parentField.Fields.Count == 1 && !string.IsNullOrEmpty(parentField.Name) && !string.IsNullOrEmpty(field.Name) && field.Type == FieldType.Scalar)
|
||||||
|
{
|
||||||
|
var msg = $"Parent field {parentField.Name} has a single named field {field.Name}.";
|
||||||
|
results.Results.Add(ValidationResult.Warning(_sheetName, ValidatorName(), msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.Type != FieldType.Array)
|
||||||
|
{
|
||||||
|
// Single field
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.Type == FieldType.Array)
|
||||||
|
{
|
||||||
|
if (field.Fields != null)
|
||||||
|
{
|
||||||
|
foreach (var nestedField in field.Fields)
|
||||||
|
{
|
||||||
|
CheckNames(results, field, nestedField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
SchemaValidator/Validation/Validators/SchemaFileValidator.cs
Normal file
43
SchemaValidator/Validation/Validators/SchemaFileValidator.cs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Json.Schema;
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class SchemaFileValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "SchemaFileValidator";
|
||||||
|
|
||||||
|
private readonly JsonSerializerSettings _settings = new()
|
||||||
|
{
|
||||||
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
|
DefaultValueHandling = DefaultValueHandling.Ignore,
|
||||||
|
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly JsonSchema _schema;
|
||||||
|
|
||||||
|
public SchemaFileValidator(GameData gameData, string schemaText) : base(gameData)
|
||||||
|
{
|
||||||
|
_schema = JsonSchema.FromText(schemaText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
// Re-serialize the yml sheet into json
|
||||||
|
var json = JsonConvert.SerializeObject(sheet, _settings);
|
||||||
|
|
||||||
|
if (json == null) return ValidationResults.Failed(sheet.Name, ValidatorName(), "Json serialization returned null.");
|
||||||
|
|
||||||
|
var node = JsonNode.Parse(json);
|
||||||
|
var schemaResult = _schema.Evaluate(node);
|
||||||
|
|
||||||
|
if (schemaResult == null) return ValidationResults.Failed(sheet.Name, ValidatorName(), "Schema validation returned null.");
|
||||||
|
if (schemaResult.IsValid) return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return ValidationResults.Error(sheet.Name, ValidatorName());
|
||||||
|
}
|
||||||
|
}
|
114
SchemaValidator/Validation/Validators/SingleLinkRefValidator.cs
Normal file
114
SchemaValidator/Validation/Validators/SingleLinkRefValidator.cs
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Lumina;
|
||||||
|
using Lumina.Data.Files.Excel;
|
||||||
|
using SchemaValidator.New;
|
||||||
|
using SchemaValidator.Util;
|
||||||
|
|
||||||
|
namespace SchemaValidator.Validation.Validators;
|
||||||
|
|
||||||
|
public class SingleLinkRefValidator : Validator
|
||||||
|
{
|
||||||
|
public override string ValidatorName() => "SingleLinkRefValidator";
|
||||||
|
|
||||||
|
private Dictionary<string, List<int>> _ignoredValues = new();
|
||||||
|
|
||||||
|
public SingleLinkRefValidator(GameData gameData) : base(gameData) { }
|
||||||
|
|
||||||
|
public SingleLinkRefValidator(GameData gameData, Dictionary<string, List<int>> ignoredValues) : base(gameData)
|
||||||
|
{
|
||||||
|
_ignoredValues = ignoredValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LinkTargetData
|
||||||
|
{
|
||||||
|
public string SourceSheet { get; set; }
|
||||||
|
public DefinedColumn Source { get; set; }
|
||||||
|
|
||||||
|
public string TargetSheet { get; set; }
|
||||||
|
public HashSet<long> TargetKeys { get; set; }
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is not LinkTargetData other)
|
||||||
|
return false;
|
||||||
|
return SourceSheet == other.SourceSheet && Source.Field.Equals(other.Source.Field) && TargetSheet == other.TargetSheet && TargetKeys.SetEquals(other.TargetKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResults Validate(ExcelHeaderFile exh, Sheet sheet)
|
||||||
|
{
|
||||||
|
var results = new ValidationResults();
|
||||||
|
var fields = SchemaUtil.Flatten(exh, sheet);
|
||||||
|
var dataFile = GameData.Excel.GetSheetRaw($"{sheet.Name}");
|
||||||
|
if (dataFile == null)
|
||||||
|
{
|
||||||
|
var msg = $"Failed to obtain sheet {sheet.Name} from game data.";
|
||||||
|
results.Results.Add(ValidationResult.Failed(sheet.Name, ValidatorName(), msg));
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all link fields with null condition, 1 target
|
||||||
|
var linkFields = fields.Where(f => f.Field is { Type: FieldType.Link, Condition: null, Targets.Count: 1 }).ToList();
|
||||||
|
|
||||||
|
var linkVals = new List<LinkTargetData>();
|
||||||
|
foreach (var field in linkFields)
|
||||||
|
{
|
||||||
|
var offset = field.Definition.Offset;
|
||||||
|
|
||||||
|
var columnValues = new HashSet<long>();
|
||||||
|
|
||||||
|
foreach (var row in dataFile.GetRowParsers())
|
||||||
|
{
|
||||||
|
var columnValue = ReadColumnIntegerValue(dataFile, row, field);
|
||||||
|
if (columnValue == null)
|
||||||
|
{
|
||||||
|
var msg = $"Row {row.RowId} column {field.Field.Name}@0x{offset:X} type {field.Definition.Type} failed to read properly.";
|
||||||
|
results.Results.Add(ValidationResult.Failed(sheet.Name, ValidatorName(), msg));
|
||||||
|
break; // don't spam the same error
|
||||||
|
}
|
||||||
|
columnValues.Add(columnValue.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// our filtering ensures we have exactly one target
|
||||||
|
var targetData = new LinkTargetData
|
||||||
|
{
|
||||||
|
SourceSheet = sheet.Name,
|
||||||
|
Source = field,
|
||||||
|
TargetSheet = field.Field.Targets[0],
|
||||||
|
TargetKeys = columnValues,
|
||||||
|
};
|
||||||
|
linkVals.Add(targetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var linkData in linkVals.Distinct())
|
||||||
|
{
|
||||||
|
var sheetName = linkData.TargetSheet;
|
||||||
|
var sheetKeys = linkData.TargetKeys;
|
||||||
|
|
||||||
|
var linkedDataFile = GameData.Excel.GetSheetRaw(sheetName);
|
||||||
|
if (linkedDataFile == null)
|
||||||
|
{
|
||||||
|
var msg = $"Source {linkData.SourceSheet} field {linkData.Source.Field.Name}@0x{linkData.Source.Definition.Offset:X} references non-existent sheet {sheetName}.";
|
||||||
|
results.Add(ValidationResult.Error(sheet.Name, ValidatorName(), msg));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var key in sheetKeys)
|
||||||
|
{
|
||||||
|
if (_ignoredValues.TryGetValue(sheetName, out var ignoredKeys) && ignoredKeys.Contains((int)key)) continue;
|
||||||
|
if (_ignoredValues.TryGetValue("all", out ignoredKeys) && ignoredKeys.Contains((int)key)) continue;
|
||||||
|
|
||||||
|
if (linkedDataFile.GetRow((uint)key) == null)
|
||||||
|
{
|
||||||
|
var display = $"{linkData.Source.Field.Name}@0x{linkData.Source.Definition.Offset:X}";
|
||||||
|
var msg = $"Source {linkData.SourceSheet} field {display} references {sheetName} key '{key}' which does not exist.";
|
||||||
|
results.Add(ValidationResult.Warning(sheet.Name, ValidatorName(), msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.Results.Count == 0)
|
||||||
|
return ValidationResults.Success(sheet.Name, ValidatorName());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
6
Schemas/AOZArrangement.yml
Normal file
6
Schemas/AOZArrangement.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name: AOZArrangement
|
||||||
|
fields:
|
||||||
|
- name: AOZContentBriefingBNpc
|
||||||
|
type: link
|
||||||
|
targets: [AOZContentBriefingBNpc]
|
||||||
|
- name: Position
|
7
Schemas/AOZBoss.yml
Normal file
7
Schemas/AOZBoss.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: AOZBoss
|
||||||
|
displayField: Boss
|
||||||
|
fields:
|
||||||
|
- name: Boss
|
||||||
|
type: link
|
||||||
|
targets: [AOZContentBriefingBNpc]
|
||||||
|
- name: Position
|
41
Schemas/AOZContent.yml
Normal file
41
Schemas/AOZContent.yml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
name: AOZContent
|
||||||
|
fields:
|
||||||
|
- name: GilReward
|
||||||
|
- name: AlliedSealsReward
|
||||||
|
- name: TomestonesReward
|
||||||
|
- name: ContentEntry
|
||||||
|
type: link
|
||||||
|
targets: [ContentEntry]
|
||||||
|
- name: StandardFinishTime
|
||||||
|
- name: IdealFinishTime
|
||||||
|
- name: Act1
|
||||||
|
type: link
|
||||||
|
condition:
|
||||||
|
switch: Act1FightType
|
||||||
|
cases:
|
||||||
|
1: [AOZArrangement]
|
||||||
|
2: [AOZBoss]
|
||||||
|
- name: Act2
|
||||||
|
type: link
|
||||||
|
condition:
|
||||||
|
switch: Act2FightType
|
||||||
|
cases:
|
||||||
|
1: [AOZArrangement]
|
||||||
|
2: [AOZBoss]
|
||||||
|
- name: Act3
|
||||||
|
type: link
|
||||||
|
condition:
|
||||||
|
switch: Act3FightType
|
||||||
|
cases:
|
||||||
|
1: [AOZArrangement]
|
||||||
|
2: [AOZBoss]
|
||||||
|
- name: Unknown5
|
||||||
|
- name: Unknown9
|
||||||
|
- name: Unknown13
|
||||||
|
- name: Act1FightType
|
||||||
|
- name: Act2FightType
|
||||||
|
- name: Act3FightType
|
||||||
|
- name: ArenaType1
|
||||||
|
- name: ArenaType2
|
||||||
|
- name: ArenaType3
|
||||||
|
- name: Order
|
32
Schemas/AOZContentBriefingBNpc.yml
Normal file
32
Schemas/AOZContentBriefingBNpc.yml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
name: AOZContentBriefingBNpc
|
||||||
|
displayField: BNpcName
|
||||||
|
fields:
|
||||||
|
- name: BNpcName
|
||||||
|
type: link
|
||||||
|
targets: [BNpcName]
|
||||||
|
- name: TargetSmall
|
||||||
|
type: icon
|
||||||
|
- name: TargetLarge
|
||||||
|
type: icon
|
||||||
|
- name: Endurance
|
||||||
|
- name: Fire
|
||||||
|
- name: Ice
|
||||||
|
- name: Wind
|
||||||
|
- name: Earth
|
||||||
|
- name: Thunder
|
||||||
|
- name: Water
|
||||||
|
- name: Slashing
|
||||||
|
- name: Piercing
|
||||||
|
- name: Blunt
|
||||||
|
- name: Magic
|
||||||
|
- name: HideStats
|
||||||
|
- name: SlowVuln
|
||||||
|
- name: PetrificationVuln
|
||||||
|
- name: ParalysisVuln
|
||||||
|
- name: InterruptionVuln
|
||||||
|
- name: BlindVuln
|
||||||
|
- name: StunVuln
|
||||||
|
- name: SleepVuln
|
||||||
|
- name: BindVuln
|
||||||
|
- name: HeavyVuln
|
||||||
|
- name: FlatOrDeathVuln
|
5
Schemas/AOZContentBriefingObject.yml
Normal file
5
Schemas/AOZContentBriefingObject.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: AOZContentBriefingObject
|
||||||
|
fields:
|
||||||
|
- name: Icon
|
||||||
|
type: icon
|
||||||
|
- name: Unknown1
|
7
Schemas/AOZReport.yml
Normal file
7
Schemas/AOZReport.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: AOZReport
|
||||||
|
fields:
|
||||||
|
- name: Unknown0
|
||||||
|
- name: Reward
|
||||||
|
type: link
|
||||||
|
targets: [AOZReportReward]
|
||||||
|
- name: Order
|
7
Schemas/AOZScore.yml
Normal file
7
Schemas/AOZScore.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: AOZScore
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: Description
|
||||||
|
- name: Score
|
||||||
|
- name: IsHidden
|
62
Schemas/Achievement.yml
Normal file
62
Schemas/Achievement.yml
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
name: Achievement
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: Description
|
||||||
|
- name: Item
|
||||||
|
type: link
|
||||||
|
targets: [Item]
|
||||||
|
- name: Key
|
||||||
|
type: link
|
||||||
|
condition:
|
||||||
|
switch: Type
|
||||||
|
cases:
|
||||||
|
2: [Achievement]
|
||||||
|
3: [ClassJob]
|
||||||
|
6: [Quest]
|
||||||
|
7: [ClassJob]
|
||||||
|
8: [Map]
|
||||||
|
9: [Quest]
|
||||||
|
11: [GrandCompany]
|
||||||
|
14: [InstanceContent]
|
||||||
|
15: [BeastTribe]
|
||||||
|
18: [GrandCompany]
|
||||||
|
20: [AetherCurrentCompFlgSet]
|
||||||
|
24: [Quest]
|
||||||
|
- name: Data
|
||||||
|
type: array
|
||||||
|
count: 8
|
||||||
|
fields:
|
||||||
|
- type: link
|
||||||
|
condition:
|
||||||
|
switch: Type
|
||||||
|
cases:
|
||||||
|
2: [Achievement]
|
||||||
|
6: [Quest]
|
||||||
|
9: [Quest]
|
||||||
|
15: [BeastReputationRank]
|
||||||
|
20: [AetherCurrentCompFlgSet]
|
||||||
|
24: [ClassJob, Quest]
|
||||||
|
- name: Title
|
||||||
|
type: link
|
||||||
|
targets: [Title]
|
||||||
|
- name: Icon
|
||||||
|
type: icon
|
||||||
|
- name: Order
|
||||||
|
- name: AchievementCategory
|
||||||
|
type: link
|
||||||
|
targets: [AchievementCategory]
|
||||||
|
- name: AchievementTarget
|
||||||
|
type: link
|
||||||
|
targets: [AchievementTarget]
|
||||||
|
- name: Unknown4
|
||||||
|
- name: Points
|
||||||
|
- name: Unknown8
|
||||||
|
- name: Unknown9
|
||||||
|
- name: Unknown10
|
||||||
|
- name: Unknown12
|
||||||
|
- name: Type
|
||||||
|
- name: Unknown24
|
||||||
|
- name: AchievementHideCondition
|
||||||
|
type: link
|
||||||
|
targets: [AchievementHideCondition]
|
10
Schemas/AchievementCategory.yml
Normal file
10
Schemas/AchievementCategory.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
name: AchievementCategory
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: AchievementKind
|
||||||
|
type: link
|
||||||
|
targets: [AchievementKind]
|
||||||
|
- name: Order
|
||||||
|
- name: ShowComplete
|
||||||
|
- name: HideCategory
|
5
Schemas/AchievementHideCondition.yml
Normal file
5
Schemas/AchievementHideCondition.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: AchievementHideCondition
|
||||||
|
fields:
|
||||||
|
- name: HideAchievement
|
||||||
|
- name: HideName
|
||||||
|
- name: HideConditions
|
5
Schemas/AchievementKind.yml
Normal file
5
Schemas/AchievementKind.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: AchievementKind
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: Order
|
5
Schemas/AchievementTarget.yml
Normal file
5
Schemas/AchievementTarget.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: AchievementTarget
|
||||||
|
displayField: Value
|
||||||
|
fields:
|
||||||
|
- name: Value
|
||||||
|
- name: Type
|
105
Schemas/Action.yml
Normal file
105
Schemas/Action.yml
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
name: Action
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: UnlockLink
|
||||||
|
type: link
|
||||||
|
targets: [ChocoboTaxiStand, CraftLeve, CustomTalk, DefaultTalk, FccShop, GCShop, GilShop, GuildleveAssignment, GuildOrderGuide, GuildOrderOfficer, Quest, SpecialShop, Story, SwitchTalk, TopicSelect, TripleTriad, Warp]
|
||||||
|
- name: Icon
|
||||||
|
type: icon
|
||||||
|
- name: VFX
|
||||||
|
type: link
|
||||||
|
targets: [ActionCastVFX]
|
||||||
|
- name: ActionTimelineHit
|
||||||
|
type: link
|
||||||
|
targets: [ActionTimeline]
|
||||||
|
- name: PrimaryCostValue
|
||||||
|
- name: SecondaryCostValue
|
||||||
|
type: link
|
||||||
|
condition:
|
||||||
|
switch: SecondaryCostType
|
||||||
|
cases:
|
||||||
|
32: [Status]
|
||||||
|
35: [Status]
|
||||||
|
46: [Status]
|
||||||
|
- name: ActionCombo
|
||||||
|
type: link
|
||||||
|
targets: [Action]
|
||||||
|
- name: Cast100ms
|
||||||
|
- name: Recast100ms
|
||||||
|
- name: StatusGainSelf
|
||||||
|
type: link
|
||||||
|
targets: [Status]
|
||||||
|
- name: Omen
|
||||||
|
type: link
|
||||||
|
targets: [Omen]
|
||||||
|
- name: Unknown54
|
||||||
|
- name: AnimationEnd
|
||||||
|
type: link
|
||||||
|
targets: [ActionTimeline]
|
||||||
|
- name: ActionCategory
|
||||||
|
type: link
|
||||||
|
targets: [ActionCategory]
|
||||||
|
- name: Unknown4
|
||||||
|
- name: AnimationStart
|
||||||
|
type: link
|
||||||
|
targets: [ActionCastTimeline]
|
||||||
|
- name: Unknown9
|
||||||
|
- name: BehaviourType
|
||||||
|
- name: ClassJobLevel
|
||||||
|
- name: CastType
|
||||||
|
- name: EffectRange
|
||||||
|
- name: XAxisModifier
|
||||||
|
- name: PrimaryCostType
|
||||||
|
- name: SecondaryCostType
|
||||||
|
- name: Unknown38
|
||||||
|
- name: CooldownGroup
|
||||||
|
- name: AdditionalCooldownGroup
|
||||||
|
- name: MaxCharges
|
||||||
|
- name: Aspect
|
||||||
|
- name: ActionProcStatus
|
||||||
|
type: link
|
||||||
|
targets: [ActionProcStatus]
|
||||||
|
- name: Unknown46
|
||||||
|
- name: ClassJobCategory
|
||||||
|
type: link
|
||||||
|
targets: [ClassJobCategory]
|
||||||
|
- name: Unknown50
|
||||||
|
- name: Unknown64
|
||||||
|
- name: ClassJob
|
||||||
|
type: link
|
||||||
|
targets: [ClassJob]
|
||||||
|
- name: Range
|
||||||
|
- name: Unknown24
|
||||||
|
- name: AttackType
|
||||||
|
type: link
|
||||||
|
targets: [AttackType]
|
||||||
|
- name: Unknown1
|
||||||
|
- name: IsRoleAction
|
||||||
|
- name: CanTargetSelf
|
||||||
|
- name: CanTargetParty
|
||||||
|
- name: CanTargetFriendly
|
||||||
|
- name: CanTargetHostile
|
||||||
|
- name: Unknown19
|
||||||
|
- name: Unknown20
|
||||||
|
- name: TargetArea
|
||||||
|
- name: Unknown22
|
||||||
|
- name: Unknown23
|
||||||
|
- name: CanTargetDead
|
||||||
|
- name: Unknown26
|
||||||
|
- name: Unknown30
|
||||||
|
- name: PreservesCombo
|
||||||
|
- name: Unknown51
|
||||||
|
- name: AffectsPosition
|
||||||
|
- name: IsPvP
|
||||||
|
- name: Unknown56
|
||||||
|
- name: Unknown57
|
||||||
|
- name: Unknown58
|
||||||
|
- name: Unknown59
|
||||||
|
- name: Unknown60
|
||||||
|
- name: Unknown61
|
||||||
|
- name: Unknown62
|
||||||
|
- name: Unknown63
|
||||||
|
- name: Unknown65
|
||||||
|
- name: Unknown66
|
||||||
|
- name: IsPlayerAction
|
9
Schemas/ActionCastTimeline.yml
Normal file
9
Schemas/ActionCastTimeline.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
name: ActionCastTimeline
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
type: link
|
||||||
|
targets: [ActionTimeline]
|
||||||
|
- name: VFX
|
||||||
|
type: link
|
||||||
|
targets: [VFX]
|
6
Schemas/ActionCastVFX.yml
Normal file
6
Schemas/ActionCastVFX.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name: ActionCastVFX
|
||||||
|
displayField: VFX
|
||||||
|
fields:
|
||||||
|
- name: VFX
|
||||||
|
type: link
|
||||||
|
targets: [VFX]
|
4
Schemas/ActionCategory.yml
Normal file
4
Schemas/ActionCategory.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: ActionCategory
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
15
Schemas/ActionComboRoute.yml
Normal file
15
Schemas/ActionComboRoute.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
name: ActionComboRoute
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: Action
|
||||||
|
type: array
|
||||||
|
count: 4
|
||||||
|
fields:
|
||||||
|
- type: link
|
||||||
|
targets: [Action]
|
||||||
|
- name: Unknown6
|
||||||
|
- name: Unknown7
|
||||||
|
- name: Unknown8
|
||||||
|
- name: Unknown1
|
||||||
|
- name: Unknown9
|
12
Schemas/ActionIndirection.yml
Normal file
12
Schemas/ActionIndirection.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name: ActionIndirection
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
type: link
|
||||||
|
targets: [Action]
|
||||||
|
- name: PreviousComboAction
|
||||||
|
type: link
|
||||||
|
targets: [Action]
|
||||||
|
- name: ClassJob
|
||||||
|
type: link
|
||||||
|
targets: [ClassJob]
|
5
Schemas/ActionParam.yml
Normal file
5
Schemas/ActionParam.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: ActionParam
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: Unknown1
|
6
Schemas/ActionProcStatus.yml
Normal file
6
Schemas/ActionProcStatus.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name: ActionProcStatus
|
||||||
|
displayField: Status
|
||||||
|
fields:
|
||||||
|
- name: Status
|
||||||
|
type: link
|
||||||
|
targets: [Status]
|
27
Schemas/ActionTimeline.yml
Normal file
27
Schemas/ActionTimeline.yml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
name: ActionTimeline
|
||||||
|
displayField: Key
|
||||||
|
fields:
|
||||||
|
- name: Key
|
||||||
|
- name: KillUpper
|
||||||
|
- name: Type
|
||||||
|
- name: Priority
|
||||||
|
- name: Stance
|
||||||
|
- name: Slot
|
||||||
|
- name: LookAtMode
|
||||||
|
- name: ActionTimelineIDMode
|
||||||
|
- name: WeaponTimeline
|
||||||
|
type: link
|
||||||
|
targets: [WeaponTimeline]
|
||||||
|
- name: LoadType
|
||||||
|
- name: StartAttach
|
||||||
|
- name: ResidentPap
|
||||||
|
- name: IsLoop
|
||||||
|
- name: Unknown20
|
||||||
|
- name: Unknown21
|
||||||
|
- name: Pause
|
||||||
|
- name: Resident
|
||||||
|
- name: IsMotionCanceledByMoving
|
||||||
|
- name: Unknown15
|
||||||
|
- name: Unknown17
|
||||||
|
- name: Unknown18
|
||||||
|
- name: Unknown19
|
8
Schemas/ActionTimelineMove.yml
Normal file
8
Schemas/ActionTimelineMove.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
name: ActionTimelineMove
|
||||||
|
fields:
|
||||||
|
- name: Unknown4
|
||||||
|
- name: Unknown0
|
||||||
|
- name: Unknown1
|
||||||
|
- name: Unknown2
|
||||||
|
- name: Unknown3
|
||||||
|
- name: Unknown5
|
8
Schemas/ActionTimelineReplace.yml
Normal file
8
Schemas/ActionTimelineReplace.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
name: ActionTimelineReplace
|
||||||
|
fields:
|
||||||
|
- name: Old
|
||||||
|
type: link
|
||||||
|
targets: [ActionTimeline]
|
||||||
|
- name: New
|
||||||
|
type: link
|
||||||
|
targets: [ActionTimeline]
|
4
Schemas/ActionTransient.yml
Normal file
4
Schemas/ActionTransient.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: ActionTransient
|
||||||
|
displayField: Description
|
||||||
|
fields:
|
||||||
|
- name: Description
|
7
Schemas/ActivityFeedButtons.yml
Normal file
7
Schemas/ActivityFeedButtons.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: ActivityFeedButtons
|
||||||
|
fields:
|
||||||
|
- name: BannerURL
|
||||||
|
- name: Description
|
||||||
|
- name: Language
|
||||||
|
- name: PictureURL
|
||||||
|
- name: Unknown0
|
6
Schemas/ActivityFeedCaptions.yml
Normal file
6
Schemas/ActivityFeedCaptions.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name: ActivityFeedCaptions
|
||||||
|
fields:
|
||||||
|
- name: JA
|
||||||
|
- name: EN
|
||||||
|
- name: DE
|
||||||
|
- name: FR
|
6
Schemas/ActivityFeedGroupCaptions.yml
Normal file
6
Schemas/ActivityFeedGroupCaptions.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name: ActivityFeedGroupCaptions
|
||||||
|
fields:
|
||||||
|
- name: JA
|
||||||
|
- name: EN
|
||||||
|
- name: DE
|
||||||
|
- name: FR
|
7
Schemas/ActivityFeedImages.yml
Normal file
7
Schemas/ActivityFeedImages.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: ActivityFeedImages
|
||||||
|
fields:
|
||||||
|
- name: ExpansionImage
|
||||||
|
- name: ActivityFeedJA
|
||||||
|
- name: ActivityFeedEN
|
||||||
|
- name: ActivityFeedDE
|
||||||
|
- name: ActivityFeedFR
|
4
Schemas/Addon.yml
Normal file
4
Schemas/Addon.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: Addon
|
||||||
|
displayField: Text
|
||||||
|
fields:
|
||||||
|
- name: Text
|
26
Schemas/Adventure.yml
Normal file
26
Schemas/Adventure.yml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name: Adventure
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: Impression
|
||||||
|
- name: Description
|
||||||
|
- name: Level
|
||||||
|
type: link
|
||||||
|
targets: [Level]
|
||||||
|
- name: MinLevel
|
||||||
|
- name: PlaceName
|
||||||
|
type: link
|
||||||
|
targets: [PlaceName]
|
||||||
|
- name: IconList
|
||||||
|
type: icon
|
||||||
|
- name: IconDiscovered
|
||||||
|
type: icon
|
||||||
|
- name: IconUndiscovered
|
||||||
|
type: icon
|
||||||
|
- name: Emote
|
||||||
|
type: link
|
||||||
|
targets: [Emote]
|
||||||
|
- name: MinTime
|
||||||
|
- name: MaxTime
|
||||||
|
- name: MaxLevel
|
||||||
|
- name: IsInitial
|
15
Schemas/AdventureExPhase.yml
Normal file
15
Schemas/AdventureExPhase.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
name: AdventureExPhase
|
||||||
|
fields:
|
||||||
|
- name: Quest
|
||||||
|
type: link
|
||||||
|
targets: [Quest]
|
||||||
|
- name: AdventureBegin
|
||||||
|
type: link
|
||||||
|
targets: [Adventure]
|
||||||
|
- name: AdventureEnd
|
||||||
|
type: link
|
||||||
|
targets: [Adventure]
|
||||||
|
- name: Unknown4
|
||||||
|
- name: Expansion
|
||||||
|
type: link
|
||||||
|
targets: [ExVersion]
|
6
Schemas/AetherCurrent.yml
Normal file
6
Schemas/AetherCurrent.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name: AetherCurrent
|
||||||
|
displayField: Quest
|
||||||
|
fields:
|
||||||
|
- name: Quest
|
||||||
|
type: link
|
||||||
|
targets: [Quest]
|
11
Schemas/AetherCurrentCompFlgSet.yml
Normal file
11
Schemas/AetherCurrentCompFlgSet.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
name: AetherCurrentCompFlgSet
|
||||||
|
fields:
|
||||||
|
- name: Territory
|
||||||
|
type: link
|
||||||
|
targets: [TerritoryType]
|
||||||
|
- name: AetherCurrents
|
||||||
|
type: array
|
||||||
|
count: 15
|
||||||
|
fields:
|
||||||
|
- type: link
|
||||||
|
targets: [AetherCurrent]
|
10
Schemas/AetherialWheel.yml
Normal file
10
Schemas/AetherialWheel.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
name: AetherialWheel
|
||||||
|
fields:
|
||||||
|
- name: ItemUnprimed
|
||||||
|
type: link
|
||||||
|
targets: [Item]
|
||||||
|
- name: ItemPrimed
|
||||||
|
type: link
|
||||||
|
targets: [Item]
|
||||||
|
- name: Grade
|
||||||
|
- name: HoursRequired
|
39
Schemas/Aetheryte.yml
Normal file
39
Schemas/Aetheryte.yml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
name: Aetheryte
|
||||||
|
displayField: PlaceName
|
||||||
|
fields:
|
||||||
|
- name: Singular
|
||||||
|
- name: Plural
|
||||||
|
- name: Adjective
|
||||||
|
- name: PossessivePronoun
|
||||||
|
- name: StartsWithVowel
|
||||||
|
- name: Unknown5
|
||||||
|
- name: Pronoun
|
||||||
|
- name: Article
|
||||||
|
- name: Unknown16
|
||||||
|
- name: Level
|
||||||
|
type: array
|
||||||
|
count: 4
|
||||||
|
fields:
|
||||||
|
- type: link
|
||||||
|
targets: [Level]
|
||||||
|
- name: RequiredQuest
|
||||||
|
type: link
|
||||||
|
targets: [Quest]
|
||||||
|
- name: PlaceName
|
||||||
|
type: link
|
||||||
|
targets: [PlaceName]
|
||||||
|
- name: AethernetName
|
||||||
|
type: link
|
||||||
|
targets: [PlaceName]
|
||||||
|
- name: Territory
|
||||||
|
type: link
|
||||||
|
targets: [TerritoryType]
|
||||||
|
- name: Map
|
||||||
|
type: link
|
||||||
|
targets: [Map]
|
||||||
|
- name: AetherstreamX
|
||||||
|
- name: AetherstreamY
|
||||||
|
- name: AethernetGroup
|
||||||
|
- name: Order
|
||||||
|
- name: IsAetheryte
|
||||||
|
- name: Invisible
|
5
Schemas/AetheryteSystemDefine.yml
Normal file
5
Schemas/AetheryteSystemDefine.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: AetheryteSystemDefine
|
||||||
|
displayField: Text
|
||||||
|
fields:
|
||||||
|
- name: Text
|
||||||
|
- name: DefineValue
|
3
Schemas/AetheryteTransient.yml
Normal file
3
Schemas/AetheryteTransient.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
name: AetheryteTransient
|
||||||
|
fields:
|
||||||
|
- name: Unknown0
|
4
Schemas/AirshipExplorationLevel.yml
Normal file
4
Schemas/AirshipExplorationLevel.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: AirshipExplorationLevel
|
||||||
|
fields:
|
||||||
|
- name: ExpToNext
|
||||||
|
- name: Capacity
|
4
Schemas/AirshipExplorationLog.yml
Normal file
4
Schemas/AirshipExplorationLog.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: AirshipExplorationLog
|
||||||
|
displayField: Text
|
||||||
|
fields:
|
||||||
|
- name: Text
|
4
Schemas/AirshipExplorationParamType.yml
Normal file
4
Schemas/AirshipExplorationParamType.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: AirshipExplorationParamType
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
12
Schemas/AirshipExplorationPart.yml
Normal file
12
Schemas/AirshipExplorationPart.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name: AirshipExplorationPart
|
||||||
|
fields:
|
||||||
|
- name: Class
|
||||||
|
- name: Surveillance
|
||||||
|
- name: Retrieval
|
||||||
|
- name: Speed
|
||||||
|
- name: Range
|
||||||
|
- name: Favor
|
||||||
|
- name: Slot
|
||||||
|
- name: Rank
|
||||||
|
- name: Components
|
||||||
|
- name: RepairMaterials
|
17
Schemas/AirshipExplorationPoint.yml
Normal file
17
Schemas/AirshipExplorationPoint.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
name: AirshipExplorationPoint
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: NameShort
|
||||||
|
- name: ExpReward
|
||||||
|
- name: CeruleumTankReq
|
||||||
|
- name: SurveyDurationmin
|
||||||
|
- name: SurveyDistance
|
||||||
|
- name: X
|
||||||
|
- name: Y
|
||||||
|
- name: RankReq
|
||||||
|
- name: Unknown9
|
||||||
|
- name: Unknown11
|
||||||
|
- name: SurveillanceReq
|
||||||
|
- name: Unknown12
|
||||||
|
- name: Passengers
|
11
Schemas/AkatsukiNote.yml
Normal file
11
Schemas/AkatsukiNote.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
name: AkatsukiNote
|
||||||
|
fields:
|
||||||
|
- name: Unknown0
|
||||||
|
- name: Unknown1
|
||||||
|
- name: Unknown2
|
||||||
|
- name: Unknown3
|
||||||
|
- name: Unknown4
|
||||||
|
- name: Unknown5
|
||||||
|
- name: Unknown6
|
||||||
|
- name: Unknown7
|
||||||
|
- name: Unknown8
|
3
Schemas/AkatsukiNoteString.yml
Normal file
3
Schemas/AkatsukiNoteString.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
name: AkatsukiNoteString
|
||||||
|
fields:
|
||||||
|
- name: Unknown0
|
13
Schemas/AnimaWeapon5.yml
Normal file
13
Schemas/AnimaWeapon5.yml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
name: AnimaWeapon5
|
||||||
|
fields:
|
||||||
|
- name: Item
|
||||||
|
type: link
|
||||||
|
targets: [Item]
|
||||||
|
- name: Unknown1
|
||||||
|
- name: SecondaryStatTotal
|
||||||
|
- name: Parameter
|
||||||
|
type: array
|
||||||
|
count: 5
|
||||||
|
fields:
|
||||||
|
- type: link
|
||||||
|
targets: [AnimaWeapon5Param]
|
7
Schemas/AnimaWeapon5Param.yml
Normal file
7
Schemas/AnimaWeapon5Param.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: AnimaWeapon5Param
|
||||||
|
displayField: BaseParam
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: BaseParam
|
||||||
|
type: link
|
||||||
|
targets: [BaseParam]
|
4
Schemas/AnimaWeapon5PatternGroup.yml
Normal file
4
Schemas/AnimaWeapon5PatternGroup.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: AnimaWeapon5PatternGroup
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
6
Schemas/AnimaWeapon5SpiritTalk.yml
Normal file
6
Schemas/AnimaWeapon5SpiritTalk.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name: AnimaWeapon5SpiritTalk
|
||||||
|
displayField: Dialogue
|
||||||
|
fields:
|
||||||
|
- name: Dialogue
|
||||||
|
type: link
|
||||||
|
targets: [AnimaWeapon5SpiritTalkParam]
|
5
Schemas/AnimaWeapon5SpiritTalkParam.yml
Normal file
5
Schemas/AnimaWeapon5SpiritTalkParam.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: AnimaWeapon5SpiritTalkParam
|
||||||
|
displayField: Prologue
|
||||||
|
fields:
|
||||||
|
- name: Prologue
|
||||||
|
- name: Epilogue
|
22
Schemas/AnimaWeapon5TradeItem.yml
Normal file
22
Schemas/AnimaWeapon5TradeItem.yml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
name: AnimaWeapon5TradeItem
|
||||||
|
fields:
|
||||||
|
- name: CrystalSand
|
||||||
|
type: link
|
||||||
|
targets: [Item]
|
||||||
|
- name: Item
|
||||||
|
type: array
|
||||||
|
count: 8
|
||||||
|
fields:
|
||||||
|
- type: link
|
||||||
|
targets: [ Item ]
|
||||||
|
- name: Order
|
||||||
|
- name: ReceiveQuantity
|
||||||
|
- name: Quantity
|
||||||
|
type: array
|
||||||
|
count: 8
|
||||||
|
- name: Category
|
||||||
|
type: link
|
||||||
|
targets: [AnimaWeapon5PatternGroup]
|
||||||
|
- name: IsHQ
|
||||||
|
type: array
|
||||||
|
count: 8
|
6
Schemas/AnimaWeaponFUITalk.yml
Normal file
6
Schemas/AnimaWeaponFUITalk.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name: AnimaWeaponFUITalk
|
||||||
|
displayField: Dialogue
|
||||||
|
fields:
|
||||||
|
- name: Dialogue
|
||||||
|
type: link
|
||||||
|
targets: [AnimaWeaponFUITalkParam]
|
5
Schemas/AnimaWeaponFUITalkParam.yml
Normal file
5
Schemas/AnimaWeaponFUITalkParam.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: AnimaWeaponFUITalkParam
|
||||||
|
displayField: Prologue
|
||||||
|
fields:
|
||||||
|
- name: Prologue
|
||||||
|
- name: Epilogue
|
12
Schemas/AnimaWeaponIcon.yml
Normal file
12
Schemas/AnimaWeaponIcon.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name: AnimaWeaponIcon
|
||||||
|
fields:
|
||||||
|
- name: Hyperconductive
|
||||||
|
type: icon
|
||||||
|
- name: Reborn
|
||||||
|
type: icon
|
||||||
|
- name: Sharpened
|
||||||
|
type: icon
|
||||||
|
- name: Zodiac
|
||||||
|
type: icon
|
||||||
|
- name: ZodiacLux
|
||||||
|
type: icon
|
8
Schemas/AnimaWeaponItem.yml
Normal file
8
Schemas/AnimaWeaponItem.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
name: AnimaWeaponItem
|
||||||
|
fields:
|
||||||
|
- name: Item
|
||||||
|
type: array
|
||||||
|
count: 14
|
||||||
|
fields:
|
||||||
|
- type: link
|
||||||
|
targets: [Item]
|
8
Schemas/AnimationLOD.yml
Normal file
8
Schemas/AnimationLOD.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
name: AnimationLOD
|
||||||
|
fields:
|
||||||
|
- name: CameraDistance
|
||||||
|
- name: SampleInterval
|
||||||
|
- name: BoneLOD
|
||||||
|
- name: AnimationEnable
|
||||||
|
type: array
|
||||||
|
count: 8
|
7
Schemas/AozAction.yml
Normal file
7
Schemas/AozAction.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: AozAction
|
||||||
|
displayField: Action
|
||||||
|
fields:
|
||||||
|
- name: Action
|
||||||
|
type: link
|
||||||
|
targets: [Action]
|
||||||
|
- name: Rank
|
34
Schemas/AozActionTransient.yml
Normal file
34
Schemas/AozActionTransient.yml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
name: AozActionTransient
|
||||||
|
displayField: Action
|
||||||
|
fields:
|
||||||
|
- name: Stats
|
||||||
|
- name: Description
|
||||||
|
- name: Icon
|
||||||
|
type: icon
|
||||||
|
- name: RequiredForQuest
|
||||||
|
type: link
|
||||||
|
targets: [Quest]
|
||||||
|
- name: PreviousQuest
|
||||||
|
type: link
|
||||||
|
targets: [Quest]
|
||||||
|
- name: Location
|
||||||
|
type: link
|
||||||
|
condition:
|
||||||
|
switch: LocationKey
|
||||||
|
cases:
|
||||||
|
1: [PlaceName]
|
||||||
|
4: [ContentFinderCondition]
|
||||||
|
- name: Number
|
||||||
|
- name: LocationKey
|
||||||
|
- name: TargetsEnemy
|
||||||
|
- name: TargetsSelfOrAlly
|
||||||
|
- name: CauseSlow
|
||||||
|
- name: CausePetrify
|
||||||
|
- name: CauseParalysis
|
||||||
|
- name: CauseInterrupt
|
||||||
|
- name: CauseBlind
|
||||||
|
- name: CauseStun
|
||||||
|
- name: CauseSleep
|
||||||
|
- name: CauseBind
|
||||||
|
- name: CauseHeavy
|
||||||
|
- name: CauseDeath
|
11
Schemas/AquariumFish.yml
Normal file
11
Schemas/AquariumFish.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
name: AquariumFish
|
||||||
|
displayField: Item
|
||||||
|
fields:
|
||||||
|
- name: Item
|
||||||
|
type: link
|
||||||
|
targets: [Item]
|
||||||
|
- name: Unknown3
|
||||||
|
- name: AquariumWater
|
||||||
|
type: link
|
||||||
|
targets: [AquariumWater]
|
||||||
|
- name: Size
|
5
Schemas/AquariumWater.yml
Normal file
5
Schemas/AquariumWater.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: AquariumWater
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
||||||
|
- name: Unknown0
|
5
Schemas/ArchiveItem.yml
Normal file
5
Schemas/ArchiveItem.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: ArchiveItem
|
||||||
|
fields:
|
||||||
|
- name: Unknown0
|
||||||
|
- name: Unknown1
|
||||||
|
- name: Unknown2
|
8
Schemas/ArrayEventHandler.yml
Normal file
8
Schemas/ArrayEventHandler.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
name: ArrayEventHandler
|
||||||
|
fields:
|
||||||
|
- name: Data
|
||||||
|
type: array
|
||||||
|
count: 16
|
||||||
|
fields:
|
||||||
|
- type: link
|
||||||
|
targets: [InstanceContentGuide, Story, Opening, CustomTalk, DefaultTalk, GilShop, Warp, Quest]
|
4
Schemas/AttackType.yml
Normal file
4
Schemas/AttackType.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: AttackType
|
||||||
|
displayField: Name
|
||||||
|
fields:
|
||||||
|
- name: Name
|
7
Schemas/Attract.yml
Normal file
7
Schemas/Attract.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: Attract
|
||||||
|
fields:
|
||||||
|
- name: MaxDistance
|
||||||
|
- name: Speed
|
||||||
|
- name: MinRemainingDistance
|
||||||
|
- name: Direction
|
||||||
|
- name: UseDistanceBetweenHitboxes
|
10
Schemas/BGM.yml
Normal file
10
Schemas/BGM.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
name: BGM
|
||||||
|
displayField: File
|
||||||
|
fields:
|
||||||
|
- name: File
|
||||||
|
- name: DisableRestartResetTime
|
||||||
|
- name: Priority
|
||||||
|
- name: SpecialMode
|
||||||
|
- name: DisableRestartTimeOut
|
||||||
|
- name: DisableRestart
|
||||||
|
- name: PassEnd
|
7
Schemas/BGMFade.yml
Normal file
7
Schemas/BGMFade.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: BGMFade
|
||||||
|
fields:
|
||||||
|
- name: SceneOut
|
||||||
|
- name: SceneIn
|
||||||
|
- name: BGMFadeType
|
||||||
|
type: link
|
||||||
|
targets: [BGMFadeType]
|
6
Schemas/BGMFadeType.yml
Normal file
6
Schemas/BGMFadeType.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name: BGMFadeType
|
||||||
|
fields:
|
||||||
|
- name: FadeOutTime
|
||||||
|
- name: FadeInTime
|
||||||
|
- name: FadeInStartTime
|
||||||
|
- name: ResumeFadeInTime
|
7
Schemas/BGMScene.yml
Normal file
7
Schemas/BGMScene.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name: BGMScene
|
||||||
|
fields:
|
||||||
|
- name: EnableDisableRestart
|
||||||
|
- name: Resume
|
||||||
|
- name: EnablePassEnd
|
||||||
|
- name: ForceAutoReset
|
||||||
|
- name: IgnoreBattle
|
17
Schemas/BGMSituation.yml
Normal file
17
Schemas/BGMSituation.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
name: BGMSituation
|
||||||
|
fields:
|
||||||
|
- name: DaytimeID
|
||||||
|
type: link
|
||||||
|
targets: [BGM]
|
||||||
|
- name: NightID
|
||||||
|
type: link
|
||||||
|
targets: [BGM]
|
||||||
|
- name: BattleID
|
||||||
|
type: link
|
||||||
|
targets: [BGM]
|
||||||
|
- name: DaybreakID
|
||||||
|
type: link
|
||||||
|
targets: [BGM]
|
||||||
|
- name: TwilightID
|
||||||
|
type: link
|
||||||
|
targets: [BGM]
|
12
Schemas/BGMSwitch.yml
Normal file
12
Schemas/BGMSwitch.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name: BGMSwitch
|
||||||
|
fields:
|
||||||
|
- name: Quest
|
||||||
|
type: link
|
||||||
|
targets: [Quest]
|
||||||
|
- name: BGM
|
||||||
|
type: link
|
||||||
|
targets: [BGM, BGMSituation]
|
||||||
|
- name: BGMSystemDefine
|
||||||
|
type: link
|
||||||
|
targets: [BGMSystemDefine]
|
||||||
|
- name: Unknown2
|
4
Schemas/BGMSystemDefine.yml
Normal file
4
Schemas/BGMSystemDefine.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: BGMSystemDefine
|
||||||
|
displayField: Define
|
||||||
|
fields:
|
||||||
|
- name: Define
|
5
Schemas/BNpcAnnounceIcon.yml
Normal file
5
Schemas/BNpcAnnounceIcon.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: BNpcAnnounceIcon
|
||||||
|
displayField: Icon
|
||||||
|
fields:
|
||||||
|
- name: Icon
|
||||||
|
type: icon
|
40
Schemas/BNpcBase.yml
Normal file
40
Schemas/BNpcBase.yml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
name: BNpcBase
|
||||||
|
fields:
|
||||||
|
- name: Scale
|
||||||
|
- name: ArrayEventHandler
|
||||||
|
type: link
|
||||||
|
targets: [ArrayEventHandler]
|
||||||
|
- name: Behavior
|
||||||
|
type: link
|
||||||
|
targets: [Behavior]
|
||||||
|
- name: ModelChara
|
||||||
|
type: link
|
||||||
|
targets: [ModelChara]
|
||||||
|
- name: BNpcCustomize
|
||||||
|
type: link
|
||||||
|
targets: [BNpcCustomize]
|
||||||
|
- name: NpcEquip
|
||||||
|
type: link
|
||||||
|
targets: [NpcEquip]
|
||||||
|
- name: Special
|
||||||
|
- name: Battalion
|
||||||
|
type: link
|
||||||
|
targets: [Battalion]
|
||||||
|
- name: LinkRace
|
||||||
|
type: link
|
||||||
|
targets: [LinkRace]
|
||||||
|
- name: Rank
|
||||||
|
- name: SEPack
|
||||||
|
- name: Unknown12
|
||||||
|
- name: BNpcParts
|
||||||
|
type: link
|
||||||
|
targets: [BNpcParts]
|
||||||
|
- name: Unknown14
|
||||||
|
- name: Unknown19
|
||||||
|
- name: Unknown20
|
||||||
|
- name: Unknown21
|
||||||
|
- name: Unknown10
|
||||||
|
- name: Unknown15
|
||||||
|
- name: IsTargetLine
|
||||||
|
- name: IsDisplayLevel
|
||||||
|
- name: Unknown18
|
3
Schemas/BNpcBasePopVfx.yml
Normal file
3
Schemas/BNpcBasePopVfx.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
name: BNpcBasePopVfx
|
||||||
|
fields:
|
||||||
|
- name: Unknown0
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue