diff --git a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj index 8f55d81ed..77380f522 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj +++ b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj @@ -31,6 +31,9 @@ false + + ..\packages\Claunia.Encoding.1.4.0\lib\portable40-net40+sl5+win8+wp8\Claunia.Encoding.dll + ..\packages\SharpCompress.0.19.2\lib\net35\SharpCompress.dll diff --git a/DiscImageChef.DiscImages/DiskCopy42.cs b/DiscImageChef.DiscImages/DiskCopy42.cs index ab8a1dc12..c0a3db5e3 100644 --- a/DiscImageChef.DiscImages/DiskCopy42.cs +++ b/DiscImageChef.DiscImages/DiskCopy42.cs @@ -34,19 +34,20 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Text; using System.Text.RegularExpressions; +using Claunia.Encoding; using Claunia.RsrcFork; using DiscImageChef.CommonTypes; using DiscImageChef.Console; using DiscImageChef.Filters; +using Encoding = System.Text.Encoding; using Version = Resources.Version; namespace DiscImageChef.DiscImages { // Checked using several images and strings inside Apple's DiskImages.framework [SuppressMessage("ReSharper", "InconsistentNaming")] - public class DiskCopy42 : IMediaImage + public class DiskCopy42 : IWritableImage { // format byte /// 3.5", single side, double density, GCR @@ -72,8 +73,8 @@ namespace DiscImageChef.DiscImages /// Defined by Sigma Seven's BLU const byte kSigmaFmtByteTwiggy = 0x01; /// 3.5" single side double density GCR and MFM all use same code - const byte kSonyFmtByte400K = 0x02; - const byte kSonyFmtByte720K = kSonyFmtByte400K; + const byte kSonyFmtByte400K = 0x02; + const byte kSonyFmtByte720K = kSonyFmtByte400K; const byte kSonyFmtByte1440K = kSonyFmtByte400K; const byte kSonyFmtByte1680K = kSonyFmtByte400K; /// 3.5" double side double density GCR, 512 bytes/sector, interleave 2:1 @@ -90,7 +91,8 @@ namespace DiscImageChef.DiscImages /// Defined by LisaEm const byte kFmtNotStandard = 0x93; /// Used incorrectly by Mac OS X with certaing disk images - const byte kMacOSXFmtByte = 0x00; + const byte kMacOSXFmtByte = 0x00; + const string REGEX_DCPY = @"(?\S+)\s(?\S+)\rData checksum=\$(?\S+)$"; /// Bytes per tag, should be 12 uint bptag; @@ -100,49 +102,54 @@ namespace DiscImageChef.DiscImages IFilter dc42ImageFilter; /// Header of opened image Dc42Header header; + ImageInfo imageInfo; + ulong sectorsToWrite; /// Start of tags in disk image, after data sectors - uint tagOffset; - bool twiggy; - byte[] twiggyCache; - byte[] twiggyCacheTags; - ImageInfo imageInfo; - public ImageInfo Info => imageInfo; + uint tagOffset; + bool twiggy; + byte[] twiggyCache; + byte[] twiggyCacheTags; + MemoryStream twiggyDataCache; + MemoryStream twiggyTagCache; - public string Name => "Apple DiskCopy 4.2"; - public Guid Id => new Guid("0240B7B1-E959-4CDC-B0BD-386D6E467B88"); + FileStream writingStream; public DiskCopy42() { imageInfo = new ImageInfo { - ReadableSectorTags = new List(), - ReadableMediaTags = new List(), - HasPartitions = false, - HasSessions = false, - Version = "4.2", - Application = "Apple DiskCopy", - ApplicationVersion = "4.2", - 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 = "4.2", + Application = "Apple DiskCopy", + ApplicationVersion = "4.2", + 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 }; } + public ImageInfo Info => imageInfo; + public string Name => "Apple DiskCopy 4.2"; + public Guid Id => new Guid("0240B7B1-E959-4CDC-B0BD-386D6E467B88"); + public bool Identify(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); - byte[] buffer = new byte[0x58]; + byte[] buffer = new byte[0x58]; byte[] pString = new byte[64]; stream.Read(buffer, 0, 0x58); @@ -155,43 +162,43 @@ namespace DiscImageChef.DiscImages BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; - tmpHeader.DiskName = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh")); - tmpHeader.DataSize = BigEndianBitConverter.ToUInt32(buffer, 0x40); - tmpHeader.TagSize = BigEndianBitConverter.ToUInt32(buffer, 0x44); + tmpHeader.DiskName = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh")); + tmpHeader.DataSize = BigEndianBitConverter.ToUInt32(buffer, 0x40); + tmpHeader.TagSize = BigEndianBitConverter.ToUInt32(buffer, 0x44); tmpHeader.DataChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x48); - tmpHeader.TagChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x4C); - tmpHeader.Format = buffer[0x50]; - tmpHeader.FmtByte = buffer[0x51]; - tmpHeader.Valid = buffer[0x52]; - tmpHeader.Reserved = buffer[0x53]; + tmpHeader.TagChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x4C); + tmpHeader.Format = buffer[0x50]; + tmpHeader.FmtByte = buffer[0x51]; + tmpHeader.Valid = buffer[0x52]; + tmpHeader.Reserved = buffer[0x53]; - DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.diskName = \"{0}\"", tmpHeader.DiskName); - DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.dataSize = {0} bytes", tmpHeader.DataSize); - DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.tagSize = {0} bytes", tmpHeader.TagSize); + DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.diskName = \"{0}\"", tmpHeader.DiskName); + DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.dataSize = {0} bytes", tmpHeader.DataSize); + DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.tagSize = {0} bytes", tmpHeader.TagSize); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.dataChecksum = 0x{0:X8}", tmpHeader.DataChecksum); - DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.tagChecksum = 0x{0:X8}", tmpHeader.TagChecksum); - DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.format = 0x{0:X2}", tmpHeader.Format); - DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.fmtByte = 0x{0:X2}", tmpHeader.FmtByte); - DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.valid = {0}", tmpHeader.Valid); - DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.reserved = {0}", tmpHeader.Reserved); + DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.tagChecksum = 0x{0:X8}", tmpHeader.TagChecksum); + DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.format = 0x{0:X2}", tmpHeader.Format); + DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.fmtByte = 0x{0:X2}", tmpHeader.FmtByte); + DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.valid = {0}", tmpHeader.Valid); + DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.reserved = {0}", tmpHeader.Reserved); if(tmpHeader.Valid != 1 || tmpHeader.Reserved != 0) return false; // Some versions seem to incorrectly create little endian fields if(tmpHeader.DataSize + tmpHeader.TagSize + 0x54 != imageFilter.GetDataForkLength() && - tmpHeader.Format != kSigmaFormatTwiggy) + tmpHeader.Format != kSigmaFormatTwiggy) { - tmpHeader.DataSize = BitConverter.ToUInt32(buffer, 0x40); - tmpHeader.TagSize = BitConverter.ToUInt32(buffer, 0x44); + tmpHeader.DataSize = BitConverter.ToUInt32(buffer, 0x40); + tmpHeader.TagSize = BitConverter.ToUInt32(buffer, 0x44); tmpHeader.DataChecksum = BitConverter.ToUInt32(buffer, 0x48); - tmpHeader.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C); + tmpHeader.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C); if(tmpHeader.DataSize + tmpHeader.TagSize + 0x54 != imageFilter.GetDataForkLength() && - tmpHeader.Format != kSigmaFormatTwiggy) return false; + tmpHeader.Format != kSigmaFormatTwiggy) return false; } - if(tmpHeader.Format != kSonyFormat400K && tmpHeader.Format != kSonyFormat800K && - tmpHeader.Format != kSonyFormat720K && tmpHeader.Format != kSonyFormat1440K && + if(tmpHeader.Format != kSonyFormat400K && tmpHeader.Format != kSonyFormat800K && + tmpHeader.Format != kSonyFormat720K && tmpHeader.Format != kSonyFormat1440K && tmpHeader.Format != kSonyFormat1680K && tmpHeader.Format != kSigmaFormatTwiggy && tmpHeader.Format != kNotStandardFormat) { @@ -201,10 +208,10 @@ namespace DiscImageChef.DiscImages return false; } - if(tmpHeader.FmtByte != kSonyFmtByte400K && tmpHeader.FmtByte != kSonyFmtByte800K && - tmpHeader.FmtByte != kSonyFmtByte800KIncorrect && tmpHeader.FmtByte != kSonyFmtByteProDos && - tmpHeader.FmtByte != kInvalidFmtByte && tmpHeader.FmtByte != kSigmaFmtByteTwiggy && - tmpHeader.FmtByte != kFmtNotStandard && tmpHeader.FmtByte != kMacOSXFmtByte) + if(tmpHeader.FmtByte != kSonyFmtByte400K && tmpHeader.FmtByte != kSonyFmtByte800K && + tmpHeader.FmtByte != kSonyFmtByte800KIncorrect && tmpHeader.FmtByte != kSonyFmtByteProDos && + tmpHeader.FmtByte != kInvalidFmtByte && tmpHeader.FmtByte != kSigmaFmtByteTwiggy && + tmpHeader.FmtByte != kFmtNotStandard && tmpHeader.FmtByte != kMacOSXFmtByte) { DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value", tmpHeader.FmtByte); @@ -223,54 +230,55 @@ namespace DiscImageChef.DiscImages { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); - byte[] buffer = new byte[0x58]; + byte[] buffer = new byte[0x58]; byte[] pString = new byte[64]; stream.Read(buffer, 0, 0x58); + IsWriting = false; // Incorrect pascal string length, not DC42 if(buffer[0] > 63) return false; - header = new Dc42Header(); + header = new Dc42Header(); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; Array.Copy(buffer, 0, pString, 0, 64); - header.DiskName = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh")); - header.DataSize = BigEndianBitConverter.ToUInt32(buffer, 0x40); - header.TagSize = BigEndianBitConverter.ToUInt32(buffer, 0x44); + header.DiskName = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh")); + header.DataSize = BigEndianBitConverter.ToUInt32(buffer, 0x40); + header.TagSize = BigEndianBitConverter.ToUInt32(buffer, 0x44); header.DataChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x48); - header.TagChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x4C); - header.Format = buffer[0x50]; - header.FmtByte = buffer[0x51]; - header.Valid = buffer[0x52]; - header.Reserved = buffer[0x53]; + header.TagChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x4C); + header.Format = buffer[0x50]; + header.FmtByte = buffer[0x51]; + header.Valid = buffer[0x52]; + header.Reserved = buffer[0x53]; - DicConsole.DebugWriteLine("DC42 plugin", "header.diskName = \"{0}\"", header.DiskName); - DicConsole.DebugWriteLine("DC42 plugin", "header.dataSize = {0} bytes", header.DataSize); - DicConsole.DebugWriteLine("DC42 plugin", "header.tagSize = {0} bytes", header.TagSize); + DicConsole.DebugWriteLine("DC42 plugin", "header.diskName = \"{0}\"", header.DiskName); + DicConsole.DebugWriteLine("DC42 plugin", "header.dataSize = {0} bytes", header.DataSize); + DicConsole.DebugWriteLine("DC42 plugin", "header.tagSize = {0} bytes", header.TagSize); DicConsole.DebugWriteLine("DC42 plugin", "header.dataChecksum = 0x{0:X8}", header.DataChecksum); - DicConsole.DebugWriteLine("DC42 plugin", "header.tagChecksum = 0x{0:X8}", header.TagChecksum); - DicConsole.DebugWriteLine("DC42 plugin", "header.format = 0x{0:X2}", header.Format); - DicConsole.DebugWriteLine("DC42 plugin", "header.fmtByte = 0x{0:X2}", header.FmtByte); - DicConsole.DebugWriteLine("DC42 plugin", "header.valid = {0}", header.Valid); - DicConsole.DebugWriteLine("DC42 plugin", "header.reserved = {0}", header.Reserved); + DicConsole.DebugWriteLine("DC42 plugin", "header.tagChecksum = 0x{0:X8}", header.TagChecksum); + DicConsole.DebugWriteLine("DC42 plugin", "header.format = 0x{0:X2}", header.Format); + DicConsole.DebugWriteLine("DC42 plugin", "header.fmtByte = 0x{0:X2}", header.FmtByte); + DicConsole.DebugWriteLine("DC42 plugin", "header.valid = {0}", header.Valid); + DicConsole.DebugWriteLine("DC42 plugin", "header.reserved = {0}", header.Reserved); if(header.Valid != 1 || header.Reserved != 0) return false; // Some versions seem to incorrectly create little endian fields if(header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() && - header.Format != kSigmaFormatTwiggy) + header.Format != kSigmaFormatTwiggy) { - header.DataSize = BitConverter.ToUInt32(buffer, 0x40); - header.TagSize = BitConverter.ToUInt32(buffer, 0x44); + header.DataSize = BitConverter.ToUInt32(buffer, 0x40); + header.TagSize = BitConverter.ToUInt32(buffer, 0x44); header.DataChecksum = BitConverter.ToUInt32(buffer, 0x48); - header.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C); + header.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C); if(header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() && - header.Format != kSigmaFormatTwiggy) return false; + header.Format != kSigmaFormatTwiggy) return false; } - if(header.Format != kSonyFormat400K && header.Format != kSonyFormat800K && - header.Format != kSonyFormat720K && header.Format != kSonyFormat1440K && + if(header.Format != kSonyFormat400K && header.Format != kSonyFormat800K && + header.Format != kSonyFormat720K && header.Format != kSonyFormat1440K && header.Format != kSonyFormat1680K && header.Format != kSigmaFormatTwiggy && header.Format != kNotStandardFormat) { @@ -279,10 +287,10 @@ namespace DiscImageChef.DiscImages return false; } - if(header.FmtByte != kSonyFmtByte400K && header.FmtByte != kSonyFmtByte800K && - header.FmtByte != kSonyFmtByte800KIncorrect && header.FmtByte != kSonyFmtByteProDos && - header.FmtByte != kInvalidFmtByte && header.FmtByte != kSigmaFmtByteTwiggy && - header.FmtByte != kFmtNotStandard && header.FmtByte != kMacOSXFmtByte) + if(header.FmtByte != kSonyFmtByte400K && header.FmtByte != kSonyFmtByte800K && + header.FmtByte != kSonyFmtByte800KIncorrect && header.FmtByte != kSonyFmtByteProDos && + header.FmtByte != kInvalidFmtByte && header.FmtByte != kSigmaFmtByteTwiggy && + header.FmtByte != kFmtNotStandard && header.FmtByte != kMacOSXFmtByte) { DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value", header.FmtByte); @@ -296,11 +304,11 @@ namespace DiscImageChef.DiscImages return false; } - dataOffset = 0x54; - tagOffset = header.TagSize != 0 ? 0x54 + header.DataSize : 0; + dataOffset = 0x54; + tagOffset = header.TagSize != 0 ? 0x54 + header.DataSize : 0; imageInfo.SectorSize = 512; - bptag = (uint)(header.TagSize != 0 ? 12 : 0); - dc42ImageFilter = imageFilter; + bptag = (uint)(header.TagSize != 0 ? 12 : 0); + dc42ImageFilter = imageFilter; imageInfo.Sectors = header.DataSize / 512; @@ -318,10 +326,10 @@ namespace DiscImageChef.DiscImages imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag); } - imageInfo.ImageSize = imageInfo.Sectors * imageInfo.SectorSize + imageInfo.Sectors * bptag; - imageInfo.CreationTime = imageFilter.GetCreationTime(); + imageInfo.ImageSize = imageInfo.Sectors * imageInfo.SectorSize + imageInfo.Sectors * bptag; + imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); - imageInfo.MediaTitle = header.DiskName; + imageInfo.MediaTitle = header.DiskName; switch(header.Format) { @@ -374,9 +382,9 @@ namespace DiscImageChef.DiscImages byte[] data = new byte[header.DataSize]; byte[] tags = new byte[header.TagSize]; - twiggyCache = new byte[header.DataSize]; + twiggyCache = new byte[header.DataSize]; twiggyCacheTags = new byte[header.TagSize]; - twiggy = true; + twiggy = true; Stream datastream = imageFilter.GetDataForkStream(); datastream.Seek(dataOffset, SeekOrigin.Begin); @@ -386,31 +394,31 @@ namespace DiscImageChef.DiscImages tagstream.Seek(tagOffset, SeekOrigin.Begin); tagstream.Read(tags, 0, (int)header.TagSize); - ushort mfsMagic = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x400); + ushort mfsMagic = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x400); ushort mfsAllBlocks = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x412); // Detect a Macintosh Twiggy if(mfsMagic == 0xD2D7 && mfsAllBlocks == 422) { DicConsole.DebugWriteLine("DC42 plugin", "Macintosh Twiggy detected, reversing disk sides"); - Array.Copy(data, header.DataSize / 2, twiggyCache, 0, header.DataSize / 2); - Array.Copy(tags, header.TagSize / 2, twiggyCacheTags, 0, header.TagSize / 2); - Array.Copy(data, 0, twiggyCache, header.DataSize / 2, header.DataSize / 2); - Array.Copy(tags, 0, twiggyCacheTags, header.TagSize / 2, header.TagSize / 2); + Array.Copy(data, header.DataSize / 2, twiggyCache, 0, header.DataSize / 2); + Array.Copy(tags, header.TagSize / 2, twiggyCacheTags, 0, header.TagSize / 2); + Array.Copy(data, 0, twiggyCache, header.DataSize / 2, header.DataSize / 2); + Array.Copy(tags, 0, twiggyCacheTags, header.TagSize / 2, header.TagSize / 2); } else { DicConsole.DebugWriteLine("DC42 plugin", "Lisa Twiggy detected, reversing second half of disk"); - Array.Copy(data, 0, twiggyCache, 0, header.DataSize / 2); - Array.Copy(tags, 0, twiggyCacheTags, 0, header.TagSize / 2); + Array.Copy(data, 0, twiggyCache, 0, header.DataSize / 2); + Array.Copy(tags, 0, twiggyCacheTags, 0, header.TagSize / 2); int copiedSectors = 0; int sectorsToCopy = 0; for(int i = 0; i < 46; i++) { - if(i >= 0 && i <= 3) sectorsToCopy = 22; - if(i >= 4 && i <= 10) sectorsToCopy = 21; + if(i >= 0 && i <= 3) sectorsToCopy = 22; + if(i >= 4 && i <= 10) sectorsToCopy = 21; if(i >= 11 && i <= 16) sectorsToCopy = 20; if(i >= 17 && i <= 22) sectorsToCopy = 19; if(i >= 23 && i <= 28) sectorsToCopy = 18; @@ -419,10 +427,13 @@ namespace DiscImageChef.DiscImages if(i >= 42 && i <= 45) sectorsToCopy = 15; Array.Copy(data, header.DataSize / 2 + copiedSectors * 512, twiggyCache, - twiggyCache.Length - copiedSectors * 512 - sectorsToCopy * 512, sectorsToCopy * 512); - Array.Copy(tags, header.TagSize / 2 + copiedSectors * bptag, twiggyCacheTags, - twiggyCacheTags.Length - copiedSectors * bptag - sectorsToCopy * bptag, - sectorsToCopy * bptag); + twiggyCache.Length - copiedSectors * 512 - sectorsToCopy * 512, + sectorsToCopy * 512); + Array.Copy(tags, header.TagSize / 2 + copiedSectors * bptag, + twiggyCacheTags, + twiggyCacheTags.Length - copiedSectors * bptag - + sectorsToCopy * bptag, + sectorsToCopy * bptag); copiedSectors += sectorsToCopy; } @@ -445,12 +456,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: @@ -469,8 +480,8 @@ 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; } } @@ -481,18 +492,13 @@ namespace DiscImageChef.DiscImages { string dCpy = StringHandlers.PascalToString(dCpyRsrc.GetResource(dCpyRsrc.GetIds()[0]), Encoding.GetEncoding("macintosh")); - string dCpyRegEx = - @"(?\S+)\s(?\S+)\rData checksum=\$(?\S+)$"; - Regex dCpyEx = new Regex(dCpyRegEx); + Regex dCpyEx = new Regex(REGEX_DCPY); Match dCpyMatch = dCpyEx.Match(dCpy); if(dCpyMatch.Success) { - imageInfo.Application = dCpyMatch.Groups["application"].Value; + imageInfo.Application = dCpyMatch.Groups["application"].Value; imageInfo.ApplicationVersion = dCpyMatch.Groups["version"].Value; - - // Until MacRoman is implemented - if(imageInfo.Application == "ShrinkWrap?") imageInfo.Application = "ShrinkWrap™"; } } } @@ -509,28 +515,28 @@ namespace DiscImageChef.DiscImages switch(imageInfo.MediaType) { case MediaType.AppleSonySS: - imageInfo.Cylinders = 80; - imageInfo.Heads = 1; + imageInfo.Cylinders = 80; + imageInfo.Heads = 1; imageInfo.SectorsPerTrack = 10; break; case MediaType.AppleSonyDS: - imageInfo.Cylinders = 80; - imageInfo.Heads = 2; + imageInfo.Cylinders = 80; + imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 10; break; case MediaType.DOS_35_DS_DD_9: - imageInfo.Cylinders = 80; - imageInfo.Heads = 2; + imageInfo.Cylinders = 80; + imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 9; break; case MediaType.DOS_35_HD: - imageInfo.Cylinders = 80; - imageInfo.Heads = 2; + imageInfo.Cylinders = 80; + imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 18; break; case MediaType.DMF: - imageInfo.Cylinders = 80; - imageInfo.Heads = 2; + imageInfo.Cylinders = 80; + imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 21; break; case MediaType.AppleProfile: @@ -544,22 +550,22 @@ namespace DiscImageChef.DiscImages break; } - imageInfo.Heads = 4; + imageInfo.Heads = 4; imageInfo.SectorsPerTrack = 16; break; case MediaType.AppleWidget: - imageInfo.Cylinders = 608; - imageInfo.Heads = 2; + imageInfo.Cylinders = 608; + imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 16; break; case MediaType.AppleHD20: - imageInfo.Cylinders = 610; - imageInfo.Heads = 2; + imageInfo.Cylinders = 610; + imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 16; break; default: - imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); - imageInfo.Heads = 16; + imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); + imageInfo.Heads = 16; imageInfo.SectorsPerTrack = 63; break; } @@ -578,7 +584,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(); @@ -589,7 +595,7 @@ namespace DiscImageChef.DiscImages } public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List failingLbas, - out List unknownLbas) + out List unknownLbas) { failingLbas = new List(); unknownLbas = new List(); @@ -601,10 +607,9 @@ namespace DiscImageChef.DiscImages public bool? VerifyMediaImage() { - byte[] data = new byte[header.DataSize]; - byte[] tags = new byte[header.TagSize]; - uint dataChk; - uint tagsChk = 0; + byte[] data = new byte[header.DataSize]; + byte[] tags = new byte[header.TagSize]; + uint tagsChk = 0; DicConsole.DebugWriteLine("DC42 plugin", "Reading data"); Stream datastream = dc42ImageFilter.GetDataForkStream(); @@ -612,9 +617,9 @@ namespace DiscImageChef.DiscImages datastream.Read(data, 0, (int)header.DataSize); DicConsole.DebugWriteLine("DC42 plugin", "Calculating data checksum"); - dataChk = DC42CheckSum(data); + uint dataChk = DC42CheckSum(data); DicConsole.DebugWriteLine("DC42 plugin", "Calculated data checksum = 0x{0:X8}", dataChk); - DicConsole.DebugWriteLine("DC42 plugin", "Stored data checksum = 0x{0:X8}", header.DataChecksum); + DicConsole.DebugWriteLine("DC42 plugin", "Stored data checksum = 0x{0:X8}", header.DataChecksum); if(header.TagSize <= 0) return dataChk == header.DataChecksum && tagsChk == header.TagChecksum; @@ -626,7 +631,7 @@ namespace DiscImageChef.DiscImages DicConsole.DebugWriteLine("DC42 plugin", "Calculating tag checksum"); tagsChk = DC42CheckSum(tags); DicConsole.DebugWriteLine("DC42 plugin", "Calculated tag checksum = 0x{0:X8}", tagsChk); - DicConsole.DebugWriteLine("DC42 plugin", "Stored tag checksum = 0x{0:X8}", header.TagChecksum); + DicConsole.DebugWriteLine("DC42 plugin", "Stored tag checksum = 0x{0:X8}", header.TagChecksum); return dataChk == header.DataChecksum && tagsChk == header.TagChecksum; } @@ -653,12 +658,12 @@ namespace DiscImageChef.DiscImages if(twiggy) Array.Copy(twiggyCache, (int)sectorAddress * imageInfo.SectorSize, buffer, 0, - length * imageInfo.SectorSize); + length * imageInfo.SectorSize); else { Stream stream = dc42ImageFilter.GetDataForkStream(); stream.Seek((long)(dataOffset + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin); - stream.Read(buffer, 0, (int)(length * imageInfo.SectorSize)); + stream.Read(buffer, 0, (int)(length * imageInfo.SectorSize)); } return buffer; @@ -684,7 +689,7 @@ namespace DiscImageChef.DiscImages { Stream stream = dc42ImageFilter.GetDataForkStream(); stream.Seek((long)(tagOffset + sectorAddress * bptag), SeekOrigin.Begin); - stream.Read(buffer, 0, (int)(length * bptag)); + stream.Read(buffer, 0, (int)(length * bptag)); } return buffer; @@ -703,8 +708,8 @@ namespace DiscImageChef.DiscImages if(sectorAddress + length > imageInfo.Sectors) throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available"); - byte[] data = ReadSectors(sectorAddress, length); - byte[] tags = ReadSectorsTag(sectorAddress, length, SectorTagType.AppleSectorTag); + byte[] data = ReadSectors(sectorAddress, length); + byte[] tags = ReadSectorsTag(sectorAddress, length, SectorTagType.AppleSectorTag); byte[] buffer = new byte[data.Length + tags.Length]; for(uint i = 0; i < length; i++) @@ -773,6 +778,333 @@ namespace DiscImageChef.DiscImages throw new FeatureUnsupportedImageException("Feature not supported by image format"); } + public IEnumerable SupportedMediaTags => new MediaTagType[] { }; + public IEnumerable SupportedSectorTags => new[] {SectorTagType.AppleSectorTag}; + public IEnumerable SupportedMediaTypes => + new[] + { + MediaType.AppleFileWare, MediaType.AppleHD20, MediaType.AppleProfile, MediaType.AppleSonyDS, + MediaType.AppleSonySS, MediaType.AppleWidget, MediaType.DOS_35_DS_DD_9, MediaType.DOS_35_HD, + MediaType.DMF + }; + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new[] {("macosx", typeof(bool), "Use Mac OS X format byte")}; + public IEnumerable KnownExtensions => new[] {".dc42", ".diskcopy42", ".image"}; + public bool IsWriting { get; private set; } + public string ErrorMessage { get; private set; } + + public bool Create(string path, MediaType mediaType, Dictionary options, ulong sectors) + { + header = new Dc42Header(); + bool tags = false; + bool macosx; + + if(options != null && options.TryGetValue("macosx", out string tmpOption)) + bool.TryParse(tmpOption, out macosx); + + switch(mediaType) + { + case MediaType.AppleFileWare: + header.FmtByte = kSigmaFmtByteTwiggy; + header.Format = kSigmaFormatTwiggy; + twiggy = true; + tags = true; + twiggyDataCache = new MemoryStream(); + twiggyTagCache = new MemoryStream(); + + // TODO + ErrorMessage = "Twiggy write support not yet implemented"; + return false; + case MediaType.AppleHD20: + if(sectors != 39040) + { + ErrorMessage = "Incorrect number of sectors for Apple HD20 image"; + return false; + } + + header.FmtByte = kFmtNotStandard; + header.Format = kNotStandardFormat; + tags = true; + break; + case MediaType.AppleProfile: + if(sectors != 9728 && sectors != 19456) + { + ErrorMessage = "Incorrect number of sectors for Apple Profile image"; + return false; + } + + header.FmtByte = kFmtNotStandard; + header.Format = kNotStandardFormat; + tags = true; + break; + case MediaType.AppleSonyDS: + if(sectors != 1600) + { + ErrorMessage = "Incorrect number of sectors for Apple MF2DD image"; + return false; + } + + header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte800K; + header.Format = kSonyFormat800K; + tags = true; + break; + case MediaType.AppleSonySS: + if(sectors != 800) + { + ErrorMessage = "Incorrect number of sectors for Apple MF1DD image"; + return false; + } + + header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte400K; + header.Format = kSonyFormat400K; + tags = true; + break; + case MediaType.AppleWidget: + if(sectors != 39040) + { + ErrorMessage = "Incorrect number of sectors for Apple Widget image"; + return false; + } + + header.FmtByte = kFmtNotStandard; + header.Format = kNotStandardFormat; + tags = true; + break; + case MediaType.DOS_35_DS_DD_9: + if(sectors != 1440) + { + ErrorMessage = "Incorrect number of sectors for MF2DD image"; + return false; + } + + header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte720K; + header.Format = kSonyFormat720K; + break; + case MediaType.DOS_35_HD: + if(sectors != 2880) + { + ErrorMessage = "Incorrect number of sectors for MF2HD image"; + return false; + } + + header.Format = kSonyFmtByte1440K; + header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte1440K; + break; + case MediaType.DMF: + if(sectors != 3360) + { + ErrorMessage = "Incorrect number of sectors for DMF image"; + return false; + } + + header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte1680K; + header.Format = kSonyFormat1680K; + break; + default: + ErrorMessage = $"Unsupport media format {mediaType}"; + return false; + } + + dataOffset = 0x54; + tagOffset = header.TagSize != 0 ? 0x54 + header.DataSize : 0; + header.DiskName = "-DiscImageChef converted image-"; + header.Valid = 1; + header.DataSize = (uint)(sectors * 512); + if(tags) header.TagSize = (uint)(sectors * 12); + sectorsToWrite = 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; + } + + ErrorMessage = null; + return true; + } + + public bool WriteMediaTag(byte[] data, MediaTagType tag) + { + ErrorMessage = "Unsupported feature"; + return false; + } + + public bool WriteSector(byte[] data, ulong sectorAddress) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(sectorAddress >= sectorsToWrite) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + writingStream.Seek((long)(dataOffset + sectorAddress * 512), SeekOrigin.Begin); + writingStream.Write(data, 0, data.Length); + + ErrorMessage = ""; + return true; + } + + 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 != 512) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(data.Length % 512 != 0) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(sectorAddress + length >= sectorsToWrite) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + writingStream.Seek((long)(dataOffset + sectorAddress * 512), SeekOrigin.Begin); + writingStream.Write(data, 0, data.Length); + + ErrorMessage = ""; + return true; + } + + public bool WriteSectorLong(byte[] data, ulong sectorAddress) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(header.TagSize == 0) + { + ErrorMessage = "Image does not support tags"; + return false; + } + + if(data.Length != 524) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(sectorAddress >= sectorsToWrite) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + writingStream.Seek((long)(dataOffset + sectorAddress * 512), SeekOrigin.Begin); + writingStream.Write(data, 0, 512); + writingStream.Seek((long)(tagOffset + sectorAddress * 12), SeekOrigin.Begin); + writingStream.Write(data, 512, 12); + + ErrorMessage = ""; + return true; + } + + public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(header.TagSize == 0) + { + ErrorMessage = "Image does not support tags"; + return false; + } + + if(data.Length % 524 != 0) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(sectorAddress + length >= sectorsToWrite) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + for(uint i = 0; i < length; i++) + { + writingStream.Seek((long)(dataOffset + (sectorAddress + i) * 512), SeekOrigin.Begin); + writingStream.Write(data, (int)(i * 524 + 0), 512); + writingStream.Seek((long)(tagOffset + (sectorAddress + i) * 12), + SeekOrigin.Begin); + writingStream.Write(data, (int)(i * 524 + 512), + 12); + } + + ErrorMessage = ""; + return true; + } + + public bool SetTracks(List tracks) + { + ErrorMessage = "Unsupported feature"; + return false; + } + + public bool Close() + { + if(!IsWriting) + { + ErrorMessage = "Image is not opened for writing"; + return false; + } + + writingStream.Seek(0x54, SeekOrigin.Begin); + byte[] data = new byte[header.DataSize]; + writingStream.Read(data, 0, (int)header.DataSize); + header.DataChecksum = DC42CheckSum(data); + writingStream.Seek(0x54 + header.DataSize, SeekOrigin.Begin); + data = new byte[header.TagSize]; + writingStream.Read(data, 0, (int)header.TagSize); + header.TagChecksum = DC42CheckSum(data); + + writingStream.Seek(0, SeekOrigin.Begin); + if(header.DiskName.Length > 63) header.DiskName = header.DiskName.Substring(0, 63); + writingStream.WriteByte((byte)header.DiskName.Length); + Encoding macRoman = new MacRoman(); + writingStream.Write(macRoman.GetBytes(header.DiskName), 0, header.DiskName.Length); + + writingStream.Seek(64, SeekOrigin.Begin); + writingStream.Write(BigEndianBitConverter.GetBytes(header.DataSize), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(header.TagSize), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(header.DataChecksum), 0, 4); + writingStream.Write(BigEndianBitConverter.GetBytes(header.TagChecksum), 0, 4); + writingStream.WriteByte(header.Format); + writingStream.WriteByte(header.FmtByte); + writingStream.WriteByte(1); + writingStream.WriteByte(0); + + writingStream.Flush(); + writingStream.Close(); + IsWriting = false; + + return true; + } + static uint DC42CheckSum(byte[] buffer) { uint dc42Chk = 0; @@ -782,12 +1114,17 @@ namespace DiscImageChef.DiscImages { dc42Chk += (uint)(buffer[i] << 8); dc42Chk += buffer[i + 1]; - dc42Chk = (dc42Chk >> 1) | (dc42Chk << 31); + dc42Chk = (dc42Chk >> 1) | (dc42Chk << 31); } return dc42Chk; } + ~DiskCopy42() + { + Close(); + } + // DiskCopy 4.2 header, big-endian, data-fork, start of file, 84 bytes struct Dc42Header { diff --git a/DiscImageChef.DiscImages/IWritableImage.cs b/DiscImageChef.DiscImages/IWritableImage.cs index f5e4697b4..3e1d228f6 100644 --- a/DiscImageChef.DiscImages/IWritableImage.cs +++ b/DiscImageChef.DiscImages/IWritableImage.cs @@ -45,15 +45,15 @@ namespace DiscImageChef.DiscImages /// /// Gets a list of that are supported by the media image format /// - List SupportedMediaTags { get; } + IEnumerable SupportedMediaTags { get; } /// /// Gets a list of that are supported by the media image format /// - List SupportedSectorTags { get; } + IEnumerable SupportedSectorTags { get; } /// /// Gets a list of that are supported by the media image format /// - List SupportedMediaTypes { get; } + IEnumerable SupportedMediaTypes { get; } /// /// Retrieves a list of options supported by the filesystem, with name, type and description /// @@ -61,8 +61,11 @@ namespace DiscImageChef.DiscImages /// /// Gets a list of known extensions for format auto-chosing /// - List KnownExtensions { get; } + IEnumerable KnownExtensions { get; } + bool IsWriting { get; } + string ErrorMessage { get; } + /// /// Creates a new image in the specified path, for the specified , with the /// specified options to hold a media with the specified number of sectors