public bool Identify(IFilter imageFilter) { if (!imageFilter.HasResourceFork() || imageFilter.GetResourceForkLength() == 0) { return(false); } try { ResourceFork rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream()); if (!rsrcFork.ContainsKey(NDIF_RESOURCE)) { return(false); } Resource rsrc = rsrcFork.GetResource(NDIF_RESOURCE); if (rsrc.ContainsId(NDIF_RESOURCEID)) { return(true); } } catch (InvalidCastException) { return(false); } return(false); }
public bool Identify(IFilter imageFilter) { if (!imageFilter.HasResourceFork() || imageFilter.GetResourceForkLength() == 0) { return(false); } try { var rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream()); if (!rsrcFork.ContainsKey(NDIF_RESOURCE)) { return(false); } Resource rsrc = rsrcFork.GetResource(NDIF_RESOURCE); Stream dataFork = imageFilter.GetDataForkStream(); byte[] udifMagic = new byte[4]; dataFork.Read(udifMagic, 0, 4); if (BitConverter.ToUInt32(udifMagic, 0) == 0x796C6F6B) { return(false); } if (rsrc.ContainsId(NDIF_RESOURCEID)) { return(true); } } catch (InvalidCastException) { return(false); } return(false); }
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); if (stream.Length < 512) { return(false); } stream.Seek(-Marshal.SizeOf <UdifFooter>(), SeekOrigin.End); byte[] footerB = new byte[Marshal.SizeOf <UdifFooter>()]; stream.Read(footerB, 0, Marshal.SizeOf <UdifFooter>()); footer = Marshal.ByteArrayToStructureBigEndian <UdifFooter>(footerB); if (footer.signature != UDIF_SIGNATURE) { stream.Seek(0, SeekOrigin.Begin); footerB = new byte[Marshal.SizeOf <UdifFooter>()]; stream.Read(footerB, 0, Marshal.SizeOf <UdifFooter>()); footer = Marshal.ByteArrayToStructureBigEndian <UdifFooter>(footerB); if (footer.signature != UDIF_SIGNATURE) { throw new Exception("Unable to find UDIF signature."); } AaruConsole.VerboseWriteLine("Found obsolete UDIF format."); } AaruConsole.DebugWriteLine("UDIF plugin", "footer.signature = 0x{0:X8}", footer.signature); AaruConsole.DebugWriteLine("UDIF plugin", "footer.version = {0}", footer.version); AaruConsole.DebugWriteLine("UDIF plugin", "footer.headerSize = {0}", footer.headerSize); AaruConsole.DebugWriteLine("UDIF plugin", "footer.flags = {0}", footer.flags); AaruConsole.DebugWriteLine("UDIF plugin", "footer.runningDataForkOff = {0}", footer.runningDataForkOff); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkOff = {0}", footer.dataForkOff); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkOff = {0}", footer.rsrcForkOff); AaruConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkLen = {0}", footer.rsrcForkLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.segmentNumber = {0}", footer.segmentNumber); AaruConsole.DebugWriteLine("UDIF plugin", "footer.segmentCount = {0}", footer.segmentCount); AaruConsole.DebugWriteLine("UDIF plugin", "footer.segmentId = {0}", footer.segmentId); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChkType = {0}", footer.dataForkChkType); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChk = 0x{0:X8}", footer.dataForkChk); AaruConsole.DebugWriteLine("UDIF plugin", "footer.plistOff = {0}", footer.plistOff); AaruConsole.DebugWriteLine("UDIF plugin", "footer.plistLen = {0}", footer.plistLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.masterChkType = {0}", footer.masterChkType); AaruConsole.DebugWriteLine("UDIF plugin", "footer.masterChkLen = {0}", footer.masterChkLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.masterChk = 0x{0:X8}", footer.masterChk); AaruConsole.DebugWriteLine("UDIF plugin", "footer.imageVariant = {0}", footer.imageVariant); AaruConsole.DebugWriteLine("UDIF plugin", "footer.sectorCount = {0}", footer.sectorCount); AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved1 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved1)); AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved2 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved2)); AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved3 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved3)); AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved4 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved4)); // Block chunks and headers List <byte[]> blkxList = new List <byte[]>(); chunks = new Dictionary <ulong, BlockChunk>(); bool fakeBlockChunks = false; byte[] vers = null; if (footer.plistLen == 0 && footer.rsrcForkLen != 0) { AaruConsole.DebugWriteLine("UDIF plugin", "Reading resource fork."); byte[] rsrcB = new byte[footer.rsrcForkLen]; stream.Seek((long)footer.rsrcForkOff, SeekOrigin.Begin); stream.Read(rsrcB, 0, rsrcB.Length); var rsrc = new ResourceFork(rsrcB); if (!rsrc.ContainsKey(BLOCK_OS_TYPE)) { throw new ImageNotSupportedException("Image resource fork doesn't contain UDIF block chunks. Please fill an issue and send it to us."); } Resource blkxRez = rsrc.GetResource(BLOCK_OS_TYPE); if (blkxRez == null) { throw new ImageNotSupportedException("Image resource fork doesn't contain UDIF block chunks. Please fill an issue and send it to us."); } if (blkxRez.GetIds().Length == 0) { throw new ImageNotSupportedException("Image resource fork doesn't contain UDIF block chunks. Please fill an issue and send it to us."); } blkxList.AddRange(blkxRez.GetIds().Select(blkxId => blkxRez.GetResource(blkxId))); Resource versRez = rsrc.GetResource(0x76657273); if (versRez != null) { vers = versRez.GetResource(versRez.GetIds()[0]); } } else if (footer.plistLen != 0) { AaruConsole.DebugWriteLine("UDIF plugin", "Reading property list."); byte[] plistB = new byte[footer.plistLen]; stream.Seek((long)footer.plistOff, SeekOrigin.Begin); stream.Read(plistB, 0, plistB.Length); AaruConsole.DebugWriteLine("UDIF plugin", "Parsing property list."); var plist = (NSDictionary)XmlPropertyListParser.Parse(plistB); if (plist == null) { throw new Exception("Could not parse property list."); } if (!plist.TryGetValue(RESOURCE_FORK_KEY, out NSObject rsrcObj)) { throw new Exception("Could not retrieve resource fork."); } var rsrc = (NSDictionary)rsrcObj; if (!rsrc.TryGetValue(BLOCK_KEY, out NSObject blkxObj)) { throw new Exception("Could not retrieve block chunks array."); } NSObject[] blkx = ((NSArray)blkxObj).GetArray(); foreach (NSDictionary part in blkx.Cast <NSDictionary>()) { if (!part.TryGetValue("Name", out _)) { throw new Exception("Could not retrieve Name"); } if (!part.TryGetValue("Data", out NSObject dataObj)) { throw new Exception("Could not retrieve Data"); } blkxList.Add(((NSData)dataObj).Bytes); } if (rsrc.TryGetValue("vers", out NSObject versObj)) { NSObject[] versArray = ((NSArray)versObj).GetArray(); if (versArray.Length >= 1) { vers = ((NSData)versArray[0]).Bytes; } } } else { // Obsolete read-only UDIF only prepended the header and then put the image without any kind of block references. // So let's falsify a block chunk var bChnk = new BlockChunk { length = footer.dataForkLen, offset = footer.dataForkOff, sector = 0, sectors = footer.sectorCount, type = CHUNK_TYPE_COPY }; imageInfo.Sectors = footer.sectorCount; chunks.Add(bChnk.sector, bChnk); buffersize = 2048 * SECTOR_SIZE; fakeBlockChunks = true; } if (vers != null) { var version = new Version(vers); string release = 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}"; } switch (version.DevStage) { case Version.DevelopmentStage.Alpha: dev = "a"; break; case Version.DevelopmentStage.Beta: dev = "b"; break; case Version.DevelopmentStage.PreAlpha: dev = "d"; break; } if (dev == null && version.PreReleaseVersion > 0) { dev = "f"; } if (dev != null) { pre = $"{version.PreReleaseVersion}"; } imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}"; imageInfo.Application = version.VersionString; imageInfo.Comments = version.VersionMessage; if (version.MajorVersion == 3) { imageInfo.Application = "ShrinkWrap™"; } else if (version.MajorVersion == 6) { imageInfo.Application = "DiskCopy"; } } else { imageInfo.Application = "DiskCopy"; } AaruConsole.DebugWriteLine("UDIF plugin", "Image application = {0} version {1}", imageInfo.Application, imageInfo.ApplicationVersion); imageInfo.Sectors = 0; if (!fakeBlockChunks) { if (blkxList.Count == 0) { throw new ImageNotSupportedException("Could not retrieve block chunks. Please fill an issue and send it to us."); } buffersize = 0; foreach (byte[] blkxBytes in blkxList) { var bHdr = new BlockHeader(); byte[] bHdrB = new byte[Marshal.SizeOf <BlockHeader>()]; Array.Copy(blkxBytes, 0, bHdrB, 0, Marshal.SizeOf <BlockHeader>()); bHdr = Marshal.ByteArrayToStructureBigEndian <BlockHeader>(bHdrB); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.signature = 0x{0:X8}", bHdr.signature); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.version = {0}", bHdr.version); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorStart = {0}", bHdr.sectorStart); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorCount = {0}", bHdr.sectorCount); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.dataOffset = {0}", bHdr.dataOffset); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.buffers = {0}", bHdr.buffers); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.descriptor = 0x{0:X8}", bHdr.descriptor); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved1 = {0}", bHdr.reserved1); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved2 = {0}", bHdr.reserved2); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved3 = {0}", bHdr.reserved3); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved4 = {0}", bHdr.reserved4); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved5 = {0}", bHdr.reserved5); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved6 = {0}", bHdr.reserved6); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumType = {0}", bHdr.checksumType); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumLen = {0}", bHdr.checksumLen); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.checksum = 0x{0:X8}", bHdr.checksum); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunks = {0}", bHdr.chunks); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reservedChk is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(bHdr.reservedChk)); if (bHdr.buffers > buffersize) { buffersize = bHdr.buffers * SECTOR_SIZE; } for (int i = 0; i < bHdr.chunks; i++) { var bChnk = new BlockChunk(); byte[] bChnkB = new byte[Marshal.SizeOf <BlockChunk>()]; Array.Copy(blkxBytes, Marshal.SizeOf <BlockHeader>() + (Marshal.SizeOf <BlockChunk>() * i), bChnkB, 0, Marshal.SizeOf <BlockChunk>()); bChnk = Marshal.ByteArrayToStructureBigEndian <BlockChunk>(bChnkB); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].type = 0x{1:X8}", i, bChnk.type); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].comment = {1}", i, bChnk.comment); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sector = {1}", i, bChnk.sector); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sectors = {1}", i, bChnk.sectors); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].offset = {1}", i, bChnk.offset); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].length = {1}", i, bChnk.length); if (bChnk.type == CHUNK_TYPE_END) { break; } imageInfo.Sectors += bChnk.sectors; // Chunk offset is relative bChnk.sector += bHdr.sectorStart; bChnk.offset += bHdr.dataOffset; switch (bChnk.type) { // TODO: Handle comments case CHUNK_TYPE_COMMNT: continue; // TODO: Handle compressed chunks case CHUNK_TYPE_KENCODE: throw new ImageNotSupportedException("Chunks compressed with KenCode are not yet supported."); case CHUNK_TYPE_LZH: throw new ImageNotSupportedException("Chunks compressed with LZH are not yet supported."); case CHUNK_TYPE_LZFSE: throw new ImageNotSupportedException("Chunks compressed with lzfse are not yet supported."); } if ((bChnk.type > CHUNK_TYPE_NOCOPY && bChnk.type < CHUNK_TYPE_COMMNT) || (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); } } } } sectorCache = new Dictionary <ulong, byte[]>(); chunkCache = new Dictionary <ulong, byte[]>(); currentChunkCacheSize = 0; imageStream = stream; 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.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); imageInfo.Heads = 16; imageInfo.SectorsPerTrack = 63; return(true); }
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); if (stream.Length < 84) { return(false); } DartHeader header = new DartHeader(); stream.Seek(0, SeekOrigin.Begin); byte[] headerB = new byte[Marshal.SizeOf(header)]; stream.Read(headerB, 0, Marshal.SizeOf(header)); header = BigEndianMarshal.ByteArrayToStructureBigEndian <DartHeader>(headerB); if (header.srcCmp > COMPRESS_NONE) { return(false); } int expectedMaxSize = 84 + header.srcSize * 2 * 524; switch (header.srcType) { case DISK_MAC: if (header.srcSize != SIZE_MAC_SS && header.srcSize != SIZE_MAC) { return(false); } break; case DISK_LISA: if (header.srcSize != SIZE_LISA) { return(false); } break; case DISK_APPLE2: if (header.srcSize != DISK_APPLE2) { return(false); } break; case DISK_MAC_HD: if (header.srcSize != SIZE_MAC_HD) { return(false); } expectedMaxSize += 64; break; case DISK_DOS: if (header.srcSize != SIZE_DOS) { return(false); } break; case DISK_DOS_HD: if (header.srcSize != SIZE_DOS_HD) { return(false); } expectedMaxSize += 64; break; default: return(false); } if (stream.Length > expectedMaxSize) { return(false); } short[] bLength; if (header.srcType == DISK_MAC_HD || header.srcType == DISK_DOS_HD) { bLength = new short[BLOCK_ARRAY_LEN_HIGH]; } else { bLength = new short[BLOCK_ARRAY_LEN_LOW]; } BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; for (int i = 0; i < bLength.Length; i++) { byte[] tmpShort = new byte[2]; stream.Read(tmpShort, 0, 2); bLength[i] = BigEndianBitConverter.ToInt16(tmpShort, 0); } MemoryStream dataMs = new MemoryStream(); MemoryStream tagMs = new MemoryStream(); foreach (short l in bLength) { if (l != 0) { byte[] buffer = new byte[BUFFER_SIZE]; if (l == -1) { stream.Read(buffer, 0, BUFFER_SIZE); dataMs.Write(buffer, 0, DATA_SIZE); tagMs.Write(buffer, DATA_SIZE, TAG_SIZE); } else { byte[] temp; if (header.srcCmp == COMPRESS_RLE) { temp = new byte[l * 2]; stream.Read(temp, 0, temp.Length); AppleRle rle = new AppleRle(new MemoryStream(temp)); buffer = new byte[BUFFER_SIZE]; for (int i = 0; i < BUFFER_SIZE; i++) { buffer[i] = (byte)rle.ProduceByte(); } dataMs.Write(buffer, 0, DATA_SIZE); tagMs.Write(buffer, DATA_SIZE, TAG_SIZE); } else { temp = new byte[l]; stream.Read(temp, 0, temp.Length); throw new ImageNotSupportedException("LZH Compressed images not yet supported"); } } } } dataCache = dataMs.ToArray(); if (header.srcType == DISK_LISA || header.srcType == DISK_MAC || header.srcType == DISK_APPLE2) { imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag); tagCache = tagMs.ToArray(); } try { if (imageFilter.HasResourceFork()) { ResourceFork rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream()); // "vers" if (rsrcFork.ContainsKey(0x76657273)) { Resource versRsrc = rsrcFork.GetResource(0x76657273); byte[] vers = versRsrc?.GetResource(versRsrc.GetIds()[0]); if (vers != null) { Version version = new Version(vers); string release = 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}"; } switch (version.DevStage) { case Version.DevelopmentStage.Alpha: dev = "a"; break; case Version.DevelopmentStage.Beta: dev = "b"; break; case Version.DevelopmentStage.PreAlpha: dev = "d"; break; } if (dev == null && version.PreReleaseVersion > 0) { dev = "f"; } if (dev != null) { pre = $"{version.PreReleaseVersion}"; } imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}"; imageInfo.Application = version.VersionString; imageInfo.Comments = version.VersionMessage; } } // "dart" if (rsrcFork.ContainsKey(0x44415254)) { Resource dartRsrc = rsrcFork.GetResource(0x44415254); if (dartRsrc != null) { string dArt = StringHandlers.PascalToString(dartRsrc.GetResource(dartRsrc.GetIds()[0]), Encoding.GetEncoding("macintosh")); const string DART_REGEX = @"(?<version>\S+), tag checksum=\$(?<tagchk>[0123456789ABCDEF]{8}), data checksum=\$(?<datachk>[0123456789ABCDEF]{8})$"; Regex dArtEx = new Regex(DART_REGEX); Match dArtMatch = dArtEx.Match(dArt); if (dArtMatch.Success) { imageInfo.Application = "DART"; imageInfo.ApplicationVersion = dArtMatch.Groups["version"].Value; dataChecksum = Convert.ToUInt32(dArtMatch.Groups["datachk"].Value, 16); tagChecksum = Convert.ToUInt32(dArtMatch.Groups["tagchk"].Value, 16); } } } // "cksm" if (rsrcFork.ContainsKey(0x434B534D)) { Resource cksmRsrc = rsrcFork.GetResource(0x434B534D); if (cksmRsrc?.ContainsId(1) == true) { byte[] tagChk = cksmRsrc.GetResource(1); tagChecksum = BigEndianBitConverter.ToUInt32(tagChk, 0); } if (cksmRsrc?.ContainsId(2) == true) { byte[] dataChk = cksmRsrc.GetResource(1); dataChecksum = BigEndianBitConverter.ToUInt32(dataChk, 0); } } } } catch (InvalidCastException) { } DicConsole.DebugWriteLine("DART plugin", "Image application = {0} version {1}", imageInfo.Application, imageInfo.ApplicationVersion); imageInfo.Sectors = (ulong)(header.srcSize * 2); imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); imageInfo.SectorSize = SECTOR_SIZE; imageInfo.XmlMediaType = XmlMediaType.BlockMedia; imageInfo.ImageSize = imageInfo.Sectors * SECTOR_SIZE; imageInfo.Version = header.srcCmp == COMPRESS_NONE ? "1.4" : "1.5"; switch (header.srcSize) { case SIZE_MAC_SS: imageInfo.Cylinders = 80; imageInfo.Heads = 1; imageInfo.SectorsPerTrack = 10; imageInfo.MediaType = MediaType.AppleSonySS; break; case SIZE_MAC: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 10; imageInfo.MediaType = MediaType.AppleSonyDS; break; case SIZE_DOS: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 9; imageInfo.MediaType = MediaType.DOS_35_DS_DD_9; break; case SIZE_MAC_HD: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 18; imageInfo.MediaType = MediaType.DOS_35_HD; break; } return(true); }
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); 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(); 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.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]; AaruConsole.DebugWriteLine("DC42 plugin", "header.diskName = \"{0}\"", header.DiskName); AaruConsole.DebugWriteLine("DC42 plugin", "header.dataSize = {0} bytes", header.DataSize); AaruConsole.DebugWriteLine("DC42 plugin", "header.tagSize = {0} bytes", header.TagSize); AaruConsole.DebugWriteLine("DC42 plugin", "header.dataChecksum = 0x{0:X8}", header.DataChecksum); AaruConsole.DebugWriteLine("DC42 plugin", "header.tagChecksum = 0x{0:X8}", header.TagChecksum); AaruConsole.DebugWriteLine("DC42 plugin", "header.format = 0x{0:X2}", header.Format); AaruConsole.DebugWriteLine("DC42 plugin", "header.fmtByte = 0x{0:X2}", header.FmtByte); AaruConsole.DebugWriteLine("DC42 plugin", "header.valid = {0}", header.Valid); AaruConsole.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.DataSize = BitConverter.ToUInt32(buffer, 0x40); header.TagSize = BitConverter.ToUInt32(buffer, 0x44); header.DataChecksum = BitConverter.ToUInt32(buffer, 0x48); header.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C); if (header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() && header.Format != kSigmaFormatTwiggy) { return(false); } } if (header.Format != kSonyFormat400K && header.Format != kSonyFormat800K && header.Format != kSonyFormat720K && header.Format != kSonyFormat1440K && header.Format != kSonyFormat1680K && header.Format != kSigmaFormatTwiggy && header.Format != kNotStandardFormat) { AaruConsole.DebugWriteLine("DC42 plugin", "Unknown header.format = 0x{0:X2} value", header.Format); 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) { AaruConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value", header.FmtByte); return(false); } if (header.FmtByte == kInvalidFmtByte) { AaruConsole.DebugWriteLine("DC42 plugin", "Image says it's unformatted"); return(false); } dataOffset = 0x54; tagOffset = header.TagSize != 0 ? 0x54 + header.DataSize : 0; imageInfo.SectorSize = 512; bptag = (uint)(header.TagSize != 0 ? 12 : 0); dc42ImageFilter = imageFilter; imageInfo.Sectors = header.DataSize / 512; if (header.TagSize != 0) { bptag = (uint)(header.TagSize / imageInfo.Sectors); AaruConsole.DebugWriteLine("DC42 plugin", "bptag = {0} bytes", bptag); if (bptag != 12 && bptag != 20 && bptag != 24) { AaruConsole.DebugWriteLine("DC42 plugin", "Unknown tag size"); return(false); } imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag); } imageInfo.ImageSize = (imageInfo.Sectors * imageInfo.SectorSize) + (imageInfo.Sectors * bptag); imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); imageInfo.MediaTitle = header.DiskName; switch (header.Format) { case kSonyFormat400K: imageInfo.MediaType = imageInfo.Sectors == 1600 ? MediaType.AppleSonyDS : MediaType.AppleSonySS; break; case kSonyFormat800K: imageInfo.MediaType = MediaType.AppleSonyDS; break; case kSonyFormat720K: imageInfo.MediaType = MediaType.DOS_35_DS_DD_9; break; case kSonyFormat1440K: imageInfo.MediaType = MediaType.DOS_35_HD; break; case kSonyFormat1680K: imageInfo.MediaType = MediaType.DMF; break; case kSigmaFormatTwiggy: imageInfo.MediaType = MediaType.AppleFileWare; break; case kNotStandardFormat: switch (imageInfo.Sectors) { case 9728: imageInfo.MediaType = MediaType.AppleProfile; break; case 19456: imageInfo.MediaType = MediaType.AppleProfile; break; case 38912: imageInfo.MediaType = MediaType.AppleWidget; break; case 39040: imageInfo.MediaType = MediaType.AppleHD20; break; default: imageInfo.MediaType = MediaType.Unknown; break; } break; default: imageInfo.MediaType = MediaType.Unknown; break; } if (imageInfo.MediaType == MediaType.AppleFileWare) { byte[] data = new byte[header.DataSize]; byte[] tags = new byte[header.TagSize]; twiggyCache = new byte[header.DataSize]; twiggyCacheTags = new byte[header.TagSize]; twiggy = true; Stream datastream = imageFilter.GetDataForkStream(); datastream.Seek(dataOffset, SeekOrigin.Begin); datastream.Read(data, 0, (int)header.DataSize); Stream tagstream = imageFilter.GetDataForkStream(); tagstream.Seek(tagOffset, SeekOrigin.Begin); tagstream.Read(tags, 0, (int)header.TagSize); 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) { AaruConsole.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); } else { AaruConsole.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); 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 >= 11 && i <= 16) { sectorsToCopy = 20; } if (i >= 17 && i <= 22) { sectorsToCopy = 19; } if (i >= 23 && i <= 28) { sectorsToCopy = 18; } if (i >= 29 && i <= 34) { sectorsToCopy = 17; } if (i >= 35 && i <= 41) { sectorsToCopy = 16; } 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); copiedSectors += sectorsToCopy; } } } try { if (imageFilter.HasResourceFork()) { var rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream()); if (rsrcFork.ContainsKey(0x76657273)) { Resource versRsrc = rsrcFork.GetResource(0x76657273); byte[] vers = versRsrc?.GetResource(versRsrc.GetIds()[0]); if (vers != null) { var version = new Version(vers); string release = 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}"; } switch (version.DevStage) { case Version.DevelopmentStage.Alpha: dev = "a"; break; case Version.DevelopmentStage.Beta: dev = "b"; break; case Version.DevelopmentStage.PreAlpha: dev = "d"; break; } if (dev == null && version.PreReleaseVersion > 0) { dev = "f"; } if (dev != null) { pre = $"{version.PreReleaseVersion}"; } imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}"; imageInfo.Application = version.VersionString; imageInfo.Comments = version.VersionMessage; } } if (rsrcFork.ContainsKey(0x64437079)) { Resource dCpyRsrc = rsrcFork.GetResource(0x64437079); if (dCpyRsrc != null) { string dCpy = StringHandlers.PascalToString(dCpyRsrc.GetResource(dCpyRsrc.GetIds()[0]), Encoding.GetEncoding("macintosh")); var dCpyEx = new Regex(REGEX_DCPY); Match dCpyMatch = dCpyEx.Match(dCpy); if (dCpyMatch.Success) { imageInfo.Application = dCpyMatch.Groups["application"].Value; imageInfo.ApplicationVersion = dCpyMatch.Groups["version"].Value; } } } } } catch (InvalidCastException) {} AaruConsole.DebugWriteLine("DC42 plugin", "Image application = {0} version {1}", imageInfo.Application, imageInfo.ApplicationVersion); imageInfo.XmlMediaType = XmlMediaType.BlockMedia; AaruConsole.VerboseWriteLine("DiskCopy 4.2 image contains a disk of type {0}", imageInfo.MediaType); switch (imageInfo.MediaType) { case MediaType.AppleSonySS: imageInfo.Cylinders = 80; imageInfo.Heads = 1; imageInfo.SectorsPerTrack = 10; break; case MediaType.AppleSonyDS: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 10; break; case MediaType.DOS_35_DS_DD_9: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 9; break; case MediaType.DOS_35_HD: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 18; break; case MediaType.DMF: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 21; break; case MediaType.AppleProfile: switch (imageInfo.Sectors) { case 9728: imageInfo.Cylinders = 152; break; case 19456: imageInfo.Cylinders = 304; break; } imageInfo.Heads = 4; imageInfo.SectorsPerTrack = 16; break; case MediaType.AppleWidget: imageInfo.Cylinders = 608; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 16; break; case MediaType.AppleHD20: imageInfo.Cylinders = 610; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 16; break; default: imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); imageInfo.Heads = 16; imageInfo.SectorsPerTrack = 63; break; } return(true); }
public bool Open(IFilter imageFilter) { if (!imageFilter.HasResourceFork() || imageFilter.GetResourceForkLength() == 0) { return(false); } ResourceFork rsrcFork; Resource rsrc; short[] bcems; try { rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream()); if (!rsrcFork.ContainsKey(NDIF_RESOURCE)) { return(false); } rsrc = rsrcFork.GetResource(NDIF_RESOURCE); bcems = rsrc.GetIds(); if (bcems == null || bcems.Length == 0) { return(false); } } catch (InvalidCastException) { return(false); } imageInfo.Sectors = 0; foreach (byte[] bcem in bcems.Select(id => rsrc.GetResource(NDIF_RESOURCEID))) { if (bcem.Length < 128) { return(false); } header = Marshal.ByteArrayToStructureBigEndian <ChunkHeader>(bcem); DicConsole.DebugWriteLine("NDIF plugin", "footer.type = {0}", header.version); DicConsole.DebugWriteLine("NDIF plugin", "footer.driver = {0}", header.driver); DicConsole.DebugWriteLine("NDIF plugin", "footer.name = {0}", StringHandlers.PascalToString(header.name, Encoding.GetEncoding("macintosh"))); DicConsole.DebugWriteLine("NDIF plugin", "footer.sectors = {0}", header.sectors); DicConsole.DebugWriteLine("NDIF plugin", "footer.maxSectorsPerChunk = {0}", header.maxSectorsPerChunk); DicConsole.DebugWriteLine("NDIF plugin", "footer.dataOffset = {0}", header.dataOffset); DicConsole.DebugWriteLine("NDIF plugin", "footer.crc = 0x{0:X7}", header.crc); DicConsole.DebugWriteLine("NDIF plugin", "footer.segmented = {0}", header.segmented); DicConsole.DebugWriteLine("NDIF plugin", "footer.p1 = 0x{0:X8}", header.p1); DicConsole.DebugWriteLine("NDIF plugin", "footer.p2 = 0x{0:X8}", header.p2); DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[0] = 0x{0:X8}", header.unknown[0]); DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[1] = 0x{0:X8}", header.unknown[1]); DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[2] = 0x{0:X8}", header.unknown[2]); DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[3] = 0x{0:X8}", header.unknown[3]); DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[4] = 0x{0:X8}", header.unknown[4]); DicConsole.DebugWriteLine("NDIF plugin", "footer.encrypted = {0}", header.encrypted); DicConsole.DebugWriteLine("NDIF plugin", "footer.hash = 0x{0:X8}", header.hash); DicConsole.DebugWriteLine("NDIF plugin", "footer.chunks = {0}", header.chunks); // Block chunks and headers chunks = new Dictionary <ulong, BlockChunk>(); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; for (int i = 0; i < header.chunks; i++) { // Obsolete read-only NDIF only prepended the header and then put the image without any kind of block references. // So let's falsify a block chunk BlockChunk bChnk = new BlockChunk(); byte[] sector = new byte[4]; Array.Copy(bcem, 128 + 0 + i * 12, sector, 1, 3); bChnk.sector = BigEndianBitConverter.ToUInt32(sector, 0); bChnk.type = bcem[128 + 3 + i * 12]; bChnk.offset = BigEndianBitConverter.ToUInt32(bcem, 128 + 4 + i * 12); bChnk.length = BigEndianBitConverter.ToUInt32(bcem, 128 + 8 + i * 12); DicConsole.DebugWriteLine("NDIF plugin", "bHdr.chunk[{0}].type = 0x{1:X2}", i, bChnk.type); DicConsole.DebugWriteLine("NDIF plugin", "bHdr.chunk[{0}].sector = {1}", i, bChnk.sector); DicConsole.DebugWriteLine("NDIF plugin", "bHdr.chunk[{0}].offset = {1}", i, bChnk.offset); DicConsole.DebugWriteLine("NDIF plugin", "bHdr.chunk[{0}].length = {1}", i, bChnk.length); if (bChnk.type == CHUNK_TYPE_END) { break; } bChnk.offset += header.dataOffset; bChnk.sector += (uint)imageInfo.Sectors; // TODO: Handle compressed chunks switch (bChnk.type) { case CHUNK_TYPE_KENCODE: throw new ImageNotSupportedException("Chunks compressed with KenCode are not yet supported."); case CHUNK_TYPE_LZH: throw new ImageNotSupportedException("Chunks compressed with LZH are not yet supported."); case CHUNK_TYPE_STUFFIT: throw new ImageNotSupportedException("Chunks compressed with StuffIt! are not yet supported."); } // TODO: Handle compressed chunks if (bChnk.type > CHUNK_TYPE_COPY && bChnk.type < CHUNK_TYPE_KENCODE || bChnk.type > CHUNK_TYPE_ADC && bChnk.type < CHUNK_TYPE_STUFFIT || bChnk.type > CHUNK_TYPE_STUFFIT && bChnk.type < CHUNK_TYPE_END || bChnk.type == 1) { throw new ImageNotSupportedException($"Unsupported chunk type 0x{bChnk.type:X8} found"); } chunks.Add(bChnk.sector, bChnk); } imageInfo.Sectors += header.sectors; } if (header.segmented > 0) { throw new ImageNotSupportedException("Segmented images are not yet supported."); } if (header.encrypted > 0) { throw new ImageNotSupportedException("Encrypted images are not yet supported."); } switch (imageInfo.Sectors) { case 1440: imageInfo.MediaType = MediaType.DOS_35_DS_DD_9; break; case 1600: imageInfo.MediaType = MediaType.AppleSonyDS; break; case 2880: imageInfo.MediaType = MediaType.DOS_35_HD; break; case 3360: imageInfo.MediaType = MediaType.DMF; break; default: imageInfo.MediaType = MediaType.GENERIC_HDD; break; } if (rsrcFork.ContainsKey(0x76657273)) { Resource versRsrc = rsrcFork.GetResource(0x76657273); if (versRsrc != null) { byte[] vers = versRsrc.GetResource(versRsrc.GetIds()[0]); Version version = new Version(vers); string release = 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}"; } switch (version.DevStage) { case Version.DevelopmentStage.Alpha: dev = "a"; break; case Version.DevelopmentStage.Beta: dev = "b"; break; case Version.DevelopmentStage.PreAlpha: dev = "d"; break; } if (dev == null && version.PreReleaseVersion > 0) { dev = "f"; } if (dev != null) { pre = $"{version.PreReleaseVersion}"; } imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}"; imageInfo.Application = version.VersionString; imageInfo.Comments = version.VersionMessage; if (version.MajorVersion == 3) { imageInfo.Application = "ShrinkWrap™"; } else if (version.MajorVersion == 6) { imageInfo.Application = "DiskCopy"; } } } DicConsole.DebugWriteLine("NDIF plugin", "Image application = {0} version {1}", imageInfo.Application, imageInfo.ApplicationVersion); sectorCache = new Dictionary <ulong, byte[]>(); chunkCache = new Dictionary <ulong, byte[]>(); currentChunkCacheSize = 0; imageStream = imageFilter.GetDataForkStream(); buffersize = header.maxSectorsPerChunk * SECTOR_SIZE; imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); imageInfo.MediaTitle = StringHandlers.PascalToString(header.name, Encoding.GetEncoding("macintosh")); imageInfo.SectorSize = SECTOR_SIZE; imageInfo.XmlMediaType = XmlMediaType.BlockMedia; imageInfo.ImageSize = imageInfo.Sectors * SECTOR_SIZE; imageInfo.ApplicationVersion = "6"; imageInfo.Application = "Apple DiskCopy"; switch (imageInfo.MediaType) { case MediaType.AppleSonyDS: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 10; break; case MediaType.DOS_35_DS_DD_9: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 9; break; case MediaType.DOS_35_HD: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 18; break; case MediaType.DMF: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 21; break; default: imageInfo.MediaType = MediaType.GENERIC_HDD; imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); imageInfo.Heads = 16; imageInfo.SectorsPerTrack = 63; break; } return(true); }