diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index 18a67633..d5fd1a52 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -53,7 +53,7 @@ TODO: Streaming tapes contain a file block that describes the files and an optional partition block that describes the tape partitions. - TODO: There are also blocks for image metadata, contents metadata and dump hardware information. + There are also blocks for image metadata, contents metadata and dump hardware information. A differencing image will have all the metadata and deduplication tables, but the entries in these ones will be set to 0 if the block is stored in the parent image. TODO: This is not yet implemented. @@ -777,6 +777,125 @@ namespace DiscImageChef.DiscImages CicmMetadata = null; } + break; + // Dump hardware block + case BlockType.DumpHardwareBlock: + DumpHardwareHeader dumpBlock = new DumpHardwareHeader(); + structureBytes = new byte[Marshal.SizeOf(dumpBlock)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(dumpBlock)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(dumpBlock)); + dumpBlock = (DumpHardwareHeader)Marshal.PtrToStructure(structurePointer, + typeof(DumpHardwareHeader)); + Marshal.FreeHGlobal(structurePointer); + if(dumpBlock.identifier != BlockType.DumpHardwareBlock) break; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Found dump hardware block at position {0}", entry.offset); + + structureBytes = new byte[dumpBlock.length]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + Crc64Context.Data(structureBytes, out byte[] dumpCrc); + if(BitConverter.ToUInt64(dumpCrc, 0) != dumpBlock.crc64) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", + BitConverter.ToUInt64(dumpCrc, 0), dumpBlock.crc64); + break; + } + + imageStream.Position -= structureBytes.Length; + + DumpHardware = new List(); + + for(ushort i = 0; i < dumpBlock.entries; i++) + { + DumpHardwareEntry dumpEntry = new DumpHardwareEntry(); + structureBytes = new byte[Marshal.SizeOf(dumpEntry)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(dumpEntry)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(dumpEntry)); + dumpEntry = (DumpHardwareEntry)Marshal.PtrToStructure(structurePointer, + typeof(DumpHardwareEntry)); + Marshal.FreeHGlobal(structurePointer); + + DumpHardwareType dump = new DumpHardwareType + { + Software = new SoftwareType(), + Extents = new ExtentType[dumpEntry.extents] + }; + + byte[] tmp; + + if(dumpEntry.manufacturerLength > 0) + { + tmp = new byte[dumpEntry.manufacturerLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Manufacturer = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.modelLength > 0) + { + tmp = new byte[dumpEntry.modelLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Model = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.revisionLength > 0) + { + tmp = new byte[dumpEntry.revisionLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Revision = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.firmwareLength > 0) + { + tmp = new byte[dumpEntry.firmwareLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Firmware = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.serialLength > 0) + { + tmp = new byte[dumpEntry.serialLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Serial = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.softwareNameLength > 0) + { + tmp = new byte[dumpEntry.softwareNameLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Software.Name = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.softwareVersionLength > 0) + { + tmp = new byte[dumpEntry.softwareVersionLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Software.Version = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.softwareOperatingSystemLength > 0) + { + tmp = new byte[dumpEntry.softwareOperatingSystemLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Software.OperatingSystem = Encoding.UTF8.GetString(tmp); + } + + tmp = new byte[16]; + for(uint j = 0; j < dumpEntry.extents; j++) + { + imageStream.Read(tmp, 0, tmp.Length); + dump.Extents[0].Start = BitConverter.ToUInt64(tmp, 0); + dump.Extents[0].End = BitConverter.ToUInt64(tmp, 8); + } + + dump.Extents = dump.Extents.OrderBy(t => t.Start).ToArray(); + if(dump.Extents.Length > 0) DumpHardware.Add(dump); + } + + if(DumpHardware.Count == 0) DumpHardware = null; break; } } @@ -2083,8 +2202,8 @@ namespace DiscImageChef.DiscImages imageStream.Read(structureBytes, 0, structureBytes.Length); structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(cicmBlock)); Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(cicmBlock)); - cicmBlock = - (CicmMetadataBlock)Marshal.PtrToStructure(structurePointer, typeof(GeometryBlock)); + cicmBlock = (CicmMetadataBlock)Marshal.PtrToStructure(structurePointer, + typeof(CicmMetadataBlock)); Marshal.FreeHGlobal(structurePointer); if(cicmBlock.identifier != BlockType.CicmBlock) break; @@ -2109,6 +2228,125 @@ namespace DiscImageChef.DiscImages CicmMetadata = null; } + break; + // Dump hardware block + case BlockType.DumpHardwareBlock: + DumpHardwareHeader dumpBlock = new DumpHardwareHeader(); + structureBytes = new byte[Marshal.SizeOf(dumpBlock)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(dumpBlock)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(dumpBlock)); + dumpBlock = (DumpHardwareHeader)Marshal.PtrToStructure(structurePointer, + typeof(DumpHardwareHeader)); + Marshal.FreeHGlobal(structurePointer); + if(dumpBlock.identifier != BlockType.DumpHardwareBlock) break; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Found dump hardware block at position {0}", entry.offset); + + structureBytes = new byte[dumpBlock.length]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + Crc64Context.Data(structureBytes, out byte[] dumpCrc); + if(BitConverter.ToUInt64(dumpCrc, 0) != dumpBlock.crc64) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", + BitConverter.ToUInt64(dumpCrc, 0), dumpBlock.crc64); + break; + } + + imageStream.Position -= structureBytes.Length; + + DumpHardware = new List(); + + for(ushort i = 0; i < dumpBlock.entries; i++) + { + DumpHardwareEntry dumpEntry = new DumpHardwareEntry(); + structureBytes = new byte[Marshal.SizeOf(dumpEntry)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(dumpEntry)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(dumpEntry)); + dumpEntry = (DumpHardwareEntry)Marshal.PtrToStructure(structurePointer, + typeof(DumpHardwareEntry)); + Marshal.FreeHGlobal(structurePointer); + + DumpHardwareType dump = new DumpHardwareType + { + Software = new SoftwareType(), + Extents = new ExtentType[dumpEntry.extents] + }; + + byte[] tmp; + + if(dumpEntry.manufacturerLength > 0) + { + tmp = new byte[dumpEntry.manufacturerLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Manufacturer = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.modelLength > 0) + { + tmp = new byte[dumpEntry.modelLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Model = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.revisionLength > 0) + { + tmp = new byte[dumpEntry.revisionLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Revision = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.firmwareLength > 0) + { + tmp = new byte[dumpEntry.firmwareLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Firmware = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.serialLength > 0) + { + tmp = new byte[dumpEntry.serialLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Serial = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.softwareNameLength > 0) + { + tmp = new byte[dumpEntry.softwareNameLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Software.Name = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.softwareVersionLength > 0) + { + tmp = new byte[dumpEntry.softwareVersionLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Software.Version = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.softwareOperatingSystemLength > 0) + { + tmp = new byte[dumpEntry.softwareOperatingSystemLength - 1]; + imageStream.Read(tmp, 0, tmp.Length); + dump.Software.OperatingSystem = Encoding.UTF8.GetString(tmp); + } + + tmp = new byte[16]; + for(uint j = 0; j < dumpEntry.extents; j++) + { + imageStream.Read(tmp, 0, tmp.Length); + dump.Extents[0].Start = BitConverter.ToUInt64(tmp, 0); + dump.Extents[0].End = BitConverter.ToUInt64(tmp, 8); + } + + dump.Extents = dump.Extents.OrderBy(t => t.Start).ToArray(); + if(dump.Extents.Length > 0) DumpHardware.Add(dump); + } + + if(DumpHardware.Count == 0) DumpHardware = null; break; } } @@ -2804,6 +3042,145 @@ namespace DiscImageChef.DiscImages index.Add(idxEntry); } + // If we have dump hardware, write it + if(DumpHardware != null) + { + MemoryStream dumpMs = new MemoryStream(); + foreach(DumpHardwareType dump in DumpHardware) + { + byte[] dumpManufacturer = null; + byte[] dumpModel = null; + byte[] dumpRevision = null; + byte[] dumpFirmware = null; + byte[] dumpSerial = null; + byte[] dumpSoftwareName = null; + byte[] dumpSoftwareVersion = null; + byte[] dumpSoftwareOperatingSystem = null; + + if(!string.IsNullOrWhiteSpace(dump.Manufacturer)) + dumpManufacturer = + Encoding.UTF8.GetBytes(dump.Manufacturer); + if(!string.IsNullOrWhiteSpace(dump.Model)) dumpModel = Encoding.UTF8.GetBytes(dump.Model); + if(!string.IsNullOrWhiteSpace(dump.Revision)) dumpRevision = Encoding.UTF8.GetBytes(dump.Revision); + if(!string.IsNullOrWhiteSpace(dump.Firmware)) dumpFirmware = Encoding.UTF8.GetBytes(dump.Firmware); + if(!string.IsNullOrWhiteSpace(dump.Serial)) dumpSerial = Encoding.UTF8.GetBytes(dump.Serial); + if(dump.Software != null) + { + if(!string.IsNullOrWhiteSpace(dump.Software.Name)) + dumpSoftwareName = Encoding.UTF8.GetBytes(dump.Software.Name); + if(!string.IsNullOrWhiteSpace(dump.Software.Version)) + dumpSoftwareVersion = Encoding.UTF8.GetBytes(dump.Software.Version); + if(!string.IsNullOrWhiteSpace(dump.Software.OperatingSystem)) + dumpSoftwareOperatingSystem = Encoding.UTF8.GetBytes(dump.Software.OperatingSystem); + } + + DumpHardwareEntry dumpEntry = new DumpHardwareEntry + { + manufacturerLength = (uint)(dumpManufacturer?.Length + 1 ?? 0), + modelLength = (uint)(dumpModel?.Length + 1 ?? 0), + revisionLength = (uint)(dumpRevision?.Length + 1 ?? 0), + firmwareLength = (uint)(dumpFirmware?.Length + 1 ?? 0), + serialLength = (uint)(dumpSerial?.Length + 1 ?? 0), + softwareNameLength = (uint)(dumpSoftwareName?.Length + 1 ?? 0), + softwareVersionLength = (uint)(dumpSoftwareVersion?.Length + 1 ?? 0), + softwareOperatingSystemLength = (uint)(dumpSoftwareOperatingSystem?.Length + 1 ?? 0), + extents = (uint)dump.Extents.Length + }; + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(dumpEntry)); + structureBytes = new byte[Marshal.SizeOf(dumpEntry)]; + Marshal.StructureToPtr(dumpEntry, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + dumpMs.Write(structureBytes, 0, structureBytes.Length); + + if(dumpManufacturer != null) + { + dumpMs.Write(dumpManufacturer, 0, dumpManufacturer.Length); + dumpMs.WriteByte(0); + } + + if(dumpModel != null) + { + dumpMs.Write(dumpModel, 0, dumpModel.Length); + dumpMs.WriteByte(0); + } + + if(dumpRevision != null) + { + dumpMs.Write(dumpRevision, 0, dumpRevision.Length); + dumpMs.WriteByte(0); + } + + if(dumpFirmware != null) + { + dumpMs.Write(dumpFirmware, 0, dumpFirmware.Length); + dumpMs.WriteByte(0); + } + + if(dumpSerial != null) + { + dumpMs.Write(dumpSerial, 0, dumpSerial.Length); + dumpMs.WriteByte(0); + } + + if(dumpSoftwareName != null) + { + dumpMs.Write(dumpSoftwareName, 0, dumpSoftwareName.Length); + dumpMs.WriteByte(0); + } + + if(dumpSoftwareVersion != null) + { + dumpMs.Write(dumpSoftwareVersion, 0, dumpSoftwareVersion.Length); + dumpMs.WriteByte(0); + } + + if(dumpSoftwareOperatingSystem != null) + { + dumpMs.Write(dumpSoftwareOperatingSystem, 0, dumpSoftwareOperatingSystem.Length); + dumpMs.WriteByte(0); + } + + foreach(ExtentType extent in dump.Extents) + { + dumpMs.Write(BitConverter.GetBytes(extent.Start), 0, sizeof(ulong)); + dumpMs.Write(BitConverter.GetBytes(extent.End), 0, sizeof(ulong)); + } + } + + idxEntry = new IndexEntry + { + blockType = BlockType.DumpHardwareBlock, + dataType = DataType.NoData, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing dump hardware block to position {0}", + idxEntry.offset); + + Crc64Context.Data(dumpMs.ToArray(), out byte[] dumpCrc); + DumpHardwareHeader dumpBlock = new DumpHardwareHeader + { + identifier = BlockType.DumpHardwareBlock, + entries = (ushort)DumpHardware.Count, + crc64 = BitConverter.ToUInt64(dumpCrc, 0), + length = (uint)dumpMs.Length + }; + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(dumpBlock)); + structureBytes = new byte[Marshal.SizeOf(dumpBlock)]; + Marshal.StructureToPtr(dumpBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + imageStream.Write(dumpMs.ToArray(), 0, (int)dumpMs.Length); + + index.RemoveAll(t => t.blockType == BlockType.DumpHardwareBlock && t.dataType == DataType.NoData); + + index.Add(idxEntry); + } + // If we have CICM XML metadata, write it if(CicmMetadata != null) { @@ -3577,8 +3954,8 @@ namespace DiscImageChef.DiscImages public bool SetDumpHardware(List dumpHardware) { - // TODO: Implement - return false; + DumpHardware = dumpHardware; + return true; } public bool SetCicmMetadata(CICMMetadataType metadata) @@ -4137,7 +4514,7 @@ namespace DiscImageChef.DiscImages SnapshotBlock = 0x50414E53, /// TODO: Block containing how to locate the parent image ParentBlock = 0x50524E54, - /// TODO: Block containing an array of hardware used to create the image + /// Block containing an array of hardware used to create the image DumpHardwareBlock = 0x2A504D44, /// TODO: Block containing list of files for a tape image TapeFileBlock = 0x454C4654 @@ -4356,5 +4733,51 @@ namespace DiscImageChef.DiscImages public BlockType identifier; public uint length; } + + /// Dump hardware block, contains a list of hardware used to dump the media on this image + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DumpHardwareHeader + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public ushort entries; + /// Size of the whole block, not including this header, in bytes + public uint length; + /// CRC64-ECMA of the block + public ulong crc64; + } + + /// Dump hardware entry, contains length of strings that follow, in the same order as the length, this structure + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DumpHardwareEntry + { + /// Length of UTF-8 manufacturer string + public uint manufacturerLength; + /// Length of UTF-8 model string + public uint modelLength; + /// Length of UTF-8 revision string + public uint revisionLength; + /// Length of UTF-8 firmware version string + public uint firmwareLength; + /// Length of UTF-8 serial string + public uint serialLength; + /// Length of UTF-8 software name string + public uint softwareNameLength; + /// Length of UTF-8 software version string + public uint softwareVersionLength; + /// Length of UTF-8 software operating system string + public uint softwareOperatingSystemLength; + /// How many extents are after the strings + public uint extents; + } + + /// Dump hardware extent, first and last sector dumped, both inclusive + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DumpHardwareExtent + { + public ulong start; + public ulong end; + } } } \ No newline at end of file diff --git a/templates/dicformat.bt b/templates/dicformat.bt index 9ccf3d2c..0eac4e2c 100644 --- a/templates/dicformat.bt +++ b/templates/dicformat.bt @@ -766,6 +766,43 @@ typedef struct char xml[length]; } CicmMetadataBlock; +typedef struct +{ + uint64 start; + uint64 end; +} DumpHardwareExtent; + +typedef struct +{ + uint manufacturerLength; + uint modelLength; + uint revisionLength; + uint firmwareLength; + uint serialLength; + uint softwareNameLength; + uint softwareVersionLength; + uint softwareOperatingSystemLength; + uint extentCount; + char manufacturer[manufacturerLength]; + char model[modelLength]; + char revision[revisionLength]; + char firmware[firmwareLength]; + char serial[serialLength]; + char softwareName[softwareNameLength]; + char softwareVersion[softwareVersionLength]; + char softwareOperatingSystem[softwareOperatingSystemLength]; + DumpHardwareExtent extents[extentCount]; +} DumpHardwareEntry; + +typedef struct +{ + BlockType identifier; + ushort entries; + uint length; + uint64 crc64 ; + DumpHardwareEntry dumpHardware[entries]; +} DumpHardwareHeader; + LittleEndian(); local int i; @@ -859,5 +896,8 @@ for(i = 0; i < index.entries; i++) case 0x4D434943: CicmMetadataBlock cicmMetadata; break; + case 0x2A504D44: + DumpHardwareHeader dumpHardware; + break; } } \ No newline at end of file