diff --git a/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs b/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs
index 1ad97d79..d2976478 100644
--- a/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs
+++ b/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs
@@ -10,7 +10,7 @@ namespace BurnOutSharp.FileType
///
// TODO: Add multi-cabinet reading
- internal class MSCABCabinet
+ public class MSCABCabinet
{
#region Constants
@@ -82,7 +82,7 @@ namespace BurnOutSharp.FileType
cabinet.Folders = new CFFOLDER[cabinet.Header.FolderCount];
for (int i = 0; i < cabinet.Header.FolderCount; i++)
{
- cabinet.Folders[i] = CFFOLDER.Deserialize(data, ref dataPtr, basePtr, cabinet.Header.FolderReservedSize, cabinet.Header.DataReservedSize);
+ cabinet.Folders[i] = CFFOLDER.Deserialize(data, ref dataPtr, basePtr, cabinet.Header);
if (cabinet.Folders[i] == null)
return null;
}
@@ -102,6 +102,45 @@ namespace BurnOutSharp.FileType
return cabinet;
}
+ ///
+ /// Deserialize into a MSCABCabinet object
+ ///
+ public static MSCABCabinet Deserialize(Stream data)
+ {
+ if (data == null || data.Position < 0)
+ return null;
+
+ MSCABCabinet cabinet = new MSCABCabinet();
+
+ // Start with the header
+ cabinet.Header = CFHEADER.Deserialize(data);
+ if (cabinet.Header == null)
+ return null;
+
+ // Then retrieve all folder headers
+ cabinet.Folders = new CFFOLDER[cabinet.Header.FolderCount];
+ for (int i = 0; i < cabinet.Header.FolderCount; i++)
+ {
+ cabinet.Folders[i] = CFFOLDER.Deserialize(data, cabinet.Header);
+ if (cabinet.Folders[i] == null)
+ return null;
+ }
+
+ // We need to move to where the file headers are stored
+ data.Seek((int)cabinet.Header.FilesOffset, SeekOrigin.Begin);
+
+ // Then retrieve all file headers
+ cabinet.Files = new CFFILE[cabinet.Header.FileCount];
+ for (int i = 0; i < cabinet.Header.FileCount; i++)
+ {
+ cabinet.Files[i] = CFFILE.Deserialize(data);
+ if (cabinet.Files[i] == null)
+ return null;
+ }
+
+ return cabinet;
+ }
+
#endregion
#region Public Functionality
@@ -131,7 +170,7 @@ namespace BurnOutSharp.FileType
foreach (CFFILE file in Files)
{
// Create the output path
- string outputPath = Path.Combine(outputDirectory, file.NameAsString);
+ string outputPath = Path.Combine(outputDirectory, file.Name);
// Get the associated folder, if possible
CFFOLDER folder = null;
@@ -168,7 +207,7 @@ namespace BurnOutSharp.FileType
continue;
// Check for a match
- if (exact ? tempFile.NameAsString == filePath : tempFile.NameAsString.EndsWith(filePath, StringComparison.OrdinalIgnoreCase))
+ if (exact ? tempFile.Name == filePath : tempFile.Name.EndsWith(filePath, StringComparison.OrdinalIgnoreCase))
{
fileIndex = i;
break;
@@ -183,7 +222,7 @@ namespace BurnOutSharp.FileType
CFFILE file = Files[fileIndex];
// Create the output path
- string outputPath = Path.Combine(outputDirectory, file.NameAsString);
+ string outputPath = Path.Combine(outputDirectory, file.Name);
// Get the associated folder, if possible
CFFOLDER folder = null;
@@ -278,33 +317,13 @@ namespace BurnOutSharp.FileType
}
#endregion
-
- #region Internal Functionality
-
- ///
- /// Get a null-terminated string as a byte array from input data
- ///
- internal static byte[] GetNullTerminatedString(byte[] data, ref int dataPtr)
- {
- int nullIndex = Array.IndexOf(data, 0x00, dataPtr, 0xFF);
- int stringSize = nullIndex - dataPtr;
- if (stringSize < 0 || stringSize > 256)
- return null;
-
- byte[] str = new byte[stringSize];
- Array.Copy(data, dataPtr, str, 0, stringSize);
- dataPtr += stringSize + 1;
- return str;
- }
-
- #endregion
}
///
/// The CFHEADER structure shown in the following packet diagram provides information about this
/// cabinet (.cab) file.
///
- internal class CFHEADER
+ public class CFHEADER
{
#region Constants
@@ -439,7 +458,7 @@ namespace BurnOutSharp.FileType
/// reported to begin in the "previous cabinet," the szCabinetPrev field would indicate the name of the
/// cabinet to examine.
///
- public byte[] CabinetPrev { get; private set; }
+ public string CabinetPrev { get; private set; }
///
/// If the flags.cfhdrPREV_CABINET field is not set, then this
@@ -448,7 +467,7 @@ namespace BurnOutSharp.FileType
/// This string can be used when prompting the user to insert a disk. The string can contain up to 255
/// bytes, plus the null byte.
///
- public byte[] DiskPrev { get; private set; }
+ public string DiskPrev { get; private set; }
///
/// If the flags.cfhdrNEXT_CABINET field is not set, this
@@ -456,7 +475,7 @@ namespace BurnOutSharp.FileType
/// cabinet file in a set. The string can contain up to 255 bytes, plus the null byte. Files that extend
/// beyond the end of the current cabinet file are continued in the named cabinet file.
///
- public byte[] CabinetNext { get; private set; }
+ public string CabinetNext { get; private set; }
///
/// If the flags.cfhdrNEXT_CABINET field is not set, this field is
@@ -465,7 +484,7 @@ namespace BurnOutSharp.FileType
/// string can contain up to 255 bytes, plus the null byte. This string can be used when prompting the
/// user to insert a disk.
///
- public byte[] DiskNext { get; private set; }
+ public string DiskNext { get; private set; }
#endregion
@@ -481,91 +500,145 @@ namespace BurnOutSharp.FileType
CFHEADER header = new CFHEADER();
- header.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ header.Signature = data.ReadUInt32(ref dataPtr);
if (header.Signature != SignatureValue)
return null;
- header.Reserved1 = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ header.Reserved1 = data.ReadUInt32(ref dataPtr);
if (header.Reserved1 != 0x00000000)
return null;
- header.CabinetSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ header.CabinetSize = data.ReadUInt32(ref dataPtr);
if (header.CabinetSize > MSCABCabinet.MaximumCabSize)
return null;
- header.Reserved2 = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ header.Reserved2 = data.ReadUInt32(ref dataPtr);
if (header.Reserved2 != 0x00000000)
return null;
- header.FilesOffset = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ header.FilesOffset = data.ReadUInt32(ref dataPtr);
- header.Reserved3 = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ header.Reserved3 = data.ReadUInt32(ref dataPtr);
if (header.Reserved3 != 0x00000000)
return null;
- header.VersionMinor = data[dataPtr++];
- header.VersionMajor = data[dataPtr++];
+ header.VersionMinor = data.ReadByte(ref dataPtr);
+ header.VersionMajor = data.ReadByte(ref dataPtr);
if (header.VersionMajor != 0x00000001 || header.VersionMinor != 0x00000003)
return null;
- header.FolderCount = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
+ header.FolderCount = data.ReadUInt16(ref dataPtr);
if (header.FolderCount > MSCABCabinet.MaximumFolderCount)
return null;
- header.FileCount = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
+ header.FileCount = data.ReadUInt16(ref dataPtr);
if (header.FileCount > MSCABCabinet.MaximumFileCount)
return null;
- header.Flags = (HeaderFlags)BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
- header.SetID = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
- header.CabinetIndex = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
+ header.Flags = (HeaderFlags)data.ReadUInt16(ref dataPtr);
+ header.SetID = data.ReadUInt16(ref dataPtr);
+ header.CabinetIndex = data.ReadUInt16(ref dataPtr);
if (header.Flags.HasFlag(HeaderFlags.RESERVE_PRESENT))
{
- header.HeaderReservedSize = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
+ header.HeaderReservedSize = data.ReadUInt16(ref dataPtr);
if (header.HeaderReservedSize > 60_000)
return null;
- header.FolderReservedSize = data[dataPtr++];
- header.DataReservedSize = data[dataPtr++];
+ header.FolderReservedSize = data.ReadByte(ref dataPtr);
+ header.DataReservedSize = data.ReadByte(ref dataPtr);
if (header.HeaderReservedSize > 0)
- {
- header.ReservedData = new byte[header.HeaderReservedSize];
- Array.Copy(data, dataPtr, header.ReservedData, 0, header.HeaderReservedSize);
- dataPtr += header.HeaderReservedSize;
- }
+ header.ReservedData = data.ReadBytes(ref dataPtr, header.HeaderReservedSize);
}
- // TODO: Make string-finding block a helper method
if (header.Flags.HasFlag(HeaderFlags.PREV_CABINET))
{
- byte[] cabPrev = MSCABCabinet.GetNullTerminatedString(data, ref dataPtr);
- if (cabPrev == null)
- return null;
-
- header.CabinetPrev = cabPrev;
-
- byte[] diskPrev = MSCABCabinet.GetNullTerminatedString(data, ref dataPtr);
- if (diskPrev == null)
- return null;
-
- header.DiskPrev = diskPrev;
+ header.CabinetPrev = data.ReadString(ref dataPtr, Encoding.ASCII);
+ header.DiskPrev = data.ReadString(ref dataPtr, Encoding.ASCII);
}
if (header.Flags.HasFlag(HeaderFlags.NEXT_CABINET))
{
- byte[] cabNext = MSCABCabinet.GetNullTerminatedString(data, ref dataPtr);
- if (cabNext == null)
+ header.CabinetNext = data.ReadString(ref dataPtr, Encoding.ASCII);
+ header.DiskNext = data.ReadString(ref dataPtr, Encoding.ASCII);
+ }
+
+ return header;
+ }
+
+ ///
+ /// Deserialize into a CFHEADER object
+ ///
+ public static CFHEADER Deserialize(Stream data)
+ {
+ if (data == null || data.Position < 0)
+ return null;
+
+ CFHEADER header = new CFHEADER();
+
+ header.Signature = data.ReadUInt32();
+ if (header.Signature != SignatureValue)
+ return null;
+
+ header.Reserved1 = data.ReadUInt32();
+ if (header.Reserved1 != 0x00000000)
+ return null;
+
+ header.CabinetSize = data.ReadUInt32();
+ if (header.CabinetSize > MSCABCabinet.MaximumCabSize)
+ return null;
+
+ header.Reserved2 = data.ReadUInt32();
+ if (header.Reserved2 != 0x00000000)
+ return null;
+
+ header.FilesOffset = data.ReadUInt32();
+
+ header.Reserved3 = data.ReadUInt32();
+ if (header.Reserved3 != 0x00000000)
+ return null;
+
+ header.VersionMinor = data.ReadByteValue();
+ header.VersionMajor = data.ReadByteValue();
+ if (header.VersionMajor != 0x00000001 || header.VersionMinor != 0x00000003)
+ return null;
+
+ header.FolderCount = data.ReadUInt16();
+ if (header.FolderCount > MSCABCabinet.MaximumFolderCount)
+ return null;
+
+ header.FileCount = data.ReadUInt16();
+ if (header.FileCount > MSCABCabinet.MaximumFileCount)
+ return null;
+
+ header.Flags = (HeaderFlags)data.ReadUInt16();
+ header.SetID = data.ReadUInt16();
+ header.CabinetIndex = data.ReadUInt16();
+
+ if (header.Flags.HasFlag(HeaderFlags.RESERVE_PRESENT))
+ {
+ header.HeaderReservedSize = data.ReadUInt16();
+ if (header.HeaderReservedSize > 60_000)
return null;
- header.CabinetNext = cabNext;
+ header.FolderReservedSize = data.ReadByteValue();
+ header.DataReservedSize = data.ReadByteValue();
- byte[] diskNext = MSCABCabinet.GetNullTerminatedString(data, ref dataPtr);
- if (diskNext == null)
- return null;
+ if (header.HeaderReservedSize > 0)
+ header.ReservedData = data.ReadBytes(header.HeaderReservedSize);
+ }
- header.DiskNext = diskNext;
+ if (header.Flags.HasFlag(HeaderFlags.PREV_CABINET))
+ {
+ header.CabinetPrev = data.ReadString(Encoding.ASCII);
+ header.DiskPrev = data.ReadString(Encoding.ASCII);
+ }
+
+ if (header.Flags.HasFlag(HeaderFlags.NEXT_CABINET))
+ {
+ header.CabinetNext = data.ReadString(Encoding.ASCII);
+ header.DiskNext = data.ReadString(Encoding.ASCII);
}
return header;
@@ -605,14 +678,14 @@ namespace BurnOutSharp.FileType
if (Flags.HasFlag(HeaderFlags.PREV_CABINET))
{
- Console.WriteLine($" CabinetPrev: {Encoding.ASCII.GetString(CabinetPrev).TrimEnd('\0')}");
- Console.WriteLine($" DiskPrev: {Encoding.ASCII.GetString(DiskPrev).TrimEnd('\0')}");
+ Console.WriteLine($" CabinetPrev: {CabinetPrev}");
+ Console.WriteLine($" DiskPrev: {DiskPrev}");
}
if (Flags.HasFlag(HeaderFlags.NEXT_CABINET))
{
- Console.WriteLine($" CabinetNext: {Encoding.ASCII.GetString(CabinetNext).TrimEnd('\0')}");
- Console.WriteLine($" DiskNext: {Encoding.ASCII.GetString(DiskNext).TrimEnd('\0')}");
+ Console.WriteLine($" CabinetNext: {CabinetNext}");
+ Console.WriteLine($" DiskNext: {DiskNext}");
}
Console.WriteLine();
@@ -622,7 +695,7 @@ namespace BurnOutSharp.FileType
}
[Flags]
- internal enum HeaderFlags : ushort
+ public enum HeaderFlags : ushort
{
///
/// The flag is set if this cabinet file is not the first in a set of cabinet files.
@@ -665,7 +738,7 @@ namespace BurnOutSharp.FileType
/// The typeCompress field can vary from one folder to the next, unless the folder is continued from a
/// previous cabinet file.
///
- internal class CFFOLDER
+ public class CFFOLDER
{
#region Properties
@@ -755,23 +828,19 @@ namespace BurnOutSharp.FileType
///
/// Deserialize at into a CFFOLDER object
///
- public static CFFOLDER Deserialize(byte[] data, ref int dataPtr, int basePtr, byte folderReservedSize, byte dataReservedSize)
+ public static CFFOLDER Deserialize(byte[] data, ref int dataPtr, int basePtr, CFHEADER header)
{
if (data == null || dataPtr < 0)
return null;
CFFOLDER folder = new CFFOLDER();
- folder.CabStartOffset = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
- folder.DataCount = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
- folder.CompressionType = (CompressionType)BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
+ folder.CabStartOffset = data.ReadUInt32(ref dataPtr);
+ folder.DataCount = data.ReadUInt16(ref dataPtr);
+ folder.CompressionType = (CompressionType)data.ReadUInt16(ref dataPtr);
- if (folderReservedSize > 0)
- {
- folder.ReservedData = new byte[folderReservedSize];
- Array.Copy(data, dataPtr, folder.ReservedData, 0, folderReservedSize);
- dataPtr += folderReservedSize;
- }
+ if (header.FolderReservedSize > 0)
+ folder.ReservedData = data.ReadBytes(ref dataPtr, header.FolderReservedSize);
if (folder.CabStartOffset > 0)
{
@@ -779,7 +848,7 @@ namespace BurnOutSharp.FileType
for (int i = 0; i < folder.DataCount; i++)
{
int offset = blockPtr;
- CFDATA dataBlock = CFDATA.Deserialize(data, ref blockPtr, dataReservedSize);
+ CFDATA dataBlock = CFDATA.Deserialize(data, ref blockPtr, header.DataReservedSize);
folder.DataBlocks[offset] = dataBlock;
}
}
@@ -787,6 +856,40 @@ namespace BurnOutSharp.FileType
return folder;
}
+ ///
+ /// Deserialize into a CFFOLDER object
+ ///
+ public static CFFOLDER Deserialize(Stream data, CFHEADER header)
+ {
+ if (data == null || data.Position < 0)
+ return null;
+
+ CFFOLDER folder = new CFFOLDER();
+
+ folder.CabStartOffset = data.ReadUInt32();
+ folder.DataCount = data.ReadUInt16();
+ folder.CompressionType = (CompressionType)data.ReadUInt16();
+
+ if (header.FolderReservedSize > 0)
+ folder.ReservedData = data.ReadBytes(header.FolderReservedSize);
+
+ if (folder.CabStartOffset > 0)
+ {
+ long currentPosition = data.Position;
+ data.Seek(folder.CabStartOffset, SeekOrigin.Begin);
+
+ for (int i = 0; i < folder.DataCount; i++)
+ {
+ CFDATA dataBlock = CFDATA.Deserialize(data, header.DataReservedSize);
+ folder.DataBlocks[(int)folder.CabStartOffset] = dataBlock;
+ }
+
+ data.Seek(currentPosition, SeekOrigin.Begin);
+ }
+
+ return folder;
+ }
+
#endregion
#region Public Functionality
@@ -807,7 +910,7 @@ namespace BurnOutSharp.FileType
#endregion
}
- internal enum CompressionType : ushort
+ public enum CompressionType : ushort
{
///
/// Mask for compression type.
@@ -844,7 +947,7 @@ namespace BurnOutSharp.FileType
/// continued from the previous cabinet will be first, and entries for files continued to the next cabinet
/// will be last.
///
- internal class CFFILE
+ public class CFFILE
{
#region Properties
@@ -896,38 +999,12 @@ namespace BurnOutSharp.FileType
/// CFFILE.szName field, but the _A_NAME_IS_UTF attribute is not set, the characters SHOULD be
/// interpreted according to the current location.
///
- public byte[] Name { get; private set; }
+ public string Name { get; private set; }
#endregion
#region Generated Properties
- ///
- /// Name value as a string (not null-terminated)
- ///
- public string NameAsString
- {
- get
- {
- // Perform sanity checks
- if (Name == null || Name.Length == 0)
- return null;
-
- // Attempt to respect the attribute flag for UTF-8
- if (Attributes.HasFlag(FileAttributes.NAME_IS_UTF))
- {
- try
- {
- return Encoding.UTF8.GetString(Name).TrimEnd('\0');
- }
- catch { }
- }
-
- // Default case uses local encoding
- return Encoding.Default.GetString(Name).TrimEnd('\0');
- }
- }
-
///
/// Convert the internal values into a DateTime object, if possible
///
@@ -935,17 +1012,28 @@ namespace BurnOutSharp.FileType
{
get
{
- // Date property
- int year = (Date >> 9) + 1980;
- int month = (Date >> 5) & 0x0F;
- int day = Date & 0x1F;
+ // If we have an invalid DateTime
+ if (Date == 0 && Time == 0)
+ return DateTime.MinValue;
- // Time property
- int hour = Time >> 11;
- int minute = (Time >> 5) & 0x3F;
- int second = (Time << 1) & 0x3E;
+ try
+ {
+ // Date property
+ int year = (Date >> 9) + 1980;
+ int month = (Date >> 5) & 0x0F;
+ int day = Date & 0x1F;
- return new DateTime(year, month, day, hour, minute, second);
+ // Time property
+ int hour = Time >> 11;
+ int minute = (Time >> 5) & 0x3F;
+ int second = (Time << 1) & 0x3E;
+
+ return new DateTime(year, month, day, hour, minute, second);
+ }
+ catch
+ {
+ return DateTime.MinValue;
+ }
}
set
{
@@ -968,18 +1056,42 @@ namespace BurnOutSharp.FileType
CFFILE file = new CFFILE();
- file.FileSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
- file.FolderStartOffset = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
- file.FolderIndex = (FolderIndex)BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
- file.Date = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
- file.Time = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
- file.Attributes = (FileAttributes)BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
+ file.FileSize = data.ReadUInt32(ref dataPtr);
+ file.FolderStartOffset = data.ReadUInt32(ref dataPtr);
+ file.FolderIndex = (FolderIndex)data.ReadUInt16(ref dataPtr);
+ file.Date = data.ReadUInt16(ref dataPtr);
+ file.Time = data.ReadUInt16(ref dataPtr);
+ file.Attributes = (FileAttributes)data.ReadUInt16(ref dataPtr);
- byte[] name = MSCABCabinet.GetNullTerminatedString(data, ref dataPtr);
- if (name == null)
+ if (file.Attributes.HasFlag(FileAttributes.NAME_IS_UTF))
+ file.Name = data.ReadString(ref dataPtr, Encoding.Unicode);
+ else
+ file.Name = data.ReadString(ref dataPtr, Encoding.ASCII);
+
+ return file;
+ }
+
+ ///
+ /// Deserialize into a CFFILE object
+ ///
+ public static CFFILE Deserialize(Stream data)
+ {
+ if (data == null || data.Position < 0)
return null;
- file.Name = name;
+ CFFILE file = new CFFILE();
+
+ file.FileSize = data.ReadUInt32();
+ file.FolderStartOffset = data.ReadUInt32();
+ file.FolderIndex = (FolderIndex)data.ReadUInt16();
+ file.Date = data.ReadUInt16();
+ file.Time = data.ReadUInt16();
+ file.Attributes = (FileAttributes)data.ReadUInt16();
+
+ if (file.Attributes.HasFlag(FileAttributes.NAME_IS_UTF))
+ file.Name = data.ReadString(Encoding.Unicode);
+ else
+ file.Name = data.ReadString(Encoding.ASCII);
return file;
}
@@ -998,7 +1110,7 @@ namespace BurnOutSharp.FileType
Console.WriteLine($" FolderIndex: {FolderIndex} (0x{(ushort)FolderIndex:X4})");
Console.WriteLine($" DateTime: {DateAndTimeAsDateTime} (0x{Date:X4} 0x{Time:X4})");
Console.WriteLine($" Attributes: {Attributes} (0x{(ushort)Attributes:X4})");
- Console.WriteLine($" Name: {NameAsString}");
+ Console.WriteLine($" Name: {Name}");
Console.WriteLine();
}
@@ -1006,7 +1118,7 @@ namespace BurnOutSharp.FileType
#endregion
}
- internal enum FolderIndex : ushort
+ public enum FolderIndex : ushort
{
///
/// A value of zero indicates that this is the
@@ -1034,7 +1146,7 @@ namespace BurnOutSharp.FileType
}
[Flags]
- internal enum FileAttributes : ushort
+ public enum FileAttributes : ushort
{
///
/// File is read-only.
@@ -1073,7 +1185,7 @@ namespace BurnOutSharp.FileType
/// field. Subsequent CFDATA structure records for this folder are
/// contiguous.
///
- internal class CFDATA
+ public class CFDATA
{
#region Properties
@@ -1128,26 +1240,44 @@ namespace BurnOutSharp.FileType
CFDATA dataBlock = new CFDATA();
- dataBlock.Checksum = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
- dataBlock.CompressedSize = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
- dataBlock.UncompressedSize = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
+ dataBlock.Checksum = data.ReadUInt32(ref dataPtr);
+ dataBlock.CompressedSize = data.ReadUInt16(ref dataPtr);
+ dataBlock.UncompressedSize = data.ReadUInt16(ref dataPtr);
if (dataBlock.UncompressedSize != 0 && dataBlock.CompressedSize > dataBlock.UncompressedSize)
return null;
if (dataReservedSize > 0)
- {
- dataBlock.ReservedData = new byte[dataReservedSize];
- Array.Copy(data, dataPtr, dataBlock.ReservedData, 0, dataReservedSize);
- dataPtr += dataReservedSize;
- }
+ dataBlock.ReservedData = data.ReadBytes(ref dataPtr, dataReservedSize);
if (dataBlock.CompressedSize > 0)
- {
- dataBlock.CompressedData = new byte[dataBlock.CompressedSize];
- Array.Copy(data, dataPtr, dataBlock.CompressedData, 0, dataBlock.CompressedSize);
- dataPtr += dataBlock.CompressedSize;
- }
+ dataBlock.CompressedData = data.ReadBytes(ref dataPtr, dataBlock.CompressedSize);
+
+ return dataBlock;
+ }
+
+ ///
+ /// Deserialize into a CFDATA object
+ ///
+ public static CFDATA Deserialize(Stream data, byte dataReservedSize = 0)
+ {
+ if (data == null || data.Position < 0)
+ return null;
+
+ CFDATA dataBlock = new CFDATA();
+
+ dataBlock.Checksum = data.ReadUInt32();
+ dataBlock.CompressedSize = data.ReadUInt16();
+ dataBlock.UncompressedSize = data.ReadUInt16();
+
+ if (dataBlock.UncompressedSize != 0 && dataBlock.CompressedSize > dataBlock.UncompressedSize)
+ return null;
+
+ if (dataReservedSize > 0)
+ dataBlock.ReservedData = data.ReadBytes(dataReservedSize);
+
+ if (dataBlock.CompressedSize > 0)
+ dataBlock.CompressedData = data.ReadBytes(dataBlock.CompressedSize);
return dataBlock;
}
@@ -1161,7 +1291,7 @@ namespace BurnOutSharp.FileType
/// not supplied by the cabinet file creating application, the checksum field is set to 0 (zero). Cabinet
/// extracting applications do not compute or verify the checksum if the field is set to 0 (zero).
///
- internal static class Checksum
+ public static class Checksum
{
public static uint ChecksumData(byte[] data)
{
diff --git a/BurnOutSharp/Tools/Extensions.cs b/BurnOutSharp/Tools/Extensions.cs
index ab8563c4..c5261c56 100644
--- a/BurnOutSharp/Tools/Extensions.cs
+++ b/BurnOutSharp/Tools/Extensions.cs
@@ -336,7 +336,7 @@ namespace BurnOutSharp.Tools
List tempBuffer = new List();
byte[] buffer = new byte[charWidth];
- while (stream.Read(buffer, 0, charWidth) != 0 && buffer.SequenceEqual(nullTerminator))
+ while (stream.Read(buffer, 0, charWidth) != 0 && !buffer.SequenceEqual(nullTerminator))
{
tempBuffer.AddRange(buffer);
}
diff --git a/Test/Program.cs b/Test/Program.cs
index 884dbba3..4edbf1be 100644
--- a/Test/Program.cs
+++ b/Test/Program.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using BurnOutSharp;
+using BurnOutSharp.FileType;
using BurnOutSharp.Wrappers;
using static BurnOutSharp.Builder.Extensions;
@@ -80,7 +81,7 @@ namespace Test
foreach (string inputPath in inputPaths)
{
if (info)
- PrintExecutableInfo(inputPath);
+ PrintPathInfo(inputPath);
else
GetAndWriteProtections(scanner, inputPath);
}
@@ -177,10 +178,10 @@ namespace Test
#region Printing
///
- /// Wrapper to print executable information for a single path
+ /// Wrapper to print information for a single path
///
/// File or directory path
- private static void PrintExecutableInfo(string path)
+ private static void PrintPathInfo(string path)
{
Console.WriteLine($"Checking possible path: {path}");
@@ -210,93 +211,128 @@ namespace Test
using (Stream stream = File.OpenRead(file))
{
// Read the first 4 bytes
- byte[] magic = stream.ReadBytes(2);
-
- if (!IsMSDOS(magic))
- {
- Console.WriteLine("Not a recognized executable format, skipping...");
- Console.WriteLine();
- return;
- }
-
- // Build the executable information
- Console.WriteLine("Creating MS-DOS executable builder");
- Console.WriteLine();
-
+ byte[] magic = stream.ReadBytes(4);
stream.Seek(0, SeekOrigin.Begin);
- var msdos = MSDOS.Create(stream);
- if (msdos == null)
+
+ // MS-DOS executable and decendents
+ if (IsMSDOS(magic))
{
- Console.WriteLine("Something went wrong parsing MS-DOS executable");
+ // Build the executable information
+ Console.WriteLine("Creating MS-DOS executable builder");
Console.WriteLine();
- return;
- }
- // Print the executable info to screen
- msdos.Print();
-
- // Check for a valid new executable address
- if (msdos.NewExeHeaderAddr >= stream.Length)
- {
- Console.WriteLine("New EXE header address invalid, skipping additional reading...");
- Console.WriteLine();
- return;
- }
-
- // Try to read the executable info
- stream.Seek(msdos.NewExeHeaderAddr, SeekOrigin.Begin);
- magic = stream.ReadBytes(4);
-
- // New Executable
- if (IsNE(magic))
- {
- stream.Seek(0, SeekOrigin.Begin);
- var newExecutable = NewExecutable.Create(stream);
- if (newExecutable == null)
+ var msdos = MSDOS.Create(stream);
+ if (msdos == null)
{
- Console.WriteLine("Something went wrong parsing New Executable");
+ Console.WriteLine("Something went wrong parsing MS-DOS executable");
Console.WriteLine();
return;
}
// Print the executable info to screen
- newExecutable.Print();
- }
+ msdos.Print();
- // Linear Executable
- else if (IsLE(magic))
- {
- Console.WriteLine($"Linear executable found. No parsing currently available.");
- Console.WriteLine();
- return;
- }
-
- // Portable Executable
- else if (IsPE(magic))
- {
- stream.Seek(0, SeekOrigin.Begin);
- var portableExecutable = PortableExecutable.Create(stream);
- if (portableExecutable == null)
+ // Check for a valid new executable address
+ if (msdos.NewExeHeaderAddr >= stream.Length)
{
- Console.WriteLine("Something went wrong parsing Portable Executable");
+ Console.WriteLine("New EXE header address invalid, skipping additional reading...");
Console.WriteLine();
return;
}
- // Print the executable info to screen
- portableExecutable.Print();
+ // Try to read the executable info
+ stream.Seek(msdos.NewExeHeaderAddr, SeekOrigin.Begin);
+ magic = stream.ReadBytes(4);
+
+ // New Executable
+ if (IsNE(magic))
+ {
+ stream.Seek(0, SeekOrigin.Begin);
+ var newExecutable = NewExecutable.Create(stream);
+ if (newExecutable == null)
+ {
+ Console.WriteLine("Something went wrong parsing New Executable");
+ Console.WriteLine();
+ return;
+ }
+
+ // Print the executable info to screen
+ newExecutable.Print();
+ }
+
+ // Linear Executable
+ else if (IsLE(magic))
+ {
+ Console.WriteLine($"Linear executable found. No parsing currently available.");
+ Console.WriteLine();
+ return;
+ }
+
+ // Portable Executable
+ else if (IsPE(magic))
+ {
+ stream.Seek(0, SeekOrigin.Begin);
+ var portableExecutable = PortableExecutable.Create(stream);
+ if (portableExecutable == null)
+ {
+ Console.WriteLine("Something went wrong parsing Portable Executable");
+ Console.WriteLine();
+ return;
+ }
+
+ // Print the executable info to screen
+ portableExecutable.Print();
+ }
+
+ // Unknown
+ else
+ {
+ Console.WriteLine($"Unrecognized header signature: {BitConverter.ToString(magic).Replace("-", string.Empty)}");
+ Console.WriteLine();
+ return;
+ }
}
- // Unknown
+ // MS-CAB archive
+ else if (IsMSCAB(magic))
+ {
+ // Build the cabinet information
+ Console.WriteLine("Creating MS-CAB deserializer");
+ Console.WriteLine();
+
+ var cabinet = MSCABCabinet.Deserialize(stream);
+ if (cabinet == null)
+ {
+ Console.WriteLine("Something went wrong parsing MS-CAB archive");
+ Console.WriteLine();
+ return;
+ }
+
+ // Print the cabinet info to screen
+ cabinet.PrintInfo();
+ }
+
+ // Everything else
else
{
- Console.WriteLine($"Unrecognized header signature: {BitConverter.ToString(magic).Replace("-", string.Empty)}");
+ Console.WriteLine("Not a recognized file format, skipping...");
Console.WriteLine();
return;
}
}
}
+ ///
+ /// Determine if the magic bytes indicate an MS-CAB archive
+ ///
+ private static bool IsMSCAB(byte[] magic)
+ {
+ if (magic == null || magic.Length < 4)
+ return false;
+
+ return magic[0] == 'M' && magic[1] == 'S' && magic[2] == 'C' && magic[3] == 'F';
+ }
+
///
/// Determine if the magic bytes indicate an MS-DOS executable
///