diff --git a/DiscImageChef.DiscImages/QCOW2.cs b/DiscImageChef.DiscImages/QCOW2.cs index 8ef5ef97..440ddc42 100644 --- a/DiscImageChef.DiscImages/QCOW2.cs +++ b/DiscImageChef.DiscImages/QCOW2.cs @@ -788,6 +788,8 @@ namespace DiscImageChef.DiscImages for(long i = 0; i < l1Table.LongLength; i++) writingStream.Write(BigEndianBitConverter.GetBytes(l1Table[i]), 0, 8); + IsWriting = false; + ErrorMessage = ""; return true; } diff --git a/DiscImageChef.DiscImages/UDIF.cs b/DiscImageChef.DiscImages/UDIF.cs index 29f40882..e6b26212 100644 --- a/DiscImageChef.DiscImages/UDIF.cs +++ b/DiscImageChef.DiscImages/UDIF.cs @@ -35,8 +35,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Text; using Claunia.PropertyList; using Claunia.RsrcFork; +using DiscImageChef.Checksums; using DiscImageChef.CommonTypes; using DiscImageChef.Console; using DiscImageChef.Filters; @@ -49,68 +51,84 @@ using Version = Resources.Version; namespace DiscImageChef.DiscImages { - public class Udif : IMediaImage + public class Udif : IWritableImage { - const uint UDIF_SIGNATURE = 0x6B6F6C79; + const uint UDIF_SIGNATURE = 0x6B6F6C79; const uint CHUNK_SIGNATURE = 0x6D697368; // All chunk types with this mask are compressed const uint CHUNK_TYPE_COMPRESSED_MASK = 0x80000000; - const uint CHUNK_TYPE_ZERO = 0x00000000; - const uint CHUNK_TYPE_COPY = 0x00000001; - const uint CHUNK_TYPE_NOCOPY = 0x00000002; + const uint CHUNK_TYPE_ZERO = 0x00000000; + const uint CHUNK_TYPE_COPY = 0x00000001; + const uint CHUNK_TYPE_NOCOPY = 0x00000002; const uint CHUNK_TYPE_KENCODE = 0x80000001; - const uint CHUNK_TYPE_RLE = 0x80000002; - const uint CHUNK_TYPE_LZH = 0x80000003; - const uint CHUNK_TYPE_ADC = 0x80000004; - const uint CHUNK_TYPE_ZLIB = 0x80000005; - const uint CHUNK_TYPE_BZIP = 0x80000006; - const uint CHUNK_TYPE_LZFSE = 0x80000007; - const uint CHUNK_TYPE_COMMNT = 0x7FFFFFFE; - const uint CHUNK_TYPE_END = 0xFFFFFFFF; + const uint CHUNK_TYPE_RLE = 0x80000002; + const uint CHUNK_TYPE_LZH = 0x80000003; + const uint CHUNK_TYPE_ADC = 0x80000004; + const uint CHUNK_TYPE_ZLIB = 0x80000005; + const uint CHUNK_TYPE_BZIP = 0x80000006; + const uint CHUNK_TYPE_LZFSE = 0x80000007; + const uint CHUNK_TYPE_COMMNT = 0x7FFFFFFE; + const uint CHUNK_TYPE_END = 0xFFFFFFFF; - const string RESOURCE_FORK_KEY = "resource-fork"; - const string BLOCK_KEY = "blkx"; - const uint BLOCK_OS_TYPE = 0x626C6B78; - const uint MAX_CACHE_SIZE = 16777216; - const uint SECTOR_SIZE = 512; - const uint MAX_CACHED_SECTORS = MAX_CACHE_SIZE / SECTOR_SIZE; - uint buffersize; - Dictionary chunkCache; + const uint UDIF_CHECKSUM_TYPE_NONE = 0; + const uint UDIF_CHECKSUM_TYPE_CRC28 = 1; + const uint UDIF_CHECKSUM_TYPE_CRC32 = 2; + const uint UDIF_CHECKSUM_TYPE_DC42 = 3; + const uint UDIF_CHECKSUM_TYPE_MD5 = 4; + const uint UDIF_CHECKSUM_TYPE_SHA = 5; + const uint UDIF_CHECKSUM_TYPE_SHA1 = 6; + const uint UDIF_CHECKSUM_TYPE_SHA256 = 7; + const uint UDIF_CHECKSUM_TYPE_SHA384 = 8; + const uint UDIF_CHECKSUM_TYPE_SHA512 = 9; + + const string RESOURCE_FORK_KEY = "resource-fork"; + const string BLOCK_KEY = "blkx"; + const uint BLOCK_OS_TYPE = 0x626C6B78; + const uint MAX_CACHE_SIZE = 16777216; + const uint SECTOR_SIZE = 512; + const uint MAX_CACHED_SECTORS = MAX_CACHE_SIZE / SECTOR_SIZE; + uint buffersize; + Dictionary chunkCache; Dictionary chunks; - uint currentChunkCacheSize; + BlockChunk currentChunk; + uint currentChunkCacheSize; + ulong currentSector; + Crc32Context dataForkChecksum; UdifFooter footer; - ImageInfo imageInfo; + ImageInfo imageInfo; - Stream imageStream; + Stream imageStream; + Crc32Context masterChecksum; Dictionary sectorCache; + FileStream writingStream; public Udif() { imageInfo = new ImageInfo { - ReadableSectorTags = new List(), - ReadableMediaTags = new List(), - HasPartitions = false, - HasSessions = false, - Version = null, - Application = null, - ApplicationVersion = null, - Creator = null, - Comments = null, - MediaManufacturer = null, - MediaModel = null, - MediaSerialNumber = null, - MediaBarcode = null, - MediaPartNumber = null, - MediaSequence = 0, - LastMediaSequence = 0, - DriveManufacturer = null, - DriveModel = null, - DriveSerialNumber = null, + ReadableSectorTags = new List(), + ReadableMediaTags = new List(), + HasPartitions = false, + HasSessions = false, + Version = null, + Application = null, + ApplicationVersion = null, + Creator = null, + Comments = null, + MediaManufacturer = null, + MediaModel = null, + MediaSerialNumber = null, + MediaBarcode = null, + MediaPartNumber = null, + MediaSequence = 0, + LastMediaSequence = 0, + DriveManufacturer = null, + DriveModel = null, + DriveSerialNumber = null, DriveFirmwareRevision = null }; } @@ -118,7 +136,7 @@ namespace DiscImageChef.DiscImages public ImageInfo Info => imageInfo; public string Name => "Apple Universal Disk Image Format"; - public Guid Id => new Guid("5BEB9002-CF3D-429C-8E06-9A96F49203FF"); + public Guid Id => new Guid("5BEB9002-CF3D-429C-8E06-9A96F49203FF"); public string Format => "Apple Universal Disk Image Format"; @@ -180,28 +198,28 @@ namespace DiscImageChef.DiscImages DicConsole.VerboseWriteLine("Found obsolete UDIF format."); } - DicConsole.DebugWriteLine("UDIF plugin", "footer.signature = 0x{0:X8}", footer.signature); - DicConsole.DebugWriteLine("UDIF plugin", "footer.version = {0}", footer.version); - DicConsole.DebugWriteLine("UDIF plugin", "footer.headerSize = {0}", footer.headerSize); - DicConsole.DebugWriteLine("UDIF plugin", "footer.flags = {0}", footer.flags); + DicConsole.DebugWriteLine("UDIF plugin", "footer.signature = 0x{0:X8}", footer.signature); + DicConsole.DebugWriteLine("UDIF plugin", "footer.version = {0}", footer.version); + DicConsole.DebugWriteLine("UDIF plugin", "footer.headerSize = {0}", footer.headerSize); + DicConsole.DebugWriteLine("UDIF plugin", "footer.flags = {0}", footer.flags); DicConsole.DebugWriteLine("UDIF plugin", "footer.runningDataForkOff = {0}", footer.runningDataForkOff); - DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkOff = {0}", footer.dataForkOff); - DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen); - DicConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkOff = {0}", footer.rsrcForkOff); - DicConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkLen = {0}", footer.rsrcForkLen); - DicConsole.DebugWriteLine("UDIF plugin", "footer.segmentNumber = {0}", footer.segmentNumber); - DicConsole.DebugWriteLine("UDIF plugin", "footer.segmentCount = {0}", footer.segmentCount); - DicConsole.DebugWriteLine("UDIF plugin", "footer.segmentId = {0}", footer.segmentId); - DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChkType = {0}", footer.dataForkChkType); - DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen); - DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChk = 0x{0:X8}", footer.dataForkChk); - DicConsole.DebugWriteLine("UDIF plugin", "footer.plistOff = {0}", footer.plistOff); - DicConsole.DebugWriteLine("UDIF plugin", "footer.plistLen = {0}", footer.plistLen); - DicConsole.DebugWriteLine("UDIF plugin", "footer.masterChkType = {0}", footer.masterChkType); - DicConsole.DebugWriteLine("UDIF plugin", "footer.masterChkLen = {0}", footer.masterChkLen); - DicConsole.DebugWriteLine("UDIF plugin", "footer.masterChk = 0x{0:X8}", footer.masterChk); - DicConsole.DebugWriteLine("UDIF plugin", "footer.imageVariant = {0}", footer.imageVariant); - DicConsole.DebugWriteLine("UDIF plugin", "footer.sectorCount = {0}", footer.sectorCount); + DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkOff = {0}", footer.dataForkOff); + DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen); + DicConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkOff = {0}", footer.rsrcForkOff); + DicConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkLen = {0}", footer.rsrcForkLen); + DicConsole.DebugWriteLine("UDIF plugin", "footer.segmentNumber = {0}", footer.segmentNumber); + DicConsole.DebugWriteLine("UDIF plugin", "footer.segmentCount = {0}", footer.segmentCount); + DicConsole.DebugWriteLine("UDIF plugin", "footer.segmentId = {0}", footer.segmentId); + DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChkType = {0}", footer.dataForkChkType); + DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen); + DicConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChk = 0x{0:X8}", footer.dataForkChk); + DicConsole.DebugWriteLine("UDIF plugin", "footer.plistOff = {0}", footer.plistOff); + DicConsole.DebugWriteLine("UDIF plugin", "footer.plistLen = {0}", footer.plistLen); + DicConsole.DebugWriteLine("UDIF plugin", "footer.masterChkType = {0}", footer.masterChkType); + DicConsole.DebugWriteLine("UDIF plugin", "footer.masterChkLen = {0}", footer.masterChkLen); + DicConsole.DebugWriteLine("UDIF plugin", "footer.masterChk = 0x{0:X8}", footer.masterChk); + DicConsole.DebugWriteLine("UDIF plugin", "footer.imageVariant = {0}", footer.imageVariant); + DicConsole.DebugWriteLine("UDIF plugin", "footer.sectorCount = {0}", footer.sectorCount); DicConsole.DebugWriteLine("UDIF plugin", "footer.reserved1 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved1)); DicConsole.DebugWriteLine("UDIF plugin", "footer.reserved2 is empty? = {0}", @@ -213,7 +231,7 @@ namespace DiscImageChef.DiscImages // Block chunks and headers List blkxList = new List(); - chunks = new Dictionary(); + chunks = new Dictionary(); bool fakeBlockChunks = false; @@ -280,7 +298,7 @@ namespace DiscImageChef.DiscImages if(rsrc.TryGetValue("vers", out NSObject versObj)) { - NSObject[] versArray = ((NSArray)versObj).GetArray(); + NSObject[] versArray = ((NSArray)versObj).GetArray(); if(versArray.Length >= 1) vers = ((NSData)versArray[0]).Bytes; } } @@ -290,15 +308,15 @@ namespace DiscImageChef.DiscImages // So let's falsify a block chunk BlockChunk bChnk = new BlockChunk { - length = footer.dataForkLen, - offset = footer.dataForkOff, - sector = 0, + length = footer.dataForkLen, + offset = footer.dataForkOff, + sector = 0, sectors = footer.sectorCount, - type = CHUNK_TYPE_COPY + type = CHUNK_TYPE_COPY }; imageInfo.Sectors = footer.sectorCount; chunks.Add(bChnk.sector, bChnk); - buffersize = 2048 * SECTOR_SIZE; + buffersize = 2048 * SECTOR_SIZE; fakeBlockChunks = true; } @@ -307,12 +325,12 @@ namespace DiscImageChef.DiscImages Version version = new Version(vers); string release = null; - string dev = null; - string pre = null; + string dev = null; + string pre = null; string major = $"{version.MajorVersion}"; string minor = $".{version.MinorVersion / 10}"; - if(version.MinorVersion % 10 > 0) release = $".{version.MinorVersion % 10}"; + if(version.MinorVersion % 10 > 0) release = $".{version.MinorVersion % 10}"; switch(version.DevStage) { case Version.DevelopmentStage.Alpha: @@ -331,11 +349,12 @@ namespace DiscImageChef.DiscImages if(dev != null) pre = $"{version.PreReleaseVersion}"; imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}"; - imageInfo.Application = version.VersionString; - imageInfo.Comments = version.VersionMessage; + imageInfo.Application = version.VersionString; + imageInfo.Comments = version.VersionMessage; - if(version.MajorVersion == 3) imageInfo.Application = "ShrinkWrap™"; - else if(version.MajorVersion == 6) imageInfo.Application = "DiskCopy"; + if(version.MajorVersion == 3) imageInfo.Application = "ShrinkWrap™"; + else if(version.MajorVersion == 6) + imageInfo.Application = "DiskCopy"; } else imageInfo.Application = "DiskCopy"; @@ -353,28 +372,28 @@ namespace DiscImageChef.DiscImages foreach(byte[] blkxBytes in blkxList) { - BlockHeader bHdr = new BlockHeader(); - byte[] bHdrB = new byte[Marshal.SizeOf(bHdr)]; + BlockHeader bHdr = new BlockHeader(); + byte[] bHdrB = new byte[Marshal.SizeOf(bHdr)]; Array.Copy(blkxBytes, 0, bHdrB, 0, Marshal.SizeOf(bHdr)); bHdr = BigEndianMarshal.ByteArrayToStructureBigEndian(bHdrB); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.signature = 0x{0:X8}", bHdr.signature); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.version = {0}", bHdr.version); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorStart = {0}", bHdr.sectorStart); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorCount = {0}", bHdr.sectorCount); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.dataOffset = {0}", bHdr.dataOffset); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.buffers = {0}", bHdr.buffers); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.signature = 0x{0:X8}", bHdr.signature); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.version = {0}", bHdr.version); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorStart = {0}", bHdr.sectorStart); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorCount = {0}", bHdr.sectorCount); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.dataOffset = {0}", bHdr.dataOffset); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.buffers = {0}", bHdr.buffers); DicConsole.DebugWriteLine("UDIF plugin", "bHdr.descriptor = 0x{0:X8}", bHdr.descriptor); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved1 = {0}", bHdr.reserved1); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved2 = {0}", bHdr.reserved2); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved3 = {0}", bHdr.reserved3); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved4 = {0}", bHdr.reserved4); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved5 = {0}", bHdr.reserved5); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved6 = {0}", bHdr.reserved6); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumType = {0}", bHdr.checksumType); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumLen = {0}", bHdr.checksumLen); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.checksum = 0x{0:X8}", bHdr.checksum); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunks = {0}", bHdr.chunks); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved1 = {0}", bHdr.reserved1); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved2 = {0}", bHdr.reserved2); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved3 = {0}", bHdr.reserved3); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved4 = {0}", bHdr.reserved4); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved5 = {0}", bHdr.reserved5); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved6 = {0}", bHdr.reserved6); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumType = {0}", bHdr.checksumType); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumLen = {0}", bHdr.checksumLen); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.checksum = 0x{0:X8}", bHdr.checksum); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunks = {0}", bHdr.chunks); DicConsole.DebugWriteLine("UDIF plugin", "bHdr.reservedChk is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(bHdr.reservedChk)); @@ -382,18 +401,18 @@ namespace DiscImageChef.DiscImages for(int i = 0; i < bHdr.chunks; i++) { - BlockChunk bChnk = new BlockChunk(); - byte[] bChnkB = new byte[Marshal.SizeOf(bChnk)]; + BlockChunk bChnk = new BlockChunk(); + byte[] bChnkB = new byte[Marshal.SizeOf(bChnk)]; Array.Copy(blkxBytes, Marshal.SizeOf(bHdr) + Marshal.SizeOf(bChnk) * i, bChnkB, 0, Marshal.SizeOf(bChnk)); bChnk = BigEndianMarshal.ByteArrayToStructureBigEndian(bChnkB); DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].type = 0x{1:X8}", i, bChnk.type); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].comment = {1}", i, bChnk.comment); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sector = {1}", i, bChnk.sector); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sectors = {1}", i, bChnk.sectors); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].offset = {1}", i, bChnk.offset); - DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].length = {1}", i, bChnk.length); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].comment = {1}", i, bChnk.comment); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sector = {1}", i, bChnk.sector); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sectors = {1}", i, bChnk.sectors); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].offset = {1}", i, bChnk.offset); + DicConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].length = {1}", i, bChnk.length); if(bChnk.type == CHUNK_TYPE_END) break; @@ -423,7 +442,7 @@ namespace DiscImageChef.DiscImages } if(bChnk.type > CHUNK_TYPE_NOCOPY && bChnk.type < CHUNK_TYPE_COMMNT || - bChnk.type > CHUNK_TYPE_LZFSE && bChnk.type < CHUNK_TYPE_END) + bChnk.type > CHUNK_TYPE_LZFSE && bChnk.type < CHUNK_TYPE_END) throw new ImageNotSupportedException($"Unsupported chunk type 0x{bChnk.type:X8} found"); if(bChnk.sectors > 0) chunks.Add(bChnk.sector, bChnk); @@ -431,22 +450,22 @@ namespace DiscImageChef.DiscImages } } - sectorCache = new Dictionary(); - chunkCache = new Dictionary(); + sectorCache = new Dictionary(); + chunkCache = new Dictionary(); currentChunkCacheSize = 0; - imageStream = stream; + imageStream = stream; - imageInfo.CreationTime = imageFilter.GetCreationTime(); + imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); - imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); - imageInfo.SectorSize = SECTOR_SIZE; - imageInfo.XmlMediaType = XmlMediaType.BlockMedia; - imageInfo.MediaType = MediaType.GENERIC_HDD; - imageInfo.ImageSize = imageInfo.Sectors * SECTOR_SIZE; - imageInfo.Version = $"{footer.version}"; + imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); + imageInfo.SectorSize = SECTOR_SIZE; + imageInfo.XmlMediaType = XmlMediaType.BlockMedia; + imageInfo.MediaType = MediaType.GENERIC_HDD; + imageInfo.ImageSize = imageInfo.Sectors * SECTOR_SIZE; + imageInfo.Version = $"{footer.version}"; - imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); - imageInfo.Heads = 16; + imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); + imageInfo.Heads = 16; imageInfo.SectorsPerTrack = 63; return true; @@ -460,14 +479,14 @@ namespace DiscImageChef.DiscImages if(sectorCache.TryGetValue(sectorAddress, out byte[] sector)) return sector; - BlockChunk currentChunk = new BlockChunk(); - bool chunkFound = false; - ulong chunkStartSector = 0; + BlockChunk currentChunk = new BlockChunk(); + bool chunkFound = false; + ulong chunkStartSector = 0; foreach(KeyValuePair kvp in chunks.Where(kvp => sectorAddress >= kvp.Key)) { - currentChunk = kvp.Value; - chunkFound = true; + currentChunk = kvp.Value; + chunkFound = true; chunkStartSector = kvp.Key; } @@ -489,7 +508,7 @@ namespace DiscImageChef.DiscImages imageStream.Seek((long)currentChunk.offset, SeekOrigin.Begin); imageStream.Read(cmpBuffer, 0, cmpBuffer.Length); MemoryStream cmpMs = new MemoryStream(cmpBuffer); - Stream decStream; + Stream decStream; switch(currentChunk.type) { @@ -507,13 +526,13 @@ namespace DiscImageChef.DiscImages ImageNotSupportedException($"Unsupported chunk type 0x{currentChunk.type:X8} found"); } -#if DEBUG + #if DEBUG try { -#endif + #endif byte[] tmpBuffer = new byte[buffersize]; - int realSize = decStream.Read(tmpBuffer, 0, (int)buffersize); - buffer = new byte[realSize]; + int realSize = decStream.Read(tmpBuffer, 0, (int)buffersize); + buffer = new byte[realSize]; Array.Copy(tmpBuffer, 0, buffer, 0, realSize); if(currentChunkCacheSize + realSize > MAX_CACHE_SIZE) @@ -524,14 +543,14 @@ namespace DiscImageChef.DiscImages chunkCache.Add(chunkStartSector, buffer); currentChunkCacheSize += (uint)realSize; -#if DEBUG + #if DEBUG } catch(ZlibException) { DicConsole.WriteLine("zlib exception on chunk starting at sector {0}", currentChunk.sector); throw; } -#endif + #endif } sector = new byte[SECTOR_SIZE]; @@ -664,7 +683,7 @@ namespace DiscImageChef.DiscImages } public bool? VerifySectors(ulong sectorAddress, uint length, out List failingLbas, - out List unknownLbas) + out List unknownLbas) { failingLbas = new List(); unknownLbas = new List(); @@ -674,7 +693,7 @@ namespace DiscImageChef.DiscImages } public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List failingLbas, - out List unknownLbas) + out List unknownLbas) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } @@ -684,65 +703,414 @@ namespace DiscImageChef.DiscImages return null; } + public IEnumerable SupportedMediaTags => new MediaTagType[] { }; + public IEnumerable SupportedSectorTags => new SectorTagType[] { }; + public IEnumerable SupportedMediaTypes => + new[] {MediaType.Unknown, MediaType.GENERIC_HDD}; + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] { }; + public IEnumerable KnownExtensions => new[] {".dmg"}; + public bool IsWriting { get; private set; } + public string ErrorMessage { get; private set; } + + public bool Create(string path, MediaType mediaType, Dictionary options, ulong sectors, + uint sectorSize) + { + if(sectorSize != 512) + { + ErrorMessage = "Unsupported sector size"; + return false; + } + + if(!SupportedMediaTypes.Contains(mediaType)) + { + ErrorMessage = $"Unsupport media format {mediaType}"; + return false; + } + + imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors}; + + try { writingStream = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); } + catch(IOException e) + { + ErrorMessage = $"Could not create new image file, exception {e.Message}"; + return false; + } + + chunks = new Dictionary(); + currentChunk = new BlockChunk(); + currentSector = 0; + dataForkChecksum = new Crc32Context(); + dataForkChecksum.Init(); + masterChecksum = new Crc32Context(); + masterChecksum.Init(); + + IsWriting = true; + ErrorMessage = null; + return true; + } + + public bool WriteMediaTag(byte[] data, MediaTagType tag) + { + ErrorMessage = "Writing media tags is not supported."; + return false; + } + + public bool WriteSector(byte[] data, ulong sectorAddress) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(data.Length != imageInfo.SectorSize) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(sectorAddress >= imageInfo.Sectors) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + if(sectorAddress < currentSector) + { + ErrorMessage = "Tried to rewind, this format rewinded on writing"; + return false; + } + + masterChecksum.Update(data); + + bool isEmpty = ArrayHelpers.ArrayIsNullOrEmpty(data); + + switch(currentChunk.type) + { + case CHUNK_TYPE_ZERO: + currentChunk.type = isEmpty ? CHUNK_TYPE_NOCOPY : CHUNK_TYPE_COPY; + break; + case CHUNK_TYPE_NOCOPY when !isEmpty: + case CHUNK_TYPE_COPY when isEmpty: + chunks.Add(currentChunk.sector, currentChunk); + currentChunk = new BlockChunk + { + type = isEmpty ? CHUNK_TYPE_NOCOPY : CHUNK_TYPE_COPY, + sector = currentSector, + offset = (ulong)(isEmpty ? 0 : writingStream.Position) + }; + break; + } + + currentChunk.sectors++; + currentChunk.length += (ulong)(isEmpty ? 0 : 512); + currentSector++; + + if(!isEmpty) + { + dataForkChecksum.Update(data); + writingStream.Write(data, 0, data.Length); + } + + ErrorMessage = ""; + return true; + } + + // TODO: This can be optimized + public bool WriteSectors(byte[] data, ulong sectorAddress, uint length) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(data.Length % imageInfo.SectorSize != 0) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(sectorAddress + length > imageInfo.Sectors) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + // Ignore empty sectors + if(ArrayHelpers.ArrayIsNullOrEmpty(data)) + { + if(currentChunk.type == CHUNK_TYPE_COPY) + { + chunks.Add(currentChunk.sector, currentChunk); + currentChunk = new BlockChunk {type = CHUNK_TYPE_NOCOPY, sector = currentSector}; + } + + currentChunk.sectors += (ulong)(data.Length / imageInfo.SectorSize); + currentSector += (ulong)(data.Length / imageInfo.SectorSize); + masterChecksum.Update(data); + + ErrorMessage = ""; + return true; + } + + for(uint i = 0; i < length; i++) + { + byte[] tmp = new byte[imageInfo.SectorSize]; + Array.Copy(data, i * imageInfo.SectorSize, tmp, 0, imageInfo.SectorSize); + if(!WriteSector(tmp, sectorAddress + i)) return false; + } + + ErrorMessage = ""; + return true; + } + + public bool WriteSectorLong(byte[] data, ulong sectorAddress) + { + ErrorMessage = "Writing sectors with tags is not supported."; + return false; + } + + public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length) + { + ErrorMessage = "Writing sectors with tags is not supported."; + return false; + } + + public bool SetTracks(List tracks) + { + ErrorMessage = "Unsupported feature"; + return false; + } + + public bool Close() + { + if(!IsWriting) + { + ErrorMessage = "Image is not opened for writing"; + return false; + } + + if(currentChunk.type != CHUNK_TYPE_NOCOPY) currentChunk.length = currentChunk.sectors * 512; + chunks.Add(currentChunk.sector, currentChunk); + + chunks.Add(imageInfo.Sectors, new BlockChunk {type = CHUNK_TYPE_END, sector = imageInfo.Sectors}); + + BlockHeader bHdr = new BlockHeader + { + signature = CHUNK_SIGNATURE, + version = 1, + sectorCount = imageInfo.Sectors, + checksumType = UDIF_CHECKSUM_TYPE_CRC32, + checksumLen = 32, + checksum = BitConverter.ToUInt32(dataForkChecksum.Final().Reverse().ToArray(), 0), + chunks = (uint)chunks.Count + }; + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + MemoryStream chunkMs = new MemoryStream(); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.signature), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.version), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.sectorStart), 0, 8); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.sectorCount), 0, 8); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.dataOffset), 0, 8); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.buffers), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.descriptor), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved1), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved2), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved3), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved4), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved5), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved6), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.checksumType), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.checksumLen), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.checksum), 0, 4); + chunkMs.Write(new byte[124], 0, 124); + chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.chunks), 0, 4); + + foreach(BlockChunk chunk in chunks.Values) + { + chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.type), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.comment), 0, 4); + chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.sector), 0, 8); + chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.sectors), 0, 8); + chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.offset), 0, 8); + chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.length), 0, 8); + } + + byte[] plist = Encoding.UTF8.GetBytes(new NSDictionary + { + { + "resource-fork", + new NSDictionary + { + { + "blkx", + new NSArray + { + new NSDictionary + { + {"Attributes", "0x0050"}, + {"CFName", "whole disk (DiscImageChef : 0)"}, + {"Data", chunkMs.ToArray()}, + {"ID", "0"}, + {"Name", "whole disk (DiscImageChef : 0)"} + } + } + } + } + } + }.ToXmlPropertyList()); + + footer = new UdifFooter + { + signature = UDIF_SIGNATURE, + version = 4, + headerSize = 512, + flags = 1, + dataForkLen = (ulong)writingStream.Length, + segmentNumber = 1, + segmentCount = 1, + segmentId = Guid.NewGuid(), + dataForkChkType = UDIF_CHECKSUM_TYPE_CRC32, + dataForkChkLen = 32, + dataForkChk = BitConverter.ToUInt32(dataForkChecksum.Final().Reverse().ToArray(), 0), + plistOff = (ulong)writingStream.Length, + plistLen = (ulong)plist.Length, + // TODO: Find how is this calculated + /*masterChkType = 2, + masterChkLen = 32, + masterChk = BitConverter.ToUInt32(masterChecksum.Final().Reverse().ToArray(), 0),*/ + imageVariant = 2, + sectorCount = imageInfo.Sectors + }; + + writingStream.Seek(0, SeekOrigin.End); + writingStream.Write(plist, 0, plist.Length); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.signature), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.version), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.headerSize), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.flags), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.runningDataForkOff), 0, 8); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkOff), 0, 8); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkLen), 0, 8); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.rsrcForkOff), 0, 8); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.rsrcForkLen), 0, 8); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.segmentNumber), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.segmentCount), 0, 4); + writingStream.Write(footer.segmentId.ToByteArray(), 0, 16); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkChkType), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkChkLen), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkChk), 0, 4); + writingStream.Write(new byte[124], 0, 124); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.plistOff), 0, 8); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.plistLen), 0, 8); + writingStream.Write(new byte[120], 0, 120); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.masterChkType), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.masterChkLen), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.masterChk), 0, 4); + writingStream.Write(new byte[124], 0, 124); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.imageVariant), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(footer.sectorCount), 0, 8); + writingStream.Write(new byte[12], 0, 12); + writingStream.Close(); + + Crc32Context.File(writingStream.Name); + + IsWriting = false; + ErrorMessage = ""; + return true; + } + + // TODO: Comments + public bool SetMetadata(ImageInfo metadata) + { + return true; + } + + public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack) + { + // Not stored in image + return true; + } + + public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag) + { + ErrorMessage = "Writing sectors with tags is not supported."; + return false; + } + + public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag) + { + ErrorMessage = "Writing sectors with tags is not supported."; + return false; + } + [StructLayout(LayoutKind.Sequential, Pack = 1)] struct UdifFooter { - public uint signature; - public uint version; - public uint headerSize; - public uint flags; + public uint signature; + public uint version; + public uint headerSize; + public uint flags; public ulong runningDataForkOff; public ulong dataForkOff; public ulong dataForkLen; public ulong rsrcForkOff; public ulong rsrcForkLen; - public uint segmentNumber; - public uint segmentCount; - public Guid segmentId; - public uint dataForkChkType; - public uint dataForkChkLen; - public uint dataForkChk; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 124)] public byte[] reserved1; - public ulong plistOff; - public ulong plistLen; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 120)] public byte[] reserved2; - public uint masterChkType; - public uint masterChkLen; - public uint masterChk; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 124)] public byte[] reserved3; - public uint imageVariant; - public ulong sectorCount; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] public byte[] reserved4; + public uint segmentNumber; + public uint segmentCount; + public Guid segmentId; + public uint dataForkChkType; + public uint dataForkChkLen; + public uint dataForkChk; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 124)] + public byte[] reserved1; + public ulong plistOff; + public ulong plistLen; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 120)] + public byte[] reserved2; + public uint masterChkType; + public uint masterChkLen; + public uint masterChk; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 124)] + public byte[] reserved3; + public uint imageVariant; + public ulong sectorCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public byte[] reserved4; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct BlockHeader { - public uint signature; - public uint version; + public uint signature; + public uint version; public ulong sectorStart; public ulong sectorCount; public ulong dataOffset; - public uint buffers; - public uint descriptor; - public uint reserved1; - public uint reserved2; - public uint reserved3; - public uint reserved4; - public uint reserved5; - public uint reserved6; - public uint checksumType; - public uint checksumLen; - public uint checksum; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 124)] public byte[] reservedChk; - public uint chunks; + public uint buffers; + public uint descriptor; + public uint reserved1; + public uint reserved2; + public uint reserved3; + public uint reserved4; + public uint reserved5; + public uint reserved6; + public uint checksumType; + public uint checksumLen; + public uint checksum; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 124)] + public byte[] reservedChk; + public uint chunks; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct BlockChunk { - public uint type; - public uint comment; + public uint type; + public uint comment; public ulong sector; public ulong sectors; public ulong offset;