diff --git a/Bytes/IRD.Deserializer.cs b/Bytes/IRD.Deserializer.cs new file mode 100644 index 00000000..8ea4bfd6 --- /dev/null +++ b/Bytes/IRD.Deserializer.cs @@ -0,0 +1,28 @@ +using System.IO; +using SabreTools.Serialization.Interfaces; + +namespace SabreTools.Serialization.Bytes +{ + public partial class IRD : IByteSerializer + { + /// +#if NET48 + public Models.IRD.IRD Deserialize(byte[] data, int offset) +#else + public Models.IRD.IRD? Deserialize(byte[]? data, int offset) +#endif + { + // If the data is invalid + if (data == null) + return null; + + // If the offset is out of bounds + if (offset < 0 || offset >= data.Length) + return null; + + // Create a memory stream and parse that + MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset); + return new Streams.IRD().Deserialize(dataStream); + } + } +} \ No newline at end of file diff --git a/Files/IRD.Deserializer.cs b/Files/IRD.Deserializer.cs new file mode 100644 index 00000000..0120458a --- /dev/null +++ b/Files/IRD.Deserializer.cs @@ -0,0 +1,20 @@ +using SabreTools.Serialization.Interfaces; + +namespace SabreTools.Serialization.Files +{ + public partial class IRD : IFileSerializer + { + /// +#if NET48 + public Models.IRD.IRD Deserialize(string path) +#else + public Models.IRD.IRD? Deserialize(string? path) +#endif + { + using (var stream = PathProcessor.OpenStream(path)) + { + return new Streams.IRD().Deserialize(stream); + } + } + } +} \ No newline at end of file diff --git a/Files/IRD.Serializer.cs b/Files/IRD.Serializer.cs new file mode 100644 index 00000000..cde7a595 --- /dev/null +++ b/Files/IRD.Serializer.cs @@ -0,0 +1,30 @@ +using SabreTools.Serialization.Interfaces; + +namespace SabreTools.Serialization.Files +{ + public partial class IRD : IFileSerializer + { + /// +#if NET48 + public bool Serialize(Models.IRD.IRD obj, string path) +#else + public bool Serialize(Models.IRD.IRD? obj, string? path) +#endif + { + if (string.IsNullOrWhiteSpace(path)) + return false; + + using (var stream = new Streams.IRD().Serialize(obj)) + { + if (stream == null) + return false; + + using (var fs = System.IO.File.OpenWrite(path)) + { + stream.CopyTo(fs); + return true; + } + } + } + } +} \ No newline at end of file diff --git a/Streams/IRD.Deserializer.cs b/Streams/IRD.Deserializer.cs new file mode 100644 index 00000000..69e7846a --- /dev/null +++ b/Streams/IRD.Deserializer.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; +using System.Text; +using SabreTools.IO; +using SabreTools.Serialization.Interfaces; + +namespace SabreTools.Serialization.Streams +{ + public partial class IRD : IStreamSerializer + { + /// +#if NET48 + public Models.IRD.IRD Deserialize(Stream data) +#else + public Models.IRD.IRD? Deserialize(Stream? data) +#endif + { + // If the data is invalid + if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead) + return null; + + // If the offset is out of bounds + if (data.Position < 0 || data.Position >= data.Length) + return null; + + // Cache the current offset + int initialOffset = (int)data.Position; + + // Create a new media key block to fill + var ird = new Models.IRD.IRD(); + + ird.Magic = data.ReadBytes(4); + if (ird.Magic == null) + return null; + + string magic = Encoding.ASCII.GetString(ird.Magic); + if (magic != "3IRD") + return null; + + ird.Version = data.ReadByteValue(); + if (ird.Version < 6) + return null; + + var titleId = data.ReadBytes(9); + if (titleId == null) + return null; + + ird.TitleID = Encoding.ASCII.GetString(titleId); + + ird.TitleLength = data.ReadByteValue(); + var title = data.ReadBytes(ird.TitleLength); + if (title == null) + return null; + + ird.Title = Encoding.ASCII.GetString(title); + + var systemVersion = data.ReadBytes(4); + if (systemVersion == null) + return null; + + ird.SystemVersion = Encoding.ASCII.GetString(systemVersion); + + var gameVersion = data.ReadBytes(5); + if (gameVersion == null) + return null; + + ird.GameVersion = Encoding.ASCII.GetString(gameVersion); + + var appVersion = data.ReadBytes(5); + if (appVersion == null) + return null; + + ird.AppVersion = Encoding.ASCII.GetString(appVersion); + + if (ird.Version == 7) + ird.UID = data.ReadUInt32(); + + ird.HeaderLength = data.ReadByteValue(); + ird.Header = data.ReadBytes((int)ird.HeaderLength); + ird.FooterLength = data.ReadByteValue(); + ird.Footer = data.ReadBytes((int)ird.FooterLength); + + ird.RegionCount = data.ReadByteValue(); + ird.RegionHashes = new byte[ird.RegionCount][]; + for (int i = 0; i < ird.RegionCount; i++) + { + ird.RegionHashes[i] = data.ReadBytes(16) ?? Array.Empty(); + } + + ird.FileCount = data.ReadByteValue(); + ird.FileKeys = new ulong[ird.FileCount]; + ird.FileHashes = new byte[ird.FileCount][]; + for (int i = 0; i < ird.FileCount; i++) + { + ird.FileKeys[i] = data.ReadUInt64(); + ird.FileHashes[i] = data.ReadBytes(16) ?? Array.Empty(); + } + + ird.ExtraConfig = data.ReadUInt16(); + ird.Attachments = data.ReadUInt16(); + + if (ird.Version >= 9) + ird.PIC = data.ReadBytes(115); + + ird.Data1Key = data.ReadBytes(16); + ird.Data2Key = data.ReadBytes(16); + + if (ird.Version < 9) + ird.PIC = data.ReadBytes(115); + + if (ird.Version > 7) + ird.UID = data.ReadUInt32(); + + ird.CRC = data.ReadUInt32(); + + return ird; + } + } +} \ No newline at end of file diff --git a/Streams/IRD.Serializer.cs b/Streams/IRD.Serializer.cs new file mode 100644 index 00000000..061c4885 --- /dev/null +++ b/Streams/IRD.Serializer.cs @@ -0,0 +1,136 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using SabreTools.Serialization.Interfaces; + +namespace SabreTools.Serialization.Streams +{ + public partial class IRD : IStreamSerializer + { + /// +#if NET48 + public Stream Serialize(Models.IRD.IRD obj) +#else + public Stream? Serialize(Models.IRD.IRD? obj) +#endif + { + // If the data is invalid + if (obj?.Magic == null) + return null; + + // If the magic doesn't match + string magic = Encoding.ASCII.GetString(obj.Magic); + if (magic != "3IRD") + return null; + + // If the version is less than the supported + if (obj.Version < 6) + return null; + + // If any static-length fields aren't the correct length + if (obj.TitleID == null || obj.TitleID.Length != 9) + return null; + if (obj.Title == null || obj.Title.Length != obj.TitleLength) + return null; + if (obj.SystemVersion == null || obj.SystemVersion.Length != 4) + return null; + if (obj.GameVersion == null || obj.GameVersion.Length != 5) + return null; + if (obj.AppVersion == null || obj.AppVersion.Length != 5) + return null; + if (obj.Header == null || obj.Header.Length != obj.HeaderLength) + return null; + if (obj.Footer == null || obj.Footer.Length != obj.FooterLength) + return null; + if (obj.RegionHashes == null || obj.RegionHashes.Length != obj.RegionCount || obj.RegionHashes.Any(h => h == null || h.Length != 16)) + return null; + if (obj.FileKeys == null || obj.FileKeys.Length != obj.FileCount) + return null; + if (obj.FileHashes == null || obj.FileHashes.Length != obj.FileCount || obj.FileHashes.Any(h => h == null || h.Length != 16)) + return null; + if (obj.PIC == null || obj.PIC.Length != 115) + return null; + if (obj.Data1Key == null || obj.Data1Key.Length != 16) + return null; + if (obj.Data2Key == null || obj.Data2Key.Length != 16) + return null; + + // Create the output stream + var stream = new MemoryStream(); + + stream.Write(obj.Magic, 0, obj.Magic.Length); + stream.WriteByte(obj.Version); + + byte[] titleId = Encoding.ASCII.GetBytes(obj.TitleID); + stream.Write(titleId, 0, titleId.Length); + + stream.WriteByte(obj.TitleLength); + byte[] title = Encoding.ASCII.GetBytes(obj.Title); + stream.Write(title, 0, title.Length); + + byte[] systemVersion = Encoding.ASCII.GetBytes(obj.SystemVersion); + stream.Write(systemVersion, 0, systemVersion.Length); + + byte[] gameVersion = Encoding.ASCII.GetBytes(obj.GameVersion); + stream.Write(gameVersion, 0, gameVersion.Length); + + byte[] appVersion = Encoding.ASCII.GetBytes(obj.AppVersion); + stream.Write(appVersion, 0, appVersion.Length); + + if (obj.Version == 7) + { + byte[] uid = BitConverter.GetBytes(obj.UID); + stream.Write(uid, 0, uid.Length); + } + + byte[] headerLength = BitConverter.GetBytes(obj.HeaderLength); + stream.Write(headerLength, 0, headerLength.Length); + stream.Write(obj.Header, 0, obj.Header.Length); + + byte[] footerLength = BitConverter.GetBytes(obj.FooterLength); + stream.Write(footerLength, 0, footerLength.Length); + stream.Write(obj.Footer, 0, obj.Footer.Length); + + stream.WriteByte(obj.RegionCount); + for (int i = 0; i < obj.RegionCount; i++) + { + stream.Write(obj.RegionHashes[i], 0, obj.RegionHashes[i].Length); + } + + byte[] fileCount = BitConverter.GetBytes(obj.FileCount); + stream.Write(fileCount, 0, fileCount.Length); + for (int i = 0; i < obj.FileCount; i++) + { + byte[] fileKey = BitConverter.GetBytes(obj.FileKeys[i]); + stream.Write(fileKey, 0, fileKey.Length); + stream.Write(obj.FileHashes[i], 0, obj.FileHashes[i].Length); + } + + byte[] extraConfig = BitConverter.GetBytes(obj.ExtraConfig); + stream.Write(extraConfig, 0, extraConfig.Length); + byte[] attachments = BitConverter.GetBytes(obj.Attachments); + stream.Write(attachments, 0, attachments.Length); + + if (obj.Version >= 9) + stream.Write(obj.PIC, 0, obj.PIC.Length); + + stream.Write(obj.Data1Key, 0, obj.Data1Key.Length); + stream.Write(obj.Data2Key, 0, obj.Data2Key.Length); + + if (obj.Version < 9) + stream.Write(obj.PIC, 0, obj.PIC.Length); + + if (obj.Version > 7) + { + byte[] uid = BitConverter.GetBytes(obj.UID); + stream.Write(uid, 0, uid.Length); + } + + byte[] crc = BitConverter.GetBytes(obj.CRC); + stream.Write(crc, 0, crc.Length); + + return stream; + } + } +} \ No newline at end of file