public void Open(string path) { var fs = new FileStream(path, FileMode.Open, FileAccess.Read); fs.Seek(0, SeekOrigin.Begin); byte[] hdrB = new byte[128]; fs.Read(hdrB, 0, 128); _header = Marshal.ByteArrayToStructureBigEndian <Header>(hdrB); uint blocks = 1; blocks += (uint)(_header.secondaryHeaderLength / 128); if (_header.secondaryHeaderLength % 128 > 0) { blocks++; } _dataForkOff = blocks * 128; blocks += _header.dataLength / 128; if (_header.dataLength % 128 > 0) { blocks++; } _rsrcForkOff = blocks * 128; _filename = StringHandlers.PascalToString(_header.filename, Encoding.GetEncoding("macintosh")); _creationTime = DateHandlers.MacToDateTime(_header.creationTime); _lastWriteTime = DateHandlers.MacToDateTime(_header.modificationTime); fs.Close(); _opened = true; _isPath = true; _basePath = path; }
public void Open(byte[] buffer) { var ms = new MemoryStream(buffer); ms.Seek(0, SeekOrigin.Begin); byte[] hdrB = new byte[128]; ms.Read(hdrB, 0, 128); _header = Marshal.ByteArrayToStructureBigEndian <Header>(hdrB); uint blocks = 1; blocks += (uint)(_header.secondaryHeaderLength / 128); if (_header.secondaryHeaderLength % 128 > 0) { blocks++; } _dataForkOff = blocks * 128; blocks += _header.dataLength / 128; if (_header.dataLength % 128 > 0) { blocks++; } _rsrcForkOff = blocks * 128; _filename = StringHandlers.PascalToString(_header.filename, Encoding.GetEncoding("macintosh")); _creationTime = DateHandlers.MacToDateTime(_header.creationTime); _lastWriteTime = DateHandlers.MacToDateTime(_header.modificationTime); ms.Close(); _opened = true; _isBytes = true; _bytes = buffer; }
public Errno ReadDir(string path, out List <string> contents) { contents = null; if (!mounted) { return(Errno.AccessDenied); } if (!string.IsNullOrEmpty(path) && string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) { return(Errno.NotSupported); } contents = fileEntries.Select(ent => StringHandlers.PascalToString(ent.Filename, Encoding)).ToList(); if (debug) { contents.Add("$"); contents.Add("$Boot"); } contents.Sort(); return(Errno.NoError); }
public void Open(Stream stream) { stream.Seek(0, SeekOrigin.Begin); byte[] hdr_b = new byte[128]; stream.Read(hdr_b, 0, 128); header = Marshal.ByteArrayToStructureBigEndian <MacBinaryHeader>(hdr_b); uint blocks = 1; blocks += (uint)(header.secondaryHeaderLength / 128); if (header.secondaryHeaderLength % 128 > 0) { blocks++; } dataForkOff = blocks * 128; blocks += header.dataLength / 128; if (header.dataLength % 128 > 0) { blocks++; } rsrcForkOff = blocks * 128; filename = StringHandlers.PascalToString(header.filename, Encoding.GetEncoding("macintosh")); creationTime = DateHandlers.MacToDateTime(header.creationTime); lastWriteTime = DateHandlers.MacToDateTime(header.modificationTime); stream.Seek(0, SeekOrigin.Begin); opened = true; isStream = true; this.stream = stream; }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { device = imagePlugin; partitionStart = partition.Start; Encoding = encoding ?? Encoding.GetEncoding("macintosh"); if (options == null) { options = GetDefaultOptions(); } if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out debug); } volMDB = new MFS_MasterDirectoryBlock(); mdbBlocks = device.ReadSector(2 + partitionStart); bootBlocks = device.ReadSector(0 + partitionStart); volMDB.drSigWord = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x000); if (volMDB.drSigWord != MFS_MAGIC) { return(Errno.InvalidArgument); } volMDB.drCrDate = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x002); volMDB.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x006); volMDB.drAtrb = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00A); volMDB.drNmFls = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00C); volMDB.drDirSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00E); volMDB.drBlLen = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x010); volMDB.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x012); volMDB.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x014); volMDB.drClpSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x018); volMDB.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x01C); volMDB.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x01E); volMDB.drFreeBks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x022); volMDB.drVNSiz = mdbBlocks[0x024]; byte[] variableSize = new byte[volMDB.drVNSiz + 1]; Array.Copy(mdbBlocks, 0x024, variableSize, 0, volMDB.drVNSiz + 1); volMDB.drVN = StringHandlers.PascalToString(variableSize, Encoding); directoryBlocks = device.ReadSectors(volMDB.drDirSt + partitionStart, volMDB.drBlLen); int bytesInBlockMap = volMDB.drNmAlBlks * 12 / 8 + volMDB.drNmAlBlks * 12 % 8; const int BYTES_BEFORE_BLOCK_MAP = 64; int bytesInWholeMdb = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP; int sectorsInWholeMdb = bytesInWholeMdb / (int)device.Info.SectorSize + bytesInWholeMdb % (int)device.Info.SectorSize; byte[] wholeMdb = device.ReadSectors(partitionStart + 2, (uint)sectorsInWholeMdb); blockMapBytes = new byte[bytesInBlockMap]; Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, blockMapBytes, 0, blockMapBytes.Length); int offset = 0; blockMap = new uint[volMDB.drNmAlBlks + 2 + 1]; for (int i = 2; i < volMDB.drNmAlBlks + 2; i += 8) { uint tmp1 = 0; uint tmp2 = 0; uint tmp3 = 0; if (offset + 4 <= blockMapBytes.Length) { tmp1 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset); } if (offset + 4 + 4 <= blockMapBytes.Length) { tmp2 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 4); } if (offset + 8 + 4 <= blockMapBytes.Length) { tmp3 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 8); } if (i < blockMap.Length) { blockMap[i] = (tmp1 & 0xFFF00000) >> 20; } if (i + 2 < blockMap.Length) { blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8; } if (i + 3 < blockMap.Length) { blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28); } if (i + 4 < blockMap.Length) { blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16; } if (i + 5 < blockMap.Length) { blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4; } if (i + 6 < blockMap.Length) { blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24); } if (i + 7 < blockMap.Length) { blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12; } if (i + 8 < blockMap.Length) { blockMap[i + 7] = tmp3 & 0xFFF; } offset += 12; } if (device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) { mdbTags = device.ReadSectorTag(2 + partitionStart, SectorTagType.AppleSectorTag); bootTags = device.ReadSectorTag(0 + partitionStart, SectorTagType.AppleSectorTag); directoryTags = device.ReadSectorsTag(volMDB.drDirSt + partitionStart, volMDB.drBlLen, SectorTagType.AppleSectorTag); bitmapTags = device.ReadSectorsTag(partitionStart + 2, (uint)sectorsInWholeMdb, SectorTagType.AppleSectorTag); } sectorsPerBlock = (int)(volMDB.drAlBlkSiz / device.Info.SectorSize); if (!FillDirectory()) { return(Errno.InvalidArgument); } mounted = true; ushort bbSig = BigEndianBitConverter.ToUInt16(bootBlocks, 0x000); if (bbSig != MFSBB_MAGIC) { bootBlocks = null; } XmlFsType = new FileSystemType(); if (volMDB.drLsBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(volMDB.drLsBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bbSig == MFSBB_MAGIC; XmlFsType.Clusters = volMDB.drNmAlBlks; XmlFsType.ClusterSize = volMDB.drAlBlkSiz; if (volMDB.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(volMDB.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Files = volMDB.drNmFls; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = volMDB.drFreeBks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "MFS"; XmlFsType.VolumeName = volMDB.drVN; return(Errno.NoError); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? new Apple2(); var sbInformation = new StringBuilder(); information = ""; _multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); if (imagePlugin.Info.Sectors < 3) { return; } // Blocks 0 and 1 are boot code byte[] volBlock = imagePlugin.ReadSectors((_multiplier * 2) + partition.Start, _multiplier); // On Apple //, it's little endian // TODO: Fix //BigEndianBitConverter.IsLittleEndian = // multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; var volEntry = new PascalVolumeEntry { FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00), LastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02), EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04), VolumeName = new byte[8], Blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E), Files = BigEndianBitConverter.ToInt16(volBlock, 0x10), Dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12), LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14), Tail = BigEndianBitConverter.ToInt32(volBlock, 0x16) }; Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8); // First block is always 0 (even is it's sector 2) if (volEntry.FirstBlock != 0) { return; } // Last volume record block must be after first block, and before end of device if (volEntry.LastBlock <= volEntry.FirstBlock || (ulong)volEntry.LastBlock > (imagePlugin.Info.Sectors / _multiplier) - 2) { return; } // Volume record entry type must be volume or secure if (volEntry.EntryType != PascalFileKind.Volume && volEntry.EntryType != PascalFileKind.Secure) { return; } // Volume name is max 7 characters if (volEntry.VolumeName[0] > 7) { return; } // Volume blocks is equal to volume sectors if (volEntry.Blocks < 0 || (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / _multiplier) { return; } // There can be not less than zero files if (volEntry.Files < 0) { return; } sbInformation.AppendFormat("Volume record spans from block {0} to block {1}", volEntry.FirstBlock, volEntry.LastBlock).AppendLine(); sbInformation. AppendFormat("Volume name: {0}", StringHandlers.PascalToString(volEntry.VolumeName, Encoding)). AppendLine(); sbInformation.AppendFormat("Volume has {0} blocks", volEntry.Blocks).AppendLine(); sbInformation.AppendFormat("Volume has {0} files", volEntry.Files).AppendLine(); sbInformation. AppendFormat("Volume last booted at {0}", DateHandlers.UcsdPascalToDateTime(volEntry.LastBoot)). AppendLine(); information = sbInformation.ToString(); XmlFsType = new FileSystemType { Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(imagePlugin.ReadSectors(partition.Start, _multiplier * 2)), Clusters = (ulong)volEntry.Blocks, ClusterSize = imagePlugin.Info.SectorSize, Files = (ulong)volEntry.Files, FilesSpecified = true, Type = "UCSD Pascal", VolumeName = StringHandlers.PascalToString(volEntry.VolumeName, Encoding) }; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? new MacRoman(); information = ""; var sb = new StringBuilder(); var mdb = new MasterDirectoryBlock(); byte[] mdbSector = imagePlugin.ReadSector(2 + partition.Start); byte[] bbSector = imagePlugin.ReadSector(0 + partition.Start); mdb.drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x000); if (mdb.drSigWord != MFS_MAGIC) { return; } mdb.drCrDate = BigEndianBitConverter.ToUInt32(mdbSector, 0x002); mdb.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbSector, 0x006); mdb.drAtrb = (AppleCommon.VolumeAttributes)BigEndianBitConverter.ToUInt16(mdbSector, 0x00A); mdb.drNmFls = BigEndianBitConverter.ToUInt16(mdbSector, 0x00C); mdb.drDirSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x00E); mdb.drBlLen = BigEndianBitConverter.ToUInt16(mdbSector, 0x010); mdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbSector, 0x012); mdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x014); mdb.drClpSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x018); mdb.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x01C); mdb.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbSector, 0x01E); mdb.drFreeBks = BigEndianBitConverter.ToUInt16(mdbSector, 0x022); mdb.drVNSiz = mdbSector[0x024]; byte[] variableSize = new byte[mdb.drVNSiz + 1]; Array.Copy(mdbSector, 0x024, variableSize, 0, mdb.drVNSiz + 1); mdb.drVN = StringHandlers.PascalToString(variableSize, Encoding); sb.AppendLine("Apple Macintosh File System"); sb.AppendLine(); sb.AppendLine("Master Directory Block:"); sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drLsBkUp)).AppendLine(); if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.HardwareLock)) { sb.AppendLine("Volume is locked by hardware."); } sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted." : "Volume is mounted."); if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks)) { sb.AppendLine("Volume has spared bad blocks."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache)) { sb.AppendLine("Volume does not need cache."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent)) { sb.AppendLine("Boot volume is inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds)) { sb.AppendLine("There are reused CNIDs."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent)) { sb.AppendLine("Volume is seriously inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock)) { sb.AppendLine("Volume is locked by software."); } sb.AppendFormat("{0} files on volume", mdb.drNmFls).AppendLine(); sb.AppendFormat("First directory sector: {0}", mdb.drDirSt).AppendLine(); sb.AppendFormat("{0} sectors in directory.", mdb.drBlLen).AppendLine(); sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks + 1).AppendLine(); sb.AppendFormat("Size of allocation blocks: {0} bytes", mdb.drAlBlkSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate.", mdb.drClpSiz).AppendLine(); sb.AppendFormat("First allocation block (#2) starts in sector {0}.", mdb.drAlBlSt).AppendLine(); sb.AppendFormat("Next unused file number: {0}", mdb.drNxtFNum).AppendLine(); sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); sb.AppendFormat("Volume name: {0}", mdb.drVN).AppendLine(); string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding); if (bootBlockInfo != null) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine(bootBlockInfo); } else { sb.AppendLine("Volume is not bootable."); } information = sb.ToString(); XmlFsType = new FileSystemType(); if (mdb.drLsBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drLsBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bootBlockInfo != null; XmlFsType.Clusters = mdb.drNmAlBlks; XmlFsType.ClusterSize = mdb.drAlBlkSiz; if (mdb.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Files = mdb.drNmFls; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = mdb.drFreeBks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "MFS"; XmlFsType.VolumeName = mdb.drVN; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = new LisaRoman(); information = ""; StringBuilder sb = new StringBuilder(); try { if (imagePlugin.Info.ReadableSectorTags == null) { return; } if (!imagePlugin.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) { return; } // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors if (imagePlugin.Info.Sectors < 800) { return; } int beforeMddf = -1; // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors for (int i = 0; i < 100; i++) { DecodeTag(imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag), out LisaTag.PriamTag searchTag); DicConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); if (beforeMddf == -1 && searchTag.FileId == FILEID_LOADER_SIGNED) { beforeMddf = i - 1; } if (searchTag.FileId != FILEID_MDDF) { continue; } byte[] sector = imagePlugin.ReadSector((ulong)i); MDDF infoMddf = new MDDF(); byte[] pString = new byte[33]; infoMddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00); infoMddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02); infoMddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A); Array.Copy(sector, 0x0C, pString, 0, 33); infoMddf.volname = StringHandlers.PascalToString(pString, Encoding); infoMddf.unknown1 = sector[0x2D]; Array.Copy(sector, 0x2E, pString, 0, 33); // Prevent garbage infoMddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : ""; infoMddf.unknown2 = sector[0x4F]; infoMddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50); infoMddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54); uint lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58); infoMddf.dtvc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C); infoMddf.dtcc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60); infoMddf.dtvb = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64); infoMddf.dtvs = DateHandlers.LisaToDateTime(lisaTime); infoMddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68); infoMddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C); infoMddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70); infoMddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74); infoMddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78); infoMddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C); infoMddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E); infoMddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80); infoMddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82); infoMddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86); infoMddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A); infoMddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C); infoMddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90); infoMddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94); infoMddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98); infoMddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A); infoMddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C); infoMddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0); infoMddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4); infoMddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8); infoMddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC); infoMddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0); infoMddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2); infoMddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6); infoMddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA); infoMddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE); infoMddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0); infoMddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4); infoMddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC); infoMddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0); infoMddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4); infoMddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8); infoMddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC); infoMddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0); infoMddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4); infoMddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8); infoMddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC); infoMddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0); infoMddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4); infoMddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8); infoMddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC); infoMddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100); infoMddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104); infoMddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108); infoMddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C); infoMddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110); infoMddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114); infoMddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118); infoMddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120); infoMddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122); infoMddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124); infoMddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126); infoMddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C); infoMddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A); infoMddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E); infoMddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132); infoMddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136); infoMddf.vol_left_mounted = sector[0x138]; DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown1 = 0x{0:X2} ({0})", infoMddf.unknown1); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown2 = 0x{0:X2} ({0})", infoMddf.unknown2); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown3 = 0x{0:X8} ({0})", infoMddf.unknown3); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown4 = 0x{0:X4} ({0})", infoMddf.unknown4); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown5 = 0x{0:X8} ({0})", infoMddf.unknown5); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown6 = 0x{0:X8} ({0})", infoMddf.unknown6); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown7 = 0x{0:X8} ({0})", infoMddf.unknown7); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown9 = 0x{0:X4} ({0})", infoMddf.unknown9); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown10 = 0x{0:X8} ({0})", infoMddf.unknown10); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown11 = 0x{0:X8} ({0})", infoMddf.unknown11); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown12 = 0x{0:X8} ({0})", infoMddf.unknown12); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown13 = 0x{0:X8} ({0})", infoMddf.unknown13); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown14 = 0x{0:X8} ({0})", infoMddf.unknown14); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown15 = 0x{0:X8} ({0})", infoMddf.unknown15); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown16 = 0x{0:X8} ({0})", infoMddf.unknown16); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown17 = 0x{0:X4} ({0})", infoMddf.unknown17); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown18 = 0x{0:X8} ({0})", infoMddf.unknown18); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown19 = 0x{0:X8} ({0})", infoMddf.unknown19); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown20 = 0x{0:X8} ({0})", infoMddf.unknown20); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown21 = 0x{0:X8} ({0})", infoMddf.unknown21); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown22 = 0x{0:X8} ({0})", infoMddf.unknown22); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown23 = 0x{0:X8} ({0})", infoMddf.unknown23); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown24 = 0x{0:X8} ({0})", infoMddf.unknown24); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown25 = 0x{0:X8} ({0})", infoMddf.unknown25); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown26 = 0x{0:X8} ({0})", infoMddf.unknown26); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown27 = 0x{0:X8} ({0})", infoMddf.unknown27); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown28 = 0x{0:X8} ({0})", infoMddf.unknown28); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown29 = 0x{0:X8} ({0})", infoMddf.unknown29); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown30 = 0x{0:X8} ({0})", infoMddf.unknown30); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown31 = 0x{0:X8} ({0})", infoMddf.unknown31); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown32 = 0x{0:X8} ({0})", infoMddf.unknown32); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown33 = 0x{0:X8} ({0})", infoMddf.unknown33); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown34 = 0x{0:X8} ({0})", infoMddf.unknown34); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown35 = 0x{0:X8} ({0})", infoMddf.unknown35); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown36 = 0x{0:X8} ({0})", infoMddf.unknown36); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown37 = 0x{0:X8} ({0})", infoMddf.unknown37); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown38 = 0x{0:X8} ({0})", infoMddf.unknown38); DicConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown_timestamp = 0x{0:X8} ({0}, {1})", infoMddf.unknown_timestamp, DateHandlers.LisaToDateTime(infoMddf.unknown_timestamp)); if (infoMddf.mddf_block != i - beforeMddf) { return; } if (infoMddf.vol_size > imagePlugin.Info.Sectors) { return; } if (infoMddf.vol_size - 1 != infoMddf.volsize_minus_one) { return; } if (infoMddf.vol_size - i - 1 != infoMddf.volsize_minus_mddf_minus_one - beforeMddf) { return; } if (infoMddf.datasize > infoMddf.blocksize) { return; } if (infoMddf.blocksize < imagePlugin.Info.SectorSize) { return; } if (infoMddf.datasize != imagePlugin.Info.SectorSize) { return; } switch (infoMddf.fsversion) { case LISA_V1: sb.AppendLine("LisaFS v1"); break; case LISA_V2: sb.AppendLine("LisaFS v2"); break; case LISA_V3: sb.AppendLine("LisaFS v3"); break; default: sb.AppendFormat("Uknown LisaFS version {0}", infoMddf.fsversion).AppendLine(); break; } sb.AppendFormat("Volume name: \"{0}\"", infoMddf.volname).AppendLine(); sb.AppendFormat("Volume password: \"{0}\"", infoMddf.password).AppendLine(); sb.AppendFormat("Volume ID: 0x{0:X16}", infoMddf.volid).AppendLine(); sb.AppendFormat("Backup volume ID: 0x{0:X16}", infoMddf.backup_volid).AppendLine(); sb.AppendFormat("Master copy ID: 0x{0:X8}", infoMddf.master_copy_id).AppendLine(); sb.AppendFormat("Volume is number {0} of {1}", infoMddf.volnum, infoMddf.vol_sequence).AppendLine(); sb.AppendFormat("Serial number of Lisa computer that created this volume: {0}", infoMddf.machine_id) .AppendLine(); sb.AppendFormat("Serial number of Lisa computer that can use this volume's software {0}", infoMddf.serialization).AppendLine(); sb.AppendFormat("Volume created on {0}", infoMddf.dtvc).AppendLine(); sb.AppendFormat("Some timestamp, says {0}", infoMddf.dtcc).AppendLine(); sb.AppendFormat("Volume backed up on {0}", infoMddf.dtvb).AppendLine(); sb.AppendFormat("Volume scavenged on {0}", infoMddf.dtvs).AppendLine(); sb.AppendFormat("MDDF is in block {0}", infoMddf.mddf_block + beforeMddf).AppendLine(); sb.AppendFormat("There are {0} reserved blocks before volume", beforeMddf).AppendLine(); sb.AppendFormat("{0} blocks minus one", infoMddf.volsize_minus_one).AppendLine(); sb.AppendFormat("{0} blocks minus one minus MDDF offset", infoMddf.volsize_minus_mddf_minus_one) .AppendLine(); sb.AppendFormat("{0} blocks in volume", infoMddf.vol_size).AppendLine(); sb.AppendFormat("{0} bytes per sector (uncooked)", infoMddf.blocksize).AppendLine(); sb.AppendFormat("{0} bytes per sector", infoMddf.datasize).AppendLine(); sb.AppendFormat("{0} blocks per cluster", infoMddf.clustersize).AppendLine(); sb.AppendFormat("{0} blocks in filesystem", infoMddf.fs_size).AppendLine(); sb.AppendFormat("{0} files in volume", infoMddf.filecount).AppendLine(); sb.AppendFormat("{0} blocks free", infoMddf.freecount).AppendLine(); sb.AppendFormat("{0} bytes in LisaInfo", infoMddf.label_size).AppendLine(); sb.AppendFormat("Filesystem overhead: {0}", infoMddf.fs_overhead).AppendLine(); sb.AppendFormat("Scanvenger result code: 0x{0:X8}", infoMddf.result_scavenge).AppendLine(); sb.AppendFormat("Boot code: 0x{0:X8}", infoMddf.boot_code).AppendLine(); sb.AppendFormat("Boot environment: 0x{0:X8}", infoMddf.boot_environ).AppendLine(); sb.AppendFormat("Overmount stamp: 0x{0:X16}", infoMddf.overmount_stamp).AppendLine(); sb.AppendFormat("S-Records start at {0} and spans for {1} blocks", infoMddf.srec_ptr + infoMddf.mddf_block + beforeMddf, infoMddf.srec_len) .AppendLine(); sb.AppendLine(infoMddf.vol_left_mounted == 0 ? "Volume is clean" : "Volume is dirty"); information = sb.ToString(); XmlFsType = new FileSystemType(); if (DateTime.Compare(infoMddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0) { XmlFsType.BackupDate = infoMddf.dtvb; XmlFsType.BackupDateSpecified = true; } XmlFsType.Clusters = infoMddf.vol_size; XmlFsType.ClusterSize = (uint)(infoMddf.clustersize * infoMddf.datasize); if (DateTime.Compare(infoMddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0) { XmlFsType.CreationDate = infoMddf.dtvc; XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = infoMddf.vol_left_mounted != 0; XmlFsType.Files = infoMddf.filecount; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = infoMddf.freecount; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "LisaFS"; XmlFsType.VolumeName = infoMddf.volname; XmlFsType.VolumeSerial = $"{infoMddf.volid:X16}"; return; } } catch (Exception ex) { DicConsole.ErrorWriteLine("Exception {0}, {1}, {2}", ex.Message, ex.InnerException, ex.StackTrace); } }
public bool Identify(string path) { string parentFolder = Path.GetDirectoryName(path); parentFolder = parentFolder ?? ""; if (!File.Exists(Path.Combine(parentFolder, FINDER_INFO))) { return(false); } if (!Directory.Exists(Path.Combine(parentFolder, RESOURCES))) { return(false); } string baseFilename = Path.GetFileName(path); bool dataFound = false; bool rsrcFound = false; FileStream finderDatStream = new FileStream(Path.Combine(parentFolder, FINDER_INFO), FileMode.Open, FileAccess.Read); while (finderDatStream.Position + 0x5C <= finderDatStream.Length) { PCExchangeEntry datEntry = new PCExchangeEntry(); byte[] datEntry_b = new byte[Marshal.SizeOf(datEntry)]; finderDatStream.Read(datEntry_b, 0, Marshal.SizeOf(datEntry)); datEntry = Helpers.Marshal.ByteArrayToStructureBigEndian <PCExchangeEntry>(datEntry_b); // TODO: Add support for encoding on filters string macName = StringHandlers.PascalToString(datEntry.macName, Encoding.GetEncoding("macintosh")); byte[] tmpDosName_b = new byte[8]; byte[] tmpDosExt_b = new byte[3]; Array.Copy(datEntry.dosName, 0, tmpDosName_b, 0, 8); Array.Copy(datEntry.dosName, 8, tmpDosExt_b, 0, 3); string dosName = Encoding.ASCII.GetString(tmpDosName_b).Trim() + "." + Encoding.ASCII.GetString(tmpDosExt_b).Trim(); string dosNameLow = dosName.ToLower(CultureInfo.CurrentCulture); if (baseFilename != macName && baseFilename != dosName && baseFilename != dosNameLow) { continue; } dataFound |= File.Exists(Path.Combine(parentFolder, macName ?? throw new InvalidOperationException())) || File.Exists(Path.Combine(parentFolder, dosName)) || File.Exists(Path.Combine(parentFolder, dosNameLow)); rsrcFound |= File.Exists(Path.Combine(parentFolder, RESOURCES, dosName)) || File.Exists(Path.Combine(parentFolder, RESOURCES, dosNameLow)); break; } finderDatStream.Close(); return(dataFound && rsrcFound); }
bool FillDirectory() { idToFilename = new Dictionary <uint, string>(); idToEntry = new Dictionary <uint, MFS_FileEntry>(); filenameToId = new Dictionary <string, uint>(); int offset = 0; while (offset + 51 < directoryBlocks.Length) { MFS_FileEntry entry = new MFS_FileEntry { flUsrWds = new byte[16], flFlags = (MFS_FileFlags)directoryBlocks[offset + 0] }; if (!entry.flFlags.HasFlag(MFS_FileFlags.Used)) { break; } entry.flTyp = directoryBlocks[offset + 1]; Array.Copy(directoryBlocks, offset + 2, entry.flUsrWds, 0, 16); entry.flFlNum = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 18); entry.flStBlk = BigEndianBitConverter.ToUInt16(directoryBlocks, offset + 22); entry.flLgLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 24); entry.flPyLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 28); entry.flRStBlk = BigEndianBitConverter.ToUInt16(directoryBlocks, offset + 32); entry.flRLgLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 34); entry.flRPyLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 38); entry.flCrDat = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 42); entry.flMdDat = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 46); entry.flNam = new byte[directoryBlocks[offset + 50] + 1]; Array.Copy(directoryBlocks, offset + 50, entry.flNam, 0, entry.flNam.Length); string lowerFilename = StringHandlers .PascalToString(entry.flNam, Encoding).ToLowerInvariant().Replace('/', ':'); if (entry.flFlags.HasFlag(MFS_FileFlags.Used) && !idToFilename.ContainsKey(entry.flFlNum) && !idToEntry.ContainsKey(entry.flFlNum) && !filenameToId.ContainsKey(lowerFilename) && entry.flFlNum > 0) { idToEntry.Add(entry.flFlNum, entry); idToFilename.Add(entry.flFlNum, StringHandlers.PascalToString(entry.flNam, Encoding).Replace('/', ':')); filenameToId.Add(lowerFilename, entry.flFlNum); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlags = {0}", entry.flFlags); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flTyp = {0}", entry.flTyp); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlNum = {0}", entry.flFlNum); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flStBlk = {0}", entry.flStBlk); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flLgLen = {0}", entry.flLgLen); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flPyLen = {0}", entry.flPyLen); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRStBlk = {0}", entry.flRStBlk); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRLgLen = {0}", entry.flRLgLen); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRPyLen = {0}", entry.flRPyLen); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flCrDat = {0}", DateHandlers.MacToDateTime(entry.flCrDat)); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flMdDat = {0}", DateHandlers.MacToDateTime(entry.flMdDat)); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flNam0 = {0}", StringHandlers.PascalToString(entry.flNam, Encoding)); } offset += 50 + entry.flNam.Length; // "Entries are always an integral number of words" if (offset % 2 != 0) { offset++; } // TODO: "Entries don't cross logical block boundaries" } return(true); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? new MacRoman(); information = ""; StringBuilder sb = new StringBuilder(); MFS_MasterDirectoryBlock mdb = new MFS_MasterDirectoryBlock(); MFS_BootBlock bb = new MFS_BootBlock(); byte[] pString = new byte[16]; byte[] mdbSector = imagePlugin.ReadSector(2 + partition.Start); byte[] bbSector = imagePlugin.ReadSector(0 + partition.Start); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; mdb.drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x000); if (mdb.drSigWord != MFS_MAGIC) { return; } mdb.drCrDate = BigEndianBitConverter.ToUInt32(mdbSector, 0x002); mdb.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbSector, 0x006); mdb.drAtrb = BigEndianBitConverter.ToUInt16(mdbSector, 0x00A); mdb.drNmFls = BigEndianBitConverter.ToUInt16(mdbSector, 0x00C); mdb.drDirSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x00E); mdb.drBlLen = BigEndianBitConverter.ToUInt16(mdbSector, 0x010); mdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbSector, 0x012); mdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x014); mdb.drClpSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x018); mdb.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x01C); mdb.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbSector, 0x01E); mdb.drFreeBks = BigEndianBitConverter.ToUInt16(mdbSector, 0x022); mdb.drVNSiz = mdbSector[0x024]; byte[] variableSize = new byte[mdb.drVNSiz + 1]; Array.Copy(mdbSector, 0x024, variableSize, 0, mdb.drVNSiz + 1); mdb.drVN = StringHandlers.PascalToString(variableSize, Encoding); bb.signature = BigEndianBitConverter.ToUInt16(bbSector, 0x000); if (bb.signature == MFSBB_MAGIC) { bb.branch = BigEndianBitConverter.ToUInt32(bbSector, 0x002); bb.boot_flags = bbSector[0x006]; bb.boot_version = bbSector[0x007]; bb.sec_sv_pages = BigEndianBitConverter.ToInt16(bbSector, 0x008); Array.Copy(mdbSector, 0x00A, pString, 0, 16); bb.system_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x01A, pString, 0, 16); bb.finder_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x02A, pString, 0, 16); bb.debug_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x03A, pString, 0, 16); bb.disasm_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x04A, pString, 0, 16); bb.stupscr_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x05A, pString, 0, 16); bb.bootup_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x06A, pString, 0, 16); bb.clipbrd_name = StringHandlers.PascalToString(pString, Encoding); bb.max_files = BigEndianBitConverter.ToUInt16(bbSector, 0x07A); bb.queue_size = BigEndianBitConverter.ToUInt16(bbSector, 0x07C); bb.heap_128k = BigEndianBitConverter.ToUInt32(bbSector, 0x07E); bb.heap_256k = BigEndianBitConverter.ToUInt32(bbSector, 0x082); bb.heap_512k = BigEndianBitConverter.ToUInt32(bbSector, 0x086); } else { bb.signature = 0x0000; } sb.AppendLine("Apple Macintosh File System"); sb.AppendLine(); sb.AppendLine("Master Directory Block:"); sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drLsBkUp)).AppendLine(); if ((mdb.drAtrb & 0x80) == 0x80) { sb.AppendLine("Volume is locked by hardware."); } if ((mdb.drAtrb & 0x8000) == 0x8000) { sb.AppendLine("Volume is locked by software."); } sb.AppendFormat("{0} files on volume", mdb.drNmFls).AppendLine(); sb.AppendFormat("First directory sector: {0}", mdb.drDirSt).AppendLine(); sb.AppendFormat("{0} sectors in directory.", mdb.drBlLen).AppendLine(); sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks + 1).AppendLine(); sb.AppendFormat("Size of allocation blocks: {0} bytes", mdb.drAlBlkSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate.", mdb.drClpSiz).AppendLine(); sb.AppendFormat("First allocation block (#2) starts in sector {0}.", mdb.drAlBlSt).AppendLine(); sb.AppendFormat("Next unused file number: {0}", mdb.drNxtFNum).AppendLine(); sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); sb.AppendFormat("Volume name: {0}", mdb.drVN).AppendLine(); if (bb.signature == MFSBB_MAGIC) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine("Boot Block:"); if ((bb.boot_flags & 0x40) == 0x40) { sb.AppendLine("Boot block should be executed."); } if ((bb.boot_flags & 0x80) == 0x80) { sb.AppendLine("Boot block is in new unknown format."); } else { if (bb.sec_sv_pages > 0) { sb.AppendLine("Allocate secondary sound buffer at boot."); } else if (bb.sec_sv_pages < 0) { sb.AppendLine("Allocate secondary sound and video buffers at boot."); } sb.AppendFormat("System filename: {0}", bb.system_name).AppendLine(); sb.AppendFormat("Finder filename: {0}", bb.finder_name).AppendLine(); sb.AppendFormat("Debugger filename: {0}", bb.debug_name).AppendLine(); sb.AppendFormat("Disassembler filename: {0}", bb.disasm_name).AppendLine(); sb.AppendFormat("Startup screen filename: {0}", bb.stupscr_name).AppendLine(); sb.AppendFormat("First program to execute at boot: {0}", bb.bootup_name).AppendLine(); sb.AppendFormat("Clipboard filename: {0}", bb.clipbrd_name).AppendLine(); sb.AppendFormat("Maximum opened files: {0}", bb.max_files * 4).AppendLine(); sb.AppendFormat("Event queue size: {0}", bb.queue_size).AppendLine(); sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.heap_128k).AppendLine(); sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.heap_256k).AppendLine(); sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.heap_512k).AppendLine(); } } else { sb.AppendLine("Volume is not bootable."); } information = sb.ToString(); XmlFsType = new FileSystemType(); if (mdb.drLsBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drLsBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bb.signature == MFSBB_MAGIC; XmlFsType.Clusters = mdb.drNmAlBlks; XmlFsType.ClusterSize = (int)mdb.drAlBlkSiz; if (mdb.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Files = mdb.drNmFls; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = mdb.drFreeBks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "MFS"; XmlFsType.VolumeName = mdb.drVN; }
/// <summary> /// Mounts an Apple Lisa filesystem /// </summary> public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { try { device = imagePlugin; Encoding = new LisaRoman(); // Lisa OS is unable to work on disks without tags. // This code is designed like that. // However with some effort the code may be modified to ignore them. if (device.Info.ReadableSectorTags == null || !device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) { DicConsole.DebugWriteLine("LisaFS plugin", "Underlying device does not support Lisa tags"); return(Errno.InOutError); } // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors if (device.Info.Sectors < 800) { DicConsole.DebugWriteLine("LisaFS plugin", "Device is too small"); return(Errno.InOutError); } // MDDF cannot be at end of device, of course volumePrefix = device.Info.Sectors; // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors for (ulong i = 0; i < 100; i++) { DecodeTag(device.ReadSectorTag(i, SectorTagType.AppleSectorTag), out LisaTag.PriamTag searchTag); DicConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); if (volumePrefix == device.Info.Sectors && searchTag.FileId == FILEID_LOADER_SIGNED) { volumePrefix = i - 1; } if (searchTag.FileId != FILEID_MDDF) { continue; } devTagSize = device.ReadSectorTag(i, SectorTagType.AppleSectorTag).Length; byte[] sector = device.ReadSector(i); mddf = new MDDF(); byte[] pString = new byte[33]; mddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00); mddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02); mddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A); Array.Copy(sector, 0x0C, pString, 0, 33); mddf.volname = StringHandlers.PascalToString(pString, Encoding); mddf.unknown1 = sector[0x2D]; Array.Copy(sector, 0x2E, pString, 0, 33); // Prevent garbage mddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : ""; mddf.unknown2 = sector[0x4F]; mddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50); mddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54); uint lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58); mddf.dtvc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C); mddf.dtcc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60); mddf.dtvb = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64); mddf.dtvs = DateHandlers.LisaToDateTime(lisaTime); mddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68); mddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C); mddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70); mddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74); mddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78); mddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C); mddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E); mddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80); mddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82); mddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86); mddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A); mddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C); mddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90); mddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94); mddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98); mddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A); mddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C); mddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0); mddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4); mddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8); mddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC); mddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0); mddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2); mddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6); mddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA); mddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE); mddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0); mddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4); mddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC); mddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0); mddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4); mddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8); mddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC); mddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0); mddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4); mddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8); mddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC); mddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0); mddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4); mddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8); mddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC); mddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100); mddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104); mddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108); mddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C); mddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110); mddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114); mddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118); mddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120); mddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122); mddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124); mddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126); mddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C); mddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A); mddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E); mddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132); mddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136); mddf.vol_left_mounted = sector[0x138]; // Check that the MDDF is correct if (mddf.mddf_block != i - volumePrefix || mddf.vol_size > device.Info.Sectors || mddf.vol_size - 1 != mddf.volsize_minus_one || mddf.vol_size - i - 1 != mddf.volsize_minus_mddf_minus_one - volumePrefix || mddf.datasize > mddf.blocksize || mddf.blocksize < device.Info.SectorSize || mddf.datasize != device.Info.SectorSize) { DicConsole.DebugWriteLine("LisaFS plugin", "Incorrect MDDF found"); return(Errno.InvalidArgument); } // Check MDDF version switch (mddf.fsversion) { case LISA_V1: DicConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v1"); break; case LISA_V2: DicConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v2"); break; case LISA_V3: DicConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v3"); break; default: DicConsole.ErrorWriteLine("Cannot mount LisaFS version {0}", mddf.fsversion.ToString()); return(Errno.NotSupported); } // Initialize caches extentCache = new Dictionary <short, ExtentFile>(); systemFileCache = new Dictionary <short, byte[]>(); fileCache = new Dictionary <short, byte[]>(); //catalogCache = new Dictionary<short, List<CatalogEntry>>(); fileSizeCache = new Dictionary <short, int>(); mounted = true; if (options == null) { options = GetDefaultOptions(); } if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out debug); } if (debug) { printedExtents = new List <short>(); } // Read the S-Records file Errno error = ReadSRecords(); if (error != Errno.NoError) { DicConsole.ErrorWriteLine("Error {0} reading S-Records file.", error); return(error); } directoryDtcCache = new Dictionary <short, DateTime> { { DIRID_ROOT, mddf.dtcc } }; // Read the Catalog File error = ReadCatalog(); if (error != Errno.NoError) { DicConsole.DebugWriteLine("LisaFS plugin", "Cannot read Catalog File, error {0}", error.ToString()); mounted = false; return(error); } // If debug, cache system files if (debug) { error = ReadSystemFile(FILEID_BOOT_SIGNED, out _); if (error != Errno.NoError) { DicConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot blocks"); mounted = false; return(error); } error = ReadSystemFile(FILEID_LOADER_SIGNED, out _); if (error != Errno.NoError) { DicConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot loader"); mounted = false; return(error); } error = ReadSystemFile((short)FILEID_MDDF, out _); if (error != Errno.NoError) { DicConsole.DebugWriteLine("LisaFS plugin", "Unable to read MDDF"); mounted = false; return(error); } error = ReadSystemFile((short)FILEID_BITMAP, out _); if (error != Errno.NoError) { DicConsole.DebugWriteLine("LisaFS plugin", "Unable to read volume bitmap"); mounted = false; return(error); } error = ReadSystemFile((short)FILEID_SRECORD, out _); if (error != Errno.NoError) { DicConsole.DebugWriteLine("LisaFS plugin", "Unable to read S-Records file"); mounted = false; return(error); } } // Create XML metadata for mounted filesystem XmlFsType = new FileSystemType(); if (DateTime.Compare(mddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0) { XmlFsType.BackupDate = mddf.dtvb; XmlFsType.BackupDateSpecified = true; } XmlFsType.Clusters = mddf.vol_size; XmlFsType.ClusterSize = (uint)(mddf.clustersize * mddf.datasize); if (DateTime.Compare(mddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0) { XmlFsType.CreationDate = mddf.dtvc; XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = mddf.vol_left_mounted != 0; XmlFsType.Files = mddf.filecount; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = mddf.freecount; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "LisaFS"; XmlFsType.VolumeName = mddf.volname; XmlFsType.VolumeSerial = $"{mddf.volid:X16}"; return(Errno.NoError); } DicConsole.DebugWriteLine("LisaFS plugin", "Not a Lisa filesystem"); return(Errno.NotSupported); } catch (Exception ex) { DicConsole.ErrorWriteLine("Exception {0}, {1}, {2}", ex.Message, ex.InnerException, ex.StackTrace); return(Errno.InOutError); } }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); byte[] rootBlockSector = imagePlugin.ReadSector(2 + partition.Start); RootBlock rootBlock = Marshal.ByteArrayToStructureBigEndian <RootBlock>(rootBlockSector); var sbInformation = new StringBuilder(); XmlFsType = new FileSystemType(); switch (rootBlock.diskType) { case AFS_DISK: case MUAF_DISK: sbInformation.Append("Professional File System v1"); XmlFsType.Type = "PFS v1"; break; case PFS2_DISK: sbInformation.Append("Professional File System v2"); XmlFsType.Type = "PFS v2"; break; case PFS_DISK: case MUPFS_DISK: sbInformation.Append("Professional File System v3"); XmlFsType.Type = "PFS v3"; break; } if (rootBlock.diskType == MUAF_DISK || rootBlock.diskType == MUPFS_DISK) { sbInformation.Append(", with multi-user support"); } sbInformation.AppendLine(); sbInformation.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(rootBlock.diskname, Encoding)). AppendLine(); sbInformation.AppendFormat("Volume has {0} free sectors of {1}", rootBlock.blocksfree, rootBlock.diskSize). AppendLine(); sbInformation.AppendFormat("Volume created on {0}", DateHandlers.AmigaToDateTime(rootBlock.creationday, rootBlock.creationminute, rootBlock.creationtick)).AppendLine(); if (rootBlock.extension > 0) { sbInformation.AppendFormat("Root block extension resides at block {0}", rootBlock.extension). AppendLine(); } information = sbInformation.ToString(); XmlFsType.CreationDate = DateHandlers.AmigaToDateTime(rootBlock.creationday, rootBlock.creationminute, rootBlock.creationtick); XmlFsType.CreationDateSpecified = true; XmlFsType.FreeClusters = rootBlock.blocksfree; XmlFsType.FreeClustersSpecified = true; XmlFsType.Clusters = rootBlock.diskSize; XmlFsType.ClusterSize = imagePlugin.Info.SectorSize; XmlFsType.VolumeName = StringHandlers.PascalToString(rootBlock.diskname, Encoding); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("macintosh"); information = ""; StringBuilder sb = new StringBuilder(); byte[] bbSector = null; byte[] mdbSector = null; ushort drSigWord; bool APMFromHDDOnCD = false; if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 || imagePlugin.Info.SectorSize == 2048) { byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2); foreach (int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) { drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); if (drSigWord != HFS_MAGIC) { continue; } bbSector = new byte[1024]; mdbSector = new byte[512]; if (offset >= 0x400) { Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024); } Array.Copy(tmpSector, offset, mdbSector, 0, 512); APMFromHDDOnCD = true; break; } if (!APMFromHDDOnCD) { return; } } else { mdbSector = imagePlugin.ReadSector(2 + partition.Start); drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); if (drSigWord == HFS_MAGIC) { bbSector = imagePlugin.ReadSector(partition.Start); } else { return; } } HFS_MasterDirectoryBlock MDB = BigEndianMarshal.ByteArrayToStructureBigEndian <HFS_MasterDirectoryBlock>(mdbSector); HFS_BootBlock BB = BigEndianMarshal.ByteArrayToStructureBigEndian <HFS_BootBlock>(bbSector); sb.AppendLine("Apple Hierarchical File System"); sb.AppendLine(); if (APMFromHDDOnCD) { sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine(); } sb.AppendLine("Master Directory Block:"); sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(MDB.drCrDate)).AppendLine(); sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(MDB.drLsMod)).AppendLine(); if (MDB.drVolBkUp > 0) { sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(MDB.drVolBkUp)).AppendLine(); sb.AppendFormat("Backup sequence number: {0}", MDB.drVSeqNum).AppendLine(); } else { sb.AppendLine("Volume has never been backed up"); } if ((MDB.drAtrb & 0x80) == 0x80) { sb.AppendLine("Volume is locked by hardware."); } sb.AppendLine((MDB.drAtrb & 0x100) == 0x100 ? "Volume was unmonted." : "Volume is mounted."); if ((MDB.drAtrb & 0x200) == 0x200) { sb.AppendLine("Volume has spared bad blocks."); } if ((MDB.drAtrb & 0x400) == 0x400) { sb.AppendLine("Volume does not need cache."); } if ((MDB.drAtrb & 0x800) == 0x800) { sb.AppendLine("Boot volume is inconsistent."); } if ((MDB.drAtrb & 0x1000) == 0x1000) { sb.AppendLine("There are reused CNIDs."); } if ((MDB.drAtrb & 0x2000) == 0x2000) { sb.AppendLine("Volume is journaled."); } if ((MDB.drAtrb & 0x4000) == 0x4000) { sb.AppendLine("Volume is seriously inconsistent."); } if ((MDB.drAtrb & 0x8000) == 0x8000) { sb.AppendLine("Volume is locked by software."); } sb.AppendFormat("{0} files on root directory", MDB.drNmFls).AppendLine(); sb.AppendFormat("{0} directories on root directory", MDB.drNmRtDirs).AppendLine(); sb.AppendFormat("{0} files on volume", MDB.drFilCnt).AppendLine(); sb.AppendFormat("{0} directories on volume", MDB.drDirCnt).AppendLine(); sb.AppendFormat("Volume write count: {0}", MDB.drWrCnt).AppendLine(); sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", MDB.drVBMSt).AppendLine(); sb.AppendFormat("Next allocation block: {0}.", MDB.drAllocPtr).AppendLine(); sb.AppendFormat("{0} volume allocation blocks.", MDB.drNmAlBlks).AppendLine(); sb.AppendFormat("{0} bytes per allocation block.", MDB.drAlBlkSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a file.", MDB.drClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", MDB.drXTClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", MDB.drCTClpSiz).AppendLine(); sb.AppendFormat("Sector of first allocation block: {0}", MDB.drAlBlSt).AppendLine(); sb.AppendFormat("Next unused CNID: {0}", MDB.drNxtCNID).AppendLine(); sb.AppendFormat("{0} unused allocation blocks.", MDB.drFreeBks).AppendLine(); sb.AppendFormat("{0} bytes in the Extents B-Tree", MDB.drXTFlSize).AppendLine(); sb.AppendFormat("{0} bytes in the Catalog B-Tree", MDB.drCTFlSize).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(MDB.drVN, Encoding)).AppendLine(); sb.AppendLine("Finder info:"); sb.AppendFormat("CNID of bootable system's directory: {0}", MDB.drFndrInfo0).AppendLine(); sb.AppendFormat("CNID of first-run application's directory: {0}", MDB.drFndrInfo1).AppendLine(); sb.AppendFormat("CNID of previously opened directory: {0}", MDB.drFndrInfo2).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", MDB.drFndrInfo3).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", MDB.drFndrInfo5).AppendLine(); if (MDB.drFndrInfo6 != 0 && MDB.drFndrInfo7 != 0) { sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", MDB.drFndrInfo6, MDB.drFndrInfo7).AppendLine(); } if (MDB.drEmbedSigWord == HFSP_MAGIC) { sb.AppendLine("Volume wraps a HFS+ volume."); sb.AppendFormat("Starting block of the HFS+ volume: {0}", MDB.xdrStABNt).AppendLine(); sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", MDB.xdrNumABlks).AppendLine(); } else { sb.AppendFormat("{0} blocks in volume cache", MDB.drVCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume bitmap cache", MDB.drVBMCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume common cache", MDB.drCtlCSize).AppendLine(); } if (BB.signature == HFSBB_MAGIC) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine("Boot Block:"); if ((BB.boot_flags & 0x40) == 0x40) { sb.AppendLine("Boot block should be executed."); } if ((BB.boot_flags & 0x80) == 0x80) { sb.AppendLine("Boot block is in new unknown format."); } else { if (BB.boot_flags > 0) { sb.AppendLine("Allocate secondary sound buffer at boot."); } else if (BB.boot_flags < 0) { sb.AppendLine("Allocate secondary sound and video buffers at boot."); } sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(BB.system_name, Encoding)) .AppendLine(); sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(BB.finder_name, Encoding)) .AppendLine(); sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(BB.debug_name, Encoding)) .AppendLine(); sb.AppendFormat("Disassembler filename: {0}", StringHandlers.PascalToString(BB.disasm_name, Encoding)).AppendLine(); sb.AppendFormat("Startup screen filename: {0}", StringHandlers.PascalToString(BB.stupscr_name, Encoding)).AppendLine(); sb.AppendFormat("First program to execute at boot: {0}", StringHandlers.PascalToString(BB.bootup_name, Encoding)).AppendLine(); sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(BB.clipbrd_name, Encoding)) .AppendLine(); sb.AppendFormat("Maximum opened files: {0}", BB.max_files * 4).AppendLine(); sb.AppendFormat("Event queue size: {0}", BB.queue_size).AppendLine(); sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", BB.heap_128k).AppendLine(); sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", BB.heap_256k).AppendLine(); sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", BB.heap_512k).AppendLine(); } } else if (MDB.drFndrInfo0 != 0 || MDB.drFndrInfo3 != 0 || MDB.drFndrInfo5 != 0) { sb.AppendLine("Volume is bootable."); } else { sb.AppendLine("Volume is not bootable."); } information = sb.ToString(); XmlFsType = new FileSystemType(); if (MDB.drVolBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(MDB.drVolBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = BB.signature == HFSBB_MAGIC || MDB.drFndrInfo0 != 0 || MDB.drFndrInfo3 != 0 || MDB.drFndrInfo5 != 0; XmlFsType.Clusters = MDB.drNmAlBlks; XmlFsType.ClusterSize = (int)MDB.drAlBlkSiz; if (MDB.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(MDB.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = (MDB.drAtrb & 0x100) != 0x100; XmlFsType.Files = MDB.drFilCnt; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = MDB.drFreeBks; XmlFsType.FreeClustersSpecified = true; if (MDB.drLsMod > 0) { XmlFsType.ModificationDate = DateHandlers.MacToDateTime(MDB.drLsMod); XmlFsType.ModificationDateSpecified = true; } XmlFsType.Type = "HFS"; XmlFsType.VolumeName = StringHandlers.PascalToString(MDB.drVN, Encoding); if (MDB.drFndrInfo6 != 0 && MDB.drFndrInfo7 != 0) { XmlFsType.VolumeSerial = $"{MDB.drFndrInfo6:X8}{MDB.drFndrInfo7:X8}"; } }
internal static string GetBootBlockInformation(byte[] bbSector, Encoding encoding) { if (bbSector is null || bbSector.Length < 0x100) { return(null); } BootBlock bb = Marshal.ByteArrayToStructureBigEndian <BootBlock>(bbSector); if (bb.bbID != BB_MAGIC) { return(null); } var sb = new StringBuilder(); sb.AppendLine("Boot Block:"); if ((bb.bbVersion & 0x8000) > 0) { sb.AppendLine("Boot block is in new format."); if ((bb.bbVersion & 0x4000) > 0) { sb.AppendLine("Boot block should be executed."); if ((bb.bbVersion & 0x2000) > 0) { sb. AppendFormat("System heap will be extended by {0} bytes and a {1} fraction of the available RAM", bb.bbSysHeapExtra, bb.bbSysHeapFract).AppendLine(); } } } else if ((bb.bbVersion & 0xFF) == 0x0D) { sb.AppendLine("Boot block should be executed."); } if (bb.bbPageFlags > 0) { sb.AppendLine("Allocate secondary sound buffer at boot."); } else if (bb.bbPageFlags < 0) { sb.AppendLine("Allocate secondary sound and video buffers at boot."); } sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.bbSysName, encoding)).AppendLine(); sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.bbShellName, encoding)). AppendLine(); sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.bbDbg1Name, encoding)). AppendLine(); sb.AppendFormat("Disassembler filename: {0}", StringHandlers.PascalToString(bb.bbDbg2Name, encoding)). AppendLine(); sb.AppendFormat("Startup screen filename: {0}", StringHandlers.PascalToString(bb.bbScreenName, encoding)). AppendLine(); sb.AppendFormat("First program to execute at boot: {0}", StringHandlers.PascalToString(bb.bbHelloName, encoding)).AppendLine(); sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(bb.bbScrapName, encoding)). AppendLine(); sb.AppendFormat("Maximum opened files: {0}", bb.bbCntFCBs * 4).AppendLine(); sb.AppendFormat("Event queue size: {0}", bb.bbCntEvts).AppendLine(); sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.bb128KSHeap).AppendLine(); sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.bb256KSHeap).AppendLine(); sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.bbSysHeapSize).AppendLine(); return(sb.ToString()); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("macintosh"); information = ""; var sb = new StringBuilder(); byte[] bbSector = null; byte[] mdbSector = null; ushort drSigWord; bool apmFromHddOnCd = false; if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 || imagePlugin.Info.SectorSize == 2048) { byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2); foreach (int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) { drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); if (drSigWord != AppleCommon.HFS_MAGIC) { continue; } bbSector = new byte[1024]; mdbSector = new byte[512]; if (offset >= 0x400) { Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024); } Array.Copy(tmpSector, offset, mdbSector, 0, 512); apmFromHddOnCd = true; break; } if (!apmFromHddOnCd) { return; } } else { mdbSector = imagePlugin.ReadSector(2 + partition.Start); drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); if (drSigWord == AppleCommon.HFS_MAGIC) { bbSector = imagePlugin.ReadSector(partition.Start); } else { return; } } MasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian <MasterDirectoryBlock>(mdbSector); sb.AppendLine("Apple Hierarchical File System"); sb.AppendLine(); if (apmFromHddOnCd) { sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine(); } sb.AppendLine("Master Directory Block:"); sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine(); if (mdb.drVolBkUp > 0) { sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine(); sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine(); } else { sb.AppendLine("Volume has never been backed up"); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.HardwareLock)) { sb.AppendLine("Volume is locked by hardware."); } sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted." : "Volume is mounted."); if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks)) { sb.AppendLine("Volume has spared bad blocks."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache)) { sb.AppendLine("Volume does not need cache."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent)) { sb.AppendLine("Boot volume is inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds)) { sb.AppendLine("There are reused CNIDs."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Journaled)) { sb.AppendLine("Volume is journaled."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent)) { sb.AppendLine("Volume is seriously inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock)) { sb.AppendLine("Volume is locked by software."); } sb.AppendFormat("{0} files on root directory", mdb.drNmFls).AppendLine(); sb.AppendFormat("{0} directories on root directory", mdb.drNmRtDirs).AppendLine(); sb.AppendFormat("{0} files on volume", mdb.drFilCnt).AppendLine(); sb.AppendFormat("{0} directories on volume", mdb.drDirCnt).AppendLine(); sb.AppendFormat("Volume write count: {0}", mdb.drWrCnt).AppendLine(); sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", mdb.drVBMSt).AppendLine(); sb.AppendFormat("Next allocation block: {0}.", mdb.drAllocPtr).AppendLine(); sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks).AppendLine(); sb.AppendFormat("{0} bytes per allocation block.", mdb.drAlBlkSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a file.", mdb.drClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", mdb.drXTClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", mdb.drCTClpSiz).AppendLine(); sb.AppendFormat("Sector of first allocation block: {0}", mdb.drAlBlSt).AppendLine(); sb.AppendFormat("Next unused CNID: {0}", mdb.drNxtCNID).AppendLine(); sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); sb.AppendFormat("{0} bytes in the Extents B-Tree", mdb.drXTFlSize).AppendLine(); sb.AppendFormat("{0} bytes in the Catalog B-Tree", mdb.drCTFlSize).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(mdb.drVN, Encoding)).AppendLine(); sb.AppendLine("Finder info:"); sb.AppendFormat("CNID of bootable system's directory: {0}", mdb.drFndrInfo0).AppendLine(); sb.AppendFormat("CNID of first-run application's directory: {0}", mdb.drFndrInfo1).AppendLine(); sb.AppendFormat("CNID of previously opened directory: {0}", mdb.drFndrInfo2).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", mdb.drFndrInfo3).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", mdb.drFndrInfo5).AppendLine(); if (mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0) { sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", mdb.drFndrInfo6, mdb.drFndrInfo7).AppendLine(); } if (mdb.drEmbedSigWord == AppleCommon.HFSP_MAGIC) { sb.AppendLine("Volume wraps a HFS+ volume."); sb.AppendFormat("Starting block of the HFS+ volume: {0}", mdb.xdrStABNt).AppendLine(); sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", mdb.xdrNumABlks).AppendLine(); } else { sb.AppendFormat("{0} blocks in volume cache", mdb.drVCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume bitmap cache", mdb.drVBMCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume common cache", mdb.drCtlCSize).AppendLine(); } string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding); if (bootBlockInfo != null) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine(bootBlockInfo); } else if (mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0) { sb.AppendLine("Volume is bootable."); } else { sb.AppendLine("Volume is not bootable."); } information = sb.ToString(); XmlFsType = new FileSystemType(); if (mdb.drVolBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drVolBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bootBlockInfo != null || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0; XmlFsType.Clusters = mdb.drNmAlBlks; XmlFsType.ClusterSize = mdb.drAlBlkSiz; if (mdb.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = !mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted); XmlFsType.Files = mdb.drFilCnt; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = mdb.drFreeBks; XmlFsType.FreeClustersSpecified = true; if (mdb.drLsMod > 0) { XmlFsType.ModificationDate = DateHandlers.MacToDateTime(mdb.drLsMod); XmlFsType.ModificationDateSpecified = true; } XmlFsType.Type = "HFS"; XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding); if (mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0) { XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}"; } }
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 Identify(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); // Incorrect pascal string length, not DC42 if (buffer[0] > 63) { return(false); } Dc42Header tmpHeader = new Dc42Header(); Array.Copy(buffer, 0, pString, 0, 64); 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.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]; 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); 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.DataSize = BitConverter.ToUInt32(buffer, 0x40); tmpHeader.TagSize = BitConverter.ToUInt32(buffer, 0x44); tmpHeader.DataChecksum = BitConverter.ToUInt32(buffer, 0x48); tmpHeader.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C); if (tmpHeader.DataSize + tmpHeader.TagSize + 0x54 != imageFilter.GetDataForkLength() && tmpHeader.Format != kSigmaFormatTwiggy) { return(false); } } if (tmpHeader.Format != kSonyFormat400K && tmpHeader.Format != kSonyFormat800K && tmpHeader.Format != kSonyFormat720K && tmpHeader.Format != kSonyFormat1440K && tmpHeader.Format != kSonyFormat1680K && tmpHeader.Format != kSigmaFormatTwiggy && tmpHeader.Format != kNotStandardFormat) { DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.format = 0x{0:X2} value", tmpHeader.Format); 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) { DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value", tmpHeader.FmtByte); return(false); } if (tmpHeader.FmtByte != kInvalidFmtByte) { return(true); } DicConsole.DebugWriteLine("DC42 plugin", "Image says it's unformatted"); return(false); }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { device = imagePlugin; Encoding = encoding ?? new Apple2(); if (options == null) { options = GetDefaultOptions(); } if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out debug); } if (device.Info.Sectors < 3) { return(Errno.InvalidArgument); } multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); // Blocks 0 and 1 are boot code catalogBlocks = device.ReadSectors(multiplier * 2, multiplier); // On Apple //, it's little endian // TODO: Fix //BigEndianBitConverter.IsLittleEndian = // multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; mountedVolEntry.FirstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x00); mountedVolEntry.LastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x02); mountedVolEntry.EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, 0x04); mountedVolEntry.VolumeName = new byte[8]; Array.Copy(catalogBlocks, 0x06, mountedVolEntry.VolumeName, 0, 8); mountedVolEntry.Blocks = BigEndianBitConverter.ToInt16(catalogBlocks, 0x0E); mountedVolEntry.Files = BigEndianBitConverter.ToInt16(catalogBlocks, 0x10); mountedVolEntry.Dummy = BigEndianBitConverter.ToInt16(catalogBlocks, 0x12); mountedVolEntry.LastBoot = BigEndianBitConverter.ToInt16(catalogBlocks, 0x14); mountedVolEntry.Tail = BigEndianBitConverter.ToInt32(catalogBlocks, 0x16); if (mountedVolEntry.FirstBlock != 0 || mountedVolEntry.LastBlock <= mountedVolEntry.FirstBlock || (ulong)mountedVolEntry.LastBlock > (device.Info.Sectors / multiplier) - 2 || (mountedVolEntry.EntryType != PascalFileKind.Volume && mountedVolEntry.EntryType != PascalFileKind.Secure) || mountedVolEntry.VolumeName[0] > 7 || mountedVolEntry.Blocks < 0 || (ulong)mountedVolEntry.Blocks != device.Info.Sectors / multiplier || mountedVolEntry.Files < 0) { return(Errno.InvalidArgument); } catalogBlocks = device.ReadSectors(multiplier * 2, (uint)(mountedVolEntry.LastBlock - mountedVolEntry.FirstBlock - 2) * multiplier); int offset = 26; fileEntries = new List <PascalFileEntry>(); while (offset + 26 < catalogBlocks.Length) { var entry = new PascalFileEntry { Filename = new byte[16], FirstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x00), LastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x02), EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x04), LastBytes = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x16), ModificationTime = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x18) }; Array.Copy(catalogBlocks, offset + 0x06, entry.Filename, 0, 16); if (entry.Filename[0] <= 15 && entry.Filename[0] > 0) { fileEntries.Add(entry); } offset += 26; } bootBlocks = device.ReadSectors(0, 2 * multiplier); XmlFsType = new FileSystemType { Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(bootBlocks), Clusters = (ulong)mountedVolEntry.Blocks, ClusterSize = device.Info.SectorSize, Files = (ulong)mountedVolEntry.Files, FilesSpecified = true, Type = "UCSD Pascal", VolumeName = StringHandlers.PascalToString(mountedVolEntry.VolumeName, Encoding) }; mounted = true; return(Errno.NoError); }
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); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); StringBuilder sbInformation = new StringBuilder(); XmlFsType = new FileSystemType(); information = null; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; byte[] bootBlockSectors = imagePlugin.ReadSectors(0 + partition.Start, 2); BootBlock bootBlk = Marshal.ByteArrayToStructureBigEndian <BootBlock>(bootBlockSectors); bootBlk.bootCode = new byte[bootBlockSectors.Length - 12]; Array.Copy(bootBlockSectors, 12, bootBlk.bootCode, 0, bootBlk.bootCode.Length); bootBlockSectors[4] = bootBlockSectors[5] = bootBlockSectors[6] = bootBlockSectors[7] = 0; uint bsum = AmigaBootChecksum(bootBlockSectors); ulong bRootPtr = 0; // If bootblock is correct, let's take its rootblock pointer if (bsum == bootBlk.checksum) { bRootPtr = bootBlk.root_ptr + partition.Start; DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); } ulong[] rootPtrs = { bRootPtr + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start - 2, (partition.End - partition.Start + 1) / 2 + partition.Start - 1, (partition.End - partition.Start + 1) / 2 + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start + 4 }; RootBlock rootBlk = new RootBlock(); byte[] rootBlockSector = null; bool rootFound = false; uint blockSize = 0; // So to handle even number of sectors foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) { DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); rootBlockSector = imagePlugin.ReadSector(rootPtr); rootBlk.type = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x00); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.type = {0}", rootBlk.type); if (rootBlk.type != TYPE_HEADER) { continue; } rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x0C); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize); blockSize = (rootBlk.hashTableSize + 56) * 4; uint sectorsPerBlock = (uint)(blockSize / rootBlockSector.Length); DicConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); DicConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); if (blockSize % rootBlockSector.Length > 0) { sectorsPerBlock++; } if (rootPtr + sectorsPerBlock >= partition.End) { continue; } rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock); // Clear checksum on sector rootBlk.checksum = BigEndianBitConverter.ToUInt32(rootBlockSector, 20); rootBlockSector[20] = rootBlockSector[21] = rootBlockSector[22] = rootBlockSector[23] = 0; uint rsum = AmigaChecksum(rootBlockSector); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.checksum = 0x{0:X8}", rootBlk.checksum); DicConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum); rootBlk.sec_type = BigEndianBitConverter.ToUInt32(rootBlockSector, rootBlockSector.Length - 4); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.sec_type = {0}", rootBlk.sec_type); if (rootBlk.sec_type != SUBTYPE_ROOT || rootBlk.checksum != rsum) { continue; } rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock); rootFound = true; break; } if (!rootFound) { return; } rootBlk = MarshalRootBlock(rootBlockSector); string diskName = StringHandlers.PascalToString(rootBlk.diskName, Encoding); switch (bootBlk.diskType & 0xFF) { case 0: sbInformation.Append("Amiga Original File System"); XmlFsType.Type = "Amiga OFS"; break; case 1: sbInformation.Append("Amiga Fast File System"); XmlFsType.Type = "Amiga FFS"; break; case 2: sbInformation.Append("Amiga Original File System with international characters"); XmlFsType.Type = "Amiga OFS"; break; case 3: sbInformation.Append("Amiga Fast File System with international characters"); XmlFsType.Type = "Amiga FFS"; break; case 4: sbInformation.Append("Amiga Original File System with directory cache"); XmlFsType.Type = "Amiga OFS"; break; case 5: sbInformation.Append("Amiga Fast File System with directory cache"); XmlFsType.Type = "Amiga FFS"; break; case 6: sbInformation.Append("Amiga Original File System with long filenames"); XmlFsType.Type = "Amiga OFS2"; break; case 7: sbInformation.Append("Amiga Fast File System with long filenames"); XmlFsType.Type = "Amiga FFS2"; break; } if ((bootBlk.diskType & 0x6D754600) == 0x6D754600) { sbInformation.Append(", with multi-user patches"); } sbInformation.AppendLine(); sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine(); if (bootBlk.checksum == bsum) { Sha1Context sha1Ctx = new Sha1Context(); sha1Ctx.Update(bootBlk.bootCode); sbInformation.AppendLine("Volume is bootable"); sbInformation.AppendFormat("Boot code SHA1 is {0}", sha1Ctx.End()).AppendLine(); } if (rootBlk.bitmapFlag == 0xFFFFFFFF) { sbInformation.AppendLine("Volume bitmap is valid"); } if (rootBlk.bitmapExtensionBlock != 0x00000000 && rootBlk.bitmapExtensionBlock != 0xFFFFFFFF) { sbInformation.AppendFormat("Bitmap extension at block {0}", rootBlk.bitmapExtensionBlock).AppendLine(); } if ((bootBlk.diskType & 0xFF) == 4 || (bootBlk.diskType & 0xFF) == 5) { sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine(); } ulong blocks = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / blockSize; sbInformation.AppendFormat("Volume block size is {0} bytes", blockSize).AppendLine(); sbInformation.AppendFormat("Volume has {0} blocks", blocks).AppendLine(); sbInformation.AppendFormat("Volume created on {0}", DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks)) .AppendLine(); sbInformation.AppendFormat("Volume last modified on {0}", DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks)) .AppendLine(); sbInformation.AppendFormat("Volume root directory last modified on on {0}", DateHandlers.AmigaToDateTime(rootBlk.rDays, rootBlk.rMins, rootBlk.rTicks)) .AppendLine(); sbInformation.AppendFormat("Root block checksum is 0x{0:X8}", rootBlk.checksum).AppendLine(); information = sbInformation.ToString(); XmlFsType.CreationDate = DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks); XmlFsType.CreationDateSpecified = true; XmlFsType.ModificationDate = DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks); XmlFsType.ModificationDateSpecified = true; XmlFsType.Dirty = rootBlk.bitmapFlag != 0xFFFFFFFF; XmlFsType.Clusters = blocks; XmlFsType.ClusterSize = blockSize; XmlFsType.VolumeName = diskName; XmlFsType.Bootable = bsum == bootBlk.checksum; // Useful as a serial XmlFsType.VolumeSerial = $"{rootBlk.checksum:X8}"; }
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); }