From 5a613be9bf05a14402f88d01f935ecffe3343524 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 13 Sep 2023 15:11:44 -0400 Subject: [PATCH] Port PIC serialization from MPF --- Files/PIC.Deserializer.cs | 20 ++++ Files/PIC.Serializer.cs | 30 ++++++ Streams/PIC.Deserializer.cs | 202 ++++++++++++++++++++++++++++++++++++ Streams/PIC.Serializer.cs | 16 +++ 4 files changed, 268 insertions(+) create mode 100644 Files/PIC.Deserializer.cs create mode 100644 Files/PIC.Serializer.cs create mode 100644 Streams/PIC.Deserializer.cs create mode 100644 Streams/PIC.Serializer.cs diff --git a/Files/PIC.Deserializer.cs b/Files/PIC.Deserializer.cs new file mode 100644 index 00000000..a5771e7d --- /dev/null +++ b/Files/PIC.Deserializer.cs @@ -0,0 +1,20 @@ +using SabreTools.Models.PIC; + +namespace SabreTools.Serialization.Files +{ + public partial class PIC : IFileSerializer + { + /// +#if NET48 + public DiscInformation Deserialize(string path) +#else + public DiscInformation? Deserialize(string? path) +#endif + { + using (var stream = PathProcessor.OpenStream(path)) + { + return new Streams.PIC().Deserialize(stream); + } + } + } +} \ No newline at end of file diff --git a/Files/PIC.Serializer.cs b/Files/PIC.Serializer.cs new file mode 100644 index 00000000..0ff27b23 --- /dev/null +++ b/Files/PIC.Serializer.cs @@ -0,0 +1,30 @@ +using SabreTools.Models.PIC; + +namespace SabreTools.Serialization.Files +{ + public partial class PIC : IFileSerializer + { + /// +#if NET48 + public bool Serialize(DiscInformation obj, string path) +#else + public bool Serialize(DiscInformation? obj, string? path) +#endif + { + if (string.IsNullOrWhiteSpace(path)) + return false; + + using (var stream = new Streams.PIC().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/PIC.Deserializer.cs b/Streams/PIC.Deserializer.cs new file mode 100644 index 00000000..22857285 --- /dev/null +++ b/Streams/PIC.Deserializer.cs @@ -0,0 +1,202 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using SabreTools.IO; +using SabreTools.Models.PIC; +using static SabreTools.Models.PIC.Constants; + +namespace SabreTools.Serialization.Streams +{ + public partial class PIC : IStreamSerializer + { + /// +#if NET48 + public DiscInformation Deserialize(Stream data) +#else + public DiscInformation? 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; + + var di = new DiscInformation(); + + // Read the initial disc information + di.DataStructureLength = data.ReadUInt16BigEndian(); + di.Reserved0 = data.ReadByteValue(); + di.Reserved1 = data.ReadByteValue(); + + // Create a list for the units + var diUnits = new List(); + + // Loop and read all available units + for (int i = 0; i < 32; i++) + { + var unit = ParseDiscInformationUnit(data); + if (unit == null) + continue; + + diUnits.Add(unit); + } + + // Assign the units and return + di.Units = diUnits.ToArray(); + return di; + } + + /// + /// Parse a Stream into a disc information unit + /// + /// Stream to parse + /// Filled disc information unit on success, null on error +#if NET48 + private static DiscInformationUnit ParseDiscInformationUnit(Stream data) +#else + private static DiscInformationUnit? ParseDiscInformationUnit(Stream data) +#endif + { + // TODO: Use marshalling here instead of building + var unit = new DiscInformationUnit(); + + #region Header + + // Try to parse the header + var header = ParseDiscInformationUnitHeader(data); + if (header == null) + return null; + + // Set the information unit header + unit.Header = header; + + #endregion + + #region Body + + // Try to parse the body + var body = ParseDiscInformationUnitBody(data); + if (body == null) + return null; + + // Set the information unit body + unit.Body = body; + + #endregion + + #region Trailer + + if (unit.Body.DiscTypeIdentifier == DiscTypeIdentifierReWritable || unit.Body.DiscTypeIdentifier == DiscTypeIdentifierRecordable) + { + // Try to parse the trailer + var trailer = ParseDiscInformationUnitTrailer(data); + if (trailer == null) + return null; + + // Set the information unit trailer + unit.Trailer = trailer; + } + + #endregion + + return unit; + } + + /// + /// Parse a Stream into a disc information unit header + /// + /// Stream to parse + /// Filled disc information unit header on success, null on error +#if NET48 + private static DiscInformationUnitHeader ParseDiscInformationUnitHeader(Stream data) +#else + private static DiscInformationUnitHeader? ParseDiscInformationUnitHeader(Stream data) +#endif + { + // TODO: Use marshalling here instead of building + var header = new DiscInformationUnitHeader(); + + // We only accept Disc Information units, not Emergency Brake or other +#if NET48 + byte[] dic = data.ReadBytes(2); +#else + byte[]? dic = data.ReadBytes(2); +#endif + if (dic == null) + return null; + + header.DiscInformationIdentifier = Encoding.ASCII.GetString(dic); + if (header.DiscInformationIdentifier != "DI") + return null; + + header.DiscInformationFormat = data.ReadByteValue(); + header.NumberOfUnitsInBlock = data.ReadByteValue(); + header.Reserved0 = data.ReadByteValue(); + header.SequenceNumber = data.ReadByteValue(); + header.BytesInUse = data.ReadByteValue(); + header.Reserved1 = data.ReadByteValue(); + + return header; + } + + /// + /// Parse a Stream into a disc information unit body + /// + /// Stream to parse + /// Filled disc information unit body on success, null on error +#if NET48 + private static DiscInformationUnitBody ParseDiscInformationUnitBody(Stream data) +#else + private static DiscInformationUnitBody? ParseDiscInformationUnitBody(Stream data) +#endif + { + // TODO: Use marshalling here instead of building + var body = new DiscInformationUnitBody(); + +#if NET48 + byte[] dti = data.ReadBytes(3); +#else + byte[]? dti = data.ReadBytes(3); +#endif + if (dti == null) + return null; + + body.DiscTypeIdentifier = Encoding.ASCII.GetString(dti); + body.DiscSizeClassVersion = data.ReadByteValue(); + switch (body.DiscTypeIdentifier) + { + case DiscTypeIdentifierROM: + case DiscTypeIdentifierROMUltra: + body.FormatDependentContents = data.ReadBytes(52); + break; + case DiscTypeIdentifierReWritable: + case DiscTypeIdentifierRecordable: + body.FormatDependentContents = data.ReadBytes(100); + break; + } + + return body; + } + + /// + /// Parse a Stream into a disc information unit trailer + /// + /// Stream to parse + /// Filled disc information unit trailer on success, null on error + private static DiscInformationUnitTrailer ParseDiscInformationUnitTrailer(Stream data) + { + // TODO: Use marshalling here instead of building + var trailer = new DiscInformationUnitTrailer(); + + trailer.DiscManufacturerID = data.ReadBytes(6); + trailer.MediaTypeID = data.ReadBytes(3); + trailer.TimeStamp = data.ReadUInt16(); + trailer.ProductRevisionNumber = data.ReadByteValue(); + + return trailer; + } + } +} \ No newline at end of file diff --git a/Streams/PIC.Serializer.cs b/Streams/PIC.Serializer.cs new file mode 100644 index 00000000..81ffe298 --- /dev/null +++ b/Streams/PIC.Serializer.cs @@ -0,0 +1,16 @@ +using System; +using System.IO; +using SabreTools.Models.PIC; + +namespace SabreTools.Serialization.Streams +{ + public partial class PIC : IStreamSerializer + { + /// +#if NET48 + public Stream Serialize(DiscInformation obj) => throw new NotImplementedException(); +#else + public Stream? Serialize(DiscInformation? obj) => throw new NotImplementedException(); +#endif + } +} \ No newline at end of file