diff --git a/.editorconfig b/.editorconfig index 0cef349d..8da92064 100644 --- a/.editorconfig +++ b/.editorconfig @@ -45,7 +45,7 @@ dotnet_style_qualification_for_method = false:suggestion dotnet_style_qualification_for_event = false:suggestion # Types: use keywords instead of BCL types, and permit var only when the type is clear -csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_when_type_is_apparent = false:none csharp_style_var_elsewhere = false:suggestion dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion diff --git a/README.md b/README.md index 1477ddaf..f3efb567 100644 --- a/README.md +++ b/README.md @@ -82,27 +82,27 @@ might not work with them. ```cs // Read file with default episode (EP5) and encoding (ASCII) -var svmap = Reader.ReadFromFile("0.svmap"); +var svmap = ParsecReader.FromFile("0.svmap"); // Read file with a specific episode and default encoding -var svmap = Reader.ReadFromFile("0.svmap", Episode.Ep6); +var svmap = ParsecReader.FromFile("0.svmap", Episode.Ep6); // Read file with a specific episode and a specific encoding var windows1252Encoding = CodePagesEncodingProvider.Instance.GetEncoding(1252); -var svmap = Reader.ReadFromFile("0.svmap", Episode.Ep6, windows1252Encoding); +var svmap = ParsecReader.FromFile("0.svmap", Episode.Ep6, windows1252Encoding); ``` #### From data.saf ```cs // Load data (sah and saf) -var data = new Data("data.sah"); +var data = new Data("data.sah", "data.saf"); // Find the file you want to read var file = data.GetFile("world/0.svmap"); // Read and parse the file's content directly from the saf file -var svmap = Reader.ReadFromBuffer(file.Name, data.GetFileBuffer(file)); +var svmap = ParsecReader.FromBuffer(file.Name, data.GetFileBuffer(file)); ``` #### From a JSON file @@ -113,7 +113,7 @@ format. ```cs // Read JSON file -var svmap = Reader.ReadFromJsonFile("0_svmap.json"); +var svmap = ParsecReader.FromJsonFile("0_svmap.json"); ``` It is advised to first read a file from its original format, export it as JSON, edit it, and import it once again as @@ -126,13 +126,13 @@ All of the Episode 8 `BinarySData` formats have `CSV` support. ```cs // Read csv file -var item = Item.ReadFromCsv("Item.csv"); +var item = Item.FromCsv("Item.csv"); ``` ### Encoding When reading files, the default encoding is `ASCII`. If you want to read a file with a different encoding, you can -specify it as a parameter when calling the ReadFromFile/Json/Csv methods. +specify it as a parameter when calling the FromFile/Json/Csv methods. ### Writing @@ -186,7 +186,7 @@ specify it as a parameter when calling the `Write`, `WriteJson` and `WriteCsv` m ```cs // Load data (sah and saf) -var data = new Data("data.sah"); +var data = new Data("data.sah", "data.saf"); // Find the file you want to extract var file = data.GetFile("world/2.svmap"); @@ -206,8 +206,8 @@ DataBuilder.CreateFromDirectory("input", "output"); ```cs // Load target data and patch data -var data = new Data("data.sah"); -var update = new Data("update.sah"); +var data = new Data("data.sah", "data.saf"); +var update = new Data("update.sah", "update.saf"); // Patch data using (var dataPatcher = new DataPatcher()) diff --git a/samples/Data/Program.cs b/samples/Data/Program.cs index e96c1735..7cbd5a6d 100644 --- a/samples/Data/Program.cs +++ b/samples/Data/Program.cs @@ -1,5 +1,4 @@ using System; -using Parsec; using Parsec.Shaiya.Data; using Parsec.Shaiya.Svmap; @@ -12,16 +11,15 @@ private static void Main(string[] args) #region Read Data // Read data from .sah and .saf files - Parsec.Shaiya.Data.Data data = new("data.sah"); + Parsec.Shaiya.Data.Data data = new("data.sah", "data.saf"); // Find the file you want to extract with it's full relative path - // Keep in mind that in Episode 8, the relative path for this same case would be "data/world/2.svmap var file = data.GetFile("world/2.svmap"); // Extract the selected file data.Extract(file, "extracted"); // Read and parse the file's content directly from the saf file - Svmap svmap = Reader.ReadFromBuffer(file.Name, data.GetFileBuffer(file)); + Svmap svmap = Parsec.ParsecReader.FromBuffer(file.Name, data.GetFileBuffer(file)); Console.WriteLine($"File: {svmap.FileName}"); Console.WriteLine($"MapSize: {svmap.MapSize}"); diff --git a/samples/Files/Program.cs b/samples/Files/Program.cs index d0336926..46fee6d6 100644 --- a/samples/Files/Program.cs +++ b/samples/Files/Program.cs @@ -1,6 +1,6 @@ -using System; -using Parsec; -using Parsec.Shaiya.Svmap; +using Parsec; +using Parsec.Shaiya.SData; +using Parsec.Shaiya.Skill; namespace Sample.Files; @@ -8,27 +8,12 @@ internal static class Program { private static void Main(string[] args) { - // This sample shows how you can convert a shaiya file format (in this case svmap) into json to be able to - // edit its properties as plain text, and then, convert it back to its original format + SData.DecryptFile("/home/matias/Desktop/DBSkillData.SData", "/home/matias/Desktop/DBSkillData.dec.SData"); - // Step 1: Read a svmap from a file - Svmap svmap = Reader.ReadFromFile("2.svmap"); + var skillData = ParsecReader.FromFile("/home/matias/Desktop/DBSkillData.dec.SData"); - // You can go through and modify its properties here too - foreach (Npc npc in svmap.Npcs) - { - Console.WriteLine($"NpcId: {npc.NpcId}, Type: {npc.Type}"); - } + skillData.Write("/home/matias/Desktop/DBSkillData.dec.new.SData"); - // Step 2: Export svmap as JSON - svmap.WriteJson("2.svmap.json"); - - // Step 3: Modify the json file in any text editor - - // Step 4: Read svmap from the modified JSON file - Svmap svmapFromJson = Reader.ReadFromJsonFile("2.svmap.json"); - - // Step 5: Write the edited instance as .svmap - svmapFromJson.Write("2.edited.svmap"); + var newSkillData = ParsecReader.FromFile("/home/matias/Desktop/DBSkillData.dec.new.SData"); } } diff --git a/samples/SData/Program.cs b/samples/SData/Program.cs index 01e71f0e..731bf03d 100644 --- a/samples/SData/Program.cs +++ b/samples/SData/Program.cs @@ -1,5 +1,4 @@ -using Parsec; -using Parsec.Common; +using Parsec.Common; using Parsec.Shaiya.Item; namespace Sample.SData; @@ -12,7 +11,7 @@ private static void Main(string[] args) Parsec.Shaiya.SData.SData.DecryptFile("ItemEP6.SData", "ItemEP6.Decrypted.SData"); // Read SData from file - Item item = Reader.ReadFromFile("ItemEP6.SData", Episode.EP6); + Item item = Parsec.ParsecReader.FromFile("ItemEP6.SData", Episode.EP6); // or.. export it as csv item.WriteCsv("Item.csv"); @@ -20,7 +19,7 @@ private static void Main(string[] args) // Modify the csv file // Load the modified csv - Item itemFromCsv = Item.ReadFromCsv("Item.csv", Episode.EP6); + Item itemFromCsv = Item.FromCsv("Item.csv", Episode.EP6); // Save the modified file encrypted itemFromCsv.WriteEncrypted("Item.Encrypted.SData"); diff --git a/src/Parsec/Attributes/ConditionalPropertyAttribute.cs b/src/Parsec/Attributes/ConditionalPropertyAttribute.cs deleted file mode 100644 index 51421f6c..00000000 --- a/src/Parsec/Attributes/ConditionalPropertyAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property)] -public class ConditionalPropertyAttribute : Attribute -{ - public ConditionalPropertyAttribute(string conditioningPropertyName, object conditioningPropertyValue) - { - ConditioningPropertyName = conditioningPropertyName; - ConditioningPropertyValue = conditioningPropertyValue; - } - - public string ConditioningPropertyName { get; set; } - public object ConditioningPropertyValue { get; set; } -} diff --git a/src/Parsec/Attributes/CustomDefinitionAttribute.cs b/src/Parsec/Attributes/CustomDefinitionAttribute.cs deleted file mode 100644 index b89091c4..00000000 --- a/src/Parsec/Attributes/CustomDefinitionAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property)] -public class CustomDefinitionAttribute : Attribute -{ - public CustomDefinitionAttribute(Func read, Func> getBytes) - { - Read = read; - GetBytes = getBytes; - } - - public Func Read { get; set; } - public Func> GetBytes { get; set; } -} diff --git a/src/Parsec/Attributes/DefaultVersionAttribute.cs b/src/Parsec/Attributes/DefaultVersionAttribute.cs deleted file mode 100644 index d11258e9..00000000 --- a/src/Parsec/Attributes/DefaultVersionAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Parsec.Common; - -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Class)] -public class DefaultVersionAttribute : Attribute -{ - public Episode Episode { get; set; } - - public DefaultVersionAttribute(Episode episode) - { - Episode = episode; - } -} diff --git a/src/Parsec/Attributes/EpisodeDefinerAttribute.cs b/src/Parsec/Attributes/EpisodeDefinerAttribute.cs deleted file mode 100644 index 1327656a..00000000 --- a/src/Parsec/Attributes/EpisodeDefinerAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Parsec.Common; - -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] -public class EpisodeDefinerAttribute : Attribute -{ - public Episode Episode { get; set; } - public object Value { get; set; } - - public EpisodeDefinerAttribute(Episode episode, object value) - { - Episode = episode; - Value = value; - } -} diff --git a/src/Parsec/Attributes/FixedLengthListAttribute.cs b/src/Parsec/Attributes/FixedLengthListAttribute.cs deleted file mode 100644 index 4de0023e..00000000 --- a/src/Parsec/Attributes/FixedLengthListAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property)] -public class FixedLengthListAttribute : Attribute -{ - public Type ItemType { get; set; } - public int Length { get; set; } - - public FixedLengthListAttribute(Type itemType, int length) - { - ItemType = itemType; - Length = length; - } -} diff --git a/src/Parsec/Attributes/FixedLengthStringAttribute.cs b/src/Parsec/Attributes/FixedLengthStringAttribute.cs deleted file mode 100644 index 36f8ed29..00000000 --- a/src/Parsec/Attributes/FixedLengthStringAttribute.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property)] -public class FixedLengthStringAttribute : Attribute -{ - public int Length { get; set; } - public bool IncludeStringTerminator { get; set; } - public bool IsString256 { get; set; } - public string Suffix { get; set; } - - public FixedLengthStringAttribute(int length) - { - Length = length; - } - - public FixedLengthStringAttribute(bool isString256) - { - IsString256 = isString256; - } - - public FixedLengthStringAttribute(int length, bool removeStringTerminator) - { - Length = length; - IncludeStringTerminator = removeStringTerminator; - } -} diff --git a/src/Parsec/Attributes/LengthPrefixedListAttribute.cs b/src/Parsec/Attributes/LengthPrefixedListAttribute.cs deleted file mode 100644 index d26c611b..00000000 --- a/src/Parsec/Attributes/LengthPrefixedListAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property)] -public class LengthPrefixedListAttribute : Attribute -{ - public Type ItemType { get; set; } - public Type LengthType { get; set; } = typeof(int); - - public LengthPrefixedListAttribute(Type itemType) - { - ItemType = itemType; - } - - public LengthPrefixedListAttribute(Type itemType, Type lengthType) - { - ItemType = itemType; - LengthType = lengthType; - } -} diff --git a/src/Parsec/Attributes/LengthPrefixedStringAttribute.cs b/src/Parsec/Attributes/LengthPrefixedStringAttribute.cs deleted file mode 100644 index dea71831..00000000 --- a/src/Parsec/Attributes/LengthPrefixedStringAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property)] -public class LengthPrefixedStringAttribute : Attribute -{ - public bool IncludeStringTerminator { get; set; } - - public string Suffix { get; set; } - - public LengthPrefixedStringAttribute() - { - } - - public LengthPrefixedStringAttribute(bool includeStringTerminator) - { - IncludeStringTerminator = includeStringTerminator; - } -} diff --git a/src/Parsec/Attributes/ListLengthMultiplierAttribute.cs b/src/Parsec/Attributes/ListLengthMultiplierAttribute.cs deleted file mode 100644 index 6ae67faf..00000000 --- a/src/Parsec/Attributes/ListLengthMultiplierAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Parsec.Common; - -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] -internal sealed class ListLengthMultiplierAttribute : Attribute -{ - public Episode Episode { get; set; } - public int Multiplier { get; set; } - - public ListLengthMultiplierAttribute(Episode episode, int multiplier) - { - Episode = episode; - Multiplier = multiplier; - } -} diff --git a/src/Parsec/Attributes/ShaiyaPropertyAttribute.cs b/src/Parsec/Attributes/ShaiyaPropertyAttribute.cs deleted file mode 100644 index 554a59bc..00000000 --- a/src/Parsec/Attributes/ShaiyaPropertyAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Parsec.Common; - -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property)] -public class ShaiyaPropertyAttribute : Attribute -{ - public Episode MinEpisode { get; set; } - public Episode MaxEpisode { get; set; } - - public ShaiyaPropertyAttribute() - { - } - - public ShaiyaPropertyAttribute(Episode MinEpisode) - { - this.MinEpisode = MinEpisode; - } - - public ShaiyaPropertyAttribute(Episode MinEpisode, Episode MaxEpisode) - { - this.MinEpisode = MinEpisode; - this.MaxEpisode = MaxEpisode; - } -} diff --git a/src/Parsec/Attributes/UsePropertyAttribute.cs b/src/Parsec/Attributes/UsePropertyAttribute.cs deleted file mode 100644 index 7f900edf..00000000 --- a/src/Parsec/Attributes/UsePropertyAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Property)] -public class UsePropertyAttribute : Attribute -{ - public UsePropertyAttribute(string propertyName) - { - PropertyName = propertyName; - } - - public string PropertyName { get; set; } -} diff --git a/src/Parsec/Attributes/VersionPrefixedAttribute.cs b/src/Parsec/Attributes/VersionPrefixedAttribute.cs deleted file mode 100644 index 6754e401..00000000 --- a/src/Parsec/Attributes/VersionPrefixedAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Parsec.Common; - -namespace Parsec.Attributes; - -[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] -public class VersionPrefixedAttribute : Attribute -{ - public Type PrefixType { get; set; } - public object Prefix { get; set; } - public Episode MinEpisode { get; set; } - public Episode MaxEpisode { get; set; } = Episode.Unknown; - - public VersionPrefixedAttribute(Type prefixType, object prefix, Episode minEpisode) - { - PrefixType = prefixType; - Prefix = prefix; - MinEpisode = minEpisode; - } - - public VersionPrefixedAttribute(Type prefixType, object prefix, Episode minEpisode, Episode maxEpisode) - { - PrefixType = prefixType; - Prefix = prefix; - MinEpisode = minEpisode; - MaxEpisode = maxEpisode; - } -} diff --git a/src/Parsec/Common/ICsv.cs b/src/Parsec/Common/ICsv.cs index 76af215b..8e6db691 100644 --- a/src/Parsec/Common/ICsv.cs +++ b/src/Parsec/Common/ICsv.cs @@ -9,5 +9,5 @@ public interface ICsv /// /// Export file path /// File encoding - void WriteCsv(string outputPath, Encoding encoding = null); + void WriteCsv(string outputPath, Encoding? encoding = null); } diff --git a/src/Parsec/Common/IJsonable.cs b/src/Parsec/Common/IJsonSerializable.cs similarity index 93% rename from src/Parsec/Common/IJsonable.cs rename to src/Parsec/Common/IJsonSerializable.cs index d3b3dab7..054abb40 100644 --- a/src/Parsec/Common/IJsonable.cs +++ b/src/Parsec/Common/IJsonSerializable.cs @@ -3,7 +3,7 @@ /// /// Interface that defines the behavior of files that can be exported as json /// -public interface IJsonable +public interface IJsonSerializable { /// /// Serializes an object into json diff --git a/src/Parsec/Common/IExportable.cs b/src/Parsec/Common/IJsonWritable.cs similarity index 86% rename from src/Parsec/Common/IExportable.cs rename to src/Parsec/Common/IJsonWritable.cs index aba60519..d85e226b 100644 --- a/src/Parsec/Common/IExportable.cs +++ b/src/Parsec/Common/IJsonWritable.cs @@ -1,6 +1,6 @@ namespace Parsec.Common; -public interface IExportable : IJsonable +public interface IJsonWritable : IJsonSerializable { /// /// Writes the file as JSON with the possibility of ignoring some properties diff --git a/src/Parsec/Cryptography/SeedHeader.cs b/src/Parsec/Cryptography/SeedHeader.cs index f4f349b8..79da4e88 100644 --- a/src/Parsec/Cryptography/SeedHeader.cs +++ b/src/Parsec/Cryptography/SeedHeader.cs @@ -1,11 +1,10 @@ using System.Text; using Parsec.Extensions; -using Parsec.Shaiya.Core; using Parsec.Shaiya.SData; namespace Parsec.Cryptography; -public sealed class SeedHeader : IBinary +public sealed class SeedHeader { public SeedHeader(string signature, uint checksum, uint realSize, byte[] padding) { @@ -19,7 +18,7 @@ public SeedHeader(byte[] data) { Signature = Encoding.ASCII.GetString(data.SubArray(0, 40)); - int currentOffset = 40; + var currentOffset = 40; Checksum = BitConverter.ToUInt32(data, currentOffset); currentOffset += 4; @@ -34,7 +33,7 @@ public SeedHeader(byte[] data) currentOffset += 4; // Depending on the header type, the padding is 12 or 16 bytes (based on the existence of the 4 empty bytes) - int paddingLength = currentOffset == 48 ? 16 : 12; + var paddingLength = currentOffset == 48 ? 16 : 12; Padding = data.SubArray(currentOffset, paddingLength); } @@ -52,22 +51,19 @@ public SeedHeader(byte[] data) /// public byte[] Padding { get; set; } - public IEnumerable GetBytes(params object[] options) + public IEnumerable GetBytes(SDataVersion version) { - var version = SDataVersion.Regular; - - if (options.Length > 0) - version = (SDataVersion)options[0]; - var buffer = new List(); - buffer.AddRange(Signature.GetBytes()); + buffer.AddRange(Encoding.ASCII.GetBytes(Signature)); if (version == SDataVersion.Binary) + { buffer.AddRange(new byte[4]); + } - buffer.AddRange(Checksum.GetBytes()); - buffer.AddRange(RealSize.GetBytes()); - buffer.AddRange(Padding); + buffer.AddRange(BitConverter.GetBytes(Checksum)); + buffer.AddRange(BitConverter.GetBytes(RealSize)); + buffer.AddRange(version == SDataVersion.Binary ? new byte[12] : new byte[16]); return buffer; } } diff --git a/src/Parsec/Extensions/BinaryExtensions.cs b/src/Parsec/Extensions/BinaryExtensions.cs deleted file mode 100644 index 8a422058..00000000 --- a/src/Parsec/Extensions/BinaryExtensions.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Text; -using Parsec.Shaiya.Core; - -namespace Parsec.Extensions; - -public static class BinaryExtensions -{ - /// - /// Serializes a short value (int16) into a byte array - /// - public static IEnumerable GetBytes(this short value) => BitConverter.GetBytes(value); - - /// - /// Serializes an unsigned short value (uint16) into a byte array - /// - public static IEnumerable GetBytes(this ushort value) => BitConverter.GetBytes(value); - - /// - /// Serializes an int value (int32) into a byte array - /// - public static IEnumerable GetBytes(this int value) => BitConverter.GetBytes(value); - - /// - /// Serializes an unsigned int value (uint32) into a byte array - /// - public static IEnumerable GetBytes(this uint value) => BitConverter.GetBytes(value); - - /// - /// Serializes an int value (int64) into a byte array - /// - public static IEnumerable GetBytes(this long value) => BitConverter.GetBytes(value); - - /// - /// Serializes an unsigned int value (uint64) into a byte array - /// - public static IEnumerable GetBytes(this ulong value) => BitConverter.GetBytes(value); - - /// - /// Serializes a float value (single) into a byte array - /// - public static IEnumerable GetBytes(this float value) => BitConverter.GetBytes(value); - - /// - /// Serializes a boolean value into a byte array - /// - public static IEnumerable GetBytes(this bool value) => BitConverter.GetBytes(value); - - /// - /// Serializes a string prefixed by its length (int32) using the provided encoding - /// - public static IEnumerable GetLengthPrefixedBytes(this string str, Encoding encoding, bool includeStringTerminator = true) - { - var buffer = new List(); - - string finalStr = includeStringTerminator ? str + '\0' : str; - buffer.AddRange(finalStr.Length.GetBytes()); - buffer.AddRange(finalStr.GetBytes(encoding)); - return buffer; - } - - /// - /// Serializes a string prefixed by its length (int32) using ASCII encoding - /// - public static IEnumerable GetLengthPrefixedBytes(this string str, bool includeStringTerminator = true) => - GetLengthPrefixedBytes(str, Encoding.ASCII, includeStringTerminator); - - /// - /// Serializes the string into a byte array with ASCII encoding. It doesn't include a string terminator. - /// - public static IEnumerable GetBytes(this string str, bool includeStringTerminator = false) => - GetBytes(str, Encoding.ASCII, includeStringTerminator); - - /// - /// Serializes the string into a byte array with the provided encoding. It doesn't include a string terminator. - /// - public static IEnumerable GetBytes(this string str, Encoding encoding, bool includeStringTerminator = false) - { - var buffer = new List(); - string finalStr = includeStringTerminator ? str + '\0' : str; - buffer.AddRange(encoding.GetBytes(finalStr)); - return buffer; - } - - /// - /// Serializes a list of items into a byte array - /// - /// of type - /// Indicates if the data should be prefixed with the array's length (int32) - /// Optional parameters - public static IEnumerable GetBytes(this IEnumerable list, bool lengthPrefixed = true, params object[] options) - { - var buffer = new List(); - - var enumerable = list as IBinary[] ?? list.ToArray(); - - // Add length bytes - if (lengthPrefixed) - buffer.AddRange(enumerable.Length.GetBytes()); - - // Add item bytes - foreach (var item in enumerable) - buffer.AddRange(item.GetBytes(options)); - - return buffer; - } -} diff --git a/src/Parsec/Extensions/ListExtensions.cs b/src/Parsec/Extensions/ListExtensions.cs new file mode 100644 index 00000000..710dd027 --- /dev/null +++ b/src/Parsec/Extensions/ListExtensions.cs @@ -0,0 +1,11 @@ +using Parsec.Shaiya.Core; + +namespace Parsec.Extensions; + +public static class ListExtensions +{ + public static List ToSerializable(this IEnumerable list) where T : ISerializable + { + return list.Cast().ToList(); + } +} diff --git a/src/Parsec/Extensions/PropertyFilterCamelCaseResolver.cs b/src/Parsec/Extensions/PropertyFilterCamelCaseResolver.cs index 05d6859a..ec0688fa 100644 --- a/src/Parsec/Extensions/PropertyFilterCamelCaseResolver.cs +++ b/src/Parsec/Extensions/PropertyFilterCamelCaseResolver.cs @@ -1,5 +1,4 @@ -using System.Linq; -using Newtonsoft.Json; +using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace Parsec.Extensions; @@ -12,7 +11,7 @@ public class PropertyFilterCamelCaseResolver : CamelCasePropertyNamesContractRes { private IEnumerable _ignoredProps { get; } - public PropertyFilterCamelCaseResolver(IEnumerable ignoredProps = null) + public PropertyFilterCamelCaseResolver(IEnumerable? ignoredProps = null) { if (ignoredProps == null) { diff --git a/src/Parsec/Helpers/FileHelper.cs b/src/Parsec/Helpers/FileHelper.cs index 4f327bcc..8f245de4 100644 --- a/src/Parsec/Helpers/FileHelper.cs +++ b/src/Parsec/Helpers/FileHelper.cs @@ -17,7 +17,7 @@ public static class FileHelper /// The file content /// File encoding /// Makes a backup of the file if it already existed - public static bool WriteFile(string path, string content, Encoding encoding = null, bool backupIfExists = false) + public static bool WriteFile(string path, string content, Encoding? encoding = null, bool backupIfExists = false) { encoding ??= Encoding.ASCII; diff --git a/src/Parsec/Parsec.csproj b/src/Parsec/Parsec.csproj index f9447159..8af6b6fb 100644 --- a/src/Parsec/Parsec.csproj +++ b/src/Parsec/Parsec.csproj @@ -15,6 +15,7 @@ 1.0.0-alpha.9.0.0 netstandard2.0;netstandard2.1;net6.0;net7.0 true + enable diff --git a/src/Parsec/Readers/Reader.cs b/src/Parsec/ParsecReader.cs similarity index 59% rename from src/Parsec/Readers/Reader.cs rename to src/Parsec/ParsecReader.cs index 1348adfe..751e2af6 100644 --- a/src/Parsec/Readers/Reader.cs +++ b/src/Parsec/ParsecReader.cs @@ -1,14 +1,16 @@ +using System.Runtime.Serialization; using System.Text; using Newtonsoft.Json; using Parsec.Common; using Parsec.Extensions; using Parsec.Helpers; +using Parsec.Serialization; using Parsec.Shaiya.Core; using Parsec.Shaiya.Data; namespace Parsec; -public static class Reader +public static class ParsecReader { /// /// Reads a shaiya file format from a file @@ -18,8 +20,11 @@ public static class Reader /// File encoding /// Shaiya File Format Type /// T instance - public static T ReadFromFile(string path, Episode episode = Episode.EP5, Encoding encoding = null) where T : FileBase, new() => - FileBase.ReadFromFile(path, episode, encoding); + public static T FromFile(string path, Episode episode = Episode.EP5, Encoding? encoding = null) where T : FileBase, new() + { + var serializationOptions = new BinarySerializationOptions(episode, encoding); + return FileBase.ReadFromFile(path, serializationOptions); + } /// /// Reads a shaiya file format from a file @@ -28,9 +33,10 @@ public static class Reader /// File episode /// File encoding /// Shaiya File Format Type - public static Task ReadFromFileAsync(string path, Episode episode = Episode.EP5, Encoding encoding = null) - where T : FileBase, new() => - Task.FromResult(ReadFromFile(path, episode, encoding)); + public static Task FromFileAsync(string path, Episode episode = Episode.EP5, Encoding? encoding = null) where T : FileBase, new() + { + return Task.FromResult(FromFile(path, episode, encoding)); + } /// /// Reads the shaiya file format from a file @@ -40,8 +46,11 @@ public static Task ReadFromFileAsync(string path, Episode episode = Episod /// File episode /// File encoding /// FileBase instance - public static FileBase ReadFromFile(string path, Type type, Episode episode = Episode.EP5, Encoding encoding = null) => - FileBase.ReadFromFile(path, type, episode, encoding); + public static FileBase FromFile(string path, Type type, Episode episode = Episode.EP5, Encoding? encoding = null) + { + var serializationOptions = new BinarySerializationOptions(episode, encoding); + return FileBase.ReadFromFile(path, type, serializationOptions); + } /// /// Reads the shaiya file format from a file @@ -50,8 +59,10 @@ public static FileBase ReadFromFile(string path, Type type, Episode episode = Ep /// FileBase child type to be read /// File episode /// File encoding - public static Task ReadFromFileAsync(string path, Type type, Episode episode = Episode.EP5, Encoding encoding = null) => - Task.FromResult(ReadFromFile(path, type, episode, encoding)); + public static Task FromFileAsync(string path, Type type, Episode episode = Episode.EP5, Encoding? encoding = null) + { + return Task.FromResult(FromFile(path, type, episode, encoding)); + } /// /// Reads a shaiya file format from a buffer (byte array) @@ -62,10 +73,11 @@ public static Task ReadFromFileAsync(string path, Type type, Episode e /// File encoding /// Shaiya File Format Type /// T instance - public static T ReadFromBuffer(string name, byte[] buffer, Episode episode = Episode.EP5, Encoding encoding = null) - where T : FileBase, new() => - FileBase.ReadFromBuffer(name, buffer, episode, encoding); - + public static T FromBuffer(string name, byte[] buffer, Episode episode = Episode.EP5, Encoding? encoding = null) where T : FileBase, new() + { + var serializationOptions = new BinarySerializationOptions(episode, encoding); + return FileBase.ReadFromBuffer(name, buffer, serializationOptions); + } /// /// Reads a shaiya file format from a buffer (byte array) @@ -75,9 +87,10 @@ public static T ReadFromBuffer(string name, byte[] buffer, Episode episode = /// File episode /// File encoding /// Shaiya File Format Type - public static Task ReadFromBufferAsync(string name, byte[] buffer, Episode episode = Episode.EP5, Encoding encoding = null) - where T : FileBase, new() => - Task.FromResult(ReadFromBuffer(name, buffer, episode)); + public static Task FromBufferAsync(string name, byte[] buffer, Episode episode = Episode.EP5, Encoding? encoding = null) where T : FileBase, new() + { + return Task.FromResult(FromBuffer(name, buffer, episode)); + } /// /// Reads the shaiya file format from a buffer (byte array) @@ -88,8 +101,11 @@ public static Task ReadFromBufferAsync(string name, byte[] buffer, Episode /// File episode /// File encoding /// FileBase instance - public static FileBase ReadFromBuffer(string name, byte[] buffer, Type type, Episode episode = Episode.EP5, Encoding encoding = null) => - FileBase.ReadFromBuffer(name, buffer, type, episode, encoding); + public static FileBase FromBuffer(string name, byte[] buffer, Type type, Episode episode = Episode.EP5, Encoding? encoding = null) + { + var serializationOptions = new BinarySerializationOptions(episode, encoding); + return FileBase.ReadFromBuffer(name, buffer, type, serializationOptions); + } /// /// Reads the shaiya file format from a buffer (byte array) @@ -99,9 +115,10 @@ public static FileBase ReadFromBuffer(string name, byte[] buffer, Type type, Epi /// FileBase child type to be read /// File episode /// File encoding - public static Task ReadFromBufferAsync(string name, byte[] buffer, Type type, Episode episode = Episode.EP5, - Encoding encoding = null) => - Task.FromResult(ReadFromBuffer(name, buffer, type, episode, encoding)); + public static Task FromBufferAsync(string name, byte[] buffer, Type type, Episode episode = Episode.EP5, Encoding? encoding = null) + { + return Task.FromResult(FromBuffer(name, buffer, type, episode, encoding)); + } /// /// Reads a shaiya file format from a json file @@ -110,8 +127,10 @@ public static Task ReadFromBufferAsync(string name, byte[] buffer, Typ /// String encoding /// type /// instance - public static T ReadFromJsonFile(string path, Encoding encoding = null) where T : FileBase => - (T)ReadFromJsonFile(path, typeof(T), encoding); + public static T FromJsonFile(string path, Encoding? encoding = null) where T : FileBase + { + return (T)FromJsonFile(path, typeof(T), encoding); + } /// /// Reads a shaiya file format from a json file @@ -119,8 +138,10 @@ public static T ReadFromJsonFile(string path, Encoding encoding = null) where /// Path to json file /// String encoding /// type - public static Task ReadFromJsonFileAsync(string path, Encoding encoding = null) where T : FileBase => - Task.FromResult(ReadFromJsonFile(path, encoding)); + public static Task FromJsonFileAsync(string path, Encoding? encoding = null) where T : FileBase + { + return Task.FromResult(FromJsonFile(path, encoding)); + } /// /// Reads a shaiya file format from a json file @@ -129,7 +150,7 @@ public static Task ReadFromJsonFileAsync(string path, Encoding encoding = /// FileBase child type to be read /// String encoding /// instance - public static FileBase ReadFromJsonFile(string path, Type type, Encoding encoding = null) + public static FileBase FromJsonFile(string path, Type type, Encoding? encoding = null) { if (!type.GetBaseClassesAndInterfaces().Contains(typeof(FileBase))) throw new ArgumentException("Type must be a child of FileBase"); @@ -142,20 +163,25 @@ public static FileBase ReadFromJsonFile(string path, Type type, Encoding encodin encoding ??= Encoding.ASCII; - string jsonContent = File.ReadAllText(path, encoding); - var deserializedObject = (FileBase)JsonConvert.DeserializeObject(jsonContent, type); - - string fileNameWithoutJsonExtension = Path.GetFileNameWithoutExtension(path); + var jsonContent = File.ReadAllText(path, encoding); + var deserializedObject = JsonConvert.DeserializeObject(jsonContent, type); if (deserializedObject == null) - return null; + { + throw new SerializationException("The provided file to deserialize is not a valid json file"); + } + + var fileBase = (FileBase)deserializedObject; + var fileNameWithoutJsonExtension = Path.GetFileNameWithoutExtension(path); - deserializedObject.Encoding = encoding; - string objectExtension = deserializedObject.Extension; + fileBase.Encoding = encoding; + var objectExtension = fileBase.Extension; if (Path.GetExtension(fileNameWithoutJsonExtension) != objectExtension) - deserializedObject.Path = $"{fileNameWithoutJsonExtension}.{objectExtension}"; + { + fileBase.Path = $"{fileNameWithoutJsonExtension}.{objectExtension}"; + } - return deserializedObject; + return fileBase; } /// @@ -164,8 +190,10 @@ public static FileBase ReadFromJsonFile(string path, Type type, Encoding encodin /// Path to json file /// FileBase child type to be read /// String encoding - public static Task ReadFromJsonFileAsync(string path, Type type, Encoding encoding = null) => - Task.FromResult(ReadFromJsonFile(path, type, encoding)); + public static Task FromJsonFileAsync(string path, Type type, Encoding? encoding = null) + { + return Task.FromResult(FromJsonFile(path, type, encoding)); + } /// /// Reads a shaiya file format from a json file @@ -175,8 +203,10 @@ public static Task ReadFromJsonFileAsync(string path, Type type, Encod /// String encoding /// type /// instance - public static T ReadFromJson(string name, string jsonText, Encoding encoding = null) where T : FileBase - => (T)ReadFromJson(name, jsonText, typeof(T), encoding); + public static T FromJson(string name, string jsonText, Encoding? encoding = null) where T : FileBase + { + return (T)FromJson(name, jsonText, typeof(T), encoding); + } /// /// Reads a shaiya file format from a json file @@ -185,8 +215,10 @@ public static T ReadFromJson(string name, string jsonText, Encoding encoding /// json text /// String encoding /// type - public static Task ReadFromJsonAsync(string name, string jsonText, Encoding encoding = null) where T : FileBase - => Task.FromResult(ReadFromJson(name, jsonText, encoding)); + public static Task FromJsonAsync(string name, string jsonText, Encoding? encoding = null) where T : FileBase + { + return Task.FromResult(FromJson(name, jsonText, encoding)); + } /// /// Reads a shaiya file format from a json file @@ -196,20 +228,24 @@ public static Task ReadFromJsonAsync(string name, string jsonText, Encodin /// FileBase child type to be read /// String encoding /// instance - public static FileBase ReadFromJson(string name, string jsonText, Type type, Encoding encoding = null) + public static FileBase FromJson(string name, string jsonText, Type type, Encoding? encoding = null) { if (!type.GetBaseClassesAndInterfaces().Contains(typeof(FileBase))) throw new ArgumentException("Type must be a child of FileBase"); encoding ??= Encoding.ASCII; - var deserializedObject = (FileBase)JsonConvert.DeserializeObject(jsonText, type); - if (deserializedObject == null) - return null; + var deserializedObject = JsonConvert.DeserializeObject(jsonText, type); - deserializedObject.Encoding = encoding; - deserializedObject.Path = name; - return deserializedObject; + if (deserializedObject == null) + { + throw new SerializationException("The provided file to deserialize is not a valid json file"); + } + + var fileBase = (FileBase)deserializedObject; + fileBase.Encoding = encoding; + fileBase.Path = name; + return fileBase; } /// @@ -219,8 +255,10 @@ public static FileBase ReadFromJson(string name, string jsonText, Type type, Enc /// json text /// FileBase child type to be read /// String encoding - public static Task ReadFromJsonAsync(string name, string jsonText, Type type, Encoding encoding = null) => - Task.FromResult(ReadFromJson(name, jsonText, type, encoding)); + public static Task FromJsonAsync(string name, string jsonText, Type type, Encoding? encoding = null) + { + return Task.FromResult(FromJson(name, jsonText, type, encoding)); + } /// /// Reads the shaiya file format from a buffer (byte array) within a instance @@ -230,9 +268,11 @@ public static Task ReadFromJsonAsync(string name, string jsonText, Typ /// File episode /// File encoding /// FileBase instance - public static T ReadFromData(Data data, SFile file, Episode episode = Episode.EP5, Encoding encoding = null) - where T : FileBase, new() => - FileBase.ReadFromData(data, file, episode, encoding); + public static T FromData(Data data, SFile file, Episode episode = Episode.EP5, Encoding? encoding = null) where T : FileBase, new() + { + var serializationOptions = new BinarySerializationOptions(episode, encoding); + return FileBase.ReadFromData(data, file, serializationOptions); + } /// /// Reads the shaiya file format from a buffer (byte array) within a instance @@ -241,9 +281,10 @@ public static T ReadFromData(Data data, SFile file, Episode episode = Episode /// instance /// File episode /// File encoding - public static Task ReadFromDataAsync(Data data, SFile file, Episode episode = Episode.EP5, Encoding encoding = null) - where T : FileBase, new() => - Task.FromResult(ReadFromData(data, file, episode, encoding)); + public static Task FromDataAsync(Data data, SFile file, Episode episode = Episode.EP5, Encoding? encoding = null) where T : FileBase, new() + { + return Task.FromResult(FromData(data, file, episode, encoding)); + } /// /// Reads the shaiya file format from a buffer (byte array) within a instance @@ -254,8 +295,11 @@ public static Task ReadFromDataAsync(Data data, SFile file, Episode episod /// File episode /// File encoding /// FileBase instance - public static FileBase ReadFromData(Data data, SFile file, Type type, Episode episode = Episode.EP5, Encoding encoding = null) => - FileBase.ReadFromData(data, file, type, episode, encoding); + public static FileBase FromData(Data data, SFile file, Type type, Episode episode = Episode.EP5, Encoding? encoding = null) + { + var serializationOptions = new BinarySerializationOptions(episode, encoding); + return FileBase.ReadFromData(data, file, type, serializationOptions); + } /// /// Reads the shaiya file format from a buffer (byte array) within a instance @@ -265,7 +309,8 @@ public static FileBase ReadFromData(Data data, SFile file, Type type, Episode ep /// FileBase child type to be read /// File episode /// File encoding - public static Task ReadFromDataAsync(Data data, SFile file, Type type, Episode episode = Episode.EP5, - Encoding encoding = null) => - Task.FromResult(ReadFromData(data, file, type, episode, encoding)); + public static Task FromDataAsync(Data data, SFile file, Type type, Episode episode = Episode.EP5, Encoding? encoding = null) + { + return Task.FromResult(FromData(data, file, type, episode, encoding)); + } } diff --git a/src/Parsec/Serialization/BinarySerializationOptions.cs b/src/Parsec/Serialization/BinarySerializationOptions.cs new file mode 100644 index 00000000..91dbdf09 --- /dev/null +++ b/src/Parsec/Serialization/BinarySerializationOptions.cs @@ -0,0 +1,35 @@ +using System.Text; +using Parsec.Common; + +namespace Parsec.Serialization; + +public class BinarySerializationOptions +{ + public BinarySerializationOptions() + { + } + + public BinarySerializationOptions(Episode episode) + { + Episode = episode; + } + + public BinarySerializationOptions(Encoding encoding) + { + Encoding = encoding; + } + + public BinarySerializationOptions(Episode episode, Encoding? encoding) + { + Episode = episode; + Encoding = encoding ?? Encoding.ASCII; + } + + public Episode Episode { get; set; } = Episode.Unknown; + + public Encoding Encoding { get; set; } = Encoding.ASCII; + + public object? ExtraOption { get; set; } + + public static BinarySerializationOptions Default => new(); +} diff --git a/src/Parsec/Readers/SBinaryReader.cs b/src/Parsec/Serialization/SBinaryReader.cs similarity index 65% rename from src/Parsec/Readers/SBinaryReader.cs rename to src/Parsec/Serialization/SBinaryReader.cs index c0f6f3a1..66ed0d25 100644 --- a/src/Parsec/Readers/SBinaryReader.cs +++ b/src/Parsec/Serialization/SBinaryReader.cs @@ -1,105 +1,54 @@ using System.Text; +using Parsec.Shaiya.Core; -namespace Parsec.Readers; +namespace Parsec.Serialization; /// /// A binary reader made specifically to read Shaiya file formats /// public sealed class SBinaryReader : IDisposable { - private readonly BinaryReader _binaryReader; + private BinaryReader _binaryReader; + public readonly BinarySerializationOptions SerializationOptions; - public SBinaryReader(Stream stream) + public long StreamLength => _binaryReader.BaseStream.Length; + + public SBinaryReader(Stream stream, BinarySerializationOptions serializationOptions) { _binaryReader = new BinaryReader(stream); + SerializationOptions = serializationOptions; } - public SBinaryReader(FileStream fileStream) : this((Stream)fileStream) + public SBinaryReader(FileStream fileStream, BinarySerializationOptions serializationOptions) : this((Stream)fileStream, serializationOptions) { } - public SBinaryReader(MemoryStream memoryStream) : this((Stream)memoryStream) + public SBinaryReader(MemoryStream memoryStream, BinarySerializationOptions serializationOptions) : this((Stream)memoryStream, serializationOptions) { } - public SBinaryReader(string filePath) + public SBinaryReader(string filePath, BinarySerializationOptions serializationOptions) { var fileStream = File.OpenRead(filePath); _binaryReader = new BinaryReader(fileStream); + SerializationOptions = serializationOptions; } - public SBinaryReader(byte[] buffer) + public SBinaryReader(byte[] buffer, BinarySerializationOptions serializationOptions) { var memoryStream = new MemoryStream(buffer); _binaryReader = new BinaryReader(memoryStream); + SerializationOptions = serializationOptions; } - public long Length => _binaryReader.BaseStream.Length; - - public void Dispose() + public void ResetBuffer(byte[] buffer) { - _binaryReader?.Dispose(); - } - - /// - /// Reads a generic type from the byte buffer - /// - /// - /// Read value - /// When the provided type is not supported - public T Read() - { - var type = Type.GetTypeCode(typeof(T)); - - object value = type switch - { - TypeCode.Byte => ReadByte(), - TypeCode.SByte => ReadSByte(), - TypeCode.Char => ReadChar(), - TypeCode.Boolean => ReadBoolean(), - TypeCode.Int16 => ReadInt16(), - TypeCode.UInt16 => ReadUInt16(), - TypeCode.Int32 => ReadInt32(), - TypeCode.UInt32 => ReadUInt32(), - TypeCode.Int64 => ReadInt64(), - TypeCode.UInt64 => ReadUInt64(), - TypeCode.Single => ReadSingle(), - TypeCode.Double => ReadDouble(), - _ => throw new NotSupportedException() - }; - - return (T)value; + _binaryReader.Dispose(); + var memoryStream = new MemoryStream(buffer); + _binaryReader = new BinaryReader(memoryStream); } - /// - /// Reads a value from the byte buffer based on its - /// - /// The of the value to be read - /// Read value as an object - /// When the provided is not supported - public object Read(Type type) - { - var typeCode = Type.GetTypeCode(type); - - object value = typeCode switch - { - TypeCode.Byte => ReadByte(), - TypeCode.SByte => ReadSByte(), - TypeCode.Char => ReadChar(), - TypeCode.Boolean => ReadBoolean(), - TypeCode.Int16 => ReadInt16(), - TypeCode.UInt16 => ReadUInt16(), - TypeCode.Int32 => ReadInt32(), - TypeCode.UInt32 => ReadUInt32(), - TypeCode.Int64 => ReadInt64(), - TypeCode.UInt64 => ReadUInt64(), - TypeCode.Single => ReadSingle(), - TypeCode.Double => ReadDouble(), - _ => throw new NotSupportedException() - }; - - return value; - } + public long Length => _binaryReader.BaseStream.Length; /// /// Reads a byte (unsigned) @@ -129,7 +78,7 @@ public byte[] ReadBytes(int count) /// /// Reads a boolean value /// - public bool ReadBoolean() + public bool ReadBool() { return _binaryReader.ReadBoolean(); } @@ -215,20 +164,28 @@ public double ReadDouble() public string ReadString(Encoding encoding, int length, bool removeStringTerminator = true) { if (length <= 0) + { return string.Empty; + } // If encoding is UTF16, length needs to be doubled, since UTF16 uses 2 bytes per character if (encoding.Equals(Encoding.Unicode)) + { length *= 2; + } var stringBytes = ReadBytes(length); - string str = encoding.GetString(stringBytes, 0, length); + var str = encoding.GetString(stringBytes, 0, length); if (removeStringTerminator && str.Length > 1 && str[str.Length - 1] == '\0') + { str = str.Trim('\0'); + } if (str == "\0") + { str = string.Empty; + } return str; } @@ -245,16 +202,22 @@ public string ReadString(Encoding encoding, bool removeStringTerminator = true) } /// - /// Reads length-fixed string with ASCII encoding + /// Reads length-fixed string using the encoding specified on the serialization options /// /// Indicates whether the string terminator (\0) should be removed or not - public string ReadString(bool removeStringTerminator = true) => ReadString(Encoding.ASCII, removeStringTerminator); + public string ReadString(bool removeStringTerminator = true) + { + return ReadString(SerializationOptions.Encoding, removeStringTerminator); + } /// - /// Reads length-fixed string with ASCII encoding + /// Reads length-fixed string using the encoding specified on the serialization options /// /// The string's length - public string ReadString(int length) => ReadString(Encoding.ASCII, length); + public string ReadString(int length) + { + return ReadString(SerializationOptions.Encoding, length); + } /// /// Resets the reading offset @@ -284,4 +247,35 @@ public byte[] ReadAllBytes() _binaryReader.BaseStream.CopyTo(tempMemoryStream); return tempMemoryStream.ToArray(); } + + public T Read() where T : ISerializable, new() + { + var instance = new T(); + instance.Read(this); + return instance; + } + + public IList ReadList(int count) where T : ISerializable, new() + { + var list = new List(); + + for (var i = 0; i < count; i++) + { + var item = Read(); + list.Add(item); + } + + return list; + } + + public IList ReadList() where T : ISerializable, new() + { + var count = ReadInt32(); + return ReadList(count); + } + + public void Dispose() + { + _binaryReader.Dispose(); + } } diff --git a/src/Parsec/Serialization/SBinaryWriter.cs b/src/Parsec/Serialization/SBinaryWriter.cs new file mode 100644 index 00000000..b6383038 --- /dev/null +++ b/src/Parsec/Serialization/SBinaryWriter.cs @@ -0,0 +1,131 @@ +using System.Text; +using Parsec.Shaiya.Core; + +namespace Parsec.Serialization; + +public class SBinaryWriter : IDisposable +{ + private readonly BinaryWriter _binaryWriter; + public readonly BinarySerializationOptions SerializationOptions; + + public SBinaryWriter(Stream stream, BinarySerializationOptions serializationOptions) + { + _binaryWriter = new BinaryWriter(stream); + SerializationOptions = serializationOptions; + } + + public SBinaryWriter(FileStream fileStream, BinarySerializationOptions serializationOptions) : this((Stream)fileStream, serializationOptions) + { + } + + public SBinaryWriter(MemoryStream memoryStream, BinarySerializationOptions serializationOptions) : this((Stream)memoryStream, serializationOptions) + { + } + + public SBinaryWriter(string filePath, BinarySerializationOptions serializationOptions) + { + var fileStream = File.OpenWrite(filePath); + _binaryWriter = new BinaryWriter(fileStream); + SerializationOptions = serializationOptions; + } + + public void Write(byte value) + { + _binaryWriter.Write(value); + } + + public void Write(sbyte value) + { + _binaryWriter.Write(value); + } + + public void Write(bool value) + { + _binaryWriter.Write(value); + } + + public void Write(short value) + { + _binaryWriter.Write(value); + } + + public void Write(ushort value) + { + _binaryWriter.Write(value); + } + + public void Write(int value) + { + _binaryWriter.Write(value); + } + + public void Write(uint value) + { + _binaryWriter.Write(value); + } + + public void Write(long value) + { + _binaryWriter.Write(value); + } + + public void Write(ulong value) + { + _binaryWriter.Write(value); + } + + public void Write(float value) + { + _binaryWriter.Write(value); + } + + public void Write(double value) + { + _binaryWriter.Write(value); + } + + public void Write(byte[] bytes) + { + _binaryWriter.Write(bytes); + } + + public void Write(string str, bool isLengthPrefixed = true, bool includeStringTerminator = true) + { + Write(str, SerializationOptions.Encoding, isLengthPrefixed, includeStringTerminator); + } + + public void Write(string str, Encoding encoding, bool isLengthPrefixed = true, bool includeStringTerminator = true) + { + var finalStr = includeStringTerminator ? str + '\0' : str; + + if (isLengthPrefixed) + { + Write(finalStr.Length); + } + + Write(encoding.GetBytes(finalStr)); + } + + public void Write(ISerializable serializable) + { + serializable.Write(this); + } + + public void Write(IList list, bool lengthPrefixed = true) + { + if (lengthPrefixed) + { + Write(list.Count); + } + + foreach (var item in list) + { + Write(item); + } + } + + public void Dispose() + { + _binaryWriter.Dispose(); + } +} diff --git a/src/Parsec/Shaiya/3DC/Bone.cs b/src/Parsec/Shaiya/3DC/Bone.cs deleted file mode 100644 index 75520ead..00000000 --- a/src/Parsec/Shaiya/3DC/Bone.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Parsec.Attributes; -using Parsec.Shaiya.Common; - -namespace Parsec.Shaiya._3DC; - -public sealed class Bone -{ - /// - /// The transformation matrix of this bone, which holds the starting position and rotation of the bone - /// - [ShaiyaProperty] - public Matrix4x4 Matrix { get; set; } -} diff --git a/src/Parsec/Shaiya/3DC/Static3DC.cs b/src/Parsec/Shaiya/3DC/Static3DC.cs deleted file mode 100644 index e0b3ff10..00000000 --- a/src/Parsec/Shaiya/3DC/Static3DC.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Attributes; -using Parsec.Common; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya._3DC; - -/// -/// Class that represents a .3DC model which is used for capes -/// -[DefaultVersion(Episode.EP5)] -[VersionPrefixed(typeof(int), 0, Episode.EP5)] -[VersionPrefixed(typeof(int), 444, Episode.EP6)] -public sealed class Static3DC : FileBase -{ - /// - /// List of vertices which are used to make faces (polygons) - /// - [ShaiyaProperty] - [LengthPrefixedList(typeof(Vertex))] - public List Vertices { get; set; } = new(); - - /// - /// List of faces (polygons) that give shape to the mesh of the 3d model. Faces can only be made up of 3 vertices, so - /// they'll all be triangular - /// - [ShaiyaProperty] - [LengthPrefixedList(typeof(Face))] - public List Faces { get; set; } = new(); - - [JsonIgnore] - public override string Extension => "3DC"; -} diff --git a/src/Parsec/Shaiya/3DC/_3DC.cs b/src/Parsec/Shaiya/3DC/_3DC.cs deleted file mode 100644 index 1f03166b..00000000 --- a/src/Parsec/Shaiya/3DC/_3DC.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Attributes; -using Parsec.Common; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya._3DC; - -/// -/// Class that represents a .3DC model which is used for characters, mobs, npcs, wings, mounts and any model that requires -/// "complex" animations done through its skeleton. -/// -[DefaultVersion(Episode.EP5)] -[VersionPrefixed(typeof(int), 0, Episode.EP5)] -[VersionPrefixed(typeof(int), 444, Episode.EP6)] -public sealed class _3DC : FileBase -{ - /// - /// List of bones linked to this 3d model. Although a model might be linked to a few bones (for example boots models), the - /// 3DC file contains the definitions for all the bones in the whole skeleton. - /// - [ShaiyaProperty] - [LengthPrefixedList(typeof(Bone))] - public List Bones { get; set; } = new(); - - /// - /// List of vertices which are used to make faces (polygons) - /// - [ShaiyaProperty] - [LengthPrefixedList(typeof(Vertex))] - public List Vertices { get; set; } = new(); - - /// - /// List of faces (polygons) that give shape to the mesh of the 3d model. Faces can only be made up of 3 vertices, so - /// they'll all be triangular - /// - [ShaiyaProperty] - [LengthPrefixedList(typeof(Face))] - public List Faces { get; set; } = new(); - - [JsonIgnore] - public override string Extension => "3DC"; -} diff --git a/src/Parsec/Shaiya/3DE/Frame.cs b/src/Parsec/Shaiya/3DE/Frame.cs deleted file mode 100644 index 479fb6dd..00000000 --- a/src/Parsec/Shaiya/3DE/Frame.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya._3DE; - -public sealed class Frame : IBinary -{ - [JsonConstructor] - public Frame() - { - } - - public Frame(SBinaryReader binaryReader, int vertexCount) - { - Keyframe = binaryReader.Read(); - - for (int i = 0; i < vertexCount; i++) - { - var translation = new VertexFrame(binaryReader); - VertexFrames.Add(translation); - } - } - - /// - /// The frame's key. - /// - public int Keyframe { get; set; } - - /// - /// The frame's vertex translations. There's one translation defined for each vertex. - /// - public List VertexFrames { get; } = new(); - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Keyframe.GetBytes()); - buffer.AddRange(VertexFrames.GetBytes(false)); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/3DE/Vertex.cs b/src/Parsec/Shaiya/3DE/Vertex.cs deleted file mode 100644 index 5d555041..00000000 --- a/src/Parsec/Shaiya/3DE/Vertex.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya._3DE; - -public sealed class Vertex : IBinary -{ - [JsonConstructor] - public Vertex() - { - } - - public Vertex(SBinaryReader binaryReader) - { - Coordinates = new Vector3(binaryReader); - BoneId = binaryReader.Read(); - UV = new Vector2(binaryReader); - } - - /// - /// Vertex coordinates in the 3D space - /// - public Vector3 Coordinates { get; set; } - - /// - /// 3DE's don't have vertex groups like 3DC's, that's why this value is always -1. - /// - public int BoneId { get; set; } = -1; - - /// - /// UV Texture mapping - /// - public Vector2 UV { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Coordinates.GetBytes()); - buffer.AddRange(BoneId.GetBytes()); - buffer.AddRange(UV.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/3DE/VertexFrame.cs b/src/Parsec/Shaiya/3DE/VertexFrame.cs deleted file mode 100644 index 44910234..00000000 --- a/src/Parsec/Shaiya/3DE/VertexFrame.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya._3DE; - -public sealed class VertexFrame : IBinary -{ - [JsonConstructor] - public VertexFrame() - { - } - - public VertexFrame(SBinaryReader binaryReader) - { - Coordinates = new Vector3(binaryReader); - UV = new Vector2(binaryReader); - } - - /// - /// The vertex coordinates - /// - public Vector3 Coordinates { get; set; } - - /// - /// The vertex UV mapping - /// - public Vector2 UV { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Coordinates.GetBytes()); - buffer.AddRange(UV.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/3DE/_3DE.cs b/src/Parsec/Shaiya/3DE/_3DE.cs deleted file mode 100644 index 803cf297..00000000 --- a/src/Parsec/Shaiya/3DE/_3DE.cs +++ /dev/null @@ -1,78 +0,0 @@ -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya._3DE; - -/// -/// Class that represents the .3DE file structure. -/// 3DE (3D Effect) files are textured meshes with vertex animations used for effects -/// -public sealed class _3DE : FileBase -{ - /// - /// The mesh .dds texture - /// - public string Texture { get; set; } - - /// - /// The mesh vertices - /// - public List Vertices { get; } = new(); - - /// - /// The mesh triangular faces - /// - public List Faces { get; } = new(); - - /// - /// The maximum animation keyframe - /// - public int MaxKeyframe { get; set; } - - /// - /// List of animation frames - /// - public List Frames { get; } = new(); - - public override string Extension => "3DE"; - - /// - /// Reads the .3DE file from the file buffer. This format requires a manually defined Read method because of its "complexity" when - /// dealing with the vertex translation frames. - /// - public override void Read() - { - Texture = _binaryReader.ReadString(); - - int vertexCount = _binaryReader.Read(); - for (int i = 0; i < vertexCount; i++) - Vertices.Add(new Vertex(_binaryReader)); - - int faceCount = _binaryReader.Read(); - for (int i = 0; i < faceCount; i++) - Faces.Add(new Face(_binaryReader)); - - MaxKeyframe = _binaryReader.Read(); - - int frameCount = _binaryReader.Read(); - for (int i = 0; i < frameCount; i++) - Frames.Add(new Frame(_binaryReader, vertexCount)); - } - - /// - /// Gets the file buffer. This format requires a manually defined GetBytes method because of its "complexity" when - /// dealing with the vertex translation frames. - /// - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Texture.GetLengthPrefixedBytes()); - buffer.AddRange(Vertices.GetBytes()); - buffer.AddRange(Faces.GetBytes()); - buffer.AddRange(MaxKeyframe.GetBytes()); - buffer.AddRange(Frames.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/3DO/Vertex.cs b/src/Parsec/Shaiya/3DO/Vertex.cs deleted file mode 100644 index 26dcfbc0..00000000 --- a/src/Parsec/Shaiya/3DO/Vertex.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Parsec.Attributes; -using Parsec.Shaiya.Common; - -namespace Parsec.Shaiya._3DO; - -public sealed class Vertex -{ - [ShaiyaProperty] - public Vector3 Coordinates { get; set; } - - [ShaiyaProperty] - public Vector3 Normal { get; set; } - - [ShaiyaProperty] - public Vector2 UV { get; set; } -} diff --git a/src/Parsec/Shaiya/3DO/_3DO.cs b/src/Parsec/Shaiya/3DO/_3DO.cs deleted file mode 100644 index 9988b1f6..00000000 --- a/src/Parsec/Shaiya/3DO/_3DO.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Attributes; -using Parsec.Common; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya._3DO; - -/// -/// Class that represents a .3DO which is used for weapons and shields. This format doesn't contain bones, it's just a mesh with a texture. -/// -[DefaultVersion(Episode.EP5)] -public sealed class _3DO : FileBase -{ - [ShaiyaProperty] - [LengthPrefixedString(includeStringTerminator: false)] - public string TextureName { get; set; } - - [ShaiyaProperty] - [LengthPrefixedList(typeof(Vertex))] - public List Vertices { get; set; } = new(); - - [ShaiyaProperty] - [LengthPrefixedList(typeof(Face))] - public List Faces { get; set; } = new(); - - [JsonIgnore] - public override string Extension => "3DO"; -} diff --git a/src/Parsec/Shaiya/3dc/Static3dc.cs b/src/Parsec/Shaiya/3dc/Static3dc.cs new file mode 100644 index 00000000..f4f269ba --- /dev/null +++ b/src/Parsec/Shaiya/3dc/Static3dc.cs @@ -0,0 +1,62 @@ +using Newtonsoft.Json; +using Parsec.Common; +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya._3dc; + +/// +/// Class that represents a .3DC model which is used for capes +/// +public sealed class Static3dc : FileBase +{ + /// + /// List of vertices which are used to make faces (polygons) + /// + public List<_3dcVertex> Vertices { get; set; } = new(); + + /// + /// List of faces (polygons) that give shape to the mesh of the 3d model. Faces can only be made up of 3 vertices, so + /// they'll all be triangular + /// + public List Faces { get; set; } = new(); + + [JsonIgnore] + public override string Extension => "3DC"; + + protected override void Read(SBinaryReader binaryReader) + { + var version = binaryReader.ReadInt32(); + Episode = Episode.EP5; + + if (version == 444) + { + Episode = Episode.EP6; + } + + // Vertex instances expect the episode to be set on the serialization options + binaryReader.SerializationOptions.Episode = Episode; + + Vertices = binaryReader.ReadList<_3dcVertex>().ToList(); + Faces = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + var version = 0; + + if (Episode >= Episode.EP6) + { + version = 444; + } + + // Vertex instances expect the episode to be set on the serialization options + binaryWriter.SerializationOptions.Episode = Episode; + + binaryWriter.Write(version); + binaryWriter.Write(Vertices.ToSerializable()); + binaryWriter.Write(Faces.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/3dc/_3dc.cs b/src/Parsec/Shaiya/3dc/_3dc.cs new file mode 100644 index 00000000..276bd795 --- /dev/null +++ b/src/Parsec/Shaiya/3dc/_3dc.cs @@ -0,0 +1,71 @@ +using Newtonsoft.Json; +using Parsec.Common; +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya._3dc; + +/// +/// Class that represents a .3DC model which is used for characters, mobs, npcs, wings, mounts and any model that requires +/// "complex" animations done through its skeleton. +/// +public sealed class _3dc : FileBase +{ + /// + /// List of bones linked to this 3d model. Although a model might be linked to a few bones (for example boots models), the + /// 3DC file contains the definitions for all the bones in the whole skeleton. + /// + public List<_3dcBone> Bones { get; set; } = new(); + + /// + /// List of vertices which are used to make faces (polygons) + /// + public List<_3dcVertex> Vertices { get; set; } = new(); + + /// + /// List of faces (polygons) that give shape to the mesh of the 3d model. Faces can only be made up of 3 vertices, so + /// they'll all be triangular + /// + public List Faces { get; set; } = new(); + + [JsonIgnore] + public override string Extension => "3DC"; + + protected override void Read(SBinaryReader binaryReader) + { + var version = binaryReader.ReadInt32(); + Episode = Episode.EP5; + + if (version == 444) + { + Episode = Episode.EP6; + } + + // Vertex instances expect the episode to be set on the serialization options + binaryReader.SerializationOptions.Episode = Episode; + + Bones = binaryReader.ReadList<_3dcBone>().ToList(); + Vertices = binaryReader.ReadList<_3dcVertex>().ToList(); + Faces = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + var version = 0; + + if (Episode >= Episode.EP6) + { + version = 444; + } + + // Vertex instances expect the episode to be set on the serialization options + binaryWriter.SerializationOptions.Episode = Episode; + + binaryWriter.Write(version); + binaryWriter.Write(Bones.ToSerializable()); + binaryWriter.Write(Vertices.ToSerializable()); + binaryWriter.Write(Faces.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/3dc/_3dcBone.cs b/src/Parsec/Shaiya/3dc/_3dcBone.cs new file mode 100644 index 00000000..d79779b5 --- /dev/null +++ b/src/Parsec/Shaiya/3dc/_3dcBone.cs @@ -0,0 +1,23 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya._3dc; + +public sealed class _3dcBone : ISerializable +{ + /// + /// The transformation matrix of this bone, which holds the starting position and rotation of the bone + /// + public Matrix4x4 Matrix { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Matrix = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Matrix); + } +} diff --git a/src/Parsec/Shaiya/3DC/Vertex.cs b/src/Parsec/Shaiya/3dc/_3dcVertex.cs similarity index 57% rename from src/Parsec/Shaiya/3DC/Vertex.cs rename to src/Parsec/Shaiya/3dc/_3dcVertex.cs index 1e287183..e8f839ff 100644 --- a/src/Parsec/Shaiya/3DC/Vertex.cs +++ b/src/Parsec/Shaiya/3dc/_3dcVertex.cs @@ -1,15 +1,15 @@ -using Parsec.Attributes; -using Parsec.Common; +using Parsec.Common; +using Parsec.Serialization; using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; -namespace Parsec.Shaiya._3DC; +namespace Parsec.Shaiya._3dc; -public sealed class Vertex +public sealed class _3dcVertex : ISerializable { /// /// The vertex's 3d coordinates /// - [ShaiyaProperty] public Vector3 Coordinates { get; set; } /// @@ -17,55 +17,84 @@ public sealed class Vertex /// , it indicates the weight of this vertex for those vertex groups. /// If the file's format is EP6 or superior, this value is the weight for only. /// - [ShaiyaProperty] public float Bone1Weight { get; set; } /// /// Bone weight for Present in EP6+ format only /// - [ShaiyaProperty(Episode.EP6, Episode.EP8)] public float Bone2Weight { get; set; } /// /// Present in EP6+ format /// - [ShaiyaProperty(Episode.EP6, Episode.EP8)] public float Bone3Weight { get; set; } /// /// The first vertex group this vertex belongs. The vertex group belongs to a bone. /// - [ShaiyaProperty] public byte BoneVertexGroup1 { get; set; } /// /// The second vertex group this vertex belongs. The vertex group belongs to a bone. /// - [ShaiyaProperty] public byte BoneVertexGroup2 { get; set; } /// /// The third vertex group this vertex belongs. The vertex group belongs to a bone. /// - [ShaiyaProperty] public byte BoneVertexGroup3 { get; set; } /// /// Unknown byte. Always 0. /// - [ShaiyaProperty] public byte Unknown { get; set; } /// /// Normal of this point, used for lighting computation. /// - [ShaiyaProperty] public Vector3 Normal { get; set; } /// /// UV mapping for the 2D texture. For more information visit /// this link. /// - [ShaiyaProperty] public Vector2 UV { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Coordinates = binaryReader.Read(); + Bone1Weight = binaryReader.ReadSingle(); + + if (binaryReader.SerializationOptions.Episode >= Episode.EP6) + { + Bone2Weight = binaryReader.ReadSingle(); + Bone3Weight = binaryReader.ReadSingle(); + } + + BoneVertexGroup1 = binaryReader.ReadByte(); + BoneVertexGroup2 = binaryReader.ReadByte(); + BoneVertexGroup3 = binaryReader.ReadByte(); + Unknown = binaryReader.ReadByte(); + Normal = binaryReader.Read(); + UV = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Coordinates); + binaryWriter.Write(Bone1Weight); + + if (binaryWriter.SerializationOptions.Episode >= Episode.EP6) + { + binaryWriter.Write(Bone2Weight); + binaryWriter.Write(Bone3Weight); + } + + binaryWriter.Write(BoneVertexGroup1); + binaryWriter.Write(BoneVertexGroup2); + binaryWriter.Write(BoneVertexGroup3); + binaryWriter.Write(Unknown); + binaryWriter.Write(Normal); + binaryWriter.Write(UV); + } } diff --git a/src/Parsec/Shaiya/3de/_3de.cs b/src/Parsec/Shaiya/3de/_3de.cs new file mode 100644 index 00000000..b7677977 --- /dev/null +++ b/src/Parsec/Shaiya/3de/_3de.cs @@ -0,0 +1,68 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya._3de; + +/// +/// Class that represents the .3DE file structure. +/// 3DE (3D Effect) files are textured meshes with vertex animations used for effects +/// +public sealed class _3de : FileBase +{ + /// + /// The mesh .dds texture + /// + public string Texture { get; set; } = string.Empty; + + /// + /// The mesh vertices + /// + public List<_3deVertex> Vertices { get; set; } = new(); + + /// + /// The mesh triangular faces + /// + public List Faces { get; set; } = new(); + + /// + /// The maximum animation keyframe + /// + public int MaxKeyframe { get; set; } + + /// + /// List of animation frames + /// + public List<_3deFrame> Frames { get; set; } = new(); + + public override string Extension => "3DE"; + + /// + /// Reads the .3DE file from the file buffer. This format requires a manually defined Read method because of its "complexity" when + /// dealing with the vertex translation frames. + /// + protected override void Read(SBinaryReader binaryReader) + { + Texture = binaryReader.ReadString(); + Vertices = binaryReader.ReadList<_3deVertex>().ToList(); + Faces = binaryReader.ReadList().ToList(); + MaxKeyframe = binaryReader.ReadInt32(); + + // Frame instances expect the vertex count to be set as the ExtraOption on the serialization options + binaryReader.SerializationOptions.ExtraOption = Vertices.Count; + Frames = binaryReader.ReadList<_3deFrame>().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + // Frame instances expect the vertex count to be set as the ExtraOption on the serialization options + binaryWriter.SerializationOptions.ExtraOption = Vertices.Count; + + binaryWriter.Write(Texture); + binaryWriter.Write(Vertices.ToSerializable()); + binaryWriter.Write(Faces.ToSerializable()); + binaryWriter.Write(MaxKeyframe); + binaryWriter.Write(Frames.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/3de/_3deFrame.cs b/src/Parsec/Shaiya/3de/_3deFrame.cs new file mode 100644 index 00000000..fed2efd1 --- /dev/null +++ b/src/Parsec/Shaiya/3de/_3deFrame.cs @@ -0,0 +1,37 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya._3de; + +public sealed class _3deFrame : ISerializable +{ + /// + /// The frame's key. + /// + public int Keyframe { get; set; } + + /// + /// The frame's vertex translations. There's one translation defined for each vertex. + /// + public List<_3deVertexFrame> VertexFrames { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + var vertexCount = 0; + + if (binaryReader.SerializationOptions.ExtraOption is int vertexCountOption) + { + vertexCount = vertexCountOption; + } + + Keyframe = binaryReader.ReadInt32(); + VertexFrames = binaryReader.ReadList<_3deVertexFrame>(vertexCount).ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Keyframe); + binaryWriter.Write(VertexFrames.ToSerializable(), false); + } +} diff --git a/src/Parsec/Shaiya/3de/_3deVertex.cs b/src/Parsec/Shaiya/3de/_3deVertex.cs new file mode 100644 index 00000000..16a62082 --- /dev/null +++ b/src/Parsec/Shaiya/3de/_3deVertex.cs @@ -0,0 +1,37 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya._3de; + +public sealed class _3deVertex : ISerializable +{ + /// + /// Vertex coordinates in the 3D space + /// + public Vector3 Coordinates { get; set; } + + /// + /// 3DE's don't have vertex groups like 3DC's, that's why this value is always -1. + /// + public int BoneId { get; set; } = -1; + + /// + /// UV Texture mapping + /// + public Vector2 UV { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Coordinates = binaryReader.Read(); + BoneId = binaryReader.ReadInt32(); + UV = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Coordinates); + binaryWriter.Write(BoneId); + binaryWriter.Write(UV); + } +} diff --git a/src/Parsec/Shaiya/3de/_3deVertexFrame.cs b/src/Parsec/Shaiya/3de/_3deVertexFrame.cs new file mode 100644 index 00000000..18eba170 --- /dev/null +++ b/src/Parsec/Shaiya/3de/_3deVertexFrame.cs @@ -0,0 +1,31 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya._3de; + +public sealed class _3deVertexFrame : ISerializable +{ + /// + /// The vertex coordinates + /// + public Vector3 Coordinates { get; set; } + + /// + /// The vertex UV mapping + /// + public Vector2 UV { get; set; } + + + public void Read(SBinaryReader binaryReader) + { + Coordinates = binaryReader.Read(); + UV = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Coordinates); + binaryWriter.Write(UV); + } +} diff --git a/src/Parsec/Shaiya/3do/_3do.cs b/src/Parsec/Shaiya/3do/_3do.cs new file mode 100644 index 00000000..92a9d618 --- /dev/null +++ b/src/Parsec/Shaiya/3do/_3do.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya._3do; + +/// +/// Class that represents a .3DO which is used for weapons and shields. This format doesn't contain bones, it's just a mesh with a texture. +/// +public sealed class _3do : FileBase +{ + public string TextureName { get; set; } = string.Empty; + + public List<_3doVertex> Vertices { get; set; } = new(); + + public List Faces { get; set; } = new(); + + [JsonIgnore] + public override string Extension => "3DO"; + + protected override void Read(SBinaryReader binaryReader) + { + TextureName = binaryReader.ReadString(); + Vertices = binaryReader.ReadList<_3doVertex>().ToList(); + Faces = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(TextureName, includeStringTerminator: false); + binaryWriter.Write(Vertices.ToSerializable()); + binaryWriter.Write(Faces.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/3do/_3doVertex.cs b/src/Parsec/Shaiya/3do/_3doVertex.cs new file mode 100644 index 00000000..c1236c65 --- /dev/null +++ b/src/Parsec/Shaiya/3do/_3doVertex.cs @@ -0,0 +1,28 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya._3do; + +public sealed class _3doVertex : ISerializable +{ + public Vector3 Coordinates { get; set; } + + public Vector3 Normal { get; set; } + + public Vector2 UV { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Coordinates = binaryReader.Read(); + Normal = binaryReader.Read(); + UV = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Coordinates); + binaryWriter.Write(Normal); + binaryWriter.Write(UV); + } +} diff --git a/src/Parsec/Shaiya/ALT/ALT.cs b/src/Parsec/Shaiya/ALT/ALT.cs deleted file mode 100644 index 656b082a..00000000 --- a/src/Parsec/Shaiya/ALT/ALT.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.ALT; - -/// -/// Class that represents the ALT format which is used to define the available animations for characters. -/// This class has custom implementations of the and methods because its subclass -/// has a serialization anti-pattern. -/// -public sealed class ALT : FileBase -{ - public string Signature { get; set; } - public List Animations { get; } = new(); - - public override string Extension => ".ALT"; - - public override void Read() - { - Signature = _binaryReader.ReadString(3); - - int animationCount = _binaryReader.Read(); - for (int i = 0; i < animationCount; i++) - { - var animation = new Animation(_binaryReader); - Animations.Add(animation); - } - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Signature.GetBytes()); - buffer.AddRange(Animations.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/ALT/Animation.cs b/src/Parsec/Shaiya/ALT/Animation.cs deleted file mode 100644 index 272c65ce..00000000 --- a/src/Parsec/Shaiya/ALT/Animation.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.ALT; - -/// -/// Class that represents an animation record in an ALT file. -/// Throughout the list of animations, there's some of them which have 0-length names, when this happens, the rest of the fields are not present -/// in the file, so they need to be skipped when reading or writing. -/// This is considered a serialization anti-pattern and that's why its read through a custom constructor and written using a custom -/// method. -/// -public sealed class Animation : IBinary -{ - [JsonConstructor] - public Animation() - { - } - - public Animation(SBinaryReader binaryReader) - { - Name = binaryReader.ReadString(); - - // When name is empty, the rest of the fields are not read - if (string.IsNullOrEmpty(Name)) - return; - - Mode = binaryReader.Read(); - Unknown = binaryReader.Read(); - Float1 = binaryReader.Read(); - Float2 = binaryReader.Read(); - Float3 = binaryReader.Read(); - Float4 = binaryReader.Read(); - } - - public string Name { get; set; } - - /// TODO: In shStudio, Mode is split into 4 separate bytes, investigate if that's correct, since the game client actually reads a single value 4-byte - public int Mode { get; set; } - - public int Unknown { get; set; } - public float Float1 { get; set; } - public float Float2 { get; set; } - public float Float3 { get; set; } - public float Float4 { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - // When name is empty, the rest of the fields are not written and the name length must be set to '0' - if (string.IsNullOrEmpty(Name)) - { - return 0.GetBytes(); - } - - var buffer = new List(); - - buffer.AddRange(Name.GetLengthPrefixedBytes()); - buffer.AddRange(Mode.GetBytes()); - buffer.AddRange(Unknown.GetBytes()); - buffer.AddRange(Float1.GetBytes()); - buffer.AddRange(Float2.GetBytes()); - buffer.AddRange(Float3.GetBytes()); - buffer.AddRange(Float4.GetBytes()); - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Alt/Alt.cs b/src/Parsec/Shaiya/Alt/Alt.cs new file mode 100644 index 00000000..dd6a70ff --- /dev/null +++ b/src/Parsec/Shaiya/Alt/Alt.cs @@ -0,0 +1,30 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Alt; + +/// +/// Class that represents the ALT format which is used to define the available animations for characters. +/// This class has custom implementations of the and methods because its subclass +/// has a serialization anti-pattern. +/// +public sealed class Alt : FileBase +{ + public string Signature { get; set; } = string.Empty; + public List Animations { get; set; } = new(); + + public override string Extension => ".ALT"; + + protected override void Read(SBinaryReader binaryReader) + { + Signature = binaryReader.ReadString(3); + Animations = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Signature, isLengthPrefixed: false, includeStringTerminator: false); + binaryWriter.Write(Animations.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Alt/AltAnimation.cs b/src/Parsec/Shaiya/Alt/AltAnimation.cs new file mode 100644 index 00000000..1a1dac80 --- /dev/null +++ b/src/Parsec/Shaiya/Alt/AltAnimation.cs @@ -0,0 +1,61 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Alt; + +/// +/// Class that represents an animation record in an ALT file. +/// Throughout the list of animations, there's some of them which have 0-length names, when this happens, the rest of the fields are not present +/// in the file, so they need to be skipped when reading or writing. +/// +public sealed class AltAnimation : ISerializable +{ + public string Name { get; set; } = string.Empty; + + /// TODO: In shStudio, Mode is split into 4 separate bytes, investigate if that's correct, since the game client actually reads a single value 4-byte + public int Mode { get; set; } + + public int Unknown { get; set; } + + public float Float1 { get; set; } + + public float Float2 { get; set; } + + public float Float3 { get; set; } + + public float Float4 { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Name = binaryReader.ReadString(); + + // When name is empty, the rest of the fields are not read + if (string.IsNullOrEmpty(Name)) + return; + + Mode = binaryReader.ReadInt32(); + Unknown = binaryReader.ReadInt32(); + Float1 = binaryReader.ReadSingle(); + Float2 = binaryReader.ReadSingle(); + Float3 = binaryReader.ReadSingle(); + Float4 = binaryReader.ReadSingle(); + } + + public void Write(SBinaryWriter binaryWriter) + { + // When name is empty, the rest of the fields are not written and the name length must be set to '0' + if (string.IsNullOrEmpty(Name)) + { + binaryWriter.Write(0); + return; + } + + binaryWriter.Write(Name); + binaryWriter.Write(Mode); + binaryWriter.Write(Unknown); + binaryWriter.Write(Float1); + binaryWriter.Write(Float2); + binaryWriter.Write(Float3); + binaryWriter.Write(Float4); + } +} diff --git a/src/Parsec/Shaiya/Ani/Ani.cs b/src/Parsec/Shaiya/Ani/Ani.cs index bb933962..685e828c 100644 --- a/src/Parsec/Shaiya/Ani/Ani.cs +++ b/src/Parsec/Shaiya/Ani/Ani.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; -using Parsec.Attributes; using Parsec.Common; +using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Ani; @@ -8,29 +9,58 @@ namespace Parsec.Shaiya.Ani; /// /// Class that represents an .ANI file which is used to animate a .3DC model. /// -[DefaultVersion(Episode.EP5)] -[VersionPrefixed(typeof(string), "ANI_V2", Episode.EP6, Episode.EP8)] public sealed class Ani : FileBase { /// /// Starting keyframe. 0 for most animations /// - [ShaiyaProperty] - public int StartKeyframe { get; set; } + public uint StartKeyframe { get; set; } /// /// The ending animation keyframe /// - [ShaiyaProperty] - public int EndKeyframe { get; set; } + public uint EndKeyframe { get; set; } /// /// The list of bones with their translations and rotations for each keyframe /// - [ShaiyaProperty] - [LengthPrefixedList(typeof(Bone), typeof(short))] - public List Bones { get; set; } = new(); + public List Bones { get; set; } = new(); [JsonIgnore] public override string Extension => "ANI"; + + protected override void Read(SBinaryReader binaryReader) + { + var signature = binaryReader.ReadString(6); + + if (signature == "ANI_V2") + { + Episode = Episode.EP6; + } + else + { + binaryReader.ResetOffset(); + } + + binaryReader.SerializationOptions.Episode = Episode; + + StartKeyframe = binaryReader.ReadUInt32(); + EndKeyframe = binaryReader.ReadUInt32(); + + var boneCount = binaryReader.ReadUInt16(); + Bones = binaryReader.ReadList(boneCount).ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + if (binaryWriter.SerializationOptions.Episode >= Episode.EP6) + { + binaryWriter.Write("ANI_V2", isLengthPrefixed: false, includeStringTerminator: false); + } + + binaryWriter.Write(StartKeyframe); + binaryWriter.Write(EndKeyframe); + binaryWriter.Write((ushort)Bones.Count); + binaryWriter.Write(Bones.ToSerializable(), false); + } } diff --git a/src/Parsec/Shaiya/Ani/AniBone.cs b/src/Parsec/Shaiya/Ani/AniBone.cs new file mode 100644 index 00000000..90bbaa06 --- /dev/null +++ b/src/Parsec/Shaiya/Ani/AniBone.cs @@ -0,0 +1,48 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Ani; + +/// +/// Class that represents the information for each bone present in the ani file +/// +public sealed class AniBone : ISerializable +{ + /// + /// The bone's parent bone index + /// + public int ParentBoneIndex { get; set; } + + /// + /// The transformation matrix for the initial position of the bone + /// + public Matrix4x4 Matrix { get; set; } + + /// + /// List of rotations for each keyframe + /// + public List RotationFrames { get; set; } = new(); + + /// + /// List of translations for each keyframe + /// + public List TranslationFrames { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + ParentBoneIndex = binaryReader.ReadInt32(); + Matrix = binaryReader.Read(); + RotationFrames = binaryReader.ReadList().ToList(); + TranslationFrames = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(ParentBoneIndex); + binaryWriter.Write(Matrix); + binaryWriter.Write(RotationFrames.ToSerializable()); + binaryWriter.Write(TranslationFrames.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Ani/AniRotationFrame.cs b/src/Parsec/Shaiya/Ani/AniRotationFrame.cs new file mode 100644 index 00000000..45b13475 --- /dev/null +++ b/src/Parsec/Shaiya/Ani/AniRotationFrame.cs @@ -0,0 +1,24 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Ani; + +public sealed class AniRotationFrame : ISerializable +{ + public uint Frame { get; set; } + + public Quaternion Quaternion { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Frame = binaryReader.ReadUInt32(); + Quaternion = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Frame); + binaryWriter.Write(Quaternion); + } +} diff --git a/src/Parsec/Shaiya/Ani/AniTranslationFrame.cs b/src/Parsec/Shaiya/Ani/AniTranslationFrame.cs new file mode 100644 index 00000000..85e40c73 --- /dev/null +++ b/src/Parsec/Shaiya/Ani/AniTranslationFrame.cs @@ -0,0 +1,24 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Ani; + +public sealed class AniTranslationFrame : ISerializable +{ + public uint Frame { get; set; } + + public Vector3 Translation { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Frame = binaryReader.ReadUInt32(); + Translation = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Frame); + binaryWriter.Write(Translation); + } +} diff --git a/src/Parsec/Shaiya/Ani/Bone.cs b/src/Parsec/Shaiya/Ani/Bone.cs deleted file mode 100644 index b037ef53..00000000 --- a/src/Parsec/Shaiya/Ani/Bone.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Parsec.Attributes; -using Parsec.Shaiya.Common; - -namespace Parsec.Shaiya.Ani; - -/// -/// Class that represents the information for each bone present in the ani file -/// -public sealed class Bone -{ - /// - /// The index of the bone which matches the .3DC bone - /// - public int BoneIndex { get; set; } - - /// - /// The bone's parent bone index - /// - [ShaiyaProperty] - public int ParentBoneIndex { get; set; } - - /// - /// The transformation matrix for the initial position of the bone - /// - [ShaiyaProperty] - public Matrix4x4 Matrix { get; set; } - - /// - /// List of rotations for each keyframe - /// - [ShaiyaProperty] - [LengthPrefixedList(typeof(RotationFrame))] - public List RotationFrames { get; set; } = new(); - - /// - /// List of translations for each keyframe - /// - [ShaiyaProperty] - [LengthPrefixedList(typeof(TranslationFrame))] - public List TranslationFrames { get; set; } = new(); -} diff --git a/src/Parsec/Shaiya/Ani/RotationFrame.cs b/src/Parsec/Shaiya/Ani/RotationFrame.cs deleted file mode 100644 index 7898043f..00000000 --- a/src/Parsec/Shaiya/Ani/RotationFrame.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Parsec.Attributes; -using Parsec.Shaiya.Common; - -namespace Parsec.Shaiya.Ani; - -public sealed class RotationFrame -{ - [ShaiyaProperty] - public int Keyframe { get; set; } - - [ShaiyaProperty] - public Quaternion Quaternion { get; set; } -} diff --git a/src/Parsec/Shaiya/Ani/TranslationFrame.cs b/src/Parsec/Shaiya/Ani/TranslationFrame.cs deleted file mode 100644 index 4e5db282..00000000 --- a/src/Parsec/Shaiya/Ani/TranslationFrame.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Parsec.Attributes; -using Parsec.Shaiya.Common; - -namespace Parsec.Shaiya.Ani; - -public sealed class TranslationFrame -{ - [ShaiyaProperty] - public int Keyframe { get; set; } - - [ShaiyaProperty] - public Vector3 Vector { get; set; } -} diff --git a/src/Parsec/Shaiya/Cash/Cash.cs b/src/Parsec/Shaiya/Cash/Cash.cs index ff34ec99..aad3a496 100644 --- a/src/Parsec/Shaiya/Cash/Cash.cs +++ b/src/Parsec/Shaiya/Cash/Cash.cs @@ -1,4 +1,5 @@ -using Parsec.Attributes; +using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.Cash; @@ -7,7 +8,15 @@ namespace Parsec.Shaiya.Cash; /// public sealed class Cash : SData.SData { - [ShaiyaProperty] - [LengthPrefixedList(typeof(Product))] - public List Products { get; set; } = new(); + public List Products { get; set; } = new(); + + protected override void Read(SBinaryReader binaryReader) + { + Products = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Products.ToSerializable()); + } } diff --git a/src/Parsec/Shaiya/Cash/CashItem.cs b/src/Parsec/Shaiya/Cash/CashItem.cs deleted file mode 100644 index 1a56c717..00000000 --- a/src/Parsec/Shaiya/Cash/CashItem.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Parsec.Attributes; - -namespace Parsec.Shaiya.Cash; - -public sealed class CashItem -{ - [ShaiyaProperty] - public int ItemId { get; set; } - - [ShaiyaProperty] - public byte Count { get; set; } -} diff --git a/src/Parsec/Shaiya/Cash/CashProduct.cs b/src/Parsec/Shaiya/Cash/CashProduct.cs new file mode 100644 index 00000000..fb3a83ab --- /dev/null +++ b/src/Parsec/Shaiya/Cash/CashProduct.cs @@ -0,0 +1,53 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Cash; + +public sealed class CashProduct : ISerializable +{ + public uint Index { get; set; } + + public uint Bag { get; set; } + + public uint Unknown { get; set; } + + public uint Cost { get; set; } + + public List Items { get; set; } = new(); + + public string ProductName { get; set; } = string.Empty; + + public string ProductCode { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Index = binaryReader.ReadUInt32(); + Bag = binaryReader.ReadUInt32(); + Unknown = binaryReader.ReadUInt32(); + Cost = binaryReader.ReadUInt32(); + Items = binaryReader.ReadList(24).ToList(); + ProductName = binaryReader.ReadString(false); + ProductCode = binaryReader.ReadString(false); + Description = binaryReader.ReadString(false); + + // Manually remove double string terminator on the end of each string. + ProductName = ProductName.Substring(0, ProductName.Length - 2); + ProductCode = ProductCode.Substring(0, ProductCode.Length - 2); + Description = Description.Substring(0, Description.Length - 2); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Index); + binaryWriter.Write(Bag); + binaryWriter.Write(Unknown); + binaryWriter.Write(Cost); + binaryWriter.Write(Items.Take(24).ToSerializable(), lengthPrefixed: false); + binaryWriter.Write(ProductName + "\0\0", includeStringTerminator: false); + binaryWriter.Write(ProductCode + "\0\0", includeStringTerminator: false); + binaryWriter.Write(Description + "\0\0", includeStringTerminator: false); + } +} diff --git a/src/Parsec/Shaiya/Cash/CashProductItem.cs b/src/Parsec/Shaiya/Cash/CashProductItem.cs new file mode 100644 index 00000000..0e55e9cd --- /dev/null +++ b/src/Parsec/Shaiya/Cash/CashProductItem.cs @@ -0,0 +1,23 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Cash; + +public sealed class CashProductItem : ISerializable +{ + public uint ItemId { get; set; } + + public byte Count { get; set; } + + public void Read(SBinaryReader binaryReader) + { + ItemId = binaryReader.ReadUInt32(); + Count = binaryReader.ReadByte(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(ItemId); + binaryWriter.Write(Count); + } +} diff --git a/src/Parsec/Shaiya/Cash/DBItemSellRecord.cs b/src/Parsec/Shaiya/Cash/DBItemSellRecord.cs index 107bdb61..845ea9bb 100644 --- a/src/Parsec/Shaiya/Cash/DBItemSellRecord.cs +++ b/src/Parsec/Shaiya/Cash/DBItemSellRecord.cs @@ -1,166 +1,227 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.Cash; public sealed class DBItemSellRecord : IBinarySDataRecord { - [ShaiyaProperty] public long ProductCode { get; set; } - [ShaiyaProperty] public long Goods_Id { get; set; } - [ShaiyaProperty] public long Multi_BuyCost1 { get; set; } - [ShaiyaProperty] public long Multi_BuyCost2 { get; set; } - [ShaiyaProperty] public long ItemID1 { get; set; } - [ShaiyaProperty] public long ItemCount1 { get; set; } - [ShaiyaProperty] public long ItemID2 { get; set; } - [ShaiyaProperty] public long ItemCount2 { get; set; } - [ShaiyaProperty] public long ItemID3 { get; set; } - [ShaiyaProperty] public long ItemCount3 { get; set; } - [ShaiyaProperty] public long ItemID4 { get; set; } - [ShaiyaProperty] public long ItemCount4 { get; set; } - [ShaiyaProperty] public long ItemID5 { get; set; } - [ShaiyaProperty] public long ItemCount5 { get; set; } - [ShaiyaProperty] public long ItemID6 { get; set; } - [ShaiyaProperty] public long ItemCount6 { get; set; } - [ShaiyaProperty] public long ItemID7 { get; set; } - [ShaiyaProperty] public long ItemCount7 { get; set; } - [ShaiyaProperty] public long ItemID8 { get; set; } - [ShaiyaProperty] public long ItemCount8 { get; set; } - [ShaiyaProperty] public long ItemID9 { get; set; } - [ShaiyaProperty] public long ItemCount9 { get; set; } - [ShaiyaProperty] public long ItemID10 { get; set; } - [ShaiyaProperty] public long ItemCount10 { get; set; } - [ShaiyaProperty] public long ItemID11 { get; set; } - [ShaiyaProperty] public long ItemCount11 { get; set; } - [ShaiyaProperty] public long ItemID12 { get; set; } - [ShaiyaProperty] public long ItemCount12 { get; set; } - [ShaiyaProperty] public long ItemID13 { get; set; } - [ShaiyaProperty] public long ItemCount13 { get; set; } - [ShaiyaProperty] public long ItemID14 { get; set; } - [ShaiyaProperty] public long ItemCount14 { get; set; } - [ShaiyaProperty] public long ItemID15 { get; set; } - [ShaiyaProperty] public long ItemCount15 { get; set; } - [ShaiyaProperty] public long ItemID16 { get; set; } - [ShaiyaProperty] public long ItemCount16 { get; set; } - [ShaiyaProperty] public long ItemID17 { get; set; } - [ShaiyaProperty] public long ItemCount17 { get; set; } - [ShaiyaProperty] public long ItemID18 { get; set; } - [ShaiyaProperty] public long ItemCount18 { get; set; } - [ShaiyaProperty] public long ItemID19 { get; set; } - [ShaiyaProperty] public long ItemCount19 { get; set; } - [ShaiyaProperty] public long ItemID20 { get; set; } - [ShaiyaProperty] public long ItemCount20 { get; set; } - [ShaiyaProperty] public long ItemID21 { get; set; } - [ShaiyaProperty] public long ItemCount21 { get; set; } - [ShaiyaProperty] public long ItemID22 { get; set; } - [ShaiyaProperty] public long ItemCount22 { get; set; } - [ShaiyaProperty] public long ItemID23 { get; set; } - [ShaiyaProperty] public long ItemCount23 { get; set; } - [ShaiyaProperty] public long ItemID24 { get; set; } - [ShaiyaProperty] public long ItemCount24 { get; set; } - [ShaiyaProperty] public long Type { get; set; } + + public void Read(SBinaryReader binaryReader) + { + ProductCode = binaryReader.ReadInt64(); + Goods_Id = binaryReader.ReadInt64(); + Multi_BuyCost1 = binaryReader.ReadInt64(); + Multi_BuyCost2 = binaryReader.ReadInt64(); + ItemID1 = binaryReader.ReadInt64(); + ItemCount1 = binaryReader.ReadInt64(); + ItemID2 = binaryReader.ReadInt64(); + ItemCount2 = binaryReader.ReadInt64(); + ItemID3 = binaryReader.ReadInt64(); + ItemCount3 = binaryReader.ReadInt64(); + ItemID4 = binaryReader.ReadInt64(); + ItemCount4 = binaryReader.ReadInt64(); + ItemID5 = binaryReader.ReadInt64(); + ItemCount5 = binaryReader.ReadInt64(); + ItemID6 = binaryReader.ReadInt64(); + ItemCount6 = binaryReader.ReadInt64(); + ItemID7 = binaryReader.ReadInt64(); + ItemCount7 = binaryReader.ReadInt64(); + ItemID8 = binaryReader.ReadInt64(); + ItemCount8 = binaryReader.ReadInt64(); + ItemID9 = binaryReader.ReadInt64(); + ItemCount9 = binaryReader.ReadInt64(); + ItemID10 = binaryReader.ReadInt64(); + ItemCount10 = binaryReader.ReadInt64(); + ItemID11 = binaryReader.ReadInt64(); + ItemCount11 = binaryReader.ReadInt64(); + ItemID12 = binaryReader.ReadInt64(); + ItemCount12 = binaryReader.ReadInt64(); + ItemID13 = binaryReader.ReadInt64(); + ItemCount13 = binaryReader.ReadInt64(); + ItemID14 = binaryReader.ReadInt64(); + ItemCount14 = binaryReader.ReadInt64(); + ItemID15 = binaryReader.ReadInt64(); + ItemCount15 = binaryReader.ReadInt64(); + ItemID16 = binaryReader.ReadInt64(); + ItemCount16 = binaryReader.ReadInt64(); + ItemID17 = binaryReader.ReadInt64(); + ItemCount17 = binaryReader.ReadInt64(); + ItemID18 = binaryReader.ReadInt64(); + ItemCount18 = binaryReader.ReadInt64(); + ItemID19 = binaryReader.ReadInt64(); + ItemCount19 = binaryReader.ReadInt64(); + ItemID20 = binaryReader.ReadInt64(); + ItemCount20 = binaryReader.ReadInt64(); + ItemID21 = binaryReader.ReadInt64(); + ItemCount21 = binaryReader.ReadInt64(); + ItemID22 = binaryReader.ReadInt64(); + ItemCount22 = binaryReader.ReadInt64(); + ItemID23 = binaryReader.ReadInt64(); + ItemCount23 = binaryReader.ReadInt64(); + ItemID24 = binaryReader.ReadInt64(); + ItemCount24 = binaryReader.ReadInt64(); + Type = binaryReader.ReadInt64(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(ProductCode); + binaryWriter.Write(Goods_Id); + binaryWriter.Write(Multi_BuyCost1); + binaryWriter.Write(Multi_BuyCost2); + binaryWriter.Write(ItemID1); + binaryWriter.Write(ItemCount1); + binaryWriter.Write(ItemID2); + binaryWriter.Write(ItemCount2); + binaryWriter.Write(ItemID3); + binaryWriter.Write(ItemCount3); + binaryWriter.Write(ItemID4); + binaryWriter.Write(ItemCount4); + binaryWriter.Write(ItemID5); + binaryWriter.Write(ItemCount5); + binaryWriter.Write(ItemID6); + binaryWriter.Write(ItemCount6); + binaryWriter.Write(ItemID7); + binaryWriter.Write(ItemCount7); + binaryWriter.Write(ItemID8); + binaryWriter.Write(ItemCount8); + binaryWriter.Write(ItemID9); + binaryWriter.Write(ItemCount9); + binaryWriter.Write(ItemID10); + binaryWriter.Write(ItemCount10); + binaryWriter.Write(ItemID11); + binaryWriter.Write(ItemCount11); + binaryWriter.Write(ItemID12); + binaryWriter.Write(ItemCount12); + binaryWriter.Write(ItemID13); + binaryWriter.Write(ItemCount13); + binaryWriter.Write(ItemID14); + binaryWriter.Write(ItemCount14); + binaryWriter.Write(ItemID15); + binaryWriter.Write(ItemCount15); + binaryWriter.Write(ItemID16); + binaryWriter.Write(ItemCount16); + binaryWriter.Write(ItemID17); + binaryWriter.Write(ItemCount17); + binaryWriter.Write(ItemID18); + binaryWriter.Write(ItemCount18); + binaryWriter.Write(ItemID19); + binaryWriter.Write(ItemCount19); + binaryWriter.Write(ItemID20); + binaryWriter.Write(ItemCount20); + binaryWriter.Write(ItemID21); + binaryWriter.Write(ItemCount21); + binaryWriter.Write(ItemID22); + binaryWriter.Write(ItemCount22); + binaryWriter.Write(ItemID23); + binaryWriter.Write(ItemCount23); + binaryWriter.Write(ItemID24); + binaryWriter.Write(ItemCount24); + binaryWriter.Write(Type); + } } diff --git a/src/Parsec/Shaiya/Cash/DBItemSellTextRecord.cs b/src/Parsec/Shaiya/Cash/DBItemSellTextRecord.cs index b94a28c7..c04630e9 100644 --- a/src/Parsec/Shaiya/Cash/DBItemSellTextRecord.cs +++ b/src/Parsec/Shaiya/Cash/DBItemSellTextRecord.cs @@ -1,18 +1,27 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.Cash; public sealed class DBItemSellTextRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] - [LengthPrefixedString(false)] - public string ProductName { get; set; } + public string ProductName { get; set; } = string.Empty; - [ShaiyaProperty] - [LengthPrefixedString(false)] - public string Text { get; set; } + public string Text { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + ProductName = binaryReader.ReadString(); + Text = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(ProductName, includeStringTerminator: false); + binaryWriter.Write(Text, includeStringTerminator: false); + } } diff --git a/src/Parsec/Shaiya/Cash/Product.cs b/src/Parsec/Shaiya/Cash/Product.cs deleted file mode 100644 index 92aa5d59..00000000 --- a/src/Parsec/Shaiya/Cash/Product.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Parsec.Attributes; - -namespace Parsec.Shaiya.Cash; - -public sealed class Product -{ - [ShaiyaProperty] - public int Index { get; set; } - - [ShaiyaProperty] - public int Bag { get; set; } - - [ShaiyaProperty] - public int Unknown { get; set; } - - [ShaiyaProperty] - public int Cost { get; set; } - - [ShaiyaProperty] - [FixedLengthList(typeof(CashItem), 24)] - public List Items { get; set; } = new(); - - [ShaiyaProperty] - [LengthPrefixedString(IncludeStringTerminator = false, Suffix = "\0\0")] - public string ProductName { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString(IncludeStringTerminator = false, Suffix = "\0\0")] - public string ProductCode { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString(IncludeStringTerminator = false, Suffix = "\0\0")] - public string Description { get; set; } -} diff --git a/src/Parsec/Shaiya/Cloak/ClothTexture/CTL.cs b/src/Parsec/Shaiya/Cloak/ClothTexture/CTL.cs deleted file mode 100644 index 4bbf1b04..00000000 --- a/src/Parsec/Shaiya/Cloak/ClothTexture/CTL.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Cloak.ClothTexture; - -public sealed class CTL : FileBase -{ - [JsonConstructor] - public CTL() - { - } - - public List Textures { get; } = new(); - - [JsonIgnore] - public override string Extension => "CTL"; - - public override void Read() - { - int textureCount = _binaryReader.Read(); - for (int i = 0; i < textureCount; i++) - Textures.Add(new Texture(_binaryReader, 256, (char)0xCD)); - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Textures.Count.GetBytes()); - foreach (var texture in Textures) - buffer.AddRange(texture.GetBytes(256, (char)0xCD)); - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Cloak/ClothTexture/Ctl.cs b/src/Parsec/Shaiya/Cloak/ClothTexture/Ctl.cs new file mode 100644 index 00000000..a6311d3f --- /dev/null +++ b/src/Parsec/Shaiya/Cloak/ClothTexture/Ctl.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Cloak.ClothTexture; + +public sealed class Ctl : FileBase +{ + public List Textures { get; set; } = new(); + + [JsonIgnore] + public override string Extension => "CTL"; + + protected override void Read(SBinaryReader binaryReader) + { + Textures = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Textures.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Cloak/Emblem/EmblemDat.cs b/src/Parsec/Shaiya/Cloak/Emblem/EmblemDat.cs index 28e2f89c..88a4a31a 100644 --- a/src/Parsec/Shaiya/Cloak/Emblem/EmblemDat.cs +++ b/src/Parsec/Shaiya/Cloak/Emblem/EmblemDat.cs @@ -1,37 +1,25 @@ using Newtonsoft.Json; -using Parsec.Common; using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Cloak.Emblem; public sealed class EmblemDat : FileBase { - [JsonConstructor] - public EmblemDat() - { - } - - public List Textures { get; } = new(); + public List Textures { get; set; } = new(); [JsonIgnore] public override string Extension => "dat"; - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - int textureCount = _binaryReader.Read(); - for (int i = 0; i < textureCount; i++) - Textures.Add(new Texture(_binaryReader, 260, (char)0xCC)); + Textures = binaryReader.ReadList().ToList(); } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Textures.Count.GetBytes()); - - foreach (var texture in Textures) - buffer.AddRange(texture.GetBytes(260, 0xCC)); - - return buffer; + binaryWriter.Write(Textures.ToSerializable()); } } diff --git a/src/Parsec/Shaiya/Cloak/Texture.cs b/src/Parsec/Shaiya/Cloak/Texture.cs deleted file mode 100644 index 169b048f..00000000 --- a/src/Parsec/Shaiya/Cloak/Texture.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Text; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Cloak; - -public sealed class Texture : IBinary -{ - [JsonIgnore] - private int _fixedLength; - - [JsonIgnore] - private char _padChar = '\0'; - - [JsonConstructor] - public Texture() - { - } - - public Texture(SBinaryReader binaryReader, int length, char padChar) - { - _fixedLength = length; - _padChar = padChar; - - byte[] stringBuffer = binaryReader.ReadBytes(_fixedLength); - Name = Encoding.ASCII.GetString(stringBuffer.TakeWhile(b => b != '\0').ToArray()); - } - - public string Name { get; set; } = string.Empty; - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - - if (options.Length >= 2) - { - _fixedLength = (int)options[0]; - _padChar = Convert.ToChar(options[1]); - } - - buffer.AddRange(Name.GetLengthPrefixedBytes()); - - int padLength = _fixedLength - Name.Length - 1; - - if (padLength > 0) - { - string pad = "".PadRight(padLength, _padChar); - buffer.AddRange(pad.GetBytes()); - } - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Common/AlphaBlendingMode.cs b/src/Parsec/Shaiya/Common/AlphaBlendingMode.cs new file mode 100644 index 00000000..5d9cdefe --- /dev/null +++ b/src/Parsec/Shaiya/Common/AlphaBlendingMode.cs @@ -0,0 +1,7 @@ +namespace Parsec.Shaiya.Common; + +public enum AlphaBlendingMode +{ + Visibility, + Glow +} diff --git a/src/Parsec/Shaiya/Common/BoundingBox.cs b/src/Parsec/Shaiya/Common/BoundingBox.cs index 64c73527..5fedb10b 100644 --- a/src/Parsec/Shaiya/Common/BoundingBox.cs +++ b/src/Parsec/Shaiya/Common/BoundingBox.cs @@ -1,5 +1,4 @@ -using Parsec.Attributes; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Common; @@ -8,18 +7,16 @@ namespace Parsec.Shaiya.Common; /// Cube formed by the volume between 2 points which form its diagonal. /// The cube is unique; it doesn't form any angle with the x and y axis. /// -public struct BoundingBox : IBinary +public struct BoundingBox : ISerializable { /// /// Point used as reference for the rectangle /// - [ShaiyaProperty] public Vector3 LowerLimit { get; set; } /// /// Point used as reference for the rectangle /// - [ShaiyaProperty] public Vector3 UpperLimit { get; set; } public BoundingBox(Vector3 lowerLimit, Vector3 upperLimit) @@ -28,10 +25,16 @@ public BoundingBox(Vector3 lowerLimit, Vector3 upperLimit) UpperLimit = upperLimit; } - public BoundingBox(SBinaryReader binaryReader) + public void Read(SBinaryReader binaryReader) { - LowerLimit = new Vector3(binaryReader); - UpperLimit = new Vector3(binaryReader); + LowerLimit = binaryReader.Read(); + UpperLimit = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(LowerLimit); + binaryWriter.Write(UpperLimit); } public float GetRadius() @@ -41,13 +44,4 @@ public float GetRadius() var z = UpperLimit.Z - LowerLimit.Z; return (float)Math.Sqrt(x * x + y * y + z * z) / 2f; } - - /// - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(LowerLimit.GetBytes()); - buffer.AddRange(UpperLimit.GetBytes()); - return buffer; - } } diff --git a/src/Parsec/Shaiya/Common/Face.cs b/src/Parsec/Shaiya/Common/Face.cs deleted file mode 100644 index 0149fc6e..00000000 --- a/src/Parsec/Shaiya/Common/Face.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Attributes; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Common; - -/// -/// Class that represents a mesh's triangular face (polygon). All faces in shaiya (3DC, 3DO, SMOD, VAni, etc.) are triangular. -/// -public sealed class Face : IBinary -{ - [JsonConstructor] - public Face() - { - } - - public Face(SBinaryReader binaryReader) - { - VertexIndex1 = binaryReader.Read(); - VertexIndex2 = binaryReader.Read(); - VertexIndex3 = binaryReader.Read(); - } - - /// - /// Index of the first vertex of the mesh - /// - [ShaiyaProperty] - public ushort VertexIndex1 { get; set; } - - /// - /// Index of the second vertex of the mesh - /// - [ShaiyaProperty] - public ushort VertexIndex2 { get; set; } - - /// - /// Index of the third vertex of the mesh - /// - [ShaiyaProperty] - public ushort VertexIndex3 { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(VertexIndex1.GetBytes()); - buffer.AddRange(VertexIndex2.GetBytes()); - buffer.AddRange(VertexIndex3.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Common/Faction.cs b/src/Parsec/Shaiya/Common/Faction.cs deleted file mode 100644 index d55b38f6..00000000 --- a/src/Parsec/Shaiya/Common/Faction.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Parsec.Shaiya.Common; - -public enum FactionInt -{ - Light, - Fury, - Any -} - -public enum FactionShort : short -{ - Light, - Fury, - Any -} - -public enum FactionByte : byte -{ - Light, - Fury, - Any -} diff --git a/src/Parsec/Shaiya/Common/Matrix4x4.cs b/src/Parsec/Shaiya/Common/Matrix4x4.cs index a76eebc1..1429d06c 100644 --- a/src/Parsec/Shaiya/Common/Matrix4x4.cs +++ b/src/Parsec/Shaiya/Common/Matrix4x4.cs @@ -1,14 +1,5 @@ -/* - Code based on https://github.com/microsoft/referencesource/blob/master/System.Numerics/System/Numerics/Matrix4x4.cs - From the repository https://github.com/microsoft/referencesource - Copyright (c) Microsoft. All rights reserved. - Licensed under the MIT license. See LICENSE file in the project root for full license information. -*/ - -using System.Globalization; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Newtonsoft.Json; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Common; @@ -16,10 +7,8 @@ namespace Parsec.Shaiya.Common; /// /// A structure encapsulating a 4x4 matrix. /// -public struct Matrix4x4 : IEquatable, IBinary +public struct Matrix4x4 : ISerializable { - #region Public Fields - /// /// The matrix's data serialized into an array of float arrays /// @@ -146,66 +135,6 @@ public struct Matrix4x4 : IEquatable, IBinary [JsonIgnore] public float M44; - #endregion Public Fields - - private static readonly Matrix4x4 _identity = new(1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f); - - /// - /// Returns the multiplicative identity matrix. - /// - public static Matrix4x4 Identity - { - get { return _identity; } - } - - /// - /// Returns whether the matrix is the identity matrix. - /// - [JsonIgnore] - public bool IsIdentity - { - get - { - return M11 == 1f && - M22 == 1f && - M33 == 1f && - M44 == 1f && // Check diagonal element first for early out. - M12 == 0f && - M13 == 0f && - M14 == 0f && - M21 == 0f && - M23 == 0f && - M24 == 0f && - M31 == 0f && - M32 == 0f && - M34 == 0f && - M41 == 0f && - M42 == 0f && - M43 == 0f; - } - } - - /// - /// Gets or sets the translation component of this matrix. - /// - [JsonIgnore] - public Vector3 Translation - { - get { return new Vector3(M14, M24, M34); } - set - { - M14 = value.X; - M24 = value.Y; - M34 = value.Z; - } - } - - [JsonIgnore] - public Quaternion Rotation - { - get => Quaternion.CreateFromRotationMatrix(this); - } - /// /// Constructs a Matrix4x4 from the given components. /// @@ -249,972 +178,43 @@ float m44 M44 = m44; } - public Matrix4x4(SBinaryReader binaryReader) - { - M11 = binaryReader.Read(); - M21 = binaryReader.Read(); - M31 = binaryReader.Read(); - M41 = binaryReader.Read(); - M12 = binaryReader.Read(); - M22 = binaryReader.Read(); - M32 = binaryReader.Read(); - M42 = binaryReader.Read(); - M13 = binaryReader.Read(); - M23 = binaryReader.Read(); - M33 = binaryReader.Read(); - M43 = binaryReader.Read(); - M14 = binaryReader.Read(); - M24 = binaryReader.Read(); - M34 = binaryReader.Read(); - M44 = binaryReader.Read(); - } - - /// - /// Creates a translation matrix. - /// - /// The amount to translate in each axis. - /// The translation matrix. - public static Matrix4x4 CreateTranslation(Vector3 position) - { - Matrix4x4 result; - - result.M11 = 1.0f; - result.M12 = 0.0f; - result.M13 = 0.0f; - result.M14 = 0.0f; - result.M21 = 0.0f; - result.M22 = 1.0f; - result.M23 = 0.0f; - result.M24 = 0.0f; - result.M31 = 0.0f; - result.M32 = 0.0f; - result.M33 = 1.0f; - result.M34 = 0.0f; - - result.M41 = position.X; - result.M42 = position.Y; - result.M43 = position.Z; - result.M44 = 1.0f; - - return result; - } - - /// - /// Creates a translation matrix. - /// - /// The amount to translate on the X-axis. - /// The amount to translate on the Y-axis. - /// The amount to translate on the Z-axis. - /// The translation matrix. - public static Matrix4x4 CreateTranslation(float xPosition, float yPosition, float zPosition) - { - Matrix4x4 result; - - result.M11 = 1.0f; - result.M12 = 0.0f; - result.M13 = 0.0f; - result.M14 = 0.0f; - result.M21 = 0.0f; - result.M22 = 1.0f; - result.M23 = 0.0f; - result.M24 = 0.0f; - result.M31 = 0.0f; - result.M32 = 0.0f; - result.M33 = 1.0f; - result.M34 = 0.0f; - - result.M41 = xPosition; - result.M42 = yPosition; - result.M43 = zPosition; - result.M44 = 1.0f; - - return result; - } - - /// - /// Creates a rotation matrix from the given Quaternion rotation value. - /// - /// The source Quaternion. - /// The rotation matrix. - public static Matrix4x4 CreateFromQuaternion(Quaternion quaternion) - { - Matrix4x4 result; - - float xx = quaternion.X * quaternion.X; - float yy = quaternion.Y * quaternion.Y; - float zz = quaternion.Z * quaternion.Z; - - float xy = quaternion.X * quaternion.Y; - float wz = quaternion.Z * quaternion.W; - float xz = quaternion.Z * quaternion.X; - float wy = quaternion.Y * quaternion.W; - float yz = quaternion.Y * quaternion.Z; - float wx = quaternion.X * quaternion.W; - - result.M11 = 1.0f - 2.0f * (yy + zz); - result.M12 = 2.0f * (xy + wz); - result.M13 = 2.0f * (xz - wy); - result.M14 = 0.0f; - result.M21 = 2.0f * (xy - wz); - result.M22 = 1.0f - 2.0f * (zz + xx); - result.M23 = 2.0f * (yz + wx); - result.M24 = 0.0f; - result.M31 = 2.0f * (xz + wy); - result.M32 = 2.0f * (yz - wx); - result.M33 = 1.0f - 2.0f * (yy + xx); - result.M34 = 0.0f; - result.M41 = 0.0f; - result.M42 = 0.0f; - result.M43 = 0.0f; - result.M44 = 1.0f; - - return result; - } - - /// - /// Calculates the determinant of the matrix. - /// - /// The determinant of the matrix. - public float GetDeterminant() - { - // | a b c d | | f g h | | e g h | | e f h | | e f g | - // | e f g h | = a | j k l | - b | i k l | + c | i j l | - d | i j k | - // | i j k l | | n o p | | m o p | | m n p | | m n o | - // | m n o p | - // - // | f g h | - // a | j k l | = a ( f ( kp - lo ) - g ( jp - ln ) + h ( jo - kn ) ) - // | n o p | - // - // | e g h | - // b | i k l | = b ( e ( kp - lo ) - g ( ip - lm ) + h ( io - km ) ) - // | m o p | - // - // | e f h | - // c | i j l | = c ( e ( jp - ln ) - f ( ip - lm ) + h ( in - jm ) ) - // | m n p | - // - // | e f g | - // d | i j k | = d ( e ( jo - kn ) - f ( io - km ) + g ( in - jm ) ) - // | m n o | - // - // Cost of operation - // 17 adds and 28 muls. - // - // add: 6 + 8 + 3 = 17 - // mul: 12 + 16 = 28 - - float a = M11, b = M12, c = M13, d = M14; - float e = M21, f = M22, g = M23, h = M24; - float i = M31, j = M32, k = M33, l = M34; - float m = M41, n = M42, o = M43, p = M44; - - float kp_lo = k * p - l * o; - float jp_ln = j * p - l * n; - float jo_kn = j * o - k * n; - float ip_lm = i * p - l * m; - float io_km = i * o - k * m; - float in_jm = i * n - j * m; - - return a * (f * kp_lo - g * jp_ln + h * jo_kn) - - b * (e * kp_lo - g * ip_lm + h * io_km) + - c * (e * jp_ln - f * ip_lm + h * in_jm) - - d * (e * jo_kn - f * io_km + g * in_jm); - } - - /// - /// Attempts to calculate the inverse of the given matrix. If successful, result will contain the inverted matrix. - /// - /// The source matrix to invert. - /// If successful, contains the inverted matrix. - /// True if the source matrix could be inverted; False otherwise. - public static bool Invert(Matrix4x4 matrix, out Matrix4x4 result) - { - // -1 - // If you have matrix M, inverse Matrix M can compute - // - // -1 1 - // M = --------- A - // det(M) - // - // A is adjugate (adjoint) of M, where, - // - // T - // A = C - // - // C is Cofactor matrix of M, where, - // i + j - // C = (-1) * det(M ) - // ij ij - // - // [ a b c d ] - // M = [ e f g h ] - // [ i j k l ] - // [ m n o p ] - // - // First Row - // 2 | f g h | - // C = (-1) | j k l | = + ( f ( kp - lo ) - g ( jp - ln ) + h ( jo - kn ) ) - // 11 | n o p | - // - // 3 | e g h | - // C = (-1) | i k l | = - ( e ( kp - lo ) - g ( ip - lm ) + h ( io - km ) ) - // 12 | m o p | - // - // 4 | e f h | - // C = (-1) | i j l | = + ( e ( jp - ln ) - f ( ip - lm ) + h ( in - jm ) ) - // 13 | m n p | - // - // 5 | e f g | - // C = (-1) | i j k | = - ( e ( jo - kn ) - f ( io - km ) + g ( in - jm ) ) - // 14 | m n o | - // - // Second Row - // 3 | b c d | - // C = (-1) | j k l | = - ( b ( kp - lo ) - c ( jp - ln ) + d ( jo - kn ) ) - // 21 | n o p | - // - // 4 | a c d | - // C = (-1) | i k l | = + ( a ( kp - lo ) - c ( ip - lm ) + d ( io - km ) ) - // 22 | m o p | - // - // 5 | a b d | - // C = (-1) | i j l | = - ( a ( jp - ln ) - b ( ip - lm ) + d ( in - jm ) ) - // 23 | m n p | - // - // 6 | a b c | - // C = (-1) | i j k | = + ( a ( jo - kn ) - b ( io - km ) + c ( in - jm ) ) - // 24 | m n o | - // - // Third Row - // 4 | b c d | - // C = (-1) | f g h | = + ( b ( gp - ho ) - c ( fp - hn ) + d ( fo - gn ) ) - // 31 | n o p | - // - // 5 | a c d | - // C = (-1) | e g h | = - ( a ( gp - ho ) - c ( ep - hm ) + d ( eo - gm ) ) - // 32 | m o p | - // - // 6 | a b d | - // C = (-1) | e f h | = + ( a ( fp - hn ) - b ( ep - hm ) + d ( en - fm ) ) - // 33 | m n p | - // - // 7 | a b c | - // C = (-1) | e f g | = - ( a ( fo - gn ) - b ( eo - gm ) + c ( en - fm ) ) - // 34 | m n o | - // - // Fourth Row - // 5 | b c d | - // C = (-1) | f g h | = - ( b ( gl - hk ) - c ( fl - hj ) + d ( fk - gj ) ) - // 41 | j k l | - // - // 6 | a c d | - // C = (-1) | e g h | = + ( a ( gl - hk ) - c ( el - hi ) + d ( ek - gi ) ) - // 42 | i k l | - // - // 7 | a b d | - // C = (-1) | e f h | = - ( a ( fl - hj ) - b ( el - hi ) + d ( ej - fi ) ) - // 43 | i j l | - // - // 8 | a b c | - // C = (-1) | e f g | = + ( a ( fk - gj ) - b ( ek - gi ) + c ( ej - fi ) ) - // 44 | i j k | - // - // Cost of operation - // 53 adds, 104 muls, and 1 div. - float a = matrix.M11, b = matrix.M12, c = matrix.M13, d = matrix.M14; - float e = matrix.M21, f = matrix.M22, g = matrix.M23, h = matrix.M24; - float i = matrix.M31, j = matrix.M32, k = matrix.M33, l = matrix.M34; - float m = matrix.M41, n = matrix.M42, o = matrix.M43, p = matrix.M44; - - float kp_lo = k * p - l * o; - float jp_ln = j * p - l * n; - float jo_kn = j * o - k * n; - float ip_lm = i * p - l * m; - float io_km = i * o - k * m; - float in_jm = i * n - j * m; - - float a11 = +(f * kp_lo - g * jp_ln + h * jo_kn); - float a12 = -(e * kp_lo - g * ip_lm + h * io_km); - float a13 = +(e * jp_ln - f * ip_lm + h * in_jm); - float a14 = -(e * jo_kn - f * io_km + g * in_jm); - - float det = a * a11 + b * a12 + c * a13 + d * a14; - - if (Math.Abs(det) < float.Epsilon) - { - result = new Matrix4x4(float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, - float.NaN, - float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN); - - return false; - } - - float invDet = 1.0f / det; - - result.M11 = a11 * invDet; - result.M21 = a12 * invDet; - result.M31 = a13 * invDet; - result.M41 = a14 * invDet; - - result.M12 = -(b * kp_lo - c * jp_ln + d * jo_kn) * invDet; - result.M22 = +(a * kp_lo - c * ip_lm + d * io_km) * invDet; - result.M32 = -(a * jp_ln - b * ip_lm + d * in_jm) * invDet; - result.M42 = +(a * jo_kn - b * io_km + c * in_jm) * invDet; - - float gp_ho = g * p - h * o; - float fp_hn = f * p - h * n; - float fo_gn = f * o - g * n; - float ep_hm = e * p - h * m; - float eo_gm = e * o - g * m; - float en_fm = e * n - f * m; - - result.M13 = +(b * gp_ho - c * fp_hn + d * fo_gn) * invDet; - result.M23 = -(a * gp_ho - c * ep_hm + d * eo_gm) * invDet; - result.M33 = +(a * fp_hn - b * ep_hm + d * en_fm) * invDet; - result.M43 = -(a * fo_gn - b * eo_gm + c * en_fm) * invDet; - - float gl_hk = g * l - h * k; - float fl_hj = f * l - h * j; - float fk_gj = f * k - g * j; - float el_hi = e * l - h * i; - float ek_gi = e * k - g * i; - float ej_fi = e * j - f * i; - - result.M14 = -(b * gl_hk - c * fl_hj + d * fk_gj) * invDet; - result.M24 = +(a * gl_hk - c * el_hi + d * ek_gi) * invDet; - result.M34 = -(a * fl_hj - b * el_hi + d * ej_fi) * invDet; - result.M44 = +(a * fk_gj - b * ek_gi + c * ej_fi) * invDet; - - return true; - } - - public Matrix4x4 Inverted() - { - if (Invert(this, out var invertedMatrix)) - return invertedMatrix; - - throw new Exception("Matrix is not invertible."); - } - - /// - /// Transforms the given matrix by applying the given Quaternion rotation. - /// - /// The source matrix to transform. - /// The rotation to apply. - /// The transformed matrix. - public static Matrix4x4 Transform(Matrix4x4 value, Quaternion rotation) - { - // Compute rotation matrix. - float x2 = rotation.X + rotation.X; - float y2 = rotation.Y + rotation.Y; - float z2 = rotation.Z + rotation.Z; - - float wx2 = rotation.W * x2; - float wy2 = rotation.W * y2; - float wz2 = rotation.W * z2; - float xx2 = rotation.X * x2; - float xy2 = rotation.X * y2; - float xz2 = rotation.X * z2; - float yy2 = rotation.Y * y2; - float yz2 = rotation.Y * z2; - float zz2 = rotation.Z * z2; - - float q11 = 1.0f - yy2 - zz2; - float q21 = xy2 - wz2; - float q31 = xz2 + wy2; - - float q12 = xy2 + wz2; - float q22 = 1.0f - xx2 - zz2; - float q32 = yz2 - wx2; - - float q13 = xz2 - wy2; - float q23 = yz2 + wx2; - float q33 = 1.0f - xx2 - yy2; - - Matrix4x4 result; - - // First row - result.M11 = value.M11 * q11 + value.M12 * q21 + value.M13 * q31; - result.M12 = value.M11 * q12 + value.M12 * q22 + value.M13 * q32; - result.M13 = value.M11 * q13 + value.M12 * q23 + value.M13 * q33; - result.M14 = value.M14; - - // Second row - result.M21 = value.M21 * q11 + value.M22 * q21 + value.M23 * q31; - result.M22 = value.M21 * q12 + value.M22 * q22 + value.M23 * q32; - result.M23 = value.M21 * q13 + value.M22 * q23 + value.M23 * q33; - result.M24 = value.M24; - - // Third row - result.M31 = value.M31 * q11 + value.M32 * q21 + value.M33 * q31; - result.M32 = value.M31 * q12 + value.M32 * q22 + value.M33 * q32; - result.M33 = value.M31 * q13 + value.M32 * q23 + value.M33 * q33; - result.M34 = value.M34; - - // Fourth row - result.M41 = value.M41 * q11 + value.M42 * q21 + value.M43 * q31; - result.M42 = value.M41 * q12 + value.M42 * q22 + value.M43 * q32; - result.M43 = value.M41 * q13 + value.M42 * q23 + value.M43 * q33; - result.M44 = value.M44; - - return result; - } - - /// - /// Transposes the rows and columns of a matrix. - /// - /// The source matrix. - /// The transposed matrix. - public static Matrix4x4 Transpose(Matrix4x4 matrix) - { - Matrix4x4 result; - - result.M11 = matrix.M11; - result.M12 = matrix.M21; - result.M13 = matrix.M31; - result.M14 = matrix.M41; - result.M21 = matrix.M12; - result.M22 = matrix.M22; - result.M23 = matrix.M32; - result.M24 = matrix.M42; - result.M31 = matrix.M13; - result.M32 = matrix.M23; - result.M33 = matrix.M33; - result.M34 = matrix.M43; - result.M41 = matrix.M14; - result.M42 = matrix.M24; - result.M43 = matrix.M34; - result.M44 = matrix.M44; - - return result; - } - - /// - /// Linearly interpolates between the corresponding values of two matrices. - /// - /// The first source matrix. - /// The second source matrix. - /// The relative weight of the second source matrix. - /// The interpolated matrix. - public static Matrix4x4 Lerp(Matrix4x4 matrix1, Matrix4x4 matrix2, float amount) - { - Matrix4x4 result; - - // First row - result.M11 = matrix1.M11 + (matrix2.M11 - matrix1.M11) * amount; - result.M12 = matrix1.M12 + (matrix2.M12 - matrix1.M12) * amount; - result.M13 = matrix1.M13 + (matrix2.M13 - matrix1.M13) * amount; - result.M14 = matrix1.M14 + (matrix2.M14 - matrix1.M14) * amount; - - // Second row - result.M21 = matrix1.M21 + (matrix2.M21 - matrix1.M21) * amount; - result.M22 = matrix1.M22 + (matrix2.M22 - matrix1.M22) * amount; - result.M23 = matrix1.M23 + (matrix2.M23 - matrix1.M23) * amount; - result.M24 = matrix1.M24 + (matrix2.M24 - matrix1.M24) * amount; - - // Third row - result.M31 = matrix1.M31 + (matrix2.M31 - matrix1.M31) * amount; - result.M32 = matrix1.M32 + (matrix2.M32 - matrix1.M32) * amount; - result.M33 = matrix1.M33 + (matrix2.M33 - matrix1.M33) * amount; - result.M34 = matrix1.M34 + (matrix2.M34 - matrix1.M34) * amount; - - // Fourth row - result.M41 = matrix1.M41 + (matrix2.M41 - matrix1.M41) * amount; - result.M42 = matrix1.M42 + (matrix2.M42 - matrix1.M42) * amount; - result.M43 = matrix1.M43 + (matrix2.M43 - matrix1.M43) * amount; - result.M44 = matrix1.M44 + (matrix2.M44 - matrix1.M44) * amount; - - return result; - } - - /// - /// Returns a new matrix with the negated elements of the given matrix. - /// - /// The source matrix. - /// The negated matrix. - public static Matrix4x4 Negate(Matrix4x4 value) - { - Matrix4x4 result; - - result.M11 = -value.M11; - result.M12 = -value.M12; - result.M13 = -value.M13; - result.M14 = -value.M14; - result.M21 = -value.M21; - result.M22 = -value.M22; - result.M23 = -value.M23; - result.M24 = -value.M24; - result.M31 = -value.M31; - result.M32 = -value.M32; - result.M33 = -value.M33; - result.M34 = -value.M34; - result.M41 = -value.M41; - result.M42 = -value.M42; - result.M43 = -value.M43; - result.M44 = -value.M44; - - return result; - } - - /// - /// Adds two matrices together. - /// - /// The first source matrix. - /// The second source matrix. - /// The resulting matrix. - public static Matrix4x4 Add(Matrix4x4 value1, Matrix4x4 value2) - { - Matrix4x4 result; - - result.M11 = value1.M11 + value2.M11; - result.M12 = value1.M12 + value2.M12; - result.M13 = value1.M13 + value2.M13; - result.M14 = value1.M14 + value2.M14; - result.M21 = value1.M21 + value2.M21; - result.M22 = value1.M22 + value2.M22; - result.M23 = value1.M23 + value2.M23; - result.M24 = value1.M24 + value2.M24; - result.M31 = value1.M31 + value2.M31; - result.M32 = value1.M32 + value2.M32; - result.M33 = value1.M33 + value2.M33; - result.M34 = value1.M34 + value2.M34; - result.M41 = value1.M41 + value2.M41; - result.M42 = value1.M42 + value2.M42; - result.M43 = value1.M43 + value2.M43; - result.M44 = value1.M44 + value2.M44; - - return result; - } - - /// - /// Subtracts the second matrix from the first. - /// - /// The first source matrix. - /// The second source matrix. - /// The result of the subtraction. - public static Matrix4x4 Subtract(Matrix4x4 value1, Matrix4x4 value2) - { - Matrix4x4 result; - - result.M11 = value1.M11 - value2.M11; - result.M12 = value1.M12 - value2.M12; - result.M13 = value1.M13 - value2.M13; - result.M14 = value1.M14 - value2.M14; - result.M21 = value1.M21 - value2.M21; - result.M22 = value1.M22 - value2.M22; - result.M23 = value1.M23 - value2.M23; - result.M24 = value1.M24 - value2.M24; - result.M31 = value1.M31 - value2.M31; - result.M32 = value1.M32 - value2.M32; - result.M33 = value1.M33 - value2.M33; - result.M34 = value1.M34 - value2.M34; - result.M41 = value1.M41 - value2.M41; - result.M42 = value1.M42 - value2.M42; - result.M43 = value1.M43 - value2.M43; - result.M44 = value1.M44 - value2.M44; - - return result; - } - - /// - /// Multiplies a matrix by another matrix. - /// - /// The first source matrix. - /// The second source matrix. - /// The result of the multiplication. - public static Matrix4x4 Multiply(Matrix4x4 value1, Matrix4x4 value2) - { - Matrix4x4 result; - - // First row - result.M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21 + value1.M13 * value2.M31 + value1.M14 * value2.M41; - - result.M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22 + value1.M13 * value2.M32 + value1.M14 * value2.M42; - - result.M13 = value1.M11 * value2.M13 + value1.M12 * value2.M23 + value1.M13 * value2.M33 + value1.M14 * value2.M43; - - result.M14 = value1.M11 * value2.M14 + value1.M12 * value2.M24 + value1.M13 * value2.M34 + value1.M14 * value2.M44; - - // Second row - result.M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21 + value1.M23 * value2.M31 + value1.M24 * value2.M41; - - result.M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22 + value1.M23 * value2.M32 + value1.M24 * value2.M42; - - result.M23 = value1.M21 * value2.M13 + value1.M22 * value2.M23 + value1.M23 * value2.M33 + value1.M24 * value2.M43; - - result.M24 = value1.M21 * value2.M14 + value1.M22 * value2.M24 + value1.M23 * value2.M34 + value1.M24 * value2.M44; - - // Third row - result.M31 = value1.M31 * value2.M11 + value1.M32 * value2.M21 + value1.M33 * value2.M31 + value1.M34 * value2.M41; - - result.M32 = value1.M31 * value2.M12 + value1.M32 * value2.M22 + value1.M33 * value2.M32 + value1.M34 * value2.M42; - - result.M33 = value1.M31 * value2.M13 + value1.M32 * value2.M23 + value1.M33 * value2.M33 + value1.M34 * value2.M43; - - result.M34 = value1.M31 * value2.M14 + value1.M32 * value2.M24 + value1.M33 * value2.M34 + value1.M34 * value2.M44; - - // Fourth row - result.M41 = value1.M41 * value2.M11 + value1.M42 * value2.M21 + value1.M43 * value2.M31 + value1.M44 * value2.M41; - - result.M42 = value1.M41 * value2.M12 + value1.M42 * value2.M22 + value1.M43 * value2.M32 + value1.M44 * value2.M42; - - result.M43 = value1.M41 * value2.M13 + value1.M42 * value2.M23 + value1.M43 * value2.M33 + value1.M44 * value2.M43; - - result.M44 = value1.M41 * value2.M14 + value1.M42 * value2.M24 + value1.M43 * value2.M34 + value1.M44 * value2.M44; - - return result; - } - - /// - /// Multiplies a matrix by a scalar value. - /// - /// The source matrix. - /// The scaling factor. - /// The scaled matrix. - public static Matrix4x4 Multiply(Matrix4x4 value1, float value2) - { - Matrix4x4 result; - - result.M11 = value1.M11 * value2; - result.M12 = value1.M12 * value2; - result.M13 = value1.M13 * value2; - result.M14 = value1.M14 * value2; - result.M21 = value1.M21 * value2; - result.M22 = value1.M22 * value2; - result.M23 = value1.M23 * value2; - result.M24 = value1.M24 * value2; - result.M31 = value1.M31 * value2; - result.M32 = value1.M32 * value2; - result.M33 = value1.M33 * value2; - result.M34 = value1.M34 * value2; - result.M41 = value1.M41 * value2; - result.M42 = value1.M42 * value2; - result.M43 = value1.M43 * value2; - result.M44 = value1.M44 * value2; - - return result; - } - - /// - /// Returns a new matrix with the negated elements of the given matrix. - /// - /// The source matrix. - /// The negated matrix. - public static Matrix4x4 operator -(Matrix4x4 value) - { - Matrix4x4 m; - - m.M11 = -value.M11; - m.M12 = -value.M12; - m.M13 = -value.M13; - m.M14 = -value.M14; - m.M21 = -value.M21; - m.M22 = -value.M22; - m.M23 = -value.M23; - m.M24 = -value.M24; - m.M31 = -value.M31; - m.M32 = -value.M32; - m.M33 = -value.M33; - m.M34 = -value.M34; - m.M41 = -value.M41; - m.M42 = -value.M42; - m.M43 = -value.M43; - m.M44 = -value.M44; - - return m; - } - - /// - /// Adds two matrices together. - /// - /// The first source matrix. - /// The second source matrix. - /// The resulting matrix. - public static Matrix4x4 operator +(Matrix4x4 value1, Matrix4x4 value2) - { - Matrix4x4 m; - - m.M11 = value1.M11 + value2.M11; - m.M12 = value1.M12 + value2.M12; - m.M13 = value1.M13 + value2.M13; - m.M14 = value1.M14 + value2.M14; - m.M21 = value1.M21 + value2.M21; - m.M22 = value1.M22 + value2.M22; - m.M23 = value1.M23 + value2.M23; - m.M24 = value1.M24 + value2.M24; - m.M31 = value1.M31 + value2.M31; - m.M32 = value1.M32 + value2.M32; - m.M33 = value1.M33 + value2.M33; - m.M34 = value1.M34 + value2.M34; - m.M41 = value1.M41 + value2.M41; - m.M42 = value1.M42 + value2.M42; - m.M43 = value1.M43 + value2.M43; - m.M44 = value1.M44 + value2.M44; - - return m; - } - - /// - /// Subtracts the second matrix from the first. - /// - /// The first source matrix. - /// The second source matrix. - /// The result of the subtraction. - public static Matrix4x4 operator -(Matrix4x4 value1, Matrix4x4 value2) - { - Matrix4x4 m; - - m.M11 = value1.M11 - value2.M11; - m.M12 = value1.M12 - value2.M12; - m.M13 = value1.M13 - value2.M13; - m.M14 = value1.M14 - value2.M14; - m.M21 = value1.M21 - value2.M21; - m.M22 = value1.M22 - value2.M22; - m.M23 = value1.M23 - value2.M23; - m.M24 = value1.M24 - value2.M24; - m.M31 = value1.M31 - value2.M31; - m.M32 = value1.M32 - value2.M32; - m.M33 = value1.M33 - value2.M33; - m.M34 = value1.M34 - value2.M34; - m.M41 = value1.M41 - value2.M41; - m.M42 = value1.M42 - value2.M42; - m.M43 = value1.M43 - value2.M43; - m.M44 = value1.M44 - value2.M44; - - return m; - } - - /// - /// Multiplies a matrix by another matrix. - /// - /// The first source matrix. - /// The second source matrix. - /// The result of the multiplication. - public static Matrix4x4 operator *(Matrix4x4 value1, Matrix4x4 value2) - { - Matrix4x4 m; - - // First row - m.M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21 + value1.M13 * value2.M31 + value1.M14 * value2.M41; - - m.M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22 + value1.M13 * value2.M32 + value1.M14 * value2.M42; - - m.M13 = value1.M11 * value2.M13 + value1.M12 * value2.M23 + value1.M13 * value2.M33 + value1.M14 * value2.M43; - - m.M14 = value1.M11 * value2.M14 + value1.M12 * value2.M24 + value1.M13 * value2.M34 + value1.M14 * value2.M44; - - // Second row - m.M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21 + value1.M23 * value2.M31 + value1.M24 * value2.M41; - - m.M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22 + value1.M23 * value2.M32 + value1.M24 * value2.M42; - - m.M23 = value1.M21 * value2.M13 + value1.M22 * value2.M23 + value1.M23 * value2.M33 + value1.M24 * value2.M43; - - m.M24 = value1.M21 * value2.M14 + value1.M22 * value2.M24 + value1.M23 * value2.M34 + value1.M24 * value2.M44; - - // Third row - m.M31 = value1.M31 * value2.M11 + value1.M32 * value2.M21 + value1.M33 * value2.M31 + value1.M34 * value2.M41; - - m.M32 = value1.M31 * value2.M12 + value1.M32 * value2.M22 + value1.M33 * value2.M32 + value1.M34 * value2.M42; - - m.M33 = value1.M31 * value2.M13 + value1.M32 * value2.M23 + value1.M33 * value2.M33 + value1.M34 * value2.M43; - - m.M34 = value1.M31 * value2.M14 + value1.M32 * value2.M24 + value1.M33 * value2.M34 + value1.M34 * value2.M44; - - // Fourth row - m.M41 = value1.M41 * value2.M11 + value1.M42 * value2.M21 + value1.M43 * value2.M31 + value1.M44 * value2.M41; - - m.M42 = value1.M41 * value2.M12 + value1.M42 * value2.M22 + value1.M43 * value2.M32 + value1.M44 * value2.M42; - - m.M43 = value1.M41 * value2.M13 + value1.M42 * value2.M23 + value1.M43 * value2.M33 + value1.M44 * value2.M43; - - m.M44 = value1.M41 * value2.M14 + value1.M42 * value2.M24 + value1.M43 * value2.M34 + value1.M44 * value2.M44; - - return m; - } - - /// - /// Multiplies a matrix by a scalar value. - /// - /// The source matrix. - /// The scaling factor. - /// The scaled matrix. - public static Matrix4x4 operator *(Matrix4x4 value1, float value2) - { - Matrix4x4 m; - - m.M11 = value1.M11 * value2; - m.M12 = value1.M12 * value2; - m.M13 = value1.M13 * value2; - m.M14 = value1.M14 * value2; - m.M21 = value1.M21 * value2; - m.M22 = value1.M22 * value2; - m.M23 = value1.M23 * value2; - m.M24 = value1.M24 * value2; - m.M31 = value1.M31 * value2; - m.M32 = value1.M32 * value2; - m.M33 = value1.M33 * value2; - m.M34 = value1.M34 * value2; - m.M41 = value1.M41 * value2; - m.M42 = value1.M42 * value2; - m.M43 = value1.M43 * value2; - m.M44 = value1.M44 * value2; - return m; - } - - /// - /// Returns a boolean indicating whether the given two matrices are equal. - /// - /// The first matrix to compare. - /// The second matrix to compare. - /// True if the given matrices are equal; False otherwise. - public static bool operator ==(Matrix4x4 value1, Matrix4x4 value2) - { - return value1.M11 == value2.M11 && - value1.M22 == value2.M22 && - value1.M33 == value2.M33 && - value1.M44 == value2.M44 && // Check diagonal element first for early out. - value1.M12 == value2.M12 && - value1.M13 == value2.M13 && - value1.M14 == value2.M14 && - value1.M21 == value2.M21 && - value1.M23 == value2.M23 && - value1.M24 == value2.M24 && - value1.M31 == value2.M31 && - value1.M32 == value2.M32 && - value1.M34 == value2.M34 && - value1.M41 == value2.M41 && - value1.M42 == value2.M42 && - value1.M43 == value2.M43; - } - - /// - /// Returns a boolean indicating whether the given two matrices are not equal. - /// - /// The first matrix to compare. - /// The second matrix to compare. - /// True if the given matrices are not equal; False if they are equal. - public static bool operator !=(Matrix4x4 value1, Matrix4x4 value2) - { - return value1.M11 != value2.M11 || - value1.M12 != value2.M12 || - value1.M13 != value2.M13 || - value1.M14 != value2.M14 || - value1.M21 != value2.M21 || - value1.M22 != value2.M22 || - value1.M23 != value2.M23 || - value1.M24 != value2.M24 || - value1.M31 != value2.M31 || - value1.M32 != value2.M32 || - value1.M33 != value2.M33 || - value1.M34 != value2.M34 || - value1.M41 != value2.M41 || - value1.M42 != value2.M42 || - value1.M43 != value2.M43 || - value1.M44 != value2.M44; - } - - /// - /// Returns a boolean indicating whether this matrix instance is equal to the other given matrix. - /// - /// The matrix to compare this instance to. - /// True if the matrices are equal; False otherwise. - public bool Equals(Matrix4x4 other) - { - return M11 == other.M11 && - M22 == other.M22 && - M33 == other.M33 && - M44 == other.M44 && // Check diagonal element first for early out. - M12 == other.M12 && - M13 == other.M13 && - M14 == other.M14 && - M21 == other.M21 && - M23 == other.M23 && - M24 == other.M24 && - M31 == other.M31 && - M32 == other.M32 && - M34 == other.M34 && - M41 == other.M41 && - M42 == other.M42 && - M43 == other.M43; - } - - /// - /// Returns a boolean indicating whether the given Object is equal to this matrix instance. - /// - /// The Object to compare against. - /// True if the Object is equal to this matrix; False otherwise. - public override bool Equals(object obj) - { - if (obj is Matrix4x4 matrix4) - { - return Equals(matrix4); - } - - return false; - } - - /// - /// Returns a String representing this matrix instance. - /// - /// The string representation. - public override string ToString() - { - CultureInfo ci = CultureInfo.CurrentCulture; - - return string.Format( - ci, - "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} }}", - M11.ToString(ci), M12.ToString(ci), M13.ToString(ci), M14.ToString(ci), M21.ToString(ci), M22.ToString(ci), M23.ToString(ci), - M24.ToString(ci), M31.ToString(ci), M32.ToString(ci), M33.ToString(ci), M34.ToString(ci), M41.ToString(ci), M42.ToString(ci), - M43.ToString(ci), M44.ToString(ci)); - } - - /// - /// Returns the hash code for this instance. - /// - /// The hash code. - public override int GetHashCode() - { - return M11.GetHashCode() + - M12.GetHashCode() + - M13.GetHashCode() + - M14.GetHashCode() + - M21.GetHashCode() + - M22.GetHashCode() + - M23.GetHashCode() + - M24.GetHashCode() + - M31.GetHashCode() + - M32.GetHashCode() + - M33.GetHashCode() + - M34.GetHashCode() + - M41.GetHashCode() + - M42.GetHashCode() + - M43.GetHashCode() + - M44.GetHashCode(); - } - - public IEnumerable GetBytes(params object[] options) - { - // 4 bytes per matrix cell - var buffer = new List(); - buffer.AddRange(M11.GetBytes()); - buffer.AddRange(M21.GetBytes()); - buffer.AddRange(M31.GetBytes()); - buffer.AddRange(M41.GetBytes()); - buffer.AddRange(M12.GetBytes()); - buffer.AddRange(M22.GetBytes()); - buffer.AddRange(M32.GetBytes()); - buffer.AddRange(M42.GetBytes()); - buffer.AddRange(M13.GetBytes()); - buffer.AddRange(M23.GetBytes()); - buffer.AddRange(M33.GetBytes()); - buffer.AddRange(M43.GetBytes()); - buffer.AddRange(M14.GetBytes()); - buffer.AddRange(M24.GetBytes()); - buffer.AddRange(M34.GetBytes()); - buffer.AddRange(M44.GetBytes()); - return buffer; + public void Read(SBinaryReader binaryReader) + { + M11 = binaryReader.ReadSingle(); + M21 = binaryReader.ReadSingle(); + M31 = binaryReader.ReadSingle(); + M41 = binaryReader.ReadSingle(); + M12 = binaryReader.ReadSingle(); + M22 = binaryReader.ReadSingle(); + M32 = binaryReader.ReadSingle(); + M42 = binaryReader.ReadSingle(); + M13 = binaryReader.ReadSingle(); + M23 = binaryReader.ReadSingle(); + M33 = binaryReader.ReadSingle(); + M43 = binaryReader.ReadSingle(); + M14 = binaryReader.ReadSingle(); + M24 = binaryReader.ReadSingle(); + M34 = binaryReader.ReadSingle(); + M44 = binaryReader.ReadSingle(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(M11); + binaryWriter.Write(M21); + binaryWriter.Write(M31); + binaryWriter.Write(M41); + binaryWriter.Write(M12); + binaryWriter.Write(M22); + binaryWriter.Write(M32); + binaryWriter.Write(M42); + binaryWriter.Write(M13); + binaryWriter.Write(M23); + binaryWriter.Write(M33); + binaryWriter.Write(M43); + binaryWriter.Write(M14); + binaryWriter.Write(M24); + binaryWriter.Write(M34); + binaryWriter.Write(M44); } } diff --git a/src/Parsec/Shaiya/Common/MeshFace.cs b/src/Parsec/Shaiya/Common/MeshFace.cs new file mode 100644 index 00000000..a1344fa4 --- /dev/null +++ b/src/Parsec/Shaiya/Common/MeshFace.cs @@ -0,0 +1,39 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Common; + +/// +/// Class that represents a mesh's triangular face (polygon). All faces in shaiya (3DC, 3DO, SMOD, VAni, etc.) are triangular. +/// +public sealed class MeshFace : ISerializable +{ + /// + /// Index of the first vertex of the mesh + /// + public ushort VertexIndex1 { get; set; } + + /// + /// Index of the second vertex of the mesh + /// + public ushort VertexIndex2 { get; set; } + + /// + /// Index of the third vertex of the mesh + /// + public ushort VertexIndex3 { get; set; } + + public void Read(SBinaryReader binaryReader) + { + VertexIndex1 = binaryReader.ReadUInt16(); + VertexIndex2 = binaryReader.ReadUInt16(); + VertexIndex3 = binaryReader.ReadUInt16(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(VertexIndex1); + binaryWriter.Write(VertexIndex2); + binaryWriter.Write(VertexIndex3); + } +} diff --git a/src/Parsec/Shaiya/Common/Quaternion.cs b/src/Parsec/Shaiya/Common/Quaternion.cs index ab409644..2123adc2 100644 --- a/src/Parsec/Shaiya/Common/Quaternion.cs +++ b/src/Parsec/Shaiya/Common/Quaternion.cs @@ -1,14 +1,4 @@ -/* - Code based on https://github.com/microsoft/referencesource/blob/master/System.Numerics/System/Numerics/Quaternion.cs - From the repository https://github.com/microsoft/referencesource - Copyright (c) Microsoft. All rights reserved. - Licensed under the MIT license. See LICENSE file in the project root for full license information. -*/ - -using System.Globalization; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Common; @@ -17,7 +7,7 @@ namespace Parsec.Shaiya.Common; /// A structure encapsulating a four-dimensional vector (x,y,z,w), /// which is used to efficiently rotate an object about the (x,y,z) vector by the angle theta, where w = cos(theta/2). /// -public struct Quaternion : IEquatable, IBinary +public struct Quaternion : ISerializable { /// /// Specifies the X-value of the vector component of the Quaternion. @@ -39,38 +29,6 @@ public struct Quaternion : IEquatable, IBinary /// public float W; - public Quaternion(SBinaryReader binaryReader) - { - X = binaryReader.Read(); - Y = binaryReader.Read(); - Z = binaryReader.Read(); - W = binaryReader.Read(); - } - - /// - /// Returns a Quaternion representing no rotation. - /// - public static Quaternion Identity - { - get { return new Quaternion(0, 0, 0, 1); } - } - - /// - /// Returns whether the Quaternion is the identity Quaternion. - /// - [JsonIgnore] - public bool IsIdentity - { - get { return X == 0f && Y == 0f && Z == 0f && W == 1f; } - } - - /// - /// Constructs a Quaternion from the given components. - /// - /// The X component of the Quaternion. - /// The Y component of the Quaternion. - /// The Z component of the Quaternion. - /// The W component of the Quaternion. public Quaternion(float x, float y, float z, float w) { X = x; @@ -79,567 +37,19 @@ public Quaternion(float x, float y, float z, float w) W = w; } - /// - /// Returns the inverse of a Quaternion. - /// - /// The source Quaternion. - /// The inverted Quaternion. - public static Quaternion Inverse(Quaternion value) - { - // -1 ( a -v ) - // q = ( ------------- ------------- ) - // ( a^2 + |v|^2 , a^2 + |v|^2 ) - - Quaternion ans; - - float ls = value.X * value.X + value.Y * value.Y + value.Z * value.Z + value.W * value.W; - float invNorm = 1.0f / ls; - - ans.X = -value.X * invNorm; - ans.Y = -value.Y * invNorm; - ans.Z = -value.Z * invNorm; - ans.W = value.W * invNorm; - - return ans; - } - - /// - /// Creates a Quaternion from the given rotation matrix. - /// - /// The rotation matrix. - /// The created Quaternion. - public static Quaternion CreateFromRotationMatrix(Matrix4x4 matrix) - { - float trace = matrix.M11 + matrix.M22 + matrix.M33; - - Quaternion q = new Quaternion(); - - if (trace > 0.0f) - { - float s = (float)Math.Sqrt(trace + 1.0f); - q.W = s * 0.5f; - s = 0.5f / s; - q.X = (matrix.M23 - matrix.M32) * s; - q.Y = (matrix.M31 - matrix.M13) * s; - q.Z = (matrix.M12 - matrix.M21) * s; - } - else - { - if (matrix.M11 >= matrix.M22 && matrix.M11 >= matrix.M33) - { - float s = (float)Math.Sqrt(1.0f + matrix.M11 - matrix.M22 - matrix.M33); - float invS = 0.5f / s; - q.X = 0.5f * s; - q.Y = (matrix.M12 + matrix.M21) * invS; - q.Z = (matrix.M13 + matrix.M31) * invS; - q.W = (matrix.M23 - matrix.M32) * invS; - } - else if (matrix.M22 > matrix.M33) - { - float s = (float)Math.Sqrt(1.0f + matrix.M22 - matrix.M11 - matrix.M33); - float invS = 0.5f / s; - q.X = (matrix.M21 + matrix.M12) * invS; - q.Y = 0.5f * s; - q.Z = (matrix.M32 + matrix.M23) * invS; - q.W = (matrix.M31 - matrix.M13) * invS; - } - else - { - float s = (float)Math.Sqrt(1.0f + matrix.M33 - matrix.M11 - matrix.M22); - float invS = 0.5f / s; - q.X = (matrix.M31 + matrix.M13) * invS; - q.Y = (matrix.M32 + matrix.M23) * invS; - q.Z = 0.5f * s; - q.W = (matrix.M12 - matrix.M21) * invS; - } - } - - return q; - } - - /// - /// Calculates the dot product of two Quaternions. - /// - /// The first source Quaternion. - /// The second source Quaternion. - /// The dot product of the Quaternions. - public static float Dot(Quaternion quaternion1, Quaternion quaternion2) - { - return quaternion1.X * quaternion2.X + quaternion1.Y * quaternion2.Y + quaternion1.Z * quaternion2.Z + - quaternion1.W * quaternion2.W; - } - - /// - /// Interpolates between two quaternions, using spherical linear interpolation. - /// - /// The first source Quaternion. - /// The second source Quaternion. - /// The relative weight of the second source Quaternion in the interpolation. - /// The interpolated Quaternion. - public static Quaternion Slerp(Quaternion quaternion1, Quaternion quaternion2, float amount) - { - const float epsilon = 1e-6f; - - float t = amount; - - float cosOmega = quaternion1.X * quaternion2.X + - quaternion1.Y * quaternion2.Y + - quaternion1.Z * quaternion2.Z + - quaternion1.W * quaternion2.W; - - bool flip = false; - - if (cosOmega < 0.0f) - { - flip = true; - cosOmega = -cosOmega; - } - - float s1, s2; - - if (cosOmega > 1.0f - epsilon) - { - // Too close, do straight linear interpolation. - s1 = 1.0f - t; - s2 = flip ? -t : t; - } - else - { - float omega = (float)Math.Acos(cosOmega); - float invSinOmega = (float)(1 / Math.Sin(omega)); - - s1 = (float)Math.Sin((1.0f - t) * omega) * invSinOmega; - s2 = flip ? (float)-Math.Sin(t * omega) * invSinOmega : (float)Math.Sin(t * omega) * invSinOmega; - } - - Quaternion ans; - - ans.X = s1 * quaternion1.X + s2 * quaternion2.X; - ans.Y = s1 * quaternion1.Y + s2 * quaternion2.Y; - ans.Z = s1 * quaternion1.Z + s2 * quaternion2.Z; - ans.W = s1 * quaternion1.W + s2 * quaternion2.W; - - return ans; - } - - /// - /// Linearly interpolates between two quaternions. - /// - /// The first source Quaternion. - /// The second source Quaternion. - /// The relative weight of the second source Quaternion in the interpolation. - /// The interpolated Quaternion. - public static Quaternion Lerp(Quaternion quaternion1, Quaternion quaternion2, float amount) - { - float t = amount; - float t1 = 1.0f - t; - - Quaternion r = new Quaternion(); - - float dot = quaternion1.X * quaternion2.X + quaternion1.Y * quaternion2.Y + quaternion1.Z * quaternion2.Z + - quaternion1.W * quaternion2.W; - - if (dot >= 0.0f) - { - r.X = t1 * quaternion1.X + t * quaternion2.X; - r.Y = t1 * quaternion1.Y + t * quaternion2.Y; - r.Z = t1 * quaternion1.Z + t * quaternion2.Z; - r.W = t1 * quaternion1.W + t * quaternion2.W; - } - else - { - r.X = t1 * quaternion1.X - t * quaternion2.X; - r.Y = t1 * quaternion1.Y - t * quaternion2.Y; - r.Z = t1 * quaternion1.Z - t * quaternion2.Z; - r.W = t1 * quaternion1.W - t * quaternion2.W; - } - - // Normalize it. - float ls = r.X * r.X + r.Y * r.Y + r.Z * r.Z + r.W * r.W; - float invNorm = 1.0f / (float)Math.Sqrt(ls); - - r.X *= invNorm; - r.Y *= invNorm; - r.Z *= invNorm; - r.W *= invNorm; - - return r; - } - - /// - /// Flips the sign of each component of the quaternion. - /// - /// The source Quaternion. - /// The negated Quaternion. - public static Quaternion Negate(Quaternion value) - { - Quaternion ans; - - ans.X = -value.X; - ans.Y = -value.Y; - ans.Z = -value.Z; - ans.W = -value.W; - - return ans; - } - - /// - /// Adds two Quaternions element-by-element. - /// - /// The first source Quaternion. - /// The second source Quaternion. - /// The result of adding the Quaternions. - public static Quaternion Add(Quaternion value1, Quaternion value2) - { - Quaternion ans; - - ans.X = value1.X + value2.X; - ans.Y = value1.Y + value2.Y; - ans.Z = value1.Z + value2.Z; - ans.W = value1.W + value2.W; - - return ans; - } - - /// - /// Subtracts one Quaternion from another. - /// - /// The first source Quaternion. - /// The second Quaternion, to be subtracted from the first. - /// The result of the subtraction. - public static Quaternion Subtract(Quaternion value1, Quaternion value2) + public void Read(SBinaryReader binaryReader) { - Quaternion ans; - - ans.X = value1.X - value2.X; - ans.Y = value1.Y - value2.Y; - ans.Z = value1.Z - value2.Z; - ans.W = value1.W - value2.W; - - return ans; + X = binaryReader.ReadSingle(); + Y = binaryReader.ReadSingle(); + Z = binaryReader.ReadSingle(); + W = binaryReader.ReadSingle(); } - /// - /// Multiplies two Quaternions together. - /// - /// The Quaternion on the left side of the multiplication. - /// The Quaternion on the right side of the multiplication. - /// The result of the multiplication. - public static Quaternion Multiply(Quaternion value1, Quaternion value2) + public void Write(SBinaryWriter binaryWriter) { - Quaternion ans; - - float q1x = value1.X; - float q1y = value1.Y; - float q1z = value1.Z; - float q1w = value1.W; - - float q2x = value2.X; - float q2y = value2.Y; - float q2z = value2.Z; - float q2w = value2.W; - - // cross(av, bv) - float cx = q1y * q2z - q1z * q2y; - float cy = q1z * q2x - q1x * q2z; - float cz = q1x * q2y - q1y * q2x; - - float dot = q1x * q2x + q1y * q2y + q1z * q2z; - - ans.X = q1x * q2w + q2x * q1w + cx; - ans.Y = q1y * q2w + q2y * q1w + cy; - ans.Z = q1z * q2w + q2z * q1w + cz; - ans.W = q1w * q2w - dot; - - return ans; - } - - /// - /// Multiplies a Quaternion by a scalar value. - /// - /// The source Quaternion. - /// The scalar value. - /// The result of the multiplication. - public static Quaternion Multiply(Quaternion value1, float value2) - { - Quaternion ans; - - ans.X = value1.X * value2; - ans.Y = value1.Y * value2; - ans.Z = value1.Z * value2; - ans.W = value1.W * value2; - - return ans; - } - - /// - /// Divides a Quaternion by another Quaternion. - /// - /// The source Quaternion. - /// The divisor. - /// The result of the division. - public static Quaternion Divide(Quaternion value1, Quaternion value2) - { - Quaternion ans; - - float q1x = value1.X; - float q1y = value1.Y; - float q1z = value1.Z; - float q1w = value1.W; - - //------------------------------------- - // Inverse part. - float ls = value2.X * value2.X + value2.Y * value2.Y + value2.Z * value2.Z + value2.W * value2.W; - float invNorm = 1.0f / ls; - - float q2x = -value2.X * invNorm; - float q2y = -value2.Y * invNorm; - float q2z = -value2.Z * invNorm; - float q2w = value2.W * invNorm; - - //------------------------------------- - // Multiply part. - - // cross(av, bv) - float cx = q1y * q2z - q1z * q2y; - float cy = q1z * q2x - q1x * q2z; - float cz = q1x * q2y - q1y * q2x; - - float dot = q1x * q2x + q1y * q2y + q1z * q2z; - - ans.X = q1x * q2w + q2x * q1w + cx; - ans.Y = q1y * q2w + q2y * q1w + cy; - ans.Z = q1z * q2w + q2z * q1w + cz; - ans.W = q1w * q2w - dot; - - return ans; - } - - /// - /// Flips the sign of each component of the quaternion. - /// - /// The source Quaternion. - /// The negated Quaternion. - public static Quaternion operator -(Quaternion value) - { - Quaternion ans; - - ans.X = -value.X; - ans.Y = -value.Y; - ans.Z = -value.Z; - ans.W = -value.W; - - return ans; - } - - /// - /// Adds two Quaternions element-by-element. - /// - /// The first source Quaternion. - /// The second source Quaternion. - /// The result of adding the Quaternions. - public static Quaternion operator +(Quaternion value1, Quaternion value2) - { - Quaternion ans; - - ans.X = value1.X + value2.X; - ans.Y = value1.Y + value2.Y; - ans.Z = value1.Z + value2.Z; - ans.W = value1.W + value2.W; - - return ans; - } - - /// - /// Subtracts one Quaternion from another. - /// - /// The first source Quaternion. - /// The second Quaternion, to be subtracted from the first. - /// The result of the subtraction. - public static Quaternion operator -(Quaternion value1, Quaternion value2) - { - Quaternion ans; - - ans.X = value1.X - value2.X; - ans.Y = value1.Y - value2.Y; - ans.Z = value1.Z - value2.Z; - ans.W = value1.W - value2.W; - - return ans; - } - - /// - /// Multiplies two Quaternions together. - /// - /// The Quaternion on the left side of the multiplication. - /// The Quaternion on the right side of the multiplication. - /// The result of the multiplication. - public static Quaternion operator *(Quaternion value1, Quaternion value2) - { - Quaternion ans; - - float q1x = value1.X; - float q1y = value1.Y; - float q1z = value1.Z; - float q1w = value1.W; - - float q2x = value2.X; - float q2y = value2.Y; - float q2z = value2.Z; - float q2w = value2.W; - - // cross(av, bv) - float cx = q1y * q2z - q1z * q2y; - float cy = q1z * q2x - q1x * q2z; - float cz = q1x * q2y - q1y * q2x; - - float dot = q1x * q2x + q1y * q2y + q1z * q2z; - - ans.X = q1x * q2w + q2x * q1w + cx; - ans.Y = q1y * q2w + q2y * q1w + cy; - ans.Z = q1z * q2w + q2z * q1w + cz; - ans.W = q1w * q2w - dot; - - return ans; - } - - /// - /// Multiplies a Quaternion by a scalar value. - /// - /// The source Quaternion. - /// The scalar value. - /// The result of the multiplication. - public static Quaternion operator *(Quaternion value1, float value2) - { - Quaternion ans; - - ans.X = value1.X * value2; - ans.Y = value1.Y * value2; - ans.Z = value1.Z * value2; - ans.W = value1.W * value2; - - return ans; - } - - /// - /// Divides a Quaternion by another Quaternion. - /// - /// The source Quaternion. - /// The divisor. - /// The result of the division. - public static Quaternion operator /(Quaternion value1, Quaternion value2) - { - Quaternion ans; - - float q1x = value1.X; - float q1y = value1.Y; - float q1z = value1.Z; - float q1w = value1.W; - - //------------------------------------- - // Inverse part. - float ls = value2.X * value2.X + value2.Y * value2.Y + value2.Z * value2.Z + value2.W * value2.W; - float invNorm = 1.0f / ls; - - float q2x = -value2.X * invNorm; - float q2y = -value2.Y * invNorm; - float q2z = -value2.Z * invNorm; - float q2w = value2.W * invNorm; - - //------------------------------------- - // Multiply part. - - // cross(av, bv) - float cx = q1y * q2z - q1z * q2y; - float cy = q1z * q2x - q1x * q2z; - float cz = q1x * q2y - q1y * q2x; - - float dot = q1x * q2x + q1y * q2y + q1z * q2z; - - ans.X = q1x * q2w + q2x * q1w + cx; - ans.Y = q1y * q2w + q2y * q1w + cy; - ans.Z = q1z * q2w + q2z * q1w + cz; - ans.W = q1w * q2w - dot; - - return ans; - } - - /// - /// Returns a boolean indicating whether the two given Quaternions are equal. - /// - /// The first Quaternion to compare. - /// The second Quaternion to compare. - /// True if the Quaternions are equal; False otherwise. - public static bool operator ==(Quaternion value1, Quaternion value2) - { - return value1.X == value2.X && value1.Y == value2.Y && value1.Z == value2.Z && value1.W == value2.W; - } - - /// - /// Returns a boolean indicating whether the two given Quaternions are not equal. - /// - /// The first Quaternion to compare. - /// The second Quaternion to compare. - /// True if the Quaternions are not equal; False if they are equal. - public static bool operator !=(Quaternion value1, Quaternion value2) - { - return value1.X != value2.X || value1.Y != value2.Y || value1.Z != value2.Z || value1.W != value2.W; - } - - /// - /// Returns a boolean indicating whether the given Quaternion is equal to this Quaternion instance. - /// - /// The Quaternion to compare this instance to. - /// True if the other Quaternion is equal to this instance; False otherwise. - public bool Equals(Quaternion other) - { - return X == other.X && Y == other.Y && Z == other.Z && W == other.W; - } - - /// - /// Returns a boolean indicating whether the given Object is equal to this Quaternion instance. - /// - /// The Object to compare against. - /// True if the Object is equal to this Quaternion; False otherwise. - public override bool Equals(object obj) - { - if (obj is Quaternion) - { - return Equals((Quaternion)obj); - } - - return false; - } - - /// - /// Returns a String representing this Quaternion instance. - /// - /// The string representation. - public override string ToString() - { - CultureInfo ci = CultureInfo.CurrentCulture; - - return String.Format(ci, "{{X:{0} Y:{1} Z:{2} W:{3}}}", X.ToString(ci), Y.ToString(ci), Z.ToString(ci), W.ToString(ci)); - } - - /// - /// Returns the hash code for this instance. - /// - /// The hash code. - public override int GetHashCode() - { - return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode(); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - - buffer.AddRange(X.GetBytes()); - buffer.AddRange(Y.GetBytes()); - buffer.AddRange(Z.GetBytes()); - buffer.AddRange(W.GetBytes()); - - return buffer; + binaryWriter.Write(X); + binaryWriter.Write(Y); + binaryWriter.Write(Z); + binaryWriter.Write(W); } } diff --git a/src/Parsec/Shaiya/Common/String256.cs b/src/Parsec/Shaiya/Common/String256.cs index e656bebd..1ac276d0 100644 --- a/src/Parsec/Shaiya/Common/String256.cs +++ b/src/Parsec/Shaiya/Common/String256.cs @@ -1,28 +1,21 @@ using System.Text; -using Parsec.Attributes; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Common; -public sealed class String256 : IBinary +public sealed class String256 : ISerializable { - public String256() - { - } + public string Value { get; set; } = string.Empty; - public String256(string str) - { - Value = str; - } + public char PaddingChar { get; set; } = '\0'; - public String256(SBinaryReader binaryReader) + public void Read(SBinaryReader binaryReader) { - byte[] unparsedBytes = binaryReader.ReadBytes(256); - int stringEndIndex = 0; + var unparsedBytes = binaryReader.ReadBytes(256); + var stringEndIndex = 0; - for (int i = 0; i < 256; i++) + for (var i = 0; i < 256; i++) { if (unparsedBytes[i] == 0) { @@ -34,15 +27,15 @@ public String256(SBinaryReader binaryReader) Value = Encoding.ASCII.GetString(unparsedBytes, 0, stringEndIndex); } - [ShaiyaProperty] - [FixedLengthString(isString256: true)] - public string Value { get; set; } - - public IEnumerable GetBytes(params object[] options) => Value.PadRight(256, '\0').GetBytes(); + public void Write(SBinaryWriter binaryWriter) + { + var value = Value.PadRight(256, PaddingChar); + binaryWriter.Write(value, isLengthPrefixed: false, includeStringTerminator: false); + } public override string ToString() => Value; public static implicit operator string(String256 string256) => string256.ToString(); - public static implicit operator String256(string str) => new(str); + public static implicit operator String256(string str) => new() { Value = str }; } diff --git a/src/Parsec/Shaiya/Common/Vector2.cs b/src/Parsec/Shaiya/Common/Vector2.cs index 1bb8696e..56df358e 100644 --- a/src/Parsec/Shaiya/Common/Vector2.cs +++ b/src/Parsec/Shaiya/Common/Vector2.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Common; @@ -8,7 +7,7 @@ namespace Parsec.Shaiya.Common; /// /// Represents a vector in a 2-dimensional space /// -public struct Vector2 : IBinary +public struct Vector2 : ISerializable { /// /// 1st (first) element of the vector @@ -27,17 +26,19 @@ public Vector2(float x, float y) Y = y; } - public Vector2(SBinaryReader binaryReader) + public void Read(SBinaryReader binaryReader) { - X = binaryReader.Read(); - Y = binaryReader.Read(); + X = binaryReader.ReadSingle(); + Y = binaryReader.ReadSingle(); } - public IEnumerable GetBytes(params object[] options) + public void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(X.GetBytes()); - buffer.AddRange(Y.GetBytes()); - return buffer; + binaryWriter.Write(X); + binaryWriter.Write(Y); } + + public static Vector2 operator +(Vector2 vec1, Vector2 vec2) => new(vec1.X + vec2.X, vec1.Y + vec2.Y); + + public static Vector2 operator -(Vector2 vec1, Vector2 vec2) => new(vec1.X - vec2.X, vec1.Y - vec2.Y); } diff --git a/src/Parsec/Shaiya/Common/Vector3.cs b/src/Parsec/Shaiya/Common/Vector3.cs index 6d2dcb0a..0dcc2b41 100644 --- a/src/Parsec/Shaiya/Common/Vector3.cs +++ b/src/Parsec/Shaiya/Common/Vector3.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Common; @@ -8,7 +7,7 @@ namespace Parsec.Shaiya.Common; /// /// Represents a vector in a 3-dimensional space /// -public struct Vector3 : IBinary +public struct Vector3 : ISerializable { /// /// 1st (first) element of the vector @@ -33,20 +32,18 @@ public Vector3(float x, float y, float z) Z = z; } - public Vector3(SBinaryReader binaryReader) + public void Read(SBinaryReader binaryReader) { - X = binaryReader.Read(); - Y = binaryReader.Read(); - Z = binaryReader.Read(); + X = binaryReader.ReadSingle(); + Y = binaryReader.ReadSingle(); + Z = binaryReader.ReadSingle(); } - public IEnumerable GetBytes(params object[] options) + public void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(X.GetBytes()); - buffer.AddRange(Y.GetBytes()); - buffer.AddRange(Z.GetBytes()); - return buffer; + binaryWriter.Write(X); + binaryWriter.Write(Y); + binaryWriter.Write(Z); } public static Vector3 operator +(Vector3 vec1, Vector3 vec2) => new(vec1.X + vec2.X, vec1.Y + vec2.Y, vec1.Z + vec2.Z); diff --git a/src/Parsec/Shaiya/Common/Vector4.cs b/src/Parsec/Shaiya/Common/Vector4.cs index 64b81683..a58ef94f 100644 --- a/src/Parsec/Shaiya/Common/Vector4.cs +++ b/src/Parsec/Shaiya/Common/Vector4.cs @@ -1,5 +1,4 @@ -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Common; @@ -7,7 +6,7 @@ namespace Parsec.Shaiya.Common; /// /// Represents a vector in a 4-dimensional space /// -public struct Vector4 : IBinary +public struct Vector4 : ISerializable { /// /// 1st (first) element of the vector @@ -37,21 +36,19 @@ public Vector4(float x, float y, float z, float w) W = w; } - public Vector4(SBinaryReader binaryReader) + public void Read(SBinaryReader binaryReader) { - X = binaryReader.Read(); - Y = binaryReader.Read(); - Z = binaryReader.Read(); - W = binaryReader.Read(); + X = binaryReader.ReadSingle(); + Y = binaryReader.ReadSingle(); + Z = binaryReader.ReadSingle(); + W = binaryReader.ReadSingle(); } - public IEnumerable GetBytes(params object[] options) + public void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(X.GetBytes()); - buffer.AddRange(Y.GetBytes()); - buffer.AddRange(Z.GetBytes()); - buffer.AddRange(W.GetBytes()); - return buffer; + binaryWriter.Write(X); + binaryWriter.Write(Y); + binaryWriter.Write(Z); + binaryWriter.Write(W); } } diff --git a/src/Parsec/Shaiya/Common/Vector5.cs b/src/Parsec/Shaiya/Common/Vector5.cs deleted file mode 100644 index 2f1165eb..00000000 --- a/src/Parsec/Shaiya/Common/Vector5.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Common; - -/// -/// Represents a vector in a 5-dimensional space -/// -public struct Vector5 : IBinary -{ - /// - /// 1st (first) element of the vector - /// - public float X { get; set; } - - /// - /// 2nd (second) element of the vector - /// - public float Y { get; set; } - - /// - /// 3rd (third) element of the vector - /// - public float Z { get; set; } - - /// - /// 4th (fourth) element of the vector - /// - public float W { get; set; } - - /// - /// 5th (fifth) element of the vector - /// - public float U { get; set; } - - public Vector5(float x, float y, float z, float w, float u) - { - X = x; - Y = y; - Z = z; - W = w; - U = u; - } - - public Vector5(SBinaryReader binaryReader) - { - X = binaryReader.Read(); - Y = binaryReader.Read(); - Z = binaryReader.Read(); - W = binaryReader.Read(); - U = binaryReader.Read(); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(X.GetBytes()); - buffer.AddRange(Y.GetBytes()); - buffer.AddRange(Z.GetBytes()); - buffer.AddRange(W.GetBytes()); - buffer.AddRange(U.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Core/FileBase.cs b/src/Parsec/Shaiya/Core/FileBase.cs index c3003c5b..132693bd 100644 --- a/src/Parsec/Shaiya/Core/FileBase.cs +++ b/src/Parsec/Shaiya/Core/FileBase.cs @@ -1,22 +1,17 @@ -using System.Reflection; -using System.Text; +using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; -using Parsec.Attributes; using Parsec.Common; using Parsec.Extensions; using Parsec.Helpers; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Data; namespace Parsec.Shaiya.Core; -public abstract class FileBase : IFileBase, IExportable +public abstract class FileBase : IJsonWritable { - [JsonIgnore] - protected SBinaryReader _binaryReader; - /// /// Full path to the file /// @@ -44,8 +39,10 @@ public abstract class FileBase : IFileBase, IExportable public string FileNameWithoutExtension => System.IO.Path.GetFileNameWithoutExtension(Path); /// - public void WriteJson(string path, params string[] ignoredPropertyNames) => + public void WriteJson(string path, params string[] ignoredPropertyNames) + { FileHelper.WriteFile(path, JsonSerialize(this, ignoredPropertyNames), Encoding); + } /// public virtual string JsonSerialize(FileBase obj, params string[] ignoredPropertyNames) @@ -64,155 +61,44 @@ public virtual string JsonSerialize(FileBase obj, params string[] ignoredPropert return JsonConvert.SerializeObject(obj, settings); } - /// - public virtual void Write(string path, Episode episode = Episode.Unknown) => FileHelper.WriteFile(path, GetBytes(episode)); + protected abstract void Read(SBinaryReader binaryReader); - /// - public virtual IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - var type = GetType(); - - // If episode wasn't explicitly set, use former episode - if (episode == Episode.Unknown) - episode = Episode; + protected abstract void Write(SBinaryWriter binaryWriter); - if (episode == Episode.Unknown && type.IsDefined(typeof(DefaultVersionAttribute))) - { - var defaultEpisodeAttribute = type.GetCustomAttributes().FirstOrDefault(); - episode = defaultEpisodeAttribute!.Episode; - } - - // Add version prefix if present (eg. "ANI_V2", "MO2", "MO4", etc) - bool isVersionPrefixed = type.IsDefined(typeof(VersionPrefixedAttribute)); - if (isVersionPrefixed) - { - var versionPrefixes = type.GetCustomAttributes(); - - foreach (var versionPrefix in versionPrefixes) - { - if ((episode == versionPrefix.MinEpisode && versionPrefix.MaxEpisode == Episode.Unknown) || - (episode >= versionPrefix.MinEpisode && episode <= versionPrefix.MaxEpisode)) - { - if (versionPrefix.PrefixType == typeof(string)) - { - buffer.AddRange(((string)versionPrefix.Prefix).GetBytes()); - } - else if (versionPrefix.PrefixType.IsPrimitive) - { - buffer.AddRange(ReflectionHelper.GetPrimitiveBytes(versionPrefix.PrefixType, versionPrefix.Prefix)); - } - } - } - } - - // Get bytes for all properties - var properties = GetType().GetProperties(); - foreach (var property in properties) - { - if (!property.IsDefined(typeof(ShaiyaPropertyAttribute))) - continue; - - buffer.AddRange(ReflectionHelper.GetPropertyBytes(type, this, property, Encoding, episode)); - } - - return buffer; + public void Write(string path) + { + var serializationOptions = new BinarySerializationOptions(Episode, Encoding); + using var binaryWriter = new SBinaryWriter(path, serializationOptions); + Write(binaryWriter); } - /// - public virtual void Read() + public void Write(string path, Episode episode, Encoding? encoding = null) { - var type = GetType(); - - // Set default version (Episode) if defined. This must be checked/set before checking the existence of the VersionPrefixedAttribute - if (type.IsDefined(typeof(DefaultVersionAttribute))) - { - var defaultEpisodeAttribute = type.GetCustomAttributes().First(); - Episode = defaultEpisodeAttribute.Episode; - } - - // Check if version prefix could be present (eg. "ANI_V2", "MO2", "MO4", etc) - bool isVersionPrefixed = type.IsDefined(typeof(VersionPrefixedAttribute)); + encoding ??= Encoding.ASCII; - if (isVersionPrefixed) - { - var versionPrefixes = type.GetCustomAttributes(); - - foreach (var versionPrefix in versionPrefixes) - { - object filePrefix; - if (versionPrefix.PrefixType == typeof(string)) - { - filePrefix = _binaryReader.ReadString(((string)versionPrefix.Prefix).Length); - } - else if (versionPrefix.PrefixType.IsPrimitive) - { - filePrefix = ReflectionHelper.ReadPrimitive(_binaryReader, versionPrefix.PrefixType); - } - else - { - continue; - } - - // If prefix matches, episode must be set and reading must continue. If it doesn't, the reading offset must be reset to the beginning of the file - if (filePrefix.Equals(versionPrefix.Prefix)) - { - Episode = versionPrefix.MinEpisode; - break; - } - - _binaryReader.ResetOffset(); - } - } - - // Read all properties - var properties = GetType().GetProperties(); - - foreach (var property in properties) - { - // skip non ShaiyaProperty properties - if (!property.IsDefined(typeof(ShaiyaPropertyAttribute))) - continue; - - object value = ReflectionHelper.ReadProperty(_binaryReader, type, this, property, Episode, Encoding); - property.SetValue(this, Convert.ChangeType(value, property.PropertyType)); - - // Set episode based on property - if (property.IsDefined(typeof(EpisodeDefinerAttribute))) - { - var definerAttributes = property.GetCustomAttributes(); - foreach (var definer in definerAttributes) - { - if (value.Equals(definer.Value)) - { - Episode = definer.Episode; - } - } - } - } + var serializationOptions = new BinarySerializationOptions(episode, encoding); + using var binaryWriter = new SBinaryWriter(path, serializationOptions); + Write(binaryWriter); } /// /// Reads the shaiya file format from a file /// /// File path - /// File episode - /// File encoding + /// Serialization options /// Shaiya File Format Type /// T instance - public static T ReadFromFile(string path, Episode episode = Episode.EP5, Encoding encoding = null) where T : FileBase, new() + internal static T ReadFromFile(string path, BinarySerializationOptions serializationOptions) where T : FileBase, new() { - var instance = new T - { - Path = path, _binaryReader = new SBinaryReader(path), Episode = episode, Encoding = encoding ?? Encoding.ASCII - }; + var instance = new T { Path = path, Episode = serializationOptions.Episode, Encoding = serializationOptions.Encoding }; + using var binaryReader = new SBinaryReader(path, serializationOptions); if (instance is IEncryptable encryptableInstance) - encryptableInstance.DecryptBuffer(); - - instance.Read(); - instance._binaryReader?.Dispose(); + { + encryptableInstance.DecryptBuffer(binaryReader); + } + instance.Read(binaryReader); return instance; } @@ -221,27 +107,26 @@ public virtual void Read() /// /// File path /// FileBase child type to be read - /// File episode - /// File encoding + /// Serialization options /// FileBase instance - public static FileBase ReadFromFile(string path, Type type, Episode episode = Episode.EP5, Encoding encoding = null) + internal static FileBase ReadFromFile(string path, Type type, BinarySerializationOptions serializationOptions) { if (!type.GetBaseClassesAndInterfaces().Contains(typeof(FileBase))) throw new ArgumentException("Type must be a child of FileBase"); - var binaryReader = new SBinaryReader(path); var instance = (FileBase)Activator.CreateInstance(type); instance.Path = path; - instance._binaryReader = binaryReader; - instance.Episode = episode; - instance.Encoding = encoding ?? Encoding.ASCII; + instance.Episode = serializationOptions.Episode; + instance.Encoding = serializationOptions.Encoding; - if (instance is IEncryptable encryptableInstance) - encryptableInstance.DecryptBuffer(); + using var binaryReader = new SBinaryReader(path, serializationOptions); - instance.Read(); - instance._binaryReader?.Dispose(); + if (instance is IEncryptable encryptableInstance) + { + encryptableInstance.DecryptBuffer(binaryReader); + } + instance.Read(binaryReader); return instance; } @@ -250,24 +135,20 @@ public static FileBase ReadFromFile(string path, Type type, Episode episode = Ep /// /// File name /// File buffer - /// File episode - /// File encoding + /// Serialization options /// Shaiya File Format Type /// T instance - public static T ReadFromBuffer(string name, byte[] buffer, Episode episode = Episode.EP5, Encoding encoding = null) - where T : FileBase, new() + internal static T ReadFromBuffer(string name, byte[] buffer, BinarySerializationOptions serializationOptions) where T : FileBase, new() { - var instance = new T - { - Path = name, _binaryReader = new SBinaryReader(buffer), Episode = episode, Encoding = encoding ?? Encoding.ASCII - }; + var instance = new T { Path = name, Episode = serializationOptions.Episode, Encoding = serializationOptions.Encoding }; + using var binaryReader = new SBinaryReader(buffer, serializationOptions); if (instance is IEncryptable encryptableInstance) - encryptableInstance.DecryptBuffer(); - - instance.Read(); - instance._binaryReader?.Dispose(); + { + encryptableInstance.DecryptBuffer(binaryReader); + } + instance.Read(binaryReader); return instance; } @@ -277,26 +158,26 @@ public static T ReadFromBuffer(string name, byte[] buffer, Episode episode = /// File name /// File buffer /// FileBase child type to be read - /// File episode - /// File encoding + /// Serialization options /// FileBase instance - public static FileBase ReadFromBuffer(string name, byte[] buffer, Type type, Episode episode = Episode.EP5, Encoding encoding = null) + internal static FileBase ReadFromBuffer(string name, byte[] buffer, Type type, BinarySerializationOptions serializationOptions) { if (!type.GetBaseClassesAndInterfaces().Contains(typeof(FileBase))) throw new ArgumentException("Type must be a child of FileBase"); var instance = (FileBase)Activator.CreateInstance(type); instance.Path = name; - instance._binaryReader = new SBinaryReader(buffer); - instance.Episode = episode; - instance.Encoding = encoding ?? Encoding.ASCII; + instance.Episode = serializationOptions.Episode; + instance.Encoding = serializationOptions.Encoding; - if (instance is IEncryptable encryptableInstance) - encryptableInstance.DecryptBuffer(); + using var binaryReader = new SBinaryReader(buffer, serializationOptions); - instance.Read(); - instance._binaryReader?.Dispose(); + if (instance is IEncryptable encryptableInstance) + { + encryptableInstance.DecryptBuffer(binaryReader); + } + instance.Read(binaryReader); return instance; } @@ -305,12 +186,12 @@ public static FileBase ReadFromBuffer(string name, byte[] buffer, Type type, Epi /// /// instance /// instance - /// File episode - /// File encoding + /// Serialization options /// FileBase instance - public static T ReadFromData(Data.Data data, SFile file, Episode episode = Episode.EP5, Encoding encoding = null) - where T : FileBase, new() => - ReadFromBuffer(file.Name, data.GetFileBuffer(file), episode, encoding); + internal static T ReadFromData(Data.Data data, SFile file, BinarySerializationOptions serializationOptions) where T : FileBase, new() + { + return ReadFromBuffer(file.Name, data.GetFileBuffer(file), serializationOptions); + } /// /// Reads the shaiya file format from a buffer (byte array) within a instance @@ -318,14 +199,33 @@ public static T ReadFromData(Data.Data data, SFile file, Episode episode = Ep /// instance /// instance /// FileBase child type to be read - /// File episode - /// File encoding + /// /// FileBase instance - public static FileBase ReadFromData(Data.Data data, SFile file, Type type, Episode episode = Episode.EP5, Encoding encoding = null) + internal static FileBase ReadFromData(Data.Data data, SFile file, Type type, BinarySerializationOptions serializationOptions) { if (!data.FileIndex.ContainsValue(file)) throw new FileNotFoundException("The provided SFile instance is not part of the Data"); - return ReadFromBuffer(file.Name, data.GetFileBuffer(file), type, episode, encoding); + return ReadFromBuffer(file.Name, data.GetFileBuffer(file), type, serializationOptions); + } + + public IEnumerable GetBytes() + { + var serializationOptions = BinarySerializationOptions.Default; + return GetBytes(serializationOptions); + } + + public IEnumerable GetBytes(Episode episode, Encoding? encoding = null) + { + var serializationOptions = new BinarySerializationOptions(episode, encoding); + return GetBytes(serializationOptions); + } + + public IEnumerable GetBytes(BinarySerializationOptions serializationOptions) + { + using var memoryStream = new MemoryStream(); + using var binaryWriter = new SBinaryWriter(memoryStream, serializationOptions); + Write(binaryWriter); + return memoryStream.ToArray(); } } diff --git a/src/Parsec/Shaiya/Core/IBinary.cs b/src/Parsec/Shaiya/Core/IBinary.cs deleted file mode 100644 index 6add7aae..00000000 --- a/src/Parsec/Shaiya/Core/IBinary.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Parsec.Shaiya.Core; - -public interface IBinary -{ - /// - /// Serializes the file into a byte array - /// - /// Extra options - IEnumerable GetBytes(params object[] options); -} diff --git a/src/Parsec/Shaiya/Core/IEncryptable.cs b/src/Parsec/Shaiya/Core/IEncryptable.cs index 924e2752..3753d8c1 100644 --- a/src/Parsec/Shaiya/Core/IEncryptable.cs +++ b/src/Parsec/Shaiya/Core/IEncryptable.cs @@ -1,11 +1,13 @@ -namespace Parsec.Shaiya.Core; +using Parsec.Serialization; + +namespace Parsec.Shaiya.Core; public interface IEncryptable { /// /// Decrypts the file's buffer /// - void DecryptBuffer(bool validateChecksum = false); + void DecryptBuffer(SBinaryReader binaryReader, bool validateChecksum = false); /// /// Gets the encrypted file bytes diff --git a/src/Parsec/Shaiya/Core/IFileBase.cs b/src/Parsec/Shaiya/Core/IFileBase.cs deleted file mode 100644 index 970dc848..00000000 --- a/src/Parsec/Shaiya/Core/IFileBase.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Parsec.Common; - -namespace Parsec.Shaiya.Core; - -public interface IFileBase -{ - /// - /// Reads and parses the file - /// - void Read(); - - /// - /// Writes the file to its original format - /// - /// Path where to write the file - /// The desired output format - void Write(string path, Episode episode = Episode.Unknown); - - /// - /// Gets the serialized file data - /// - /// The desired file episode - IEnumerable GetBytes(Episode episode = Episode.Unknown); -} diff --git a/src/Parsec/Shaiya/Core/ISerializable.cs b/src/Parsec/Shaiya/Core/ISerializable.cs new file mode 100644 index 00000000..c856b8f3 --- /dev/null +++ b/src/Parsec/Shaiya/Core/ISerializable.cs @@ -0,0 +1,10 @@ +using Parsec.Serialization; + +namespace Parsec.Shaiya.Core; + +public interface ISerializable +{ + void Read(SBinaryReader binaryReader); + + void Write(SBinaryWriter binaryWriter); +} diff --git a/src/Parsec/Shaiya/Core/ReflectionHelper.cs b/src/Parsec/Shaiya/Core/ReflectionHelper.cs deleted file mode 100644 index 47881891..00000000 --- a/src/Parsec/Shaiya/Core/ReflectionHelper.cs +++ /dev/null @@ -1,409 +0,0 @@ -using System.Reflection; -using System.Text; -using Parsec.Attributes; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; - -namespace Parsec.Shaiya.Core; - -internal static class ReflectionHelper -{ - public static object ReadProperty( - SBinaryReader binaryReader, - Type parentType, - object parentInstance, - PropertyInfo propertyInfo, - Episode episode = Episode.Unknown, - Encoding encoding = null - ) - { - encoding ??= Encoding.ASCII; - var type = propertyInfo.PropertyType; - - var attributes = propertyInfo.GetCustomAttributes().ToList(); - - // If property isn't marked as a ShaiyaProperty, it must be skipped - if (!attributes.Exists(a => a.GetType() == typeof(ShaiyaPropertyAttribute))) - return null; - - foreach (var attribute in attributes) - { - switch (attribute) - { - case ShaiyaPropertyAttribute shaiyaPropertyAttribute: - // If episode limits weren't specified, then property must be read - if (shaiyaPropertyAttribute.MinEpisode == Episode.Unknown && shaiyaPropertyAttribute.MaxEpisode == Episode.Unknown) - break; - - if (shaiyaPropertyAttribute.MaxEpisode == Episode.Unknown && episode >= shaiyaPropertyAttribute.MinEpisode) - break; - - if (shaiyaPropertyAttribute.MinEpisode == Episode.Unknown && episode < shaiyaPropertyAttribute.MaxEpisode) - break; - - if (episode >= shaiyaPropertyAttribute.MinEpisode && episode <= shaiyaPropertyAttribute.MaxEpisode) - break; - - return null; - - case ConditionalPropertyAttribute conditionalPropertyAttribute: - var conditioningPropertyType = parentType.GetProperty(conditionalPropertyAttribute.ConditioningPropertyName); - object conditioningPropertyValue = conditioningPropertyType?.GetValue(parentInstance); - - if (!conditionalPropertyAttribute.ConditioningPropertyValue.Equals(conditioningPropertyValue)) - return GetDefault(type); - break; - - case FixedLengthListAttribute fixedLengthListAttribute: - // Create generic list - var fixedLengthListType = typeof(List<>); - var constructedFixedListType = fixedLengthListType.MakeGenericType(fixedLengthListAttribute.ItemType); - var itemList = (IList)Activator.CreateInstance(constructedFixedListType); - - var typeProperties = fixedLengthListAttribute.ItemType.GetProperties(); - var genericArgumentType = type.GetGenericArguments().First(); - - for (int i = 0; i < fixedLengthListAttribute.Length; i++) - { - object item = Activator.CreateInstance(fixedLengthListAttribute.ItemType); - - if (genericArgumentType.IsPrimitive) - { - item = ReadPrimitive(binaryReader, genericArgumentType); - } - else - { - foreach (var property in typeProperties) - { - // skip non ShaiyaProperty properties - if (!property.IsDefined(typeof(ShaiyaPropertyAttribute))) - continue; - - object propertyValue = ReadProperty(binaryReader, genericArgumentType, item, property, episode, encoding); - property.SetValue(item, propertyValue); - } - } - - itemList.Add(item); - } - - return itemList; - - case LengthPrefixedListAttribute lengthPrefixedListAttribute: - int length = 0; - - var lengthType = lengthPrefixedListAttribute.LengthType; - - if (lengthType == typeof(int)) - { - length = binaryReader.Read(); - } - else if (lengthType == typeof(uint)) - { - length = (int)binaryReader.Read(); - } - else if (lengthType == typeof(short)) - { - length = binaryReader.Read(); - } - else if (lengthType == typeof(ushort)) - { - length = binaryReader.Read(); - } - else if (lengthType == typeof(byte)) - { - length = binaryReader.Read(); - } - else if (lengthType == typeof(sbyte)) - { - length = binaryReader.Read(); - } - else - { - throw new NotSupportedException(); - } - - // Apply multiplier - var multiplierAttribute = attributes.FirstOrDefault(x => - x is ListLengthMultiplierAttribute multiplierAttribute && multiplierAttribute.Episode == episode); - if (multiplierAttribute is ListLengthMultiplierAttribute mAttr) - length *= mAttr.Multiplier; - - // Create generic list - var listType = typeof(List<>); - var constructedListType = listType.MakeGenericType(lengthPrefixedListAttribute.ItemType); - var list = (IList)Activator.CreateInstance(constructedListType); - - var genericItemType = lengthPrefixedListAttribute.ItemType; - var properties = genericItemType.GetProperties(); - - for (int i = 0; i < length; i++) - { - object item = Activator.CreateInstance(lengthPrefixedListAttribute.ItemType); - - if (genericItemType.IsPrimitive) - { - item = ReadPrimitive(binaryReader, genericItemType); - } - else - { - foreach (var property in properties) - { - // skip non ShaiyaProperty properties - if (!property.IsDefined(typeof(ShaiyaPropertyAttribute))) - continue; - - object propertyValue = ReadProperty(binaryReader, genericItemType, item, property, episode, encoding); - property.SetValue(item, propertyValue); - } - } - - list.Add(item); - } - - return list; - - case LengthPrefixedStringAttribute lengthPrefixedStringAttribute: - string lengthPrefixedStr = binaryReader.ReadString(encoding, !lengthPrefixedStringAttribute.IncludeStringTerminator); - return lengthPrefixedStr; - - case FixedLengthStringAttribute fixedLengthStringAttribute: - if (fixedLengthStringAttribute.IsString256) - return new String256(binaryReader).Value; - - string fixedLengthStr = binaryReader.ReadString(encoding, fixedLengthStringAttribute.Length, - !fixedLengthStringAttribute.IncludeStringTerminator); - - return fixedLengthStr; - } - } - - // If property implements IBinary, the IBinary must be instantiated through its single parameter constructor with takes the SBinaryReader instance - // this is the case for types Vector, Quaternion, Matrix, BoundingBox, etc. - if (type.GetInterfaces().Contains(typeof(IBinary))) - { - var binary = (IBinary)Activator.CreateInstance(type, binaryReader); - return binary; - } - - if (type.IsEnum) - { - var underlyingEnumType = Enum.GetUnderlyingType(type); - return ReadPrimitive(binaryReader, underlyingEnumType); - } - - return ReadPrimitive(binaryReader, type); - } - - public static object ReadPrimitive(SBinaryReader binaryReader, Type type) => binaryReader.Read(type); - - public static IEnumerable GetPropertyBytes(Type parentType, object obj, PropertyInfo propertyInfo, Encoding encoding, - Episode episode = Episode.Unknown) - { - var type = propertyInfo.PropertyType; - var attributes = propertyInfo.GetCustomAttributes().ToList(); - - // If property isn't marked as a ShaiyaAttribute, it must be skipped - if (!attributes.Exists(a => a.GetType() == typeof(ShaiyaPropertyAttribute))) - return Array.Empty(); - - object propertyValue = propertyInfo.GetValue(obj); - - foreach (var attribute in attributes) - { - switch (attribute) - { - case ShaiyaPropertyAttribute shaiyaProperty: - var ep = episode; - - // FileBase instances include the Episode property - if (obj is FileBase fileBase) - { - if (episode == Episode.Unknown) - ep = fileBase.Episode; - } - - // if format can't be determined, nothing else should be done here - if (ep == Episode.Unknown) - break; - - // if the ShaiyaProperty didn't specify an episode, then it's present in all of them - if (shaiyaProperty.MinEpisode == Episode.Unknown && shaiyaProperty.MaxEpisode == Episode.Unknown) - break; - - // single episode checks - if (shaiyaProperty.MaxEpisode == Episode.Unknown && ep >= shaiyaProperty.MinEpisode) - break; - - if (shaiyaProperty.MinEpisode == Episode.Unknown && ep <= shaiyaProperty.MaxEpisode) - break; - - // multiple episode check - if (ep >= shaiyaProperty.MinEpisode && ep <= shaiyaProperty.MaxEpisode) - break; - - return Array.Empty(); - - case ConditionalPropertyAttribute conditionalPropertyAttribute: - var conditioningPropertyType = parentType.GetProperty(conditionalPropertyAttribute.ConditioningPropertyName); - object conditioningPropertyValue = conditioningPropertyType?.GetValue(obj); - - if (!conditionalPropertyAttribute.ConditioningPropertyValue.Equals(conditioningPropertyValue)) - return Array.Empty(); - - break; - - case FixedLengthListAttribute fixedLengthListAttribute: - var listItems = (propertyValue as IEnumerable).Cast().Take(fixedLengthListAttribute.Length); - - var buf = new List(); - - var genericArgumentType = type.GetGenericArguments().First(); - - foreach (object item in listItems) - { - if (genericArgumentType.IsPrimitive) - { - buf.AddRange(GetPrimitiveBytes(genericArgumentType, item)); - } - else - { - foreach (var property in fixedLengthListAttribute.ItemType.GetProperties()) - { - if (!property.IsDefined(typeof(ShaiyaPropertyAttribute)) && !fixedLengthListAttribute.ItemType.IsPrimitive) - continue; - - buf.AddRange(GetPropertyBytes(genericArgumentType, item, property, encoding, episode)); - } - } - } - - return buf.ToArray(); - - case LengthPrefixedListAttribute lengthPrefixedListAttribute: - var lengthType = lengthPrefixedListAttribute.LengthType; - var items = propertyValue as IEnumerable; - int itemCount = items.Cast().Count(); - - // Restore length if it had been multiplied previously - var multiplierAttribute = attributes.FirstOrDefault(x => - x is ListLengthMultiplierAttribute multiplierAttribute && multiplierAttribute.Episode == episode); - if (multiplierAttribute is ListLengthMultiplierAttribute mAttr) - itemCount /= mAttr.Multiplier; - - - var genericItemType = type.GetGenericArguments().First(); - var buffer = new List(); - - if (lengthType == typeof(int)) - { - buffer.AddRange(itemCount.GetBytes()); - } - else if (lengthType == typeof(short)) - { - buffer.AddRange(((short)itemCount).GetBytes()); - } - else if (lengthType == typeof(byte)) - { - buffer.Add((byte)itemCount); - } - else - { - // only int, short and byte lengths are expected - throw new NotSupportedException(); - } - - foreach (object item in items) - { - if (genericItemType.IsPrimitive) - { - buffer.AddRange(GetPrimitiveBytes(genericItemType, item)); - } - else - { - foreach (var property in lengthPrefixedListAttribute.ItemType.GetProperties()) - { - if (!property.IsDefined(typeof(ShaiyaPropertyAttribute)) && - !lengthPrefixedListAttribute.ItemType.IsPrimitive) - continue; - - buffer.AddRange(GetPropertyBytes(genericItemType, item, property, encoding, episode)); - } - } - } - - return buffer; - - case LengthPrefixedStringAttribute lengthPrefixedStringAttribute: - return ((string)propertyValue + lengthPrefixedStringAttribute.Suffix).GetLengthPrefixedBytes(encoding, - lengthPrefixedStringAttribute.IncludeStringTerminator); - - case FixedLengthStringAttribute fixedLengthStringAttribute: - if (fixedLengthStringAttribute.IsString256) - return ((string)propertyValue).PadRight(256, '\0').GetBytes(encoding); - return ((string)propertyValue + fixedLengthStringAttribute.Suffix) - .PadRight(fixedLengthStringAttribute.Length, '\0').GetBytes(encoding); - } - } - - // If property implements IBinary, bytes can be retrieved by calling GetBytes() - // this is the case for types Vector, Quaternion, Matrix, BoundingBox, etc. - if (type.GetInterfaces().Contains(typeof(IBinary))) - return ((IBinary)propertyValue).GetBytes(); - - if (type.IsEnum) - { - var underlyingEnumType = Enum.GetUnderlyingType(type); - return GetPrimitiveBytes(underlyingEnumType, propertyValue); - } - - return GetPrimitiveBytes(type, propertyValue); - } - - public static IEnumerable GetPrimitiveBytes(Type type, object value) - { - if (type == typeof(byte)) - return new[] { (byte)value }; - - if (type == typeof(bool)) - return new[] { (byte)value }; - - if (type == typeof(int)) - return ((int)value).GetBytes(); - - if (type == typeof(uint)) - return ((uint)value).GetBytes(); - - if (type == typeof(short)) - return ((short)value).GetBytes(); - - if (type == typeof(ushort)) - return ((ushort)value).GetBytes(); - - if (type == typeof(long)) - return ((long)value).GetBytes(); - - if (type == typeof(ulong)) - return ((ulong)value).GetBytes(); - - if (type == typeof(float)) - return ((float)value).GetBytes(); - - if (type == typeof(byte[])) - return (byte[])value; - - throw new ArgumentException(); - } - - private static object GetDefault(Type type) - { - if (type.IsValueType) - { - return Activator.CreateInstance(type); - } - - return null; - } -} diff --git a/src/Parsec/Shaiya/Data/Data.cs b/src/Parsec/Shaiya/Data/Data.cs index 62fb2cbe..10aa2881 100644 --- a/src/Parsec/Shaiya/Data/Data.cs +++ b/src/Parsec/Shaiya/Data/Data.cs @@ -10,37 +10,20 @@ public Data(Sah sah, Saf saf) Saf = saf; } - public Data(string path) + public Data(string sahPath, string safPath) { - if (!FileHelper.FileExists(path)) - throw new FileNotFoundException($"Data file not found at {path}"); - - switch (Path.GetExtension(path)) + if (!FileHelper.FileExists(sahPath)) { - case ".sah": - { - Sah = Reader.ReadFromFile(path); - - if (!FileHelper.FileExists(Sah.SafPath)) - throw new FileNotFoundException("A valid saf file must be placed in the same directory as the sah file."); - - Saf = new Saf(Sah.SafPath); - break; - } - case ".saf": - { - Saf = new Saf(path); - - if (!FileHelper.FileExists(Saf.SahPath)) - throw new FileNotFoundException("A valid sah file must be placed in the same directory as the saf file."); - - Sah = Reader.ReadFromFile(Saf.SahPath); + throw new FileNotFoundException($"data.sah file not found at {sahPath}"); + } - break; - } - default: - throw new ArgumentException("The provided path must belong to either a .sah or a .saf file"); + if (!FileHelper.FileExists(safPath)) + { + throw new FileNotFoundException($"data.saf file not found at {safPath}"); } + + Sah = ParsecReader.FromFile(sahPath); + Saf = new Saf(safPath); } /// @@ -81,7 +64,7 @@ public int FileCount /// Gets a data folder from the FolderIndex /// /// Folder path - public SDirectory GetFolder(string path) + public SDirectory? GetFolder(string path) { DirectoryIndex.TryGetValue(path, out var folder); return folder; @@ -91,7 +74,7 @@ public SDirectory GetFolder(string path) /// Gets a data file from the FileIndex /// /// File path - public SFile GetFile(string path) + public SFile? GetFile(string path) { FileIndex.TryGetValue(path, out var file); return file; diff --git a/src/Parsec/Shaiya/Data/DataPatcher.cs b/src/Parsec/Shaiya/Data/DataPatcher.cs index 8aa1d349..2f19a620 100644 --- a/src/Parsec/Shaiya/Data/DataPatcher.cs +++ b/src/Parsec/Shaiya/Data/DataPatcher.cs @@ -4,8 +4,8 @@ namespace Parsec.Shaiya.Data; public class DataPatcher : IDisposable { - private BinaryReader _patchBinaryReader; - private BinaryWriter _targetBinaryWriter; + private BinaryReader? _patchBinaryReader; + private BinaryWriter? _targetBinaryWriter; public void Dispose() { @@ -19,7 +19,7 @@ public void Dispose() /// Data instance where the patch should be applied /// Data instance containing the patch files /// Action which gets invoked when a file gets patched - public void Patch(Data targetData, Data patchData, Action filePatchedCallback = null) + public void Patch(Data targetData, Data patchData, Action? filePatchedCallback = null) { _targetBinaryWriter = new BinaryWriter(File.OpenWrite(targetData.Saf.Path)); _patchBinaryReader = new BinaryReader(File.OpenRead(patchData.Saf.Path)); @@ -39,7 +39,7 @@ public void Patch(Data targetData, Data patchData, Action filePatchedCallback = /// Data where to save the files /// Data where to take the files from /// Action which gets invoked when a file gets patched - private void PatchFiles(Data targetData, Data patchData, Action filePatchedCallback = null) + private void PatchFiles(Data targetData, Data patchData, Action? filePatchedCallback = null) { foreach (var patchFile in patchData.FileIndex.Values) { @@ -89,10 +89,10 @@ private void PatchFiles(Data targetData, Data patchData, Action filePatchedCallb /// Amount of bytes to set to 0 private void ClearBytes(long offset, int length) { - _targetBinaryWriter.BaseStream.Seek(offset, SeekOrigin.Begin); + _targetBinaryWriter!.BaseStream.Seek(offset, SeekOrigin.Begin); var emptyData = new byte[length]; - _targetBinaryWriter.Write(emptyData); + _targetBinaryWriter!.Write(emptyData); } /// @@ -102,11 +102,11 @@ private void ClearBytes(long offset, int length) /// Patch file instance private long WriteFile(long targetOffset, SFile patchFile) { - _patchBinaryReader.BaseStream.Seek(patchFile.Offset, SeekOrigin.Begin); + _patchBinaryReader!.BaseStream.Seek(patchFile.Offset, SeekOrigin.Begin); var patchBuffer = _patchBinaryReader.ReadBytes(patchFile.Length); - _targetBinaryWriter.BaseStream.Seek(targetOffset, SeekOrigin.Begin); + _targetBinaryWriter!.BaseStream.Seek(targetOffset, SeekOrigin.Begin); _targetBinaryWriter.Write(patchBuffer); return targetOffset; @@ -116,5 +116,8 @@ private long WriteFile(long targetOffset, SFile patchFile) /// Appends a file at the end of the target Saf file from a patch Saf file /// /// Patch file instance - private long AppendFile(SFile patchFile) => WriteFile(_targetBinaryWriter.BaseStream.Length, patchFile); + private long AppendFile(SFile patchFile) + { + return WriteFile(_targetBinaryWriter!.BaseStream.Length, patchFile); + } } diff --git a/src/Parsec/Shaiya/Data/SDirectory.cs b/src/Parsec/Shaiya/Data/SDirectory.cs index 71a51b98..e5387bca 100644 --- a/src/Parsec/Shaiya/Data/SDirectory.cs +++ b/src/Parsec/Shaiya/Data/SDirectory.cs @@ -1,13 +1,11 @@ using System.Runtime.Serialization; using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; +using Parsec.Serialization; namespace Parsec.Shaiya.Data; [DataContract] -public sealed class SDirectory : IBinary +public sealed class SDirectory { [JsonConstructor] public SDirectory() @@ -21,7 +19,7 @@ public SDirectory(SDirectory parentDirectory) public SDirectory( SBinaryReader binaryReader, - SDirectory parentDirectory, + SDirectory? parentDirectory, Dictionary directoryIndex, Dictionary fileIndex ) @@ -39,19 +37,17 @@ Dictionary fileIndex directoryIndex.Add(RelativePath, this); - int fileCount = binaryReader.Read(); - - // Read all files in this folder - for (int i = 0; i < fileCount; i++) + var fileCount = binaryReader.ReadInt32(); + for (var i = 0; i < fileCount; i++) { var file = new SFile(binaryReader, this, fileIndex); AddFile(file); } - int subfolderCount = binaryReader.Read(); + var subfolderCount = binaryReader.ReadInt32(); - // Recursively read subfolders data - for (int i = 0; i < subfolderCount; i++) + // Recursively read subfolder data + for (var i = 0; i < subfolderCount; i++) { var subfolder = new SDirectory(binaryReader, this, directoryIndex, fileIndex); AddDirectory(subfolder); @@ -93,16 +89,30 @@ public SDirectory(string name, SDirectory parentDirectory) : this(parentDirector /// /// The folder's parent directory /// - public SDirectory ParentDirectory { get; set; } + public SDirectory? ParentDirectory { get; set; } - /// - public IEnumerable GetBytes(params object[] options) + public void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(ParentDirectory == null ? RealName.GetLengthPrefixedBytes() : Name.GetLengthPrefixedBytes()); - buffer.AddRange(Files.GetBytes()); - buffer.AddRange(Directories.GetBytes()); - return buffer; + if (ParentDirectory == null) + { + binaryWriter.Write(RealName); + } + else + { + binaryWriter.Write(Name); + } + + binaryWriter.Write(Files.Count); + foreach (var file in Files) + { + file.Write(binaryWriter); + } + + binaryWriter.Write(Directories.Count); + foreach (var directory in Directories) + { + directory.Write(binaryWriter); + } } /// @@ -113,7 +123,9 @@ public IEnumerable GetBytes(params object[] options) public void AddFile(SFile file) { if (HasFile(file.Name)) - throw new Exception($"File {file.Name} already exists in folder {Name}"); + { + return; + } Files.Add(file); } @@ -147,11 +159,11 @@ public void AddDirectory(SDirectory childDirectory) /// Gets a folder's file /// /// File name - public SFile GetFile(string name) => Files.FirstOrDefault(f => f.Name == name); + public SFile? GetFile(string name) => Files.FirstOrDefault(f => f.Name == name); /// /// Gets a folder's subfolder /// /// Subfolder name - public SDirectory GetSubfolder(string name) => Directories.FirstOrDefault(sf => sf.Name == name); + public SDirectory? GetSubfolder(string name) => Directories.FirstOrDefault(sf => sf.Name == name); } diff --git a/src/Parsec/Shaiya/Data/SFile.cs b/src/Parsec/Shaiya/Data/SFile.cs index 93f51b18..2a94b286 100644 --- a/src/Parsec/Shaiya/Data/SFile.cs +++ b/src/Parsec/Shaiya/Data/SFile.cs @@ -1,13 +1,11 @@ using System.Runtime.Serialization; using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; +using Parsec.Serialization; namespace Parsec.Shaiya.Data; [DataContract] -public sealed class SFile : IBinary +public sealed class SFile { [JsonConstructor] public SFile() @@ -29,9 +27,9 @@ public SFile(string name, long offset, int length) public SFile(SBinaryReader binaryReader, SDirectory directory, Dictionary fileIndex) : this(directory) { Name = binaryReader.ReadString(); - Offset = binaryReader.Read(); - Length = binaryReader.Read(); - Version = binaryReader.Read(); + Offset = binaryReader.ReadInt64(); + Length = binaryReader.ReadInt32(); + Version = binaryReader.ReadInt32(); if (!fileIndex.ContainsKey(RelativePath)) fileIndex.Add(RelativePath, this); @@ -41,7 +39,7 @@ public SFile(SBinaryReader binaryReader, SDirectory directory, Dictionary [DataMember] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; /// /// The offset in data.saf where the file is located @@ -64,23 +62,18 @@ public SFile(SBinaryReader binaryReader, SDirectory directory, Dictionary /// The relative path to the file /// - public string RelativePath => ParentDirectory == null || string.IsNullOrEmpty(ParentDirectory.Name) - ? Name - : Path.Combine(ParentDirectory.RelativePath, Name); + public string RelativePath => string.IsNullOrEmpty(ParentDirectory.Name) ? Name : Path.Combine(ParentDirectory.RelativePath, Name); /// /// The directory in which the file is /// - public SDirectory ParentDirectory { get; set; } + public SDirectory ParentDirectory { get; set; } = null!; - /// - public IEnumerable GetBytes(params object[] options) + public void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Name.GetLengthPrefixedBytes()); - buffer.AddRange(Offset.GetBytes()); - buffer.AddRange(Length.GetBytes()); - buffer.AddRange(Version.GetBytes()); - return buffer; + binaryWriter.Write(Name); + binaryWriter.Write(Offset); + binaryWriter.Write(Length); + binaryWriter.Write(Version); } } diff --git a/src/Parsec/Shaiya/Data/Saf.cs b/src/Parsec/Shaiya/Data/Saf.cs index d767ebef..80b7253e 100644 --- a/src/Parsec/Shaiya/Data/Saf.cs +++ b/src/Parsec/Shaiya/Data/Saf.cs @@ -12,11 +12,6 @@ public Saf(string path) /// public string Path { get; set; } - /// - /// Absolute path to the saf file linked to this saf - /// - public string SahPath => string.Concat(Path.Substring(0, Path.Length - 3), "sah"); - /// /// Reads an array of bytes from the saf file /// @@ -27,7 +22,7 @@ public byte[] ReadBytes(long offset, int length) using var safReader = new BinaryReader(File.OpenRead(Path)); safReader.BaseStream.Seek(offset, SeekOrigin.Begin); - byte[] bytes = safReader.ReadBytes(length); + var bytes = safReader.ReadBytes(length); return bytes; } diff --git a/src/Parsec/Shaiya/Data/Sah.cs b/src/Parsec/Shaiya/Data/Sah.cs index e59aa996..9e80468c 100644 --- a/src/Parsec/Shaiya/Data/Sah.cs +++ b/src/Parsec/Shaiya/Data/Sah.cs @@ -1,7 +1,7 @@ using System.Runtime.Serialization; using Newtonsoft.Json; -using Parsec.Common; using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Data; @@ -47,11 +47,6 @@ public Sah(string path, SDirectory rootDirectory, int fileCount) /// public int Version { get; set; } - /// - /// Path to the saf file linked to this sah - /// - public string SafPath => string.Concat(Path.Substring(0, Path.Length - 3), "saf"); - /// /// Total amount of files that are present in the data; does not include directories. /// @@ -68,16 +63,16 @@ public Sah(string path, SDirectory rootDirectory, int fileCount) public override string Extension => "sah"; /// - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - Signature = _binaryReader.ReadString(3); - Version = _binaryReader.Read(); - FileCount = _binaryReader.Read(); + Signature = binaryReader.ReadString(3); + Version = binaryReader.ReadInt32(); + FileCount = binaryReader.ReadInt32(); // Index where data starts (after header - skip padding bytes) - _binaryReader.Skip(40); + binaryReader.Skip(40); - RootDirectory = new SDirectory(_binaryReader, null, DirectoryIndex, FileIndex); + RootDirectory = new SDirectory(binaryReader, null, DirectoryIndex, FileIndex); } /// @@ -112,7 +107,7 @@ public SDirectory EnsureFolderExists(string path) var pathFolders = path.Separate().ToList(); var currentFolder = RootDirectory; - foreach (string folderName in pathFolders) + foreach (var folderName in pathFolders) { if (!currentFolder.HasSubfolder(folderName)) { @@ -143,17 +138,16 @@ public SDirectory EnsureFolderExists(string path) /// File's relative path (ie. "Character/Human/3DC/model.3DC") public bool HasFile(string relativePath) => FileIndex.ContainsKey(relativePath); - /// - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Signature.GetBytes()); - buffer.AddRange(Version.GetBytes()); - buffer.AddRange(FileCount.GetBytes()); - buffer.AddRange(new byte[40]); // Padding - buffer.AddRange(RootDirectory.GetBytes()); + binaryWriter.Write(Signature.Take(3).ToString()); + binaryWriter.Write(Version); + binaryWriter.Write(FileCount); + binaryWriter.Write(new byte[40]); // Padding + + RootDirectory.Write(binaryWriter); + // Suffix with 8 empty bytes - I don't think the game cares about these at all, but some other tools do - buffer.AddRange(new byte[8]); - return buffer; + binaryWriter.Write(new byte[8]); } } diff --git a/src/Parsec/Shaiya/DualLayerClothes/DBDualLayerClothesDataRecord.cs b/src/Parsec/Shaiya/DualLayerClothes/DBDualLayerClothesDataRecord.cs index 3e31bf3e..917504a5 100644 --- a/src/Parsec/Shaiya/DualLayerClothes/DBDualLayerClothesDataRecord.cs +++ b/src/Parsec/Shaiya/DualLayerClothes/DBDualLayerClothesDataRecord.cs @@ -1,28 +1,44 @@ -using Parsec.Attributes; + +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.DualLayerClothes; public sealed class DBDualLayerClothesDataRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] public long Top { get; set; } - [ShaiyaProperty] public long Hand { get; set; } - [ShaiyaProperty] public long Bottom { get; set; } - [ShaiyaProperty] public long Shoe { get; set; } - [ShaiyaProperty] public long Empty { get; set; } - [ShaiyaProperty] public long Helmet { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + Top = binaryReader.ReadInt64(); + Hand = binaryReader.ReadInt64(); + Bottom = binaryReader.ReadInt64(); + Shoe = binaryReader.ReadInt64(); + Empty = binaryReader.ReadInt64(); + Helmet = binaryReader.ReadInt64(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(Top); + binaryWriter.Write(Hand); + binaryWriter.Write(Bottom); + binaryWriter.Write(Shoe); + binaryWriter.Write(Empty); + binaryWriter.Write(Helmet); + } } diff --git a/src/Parsec/Shaiya/DualLayerClothes/DualLayerClothes.cs b/src/Parsec/Shaiya/DualLayerClothes/DualLayerClothes.cs index 6b33fe6d..22c5b8a8 100644 --- a/src/Parsec/Shaiya/DualLayerClothes/DualLayerClothes.cs +++ b/src/Parsec/Shaiya/DualLayerClothes/DualLayerClothes.cs @@ -1,12 +1,19 @@ -using Parsec.Attributes; -using Parsec.Common; +using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.DualLayerClothes; -[DefaultVersion(Episode.EP6)] public sealed class DualLayerClothes : SData.SData { - [ShaiyaProperty] - [LengthPrefixedList(typeof(DualLayerClothesRecord))] public List Records { get; set; } = new(); + + protected override void Read(SBinaryReader binaryReader) + { + Records = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Records.ToSerializable()); + } } diff --git a/src/Parsec/Shaiya/DualLayerClothes/DualLayerClothesRecord.cs b/src/Parsec/Shaiya/DualLayerClothes/DualLayerClothesRecord.cs index b1a1b65e..3899c32a 100644 --- a/src/Parsec/Shaiya/DualLayerClothes/DualLayerClothesRecord.cs +++ b/src/Parsec/Shaiya/DualLayerClothes/DualLayerClothesRecord.cs @@ -1,27 +1,43 @@ -using Parsec.Attributes; +using Parsec.Serialization; +using Parsec.Shaiya.Core; namespace Parsec.Shaiya.DualLayerClothes; -public sealed class DualLayerClothesRecord +public sealed class DualLayerClothesRecord : ISerializable { - [ShaiyaProperty] - public short Index { get; set; } + public ushort Index { get; set; } - [ShaiyaProperty] - public short Upper { get; set; } + public ushort Upper { get; set; } - [ShaiyaProperty] - public short Hands { get; set; } + public ushort Hands { get; set; } - [ShaiyaProperty] - public short Lower { get; set; } + public ushort Lower { get; set; } - [ShaiyaProperty] - public short Feet { get; set; } + public ushort Feet { get; set; } - [ShaiyaProperty] - public short Face { get; set; } + public ushort Face { get; set; } - [ShaiyaProperty] - public short Head { get; set; } + public ushort Head { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Index = binaryReader.ReadUInt16(); + Upper = binaryReader.ReadUInt16(); + Hands = binaryReader.ReadUInt16(); + Lower = binaryReader.ReadUInt16(); + Feet = binaryReader.ReadUInt16(); + Face = binaryReader.ReadUInt16(); + Head = binaryReader.ReadUInt16(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Index); + binaryWriter.Write(Upper); + binaryWriter.Write(Hands); + binaryWriter.Write(Lower); + binaryWriter.Write(Feet); + binaryWriter.Write(Face); + binaryWriter.Write(Head); + } } diff --git a/src/Parsec/Shaiya/EFT/EFT.cs b/src/Parsec/Shaiya/EFT/EFT.cs deleted file mode 100644 index 028d1ea5..00000000 --- a/src/Parsec/Shaiya/EFT/EFT.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class EFT : FileBase -{ - [JsonIgnore] - public EFTFormat Format { get; set; } - - public List Objects { get; } = new(); - public List Textures { get; } = new(); - public List Effects { get; } = new(); - public List EffectSequences { get; } = new(); - - [JsonIgnore] - public override string Extension => "EFT"; - - public override void Read() - { - string signature = _binaryReader.ReadString(3); - - Format = signature switch - { - "EFT" => EFTFormat.EFT, - "EF2" => EFTFormat.EF2, - "EF3" => EFTFormat.EF3, - _ => EFTFormat.Unknown - }; - - int effectObjectCount = _binaryReader.Read(); - for (int i = 0; i < effectObjectCount; i++) - Objects.Add(new EffectObject(_binaryReader, i)); - - int textureCount = _binaryReader.Read(); - for (int i = 0; i < textureCount; i++) - Textures.Add(new EFTTexture(_binaryReader, i)); - - int effectCount = _binaryReader.Read(); - for (int i = 0; i < effectCount; i++) - Effects.Add(new Effect(_binaryReader, Format, i)); - - int sequenceCount = _binaryReader.Read(); - for (int i = 0; i < sequenceCount; i++) - EffectSequences.Add(new EffectSequence(_binaryReader)); - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Format.ToString().GetBytes()); - buffer.AddRange(Objects.GetBytes()); - buffer.AddRange(Textures.GetBytes()); - - buffer.AddRange(Effects.Count.GetBytes()); - foreach (var effect in Effects) - buffer.AddRange(effect.GetBytes(Format)); - - buffer.AddRange(EffectSequences.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/EFT/EFTFormat.cs b/src/Parsec/Shaiya/EFT/EFTFormat.cs deleted file mode 100644 index 9466416d..00000000 --- a/src/Parsec/Shaiya/EFT/EFTFormat.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Parsec.Shaiya.EFT; - -public enum EFTFormat -{ - EFT, - EF2, - EF3, - Unknown -} diff --git a/src/Parsec/Shaiya/EFT/EFTTexture.cs b/src/Parsec/Shaiya/EFT/EFTTexture.cs deleted file mode 100644 index 3d7b65ef..00000000 --- a/src/Parsec/Shaiya/EFT/EFTTexture.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class EFTTexture : IBinary -{ - [JsonConstructor] - public EFTTexture() - { - } - - public EFTTexture(SBinaryReader binaryReader, int index) - { - Index = index; - Name = binaryReader.ReadString(); - } - - public int Index { get; set; } - public string Name { get; set; } - - public IEnumerable GetBytes(params object[] options) => Name.GetLengthPrefixedBytes(); -} diff --git a/src/Parsec/Shaiya/EFT/Effect.cs b/src/Parsec/Shaiya/EFT/Effect.cs deleted file mode 100644 index 168b9e91..00000000 --- a/src/Parsec/Shaiya/EFT/Effect.cs +++ /dev/null @@ -1,231 +0,0 @@ -using System.Text; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT -{ - public class Effect : IBinary - { - /// - /// Not part of the structure, but left here for readability purposes - /// - public int Index { get; set; } - public string Name { get; set; } - public int Unknown1 { get; set; } - public int Unknown2 { get; set; } - public int Unknown3 { get; set; } - public int Unknown4 { get; set; } - public int Unknown5 { get; set; } - public int Unknown6 { get; set; } - public int Unknown7 { get; set; } - public int Unknown8 { get; set; } - public int Object3DEId { get; set; } - public int Unknown10 { get; set; } - public float Unknown11 { get; set; } - public float Unknown12 { get; set; } - public float Unknown13 { get; set; } - public float Unknown14 { get; set; } - public float Unknown15 { get; set; } - public float Unknown16 { get; set; } - public float Unknown17 { get; set; } - public float Unknown18 { get; set; } - - public Vector3 UnknownVec1 { get; set; } - public Vector3 UnknownVec2 { get; set; } - - /// - /// The position where the effect should be rendered, relative to the effect's origin. - /// In the case of mob effects, the origin is the bone to which the effect is attached to. - /// - public Vector3 Position { get; set; } - public Vector3 UnknownVec4 { get; set; } - public Vector3 UnknownVec5 { get; set; } - - public int Unknown19 { get; set; } - public int Unknown20 { get; set; } - public int Unknown21 { get; set; } - - public Vector3 UnknownVec6 { get; set; } - - public float Unknown22 { get; set; } - public int Unknown23 { get; set; } - public int Unknown24 { get; set; } - public float Unknown25 { get; set; } - public int Unknown26 { get; set; } - - /// - /// Only present in EF3 - /// - public float Unknown27 { get; set; } - - /// - /// Only present in EF3 - /// - public float Unknown28 { get; set; } - - public List Rotations { get; } = new(); - public List OpacityFrames { get; } = new(); - public List EffectSub3 { get; } = new(); - - public int Unknown29 { get; set; } - public int Unknown30 { get; set; } - public int Unknown31 { get; set; } - public int Unknown32 { get; set; } - - public List Textures { get; } = new(); - - [JsonConstructor] - public Effect() - { - } - - public Effect(SBinaryReader binaryReader, EFTFormat format, int index) - { - Index = index; - Name = binaryReader.ReadString(Encoding.ASCII); - - Unknown1 = binaryReader.Read(); - Unknown2 = binaryReader.Read(); - Unknown3 = binaryReader.Read(); - Unknown4 = binaryReader.Read(); - Unknown5 = binaryReader.Read(); - Unknown6 = binaryReader.Read(); - Unknown7 = binaryReader.Read(); - Unknown8 = binaryReader.Read(); - Object3DEId = binaryReader.Read(); - Unknown10 = binaryReader.Read(); - - Unknown11 = binaryReader.Read(); - Unknown12 = binaryReader.Read(); - Unknown13 = binaryReader.Read(); - Unknown14 = binaryReader.Read(); - Unknown15 = binaryReader.Read(); - Unknown16 = binaryReader.Read(); - Unknown17 = binaryReader.Read(); - Unknown18 = binaryReader.Read(); - - UnknownVec1 = new Vector3(binaryReader); - UnknownVec2 = new Vector3(binaryReader); - Position = new Vector3(binaryReader); - UnknownVec4 = new Vector3(binaryReader); - UnknownVec5 = new Vector3(binaryReader); - - Unknown19 = binaryReader.Read(); - Unknown20 = binaryReader.Read(); - Unknown21 = binaryReader.Read(); - - UnknownVec6 = new Vector3(binaryReader); - - Unknown22 = binaryReader.Read(); - Unknown23 = binaryReader.Read(); - Unknown24 = binaryReader.Read(); - Unknown25 = binaryReader.Read(); - Unknown26 = binaryReader.Read(); - - if (format == EFTFormat.EF3) - { - Unknown27 = binaryReader.Read(); - Unknown28 = binaryReader.Read(); - } - - int rotationCount = binaryReader.Read(); - for (int i = 0; i < rotationCount; i++) - { - var rotation = new Rotation(binaryReader); - Rotations.Add(rotation); - } - - int sceneSub2Count = binaryReader.Read(); - for (int i = 0; i < sceneSub2Count; i++) - { - var frame = new OpacityFrame(binaryReader); - OpacityFrames.Add(frame); - } - - int sceneSub3Count = binaryReader.Read(); - for (int i = 0; i < sceneSub3Count; i++) - { - var sub3 = new EffectSub3(binaryReader); - EffectSub3.Add(sub3); - } - - Unknown29 = binaryReader.Read(); - Unknown30 = binaryReader.Read(); - Unknown31 = binaryReader.Read(); - Unknown32 = binaryReader.Read(); - - int sceneSub4Count = binaryReader.Read(); - for (int i = 0; i < sceneSub4Count; i++) - { - var texture = new EffectTexture(binaryReader); - Textures.Add(texture); - } - } - - /// - /// Expects as a parameter - /// - public IEnumerable GetBytes(params object[] options) - { - var format = EFTFormat.Unknown; - - if (options.Length > 0) - format = (EFTFormat)options[0]; - - var buffer = new List(); - buffer.AddRange(Name.GetLengthPrefixedBytes(Encoding.ASCII)); - - buffer.AddRange(Unknown1.GetBytes()); - buffer.AddRange(Unknown2.GetBytes()); - buffer.AddRange(Unknown3.GetBytes()); - buffer.AddRange(Unknown4.GetBytes()); - buffer.AddRange(Unknown5.GetBytes()); - buffer.AddRange(Unknown6.GetBytes()); - buffer.AddRange(Unknown7.GetBytes()); - buffer.AddRange(Unknown8.GetBytes()); - buffer.AddRange(Object3DEId.GetBytes()); - buffer.AddRange(Unknown10.GetBytes()); - buffer.AddRange(Unknown11.GetBytes()); - buffer.AddRange(Unknown12.GetBytes()); - buffer.AddRange(Unknown13.GetBytes()); - buffer.AddRange(Unknown14.GetBytes()); - buffer.AddRange(Unknown15.GetBytes()); - buffer.AddRange(Unknown16.GetBytes()); - buffer.AddRange(Unknown17.GetBytes()); - buffer.AddRange(Unknown18.GetBytes()); - buffer.AddRange(UnknownVec1.GetBytes()); - buffer.AddRange(UnknownVec2.GetBytes()); - buffer.AddRange(Position.GetBytes()); - buffer.AddRange(UnknownVec4.GetBytes()); - buffer.AddRange(UnknownVec5.GetBytes()); - buffer.AddRange(Unknown19.GetBytes()); - buffer.AddRange(Unknown20.GetBytes()); - buffer.AddRange(Unknown21.GetBytes()); - buffer.AddRange(UnknownVec6.GetBytes()); - buffer.AddRange(Unknown22.GetBytes()); - buffer.AddRange(Unknown23.GetBytes()); - buffer.AddRange(Unknown24.GetBytes()); - buffer.AddRange(Unknown25.GetBytes()); - buffer.AddRange(Unknown26.GetBytes()); - - if (format == EFTFormat.EF3) - { - buffer.AddRange(Unknown27.GetBytes()); - buffer.AddRange(Unknown28.GetBytes()); - } - - buffer.AddRange(Rotations.GetBytes()); - buffer.AddRange(OpacityFrames.GetBytes()); - buffer.AddRange(EffectSub3.GetBytes()); - buffer.AddRange(Unknown29.GetBytes()); - buffer.AddRange(Unknown30.GetBytes()); - buffer.AddRange(Unknown31.GetBytes()); - buffer.AddRange(Unknown32.GetBytes()); - buffer.AddRange(Textures.GetBytes()); - return buffer; - } - } -} diff --git a/src/Parsec/Shaiya/EFT/EffectObject.cs b/src/Parsec/Shaiya/EFT/EffectObject.cs deleted file mode 100644 index 50ea7508..00000000 --- a/src/Parsec/Shaiya/EFT/EffectObject.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class EffectObject : IBinary -{ - [JsonConstructor] - public EffectObject() - { - } - - public EffectObject(SBinaryReader binaryReader, int index) - { - Index = index; - Name = binaryReader.ReadString(); - } - - public int Index { get; set; } - public string Name { get; set; } - - public IEnumerable GetBytes(params object[] options) => Name.GetLengthPrefixedBytes(); -} diff --git a/src/Parsec/Shaiya/EFT/EffectSequence.cs b/src/Parsec/Shaiya/EFT/EffectSequence.cs deleted file mode 100644 index e02a4345..00000000 --- a/src/Parsec/Shaiya/EFT/EffectSequence.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Text; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class EffectSequence : IBinary -{ - [JsonConstructor] - public EffectSequence() - { - } - - public EffectSequence(SBinaryReader binaryReader) - { - Name = binaryReader.ReadString(Encoding.ASCII); - - int recordCount = binaryReader.Read(); - for (int i = 0; i < recordCount; i++) - { - var record = new SequenceRecord(binaryReader); - Records.Add(record); - } - } - - public string Name { get; set; } - public List Records { get; } = new(); - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Name.GetLengthPrefixedBytes(Encoding.ASCII)); - buffer.AddRange(Records.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/EFT/EffectSub3.cs b/src/Parsec/Shaiya/EFT/EffectSub3.cs deleted file mode 100644 index f6862e61..00000000 --- a/src/Parsec/Shaiya/EFT/EffectSub3.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class EffectSub3 : IBinary -{ - [JsonConstructor] - public EffectSub3() - { - } - - public EffectSub3(SBinaryReader binaryReader) - { - Unknown1 = binaryReader.Read(); - Unknown2 = binaryReader.Read(); - Time = binaryReader.Read(); - } - - public float Unknown1 { get; set; } - public float Unknown2 { get; set; } - public float Time { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Unknown1.GetBytes()); - buffer.AddRange(Unknown2.GetBytes()); - buffer.AddRange(Time.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/EFT/EffectSub4.cs b/src/Parsec/Shaiya/EFT/EffectSub4.cs deleted file mode 100644 index 26c505ba..00000000 --- a/src/Parsec/Shaiya/EFT/EffectSub4.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class EffectSub4 : IBinary -{ - [JsonConstructor] - public EffectSub4() - { - } - - public EffectSub4(SBinaryReader binaryReader) - { - Unknown = binaryReader.Read(); - } - - public int Unknown { get; set; } - - public IEnumerable GetBytes(params object[] options) => Unknown.GetBytes(); -} diff --git a/src/Parsec/Shaiya/EFT/EffectTexture.cs b/src/Parsec/Shaiya/EFT/EffectTexture.cs deleted file mode 100644 index 44ef6201..00000000 --- a/src/Parsec/Shaiya/EFT/EffectTexture.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class EffectTexture : IBinary -{ - [JsonConstructor] - public EffectTexture() - { - } - - public EffectTexture(SBinaryReader binaryReader) - { - TextureId = binaryReader.Read(); - } - - public int TextureId { get; set; } - - public IEnumerable GetBytes(params object[] options) => TextureId.GetBytes(); -} diff --git a/src/Parsec/Shaiya/EFT/Eft.cs b/src/Parsec/Shaiya/EFT/Eft.cs new file mode 100644 index 00000000..9f030fe6 --- /dev/null +++ b/src/Parsec/Shaiya/EFT/Eft.cs @@ -0,0 +1,64 @@ +using Newtonsoft.Json; +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class Eft : FileBase +{ + [JsonIgnore] + public EftFormat Format { get; set; } + + public List Meshes { get; set; } = new(); + + public List Textures { get; set; } = new(); + + public List Effects { get; set; } = new(); + + public List EffectSequences { get; set; } = new(); + + [JsonIgnore] + public override string Extension => "EFT"; + + protected override void Read(SBinaryReader binaryReader) + { + var signature = binaryReader.ReadString(3); + + Format = signature switch + { + "EFT" => EftFormat.EFT, + "EF2" => EftFormat.EF2, + "EF3" => EftFormat.EF3, + _ => EftFormat.Unknown + }; + + // Effect instances expect the Format to be set as the ExtraOption property on the serialization settings + binaryReader.SerializationOptions.ExtraOption = Format; + + Meshes = binaryReader.ReadList().ToList(); + Textures = binaryReader.ReadList().ToList(); + Effects = binaryReader.ReadList().ToList(); + EffectSequences = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + var signature = Format switch + { + EftFormat.EFT => "EFT", + EftFormat.EF2 => "EF2", + EftFormat.EF3 => "EF3", + _ => "EFT" + }; + + // Effect instances expect the Format to be set as the ExtraOption property on the serialization settings + binaryWriter.SerializationOptions.ExtraOption = Format; + + binaryWriter.Write(signature, isLengthPrefixed: false, includeStringTerminator: false); + binaryWriter.Write(Meshes.ToSerializable()); + binaryWriter.Write(Textures.ToSerializable()); + binaryWriter.Write(Effects.ToSerializable()); + binaryWriter.Write(EffectSequences.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftEffect.cs b/src/Parsec/Shaiya/EFT/EftEffect.cs new file mode 100644 index 00000000..8b6c7298 --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftEffect.cs @@ -0,0 +1,239 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public class EftEffect : ISerializable +{ + public string Name { get; set; } = string.Empty; + + public int Unknown1 { get; set; } + + public int Unknown2 { get; set; } + + public int Unknown3 { get; set; } + + public int Unknown4 { get; set; } + + public int Unknown5 { get; set; } + + public int Unknown6 { get; set; } + + public int Unknown7 { get; set; } + + public int Unknown8 { get; set; } + + /// + /// Index of the 3DE mesh object + /// + public int MeshIndex { get; set; } + + public int Unknown10 { get; set; } + + public float Unknown11 { get; set; } + + public float Unknown12 { get; set; } + + public float Unknown13 { get; set; } + + public float Unknown14 { get; set; } + + public float Unknown15 { get; set; } + + public float Unknown16 { get; set; } + + public float Unknown17 { get; set; } + + public float Unknown18 { get; set; } + + public Vector3 UnknownVec1 { get; set; } + + public Vector3 UnknownVec2 { get; set; } + + /// + /// The position where the effect should be rendered, relative to the effect's origin. + /// In the case of mob effects, the origin is the bone to which the effect is attached to. + /// + public Vector3 Position { get; set; } + + public Vector3 UnknownVec4 { get; set; } + + public Vector3 UnknownVec5 { get; set; } + + public int Unknown19 { get; set; } + + public int Unknown20 { get; set; } + + public int Unknown21 { get; set; } + + public Vector3 UnknownVec6 { get; set; } + + public float Unknown22 { get; set; } + + public int Unknown23 { get; set; } + + public int Unknown24 { get; set; } + + public float Unknown25 { get; set; } + + public int Unknown26 { get; set; } + + /// + /// Only present in EF3 + /// + public float Unknown27 { get; set; } + + /// + /// Only present in EF3 + /// + public float Unknown28 { get; set; } + + public List Rotations { get; set; } = new(); + + public List OpacityFrames { get; set; } = new(); + + public List EffectSub3List { get; set; } = new(); + + public int Unknown29 { get; set; } + + public int Unknown30 { get; set; } + + public int Unknown31 { get; set; } + + public int Unknown32 { get; set; } + + public List Textures { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + var format = EftFormat.EFT; + + if (binaryReader.SerializationOptions.ExtraOption is EftFormat formatOption) + { + format = formatOption; + } + + Name = binaryReader.ReadString(); + Unknown1 = binaryReader.ReadInt32(); + Unknown2 = binaryReader.ReadInt32(); + Unknown3 = binaryReader.ReadInt32(); + Unknown4 = binaryReader.ReadInt32(); + Unknown5 = binaryReader.ReadInt32(); + Unknown6 = binaryReader.ReadInt32(); + Unknown7 = binaryReader.ReadInt32(); + Unknown8 = binaryReader.ReadInt32(); + MeshIndex = binaryReader.ReadInt32(); + Unknown10 = binaryReader.ReadInt32(); + + Unknown11 = binaryReader.ReadSingle(); + Unknown12 = binaryReader.ReadSingle(); + Unknown13 = binaryReader.ReadSingle(); + Unknown14 = binaryReader.ReadSingle(); + Unknown15 = binaryReader.ReadSingle(); + Unknown16 = binaryReader.ReadSingle(); + Unknown17 = binaryReader.ReadSingle(); + Unknown18 = binaryReader.ReadSingle(); + + UnknownVec1 = binaryReader.Read(); + UnknownVec2 = binaryReader.Read(); + Position = binaryReader.Read(); + UnknownVec4 = binaryReader.Read(); + UnknownVec5 = binaryReader.Read(); + + Unknown19 = binaryReader.ReadInt32(); + Unknown20 = binaryReader.ReadInt32(); + Unknown21 = binaryReader.ReadInt32(); + + UnknownVec6 = binaryReader.Read(); + + Unknown22 = binaryReader.ReadSingle(); + Unknown23 = binaryReader.ReadInt32(); + Unknown24 = binaryReader.ReadInt32(); + Unknown25 = binaryReader.ReadSingle(); + Unknown26 = binaryReader.ReadInt32(); + + if (format == EftFormat.EF3) + { + Unknown27 = binaryReader.ReadSingle(); + Unknown28 = binaryReader.ReadSingle(); + } + + Rotations = binaryReader.ReadList().ToList(); + OpacityFrames = binaryReader.ReadList().ToList(); + EffectSub3List = binaryReader.ReadList().ToList(); + + Unknown29 = binaryReader.ReadInt32(); + Unknown30 = binaryReader.ReadInt32(); + Unknown31 = binaryReader.ReadInt32(); + Unknown32 = binaryReader.ReadInt32(); + + Textures = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + var format = EftFormat.EFT; + + if (binaryWriter.SerializationOptions.ExtraOption is EftFormat formatOption) + { + format = formatOption; + } + + binaryWriter.Write(Name); + binaryWriter.Write(Unknown1); + binaryWriter.Write(Unknown2); + binaryWriter.Write(Unknown3); + binaryWriter.Write(Unknown4); + binaryWriter.Write(Unknown5); + binaryWriter.Write(Unknown6); + binaryWriter.Write(Unknown7); + binaryWriter.Write(Unknown8); + binaryWriter.Write(MeshIndex); + binaryWriter.Write(Unknown10); + binaryWriter.Write(Unknown11); + binaryWriter.Write(Unknown12); + binaryWriter.Write(Unknown13); + binaryWriter.Write(Unknown14); + binaryWriter.Write(Unknown15); + binaryWriter.Write(Unknown16); + binaryWriter.Write(Unknown17); + binaryWriter.Write(Unknown18); + + binaryWriter.Write(UnknownVec1); + binaryWriter.Write(UnknownVec2); + binaryWriter.Write(Position); + binaryWriter.Write(UnknownVec4); + binaryWriter.Write(UnknownVec5); + + binaryWriter.Write(Unknown19); + binaryWriter.Write(Unknown20); + binaryWriter.Write(Unknown21); + + binaryWriter.Write(UnknownVec6); + + binaryWriter.Write(Unknown22); + binaryWriter.Write(Unknown23); + binaryWriter.Write(Unknown24); + binaryWriter.Write(Unknown25); + binaryWriter.Write(Unknown26); + + if (format == EftFormat.EF3) + { + binaryWriter.Write(Unknown27); + binaryWriter.Write(Unknown28); + } + + binaryWriter.Write(Rotations.ToSerializable()); + binaryWriter.Write(OpacityFrames.ToSerializable()); + binaryWriter.Write(EffectSub3List.ToSerializable()); + + binaryWriter.Write(Unknown29); + binaryWriter.Write(Unknown30); + binaryWriter.Write(Unknown31); + binaryWriter.Write(Unknown32); + + binaryWriter.Write(Textures.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftEffectMesh.cs b/src/Parsec/Shaiya/EFT/EftEffectMesh.cs new file mode 100644 index 00000000..452aaf84 --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftEffectMesh.cs @@ -0,0 +1,19 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class EftEffectMesh : ISerializable +{ + public string Name { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Name = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Name); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftEffectSequence.cs b/src/Parsec/Shaiya/EFT/EftEffectSequence.cs new file mode 100644 index 00000000..e34f61ca --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftEffectSequence.cs @@ -0,0 +1,24 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class EftEffectSequence : ISerializable +{ + public string Name { get; set; } = string.Empty; + + public List Records { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + Name = binaryReader.ReadString(); + Records = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Name); + binaryWriter.Write(Records.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftEffectSequenceRecord.cs b/src/Parsec/Shaiya/EFT/EftEffectSequenceRecord.cs new file mode 100644 index 00000000..f831541c --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftEffectSequenceRecord.cs @@ -0,0 +1,23 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class EftEffectSequenceRecord : ISerializable +{ + public int EffectId { get; set; } + + public float Time { get; set; } + + public void Read(SBinaryReader binaryReader) + { + EffectId = binaryReader.ReadInt32(); + Time = binaryReader.ReadSingle(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(EffectId); + binaryWriter.Write(Time); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftEffectSub3.cs b/src/Parsec/Shaiya/EFT/EftEffectSub3.cs new file mode 100644 index 00000000..d7e67216 --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftEffectSub3.cs @@ -0,0 +1,27 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class EftEffectSub3 : ISerializable +{ + public float Unknown1 { get; set; } + + public float Unknown2 { get; set; } + + public float Time { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Unknown1 = binaryReader.ReadSingle(); + Unknown2 = binaryReader.ReadSingle(); + Time = binaryReader.ReadSingle(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Unknown1); + binaryWriter.Write(Unknown2); + binaryWriter.Write(Time); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftEffectSub4.cs b/src/Parsec/Shaiya/EFT/EftEffectSub4.cs new file mode 100644 index 00000000..fa8dd389 --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftEffectSub4.cs @@ -0,0 +1,19 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class EftEffectSub4 : ISerializable +{ + public int Unknown { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Unknown = binaryReader.ReadInt32(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Unknown); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftEffectTexture.cs b/src/Parsec/Shaiya/EFT/EftEffectTexture.cs new file mode 100644 index 00000000..4fc9e77e --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftEffectTexture.cs @@ -0,0 +1,19 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class EftEffectTexture : ISerializable +{ + public int TextureId { get; set; } + + public void Read(SBinaryReader binaryReader) + { + TextureId = binaryReader.ReadInt32(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(TextureId); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftFormat.cs b/src/Parsec/Shaiya/EFT/EftFormat.cs new file mode 100644 index 00000000..d67dad59 --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftFormat.cs @@ -0,0 +1,9 @@ +namespace Parsec.Shaiya.Eft; + +public enum EftFormat +{ + EFT, + EF2, + EF3, + Unknown +} diff --git a/src/Parsec/Shaiya/EFT/EftOpacityFrame.cs b/src/Parsec/Shaiya/EFT/EftOpacityFrame.cs new file mode 100644 index 00000000..59abcaf2 --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftOpacityFrame.cs @@ -0,0 +1,23 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class EftOpacityFrame : ISerializable +{ + public float Opacity { get; set; } + + public float Time { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Opacity = binaryReader.ReadSingle(); + Time = binaryReader.ReadSingle(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Opacity); + binaryWriter.Write(Time); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftRotation.cs b/src/Parsec/Shaiya/EFT/EftRotation.cs new file mode 100644 index 00000000..e67c0d91 --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftRotation.cs @@ -0,0 +1,24 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class EftRotation : ISerializable +{ + public Quaternion Quaternion { get; set; } + + public float Time { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Quaternion = binaryReader.Read(); + Time = binaryReader.ReadSingle(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Quaternion); + binaryWriter.Write(Time); + } +} diff --git a/src/Parsec/Shaiya/EFT/EftTexture.cs b/src/Parsec/Shaiya/EFT/EftTexture.cs new file mode 100644 index 00000000..fe176cdb --- /dev/null +++ b/src/Parsec/Shaiya/EFT/EftTexture.cs @@ -0,0 +1,19 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Eft; + +public sealed class EftTexture : ISerializable +{ + public string Name { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Name = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Name); + } +} diff --git a/src/Parsec/Shaiya/EFT/OpacityFrame.cs b/src/Parsec/Shaiya/EFT/OpacityFrame.cs deleted file mode 100644 index accc6be1..00000000 --- a/src/Parsec/Shaiya/EFT/OpacityFrame.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class OpacityFrame : IBinary -{ - [JsonConstructor] - public OpacityFrame() - { - } - - public OpacityFrame(SBinaryReader binaryReader) - { - Opacity = binaryReader.Read(); - Time = binaryReader.Read(); - } - - public float Opacity { get; set; } - public float Time { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Opacity.GetBytes()); - buffer.AddRange(Time.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/EFT/Rotation.cs b/src/Parsec/Shaiya/EFT/Rotation.cs deleted file mode 100644 index 191ceea6..00000000 --- a/src/Parsec/Shaiya/EFT/Rotation.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class Rotation : IBinary -{ - [JsonConstructor] - public Rotation() - { - } - - public Rotation(SBinaryReader binaryReader) - { - Quaternion = new Quaternion(binaryReader); - Time = binaryReader.Read(); - } - - public Quaternion Quaternion { get; set; } - public float Time { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Quaternion.GetBytes()); - buffer.AddRange(Time.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/EFT/SequenceRecord.cs b/src/Parsec/Shaiya/EFT/SequenceRecord.cs deleted file mode 100644 index 133445a1..00000000 --- a/src/Parsec/Shaiya/EFT/SequenceRecord.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.EFT; - -public sealed class SequenceRecord : IBinary -{ - [JsonConstructor] - public SequenceRecord() - { - } - - public SequenceRecord(SBinaryReader binaryReader) - { - EffectId = binaryReader.Read(); - Time = binaryReader.Read(); - } - - public int EffectId { get; set; } - public float Time { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(EffectId.GetBytes()); - buffer.AddRange(Time.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/GuildHouse/GuildHouse.cs b/src/Parsec/Shaiya/GuildHouse/GuildHouse.cs index cfc3a676..bddd915e 100644 --- a/src/Parsec/Shaiya/GuildHouse/GuildHouse.cs +++ b/src/Parsec/Shaiya/GuildHouse/GuildHouse.cs @@ -1,5 +1,5 @@ -using Parsec.Common; -using Parsec.Extensions; +using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.GuildHouse; @@ -11,33 +11,32 @@ public sealed class GuildHouse : SData.SData public int ServicePrice { get; set; } - public List NpcInfoList { get; set; } = new(); + /// + /// Npc detail list. 36 elements + /// + public List NpcInfoList { get; set; } = new(); + /// + /// Npc Id list. 24 elements + /// public List Npcs { get; set; } = new(); /// - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - Unknown = _binaryReader.Read(); - HousePrice = _binaryReader.Read(); - ServicePrice = _binaryReader.Read(); - - for (int i = 0; i < 36; i++) - NpcInfoList.Add(new NpcInfo(_binaryReader)); - - for (int i = 0; i < 24; i++) - Npcs.Add(new GuildHouseNpc(_binaryReader)); + Unknown = binaryReader.ReadInt32(); + HousePrice = binaryReader.ReadInt32(); + ServicePrice = binaryReader.ReadInt32(); + NpcInfoList = binaryReader.ReadList(36).ToList(); + Npcs = binaryReader.ReadList(24).ToList(); } - /// - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Unknown.GetBytes()); - buffer.AddRange(HousePrice.GetBytes()); - buffer.AddRange(ServicePrice.GetBytes()); - buffer.AddRange(NpcInfoList.Take(36).GetBytes(false)); - buffer.AddRange(Npcs.Take(24).GetBytes(false)); - return buffer; + binaryWriter.Write(Unknown); + binaryWriter.Write(HousePrice); + binaryWriter.Write(ServicePrice); + binaryWriter.Write(NpcInfoList.ToSerializable(), lengthPrefixed: false); + binaryWriter.Write(Npcs.ToSerializable(), lengthPrefixed: false); } } diff --git a/src/Parsec/Shaiya/GuildHouse/GuildHouseNpc.cs b/src/Parsec/Shaiya/GuildHouse/GuildHouseNpc.cs index 99656c0b..a273b0d1 100644 --- a/src/Parsec/Shaiya/GuildHouse/GuildHouseNpc.cs +++ b/src/Parsec/Shaiya/GuildHouse/GuildHouseNpc.cs @@ -1,24 +1,19 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.GuildHouse; -public class GuildHouseNpc : IBinary +public class GuildHouseNpc : ISerializable { - public int NpcId { get; set; } + public uint NpcId { get; set; } - [JsonConstructor] - public GuildHouseNpc() + public void Read(SBinaryReader binaryReader) { + NpcId = binaryReader.ReadUInt32(); } - public GuildHouseNpc(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - NpcId = binaryReader.Read(); + binaryWriter.Write(NpcId); } - - /// - public IEnumerable GetBytes(params object[] options) => NpcId.GetBytes(); } diff --git a/src/Parsec/Shaiya/GuildHouse/GuildHouseNpcInfo.cs b/src/Parsec/Shaiya/GuildHouse/GuildHouseNpcInfo.cs new file mode 100644 index 00000000..ca6b95d0 --- /dev/null +++ b/src/Parsec/Shaiya/GuildHouse/GuildHouseNpcInfo.cs @@ -0,0 +1,59 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.GuildHouse; + +public class GuildHouseNpcInfo : ISerializable +{ + public byte PriceRate { get; set; } + + public byte NpcLvl { get; set; } + + public byte RapiceMixPercentRate { get; set; } + + public byte RapiceMixDecreRate { get; set; } + + public byte MinRank { get; set; } + + public ushort Icon { get; set; } + + public ushort SysMsgId { get; set; } + + public ushort UpPrice { get; set; } + + public ushort ServicePrice { get; set; } + + public byte NpcType { get; set; } + + public byte Group { get; set; } + + public void Read(SBinaryReader binaryReader) + { + PriceRate = binaryReader.ReadByte(); + NpcLvl = binaryReader.ReadByte(); + RapiceMixPercentRate = binaryReader.ReadByte(); + RapiceMixDecreRate = binaryReader.ReadByte(); + MinRank = binaryReader.ReadByte(); + Icon = binaryReader.ReadUInt16(); + SysMsgId = binaryReader.ReadUInt16(); + UpPrice = binaryReader.ReadUInt16(); + ServicePrice = binaryReader.ReadUInt16(); + NpcType = binaryReader.ReadByte(); + Group = binaryReader.ReadByte(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(PriceRate); + binaryWriter.Write(NpcLvl); + binaryWriter.Write(RapiceMixPercentRate); + binaryWriter.Write(RapiceMixDecreRate); + binaryWriter.Write(MinRank); + binaryWriter.Write(Icon); + binaryWriter.Write(SysMsgId); + binaryWriter.Write(UpPrice); + binaryWriter.Write(ServicePrice); + binaryWriter.Write(NpcType); + binaryWriter.Write(Group); + } +} diff --git a/src/Parsec/Shaiya/GuildHouse/NpcInfo.cs b/src/Parsec/Shaiya/GuildHouse/NpcInfo.cs deleted file mode 100644 index 2ed258d6..00000000 --- a/src/Parsec/Shaiya/GuildHouse/NpcInfo.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.GuildHouse; - -public class NpcInfo : IBinary -{ - public byte PriceRate { get; set; } - public byte NpcLvl { get; set; } - public byte RapiceMixPercentRate { get; set; } - public byte RapiceMixDecreRate { get; set; } - public byte MinRank { get; set; } - public ushort Icon { get; set; } - public ushort SysMsgId { get; set; } - public ushort UpPrice { get; set; } - public ushort ServicePrice { get; set; } - public byte NpcType { get; set; } - public byte Group { get; set; } - - [JsonConstructor] - public NpcInfo() - { - } - - public NpcInfo(SBinaryReader binaryReader) - { - PriceRate = binaryReader.Read(); - NpcLvl = binaryReader.Read(); - RapiceMixPercentRate = binaryReader.Read(); - RapiceMixDecreRate = binaryReader.Read(); - MinRank = binaryReader.Read(); - Icon = binaryReader.Read(); - SysMsgId = binaryReader.Read(); - UpPrice = binaryReader.Read(); - ServicePrice = binaryReader.Read(); - NpcType = binaryReader.Read(); - Group = binaryReader.Read(); - } - - /// - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.Add(PriceRate); - buffer.Add(NpcLvl); - buffer.Add(RapiceMixPercentRate); - buffer.Add(RapiceMixDecreRate); - buffer.Add(MinRank); - buffer.AddRange(Icon.GetBytes()); - buffer.AddRange(SysMsgId.GetBytes()); - buffer.AddRange(UpPrice.GetBytes()); - buffer.AddRange(ServicePrice.GetBytes()); - buffer.Add(NpcType); - buffer.Add(Group); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/ITM/ITM.cs b/src/Parsec/Shaiya/ITM/ITM.cs deleted file mode 100644 index 9a038754..00000000 --- a/src/Parsec/Shaiya/ITM/ITM.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.ITM; - -public sealed class ITM : FileBase -{ - /// - /// File Signature. Read as char[3]. "ITM" or "IT2" - /// - public string Signature { get; set; } - - [JsonIgnore] - public ITMFormat Format { get; set; } - - /// - /// List of .3DO object names - /// - public List Obj3DONames { get; } = new(); - - /// - /// List of .dds texture names - /// - public List TextureNames { get; } = new(); - - /// - /// List of ITM records - /// - public List Records { get; } = new(); - - [JsonIgnore] - public override string Extension => "ITM"; - - public override void Read() - { - Signature = _binaryReader.ReadString(3); - - Format = Signature switch - { - "ITM" => ITMFormat.ITM, - "IT2" => ITMFormat.IT2, - _ => ITMFormat.Unknown - }; - - int obj3doCount = _binaryReader.Read(); - for (int i = 0; i < obj3doCount; i++) - { - string obj3doName = _binaryReader.ReadString(); - Obj3DONames.Add(obj3doName); - } - - int textureNameCount = _binaryReader.Read(); - for (int i = 0; i < textureNameCount; i++) - { - string textureName = _binaryReader.ReadString(); - TextureNames.Add(textureName); - } - - int recordCount = _binaryReader.Read(); - for (int i = 0; i < recordCount; i++) - { - var record = new Record(Format, _binaryReader); - Records.Add(record); - } - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Signature.GetBytes()); - - buffer.AddRange(Obj3DONames.Count.GetBytes()); - foreach (string obj3doName in Obj3DONames) - buffer.AddRange(obj3doName.GetLengthPrefixedBytes()); - - buffer.AddRange(TextureNames.Count.GetBytes()); - foreach (string textureName in TextureNames) - buffer.AddRange(textureName.GetLengthPrefixedBytes()); - - buffer.AddRange(Records.Count.GetBytes()); - foreach (var record in Records) - buffer.AddRange(record.GetBytes(Format)); - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/ITM/ITMFormat.cs b/src/Parsec/Shaiya/ITM/ITMFormat.cs deleted file mode 100644 index ec9bf03c..00000000 --- a/src/Parsec/Shaiya/ITM/ITMFormat.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Parsec.Shaiya.ITM; - -public enum ITMFormat -{ - ITM, - IT2, - Unknown -} diff --git a/src/Parsec/Shaiya/ITM/Record.cs b/src/Parsec/Shaiya/ITM/Record.cs deleted file mode 100644 index c738aceb..00000000 --- a/src/Parsec/Shaiya/ITM/Record.cs +++ /dev/null @@ -1,114 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.ITM; - -public sealed class Record : IBinary -{ - [JsonConstructor] - public Record() - { - } - - public Record(ITMFormat format, SBinaryReader binaryReader) - { - Obj3DOIndex = binaryReader.Read(); - TextureIndex = binaryReader.Read(); - Glow = binaryReader.Read(); - Unknown1 = binaryReader.Read(); - Format = binaryReader.Read(); - Unknown2 = binaryReader.Read(); - - if (Format == 1) - { - RGBA = binaryReader.Read(); - Rotation = binaryReader.Read(); - Scale = binaryReader.Read(); - Unknown3 = binaryReader.Read(); - } - - if (format == ITMFormat.IT2) - Unknown4 = binaryReader.ReadBytes(1024); - } - - /// - /// Index of the .3DO filename - /// - public int Obj3DOIndex { get; set; } - - /// - /// Index of the .DDS filename - /// - public int TextureIndex { get; set; } - - public int Glow { get; set; } - public int Unknown1 { get; set; } - - /// - /// Record format. 0 or 1. - /// - public int Format { get; set; } - - public int Unknown2 { get; set; } - - /// - /// Present if is 1. - /// - public uint RGBA { get; set; } - - /// - /// Present if is 1. - /// - public float Rotation { get; set; } - - /// - /// Present if is 1. - /// - public float Scale { get; set; } - - /// - /// Present if is 1. - /// - public int Unknown3 { get; set; } - - /// - /// Present if is "IT2". - /// 1024 unknown bytes. 32 blocks of 8 uint32. - /// - public byte[] Unknown4 { get; } = new byte[1024]; - - /// - /// Expects as an option - /// - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - - var format = ITMFormat.ITM; - - if (options.Length > 0) - format = (ITMFormat)options[0]; - - buffer.AddRange(Obj3DOIndex.GetBytes()); - buffer.AddRange(TextureIndex.GetBytes()); - buffer.AddRange(Glow.GetBytes()); - buffer.AddRange(Unknown1.GetBytes()); - buffer.AddRange(Format.GetBytes()); - buffer.AddRange(Unknown2.GetBytes()); - - if (Format == 1) - { - buffer.AddRange(RGBA.GetBytes()); - buffer.AddRange(Rotation.GetBytes()); - buffer.AddRange(Scale.GetBytes()); - buffer.AddRange(Unknown3.GetBytes()); - } - - if (format == ITMFormat.IT2) - buffer.AddRange(Unknown4); - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Item/DBItemDataRecord.cs b/src/Parsec/Shaiya/Item/DBItemDataRecord.cs index 7069b385..b6eb0010 100644 --- a/src/Parsec/Shaiya/Item/DBItemDataRecord.cs +++ b/src/Parsec/Shaiya/Item/DBItemDataRecord.cs @@ -1,217 +1,295 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.Item; public sealed class DBItemDataRecord : IBinarySDataRecord { - [ShaiyaProperty] public long ItemType { get; set; } - [ShaiyaProperty] public long ItemTypeId { get; set; } - [ShaiyaProperty] public long Image { get; set; } - [ShaiyaProperty] public long Icon { get; set; } - [ShaiyaProperty] public long Level { get; set; } - [ShaiyaProperty] public long Country { get; set; } - [ShaiyaProperty] public long AttackFighter { get; set; } - [ShaiyaProperty] public long DefenseFighter { get; set; } - [ShaiyaProperty] public long PatrolRogue { get; set; } - [ShaiyaProperty] public long ShootRogue { get; set; } - [ShaiyaProperty] public long AttackMage { get; set; } - [ShaiyaProperty] public long DefenseMage { get; set; } - [ShaiyaProperty] public long Grow { get; set; } - [ShaiyaProperty] public long Str { get; set; } - [ShaiyaProperty] public long Dex { get; set; } - [ShaiyaProperty] public long Rec { get; set; } - [ShaiyaProperty] public long Int { get; set; } - [ShaiyaProperty] public long Wis { get; set; } - [ShaiyaProperty] public long Luc { get; set; } - [ShaiyaProperty] public long Vg { get; set; } - [ShaiyaProperty] public long Og { get; set; } - [ShaiyaProperty] public long Ig { get; set; } - [ShaiyaProperty] public long Range { get; set; } - [ShaiyaProperty] public long AttackTime { get; set; } - [ShaiyaProperty] public long Attrib { get; set; } - [ShaiyaProperty] public long Special { get; set; } - [ShaiyaProperty] public long Slot { get; set; } - [ShaiyaProperty] public long Quality { get; set; } - [ShaiyaProperty] public long Effect1 { get; set; } - [ShaiyaProperty] public long Effect2 { get; set; } - [ShaiyaProperty] public long Effect3 { get; set; } - [ShaiyaProperty] public long Effect4 { get; set; } - [ShaiyaProperty] public long ConstHp { get; set; } - [ShaiyaProperty] public long ConstSp { get; set; } - [ShaiyaProperty] public long ConstMp { get; set; } - [ShaiyaProperty] public long ConstStr { get; set; } - [ShaiyaProperty] public long ConstDex { get; set; } - [ShaiyaProperty] public long ConstRec { get; set; } - [ShaiyaProperty] public long ConstInt { get; set; } - [ShaiyaProperty] public long ConstWis { get; set; } - [ShaiyaProperty] public long ConstLuc { get; set; } - [ShaiyaProperty] public long Speed { get; set; } - [ShaiyaProperty] public long Exp { get; set; } - [ShaiyaProperty] public long Buy { get; set; } - [ShaiyaProperty] public long Sell { get; set; } - [ShaiyaProperty] public long Grade { get; set; } - [ShaiyaProperty] public long Drop { get; set; } - [ShaiyaProperty] public long Server { get; set; } - [ShaiyaProperty] public long Count { get; set; } - [ShaiyaProperty] public long Duration { get; set; } - [ShaiyaProperty] public long ExtDuration { get; set; } - [ShaiyaProperty] public long SecOption { get; set; } - [ShaiyaProperty] public long OptionRate { get; set; } - [ShaiyaProperty] public long BuyMethod { get; set; } - [ShaiyaProperty] public long MaxLevel { get; set; } - [ShaiyaProperty] public long WeaponPart { get; set; } - [ShaiyaProperty] public long DyeingType { get; set; } - [ShaiyaProperty] public long Arg3 { get; set; } - [ShaiyaProperty] public long Arg4 { get; set; } - [ShaiyaProperty] public long UseConType { get; set; } - [ShaiyaProperty] public long UseConVar { get; set; } - [ShaiyaProperty] public long MoneyType { get; set; } - [ShaiyaProperty] public long ItemSkill { get; set; } - [ShaiyaProperty] public long ItemUpgrade { get; set; } - [ShaiyaProperty] public long Arg10 { get; set; } - [ShaiyaProperty] public long GeneCount { get; set; } - [ShaiyaProperty] public long Arg12 { get; set; } - [ShaiyaProperty] public long SpellBookExp { get; set; } - [ShaiyaProperty] public long SpellBookDurability { get; set; } - [ShaiyaProperty] public long CastTime { get; set; } + + public void Read(SBinaryReader binaryReader) + { + ItemType = binaryReader.ReadInt64(); + ItemTypeId = binaryReader.ReadInt64(); + Image = binaryReader.ReadInt64(); + Icon = binaryReader.ReadInt64(); + Level = binaryReader.ReadInt64(); + Country = binaryReader.ReadInt64(); + AttackFighter = binaryReader.ReadInt64(); + DefenseFighter = binaryReader.ReadInt64(); + PatrolRogue = binaryReader.ReadInt64(); + ShootRogue = binaryReader.ReadInt64(); + AttackMage = binaryReader.ReadInt64(); + DefenseMage = binaryReader.ReadInt64(); + Grow = binaryReader.ReadInt64(); + Str = binaryReader.ReadInt64(); + Dex = binaryReader.ReadInt64(); + Rec = binaryReader.ReadInt64(); + Int = binaryReader.ReadInt64(); + Wis = binaryReader.ReadInt64(); + Luc = binaryReader.ReadInt64(); + Vg = binaryReader.ReadInt64(); + Og = binaryReader.ReadInt64(); + Ig = binaryReader.ReadInt64(); + Range = binaryReader.ReadInt64(); + AttackTime = binaryReader.ReadInt64(); + Attrib = binaryReader.ReadInt64(); + Special = binaryReader.ReadInt64(); + Slot = binaryReader.ReadInt64(); + Quality = binaryReader.ReadInt64(); + Effect1 = binaryReader.ReadInt64(); + Effect2 = binaryReader.ReadInt64(); + Effect3 = binaryReader.ReadInt64(); + Effect4 = binaryReader.ReadInt64(); + ConstHp = binaryReader.ReadInt64(); + ConstSp = binaryReader.ReadInt64(); + ConstMp = binaryReader.ReadInt64(); + ConstStr = binaryReader.ReadInt64(); + ConstDex = binaryReader.ReadInt64(); + ConstRec = binaryReader.ReadInt64(); + ConstInt = binaryReader.ReadInt64(); + ConstWis = binaryReader.ReadInt64(); + ConstLuc = binaryReader.ReadInt64(); + Speed = binaryReader.ReadInt64(); + Exp = binaryReader.ReadInt64(); + Buy = binaryReader.ReadInt64(); + Sell = binaryReader.ReadInt64(); + Grade = binaryReader.ReadInt64(); + Drop = binaryReader.ReadInt64(); + Server = binaryReader.ReadInt64(); + Count = binaryReader.ReadInt64(); + Duration = binaryReader.ReadInt64(); + ExtDuration = binaryReader.ReadInt64(); + SecOption = binaryReader.ReadInt64(); + OptionRate = binaryReader.ReadInt64(); + BuyMethod = binaryReader.ReadInt64(); + MaxLevel = binaryReader.ReadInt64(); + WeaponPart = binaryReader.ReadInt64(); + DyeingType = binaryReader.ReadInt64(); + Arg3 = binaryReader.ReadInt64(); + Arg4 = binaryReader.ReadInt64(); + UseConType = binaryReader.ReadInt64(); + UseConVar = binaryReader.ReadInt64(); + MoneyType = binaryReader.ReadInt64(); + ItemSkill = binaryReader.ReadInt64(); + ItemUpgrade = binaryReader.ReadInt64(); + Arg10 = binaryReader.ReadInt64(); + GeneCount = binaryReader.ReadInt64(); + Arg12 = binaryReader.ReadInt64(); + SpellBookExp = binaryReader.ReadInt64(); + SpellBookDurability = binaryReader.ReadInt64(); + CastTime = binaryReader.ReadInt64(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(ItemType); + binaryWriter.Write(ItemTypeId); + binaryWriter.Write(Image); + binaryWriter.Write(Icon); + binaryWriter.Write(Level); + binaryWriter.Write(Country); + binaryWriter.Write(AttackFighter); + binaryWriter.Write(DefenseFighter); + binaryWriter.Write(PatrolRogue); + binaryWriter.Write(ShootRogue); + binaryWriter.Write(AttackMage); + binaryWriter.Write(DefenseMage); + binaryWriter.Write(Grow); + binaryWriter.Write(Str); + binaryWriter.Write(Dex); + binaryWriter.Write(Rec); + binaryWriter.Write(Int); + binaryWriter.Write(Wis); + binaryWriter.Write(Luc); + binaryWriter.Write(Vg); + binaryWriter.Write(Og); + binaryWriter.Write(Ig); + binaryWriter.Write(Range); + binaryWriter.Write(AttackTime); + binaryWriter.Write(Attrib); + binaryWriter.Write(Special); + binaryWriter.Write(Slot); + binaryWriter.Write(Quality); + binaryWriter.Write(Effect1); + binaryWriter.Write(Effect2); + binaryWriter.Write(Effect3); + binaryWriter.Write(Effect4); + binaryWriter.Write(ConstHp); + binaryWriter.Write(ConstSp); + binaryWriter.Write(ConstMp); + binaryWriter.Write(ConstStr); + binaryWriter.Write(ConstDex); + binaryWriter.Write(ConstRec); + binaryWriter.Write(ConstInt); + binaryWriter.Write(ConstWis); + binaryWriter.Write(ConstLuc); + binaryWriter.Write(Speed); + binaryWriter.Write(Exp); + binaryWriter.Write(Buy); + binaryWriter.Write(Sell); + binaryWriter.Write(Grade); + binaryWriter.Write(Drop); + binaryWriter.Write(Server); + binaryWriter.Write(Count); + binaryWriter.Write(Duration); + binaryWriter.Write(ExtDuration); + binaryWriter.Write(SecOption); + binaryWriter.Write(OptionRate); + binaryWriter.Write(BuyMethod); + binaryWriter.Write(MaxLevel); + binaryWriter.Write(WeaponPart); + binaryWriter.Write(DyeingType); + binaryWriter.Write(Arg3); + binaryWriter.Write(Arg4); + binaryWriter.Write(UseConType); + binaryWriter.Write(UseConVar); + binaryWriter.Write(MoneyType); + binaryWriter.Write(ItemSkill); + binaryWriter.Write(ItemUpgrade); + binaryWriter.Write(Arg10); + binaryWriter.Write(GeneCount); + binaryWriter.Write(Arg12); + binaryWriter.Write(SpellBookExp); + binaryWriter.Write(SpellBookDurability); + binaryWriter.Write(CastTime); + } } diff --git a/src/Parsec/Shaiya/Item/DBItemTextRecord.cs b/src/Parsec/Shaiya/Item/DBItemTextRecord.cs index 5e4b22c5..e0b16c92 100644 --- a/src/Parsec/Shaiya/Item/DBItemTextRecord.cs +++ b/src/Parsec/Shaiya/Item/DBItemTextRecord.cs @@ -1,21 +1,31 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.Item; public sealed class DBItemTextRecord : IBinarySDataRecord { - [ShaiyaProperty] public long ItemType { get; set; } - [ShaiyaProperty] public long ItemTypeId { get; set; } - [ShaiyaProperty] - [LengthPrefixedString(false)] - public string ItemName { get; set; } + public string ItemName { get; set; } = string.Empty; - [ShaiyaProperty] - [LengthPrefixedString(false)] - public string Text { get; set; } + public string Text { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + ItemType = binaryReader.ReadInt64(); + ItemTypeId = binaryReader.ReadInt64(); + ItemName = binaryReader.ReadString(); + Text = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(ItemType); + binaryWriter.Write(ItemTypeId); + binaryWriter.Write(ItemName, includeStringTerminator: false); + binaryWriter.Write(Text, includeStringTerminator: false); + } } diff --git a/src/Parsec/Shaiya/Item/IItemDefinition.cs b/src/Parsec/Shaiya/Item/IItemDefinition.cs deleted file mode 100644 index bddef533..00000000 --- a/src/Parsec/Shaiya/Item/IItemDefinition.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Parsec.Shaiya.Item; - -public interface IItemDefinition -{ - public byte Type { get; set; } - - public byte TypeId { get; set; } -} diff --git a/src/Parsec/Shaiya/Item/Item.cs b/src/Parsec/Shaiya/Item/Item.cs index c0124b5a..0cd85266 100644 --- a/src/Parsec/Shaiya/Item/Item.cs +++ b/src/Parsec/Shaiya/Item/Item.cs @@ -1,51 +1,24 @@ using System.Globalization; using System.Text; using CsvHelper; -using Newtonsoft.Json; using Parsec.Common; using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.Item; public sealed class Item : SData.SData, ICsv { - [JsonIgnore] - public Dictionary<(byte type, byte typeId), IItemDefinition> ItemIndex = new(); + public List ItemGroups { get; set; } = new(); - public int MaxItemType { get; set; } - - public List ItemTypes { get; } = new(); - - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - MaxItemType = _binaryReader.Read(); - for (int i = 0; i < MaxItemType; i++) - { - var itemType = new ItemType(_binaryReader, i + 1, Episode, ItemIndex, Encoding); - ItemTypes.Add(itemType); - } + ItemGroups = binaryReader.ReadList().ToList(); } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(MaxItemType.GetBytes()); - - for (int i = 1; i <= MaxItemType; i++) - { - var type = ItemTypes.SingleOrDefault(t => t.Id == i); - - // When type isn't part of the item, its MaxTypeId = 0 must be written to the file anyways - if (type == null) - { - buffer.AddRange(0.GetBytes()); - continue; - } - - buffer.AddRange(type.GetBytes(episode, Encoding)); - } - - return buffer; + binaryWriter.Write(ItemGroups.ToSerializable()); } /// @@ -55,98 +28,45 @@ public override IEnumerable GetBytes(Episode episode = Episode.Unknown) /// The Item.SData format /// Item.SData encoding /// instance - public static Item ReadFromCsv(string csvPath, Episode episode, Encoding encoding = null) + public static Item FromCsv(string csvPath, Episode episode, Encoding? encoding = null) { encoding ??= Encoding.ASCII; - // Create Item.SData instance - var item = new Item { Episode = episode, Encoding = encoding }; - var itemDefinitions = new List(); - - // Read all item definitions from csv file - switch (episode) - { - case Episode.EP4: - case Episode.EP5: - case Episode.Unknown: - default: - { - // Read item definitions from csv - using var reader = new StreamReader(csvPath, encoding); - using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture); - var records = csvReader.GetRecords().ToList(); - - // Cast item definitions to IItemDefinition since the FileIndex is generic for every format - itemDefinitions = records.Cast().ToList(); - break; - } - case Episode.EP6: - case Episode.EP7: - { - // Read item definitions from csv - using var reader = new StreamReader(csvPath, encoding); - using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture); - var records = csvReader.GetRecords().ToList(); + // Read item definitions from csv + using var reader = new StreamReader(csvPath, encoding); + using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture); + var csvItemRecords = csvReader.GetRecords().ToList(); - // Cast item definitions to IItemDefinition since the FileIndex is generic for every format - itemDefinitions = records.Cast().ToList(); - break; - } - case Episode.EP8: - throw new Exception("Episode 8 must use the DBItemData class."); - } - - // Get max type from items - item.MaxItemType = itemDefinitions.Max(x => x.Type); + var groups = csvItemRecords.GroupBy(x => x.ItemType).ToList(); + var item = new Item { Episode = episode, Encoding = encoding }; - // Add all items to item index - var itemIndex = itemDefinitions.ToDictionary(itemDef => (itemDef.Type, itemDef.TypeId)); - item.ItemIndex = itemIndex; + var maxGroupId = groups.Max(x => x.Key); - // Create item types - for (int i = 1; i <= item.MaxItemType; i++) + for (var i = 1; i <= maxGroupId; i++) { - // Get items for this type - var items = item.ItemIndex.Values.Where(x => x.Type == i).ToList(); + var itemGroup = new ItemGroup(); + item.ItemGroups.Add(itemGroup); - int maxTypeId = items.Count == 0 ? 0 : items.Max(x => x.TypeId); + var group = groups.FirstOrDefault(x => x.Key == i); - var type = new ItemType(i, maxTypeId, items); - item.ItemTypes.Add(type); + if (group != null) + { + itemGroup.ItemDefinitions = group.ToList(); + } } return item; } - /// - public void WriteCsv(string outputPath, Encoding encoding = null) + public void WriteCsv(string outputPath, Encoding? encoding = null) { encoding ??= Encoding.ASCII; + using var writer = new StreamWriter(outputPath, false, encoding); + using var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture); - switch (Episode) + foreach (var itemGroup in ItemGroups) { - case Episode.Unknown: - case Episode.EP4: - case Episode.EP5: - default: - { - var items = ItemIndex.Values.ToList().Cast().ToList(); - using var writer = new StreamWriter(outputPath, false, encoding); - using var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture); - csvWriter.WriteRecords(items); - break; - } - case Episode.EP6: - case Episode.EP7: - { - var items = ItemIndex.Values.ToList().Cast().ToList(); - using var writer = new StreamWriter(outputPath, false, encoding); - using var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture); - csvWriter.WriteRecords(items); - break; - } - case Episode.EP8: - throw new Exception("Episode 8 must use the DBItemData class."); + csvWriter.WriteRecords(itemGroup.ItemDefinitions); } } } diff --git a/src/Parsec/Shaiya/Item/ItemDefinition.cs b/src/Parsec/Shaiya/Item/ItemDefinition.cs new file mode 100644 index 00000000..9858d8e3 --- /dev/null +++ b/src/Parsec/Shaiya/Item/ItemDefinition.cs @@ -0,0 +1,362 @@ +using CsvHelper.Configuration.Attributes; +using Parsec.Common; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Item; + +public sealed class ItemDefinition : ISerializable +{ + [Index(2)] + public string Name { get; set; } = string.Empty; + + [Index(3)] + public string Description { get; set; } = string.Empty; + + [Index(0)] + public byte ItemType { get; set; } + + [Index(1)] + public byte ItemTypeId { get; set; } + + public byte Model { get; set; } + + public byte Icon { get; set; } + + public ushort MinLevel { get; set; } + + public byte Country { get; set; } + + public byte AttackFighter { get; set; } + + public byte DefenseFighter { get; set; } + + public byte PatrolRogue { get; set; } + + public byte ShootRogue { get; set; } + + public byte AttackMage { get; set; } + + public byte DefenseMage { get; set; } + + public byte Grow { get; set; } + + public byte Type2 { get; set; } + + public byte Type3 { get; set; } + + public ushort ReqStr { get; set; } + + public ushort ReqDex { get; set; } + + public ushort ReqRec { get; set; } + + public ushort ReqInt { get; set; } + + public ushort ReqWis { get; set; } + + public ushort ReqLuc { get; set; } + + public ushort ReqVg { get; set; } + + public ushort Unknown { get; set; } + + public byte ReqOg { get; set; } + + public byte ReqIg { get; set; } + + public ushort Range { get; set; } + + public byte AttackTime { get; set; } + + public byte Attrib { get; set; } + + public byte Special { get; set; } + + public byte Slot { get; set; } + + public ushort Quality { get; set; } + + public ushort Attack { get; set; } + + public ushort AttackAdd { get; set; } + + public ushort Def { get; set; } + + public ushort Resist { get; set; } + + public ushort Hp { get; set; } + + public ushort Sp { get; set; } + + public ushort Mp { get; set; } + + public ushort Str { get; set; } + + public ushort Dex { get; set; } + + public ushort Rec { get; set; } + + public ushort Int { get; set; } + + public ushort Wis { get; set; } + + public ushort Luc { get; set; } + + public byte Speed { get; set; } + + public byte Exp { get; set; } + + public uint BuyPrice { get; set; } + + public uint SellPrice { get; set; } + + public ushort Grade { get; set; } + + public ushort Drop { get; set; } + + public byte Server { get; set; } + + public byte Count { get; set; } + + public uint Duration { get; set; } + + public byte ExtDuration { get; set; } + + public byte SecOption { get; set; } + + public byte OptionRate { get; set; } + + public byte BuyMethod { get; set; } + + public byte MaxLevel { get; set; } + + public byte Arg1 { get; set; } + + public byte Arg2 { get; set; } + + public byte Arg3 { get; set; } + + public byte Arg4 { get; set; } + + public byte Arg5 { get; set; } + + public uint Arg6 { get; set; } + + public uint Arg7 { get; set; } + + public uint Arg8 { get; set; } + + public uint Arg9 { get; set; } + + public uint Arg10 { get; set; } + + public uint Arg11 { get; set; } + + public uint Arg12 { get; set; } + + public uint Arg13 { get; set; } + + public uint Arg14 { get; set; } + + public void Read(SBinaryReader binaryReader) + { + var episode = binaryReader.SerializationOptions.Episode; + + Name = binaryReader.ReadString(); + Description = binaryReader.ReadString(); + ItemType = binaryReader.ReadByte(); + ItemTypeId = binaryReader.ReadByte(); + Model = binaryReader.ReadByte(); + Icon = binaryReader.ReadByte(); + MinLevel = binaryReader.ReadUInt16(); + Country = binaryReader.ReadByte(); + AttackFighter = binaryReader.ReadByte(); + DefenseFighter = binaryReader.ReadByte(); + PatrolRogue = binaryReader.ReadByte(); + ShootRogue = binaryReader.ReadByte(); + AttackMage = binaryReader.ReadByte(); + DefenseMage = binaryReader.ReadByte(); + Grow = binaryReader.ReadByte(); + Type2 = binaryReader.ReadByte(); + Type3 = binaryReader.ReadByte(); + ReqStr = binaryReader.ReadUInt16(); + ReqDex = binaryReader.ReadUInt16(); + ReqRec = binaryReader.ReadUInt16(); + ReqInt = binaryReader.ReadUInt16(); + ReqWis = binaryReader.ReadUInt16(); + ReqLuc = binaryReader.ReadUInt16(); + ReqVg = binaryReader.ReadUInt16(); + + if (episode >= Episode.EP6) + { + Unknown = binaryReader.ReadUInt16(); + } + + ReqOg = binaryReader.ReadByte(); + ReqIg = binaryReader.ReadByte(); + + + if (episode <= Episode.EP5) + { + Range = binaryReader.ReadByte(); + } + else + { + Range = binaryReader.ReadUInt16(); + } + + AttackTime = binaryReader.ReadByte(); + Attrib = binaryReader.ReadByte(); + Special = binaryReader.ReadByte(); + Slot = binaryReader.ReadByte(); + Quality = binaryReader.ReadUInt16(); + Attack = binaryReader.ReadUInt16(); + AttackAdd = binaryReader.ReadUInt16(); + Def = binaryReader.ReadUInt16(); + Resist = binaryReader.ReadUInt16(); + Hp = binaryReader.ReadUInt16(); + Sp = binaryReader.ReadUInt16(); + Mp = binaryReader.ReadUInt16(); + Str = binaryReader.ReadUInt16(); + Dex = binaryReader.ReadUInt16(); + Rec = binaryReader.ReadUInt16(); + Int = binaryReader.ReadUInt16(); + Wis = binaryReader.ReadUInt16(); + Luc = binaryReader.ReadUInt16(); + Speed = binaryReader.ReadByte(); + Exp = binaryReader.ReadByte(); + BuyPrice = binaryReader.ReadUInt32(); + SellPrice = binaryReader.ReadUInt32(); + Grade = binaryReader.ReadUInt16(); + Drop = binaryReader.ReadUInt16(); + Server = binaryReader.ReadByte(); + Count = binaryReader.ReadByte(); + + if (episode >= Episode.EP6) + { + Duration = binaryReader.ReadUInt32(); + ExtDuration = binaryReader.ReadByte(); + SecOption = binaryReader.ReadByte(); + OptionRate = binaryReader.ReadByte(); + BuyMethod = binaryReader.ReadByte(); + MaxLevel = binaryReader.ReadByte(); + + Arg1 = binaryReader.ReadByte(); + Arg2 = binaryReader.ReadByte(); + Arg3 = binaryReader.ReadByte(); + Arg4 = binaryReader.ReadByte(); + Arg5 = binaryReader.ReadByte(); + + Arg6 = binaryReader.ReadUInt32(); + Arg7 = binaryReader.ReadUInt32(); + Arg8 = binaryReader.ReadUInt32(); + Arg9 = binaryReader.ReadUInt32(); + Arg10 = binaryReader.ReadUInt32(); + Arg11 = binaryReader.ReadUInt32(); + Arg12 = binaryReader.ReadUInt32(); + Arg13 = binaryReader.ReadUInt32(); + Arg14 = binaryReader.ReadUInt32(); + } + } + + public void Write(SBinaryWriter binaryWriter) + { + var episode = binaryWriter.SerializationOptions.Episode; + + binaryWriter.Write(Name); + binaryWriter.Write(Description); + binaryWriter.Write(ItemType); + binaryWriter.Write(ItemTypeId); + binaryWriter.Write(Model); + binaryWriter.Write(Icon); + binaryWriter.Write(MinLevel); + binaryWriter.Write(Country); + binaryWriter.Write(AttackFighter); + binaryWriter.Write(DefenseFighter); + binaryWriter.Write(PatrolRogue); + binaryWriter.Write(ShootRogue); + binaryWriter.Write(AttackMage); + binaryWriter.Write(DefenseMage); + binaryWriter.Write(Grow); + binaryWriter.Write(Type2); + binaryWriter.Write(Type3); + binaryWriter.Write(ReqStr); + binaryWriter.Write(ReqDex); + binaryWriter.Write(ReqRec); + binaryWriter.Write(ReqInt); + binaryWriter.Write(ReqWis); + binaryWriter.Write(ReqLuc); + binaryWriter.Write(ReqVg); + + if (episode >= Episode.EP6) + { + binaryWriter.Write(Unknown); + } + + binaryWriter.Write(ReqOg); + binaryWriter.Write(ReqIg); + + if (episode <= Episode.EP5) + { + binaryWriter.Write(Range); + } + else + { + binaryWriter.Write(Range); + } + + binaryWriter.Write(AttackTime); + binaryWriter.Write(Attrib); + binaryWriter.Write(Special); + binaryWriter.Write(Slot); + binaryWriter.Write(Quality); + binaryWriter.Write(Attack); + binaryWriter.Write(AttackAdd); + binaryWriter.Write(Def); + binaryWriter.Write(Resist); + binaryWriter.Write(Hp); + binaryWriter.Write(Sp); + binaryWriter.Write(Mp); + binaryWriter.Write(Str); + binaryWriter.Write(Dex); + binaryWriter.Write(Rec); + binaryWriter.Write(Int); + binaryWriter.Write(Wis); + binaryWriter.Write(Luc); + binaryWriter.Write(Speed); + binaryWriter.Write(Exp); + binaryWriter.Write(BuyPrice); + binaryWriter.Write(SellPrice); + binaryWriter.Write(Grade); + binaryWriter.Write(Drop); + binaryWriter.Write(Server); + binaryWriter.Write(Count); + + if (episode >= Episode.EP6) + { + binaryWriter.Write(Duration); + binaryWriter.Write(ExtDuration); + binaryWriter.Write(SecOption); + binaryWriter.Write(OptionRate); + binaryWriter.Write(BuyMethod); + binaryWriter.Write(MaxLevel); + + binaryWriter.Write(Arg1); + binaryWriter.Write(Arg2); + binaryWriter.Write(Arg3); + binaryWriter.Write(Arg4); + binaryWriter.Write(Arg5); + + binaryWriter.Write(Arg6); + binaryWriter.Write(Arg7); + binaryWriter.Write(Arg8); + binaryWriter.Write(Arg9); + binaryWriter.Write(Arg10); + binaryWriter.Write(Arg11); + binaryWriter.Write(Arg12); + binaryWriter.Write(Arg13); + binaryWriter.Write(Arg14); + } + } +} diff --git a/src/Parsec/Shaiya/Item/ItemDefinitionEp5.cs b/src/Parsec/Shaiya/Item/ItemDefinitionEp5.cs deleted file mode 100644 index d77367e0..00000000 --- a/src/Parsec/Shaiya/Item/ItemDefinitionEp5.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System.Text; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Item; - -public sealed class ItemDefinitionEp5 : IBinary, IItemDefinition -{ - [JsonConstructor] - public ItemDefinitionEp5() - { - } - - public ItemDefinitionEp5(SBinaryReader binaryReader, Encoding encoding) - { - Name = binaryReader.ReadString(encoding); - Description = binaryReader.ReadString(encoding); - Type = binaryReader.Read(); - TypeId = binaryReader.Read(); - Model = binaryReader.Read(); - Icon = binaryReader.Read(); - MinLevel = binaryReader.Read(); - Country = binaryReader.Read(); - AttackFighter = binaryReader.Read(); - DefenseFighter = binaryReader.Read(); - PatrolRogue = binaryReader.Read(); - ShootRogue = binaryReader.Read(); - AttackMage = binaryReader.Read(); - DefenseMage = binaryReader.Read(); - Grow = binaryReader.Read(); - Type2 = binaryReader.Read(); - Type3 = binaryReader.Read(); - ReqStr = binaryReader.Read(); - ReqDex = binaryReader.Read(); - ReqRec = binaryReader.Read(); - ReqInt = binaryReader.Read(); - ReqWis = binaryReader.Read(); - ReqLuc = binaryReader.Read(); - ReqVg = binaryReader.Read(); - ReqOg = binaryReader.Read(); - ReqIg = binaryReader.Read(); - Range = binaryReader.Read(); - AttackTime = binaryReader.Read(); - Attrib = binaryReader.Read(); - Special = binaryReader.Read(); - Slot = binaryReader.Read(); - Quality = binaryReader.Read(); - Attack = binaryReader.Read(); - AttackAdd = binaryReader.Read(); - Def = binaryReader.Read(); - Resist = binaryReader.Read(); - Hp = binaryReader.Read(); - Sp = binaryReader.Read(); - Mp = binaryReader.Read(); - Str = binaryReader.Read(); - Dex = binaryReader.Read(); - Rec = binaryReader.Read(); - Int = binaryReader.Read(); - Wis = binaryReader.Read(); - Luc = binaryReader.Read(); - Speed = binaryReader.Read(); - Exp = binaryReader.Read(); - BuyPrice = binaryReader.Read(); - SellPrice = binaryReader.Read(); - Grade = binaryReader.Read(); - Drop = binaryReader.Read(); - Server = binaryReader.Read(); - Count = binaryReader.Read(); - } - - /// - /// Order: 2. Changed because of CSV. - /// - public byte Type { get; set; } - - /// - /// Order: 3. Changed because of CSV. - /// - public byte TypeId { get; set; } - - /// - /// Order: 0. Changed because of CSV. - /// - public string Name { get; set; } - - /// - /// Order: 1. Changed because of CSV. - /// - public string Description { get; set; } - - public byte Model { get; set; } - public byte Icon { get; set; } - public ushort MinLevel { get; set; } - public byte Country { get; set; } - public byte AttackFighter { get; set; } - public byte DefenseFighter { get; set; } - public byte PatrolRogue { get; set; } - public byte ShootRogue { get; set; } - public byte AttackMage { get; set; } - public byte DefenseMage { get; set; } - public byte Grow { get; set; } - public byte Type2 { get; set; } - public byte Type3 { get; set; } - public ushort ReqStr { get; set; } - public ushort ReqDex { get; set; } - public ushort ReqRec { get; set; } - public ushort ReqInt { get; set; } - public ushort ReqWis { get; set; } - public ushort ReqLuc { get; set; } - public ushort ReqVg { get; set; } - public byte ReqOg { get; set; } - public byte ReqIg { get; set; } - public byte Range { get; set; } - public byte AttackTime { get; set; } - public byte Attrib { get; set; } - public byte Special { get; set; } - public byte Slot { get; set; } - public ushort Quality { get; set; } - public ushort Attack { get; set; } - public ushort AttackAdd { get; set; } - public ushort Def { get; set; } - public ushort Resist { get; set; } - public ushort Hp { get; set; } - public ushort Sp { get; set; } - public ushort Mp { get; set; } - public ushort Str { get; set; } - public ushort Dex { get; set; } - public ushort Rec { get; set; } - public ushort Int { get; set; } - public ushort Wis { get; set; } - public ushort Luc { get; set; } - public byte Speed { get; set; } - public byte Exp { get; set; } - public uint BuyPrice { get; set; } - public uint SellPrice { get; set; } - public ushort Grade { get; set; } - public ushort Drop { get; set; } - public byte Server { get; set; } - public byte Count { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var encoding = Encoding.ASCII; - - if (options.Length > 0 && options[0] is Encoding stringEncoding) - { - encoding = stringEncoding; - } - - var buffer = new List(); - buffer.AddRange(Name.GetLengthPrefixedBytes(encoding)); - buffer.AddRange(Description.GetLengthPrefixedBytes(encoding)); - buffer.Add(Type); - buffer.Add(TypeId); - buffer.Add(Model); - buffer.Add(Icon); - buffer.AddRange(MinLevel.GetBytes()); - buffer.Add(Country); - buffer.Add(AttackFighter); - buffer.Add(DefenseFighter); - buffer.Add(PatrolRogue); - buffer.Add(ShootRogue); - buffer.Add(AttackMage); - buffer.Add(DefenseMage); - buffer.Add(Grow); - buffer.Add(Type2); - buffer.Add(Type3); - buffer.AddRange(ReqStr.GetBytes()); - buffer.AddRange(ReqDex.GetBytes()); - buffer.AddRange(ReqRec.GetBytes()); - buffer.AddRange(ReqInt.GetBytes()); - buffer.AddRange(ReqWis.GetBytes()); - buffer.AddRange(ReqLuc.GetBytes()); - buffer.AddRange(ReqVg.GetBytes()); - buffer.Add(ReqOg); - buffer.Add(ReqIg); - buffer.Add(Range); - buffer.Add(AttackTime); - buffer.Add(Attrib); - buffer.Add(Special); - buffer.Add(Slot); - buffer.AddRange(Quality.GetBytes()); - buffer.AddRange(Attack.GetBytes()); - buffer.AddRange(AttackAdd.GetBytes()); - buffer.AddRange(Def.GetBytes()); - buffer.AddRange(Resist.GetBytes()); - buffer.AddRange(Hp.GetBytes()); - buffer.AddRange(Sp.GetBytes()); - buffer.AddRange(Mp.GetBytes()); - buffer.AddRange(Str.GetBytes()); - buffer.AddRange(Dex.GetBytes()); - buffer.AddRange(Rec.GetBytes()); - buffer.AddRange(Int.GetBytes()); - buffer.AddRange(Wis.GetBytes()); - buffer.AddRange(Luc.GetBytes()); - buffer.Add(Speed); - buffer.Add(Exp); - buffer.AddRange(BuyPrice.GetBytes()); - buffer.AddRange(SellPrice.GetBytes()); - buffer.AddRange(Grade.GetBytes()); - buffer.AddRange(Drop.GetBytes()); - buffer.Add(Server); - buffer.Add(Count); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Item/ItemDefinitionEp6.cs b/src/Parsec/Shaiya/Item/ItemDefinitionEp6.cs deleted file mode 100644 index 75f468b8..00000000 --- a/src/Parsec/Shaiya/Item/ItemDefinitionEp6.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System.Text; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Item; - -public sealed class ItemDefinitionEp6 : IBinary, IItemDefinition -{ - [JsonConstructor] - public ItemDefinitionEp6() - { - } - - public ItemDefinitionEp6(SBinaryReader binaryReader, Encoding encoding) - { - Name = binaryReader.ReadString(encoding); - Description = binaryReader.ReadString(encoding); - Type = binaryReader.Read(); - TypeId = binaryReader.Read(); - Model = binaryReader.Read(); - Icon = binaryReader.Read(); - MinLevel = binaryReader.Read(); - Country = binaryReader.Read(); - AttackFighter = binaryReader.Read(); - DefenseFighter = binaryReader.Read(); - PatrolRogue = binaryReader.Read(); - ShootRogue = binaryReader.Read(); - AttackMage = binaryReader.Read(); - DefenseMage = binaryReader.Read(); - Grow = binaryReader.Read(); - Type2 = binaryReader.Read(); - Type3 = binaryReader.Read(); - ReqStr = binaryReader.Read(); - ReqDex = binaryReader.Read(); - ReqRec = binaryReader.Read(); - ReqInt = binaryReader.Read(); - ReqWis = binaryReader.Read(); - ReqLuc = binaryReader.Read(); - ReqVg = binaryReader.Read(); - Unknown = binaryReader.Read(); - ReqOg = binaryReader.Read(); - ReqIg = binaryReader.Read(); - Range = binaryReader.Read(); - AttackTime = binaryReader.Read(); - Attrib = binaryReader.Read(); - Special = binaryReader.Read(); - Slot = binaryReader.Read(); - Quality = binaryReader.Read(); - Attack = binaryReader.Read(); - AttackAdd = binaryReader.Read(); - Def = binaryReader.Read(); - Resist = binaryReader.Read(); - Hp = binaryReader.Read(); - Sp = binaryReader.Read(); - Mp = binaryReader.Read(); - Str = binaryReader.Read(); - Dex = binaryReader.Read(); - Rec = binaryReader.Read(); - Int = binaryReader.Read(); - Wis = binaryReader.Read(); - Luc = binaryReader.Read(); - Speed = binaryReader.Read(); - Exp = binaryReader.Read(); - BuyPrice = binaryReader.Read(); - SellPrice = binaryReader.Read(); - Grade = binaryReader.Read(); - Drop = binaryReader.Read(); - Server = binaryReader.Read(); - Count = binaryReader.Read(); - - Duration = binaryReader.Read(); - ExtDuration = binaryReader.Read(); - SecOption = binaryReader.Read(); - OptionRate = binaryReader.Read(); - BuyMethod = binaryReader.Read(); - MaxLevel = binaryReader.Read(); - - Arg1 = binaryReader.Read(); - Arg2 = binaryReader.Read(); - Arg3 = binaryReader.Read(); - Arg4 = binaryReader.Read(); - Arg5 = binaryReader.Read(); - - Arg6 = binaryReader.Read(); - Arg7 = binaryReader.Read(); - Arg8 = binaryReader.Read(); - Arg9 = binaryReader.Read(); - Arg10 = binaryReader.Read(); - Arg11 = binaryReader.Read(); - Arg12 = binaryReader.Read(); - Arg13 = binaryReader.Read(); - Arg14 = binaryReader.Read(); - } - - /// - /// Order: 2. Changed because of CSV. - /// - public byte Type { get; set; } - - /// - /// Order: 3. Changed because of CSV. - /// - public byte TypeId { get; set; } - - /// - /// Order: 0. Changed because of CSV. - /// - public string Name { get; set; } - - /// - /// Order: 1. Changed because of CSV. - /// - public string Description { get; set; } - - public byte Model { get; set; } - public byte Icon { get; set; } - public ushort MinLevel { get; set; } - public byte Country { get; set; } - public byte AttackFighter { get; set; } - public byte DefenseFighter { get; set; } - public byte PatrolRogue { get; set; } - public byte ShootRogue { get; set; } - public byte AttackMage { get; set; } - public byte DefenseMage { get; set; } - public byte Grow { get; set; } - public byte Type2 { get; set; } - public byte Type3 { get; set; } - public ushort ReqStr { get; set; } - public ushort ReqDex { get; set; } - public ushort ReqRec { get; set; } - public ushort ReqInt { get; set; } - public ushort ReqWis { get; set; } - public ushort ReqLuc { get; set; } - public ushort ReqVg { get; set; } - public ushort Unknown { get; set; } - public byte ReqOg { get; set; } - public byte ReqIg { get; set; } - public ushort Range { get; set; } - public byte AttackTime { get; set; } - public byte Attrib { get; set; } - public byte Special { get; set; } - public byte Slot { get; set; } - public ushort Quality { get; set; } - public ushort Attack { get; set; } - public ushort AttackAdd { get; set; } - public ushort Def { get; set; } - public ushort Resist { get; set; } - public ushort Hp { get; set; } - public ushort Sp { get; set; } - public ushort Mp { get; set; } - public ushort Str { get; set; } - public ushort Dex { get; set; } - public ushort Rec { get; set; } - public ushort Int { get; set; } - public ushort Wis { get; set; } - public ushort Luc { get; set; } - public byte Speed { get; set; } - public byte Exp { get; set; } - public uint BuyPrice { get; set; } - public uint SellPrice { get; set; } - public ushort Grade { get; set; } - public ushort Drop { get; set; } - public byte Server { get; set; } - public byte Count { get; set; } - - public uint Duration { get; set; } - - public byte ExtDuration { get; set; } - public byte SecOption { get; set; } - public byte OptionRate { get; set; } - public byte BuyMethod { get; set; } - public byte MaxLevel { get; set; } - public byte Arg1 { get; set; } - public byte Arg2 { get; set; } - public byte Arg3 { get; set; } - public byte Arg4 { get; set; } - public byte Arg5 { get; set; } - - public uint Arg6 { get; set; } - public uint Arg7 { get; set; } - public uint Arg8 { get; set; } - public uint Arg9 { get; set; } - public uint Arg10 { get; set; } - public uint Arg11 { get; set; } - public uint Arg12 { get; set; } - public uint Arg13 { get; set; } - public uint Arg14 { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var encoding = Encoding.ASCII; - - if (options.Length > 0 && options[0] is Encoding stringEncoding) - { - encoding = stringEncoding; - } - - var buffer = new List(); - buffer.AddRange(Name.GetLengthPrefixedBytes(encoding)); - buffer.AddRange(Description.GetLengthPrefixedBytes(encoding)); - buffer.Add(Type); - buffer.Add(TypeId); - buffer.Add(Model); - buffer.Add(Icon); - buffer.AddRange(MinLevel.GetBytes()); - buffer.Add(Country); - buffer.Add(AttackFighter); - buffer.Add(DefenseFighter); - buffer.Add(PatrolRogue); - buffer.Add(ShootRogue); - buffer.Add(AttackMage); - buffer.Add(DefenseMage); - buffer.Add(Grow); - buffer.Add(Type2); - buffer.Add(Type3); - buffer.AddRange(ReqStr.GetBytes()); - buffer.AddRange(ReqDex.GetBytes()); - buffer.AddRange(ReqRec.GetBytes()); - buffer.AddRange(ReqInt.GetBytes()); - buffer.AddRange(ReqWis.GetBytes()); - buffer.AddRange(ReqLuc.GetBytes()); - buffer.AddRange(ReqVg.GetBytes()); - buffer.AddRange(Unknown.GetBytes()); - buffer.Add(ReqOg); - buffer.Add(ReqIg); - buffer.AddRange(Range.GetBytes()); - buffer.Add(AttackTime); - buffer.Add(Attrib); - buffer.Add(Special); - buffer.Add(Slot); - buffer.AddRange(Quality.GetBytes()); - buffer.AddRange(Attack.GetBytes()); - buffer.AddRange(AttackAdd.GetBytes()); - buffer.AddRange(Def.GetBytes()); - buffer.AddRange(Resist.GetBytes()); - buffer.AddRange(Hp.GetBytes()); - buffer.AddRange(Sp.GetBytes()); - buffer.AddRange(Mp.GetBytes()); - buffer.AddRange(Str.GetBytes()); - buffer.AddRange(Dex.GetBytes()); - buffer.AddRange(Rec.GetBytes()); - buffer.AddRange(Int.GetBytes()); - buffer.AddRange(Wis.GetBytes()); - buffer.AddRange(Luc.GetBytes()); - buffer.Add(Speed); - buffer.Add(Exp); - buffer.AddRange(BuyPrice.GetBytes()); - buffer.AddRange(SellPrice.GetBytes()); - buffer.AddRange(Grade.GetBytes()); - buffer.AddRange(Drop.GetBytes()); - buffer.Add(Server); - buffer.Add(Count); - buffer.AddRange(Duration.GetBytes()); - buffer.Add(ExtDuration); - buffer.Add(SecOption); - buffer.Add(OptionRate); - buffer.Add(BuyMethod); - buffer.Add(MaxLevel); - buffer.Add(Arg1); - buffer.Add(Arg2); - buffer.Add(Arg3); - buffer.Add(Arg4); - buffer.Add(Arg5); - buffer.AddRange(Arg6.GetBytes()); - buffer.AddRange(Arg7.GetBytes()); - buffer.AddRange(Arg8.GetBytes()); - buffer.AddRange(Arg9.GetBytes()); - buffer.AddRange(Arg10.GetBytes()); - buffer.AddRange(Arg11.GetBytes()); - buffer.AddRange(Arg12.GetBytes()); - buffer.AddRange(Arg13.GetBytes()); - buffer.AddRange(Arg14.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Item/ItemGroup.cs b/src/Parsec/Shaiya/Item/ItemGroup.cs new file mode 100644 index 00000000..e3947ae1 --- /dev/null +++ b/src/Parsec/Shaiya/Item/ItemGroup.cs @@ -0,0 +1,20 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Item; + +public sealed class ItemGroup : ISerializable +{ + public List ItemDefinitions { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + ItemDefinitions = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(ItemDefinitions.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Item/ItemType.cs b/src/Parsec/Shaiya/Item/ItemType.cs deleted file mode 100644 index fc6f30f6..00000000 --- a/src/Parsec/Shaiya/Item/ItemType.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Text; -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Item; - -public sealed class ItemType : IBinary -{ - public int Id { get; set; } - - public int MaxTypeId { get; set; } - - public List ItemDefinitions { get; } = new(); - - [JsonConstructor] - public ItemType() - { - } - - public ItemType(int id, int maxTypeId, IEnumerable itemDefinitions) - { - Id = id; - MaxTypeId = maxTypeId; - ItemDefinitions = itemDefinitions.ToList(); - } - - public ItemType(SBinaryReader binaryReader, int id, Episode episode, IDictionary<(byte type, byte typeId), IItemDefinition> itemIndex, - Encoding encoding) - { - Id = id; - MaxTypeId = binaryReader.Read(); - - for (int i = 0; i < MaxTypeId; i++) - { - switch (episode) - { - case Episode.EP5: - default: - var itemEp5 = new ItemDefinitionEp5(binaryReader, encoding); - ItemDefinitions.Add(itemEp5); - itemIndex.Add((itemEp5.Type, itemEp5.TypeId), itemEp5); - break; - case Episode.EP6: - var itemEp6 = new ItemDefinitionEp6(binaryReader, encoding); - ItemDefinitions.Add(itemEp6); - itemIndex.Add((itemEp6.Type, itemEp6.TypeId), itemEp6); - break; - } - } - } - - public IEnumerable GetBytes(params object[] options) - { - var episode = Episode.EP5; - var encoding = Encoding.ASCII; - - if (options.Length > 0 && options[0] is Episode episodeOption) - { - episode = episodeOption; - } - - if (options.Length > 1 && options[1] is Encoding encodingOption) - { - encoding = encodingOption; - } - - var buffer = new List(); - buffer.AddRange(MaxTypeId.GetBytes()); - - foreach (var itemDefinition in ItemDefinitions) - { - // Add item definitions based on format - switch (episode) - { - case Episode.EP5: - default: - buffer.AddRange(((ItemDefinitionEp5)itemDefinition).GetBytes(encoding)); - break; - case Episode.EP6: - buffer.AddRange(((ItemDefinitionEp6)itemDefinition).GetBytes(encoding)); - break; - } - } - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Itm/Itm.cs b/src/Parsec/Shaiya/Itm/Itm.cs new file mode 100644 index 00000000..6b4f965a --- /dev/null +++ b/src/Parsec/Shaiya/Itm/Itm.cs @@ -0,0 +1,95 @@ +using Newtonsoft.Json; +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Itm; + +public sealed class Itm : FileBase +{ + /// + /// File Signature. Read as char[3]. "ITM" or "IT2" + /// + public string Signature { get; set; } = string.Empty; + + [JsonIgnore] + public ItmFormat Format { get; set; } + + /// + /// List of .3DO object names + /// + public List MeshNames { get; set; } = new(); + + /// + /// List of .dds texture names + /// + public List TextureNames { get; set; } = new(); + + /// + /// List of ITM records + /// + public List Records { get; set; } = new(); + + [JsonIgnore] + public override string Extension => "ITM"; + + protected override void Read(SBinaryReader binaryReader) + { + Signature = binaryReader.ReadString(3); + + Format = Signature switch + { + "ITM" => ItmFormat.ITM, + "IT2" => ItmFormat.IT2, + _ => ItmFormat.Unknown + }; + + // Records expect the format to be set on the serialization options ExtraOption property + binaryReader.SerializationOptions.ExtraOption = Format; + + var meshNameCount = binaryReader.ReadInt32(); + for (var i = 0; i < meshNameCount; i++) + { + var meshName = binaryReader.ReadString(); + MeshNames.Add(meshName); + } + + var textureNameCount = binaryReader.ReadInt32(); + for (var i = 0; i < textureNameCount; i++) + { + var textureName = binaryReader.ReadString(); + TextureNames.Add(textureName); + } + + Records = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + // Records expect the format to be set on the serialization options ExtraOption property + binaryWriter.SerializationOptions.ExtraOption = Format; + + var signature = Format switch + { + ItmFormat.ITM => "ITM", + ItmFormat.IT2 => "IT2", + _ => "ITM" + }; + + binaryWriter.Write(signature, isLengthPrefixed: false, includeStringTerminator: false); + + binaryWriter.Write(MeshNames.Count); + foreach (var meshName in MeshNames) + { + binaryWriter.Write(meshName); + } + + binaryWriter.Write(TextureNames.Count); + foreach (var textureName in TextureNames) + { + binaryWriter.Write(textureName); + } + + binaryWriter.Write(Records.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Itm/ItmFormat.cs b/src/Parsec/Shaiya/Itm/ItmFormat.cs new file mode 100644 index 00000000..f281d102 --- /dev/null +++ b/src/Parsec/Shaiya/Itm/ItmFormat.cs @@ -0,0 +1,8 @@ +namespace Parsec.Shaiya.Itm; + +public enum ItmFormat +{ + ITM, + IT2, + Unknown +} diff --git a/src/Parsec/Shaiya/Itm/ItmRecord.cs b/src/Parsec/Shaiya/Itm/ItmRecord.cs new file mode 100644 index 00000000..64b222bb --- /dev/null +++ b/src/Parsec/Shaiya/Itm/ItmRecord.cs @@ -0,0 +1,101 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Itm; + +public sealed class ItmRecord : ISerializable +{ + /// + /// Index of the .3DO filename + /// + public int MeshIndex { get; set; } + + /// + /// Index of the .DDS filename + /// + public int TextureIndex { get; set; } + + public AlphaBlendingMode AlphaBlendingMode { get; set; } + + public int Unknown1 { get; set; } + + /// + /// Record format. 0 or 1. + /// + public int RecordFormat { get; set; } + + public int Unknown2 { get; set; } + + /// + /// Present if is 1. + /// + public uint RGBA { get; set; } + + /// + /// Present if is 1. + /// + public float Rotation { get; set; } + + /// + /// Present if is 1. + /// + public float Scale { get; set; } + + /// + /// Present if is 1. + /// + public int Unknown3 { get; set; } + + /// + /// Present if is "IT2". + /// 1024 unknown bytes. 32 blocks of 8 uint32. + /// + public byte[] Unknown4 { get; set; } = new byte[1024]; + + public void Read(SBinaryReader binaryReader) + { + MeshIndex = binaryReader.ReadInt32(); + TextureIndex = binaryReader.ReadInt32(); + AlphaBlendingMode = (AlphaBlendingMode)binaryReader.ReadInt32(); + Unknown1 = binaryReader.ReadInt32(); + RecordFormat = binaryReader.ReadInt32(); + Unknown2 = binaryReader.ReadInt32(); + + if (RecordFormat == 1) + { + RGBA = binaryReader.ReadUInt32(); + Rotation = binaryReader.ReadSingle(); + Scale = binaryReader.ReadSingle(); + Unknown3 = binaryReader.ReadInt32(); + } + + if (binaryReader.SerializationOptions.ExtraOption is ItmFormat.IT2) + { + Unknown4 = binaryReader.ReadBytes(1024); + } + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(MeshIndex); + binaryWriter.Write(TextureIndex); + binaryWriter.Write((int)AlphaBlendingMode); + binaryWriter.Write(Unknown1); + binaryWriter.Write(RecordFormat); + binaryWriter.Write(Unknown2); + + if (RecordFormat == 1) + { + binaryWriter.Write(RGBA); + binaryWriter.Write(Rotation); + binaryWriter.Write(Scale); + binaryWriter.Write(Unknown3); + } + + if (binaryWriter.SerializationOptions.ExtraOption is ItmFormat.IT2) + { + binaryWriter.Write(Unknown4); + } + } +} diff --git a/src/Parsec/Shaiya/KillStatus/KillStatus.cs b/src/Parsec/Shaiya/KillStatus/KillStatus.cs index 5aa21fa5..76f19a2f 100644 --- a/src/Parsec/Shaiya/KillStatus/KillStatus.cs +++ b/src/Parsec/Shaiya/KillStatus/KillStatus.cs @@ -1,5 +1,5 @@ -using Parsec.Attributes; -using Parsec.Common; +using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.KillStatus; @@ -7,10 +7,17 @@ namespace Parsec.Shaiya.KillStatus; /// Class that represents the KillStatus.SData file format. /// This file contains the bonuses each faction receives based on bless of the goddess values. /// -[DefaultVersion(Episode.EP5)] public sealed class KillStatus : SData.SData { - [ShaiyaProperty] - [LengthPrefixedList(typeof(KillStatusRecord))] public List Records { get; set; } = new(); + + protected override void Read(SBinaryReader binaryReader) + { + Records = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Records.ToSerializable()); + } } diff --git a/src/Parsec/Shaiya/KillStatus/KillStatusBonus.cs b/src/Parsec/Shaiya/KillStatus/KillStatusBonus.cs index 48397d30..c47195ef 100644 --- a/src/Parsec/Shaiya/KillStatus/KillStatusBonus.cs +++ b/src/Parsec/Shaiya/KillStatus/KillStatusBonus.cs @@ -1,18 +1,23 @@ -using Parsec.Attributes; +using Parsec.Serialization; +using Parsec.Shaiya.Core; namespace Parsec.Shaiya.KillStatus; -public sealed class KillStatusBonus +public sealed class KillStatusBonus : ISerializable { - /// - /// The type of bonus - /// - [ShaiyaProperty] - public KillStatusBonusType Type { get; set; } + public KillStatusBonusType BonusType { get; set; } - /// - /// The value of the bonus - /// - [ShaiyaProperty] - public short Value { get; set; } + public ushort BonusValue { get; set; } + + public void Read(SBinaryReader binaryReader) + { + BonusType = (KillStatusBonusType)binaryReader.ReadByte(); + BonusValue = binaryReader.ReadUInt16(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write((byte)BonusType); + binaryWriter.Write(BonusValue); + } } diff --git a/src/Parsec/Shaiya/KillStatus/KillStatusFaction.cs b/src/Parsec/Shaiya/KillStatus/KillStatusFaction.cs new file mode 100644 index 00000000..faaa8372 --- /dev/null +++ b/src/Parsec/Shaiya/KillStatus/KillStatusFaction.cs @@ -0,0 +1,8 @@ +namespace Parsec.Shaiya.KillStatus; + +public enum KillStatusFaction : byte +{ + Light, + Fury, + Any +} diff --git a/src/Parsec/Shaiya/KillStatus/KillStatusRecord.cs b/src/Parsec/Shaiya/KillStatus/KillStatusRecord.cs index b035e306..7b891636 100644 --- a/src/Parsec/Shaiya/KillStatus/KillStatusRecord.cs +++ b/src/Parsec/Shaiya/KillStatus/KillStatusRecord.cs @@ -1,32 +1,44 @@ -using Parsec.Attributes; -using Parsec.Shaiya.Common; +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; namespace Parsec.Shaiya.KillStatus; -public sealed class KillStatusRecord +public sealed class KillStatusRecord : ISerializable { /// /// The faction that will receive the bonus /// - [ShaiyaProperty] - public FactionByte Faction { get; set; } + public KillStatusFaction Faction { get; set; } /// /// The absolute bless value at which the bonuses will take effect /// - [ShaiyaProperty] public int BlessValue { get; set; } /// /// The index of this record /// - [ShaiyaProperty] - public short Index { get; set; } + public ushort Index { get; set; } /// - /// The bonuses to be applied + /// The bonuses to be applied (fixed length of 6) /// - [ShaiyaProperty] - [FixedLengthList(typeof(KillStatusBonus), 6)] public List Bonuses { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + Faction = (KillStatusFaction)binaryReader.ReadByte(); + BlessValue = binaryReader.ReadInt32(); + Index = binaryReader.ReadUInt16(); + Bonuses = binaryReader.ReadList(6).ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write((byte)Faction); + binaryWriter.Write(BlessValue); + binaryWriter.Write(Index); + binaryWriter.Write(Bonuses.Take(6).ToSerializable(), lengthPrefixed: false); + } } diff --git a/src/Parsec/Shaiya/MAni/MAni.cs b/src/Parsec/Shaiya/MAni/MAni.cs deleted file mode 100644 index 99552a61..00000000 --- a/src/Parsec/Shaiya/MAni/MAni.cs +++ /dev/null @@ -1,117 +0,0 @@ -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.MAni; - -/// -/// Represents a .mani file. Its used to define an animation for an SMOD object, that's where the extension comes -/// from, given by "Mesh Animation" -/// -public sealed class MAni : FileBase -{ - /// - /// Always 0x21 = 33 - /// - public int Version { get; set; } - - public int Unknown1 { get; set; } - - public Vector3 UnknownVec1 { get; set; } - - public float Unknown2 { get; set; } - - public float Unknown3 { get; set; } - - public float Unknown4 { get; set; } - - public int Unknown5 { get; set; } - - public int Unknown6 { get; set; } - - public Vector3 UnknownVec2 { get; set; } - - public float Unknown7 { get; set; } - - public float Unknown8 { get; set; } - - /// - /// Indicates if the object should rotate - /// - public int EnableRotation { get; set; } - - /// - /// Indicates the axis of rotation of the object, -1f or 1f values should be used - /// - public Vector3 Rotation { get; set; } - - /// - /// Animation Speed [0.0f, 1.0f] - /// - public float AnimationSpeed { get; set; } - - public short UnknownShort1 { get; set; } - - public short UnknownShort2 { get; set; } - - public Vector3 UnknownVec4 { get; set; } - - public float Unknown11 { get; set; } - - public float Unknown12 { get; set; } - - public int Unknown13 { get; set; } - - public override string Extension => "mani"; - - public override void Read() - { - Version = _binaryReader.Read(); - Unknown1 = _binaryReader.Read(); - UnknownVec1 = new Vector3(_binaryReader); - Unknown2 = _binaryReader.Read(); - Unknown3 = _binaryReader.Read(); - Unknown4 = _binaryReader.Read(); - Unknown5 = _binaryReader.Read(); - Unknown6 = _binaryReader.Read(); - UnknownVec2 = new Vector3(_binaryReader); - Unknown7 = _binaryReader.Read(); - Unknown8 = _binaryReader.Read(); - EnableRotation = _binaryReader.Read(); - Rotation = new Vector3(_binaryReader); - AnimationSpeed = _binaryReader.Read(); - UnknownShort1 = _binaryReader.Read(); - UnknownShort2 = _binaryReader.Read(); - UnknownVec4 = new Vector3(_binaryReader); - Unknown11 = _binaryReader.Read(); - Unknown12 = _binaryReader.Read(); - Unknown13 = _binaryReader.Read(); - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Version.GetBytes()); - buffer.AddRange(Unknown1.GetBytes()); - buffer.AddRange(UnknownVec1.GetBytes()); - buffer.AddRange(Unknown2.GetBytes()); - buffer.AddRange(Unknown3.GetBytes()); - buffer.AddRange(Unknown4.GetBytes()); - buffer.AddRange(Unknown5.GetBytes()); - buffer.AddRange(Unknown6.GetBytes()); - buffer.AddRange(UnknownVec2.GetBytes()); - buffer.AddRange(Unknown7.GetBytes()); - buffer.AddRange(Unknown8.GetBytes()); - buffer.AddRange(EnableRotation.GetBytes()); - buffer.AddRange(Rotation.GetBytes()); - buffer.AddRange(AnimationSpeed.GetBytes()); - buffer.AddRange(UnknownShort1.GetBytes()); - buffer.AddRange(UnknownShort2.GetBytes()); - buffer.AddRange(UnknownVec4.GetBytes()); - buffer.AddRange(Unknown11.GetBytes()); - buffer.AddRange(Unknown12.GetBytes()); - buffer.AddRange(Unknown13.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/MLT/MLT.cs b/src/Parsec/Shaiya/MLT/MLT.cs deleted file mode 100644 index 408eaf9c..00000000 --- a/src/Parsec/Shaiya/MLT/MLT.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.MLT; - -public sealed class MLT : FileBase -{ - /// - /// File Signature. Read as char[3] - /// - public string Signature { get; set; } - - /// - /// List of .3DC object names - /// - public List Obj3DCNames { get; } = new(); - - /// - /// List of .dds texture names - /// - public List TextureNames { get; } = new(); - - /// - /// List of MLT records - /// - public List Records { get; } = new(); - - public override string Extension => "MLT"; - - public override void Read() - { - Signature = _binaryReader.ReadString(3); - - int obj3dcCount = _binaryReader.Read(); - for (int i = 0; i < obj3dcCount; i++) - { - string obj3dcName = _binaryReader.ReadString(); - Obj3DCNames.Add(obj3dcName); - } - - int textureNameCount = _binaryReader.Read(); - for (int i = 0; i < textureNameCount; i++) - { - string textureName = _binaryReader.ReadString(); - TextureNames.Add(textureName); - } - - int recordCount = _binaryReader.Read(); - for (int i = 0; i < recordCount; i++) - { - var record = new MLTRecord(_binaryReader); - Records.Add(record); - } - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Signature.GetBytes()); - - buffer.AddRange(Obj3DCNames.Count.GetBytes()); - foreach (string obj3dcName in Obj3DCNames) - buffer.AddRange(obj3dcName.GetLengthPrefixedBytes()); - - buffer.AddRange(TextureNames.Count.GetBytes()); - foreach (string textureName in TextureNames) - buffer.AddRange(textureName.GetLengthPrefixedBytes()); - - buffer.AddRange(Records.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/MLT/MLTRecord.cs b/src/Parsec/Shaiya/MLT/MLTRecord.cs deleted file mode 100644 index 7d852267..00000000 --- a/src/Parsec/Shaiya/MLT/MLTRecord.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.MLT; - -public sealed class MLTRecord : IBinary -{ - [JsonConstructor] - public MLTRecord() - { - } - - public MLTRecord(SBinaryReader binaryReader) - { - Obj3DCIndex = binaryReader.Read(); - TextureIndex = binaryReader.Read(); - Alpha = binaryReader.Read(); - } - - /// - /// Index of the .3DC filename - /// - public int Obj3DCIndex { get; set; } - - /// - /// Index of the .DDS filename - /// - public int TextureIndex { get; set; } - - /// - /// Alpha channel flag. 0: visibility 1: glow - /// - public int Alpha { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Obj3DCIndex.GetBytes()); - buffer.AddRange(TextureIndex.GetBytes()); - buffer.AddRange(Alpha.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/MLX/MLX.cs b/src/Parsec/Shaiya/MLX/MLX.cs deleted file mode 100644 index 1ff250f2..00000000 --- a/src/Parsec/Shaiya/MLX/MLX.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.MLX; - -/// -/// Represents a .MLX file, which is used as an index for 3DC-DDS combinations for each class/sex combination -/// -public sealed class MLX : FileBase -{ - public List Records { get; } = new(); - - public override string Extension => "MLX"; - - public MLXFormat Format { get; set; } = MLXFormat.MLX1; - - public override void Read() - { - // For some reason, sometimes MLX files are empty - if (_binaryReader.Length == 0) - return; - - string signature = _binaryReader.ReadString(4); - - if (signature == "MLX2") - Format = MLXFormat.MLX2; - else - _binaryReader.ResetOffset(); - - int recordCount = _binaryReader.Read(); - for (int i = 0; i < recordCount; i++) - Records.Add(new MLXRecord(_binaryReader, Format)); - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - - if (Format == MLXFormat.MLX2) - buffer.AddRange(MLXFormat.MLX2.ToString().GetBytes()); - - buffer.AddRange(Records.Count.GetBytes()); - foreach (var record in Records) - buffer.AddRange(record.GetBytes(Format)); - - return buffer; - } - - /// - /// Helper method to recalculate MLX record indices, just in case they get messed up or new records have been added - /// - public void RecalculateIndices() - { - for (int i = 0; i < Records.Count; i++) - { - var record = Records[i]; - record.Id = i; - } - } -} diff --git a/src/Parsec/Shaiya/MLX/MLXFormat.cs b/src/Parsec/Shaiya/MLX/MLXFormat.cs deleted file mode 100644 index 671abba1..00000000 --- a/src/Parsec/Shaiya/MLX/MLXFormat.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Parsec.Shaiya.MLX; - -public enum MLXFormat -{ - MLX1, - MLX2 -} diff --git a/src/Parsec/Shaiya/MLX/MLXRecord.cs b/src/Parsec/Shaiya/MLX/MLXRecord.cs deleted file mode 100644 index f0196ce8..00000000 --- a/src/Parsec/Shaiya/MLX/MLXRecord.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Text; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.MLX; - -public sealed class MLXRecord : IBinary -{ - [JsonConstructor] - public MLXRecord() - { - } - - public MLXRecord(SBinaryReader binaryReader, MLXFormat format) - { - var stringEncoding = format == MLXFormat.MLX2 ? Encoding.Unicode : Encoding.ASCII; - - Id = binaryReader.Read(); - Name = binaryReader.ReadString(stringEncoding); - - UpperTextureName = binaryReader.ReadString(stringEncoding); - Upper3DCName = binaryReader.ReadString(stringEncoding); - - if (format == MLXFormat.MLX2) - UpperNumber = binaryReader.Read(); - - LowerTextureName = binaryReader.ReadString(stringEncoding); - Lower3DCName = binaryReader.ReadString(stringEncoding); - - if (format == MLXFormat.MLX2) - LowerNumber = binaryReader.Read(); - - BootsTextureName = binaryReader.ReadString(stringEncoding); - Boots3DCName = binaryReader.ReadString(stringEncoding); - - if (format == MLXFormat.MLX2) - BootsNumber = binaryReader.Read(); - - HandsTextureName = binaryReader.ReadString(stringEncoding); - Hands3DCName = binaryReader.ReadString(stringEncoding); - - if (format == MLXFormat.MLX2) - HandsNumber = binaryReader.Read(); - } - - public int Id { get; set; } - public string Name { get; set; } - public string UpperTextureName { get; set; } - public string Upper3DCName { get; set; } - public int UpperNumber { get; set; } = 1; - public string LowerTextureName { get; set; } - public string Lower3DCName { get; set; } - public int LowerNumber { get; set; } = 1; - public string BootsTextureName { get; set; } - public string Boots3DCName { get; set; } - public int BootsNumber { get; set; } = 1; - public string HandsTextureName { get; set; } - public string Hands3DCName { get; set; } - public int HandsNumber { get; set; } = 1; - - public IEnumerable GetBytes(params object[] options) - { - var version = MLXFormat.MLX1; - - if (options.Length > 0) - version = (MLXFormat)options[0]; - - var stringEncoding = version == MLXFormat.MLX2 ? Encoding.Unicode : Encoding.ASCII; - - var buffer = new List(); - buffer.AddRange(Id.GetBytes()); - buffer.AddRange(Name.GetLengthPrefixedBytes(stringEncoding)); - buffer.AddRange(UpperTextureName.GetLengthPrefixedBytes(stringEncoding)); - buffer.AddRange(Upper3DCName.GetLengthPrefixedBytes(stringEncoding)); - - if (version == MLXFormat.MLX2) - buffer.AddRange(UpperNumber.GetBytes()); - - buffer.AddRange(LowerTextureName.GetLengthPrefixedBytes(stringEncoding)); - buffer.AddRange(Lower3DCName.GetLengthPrefixedBytes(stringEncoding)); - - if (version == MLXFormat.MLX2) - buffer.AddRange(LowerNumber.GetBytes()); - - buffer.AddRange(BootsTextureName.GetLengthPrefixedBytes(stringEncoding)); - buffer.AddRange(Boots3DCName.GetLengthPrefixedBytes(stringEncoding)); - - if (version == MLXFormat.MLX2) - buffer.AddRange(BootsNumber.GetBytes()); - - buffer.AddRange(HandsTextureName.GetLengthPrefixedBytes(stringEncoding)); - buffer.AddRange(Hands3DCName.GetLengthPrefixedBytes(stringEncoding)); - - if (version == MLXFormat.MLX2) - buffer.AddRange(HandsNumber.GetBytes()); - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/MON/MON.cs b/src/Parsec/Shaiya/MON/MON.cs deleted file mode 100644 index 4d28250e..00000000 --- a/src/Parsec/Shaiya/MON/MON.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.MON; - -public sealed class MON : FileBase -{ - /// - /// File signature. "MO2", "MO4". Read as char[3] - /// - public string Signature { get; set; } - - public MONFormat Format { get; set; } = MONFormat.MO2; - - public List Records { get; } = new(); - - public override string Extension => "MON"; - - public override void Read() - { - Signature = _binaryReader.ReadString(3); - - switch (Signature) - { - case "MO2": - default: - Format = MONFormat.MO2; - break; - case "MO4": - Format = MONFormat.MO4; - break; - } - - int recordCount = _binaryReader.Read(); - for (int i = 0; i < recordCount; i++) - { - var record = new MONRecord(_binaryReader, Format); - Records.Add(record); - } - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Format.ToString().GetBytes()); - - buffer.AddRange(Records.Count.GetBytes()); - foreach (var record in Records) - buffer.AddRange(record.GetBytes(Format)); - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/MON/MONEffect.cs b/src/Parsec/Shaiya/MON/MONEffect.cs deleted file mode 100644 index 677198d0..00000000 --- a/src/Parsec/Shaiya/MON/MONEffect.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.MON; - -public class MONEffect : IBinary -{ - public int BoneId { get; set; } - public int EffectId { get; set; } - - [JsonConstructor] - public MONEffect() - { - } - - public MONEffect(SBinaryReader binaryReader) - { - BoneId = binaryReader.Read(); - EffectId = binaryReader.Read(); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(BoneId.GetBytes()); - buffer.AddRange(EffectId.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/MON/MONFormat.cs b/src/Parsec/Shaiya/MON/MONFormat.cs deleted file mode 100644 index 4f91fe87..00000000 --- a/src/Parsec/Shaiya/MON/MONFormat.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Parsec.Shaiya.MON; - -public enum MONFormat -{ - MO2, - MO4 -} diff --git a/src/Parsec/Shaiya/MON/MONObject.cs b/src/Parsec/Shaiya/MON/MONObject.cs deleted file mode 100644 index 1aed6cfa..00000000 --- a/src/Parsec/Shaiya/MON/MONObject.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.MON; - -public sealed class MONObject : IBinary -{ - [JsonConstructor] - public MONObject() - { - } - - public MONObject(SBinaryReader binaryReader) - { - Object3DCName = binaryReader.ReadString(); - TextureName = binaryReader.ReadString(); - } - - public string Object3DCName { get; set; } - public string TextureName { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Object3DCName.GetLengthPrefixedBytes(false)); - buffer.AddRange(TextureName.GetLengthPrefixedBytes(false)); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/MON/MONRecord.cs b/src/Parsec/Shaiya/MON/MONRecord.cs deleted file mode 100644 index 6475e9ac..00000000 --- a/src/Parsec/Shaiya/MON/MONRecord.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.MON; - -public sealed class MONRecord : IBinary -{ - [JsonConstructor] - public MONRecord() - { - } - - public MONRecord(SBinaryReader binaryReader, MONFormat format) - { - Name = binaryReader.ReadString(); - Unknown = binaryReader.Read(); - WalkAnimation = binaryReader.ReadString(); - RunAnimation = binaryReader.ReadString(); - JumpAttack1Animation = binaryReader.ReadString(); - Attack2Animation = binaryReader.ReadString(); - Attack3Animation = binaryReader.ReadString(); - DeathAnimation = binaryReader.ReadString(); - BreathAnimation = binaryReader.ReadString(); - DamageAnimation = binaryReader.ReadString(); - IdleAnimation = binaryReader.ReadString(); - - Attack1Wav = binaryReader.ReadString(); - Attack2Wav = binaryReader.ReadString(); - Attack3Wav = binaryReader.ReadString(); - DeathWav = binaryReader.ReadString(); - - Attack1Effect = binaryReader.ReadString(); - Attack2Effect = binaryReader.ReadString(); - Attack3Effect = binaryReader.ReadString(); - DieEffect = binaryReader.ReadString(); - - if (format == MONFormat.MO4) - AttachEffect = binaryReader.ReadString(); - - var objectCount = binaryReader.Read(); - - for (int i = 0; i < objectCount; i++) - { - var obj = new MONObject(binaryReader); - Objects.Add(obj); - } - - Height = binaryReader.Read(); - - var effectCount = binaryReader.Read(); - - for (int i = 0; i < effectCount; i++) - { - var effect = new MONEffect(binaryReader); - Effects.Add(effect); - } - } - - public string Name { get; set; } - public byte Unknown { get; set; } - public string WalkAnimation { get; set; } - public string RunAnimation { get; set; } - - /// - /// Jump or Attack1 animation. Jump for vehicles, Attack1 for mobs. - /// - public string JumpAttack1Animation { get; set; } - - public string Attack2Animation { get; set; } - public string Attack3Animation { get; set; } - public string DeathAnimation { get; set; } - public string BreathAnimation { get; set; } - public string DamageAnimation { get; set; } - public string IdleAnimation { get; set; } - - public string Attack1Wav { get; set; } - public string Attack2Wav { get; set; } - public string Attack3Wav { get; set; } - public string DeathWav { get; set; } - - public string Attack1Effect { get; set; } - public string Attack2Effect { get; set; } - public string Attack3Effect { get; set; } - public string DieEffect { get; set; } - public string AttachEffect { get; set; } - - public List Objects { get; } = new(); - - public float Height { get; set; } - - public List Effects { get; } = new(); - - public IEnumerable GetBytes(params object[] options) - { - var outputFormat = MONFormat.MO2; - - if (options.Length > 0) - outputFormat = (MONFormat)options[0]; - - var buffer = new List(); - buffer.AddRange(Name.GetLengthPrefixedBytes(false)); - buffer.Add(Unknown); - buffer.AddRange(WalkAnimation.GetLengthPrefixedBytes(false)); - buffer.AddRange(RunAnimation.GetLengthPrefixedBytes(false)); - buffer.AddRange(JumpAttack1Animation.GetLengthPrefixedBytes(false)); - - buffer.AddRange(Attack2Animation.GetLengthPrefixedBytes(false)); - buffer.AddRange(Attack3Animation.GetLengthPrefixedBytes(false)); - buffer.AddRange(DeathAnimation.GetLengthPrefixedBytes(false)); - buffer.AddRange(BreathAnimation.GetLengthPrefixedBytes(false)); - buffer.AddRange(DamageAnimation.GetLengthPrefixedBytes(false)); - buffer.AddRange(IdleAnimation.GetLengthPrefixedBytes(false)); - - buffer.AddRange(Attack1Wav.GetLengthPrefixedBytes(false)); - buffer.AddRange(Attack2Wav.GetLengthPrefixedBytes(false)); - buffer.AddRange(Attack3Wav.GetLengthPrefixedBytes(false)); - buffer.AddRange(DeathWav.GetLengthPrefixedBytes(false)); - - buffer.AddRange(Attack1Effect.GetLengthPrefixedBytes(false)); - buffer.AddRange(Attack2Effect.GetLengthPrefixedBytes(false)); - buffer.AddRange(Attack3Effect.GetLengthPrefixedBytes(false)); - buffer.AddRange(DieEffect.GetLengthPrefixedBytes(false)); - - if (outputFormat == MONFormat.MO4) - buffer.AddRange(AttachEffect.GetLengthPrefixedBytes(false)); - - buffer.AddRange(Objects.GetBytes()); - buffer.AddRange(Height.GetBytes()); - buffer.AddRange(Effects.GetBytes()); - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Mani/Mani.cs b/src/Parsec/Shaiya/Mani/Mani.cs new file mode 100644 index 00000000..13e85483 --- /dev/null +++ b/src/Parsec/Shaiya/Mani/Mani.cs @@ -0,0 +1,114 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Mani; + +/// +/// Represents a .mani file. Its used to define an animation for an SMOD object, that's where the extension comes +/// from, given by "Mesh Animation" +/// +public sealed class Mani : FileBase +{ + /// + /// Always 0x21 = 33 + /// + public int Version { get; set; } + + public int Unknown1 { get; set; } + + public Vector3 UnknownVec1 { get; set; } + + public float Unknown2 { get; set; } + + public float Unknown3 { get; set; } + + public float Unknown4 { get; set; } + + public int Unknown5 { get; set; } + + public int Unknown6 { get; set; } + + public Vector3 UnknownVec2 { get; set; } + + public float Unknown7 { get; set; } + + public float Unknown8 { get; set; } + + /// + /// Indicates if the object should rotate + /// + public int EnableRotation { get; set; } + + /// + /// Indicates the axis of rotation of the object, -1f or 1f values should be used + /// + public Vector3 Rotation { get; set; } + + /// + /// Animation Speed [0.0f, 1.0f] + /// + public float AnimationSpeed { get; set; } + + public short UnknownShort1 { get; set; } + + public short UnknownShort2 { get; set; } + + public Vector3 UnknownVec4 { get; set; } + + public float Unknown11 { get; set; } + + public float Unknown12 { get; set; } + + public int Unknown13 { get; set; } + + public override string Extension => "mani"; + + protected override void Read(SBinaryReader binaryReader) + { + Version = binaryReader.ReadInt32(); + Unknown1 = binaryReader.ReadInt32(); + UnknownVec1 = binaryReader.Read(); + Unknown2 = binaryReader.ReadSingle(); + Unknown3 = binaryReader.ReadSingle(); + Unknown4 = binaryReader.ReadSingle(); + Unknown5 = binaryReader.ReadInt32(); + Unknown6 = binaryReader.ReadInt32(); + UnknownVec2 = binaryReader.Read(); + Unknown7 = binaryReader.ReadSingle(); + Unknown8 = binaryReader.ReadSingle(); + EnableRotation = binaryReader.ReadInt32(); + Rotation = binaryReader.Read(); + AnimationSpeed = binaryReader.ReadSingle(); + UnknownShort1 = binaryReader.ReadInt16(); + UnknownShort2 = binaryReader.ReadInt16(); + UnknownVec4 = binaryReader.Read(); + Unknown11 = binaryReader.ReadSingle(); + Unknown12 = binaryReader.ReadSingle(); + Unknown13 = binaryReader.ReadInt32(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Version); + binaryWriter.Write(Unknown1); + binaryWriter.Write(UnknownVec1); + binaryWriter.Write(Unknown2); + binaryWriter.Write(Unknown3); + binaryWriter.Write(Unknown4); + binaryWriter.Write(Unknown5); + binaryWriter.Write(Unknown6); + binaryWriter.Write(UnknownVec2); + binaryWriter.Write(Unknown7); + binaryWriter.Write(Unknown8); + binaryWriter.Write(EnableRotation); + binaryWriter.Write(Rotation); + binaryWriter.Write(AnimationSpeed); + binaryWriter.Write(UnknownShort1); + binaryWriter.Write(UnknownShort2); + binaryWriter.Write(UnknownVec4); + binaryWriter.Write(Unknown11); + binaryWriter.Write(Unknown12); + binaryWriter.Write(Unknown13); + } +} diff --git a/src/Parsec/Shaiya/Mlt/Mlt.cs b/src/Parsec/Shaiya/Mlt/Mlt.cs new file mode 100644 index 00000000..c51543fa --- /dev/null +++ b/src/Parsec/Shaiya/Mlt/Mlt.cs @@ -0,0 +1,70 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Mlt; + +public sealed class Mlt : FileBase +{ + /// + /// File Signature. Read as char[3] + /// + public string Signature { get; set; } = string.Empty; + + /// + /// List of .3DC object names + /// + public List MeshNames { get; set; } = new(); + + /// + /// List of .dds texture names + /// + public List TextureNames { get; set; } = new(); + + /// + /// List of MLT records + /// + public List Records { get; set; } = new(); + + public override string Extension => "MLT"; + + protected override void Read(SBinaryReader binaryReader) + { + Signature = binaryReader.ReadString(3); + + var meshNameCount = binaryReader.ReadInt32(); + for (var i = 0; i < meshNameCount; i++) + { + var meshName = binaryReader.ReadString(); + MeshNames.Add(meshName); + } + + var textureNameCount = binaryReader.ReadInt32(); + for (var i = 0; i < textureNameCount; i++) + { + var textureName = binaryReader.ReadString(); + TextureNames.Add(textureName); + } + + Records = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Signature, isLengthPrefixed: false, includeStringTerminator: false); + + binaryWriter.Write(MeshNames.Count); + foreach (var meshName in MeshNames) + { + binaryWriter.Write(meshName); + } + + binaryWriter.Write(TextureNames.Count); + foreach (var textureName in TextureNames) + { + binaryWriter.Write(textureName); + } + + binaryWriter.Write(Records.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Mlt/MltRecord.cs b/src/Parsec/Shaiya/Mlt/MltRecord.cs new file mode 100644 index 00000000..f51d6e94 --- /dev/null +++ b/src/Parsec/Shaiya/Mlt/MltRecord.cs @@ -0,0 +1,37 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Mlt; + +public sealed class MltRecord : ISerializable +{ + /// + /// Index of the .3DC filename + /// + public int MeshIndex { get; set; } + + /// + /// Index of the .DDS filename + /// + public int TextureIndex { get; set; } + + /// + /// Alpha blending mode + /// + public AlphaBlendingMode AlphaBlendingMode { get; set; } + + public void Read(SBinaryReader binaryReader) + { + MeshIndex = binaryReader.ReadInt32(); + TextureIndex = binaryReader.ReadInt32(); + AlphaBlendingMode = (AlphaBlendingMode)binaryReader.ReadInt32(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(MeshIndex); + binaryWriter.Write(TextureIndex); + binaryWriter.Write((int)AlphaBlendingMode); + } +} diff --git a/src/Parsec/Shaiya/Mlx/Mlx.cs b/src/Parsec/Shaiya/Mlx/Mlx.cs new file mode 100644 index 00000000..462e05c1 --- /dev/null +++ b/src/Parsec/Shaiya/Mlx/Mlx.cs @@ -0,0 +1,54 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Mlx; + +/// +/// Represents a .MLX file, which is used as an index for 3DC-DDS combinations for each class/sex combination +/// +public sealed class Mlx : FileBase +{ + public MlxFormat Format { get; set; } = MlxFormat.MLX1; + + public List Records { get; set; } = new(); + + public override string Extension => "MLX"; + + protected override void Read(SBinaryReader binaryReader) + { + // Some MLX files are empty + if (binaryReader.StreamLength == 0) + { + return; + } + + var signature = binaryReader.ReadString(4); + + if (signature == "MLX2") + { + Format = MlxFormat.MLX2; + } + else + { + Format = MlxFormat.MLX1; + binaryReader.ResetOffset(); + } + + // MlxRecord instances expect the Format to be set as the ExtraOption on the serialization options + binaryReader.SerializationOptions.ExtraOption = Format; + Records = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + if (Format == MlxFormat.MLX2) + { + binaryWriter.Write("MLX2", isLengthPrefixed: false, includeStringTerminator: false); + } + + // MlxRecord instances expect the Format to be set as the ExtraOption on the serialization options + binaryWriter.SerializationOptions.ExtraOption = Format; + binaryWriter.Write(Records.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Mlx/MlxFormat.cs b/src/Parsec/Shaiya/Mlx/MlxFormat.cs new file mode 100644 index 00000000..6b06c7f9 --- /dev/null +++ b/src/Parsec/Shaiya/Mlx/MlxFormat.cs @@ -0,0 +1,7 @@ +namespace Parsec.Shaiya.Mlx; + +public enum MlxFormat +{ + MLX1, + MLX2 +} diff --git a/src/Parsec/Shaiya/Mlx/MlxRecord.cs b/src/Parsec/Shaiya/Mlx/MlxRecord.cs new file mode 100644 index 00000000..b95b849b --- /dev/null +++ b/src/Parsec/Shaiya/Mlx/MlxRecord.cs @@ -0,0 +1,134 @@ +using System.Text; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Mlx; + +public sealed class MlxRecord : ISerializable +{ + public int Id { get; set; } + + public string Name { get; set; } = string.Empty; + + public string UpperTextureName { get; set; } = string.Empty; + + public string UpperMeshName { get; set; } = string.Empty; + + public int UpperNumber { get; set; } = 1; + + public string LowerTextureName { get; set; } = string.Empty; + + public string LowerMeshName { get; set; } = string.Empty; + + public int LowerNumber { get; set; } = 1; + + public string BootsTextureName { get; set; } = string.Empty; + + public string BootsMeshName { get; set; } = string.Empty; + + public int BootsNumber { get; set; } = 1; + + public string HandsTextureName { get; set; } = string.Empty; + + public string HandsMeshName { get; set; } = string.Empty; + + public int HandsNumber { get; set; } = 1; + + public void Read(SBinaryReader binaryReader) + { + var format = MlxFormat.MLX1; + + if (binaryReader.SerializationOptions.ExtraOption is MlxFormat optionFormat) + { + format = optionFormat; + } + + // Define encoding based on format + var stringEncoding = format == MlxFormat.MLX2 ? Encoding.Unicode : Encoding.ASCII; + binaryReader.SerializationOptions.Encoding = stringEncoding; + + Id = binaryReader.ReadInt32(); + Name = binaryReader.ReadString(); + + UpperTextureName = binaryReader.ReadString(); + UpperMeshName = binaryReader.ReadString(); + + if (format == MlxFormat.MLX2) + { + UpperNumber = binaryReader.ReadInt32(); + } + + LowerTextureName = binaryReader.ReadString(); + LowerMeshName = binaryReader.ReadString(); + + if (format == MlxFormat.MLX2) + { + LowerNumber = binaryReader.ReadInt32(); + } + + BootsTextureName = binaryReader.ReadString(); + BootsMeshName = binaryReader.ReadString(); + + if (format == MlxFormat.MLX2) + { + BootsNumber = binaryReader.ReadInt32(); + } + + HandsTextureName = binaryReader.ReadString(); + HandsMeshName = binaryReader.ReadString(); + + if (format == MlxFormat.MLX2) + { + HandsNumber = binaryReader.ReadInt32(); + } + } + + public void Write(SBinaryWriter binaryWriter) + { + var format = MlxFormat.MLX1; + + if (binaryWriter.SerializationOptions.ExtraOption is MlxFormat optionFormat) + { + format = optionFormat; + } + + // Define encoding based on format + var stringEncoding = format == MlxFormat.MLX2 ? Encoding.Unicode : Encoding.ASCII; + binaryWriter.SerializationOptions.Encoding = stringEncoding; + + binaryWriter.Write(Id); + binaryWriter.Write(Name); + + binaryWriter.Write(UpperTextureName); + binaryWriter.Write(UpperMeshName); + + if (format == MlxFormat.MLX2) + { + binaryWriter.Write(UpperNumber); + } + + binaryWriter.Write(LowerTextureName); + binaryWriter.Write(LowerMeshName); + + if (format == MlxFormat.MLX2) + { + binaryWriter.Write(LowerNumber); + } + + binaryWriter.Write(BootsTextureName); + binaryWriter.Write(BootsMeshName); + + if (format == MlxFormat.MLX2) + { + binaryWriter.Write(BootsNumber); + } + + binaryWriter.Write(HandsTextureName); + binaryWriter.Write(HandsMeshName); + + if (format == MlxFormat.MLX2) + { + binaryWriter.Write(HandsNumber); + } + } +} diff --git a/src/Parsec/Shaiya/Mon/Mon.cs b/src/Parsec/Shaiya/Mon/Mon.cs new file mode 100644 index 00000000..d9c0bb3c --- /dev/null +++ b/src/Parsec/Shaiya/Mon/Mon.cs @@ -0,0 +1,48 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Mon; + +public sealed class Mon : FileBase +{ + /// + /// File signature. "MO2", "MO4". Read as char[3] + /// + public string Signature { get; set; } = string.Empty; + + public MonFormat Format { get; set; } = MonFormat.MO2; + + public List Records { get; set; } = new(); + + public override string Extension => "MON"; + + protected override void Read(SBinaryReader binaryReader) + { + Signature = binaryReader.ReadString(3); + + switch (Signature) + { + case "MO2": + default: + Format = MonFormat.MO2; + break; + case "MO4": + Format = MonFormat.MO4; + break; + } + + // Record instances expect the Format to be set as the ExtraOption on the serialization options + binaryReader.SerializationOptions.ExtraOption = Format; + Records = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + // Record instances expect the Format to be set as the ExtraOption on the serialization options + binaryWriter.SerializationOptions.ExtraOption = Format; + + binaryWriter.Write(Signature, isLengthPrefixed: false, includeStringTerminator: false); + binaryWriter.Write(Records.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Mon/MonEffect.cs b/src/Parsec/Shaiya/Mon/MonEffect.cs new file mode 100644 index 00000000..fbd16b92 --- /dev/null +++ b/src/Parsec/Shaiya/Mon/MonEffect.cs @@ -0,0 +1,23 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Mon; + +public class MonEffect : ISerializable +{ + public int BoneId { get; set; } + + public int EffectId { get; set; } + + public void Read(SBinaryReader binaryReader) + { + BoneId = binaryReader.ReadInt32(); + EffectId = binaryReader.ReadInt32(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(BoneId); + binaryWriter.Write(EffectId); + } +} diff --git a/src/Parsec/Shaiya/Mon/MonFormat.cs b/src/Parsec/Shaiya/Mon/MonFormat.cs new file mode 100644 index 00000000..dc5b946b --- /dev/null +++ b/src/Parsec/Shaiya/Mon/MonFormat.cs @@ -0,0 +1,7 @@ +namespace Parsec.Shaiya.Mon; + +public enum MonFormat +{ + MO2, + MO4 +} diff --git a/src/Parsec/Shaiya/Mon/MonObject.cs b/src/Parsec/Shaiya/Mon/MonObject.cs new file mode 100644 index 00000000..eefe645f --- /dev/null +++ b/src/Parsec/Shaiya/Mon/MonObject.cs @@ -0,0 +1,29 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Mon; + +public sealed class MonObject : ISerializable +{ + /// + /// Name of the .3DC mesh file + /// + public string MeshName { get; set; } = string.Empty; + + /// + /// Name of the .dds texture file + /// + public string TextureName { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + MeshName = binaryReader.ReadString(); + TextureName = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(MeshName); + binaryWriter.Write(TextureName); + } +} diff --git a/src/Parsec/Shaiya/Mon/MonRecord.cs b/src/Parsec/Shaiya/Mon/MonRecord.cs new file mode 100644 index 00000000..c7dec0d1 --- /dev/null +++ b/src/Parsec/Shaiya/Mon/MonRecord.cs @@ -0,0 +1,139 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Mon; + +public sealed class MonRecord : ISerializable +{ + public string Name { get; set; } = string.Empty; + + public byte Unknown { get; set; } + + public string WalkAnimation { get; set; } = string.Empty; + + public string RunAnimation { get; set; } = string.Empty; + + /// + /// Jump or Attack1 animation. Jump for vehicles, Attack1 for mobs. + /// + public string JumpAttack1Animation { get; set; } = string.Empty; + + public string Attack2Animation { get; set; } = string.Empty; + + public string Attack3Animation { get; set; } = string.Empty; + + public string DeathAnimation { get; set; } = string.Empty; + + public string BreathAnimation { get; set; } = string.Empty; + + public string DamageAnimation { get; set; } = string.Empty; + + public string IdleAnimation { get; set; } = string.Empty; + + public string Attack1Wav { get; set; } = string.Empty; + + public string Attack2Wav { get; set; } = string.Empty; + + public string Attack3Wav { get; set; } = string.Empty; + + public string DeathWav { get; set; } = string.Empty; + + public string Attack1Effect { get; set; } = string.Empty; + + public string Attack2Effect { get; set; } = string.Empty; + + public string Attack3Effect { get; set; } = string.Empty; + + public string DieEffect { get; set; } = string.Empty; + + public string AttachEffect { get; set; } = string.Empty; + + public List Objects { get; set; } = new(); + + public float Height { get; set; } + + public List Effects { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + var format = MonFormat.MO2; + + if (binaryReader.SerializationOptions.ExtraOption is MonFormat optionFormat) + { + format = optionFormat; + } + + Name = binaryReader.ReadString(); + Unknown = binaryReader.ReadByte(); + WalkAnimation = binaryReader.ReadString(); + RunAnimation = binaryReader.ReadString(); + JumpAttack1Animation = binaryReader.ReadString(); + Attack2Animation = binaryReader.ReadString(); + Attack3Animation = binaryReader.ReadString(); + DeathAnimation = binaryReader.ReadString(); + BreathAnimation = binaryReader.ReadString(); + DamageAnimation = binaryReader.ReadString(); + IdleAnimation = binaryReader.ReadString(); + + Attack1Wav = binaryReader.ReadString(); + Attack2Wav = binaryReader.ReadString(); + Attack3Wav = binaryReader.ReadString(); + DeathWav = binaryReader.ReadString(); + + Attack1Effect = binaryReader.ReadString(); + Attack2Effect = binaryReader.ReadString(); + Attack3Effect = binaryReader.ReadString(); + DieEffect = binaryReader.ReadString(); + + if (format == MonFormat.MO4) + { + AttachEffect = binaryReader.ReadString(); + } + + Objects = binaryReader.ReadList().ToList(); + Height = binaryReader.ReadSingle(); + Effects = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + var format = MonFormat.MO2; + + if (binaryWriter.SerializationOptions.ExtraOption is MonFormat optionFormat) + { + format = optionFormat; + } + + binaryWriter.Write(Name); + binaryWriter.Write(Unknown); + binaryWriter.Write(WalkAnimation); + binaryWriter.Write(RunAnimation); + binaryWriter.Write(JumpAttack1Animation); + binaryWriter.Write(Attack2Animation); + binaryWriter.Write(Attack3Animation); + binaryWriter.Write(DeathAnimation); + binaryWriter.Write(BreathAnimation); + binaryWriter.Write(DamageAnimation); + binaryWriter.Write(IdleAnimation); + + binaryWriter.Write(Attack1Wav); + binaryWriter.Write(Attack2Wav); + binaryWriter.Write(Attack3Wav); + binaryWriter.Write(DeathWav); + + binaryWriter.Write(Attack1Effect); + binaryWriter.Write(Attack2Effect); + binaryWriter.Write(Attack3Effect); + binaryWriter.Write(DieEffect); + + if (format == MonFormat.MO4) + { + binaryWriter.Write(AttachEffect); + } + + binaryWriter.Write(Objects.ToSerializable()); + binaryWriter.Write(Height); + binaryWriter.Write(Effects.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Monster/DBMonsterDataRecord.cs b/src/Parsec/Shaiya/Monster/DBMonsterDataRecord.cs index cd2fd85b..b53f0552 100644 --- a/src/Parsec/Shaiya/Monster/DBMonsterDataRecord.cs +++ b/src/Parsec/Shaiya/Monster/DBMonsterDataRecord.cs @@ -1,289 +1,315 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.Monster; public sealed class DBMonsterDataRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] public long Image { get; set; } - [ShaiyaProperty] public long Level { get; set; } - [ShaiyaProperty] public long Exp { get; set; } - [ShaiyaProperty] public long Ai { get; set; } - [ShaiyaProperty] public long Money1 { get; set; } - [ShaiyaProperty] public long Money2 { get; set; } - [ShaiyaProperty] public long Item1 { get; set; } - [ShaiyaProperty] public long ItemDropRate1 { get; set; } - [ShaiyaProperty] public long Item2 { get; set; } - [ShaiyaProperty] public long ItemDropRate2 { get; set; } - [ShaiyaProperty] public long Item3 { get; set; } - [ShaiyaProperty] public long ItemDropRate3 { get; set; } - [ShaiyaProperty] public long Item4 { get; set; } - [ShaiyaProperty] public long ItemDropRate4 { get; set; } - [ShaiyaProperty] public long Item5 { get; set; } - [ShaiyaProperty] public long ItemDropRate5 { get; set; } - [ShaiyaProperty] public long Item6 { get; set; } - [ShaiyaProperty] public long ItemDropRate6 { get; set; } - [ShaiyaProperty] public long Item7 { get; set; } - [ShaiyaProperty] public long ItemDropRate7 { get; set; } - [ShaiyaProperty] public long Item8 { get; set; } - [ShaiyaProperty] public long ItemDropRate8 { get; set; } - [ShaiyaProperty] public long Item9 { get; set; } - [ShaiyaProperty] public long ItemDropRate9 { get; set; } - [ShaiyaProperty] public long QuestItem { get; set; } - [ShaiyaProperty] public long Hp { get; set; } - [ShaiyaProperty] public long Sp { get; set; } - [ShaiyaProperty] public long Mp { get; set; } - [ShaiyaProperty] public long Dex { get; set; } - [ShaiyaProperty] public long Wis { get; set; } - [ShaiyaProperty] public long Luc { get; set; } - [ShaiyaProperty] public long Day { get; set; } - [ShaiyaProperty] public long Size { get; set; } - [ShaiyaProperty] public long Attrib { get; set; } - [ShaiyaProperty] public long Defense { get; set; } - [ShaiyaProperty] public long Magic { get; set; } - [ShaiyaProperty] public long State1 { get; set; } - [ShaiyaProperty] public long State2 { get; set; } - [ShaiyaProperty] public long State3 { get; set; } - [ShaiyaProperty] public long State4 { get; set; } - [ShaiyaProperty] public long State5 { get; set; } - [ShaiyaProperty] public long State6 { get; set; } - [ShaiyaProperty] public long State7 { get; set; } - [ShaiyaProperty] public long State8 { get; set; } - [ShaiyaProperty] public long State9 { get; set; } - [ShaiyaProperty] public long State10 { get; set; } - [ShaiyaProperty] public long State11 { get; set; } - [ShaiyaProperty] public long State12 { get; set; } - [ShaiyaProperty] public long State13 { get; set; } - [ShaiyaProperty] public long State14 { get; set; } - [ShaiyaProperty] public long State15 { get; set; } - [ShaiyaProperty] public long Skill1 { get; set; } - [ShaiyaProperty] public long Skill2 { get; set; } - [ShaiyaProperty] public long Skill3 { get; set; } - [ShaiyaProperty] public long Skill4 { get; set; } - [ShaiyaProperty] public long Skill5 { get; set; } - [ShaiyaProperty] public long Skill6 { get; set; } - [ShaiyaProperty] public long NormalTime { get; set; } - [ShaiyaProperty] public long NormalStep { get; set; } - [ShaiyaProperty] public long ChaseTime { get; set; } - [ShaiyaProperty] public long ChaseStep { get; set; } - [ShaiyaProperty] public long ChaseRange { get; set; } - [ShaiyaProperty] public long AttackAni1 { get; set; } - [ShaiyaProperty] public long AttackType1 { get; set; } - [ShaiyaProperty] public long AttackTime1 { get; set; } - [ShaiyaProperty] public long AttackRange1 { get; set; } - [ShaiyaProperty] public long Attack1 { get; set; } - [ShaiyaProperty] public long AttackPlus1 { get; set; } - [ShaiyaProperty] public long AttackAttrib1 { get; set; } - [ShaiyaProperty] public long AttackSpecial1 { get; set; } - [ShaiyaProperty] public long AttackOk1 { get; set; } - [ShaiyaProperty] public long AttackAni2 { get; set; } - [ShaiyaProperty] public long AttackType2 { get; set; } - [ShaiyaProperty] public long AttackTime2 { get; set; } - [ShaiyaProperty] public long AttackRange2 { get; set; } - [ShaiyaProperty] public long Attack2 { get; set; } - [ShaiyaProperty] public long AttackPlus2 { get; set; } - [ShaiyaProperty] public long AttackAttrib2 { get; set; } - [ShaiyaProperty] public long AttackSpecial2 { get; set; } - [ShaiyaProperty] public long AttackOk2 { get; set; } - [ShaiyaProperty] public long AttackAni3 { get; set; } - [ShaiyaProperty] public long AttackType3 { get; set; } - [ShaiyaProperty] public long AttackTime3 { get; set; } - [ShaiyaProperty] public long AttackRange3 { get; set; } - [ShaiyaProperty] public long Attack3 { get; set; } - [ShaiyaProperty] public long AttackPlus3 { get; set; } - [ShaiyaProperty] public long AttackAttrib3 { get; set; } - [ShaiyaProperty] public long AttackSpecial3 { get; set; } - [ShaiyaProperty] public long AttackOk3 { get; set; } - [ShaiyaProperty] public long ColorType { get; set; } - [ShaiyaProperty] public long ColorHue { get; set; } - [ShaiyaProperty] public long ColorSaturation { get; set; } - [ShaiyaProperty] public long ColorLight { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + Image = binaryReader.ReadInt64(); + Level = binaryReader.ReadInt64(); + Exp = binaryReader.ReadInt64(); + Ai = binaryReader.ReadInt64(); + Money1 = binaryReader.ReadInt64(); + Money2 = binaryReader.ReadInt64(); + Item1 = binaryReader.ReadInt64(); + ItemDropRate1 = binaryReader.ReadInt64(); + Item2 = binaryReader.ReadInt64(); + ItemDropRate2 = binaryReader.ReadInt64(); + Item3 = binaryReader.ReadInt64(); + ItemDropRate3 = binaryReader.ReadInt64(); + Item4 = binaryReader.ReadInt64(); + ItemDropRate4 = binaryReader.ReadInt64(); + Item5 = binaryReader.ReadInt64(); + ItemDropRate5 = binaryReader.ReadInt64(); + Item6 = binaryReader.ReadInt64(); + ItemDropRate6 = binaryReader.ReadInt64(); + Item7 = binaryReader.ReadInt64(); + ItemDropRate7 = binaryReader.ReadInt64(); + Item8 = binaryReader.ReadInt64(); + ItemDropRate8 = binaryReader.ReadInt64(); + Item9 = binaryReader.ReadInt64(); + ItemDropRate9 = binaryReader.ReadInt64(); + QuestItem = binaryReader.ReadInt64(); + Hp = binaryReader.ReadInt64(); + Sp = binaryReader.ReadInt64(); + Mp = binaryReader.ReadInt64(); + Dex = binaryReader.ReadInt64(); + Wis = binaryReader.ReadInt64(); + Luc = binaryReader.ReadInt64(); + Day = binaryReader.ReadInt64(); + Size = binaryReader.ReadInt64(); + Attrib = binaryReader.ReadInt64(); + Defense = binaryReader.ReadInt64(); + Magic = binaryReader.ReadInt64(); + State1 = binaryReader.ReadInt64(); + State2 = binaryReader.ReadInt64(); + State3 = binaryReader.ReadInt64(); + State4 = binaryReader.ReadInt64(); + State5 = binaryReader.ReadInt64(); + State6 = binaryReader.ReadInt64(); + State7 = binaryReader.ReadInt64(); + State8 = binaryReader.ReadInt64(); + State9 = binaryReader.ReadInt64(); + State10 = binaryReader.ReadInt64(); + State11 = binaryReader.ReadInt64(); + State12 = binaryReader.ReadInt64(); + State13 = binaryReader.ReadInt64(); + State14 = binaryReader.ReadInt64(); + State15 = binaryReader.ReadInt64(); + Skill1 = binaryReader.ReadInt64(); + Skill2 = binaryReader.ReadInt64(); + Skill3 = binaryReader.ReadInt64(); + Skill4 = binaryReader.ReadInt64(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(Image); + binaryWriter.Write(Level); + binaryWriter.Write(Exp); + binaryWriter.Write(Ai); + binaryWriter.Write(Money1); + binaryWriter.Write(Money2); + binaryWriter.Write(Item1); + binaryWriter.Write(ItemDropRate1); + binaryWriter.Write(Item2); + binaryWriter.Write(ItemDropRate2); + binaryWriter.Write(Item3); + binaryWriter.Write(ItemDropRate3); + binaryWriter.Write(Item4); + binaryWriter.Write(ItemDropRate4); + binaryWriter.Write(Item5); + binaryWriter.Write(ItemDropRate5); + binaryWriter.Write(Item6); + binaryWriter.Write(ItemDropRate6); + binaryWriter.Write(Item7); + binaryWriter.Write(ItemDropRate7); + binaryWriter.Write(Item8); + binaryWriter.Write(ItemDropRate8); + binaryWriter.Write(Item9); + binaryWriter.Write(ItemDropRate9); + binaryWriter.Write(QuestItem); + binaryWriter.Write(Hp); + binaryWriter.Write(Sp); + binaryWriter.Write(Mp); + binaryWriter.Write(Dex); + binaryWriter.Write(Wis); + binaryWriter.Write(Luc); + binaryWriter.Write(Day); + binaryWriter.Write(Size); + binaryWriter.Write(Attrib); + binaryWriter.Write(Defense); + binaryWriter.Write(Magic); + binaryWriter.Write(State1); + binaryWriter.Write(State2); + binaryWriter.Write(State3); + binaryWriter.Write(State4); + binaryWriter.Write(State5); + binaryWriter.Write(State6); + binaryWriter.Write(State7); + binaryWriter.Write(State8); + binaryWriter.Write(State9); + binaryWriter.Write(State10); + binaryWriter.Write(State11); + binaryWriter.Write(State12); + binaryWriter.Write(State13); + binaryWriter.Write(State14); + binaryWriter.Write(State15); + binaryWriter.Write(Skill1); + binaryWriter.Write(Skill2); + binaryWriter.Write(Skill3); + binaryWriter.Write(Skill4); + } } diff --git a/src/Parsec/Shaiya/Monster/DBMonsterTextRecord.cs b/src/Parsec/Shaiya/Monster/DBMonsterTextRecord.cs index 0558a759..886d707c 100644 --- a/src/Parsec/Shaiya/Monster/DBMonsterTextRecord.cs +++ b/src/Parsec/Shaiya/Monster/DBMonsterTextRecord.cs @@ -1,14 +1,23 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.Monster; public sealed class DBMonsterTextRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] - [LengthPrefixedString(false)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + Name = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(Name, includeStringTerminator: false); + } } diff --git a/src/Parsec/Shaiya/Monster/Monster.cs b/src/Parsec/Shaiya/Monster/Monster.cs index a271e6b3..1a053ad8 100644 --- a/src/Parsec/Shaiya/Monster/Monster.cs +++ b/src/Parsec/Shaiya/Monster/Monster.cs @@ -1,39 +1,27 @@ using System.Globalization; using System.Text; using CsvHelper; -using Newtonsoft.Json; using Parsec.Common; using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.Monster; public sealed class Monster : SData.SData, ICsv { - [JsonConstructor] - public Monster() - { - } - - public Monster(List records) - { - Records = records; - } - - public List Records { get; } = new(); + public List Records { get; set; } = new(); public override string Extension => "SData"; - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - int recordCount = _binaryReader.Read(); - for (int i = 0; i < recordCount; i++) - { - var record = new MonsterRecord(_binaryReader, Encoding); - Records.Add(record); - } + Records = binaryReader.ReadList().ToList(); } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) => Records.GetBytes(); + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Records.ToSerializable()); + } /// /// Reads the Monster.SData format from a csv file @@ -41,7 +29,7 @@ public override void Read() /// csv file path /// File encoding /// instance - public static Monster ReadFromCsv(string csvPath, Encoding encoding = null) + public static Monster FromCsv(string csvPath, Encoding? encoding = null) { encoding ??= Encoding.ASCII; @@ -51,11 +39,11 @@ public static Monster ReadFromCsv(string csvPath, Encoding encoding = null) var records = csvReader.GetRecords().ToList(); // Create monster instance - var monster = new Monster(records) { Encoding = encoding }; + var monster = new Monster { Records = records, Encoding = encoding }; return monster; } - public void WriteCsv(string outputPath, Encoding encoding = null) + public void WriteCsv(string outputPath, Encoding? encoding = null) { encoding ??= Encoding.ASCII; using var writer = new StreamWriter(outputPath, false, encoding); diff --git a/src/Parsec/Shaiya/Monster/MonsterRecord.cs b/src/Parsec/Shaiya/Monster/MonsterRecord.cs index 2a3a70af..6972a444 100644 --- a/src/Parsec/Shaiya/Monster/MonsterRecord.cs +++ b/src/Parsec/Shaiya/Monster/MonsterRecord.cs @@ -1,86 +1,95 @@ -using System.Text; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Monster; -public sealed class MonsterRecord : IBinary +public sealed class MonsterRecord : ISerializable { - [JsonConstructor] - public MonsterRecord() - { - } + public string MobName { get; set; } = string.Empty; - public MonsterRecord(SBinaryReader binaryReader, Encoding encoding) - { - MobName = binaryReader.ReadString(encoding); - ModelId = binaryReader.Read(); - Level = binaryReader.Read(); - AI = binaryReader.Read(); - HP = binaryReader.Read(); - Day = binaryReader.Read(); - Size = binaryReader.Read(); - Element = binaryReader.Read(); - NormalTime = binaryReader.Read(); - NormalStep = binaryReader.Read(); - ChaseTime = binaryReader.Read(); - ChaseStep = binaryReader.Read(); - AttackType1 = binaryReader.Read(); - AttackElement1 = binaryReader.Read(); - AttackType2 = binaryReader.Read(); - AttackRange2 = binaryReader.Read(); - AttackType3 = binaryReader.Read(); - Unknown1 = binaryReader.Read(); - Unknown2 = binaryReader.Read(); - QuestItemId = binaryReader.Read(); - } - - public string MobName { get; set; } public short ModelId { get; set; } + public short Level { get; set; } + public byte AI { get; set; } + public int HP { get; set; } + public byte Day { get; set; } + public byte Size { get; set; } + public byte Element { get; set; } + public int NormalTime { get; set; } + public byte NormalStep { get; set; } + public int ChaseTime { get; set; } + public byte ChaseStep { get; set; } + public byte AttackType1 { get; set; } + public byte AttackElement1 { get; set; } + public byte AttackType2 { get; set; } + public byte AttackRange2 { get; set; } + public byte AttackType3 { get; set; } + public byte Unknown1 { get; set; } + public byte Unknown2 { get; set; } + public short QuestItemId { get; set; } - public IEnumerable GetBytes(params object[] options) + public void Read(SBinaryReader binaryReader) + { + MobName = binaryReader.ReadString(); + ModelId = binaryReader.ReadInt16(); + Level = binaryReader.ReadInt16(); + AI = binaryReader.ReadByte(); + HP = binaryReader.ReadInt32(); + Day = binaryReader.ReadByte(); + Size = binaryReader.ReadByte(); + Element = binaryReader.ReadByte(); + NormalTime = binaryReader.ReadInt32(); + NormalStep = binaryReader.ReadByte(); + ChaseTime = binaryReader.ReadInt32(); + ChaseStep = binaryReader.ReadByte(); + AttackType1 = binaryReader.ReadByte(); + AttackElement1 = binaryReader.ReadByte(); + AttackType2 = binaryReader.ReadByte(); + AttackRange2 = binaryReader.ReadByte(); + AttackType3 = binaryReader.ReadByte(); + Unknown1 = binaryReader.ReadByte(); + Unknown2 = binaryReader.ReadByte(); + QuestItemId = binaryReader.ReadInt16(); + } + + public void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(MobName.GetLengthPrefixedBytes()); - buffer.AddRange(ModelId.GetBytes()); - buffer.AddRange(Level.GetBytes()); - buffer.Add(AI); - buffer.AddRange(HP.GetBytes()); - buffer.Add(Day); - buffer.Add(Size); - buffer.Add(Element); - buffer.AddRange(NormalTime.GetBytes()); - buffer.Add(NormalStep); - buffer.AddRange(ChaseTime.GetBytes()); - buffer.Add(ChaseStep); - buffer.Add(AttackType1); - buffer.Add(AttackElement1); - buffer.Add(AttackType2); - buffer.Add(AttackRange2); - buffer.Add(AttackType3); - buffer.Add(Unknown1); - buffer.Add(Unknown2); - buffer.AddRange(QuestItemId.GetBytes()); - return buffer; + binaryWriter.Write(MobName); + binaryWriter.Write(ModelId); + binaryWriter.Write(Level); + binaryWriter.Write(AI); + binaryWriter.Write(HP); + binaryWriter.Write(Day); + binaryWriter.Write(Size); + binaryWriter.Write(Element); + binaryWriter.Write(NormalTime); + binaryWriter.Write(NormalStep); + binaryWriter.Write(ChaseTime); + binaryWriter.Write(ChaseStep); + binaryWriter.Write(AttackType1); + binaryWriter.Write(AttackElement1); + binaryWriter.Write(AttackType2); + binaryWriter.Write(AttackRange2); + binaryWriter.Write(AttackType3); + binaryWriter.Write(Unknown1); + binaryWriter.Write(Unknown2); + binaryWriter.Write(QuestItemId); } } diff --git a/src/Parsec/Shaiya/NpcQuest/BaseNpc.cs b/src/Parsec/Shaiya/NpcQuest/BaseNpc.cs deleted file mode 100644 index 95d0f5c9..00000000 --- a/src/Parsec/Shaiya/NpcQuest/BaseNpc.cs +++ /dev/null @@ -1,114 +0,0 @@ -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.NpcQuest; - -public abstract class BaseNpc : IBinary -{ - public NpcType Type { get; set; } - public short TypeId { get; set; } - public int Model { get; set; } - public int MoveDistance { get; set; } - public int MoveSpeed { get; set; } - public NpcFaction Faction { get; set; } - public string Name { get; set; } - public string WelcomeMessage { get; set; } - - public List InQuestIds { get; } = new(); - public List OutQuestIds { get; } = new(); - - protected void ReadNpcBaseComplete(SBinaryReader binaryReader, Episode episode) - { - ReadBaseNpcFirstSegment(binaryReader); - ReadBaseNpcSecondSegment(binaryReader, episode); - ReadBaseNpcThirdSegment(binaryReader); - } - - protected void ReadBaseNpcFirstSegment(SBinaryReader binaryReader) - { - Type = (NpcType)binaryReader.Read(); - TypeId = binaryReader.Read(); - } - - protected void WriteBaseNpcFirstSegmentBytes(List buffer) - { - buffer.Add((byte)Type); - buffer.AddRange(TypeId.GetBytes()); - } - - protected void ReadBaseNpcSecondSegment(SBinaryReader binaryReader, Episode episode) - { - Model = binaryReader.Read(); - MoveDistance = binaryReader.Read(); - MoveSpeed = binaryReader.Read(); - Faction = (NpcFaction)binaryReader.Read(); - - if (episode < Episode.EP8) // In ep 8, messages are moved to separate translation files. - { - Name = binaryReader.ReadString(false); - WelcomeMessage = binaryReader.ReadString(false); - } - } - - protected void WriteBaseNpcSecondSegmentBytes(List buffer, Episode episode) - { - buffer.AddRange(Model.GetBytes()); - buffer.AddRange(MoveDistance.GetBytes()); - buffer.AddRange(MoveSpeed.GetBytes()); - buffer.AddRange(((int)Faction).GetBytes()); - - if (episode < Episode.EP8) // In ep 8, messages are moved to separate translation files. - { - buffer.AddRange(Name.GetLengthPrefixedBytes(false)); - buffer.AddRange(WelcomeMessage.GetLengthPrefixedBytes(false)); - } - } - - protected void ReadBaseNpcThirdSegment(SBinaryReader binaryReader) - { - int inQuestQuantity = binaryReader.Read(); - - for (int i = 0; i < inQuestQuantity; i++) - { - short questId = binaryReader.Read(); - InQuestIds.Add(questId); - } - - int outQuestQuantity = binaryReader.Read(); - - for (int i = 0; i < outQuestQuantity; i++) - { - short questId = binaryReader.Read(); - OutQuestIds.Add(questId); - } - } - - protected void WriteBaseNpcThirdSegmentBytes(List buffer) - { - buffer.AddRange(InQuestIds.Count.GetBytes()); - - foreach (short inQuestId in InQuestIds) - buffer.AddRange(inQuestId.GetBytes()); - - buffer.AddRange(OutQuestIds.Count.GetBytes()); - - foreach (short outQuestId in OutQuestIds) - buffer.AddRange(outQuestId.GetBytes()); - } - - public virtual IEnumerable GetBytes(params object[] options) - { - var episode = Episode.EP5; - - if (options.Length > 0) - episode = (Episode)options[0]; - - var buffer = new List(); - WriteBaseNpcFirstSegmentBytes(buffer); - WriteBaseNpcSecondSegmentBytes(buffer, episode); - WriteBaseNpcThirdSegmentBytes(buffer); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/NpcQuest/GateKeeper.cs b/src/Parsec/Shaiya/NpcQuest/GateKeeper.cs deleted file mode 100644 index 3ca4ce1b..00000000 --- a/src/Parsec/Shaiya/NpcQuest/GateKeeper.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Readers; - -namespace Parsec.Shaiya.NpcQuest; - -public class GateKeeper : BaseNpc -{ - public List GateTargets { get; } = new(); - - public GateKeeper(SBinaryReader binaryReader, Episode episode) - { - ReadBaseNpcFirstSegment(binaryReader); - ReadBaseNpcSecondSegment(binaryReader, episode); - - for (int i = 0; i < 3; i++) - { - var gateTarget = new GateTarget(binaryReader, episode); - GateTargets.Add(gateTarget); - } - - ReadBaseNpcThirdSegment(binaryReader); - } - - [JsonConstructor] - public GateKeeper() - { - } - - public override IEnumerable GetBytes(params object[] options) - { - var episode = Episode.EP5; - - if (options.Length > 0) - episode = (Episode)options[0]; - - var buffer = new List(); - WriteBaseNpcFirstSegmentBytes(buffer); - WriteBaseNpcSecondSegmentBytes(buffer, episode); - buffer.AddRange(GateTargets.Take(3).GetBytes(false, episode)); - WriteBaseNpcThirdSegmentBytes(buffer); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/NpcQuest/GateTarget.cs b/src/Parsec/Shaiya/NpcQuest/GateTarget.cs deleted file mode 100644 index 23834136..00000000 --- a/src/Parsec/Shaiya/NpcQuest/GateTarget.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.NpcQuest; - -public class GateTarget : IBinary -{ - public short MapId { get; set; } - public Vector3 Position { get; set; } - public string TargetName { get; set; } - - /// - /// Teleporting gold cost - /// - public int Cost { get; set; } - - public GateTarget(SBinaryReader binaryReader, Episode episode) - { - MapId = binaryReader.Read(); - Position = new Vector3(binaryReader); - - if (episode < Episode.EP8) // In ep 8, messages are moved to separate translation files. - TargetName = binaryReader.ReadString(false); - - Cost = binaryReader.Read(); - } - - [JsonConstructor] - public GateTarget() - { - } - - public IEnumerable GetBytes(params object[] options) - { - var episode = Episode.EP5; - - if (options.Length > 0) - episode = (Episode)options[0]; - - var buffer = new List(); - buffer.AddRange(MapId.GetBytes()); - buffer.AddRange(Position.GetBytes()); - - if (episode < Episode.EP8) // In ep 8, messages are moved to separate translation files. - buffer.AddRange(TargetName.GetLengthPrefixedBytes(false)); - - buffer.AddRange(Cost.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/NpcQuest/Merchant.cs b/src/Parsec/Shaiya/NpcQuest/Merchant.cs deleted file mode 100644 index 4ffc8f12..00000000 --- a/src/Parsec/Shaiya/NpcQuest/Merchant.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.NpcQuest; - -public class Merchant : BaseNpc, IBinary -{ - public MerchantType MerchantType { get; set; } - - public List SaleItems { get; } = new(); - - public Merchant(SBinaryReader binaryReader, Episode episode) - { - ReadBaseNpcFirstSegment(binaryReader); - MerchantType = (MerchantType)binaryReader.Read(); - ReadBaseNpcSecondSegment(binaryReader, episode); - - int saleItemCount = binaryReader.Read(); - - for (int i = 0; i < saleItemCount; i++) - { - var merchantItem = new MerchantItem(binaryReader); - SaleItems.Add(merchantItem); - } - - ReadBaseNpcThirdSegment(binaryReader); - } - - [JsonConstructor] - public Merchant() - { - } - - public override IEnumerable GetBytes(params object[] options) - { - var episode = Episode.EP5; - - if (options.Length > 0) - episode = (Episode)options[0]; - - var buffer = new List(); - WriteBaseNpcFirstSegmentBytes(buffer); - buffer.Add((byte)MerchantType); - WriteBaseNpcSecondSegmentBytes(buffer, episode); - buffer.AddRange(SaleItems.GetBytes()); - WriteBaseNpcThirdSegmentBytes(buffer); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/NpcQuest/MerchantItem.cs b/src/Parsec/Shaiya/NpcQuest/MerchantItem.cs deleted file mode 100644 index 7dac95bf..00000000 --- a/src/Parsec/Shaiya/NpcQuest/MerchantItem.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.NpcQuest; - -public class MerchantItem : IBinary -{ - /// - /// Item Type - /// - public byte Type { get; set; } - - /// - /// Item TypeId - /// - public byte TypeId { get; set; } - - public MerchantItem(SBinaryReader binaryReader) - { - Type = binaryReader.Read(); - TypeId = binaryReader.Read(); - } - - [JsonConstructor] - public MerchantItem() - { - } - - public IEnumerable GetBytes(params object[] options) => new[] { Type, TypeId }; -} diff --git a/src/Parsec/Shaiya/Common/Mode.cs b/src/Parsec/Shaiya/NpcQuest/Mode.cs similarity index 69% rename from src/Parsec/Shaiya/Common/Mode.cs rename to src/Parsec/Shaiya/NpcQuest/Mode.cs index d7c1b1e8..48eb8d56 100644 --- a/src/Parsec/Shaiya/Common/Mode.cs +++ b/src/Parsec/Shaiya/NpcQuest/Mode.cs @@ -1,4 +1,4 @@ -namespace Parsec.Shaiya.Common; +namespace Parsec.Shaiya.NpcQuest; public enum Mode : byte { diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuest.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuest.cs index badbbaac..42ff0ed2 100644 --- a/src/Parsec/Shaiya/NpcQuest/NpcQuest.cs +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuest.cs @@ -1,85 +1,77 @@ -using Parsec.Common; -using Parsec.Extensions; +using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.NpcQuest; public class NpcQuest : SData.SData { - public List Merchants { get; } = new(); - public List Gatekeepers { get; } = new(); - public List Blacksmiths { get; } = new(); - public List PvpManagers { get; } = new(); - public List GamblingHouses { get; } = new(); - public List Warehouses { get; } = new(); - public List NormalNpcs { get; } = new(); - public List Guards { get; } = new(); - public List Animals { get; } = new(); - public List Apprentices { get; } = new(); - public List GuildMasters { get; } = new(); - public List DeadNpcs { get; } = new(); - public List CombatCommanders { get; } = new(); - public byte[] UnknownArray { get; set; } - public List Quests { get; } = new(); - - public override void Read() - { - ReadMerchants(); - ReadGatekeepers(); - ReadStandardNpcs(Blacksmiths); - ReadStandardNpcs(PvpManagers); - ReadStandardNpcs(GamblingHouses); - ReadStandardNpcs(Warehouses); - ReadStandardNpcs(NormalNpcs); - ReadStandardNpcs(Guards); - ReadStandardNpcs(Animals); - ReadStandardNpcs(Apprentices); - ReadStandardNpcs(GuildMasters); - ReadStandardNpcs(DeadNpcs); - ReadStandardNpcs(CombatCommanders); - ReadUnknownArray(); - ReadQuests(); - } + public List Merchants { get; set; } = new(); - private void ReadMerchants() - { - int merchantCount = _binaryReader.Read(); - for (int i = 0; i < merchantCount; i++) - Merchants.Add(new Merchant(_binaryReader, Episode)); - } + public List Gatekeepers { get; set; } = new(); - private void ReadGatekeepers() - { - int gateKeeperCount = _binaryReader.Read(); - for (int i = 0; i < gateKeeperCount; i++) - Gatekeepers.Add(new GateKeeper(_binaryReader, Episode)); - } + public List Blacksmiths { get; set; } = new(); + + public List PvpManagers { get; set; } = new(); + + public List GamblingHouses { get; set; } = new(); + + public List Warehouses { get; set; } = new(); + + public List NormalNpcs { get; set; } = new(); + + public List Guards { get; set; } = new(); - private void ReadStandardNpcs(ICollection npcList) + public List Animals { get; set; } = new(); + + public List Apprentices { get; set; } = new(); + + public List GuildMasters { get; set; } = new(); + + public List DeadNpcs { get; set; } = new(); + + public List CombatCommanders { get; set; } = new(); + + public byte[]? UnknownArray { get; set; } + + public List Quests { get; set; } = new(); + + protected override void Read(SBinaryReader binaryReader) { - int npcCount = _binaryReader.Read(); - for (int i = 0; i < npcCount; i++) - npcList.Add(new StandardNpc(_binaryReader, Episode)); + Merchants = binaryReader.ReadList().ToList(); + Gatekeepers = binaryReader.ReadList().ToList(); + Blacksmiths = binaryReader.ReadList().ToList(); + PvpManagers = binaryReader.ReadList().ToList(); + GamblingHouses = binaryReader.ReadList().ToList(); + Warehouses = binaryReader.ReadList().ToList(); + NormalNpcs = binaryReader.ReadList().ToList(); + Guards = binaryReader.ReadList().ToList(); + Animals = binaryReader.ReadList().ToList(); + Apprentices = binaryReader.ReadList().ToList(); + GuildMasters = binaryReader.ReadList().ToList(); + DeadNpcs = binaryReader.ReadList().ToList(); + CombatCommanders = binaryReader.ReadList().ToList(); + ReadUnknownArray(binaryReader); + Quests = binaryReader.ReadList().ToList(); } - private void ReadUnknownArray() + private void ReadUnknownArray(SBinaryReader binaryReader) { var unknownBuffer = new List(); - // 256x256 matrix - for (int i = 0; i < 256; i++) + for (var i = 0; i < 256; i++) { - for (int j = 0; j < 256; j++) + for (var j = 0; j < 256; j++) { - int value1 = _binaryReader.Read(); - byte[] array1 = _binaryReader.ReadBytes(2 * value1); + var value1 = binaryReader.ReadInt32(); + var array1 = binaryReader.ReadBytes(2 * value1); - unknownBuffer.AddRange(value1.GetBytes()); + unknownBuffer.AddRange(BitConverter.GetBytes(value1)); unknownBuffer.AddRange(array1); - int value2 = _binaryReader.Read(); - byte[] array2 = _binaryReader.ReadBytes(2 * value2); + var value2 = binaryReader.ReadInt32(); + var array2 = binaryReader.ReadBytes(2 * value2); - unknownBuffer.AddRange(value2.GetBytes()); + unknownBuffer.AddRange(BitConverter.GetBytes(value2)); unknownBuffer.AddRange(array2); } } @@ -87,34 +79,22 @@ private void ReadUnknownArray() UnknownArray = unknownBuffer.ToArray(); } - private void ReadQuests() - { - int questCount = _binaryReader.Read(); - for (int i = 0; i < questCount; i++) - Quests.Add(new Quest(_binaryReader, Episode)); - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - if (episode == Episode.Unknown) - episode = Episode; - - var buffer = new List(); - buffer.AddRange(Merchants.GetBytes(true, episode)); - buffer.AddRange(Gatekeepers.GetBytes(true, episode)); - buffer.AddRange(Blacksmiths.GetBytes(true, episode)); - buffer.AddRange(PvpManagers.GetBytes(true, episode)); - buffer.AddRange(GamblingHouses.GetBytes(true, episode)); - buffer.AddRange(Warehouses.GetBytes(true, episode)); - buffer.AddRange(NormalNpcs.GetBytes(true, episode)); - buffer.AddRange(Guards.GetBytes(true, episode)); - buffer.AddRange(Animals.GetBytes(true, episode)); - buffer.AddRange(Apprentices.GetBytes(true, episode)); - buffer.AddRange(GuildMasters.GetBytes(true, episode)); - buffer.AddRange(DeadNpcs.GetBytes(true, episode)); - buffer.AddRange(CombatCommanders.GetBytes(true, episode)); - buffer.AddRange(UnknownArray); - buffer.AddRange(Quests.GetBytes(true, episode)); - return buffer; + binaryWriter.Write(Merchants.ToSerializable()); + binaryWriter.Write(Gatekeepers.ToSerializable()); + binaryWriter.Write(Blacksmiths.ToSerializable()); + binaryWriter.Write(PvpManagers.ToSerializable()); + binaryWriter.Write(GamblingHouses.ToSerializable()); + binaryWriter.Write(Warehouses.ToSerializable()); + binaryWriter.Write(NormalNpcs.ToSerializable()); + binaryWriter.Write(Guards.ToSerializable()); + binaryWriter.Write(Animals.ToSerializable()); + binaryWriter.Write(Apprentices.ToSerializable()); + binaryWriter.Write(GuildMasters.ToSerializable()); + binaryWriter.Write(DeadNpcs.ToSerializable()); + binaryWriter.Write(CombatCommanders.ToSerializable()); + binaryWriter.Write(UnknownArray); + binaryWriter.Write(Quests.ToSerializable()); } } diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestBaseNpc.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestBaseNpc.cs new file mode 100644 index 00000000..1d3610de --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestBaseNpc.cs @@ -0,0 +1,117 @@ +using Parsec.Common; +using Parsec.Serialization; + +namespace Parsec.Shaiya.NpcQuest; + +public abstract class NpcQuestBaseNpc +{ + public NpcQuestNpcType NpcType { get; set; } + + public short NpcTypeId { get; set; } + + public int Model { get; set; } + + public int MoveDistance { get; set; } + + public int MoveSpeed { get; set; } + + public NpcFaction Faction { get; set; } + + public string Name { get; set; } = string.Empty; + + public string WelcomeMessage { get; set; } = string.Empty; + + public List InQuestIds { get; set; } = new(); + + public List OutQuestIds { get; set; } = new(); + + protected void ReadNpcBaseComplete(SBinaryReader binaryReader) + { + ReadBaseNpcFirstSegment(binaryReader); + ReadBaseNpcSecondSegment(binaryReader); + ReadBaseNpcThirdSegment(binaryReader); + } + + protected void ReadBaseNpcFirstSegment(SBinaryReader binaryReader) + { + NpcType = (NpcQuestNpcType)binaryReader.ReadByte(); + NpcTypeId = binaryReader.ReadInt16(); + } + + protected void WriteBaseNpcFirstSegment(SBinaryWriter binaryWriter) + { + binaryWriter.Write((byte)NpcType); + binaryWriter.Write(NpcTypeId); + } + + protected void ReadBaseNpcSecondSegment(SBinaryReader binaryReader) + { + Model = binaryReader.ReadInt32(); + MoveDistance = binaryReader.ReadInt32(); + MoveSpeed = binaryReader.ReadInt32(); + Faction = (NpcFaction)binaryReader.ReadInt32(); + + // In Ep8 strings are stored in separate translation files + if (binaryReader.SerializationOptions.Episode < Episode.EP8) + { + Name = binaryReader.ReadString(); + WelcomeMessage = binaryReader.ReadString(); + } + } + + protected void WriteBaseNpcSecondSegment(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Model); + binaryWriter.Write(MoveDistance); + binaryWriter.Write(MoveSpeed); + binaryWriter.Write((int)Faction); + + // In Ep8 strings are stored in separate translation files + if (binaryWriter.SerializationOptions.Episode < Episode.EP8) + { + binaryWriter.Write(Name, includeStringTerminator: false); + binaryWriter.Write(WelcomeMessage, includeStringTerminator: false); + } + } + + protected void ReadBaseNpcThirdSegment(SBinaryReader binaryReader) + { + var inQuestQuantity = binaryReader.ReadInt32(); + + for (var i = 0; i < inQuestQuantity; i++) + { + var questId = binaryReader.ReadInt16(); + InQuestIds.Add(questId); + } + + var outQuestQuantity = binaryReader.ReadInt32(); + + for (var i = 0; i < outQuestQuantity; i++) + { + var questId = binaryReader.ReadInt16(); + OutQuestIds.Add(questId); + } + } + + protected void WriteBaseNpcThirdSegment(SBinaryWriter binaryWriter) + { + binaryWriter.Write(InQuestIds.Count); + foreach (var inQuestId in InQuestIds) + { + binaryWriter.Write(inQuestId); + } + + binaryWriter.Write(OutQuestIds.Count); + foreach (var outQuestId in OutQuestIds) + { + binaryWriter.Write(outQuestId); + } + } + + protected void WriteNpcBaseComplete(SBinaryWriter binaryWriter) + { + WriteBaseNpcFirstSegment(binaryWriter); + WriteBaseNpcSecondSegment(binaryWriter); + WriteBaseNpcThirdSegment(binaryWriter); + } +} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestGateKeeper.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestGateKeeper.cs new file mode 100644 index 00000000..40670e3e --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestGateKeeper.cs @@ -0,0 +1,26 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.NpcQuest; + +public class NpcQuestGateKeeper : NpcQuestBaseNpc, ISerializable +{ + public List GateTargets { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + ReadBaseNpcFirstSegment(binaryReader); + ReadBaseNpcSecondSegment(binaryReader); + GateTargets = binaryReader.ReadList(3).ToList(); + ReadBaseNpcThirdSegment(binaryReader); + } + + public void Write(SBinaryWriter binaryWriter) + { + WriteBaseNpcFirstSegment(binaryWriter); + WriteBaseNpcSecondSegment(binaryWriter); + binaryWriter.Write(GateTargets.Take(3).ToSerializable(), lengthPrefixed: false); + WriteBaseNpcThirdSegment(binaryWriter); + } +} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestGateTarget.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestGateTarget.cs new file mode 100644 index 00000000..0d45b9fd --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestGateTarget.cs @@ -0,0 +1,46 @@ +using Parsec.Common; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.NpcQuest; + +public class NpcQuestGateTarget : ISerializable +{ + public ushort MapId { get; set; } + + public Vector3 Position { get; set; } + + public string TargetName { get; set; } = string.Empty; + + /// + /// Teleporting gold cost + /// + public uint Cost { get; set; } + + public void Read(SBinaryReader binaryReader) + { + MapId = binaryReader.ReadUInt16(); + Position = binaryReader.Read(); + + if (binaryReader.SerializationOptions.Episode < Episode.EP8) + { + TargetName = binaryReader.ReadString(); + } + + Cost = binaryReader.ReadUInt32(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(MapId); + binaryWriter.Write(Position); + + if (binaryWriter.SerializationOptions.Episode < Episode.EP8) + { + binaryWriter.Write(TargetName, includeStringTerminator: false); + } + + binaryWriter.Write(Cost); + } +} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestGatekeeperTranslation.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestGatekeeperTranslation.cs new file mode 100644 index 00000000..4903626d --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestGatekeeperTranslation.cs @@ -0,0 +1,35 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.NpcQuest; + +public class NpcQuestGatekeeperTranslation : ISerializable +{ + public string Name { get; set; } = string.Empty; + + public string WelcomeMessage { get; set; } = string.Empty; + + public string TeleportName1 { get; set; } = string.Empty; + + public string TeleportName2 { get; set; } = string.Empty; + + public string TeleportName3 { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Name = binaryReader.ReadString(); + WelcomeMessage = binaryReader.ReadString(); + TeleportName1 = binaryReader.ReadString(); + TeleportName2 = binaryReader.ReadString(); + TeleportName3 = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Name, includeStringTerminator: false); + binaryWriter.Write(WelcomeMessage, includeStringTerminator: false); + binaryWriter.Write(TeleportName1, includeStringTerminator: false); + binaryWriter.Write(TeleportName2, includeStringTerminator: false); + binaryWriter.Write(TeleportName3, includeStringTerminator: false); + } +} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestMerchant.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestMerchant.cs new file mode 100644 index 00000000..24dc0f5d --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestMerchant.cs @@ -0,0 +1,30 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.NpcQuest; + +public class NpcQuestMerchant : NpcQuestBaseNpc, ISerializable +{ + public MerchantType MerchantType { get; set; } + + public List Items { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + ReadBaseNpcFirstSegment(binaryReader); + MerchantType = (MerchantType)binaryReader.ReadByte(); + ReadBaseNpcSecondSegment(binaryReader); + Items = binaryReader.ReadList().ToList(); + ReadBaseNpcThirdSegment(binaryReader); + } + + public void Write(SBinaryWriter binaryWriter) + { + WriteBaseNpcFirstSegment(binaryWriter); + binaryWriter.Write((byte)MerchantType); + WriteBaseNpcSecondSegment(binaryWriter); + binaryWriter.Write(Items.ToSerializable()); + WriteBaseNpcThirdSegment(binaryWriter); + } +} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestMerchantItem.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestMerchantItem.cs new file mode 100644 index 00000000..5b265d7e --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestMerchantItem.cs @@ -0,0 +1,23 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.NpcQuest; + +public class NpcQuestMerchantItem : ISerializable +{ + public byte ItemType { get; set; } + + public byte ItemTypeId { get; set; } + + public void Read(SBinaryReader binaryReader) + { + ItemType = binaryReader.ReadByte(); + ItemTypeId = binaryReader.ReadByte(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(ItemType); + binaryWriter.Write(ItemTypeId); + } +} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcType.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestNpcType.cs similarity index 89% rename from src/Parsec/Shaiya/NpcQuest/NpcType.cs rename to src/Parsec/Shaiya/NpcQuest/NpcQuestNpcType.cs index 57315dfe..8019c07f 100644 --- a/src/Parsec/Shaiya/NpcQuest/NpcType.cs +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestNpcType.cs @@ -1,6 +1,6 @@ namespace Parsec.Shaiya.NpcQuest; -public enum NpcType +public enum NpcQuestNpcType { Merchant = 1, GateKeeper, diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestQuestTranslation.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestQuestTranslation.cs new file mode 100644 index 00000000..e780c56e --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestQuestTranslation.cs @@ -0,0 +1,63 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.NpcQuest; + +public class NpcQuestQuestTranslation : ISerializable +{ + public string Name { get; set; } = string.Empty; + + public string Summary { get; set; } = string.Empty; + + public string CompletionMessage { get; set; } = string.Empty; + + public string CompletionMessage2 { get; set; } = string.Empty; + + public string CompletionMessage3 { get; set; } = string.Empty; + + public string CompletionMessage4 { get; set; } = string.Empty; + + public string CompletionMessage5 { get; set; } = string.Empty; + + public string CompletionMessage6 { get; set; } = string.Empty; + + public string InitialDescription { get; set; } = string.Empty; + + public string WelcomeMessage { get; set; } = string.Empty; + + public string ReminderMessage { get; set; } = string.Empty; + + public string AlternateResponse { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Name = binaryReader.ReadString(); + Summary = binaryReader.ReadString(); + CompletionMessage = binaryReader.ReadString(); + CompletionMessage2 = binaryReader.ReadString(); + CompletionMessage3 = binaryReader.ReadString(); + CompletionMessage4 = binaryReader.ReadString(); + CompletionMessage5 = binaryReader.ReadString(); + CompletionMessage6 = binaryReader.ReadString(); + InitialDescription = binaryReader.ReadString(); + WelcomeMessage = binaryReader.ReadString(); + ReminderMessage = binaryReader.ReadString(); + AlternateResponse = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Name, includeStringTerminator: false); + binaryWriter.Write(Summary, includeStringTerminator: false); + binaryWriter.Write(CompletionMessage, includeStringTerminator: false); + binaryWriter.Write(CompletionMessage2, includeStringTerminator: false); + binaryWriter.Write(CompletionMessage3, includeStringTerminator: false); + binaryWriter.Write(CompletionMessage4, includeStringTerminator: false); + binaryWriter.Write(CompletionMessage5, includeStringTerminator: false); + binaryWriter.Write(CompletionMessage6, includeStringTerminator: false); + binaryWriter.Write(InitialDescription, includeStringTerminator: false); + binaryWriter.Write(WelcomeMessage, includeStringTerminator: false); + binaryWriter.Write(ReminderMessage, includeStringTerminator: false); + binaryWriter.Write(AlternateResponse, includeStringTerminator: false); + } +} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestStandardNpc.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestStandardNpc.cs new file mode 100644 index 00000000..de0516ef --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestStandardNpc.cs @@ -0,0 +1,17 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.NpcQuest; + +public class NpcQuestStandardNpc : NpcQuestBaseNpc, ISerializable +{ + public void Read(SBinaryReader binaryReader) + { + ReadNpcBaseComplete(binaryReader); + } + + public void Write(SBinaryWriter binaryWriter) + { + WriteNpcBaseComplete(binaryWriter); + } +} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestStandardNpcTranslation.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestStandardNpcTranslation.cs new file mode 100644 index 00000000..173eb696 --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestStandardNpcTranslation.cs @@ -0,0 +1,23 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.NpcQuest; + +public class NpcQuestStandardNpcTranslation : ISerializable +{ + public string Name { get; set; } = string.Empty; + + public string WelcomeMessage { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Name = binaryReader.ReadString(); + WelcomeMessage = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Name, includeStringTerminator: false); + binaryWriter.Write(WelcomeMessage, includeStringTerminator: false); + } +} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestTran.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestTran.cs deleted file mode 100644 index a181beae..00000000 --- a/src/Parsec/Shaiya/NpcQuest/NpcQuestTran.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Attributes; - -namespace Parsec.Shaiya.NpcQuest -{ - public class NpcQuestTran - { - [ShaiyaProperty] - [LengthPrefixedString] - public string Name { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string WelcomeMessage { get; set; } - - [JsonConstructor] - public NpcQuestTran() - { - } - } - - public class GateKeeperQuestTran - { - [ShaiyaProperty] - [LengthPrefixedString] - public string Name { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string WelcomeMessage { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string TeleportName1 { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string TeleportName2 { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string TeleportName3 { get; set; } - - [JsonConstructor] - public GateKeeperQuestTran() - { - } - } -} diff --git a/src/Parsec/Shaiya/NpcQuest/NpcQuestTrans.cs b/src/Parsec/Shaiya/NpcQuest/NpcQuestTrans.cs index f2d9dc03..28c8047b 100644 --- a/src/Parsec/Shaiya/NpcQuest/NpcQuestTrans.cs +++ b/src/Parsec/Shaiya/NpcQuest/NpcQuestTrans.cs @@ -1,62 +1,71 @@ -using Parsec.Attributes; +using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.NpcQuest; public class NpcQuestTrans : SData.SData { - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List Merchants { get; set; } = new(); + public List Merchants { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(GateKeeperQuestTran))] - public List GateKeepers { get; set; } = new(); + public List GateKeepers { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List Blacksmiths { get; set; } = new(); + public List Blacksmiths { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List PvpManagers { get; set; } = new(); + public List PvpManagers { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List GamblingHouses { get; set; } = new(); + public List GamblingHouses { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List Warehouses { get; set; } = new(); + public List Warehouses { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List NormalNpcs { get; set; } = new(); + public List NormalNpcs { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List Guards { get; set; } = new(); + public List Guards { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List Animals { get; set; } = new(); + public List Animals { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List Apprentices { get; set; } = new(); + public List Apprentices { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List GuildMasters { get; set; } = new(); + public List GuildMasters { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List DeadNpcs { get; set; } = new(); + public List DeadNpcs { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(NpcQuestTran))] - public List CombatCommanders { get; set; } = new(); + public List CombatCommanders { get; set; } = new(); - [ShaiyaProperty] - [LengthPrefixedList(typeof(QuestTranslation))] - public List QuestTranslations { get; set; } = new(); + public List QuestTranslations { get; set; } = new(); + + protected override void Read(SBinaryReader binaryReader) + { + Merchants = binaryReader.ReadList().ToList(); + GateKeepers = binaryReader.ReadList().ToList(); + Blacksmiths = binaryReader.ReadList().ToList(); + PvpManagers = binaryReader.ReadList().ToList(); + GamblingHouses = binaryReader.ReadList().ToList(); + Warehouses = binaryReader.ReadList().ToList(); + NormalNpcs = binaryReader.ReadList().ToList(); + Guards = binaryReader.ReadList().ToList(); + Animals = binaryReader.ReadList().ToList(); + Apprentices = binaryReader.ReadList().ToList(); + GuildMasters = binaryReader.ReadList().ToList(); + DeadNpcs = binaryReader.ReadList().ToList(); + CombatCommanders = binaryReader.ReadList().ToList(); + QuestTranslations = binaryReader.ReadList().ToList(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Merchants.ToSerializable()); + binaryWriter.Write(GateKeepers.ToSerializable()); + binaryWriter.Write(Blacksmiths.ToSerializable()); + binaryWriter.Write(PvpManagers.ToSerializable()); + binaryWriter.Write(GamblingHouses.ToSerializable()); + binaryWriter.Write(Warehouses.ToSerializable()); + binaryWriter.Write(NormalNpcs.ToSerializable()); + binaryWriter.Write(Guards.ToSerializable()); + binaryWriter.Write(Animals.ToSerializable()); + binaryWriter.Write(Apprentices.ToSerializable()); + binaryWriter.Write(GuildMasters.ToSerializable()); + binaryWriter.Write(DeadNpcs.ToSerializable()); + binaryWriter.Write(CombatCommanders.ToSerializable()); + binaryWriter.Write(QuestTranslations.ToSerializable()); + } } diff --git a/src/Parsec/Shaiya/NpcQuest/Quest.cs b/src/Parsec/Shaiya/NpcQuest/Quest.cs index 0bcdbde1..126aca4a 100644 --- a/src/Parsec/Shaiya/NpcQuest/Quest.cs +++ b/src/Parsec/Shaiya/NpcQuest/Quest.cs @@ -1,78 +1,107 @@ -using Newtonsoft.Json; -using Parsec.Common; +using Parsec.Common; using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.NpcQuest; -public class Quest : IBinary +public class Quest : ISerializable { - public short Id { get; set; } - public string Name { get; set; } - public string Summary { get; set; } + public ushort Id { get; set; } + + public string Name { get; set; } = string.Empty; + + public string Summary { get; set; } = string.Empty; public ushort MinLevel { get; set; } + public ushort MaxLevel { get; set; } - public FactionByte Faction { get; set; } + + public QuestFaction Faction { get; set; } + public Mode Mode { get; set; } // Sex - 2 separate bool public bool MaleSex { get; set; } + public bool FemaleSex { get; set; } // Job x6 public byte Fighter { get; set; } + public byte Defender { get; set; } + public byte Ranger { get; set; } + public byte Archer { get; set; } + public byte Mage { get; set; } + public byte Priest { get; set; } public ushort wHG { get; set; } + public short shVG { get; set; } + public byte byCG { get; set; } + public byte byOG { get; set; } + public byte byIG { get; set; } public ushort PreviousQuestId { get; set; } + public bool RequireParty { get; set; } // Party Job x6 public byte PartyFighter { get; set; } + public byte PartyDefender { get; set; } + public byte PartyRanger { get; set; } + public byte PartyArcher { get; set; } + public byte PartyMage { get; set; } + public byte PartyPriest { get; set; } // Time values public uint MinimumTime { get; set; } + public uint Time { get; set; } + public uint TickStartTerm { get; set; } + public uint TickKeepTime { get; set; } + public uint TickReceiveCount { get; set; } public byte StartType { get; set; } + public byte StartNpcType { get; set; } + public ushort StartNpcId { get; set; } + public byte StartItemType { get; set; } + public byte StartItemId { get; set; } /// /// 3 for EP4 and EP5? /// - public List RequiredItems { get; } = new(); + public List RequiredItems { get; set; } = new(); public byte EndType { get; set; } + public byte EndNpcType { get; set; } + public short EndNpcId { get; set; } /// /// 3 for EP4 and EP5? /// - public List FarmItems { get; } = new(); + public List FarmItems { get; set; } = new(); /// /// byPCKillCount = pvp kills? @@ -103,263 +132,220 @@ public class Quest : IBinary /// /// 3 for EP5 and below, 6 results for EP6 and above /// - public List Results { get; } = new(); + public List Results { get; set; } = new(); - public string InitialDescription { get; set; } - public string QuestWindowSummary { get; set; } - public string ReminderInstructions { get; set; } - public string AlternateResponse { get; set; } + public string InitialDescription { get; set; } = string.Empty; - [JsonConstructor] - public Quest() - { - } + public string QuestWindowSummary { get; set; } = string.Empty; + + public string ReminderInstructions { get; set; } = string.Empty; + + public string AlternateResponse { get; set; } = string.Empty; - public Quest(SBinaryReader binaryReader, Episode episode) + private const int RequiredItemCount = 3; + + private const int FarmItemCount = 3; + + public void Read(SBinaryReader binaryReader) { - Id = binaryReader.Read(); + var episode = binaryReader.SerializationOptions.Episode; + + Id = binaryReader.ReadUInt16(); - // In ep 8, messages are moved to separate translation files. if (episode < Episode.EP8) { Name = binaryReader.ReadString(); Summary = binaryReader.ReadString(); } - MinLevel = binaryReader.Read(); - MaxLevel = binaryReader.Read(); - Faction = (FactionByte)binaryReader.Read(); - Mode = (Mode)binaryReader.Read(); + MinLevel = binaryReader.ReadUInt16(); + MaxLevel = binaryReader.ReadUInt16(); + Faction = (QuestFaction)binaryReader.ReadByte(); + Mode = (Mode)binaryReader.ReadByte(); // Sex - 2 separate bool - MaleSex = binaryReader.Read(); - FemaleSex = binaryReader.Read(); + MaleSex = binaryReader.ReadBool(); + FemaleSex = binaryReader.ReadBool(); // Job x6 - Fighter = binaryReader.Read(); - Defender = binaryReader.Read(); - Ranger = binaryReader.Read(); - Archer = binaryReader.Read(); - Mage = binaryReader.Read(); - Priest = binaryReader.Read(); - - wHG = binaryReader.Read(); - shVG = binaryReader.Read(); - byCG = binaryReader.Read(); - byOG = binaryReader.Read(); - byIG = binaryReader.Read(); - - PreviousQuestId = binaryReader.Read(); - RequireParty = binaryReader.Read(); + Fighter = binaryReader.ReadByte(); + Defender = binaryReader.ReadByte(); + Ranger = binaryReader.ReadByte(); + Archer = binaryReader.ReadByte(); + Mage = binaryReader.ReadByte(); + Priest = binaryReader.ReadByte(); + + wHG = binaryReader.ReadUInt16(); + shVG = binaryReader.ReadInt16(); + byCG = binaryReader.ReadByte(); + byOG = binaryReader.ReadByte(); + byIG = binaryReader.ReadByte(); + + PreviousQuestId = binaryReader.ReadUInt16(); + RequireParty = binaryReader.ReadBool(); // Party Job x6 - PartyFighter = binaryReader.Read(); - PartyDefender = binaryReader.Read(); - PartyRanger = binaryReader.Read(); - PartyArcher = binaryReader.Read(); - PartyMage = binaryReader.Read(); - PartyPriest = binaryReader.Read(); + PartyFighter = binaryReader.ReadByte(); + PartyDefender = binaryReader.ReadByte(); + PartyRanger = binaryReader.ReadByte(); + PartyArcher = binaryReader.ReadByte(); + PartyMage = binaryReader.ReadByte(); + PartyPriest = binaryReader.ReadByte(); // Time values - MinimumTime = binaryReader.Read(); - Time = binaryReader.Read(); - TickStartTerm = binaryReader.Read(); - TickKeepTime = binaryReader.Read(); - TickReceiveCount = binaryReader.Read(); - - StartType = binaryReader.Read(); - StartNpcType = binaryReader.Read(); - StartNpcId = binaryReader.Read(); - StartItemType = binaryReader.Read(); - StartItemId = binaryReader.Read(); - - for (int i = 0; i < 3; i++) - { - var requiredItem = new QuestItem(binaryReader); - RequiredItems.Add(requiredItem); - } + MinimumTime = binaryReader.ReadUInt32(); + Time = binaryReader.ReadUInt32(); + TickStartTerm = binaryReader.ReadUInt32(); + TickKeepTime = binaryReader.ReadUInt32(); + TickReceiveCount = binaryReader.ReadUInt32(); - EndType = binaryReader.Read(); - EndNpcType = binaryReader.Read(); - EndNpcId = binaryReader.Read(); + StartType = binaryReader.ReadByte(); + StartNpcType = binaryReader.ReadByte(); + StartNpcId = binaryReader.ReadUInt16(); + StartItemType = binaryReader.ReadByte(); + StartItemId = binaryReader.ReadByte(); - for (int i = 0; i < 3; i++) - { - var farmItem = new QuestItem(binaryReader); - FarmItems.Add(farmItem); - } + RequiredItems = binaryReader.ReadList(RequiredItemCount).ToList(); - PvpKillCount = binaryReader.Read(); - RequiredMobId1 = binaryReader.Read(); - RequiredMobCount1 = binaryReader.Read(); - RequiredMobId2 = binaryReader.Read(); - RequiredMobCount2 = binaryReader.Read(); + EndType = binaryReader.ReadByte(); + EndNpcType = binaryReader.ReadByte(); + EndNpcId = binaryReader.ReadInt16(); - ResultType = binaryReader.Read(); - ResultUserSelect = binaryReader.Read(); + FarmItems = binaryReader.ReadList(FarmItemCount).ToList(); - switch (episode) + PvpKillCount = binaryReader.ReadByte(); + RequiredMobId1 = binaryReader.ReadUInt16(); + RequiredMobCount1 = binaryReader.ReadByte(); + RequiredMobId2 = binaryReader.ReadUInt16(); + RequiredMobCount2 = binaryReader.ReadByte(); + + ResultType = binaryReader.ReadByte(); + ResultUserSelect = binaryReader.ReadByte(); + + var resultCount = GetResultCount(episode); + + if (episode <= Episode.EP5) { - case <= Episode.EP5: - { - // Episodes 4 & 5 have 3 results and completion messages are read afterwards - for (int i = 0; i < 3; i++) - { - var result = new QuestResult(binaryReader, episode); - Results.Add(result); - } - - InitialDescription = binaryReader.ReadString(false); - QuestWindowSummary = binaryReader.ReadString(false); - ReminderInstructions = binaryReader.ReadString(false); - AlternateResponse = binaryReader.ReadString(false); - - for (int i = 0; i < 3; i++) - { - Results[i].CompletionMessage = binaryReader.ReadString(false); - } - - break; - } - case >= Episode.EP6: - { - // Episode 6 has 6 quest results and each result value is followed by its completion message - for (int i = 0; i < 6; i++) - { - var result = new QuestResult(binaryReader, episode); - Results.Add(result); - - // Episode 8 doesn't have messages, they're part of the translation files - if (episode < Episode.EP8) - result.CompletionMessage = binaryReader.ReadString(false); - } - - // Episode 8 doesn't have messages, they're part of the translation files - if (episode < Episode.EP8) - { - InitialDescription = binaryReader.ReadString(false); - QuestWindowSummary = binaryReader.ReadString(false); - ReminderInstructions = binaryReader.ReadString(false); - AlternateResponse = binaryReader.ReadString(false); - } - - break; - } + // Episodes 4 & 5 have 3 results and completion messages are read afterwards + Results = binaryReader.ReadList(resultCount).ToList(); + + InitialDescription = binaryReader.ReadString(false); + QuestWindowSummary = binaryReader.ReadString(false); + ReminderInstructions = binaryReader.ReadString(false); + AlternateResponse = binaryReader.ReadString(false); + + for (var i = 0; i < resultCount; i++) + { + Results[i].CompletionMessage = binaryReader.ReadString(false); + } + } + else + { + // Episode 6 has 6 quest results and each result value is followed by its completion message + Results = binaryReader.ReadList(resultCount).ToList(); + + if (episode < Episode.EP8) + { + InitialDescription = binaryReader.ReadString(false); + QuestWindowSummary = binaryReader.ReadString(false); + ReminderInstructions = binaryReader.ReadString(false); + AlternateResponse = binaryReader.ReadString(false); + } } } - public IEnumerable GetBytes(params object[] options) + public void Write(SBinaryWriter binaryWriter) { - var episode = Episode.EP5; - - if (options.Length > 0) - episode = (Episode)options[0]; - - var buffer = new List(); + var episode = binaryWriter.SerializationOptions.Episode; - buffer.AddRange(Id.GetBytes()); + binaryWriter.Write(Id); - if (episode < Episode.EP8) // In ep 8, messages are moved to separate translation files. + if (episode < Episode.EP8) { - buffer.AddRange(Name.GetLengthPrefixedBytes(false)); - buffer.AddRange(Summary.GetLengthPrefixedBytes(false)); + binaryWriter.Write(Name); + binaryWriter.Write(Summary); } - buffer.AddRange(MinLevel.GetBytes()); - buffer.AddRange(MaxLevel.GetBytes()); - buffer.Add((byte)Faction); - buffer.Add((byte)Mode); - buffer.AddRange(MaleSex.GetBytes()); - buffer.AddRange(FemaleSex.GetBytes()); - buffer.Add(Fighter); - buffer.Add(Defender); - buffer.Add(Ranger); - buffer.Add(Archer); - buffer.Add(Mage); - buffer.Add(Priest); - buffer.AddRange(wHG.GetBytes()); - buffer.AddRange(shVG.GetBytes()); - buffer.Add(byCG); - buffer.Add(byOG); - buffer.Add(byIG); - - buffer.AddRange(PreviousQuestId.GetBytes()); - buffer.AddRange(RequireParty.GetBytes()); - - buffer.Add(PartyFighter); - buffer.Add(PartyDefender); - buffer.Add(PartyRanger); - buffer.Add(PartyArcher); - buffer.Add(PartyMage); - buffer.Add(PartyPriest); - - buffer.AddRange(MinimumTime.GetBytes()); - buffer.AddRange(Time.GetBytes()); - buffer.AddRange(TickStartTerm.GetBytes()); - buffer.AddRange(TickKeepTime.GetBytes()); - buffer.AddRange(TickReceiveCount.GetBytes()); - - buffer.Add(StartType); - buffer.Add(StartNpcType); - buffer.AddRange(StartNpcId.GetBytes()); - buffer.Add(StartItemType); - buffer.Add(StartItemId); - - buffer.AddRange(RequiredItems.Take(3).GetBytes(false)); - - buffer.Add(EndType); - buffer.Add(EndNpcType); - buffer.AddRange(EndNpcId.GetBytes()); - - buffer.AddRange(FarmItems.Take(3).GetBytes(false)); - - buffer.Add(PvpKillCount); - buffer.AddRange(RequiredMobId1.GetBytes()); - buffer.Add(RequiredMobCount1); - buffer.AddRange(RequiredMobId2.GetBytes()); - buffer.Add(RequiredMobCount2); - - buffer.Add(ResultType); - buffer.Add(ResultUserSelect); - - switch (episode) + binaryWriter.Write(MinLevel); + binaryWriter.Write(MaxLevel); + binaryWriter.Write((byte)Faction); + binaryWriter.Write((byte)Mode); + binaryWriter.Write(MaleSex); + binaryWriter.Write(FemaleSex); + binaryWriter.Write(Fighter); + binaryWriter.Write(Defender); + binaryWriter.Write(Ranger); + binaryWriter.Write(Archer); + binaryWriter.Write(Mage); + binaryWriter.Write(Priest); + binaryWriter.Write(wHG); + binaryWriter.Write(shVG); + binaryWriter.Write(byCG); + binaryWriter.Write(byOG); + binaryWriter.Write(byIG); + binaryWriter.Write(PreviousQuestId); + binaryWriter.Write(RequireParty); + binaryWriter.Write(PartyFighter); + binaryWriter.Write(PartyDefender); + binaryWriter.Write(PartyRanger); + binaryWriter.Write(PartyArcher); + binaryWriter.Write(PartyMage); + binaryWriter.Write(PartyPriest); + binaryWriter.Write(MinimumTime); + binaryWriter.Write(Time); + binaryWriter.Write(TickStartTerm); + binaryWriter.Write(TickKeepTime); + binaryWriter.Write(TickReceiveCount); + binaryWriter.Write(StartType); + binaryWriter.Write(StartNpcType); + binaryWriter.Write(StartNpcId); + binaryWriter.Write(StartItemType); + binaryWriter.Write(StartItemId); + binaryWriter.Write(RequiredItems.Take(RequiredItemCount).ToSerializable(), lengthPrefixed: false); + binaryWriter.Write(EndType); + binaryWriter.Write(EndNpcType); + binaryWriter.Write(EndNpcId); + binaryWriter.Write(FarmItems.Take(FarmItemCount).ToSerializable(), lengthPrefixed: false); + binaryWriter.Write(PvpKillCount); + binaryWriter.Write(RequiredMobId1); + binaryWriter.Write(RequiredMobCount1); + binaryWriter.Write(RequiredMobId2); + binaryWriter.Write(RequiredMobCount2); + binaryWriter.Write(ResultType); + binaryWriter.Write(ResultUserSelect); + + var resultCount = GetResultCount(episode); + + if (episode <= Episode.EP5) { - case <= Episode.EP5: - { - buffer.AddRange(Results.Take(3).GetBytes(false)); - buffer.AddRange(InitialDescription.GetLengthPrefixedBytes(false)); - buffer.AddRange(QuestWindowSummary.GetLengthPrefixedBytes(false)); - buffer.AddRange(ReminderInstructions.GetLengthPrefixedBytes(false)); - buffer.AddRange(AlternateResponse.GetLengthPrefixedBytes(false)); - foreach (var result in Results.Take(3)) - buffer.AddRange(result.CompletionMessage.GetLengthPrefixedBytes(false)); - - break; - } - case >= Episode.EP6: - { - foreach (var result in Results.Take(6)) - { - buffer.AddRange(result.GetBytes(episode)); - - if (episode < Episode.EP8) - { - buffer.AddRange(result.CompletionMessage.GetLengthPrefixedBytes(false)); - } - } - - if (episode < Episode.EP8) - { - buffer.AddRange(InitialDescription.GetLengthPrefixedBytes(false)); - buffer.AddRange(QuestWindowSummary.GetLengthPrefixedBytes(false)); - buffer.AddRange(ReminderInstructions.GetLengthPrefixedBytes(false)); - buffer.AddRange(AlternateResponse.GetLengthPrefixedBytes(false)); - } - - break; - } + binaryWriter.Write(Results.Take(resultCount).ToSerializable(), lengthPrefixed: false); + + binaryWriter.Write(InitialDescription, includeStringTerminator: false); + binaryWriter.Write(QuestWindowSummary, includeStringTerminator: false); + binaryWriter.Write(ReminderInstructions, includeStringTerminator: false); + binaryWriter.Write(AlternateResponse, includeStringTerminator: false); + + for (var i = 0; i < resultCount; i++) + { + binaryWriter.Write(Results[i].CompletionMessage, includeStringTerminator: false); + } } + else + { + binaryWriter.Write(Results.Take(resultCount).ToSerializable(), lengthPrefixed: false); + + if (episode < Episode.EP8) + { + binaryWriter.Write(InitialDescription, includeStringTerminator: false); + binaryWriter.Write(QuestWindowSummary, includeStringTerminator: false); + binaryWriter.Write(ReminderInstructions, includeStringTerminator: false); + binaryWriter.Write(AlternateResponse, includeStringTerminator: false); + } + } + } - return buffer; + private int GetResultCount(Episode episode) + { + return episode <= Episode.EP5 ? 3 : 6; } } diff --git a/src/Parsec/Shaiya/NpcQuest/QuestFaction.cs b/src/Parsec/Shaiya/NpcQuest/QuestFaction.cs new file mode 100644 index 00000000..ad280a8b --- /dev/null +++ b/src/Parsec/Shaiya/NpcQuest/QuestFaction.cs @@ -0,0 +1,8 @@ +namespace Parsec.Shaiya.NpcQuest; + +public enum QuestFaction : byte +{ + Light, + Fury, + Any +} diff --git a/src/Parsec/Shaiya/NpcQuest/QuestItem.cs b/src/Parsec/Shaiya/NpcQuest/QuestItem.cs index 4b15f1f7..15506028 100644 --- a/src/Parsec/Shaiya/NpcQuest/QuestItem.cs +++ b/src/Parsec/Shaiya/NpcQuest/QuestItem.cs @@ -1,26 +1,27 @@ -using Newtonsoft.Json; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.NpcQuest; -public class QuestItem : IBinary +public class QuestItem : ISerializable { public byte Type { get; set; } + public byte TypeId { get; set; } + public byte Count { get; set; } - [JsonConstructor] - public QuestItem() + public void Read(SBinaryReader binaryReader) { + Type = binaryReader.ReadByte(); + TypeId = binaryReader.ReadByte(); + Count = binaryReader.ReadByte(); } - public QuestItem(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - Type = binaryReader.Read(); - TypeId = binaryReader.Read(); - Count = binaryReader.Read(); + binaryWriter.Write(Type); + binaryWriter.Write(TypeId); + binaryWriter.Write(Count); } - - public IEnumerable GetBytes(params object[] options) => new[] { Type, TypeId, Count }; } diff --git a/src/Parsec/Shaiya/NpcQuest/QuestResult.cs b/src/Parsec/Shaiya/NpcQuest/QuestResult.cs index 412f5b0d..08dc5676 100644 --- a/src/Parsec/Shaiya/NpcQuest/QuestResult.cs +++ b/src/Parsec/Shaiya/NpcQuest/QuestResult.cs @@ -1,108 +1,118 @@ -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Common; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.NpcQuest; -public class QuestResult : IBinary +public class QuestResult : ISerializable { public ushort NeedMobId { get; set; } + public byte NeedMobCount { get; set; } + public byte NeedItemId { get; set; } + public byte NeedItemCount { get; set; } + public uint NeedTime { get; set; } + public ushort NeedHG { get; set; } + public short NeedVG { get; set; } + public byte NeedOG { get; set; } + public uint Exp { get; set; } + public uint Money { get; set; } + public byte ItemType1 { get; set; } + public byte ItemTypeId1 { get; set; } + public byte ItemCount1 { get; set; } + public byte ItemType2 { get; set; } + public byte ItemTypeId2 { get; set; } + public byte ItemCount2 { get; set; } + public byte ItemType3 { get; set; } + public byte ItemTypeId3 { get; set; } + public byte ItemCount3 { get; set; } + public ushort NextQuestId { get; set; } public string CompletionMessage { get; set; } = string.Empty; - public QuestResult(SBinaryReader binaryReader, Episode episode) + public void Read(SBinaryReader binaryReader) { - NeedMobId = binaryReader.Read(); - NeedMobCount = binaryReader.Read(); - NeedItemId = binaryReader.Read(); - NeedItemCount = binaryReader.Read(); - NeedTime = binaryReader.Read(); - NeedHG = binaryReader.Read(); - NeedVG = binaryReader.Read(); - NeedOG = binaryReader.Read(); - Exp = binaryReader.Read(); - Money = binaryReader.Read(); - ItemType1 = binaryReader.Read(); - ItemTypeId1 = binaryReader.Read(); - - // Some extra values are read here for EP6+ - if (episode > Episode.EP5) + NeedMobId = binaryReader.ReadUInt16(); + NeedMobCount = binaryReader.ReadByte(); + NeedItemId = binaryReader.ReadByte(); + NeedItemCount = binaryReader.ReadByte(); + NeedTime = binaryReader.ReadUInt32(); + NeedHG = binaryReader.ReadUInt16(); + NeedVG = binaryReader.ReadInt16(); + NeedOG = binaryReader.ReadByte(); + Exp = binaryReader.ReadUInt32(); + Money = binaryReader.ReadUInt32(); + ItemType1 = binaryReader.ReadByte(); + ItemTypeId1 = binaryReader.ReadByte(); + + if (binaryReader.SerializationOptions.Episode > Episode.EP5) { - ItemCount1 = binaryReader.Read(); - - ItemType2 = binaryReader.Read(); - ItemTypeId2 = binaryReader.Read(); - ItemCount2 = binaryReader.Read(); - - ItemType3 = binaryReader.Read(); - ItemTypeId3 = binaryReader.Read(); - ItemCount3 = binaryReader.Read(); + ItemCount1 = binaryReader.ReadByte(); + ItemType2 = binaryReader.ReadByte(); + ItemTypeId2 = binaryReader.ReadByte(); + ItemCount2 = binaryReader.ReadByte(); + ItemType3 = binaryReader.ReadByte(); + ItemTypeId3 = binaryReader.ReadByte(); + ItemCount3 = binaryReader.ReadByte(); } - NextQuestId = binaryReader.Read(); - } + NextQuestId = binaryReader.ReadUInt16(); - [JsonConstructor] - public QuestResult() - { + if (binaryReader.SerializationOptions.Episode is > Episode.EP5 and < Episode.EP8) + { + CompletionMessage = binaryReader.ReadString(); + } } - public IEnumerable GetBytes(params object[] options) + public void Write(SBinaryWriter binaryWriter) { - var episode = Episode.EP5; - - if (options.Length > 0) - episode = (Episode)options[0]; - - var buffer = new List(); - buffer.AddRange(NeedMobId.GetBytes()); - buffer.Add(NeedMobCount); - buffer.Add(NeedItemId); - buffer.Add(NeedItemCount); - buffer.AddRange(NeedTime.GetBytes()); - buffer.AddRange(NeedHG.GetBytes()); - buffer.AddRange(NeedVG.GetBytes()); - buffer.Add(NeedOG); - buffer.AddRange(Exp.GetBytes()); - buffer.AddRange(Money.GetBytes()); - buffer.Add(ItemType1); - buffer.Add(ItemTypeId1); - - if (episode > Episode.EP5) + binaryWriter.Write(NeedMobId); + binaryWriter.Write(NeedMobCount); + binaryWriter.Write(NeedItemId); + binaryWriter.Write(NeedItemCount); + binaryWriter.Write(NeedTime); + binaryWriter.Write(NeedHG); + binaryWriter.Write(NeedVG); + binaryWriter.Write(NeedOG); + binaryWriter.Write(Exp); + binaryWriter.Write(Money); + binaryWriter.Write(ItemType1); + binaryWriter.Write(ItemTypeId1); + + if (binaryWriter.SerializationOptions.Episode > Episode.EP5) { - buffer.Add(ItemCount1); + binaryWriter.Write(ItemCount1); + binaryWriter.Write(ItemType2); + binaryWriter.Write(ItemTypeId2); + binaryWriter.Write(ItemCount2); + binaryWriter.Write(ItemType3); + binaryWriter.Write(ItemTypeId3); + binaryWriter.Write(ItemCount3); + } - buffer.Add(ItemType2); - buffer.Add(ItemTypeId2); - buffer.Add(ItemCount2); + binaryWriter.Write(NextQuestId); - buffer.Add(ItemType3); - buffer.Add(ItemTypeId3); - buffer.Add(ItemCount3); + if (binaryWriter.SerializationOptions.Episode is > Episode.EP5 and < Episode.EP8) + { + binaryWriter.Write(CompletionMessage, includeStringTerminator: false); } - - buffer.AddRange(NextQuestId.GetBytes()); - return buffer; } } diff --git a/src/Parsec/Shaiya/NpcQuest/QuestTranslation.cs b/src/Parsec/Shaiya/NpcQuest/QuestTranslation.cs deleted file mode 100644 index b313f8e7..00000000 --- a/src/Parsec/Shaiya/NpcQuest/QuestTranslation.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Attributes; - -namespace Parsec.Shaiya.NpcQuest -{ - public class QuestTranslation - { - [ShaiyaProperty] - [LengthPrefixedString] - public string Name { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string Summary { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string CompletionMessage { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string CompletionMessage2 { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string CompletionMessage3 { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string CompletionMessage4 { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string CompletionMessage5 { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string CompletionMessage6 { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string InitialDescription { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string WelcomeMessage { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string ReminderMessage { get; set; } - - [ShaiyaProperty] - [LengthPrefixedString] - public string AlternateResponse { get; set; } - - [JsonConstructor] - public QuestTranslation() - { - } - } -} diff --git a/src/Parsec/Shaiya/NpcQuest/StandardNpc.cs b/src/Parsec/Shaiya/NpcQuest/StandardNpc.cs deleted file mode 100644 index 52d9d4a6..00000000 --- a/src/Parsec/Shaiya/NpcQuest/StandardNpc.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Readers; - -namespace Parsec.Shaiya.NpcQuest; - -public class StandardNpc : BaseNpc -{ - public StandardNpc(SBinaryReader binaryReader, Episode episode) - { - ReadNpcBaseComplete(binaryReader, episode); - } - - [JsonConstructor] - public StandardNpc() - { - } -} diff --git a/src/Parsec/Shaiya/NpcSkill/DBNpcSkillDataRecord.cs b/src/Parsec/Shaiya/NpcSkill/DBNpcSkillDataRecord.cs index 3132072b..b91be246 100644 --- a/src/Parsec/Shaiya/NpcSkill/DBNpcSkillDataRecord.cs +++ b/src/Parsec/Shaiya/NpcSkill/DBNpcSkillDataRecord.cs @@ -1,307 +1,415 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.NpcSkill; public sealed class DBNpcSkillDataRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] public long SkillLevel { get; set; } - [ShaiyaProperty] public long Image { get; set; } - [ShaiyaProperty] public long Ani { get; set; } - [ShaiyaProperty] public long Effect { get; set; } - [ShaiyaProperty] public long ToggleType { get; set; } - [ShaiyaProperty] public long Sound { get; set; } - [ShaiyaProperty] public long Level { get; set; } - [ShaiyaProperty] public long Country { get; set; } - [ShaiyaProperty] public long AttackFighter { get; set; } - [ShaiyaProperty] public long DefenseFighter { get; set; } - [ShaiyaProperty] public long PatrolRogue { get; set; } - [ShaiyaProperty] public long ShootRogue { get; set; } - [ShaiyaProperty] public long AttackMage { get; set; } - [ShaiyaProperty] public long DefenseMage { get; set; } - [ShaiyaProperty] public long Grow { get; set; } - [ShaiyaProperty] public long Point { get; set; } - [ShaiyaProperty] public long TypeShow { get; set; } - [ShaiyaProperty] public long TypeAttack { get; set; } - [ShaiyaProperty] public long TypeEffect { get; set; } - [ShaiyaProperty] public long Type { get; set; } - [ShaiyaProperty] public long NeedWeapon1 { get; set; } - [ShaiyaProperty] public long NeedWeapon2 { get; set; } - [ShaiyaProperty] public long NeedWeapon3 { get; set; } - [ShaiyaProperty] public long NeedWeapon4 { get; set; } - [ShaiyaProperty] public long NeedWeapon5 { get; set; } - [ShaiyaProperty] public long NeedWeapon6 { get; set; } - [ShaiyaProperty] public long NeedWeapon7 { get; set; } - [ShaiyaProperty] public long NeedWeapon8 { get; set; } - [ShaiyaProperty] public long NeedWeapon9 { get; set; } - [ShaiyaProperty] public long NeedWeapon10 { get; set; } - [ShaiyaProperty] public long NeedWeapon11 { get; set; } - [ShaiyaProperty] public long NeedWeapon12 { get; set; } - [ShaiyaProperty] public long NeedWeapon13 { get; set; } - [ShaiyaProperty] public long NeedWeapon14 { get; set; } - [ShaiyaProperty] public long NeedWeapon15 { get; set; } - [ShaiyaProperty] public long Shield { get; set; } - [ShaiyaProperty] public long SP { get; set; } - [ShaiyaProperty] public long MP { get; set; } - [ShaiyaProperty] public long ReadyTime { get; set; } - [ShaiyaProperty] public long ResetTime { get; set; } - [ShaiyaProperty] public long AttackRange { get; set; } - [ShaiyaProperty] public long StateType { get; set; } - [ShaiyaProperty] public long AttribType { get; set; } - [ShaiyaProperty] public long Disable { get; set; } - [ShaiyaProperty] public long PrevSkill { get; set; } - [ShaiyaProperty] public long SuccessType { get; set; } - [ShaiyaProperty] public long SuccessValue { get; set; } - [ShaiyaProperty] public long TargetType { get; set; } - [ShaiyaProperty] public long ApplyRange { get; set; } - [ShaiyaProperty] public long MultiAttack { get; set; } - [ShaiyaProperty] public long KeepTime { get; set; } - [ShaiyaProperty] public long Weapon1 { get; set; } - [ShaiyaProperty] public long Weapon2 { get; set; } - [ShaiyaProperty] public long WeaponValue { get; set; } - [ShaiyaProperty] public long Bag { get; set; } - [ShaiyaProperty] public long Arrow { get; set; } - [ShaiyaProperty] public long DamageType { get; set; } - [ShaiyaProperty] public long Damage1 { get; set; } - [ShaiyaProperty] public long Damage2 { get; set; } - [ShaiyaProperty] public long Damage3 { get; set; } - [ShaiyaProperty] public long TimeDamageType { get; set; } - [ShaiyaProperty] public long TimeDamage1 { get; set; } - [ShaiyaProperty] public long TimeDamage2 { get; set; } - [ShaiyaProperty] public long TimeDamage3 { get; set; } - [ShaiyaProperty] public long AddDamage1 { get; set; } - [ShaiyaProperty] public long AddDamage2 { get; set; } - [ShaiyaProperty] public long AddDamage3 { get; set; } - [ShaiyaProperty] public long AbilityType1 { get; set; } - [ShaiyaProperty] public long AbilityValue1 { get; set; } - [ShaiyaProperty] public long AbilityType2 { get; set; } - [ShaiyaProperty] public long AbilityValue2 { get; set; } - [ShaiyaProperty] public long AbilityType3 { get; set; } - [ShaiyaProperty] public long AbilityValue3 { get; set; } - [ShaiyaProperty] public long AbilityType4 { get; set; } - [ShaiyaProperty] public long AbilityValue4 { get; set; } - [ShaiyaProperty] public long AbilityType5 { get; set; } - [ShaiyaProperty] public long AbilityValue5 { get; set; } - [ShaiyaProperty] public long AbilityType6 { get; set; } - [ShaiyaProperty] public long AbilityValue6 { get; set; } - [ShaiyaProperty] public long AbilityType7 { get; set; } - [ShaiyaProperty] public long AbilityValue7 { get; set; } - [ShaiyaProperty] public long AbilityType8 { get; set; } - [ShaiyaProperty] public long AbilityValue8 { get; set; } - [ShaiyaProperty] public long AbilityType9 { get; set; } - [ShaiyaProperty] public long AbilityValue9 { get; set; } - [ShaiyaProperty] public long AbilityType10 { get; set; } - [ShaiyaProperty] public long AbilityValue10 { get; set; } - [ShaiyaProperty] public long Heal1 { get; set; } - [ShaiyaProperty] public long Heal2 { get; set; } - [ShaiyaProperty] public long Heal3 { get; set; } - [ShaiyaProperty] public long TimeHeal1 { get; set; } - [ShaiyaProperty] public long TimeHeal2 { get; set; } - [ShaiyaProperty] public long TimeHeal3 { get; set; } - [ShaiyaProperty] public long DefenceType { get; set; } - [ShaiyaProperty] public long DefenceValue { get; set; } - [ShaiyaProperty] public long LimitHP { get; set; } - [ShaiyaProperty] public long FixRange { get; set; } - [ShaiyaProperty] public long ChangeType { get; set; } - [ShaiyaProperty] public long ChangeLevel { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + SkillLevel = binaryReader.ReadInt64(); + Image = binaryReader.ReadInt64(); + Ani = binaryReader.ReadInt64(); + Effect = binaryReader.ReadInt64(); + ToggleType = binaryReader.ReadInt64(); + Sound = binaryReader.ReadInt64(); + Level = binaryReader.ReadInt64(); + Country = binaryReader.ReadInt64(); + AttackFighter = binaryReader.ReadInt64(); + DefenseFighter = binaryReader.ReadInt64(); + PatrolRogue = binaryReader.ReadInt64(); + ShootRogue = binaryReader.ReadInt64(); + AttackMage = binaryReader.ReadInt64(); + DefenseMage = binaryReader.ReadInt64(); + Grow = binaryReader.ReadInt64(); + Point = binaryReader.ReadInt64(); + TypeShow = binaryReader.ReadInt64(); + TypeAttack = binaryReader.ReadInt64(); + TypeEffect = binaryReader.ReadInt64(); + Type = binaryReader.ReadInt64(); + NeedWeapon1 = binaryReader.ReadInt64(); + NeedWeapon2 = binaryReader.ReadInt64(); + NeedWeapon3 = binaryReader.ReadInt64(); + NeedWeapon4 = binaryReader.ReadInt64(); + NeedWeapon5 = binaryReader.ReadInt64(); + NeedWeapon6 = binaryReader.ReadInt64(); + NeedWeapon7 = binaryReader.ReadInt64(); + NeedWeapon8 = binaryReader.ReadInt64(); + NeedWeapon9 = binaryReader.ReadInt64(); + NeedWeapon10 = binaryReader.ReadInt64(); + NeedWeapon11 = binaryReader.ReadInt64(); + NeedWeapon12 = binaryReader.ReadInt64(); + NeedWeapon13 = binaryReader.ReadInt64(); + NeedWeapon14 = binaryReader.ReadInt64(); + NeedWeapon15 = binaryReader.ReadInt64(); + Shield = binaryReader.ReadInt64(); + SP = binaryReader.ReadInt64(); + MP = binaryReader.ReadInt64(); + ReadyTime = binaryReader.ReadInt64(); + ResetTime = binaryReader.ReadInt64(); + AttackRange = binaryReader.ReadInt64(); + StateType = binaryReader.ReadInt64(); + AttribType = binaryReader.ReadInt64(); + Disable = binaryReader.ReadInt64(); + PrevSkill = binaryReader.ReadInt64(); + SuccessType = binaryReader.ReadInt64(); + SuccessValue = binaryReader.ReadInt64(); + TargetType = binaryReader.ReadInt64(); + ApplyRange = binaryReader.ReadInt64(); + MultiAttack = binaryReader.ReadInt64(); + KeepTime = binaryReader.ReadInt64(); + Weapon1 = binaryReader.ReadInt64(); + Weapon2 = binaryReader.ReadInt64(); + WeaponValue = binaryReader.ReadInt64(); + Bag = binaryReader.ReadInt64(); + Arrow = binaryReader.ReadInt64(); + DamageType = binaryReader.ReadInt64(); + Damage1 = binaryReader.ReadInt64(); + Damage2 = binaryReader.ReadInt64(); + Damage3 = binaryReader.ReadInt64(); + TimeDamageType = binaryReader.ReadInt64(); + TimeDamage1 = binaryReader.ReadInt64(); + TimeDamage2 = binaryReader.ReadInt64(); + TimeDamage3 = binaryReader.ReadInt64(); + AddDamage1 = binaryReader.ReadInt64(); + AddDamage2 = binaryReader.ReadInt64(); + AddDamage3 = binaryReader.ReadInt64(); + AbilityType1 = binaryReader.ReadInt64(); + AbilityValue1 = binaryReader.ReadInt64(); + AbilityType2 = binaryReader.ReadInt64(); + AbilityValue2 = binaryReader.ReadInt64(); + AbilityType3 = binaryReader.ReadInt64(); + AbilityValue3 = binaryReader.ReadInt64(); + AbilityType4 = binaryReader.ReadInt64(); + AbilityValue4 = binaryReader.ReadInt64(); + AbilityType5 = binaryReader.ReadInt64(); + AbilityValue5 = binaryReader.ReadInt64(); + AbilityType6 = binaryReader.ReadInt64(); + AbilityValue6 = binaryReader.ReadInt64(); + AbilityType7 = binaryReader.ReadInt64(); + AbilityValue7 = binaryReader.ReadInt64(); + AbilityType8 = binaryReader.ReadInt64(); + AbilityValue8 = binaryReader.ReadInt64(); + AbilityType9 = binaryReader.ReadInt64(); + AbilityValue9 = binaryReader.ReadInt64(); + AbilityType10 = binaryReader.ReadInt64(); + AbilityValue10 = binaryReader.ReadInt64(); + Heal1 = binaryReader.ReadInt64(); + Heal2 = binaryReader.ReadInt64(); + Heal3 = binaryReader.ReadInt64(); + TimeHeal1 = binaryReader.ReadInt64(); + TimeHeal2 = binaryReader.ReadInt64(); + TimeHeal3 = binaryReader.ReadInt64(); + DefenceType = binaryReader.ReadInt64(); + DefenceValue = binaryReader.ReadInt64(); + LimitHP = binaryReader.ReadInt64(); + FixRange = binaryReader.ReadInt64(); + ChangeType = binaryReader.ReadInt64(); + ChangeLevel = binaryReader.ReadInt64(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(SkillLevel); + binaryWriter.Write(Image); + binaryWriter.Write(Ani); + binaryWriter.Write(Effect); + binaryWriter.Write(ToggleType); + binaryWriter.Write(Sound); + binaryWriter.Write(Level); + binaryWriter.Write(Country); + binaryWriter.Write(AttackFighter); + binaryWriter.Write(DefenseFighter); + binaryWriter.Write(PatrolRogue); + binaryWriter.Write(ShootRogue); + binaryWriter.Write(AttackMage); + binaryWriter.Write(DefenseMage); + binaryWriter.Write(Grow); + binaryWriter.Write(Point); + binaryWriter.Write(TypeShow); + binaryWriter.Write(TypeAttack); + binaryWriter.Write(TypeEffect); + binaryWriter.Write(Type); + binaryWriter.Write(NeedWeapon1); + binaryWriter.Write(NeedWeapon2); + binaryWriter.Write(NeedWeapon3); + binaryWriter.Write(NeedWeapon4); + binaryWriter.Write(NeedWeapon5); + binaryWriter.Write(NeedWeapon6); + binaryWriter.Write(NeedWeapon7); + binaryWriter.Write(NeedWeapon8); + binaryWriter.Write(NeedWeapon9); + binaryWriter.Write(NeedWeapon10); + binaryWriter.Write(NeedWeapon11); + binaryWriter.Write(NeedWeapon12); + binaryWriter.Write(NeedWeapon13); + binaryWriter.Write(NeedWeapon14); + binaryWriter.Write(NeedWeapon15); + binaryWriter.Write(Shield); + binaryWriter.Write(SP); + binaryWriter.Write(MP); + binaryWriter.Write(ReadyTime); + binaryWriter.Write(ResetTime); + binaryWriter.Write(AttackRange); + binaryWriter.Write(StateType); + binaryWriter.Write(AttribType); + binaryWriter.Write(Disable); + binaryWriter.Write(PrevSkill); + binaryWriter.Write(SuccessType); + binaryWriter.Write(SuccessValue); + binaryWriter.Write(TargetType); + binaryWriter.Write(ApplyRange); + binaryWriter.Write(MultiAttack); + binaryWriter.Write(KeepTime); + binaryWriter.Write(Weapon1); + binaryWriter.Write(Weapon2); + binaryWriter.Write(WeaponValue); + binaryWriter.Write(Bag); + binaryWriter.Write(Arrow); + binaryWriter.Write(DamageType); + binaryWriter.Write(Damage1); + binaryWriter.Write(Damage2); + binaryWriter.Write(Damage3); + binaryWriter.Write(TimeDamageType); + binaryWriter.Write(TimeDamage1); + binaryWriter.Write(TimeDamage2); + binaryWriter.Write(TimeDamage3); + binaryWriter.Write(AddDamage1); + binaryWriter.Write(AddDamage2); + binaryWriter.Write(AddDamage3); + binaryWriter.Write(AbilityType1); + binaryWriter.Write(AbilityValue1); + binaryWriter.Write(AbilityType2); + binaryWriter.Write(AbilityValue2); + binaryWriter.Write(AbilityType3); + binaryWriter.Write(AbilityValue3); + binaryWriter.Write(AbilityType4); + binaryWriter.Write(AbilityValue4); + binaryWriter.Write(AbilityType5); + binaryWriter.Write(AbilityValue5); + binaryWriter.Write(AbilityType6); + binaryWriter.Write(AbilityValue6); + binaryWriter.Write(AbilityType7); + binaryWriter.Write(AbilityValue7); + binaryWriter.Write(AbilityType8); + binaryWriter.Write(AbilityValue8); + binaryWriter.Write(AbilityType9); + binaryWriter.Write(AbilityValue9); + binaryWriter.Write(AbilityType10); + binaryWriter.Write(AbilityValue10); + binaryWriter.Write(Heal1); + binaryWriter.Write(Heal2); + binaryWriter.Write(Heal3); + binaryWriter.Write(TimeHeal1); + binaryWriter.Write(TimeHeal2); + binaryWriter.Write(TimeHeal3); + binaryWriter.Write(DefenceType); + binaryWriter.Write(DefenceValue); + binaryWriter.Write(LimitHP); + binaryWriter.Write(FixRange); + binaryWriter.Write(ChangeType); + binaryWriter.Write(ChangeLevel); + } } diff --git a/src/Parsec/Shaiya/NpcSkill/DBNpcSkillTextRecord.cs b/src/Parsec/Shaiya/NpcSkill/DBNpcSkillTextRecord.cs index 50b21c18..470438cc 100644 --- a/src/Parsec/Shaiya/NpcSkill/DBNpcSkillTextRecord.cs +++ b/src/Parsec/Shaiya/NpcSkill/DBNpcSkillTextRecord.cs @@ -1,21 +1,31 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.NpcSkill; public sealed class DBNpcSkillTextRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] public long SkillLevel { get; set; } - [ShaiyaProperty] - [LengthPrefixedString(false)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - [ShaiyaProperty] - [LengthPrefixedString(false)] - public string Text { get; set; } + public string Text { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + SkillLevel = binaryReader.ReadInt64(); + Name = binaryReader.ReadString(); + Text = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(SkillLevel); + binaryWriter.Write(Name, includeStringTerminator: false); + binaryWriter.Write(Text, includeStringTerminator: false); + } } diff --git a/src/Parsec/Shaiya/SData/BinarySData.cs b/src/Parsec/Shaiya/SData/BinarySData.cs index e78a8e67..9edfeefa 100644 --- a/src/Parsec/Shaiya/SData/BinarySData.cs +++ b/src/Parsec/Shaiya/SData/BinarySData.cs @@ -1,10 +1,9 @@ using System.Globalization; using System.Text; using CsvHelper; -using Parsec.Attributes; using Parsec.Common; using Parsec.Extensions; -using Parsec.Shaiya.Core; +using Parsec.Serialization; namespace Parsec.Shaiya.SData; @@ -13,81 +12,55 @@ namespace Parsec.Shaiya.SData; /// /// 128-byte header unused by the game itself. It looks like a file signature + metadata /// - [ShaiyaProperty] - public byte[] Header { get; set; } + public byte[] Header { get; set; } = new byte[128]; /// /// Field names are defined before the data. They aren't really used but knowing which each field means is nice /// - [ShaiyaProperty] public List Fields { get; set; } = new(); - [ShaiyaProperty] public List Records { get; set; } = new(); - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - Header = _binaryReader.ReadBytes(128); - int fieldCount = _binaryReader.Read(); - - for (int i = 0; i < fieldCount; i++) - Fields.Add(new BinarySDataField(_binaryReader)); - - int recordCount = _binaryReader.Read(); - - for (int i = 0; i < recordCount; i++) - { - var recordType = typeof(TRecord); - var record = Activator.CreateInstance(); - - foreach (var property in recordType.GetProperties()) - { - object value = ReflectionHelper.ReadProperty(_binaryReader, typeof(TRecord), record, property); - property.SetValue(record, value); - } - - Records.Add(record); - } + Header = binaryReader.ReadBytes(128); + Fields = binaryReader.ReadList().ToList(); + Records = binaryReader.ReadList().ToList(); } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Header); - buffer.AddRange(Fields.GetBytes()); - - buffer.AddRange(Records.Count.GetBytes()); - - var recordType = typeof(TRecord); - - foreach (var record in Records) + if (Header is not { Length: 128 }) { - foreach (var property in recordType.GetProperties()) - { - var propertyBytes = ReflectionHelper.GetPropertyBytes(recordType, record, property, Encoding, episode); - buffer.AddRange(propertyBytes); - } + Header = new byte[128]; } - return buffer; + binaryWriter.Write(Header); + binaryWriter.Write(Fields.ToSerializable()); + binaryWriter.Write(Records.ToSerializable()); } - public static T ReadFromCsv(string csvPath, Encoding encoding = null) where T : BinarySData, new() + public static T FromCsv(string csvPath, Encoding? encoding = null) where T : BinarySData, new() { encoding ??= Encoding.ASCII; using var reader = new StreamReader(csvPath, encoding); using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture); - // Read headers and records + // Read records var records = csvReader.GetRecords().ToList(); - var fields = csvReader.HeaderRecord?.Select(c => new BinarySDataField(c.ToLower())).ToList(); - // Create the BinarySData instance with an empty header. The header is skipped entirely by the game so this isn't an issue. - var binarySData = new T { Header = new byte[128], Fields = fields, Records = records, Encoding = encoding }; + if (csvReader.HeaderRecord == null) + { + throw new FileLoadException("Csv file doesn't have a valid header."); + } + + // Read headers + var fields = csvReader.HeaderRecord.Select(column => (BinarySDataField)column.ToLower()).ToList(); + var binarySData = new T { Fields = fields, Records = records, Encoding = encoding }; return binarySData; } - public void WriteCsv(string outputPath, Encoding encoding = null) + public void WriteCsv(string outputPath, Encoding? encoding = null) { encoding ??= Encoding.ASCII; using var writer = new StreamWriter(outputPath, false, encoding); diff --git a/src/Parsec/Shaiya/SData/BinarySDataField.cs b/src/Parsec/Shaiya/SData/BinarySDataField.cs index de6c22c2..93ff2134 100644 --- a/src/Parsec/Shaiya/SData/BinarySDataField.cs +++ b/src/Parsec/Shaiya/SData/BinarySDataField.cs @@ -1,36 +1,29 @@ using System.Text; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.SData; -public sealed class BinarySDataField : IBinary +public sealed class BinarySDataField : ISerializable { - public BinarySDataField(string name) - { - Name = name; - } + public string Name { get; set; } = string.Empty; - [JsonConstructor] - public BinarySDataField() + public void Read(SBinaryReader binaryReader) { + var length = binaryReader.ReadByte(); + Name = binaryReader.ReadString(Encoding.Unicode, length); } - public BinarySDataField(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - int length = binaryReader.Read(); - Name = binaryReader.ReadString(Encoding.Unicode, length); + var length = (byte)Name.Length; + binaryWriter.Write(length); + binaryWriter.Write(Name, Encoding.Unicode, isLengthPrefixed: false, includeStringTerminator: false); } - public string Name { get; set; } + public static implicit operator string(BinarySDataField field) => field.Name; - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.Add((byte)Name.Length); - buffer.AddRange(Name.GetBytes(Encoding.Unicode)); - return buffer; - } + public static implicit operator BinarySDataField(string fieldName) => new BinarySDataField { Name = fieldName }; + + public override string ToString() => Name; } diff --git a/src/Parsec/Shaiya/SData/IBinarySDataRecord.cs b/src/Parsec/Shaiya/SData/IBinarySDataRecord.cs index 9f0bd315..6bc5f1bd 100644 --- a/src/Parsec/Shaiya/SData/IBinarySDataRecord.cs +++ b/src/Parsec/Shaiya/SData/IBinarySDataRecord.cs @@ -1,5 +1,7 @@ -namespace Parsec.Shaiya.SData; +using Parsec.Shaiya.Core; -public interface IBinarySDataRecord +namespace Parsec.Shaiya.SData; + +public interface IBinarySDataRecord : ISerializable { } diff --git a/src/Parsec/Shaiya/SData/SData.cs b/src/Parsec/Shaiya/SData/SData.cs index fb5502be..b3738f20 100644 --- a/src/Parsec/Shaiya/SData/SData.cs +++ b/src/Parsec/Shaiya/SData/SData.cs @@ -4,7 +4,7 @@ using Parsec.Cryptography; using Parsec.Extensions; using Parsec.Helpers; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.SData; @@ -33,37 +33,43 @@ public abstract class SData : FileBase, IEncryptable public override string Extension => "SData"; /// - public void DecryptBuffer(bool validateChecksum = false) + public void DecryptBuffer(SBinaryReader binaryReader, bool validateChecksum = false) { - var fileBuffer = _binaryReader.ReadAllBytes(); + var fileBuffer = binaryReader.ReadAllBytes(); if (!IsEncrypted(fileBuffer)) { - _binaryReader.ResetOffset(); + binaryReader.ResetOffset(); return; } - byte[] decryptedBuffer = Decrypt(fileBuffer, validateChecksum); - _binaryReader = new SBinaryReader(decryptedBuffer); + var decryptedBuffer = Decrypt(fileBuffer, validateChecksum); + binaryReader.ResetBuffer(decryptedBuffer); } /// public byte[] GetEncryptedBytes() { - var version = Episode >= Episode.EP8 ? SDataVersion.Binary : SDataVersion.Regular; - return Encrypt(GetBytes(Episode).ToArray(), version); + var version = Episode == Episode.EP8 ? SDataVersion.Binary : SDataVersion.Regular; + var serializationOptions = new BinarySerializationOptions(Episode, Encoding); + + var memoryStream = new MemoryStream(); + var binaryWriter = new SBinaryWriter(memoryStream, serializationOptions); + Write(binaryWriter); + + var encryptedBuffer = Encrypt(memoryStream.ToArray(), version); + return encryptedBuffer; } /// public void WriteEncrypted(string path) { - var version = Episode >= Episode.EP8 ? SDataVersion.Binary : SDataVersion.Regular; - byte[] encryptedBuffer = Encrypt(GetBytes(Episode).ToArray(), version); + var encryptedBuffer = GetEncryptedBytes(); FileHelper.WriteFile(path, encryptedBuffer); } /// - public void WriteDecrypted(string path) => Write(path, Episode); + public void WriteDecrypted(string path) => Write(path); /// /// Checks if the file is encrypted with the SEED algorithm @@ -73,7 +79,7 @@ public static bool IsEncrypted(byte[] data) if (data.Length < SEED_SIGNATURE.Length) return false; - string sDataHeader = Encoding.ASCII.GetString(data.SubArray(0, SEED_SIGNATURE.Length)); + var sDataHeader = Encoding.ASCII.GetString(data.SubArray(0, SEED_SIGNATURE.Length)); return sDataHeader == SEED_SIGNATURE; } @@ -88,24 +94,24 @@ public static byte[] Encrypt(byte[] decryptedData, SDataVersion version = SDataV if (IsEncrypted(decryptedData)) return decryptedData; - byte[] padding = version == SDataVersion.Regular ? new byte[16] : new byte[12]; + var padding = version == SDataVersion.Regular ? new byte[16] : new byte[12]; var header = new SeedHeader(SEED_SIGNATURE, 0, (uint)decryptedData.Length, padding); - uint alignmentSize = header.RealSize; + var alignmentSize = header.RealSize; if (alignmentSize % CHUNK_SIZE != 0) alignmentSize = header.RealSize + (CHUNK_SIZE - header.RealSize % CHUNK_SIZE); // Create data array including the extra alignment bytes - byte[] data = new byte[alignmentSize]; + var data = new byte[alignmentSize]; Array.Copy(decryptedData, data, decryptedData.Length); // Calculate and set checksum - uint checksum = uint.MaxValue; + var checksum = uint.MaxValue; - for (int i = 0; i < header.RealSize; i++) + for (var i = 0; i < header.RealSize; i++) { - uint index = (checksum & 0xFF) ^ decryptedData[i]; - uint key = Seed.ByteArrayToUInt32(SeedConstants.ChecksumTable, index * 4); + var index = (checksum & 0xFF) ^ decryptedData[i]; + var key = Seed.ByteArrayToUInt32(SeedConstants.ChecksumTable, index * 4); Seed.EndiannessSwap(ref key); checksum >>= 8; checksum ^= key; @@ -120,12 +126,12 @@ public static byte[] Encrypt(byte[] decryptedData, SDataVersion version = SDataV // Encrypt data in chunks for (int i = 0; i < alignmentSize / CHUNK_SIZE; ++i) { - byte[] chunk = data.AsSpan().Slice(i * CHUNK_SIZE, CHUNK_SIZE).ToArray(); - Seed.EncryptChunk(chunk, out byte[] encryptedChunk); + var chunk = data.AsSpan().Slice(i * CHUNK_SIZE, CHUNK_SIZE).ToArray(); + Seed.EncryptChunk(chunk, out var encryptedChunk); buffer.AddRange(encryptedChunk); } - byte[] encryptedData = buffer.ToArray(); + var encryptedData = buffer.ToArray(); return encryptedData; } @@ -148,22 +154,22 @@ public static byte[] Decrypt(byte[] encryptedBuffer, bool validateChecksum = fal var data = new List(); // Decrypt data in chunks - for (int i = 0; i < encryptedData.Length / CHUNK_SIZE; ++i) + for (var i = 0; i < encryptedData.Length / CHUNK_SIZE; ++i) { var chunk = encryptedData.Slice(i * CHUNK_SIZE, CHUNK_SIZE); - Seed.DecryptChunk(chunk.ToArray(), out byte[] decryptedChunk); + Seed.DecryptChunk(chunk.ToArray(), out var decryptedChunk); data.AddRange(decryptedChunk); } if (validateChecksum) { - uint checksum = uint.MaxValue; + var checksum = uint.MaxValue; // Checksum is calculated with the whole file's data except for the header (not with the real size) - for (int i = 0; i < header.RealSize; i++) + for (var i = 0; i < header.RealSize; i++) { - uint index = (checksum & 0xFF) ^ data[i]; - uint key = Seed.ByteArrayToUInt32(SeedConstants.ChecksumTable, index * 4); + var index = (checksum & 0xFF) ^ data[i]; + var key = Seed.ByteArrayToUInt32(SeedConstants.ChecksumTable, index * 4); Seed.EndiannessSwap(ref key); checksum >>= 8; checksum ^= key; @@ -176,22 +182,22 @@ public static byte[] Decrypt(byte[] encryptedBuffer, bool validateChecksum = fal throw new FormatException("Invalid SEED checksum."); } - byte[] decryptedData = new byte[header.RealSize]; + var decryptedData = new byte[header.RealSize]; Array.Copy(data.ToArray(), decryptedData, header.RealSize); return decryptedData; } public static void EncryptFile(string inputFilePath, string outputFilePath, SDataVersion sDataVersion = SDataVersion.Regular) { - byte[] fileData = FileHelper.ReadBytes(inputFilePath); - byte[] encryptedData = Encrypt(fileData, sDataVersion); + var fileData = FileHelper.ReadBytes(inputFilePath); + var encryptedData = Encrypt(fileData, sDataVersion); FileHelper.WriteFile(outputFilePath, encryptedData); } public static void DecryptFile(string inputFilePath, string outputFilePath, bool validateChecksum = false) { - byte[] fileData = FileHelper.ReadBytes(inputFilePath); - byte[] decryptedData = Decrypt(fileData, validateChecksum); + var fileData = FileHelper.ReadBytes(inputFilePath); + var decryptedData = Decrypt(fileData, validateChecksum); FileHelper.WriteFile(outputFilePath, decryptedData); } } diff --git a/src/Parsec/Shaiya/SMOD/CollisionObject.cs b/src/Parsec/Shaiya/SMOD/CollisionObject.cs deleted file mode 100644 index 603e9a8c..00000000 --- a/src/Parsec/Shaiya/SMOD/CollisionObject.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.SMOD; - -/// -/// A 3d object used in SMOD files to represent an object where players should collide. -/// -public sealed class CollisionObject : IBinary -{ - /// - /// Vertices of the 3d object. - /// - public List Vertices { get; set; } = new(); - - /// - /// Triangular faces (polygons) of the 3d object. - /// - public List Faces { get; set; } = new(); - - [JsonConstructor] - public CollisionObject() - { - } - - public CollisionObject(SBinaryReader binaryReader) - { - int vertexCount = binaryReader.Read(); - for (int i = 0; i < vertexCount; i++) - Vertices.Add(new SimpleVertex(binaryReader)); - - int faceCount = binaryReader.Read(); - for (int i = 0; i < faceCount; i++) - Faces.Add(new Face(binaryReader)); - } - - /// - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Vertices.GetBytes()); - buffer.AddRange(Faces.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/SMOD/SimpleVertex.cs b/src/Parsec/Shaiya/SMOD/SimpleVertex.cs deleted file mode 100644 index 59f82c51..00000000 --- a/src/Parsec/Shaiya/SMOD/SimpleVertex.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.SMOD; - -/// -/// Represents a vertex used in SMOD collision objects -/// -public sealed class SimpleVertex : IBinary -{ - /// - /// Coordinates of the vertex in the 3D space. - /// - public Vector3 Coordinates { get; set; } - - [JsonConstructor] - public SimpleVertex() - { - } - - public SimpleVertex(SBinaryReader binaryReader) - { - Coordinates = new Vector3(binaryReader); - } - - /// - public IEnumerable GetBytes(params object[] options) => Coordinates.GetBytes(); -} diff --git a/src/Parsec/Shaiya/SMOD/TexturedObject.cs b/src/Parsec/Shaiya/SMOD/TexturedObject.cs deleted file mode 100644 index d49429ca..00000000 --- a/src/Parsec/Shaiya/SMOD/TexturedObject.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.SMOD; - -/// -/// A 3d mesh with a texture -/// -public sealed class TexturedObject : IBinary -{ - /// - /// Name of the .tga texture file. Although they have the .tga extension, the client actually has .dds files, so they're very likely - /// replacing the .tga extension with .dds when searching for the texture. - /// - public string TextureName { get; set; } - - /// - /// Mesh vertices - /// - public List Vertices { get; set; } = new(); - - /// - /// Mesh triangular faces - /// - public List Faces { get; set; } = new(); - - [JsonConstructor] - public TexturedObject() - { - } - - public TexturedObject(SBinaryReader binaryReader) - { - TextureName = binaryReader.ReadString(); - - int vertexCount = binaryReader.Read(); - for (int i = 0; i < vertexCount; i++) - Vertices.Add(new Vertex(binaryReader)); - - int faceCount = binaryReader.Read(); - for (int i = 0; i < faceCount; i++) - Faces.Add(new Face(binaryReader)); - } - - /// - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(TextureName.GetLengthPrefixedBytes()); - buffer.AddRange(Vertices.GetBytes()); - buffer.AddRange(Faces.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/SMOD/Vertex.cs b/src/Parsec/Shaiya/SMOD/Vertex.cs deleted file mode 100644 index d84e5341..00000000 --- a/src/Parsec/Shaiya/SMOD/Vertex.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Attributes; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.SMOD; - -/// -/// Represents a vertex used in an SMOD mesh -/// -public sealed class Vertex : IBinary -{ - /// - /// Vertex coordinates in the 3D space - /// - public Vector3 Coordinates { get; set; } - - /// - /// Vertex normal used for lighting - /// - public Vector3 Normal { get; set; } - - /// - /// SMODs don't have bones, that's why this value is always -1. - /// - public int BoneId { get; set; } = -1; - - /// - /// Texture mapping - /// - public Vector2 UV { get; set; } - - [JsonConstructor] - public Vertex() - { - } - - public Vertex(SBinaryReader binaryReader) - { - Coordinates = new Vector3(binaryReader); - Normal = new Vector3(binaryReader); - BoneId = binaryReader.Read(); - UV = new Vector2(binaryReader); - } - - /// - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Coordinates.GetBytes()); - buffer.AddRange(Normal.GetBytes()); - buffer.AddRange(BoneId.GetBytes()); - buffer.AddRange(UV.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Seff/Record.cs b/src/Parsec/Shaiya/Seff/Record.cs deleted file mode 100644 index 4ffb1f38..00000000 --- a/src/Parsec/Shaiya/Seff/Record.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Seff; - -public sealed class Record : IBinary -{ - [JsonConstructor] - public Record() - { - } - - public Record(SBinaryReader binaryReader, int format) - { - Id = binaryReader.Read(); - - int effectCount = binaryReader.Read(); - - for (int i = 0; i < effectCount; i++) - { - var effect = new SeffEffect(binaryReader, format); - Effects.Add(effect); - } - } - - public int Id { get; set; } - public List Effects { get; } = new(); - - public IEnumerable GetBytes(params object[] options) - { - int format = 0; - - var buffer = new List(); - - if (options.Length > 0) - format = (int)options[0]; - - buffer.AddRange(Id.GetBytes()); - buffer.AddRange(Effects.Count.GetBytes()); - foreach (var effect in Effects) - buffer.AddRange(effect.GetBytes(format)); - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Seff/Seff.cs b/src/Parsec/Shaiya/Seff/Seff.cs index e8a483c0..729903a2 100644 --- a/src/Parsec/Shaiya/Seff/Seff.cs +++ b/src/Parsec/Shaiya/Seff/Seff.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -using Parsec.Common; using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Seff; @@ -8,45 +8,29 @@ namespace Parsec.Shaiya.Seff; public sealed class Seff : FileBase { public int Format { get; set; } - public TimeStamp TimeStamp; - public List Records { get; } = new(); + + public SeffTimeStamp TimeStamp; + + public List Records { get; set; } = new(); [JsonIgnore] public override string Extension => "seff"; - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - Format = _binaryReader.Read(); - TimeStamp.Year = _binaryReader.Read(); - TimeStamp.Month = _binaryReader.Read(); - TimeStamp.Day = _binaryReader.Read(); - TimeStamp.Hour = _binaryReader.Read(); - TimeStamp.Minute = _binaryReader.Read(); - TimeStamp.Second = _binaryReader.Read(); - - int recordCount = _binaryReader.Read(); - for (int i = 0; i < recordCount; i++) - { - var record = new Record(_binaryReader, Format); - Records.Add(record); - } + Format = binaryReader.ReadInt32(); + binaryReader.SerializationOptions.ExtraOption = Format; + + TimeStamp = binaryReader.Read(); + Records = binaryReader.ReadList().ToList(); } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Format.GetBytes()); - buffer.AddRange(TimeStamp.Year.GetBytes()); - buffer.AddRange(TimeStamp.Month.GetBytes()); - buffer.AddRange(TimeStamp.Day.GetBytes()); - buffer.AddRange(TimeStamp.Hour.GetBytes()); - buffer.AddRange(TimeStamp.Minute.GetBytes()); - buffer.AddRange(TimeStamp.Second.GetBytes()); - - buffer.AddRange(Records.Count.GetBytes()); - foreach (var effect in Records) - buffer.AddRange(effect.GetBytes(Format)); - - return buffer; + binaryWriter.SerializationOptions.ExtraOption = Format; + + binaryWriter.Write(Format); + binaryWriter.Write(TimeStamp); + binaryWriter.Write(Records.ToSerializable()); } } diff --git a/src/Parsec/Shaiya/Seff/SeffEffect.cs b/src/Parsec/Shaiya/Seff/SeffEffect.cs index a4d2343b..64184507 100644 --- a/src/Parsec/Shaiya/Seff/SeffEffect.cs +++ b/src/Parsec/Shaiya/Seff/SeffEffect.cs @@ -1,115 +1,139 @@ using System.Text; -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Seff; -public sealed class SeffEffect +public sealed class SeffEffect : ISerializable { - [JsonConstructor] - public SeffEffect() - { - } - - public SeffEffect(SBinaryReader binaryReader, int format) - { - ParticleCount = binaryReader.Read(); - ParticleVelocity = binaryReader.Read(); - TextureBlendMode = binaryReader.Read(); - StartPositionMultiplier = binaryReader.Read(); - ParticleLifetime = binaryReader.Read(); - Unknown6 = binaryReader.Read(); - TextureFileName = binaryReader.ReadString(Encoding.Unicode); - Red = binaryReader.Read(); - Green = binaryReader.Read(); - Blue = binaryReader.Read(); - ParticleStartPosition = new Vector3(binaryReader); - Unknown10 = binaryReader.Read(); - ParticleStartSize = binaryReader.Read(); - IsVisible = binaryReader.Read(); - - Unknown12 = binaryReader.Read(); - - if (format > 2) - RotateWithStretchMultiplier = binaryReader.Read(); - - if (format > 3) - VelocityMultiplier = binaryReader.Read(); - - if (format > 5) - Unknown15 = binaryReader.Read(); - } - public uint ParticleCount { get; set; } + public float ParticleVelocity { get; set; } + /// /// Supported values are 0-3. 0 appears to be Normal. To-do: enumeration. /// https://en.wikipedia.org/wiki/Blend_modes /// public uint TextureBlendMode { get; set; } + /// /// Supported values are 0-9. /// public uint StartPositionMultiplier { get; set; } + /// /// Milliseconds /// public uint ParticleLifetime { get; set; } + public float Unknown6 { get; set; } - public string TextureFileName { get; set; } + + public string TextureFileName { get; set; } = string.Empty; + public byte Red { get; set; } + public byte Green { get; set; } + public byte Blue { get; set; } + public Vector3 ParticleStartPosition { get; set; } + public float Unknown10 { get; set; } + public float ParticleStartSize { get; set; } + public bool IsVisible { get; set; } + public float Unknown12 { get; set; } + public float RotateWithStretchMultiplier { get; set; } + public float VelocityMultiplier { get; set; } + public uint Unknown15 { get; set; } - public IEnumerable GetBytes(params object[] options) + public void Read(SBinaryReader binaryReader) { - int format = 0; - - if (options.Length > 0) - format = (int)options[0]; - - var buffer = new List(); - - buffer.AddRange(ParticleCount.GetBytes()); - buffer.AddRange(ParticleVelocity.GetBytes()); - buffer.AddRange(TextureBlendMode.GetBytes()); - buffer.AddRange(StartPositionMultiplier.GetBytes()); - buffer.AddRange(ParticleLifetime.GetBytes()); - buffer.AddRange(Unknown6.GetBytes()); + var format = 0; + + if (binaryReader.SerializationOptions.ExtraOption is int formatOption) + { + format = formatOption; + } + + ParticleCount = binaryReader.ReadUInt32(); + ParticleVelocity = binaryReader.ReadSingle(); + TextureBlendMode = binaryReader.ReadUInt32(); + StartPositionMultiplier = binaryReader.ReadUInt32(); + ParticleLifetime = binaryReader.ReadUInt32(); + Unknown6 = binaryReader.ReadSingle(); + TextureFileName = binaryReader.ReadString(Encoding.Unicode); + Red = binaryReader.ReadByte(); + Green = binaryReader.ReadByte(); + Blue = binaryReader.ReadByte(); + ParticleStartPosition = binaryReader.Read(); + Unknown10 = binaryReader.ReadSingle(); + ParticleStartSize = binaryReader.ReadSingle(); + IsVisible = binaryReader.ReadBool(); - buffer.AddRange(TextureFileName.GetLengthPrefixedBytes(Encoding.Unicode, false)); + Unknown12 = binaryReader.ReadSingle(); - buffer.Add(Red); - buffer.Add(Green); - buffer.Add(Blue); + if (format > 2) + { + RotateWithStretchMultiplier = binaryReader.ReadSingle(); + } - buffer.AddRange(ParticleStartPosition.GetBytes()); - buffer.AddRange(Unknown10.GetBytes()); - buffer.AddRange(ParticleStartSize.GetBytes()); + if (format > 3) + { + VelocityMultiplier = binaryReader.ReadSingle(); + } - buffer.Add(Convert.ToByte(IsVisible)); + if (format > 5) + { + Unknown15 = binaryReader.ReadUInt32(); + } + } - buffer.AddRange(Unknown12.GetBytes()); + public void Write(SBinaryWriter binaryWriter) + { + var format = 0; + + if (binaryWriter.SerializationOptions.ExtraOption is int formatOption) + { + format = formatOption; + } + + binaryWriter.Write(ParticleCount); + binaryWriter.Write(ParticleVelocity); + binaryWriter.Write(TextureBlendMode); + binaryWriter.Write(StartPositionMultiplier); + binaryWriter.Write(ParticleLifetime); + binaryWriter.Write(Unknown6); + binaryWriter.Write(TextureFileName, Encoding.Unicode, isLengthPrefixed: true, includeStringTerminator: false); + binaryWriter.Write(Red); + binaryWriter.Write(Green); + binaryWriter.Write(Blue); + binaryWriter.Write(ParticleStartPosition); + binaryWriter.Write(Unknown10); + binaryWriter.Write(ParticleStartSize); + binaryWriter.Write(IsVisible); + + binaryWriter.Write(Unknown12); if (format > 2) - buffer.AddRange(RotateWithStretchMultiplier.GetBytes()); + { + binaryWriter.Write(RotateWithStretchMultiplier); + } if (format > 3) - buffer.AddRange(VelocityMultiplier.GetBytes()); + { + binaryWriter.Write(VelocityMultiplier); + } if (format > 5) - buffer.AddRange(Unknown15.GetBytes()); - - return buffer; + { + binaryWriter.Write(Unknown15); + } } } diff --git a/src/Parsec/Shaiya/Seff/SeffRecord.cs b/src/Parsec/Shaiya/Seff/SeffRecord.cs new file mode 100644 index 00000000..002b3610 --- /dev/null +++ b/src/Parsec/Shaiya/Seff/SeffRecord.cs @@ -0,0 +1,24 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Seff; + +public sealed class SeffRecord : ISerializable +{ + public int Id { get; set; } + + public List Effects { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt32(); + Effects = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(Effects.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Seff/SeffTimeStamp.cs b/src/Parsec/Shaiya/Seff/SeffTimeStamp.cs new file mode 100644 index 00000000..f8bfa8f8 --- /dev/null +++ b/src/Parsec/Shaiya/Seff/SeffTimeStamp.cs @@ -0,0 +1,39 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Seff; + +public struct SeffTimeStamp : ISerializable +{ + public short Year; + + public short Month; + + public short Day; + + public short Hour; + + public short Minute; + + public short Second; + + public void Read(SBinaryReader binaryReader) + { + Year = binaryReader.ReadInt16(); + Month = binaryReader.ReadInt16(); + Day = binaryReader.ReadInt16(); + Hour = binaryReader.ReadInt16(); + Minute = binaryReader.ReadInt16(); + Second = binaryReader.ReadInt16(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Year); + binaryWriter.Write(Month); + binaryWriter.Write(Day); + binaryWriter.Write(Hour); + binaryWriter.Write(Minute); + binaryWriter.Write(Second); + } +} diff --git a/src/Parsec/Shaiya/Seff/TimeStamp.cs b/src/Parsec/Shaiya/Seff/TimeStamp.cs deleted file mode 100644 index 58c5d06e..00000000 --- a/src/Parsec/Shaiya/Seff/TimeStamp.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Parsec.Shaiya.Seff; - -public struct TimeStamp -{ - public short Year; - public short Month; - public short Day; - public short Hour; - public short Minute; - public short Second; -} diff --git a/src/Parsec/Shaiya/SetItem/DBSetItemDataRecord.cs b/src/Parsec/Shaiya/SetItem/DBSetItemDataRecord.cs index e57a1090..7fd3230c 100644 --- a/src/Parsec/Shaiya/SetItem/DBSetItemDataRecord.cs +++ b/src/Parsec/Shaiya/SetItem/DBSetItemDataRecord.cs @@ -1,88 +1,123 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.SetItem; public sealed class DBSetItemDataRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] public long Category1 { get; set; } - [ShaiyaProperty] public long Number1 { get; set; } - [ShaiyaProperty] public long Category2 { get; set; } - [ShaiyaProperty] public long Number2 { get; set; } - [ShaiyaProperty] public long Category3 { get; set; } - [ShaiyaProperty] public long Number3 { get; set; } - [ShaiyaProperty] public long Category4 { get; set; } - [ShaiyaProperty] public long Number4 { get; set; } - [ShaiyaProperty] public long Category5 { get; set; } - [ShaiyaProperty] public long Number5 { get; set; } - [ShaiyaProperty] public long Category6 { get; set; } - [ShaiyaProperty] public long Number6 { get; set; } - [ShaiyaProperty] public long Category7 { get; set; } - [ShaiyaProperty] public long Number7 { get; set; } - [ShaiyaProperty] public long Category8 { get; set; } - [ShaiyaProperty] public long Number8 { get; set; } - [ShaiyaProperty] public long Category9 { get; set; } - [ShaiyaProperty] public long Number9 { get; set; } - [ShaiyaProperty] public long Category10 { get; set; } - [ShaiyaProperty] public long Number10 { get; set; } - [ShaiyaProperty] public long Category11 { get; set; } - [ShaiyaProperty] public long Number11 { get; set; } - [ShaiyaProperty] public long Category12 { get; set; } - [ShaiyaProperty] public long Number12 { get; set; } - [ShaiyaProperty] public long Category13 { get; set; } - [ShaiyaProperty] public long Number13 { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + Category1 = binaryReader.ReadInt64(); + Number1 = binaryReader.ReadInt64(); + Category2 = binaryReader.ReadInt64(); + Number2 = binaryReader.ReadInt64(); + Category3 = binaryReader.ReadInt64(); + Number3 = binaryReader.ReadInt64(); + Category4 = binaryReader.ReadInt64(); + Number4 = binaryReader.ReadInt64(); + Category5 = binaryReader.ReadInt64(); + Number5 = binaryReader.ReadInt64(); + Category6 = binaryReader.ReadInt64(); + Number6 = binaryReader.ReadInt64(); + Category7 = binaryReader.ReadInt64(); + Number7 = binaryReader.ReadInt64(); + Category8 = binaryReader.ReadInt64(); + Number8 = binaryReader.ReadInt64(); + Category9 = binaryReader.ReadInt64(); + Number9 = binaryReader.ReadInt64(); + Category10 = binaryReader.ReadInt64(); + Number10 = binaryReader.ReadInt64(); + Category11 = binaryReader.ReadInt64(); + Number11 = binaryReader.ReadInt64(); + Category12 = binaryReader.ReadInt64(); + Number12 = binaryReader.ReadInt64(); + Category13 = binaryReader.ReadInt64(); + Number13 = binaryReader.ReadInt64(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(Category1); + binaryWriter.Write(Number1); + binaryWriter.Write(Category2); + binaryWriter.Write(Number2); + binaryWriter.Write(Category3); + binaryWriter.Write(Number3); + binaryWriter.Write(Category4); + binaryWriter.Write(Number4); + binaryWriter.Write(Category5); + binaryWriter.Write(Number5); + binaryWriter.Write(Category6); + binaryWriter.Write(Number6); + binaryWriter.Write(Category7); + binaryWriter.Write(Number7); + binaryWriter.Write(Category8); + binaryWriter.Write(Number8); + binaryWriter.Write(Category9); + binaryWriter.Write(Number9); + binaryWriter.Write(Category10); + binaryWriter.Write(Number10); + binaryWriter.Write(Category11); + binaryWriter.Write(Number11); + binaryWriter.Write(Category12); + binaryWriter.Write(Number12); + binaryWriter.Write(Category13); + binaryWriter.Write(Number13); + } } diff --git a/src/Parsec/Shaiya/SetItem/DBSetItemTextRecord.cs b/src/Parsec/Shaiya/SetItem/DBSetItemTextRecord.cs index 223904bf..b16837a7 100644 --- a/src/Parsec/Shaiya/SetItem/DBSetItemTextRecord.cs +++ b/src/Parsec/Shaiya/SetItem/DBSetItemTextRecord.cs @@ -1,52 +1,75 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.SetItem; public sealed class DBSetItemTextRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff1 { get; set; } + public string SetEff1 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff2 { get; set; } + public string SetEff2 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff3 { get; set; } + public string SetEff3 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff4 { get; set; } + public string SetEff4 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff5 { get; set; } + public string SetEff5 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff6 { get; set; } + public string SetEff6 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff7 { get; set; } + public string SetEff7 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff8 { get; set; } + public string SetEff8 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff9 { get; set; } + public string SetEff9 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff10 { get; set; } + public string SetEff10 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff11 { get; set; } + public string SetEff11 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff12 { get; set; } + public string SetEff12 { get; set; } = string.Empty; - [ShaiyaProperty] - public long SetEff13 { get; set; } + public string SetEff13 { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + Name = binaryReader.ReadString(); + SetEff1 = binaryReader.ReadString(); + SetEff2 = binaryReader.ReadString(); + SetEff3 = binaryReader.ReadString(); + SetEff4 = binaryReader.ReadString(); + SetEff5 = binaryReader.ReadString(); + SetEff6 = binaryReader.ReadString(); + SetEff7 = binaryReader.ReadString(); + SetEff8 = binaryReader.ReadString(); + SetEff9 = binaryReader.ReadString(); + SetEff10 = binaryReader.ReadString(); + SetEff11 = binaryReader.ReadString(); + SetEff12 = binaryReader.ReadString(); + SetEff13 = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(Name, includeStringTerminator: false); + binaryWriter.Write(SetEff1, includeStringTerminator: false); + binaryWriter.Write(SetEff2, includeStringTerminator: false); + binaryWriter.Write(SetEff3, includeStringTerminator: false); + binaryWriter.Write(SetEff4, includeStringTerminator: false); + binaryWriter.Write(SetEff5, includeStringTerminator: false); + binaryWriter.Write(SetEff6, includeStringTerminator: false); + binaryWriter.Write(SetEff7, includeStringTerminator: false); + binaryWriter.Write(SetEff8, includeStringTerminator: false); + binaryWriter.Write(SetEff9, includeStringTerminator: false); + binaryWriter.Write(SetEff10, includeStringTerminator: false); + binaryWriter.Write(SetEff11, includeStringTerminator: false); + binaryWriter.Write(SetEff12, includeStringTerminator: false); + binaryWriter.Write(SetEff13, includeStringTerminator: false); + } } diff --git a/src/Parsec/Shaiya/SetItem/SetItem.cs b/src/Parsec/Shaiya/SetItem/SetItem.cs index a8c03310..b2520a28 100644 --- a/src/Parsec/Shaiya/SetItem/SetItem.cs +++ b/src/Parsec/Shaiya/SetItem/SetItem.cs @@ -1,18 +1,19 @@ -using Parsec.Common; using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.SetItem; public sealed class SetItem : SData.SData { - public List Records { get; } = new(); + public List Records { get; set; } = new(); - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - int recordCount = _binaryReader.Read(); - for (int i = 0; i < recordCount; i++) - Records.Add(new SetItemRecord(_binaryReader)); + Records = binaryReader.ReadList().ToList(); } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) => Records.GetBytes(); + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Records.ToSerializable()); + } } diff --git a/src/Parsec/Shaiya/SetItem/SetItemItem.cs b/src/Parsec/Shaiya/SetItem/SetItemItem.cs deleted file mode 100644 index 226447b5..00000000 --- a/src/Parsec/Shaiya/SetItem/SetItemItem.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.SetItem; - -public sealed class SetItemItem : IBinary -{ - [JsonConstructor] - public SetItemItem() - { - } - - public SetItemItem(SBinaryReader binaryReader) - { - Type = binaryReader.Read(); - TypeId = binaryReader.Read(); - } - - public short Type { get; set; } - public short TypeId { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Type.GetBytes()); - buffer.AddRange(TypeId.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/SetItem/SetItemRecord.cs b/src/Parsec/Shaiya/SetItem/SetItemRecord.cs index c10ca752..93fb1b92 100644 --- a/src/Parsec/Shaiya/SetItem/SetItemRecord.cs +++ b/src/Parsec/Shaiya/SetItem/SetItemRecord.cs @@ -1,49 +1,33 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.SetItem; -public sealed class SetItemRecord : IBinary +public sealed class SetItemRecord : ISerializable { - [JsonConstructor] - public SetItemRecord() - { - } + public ushort Index { get; set; } - public SetItemRecord(SBinaryReader binaryReader) - { - Index = binaryReader.Read(); - Name = binaryReader.ReadString(); + public string Name { get; set; } = string.Empty; - for (int i = 0; i < 13; i++) - Items.Add(new SetItemItem(binaryReader)); + public List Items { get; set; } = new(); - for (int i = 0; i < 13; i++) - { - string synergy = binaryReader.ReadString(); - Synergies.Add(synergy); - } - } + public List Synergies { get; set; } = new(); - public short Index { get; set; } - public string Name { get; set; } - public List Items { get; } = new(); - public List Synergies { get; } = new(); - public IEnumerable GetBytes(params object[] options) + public void Read(SBinaryReader binaryReader) { - var buffer = new List(); - buffer.AddRange(Index.GetBytes()); - buffer.AddRange(Name.GetLengthPrefixedBytes()); - - foreach (var item in Items.Take(13)) - buffer.AddRange(item.GetBytes()); - - foreach (string synergy in Synergies.Take(13)) - buffer.AddRange(synergy.GetLengthPrefixedBytes()); + Index = binaryReader.ReadUInt16(); + Name = binaryReader.ReadString(); + Items = binaryReader.ReadList(13).ToList(); + Synergies = binaryReader.ReadList(13).ToList(); + } - return buffer; + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Index); + binaryWriter.Write(Name); + binaryWriter.Write(Items.Take(13).ToSerializable(), lengthPrefixed: false); + binaryWriter.Write(Synergies.Take(13).ToSerializable(), lengthPrefixed: false); } } diff --git a/src/Parsec/Shaiya/SetItem/SetItemRecordItem.cs b/src/Parsec/Shaiya/SetItem/SetItemRecordItem.cs new file mode 100644 index 00000000..51faf8ed --- /dev/null +++ b/src/Parsec/Shaiya/SetItem/SetItemRecordItem.cs @@ -0,0 +1,23 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.SetItem; + +public sealed class SetItemRecordItem : ISerializable +{ + public ushort Type { get; set; } + + public ushort TypeId { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Type = binaryReader.ReadUInt16(); + TypeId = binaryReader.ReadUInt16(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Type); + binaryWriter.Write(TypeId); + } +} diff --git a/src/Parsec/Shaiya/SetItem/SetItemSynergy.cs b/src/Parsec/Shaiya/SetItem/SetItemSynergy.cs new file mode 100644 index 00000000..3f1c9e72 --- /dev/null +++ b/src/Parsec/Shaiya/SetItem/SetItemSynergy.cs @@ -0,0 +1,19 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.SetItem; + +public class SetItemSynergy : ISerializable +{ + public string Synergy { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Synergy = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Synergy); + } +} diff --git a/src/Parsec/Shaiya/Skill/DBSkillDataRecord.cs b/src/Parsec/Shaiya/Skill/DBSkillDataRecord.cs index 92aae8ca..17b96e72 100644 --- a/src/Parsec/Shaiya/Skill/DBSkillDataRecord.cs +++ b/src/Parsec/Shaiya/Skill/DBSkillDataRecord.cs @@ -1,4 +1,4 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.Skill; @@ -8,617 +8,657 @@ public sealed class DBSkillDataRecord : IBinarySDataRecord /// /// Id of skill. /// - [ShaiyaProperty] public long Id { get; set; } /// /// Level of skill. /// - [ShaiyaProperty] public long SkillLevel { get; set; } /// /// Skill icon. /// - [ShaiyaProperty] public long Image { get; set; } /// /// Skill animation. /// - - [ShaiyaProperty] public long Ani { get; set; } /// /// ? /// - - [ShaiyaProperty] public long Effect { get; set; } /// /// ? /// - - [ShaiyaProperty] public long ToggleType { get; set; } /// /// Skill sound effect. /// - - [ShaiyaProperty] public long Sound { get; set; } /// /// Character required level. /// - - [ShaiyaProperty] public long Level { get; set; } /// /// Which faction and profession can use this skill. /// - - [ShaiyaProperty] public long Country { get; set; } /// /// Indicates if skill can be used by fighter. /// - - [ShaiyaProperty] public long AttackFighter { get; set; } /// /// Indicates if skill can be used by defender. /// - - [ShaiyaProperty] public long DefenseFighter { get; set; } /// /// Indicates if skill can be used by ranger. /// - - [ShaiyaProperty] public long PatrolRogue { get; set; } /// /// Indicates if skill can be used by archer. /// - - [ShaiyaProperty] public long ShootRogue { get; set; } /// /// Indicates if skill can be used by mage. /// - - [ShaiyaProperty] public long AttackMage { get; set; } /// /// Indicates if skill can be used by priest. /// - - [ShaiyaProperty] public long DefenseMage { get; set; } /// /// Skill can be used in basic/ultimate mode. /// - - [ShaiyaProperty] public long Grow { get; set; } /// /// How many skill points are needed in order to learn this skill. /// - - [ShaiyaProperty] public long Point { get; set; } /// /// Category of skill. E.g. combat or special. /// - - [ShaiyaProperty] public long TypeShow { get; set; } /// /// Passive, physical, magic or shooting attack. /// - - [ShaiyaProperty] public long TypeAttack { get; set; } /// /// Additional effect description. /// - - [ShaiyaProperty] public long TypeEffect { get; set; } /// /// Type detail describes what skill does. /// - - [ShaiyaProperty] public long Type { get; set; } /// /// Skill requires 1 Handed Sword. /// - - [ShaiyaProperty] public long NeedWeapon1 { get; set; } /// /// Skill requires 2 Handed Sword. /// - - [ShaiyaProperty] public long NeedWeapon2 { get; set; } /// /// Skill requires 1 Handed Axe. /// - - [ShaiyaProperty] public long NeedWeapon3 { get; set; } /// /// Skill requires 2 Handed Axe. /// - - [ShaiyaProperty] public long NeedWeapon4 { get; set; } /// /// Skill requires Double Sword. /// - - [ShaiyaProperty] public long NeedWeapon5 { get; set; } /// /// Skill requires Spear. /// - - [ShaiyaProperty] public long NeedWeapon6 { get; set; } /// /// Skill requires 1 Handed Blunt. /// - - [ShaiyaProperty] public long NeedWeapon7 { get; set; } /// /// Skill requires 2 Handed Blunt. /// - - [ShaiyaProperty] public long NeedWeapon8 { get; set; } /// /// Skill requires Reverse sword. /// - - [ShaiyaProperty] public long NeedWeapon9 { get; set; } /// /// Skill requires Dagger. /// - - [ShaiyaProperty] public long NeedWeapon10 { get; set; } /// /// Skill requires Javelin. /// - - [ShaiyaProperty] public long NeedWeapon11 { get; set; } /// /// Skill requires Staff. /// - - [ShaiyaProperty] public long NeedWeapon12 { get; set; } /// /// Skill requires Bow. /// - - [ShaiyaProperty] public long NeedWeapon13 { get; set; } /// /// Skill requires Crossbow. /// - - [ShaiyaProperty] public long NeedWeapon14 { get; set; } /// /// Skill requires Knuckle. /// - - [ShaiyaProperty] public long NeedWeapon15 { get; set; } /// /// Skill requires shield. /// - - [ShaiyaProperty] public long Shield { get; set; } /// /// How many stamina points requires the skill. /// - - [ShaiyaProperty] public long SP { get; set; } /// /// How many mana points requires the skill. /// - - [ShaiyaProperty] public long MP { get; set; } /// /// Cast time. /// - - [ShaiyaProperty] public long ReadyTime { get; set; } /// /// Time after which skill can be used again. /// - - [ShaiyaProperty] public long ResetTime { get; set; } /// /// How many meters are needed in order to use the skill. /// - - [ShaiyaProperty] public long AttackRange { get; set; } /// /// State type contains information about what bad influence debuff has on target. /// - - [ShaiyaProperty] public long StateType { get; set; } /// /// None or fire/wind/earth/water. /// - - [ShaiyaProperty] public long AttribType { get; set; } /// /// ? /// - - [ShaiyaProperty] public long Disable { get; set; } /// /// Skill, that must be used before the skill. /// - - [ShaiyaProperty] public long PrevSkill { get; set; } /// /// SuccessType is always 0 for passive skills and 1 for other. /// - - [ShaiyaProperty] public long SuccessType { get; set; } /// /// Success chance in %. /// - - [ShaiyaProperty] public long SuccessValue { get; set; } /// /// What target is required for the skill. /// - - [ShaiyaProperty] public long TargetType { get; set; } /// /// Skill will be applied within X meters. /// - - [ShaiyaProperty] public long ApplyRange { get; set; } /// /// Used in multiple skill attacks. /// - - [ShaiyaProperty] public long MultiAttack { get; set; } /// /// Time for example for buffs. This time shows how long the skill will be applied. /// - - [ShaiyaProperty] public long KeepTime { get; set; } /// /// Only for passive skills; Weapon type to which passive skill speed modificator can be applied. /// - - [ShaiyaProperty] public long Weapon1 { get; set; } /// /// Only for passive skills; Weapon type to which passive skill speed modificator can be applied. /// - - [ShaiyaProperty] public long Weapon2 { get; set; } /// /// Only for passive skills; passive skill speed modificator or passive attack power up. /// - - [ShaiyaProperty] public long WeaponValue { get; set; } /// /// ? /// - - [ShaiyaProperty] public long Bag { get; set; } /// /// ? /// - - [ShaiyaProperty] public long Arrow { get; set; } /// /// Damage type. /// - - [ShaiyaProperty] public long DamageType { get; set; } /// /// Const damage used, when skill makes fixed damage. /// - - [ShaiyaProperty] public long Damage1 { get; set; } /// /// Const damage used, when skill makes fixed damage. /// - - [ShaiyaProperty] public long Damage2 { get; set; } /// /// Const damage used, when skill makes fixed damage. /// - - [ShaiyaProperty] public long Damage3 { get; set; } /// /// Time damage type. /// - - [ShaiyaProperty] public long TimeDamageType { get; set; } /// /// Either fixed hp or % hp damage made over time. /// - - [ShaiyaProperty] public long TimeDamage1 { get; set; } /// /// Either fixed sp or % sp damage made over time. /// - - [ShaiyaProperty] public long TimeDamage2 { get; set; } /// /// Either fixed mp or % mp damage made over time. /// - - [ShaiyaProperty] public long TimeDamage3 { get; set; } /// /// Const skill damage, that is added to damage made of stats. /// - - [ShaiyaProperty] public long AddDamage1 { get; set; } /// /// Const skill damage, that is added to damage made of stats. /// - - [ShaiyaProperty] public long AddDamage2 { get; set; } /// /// Const skill damage, that is added to damage made of stats. /// - - [ShaiyaProperty] public long AddDamage3 { get; set; } - [ShaiyaProperty] public long AbilityType1 { get; set; } - [ShaiyaProperty] public long AbilityValue1 { get; set; } - [ShaiyaProperty] public long AbilityType2 { get; set; } - [ShaiyaProperty] public long AbilityValue2 { get; set; } - [ShaiyaProperty] public long AbilityType3 { get; set; } - [ShaiyaProperty] public long AbilityValue3 { get; set; } - [ShaiyaProperty] public long AbilityType4 { get; set; } - [ShaiyaProperty] public long AbilityValue4 { get; set; } - [ShaiyaProperty] public long AbilityType5 { get; set; } - [ShaiyaProperty] public long AbilityValue5 { get; set; } - [ShaiyaProperty] public long AbilityType6 { get; set; } - [ShaiyaProperty] public long AbilityValue6 { get; set; } - [ShaiyaProperty] public long AbilityType7 { get; set; } - [ShaiyaProperty] public long AbilityValue7 { get; set; } - [ShaiyaProperty] public long AbilityType8 { get; set; } - [ShaiyaProperty] public long AbilityValue8 { get; set; } - [ShaiyaProperty] public long AbilityType9 { get; set; } - [ShaiyaProperty] public long AbilityValue9 { get; set; } - [ShaiyaProperty] public long AbilityType10 { get; set; } - [ShaiyaProperty] public long AbilityValue10 { get; set; } /// /// How many health points can be healed. /// - - [ShaiyaProperty] public long Heal1 { get; set; } /// /// How many stamina points can be healed. /// - - [ShaiyaProperty] public long Heal2 { get; set; } /// /// How many mana points can be healed. /// - - [ShaiyaProperty] public long Heal3 { get; set; } /// /// HP healed over time. /// - - [ShaiyaProperty] public long TimeHeal1 { get; set; } /// /// SP healed over time. /// - [ShaiyaProperty] public long TimeHeal2 { get; set; } /// /// MP healed over time. /// - [ShaiyaProperty] public long TimeHeal3 { get; set; } /// /// For "Fleet Foot" it's value 2, which is block shoot attack for X %. /// For "Magic Veil" it's value 3, which is block X magic attacks. /// - [ShaiyaProperty] public long DefenceType { get; set; } /// /// When is 2, it's % of blocked shoot attacks. /// When is 3, it's block X magic attacks. /// - [ShaiyaProperty] public long DefenceValue { get; set; } /// /// % of hp, when this skill is activated. /// - [ShaiyaProperty] public long LimitHP { get; set; } /// /// How long the skill should be kept. /// - [ShaiyaProperty] public long FixRange { get; set; } /// /// ? /// - [ShaiyaProperty] public long ChangeType { get; set; } /// /// ? /// - [ShaiyaProperty] public long ChangeLevel { get; set; } /// /// ? /// - [ShaiyaProperty] public long TacticZoneBound { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + SkillLevel = binaryReader.ReadInt64(); + Image = binaryReader.ReadInt64(); + Ani = binaryReader.ReadInt64(); + Effect = binaryReader.ReadInt64(); + ToggleType = binaryReader.ReadInt64(); + Sound = binaryReader.ReadInt64(); + Level = binaryReader.ReadInt64(); + Country = binaryReader.ReadInt64(); + AttackFighter = binaryReader.ReadInt64(); + DefenseFighter = binaryReader.ReadInt64(); + PatrolRogue = binaryReader.ReadInt64(); + ShootRogue = binaryReader.ReadInt64(); + AttackMage = binaryReader.ReadInt64(); + DefenseMage = binaryReader.ReadInt64(); + Grow = binaryReader.ReadInt64(); + Point = binaryReader.ReadInt64(); + TypeShow = binaryReader.ReadInt64(); + TypeAttack = binaryReader.ReadInt64(); + TypeEffect = binaryReader.ReadInt64(); + Type = binaryReader.ReadInt64(); + NeedWeapon1 = binaryReader.ReadInt64(); + NeedWeapon2 = binaryReader.ReadInt64(); + NeedWeapon3 = binaryReader.ReadInt64(); + NeedWeapon4 = binaryReader.ReadInt64(); + NeedWeapon5 = binaryReader.ReadInt64(); + NeedWeapon6 = binaryReader.ReadInt64(); + NeedWeapon7 = binaryReader.ReadInt64(); + NeedWeapon8 = binaryReader.ReadInt64(); + NeedWeapon9 = binaryReader.ReadInt64(); + NeedWeapon10 = binaryReader.ReadInt64(); + NeedWeapon11 = binaryReader.ReadInt64(); + NeedWeapon12 = binaryReader.ReadInt64(); + NeedWeapon13 = binaryReader.ReadInt64(); + NeedWeapon14 = binaryReader.ReadInt64(); + NeedWeapon15 = binaryReader.ReadInt64(); + Shield = binaryReader.ReadInt64(); + SP = binaryReader.ReadInt64(); + MP = binaryReader.ReadInt64(); + ReadyTime = binaryReader.ReadInt64(); + ResetTime = binaryReader.ReadInt64(); + AttackRange = binaryReader.ReadInt64(); + StateType = binaryReader.ReadInt64(); + AttribType = binaryReader.ReadInt64(); + Disable = binaryReader.ReadInt64(); + PrevSkill = binaryReader.ReadInt64(); + SuccessType = binaryReader.ReadInt64(); + SuccessValue = binaryReader.ReadInt64(); + TargetType = binaryReader.ReadInt64(); + ApplyRange = binaryReader.ReadInt64(); + MultiAttack = binaryReader.ReadInt64(); + KeepTime = binaryReader.ReadInt64(); + Weapon1 = binaryReader.ReadInt64(); + Weapon2 = binaryReader.ReadInt64(); + WeaponValue = binaryReader.ReadInt64(); + Bag = binaryReader.ReadInt64(); + Arrow = binaryReader.ReadInt64(); + DamageType = binaryReader.ReadInt64(); + Damage1 = binaryReader.ReadInt64(); + Damage2 = binaryReader.ReadInt64(); + Damage3 = binaryReader.ReadInt64(); + TimeDamageType = binaryReader.ReadInt64(); + TimeDamage1 = binaryReader.ReadInt64(); + TimeDamage2 = binaryReader.ReadInt64(); + TimeDamage3 = binaryReader.ReadInt64(); + AddDamage1 = binaryReader.ReadInt64(); + AddDamage2 = binaryReader.ReadInt64(); + AddDamage3 = binaryReader.ReadInt64(); + AbilityType1 = binaryReader.ReadInt64(); + AbilityValue1 = binaryReader.ReadInt64(); + AbilityType2 = binaryReader.ReadInt64(); + AbilityValue2 = binaryReader.ReadInt64(); + AbilityType3 = binaryReader.ReadInt64(); + AbilityValue3 = binaryReader.ReadInt64(); + AbilityType4 = binaryReader.ReadInt64(); + AbilityValue4 = binaryReader.ReadInt64(); + AbilityType5 = binaryReader.ReadInt64(); + AbilityValue5 = binaryReader.ReadInt64(); + AbilityType6 = binaryReader.ReadInt64(); + AbilityValue6 = binaryReader.ReadInt64(); + AbilityType7 = binaryReader.ReadInt64(); + AbilityValue7 = binaryReader.ReadInt64(); + AbilityType8 = binaryReader.ReadInt64(); + AbilityValue8 = binaryReader.ReadInt64(); + AbilityType9 = binaryReader.ReadInt64(); + AbilityValue9 = binaryReader.ReadInt64(); + AbilityType10 = binaryReader.ReadInt64(); + AbilityValue10 = binaryReader.ReadInt64(); + Heal1 = binaryReader.ReadInt64(); + Heal2 = binaryReader.ReadInt64(); + Heal3 = binaryReader.ReadInt64(); + TimeHeal1 = binaryReader.ReadInt64(); + TimeHeal2 = binaryReader.ReadInt64(); + TimeHeal3 = binaryReader.ReadInt64(); + DefenceType = binaryReader.ReadInt64(); + DefenceValue = binaryReader.ReadInt64(); + LimitHP = binaryReader.ReadInt64(); + FixRange = binaryReader.ReadInt64(); + ChangeType = binaryReader.ReadInt64(); + ChangeLevel = binaryReader.ReadInt64(); + TacticZoneBound = binaryReader.ReadInt64(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(SkillLevel); + binaryWriter.Write(Image); + binaryWriter.Write(Ani); + binaryWriter.Write(Effect); + binaryWriter.Write(ToggleType); + binaryWriter.Write(Sound); + binaryWriter.Write(Level); + binaryWriter.Write(Country); + binaryWriter.Write(AttackFighter); + binaryWriter.Write(DefenseFighter); + binaryWriter.Write(PatrolRogue); + binaryWriter.Write(ShootRogue); + binaryWriter.Write(AttackMage); + binaryWriter.Write(DefenseMage); + binaryWriter.Write(Grow); + binaryWriter.Write(Point); + binaryWriter.Write(TypeShow); + binaryWriter.Write(TypeAttack); + binaryWriter.Write(TypeEffect); + binaryWriter.Write(Type); + binaryWriter.Write(NeedWeapon1); + binaryWriter.Write(NeedWeapon2); + binaryWriter.Write(NeedWeapon3); + binaryWriter.Write(NeedWeapon4); + binaryWriter.Write(NeedWeapon5); + binaryWriter.Write(NeedWeapon6); + binaryWriter.Write(NeedWeapon7); + binaryWriter.Write(NeedWeapon8); + binaryWriter.Write(NeedWeapon9); + binaryWriter.Write(NeedWeapon10); + binaryWriter.Write(NeedWeapon11); + binaryWriter.Write(NeedWeapon12); + binaryWriter.Write(NeedWeapon13); + binaryWriter.Write(NeedWeapon14); + binaryWriter.Write(NeedWeapon15); + binaryWriter.Write(Shield); + binaryWriter.Write(SP); + binaryWriter.Write(MP); + binaryWriter.Write(ReadyTime); + binaryWriter.Write(ResetTime); + binaryWriter.Write(AttackRange); + binaryWriter.Write(StateType); + binaryWriter.Write(AttribType); + binaryWriter.Write(Disable); + binaryWriter.Write(PrevSkill); + binaryWriter.Write(SuccessType); + binaryWriter.Write(SuccessValue); + binaryWriter.Write(TargetType); + binaryWriter.Write(ApplyRange); + binaryWriter.Write(MultiAttack); + binaryWriter.Write(KeepTime); + binaryWriter.Write(Weapon1); + binaryWriter.Write(Weapon2); + binaryWriter.Write(WeaponValue); + binaryWriter.Write(Bag); + binaryWriter.Write(Arrow); + binaryWriter.Write(DamageType); + binaryWriter.Write(Damage1); + binaryWriter.Write(Damage2); + binaryWriter.Write(Damage3); + binaryWriter.Write(TimeDamageType); + binaryWriter.Write(TimeDamage1); + binaryWriter.Write(TimeDamage2); + binaryWriter.Write(TimeDamage3); + binaryWriter.Write(AddDamage1); + binaryWriter.Write(AddDamage2); + binaryWriter.Write(AddDamage3); + binaryWriter.Write(AbilityType1); + binaryWriter.Write(AbilityValue1); + binaryWriter.Write(AbilityType2); + binaryWriter.Write(AbilityValue2); + binaryWriter.Write(AbilityType3); + binaryWriter.Write(AbilityValue3); + binaryWriter.Write(AbilityType4); + binaryWriter.Write(AbilityValue4); + binaryWriter.Write(AbilityType5); + binaryWriter.Write(AbilityValue5); + binaryWriter.Write(AbilityType6); + binaryWriter.Write(AbilityValue6); + binaryWriter.Write(AbilityType7); + binaryWriter.Write(AbilityValue7); + binaryWriter.Write(AbilityType8); + binaryWriter.Write(AbilityValue8); + binaryWriter.Write(AbilityType9); + binaryWriter.Write(AbilityValue9); + binaryWriter.Write(AbilityType10); + binaryWriter.Write(AbilityValue10); + binaryWriter.Write(Heal1); + binaryWriter.Write(Heal2); + binaryWriter.Write(Heal3); + binaryWriter.Write(TimeHeal1); + binaryWriter.Write(TimeHeal2); + binaryWriter.Write(TimeHeal3); + binaryWriter.Write(DefenceType); + binaryWriter.Write(DefenceValue); + binaryWriter.Write(LimitHP); + binaryWriter.Write(FixRange); + binaryWriter.Write(ChangeType); + binaryWriter.Write(ChangeLevel); + binaryWriter.Write(TacticZoneBound); + } } diff --git a/src/Parsec/Shaiya/Skill/DBSkillTextRecord.cs b/src/Parsec/Shaiya/Skill/DBSkillTextRecord.cs index efecdaf9..1e35b1c7 100644 --- a/src/Parsec/Shaiya/Skill/DBSkillTextRecord.cs +++ b/src/Parsec/Shaiya/Skill/DBSkillTextRecord.cs @@ -1,21 +1,31 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.Skill; public sealed class DBSkillTextRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] public long SkillLevel { get; set; } - [ShaiyaProperty] - [LengthPrefixedString(false)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - [ShaiyaProperty] - [LengthPrefixedString(false)] - public string Text { get; set; } + public string Text { get; set; } = string.Empty; + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + SkillLevel = binaryReader.ReadInt64(); + Name = binaryReader.ReadString(); + Text = binaryReader.ReadString(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(SkillLevel); + binaryWriter.Write(Name, includeStringTerminator: false); + binaryWriter.Write(Text, includeStringTerminator: false); + } } diff --git a/src/Parsec/Shaiya/Skill/Skill.cs b/src/Parsec/Shaiya/Skill/Skill.cs index a903620c..1e1848b4 100644 --- a/src/Parsec/Shaiya/Skill/Skill.cs +++ b/src/Parsec/Shaiya/Skill/Skill.cs @@ -3,84 +3,60 @@ using CsvHelper; using Parsec.Common; using Parsec.Extensions; +using Parsec.Serialization; namespace Parsec.Shaiya.Skill; public sealed class Skill : SData.SData, ICsv { - public List Records { get; set; } = new(); + public List SkillGroups { get; set; } = new(); - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - var skillCount = _binaryReader.Read(); - var recordCountPerSkill = GetRecordCountPerSkill(Episode); + var skillGroupCount = binaryReader.ReadInt32(); - for (int skillId = 0; skillId < skillCount; skillId++) + for (var skillGroupId = 0; skillGroupId < skillGroupCount; skillGroupId++) { - for (int i = 0; i < recordCountPerSkill; i++) - { - var record = new SkillRecord(_binaryReader, Episode, skillId); - Records.Add(record); - } + binaryReader.SerializationOptions.ExtraOption = skillGroupId; + var skillGroup = binaryReader.Read(); + SkillGroups.Add(skillGroup); } } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - if (episode == Episode.Unknown) - { - episode = Episode.EP5; - } - - var buffer = new List(); - - var skillCount = Records.Count / GetRecordCountPerSkill(episode); - buffer.AddRange(skillCount.GetBytes()); - - foreach (var record in Records) - { - buffer.AddRange(record.GetBytes(episode)); - } - - return buffer.ToArray(); + binaryWriter.Write(SkillGroups.ToSerializable()); } - private int GetRecordCountPerSkill(Episode episode) - { - return episode switch - { - Episode.EP5 => 9, - >= Episode.EP6 => 15, - _ => 3 - }; - } - - /// - /// Reads the Skill.SData format from a csv file - /// - /// csv file path - /// File episode - /// File encoding - /// instance - public static Skill ReadFromCsv(string csvPath, Episode episode, Encoding encoding = null) + public static Skill FromCsv(string csvFilePath, Episode episode = Episode.EP5, Encoding? encoding = null) { encoding ??= Encoding.ASCII; - // Read all skill definitions from csv file - using var reader = new StreamReader(csvPath, encoding); + using var reader = new StreamReader(csvFilePath, encoding); using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture); - var records = csvReader.GetRecords().ToList(); + var skillRecords = csvReader.GetRecords().ToList(); + + var groupedSkillRecords = skillRecords.GroupBy(x => x.SkillId); + var skill = new Skill { Episode = episode, Encoding = encoding }; + + foreach (var groupedSkills in groupedSkillRecords) + { + var skillGroup = new SkillGroup { SkillDefinitions = groupedSkills.ToList() }; + skill.SkillGroups.Add(skillGroup); + } - // Create skill instance - var skill = new Skill { Episode = episode, Records = records, Encoding = encoding }; return skill; } - public void WriteCsv(string outputPath, Encoding encoding = null) + public void WriteCsv(string outputPath, Encoding? encoding = null) { encoding ??= Encoding.ASCII; using var writer = new StreamWriter(outputPath, false, encoding); using var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture); - csvWriter.WriteRecords(Records); + + foreach (var skillGroup in SkillGroups) + { + csvWriter.WriteRecords(skillGroup.SkillDefinitions); + } } } diff --git a/src/Parsec/Shaiya/Skill/SkillRecord.cs b/src/Parsec/Shaiya/Skill/SkillDefinition.cs similarity index 55% rename from src/Parsec/Shaiya/Skill/SkillRecord.cs rename to src/Parsec/Shaiya/Skill/SkillDefinition.cs index 76afbe0b..a9d9b057 100644 --- a/src/Parsec/Shaiya/Skill/SkillRecord.cs +++ b/src/Parsec/Shaiya/Skill/SkillDefinition.cs @@ -1,6 +1,5 @@ using Parsec.Common; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Skill; @@ -8,142 +7,22 @@ namespace Parsec.Shaiya.Skill; /// /// Class that represents a record for the Skill.SData and NpcSkill.SData formats /// -public sealed class SkillRecord : IBinary +public sealed class SkillDefinition : ISerializable { - public SkillRecord() - { - } - - public SkillRecord(SBinaryReader binaryReader, Episode episode, int id) - { - Id = id; - Name = binaryReader.ReadString(); - Description = binaryReader.ReadString(); - SkillLevel = binaryReader.Read(); - Icon = binaryReader.Read(); - Animation = binaryReader.Read(); - - if (episode >= Episode.EP6) - { - Effect = binaryReader.Read(); - } - - ToggleType = binaryReader.Read(); - Sound = binaryReader.Read(); - RequiredLevel = binaryReader.Read(); - Country = binaryReader.Read(); - AttackFighter = binaryReader.Read(); - DefenseFighter = binaryReader.Read(); - PatrolRogue = binaryReader.Read(); - ShootRogue = binaryReader.Read(); - AttackMage = binaryReader.Read(); - DefenseMage = binaryReader.Read(); - Grow = binaryReader.Read(); - Point = binaryReader.Read(); - TypeShow = binaryReader.Read(); - TypeAttack = binaryReader.Read(); - TypeEffect = binaryReader.Read(); - TypeDetail = binaryReader.Read(); - NeedWeapon1 = binaryReader.Read(); - NeedWeapon2 = binaryReader.Read(); - NeedWeapon3 = binaryReader.Read(); - NeedWeapon4 = binaryReader.Read(); - NeedWeapon5 = binaryReader.Read(); - NeedWeapon6 = binaryReader.Read(); - NeedWeapon7 = binaryReader.Read(); - NeedWeapon8 = binaryReader.Read(); - NeedWeapon9 = binaryReader.Read(); - NeedWeapon10 = binaryReader.Read(); - NeedWeapon11 = binaryReader.Read(); - NeedWeapon12 = binaryReader.Read(); - NeedWeapon13 = binaryReader.Read(); - NeedWeapon14 = binaryReader.Read(); - NeedWeapon15 = binaryReader.Read(); - Shield = binaryReader.Read(); - SP = binaryReader.Read(); - MP = binaryReader.Read(); - ReadyTime = binaryReader.Read(); - ResetTime = binaryReader.Read(); - AttackRange = binaryReader.Read(); - StateType = binaryReader.Read(); - AttribType = binaryReader.Read(); - Disable = binaryReader.Read(); - PrevSkill = binaryReader.Read(); - SuccessType = binaryReader.Read(); - SuccessValue = binaryReader.Read(); - TargetType = binaryReader.Read(); - ApplyRange = binaryReader.Read(); - MultiAttack = binaryReader.Read(); - KeepTime = binaryReader.Read(); - Weapon1 = binaryReader.Read(); - Weapon2 = binaryReader.Read(); - WeaponValue = binaryReader.Read(); - Bag = binaryReader.Read(); - Arrow = binaryReader.Read(); - DamageType = binaryReader.Read(); - DamageHP = binaryReader.Read(); - DamageSP = binaryReader.Read(); - DamageMP = binaryReader.Read(); - TimeDamageType = binaryReader.Read(); - TimeDamageHP = binaryReader.Read(); - TimeDamageSP = binaryReader.Read(); - TimeDamageMP = binaryReader.Read(); - AddDamageHP = binaryReader.Read(); - AddDamageSP = binaryReader.Read(); - AddDamageMP = binaryReader.Read(); - AbilityType1 = binaryReader.Read(); - AbilityValue1 = binaryReader.Read(); - AbilityType2 = binaryReader.Read(); - AbilityValue2 = binaryReader.Read(); - AbilityType3 = binaryReader.Read(); - AbilityValue3 = binaryReader.Read(); - - if (episode >= Episode.EP6) - { - AbilityType4 = binaryReader.Read(); - AbilityValue4 = binaryReader.Read(); - AbilityType5 = binaryReader.Read(); - AbilityValue5 = binaryReader.Read(); - AbilityType6 = binaryReader.Read(); - AbilityValue6 = binaryReader.Read(); - AbilityType7 = binaryReader.Read(); - AbilityValue7 = binaryReader.Read(); - AbilityType8 = binaryReader.Read(); - AbilityValue8 = binaryReader.Read(); - AbilityType9 = binaryReader.Read(); - AbilityValue9 = binaryReader.Read(); - AbilityType10 = binaryReader.Read(); - AbilityValue10 = binaryReader.Read(); - } - - HealHP = binaryReader.Read(); - HealSP = binaryReader.Read(); - HealMP = binaryReader.Read(); - TimeHealHP = binaryReader.Read(); - TimeHealSP = binaryReader.Read(); - TimeHealMP = binaryReader.Read(); - DefenseType = binaryReader.Read(); - DefenseValue = binaryReader.Read(); - LimitHP = binaryReader.Read(); - FixRange = binaryReader.Read(); - ChangeType = binaryReader.Read(); - ChangeLevel = binaryReader.Read(); - } - /// - /// The skill Id. It's not part of the structure itself, but it's assigned manually when reading the file. + /// SkillId is not part of the structure itself, instead, it's set based on the order of the skill groups. /// - public int Id { get; set; } + public int SkillId { get; set; } /// /// The skill name /// - public string Name { get; set; } + public string Name { get; set; } = string.Empty; /// /// The skill description /// - public string Description { get; set; } + public string Description { get; set; } = string.Empty; /// /// Level of skill. @@ -582,127 +461,242 @@ public SkillRecord(SBinaryReader binaryReader, Episode episode, int id) /// public ushort ChangeLevel { get; set; } - public IEnumerable GetBytes(params object[] options) + public void Read(SBinaryReader binaryReader) { - var episode = Episode.EP5; + if (binaryReader.SerializationOptions.ExtraOption is int skillId) + { + SkillId = skillId; + } - if (options.Length > 0) + var episode = binaryReader.SerializationOptions.Episode; + + Name = binaryReader.ReadString(); + Description = binaryReader.ReadString(); + SkillLevel = binaryReader.ReadByte(); + Icon = binaryReader.ReadUInt16(); + Animation = binaryReader.ReadUInt16(); + + if (episode >= Episode.EP6) { - episode = (Episode)options[0]; + Effect = binaryReader.ReadByte(); } - var buffer = new List(); - buffer.AddRange(Name.GetLengthPrefixedBytes()); - buffer.AddRange(Description.GetLengthPrefixedBytes()); - buffer.Add(SkillLevel); - buffer.AddRange(Icon.GetBytes()); - buffer.AddRange(Animation.GetBytes()); + ToggleType = binaryReader.ReadByte(); + Sound = binaryReader.ReadUInt16(); + RequiredLevel = binaryReader.ReadUInt16(); + Country = binaryReader.ReadByte(); + AttackFighter = binaryReader.ReadByte(); + DefenseFighter = binaryReader.ReadByte(); + PatrolRogue = binaryReader.ReadByte(); + ShootRogue = binaryReader.ReadByte(); + AttackMage = binaryReader.ReadByte(); + DefenseMage = binaryReader.ReadByte(); + Grow = binaryReader.ReadByte(); + Point = binaryReader.ReadByte(); + TypeShow = binaryReader.ReadByte(); + TypeAttack = binaryReader.ReadByte(); + TypeEffect = binaryReader.ReadByte(); + TypeDetail = binaryReader.ReadUInt16(); + NeedWeapon1 = binaryReader.ReadByte(); + NeedWeapon2 = binaryReader.ReadByte(); + NeedWeapon3 = binaryReader.ReadByte(); + NeedWeapon4 = binaryReader.ReadByte(); + NeedWeapon5 = binaryReader.ReadByte(); + NeedWeapon6 = binaryReader.ReadByte(); + NeedWeapon7 = binaryReader.ReadByte(); + NeedWeapon8 = binaryReader.ReadByte(); + NeedWeapon9 = binaryReader.ReadByte(); + NeedWeapon10 = binaryReader.ReadByte(); + NeedWeapon11 = binaryReader.ReadByte(); + NeedWeapon12 = binaryReader.ReadByte(); + NeedWeapon13 = binaryReader.ReadByte(); + NeedWeapon14 = binaryReader.ReadByte(); + NeedWeapon15 = binaryReader.ReadByte(); + Shield = binaryReader.ReadByte(); + SP = binaryReader.ReadUInt16(); + MP = binaryReader.ReadUInt16(); + ReadyTime = binaryReader.ReadByte(); + ResetTime = binaryReader.ReadUInt16(); + AttackRange = binaryReader.ReadByte(); + StateType = binaryReader.ReadByte(); + AttribType = binaryReader.ReadByte(); + Disable = binaryReader.ReadUInt16(); + PrevSkill = binaryReader.ReadUInt16(); + SuccessType = binaryReader.ReadByte(); + SuccessValue = binaryReader.ReadByte(); + TargetType = binaryReader.ReadByte(); + ApplyRange = binaryReader.ReadByte(); + MultiAttack = binaryReader.ReadByte(); + KeepTime = binaryReader.ReadUInt16(); + Weapon1 = binaryReader.ReadByte(); + Weapon2 = binaryReader.ReadByte(); + WeaponValue = binaryReader.ReadByte(); + Bag = binaryReader.ReadByte(); + Arrow = binaryReader.ReadUInt16(); + DamageType = binaryReader.ReadByte(); + DamageHP = binaryReader.ReadUInt16(); + DamageSP = binaryReader.ReadUInt16(); + DamageMP = binaryReader.ReadUInt16(); + TimeDamageType = binaryReader.ReadByte(); + TimeDamageHP = binaryReader.ReadUInt16(); + TimeDamageSP = binaryReader.ReadUInt16(); + TimeDamageMP = binaryReader.ReadUInt16(); + AddDamageHP = binaryReader.ReadUInt16(); + AddDamageSP = binaryReader.ReadUInt16(); + AddDamageMP = binaryReader.ReadUInt16(); + AbilityType1 = binaryReader.ReadByte(); + AbilityValue1 = binaryReader.ReadUInt16(); + AbilityType2 = binaryReader.ReadByte(); + AbilityValue2 = binaryReader.ReadUInt16(); + AbilityType3 = binaryReader.ReadByte(); + AbilityValue3 = binaryReader.ReadUInt16(); + + if (episode >= Episode.EP6) + { + AbilityType4 = binaryReader.ReadByte(); + AbilityValue4 = binaryReader.ReadUInt16(); + AbilityType5 = binaryReader.ReadByte(); + AbilityValue5 = binaryReader.ReadUInt16(); + AbilityType6 = binaryReader.ReadByte(); + AbilityValue6 = binaryReader.ReadUInt16(); + AbilityType7 = binaryReader.ReadByte(); + AbilityValue7 = binaryReader.ReadUInt16(); + AbilityType8 = binaryReader.ReadByte(); + AbilityValue8 = binaryReader.ReadUInt16(); + AbilityType9 = binaryReader.ReadByte(); + AbilityValue9 = binaryReader.ReadUInt16(); + AbilityType10 = binaryReader.ReadByte(); + AbilityValue10 = binaryReader.ReadUInt16(); + } + + HealHP = binaryReader.ReadUInt16(); + HealSP = binaryReader.ReadUInt16(); + HealMP = binaryReader.ReadUInt16(); + TimeHealHP = binaryReader.ReadUInt16(); + TimeHealSP = binaryReader.ReadUInt16(); + TimeHealMP = binaryReader.ReadUInt16(); + DefenseType = binaryReader.ReadByte(); + DefenseValue = binaryReader.ReadByte(); + LimitHP = binaryReader.ReadByte(); + FixRange = binaryReader.ReadByte(); + ChangeType = binaryReader.ReadUInt16(); + ChangeLevel = binaryReader.ReadUInt16(); + } + + public void Write(SBinaryWriter binaryWriter) + { + var episode = binaryWriter.SerializationOptions.Episode; + + binaryWriter.Write(Name); + binaryWriter.Write(Description); + binaryWriter.Write(SkillLevel); + binaryWriter.Write(Icon); + binaryWriter.Write(Animation); if (episode >= Episode.EP6) { - buffer.Add(Effect); + binaryWriter.Write(Effect); } - buffer.Add(ToggleType); - buffer.AddRange(Sound.GetBytes()); - buffer.AddRange(RequiredLevel.GetBytes()); - buffer.Add(Country); - buffer.Add(AttackFighter); - buffer.Add(DefenseFighter); - buffer.Add(PatrolRogue); - buffer.Add(ShootRogue); - buffer.Add(AttackMage); - buffer.Add(DefenseMage); - buffer.Add(Grow); - buffer.Add(Point); - buffer.Add(TypeShow); - buffer.Add(TypeAttack); - buffer.Add(TypeEffect); - buffer.AddRange(TypeDetail.GetBytes()); - buffer.Add(NeedWeapon1); - buffer.Add(NeedWeapon2); - buffer.Add(NeedWeapon3); - buffer.Add(NeedWeapon4); - buffer.Add(NeedWeapon5); - buffer.Add(NeedWeapon6); - buffer.Add(NeedWeapon7); - buffer.Add(NeedWeapon8); - buffer.Add(NeedWeapon9); - buffer.Add(NeedWeapon10); - buffer.Add(NeedWeapon11); - buffer.Add(NeedWeapon12); - buffer.Add(NeedWeapon13); - buffer.Add(NeedWeapon14); - buffer.Add(NeedWeapon15); - buffer.Add(Shield); - buffer.AddRange(SP.GetBytes()); - buffer.AddRange(MP.GetBytes()); - buffer.Add(ReadyTime); - buffer.AddRange(ResetTime.GetBytes()); - buffer.Add(AttackRange); - buffer.Add(StateType); - buffer.Add(AttribType); - buffer.AddRange(Disable.GetBytes()); - buffer.AddRange(PrevSkill.GetBytes()); - buffer.Add(SuccessType); - buffer.Add(SuccessValue); - buffer.Add(TargetType); - buffer.Add(ApplyRange); - buffer.Add(MultiAttack); - buffer.AddRange(KeepTime.GetBytes()); - buffer.Add(Weapon1); - buffer.Add(Weapon2); - buffer.Add(WeaponValue); - buffer.Add(Bag); - buffer.AddRange(Arrow.GetBytes()); - buffer.Add(DamageType); - buffer.AddRange(DamageHP.GetBytes()); - buffer.AddRange(DamageSP.GetBytes()); - buffer.AddRange(DamageMP.GetBytes()); - buffer.Add(TimeDamageType); - buffer.AddRange(TimeDamageHP.GetBytes()); - buffer.AddRange(TimeDamageSP.GetBytes()); - buffer.AddRange(TimeDamageMP.GetBytes()); - buffer.AddRange(AddDamageHP.GetBytes()); - buffer.AddRange(AddDamageSP.GetBytes()); - buffer.AddRange(AddDamageMP.GetBytes()); - buffer.Add(AbilityType1); - buffer.AddRange(AbilityValue1.GetBytes()); - buffer.Add(AbilityType2); - buffer.AddRange(AbilityValue2.GetBytes()); - buffer.Add(AbilityType3); - buffer.AddRange(AbilityValue3.GetBytes()); + binaryWriter.Write(ToggleType); + binaryWriter.Write(Sound); + binaryWriter.Write(RequiredLevel); + binaryWriter.Write(Country); + binaryWriter.Write(AttackFighter); + binaryWriter.Write(DefenseFighter); + binaryWriter.Write(PatrolRogue); + binaryWriter.Write(ShootRogue); + binaryWriter.Write(AttackMage); + binaryWriter.Write(DefenseMage); + binaryWriter.Write(Grow); + binaryWriter.Write(Point); + binaryWriter.Write(TypeShow); + binaryWriter.Write(TypeAttack); + binaryWriter.Write(TypeEffect); + binaryWriter.Write(TypeDetail); + binaryWriter.Write(NeedWeapon1); + binaryWriter.Write(NeedWeapon2); + binaryWriter.Write(NeedWeapon3); + binaryWriter.Write(NeedWeapon4); + binaryWriter.Write(NeedWeapon5); + binaryWriter.Write(NeedWeapon6); + binaryWriter.Write(NeedWeapon7); + binaryWriter.Write(NeedWeapon8); + binaryWriter.Write(NeedWeapon9); + binaryWriter.Write(NeedWeapon10); + binaryWriter.Write(NeedWeapon11); + binaryWriter.Write(NeedWeapon12); + binaryWriter.Write(NeedWeapon13); + binaryWriter.Write(NeedWeapon14); + binaryWriter.Write(NeedWeapon15); + binaryWriter.Write(Shield); + binaryWriter.Write(SP); + binaryWriter.Write(MP); + binaryWriter.Write(ReadyTime); + binaryWriter.Write(ResetTime); + binaryWriter.Write(AttackRange); + binaryWriter.Write(StateType); + binaryWriter.Write(AttribType); + binaryWriter.Write(Disable); + binaryWriter.Write(PrevSkill); + binaryWriter.Write(SuccessType); + binaryWriter.Write(SuccessValue); + binaryWriter.Write(TargetType); + binaryWriter.Write(ApplyRange); + binaryWriter.Write(MultiAttack); + binaryWriter.Write(KeepTime); + binaryWriter.Write(Weapon1); + binaryWriter.Write(Weapon2); + binaryWriter.Write(WeaponValue); + binaryWriter.Write(Bag); + binaryWriter.Write(Arrow); + binaryWriter.Write(DamageType); + binaryWriter.Write(DamageHP); + binaryWriter.Write(DamageSP); + binaryWriter.Write(DamageMP); + binaryWriter.Write(TimeDamageType); + binaryWriter.Write(TimeDamageHP); + binaryWriter.Write(TimeDamageSP); + binaryWriter.Write(TimeDamageMP); + binaryWriter.Write(AddDamageHP); + binaryWriter.Write(AddDamageSP); + binaryWriter.Write(AddDamageMP); + binaryWriter.Write(AbilityType1); + binaryWriter.Write(AbilityValue1); + binaryWriter.Write(AbilityType2); + binaryWriter.Write(AbilityValue2); + binaryWriter.Write(AbilityType3); + binaryWriter.Write(AbilityValue3); if (episode >= Episode.EP6) { - buffer.Add(AbilityType4); - buffer.AddRange(AbilityValue4.GetBytes()); - buffer.Add(AbilityType5); - buffer.AddRange(AbilityValue5.GetBytes()); - buffer.Add(AbilityType6); - buffer.AddRange(AbilityValue6.GetBytes()); - buffer.Add(AbilityType7); - buffer.AddRange(AbilityValue7.GetBytes()); - buffer.Add(AbilityType8); - buffer.AddRange(AbilityValue8.GetBytes()); - buffer.Add(AbilityType9); - buffer.AddRange(AbilityValue9.GetBytes()); - buffer.Add(AbilityType10); - buffer.AddRange(AbilityValue10.GetBytes()); + binaryWriter.Write(AbilityType4); + binaryWriter.Write(AbilityValue4); + binaryWriter.Write(AbilityType5); + binaryWriter.Write(AbilityValue5); + binaryWriter.Write(AbilityType6); + binaryWriter.Write(AbilityValue6); + binaryWriter.Write(AbilityType7); + binaryWriter.Write(AbilityValue7); + binaryWriter.Write(AbilityType8); + binaryWriter.Write(AbilityValue8); + binaryWriter.Write(AbilityType9); + binaryWriter.Write(AbilityValue9); + binaryWriter.Write(AbilityType10); + binaryWriter.Write(AbilityValue10); } - buffer.AddRange(HealHP.GetBytes()); - buffer.AddRange(HealSP.GetBytes()); - buffer.AddRange(HealMP.GetBytes()); - buffer.AddRange(TimeHealHP.GetBytes()); - buffer.AddRange(TimeHealSP.GetBytes()); - buffer.AddRange(TimeHealMP.GetBytes()); - buffer.Add(DefenseType); - buffer.Add(DefenseValue); - buffer.Add(LimitHP); - buffer.Add(FixRange); - buffer.AddRange(ChangeType.GetBytes()); - buffer.AddRange(ChangeLevel.GetBytes()); - return buffer; + binaryWriter.Write(HealHP); + binaryWriter.Write(HealSP); + binaryWriter.Write(HealMP); + binaryWriter.Write(TimeHealHP); + binaryWriter.Write(TimeHealSP); + binaryWriter.Write(TimeHealMP); + binaryWriter.Write(DefenseType); + binaryWriter.Write(DefenseValue); + binaryWriter.Write(LimitHP); + binaryWriter.Write(FixRange); + binaryWriter.Write(ChangeType); + binaryWriter.Write(ChangeLevel); } } diff --git a/src/Parsec/Shaiya/Skill/SkillGroup.cs b/src/Parsec/Shaiya/Skill/SkillGroup.cs new file mode 100644 index 00000000..53322a42 --- /dev/null +++ b/src/Parsec/Shaiya/Skill/SkillGroup.cs @@ -0,0 +1,33 @@ +using Parsec.Common; +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Skill; + +public class SkillGroup : ISerializable +{ + public List SkillDefinitions { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + var recordCountPerGroup = GetRecordCountPerGroup(binaryReader.SerializationOptions.Episode); + SkillDefinitions = binaryReader.ReadList(recordCountPerGroup).ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + var recordCount = GetRecordCountPerGroup(binaryWriter.SerializationOptions.Episode); + binaryWriter.Write(SkillDefinitions.Take(recordCount).ToSerializable(), lengthPrefixed: false); + } + + private int GetRecordCountPerGroup(Episode episode) + { + return episode switch + { + Episode.EP5 => 9, + >= Episode.EP6 => 15, + _ => 3 + }; + } +} diff --git a/src/Parsec/Shaiya/SMOD/SMOD.cs b/src/Parsec/Shaiya/Smod/Smod.cs similarity index 55% rename from src/Parsec/Shaiya/SMOD/SMOD.cs rename to src/Parsec/Shaiya/Smod/Smod.cs index 631a8438..28a9f6c7 100644 --- a/src/Parsec/Shaiya/SMOD/SMOD.cs +++ b/src/Parsec/Shaiya/Smod/Smod.cs @@ -1,20 +1,21 @@ using Newtonsoft.Json; -using Parsec.Attributes; -using Parsec.Common; using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.SMOD; +namespace Parsec.Shaiya.Smod; /// /// Class that represents a .SMOD object which is used for buildings. /// Buildings can be made up of multiple parts, each with its individual texture. /// Collision objects are also included in this format in a separate list of texture-less objects. /// -[DefaultVersion(Episode.EP5)] -public sealed class SMOD : FileBase +public sealed class Smod : FileBase { + [JsonIgnore] + public override string Extension => "SMOD"; + /// /// The center of the SMOD object as a whole (center of all objects) /// @@ -35,7 +36,7 @@ public sealed class SMOD : FileBase /// /// List of textured objects /// - public List TexturedObjects { get; set; } = new(); + public List TexturedObjects { get; set; } = new(); /// /// Box that defines the area where collisions should take place. Collision objects that are outside this box are ignored. @@ -45,39 +46,26 @@ public sealed class SMOD : FileBase /// /// List of texture-less objects used for collisions /// - public List CollisionObjects { get; set; } = new(); + public List CollisionObjects { get; set; } = new(); /// - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - Center = new Vector3(_binaryReader); - DistanceToCenter = _binaryReader.Read(); - ViewBox = new BoundingBox(_binaryReader); - - int texturedObjectCount = _binaryReader.Read(); - for (int i = 0; i < texturedObjectCount; i++) - TexturedObjects.Add(new TexturedObject(_binaryReader)); - - CollisionBox = new BoundingBox(_binaryReader); - - int collisionObjectCount = _binaryReader.Read(); - for (int i = 0; i < collisionObjectCount; i++) - CollisionObjects.Add(new CollisionObject(_binaryReader)); + Center = binaryReader.Read(); + DistanceToCenter = binaryReader.ReadSingle(); + ViewBox = binaryReader.Read(); + TexturedObjects = binaryReader.ReadList().ToList(); + CollisionBox = binaryReader.Read(); + CollisionObjects = binaryReader.ReadList().ToList(); } - /// - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Center.GetBytes()); - buffer.AddRange(DistanceToCenter.GetBytes()); - buffer.AddRange(ViewBox.GetBytes()); - buffer.AddRange(TexturedObjects.GetBytes()); - buffer.AddRange(CollisionBox.GetBytes()); - buffer.AddRange(CollisionObjects.GetBytes()); - return buffer; + binaryWriter.Write(Center); + binaryWriter.Write(DistanceToCenter); + binaryWriter.Write(ViewBox); + binaryWriter.Write(TexturedObjects.ToSerializable()); + binaryWriter.Write(CollisionBox); + binaryWriter.Write(CollisionObjects.ToSerializable()); } - - [JsonIgnore] - public override string Extension => "SMOD"; } diff --git a/src/Parsec/Shaiya/Smod/SmodCollisionMesh.cs b/src/Parsec/Shaiya/Smod/SmodCollisionMesh.cs new file mode 100644 index 00000000..fddf5eee --- /dev/null +++ b/src/Parsec/Shaiya/Smod/SmodCollisionMesh.cs @@ -0,0 +1,34 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Smod; + +/// +/// A 3d object used in SMOD files to represent an object where players should collide. +/// +public sealed class SmodCollisionMesh : ISerializable +{ + /// + /// Vertices of the 3d object. + /// + public List Vertices { get; set; } = new(); + + /// + /// Triangular faces (polygons) of the 3d object. + /// + public List Faces { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + Vertices = binaryReader.ReadList().ToList(); + Faces = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Vertices.ToSerializable()); + binaryWriter.Write(Faces.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Smod/SmodCollisionMeshVertex.cs b/src/Parsec/Shaiya/Smod/SmodCollisionMeshVertex.cs new file mode 100644 index 00000000..10789f92 --- /dev/null +++ b/src/Parsec/Shaiya/Smod/SmodCollisionMeshVertex.cs @@ -0,0 +1,26 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Smod; + +/// +/// Represents a vertex used in SMOD collision objects +/// +public sealed class SmodCollisionMeshVertex : ISerializable +{ + /// + /// Coordinates of the vertex in the 3D space. + /// + public Vector3 Coordinates { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Coordinates = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Coordinates); + } +} diff --git a/src/Parsec/Shaiya/Smod/SmodMesh.cs b/src/Parsec/Shaiya/Smod/SmodMesh.cs new file mode 100644 index 00000000..bba0c1ea --- /dev/null +++ b/src/Parsec/Shaiya/Smod/SmodMesh.cs @@ -0,0 +1,42 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Smod; + +/// +/// A 3d mesh with a texture +/// +public sealed class SmodMesh : ISerializable +{ + /// + /// Name of the .tga texture file. Although they have the .tga extension, the client actually has .dds files, so they're very likely + /// replacing the .tga extension with .dds when searching for the texture. + /// + public string TextureName { get; set; } = string.Empty; + + /// + /// Mesh vertices + /// + public List Vertices { get; set; } = new(); + + /// + /// Mesh triangular faces + /// + public List Faces { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + TextureName = binaryReader.ReadString(); + Vertices = binaryReader.ReadList().ToList(); + Faces = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(TextureName); + binaryWriter.Write(Vertices.ToSerializable()); + binaryWriter.Write(Faces.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Smod/SmodVertex.cs b/src/Parsec/Shaiya/Smod/SmodVertex.cs new file mode 100644 index 00000000..a903d12d --- /dev/null +++ b/src/Parsec/Shaiya/Smod/SmodVertex.cs @@ -0,0 +1,47 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Smod; + +/// +/// Represents a vertex used in an SMOD mesh +/// +public sealed class SmodVertex : ISerializable +{ + /// + /// Vertex coordinates in the 3D space + /// + public Vector3 Coordinates { get; set; } + + /// + /// Vertex normal used for lighting + /// + public Vector3 Normal { get; set; } + + /// + /// SMODs don't have bones, that's why this value is always -1. + /// + public int BoneId { get; set; } = -1; + + /// + /// Texture mapping + /// + public Vector2 UV { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Coordinates = binaryReader.Read(); + Normal = binaryReader.Read(); + BoneId = binaryReader.ReadInt32(); + UV = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Coordinates); + binaryWriter.Write(Normal); + binaryWriter.Write(BoneId); + binaryWriter.Write(UV); + } +} diff --git a/src/Parsec/Shaiya/Svmap/Ladder.cs b/src/Parsec/Shaiya/Svmap/Ladder.cs deleted file mode 100644 index cd8556a4..00000000 --- a/src/Parsec/Shaiya/Svmap/Ladder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Svmap; - -public sealed class Ladder : IBinary -{ - [JsonConstructor] - public Ladder() - { - } - - public Ladder(SBinaryReader binaryReader) - { - Position = new Vector3(binaryReader); - } - - public Vector3 Position { get; set; } - - public IEnumerable GetBytes(params object[] options) => Position.GetBytes(); -} diff --git a/src/Parsec/Shaiya/Svmap/Monster.cs b/src/Parsec/Shaiya/Svmap/Monster.cs deleted file mode 100644 index 8eb4a0dd..00000000 --- a/src/Parsec/Shaiya/Svmap/Monster.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Svmap; - -public sealed class Monster : IBinary -{ - [JsonConstructor] - public Monster() - { - } - - public Monster(SBinaryReader binaryReader) - { - MobId = binaryReader.Read(); - Count = binaryReader.Read(); - } - - public int MobId { get; set; } - public int Count { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(MobId.GetBytes()); - buffer.AddRange(Count.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Svmap/MonsterArea.cs b/src/Parsec/Shaiya/Svmap/MonsterArea.cs deleted file mode 100644 index 48563101..00000000 --- a/src/Parsec/Shaiya/Svmap/MonsterArea.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Svmap; - -/// -/// Represents an area with monsters inside -/// -public sealed class MonsterArea : IBinary -{ - [JsonConstructor] - public MonsterArea() - { - } - - public MonsterArea(SBinaryReader binaryReader) - { - Area = new BoundingBox(binaryReader); - - var monsterCount = binaryReader.Read(); - - // Read monsters - for (int i = 0; i < monsterCount; i++) - { - var monster = new Monster(binaryReader); - Monsters.Add(monster); - } - } - - public BoundingBox Area { get; set; } - public List Monsters { get; set; } = new(); - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Area.GetBytes()); - buffer.AddRange(Monsters.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Svmap/NamedArea.cs b/src/Parsec/Shaiya/Svmap/NamedArea.cs deleted file mode 100644 index 4b316689..00000000 --- a/src/Parsec/Shaiya/Svmap/NamedArea.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Svmap; - -public sealed class NamedArea : IBinary -{ - [JsonConstructor] - public NamedArea() - { - } - - public NamedArea(SBinaryReader binaryReader) - { - Area = new BoundingBox(binaryReader); - NameIdentifier1 = binaryReader.Read(); - NameIdentifier2 = binaryReader.Read(); - } - - public BoundingBox Area { get; set; } - public int NameIdentifier1 { get; set; } - public int NameIdentifier2 { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Area.GetBytes()); - buffer.AddRange(NameIdentifier1.GetBytes()); - buffer.AddRange(NameIdentifier2.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Svmap/Npc.cs b/src/Parsec/Shaiya/Svmap/Npc.cs deleted file mode 100644 index 9fd5f1dd..00000000 --- a/src/Parsec/Shaiya/Svmap/Npc.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Svmap; - -public sealed class Npc : IBinary -{ - [JsonConstructor] - public Npc() - { - } - - public Npc(SBinaryReader binaryReader) - { - Type = binaryReader.Read(); - NpcId = binaryReader.Read(); - - var locationCount = binaryReader.Read(); - - for (int i = 0; i < locationCount; i++) - { - var npcLocation = new NpcLocation(binaryReader); - Locations.Add(npcLocation); - } - } - - public int Type { get; set; } - public int NpcId { get; set; } - public List Locations { get; set; } = new(); - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Type.GetBytes()); - buffer.AddRange(NpcId.GetBytes()); - buffer.AddRange(Locations.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Svmap/NpcLocation.cs b/src/Parsec/Shaiya/Svmap/NpcLocation.cs deleted file mode 100644 index 6e982433..00000000 --- a/src/Parsec/Shaiya/Svmap/NpcLocation.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Svmap; - -public sealed class NpcLocation : IBinary -{ - [JsonConstructor] - public NpcLocation() - { - } - - public NpcLocation(SBinaryReader binaryReader) - { - Position = new Vector3(binaryReader); - Orientation = binaryReader.Read(); - } - - public Vector3 Position { get; set; } - public float Orientation { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Position.GetBytes()); - buffer.AddRange(Orientation.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Svmap/Portal.cs b/src/Parsec/Shaiya/Svmap/Portal.cs deleted file mode 100644 index bbf68a1a..00000000 --- a/src/Parsec/Shaiya/Svmap/Portal.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Svmap; - -public sealed class Portal : IBinary -{ - [JsonConstructor] - public Portal() - { - } - - public Portal(SBinaryReader binaryReader) - { - Position = new Vector3(binaryReader); - FactionOrPortalId = binaryReader.Read(); - MinLevel = binaryReader.Read(); - MaxLevel = binaryReader.Read(); - DestinationMapId = binaryReader.Read(); - DestinationPosition = new Vector3(binaryReader); - } - - public Vector3 Position { get; set; } - public int FactionOrPortalId { get; set; } - public short MinLevel { get; set; } - public short MaxLevel { get; set; } - public int DestinationMapId { get; set; } - public Vector3 DestinationPosition { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Position.GetBytes()); - buffer.AddRange(FactionOrPortalId.GetBytes()); - buffer.AddRange(MinLevel.GetBytes()); - buffer.AddRange(MaxLevel.GetBytes()); - buffer.AddRange(DestinationMapId.GetBytes()); - buffer.AddRange(DestinationPosition.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Svmap/Spawn.cs b/src/Parsec/Shaiya/Svmap/Spawn.cs deleted file mode 100644 index 5f08d679..00000000 --- a/src/Parsec/Shaiya/Svmap/Spawn.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.Svmap; - -public sealed class Spawn : IBinary -{ - [JsonConstructor] - public Spawn() - { - } - - public Spawn(SBinaryReader binaryReader) - { - Unknown1 = binaryReader.Read(); - Faction = (FactionInt)binaryReader.Read(); - Unknown2 = binaryReader.Read(); - Area = new BoundingBox(binaryReader); - } - - public int Unknown1 { get; set; } - public FactionInt Faction { get; set; } - public int Unknown2 { get; set; } - public BoundingBox Area { get; set; } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Unknown1.GetBytes()); - buffer.AddRange(((int)Faction).GetBytes()); - buffer.AddRange(Unknown2.GetBytes()); - buffer.AddRange(Area.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Svmap/Svmap.cs b/src/Parsec/Shaiya/Svmap/Svmap.cs index 0321ede9..cdd89d92 100644 --- a/src/Parsec/Shaiya/Svmap/Svmap.cs +++ b/src/Parsec/Shaiya/Svmap/Svmap.cs @@ -1,74 +1,58 @@ using Newtonsoft.Json; -using Parsec.Common; using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Svmap; public sealed class Svmap : FileBase { - [JsonConstructor] - public Svmap() - { - } - public int MapSize { get; set; } - public List MapHeight { get; private set; } = new(); + + public byte[] MapHeight { get; set; } = Array.Empty(); + public int CellSize { get; set; } - public List Ladders { get; } = new(); - public List MonsterAreas { get; } = new(); - public List Npcs { get; } = new(); - public List Portals { get; } = new(); - public List Spawns { get; } = new(); - public List NamedAreas { get; } = new(); - [JsonIgnore] - public override string Extension => "svmap"; + public List Ladders { get; set; } = new(); - public override void Read() - { - MapSize = _binaryReader.Read(); - int mapHeightCount = MapSize * MapSize / 8; - MapHeight = _binaryReader.ReadBytes(mapHeightCount).ToList(); - CellSize = _binaryReader.Read(); + public List MonsterAreas { get; set; } = new(); - int ladderCount = _binaryReader.Read(); - for (int i = 0; i < ladderCount; i++) - Ladders.Add(new Ladder(_binaryReader)); + public List Npcs { get; set; } = new(); - int monsterAreaCount = _binaryReader.Read(); - for (int i = 0; i < monsterAreaCount; i++) - MonsterAreas.Add(new MonsterArea(_binaryReader)); + public List Portals { get; set; } = new(); - int npcCount = _binaryReader.Read(); - for (int i = 0; i < npcCount; i++) - Npcs.Add(new Npc(_binaryReader)); + public List Spawns { get; set; } = new(); - int portalCount = _binaryReader.Read(); - for (int i = 0; i < portalCount; i++) - Portals.Add(new Portal(_binaryReader)); + public List NamedAreas { get; set; } = new(); - int spawnCount = _binaryReader.Read(); - for (int i = 0; i < spawnCount; i++) - Spawns.Add(new Spawn(_binaryReader)); + [JsonIgnore] + public override string Extension => "svmap"; - int namedAreaCount = _binaryReader.Read(); - for (int i = 0; i < namedAreaCount; i++) - NamedAreas.Add(new NamedArea(_binaryReader)); + protected override void Read(SBinaryReader binaryReader) + { + MapSize = binaryReader.ReadInt32(); + var mapHeightCount = MapSize * MapSize / 8; + MapHeight = binaryReader.ReadBytes(mapHeightCount); + CellSize = binaryReader.ReadInt32(); + + Ladders = binaryReader.ReadList().ToList(); + MonsterAreas = binaryReader.ReadList().ToList(); + Npcs = binaryReader.ReadList().ToList(); + Portals = binaryReader.ReadList().ToList(); + Spawns = binaryReader.ReadList().ToList(); + NamedAreas = binaryReader.ReadList().ToList(); } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(MapSize.GetBytes()); - buffer.AddRange(MapHeight); - buffer.AddRange(CellSize.GetBytes()); - buffer.AddRange(Ladders.GetBytes()); - buffer.AddRange(MonsterAreas.GetBytes()); - buffer.AddRange(Npcs.GetBytes()); - buffer.AddRange(Portals.GetBytes()); - buffer.AddRange(Spawns.GetBytes()); - buffer.AddRange(NamedAreas.GetBytes()); - return buffer; + binaryWriter.Write(MapSize); + binaryWriter.Write(MapHeight.ToArray()); + binaryWriter.Write(CellSize); + binaryWriter.Write(Ladders.ToSerializable()); + binaryWriter.Write(MonsterAreas.ToSerializable()); + binaryWriter.Write(Npcs.ToSerializable()); + binaryWriter.Write(Portals.ToSerializable()); + binaryWriter.Write(Spawns.ToSerializable()); + binaryWriter.Write(NamedAreas.ToSerializable()); } } diff --git a/src/Parsec/Shaiya/Svmap/SvmapFaction.cs b/src/Parsec/Shaiya/Svmap/SvmapFaction.cs new file mode 100644 index 00000000..fa3a54d3 --- /dev/null +++ b/src/Parsec/Shaiya/Svmap/SvmapFaction.cs @@ -0,0 +1,8 @@ +namespace Parsec.Shaiya.Svmap; + +public enum SvmapFaction +{ + Light, + Fury, + Any +} diff --git a/src/Parsec/Shaiya/Svmap/SvmapLadder.cs b/src/Parsec/Shaiya/Svmap/SvmapLadder.cs new file mode 100644 index 00000000..c3130bd7 --- /dev/null +++ b/src/Parsec/Shaiya/Svmap/SvmapLadder.cs @@ -0,0 +1,20 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Svmap; + +public sealed class SvmapLadder : ISerializable +{ + public Vector3 Position { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Position = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Position); + } +} diff --git a/src/Parsec/Shaiya/Svmap/SvmapMonsterSpawn.cs b/src/Parsec/Shaiya/Svmap/SvmapMonsterSpawn.cs new file mode 100644 index 00000000..633a5087 --- /dev/null +++ b/src/Parsec/Shaiya/Svmap/SvmapMonsterSpawn.cs @@ -0,0 +1,23 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Svmap; + +public sealed class SvmapMonsterSpawn : ISerializable +{ + public uint MobId { get; set; } + + public uint Count { get; set; } + + public void Read(SBinaryReader binaryReader) + { + MobId = binaryReader.ReadUInt32(); + Count = binaryReader.ReadUInt32(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(MobId); + binaryWriter.Write(Count); + } +} diff --git a/src/Parsec/Shaiya/Svmap/SvmapMonsterSpawnArea.cs b/src/Parsec/Shaiya/Svmap/SvmapMonsterSpawnArea.cs new file mode 100644 index 00000000..12b8ccfd --- /dev/null +++ b/src/Parsec/Shaiya/Svmap/SvmapMonsterSpawnArea.cs @@ -0,0 +1,28 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Svmap; + +/// +/// Represents a 2-D area where monsters are spawned +/// +public sealed class SvmapMonsterSpawnArea : ISerializable +{ + public BoundingBox Area { get; set; } + + public List Monsters { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + Area = binaryReader.Read(); + Monsters = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Area); + binaryWriter.Write(Monsters.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Svmap/SvmapNamedArea.cs b/src/Parsec/Shaiya/Svmap/SvmapNamedArea.cs new file mode 100644 index 00000000..d36da57a --- /dev/null +++ b/src/Parsec/Shaiya/Svmap/SvmapNamedArea.cs @@ -0,0 +1,28 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Svmap; + +public sealed class SvmapNamedArea : ISerializable +{ + public BoundingBox Area { get; set; } + + public int NameIdentifier1 { get; set; } + + public int NameIdentifier2 { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Area = binaryReader.Read(); + NameIdentifier1 = binaryReader.ReadInt32(); + NameIdentifier2 = binaryReader.ReadInt32(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Area); + binaryWriter.Write(NameIdentifier1); + binaryWriter.Write(NameIdentifier2); + } +} diff --git a/src/Parsec/Shaiya/Svmap/SvmapNpc.cs b/src/Parsec/Shaiya/Svmap/SvmapNpc.cs new file mode 100644 index 00000000..bbb5bc87 --- /dev/null +++ b/src/Parsec/Shaiya/Svmap/SvmapNpc.cs @@ -0,0 +1,28 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Svmap; + +public sealed class SvmapNpc : ISerializable +{ + public int NpcType { get; set; } + + public int NpcId { get; set; } + + public List Positions { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + NpcType = binaryReader.ReadInt32(); + NpcId = binaryReader.ReadInt32(); + Positions = binaryReader.ReadList().ToList(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(NpcType); + binaryWriter.Write(NpcId); + binaryWriter.Write(Positions.ToSerializable()); + } +} diff --git a/src/Parsec/Shaiya/Svmap/SvmapNpcPosition.cs b/src/Parsec/Shaiya/Svmap/SvmapNpcPosition.cs new file mode 100644 index 00000000..650cd159 --- /dev/null +++ b/src/Parsec/Shaiya/Svmap/SvmapNpcPosition.cs @@ -0,0 +1,24 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Svmap; + +public sealed class SvmapNpcPosition : ISerializable +{ + public Vector3 Position { get; set; } + + public float Yaw { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Position = binaryReader.Read(); + Yaw = binaryReader.ReadSingle(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Position); + binaryWriter.Write(Yaw); + } +} diff --git a/src/Parsec/Shaiya/Svmap/SvmapPortal.cs b/src/Parsec/Shaiya/Svmap/SvmapPortal.cs new file mode 100644 index 00000000..9920969c --- /dev/null +++ b/src/Parsec/Shaiya/Svmap/SvmapPortal.cs @@ -0,0 +1,40 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Svmap; + +public sealed class SvmapPortal : ISerializable +{ + public Vector3 PortalPosition { get; set; } + + public int FactionOrPortalId { get; set; } + + public ushort MinLevel { get; set; } + + public ushort MaxLevel { get; set; } + + public uint TargetMapId { get; set; } + + public Vector3 TargetMapPosition { get; set; } + + public void Read(SBinaryReader binaryReader) + { + PortalPosition = binaryReader.Read(); + FactionOrPortalId = binaryReader.ReadInt32(); + MinLevel = binaryReader.ReadUInt16(); + MaxLevel = binaryReader.ReadUInt16(); + TargetMapId = binaryReader.ReadUInt32(); + TargetMapPosition = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(PortalPosition); + binaryWriter.Write(FactionOrPortalId); + binaryWriter.Write(MinLevel); + binaryWriter.Write(MaxLevel); + binaryWriter.Write(TargetMapId); + binaryWriter.Write(TargetMapPosition); + } +} diff --git a/src/Parsec/Shaiya/Svmap/SvmapSpawnArea.cs b/src/Parsec/Shaiya/Svmap/SvmapSpawnArea.cs new file mode 100644 index 00000000..1911ff1f --- /dev/null +++ b/src/Parsec/Shaiya/Svmap/SvmapSpawnArea.cs @@ -0,0 +1,32 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Svmap; + +public sealed class SvmapSpawnArea : ISerializable +{ + public int Unknown1 { get; set; } + + public SvmapFaction Faction { get; set; } + + public int Unknown2 { get; set; } + + public BoundingBox Area { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Unknown1 = binaryReader.ReadInt32(); + Faction = (SvmapFaction)binaryReader.ReadInt32(); + Unknown2 = binaryReader.ReadInt32(); + Area = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Unknown1); + binaryWriter.Write((int)Faction); + binaryWriter.Write(Unknown2); + binaryWriter.Write(Area); + } +} diff --git a/src/Parsec/Shaiya/TransformModel/DBTransformModelDataRecord.cs b/src/Parsec/Shaiya/TransformModel/DBTransformModelDataRecord.cs index ceda1ab2..7f813442 100644 --- a/src/Parsec/Shaiya/TransformModel/DBTransformModelDataRecord.cs +++ b/src/Parsec/Shaiya/TransformModel/DBTransformModelDataRecord.cs @@ -1,28 +1,43 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.TransformModel; public sealed class DBTransformModelDataRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Id { get; set; } - [ShaiyaProperty] public long Top { get; set; } - [ShaiyaProperty] public long Hand { get; set; } - [ShaiyaProperty] public long Bottom { get; set; } - [ShaiyaProperty] public long Shoe { get; set; } - [ShaiyaProperty] public long Empty { get; set; } - [ShaiyaProperty] public long Helmet { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Id = binaryReader.ReadInt64(); + Top = binaryReader.ReadInt64(); + Hand = binaryReader.ReadInt64(); + Bottom = binaryReader.ReadInt64(); + Shoe = binaryReader.ReadInt64(); + Empty = binaryReader.ReadInt64(); + Helmet = binaryReader.ReadInt64(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Id); + binaryWriter.Write(Top); + binaryWriter.Write(Hand); + binaryWriter.Write(Bottom); + binaryWriter.Write(Shoe); + binaryWriter.Write(Empty); + binaryWriter.Write(Helmet); + } } diff --git a/src/Parsec/Shaiya/TransformModel/DBTransformWeaponModelDataRecord.cs b/src/Parsec/Shaiya/TransformModel/DBTransformWeaponModelDataRecord.cs index 4297cada..dc31be61 100644 --- a/src/Parsec/Shaiya/TransformModel/DBTransformWeaponModelDataRecord.cs +++ b/src/Parsec/Shaiya/TransformModel/DBTransformWeaponModelDataRecord.cs @@ -1,16 +1,27 @@ -using Parsec.Attributes; +using Parsec.Serialization; using Parsec.Shaiya.SData; namespace Parsec.Shaiya.TransformModel; public sealed class DBTransformWeaponModelDataRecord : IBinarySDataRecord { - [ShaiyaProperty] public long Type { get; set; } - [ShaiyaProperty] public long Weapon { get; set; } - [ShaiyaProperty] public long Weapon1 { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Type = binaryReader.ReadInt64(); + Weapon = binaryReader.ReadInt64(); + Weapon1 = binaryReader.ReadInt64(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Type); + binaryWriter.Write(Weapon); + binaryWriter.Write(Weapon1); + } } diff --git a/src/Parsec/Shaiya/VAni/VAni.cs b/src/Parsec/Shaiya/VAni/VAni.cs deleted file mode 100644 index f51a757d..00000000 --- a/src/Parsec/Shaiya/VAni/VAni.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.VAni; - -public sealed class VAni : FileBase -{ - /// - /// Coordinates of the center of the 3d object - /// - public Vector3 Center { get; set; } - - /// - /// The distance between the vertices of the and the of the VAni object. Used for game calculations. - /// - public float DistanceToCenter { get; set; } - - /// - /// Rectangular bounding box - /// - public BoundingBox BoundingBox { get; set; } - - /// - /// Amount of frames - /// - public int FrameCount { get; set; } - - /// - /// Castor's notes: "__int32 features; // bitmask of rendering options?" - /// - public int Unknown1 { get; set; } - - public List Objects { get; } = new(); - - public BoundingBox BoundingBox2 { get; set; } - - /// - /// Always 00? - /// - public int Unknown2 { get; set; } - - public override string Extension => "VANI"; - - public override void Read() - { - Center = new Vector3(_binaryReader); - DistanceToCenter = _binaryReader.Read(); - BoundingBox = new BoundingBox(_binaryReader); - int objectCount = _binaryReader.Read(); - FrameCount = _binaryReader.Read(); - Unknown1 = _binaryReader.Read(); - - for (int i = 0; i < objectCount; i++) - Objects.Add(new VAniObject(_binaryReader, FrameCount)); - - BoundingBox2 = new BoundingBox(_binaryReader); - Unknown2 = _binaryReader.Read(); - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Center.GetBytes()); - buffer.AddRange(DistanceToCenter.GetBytes()); - buffer.AddRange(BoundingBox.GetBytes()); - buffer.AddRange(Objects.Count.GetBytes()); - buffer.AddRange(FrameCount.GetBytes()); - buffer.AddRange(Unknown1.GetBytes()); - - foreach (var obj in Objects) - buffer.AddRange(obj.GetBytes(FrameCount)); - - buffer.AddRange(BoundingBox2.GetBytes()); - buffer.AddRange(Unknown2.GetBytes()); - return buffer; - } -} diff --git a/src/Parsec/Shaiya/VAni/VAniObject.cs b/src/Parsec/Shaiya/VAni/VAniObject.cs deleted file mode 100644 index e54ee339..00000000 --- a/src/Parsec/Shaiya/VAni/VAniObject.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; - -namespace Parsec.Shaiya.VAni; - -/// -/// Represents a .VANI file (Vertex ANImation) -/// -public sealed class VAniObject : IBinary -{ - /// - /// Texture name of the .dds file - /// - public string TextureName { get; set; } - - /// - /// List of the 3d object's faces (polygons - triangles) - /// - public List Faces { get; } = new(); - - /// - /// List of the 3d object's vertices - /// - public List Vertices { get; } = new(); - - [JsonConstructor] - public VAniObject() - { - } - - public VAniObject(SBinaryReader binaryReader, int frameCount) - { - TextureName = binaryReader.ReadString(); - - int faceCount = binaryReader.Read(); - for (int i = 0; i < faceCount; i++) - Faces.Add(new Face(binaryReader)); - - int vertexCount = binaryReader.Read(); - for (int i = 0; i < vertexCount; i++) - { - var vertex = new VaniVertex(); - Vertices.Add(vertex); - } - - for (int frame = 0; frame < frameCount; frame++) - { - foreach (var vertex in Vertices) - { - var vertexFrame = new VaniVertexFrame(binaryReader); - vertex.Frames.Add(vertexFrame); - } - } - } - - public IEnumerable GetBytes(params object[] options) - { - int frameCount = 0; - if (options[0] is int frameCountOption) - frameCount = frameCountOption; - - var buffer = new List(); - buffer.AddRange(TextureName.GetLengthPrefixedBytes()); - buffer.AddRange(Faces.GetBytes()); - buffer.AddRange(Vertices.Count.GetBytes()); - - for (int frame = 0; frame < frameCount; frame++) - { - foreach (var vertex in Vertices) - { - buffer.AddRange(vertex.Frames[frame].GetBytes()); - } - } - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/VAni/Vani.cs b/src/Parsec/Shaiya/VAni/Vani.cs new file mode 100644 index 00000000..a5e74889 --- /dev/null +++ b/src/Parsec/Shaiya/VAni/Vani.cs @@ -0,0 +1,79 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Vani; + +public sealed class Vani : FileBase +{ + /// + /// Coordinates of the center of the 3d object + /// + public Vector3 Center { get; set; } + + /// + /// The distance between the vertices of the and the of the VAni object. Used for game calculations. + /// + public float DistanceToCenter { get; set; } + + /// + /// Rectangular bounding box + /// + public BoundingBox BoundingBox { get; set; } + + /// + /// Amount of frames + /// + public int FrameCount { get; set; } + + /// + /// ? + /// + public int Unknown1 { get; set; } + + public List Meshes { get; set; } = new(); + + public BoundingBox BoundingBox2 { get; set; } + + /// + /// Always 00? + /// + public int Unknown2 { get; set; } + + public override string Extension => "VANI"; + + protected override void Read(SBinaryReader binaryReader) + { + Center = binaryReader.Read(); + DistanceToCenter = binaryReader.ReadSingle(); + BoundingBox = binaryReader.Read(); + var meshCount = binaryReader.ReadInt32(); + FrameCount = binaryReader.ReadInt32(); + Unknown1 = binaryReader.ReadInt32(); + + // VaniMesh instances expect the FrameCount to be set as the ExtraOption on the serialization options + binaryReader.SerializationOptions.ExtraOption = FrameCount; + + Meshes = binaryReader.ReadList(meshCount).ToList(); + BoundingBox2 = binaryReader.Read(); + Unknown2 = binaryReader.ReadInt32(); + } + + protected override void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Center); + binaryWriter.Write(DistanceToCenter); + binaryWriter.Write(BoundingBox); + binaryWriter.Write(Meshes.Count); + binaryWriter.Write(FrameCount); + binaryWriter.Write(Unknown1); + + // VaniMesh instances expect the FrameCount to be set as the ExtraOption on the serialization options + binaryWriter.SerializationOptions.ExtraOption = FrameCount; + + binaryWriter.Write(Meshes.ToSerializable(), lengthPrefixed: false); + binaryWriter.Write(BoundingBox2); + binaryWriter.Write(Unknown2); + } +} diff --git a/src/Parsec/Shaiya/VAni/VaniMesh.cs b/src/Parsec/Shaiya/VAni/VaniMesh.cs new file mode 100644 index 00000000..0f9c7fc5 --- /dev/null +++ b/src/Parsec/Shaiya/VAni/VaniMesh.cs @@ -0,0 +1,79 @@ +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Vani; + +/// +/// Represents a .VANI file (Vertex ANImation) +/// +public sealed class VaniMesh : ISerializable +{ + /// + /// Texture name of the .dds file + /// + public string TextureName { get; set; } = string.Empty; + + /// + /// List of the 3d object's faces (polygons - triangles) + /// + public List Faces { get; set; } = new(); + + /// + /// List of the 3d object's vertices + /// + public List Vertices { get; set; } = new(); + + public void Read(SBinaryReader binaryReader) + { + var frameCount = 0; + + if (binaryReader.SerializationOptions.ExtraOption is int frameCountOption) + { + frameCount = frameCountOption; + } + + TextureName = binaryReader.ReadString(); + Faces = binaryReader.ReadList().ToList(); + + // Initialize empty vertices + var vertexCount = binaryReader.ReadInt32(); + for (var i = 0; i < vertexCount; i++) + { + var vertex = new VaniVertex(); + Vertices.Add(vertex); + } + + for (var frame = 0; frame < frameCount; frame++) + { + foreach (var vertex in Vertices) + { + var vertexFrame = binaryReader.Read(); + vertex.Frames.Add(vertexFrame); + } + } + } + + public void Write(SBinaryWriter binaryWriter) + { + var frameCount = 0; + + if (binaryWriter.SerializationOptions.ExtraOption is int frameCountOption) + { + frameCount = frameCountOption; + } + + binaryWriter.Write(TextureName); + binaryWriter.Write(Faces.ToSerializable()); + binaryWriter.Write(Vertices.Count); + + for (var frame = 0; frame < frameCount; frame++) + { + foreach (var vertex in Vertices) + { + binaryWriter.Write(vertex.Frames[frame]); + } + } + } +} diff --git a/src/Parsec/Shaiya/VAni/VaniVertex.cs b/src/Parsec/Shaiya/VAni/VaniVertex.cs index 53be9f9d..481fb2b9 100644 --- a/src/Parsec/Shaiya/VAni/VaniVertex.cs +++ b/src/Parsec/Shaiya/VAni/VaniVertex.cs @@ -1,16 +1,6 @@ -using Parsec.Shaiya.Core; +namespace Parsec.Shaiya.Vani; -namespace Parsec.Shaiya.VAni; - -public sealed class VaniVertex : IBinary +public sealed class VaniVertex { - public List Frames { get; } = new(); - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - foreach (var frame in Frames) - buffer.AddRange(frame.GetBytes()); - return buffer; - } + public List Frames { get; set; } = new(); } diff --git a/src/Parsec/Shaiya/VAni/VaniVertexFrame.cs b/src/Parsec/Shaiya/VAni/VaniVertexFrame.cs index 7b3d285d..80d8e134 100644 --- a/src/Parsec/Shaiya/VAni/VaniVertexFrame.cs +++ b/src/Parsec/Shaiya/VAni/VaniVertexFrame.cs @@ -1,26 +1,11 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.VAni; +namespace Parsec.Shaiya.Vani; -public sealed class VaniVertexFrame : IBinary +public sealed class VaniVertexFrame : ISerializable { - [JsonConstructor] - public VaniVertexFrame() - { - } - - public VaniVertexFrame(SBinaryReader binaryReader) - { - Coordinates = new Vector3(binaryReader); - Normal = new Vector3(binaryReader); - BoneId = binaryReader.Read(); - UV = new Vector2(binaryReader); - } - /// /// The vertex coordinates in the 3d space /// @@ -41,13 +26,19 @@ public VaniVertexFrame(SBinaryReader binaryReader) /// public Vector2 UV { get; set; } - public IEnumerable GetBytes(params object[] options) + public void Read(SBinaryReader binaryReader) + { + Coordinates = binaryReader.Read(); + Normal = binaryReader.Read(); + BoneId = binaryReader.ReadInt32(); + UV = binaryReader.Read(); + } + + public void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Coordinates.GetBytes()); - buffer.AddRange(Normal.GetBytes()); - buffer.AddRange(BoneId.GetBytes()); - buffer.AddRange(UV.GetBytes()); - return buffer; + binaryWriter.Write(Coordinates); + binaryWriter.Write(Normal); + binaryWriter.Write(BoneId); + binaryWriter.Write(UV); } } diff --git a/src/Parsec/Shaiya/Wld/WLD.cs b/src/Parsec/Shaiya/Wld/WLD.cs deleted file mode 100644 index 250ac828..00000000 --- a/src/Parsec/Shaiya/Wld/WLD.cs +++ /dev/null @@ -1,363 +0,0 @@ -using Newtonsoft.Json; -using Parsec.Common; -using Parsec.Extensions; -using Parsec.Shaiya.Common; -using Parsec.Shaiya.Core; -using Parsec.Shaiya.Wld; - -namespace Parsec.Shaiya.WLD; - -public sealed class WLD : FileBase -{ - public WldType Type { get; set; } = WldType.FLD; - - #region FLD Only fields - - /// - /// Map Size, must be a power of 2 (1024 or 2048) - /// - public int MapSize { get; set; } - - /// - /// Map used for Y coordinate calculation based on X and Z - /// - public byte[] Heightmap { get; set; } - - /// - /// Map used for texture usage based on X and Z - /// - public byte[] TextureMap { get; set; } - - public List Textures { get; set; } = new(); - - #endregion - - /// - /// Fixed length string, length = 256. "inner layout", ".wtr" (water) for a field, ".dg" for a dungeon - /// - public String256 InnerLayout { get; set; } = string.Empty; - - /// - /// Files from Entity/Building - /// - public List BuildingNames { get; set; } = new(); - - public List BuildingCoordinates { get; set; } = new(); - - /// - /// Files from Entity/Shape - /// - public List ShapeNames { get; set; } = new(); - - public List ShapeCoordinates { get; set; } = new(); - - /// - /// Files from Entity/Tree - /// - public List TreeNames { get; set; } = new(); - - public List TreeCoordinates { get; set; } = new(); - - /// - /// Files from Entity/Grass - /// - public List GrassNames { get; set; } = new(); - - public List GrassCoordinates { get; set; } = new(); - - /// - /// Files from Entity/VAni - /// - public List VAniNames1 { get; set; } = new(); - - public List VAniCoordinates1 { get; set; } = new(); - - /// - /// Files from Entity/VAni - /// - public List VAniNames2 { get; set; } = new(); - - public List VAniCoordinates2 { get; set; } = new(); - - /// - /// Files from World/dungeon - /// - public List DungeonNames { get; set; } = new(); - - public List DungeonCoordinates { get; set; } = new(); - - /// - /// Files from Entity/MAni - /// - public List MAniNames { get; set; } = new(); - - public List MAniCoordinates { get; set; } = new(); - - public String256 EffectName { get; set; } = string.Empty; - - /// - /// Files from Effect/ - /// - public List Effects { get; set; } = new(); - - public int Unknown1 { get; set; } - - public int Unknown2 { get; set; } - - public int Unknown3 { get; set; } - - /// - /// Files from Entity/Object - /// - public List ObjectNames { get; set; } = new(); - - public List ObjectCoordinates { get; set; } = new(); - - /// - /// Files from Sound/Music - /// - public List MusicNames { get; set; } = new(); - - public List MusicZones { get; set; } = new(); - - /// - /// Files from Sound/Music - /// - public List SoundEffectNames { get; set; } = new(); - - public List Zones { get; set; } = new(); - - public List SoundEffects { get; set; } = new(); - - public List UnknownBoundingBoxes { get; set; } = new(); - - public List Portals { get; set; } = new(); - - public List Spawns { get; set; } = new(); - - public List NamedAreas { get; set; } = new(); - - public List Npcs { get; set; } = new(); - - #region FLD Only fields - - public String256 SkyName { get; set; } = string.Empty; - - public String256 CloudsName1 { get; set; } = string.Empty; - - public String256 CloudsName2 { get; set; } = string.Empty; - - #endregion - - public Vector3 Point1 { get; set; } - - public Vector3 Point2 { get; set; } - - public Vector3 Point3 { get; set; } - - public float Unknown5 { get; set; } - - public float Unknown6 { get; set; } - - // This structure is incomplete, there's a bunch of unknown fields missing - - [JsonIgnore] - public override string Extension => "wld"; - - public override void Read() - { - string typeStr = _binaryReader.ReadString(4).Trim('\0'); - - if (typeStr == "DUN") - Type = WldType.DUN; - - - // FLD only fields - if (Type == WldType.FLD) - { - MapSize = _binaryReader.Read(); - - int mappingSize = (int)Math.Pow(MapSize / 2f + 1, 2); - Heightmap = _binaryReader.ReadBytes(mappingSize * 2); - TextureMap = _binaryReader.ReadBytes(mappingSize); - - int textureCount = _binaryReader.Read(); - for (int i = 0; i < textureCount; i++) - Textures.Add(new WldTexture(_binaryReader)); - } - - InnerLayout = new String256(_binaryReader); - - ReadNamesAndCoordinates(BuildingNames, BuildingCoordinates); - ReadNamesAndCoordinates(ShapeNames, ShapeCoordinates); - ReadNamesAndCoordinates(TreeNames, TreeCoordinates); - ReadNamesAndCoordinates(GrassNames, GrassCoordinates); - ReadNamesAndCoordinates(VAniNames1, VAniCoordinates1); - ReadNamesAndCoordinates(VAniNames2, VAniCoordinates2); - ReadNamesAndCoordinates(DungeonNames, DungeonCoordinates); - ReadNames(MAniNames); - - int maniCoordinatesCount = _binaryReader.Read(); - for (int i = 0; i < maniCoordinatesCount; i++) - MAniCoordinates.Add(new WldManiCoordinate(_binaryReader)); - - EffectName = new String256(_binaryReader); - - int effectCount = _binaryReader.Read(); - for (int i = 0; i < effectCount; i++) - Effects.Add(new WldEffect(_binaryReader)); - - Unknown1 = _binaryReader.Read(); - Unknown2 = _binaryReader.Read(); - Unknown3 = _binaryReader.Read(); - - ReadNamesAndCoordinates(ObjectNames, ObjectCoordinates); - ReadNames(MusicNames); - - int musicZoneCount = _binaryReader.Read(); - for (int i = 0; i < musicZoneCount; i++) - MusicZones.Add(new WldMusicZone(_binaryReader)); - - ReadNames(SoundEffectNames); - - int zoneCount = _binaryReader.Read(); - for (int i = 0; i < zoneCount; i++) - Zones.Add(new WldZone(_binaryReader)); - - int backgroundMusicSpotCount = _binaryReader.Read(); - for (int i = 0; i < backgroundMusicSpotCount; i++) - SoundEffects.Add(new WldSoundEffect(_binaryReader)); - - int unknownBoundingBoxesCount = _binaryReader.Read(); - for (int i = 0; i < unknownBoundingBoxesCount; i++) - UnknownBoundingBoxes.Add(new WldUnknownBox(_binaryReader)); - - int portalCount = _binaryReader.Read(); - for (int i = 0; i < portalCount; i++) - Portals.Add(new WldPortal(_binaryReader)); - - int spawnCount = _binaryReader.Read(); - for (int i = 0; i < spawnCount; i++) - Spawns.Add(new WldSpawn(_binaryReader)); - - int namedAreaCount = _binaryReader.Read(); - for (int i = 0; i < namedAreaCount; i++) - NamedAreas.Add(new WldNamedArea(_binaryReader)); - - // NOTE: npcCount is the real npc count + the patrol coordinates count - int npcCount = _binaryReader.Read(); - - while (npcCount > 0) - { - var npc = new WldNpc(_binaryReader); - Npcs.Add(npc); - npcCount -= npc.PatrolCoordinates.Count; - npcCount--; - } - - if (Type == WldType.FLD) - { - SkyName = new String256(_binaryReader); - CloudsName1 = new String256(_binaryReader); - CloudsName2 = new String256(_binaryReader); - } - - Point1 = new Vector3(_binaryReader); - Point2 = new Vector3(_binaryReader); - Point3 = new Vector3(_binaryReader); - - Unknown5 = _binaryReader.Read(); - Unknown6 = _binaryReader.Read(); - } - - private void ReadNames(ICollection namesList) - { - int namesCount = _binaryReader.Read(); - for (int i = 0; i < namesCount; i++) - namesList.Add(new String256(_binaryReader)); - } - - private void ReadNamesAndCoordinates(ICollection namesList, ICollection coordinatesList) - { - ReadNames(namesList); - - int coordinatesCount = _binaryReader.Read(); - for (int i = 0; i < coordinatesCount; i++) - coordinatesList.Add(new WldCoordinate(_binaryReader)); - } - - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) - { - var buffer = new List(); - buffer.AddRange(Type == WldType.FLD ? "FLD".GetBytes(true) : "DUN".GetBytes(true)); - - // FLD only fields - if (Type == WldType.FLD) - { - buffer.AddRange(MapSize.GetBytes()); - buffer.AddRange(Heightmap); - buffer.AddRange(TextureMap); - buffer.AddRange(Textures.GetBytes()); - } - - buffer.AddRange(InnerLayout.GetBytes()); - - buffer.AddRange(BuildingNames.GetBytes()); - buffer.AddRange(BuildingCoordinates.GetBytes()); - buffer.AddRange(ShapeNames.GetBytes()); - buffer.AddRange(ShapeCoordinates.GetBytes()); - buffer.AddRange(TreeNames.GetBytes()); - buffer.AddRange(TreeCoordinates.GetBytes()); - buffer.AddRange(GrassNames.GetBytes()); - buffer.AddRange(GrassCoordinates.GetBytes()); - buffer.AddRange(VAniNames1.GetBytes()); - buffer.AddRange(VAniCoordinates1.GetBytes()); - buffer.AddRange(VAniNames2.GetBytes()); - buffer.AddRange(VAniCoordinates2.GetBytes()); - buffer.AddRange(DungeonNames.GetBytes()); - buffer.AddRange(DungeonCoordinates.GetBytes()); - buffer.AddRange(MAniNames.GetBytes()); - buffer.AddRange(MAniCoordinates.GetBytes()); - buffer.AddRange(EffectName.GetBytes()); - buffer.AddRange(Effects.GetBytes()); - - buffer.AddRange(Unknown1.GetBytes()); - buffer.AddRange(Unknown2.GetBytes()); - buffer.AddRange(Unknown3.GetBytes()); - - buffer.AddRange(ObjectNames.GetBytes()); - buffer.AddRange(ObjectCoordinates.GetBytes()); - buffer.AddRange(MusicNames.GetBytes()); - - buffer.AddRange(MusicZones.GetBytes()); - buffer.AddRange(SoundEffectNames.GetBytes()); - buffer.AddRange(Zones.GetBytes()); - buffer.AddRange(SoundEffects.GetBytes()); - buffer.AddRange(UnknownBoundingBoxes.GetBytes()); - buffer.AddRange(Portals.GetBytes()); - buffer.AddRange(Spawns.GetBytes()); - buffer.AddRange(NamedAreas.GetBytes()); - - int npcCount = Npcs.Count + Npcs.Sum(npc => npc.PatrolCoordinates.Count); - buffer.AddRange(npcCount.GetBytes()); - - foreach (var npc in Npcs) - buffer.AddRange(npc.GetBytes()); - - if (Type == WldType.FLD) - { - buffer.AddRange(SkyName.GetBytes()); - buffer.AddRange(CloudsName1.GetBytes()); - buffer.AddRange(CloudsName2.GetBytes()); - } - - buffer.AddRange(Point1.GetBytes()); - buffer.AddRange(Point2.GetBytes()); - buffer.AddRange(Point3.GetBytes()); - - buffer.AddRange(Unknown5.GetBytes()); - buffer.AddRange(Unknown6.GetBytes()); - - return buffer; - } -} diff --git a/src/Parsec/Shaiya/Wld/Wld.cs b/src/Parsec/Shaiya/Wld/Wld.cs new file mode 100644 index 00000000..ccae37c9 --- /dev/null +++ b/src/Parsec/Shaiya/Wld/Wld.cs @@ -0,0 +1,341 @@ +using Newtonsoft.Json; +using Parsec.Extensions; +using Parsec.Serialization; +using Parsec.Shaiya.Common; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Wld; + +public sealed class Wld : FileBase +{ + public WldType WldType { get; set; } = WldType.FLD; + + #region FLD Only fields + + /// + /// Map Size, must be a power of 2 (1024 or 2048) + /// + public uint MapSize { get; set; } + + /// + /// Map used for Y coordinate calculation based on X and Z + /// + public byte[] Heightmap { get; set; } + + /// + /// Map used for texture usage based on X and Z + /// + public byte[] TextureMap { get; set; } + + public List Textures { get; set; } = new(); + + #endregion + + /// + /// Fixed length string, length = 256. "inner layout", ".wtr" (water) for a field, ".dg" for a dungeon + /// + public String256 InnerLayout { get; set; } = string.Empty; + + /// + /// Files from Entity/Building + /// + public List BuildingNames { get; set; } = new(); + + public List BuildingCoordinates { get; set; } = new(); + + /// + /// Files from Entity/Shape + /// + public List ShapeNames { get; set; } = new(); + + public List ShapeCoordinates { get; set; } = new(); + + /// + /// Files from Entity/Tree + /// + public List TreeNames { get; set; } = new(); + + public List TreeCoordinates { get; set; } = new(); + + /// + /// Files from Entity/Grass + /// + public List GrassNames { get; set; } = new(); + + public List GrassCoordinates { get; set; } = new(); + + /// + /// Files from Entity/VAni + /// + public List VAniNames1 { get; set; } = new(); + + public List VAniCoordinates1 { get; set; } = new(); + + /// + /// Files from Entity/VAni + /// + public List VAniNames2 { get; set; } = new(); + + public List VAniCoordinates2 { get; set; } = new(); + + /// + /// Files from World/dungeon + /// + public List DungeonNames { get; set; } = new(); + + public List DungeonCoordinates { get; set; } = new(); + + /// + /// Files from Entity/MAni + /// + public List MAniNames { get; set; } = new(); + + public List MAniCoordinates { get; set; } = new(); + + public String256 EffectName { get; set; } = string.Empty; + + /// + /// Files from Effect/ + /// + public List Effects { get; set; } = new(); + + public int Unknown1 { get; set; } + + public int Unknown2 { get; set; } + + public int Unknown3 { get; set; } + + /// + /// Files from Entity/Object + /// + public List ObjectNames { get; set; } = new(); + + public List ObjectCoordinates { get; set; } = new(); + + /// + /// Files from Sound/Music + /// + public List MusicNames { get; set; } = new(); + + public List MusicZones { get; set; } = new(); + + /// + /// Files from Sound/Music + /// + public List SoundEffectNames { get; set; } = new(); + + public List Zones { get; set; } = new(); + + public List SoundEffects { get; set; } = new(); + + public List UnknownBoundingBoxes { get; set; } = new(); + + public List Portals { get; set; } = new(); + + public List Spawns { get; set; } = new(); + + public List NamedAreas { get; set; } = new(); + + public List Npcs { get; set; } = new(); + + #region FLD Only fields + + public String256 SkyName { get; set; } = string.Empty; + + public String256 CloudsName1 { get; set; } = string.Empty; + + public String256 CloudsName2 { get; set; } = string.Empty; + + #endregion + + public Vector3 Point1 { get; set; } + + public Vector3 Point2 { get; set; } + + public Vector3 Point3 { get; set; } + + public float Unknown5 { get; set; } + + public float Unknown6 { get; set; } + + // This structure is incomplete, there's a bunch of unknown fields missing + + [JsonIgnore] + public override string Extension => "wld"; + + protected override void Read(SBinaryReader binaryReader) + { + var typeStr = binaryReader.ReadString(4); + + if (typeStr == "DUN") + { + WldType = WldType.DUN; + } + + // FLD only fields + if (WldType == WldType.FLD) + { + MapSize = binaryReader.ReadUInt32(); + + var mappingSize = (int)Math.Pow(MapSize / 2f + 1, 2); + Heightmap = binaryReader.ReadBytes(mappingSize * 2); + TextureMap = binaryReader.ReadBytes(mappingSize); + Textures = binaryReader.ReadList().ToList(); + } + + InnerLayout = binaryReader.Read(); + + ReadNamesAndCoordinates(binaryReader, BuildingNames, BuildingCoordinates); + ReadNamesAndCoordinates(binaryReader, ShapeNames, ShapeCoordinates); + ReadNamesAndCoordinates(binaryReader, TreeNames, TreeCoordinates); + ReadNamesAndCoordinates(binaryReader, GrassNames, GrassCoordinates); + ReadNamesAndCoordinates(binaryReader, VAniNames1, VAniCoordinates1); + ReadNamesAndCoordinates(binaryReader, VAniNames2, VAniCoordinates2); + ReadNamesAndCoordinates(binaryReader, DungeonNames, DungeonCoordinates); + ReadNames(binaryReader, MAniNames); + + MAniCoordinates = binaryReader.ReadList().ToList(); + EffectName = binaryReader.Read(); + Effects = binaryReader.ReadList().ToList(); + + Unknown1 = binaryReader.ReadInt32(); + Unknown2 = binaryReader.ReadInt32(); + Unknown3 = binaryReader.ReadInt32(); + + ReadNamesAndCoordinates(binaryReader, ObjectNames, ObjectCoordinates); + ReadNames(binaryReader, MusicNames); + + MusicZones = binaryReader.ReadList().ToList(); + + ReadNames(binaryReader, SoundEffectNames); + + Zones = binaryReader.ReadList().ToList(); + SoundEffects = binaryReader.ReadList().ToList(); + UnknownBoundingBoxes = binaryReader.ReadList().ToList(); + Portals = binaryReader.ReadList().ToList(); + Spawns = binaryReader.ReadList().ToList(); + NamedAreas = binaryReader.ReadList().ToList(); + + // NOTE: npcCount is the real npc count + the patrol coordinates count + var npcCount = binaryReader.ReadInt32(); + + while (npcCount > 0) + { + var npc = binaryReader.Read(); + Npcs.Add(npc); + npcCount -= npc.PatrolCoordinates.Count; + npcCount--; + } + + if (WldType == WldType.FLD) + { + SkyName = binaryReader.Read(); + CloudsName1 = binaryReader.Read(); + CloudsName2 = binaryReader.Read(); + } + + Point1 = binaryReader.Read(); + Point2 = binaryReader.Read(); + Point3 = binaryReader.Read(); + + Unknown5 = binaryReader.ReadSingle(); + Unknown6 = binaryReader.ReadSingle(); + } + + private void ReadNames(SBinaryReader binaryReader, ICollection namesList) + { + var namesCount = binaryReader.ReadInt32(); + for (var i = 0; i < namesCount; i++) + { + var str256 = binaryReader.Read(); + namesList.Add(str256); + } + } + + private void ReadNamesAndCoordinates(SBinaryReader binaryReader, ICollection namesList, + ICollection coordinatesList) + { + ReadNames(binaryReader, namesList); + + var coordinatesCount = binaryReader.ReadInt32(); + for (var i = 0; i < coordinatesCount; i++) + { + var coordinate = binaryReader.Read(); + coordinatesList.Add(coordinate); + } + } + + protected override void Write(SBinaryWriter binaryWriter) + { + var typeStr = WldType == WldType.FLD ? "FLD" : "DUN"; + binaryWriter.Write(typeStr, isLengthPrefixed: false, includeStringTerminator: true); + + // FLD only fields + if (WldType == WldType.FLD) + { + binaryWriter.Write(MapSize); + binaryWriter.Write(Heightmap); + binaryWriter.Write(TextureMap); + binaryWriter.Write(Textures.ToSerializable()); + } + + binaryWriter.Write(InnerLayout); + + binaryWriter.Write(BuildingNames.ToSerializable()); + binaryWriter.Write(BuildingCoordinates.ToSerializable()); + binaryWriter.Write(ShapeNames.ToSerializable()); + binaryWriter.Write(ShapeCoordinates.ToSerializable()); + binaryWriter.Write(TreeNames.ToSerializable()); + binaryWriter.Write(TreeCoordinates.ToSerializable()); + binaryWriter.Write(GrassNames.ToSerializable()); + binaryWriter.Write(GrassCoordinates.ToSerializable()); + binaryWriter.Write(VAniNames1.ToSerializable()); + binaryWriter.Write(VAniCoordinates1.ToSerializable()); + binaryWriter.Write(VAniNames2.ToSerializable()); + binaryWriter.Write(VAniCoordinates2.ToSerializable()); + binaryWriter.Write(DungeonNames.ToSerializable()); + binaryWriter.Write(DungeonCoordinates.ToSerializable()); + binaryWriter.Write(MAniNames.ToSerializable()); + binaryWriter.Write(MAniCoordinates.ToSerializable()); + binaryWriter.Write(EffectName); + binaryWriter.Write(Effects.ToSerializable()); + + binaryWriter.Write(Unknown1); + binaryWriter.Write(Unknown2); + binaryWriter.Write(Unknown3); + + binaryWriter.Write(ObjectNames.ToSerializable()); + binaryWriter.Write(ObjectCoordinates.ToSerializable()); + binaryWriter.Write(MusicNames.ToSerializable()); + binaryWriter.Write(MusicZones.ToSerializable()); + binaryWriter.Write(SoundEffectNames.ToSerializable()); + binaryWriter.Write(Zones.ToSerializable()); + binaryWriter.Write(SoundEffects.ToSerializable()); + binaryWriter.Write(UnknownBoundingBoxes.ToSerializable()); + binaryWriter.Write(Portals.ToSerializable()); + binaryWriter.Write(Spawns.ToSerializable()); + binaryWriter.Write(NamedAreas.ToSerializable()); + + var npcCount = Npcs.Count + Npcs.Sum(npc => npc.PatrolCoordinates.Count); + binaryWriter.Write(npcCount); + + foreach (var npc in Npcs) + { + binaryWriter.Write(npc); + } + + if (WldType == WldType.FLD) + { + binaryWriter.Write(SkyName); + binaryWriter.Write(CloudsName1); + binaryWriter.Write(CloudsName2); + } + + binaryWriter.Write(Point1); + binaryWriter.Write(Point2); + binaryWriter.Write(Point3); + + binaryWriter.Write(Unknown5); + binaryWriter.Write(Unknown6); + } +} diff --git a/src/Parsec/Shaiya/Wld/WldCoordinate.cs b/src/Parsec/Shaiya/Wld/WldCoordinate.cs index 001001d3..685347c8 100644 --- a/src/Parsec/Shaiya/Wld/WldCoordinate.cs +++ b/src/Parsec/Shaiya/Wld/WldCoordinate.cs @@ -1,15 +1,13 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; /// /// Coordinates to place a 3D model in the world /// -public sealed class WldCoordinate : IBinary +public sealed class WldCoordinate : ISerializable { /// /// Id of a 3D Model @@ -31,26 +29,20 @@ public sealed class WldCoordinate : IBinary /// public Vector3 RotationUp { get; set; } - [JsonConstructor] - public WldCoordinate() - { - } - public WldCoordinate(SBinaryReader binaryReader) + public void Read(SBinaryReader binaryReader) { - Id = binaryReader.Read(); - Position = new Vector3(binaryReader); - RotationForward = new Vector3(binaryReader); - RotationUp = new Vector3(binaryReader); + Id = binaryReader.ReadInt32(); + Position = binaryReader.Read(); + RotationForward = binaryReader.Read(); + RotationUp = binaryReader.Read(); } - public IEnumerable GetBytes(params object[] options) + public void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Id.GetBytes()); - buffer.AddRange(Position.GetBytes()); - buffer.AddRange(RotationForward.GetBytes()); - buffer.AddRange(RotationUp.GetBytes()); - return buffer; + binaryWriter.Write(Id); + binaryWriter.Write(Position); + binaryWriter.Write(RotationForward); + binaryWriter.Write(RotationUp); } } diff --git a/src/Parsec/Shaiya/Wld/WldEffect.cs b/src/Parsec/Shaiya/Wld/WldEffect.cs index 5606dca8..d718973e 100644 --- a/src/Parsec/Shaiya/Wld/WldEffect.cs +++ b/src/Parsec/Shaiya/Wld/WldEffect.cs @@ -1,15 +1,13 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; /// /// Represents an effect in the world /// -public sealed class WldEffect : IBinary +public sealed class WldEffect : ISerializable { /// /// Effect's position @@ -31,26 +29,19 @@ public sealed class WldEffect : IBinary /// public int EffectId { get; set; } - [JsonConstructor] - public WldEffect() + public void Read(SBinaryReader binaryReader) { + Position = binaryReader.Read(); + RotationForward = binaryReader.Read(); + RotationUp = binaryReader.Read(); + EffectId = binaryReader.ReadInt32(); } - public WldEffect(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - Position = new Vector3(binaryReader); - RotationForward = new Vector3(binaryReader); - RotationUp = new Vector3(binaryReader); - EffectId = binaryReader.Read(); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Position.GetBytes()); - buffer.AddRange(RotationForward.GetBytes()); - buffer.AddRange(RotationUp.GetBytes()); - buffer.AddRange(EffectId.GetBytes()); - return buffer; + binaryWriter.Write(Position); + binaryWriter.Write(RotationForward); + binaryWriter.Write(RotationUp); + binaryWriter.Write(EffectId); } } diff --git a/src/Parsec/Shaiya/Wld/WldFaction.cs b/src/Parsec/Shaiya/Wld/WldFaction.cs new file mode 100644 index 00000000..cfcc0b72 --- /dev/null +++ b/src/Parsec/Shaiya/Wld/WldFaction.cs @@ -0,0 +1,8 @@ +namespace Parsec.Shaiya.Wld; + +public enum WldFaction +{ + Light, + Fury, + Any +} diff --git a/src/Parsec/Shaiya/Wld/WldManiCoordinate.cs b/src/Parsec/Shaiya/Wld/WldManiCoordinate.cs index ccf3bae7..15a9d27e 100644 --- a/src/Parsec/Shaiya/Wld/WldManiCoordinate.cs +++ b/src/Parsec/Shaiya/Wld/WldManiCoordinate.cs @@ -1,15 +1,13 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; /// /// Coordinates to place a 3D object in the field. Used by 'MANI' entities only. /// -public sealed class WldManiCoordinate : IBinary +public sealed class WldManiCoordinate : ISerializable { /// /// Id of the building that should be animated using this MAni file @@ -36,28 +34,21 @@ public sealed class WldManiCoordinate : IBinary /// public Vector3 RotationUp { get; set; } - [JsonConstructor] - public WldManiCoordinate() + public void Read(SBinaryReader binaryReader) { + WorldBuildingId = binaryReader.ReadInt32(); + Id = binaryReader.ReadInt32(); + Position = binaryReader.Read(); + RotationForward = binaryReader.Read(); + RotationUp = binaryReader.Read(); } - public WldManiCoordinate(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - WorldBuildingId = binaryReader.Read(); - Id = binaryReader.Read(); - Position = new Vector3(binaryReader); - RotationForward = new Vector3(binaryReader); - RotationUp = new Vector3(binaryReader); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(WorldBuildingId.GetBytes()); - buffer.AddRange(Id.GetBytes()); - buffer.AddRange(Position.GetBytes()); - buffer.AddRange(RotationForward.GetBytes()); - buffer.AddRange(RotationUp.GetBytes()); - return buffer; + binaryWriter.Write(WorldBuildingId); + binaryWriter.Write(Id); + binaryWriter.Write(Position); + binaryWriter.Write(RotationForward); + binaryWriter.Write(RotationUp); } } diff --git a/src/Parsec/Shaiya/Wld/WldMusicZone.cs b/src/Parsec/Shaiya/Wld/WldMusicZone.cs index e65a2e02..20a2efb2 100644 --- a/src/Parsec/Shaiya/Wld/WldMusicZone.cs +++ b/src/Parsec/Shaiya/Wld/WldMusicZone.cs @@ -1,15 +1,14 @@ using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; /// /// A rectangular area of the world in which music is played /// -public sealed class WldMusicZone : IBinary +public sealed class WldMusicZone : ISerializable { /// /// The rectangular area of the music zone @@ -36,21 +35,19 @@ public WldMusicZone() { } - public WldMusicZone(SBinaryReader binaryReader) + public void Read(SBinaryReader binaryReader) { - BoundingBox = new BoundingBox(binaryReader); - Radius = binaryReader.Read(); - Id = binaryReader.Read(); - Unknown = binaryReader.Read(); + BoundingBox = binaryReader.Read(); + Radius = binaryReader.ReadSingle(); + Id = binaryReader.ReadInt32(); + Unknown = binaryReader.ReadInt32(); } - public IEnumerable GetBytes(params object[] options) + public void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(BoundingBox.GetBytes()); - buffer.AddRange(Radius.GetBytes()); - buffer.AddRange(Id.GetBytes()); - buffer.AddRange(Unknown.GetBytes()); - return buffer; + binaryWriter.Write(BoundingBox); + binaryWriter.Write(Radius); + binaryWriter.Write(Id); + binaryWriter.Write(Unknown); } } diff --git a/src/Parsec/Shaiya/Wld/WldNamedArea.cs b/src/Parsec/Shaiya/Wld/WldNamedArea.cs index cac67514..6331e40c 100644 --- a/src/Parsec/Shaiya/Wld/WldNamedArea.cs +++ b/src/Parsec/Shaiya/Wld/WldNamedArea.cs @@ -1,12 +1,10 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; -public enum NamedAreaMode +public enum WldNamedAreaMode { WorldIndexTxt = 0, BmpFile = 2 @@ -15,7 +13,7 @@ public enum NamedAreaMode /// /// Represents a NamedArea in the world /// -public sealed class WldNamedArea : IBinary +public sealed class WldNamedArea : ISerializable { /// /// The BoundingBox where this Named Area applies @@ -32,47 +30,40 @@ public sealed class WldNamedArea : IBinary /// If Mode is 0, it reads the caption from world_index.txt /// If Mode is 2, this field defines the bmp file for the area's name /// - public String256 Text1 { get; set; } + public String256 Text1 { get; set; } = string.Empty; /// /// Comment or file name (unlocalized - Korean) /// - public String256 Text2 { get; set; } + public String256 Text2 { get; set; } = string.Empty; /// /// Defines the mode for /// - public NamedAreaMode Mode { get; set; } + public WldNamedAreaMode Mode { get; set; } /// /// Almost always 0 /// public int Unknown { get; set; } - [JsonConstructor] - public WldNamedArea() + public void Read(SBinaryReader binaryReader) { + BoundingBox = binaryReader.Read(); + Radius = binaryReader.ReadSingle(); + Text1 = binaryReader.Read(); + Text2 = binaryReader.Read(); + Mode = (WldNamedAreaMode)binaryReader.ReadInt32(); + Unknown = binaryReader.ReadInt32(); } - public WldNamedArea(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - BoundingBox = new BoundingBox(binaryReader); - Radius = binaryReader.Read(); - Text1 = new String256(binaryReader); - Text2 = new String256(binaryReader); - Mode = (NamedAreaMode)binaryReader.Read(); - Unknown = binaryReader.Read(); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(BoundingBox.GetBytes()); - buffer.AddRange(Radius.GetBytes()); - buffer.AddRange(Text1.GetBytes()); - buffer.AddRange(Text2.GetBytes()); - buffer.AddRange(((int)Mode).GetBytes()); - buffer.AddRange(Unknown.GetBytes()); - return buffer; + binaryWriter.Write(BoundingBox); + binaryWriter.Write(Radius); + binaryWriter.Write(Text1); + binaryWriter.Write(Text2); + binaryWriter.Write((int)Mode); + binaryWriter.Write(Unknown); } } diff --git a/src/Parsec/Shaiya/Wld/WldNpc.cs b/src/Parsec/Shaiya/Wld/WldNpc.cs index 466b379c..d7ef5c8c 100644 --- a/src/Parsec/Shaiya/Wld/WldNpc.cs +++ b/src/Parsec/Shaiya/Wld/WldNpc.cs @@ -1,12 +1,11 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; -public sealed class WldNpc : IBinary +public sealed class WldNpc : ISerializable { public int Type { get; set; } @@ -18,31 +17,21 @@ public sealed class WldNpc : IBinary public List PatrolCoordinates { get; set; } = new(); - [JsonConstructor] - public WldNpc() + public void Read(SBinaryReader binaryReader) { + Type = binaryReader.ReadInt32(); + TypeId = binaryReader.ReadInt32(); + Coordinates = binaryReader.Read(); + Orientation = binaryReader.ReadSingle(); + PatrolCoordinates = binaryReader.ReadList().ToList(); } - public WldNpc(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - Type = binaryReader.Read(); - TypeId = binaryReader.Read(); - Coordinates = new Vector3(binaryReader); - Orientation = binaryReader.Read(); - - int patrolCoordinatesCount = binaryReader.Read(); - for (int i = 0; i < patrolCoordinatesCount; i++) - PatrolCoordinates.Add(new WldNpcPatrolCoordinate(binaryReader)); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Type.GetBytes()); - buffer.AddRange(TypeId.GetBytes()); - buffer.AddRange(Coordinates.GetBytes()); - buffer.AddRange(Orientation.GetBytes()); - buffer.AddRange(PatrolCoordinates.GetBytes()); - return buffer; + binaryWriter.Write(Type); + binaryWriter.Write(TypeId); + binaryWriter.Write(Coordinates); + binaryWriter.Write(Orientation); + binaryWriter.Write(PatrolCoordinates.ToSerializable()); } } diff --git a/src/Parsec/Shaiya/Wld/WldNpcPatrolCoordinate.cs b/src/Parsec/Shaiya/Wld/WldNpcPatrolCoordinate.cs index 5580ab76..67ce002f 100644 --- a/src/Parsec/Shaiya/Wld/WldNpcPatrolCoordinate.cs +++ b/src/Parsec/Shaiya/Wld/WldNpcPatrolCoordinate.cs @@ -1,23 +1,20 @@ -using Newtonsoft.Json; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; -public sealed class WldNpcPatrolCoordinate : IBinary +public sealed class WldNpcPatrolCoordinate : ISerializable { public Vector3 Position { get; set; } - [JsonConstructor] - public WldNpcPatrolCoordinate() + public void Read(SBinaryReader binaryReader) { + Position = binaryReader.Read(); } - public WldNpcPatrolCoordinate(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - Position = new Vector3(binaryReader); + binaryWriter.Write(Position); } - - public IEnumerable GetBytes(params object[] options) => Position.GetBytes(); } diff --git a/src/Parsec/Shaiya/Wld/WldPortal.cs b/src/Parsec/Shaiya/Wld/WldPortal.cs index ba90eba9..db83475b 100644 --- a/src/Parsec/Shaiya/Wld/WldPortal.cs +++ b/src/Parsec/Shaiya/Wld/WldPortal.cs @@ -1,15 +1,13 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; /// /// Represents a Portal in the world /// -public sealed class WldPortal : IBinary +public sealed class WldPortal : ISerializable { /// /// The area of the portal @@ -37,9 +35,9 @@ public sealed class WldPortal : IBinary public byte MapId { get; set; } /// - /// The faction which can use the portal + /// The faction which can use the portal (unsigned short) /// - public FactionShort Faction { get; set; } + public WldFaction Faction { get; set; } /// /// Almost always 0L @@ -51,34 +49,27 @@ public sealed class WldPortal : IBinary /// public Vector3 DestinationPosition { get; set; } - [JsonConstructor] - public WldPortal() + public void Read(SBinaryReader binaryReader) { + BoundingBox = binaryReader.Read(); + Radius = binaryReader.ReadSingle(); + Text1 = binaryReader.Read(); + Text2 = binaryReader.Read(); + MapId = binaryReader.ReadByte(); + Faction = (WldFaction)binaryReader.ReadInt16(); + Unknown = binaryReader.ReadByte(); + DestinationPosition = binaryReader.Read(); } - public WldPortal(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - BoundingBox = new BoundingBox(binaryReader); - Radius = binaryReader.Read(); - Text1 = new String256(binaryReader); - Text2 = new String256(binaryReader); - MapId = binaryReader.Read(); - Faction = (FactionShort)binaryReader.Read(); - Unknown = binaryReader.Read(); - DestinationPosition = new Vector3(binaryReader); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(BoundingBox.GetBytes()); - buffer.AddRange(Radius.GetBytes()); - buffer.AddRange(Text1.GetBytes()); - buffer.AddRange(Text2.GetBytes()); - buffer.Add(MapId); - buffer.AddRange(((short)Faction).GetBytes()); - buffer.Add(Unknown); - buffer.AddRange(DestinationPosition.GetBytes()); - return buffer; + binaryWriter.Write(BoundingBox); + binaryWriter.Write(Radius); + binaryWriter.Write(Text1); + binaryWriter.Write(Text2); + binaryWriter.Write(MapId); + binaryWriter.Write((short)Faction); + binaryWriter.Write(Unknown); + binaryWriter.Write(DestinationPosition); } } diff --git a/src/Parsec/Shaiya/Wld/WldSoundEffect.cs b/src/Parsec/Shaiya/Wld/WldSoundEffect.cs index 79756fb5..affd6e2a 100644 --- a/src/Parsec/Shaiya/Wld/WldSoundEffect.cs +++ b/src/Parsec/Shaiya/Wld/WldSoundEffect.cs @@ -1,15 +1,13 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; /// /// A circular area of the world in which music is played /// -public sealed class WldSoundEffect : IBinary +public sealed class WldSoundEffect : ISerializable { /// /// Id of the wav file (from the linked name list of files) @@ -26,24 +24,17 @@ public sealed class WldSoundEffect : IBinary /// public float Radius { get; set; } - [JsonConstructor] - public WldSoundEffect() + public void Read(SBinaryReader binaryReader) { + Id = binaryReader.ReadInt32(); + Center = binaryReader.Read(); + Radius = binaryReader.ReadSingle(); } - public WldSoundEffect(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - Id = binaryReader.Read(); - Center = new Vector3(binaryReader); - Radius = binaryReader.Read(); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Id.GetBytes()); - buffer.AddRange(Center.GetBytes()); - buffer.AddRange(Radius.GetBytes()); - return buffer; + binaryWriter.Write(Id); + binaryWriter.Write(Center); + binaryWriter.Write(Radius); } } diff --git a/src/Parsec/Shaiya/Wld/WldSpawn.cs b/src/Parsec/Shaiya/Wld/WldSpawn.cs index 9a24e81d..3cc8a49b 100644 --- a/src/Parsec/Shaiya/Wld/WldSpawn.cs +++ b/src/Parsec/Shaiya/Wld/WldSpawn.cs @@ -1,15 +1,13 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; /// /// Represents a spawn area in the world /// -public sealed class WldSpawn : IBinary +public sealed class WldSpawn : ISerializable { /// /// Almost always 1 @@ -29,35 +27,28 @@ public sealed class WldSpawn : IBinary /// /// Faction which uses this spawn area /// - public FactionInt Faction { get; set; } + public WldFaction Faction { get; set; } /// /// Almost always 0 /// public int Unknown3 { get; set; } - [JsonConstructor] - public WldSpawn() + public void Read(SBinaryReader binaryReader) { + Unknown1 = binaryReader.ReadInt32(); + BoundingBox = binaryReader.Read(); + Radius = binaryReader.ReadSingle(); + Faction = (WldFaction)binaryReader.ReadInt32(); + Unknown3 = binaryReader.ReadInt32(); } - public WldSpawn(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - Unknown1 = binaryReader.Read(); - BoundingBox = new BoundingBox(binaryReader); - Radius = binaryReader.Read(); - Faction = (FactionInt)binaryReader.Read(); - Unknown3 = binaryReader.Read(); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(Unknown1.GetBytes()); - buffer.AddRange(BoundingBox.GetBytes()); - buffer.AddRange(Radius.GetBytes()); - buffer.AddRange(((int)Faction).GetBytes()); - buffer.AddRange(Unknown1.GetBytes()); - return buffer; + binaryWriter.Write(Unknown1); + binaryWriter.Write(BoundingBox); + binaryWriter.Write(Radius); + binaryWriter.Write((int)Faction); + binaryWriter.Write(Unknown3); } } diff --git a/src/Parsec/Shaiya/Wld/WldTexture.cs b/src/Parsec/Shaiya/Wld/WldTexture.cs index c5a1d404..59c70edf 100644 --- a/src/Parsec/Shaiya/Wld/WldTexture.cs +++ b/src/Parsec/Shaiya/Wld/WldTexture.cs @@ -1,17 +1,15 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; -public sealed class WldTexture : IBinary +public sealed class WldTexture : ISerializable { /// /// .tga texture file /// - public String256 TextureName { get; set; } + public String256 TextureName { get; set; } = string.Empty; /// /// Texture tile size @@ -21,26 +19,19 @@ public sealed class WldTexture : IBinary /// /// .wav sound file /// - public String256 WalkSound { get; set; } + public String256 WalkSound { get; set; } = string.Empty; - [JsonConstructor] - public WldTexture() + public void Read(SBinaryReader binaryReader) { + TextureName = binaryReader.Read(); + TileSize = binaryReader.ReadSingle(); + WalkSound = binaryReader.Read(); } - public WldTexture(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - TextureName = new String256(binaryReader); - TileSize = binaryReader.Read(); - WalkSound = new String256(binaryReader); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(TextureName.GetBytes()); - buffer.AddRange(TileSize.GetBytes()); - buffer.AddRange(WalkSound.GetBytes()); - return buffer; + binaryWriter.Write(TextureName); + binaryWriter.Write(TileSize); + binaryWriter.Write(WalkSound); } } diff --git a/src/Parsec/Shaiya/Wld/WldUnknownBox.cs b/src/Parsec/Shaiya/Wld/WldUnknownBox.cs index 8e156304..1958899f 100644 --- a/src/Parsec/Shaiya/Wld/WldUnknownBox.cs +++ b/src/Parsec/Shaiya/Wld/WldUnknownBox.cs @@ -1,12 +1,10 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Wld; -public class WldUnknownBox : IBinary +public class WldUnknownBox : ISerializable { public BoundingBox BoundingBox { get; set; } @@ -15,22 +13,15 @@ public class WldUnknownBox : IBinary /// public float Radius { get; set; } - [JsonConstructor] - public WldUnknownBox() + public void Read(SBinaryReader binaryReader) { + BoundingBox = binaryReader.Read(); + Radius = binaryReader.ReadSingle(); } - public WldUnknownBox(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - BoundingBox = new BoundingBox(binaryReader); - Radius = binaryReader.Read(); - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(BoundingBox.GetBytes()); - buffer.AddRange(Radius.GetBytes()); - return buffer; + binaryWriter.Write(BoundingBox); + binaryWriter.Write(Radius); } } diff --git a/src/Parsec/Shaiya/Wld/WldZone.cs b/src/Parsec/Shaiya/Wld/WldZone.cs index ccaab630..56882c37 100644 --- a/src/Parsec/Shaiya/Wld/WldZone.cs +++ b/src/Parsec/Shaiya/Wld/WldZone.cs @@ -1,44 +1,25 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; -namespace Parsec.Shaiya.WLD; +namespace Parsec.Shaiya.Wld; -public sealed class WldZone : IBinary +public sealed class WldZone : ISerializable { public BoundingBox BoundingBox { get; set; } - public List Identifiers { get; set; } = new(); + public List Identifiers { get; set; } = new(); - [JsonConstructor] - public WldZone() + public void Read(SBinaryReader binaryReader) { + BoundingBox = binaryReader.Read(); + Identifiers = binaryReader.ReadList().ToList(); } - public WldZone(SBinaryReader binaryReader) + public void Write(SBinaryWriter binaryWriter) { - BoundingBox = new BoundingBox(binaryReader); - - int identifierCount = binaryReader.Read(); - - for (int i = 0; i < identifierCount; i++) - { - int identifier = binaryReader.Read(); - Identifiers.Add(identifier); - } - } - - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); - buffer.AddRange(BoundingBox.GetBytes()); - - buffer.AddRange(Identifiers.Count.GetBytes()); - foreach (int identifier in Identifiers) - buffer.AddRange(identifier.GetBytes()); - - return buffer; + binaryWriter.Write(BoundingBox); + binaryWriter.Write(Identifiers.ToSerializable()); } } diff --git a/src/Parsec/Shaiya/Wld/WldZoneIdentifier.cs b/src/Parsec/Shaiya/Wld/WldZoneIdentifier.cs new file mode 100644 index 00000000..4d4c7dba --- /dev/null +++ b/src/Parsec/Shaiya/Wld/WldZoneIdentifier.cs @@ -0,0 +1,19 @@ +using Parsec.Serialization; +using Parsec.Shaiya.Core; + +namespace Parsec.Shaiya.Wld; + +public class WldZoneIdentifier : ISerializable +{ + public int Idenfitier { get; set; } + + public void Read(SBinaryReader binaryReader) + { + Idenfitier = binaryReader.ReadInt32(); + } + + public void Write(SBinaryWriter binaryWriter) + { + binaryWriter.Write(Idenfitier); + } +} diff --git a/src/Parsec/Shaiya/Wtr/Wtr.cs b/src/Parsec/Shaiya/Wtr/Wtr.cs index 9c412781..010f2738 100644 --- a/src/Parsec/Shaiya/Wtr/Wtr.cs +++ b/src/Parsec/Shaiya/Wtr/Wtr.cs @@ -1,5 +1,5 @@ -using Parsec.Common; -using Parsec.Extensions; +using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; @@ -8,34 +8,31 @@ namespace Parsec.Shaiya.Wtr; public sealed class Wtr : FileBase { public float Unknown1 { get; set; } + public uint Unknown2 { get; set; } + public int Unknown3 { get; set; } /// /// List of texture file names with a fixed lenght of 256 /// - public List Textures { get; } = new(); + public List Textures { get; set; } = new(); public override string Extension => "wtr"; - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - Unknown1 = _binaryReader.Read(); - Unknown2 = _binaryReader.Read(); - Unknown3 = _binaryReader.Read(); - - int textureCount = _binaryReader.Read(); - for (int i = 0; i < textureCount; i++) - Textures.Add(new String256(_binaryReader)); + Unknown1 = binaryReader.ReadSingle(); + Unknown2 = binaryReader.ReadUInt32(); + Unknown3 = binaryReader.ReadInt32(); + Textures = binaryReader.ReadList().ToList(); } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Unknown1.GetBytes()); - buffer.AddRange(Unknown2.GetBytes()); - buffer.AddRange(Unknown3.GetBytes()); - buffer.AddRange(Textures.GetBytes()); - return buffer; + binaryWriter.Write(Unknown1); + binaryWriter.Write(Unknown2); + binaryWriter.Write(Unknown3); + binaryWriter.Write(Textures.ToSerializable()); } } diff --git a/src/Parsec/Shaiya/Zon/Zon.cs b/src/Parsec/Shaiya/Zon/Zon.cs index 8937fa0c..c51241d6 100644 --- a/src/Parsec/Shaiya/Zon/Zon.cs +++ b/src/Parsec/Shaiya/Zon/Zon.cs @@ -1,5 +1,5 @@ -using Parsec.Common; -using Parsec.Extensions; +using Parsec.Extensions; +using Parsec.Serialization; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Zon; @@ -7,28 +7,26 @@ namespace Parsec.Shaiya.Zon; public sealed class Zon : FileBase { public int Format { get; set; } - public List Records { get; } = new(); + + public List Records { get; set; } = new(); public override string Extension => "zon"; - public override void Read() + protected override void Read(SBinaryReader binaryReader) { - Format = _binaryReader.Read(); + Format = binaryReader.ReadInt32(); - int recordCount = _binaryReader.Read(); - for (int i = 0; i < recordCount; i++) - Records.Add(new ZonRecord(Format, _binaryReader)); + // Format is used as the ExtraOption on ZonRecord serialization + binaryReader.SerializationOptions.ExtraOption = Format; + Records = binaryReader.ReadList().ToList(); } - public override IEnumerable GetBytes(Episode episode = Episode.Unknown) + protected override void Write(SBinaryWriter binaryWriter) { - var buffer = new List(); - buffer.AddRange(Format.GetBytes()); - - buffer.AddRange(Records.Count.GetBytes()); - foreach (var record in Records) - buffer.AddRange(record.GetBytes(Format)); + binaryWriter.Write(Format); - return buffer; + // Format is used as the ExtraOption on ZonRecord serialization + binaryWriter.SerializationOptions.ExtraOption = Format; + binaryWriter.Write(Records.ToSerializable()); } } diff --git a/src/Parsec/Shaiya/Zon/ZonRecord.cs b/src/Parsec/Shaiya/Zon/ZonRecord.cs index 4072d668..4533b085 100644 --- a/src/Parsec/Shaiya/Zon/ZonRecord.cs +++ b/src/Parsec/Shaiya/Zon/ZonRecord.cs @@ -1,42 +1,13 @@ -using Newtonsoft.Json; -using Parsec.Extensions; -using Parsec.Readers; +using Parsec.Serialization; using Parsec.Shaiya.Common; using Parsec.Shaiya.Core; namespace Parsec.Shaiya.Zon; -public sealed class ZonRecord : IBinary +public sealed class ZonRecord : ISerializable { - public ZonRecord(int format, SBinaryReader binaryReader) - { - Index = binaryReader.Read(); - P1 = binaryReader.Read(); - - if (format > 2) - P2 = binaryReader.Read(); - - Coordinates1 = new Vector3(binaryReader); - Coordinates2 = new Vector3(binaryReader); - - if (format > 2) - { - Coordinates3 = new Vector3(binaryReader); - Coordinates4 = new Vector3(binaryReader); - Unknown1 = binaryReader.Read(); - Unknown2 = binaryReader.Read(); - } - - MapId = binaryReader.Read(); - Description = binaryReader.ReadString(false); - } - - [JsonConstructor] - public ZonRecord() - { - } - public byte Index { get; set; } + public byte P1 { get; set; } /// @@ -45,6 +16,7 @@ public ZonRecord() public byte P2 { get; set; } public Vector3 Coordinates1 { get; set; } + public Vector3 Coordinates2 { get; set; } /// @@ -67,41 +39,71 @@ public ZonRecord() /// public float Unknown2 { get; set; } - public short MapId { get; set; } - public string Description { get; set; } + public ushort MapId { get; set; } - /// - /// Expects format (int) as an option - /// - public IEnumerable GetBytes(params object[] options) - { - var buffer = new List(); + public string Description { get; set; } = string.Empty; + public void Read(SBinaryReader binaryReader) + { var format = 0; - if (options.Length > 0) - format = (int)options[0]; + if (binaryReader.SerializationOptions.ExtraOption is int optionFormat) + { + format = optionFormat; + } + + Index = binaryReader.ReadByte(); + P1 = binaryReader.ReadByte(); + + if (format > 2) + { + P2 = binaryReader.ReadByte(); + } - buffer.Add(Index); - buffer.Add(P1); + Coordinates1 = binaryReader.Read(); + Coordinates2 = binaryReader.Read(); if (format > 2) - buffer.Add(P2); + { + Coordinates3 = binaryReader.Read(); + Coordinates4 = binaryReader.Read(); + Unknown1 = binaryReader.ReadSingle(); + Unknown2 = binaryReader.ReadSingle(); + } + + MapId = binaryReader.ReadUInt16(); + Description = binaryReader.ReadString(); + } - buffer.AddRange(Coordinates1.GetBytes()); - buffer.AddRange(Coordinates2.GetBytes()); + public void Write(SBinaryWriter binaryWriter) + { + var format = 0; + + if (binaryWriter.SerializationOptions.ExtraOption is int optionFormat) + { + format = optionFormat; + } + + binaryWriter.Write(Index); + binaryWriter.Write(P1); if (format > 2) { - buffer.AddRange(Coordinates3.GetBytes()); - buffer.AddRange(Coordinates4.GetBytes()); - buffer.AddRange(Unknown1.GetBytes()); - buffer.AddRange(Unknown1.GetBytes()); + binaryWriter.Write(P2); } - buffer.AddRange(MapId.GetBytes()); - buffer.AddRange(Description.GetLengthPrefixedBytes(false)); + binaryWriter.Write(Coordinates1); + binaryWriter.Write(Coordinates2); + + if (format > 2) + { + binaryWriter.Write(Coordinates3); + binaryWriter.Write(Coordinates4); + binaryWriter.Write(Unknown1); + binaryWriter.Write(Unknown2); + } - return buffer; + binaryWriter.Write(MapId); + binaryWriter.Write(Description); } } diff --git a/tests/Parsec.Tests/FileHash.cs b/tests/Parsec.Tests/FileHash.cs index 24b8a732..62136684 100644 --- a/tests/Parsec.Tests/FileHash.cs +++ b/tests/Parsec.Tests/FileHash.cs @@ -11,4 +11,9 @@ public static byte[] Checksum(string filePath) using var stream = File.OpenRead(filePath); return md5.ComputeHash(stream); } + + public static byte[] Checksum(byte[] buffer) + { + return MD5.HashData(buffer); + } } diff --git a/tests/Parsec.Tests/Parsec.Tests.csproj b/tests/Parsec.Tests/Parsec.Tests.csproj index 464ace53..8d7cc37a 100644 --- a/tests/Parsec.Tests/Parsec.Tests.csproj +++ b/tests/Parsec.Tests/Parsec.Tests.csproj @@ -710,5 +710,38 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + diff --git a/tests/Parsec.Tests/Shaiya/3DC/_3DCTests.cs b/tests/Parsec.Tests/Shaiya/3DC/_3DCTests.cs index 9d23b166..276ff741 100644 --- a/tests/Parsec.Tests/Shaiya/3DC/_3DCTests.cs +++ b/tests/Parsec.Tests/Shaiya/3DC/_3DCTests.cs @@ -8,7 +8,7 @@ public class _3DCTests public void _3DCReadWriteEP5Test() { const string filePath = "Shaiya/3DC/Mob_Fox_01.3DC"; - var obj = Reader.ReadFromFile(filePath); + var obj = ParsecReader.FromFile(filePath); // Check original EFT values Assert.Equal(32, obj.Bones.Count); @@ -19,7 +19,7 @@ public void _3DCReadWriteEP5Test() // Export to json const string jsonPath = "Shaiya/3DC/Mob_Fox_01.3DC.json"; obj.WriteJson(jsonPath); - var objFromJson = Reader.ReadFromJsonFile(jsonPath); + var objFromJson = ParsecReader.FromJsonFile(jsonPath); // Check fields Assert.Equal(obj.Bones.Count, objFromJson.Bones.Count); @@ -32,7 +32,7 @@ public void _3DCReadWriteEP5Test() const string newObjPath = "Shaiya/3DC/Mob_Fox_01.new.3DC"; objFromJson.Write(newObjPath); - var newObj = Reader.ReadFromFile(newObjPath); + var newObj = ParsecReader.FromFile(newObjPath); // Check fields Assert.Equal(obj.Bones.Count, newObj.Bones.Count); @@ -47,7 +47,7 @@ public void _3DCReadWriteEP5Test() public void _3DCReadWriteEP6Test() { const string filePath = "Shaiya/3DC/pet_maddog.3DC"; - var obj = Reader.ReadFromFile(filePath); + var obj = ParsecReader.FromFile(filePath); // Check original EFT values Assert.Equal(48, obj.Bones.Count); @@ -58,7 +58,7 @@ public void _3DCReadWriteEP6Test() // Export to json const string jsonPath = "Shaiya/3DC/pet_maddog.3DC.json"; obj.WriteJson(jsonPath); - var objFromJson = Reader.ReadFromJsonFile(jsonPath); + var objFromJson = ParsecReader.FromJsonFile(jsonPath); // Check fields Assert.Equal(obj.Bones.Count, objFromJson.Bones.Count); @@ -71,7 +71,7 @@ public void _3DCReadWriteEP6Test() const string newObjPath = "Shaiya/3DC/pet_maddog.new.3DC"; objFromJson.Write(newObjPath); - var newObj = Reader.ReadFromFile(newObjPath); + var newObj = ParsecReader.FromFile(newObjPath); // Check fields Assert.Equal(obj.Bones.Count, newObj.Bones.Count); @@ -101,16 +101,16 @@ public void _3DCMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/3DC/{fileName}.json"; string newObjPath = $"Shaiya/3DC/new_{fileName}"; - var obj = Reader.ReadFromFile(filePath); + var obj = ParsecReader.FromFile(filePath); obj.WriteJson(jsonPath); - var objFromJson = Reader.ReadFromJsonFile(jsonPath); + var objFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(obj.GetBytes(), objFromJson.GetBytes()); objFromJson.Write(newObjPath); - var newObj = Reader.ReadFromFile(newObjPath); + var newObj = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(obj.GetBytes(), newObj.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/3DE/_3DETests.cs b/tests/Parsec.Tests/Shaiya/3DE/_3DETests.cs index 1a5f7dd8..7c4ca94b 100644 --- a/tests/Parsec.Tests/Shaiya/3DE/_3DETests.cs +++ b/tests/Parsec.Tests/Shaiya/3DE/_3DETests.cs @@ -6,7 +6,7 @@ public class _3DETests public void _3DEReadWriteTest() { const string filePath = "Shaiya/3DE/waterfall04.3DE"; - var obj = Reader.ReadFromFile(filePath); + var obj = ParsecReader.FromFile(filePath); // Check original EFT values Assert.Equal(126, obj.Vertices.Count); @@ -17,7 +17,7 @@ public void _3DEReadWriteTest() // Export to json const string jsonPath = "Shaiya/3DE/waterfall04.3DE.json"; obj.WriteJson(jsonPath); - var objFromJson = Reader.ReadFromJsonFile(jsonPath); + var objFromJson = ParsecReader.FromJsonFile(jsonPath); // Check fields Assert.Equal(obj.Vertices.Count, objFromJson.Vertices.Count); @@ -31,7 +31,7 @@ public void _3DEReadWriteTest() const string newObjPath = "Shaiya/3DE/waterfall04.new.3DE"; objFromJson.Write(newObjPath); - var newObj = Reader.ReadFromFile(newObjPath); + var newObj = ParsecReader.FromFile(newObjPath); // Check fields Assert.Equal(obj.Vertices.Count, newObj.Vertices.Count); @@ -59,15 +59,15 @@ public void _3DEMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/3DE/{fileName}.json"; string newObjPath = $"Shaiya/3DE/new_{fileName}"; - var obj = Reader.ReadFromFile(filePath); + var obj = ParsecReader.FromFile(filePath); obj.WriteJson(jsonPath); - var objFromJson = Reader.ReadFromJsonFile(jsonPath); + var objFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(obj.GetBytes(), objFromJson.GetBytes()); objFromJson.Write(newObjPath); - var newObj = Reader.ReadFromFile(newObjPath); + var newObj = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(obj.GetBytes(), newObj.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/3DO/_3DOTests.cs b/tests/Parsec.Tests/Shaiya/3DO/_3DOTests.cs index 37ede5b1..6c9be803 100644 --- a/tests/Parsec.Tests/Shaiya/3DO/_3DOTests.cs +++ b/tests/Parsec.Tests/Shaiya/3DO/_3DOTests.cs @@ -8,7 +8,7 @@ public class _3DOTests public void _3DOReadWriteTest() { const string filePath = "Shaiya/3DO/F_34_a002.3DO"; - var obj = Reader.ReadFromFile(filePath); + var obj = ParsecReader.FromFile(filePath); // Check original EFT values Assert.Equal(1087, obj.Vertices.Count); @@ -18,7 +18,7 @@ public void _3DOReadWriteTest() // Export to json const string jsonPath = "Shaiya/3DO/F_34_a002.3DO.json"; obj.WriteJson(jsonPath); - var objFromJson = Reader.ReadFromJsonFile(jsonPath); + var objFromJson = ParsecReader.FromJsonFile(jsonPath); // Check fields Assert.Equal(obj.Vertices.Count, objFromJson.Vertices.Count); @@ -30,7 +30,7 @@ public void _3DOReadWriteTest() const string newObjPath = "Shaiya/3DO/F_34_a002.new.3DO"; objFromJson.Write(newObjPath); - var newObj = Reader.ReadFromFile(newObjPath); + var newObj = ParsecReader.FromFile(newObjPath); // Check fields Assert.Equal(obj.Vertices.Count, newObj.Vertices.Count); @@ -56,15 +56,15 @@ public void _3DOMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/3DO/{fileName}.json"; string newObjPath = $"Shaiya/3DO/new_{fileName}"; - var obj = Reader.ReadFromFile(filePath); + var obj = ParsecReader.FromFile(filePath); obj.WriteJson(jsonPath); - var objFromJson = Reader.ReadFromJsonFile(jsonPath); + var objFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(obj.GetBytes(), objFromJson.GetBytes()); objFromJson.Write(newObjPath); - var newObj = Reader.ReadFromFile(newObjPath); + var newObj = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(obj.GetBytes(), newObj.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/ALT/AltTests.cs b/tests/Parsec.Tests/Shaiya/ALT/AltTests.cs index a03c06f8..68e5ab4b 100644 --- a/tests/Parsec.Tests/Shaiya/ALT/AltTests.cs +++ b/tests/Parsec.Tests/Shaiya/ALT/AltTests.cs @@ -17,15 +17,15 @@ public void AltMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/ALT/{fileName}.json"; string newObjPath = $"Shaiya/ALT/new_{fileName}"; - var alt = Reader.ReadFromFile(filePath); + var alt = ParsecReader.FromFile(filePath); alt.WriteJson(jsonPath); - var altFromJson = Reader.ReadFromJsonFile(jsonPath); + var altFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(alt.GetBytes(), altFromJson.GetBytes()); altFromJson.Write(newObjPath); - var newAlt = Reader.ReadFromFile(newObjPath); + var newAlt = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(alt.GetBytes(), newAlt.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/ANI/AniTests.cs b/tests/Parsec.Tests/Shaiya/ANI/AniTests.cs index e958ca41..a4350a78 100644 --- a/tests/Parsec.Tests/Shaiya/ANI/AniTests.cs +++ b/tests/Parsec.Tests/Shaiya/ANI/AniTests.cs @@ -26,15 +26,15 @@ public void AniMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/ANI/{fileName}.json"; string newObjPath = $"Shaiya/ANI/new_{fileName}"; - var ani = Reader.ReadFromFile(filePath); + var ani = ParsecReader.FromFile(filePath); ani.WriteJson(jsonPath); - var aniFromJson = Reader.ReadFromJsonFile(jsonPath); + var aniFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(ani.GetBytes(), aniFromJson.GetBytes()); aniFromJson.Write(newObjPath); - var newANi = Reader.ReadFromFile(newObjPath); + var newANi = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(ani.GetBytes(), newANi.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/Cash/CashTests.cs b/tests/Parsec.Tests/Shaiya/Cash/CashTests.cs index 7fa01bfc..ee4fa261 100644 --- a/tests/Parsec.Tests/Shaiya/Cash/CashTests.cs +++ b/tests/Parsec.Tests/Shaiya/Cash/CashTests.cs @@ -1,4 +1,7 @@ -namespace Parsec.Tests.Shaiya.Cash; +using System.Linq; +using Parsec.Shaiya.Cash; + +namespace Parsec.Tests.Shaiya.Cash; public class CashTests { @@ -8,10 +11,34 @@ public void CashTest() const string filePath = "Shaiya/Cash/Cash.SData"; const string outputPath = "Shaiya/Cash/Cash_new.SData"; - var cash = Reader.ReadFromFile(filePath); + var cash = ParsecReader.FromFile(filePath); cash.Write(outputPath); - var newCash = Reader.ReadFromFile(outputPath); + var newCash = ParsecReader.FromFile(outputPath); Assert.Equal(cash.GetBytes(), newCash.GetBytes()); Assert.Equal(FileHash.Checksum(filePath), FileHash.Checksum(outputPath)); } + + [Fact] + public void DbItemSellTest() + { + const string filePath = "Shaiya/Cash/DBItemSellData.SData"; + const string outputPath = "Shaiya/Cash/output_DBItemSellData.SData"; + const string jsonPath = "Shaiya/Cash/DBItemSellData.SData.json"; + const string csvPath = "Shaiya/Cash/DBItemSellData.SData.csv"; + + var dbItemSell = ParsecReader.FromFile(filePath); + dbItemSell.Write(outputPath); + dbItemSell.WriteJson(jsonPath); + dbItemSell.WriteCsv(csvPath); + + var outputDbItemSell = ParsecReader.FromFile(outputPath); + var jsonDbItemSell = ParsecReader.FromJsonFile(jsonPath); + var csvItemSell = DBItemSellData.FromCsv(csvPath); + + var expected = dbItemSell.GetBytes().ToList(); + Assert.Equal(expected, outputDbItemSell.GetBytes()); + Assert.Equal(expected, jsonDbItemSell.GetBytes()); + // For the csv check, skip the 128-byte header, which gets lost in the process + Assert.Equal(expected.Skip(128), csvItemSell.GetBytes().Skip(128)); + } } diff --git a/tests/Parsec.Tests/Shaiya/Cash/DBItemSellData.SData b/tests/Parsec.Tests/Shaiya/Cash/DBItemSellData.SData new file mode 100644 index 00000000..c683c16e Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/Cash/DBItemSellData.SData differ diff --git a/tests/Parsec.Tests/Shaiya/Data/DataTests.cs b/tests/Parsec.Tests/Shaiya/Data/DataTests.cs index bc1a32d9..9e15a417 100644 --- a/tests/Parsec.Tests/Shaiya/Data/DataTests.cs +++ b/tests/Parsec.Tests/Shaiya/Data/DataTests.cs @@ -9,17 +9,6 @@ namespace Parsec.Tests.Shaiya.Data; public class DataTests { - [Fact] - [Description("Tests opening and reading the data file")] - public void DataReadingTest() - { - var dataFromSah = new Parsec.Shaiya.Data.Data("Shaiya/Data/sample.sah"); - var dataFromSaf = new Parsec.Shaiya.Data.Data("Shaiya/Data/sample.saf"); - - Assert.Equal(dataFromSah.FileIndex.Count, dataFromSaf.FileIndex.Count); - Assert.Equal(dataFromSah.DirectoryIndex.Count, dataFromSaf.DirectoryIndex.Count); - } - [Fact] [Description("Tests building both data.sah and data.saf from a directory")] public void DataBuildingTest() @@ -36,7 +25,7 @@ public void DataBuildingTest() [Description("Tests extracting a data file fully")] public void DataExtractionTest() { - var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/sample.sah"); + var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/sample.sah", "Shaiya/Data/sample.saf"); data.ExtractAll("Shaiya/Data/extracted"); foreach (var file in data.FileIndex.Values) @@ -47,7 +36,7 @@ public void DataExtractionTest() [Description("Tests extracting a single folder from a data file")] public void DataFolderExtractionTest() { - var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/sample.sah"); + var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/sample.sah", "Shaiya/Data/sample.saf"); const string extractionDirectory = "Shaiya/Data/single_folder_extraction"; var folder1 = data.RootDirectory.Directories.FirstOrDefault(); @@ -74,7 +63,7 @@ public void DataFolderExtractionTest() [Description("Tests extracting a single file from a data file")] public void DataFileExtractionTest() { - var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/sample.sah"); + var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/sample.sah", "Shaiya/Data/sample.saf"); const string extractionDirectory = "Shaiya/Data/single_file_extraction"; var file1 = data.FileIndex.Values.FirstOrDefault(); @@ -95,12 +84,12 @@ public void DataFileExtractionTest() public void DataPatchingTest() { // Load data - var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/target.sah"); + var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/target.sah", "Shaiya/Data/sample.saf"); var initialFiles = data.FileIndex.Keys.ToList(); // Load patches - var patch = new Parsec.Shaiya.Data.Data("Shaiya/Data/patch.sah"); - var patch2 = new Parsec.Shaiya.Data.Data("Shaiya/Data/patch2.sah"); + var patch = new Parsec.Shaiya.Data.Data("Shaiya/Data/patch.sah", "Shaiya/Data/patch.saf"); + var patch2 = new Parsec.Shaiya.Data.Data("Shaiya/Data/patch2.sah", "Shaiya/Data/patch2.saf"); var patchFiles = patch.FileIndex.Keys.Concat(patch2.FileIndex.Keys).ToList(); @@ -129,7 +118,7 @@ public void DataPatchingTest() [Description("Tests deleting files from a delete.lst list")] public void DataDeleteListTest() { - var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/delete.sah"); + var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/delete.sah", "Shaiya/Data/sample.saf"); var lstPath = "Shaiya/Data/delete.lst"; data.RemoveFilesFromLst(lstPath); diff --git a/tests/Parsec.Tests/Shaiya/Data/SafTests.cs b/tests/Parsec.Tests/Shaiya/Data/SafTests.cs index 01c4fc45..7b736b91 100644 --- a/tests/Parsec.Tests/Shaiya/Data/SafTests.cs +++ b/tests/Parsec.Tests/Shaiya/Data/SafTests.cs @@ -10,7 +10,7 @@ public class SafTests [InlineData(1000, 3000)] public void SafClearingTest(long offset, int length) { - var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/clearme.sah"); + var data = new Parsec.Shaiya.Data.Data("Shaiya/Data/clearme.sah", "Shaiya/Data/clearme.saf"); var nullData = new byte[length]; data.Saf.ClearBytes(offset, length); diff --git a/tests/Parsec.Tests/Shaiya/Data/SahTests.cs b/tests/Parsec.Tests/Shaiya/Data/SahTests.cs index dfce7e59..87c7819c 100644 --- a/tests/Parsec.Tests/Shaiya/Data/SahTests.cs +++ b/tests/Parsec.Tests/Shaiya/Data/SahTests.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using Parsec.Shaiya.Data; namespace Parsec.Tests.Shaiya; @@ -13,7 +12,7 @@ public class SahTests [InlineData(@"new_folder\sub1\sub2\sub3", "new_file.fl")] public void SahFileExistenceTest(string folderName, string fileName) { - var sah = Reader.ReadFromFile("Shaiya/Data/sample.sah"); + var sah = ParsecReader.FromFile("Shaiya/Data/sample.sah"); Assert.Equal("sah", sah.Extension); // Add folder to sah @@ -31,10 +30,6 @@ public void SahFileExistenceTest(string folderName, string fileName) var newSubfolder = sah.AddFolder($"{folderName}/sub"); Assert.True(newFolder.HasSubfolder("sub")); - - // Try to re-add file and folder - Assert.Throws(() => newFolder.AddFile(newFile1)); - Assert.Throws(() => newFolder.AddDirectory(newSubfolder)); } // [Fact] diff --git a/tests/Parsec.Tests/Shaiya/EFT/EFTTests.cs b/tests/Parsec.Tests/Shaiya/EFT/EFTTests.cs index 1f2abf91..1d0a14a5 100644 --- a/tests/Parsec.Tests/Shaiya/EFT/EFTTests.cs +++ b/tests/Parsec.Tests/Shaiya/EFT/EFTTests.cs @@ -6,10 +6,10 @@ public class EftTests public void EftReadWriteTest() { const string filePath = "Shaiya/EFT/Monster.EFT"; - var eft = Reader.ReadFromFile(filePath); + var eft = ParsecReader.FromFile(filePath); // Check original EFT values - Assert.Empty(eft.Objects); + Assert.Empty(eft.Meshes); Assert.Equal(111, eft.Textures.Count); Assert.Equal(294, eft.Effects.Count); Assert.Empty(eft.EffectSequences); @@ -17,10 +17,10 @@ public void EftReadWriteTest() // Export to json const string jsonPath = "Shaiya/EFT/Monster.EFT.json"; eft.WriteJson(jsonPath); - var eftFromJson = Reader.ReadFromJsonFile(jsonPath); + var eftFromJson = ParsecReader.FromJsonFile(jsonPath); // Check fields - Assert.Equal(eft.Objects.Count, eftFromJson.Objects.Count); + Assert.Equal(eft.Meshes.Count, eftFromJson.Meshes.Count); Assert.Equal(eft.Textures.Count, eftFromJson.Textures.Count); Assert.Equal(eft.Effects.Count, eftFromJson.Effects.Count); Assert.Equal(eft.EffectSequences.Count, eftFromJson.EffectSequences.Count); @@ -28,10 +28,10 @@ public void EftReadWriteTest() const string newEftPath = "Shaiya/EFT/Monster.new.EFT"; eftFromJson.Write(newEftPath); - var newEft = Reader.ReadFromFile(newEftPath); + var newEft = ParsecReader.FromFile(newEftPath); // Check fields - Assert.Equal(eft.Objects.Count, newEft.Objects.Count); + Assert.Equal(eft.Meshes.Count, newEft.Meshes.Count); Assert.Equal(eft.Textures.Count, newEft.Textures.Count); Assert.Equal(eft.Effects.Count, newEft.Effects.Count); Assert.Equal(eft.EffectSequences.Count, newEft.EffectSequences.Count); @@ -60,23 +60,23 @@ public void EftMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/EFT/{fileName}.json"; string newObjPath = $"Shaiya/EFT/new_{fileName}"; - var eft = Reader.ReadFromFile(filePath); + var eft = ParsecReader.FromFile(filePath); eft.Write(outputPath); eft.WriteJson(jsonPath); - var outputEft = Reader.ReadFromFile(outputPath); - var eftFromJson = Reader.ReadFromJsonFile(jsonPath); + var outputEft = ParsecReader.FromFile(outputPath); + var eftFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(eft.GetBytes(), outputEft.GetBytes()); // Assert.Equal(eft.GetBytes(), eftFromJson.GetBytes()); eftFromJson.Write(newObjPath); - var newEft = Reader.ReadFromFile(newObjPath); + var newEft = ParsecReader.FromFile(newObjPath); // Since EFTs use different encodings on texts, the EFT -> JSON -> EFT conversion will modify the strings, // so a checksum can't be done here, that's why only list lengths will be compared - Assert.Equal(eft.Objects.Count, newEft.Objects.Count); + Assert.Equal(eft.Meshes.Count, newEft.Meshes.Count); Assert.Equal(eft.Textures.Count, newEft.Textures.Count); Assert.Equal(eft.Effects.Count, newEft.Effects.Count); Assert.Equal(eft.EffectSequences.Count, newEft.EffectSequences.Count); diff --git a/tests/Parsec.Tests/Shaiya/GuildHouse/GuildHouseTests.cs b/tests/Parsec.Tests/Shaiya/GuildHouse/GuildHouseTests.cs index 86285190..59445e54 100644 --- a/tests/Parsec.Tests/Shaiya/GuildHouse/GuildHouseTests.cs +++ b/tests/Parsec.Tests/Shaiya/GuildHouse/GuildHouseTests.cs @@ -10,12 +10,12 @@ public void GuildHouseTest() const string filePath = "Shaiya/GuildHouse/GuildHouse.SData"; const string outputPath = "Shaiya/GuildHouse/GuildHouse.output.SData"; const string jsonPath = "Shaiya/GuildHouse/GuildHouse.SData.json"; - var guildHouse = Reader.ReadFromFile(filePath); + var guildHouse = ParsecReader.FromFile(filePath); guildHouse.Write(outputPath); guildHouse.WriteJson(jsonPath); - var outputGuildHouse = Reader.ReadFromFile(outputPath); - var jsonGuildHouse = Reader.ReadFromJsonFile(jsonPath); + var outputGuildHouse = ParsecReader.FromFile(outputPath); + var jsonGuildHouse = ParsecReader.FromJsonFile(jsonPath); var expected = guildHouse.GetBytes().ToList(); Assert.Equal(expected, outputGuildHouse.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/ITM/ItmTests.cs b/tests/Parsec.Tests/Shaiya/ITM/ItmTests.cs index 1e988bd6..32f86a6f 100644 --- a/tests/Parsec.Tests/Shaiya/ITM/ItmTests.cs +++ b/tests/Parsec.Tests/Shaiya/ITM/ItmTests.cs @@ -16,15 +16,15 @@ public void ItmMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/ITM/{fileName}.json"; string newObjPath = $"Shaiya/ITM/new_{fileName}"; - var itm = Reader.ReadFromFile(filePath); + var itm = ParsecReader.FromFile(filePath); itm.WriteJson(jsonPath); - var itmFromJson = Reader.ReadFromJsonFile(jsonPath); + var itmFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(itm.GetBytes(), itmFromJson.GetBytes()); itmFromJson.Write(newObjPath); - var newItm = Reader.ReadFromFile(newObjPath); + var newItm = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(itm.GetBytes(), newItm.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/Item/DBItemData.SData b/tests/Parsec.Tests/Shaiya/Item/DBItemData.SData new file mode 100644 index 00000000..6d9a85d1 Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/Item/DBItemData.SData differ diff --git a/tests/Parsec.Tests/Shaiya/Item/DBItemText_USA.SData b/tests/Parsec.Tests/Shaiya/Item/DBItemText_USA.SData new file mode 100644 index 00000000..16383d87 Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/Item/DBItemText_USA.SData differ diff --git a/tests/Parsec.Tests/Shaiya/Item/ItemEp6_1251.SData b/tests/Parsec.Tests/Shaiya/Item/ItemEp6_1251.SData new file mode 100644 index 00000000..a4160430 Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/Item/ItemEp6_1251.SData differ diff --git a/tests/Parsec.Tests/Shaiya/Item/ItemTests.cs b/tests/Parsec.Tests/Shaiya/Item/ItemTests.cs index 943f07cc..38337efb 100644 --- a/tests/Parsec.Tests/Shaiya/Item/ItemTests.cs +++ b/tests/Parsec.Tests/Shaiya/Item/ItemTests.cs @@ -1,4 +1,6 @@ -using Parsec.Common; +using System.Linq; +using Parsec.Common; +using Parsec.Shaiya.Item; namespace Parsec.Tests.Shaiya.Item; @@ -10,10 +12,11 @@ public void ItemEp5CsvTest() const string filePath = "Shaiya/Item/ItemEp5.SData"; const string csvPath = "Shaiya/Item/ItemEp5.SData.csv"; - var item = Reader.ReadFromFile(filePath, Episode.EP5); + var item = ParsecReader.FromFile(filePath); item.WriteCsv(csvPath); - var itemFromCsv = Parsec.Shaiya.Item.Item.ReadFromCsv(csvPath, Episode.EP5); - Assert.Equal(item.GetBytes(Episode.EP5), itemFromCsv.GetBytes(Episode.EP5)); + var itemFromCsv = Parsec.Shaiya.Item.Item.FromCsv(csvPath, Episode.EP5); + + Assert.Equal(item.GetBytes(), itemFromCsv.GetBytes()); } [Fact] @@ -22,9 +25,70 @@ public void ItemEp6CsvTest() const string filePath = "Shaiya/Item/ItemEp6.SData"; const string csvPath = "Shaiya/Item/ItemEp6.SData.csv"; - var item = Reader.ReadFromFile(filePath, Episode.EP6); + var item = ParsecReader.FromFile(filePath, Episode.EP6); item.WriteCsv(csvPath); - var itemFromCsv = Parsec.Shaiya.Item.Item.ReadFromCsv(csvPath, Episode.EP6); + var itemFromCsv = Parsec.Shaiya.Item.Item.FromCsv(csvPath, Episode.EP6); Assert.Equal(item.GetBytes(Episode.EP6), itemFromCsv.GetBytes(Episode.EP6)); } + + [Fact] + public void ItemEp6Csv_EncodingTest() + { + const string filePath = "Shaiya/Item/ItemEp6_1251.SData"; + const string csvPath = "Shaiya/Item/ItemEp6_1252.SData.csv"; + + var encoding = TestEncodings.Encoding1251; + var item = ParsecReader.FromFile(filePath, Episode.EP6, encoding); + item.WriteCsv(csvPath, encoding); + var itemFromCsv = Parsec.Shaiya.Item.Item.FromCsv(csvPath, Episode.EP6, encoding); + Assert.Equal(item.GetBytes(Episode.EP6), itemFromCsv.GetBytes(Episode.EP6)); + } + + [Fact] + public void DbItemTest() + { + const string filePath = "Shaiya/Item/DBItemData.SData"; + const string outputPath = "Shaiya/Item/output_DBItemData.SData"; + const string jsonPath = "Shaiya/Item/DBItemData.SData.json"; + const string csvPath = "Shaiya/Item/DBItemData.SData.csv"; + + var dbItem = ParsecReader.FromFile(filePath); + dbItem.Write(outputPath); + dbItem.WriteJson(jsonPath); + dbItem.WriteCsv(csvPath); + + var outputDbItem = ParsecReader.FromFile(outputPath); + var jsonDbItem = ParsecReader.FromJsonFile(jsonPath); + var csvItem = DBItemData.FromCsv(csvPath); + + var expected = dbItem.GetBytes().ToList(); + Assert.Equal(expected, outputDbItem.GetBytes()); + Assert.Equal(expected, jsonDbItem.GetBytes()); + // For the csv check, skip the 128-byte header, which gets lost in the process + Assert.Equal(expected.Skip(128), csvItem.GetBytes().Skip(128)); + } + + [Fact] + public void DbItemTextTest() + { + const string filePath = "Shaiya/Item/DBItemText_USA.SData"; + const string outputPath = "Shaiya/Item/output_DBItemText_USA.SData"; + const string jsonPath = "Shaiya/Item/DBItemText_USA.SData.json"; + const string csvPath = "Shaiya/Item/DBItemText_USA.SData.csv"; + + var itemText = ParsecReader.FromFile(filePath); + itemText.Write(outputPath); + itemText.WriteJson(jsonPath); + itemText.WriteCsv(csvPath); + + var outputItemText = ParsecReader.FromFile(outputPath); + var jsonItemText = ParsecReader.FromJsonFile(jsonPath); + var csvItemText = DBItemText.FromCsv(csvPath); + + var expected = itemText.GetBytes().ToList(); + Assert.Equal(expected, outputItemText.GetBytes()); + Assert.Equal(expected, jsonItemText.GetBytes()); + // For the csv check, skip the 128-byte header, which gets lost in the process + Assert.Equal(expected.Skip(128), csvItemText.GetBytes().Skip(128)); + } } diff --git a/tests/Parsec.Tests/Shaiya/KillStatus/KillStatusTests.cs b/tests/Parsec.Tests/Shaiya/KillStatus/KillStatusTests.cs index 5e055e29..f6d13498 100644 --- a/tests/Parsec.Tests/Shaiya/KillStatus/KillStatusTests.cs +++ b/tests/Parsec.Tests/Shaiya/KillStatus/KillStatusTests.cs @@ -10,12 +10,12 @@ public void KillStatusTest() const string filePath = "Shaiya/KillStatus/KillStatus.SData"; const string outputPath = "Shaiya/KillStatus/KillStatus.output.SData"; const string jsonPath = "Shaiya/KillStatus/KillStatus.SData.json"; - var obj = Reader.ReadFromFile(filePath); + var obj = ParsecReader.FromFile(filePath); obj.Write(outputPath); obj.WriteJson(jsonPath); - var outputObj = Reader.ReadFromFile(outputPath); - var jsonObj = Reader.ReadFromJsonFile(jsonPath); + var outputObj = ParsecReader.FromFile(outputPath); + var jsonObj = ParsecReader.FromJsonFile(jsonPath); var expected = obj.GetBytes().ToList(); Assert.Equal(expected, outputObj.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/MAni/MAniTests.cs b/tests/Parsec.Tests/Shaiya/MAni/MAniTests.cs index 0995ba21..b85c9bed 100644 --- a/tests/Parsec.Tests/Shaiya/MAni/MAniTests.cs +++ b/tests/Parsec.Tests/Shaiya/MAni/MAniTests.cs @@ -20,15 +20,15 @@ public void MAniMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/MAni/{fileName}.json"; string newObjPath = $"Shaiya/MAni/new_{fileName}"; - var mani = Reader.ReadFromFile(filePath); + var mani = ParsecReader.FromFile(filePath); mani.WriteJson(jsonPath); - var maniFromJson = Reader.ReadFromJsonFile(jsonPath); + var maniFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(mani.GetBytes(), maniFromJson.GetBytes()); maniFromJson.Write(newObjPath); - var newMani = Reader.ReadFromFile(newObjPath); + var newMani = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(mani.GetBytes(), newMani.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/MLT/MltTests.cs b/tests/Parsec.Tests/Shaiya/MLT/MltTests.cs index f7e9a0c5..2eaca7f2 100644 --- a/tests/Parsec.Tests/Shaiya/MLT/MltTests.cs +++ b/tests/Parsec.Tests/Shaiya/MLT/MltTests.cs @@ -23,19 +23,19 @@ public void MltMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/MLT/{fileName}.json"; string newObjPath = $"Shaiya/MLT/new_{fileName}"; - var mlt = Reader.ReadFromFile(filePath); + var mlt = ParsecReader.FromFile(filePath); mlt.Write(outputPath); mlt.WriteJson(jsonPath); - var outputMlt = Reader.ReadFromFile(outputPath); - var mltFromJson = Reader.ReadFromJsonFile(jsonPath); + var outputMlt = ParsecReader.FromFile(outputPath); + var mltFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(mlt.GetBytes(), outputMlt.GetBytes()); Assert.Equal(mlt.GetBytes(), mltFromJson.GetBytes()); mltFromJson.Write(newObjPath); - var newMlt = Reader.ReadFromFile(newObjPath); + var newMlt = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(mlt.GetBytes(), newMlt.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/MLX/MlxTests.cs b/tests/Parsec.Tests/Shaiya/MLX/MlxTests.cs index 18333f1f..2d1a5f62 100644 --- a/tests/Parsec.Tests/Shaiya/MLX/MlxTests.cs +++ b/tests/Parsec.Tests/Shaiya/MLX/MlxTests.cs @@ -26,19 +26,19 @@ public void MlxMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/MLX/{fileName}.json"; string newObjPath = $"Shaiya/MLX/new_{fileName}"; - var mlx = Reader.ReadFromFile(filePath); + var mlx = ParsecReader.FromFile(filePath); mlx.Write(outputPath); mlx.WriteJson(jsonPath); - var outputMlx = Reader.ReadFromFile(outputPath); - var mlxFromJson = Reader.ReadFromJsonFile(jsonPath); + var outputMlx = ParsecReader.FromFile(outputPath); + var mlxFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(mlx.GetBytes(), outputMlx.GetBytes()); Assert.Equal(mlx.GetBytes(), mlxFromJson.GetBytes()); mlxFromJson.Write(newObjPath); - var newMlx = Reader.ReadFromFile(newObjPath); + var newMlx = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(mlx.GetBytes(), newMlx.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/MON/MonTests.cs b/tests/Parsec.Tests/Shaiya/MON/MonTests.cs index 84e01cb3..d8539aab 100644 --- a/tests/Parsec.Tests/Shaiya/MON/MonTests.cs +++ b/tests/Parsec.Tests/Shaiya/MON/MonTests.cs @@ -17,20 +17,20 @@ public void MonMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/MON/{fileName}.json"; string newObjPath = $"Shaiya/MON/new_{fileName}"; - var mon = Reader.ReadFromFile(filePath); + var mon = ParsecReader.FromFile(filePath); mon.Write(outputPath); mon.WriteJson(jsonPath); - var outputMon = Reader.ReadFromFile(outputPath); - var monFromJson = Reader.ReadFromJsonFile(jsonPath); + var outputMon = ParsecReader.FromFile(outputPath); + var monFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(mon.GetBytes(), outputMon.GetBytes()); Assert.Equal(mon.GetBytes(), monFromJson.GetBytes()); monFromJson.Write(newObjPath); - var newMon = Reader.ReadFromFile(newObjPath); + var newMon = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(mon.GetBytes(), newMon.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/Monster/DBMonsterData.SData b/tests/Parsec.Tests/Shaiya/Monster/DBMonsterData.SData new file mode 100644 index 00000000..462587d5 Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/Monster/DBMonsterData.SData differ diff --git a/tests/Parsec.Tests/Shaiya/Monster/DBMonsterText_USA.SData b/tests/Parsec.Tests/Shaiya/Monster/DBMonsterText_USA.SData new file mode 100644 index 00000000..65a2190b Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/Monster/DBMonsterText_USA.SData differ diff --git a/tests/Parsec.Tests/Shaiya/Monster/MonsterTests.cs b/tests/Parsec.Tests/Shaiya/Monster/MonsterTests.cs index 1c5afe69..9acccc4a 100644 --- a/tests/Parsec.Tests/Shaiya/Monster/MonsterTests.cs +++ b/tests/Parsec.Tests/Shaiya/Monster/MonsterTests.cs @@ -1,4 +1,7 @@ -namespace Parsec.Tests.Shaiya.Monster; +using System.Linq; +using Parsec.Shaiya.Monster; + +namespace Parsec.Tests.Shaiya.Monster; public class MonsterTests { @@ -8,9 +11,9 @@ public void MonsterCsvTest() const string filePath = "Shaiya/Monster/Monster.SData"; const string csvPath = "Shaiya/Monster/Monster.SData.csv"; - var monster = Reader.ReadFromFile(filePath); + var monster = ParsecReader.FromFile(filePath); monster.WriteCsv(csvPath); - var monsterFromCsv = Parsec.Shaiya.Monster.Monster.ReadFromCsv(csvPath); + var monsterFromCsv = Parsec.Shaiya.Monster.Monster.FromCsv(csvPath); Assert.Equal(monster.GetBytes(), monsterFromCsv.GetBytes()); } @@ -22,9 +25,57 @@ public void MonsterCsv_EncodingTest() var encoding = TestEncodings.Encoding1252; - var monster = Reader.ReadFromFile(filePath, encoding: encoding); + var monster = ParsecReader.FromFile(filePath, encoding: encoding); monster.WriteCsv(csvPath, encoding); - var monsterFromCsv = Parsec.Shaiya.Monster.Monster.ReadFromCsv(csvPath, encoding); + var monsterFromCsv = Parsec.Shaiya.Monster.Monster.FromCsv(csvPath, encoding); Assert.Equal(monster.GetBytes(), monsterFromCsv.GetBytes()); } + + [Fact] + public void DbMonsterTest() + { + const string filePath = "Shaiya/Monster/DBMonsterData.SData"; + const string outputPath = "Shaiya/Monster/output_DBMonsterData.SData"; + const string jsonPath = "Shaiya/Monster/DBMonsterData.SData.json"; + const string csvPath = "Shaiya/Monster/DBMonsterData.SData.csv"; + + var dbMonster = ParsecReader.FromFile(filePath); + dbMonster.Write(outputPath); + dbMonster.WriteJson(jsonPath); + dbMonster.WriteCsv(csvPath); + + var outputDbMonster = ParsecReader.FromFile(outputPath); + var jsonDbMonster = ParsecReader.FromJsonFile(jsonPath); + var csvMonster = DBMonsterData.FromCsv(csvPath); + + var expected = dbMonster.GetBytes().ToList(); + Assert.Equal(expected, outputDbMonster.GetBytes()); + Assert.Equal(expected, jsonDbMonster.GetBytes()); + // For the csv check, skip the 128-byte header, which gets lost in the process + Assert.Equal(expected.Skip(128), csvMonster.GetBytes().Skip(128)); + } + + [Fact] + public void DbMonsterTextTest() + { + const string filePath = "Shaiya/Monster/DBMonsterText_USA.SData"; + const string outputPath = "Shaiya/Monster/output_DBMonsterText_USA.SData"; + const string jsonPath = "Shaiya/Monster/DBMonsterText_USA.SData.json"; + const string csvPath = "Shaiya/Monster/DBMonsterText_USA.SData.csv"; + + var monsterText = ParsecReader.FromFile(filePath); + monsterText.Write(outputPath); + monsterText.WriteJson(jsonPath); + monsterText.WriteCsv(csvPath); + + var outputMonsterText = ParsecReader.FromFile(outputPath); + var jsonMonsterText = ParsecReader.FromJsonFile(jsonPath); + var csvMonsterText = DBMonsterText.FromCsv(csvPath); + + var expected = monsterText.GetBytes().ToList(); + Assert.Equal(expected, outputMonsterText.GetBytes()); + Assert.Equal(expected, jsonMonsterText.GetBytes()); + // For the csv check, skip the 128-byte header, which gets lost in the process + Assert.Equal(expected.Skip(128), csvMonsterText.GetBytes().Skip(128)); + } } diff --git a/tests/Parsec.Tests/Shaiya/NpcQuest/NpcQuestTests.cs b/tests/Parsec.Tests/Shaiya/NpcQuest/NpcQuestTests.cs index 0a132610..84cf9c50 100644 --- a/tests/Parsec.Tests/Shaiya/NpcQuest/NpcQuestTests.cs +++ b/tests/Parsec.Tests/Shaiya/NpcQuest/NpcQuestTests.cs @@ -1,5 +1,6 @@ using System.Linq; using Parsec.Common; +using Parsec.Shaiya.NpcQuest; namespace Parsec.Tests.Shaiya.NpcQuest; @@ -11,12 +12,12 @@ public void NpcQuestEp5Test() const string filePath = "Shaiya/NpcQuest/NpcQuestEp5.SData"; const string outputPath = "Shaiya/NpcQuest/NpcQuestEp5.output.SData"; const string jsonPath = "Shaiya/NpcQuest/NpcQuestEp5.SData.json"; - var npcQuest = Reader.ReadFromFile(filePath, Episode.EP5); + var npcQuest = ParsecReader.FromFile(filePath); npcQuest.Write(outputPath, Episode.EP5); npcQuest.WriteJson(jsonPath); - var outputNpcQuest = Reader.ReadFromFile(outputPath, Episode.EP5); - var jsonNpcQuest = Reader.ReadFromJsonFile(jsonPath); + var outputNpcQuest = ParsecReader.FromFile(outputPath); + var jsonNpcQuest = ParsecReader.FromJsonFile(jsonPath); var expected = npcQuest.GetBytes().ToList(); Assert.Equal(expected, outputNpcQuest.GetBytes()); @@ -29,12 +30,12 @@ public void NpcQuestEp6Test() const string filePath = "Shaiya/NpcQuest/NpcQuestEp6.SData"; const string outputPath = "Shaiya/NpcQuest/NpcQuestEp6.output.SData"; const string jsonPath = "Shaiya/NpcQuest/NpcQuestEp6.SData.json"; - var npcQuest = Reader.ReadFromFile(filePath, Episode.EP6); + var npcQuest = ParsecReader.FromFile(filePath, Episode.EP6); npcQuest.Write(outputPath, Episode.EP6); npcQuest.WriteJson(jsonPath); - var outputNpcQuest = Reader.ReadFromFile(outputPath, Episode.EP6); - var jsonNpcQuest = Reader.ReadFromJsonFile(jsonPath); + var outputNpcQuest = ParsecReader.FromFile(outputPath, Episode.EP6); + var jsonNpcQuest = ParsecReader.FromJsonFile(jsonPath); var expected = npcQuest.GetBytes().ToList(); Assert.Equal(expected, outputNpcQuest.GetBytes()); @@ -47,12 +48,30 @@ public void NpcQuestEp8Test() const string filePath = "Shaiya/NpcQuest/NpcQuestEp8.SData"; const string outputPath = "Shaiya/NpcQuest/NpcQuestEp8.output.SData"; const string jsonPath = "Shaiya/NpcQuest/NpcQuestEp8.SData.json"; - var npcQuest = Reader.ReadFromFile(filePath, Episode.EP8); + var npcQuest = ParsecReader.FromFile(filePath, Episode.EP8); npcQuest.Write(outputPath, Episode.EP8); npcQuest.WriteJson(jsonPath); - var outputNpcQuest = Reader.ReadFromFile(outputPath, Episode.EP8); - var jsonNpcQuest = Reader.ReadFromJsonFile(jsonPath); + var outputNpcQuest = ParsecReader.FromFile(outputPath, Episode.EP8); + var jsonNpcQuest = ParsecReader.FromJsonFile(jsonPath); + + var expected = npcQuest.GetBytes().ToList(); + Assert.Equal(expected, outputNpcQuest.GetBytes()); + Assert.Equal(expected, jsonNpcQuest.GetBytes()); + } + + [Fact] + public void NpcQuestTranslationTest() + { + const string filePath = "Shaiya/NpcQuest/NpcQuestTrans_USA.SData"; + const string outputPath = "Shaiya/NpcQuest/NpcQuestTrans_USA.output.SData"; + const string jsonPath = "Shaiya/NpcQuest/NpcQuestTrans_USA.SData.json"; + var npcQuest = ParsecReader.FromFile(filePath); + npcQuest.Write(outputPath); + npcQuest.WriteJson(jsonPath); + + var outputNpcQuest = ParsecReader.FromFile(outputPath); + var jsonNpcQuest = ParsecReader.FromJsonFile(jsonPath); var expected = npcQuest.GetBytes().ToList(); Assert.Equal(expected, outputNpcQuest.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/NpcQuest/NpcQuestTrans_USA.SData b/tests/Parsec.Tests/Shaiya/NpcQuest/NpcQuestTrans_USA.SData new file mode 100644 index 00000000..fe5beb6d Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/NpcQuest/NpcQuestTrans_USA.SData differ diff --git a/tests/Parsec.Tests/Shaiya/NpcSkill/NpcSkillTests.cs b/tests/Parsec.Tests/Shaiya/NpcSkill/NpcSkillTests.cs index 35161a98..d690f23b 100644 --- a/tests/Parsec.Tests/Shaiya/NpcSkill/NpcSkillTests.cs +++ b/tests/Parsec.Tests/Shaiya/NpcSkill/NpcSkillTests.cs @@ -16,14 +16,14 @@ public void NpcSkillTest(string fileName, Episode episode) string jsonPath = $"Shaiya/NpcSkill/{fileName}.SData.json"; string csvPath = $"Shaiya/NpcSkill/{fileName}.SData.csv"; - var npcSkill = Reader.ReadFromFile(filePath, episode); + var npcSkill = ParsecReader.FromFile(filePath, episode); npcSkill.Write(outputPath, episode); npcSkill.WriteJson(jsonPath); npcSkill.WriteCsv(csvPath); - var outputSkill = Reader.ReadFromFile(outputPath, episode); - var jsonSkill = Reader.ReadFromJsonFile(jsonPath); - var csvSkill = Parsec.Shaiya.Skill.Skill.ReadFromCsv(csvPath, episode); + var outputSkill = ParsecReader.FromFile(outputPath, episode); + var jsonSkill = ParsecReader.FromJsonFile(jsonPath); + var csvSkill = Parsec.Shaiya.Skill.Skill.FromCsv(csvPath, episode); var expected = npcSkill.GetBytes().ToList(); Assert.Equal(expected, outputSkill.GetBytes()); @@ -32,21 +32,21 @@ public void NpcSkillTest(string fileName, Episode episode) } [Fact] - public void DbSkillTest() + public void DbNpcSkillTest() { const string filePath = "Shaiya/NpcSkill/DBNpcSkillData.SData"; const string outputPath = "Shaiya/NpcSkill/output_DBNpcSkillData.SData"; const string jsonPath = "Shaiya/NpcSkill/DBNpcSkillData.SData.json"; const string csvPath = "Shaiya/NpcSkill/DBNpcSkillData.SData.csv"; - var npcSkill = Reader.ReadFromFile(filePath); + var npcSkill = ParsecReader.FromFile(filePath); npcSkill.Write(outputPath); npcSkill.WriteJson(jsonPath); npcSkill.WriteCsv(csvPath); - var outputNpcSkill = Reader.ReadFromFile(outputPath); - var jsonNpcSkill = Reader.ReadFromJsonFile(jsonPath); - var csvNpcSkill = DBNpcSkillData.ReadFromCsv(csvPath); + var outputNpcSkill = ParsecReader.FromFile(outputPath); + var jsonNpcSkill = ParsecReader.FromJsonFile(jsonPath); + var csvNpcSkill = DBNpcSkillData.FromCsv(csvPath); var expected = npcSkill.GetBytes().ToList(); Assert.Equal(expected, outputNpcSkill.GetBytes()); @@ -56,21 +56,21 @@ public void DbSkillTest() } [Fact] - public void DbSkillTextTest() + public void DbNpcSkillTextTest() { const string filePath = "Shaiya/NpcSkill/DBNpcSkillText_USA.SData"; const string outputPath = "Shaiya/NpcSkill/output_DBNpcSkillText_USA.SData"; const string jsonPath = "Shaiya/NpcSkill/DBNpcSkillText_USA.SData.json"; const string csvPath = "Shaiya/NpcSkill/DBNpcSkillText_USA.SData.csv"; - var npcSkillText = Reader.ReadFromFile(filePath); + var npcSkillText = ParsecReader.FromFile(filePath); npcSkillText.Write(outputPath); npcSkillText.WriteJson(jsonPath); npcSkillText.WriteCsv(csvPath); - var outputNpcSkillText = Reader.ReadFromFile(outputPath); - var jsonNpcSkillText = Reader.ReadFromJsonFile(jsonPath); - var csvNpcSkillText = DBNpcSkillText.ReadFromCsv(csvPath); + var outputNpcSkillText = ParsecReader.FromFile(outputPath); + var jsonNpcSkillText = ParsecReader.FromJsonFile(jsonPath); + var csvNpcSkillText = DBNpcSkillText.FromCsv(csvPath); var expected = npcSkillText.GetBytes().ToList(); Assert.Equal(expected, outputNpcSkillText.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/SData/BinarySDataTests.cs b/tests/Parsec.Tests/Shaiya/SData/BinarySDataTests.cs index 7f3bc813..d3d15421 100644 --- a/tests/Parsec.Tests/Shaiya/SData/BinarySDataTests.cs +++ b/tests/Parsec.Tests/Shaiya/SData/BinarySDataTests.cs @@ -14,14 +14,14 @@ public void BinarySDataCsvTest() const string csvDecryptedOutputPath = "Shaiya/SData/DBItemData.csv.dec.SData"; const string csvEncryptedOutputPath = "Shaiya/SData/DBItemData.csv.enc.SData"; - var itemData = Reader.ReadFromFile(filePath); + var itemData = ParsecReader.FromFile(filePath); // Clear header so that only the field names and data are checked itemData.Header = new byte[128]; itemData.WriteCsv(csvPath); itemData.WriteEncrypted(encryptedOutputPath); itemData.WriteDecrypted(decryptedOutputPath); - var itemDataFromCsv = DBItemData.ReadFromCsv(csvPath); + var itemDataFromCsv = DBItemData.FromCsv(csvPath); itemDataFromCsv.WriteEncrypted(csvEncryptedOutputPath); itemDataFromCsv.WriteDecrypted(csvDecryptedOutputPath); diff --git a/tests/Parsec.Tests/Shaiya/SData/SDataTests.cs b/tests/Parsec.Tests/Shaiya/SData/SDataTests.cs index d2b7d639..e2cdf544 100644 --- a/tests/Parsec.Tests/Shaiya/SData/SDataTests.cs +++ b/tests/Parsec.Tests/Shaiya/SData/SDataTests.cs @@ -26,6 +26,9 @@ public void SDataBufferEncryptionTest() Assert.True(Parsec.Shaiya.SData.SData.IsEncrypted(encryptedFileBuffer)); Assert.False(Parsec.Shaiya.SData.SData.IsEncrypted(decryptedBuffer)); + + Assert.Equal(FileHash.Checksum(decryptedBuffer), FileHash.Checksum(decryptedFileBuffer)); + Assert.Equal(FileHash.Checksum(encryptedFileBuffer), FileHash.Checksum(encryptedFileBuffer)); } [Fact] @@ -33,20 +36,21 @@ public void SDataBufferEncryptionTest() public void SDataFileEncryptionTest() { // Encrypt file - const string encryptedOutputPath = "Shaiya/SData/ItemEp6.encrypted.SData"; - Parsec.Shaiya.SData.SData.EncryptFile(DecryptedSDataFilePath, encryptedOutputPath); + const string outputEncryptedFilePath = "Shaiya/SData/ItemEp6.encrypted.SData"; + Parsec.Shaiya.SData.SData.EncryptFile(DecryptedSDataFilePath, outputEncryptedFilePath); - var encryptedBuffer = File.ReadAllBytes(encryptedOutputPath); + var encryptedBuffer = File.ReadAllBytes(outputEncryptedFilePath); Assert.True(Parsec.Shaiya.SData.SData.IsEncrypted(encryptedBuffer)); // Decrypt file - const string decryptedOutputPath = "Shaiya/SData/ItemEp6.decrypted.SData"; - Parsec.Shaiya.SData.SData.DecryptFile(EncryptedSDataFilePath, decryptedOutputPath); + const string outputDecryptedFilePath = "Shaiya/SData/ItemEp6.decrypted.SData"; + Parsec.Shaiya.SData.SData.DecryptFile(EncryptedSDataFilePath, outputDecryptedFilePath); - var decryptedBuffer = File.ReadAllBytes(decryptedOutputPath); + var decryptedBuffer = File.ReadAllBytes(outputDecryptedFilePath); Assert.False(Parsec.Shaiya.SData.SData.IsEncrypted(decryptedBuffer)); - Assert.Equal(FileHash.Checksum(EncryptedSDataFilePath), FileHash.Checksum(encryptedOutputPath)); + Assert.Equal(FileHash.Checksum(DecryptedSDataFilePath), FileHash.Checksum(outputDecryptedFilePath)); + Assert.Equal(FileHash.Checksum(EncryptedSDataFilePath), FileHash.Checksum(outputEncryptedFilePath)); } [Fact] @@ -56,7 +60,7 @@ public void SDataDecryptionTest() const string encryptedOutputPath = "Shaiya/SData/ItemEp6.encrypted.SData"; const string decryptedOutputPath = "Shaiya/SData/ItemEp6.decrypted.SData"; - var item = Reader.ReadFromFile(EncryptedSDataFilePath, Episode.EP6); + var item = ParsecReader.FromFile(EncryptedSDataFilePath, Episode.EP6); item.WriteEncrypted(encryptedOutputPath); item.WriteDecrypted(decryptedOutputPath); diff --git a/tests/Parsec.Tests/Shaiya/SMOD/SmodTests.cs b/tests/Parsec.Tests/Shaiya/SMOD/SmodTests.cs index b0cf31a7..e90f71b3 100644 --- a/tests/Parsec.Tests/Shaiya/SMOD/SmodTests.cs +++ b/tests/Parsec.Tests/Shaiya/SMOD/SmodTests.cs @@ -6,7 +6,7 @@ public class SmodTests public void SmodReadWriteTest() { const string filePath = "Shaiya/SMOD/a2_Elf_House_01.SMOD"; - var smod = Reader.ReadFromFile(filePath); + var smod = ParsecReader.FromFile(filePath); // Check original EFT values Assert.Single(smod.CollisionObjects); @@ -15,7 +15,7 @@ public void SmodReadWriteTest() // Export to json const string jsonPath = "Shaiya/SMOD/a2_Elf_House_01.SMOD.json"; smod.WriteJson(jsonPath); - var smodFromJson = Reader.ReadFromJsonFile(jsonPath); + var smodFromJson = ParsecReader.FromJsonFile(jsonPath); // Check fields Assert.Equal(smod.CollisionObjects.Count, smodFromJson.CollisionObjects.Count); @@ -27,7 +27,7 @@ public void SmodReadWriteTest() const string newObjPath = "Shaiya/SMOD/a2_Elf_House_01.new.SMOD"; smodFromJson.Write(newObjPath); - var newSmod = Reader.ReadFromFile(newObjPath); + var newSmod = ParsecReader.FromFile(newObjPath); // Check fields Assert.Equal(smod.CollisionObjects.Count, newSmod.CollisionObjects.Count); @@ -54,15 +54,15 @@ public void SmodMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/SMOD/{fileName}.json"; string newObjPath = $"Shaiya/SMOD/new_{fileName}"; - var smod = Reader.ReadFromFile(filePath); + var smod = ParsecReader.FromFile(filePath); smod.WriteJson(jsonPath); - var smodFromJson = Reader.ReadFromJsonFile(jsonPath); + var smodFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(smod.GetBytes(), smodFromJson.GetBytes()); smodFromJson.Write(newObjPath); - var newSmod = Reader.ReadFromFile(newObjPath); + var newSmod = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(smod.GetBytes(), newSmod.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/Seff/SeffTests.cs b/tests/Parsec.Tests/Shaiya/Seff/SeffTests.cs index 21ebc6ae..646e91bc 100644 --- a/tests/Parsec.Tests/Shaiya/Seff/SeffTests.cs +++ b/tests/Parsec.Tests/Shaiya/Seff/SeffTests.cs @@ -15,19 +15,19 @@ public void SeffMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/Seff/{fileName}.json"; string newFilePath = $"Shaiya/Seff/new_{fileName}"; - var seff = Reader.ReadFromFile(filePath); + var seff = ParsecReader.FromFile(filePath); seff.Write(outputPath); seff.WriteJson(jsonPath); - var outputSeff = Reader.ReadFromFile(outputPath); - var seffFromJson = Reader.ReadFromJsonFile(jsonPath); + var outputSeff = ParsecReader.FromFile(outputPath); + var seffFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(seff.GetBytes(), outputSeff.GetBytes()); Assert.Equal(seff.GetBytes(), seffFromJson.GetBytes()); seffFromJson.Write(newFilePath); - var newSeff = Reader.ReadFromFile(newFilePath); + var newSeff = ParsecReader.FromFile(newFilePath); // Check bytes Assert.Equal(seff.GetBytes(), newSeff.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/SetItem/DBSetItemData.SData b/tests/Parsec.Tests/Shaiya/SetItem/DBSetItemData.SData new file mode 100644 index 00000000..b3f46027 Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/SetItem/DBSetItemData.SData differ diff --git a/tests/Parsec.Tests/Shaiya/SetItem/DBSetItemText_USA.SData b/tests/Parsec.Tests/Shaiya/SetItem/DBSetItemText_USA.SData new file mode 100644 index 00000000..873995d4 Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/SetItem/DBSetItemText_USA.SData differ diff --git a/tests/Parsec.Tests/Shaiya/SetItem/SetItemTests.cs b/tests/Parsec.Tests/Shaiya/SetItem/SetItemTests.cs index db0b228f..c8c140f5 100644 --- a/tests/Parsec.Tests/Shaiya/SetItem/SetItemTests.cs +++ b/tests/Parsec.Tests/Shaiya/SetItem/SetItemTests.cs @@ -1,4 +1,7 @@ -namespace Parsec.Tests.Shaiya.SetItem; +using System.Linq; +using Parsec.Shaiya.SetItem; + +namespace Parsec.Tests.Shaiya.SetItem; public class SetItemTests { @@ -11,21 +14,69 @@ public void SetItemReadWriteTest() const string newFilePath = "Shaiya/SetItem/new_SetItem.SData"; Parsec.Shaiya.SData.SData.DecryptFile(filePath, filePath); - var setItem = Reader.ReadFromFile(filePath); + var setItem = ParsecReader.FromFile(filePath); setItem.Write(outputPath); setItem.WriteJson(jsonPath); - var outputSetItem = Reader.ReadFromFile(outputPath); - var setItemFromJson = Reader.ReadFromJsonFile(jsonPath); + var outputSetItem = ParsecReader.FromFile(outputPath); + var setItemFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(setItem.GetBytes(), outputSetItem.GetBytes()); Assert.Equal(setItem.GetBytes(), setItemFromJson.GetBytes()); setItemFromJson.Write(newFilePath); - var newSeff = Reader.ReadFromFile(newFilePath); + var newSeff = ParsecReader.FromFile(newFilePath); // Check bytes Assert.Equal(setItem.GetBytes(), newSeff.GetBytes()); } + + [Fact] + public void DbSetItemTest() + { + const string filePath = "Shaiya/SetItem/DBSetItemData.SData"; + const string outputPath = "Shaiya/SetItem/output_DBSetItemData.SData"; + const string jsonPath = "Shaiya/SetItem/DBSetItemData.SData.json"; + const string csvPath = "Shaiya/SetItem/DBSetItemData.SData.csv"; + + var dbSetItem = ParsecReader.FromFile(filePath); + dbSetItem.Write(outputPath); + dbSetItem.WriteJson(jsonPath); + dbSetItem.WriteCsv(csvPath); + + var outputDbSetItem = ParsecReader.FromFile(outputPath); + var jsonDbSetItem = ParsecReader.FromJsonFile(jsonPath); + var csvSetItem = DBSetItemData.FromCsv(csvPath); + + var expected = dbSetItem.GetBytes().ToList(); + Assert.Equal(expected, outputDbSetItem.GetBytes()); + Assert.Equal(expected, jsonDbSetItem.GetBytes()); + // For the csv check, skip the 128-byte header, which gets lost in the process + Assert.Equal(expected.Skip(128), csvSetItem.GetBytes().Skip(128)); + } + + [Fact] + public void DbSetItemTextTest() + { + const string filePath = "Shaiya/SetItem/DBSetItemText_USA.SData"; + const string outputPath = "Shaiya/SetItem/output_DBSetItemText_USA.SData"; + const string jsonPath = "Shaiya/SetItem/DBSetItemText_USA.SData.json"; + const string csvPath = "Shaiya/SetItem/DBSetItemText_USA.SData.csv"; + + var setItemText = ParsecReader.FromFile(filePath); + setItemText.Write(outputPath); + setItemText.WriteJson(jsonPath); + setItemText.WriteCsv(csvPath); + + var outputSetItemText = ParsecReader.FromFile(outputPath); + var jsonSetItemText = ParsecReader.FromJsonFile(jsonPath); + var csvSetItemText = DBSetItemText.FromCsv(csvPath); + + var expected = setItemText.GetBytes().ToList(); + Assert.Equal(expected, outputSetItemText.GetBytes()); + Assert.Equal(expected, jsonSetItemText.GetBytes()); + // For the csv check, skip the 128-byte header, which gets lost in the process + Assert.Equal(expected.Skip(128), csvSetItemText.GetBytes().Skip(128)); + } } diff --git a/tests/Parsec.Tests/Shaiya/Skill/SkillTests.cs b/tests/Parsec.Tests/Shaiya/Skill/SkillTests.cs index 5dd1561f..46cf6040 100644 --- a/tests/Parsec.Tests/Shaiya/Skill/SkillTests.cs +++ b/tests/Parsec.Tests/Shaiya/Skill/SkillTests.cs @@ -11,19 +11,19 @@ public class SkillTests [InlineData("SkillEp6", Episode.EP6)] public void SkillTest(string fileName, Episode episode) { - string filePath = $"Shaiya/Skill/{fileName}.SData"; - string outputPath = $"Shaiya/Skill/output_{fileName}.SData"; - string jsonPath = $"Shaiya/Skill/{fileName}.SData.json"; - string csvPath = $"Shaiya/Skill/{fileName}.SData.csv"; + var filePath = $"Shaiya/Skill/{fileName}.SData"; + var outputPath = $"Shaiya/Skill/output_{fileName}.SData"; + var jsonPath = $"Shaiya/Skill/{fileName}.SData.json"; + var csvPath = $"Shaiya/Skill/{fileName}.SData.csv"; - var skill = Reader.ReadFromFile(filePath, episode); + var skill = ParsecReader.FromFile(filePath, episode); skill.Write(outputPath, episode); skill.WriteJson(jsonPath); skill.WriteCsv(csvPath); - var outputSkill = Reader.ReadFromFile(outputPath, episode); - var jsonSkill = Reader.ReadFromJsonFile(jsonPath); - var csvSkill = Parsec.Shaiya.Skill.Skill.ReadFromCsv(csvPath, episode); + var outputSkill = ParsecReader.FromFile(outputPath, episode); + var jsonSkill = ParsecReader.FromJsonFile(jsonPath); + var csvSkill = Parsec.Shaiya.Skill.Skill.FromCsv(csvPath, episode); var expected = skill.GetBytes().ToList(); Assert.Equal(expected, outputSkill.GetBytes()); @@ -39,14 +39,14 @@ public void DbSkillTest() const string jsonPath = "Shaiya/Skill/DBSkillData.SData.json"; const string csvPath = "Shaiya/Skill/DBSkillData.SData.csv"; - var dbSkill = Reader.ReadFromFile(filePath); + var dbSkill = ParsecReader.FromFile(filePath); dbSkill.Write(outputPath); dbSkill.WriteJson(jsonPath); dbSkill.WriteCsv(csvPath); - var outputDbSkill = Reader.ReadFromFile(outputPath); - var jsonDbSkill = Reader.ReadFromJsonFile(jsonPath); - var csvSkill = DBSkillData.ReadFromCsv(csvPath); + var outputDbSkill = ParsecReader.FromFile(outputPath); + var jsonDbSkill = ParsecReader.FromJsonFile(jsonPath); + var csvSkill = DBSkillData.FromCsv(csvPath); var expected = dbSkill.GetBytes().ToList(); Assert.Equal(expected, outputDbSkill.GetBytes()); @@ -63,14 +63,14 @@ public void DbSkillTextTest() const string jsonPath = "Shaiya/Skill/DBSkillText_USA.SData.json"; const string csvPath = "Shaiya/Skill/DBSkillText_USA.SData.csv"; - var skillText = Reader.ReadFromFile(filePath); + var skillText = ParsecReader.FromFile(filePath); skillText.Write(outputPath); skillText.WriteJson(jsonPath); skillText.WriteCsv(csvPath); - var outputSkillText = Reader.ReadFromFile(outputPath); - var jsonSkillText = Reader.ReadFromJsonFile(jsonPath); - var csvSkillText = DBSkillText.ReadFromCsv(csvPath); + var outputSkillText = ParsecReader.FromFile(outputPath); + var jsonSkillText = ParsecReader.FromJsonFile(jsonPath); + var csvSkillText = DBSkillText.FromCsv(csvPath); var expected = skillText.GetBytes().ToList(); Assert.Equal(expected, outputSkillText.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/Svmap/SvmapTests.cs b/tests/Parsec.Tests/Shaiya/Svmap/SvmapTests.cs index 3398e41b..63c25638 100644 --- a/tests/Parsec.Tests/Shaiya/Svmap/SvmapTests.cs +++ b/tests/Parsec.Tests/Shaiya/Svmap/SvmapTests.cs @@ -19,17 +19,17 @@ public void SmodMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/Svmap/{fileName}.json"; string newObjPath = $"Shaiya/Svmap/new_{fileName}"; - var smod = Reader.ReadFromFile(filePath); - smod.WriteJson(jsonPath); - var smodFromJson = Reader.ReadFromJsonFile(jsonPath); + var svmap = ParsecReader.FromFile(filePath); + svmap.WriteJson(jsonPath); + var svmapFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes - Assert.Equal(smod.GetBytes(), smodFromJson.GetBytes()); + Assert.Equal(svmap.GetBytes(), svmapFromJson.GetBytes()); - smodFromJson.Write(newObjPath); - var newSmod = Reader.ReadFromFile(newObjPath); + svmapFromJson.Write(newObjPath); + var newSmod = ParsecReader.FromFile(newObjPath); // Check bytes - Assert.Equal(smod.GetBytes(), newSmod.GetBytes()); + Assert.Equal(svmap.GetBytes(), newSmod.GetBytes()); } } diff --git a/tests/Parsec.Tests/Shaiya/TransformModel/DBTransformModelData.SData b/tests/Parsec.Tests/Shaiya/TransformModel/DBTransformModelData.SData new file mode 100644 index 00000000..d0de57d9 Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/TransformModel/DBTransformModelData.SData differ diff --git a/tests/Parsec.Tests/Shaiya/TransformModel/DBTransformWeaponModelData.SData b/tests/Parsec.Tests/Shaiya/TransformModel/DBTransformWeaponModelData.SData new file mode 100644 index 00000000..0bf14c9d Binary files /dev/null and b/tests/Parsec.Tests/Shaiya/TransformModel/DBTransformWeaponModelData.SData differ diff --git a/tests/Parsec.Tests/Shaiya/TransformModel/TransformModelTests.cs b/tests/Parsec.Tests/Shaiya/TransformModel/TransformModelTests.cs new file mode 100644 index 00000000..43ec7712 --- /dev/null +++ b/tests/Parsec.Tests/Shaiya/TransformModel/TransformModelTests.cs @@ -0,0 +1,55 @@ +using System.Linq; +using Parsec.Shaiya.TransformModel; + +namespace Parsec.Tests.Shaiya.TransformModel; + +public class TransformModelTests +{ + [Fact] + public void DbTransformModelTest() + { + const string filePath = "Shaiya/TransformModel/DBTransformModelData.SData"; + const string outputPath = "Shaiya/Skill/output_DBTransformModelData.SData"; + const string jsonPath = "Shaiya/Skill/DBTransformModelData.SData.json"; + const string csvPath = "Shaiya/Skill/DBTransformModelData.SData.csv"; + + var transformModel = ParsecReader.FromFile(filePath); + transformModel.Write(outputPath); + transformModel.WriteJson(jsonPath); + transformModel.WriteCsv(csvPath); + + var outputTransformModel = ParsecReader.FromFile(outputPath); + var jsonTransformModel = ParsecReader.FromJsonFile(jsonPath); + var csvTransformModel = DBTransformModelData.FromCsv(csvPath); + + var expected = transformModel.GetBytes().ToList(); + Assert.Equal(expected, outputTransformModel.GetBytes()); + Assert.Equal(expected, jsonTransformModel.GetBytes()); + // For the csv check, skip the 128-byte header, which gets lost in the process + Assert.Equal(expected.Skip(128), csvTransformModel.GetBytes().Skip(128)); + } + + [Fact] + public void DbTransformWeaponModelTest() + { + const string filePath = "Shaiya/TransformModel/DBTransformWeaponModelData.SData"; + const string outputPath = "Shaiya/Skill/output_DBTransformWeaponModelData.SData"; + const string jsonPath = "Shaiya/Skill/DBTransformWeaponModelData.SData.json"; + const string csvPath = "Shaiya/Skill/DBTransformWeaponModelData.SData.csv"; + + var transformModel = ParsecReader.FromFile(filePath); + transformModel.Write(outputPath); + transformModel.WriteJson(jsonPath); + transformModel.WriteCsv(csvPath); + + var outputTransformModel = ParsecReader.FromFile(outputPath); + var jsonTransformModel = ParsecReader.FromJsonFile(jsonPath); + var csvTransformModel = DBTransformWeaponModelData.FromCsv(csvPath); + + var expected = transformModel.GetBytes().ToList(); + Assert.Equal(expected, outputTransformModel.GetBytes()); + Assert.Equal(expected, jsonTransformModel.GetBytes()); + // For the csv check, skip the 128-byte header, which gets lost in the process + Assert.Equal(expected.Skip(128), csvTransformModel.GetBytes().Skip(128)); + } +} diff --git a/tests/Parsec.Tests/Shaiya/VAni/VAniTests.cs b/tests/Parsec.Tests/Shaiya/VAni/VAniTests.cs index 8d05e053..312da0a8 100644 --- a/tests/Parsec.Tests/Shaiya/VAni/VAniTests.cs +++ b/tests/Parsec.Tests/Shaiya/VAni/VAniTests.cs @@ -18,15 +18,15 @@ public void VaniMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/VAni/{fileName}.json"; string newObjPath = $"Shaiya/VAni/new_{fileName}"; - var vani = Reader.ReadFromFile(filePath); + var vani = ParsecReader.FromFile(filePath); vani.WriteJson(jsonPath); - var vaniFromJson = Reader.ReadFromJsonFile(jsonPath); + var vaniFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(vani.GetBytes(), vaniFromJson.GetBytes()); vaniFromJson.Write(newObjPath); - var newVani = Reader.ReadFromFile(newObjPath); + var newVani = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(vani.GetBytes(), newVani.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/WLD/WldTests.cs b/tests/Parsec.Tests/Shaiya/WLD/WldTests.cs index 7237ebe2..229bd337 100644 --- a/tests/Parsec.Tests/Shaiya/WLD/WldTests.cs +++ b/tests/Parsec.Tests/Shaiya/WLD/WldTests.cs @@ -16,21 +16,21 @@ public void WldMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/WLD/{fileName}.json"; string newObjPath = $"Shaiya/WLD/new_{fileName}"; - var wld = Reader.ReadFromFile(filePath); + var wld = ParsecReader.FromFile(filePath); wld.Write(outputPath); wld.WriteJson(jsonPath); - var outputWld = Reader.ReadFromFile(outputPath); - var wldFromJson = Reader.ReadFromJsonFile(jsonPath); + var outputWld = ParsecReader.FromFile(outputPath); + var wldFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(wld.GetBytes(), outputWld.GetBytes()); Assert.Equal(wld.GetBytes(), wldFromJson.GetBytes()); wldFromJson.Write(newObjPath); - var newZon = Reader.ReadFromFile(newObjPath); + var newWld = ParsecReader.FromFile(newObjPath); // Check bytes - Assert.Equal(wld.GetBytes(), newZon.GetBytes()); + Assert.Equal(wld.GetBytes(), newWld.GetBytes()); } } diff --git a/tests/Parsec.Tests/Shaiya/Wtr/WtrTests.cs b/tests/Parsec.Tests/Shaiya/Wtr/WtrTests.cs index 9fb8e655..a9e25a90 100644 --- a/tests/Parsec.Tests/Shaiya/Wtr/WtrTests.cs +++ b/tests/Parsec.Tests/Shaiya/Wtr/WtrTests.cs @@ -15,15 +15,15 @@ public void WtrMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/Wtr/{fileName}.json"; string newObjPath = $"Shaiya/Wtr/new_{fileName}"; - var wtr = Reader.ReadFromFile(filePath); + var wtr = ParsecReader.FromFile(filePath); wtr.WriteJson(jsonPath); - var wtrFromJson = Reader.ReadFromJsonFile(jsonPath); + var wtrFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(wtr.GetBytes(), wtrFromJson.GetBytes()); wtrFromJson.Write(newObjPath); - var newWtr = Reader.ReadFromFile(newObjPath); + var newWtr = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(wtr.GetBytes(), newWtr.GetBytes()); diff --git a/tests/Parsec.Tests/Shaiya/Zon/ZonTests.cs b/tests/Parsec.Tests/Shaiya/Zon/ZonTests.cs index 7b7846c6..2bd8e67b 100644 --- a/tests/Parsec.Tests/Shaiya/Zon/ZonTests.cs +++ b/tests/Parsec.Tests/Shaiya/Zon/ZonTests.cs @@ -11,7 +11,7 @@ public void ZonMultipleReadWriteTest(string fileName) string jsonPath = $"Shaiya/Zon/{fileName}.json"; string newObjPath = $"Shaiya/Zon/new_{fileName}"; - var zon = Reader.ReadFromFile(filePath); + var zon = ParsecReader.FromFile(filePath); // Since record descriptions use different encodings for the Description string, the Zon -> JSON -> Zon conversion // will always modify the Description @@ -21,15 +21,15 @@ public void ZonMultipleReadWriteTest(string fileName) zon.Write(outputPath); zon.WriteJson(jsonPath); - var outputZon = Reader.ReadFromFile(outputPath); - var zonFromJson = Reader.ReadFromJsonFile(jsonPath); + var outputZon = ParsecReader.FromFile(outputPath); + var zonFromJson = ParsecReader.FromJsonFile(jsonPath); // Check bytes Assert.Equal(zon.GetBytes(), outputZon.GetBytes()); Assert.Equal(zon.GetBytes(), zonFromJson.GetBytes()); zonFromJson.Write(newObjPath); - var newZon = Reader.ReadFromFile(newObjPath); + var newZon = ParsecReader.FromFile(newObjPath); // Check bytes Assert.Equal(zon.GetBytes(), newZon.GetBytes()); diff --git a/tests/Parsec.Tests/TestEncodings.cs b/tests/Parsec.Tests/TestEncodings.cs index 72e6371e..86e4933a 100644 --- a/tests/Parsec.Tests/TestEncodings.cs +++ b/tests/Parsec.Tests/TestEncodings.cs @@ -4,5 +4,6 @@ namespace Parsec.Tests; public static class TestEncodings { + public static readonly Encoding Encoding1251 = CodePagesEncodingProvider.Instance.GetEncoding(1251); public static readonly Encoding Encoding1252 = CodePagesEncodingProvider.Instance.GetEncoding(1252); }