diff --git a/Bytes/MSDOS.Deserializer.cs b/Bytes/MSDOS.Deserializer.cs new file mode 100644 index 00000000..7dd7ade7 --- /dev/null +++ b/Bytes/MSDOS.Deserializer.cs @@ -0,0 +1,28 @@ +using System.IO; +using SabreTools.Models.MSDOS; + +namespace SabreTools.Serialization.Bytes +{ + public partial class MSDOS : IByteSerializer + { + /// +#if NET48 + public Executable Deserialize(byte[] data, int offset) +#else + public Executable? 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.MSDOS().Deserialize(dataStream); + } + } +} \ No newline at end of file diff --git a/Streams/MSDOS.Deserializer.cs b/Streams/MSDOS.Deserializer.cs new file mode 100644 index 00000000..52b930a7 --- /dev/null +++ b/Streams/MSDOS.Deserializer.cs @@ -0,0 +1,146 @@ +using System.IO; +using System.Text; +using SabreTools.IO; +using SabreTools.Models.MSDOS; +using static SabreTools.Models.MSDOS.Constants; + +namespace SabreTools.Serialization.Streams +{ + public partial class MSDOS : IStreamSerializer + { + /// +#if NET48 + public Executable Deserialize(Stream data) +#else + public Executable? 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 executable to fill + var executable = new Executable(); + + #region Executable Header + + // Try to parse the executable header + var executableHeader = ParseExecutableHeader(data); + if (executableHeader == null) + return null; + + // Set the executable header + executable.Header = executableHeader; + + #endregion + + #region Relocation Table + + // If the offset for the relocation table doesn't exist + int tableAddress = initialOffset + executableHeader.RelocationTableAddr; + if (tableAddress >= data.Length) + return executable; + + // Try to parse the relocation table + data.Seek(tableAddress, SeekOrigin.Begin); + var relocationTable = ParseRelocationTable(data, executableHeader.RelocationItems); + if (relocationTable == null) + return null; + + // Set the relocation table + executable.RelocationTable = relocationTable; + + #endregion + + // Return the executable + return executable; + } + + /// + /// Parse a Stream into an MS-DOS executable header + /// + /// Stream to parse + /// Filled executable header on success, null on error + private static ExecutableHeader ParseExecutableHeader(Stream data) + { + // TODO: Use marshalling here instead of building + var header = new ExecutableHeader(); + + #region Standard Fields + + byte[] magic = data.ReadBytes(2); + header.Magic = Encoding.ASCII.GetString(magic); + if (header.Magic != SignatureString) + return null; + + header.LastPageBytes = data.ReadUInt16(); + header.Pages = data.ReadUInt16(); + header.RelocationItems = data.ReadUInt16(); + header.HeaderParagraphSize = data.ReadUInt16(); + header.MinimumExtraParagraphs = data.ReadUInt16(); + header.MaximumExtraParagraphs = data.ReadUInt16(); + header.InitialSSValue = data.ReadUInt16(); + header.InitialSPValue = data.ReadUInt16(); + header.Checksum = data.ReadUInt16(); + header.InitialIPValue = data.ReadUInt16(); + header.InitialCSValue = data.ReadUInt16(); + header.RelocationTableAddr = data.ReadUInt16(); + header.OverlayNumber = data.ReadUInt16(); + + #endregion + + // If we don't have enough data for PE extensions + if (data.Position >= data.Length || data.Length - data.Position < 36) + return header; + + #region PE Extensions + + header.Reserved1 = new ushort[4]; + for (int i = 0; i < header.Reserved1.Length; i++) + { + header.Reserved1[i] = data.ReadUInt16(); + } + header.OEMIdentifier = data.ReadUInt16(); + header.OEMInformation = data.ReadUInt16(); + header.Reserved2 = new ushort[10]; + for (int i = 0; i < header.Reserved2.Length; i++) + { + header.Reserved2[i] = data.ReadUInt16(); + } + header.NewExeHeaderAddr = data.ReadUInt32(); + + #endregion + + return header; + } + + /// + /// Parse a Stream into a relocation table + /// + /// Stream to parse + /// Number of relocation table entries to read + /// Filled relocation table on success, null on error + private static RelocationEntry[] ParseRelocationTable(Stream data, int count) + { + // TODO: Use marshalling here instead of building + var relocationTable = new RelocationEntry[count]; + + for (int i = 0; i < count; i++) + { + var entry = new RelocationEntry(); + entry.Offset = data.ReadUInt16(); + entry.Segment = data.ReadUInt16(); + relocationTable[i] = entry; + } + + return relocationTable; + } + } +} \ No newline at end of file diff --git a/Streams/MSDOS.Serializer.cs b/Streams/MSDOS.Serializer.cs new file mode 100644 index 00000000..c84a1dc0 --- /dev/null +++ b/Streams/MSDOS.Serializer.cs @@ -0,0 +1,16 @@ +using System; +using System.IO; +using SabreTools.Models.MSDOS; + +namespace SabreTools.Serialization.Streams +{ + public partial class MSDOS : IStreamSerializer + { + /// +#if NET48 + public Stream Serialize(Executable obj) => throw new NotImplementedException(); +#else + public Stream? Serialize(Executable? obj) => throw new NotImplementedException(); +#endif + } +} \ No newline at end of file