public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.UTF8; information = ""; if (imagePlugin.Info.SectorSize < 512) { return; } bool bigEndian = true; byte[] sector = imagePlugin.ReadSector(partition.Start); Superblock fatxSb = Marshal.ByteArrayToStructureBigEndian <Superblock>(sector); if (fatxSb.magic == FATX_CIGAM) { fatxSb = Marshal.ByteArrayToStructureLittleEndian <Superblock>(sector); bigEndian = false; } if (fatxSb.magic != FATX_MAGIC) { return; } int logicalSectorsPerPhysicalSectors = partition.Offset == 0 && !bigEndian ? 8 : 1; StringBuilder sb = new StringBuilder(); sb.AppendLine("FATX filesystem"); sb.AppendFormat("{0} logical sectors ({1} bytes) per physical sector", logicalSectorsPerPhysicalSectors, logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize).AppendLine(); sb.AppendFormat("{0} sectors ({1} bytes) per cluster", fatxSb.sectorsPerCluster, fatxSb.sectorsPerCluster * logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize) .AppendLine(); sb.AppendFormat("Root directory starts on cluster {0}", fatxSb.rootDirectoryCluster).AppendLine(); string volumeLabel = StringHandlers.CToString(fatxSb.volumeLabel, bigEndian ? Encoding.BigEndianUnicode : Encoding.Unicode, true); sb.AppendFormat("Volume label: {0}", volumeLabel).AppendLine(); sb.AppendFormat("Volume serial: {0:X8}", fatxSb.id).AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "FATX filesystem", ClusterSize = (uint)(fatxSb.sectorsPerCluster * logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize), VolumeName = volumeLabel, VolumeSerial = $"{fatxSb.id:X8}" }; XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / XmlFsType.ClusterSize; }
public bool Identify(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); if (stream.Length < 512) { return(false); } stream.Seek(-Marshal.SizeOf <Footer>(), SeekOrigin.End); byte[] footerB = new byte[Marshal.SizeOf <Footer>()]; stream.Read(footerB, 0, Marshal.SizeOf <Footer>()); _footer = Marshal.ByteArrayToStructureBigEndian <Footer>(footerB); if (_footer.signature == UDIF_SIGNATURE) { return(true); } // Old UDIF as created by DiskCopy 6.5 using "OBSOLETE" format. (DiskCopy 5 rumored format?) stream.Seek(0, SeekOrigin.Begin); byte[] headerB = new byte[Marshal.SizeOf <Footer>()]; stream.Read(headerB, 0, Marshal.SizeOf <Footer>()); _footer = Marshal.ByteArrayToStructureBigEndian <Footer>(headerB); return(_footer.signature == UDIF_SIGNATURE); }
bool TryReadHeader(Stream stream, ref FileHeader fhdr, ref TrackInfo[] tmap, ref long[] toffsets) { int numTracks; int trackLen; // the length of a single track, in bytes TrackInfo[] trackMap; byte[] buffer = new byte[6]; FileHeader fHeader; long[] trackOffsets; stream.Seek(0, SeekOrigin.Begin); if (stream.Length < 256) { return(false); } // read and check signature fHeader.signature = new byte[10]; stream.Read(fHeader.signature, 0, 10); if (!fHeader.signature.SequenceEqual(_headerMagic)) { return(false); } // read and check disk type byte fHeader.diskType = (byte)stream.ReadByte(); if ((fHeader.diskType < 1) || (fHeader.diskType > 4)) { return(false); } // seek to start of the trackmap stream.Seek(TRACKMAP_OFFSET, SeekOrigin.Begin); numTracks = diskTypes[fHeader.diskType].cyl * diskTypes[fHeader.diskType].hd; trackLen = 512 * diskTypes[fHeader.diskType].spt; trackMap = new TrackInfo[numTracks]; trackOffsets = new long[numTracks]; AaruConsole.DebugWriteLine("DiskDupe plugin", "Identified image with C/H/S = {0}/{1}/{2}", diskTypes[fHeader.diskType].cyl, diskTypes[fHeader.diskType].hd, diskTypes[fHeader.diskType].spt); // read the trackmap and store the track offsets for (int i = 0; i < numTracks; i++) { stream.Read(buffer, 0, 6); trackMap[i] = Marshal.ByteArrayToStructureBigEndian <TrackInfo>(buffer); trackOffsets[i] = trackLen * trackMap[i].trackNumber; } fhdr = fHeader; tmap = trackMap; toffsets = trackOffsets; return(true); }
Dictionary <string, DirectoryEntryWithPointers> DecodeDirectory(int firstBlock) { Dictionary <string, DirectoryEntryWithPointers> entries = new Dictionary <string, DirectoryEntryWithPointers>(); int nextBlock = firstBlock; DirectoryHeader header; do { byte[] data = _image.ReadSectors((ulong)(nextBlock * _volumeBlockSizeRatio), _volumeBlockSizeRatio); header = Marshal.ByteArrayToStructureBigEndian <DirectoryHeader>(data); nextBlock = header.next_block + firstBlock; int off = (int)header.first_used; var entry = new DirectoryEntry(); while (off + _directoryEntrySize < data.Length) { entry = Marshal.ByteArrayToStructureBigEndian <DirectoryEntry>(data, off, _directoryEntrySize); string name = StringHandlers.CToString(entry.name, Encoding); var entryWithPointers = new DirectoryEntryWithPointers { entry = entry, pointers = new uint[entry.last_copy + 1] }; for (int i = 0; i <= entry.last_copy; i++) { entryWithPointers.pointers[i] = BigEndianBitConverter.ToUInt32(data, off + _directoryEntrySize + (i * 4)); } entries.Add(name, entryWithPointers); if ((entry.flags & (uint)FileFlags.LastEntry) != 0 || (entry.flags & (uint)FileFlags.LastEntryInBlock) != 0) { break; } off += (int)(_directoryEntrySize + ((entry.last_copy + 1) * 4)); } if ((entry.flags & (uint)FileFlags.LastEntry) != 0) { break; } } while(header.next_block != -1); return(entries); }
PathTableEntryInternal[] DecodePathTable(byte[] data) { if (data is null) { return(null); } List <PathTableEntryInternal> table = new List <PathTableEntryInternal>(); int off = 0; PathTableEntry entry = Marshal.ByteArrayToStructureBigEndian <PathTableEntry>(data, off, Marshal.SizeOf <PathTableEntry>()); if (entry.name_len != 1 || entry.parent_dirno != 1 || data.Length <= Marshal.SizeOf <PathTableEntry>() || data[Marshal.SizeOf <PathTableEntry>()] != 0x00) { return(null); } while (off < data.Length) { entry = Marshal.ByteArrayToStructureBigEndian <PathTableEntry>(data, off, Marshal.SizeOf <PathTableEntry>()); if (entry.name_len == 0) { break; } off += Marshal.SizeOf <PathTableEntry>(); string name = Encoding.GetString(data, off, entry.name_len); table.Add(new PathTableEntryInternal { Extent = entry.start_lbn, Name = name, Parent = entry.parent_dirno, XattrLength = entry.xattr_len }); off += entry.name_len; if (entry.name_len % 2 != 0) { off++; } } return(table.ToArray()); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (imagePlugin.Info.SectorSize < 512) { return(false); } byte[] sector = imagePlugin.ReadSector(partition.Start); Superblock sb = Marshal.ByteArrayToStructureBigEndian <Superblock>(sector); return(sb.magic == FATX_MAGIC || sb.magic == FATX_CIGAM); }
public bool Identify(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); if (stream.Length < 84) { return(false); } stream.Seek(0, SeekOrigin.Begin); byte[] headerB = new byte[Marshal.SizeOf <DartHeader>()]; stream.Read(headerB, 0, Marshal.SizeOf <DartHeader>()); DartHeader header = Marshal.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 != SIZE_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); } return(stream.Length <= expectedMaxSize); }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { Encoding = encoding ?? Encoding.GetEncoding(1252); byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" options ??= GetDefaultOptions(); if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out _debug); } if (options.TryGetValue("use_path_table", out string usePathTableString)) { bool.TryParse(usePathTableString, out _usePathTable); } if (options.TryGetValue("use_trans_tbl", out string useTransTblString)) { bool.TryParse(useTransTblString, out _useTransTbl); } if (options.TryGetValue("use_evd", out string useEvdString)) { bool.TryParse(useEvdString, out _useEvd); } // Default namespace @namespace ??= "joliet"; switch (@namespace.ToLowerInvariant()) { case "normal": _namespace = Namespace.Normal; break; case "vms": _namespace = Namespace.Vms; break; case "joliet": _namespace = Namespace.Joliet; break; case "rrip": _namespace = Namespace.Rrip; break; case "romeo": _namespace = Namespace.Romeo; break; default: return(Errno.InvalidArgument); } PrimaryVolumeDescriptor?pvd = null; PrimaryVolumeDescriptor?jolietvd = null; BootRecord?bvd = null; HighSierraPrimaryVolumeDescriptor?hsvd = null; FileStructureVolumeDescriptor? fsvd = null; // ISO9660 is designed for 2048 bytes/sector devices if (imagePlugin.Info.SectorSize < 2048) { return(Errno.InvalidArgument); } // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. if (partition.End < 16) { return(Errno.InvalidArgument); } ulong counter = 0; byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start); int xaOff = vdSector.Length == 2336 ? 8 : 0; Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); _highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; int hsOff = 0; if (_highSierra) { hsOff = 8; } _cdi = false; List <ulong> bvdSectors = new List <ulong>(); List <ulong> pvdSectors = new List <ulong>(); List <ulong> svdSectors = new List <ulong>(); List <ulong> evdSectors = new List <ulong>(); List <ulong> vpdSectors = new List <ulong>(); while (true) { AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); // Seek to Volume Descriptor AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); byte[] vdSectorTmp = imagePlugin.ReadSector(16 + counter + partition.Start); vdSector = new byte[vdSectorTmp.Length - xaOff]; Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); if (vdType == 255) // Supposedly we are in the PVD. { if (counter == 0) { return(Errno.InvalidArgument); } break; } Array.Copy(vdSector, 0x001, vdMagic, 0, 5); Array.Copy(vdSector, 0x009, hsMagic, 0, 5); if (Encoding.GetString(vdMagic) != ISO_MAGIC && Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && Encoding.GetString(vdMagic) != CDI_MAGIC ) // Recognized, it is an ISO9660, now check for rest of data. { if (counter == 0) { return(Errno.InvalidArgument); } break; } _cdi |= Encoding.GetString(vdMagic) == CDI_MAGIC; switch (vdType) { case 0: { if (_debug) { bvdSectors.Add(16 + counter + partition.Start); } break; } case 1: { if (_highSierra) { hsvd = Marshal. ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector); } else if (_cdi) { fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector); } else { pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); } if (_debug) { pvdSectors.Add(16 + counter + partition.Start); } break; } case 2: { PrimaryVolumeDescriptor svd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); // TODO: Other escape sequences // Check if this is Joliet if (svd.version == 1) { if (svd.escape_sequences[0] == '%' && svd.escape_sequences[1] == '/') { if (svd.escape_sequences[2] == '@' || svd.escape_sequences[2] == 'C' || svd.escape_sequences[2] == 'E') { jolietvd = svd; } else { AaruConsole.DebugWriteLine("ISO9660 plugin", "Found unknown supplementary volume descriptor"); } } if (_debug) { svdSectors.Add(16 + counter + partition.Start); } } else { if (_debug) { evdSectors.Add(16 + counter + partition.Start); } if (_useEvd) { // Basically until escape sequences are implemented, let the user chose the encoding. // This is the same as user choosing Romeo namespace, but using the EVD instead of the PVD _namespace = Namespace.Romeo; pvd = svd; } } break; } case 3: { if (_debug) { vpdSectors.Add(16 + counter + partition.Start); } break; } } counter++; } DecodedVolumeDescriptor decodedVd; var decodedJolietVd = new DecodedVolumeDescriptor(); XmlFsType = new FileSystemType(); if (pvd == null && hsvd == null && fsvd == null) { AaruConsole.ErrorWriteLine("ERROR: Could not find primary volume descriptor"); return(Errno.InvalidArgument); } if (_highSierra) { decodedVd = DecodeVolumeDescriptor(hsvd.Value); } else if (_cdi) { decodedVd = DecodeVolumeDescriptor(fsvd.Value); } else { decodedVd = DecodeVolumeDescriptor(pvd.Value); } if (jolietvd != null) { decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); } if (_namespace != Namespace.Romeo) { Encoding = Encoding.ASCII; } string fsFormat; byte[] pathTableData; uint pathTableMsbLocation; uint pathTableLsbLocation = 0; // Initialize to 0 as ignored in CD-i _image = imagePlugin; if (_highSierra) { pathTableData = ReadSingleExtent(0, hsvd.Value.path_table_size, Swapping.Swap(hsvd.Value.mandatory_path_table_msb)); fsFormat = "High Sierra Format"; pathTableMsbLocation = hsvd.Value.mandatory_path_table_msb; pathTableLsbLocation = hsvd.Value.mandatory_path_table_lsb; } else if (_cdi) { pathTableData = ReadSingleExtent(0, fsvd.Value.path_table_size, fsvd.Value.path_table_addr); fsFormat = "CD-i"; pathTableMsbLocation = fsvd.Value.path_table_addr; // TODO: Until escape sequences are implemented this is the default CD-i encoding. Encoding = Encoding.GetEncoding("iso8859-1"); } else { pathTableData = ReadSingleExtent(0, pvd.Value.path_table_size, Swapping.Swap(pvd.Value.type_m_path_table)); fsFormat = "ISO9660"; pathTableMsbLocation = pvd.Value.type_m_path_table; pathTableLsbLocation = pvd.Value.type_l_path_table; } _pathTable = _highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData); // High Sierra and CD-i do not support Joliet or RRIP if ((_highSierra || _cdi) && _namespace != Namespace.Normal && _namespace != Namespace.Vms) { _namespace = Namespace.Normal; } if (jolietvd is null && _namespace == Namespace.Joliet) { _namespace = Namespace.Normal; } uint rootLocation; uint rootSize; byte rootXattrLength = 0; if (!_cdi) { rootLocation = _highSierra ? hsvd.Value.root_directory_record.extent : pvd.Value.root_directory_record.extent; rootXattrLength = _highSierra ? hsvd.Value.root_directory_record.xattr_len : pvd.Value.root_directory_record.xattr_len; rootSize = _highSierra ? hsvd.Value.root_directory_record.size : pvd.Value.root_directory_record.size; if (pathTableData.Length > 1 && rootLocation != _pathTable[0].Extent) { AaruConsole.DebugWriteLine("ISO9660 plugin", "Path table and PVD do not point to the same location for the root directory!"); byte[] firstRootSector = ReadSector(rootLocation); bool pvdWrongRoot = false; if (_highSierra) { HighSierraDirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pvdWrongRoot = true; } } else { DirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pvdWrongRoot = true; } } if (pvdWrongRoot) { AaruConsole.DebugWriteLine("ISO9660 plugin", "PVD does not point to correct root directory, checking path table..."); bool pathTableWrongRoot = false; rootLocation = _pathTable[0].Extent; firstRootSector = ReadSector(_pathTable[0].Extent); if (_highSierra) { HighSierraDirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pathTableWrongRoot = true; } } else { DirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pathTableWrongRoot = true; } } if (pathTableWrongRoot) { AaruConsole.ErrorWriteLine("Cannot find root directory..."); return(Errno.InvalidArgument); } _usePathTable = true; } } } else { rootLocation = _pathTable[0].Extent; byte[] firstRootSector = ReadSector(rootLocation); CdiDirectoryRecord rootEntry = Marshal.ByteArrayToStructureBigEndian <CdiDirectoryRecord>(firstRootSector); rootSize = rootEntry.size; _usePathTable = _usePathTable || _pathTable.Length == 1; _useTransTbl = false; } // In case the path table is incomplete if (_usePathTable && pathTableData.Length == 1) { _usePathTable = false; } if (_usePathTable && !_cdi) { rootLocation = _pathTable[0].Extent; byte[] firstRootSector = ReadSector(rootLocation); if (_highSierra) { HighSierraDirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector); rootSize = rootEntry.size; } else { DirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector); rootSize = rootEntry.size; } rootXattrLength = _pathTable[0].XattrLength; } try { _ = ReadSingleExtent(0, rootSize, rootLocation); } catch { return(Errno.InvalidArgument); } byte[] ipbinSector = ReadSector(partition.Start); CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); Dreamcast.IPBin?dreamcast = Dreamcast.DecodeIPBin(ipbinSector); if (_namespace == Namespace.Joliet || _namespace == Namespace.Rrip) { _usePathTable = false; _useTransTbl = false; } // Cannot traverse path table if we substitute the names for the ones in TRANS.TBL if (_useTransTbl) { _usePathTable = false; } if (_namespace != Namespace.Joliet) { _rootDirectoryCache = _cdi ? DecodeCdiDirectory(rootLocation, rootSize, rootXattrLength) : _highSierra ? DecodeHighSierraDirectory(rootLocation, rootSize, rootXattrLength) : DecodeIsoDirectory(rootLocation, rootSize, rootXattrLength); } XmlFsType.Type = fsFormat; if (jolietvd != null && (_namespace == Namespace.Joliet || _namespace == Namespace.Rrip)) { rootLocation = jolietvd.Value.root_directory_record.extent; rootXattrLength = jolietvd.Value.root_directory_record.xattr_len; rootSize = jolietvd.Value.root_directory_record.size; _joliet = true; _rootDirectoryCache = DecodeIsoDirectory(rootLocation, rootSize, rootXattrLength); XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; if (string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; } else { XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null : decodedJolietVd.SystemIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) { XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; } else { XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null : decodedJolietVd.VolumeSetIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) { XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; } else { XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null : decodedJolietVd.PublisherIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) { XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; } else { XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) ? null : decodedJolietVd.DataPreparerIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) { XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; } else { XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null : decodedJolietVd.ApplicationIdentifier; } XmlFsType.CreationDate = decodedJolietVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedJolietVd.HasModificationTime) { XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedJolietVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedJolietVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } decodedVd = decodedJolietVd; } else { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; XmlFsType.VolumeName = decodedVd.VolumeIdentifier; XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; XmlFsType.CreationDate = decodedVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedVd.HasModificationTime) { XmlFsType.ModificationDate = decodedVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } } if (_debug) { _rootDirectoryCache.Add("$", new DecodedDirectoryEntry { Extents = new List <(uint extent, uint size)> { (rootLocation, rootSize) }, Filename = "$", Size = rootSize, Timestamp = decodedVd.CreationTime });
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { // TODO: Find correct default encoding Encoding = Encoding.ASCII; information = ""; StringBuilder superBlockMetadata = new StringBuilder(); byte[] sbSector = imagePlugin.ReadSector(0 + partition.Start); SuperBlock sb = Marshal.ByteArrayToStructureBigEndian <SuperBlock>(sbSector); if (sb.record_type != 1 || sb.record_version != 1) { return; } if (Encoding.ASCII.GetString(sb.sync_bytes) != SYNC) { return; } superBlockMetadata.AppendFormat("Opera filesystem disc.").AppendLine(); if (!string.IsNullOrEmpty(StringHandlers.CToString(sb.volume_label, Encoding))) { superBlockMetadata .AppendFormat("Volume label: {0}", StringHandlers.CToString(sb.volume_label, Encoding)).AppendLine(); } if (!string.IsNullOrEmpty(StringHandlers.CToString(sb.volume_comment, Encoding))) { superBlockMetadata .AppendFormat("Volume comment: {0}", StringHandlers.CToString(sb.volume_comment, Encoding)) .AppendLine(); } superBlockMetadata.AppendFormat("Volume identifier: 0x{0:X8}", sb.volume_id).AppendLine(); superBlockMetadata.AppendFormat("Block size: {0} bytes", sb.block_size).AppendLine(); if (imagePlugin.Info.SectorSize == 2336 || imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448) { if (sb.block_size != 2048) { superBlockMetadata .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/block", sb.block_size, 2048); } } else if (imagePlugin.Info.SectorSize != sb.block_size) { superBlockMetadata .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/block", sb.block_size, imagePlugin.Info.SectorSize); } superBlockMetadata .AppendFormat("Volume size: {0} blocks, {1} bytes", sb.block_count, sb.block_size * sb.block_count) .AppendLine(); if (sb.block_count > imagePlugin.Info.Sectors) { superBlockMetadata .AppendFormat("WARNING: Filesystem indicates {0} blocks while device indicates {1} blocks", sb.block_count, imagePlugin.Info.Sectors); } superBlockMetadata.AppendFormat("Root directory identifier: 0x{0:X8}", sb.root_dirid).AppendLine(); superBlockMetadata.AppendFormat("Root directory block size: {0} bytes", sb.rootdir_bsize).AppendLine(); superBlockMetadata.AppendFormat("Root directory size: {0} blocks, {1} bytes", sb.rootdir_blocks, sb.rootdir_bsize * sb.rootdir_blocks).AppendLine(); superBlockMetadata.AppendFormat("Last root directory copy: {0}", sb.last_root_copy).AppendLine(); information = superBlockMetadata.ToString(); XmlFsType = new FileSystemType { Type = "Opera", VolumeName = StringHandlers.CToString(sb.volume_label, Encoding), ClusterSize = sb.block_size, Clusters = sb.block_count }; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("ibm850"); information = ""; var sb = new StringBuilder(); byte[] hpofsBpbSector = imagePlugin.ReadSector(0 + partition.Start); // Seek to BIOS parameter block, on logical sector 0 byte[] medInfoSector = imagePlugin.ReadSector(13 + partition.Start); // Seek to media information block, on logical sector 13 byte[] volInfoSector = imagePlugin.ReadSector(14 + partition.Start); // Seek to volume information block, on logical sector 14 BiosParameterBlock bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock>(hpofsBpbSector); MediaInformationBlock mib = Marshal.ByteArrayToStructureBigEndian <MediaInformationBlock>(medInfoSector); VolumeInformationBlock vib = Marshal.ByteArrayToStructureBigEndian <VolumeInformationBlock>(volInfoSector); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.oem_name = \"{0}\"", StringHandlers.CToString(bpb.oem_name)); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.bps = {0}", bpb.bps); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.spc = {0}", bpb.spc); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.rsectors = {0}", bpb.rsectors); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.fats_no = {0}", bpb.fats_no); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.root_ent = {0}", bpb.root_ent); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.sectors = {0}", bpb.sectors); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.media = 0x{0:X2}", bpb.media); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.spfat = {0}", bpb.spfat); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.sptrk = {0}", bpb.sptrk); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.heads = {0}", bpb.heads); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.hsectors = {0}", bpb.hsectors); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.big_sectors = {0}", bpb.big_sectors); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.drive_no = 0x{0:X2}", bpb.drive_no); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.nt_flags = {0}", bpb.nt_flags); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.signature = 0x{0:X2}", bpb.signature); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.serial_no = 0x{0:X8}", bpb.serial_no); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.volume_label = \"{0}\"", StringHandlers.SpacePaddedToString(bpb.volume_label)); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.fs_type = \"{0}\"", StringHandlers.CToString(bpb.fs_type)); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.boot_code is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(bpb.boot_code)); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.unknown = {0}", bpb.unknown); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.unknown2 = {0}", bpb.unknown2); AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.signature2 = {0}", bpb.signature2); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.blockId = \"{0}\"", StringHandlers.CToString(mib.blockId)); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.volumeLabel = \"{0}\"", StringHandlers.SpacePaddedToString(mib.volumeLabel)); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.comment = \"{0}\"", StringHandlers.SpacePaddedToString(mib.comment)); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.serial = 0x{0:X8}", mib.serial); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.creationTimestamp = {0}", DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime)); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.codepageType = {0}", mib.codepageType); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.codepage = {0}", mib.codepage); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.rps = {0}", mib.rps); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.bps = {0}", mib.bps); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.bpc = {0}", mib.bpc); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown2 = {0}", mib.unknown2); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.sectors = {0}", mib.sectors); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown3 = {0}", mib.unknown3); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown4 = {0}", mib.unknown4); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.major = {0}", mib.major); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.minor = {0}", mib.minor); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown5 = {0}", mib.unknown5); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown6 = {0}", mib.unknown6); AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.filler is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(mib.filler)); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.blockId = \"{0}\"", StringHandlers.CToString(vib.blockId)); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown = {0}", vib.unknown); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown2 = {0}", vib.unknown2); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown3 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(vib.unknown3)); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown4 = \"{0}\"", StringHandlers.SpacePaddedToString(vib.unknown4)); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.owner = \"{0}\"", StringHandlers.SpacePaddedToString(vib.owner)); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown5 = \"{0}\"", StringHandlers.SpacePaddedToString(vib.unknown5)); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown6 = {0}", vib.unknown6); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.percentFull = {0}", vib.percentFull); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown7 = {0}", vib.unknown7); AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.filler is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(vib.filler)); sb.AppendLine("High Performance Optical File System"); sb.AppendFormat("OEM name: {0}", StringHandlers.SpacePaddedToString(bpb.oem_name)).AppendLine(); sb.AppendFormat("{0} bytes per sector", bpb.bps).AppendLine(); sb.AppendFormat("{0} sectors per cluster", bpb.spc).AppendLine(); sb.AppendFormat("Media descriptor: 0x{0:X2}", bpb.media).AppendLine(); sb.AppendFormat("{0} sectors per track", bpb.sptrk).AppendLine(); sb.AppendFormat("{0} heads", bpb.heads).AppendLine(); sb.AppendFormat("{0} sectors hidden before BPB", bpb.hsectors).AppendLine(); sb.AppendFormat("{0} sectors on volume ({1} bytes)", mib.sectors, mib.sectors * bpb.bps).AppendLine(); sb.AppendFormat("BIOS Drive Number: 0x{0:X2}", bpb.drive_no).AppendLine(); sb.AppendFormat("Serial number: 0x{0:X8}", mib.serial).AppendLine(); sb.AppendFormat("Volume label: {0}", StringHandlers.SpacePaddedToString(mib.volumeLabel, Encoding)). AppendLine(); sb.AppendFormat("Volume comment: {0}", StringHandlers.SpacePaddedToString(mib.comment, Encoding)). AppendLine(); sb.AppendFormat("Volume owner: {0}", StringHandlers.SpacePaddedToString(vib.owner, Encoding)).AppendLine(); sb.AppendFormat("Volume created on {0}", DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime)). AppendLine(); sb.AppendFormat("Volume uses {0} codepage {1}", mib.codepageType > 0 && mib.codepageType < 3 ? mib.codepageType == 2 ? "EBCDIC" : "ASCII" : "Unknown", mib.codepage).AppendLine(); sb.AppendFormat("RPS level: {0}", mib.rps).AppendLine(); sb.AppendFormat("Filesystem version: {0}.{1}", mib.major, mib.minor).AppendLine(); sb.AppendFormat("Volume can be filled up to {0}%", vib.percentFull).AppendLine(); XmlFsType = new FileSystemType { Clusters = mib.sectors / bpb.spc, ClusterSize = (uint)(bpb.bps * bpb.spc), CreationDate = DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime), CreationDateSpecified = true, DataPreparerIdentifier = StringHandlers.SpacePaddedToString(vib.owner, Encoding), Type = "HPOFS", VolumeName = StringHandlers.SpacePaddedToString(mib.volumeLabel, Encoding), VolumeSerial = $"{mib.serial:X8}", SystemIdentifier = StringHandlers.SpacePaddedToString(bpb.oem_name) }; information = sb.ToString(); }
bool FillDirectory() { idToFilename = new Dictionary <uint, string>(); idToEntry = new Dictionary <uint, FileEntry>(); filenameToId = new Dictionary <string, uint>(); int offset = 0; while (offset + 51 < directoryBlocks.Length) { var entry = new FileEntry { flFlags = (FileFlags)directoryBlocks[offset + 0] }; if (!entry.flFlags.HasFlag(FileFlags.Used)) { break; } entry.flTyp = directoryBlocks[offset + 1]; entry.flUsrWds = Marshal.ByteArrayToStructureBigEndian <AppleCommon.FInfo>(directoryBlocks, offset + 2, 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(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); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlags = {0}", entry.flFlags); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flTyp = {0}", entry.flTyp); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlNum = {0}", entry.flFlNum); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flStBlk = {0}", entry.flStBlk); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flLgLen = {0}", entry.flLgLen); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flPyLen = {0}", entry.flPyLen); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRStBlk = {0}", entry.flRStBlk); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRLgLen = {0}", entry.flRLgLen); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRPyLen = {0}", entry.flRPyLen); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flCrDat = {0}", DateHandlers.MacToDateTime(entry.flCrDat)); AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flMdDat = {0}", DateHandlers.MacToDateTime(entry.flMdDat)); AaruConsole.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 bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); if (stream.Length < 84) { return(false); } stream.Seek(0, SeekOrigin.Begin); byte[] headerB = new byte[Marshal.SizeOf <DartHeader>()]; stream.Read(headerB, 0, Marshal.SizeOf <DartHeader>()); DartHeader header = Marshal.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); }
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) { byte[] magicB = new byte[4]; Stream stream = imageFilter.GetDataForkStream(); stream.Read(magicB, 0, 4); uint magic = BitConverter.ToUInt32(magicB, 0); if (magic != DFI_MAGIC && magic != DFI_MAGIC2) { return(false); } TrackOffsets = new SortedDictionary <int, long>(); TrackLengths = new SortedDictionary <int, long>(); int t = -1; ushort lastCylinder = 0, lastHead = 0; long offset = 0; while (stream.Position < stream.Length) { long thisOffset = stream.Position; byte[] blk = new byte[Marshal.SizeOf <DfiBlockHeader>()]; stream.Read(blk, 0, Marshal.SizeOf <DfiBlockHeader>()); DfiBlockHeader blockHeader = Marshal.ByteArrayToStructureBigEndian <DfiBlockHeader>(blk); AaruConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.cylinder = {1}", thisOffset, blockHeader.cylinder); AaruConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.head = {1}", thisOffset, blockHeader.head); AaruConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.sector = {1}", thisOffset, blockHeader.sector); AaruConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.length = {1}", thisOffset, blockHeader.length); if (stream.Position + blockHeader.length > stream.Length) { AaruConsole.DebugWriteLine("DiscFerret plugin", "Invalid track block found at {0}", thisOffset); break; } stream.Position += blockHeader.length; if (blockHeader.cylinder > 0 && blockHeader.cylinder > lastCylinder) { lastCylinder = blockHeader.cylinder; lastHead = 0; TrackOffsets.Add(t, offset); TrackLengths.Add(t, (thisOffset - offset) + 1); offset = thisOffset; t++; } else if (blockHeader.head > 0 && blockHeader.head > lastHead) { lastHead = blockHeader.head; TrackOffsets.Add(t, offset); TrackLengths.Add(t, (thisOffset - offset) + 1); offset = thisOffset; t++; } if (blockHeader.cylinder > _imageInfo.Cylinders) { _imageInfo.Cylinders = blockHeader.cylinder; } if (blockHeader.head > _imageInfo.Heads) { _imageInfo.Heads = blockHeader.head; } } _imageInfo.Heads++; _imageInfo.Cylinders++; _imageInfo.Application = "DiscFerret"; _imageInfo.ApplicationVersion = magic == DFI_MAGIC2 ? "2.0" : "1.0"; throw new NotImplementedException("Flux decoding is not yet implemented."); }
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); if (stream.Length < 512) { return(false); } stream.Seek(-Marshal.SizeOf <UdifFooter>(), SeekOrigin.End); byte[] footerB = new byte[Marshal.SizeOf <UdifFooter>()]; stream.Read(footerB, 0, Marshal.SizeOf <UdifFooter>()); footer = Marshal.ByteArrayToStructureBigEndian <UdifFooter>(footerB); if (footer.signature != UDIF_SIGNATURE) { stream.Seek(0, SeekOrigin.Begin); footerB = new byte[Marshal.SizeOf <UdifFooter>()]; stream.Read(footerB, 0, Marshal.SizeOf <UdifFooter>()); footer = Marshal.ByteArrayToStructureBigEndian <UdifFooter>(footerB); if (footer.signature != UDIF_SIGNATURE) { throw new Exception("Unable to find UDIF signature."); } AaruConsole.VerboseWriteLine("Found obsolete UDIF format."); } AaruConsole.DebugWriteLine("UDIF plugin", "footer.signature = 0x{0:X8}", footer.signature); AaruConsole.DebugWriteLine("UDIF plugin", "footer.version = {0}", footer.version); AaruConsole.DebugWriteLine("UDIF plugin", "footer.headerSize = {0}", footer.headerSize); AaruConsole.DebugWriteLine("UDIF plugin", "footer.flags = {0}", footer.flags); AaruConsole.DebugWriteLine("UDIF plugin", "footer.runningDataForkOff = {0}", footer.runningDataForkOff); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkOff = {0}", footer.dataForkOff); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkOff = {0}", footer.rsrcForkOff); AaruConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkLen = {0}", footer.rsrcForkLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.segmentNumber = {0}", footer.segmentNumber); AaruConsole.DebugWriteLine("UDIF plugin", "footer.segmentCount = {0}", footer.segmentCount); AaruConsole.DebugWriteLine("UDIF plugin", "footer.segmentId = {0}", footer.segmentId); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChkType = {0}", footer.dataForkChkType); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChk = 0x{0:X8}", footer.dataForkChk); AaruConsole.DebugWriteLine("UDIF plugin", "footer.plistOff = {0}", footer.plistOff); AaruConsole.DebugWriteLine("UDIF plugin", "footer.plistLen = {0}", footer.plistLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.masterChkType = {0}", footer.masterChkType); AaruConsole.DebugWriteLine("UDIF plugin", "footer.masterChkLen = {0}", footer.masterChkLen); AaruConsole.DebugWriteLine("UDIF plugin", "footer.masterChk = 0x{0:X8}", footer.masterChk); AaruConsole.DebugWriteLine("UDIF plugin", "footer.imageVariant = {0}", footer.imageVariant); AaruConsole.DebugWriteLine("UDIF plugin", "footer.sectorCount = {0}", footer.sectorCount); AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved1 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved1)); AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved2 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved2)); AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved3 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved3)); AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved4 is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved4)); // Block chunks and headers List <byte[]> blkxList = new List <byte[]>(); chunks = new Dictionary <ulong, BlockChunk>(); bool fakeBlockChunks = false; byte[] vers = null; if (footer.plistLen == 0 && footer.rsrcForkLen != 0) { AaruConsole.DebugWriteLine("UDIF plugin", "Reading resource fork."); byte[] rsrcB = new byte[footer.rsrcForkLen]; stream.Seek((long)footer.rsrcForkOff, SeekOrigin.Begin); stream.Read(rsrcB, 0, rsrcB.Length); var rsrc = new ResourceFork(rsrcB); if (!rsrc.ContainsKey(BLOCK_OS_TYPE)) { throw new ImageNotSupportedException("Image resource fork doesn't contain UDIF block chunks. Please fill an issue and send it to us."); } Resource blkxRez = rsrc.GetResource(BLOCK_OS_TYPE); if (blkxRez == null) { throw new ImageNotSupportedException("Image resource fork doesn't contain UDIF block chunks. Please fill an issue and send it to us."); } if (blkxRez.GetIds().Length == 0) { throw new ImageNotSupportedException("Image resource fork doesn't contain UDIF block chunks. Please fill an issue and send it to us."); } blkxList.AddRange(blkxRez.GetIds().Select(blkxId => blkxRez.GetResource(blkxId))); Resource versRez = rsrc.GetResource(0x76657273); if (versRez != null) { vers = versRez.GetResource(versRez.GetIds()[0]); } } else if (footer.plistLen != 0) { AaruConsole.DebugWriteLine("UDIF plugin", "Reading property list."); byte[] plistB = new byte[footer.plistLen]; stream.Seek((long)footer.plistOff, SeekOrigin.Begin); stream.Read(plistB, 0, plistB.Length); AaruConsole.DebugWriteLine("UDIF plugin", "Parsing property list."); var plist = (NSDictionary)XmlPropertyListParser.Parse(plistB); if (plist == null) { throw new Exception("Could not parse property list."); } if (!plist.TryGetValue(RESOURCE_FORK_KEY, out NSObject rsrcObj)) { throw new Exception("Could not retrieve resource fork."); } var rsrc = (NSDictionary)rsrcObj; if (!rsrc.TryGetValue(BLOCK_KEY, out NSObject blkxObj)) { throw new Exception("Could not retrieve block chunks array."); } NSObject[] blkx = ((NSArray)blkxObj).GetArray(); foreach (NSDictionary part in blkx.Cast <NSDictionary>()) { if (!part.TryGetValue("Name", out _)) { throw new Exception("Could not retrieve Name"); } if (!part.TryGetValue("Data", out NSObject dataObj)) { throw new Exception("Could not retrieve Data"); } blkxList.Add(((NSData)dataObj).Bytes); } if (rsrc.TryGetValue("vers", out NSObject versObj)) { NSObject[] versArray = ((NSArray)versObj).GetArray(); if (versArray.Length >= 1) { vers = ((NSData)versArray[0]).Bytes; } } } else { // Obsolete read-only UDIF only prepended the header and then put the image without any kind of block references. // So let's falsify a block chunk var bChnk = new BlockChunk { length = footer.dataForkLen, offset = footer.dataForkOff, sector = 0, sectors = footer.sectorCount, type = CHUNK_TYPE_COPY }; imageInfo.Sectors = footer.sectorCount; chunks.Add(bChnk.sector, bChnk); buffersize = 2048 * SECTOR_SIZE; fakeBlockChunks = true; } if (vers != null) { var version = new Version(vers); string release = null; string dev = null; string pre = null; string major = $"{version.MajorVersion}"; string minor = $".{version.MinorVersion / 10}"; if (version.MinorVersion % 10 > 0) { release = $".{version.MinorVersion % 10}"; } switch (version.DevStage) { case Version.DevelopmentStage.Alpha: dev = "a"; break; case Version.DevelopmentStage.Beta: dev = "b"; break; case Version.DevelopmentStage.PreAlpha: dev = "d"; break; } if (dev == null && version.PreReleaseVersion > 0) { dev = "f"; } if (dev != null) { pre = $"{version.PreReleaseVersion}"; } imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}"; imageInfo.Application = version.VersionString; imageInfo.Comments = version.VersionMessage; if (version.MajorVersion == 3) { imageInfo.Application = "ShrinkWrap™"; } else if (version.MajorVersion == 6) { imageInfo.Application = "DiskCopy"; } } else { imageInfo.Application = "DiskCopy"; } AaruConsole.DebugWriteLine("UDIF plugin", "Image application = {0} version {1}", imageInfo.Application, imageInfo.ApplicationVersion); imageInfo.Sectors = 0; if (!fakeBlockChunks) { if (blkxList.Count == 0) { throw new ImageNotSupportedException("Could not retrieve block chunks. Please fill an issue and send it to us."); } buffersize = 0; foreach (byte[] blkxBytes in blkxList) { var bHdr = new BlockHeader(); byte[] bHdrB = new byte[Marshal.SizeOf <BlockHeader>()]; Array.Copy(blkxBytes, 0, bHdrB, 0, Marshal.SizeOf <BlockHeader>()); bHdr = Marshal.ByteArrayToStructureBigEndian <BlockHeader>(bHdrB); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.signature = 0x{0:X8}", bHdr.signature); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.version = {0}", bHdr.version); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorStart = {0}", bHdr.sectorStart); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorCount = {0}", bHdr.sectorCount); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.dataOffset = {0}", bHdr.dataOffset); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.buffers = {0}", bHdr.buffers); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.descriptor = 0x{0:X8}", bHdr.descriptor); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved1 = {0}", bHdr.reserved1); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved2 = {0}", bHdr.reserved2); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved3 = {0}", bHdr.reserved3); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved4 = {0}", bHdr.reserved4); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved5 = {0}", bHdr.reserved5); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved6 = {0}", bHdr.reserved6); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumType = {0}", bHdr.checksumType); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumLen = {0}", bHdr.checksumLen); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.checksum = 0x{0:X8}", bHdr.checksum); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunks = {0}", bHdr.chunks); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reservedChk is empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(bHdr.reservedChk)); if (bHdr.buffers > buffersize) { buffersize = bHdr.buffers * SECTOR_SIZE; } for (int i = 0; i < bHdr.chunks; i++) { var bChnk = new BlockChunk(); byte[] bChnkB = new byte[Marshal.SizeOf <BlockChunk>()]; Array.Copy(blkxBytes, Marshal.SizeOf <BlockHeader>() + (Marshal.SizeOf <BlockChunk>() * i), bChnkB, 0, Marshal.SizeOf <BlockChunk>()); bChnk = Marshal.ByteArrayToStructureBigEndian <BlockChunk>(bChnkB); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].type = 0x{1:X8}", i, bChnk.type); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].comment = {1}", i, bChnk.comment); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sector = {1}", i, bChnk.sector); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sectors = {1}", i, bChnk.sectors); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].offset = {1}", i, bChnk.offset); AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].length = {1}", i, bChnk.length); if (bChnk.type == CHUNK_TYPE_END) { break; } imageInfo.Sectors += bChnk.sectors; // Chunk offset is relative bChnk.sector += bHdr.sectorStart; bChnk.offset += bHdr.dataOffset; switch (bChnk.type) { // TODO: Handle comments case CHUNK_TYPE_COMMNT: continue; // TODO: Handle compressed chunks case CHUNK_TYPE_KENCODE: throw new ImageNotSupportedException("Chunks compressed with KenCode are not yet supported."); case CHUNK_TYPE_LZH: throw new ImageNotSupportedException("Chunks compressed with LZH are not yet supported."); case CHUNK_TYPE_LZFSE: throw new ImageNotSupportedException("Chunks compressed with lzfse are not yet supported."); } if ((bChnk.type > CHUNK_TYPE_NOCOPY && bChnk.type < CHUNK_TYPE_COMMNT) || (bChnk.type > CHUNK_TYPE_LZFSE && bChnk.type < CHUNK_TYPE_END)) { throw new ImageNotSupportedException($"Unsupported chunk type 0x{bChnk.type:X8} found"); } if (bChnk.sectors > 0) { chunks.Add(bChnk.sector, bChnk); } } } } sectorCache = new Dictionary <ulong, byte[]>(); chunkCache = new Dictionary <ulong, byte[]>(); currentChunkCacheSize = 0; imageStream = stream; imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); imageInfo.SectorSize = SECTOR_SIZE; imageInfo.XmlMediaType = XmlMediaType.BlockMedia; imageInfo.MediaType = MediaType.GENERIC_HDD; imageInfo.ImageSize = imageInfo.Sectors * SECTOR_SIZE; imageInfo.Version = $"{footer.version}"; imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); imageInfo.Heads = 16; imageInfo.SectorsPerTrack = 63; return(true); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.ASCII; information = ""; var isoMetadata = new StringBuilder(); byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" string bootSpec = ""; PrimaryVolumeDescriptor?pvd = null; PrimaryVolumeDescriptor?jolietvd = null; BootRecord?bvd = null; HighSierraPrimaryVolumeDescriptor?hsvd = null; FileStructureVolumeDescriptor? fsvd = null; ElToritoBootRecord?torito = null; // ISO9660 is designed for 2048 bytes/sector devices if (imagePlugin.Info.SectorSize < 2048) { return; } // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. if (partition.End < 16) { return; } ulong counter = 0; byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start); int xaOff = vdSector.Length == 2336 ? 8 : 0; Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); bool highSierraInfo = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; int hsOff = 0; if (highSierraInfo) { hsOff = 8; } bool cdiInfo = false; bool evd = false; bool vpd = false; while (true) { AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); // Seek to Volume Descriptor AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); byte[] vdSectorTmp = imagePlugin.ReadSector(16 + counter + partition.Start); vdSector = new byte[vdSectorTmp.Length - xaOff]; Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); if (vdType == 255) // Supposedly we are in the PVD. { if (counter == 0) { return; } break; } Array.Copy(vdSector, 0x001, vdMagic, 0, 5); Array.Copy(vdSector, 0x009, hsMagic, 0, 5); if (Encoding.GetString(vdMagic) != ISO_MAGIC && Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && Encoding.GetString(vdMagic) != CDI_MAGIC ) // Recognized, it is an ISO9660, now check for rest of data. { if (counter == 0) { return; } break; } cdiInfo |= Encoding.GetString(vdMagic) == CDI_MAGIC; switch (vdType) { case 0: { bvd = Marshal.ByteArrayToStructureLittleEndian <BootRecord>(vdSector, hsOff, 2048 - hsOff); bootSpec = "Unknown"; if (Encoding.GetString(bvd.Value.system_id).Substring(0, 23) == "EL TORITO SPECIFICATION") { bootSpec = "El Torito"; torito = Marshal.ByteArrayToStructureLittleEndian <ElToritoBootRecord>(vdSector, hsOff, 2048 - hsOff); } break; } case 1: { if (highSierraInfo) { hsvd = Marshal. ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector); } else if (cdiInfo) { fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector); } else { pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); } break; } case 2: { PrimaryVolumeDescriptor svd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); // Check if this is Joliet if (svd.version == 1) { if (svd.escape_sequences[0] == '%' && svd.escape_sequences[1] == '/') { if (svd.escape_sequences[2] == '@' || svd.escape_sequences[2] == 'C' || svd.escape_sequences[2] == 'E') { jolietvd = svd; } else { AaruConsole.WriteLine("ISO9660 plugin", "Found unknown supplementary volume descriptor"); } } } else { evd = true; } break; } case 3: { vpd = true; break; } } counter++; } DecodedVolumeDescriptor decodedVd; var decodedJolietVd = new DecodedVolumeDescriptor(); XmlFsType = new FileSystemType(); if (pvd == null && hsvd == null && fsvd == null) { information = "ERROR: Could not find primary volume descriptor"; return; } if (highSierraInfo) { decodedVd = DecodeVolumeDescriptor(hsvd.Value); } else if (cdiInfo) { decodedVd = DecodeVolumeDescriptor(fsvd.Value); } else { decodedVd = DecodeVolumeDescriptor(pvd.Value); } if (jolietvd != null) { decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); } uint rootLocation = 0; uint rootSize = 0; // No need to read root on CD-i, as extensions are not supported... if (!cdiInfo) { rootLocation = highSierraInfo ? hsvd.Value.root_directory_record.extent : pvd.Value.root_directory_record.extent; if (highSierraInfo) { rootSize = hsvd.Value.root_directory_record.size / hsvd.Value.logical_block_size; if (hsvd.Value.root_directory_record.size % hsvd.Value.logical_block_size > 0) { rootSize++; } } else { rootSize = pvd.Value.root_directory_record.size / pvd.Value.logical_block_size; if (pvd.Value.root_directory_record.size % pvd.Value.logical_block_size > 0) { rootSize++; } } } byte[] rootDir = new byte[0]; int rootOff = 0; bool xaExtensions = false; bool apple = false; bool susp = false; bool rrip = false; bool ziso = false; bool amiga = false; bool aaip = false; List <ContinuationArea> contareas = new List <ContinuationArea>(); List <byte[]> refareas = new List <byte[]>(); var suspInformation = new StringBuilder(); if (rootLocation + rootSize < imagePlugin.Info.Sectors) { rootDir = imagePlugin.ReadSectors(rootLocation, rootSize); } // Walk thru root directory to see system area extensions in use while (rootOff + Marshal.SizeOf <DirectoryRecord>() < rootDir.Length && !cdiInfo) { DirectoryRecord record = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(rootDir, rootOff, Marshal.SizeOf <DirectoryRecord>()); int saOff = Marshal.SizeOf <DirectoryRecord>() + record.name_len; saOff += saOff % 2; int saLen = record.length - saOff; if (saLen > 0 && rootOff + saOff + saLen <= rootDir.Length) { byte[] sa = new byte[saLen]; Array.Copy(rootDir, rootOff + saOff, sa, 0, saLen); saOff = 0; while (saOff < saLen) { bool noneFound = true; if (Marshal.SizeOf <CdromXa>() + saOff <= saLen) { CdromXa xa = Marshal.ByteArrayToStructureBigEndian <CdromXa>(sa); if (xa.signature == XA_MAGIC) { xaExtensions = true; saOff += Marshal.SizeOf <CdromXa>(); noneFound = false; } } if (saOff + 2 >= saLen) { break; } ushort nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); switch (nextSignature) { // Easy, contains size field case APPLE_MAGIC: apple = true; saOff += sa[saOff + 2]; noneFound = false; break; // Not easy, contains size field case APPLE_MAGIC_OLD: apple = true; var appleId = (AppleOldId)sa[saOff + 2]; noneFound = false; switch (appleId) { case AppleOldId.ProDOS: saOff += Marshal.SizeOf <AppleProDOSOldSystemUse>(); break; case AppleOldId.TypeCreator: case AppleOldId.TypeCreatorBundle: saOff += Marshal.SizeOf <AppleHFSTypeCreatorSystemUse>(); break; case AppleOldId.TypeCreatorIcon: case AppleOldId.TypeCreatorIconBundle: saOff += Marshal.SizeOf <AppleHFSIconSystemUse>(); break; case AppleOldId.HFS: saOff += Marshal.SizeOf <AppleHFSOldSystemUse>(); break; } break; // IEEE-P1281 aka SUSP 1.12 case SUSP_INDICATOR: susp = true; saOff += sa[saOff + 2]; noneFound = false; while (saOff + 2 < saLen) { nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); switch (nextSignature) { case APPLE_MAGIC: if (sa[saOff + 3] == 1 && sa[saOff + 2] == 7) { apple = true; } else { apple |= sa[saOff + 3] != 1; } break; case SUSP_CONTINUATION when saOff + sa[saOff + 2] <= saLen: byte[] ce = new byte[sa[saOff + 2]]; Array.Copy(sa, saOff, ce, 0, ce.Length); ContinuationArea ca = Marshal.ByteArrayToStructureBigEndian <ContinuationArea>(ce); contareas.Add(ca); break; case SUSP_REFERENCE when saOff + sa[saOff + 2] <= saLen: byte[] er = new byte[sa[saOff + 2]]; Array.Copy(sa, saOff, er, 0, er.Length); refareas.Add(er); break; } rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; ziso |= nextSignature == ZISO_MAGIC; amiga |= nextSignature == AMIGA_MAGIC; aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && sa[saOff + 3] == 1 && sa[saOff + 2] >= 9); saOff += sa[saOff + 2]; if (nextSignature == SUSP_TERMINATOR) { break; } } break; } if (noneFound) { break; } } } rootOff += record.length; if (record.length == 0) { break; } } foreach (ContinuationArea ca in contareas) { uint caLen = (ca.ca_length_be + ca.offset_be) / (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size); if ((ca.ca_length_be + ca.offset_be) % (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size) > 0) { caLen++; } byte[] caSectors = imagePlugin.ReadSectors(ca.block_be, caLen); byte[] caData = new byte[ca.ca_length_be]; Array.Copy(caSectors, ca.offset_be, caData, 0, ca.ca_length_be); int caOff = 0; while (caOff < ca.ca_length_be) { ushort nextSignature = BigEndianBitConverter.ToUInt16(caData, caOff); switch (nextSignature) { // Apple never said to include its extensions inside a continuation area, but just in case case APPLE_MAGIC: if (caData[caOff + 3] == 1 && caData[caOff + 2] == 7) { apple = true; } else { apple |= caData[caOff + 3] != 1; } break; case SUSP_REFERENCE when caOff + caData[caOff + 2] <= ca.ca_length_be: byte[] er = new byte[caData[caOff + 2]]; Array.Copy(caData, caOff, er, 0, er.Length); refareas.Add(er); break; } rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; ziso |= nextSignature == ZISO_MAGIC; amiga |= nextSignature == AMIGA_MAGIC; aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && caData[caOff + 3] == 1 && caData[caOff + 2] >= 9); caOff += caData[caOff + 2]; } } if (refareas.Count > 0) { suspInformation.AppendLine("----------------------------------------"); suspInformation.AppendLine("SYSTEM USE SHARING PROTOCOL INFORMATION:"); suspInformation.AppendLine("----------------------------------------"); counter = 1; foreach (byte[] erb in refareas) { ReferenceArea er = Marshal.ByteArrayToStructureBigEndian <ReferenceArea>(erb); string extId = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>(), er.id_len); string extDes = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len, er.des_len); string extSrc = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len + er.des_len, er.src_len); suspInformation.AppendFormat("Extension: {0}", counter).AppendLine(); suspInformation.AppendFormat("\tID: {0}, version {1}", extId, er.ext_ver).AppendLine(); suspInformation.AppendFormat("\tDescription: {0}", extDes).AppendLine(); suspInformation.AppendFormat("\tSource: {0}", extSrc).AppendLine(); counter++; } } byte[] ipbinSector = imagePlugin.ReadSector(0 + partition.Start); CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); Dreamcast.IPBin?dreamcast = Dreamcast.DecodeIPBin(ipbinSector); string fsFormat; if (highSierraInfo) { fsFormat = "High Sierra Format"; } else if (cdiInfo) { fsFormat = "CD-i"; } else { fsFormat = "ISO9660"; } isoMetadata.AppendFormat("{0} file system", fsFormat).AppendLine(); if (xaExtensions) { isoMetadata.AppendLine("CD-ROM XA extensions present."); } if (amiga) { isoMetadata.AppendLine("Amiga extensions present."); } if (apple) { isoMetadata.AppendLine("Apple extensions present."); } if (jolietvd != null) { isoMetadata.AppendLine("Joliet extensions present."); } if (susp) { isoMetadata.AppendLine("System Use Sharing Protocol present."); } if (rrip) { isoMetadata.AppendLine("Rock Ridge Interchange Protocol present."); } if (aaip) { isoMetadata.AppendLine("Arbitrary Attribute Interchange Protocol present."); } if (ziso) { isoMetadata.AppendLine("zisofs compression present."); } if (evd) { isoMetadata.AppendLine("Contains Enhanved Volume Descriptor."); } if (vpd) { isoMetadata.AppendLine("Contains Volume Partition Descriptor."); } if (bvd != null) { isoMetadata.AppendFormat("Disc bootable following {0} specifications.", bootSpec).AppendLine(); } if (segaCd != null) { isoMetadata.AppendLine("This is a SegaCD / MegaCD disc."); isoMetadata.AppendLine(CD.Prettify(segaCd)); } if (saturn != null) { isoMetadata.AppendLine("This is a Sega Saturn disc."); isoMetadata.AppendLine(Saturn.Prettify(saturn)); } if (dreamcast != null) { isoMetadata.AppendLine("This is a Sega Dreamcast disc."); isoMetadata.AppendLine(Dreamcast.Prettify(dreamcast)); } isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : ""). AppendLine(); isoMetadata.AppendFormat("{0}VOLUME DESCRIPTOR INFORMATION:", cdiInfo ? "FILE STRUCTURE " : ""). AppendLine(); isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : ""). AppendLine(); isoMetadata.AppendFormat("System identifier: {0}", decodedVd.SystemIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume identifier: {0}", decodedVd.VolumeIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume set identifier: {0}", decodedVd.VolumeSetIdentifier).AppendLine(); isoMetadata.AppendFormat("Publisher identifier: {0}", decodedVd.PublisherIdentifier).AppendLine(); isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedVd.DataPreparerIdentifier).AppendLine(); isoMetadata.AppendFormat("Application identifier: {0}", decodedVd.ApplicationIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume creation date: {0}", decodedVd.CreationTime).AppendLine(); if (decodedVd.HasModificationTime) { isoMetadata.AppendFormat("Volume modification date: {0}", decodedVd.ModificationTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume has not been modified.").AppendLine(); } if (decodedVd.HasExpirationTime) { isoMetadata.AppendFormat("Volume expiration date: {0}", decodedVd.ExpirationTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume does not expire.").AppendLine(); } if (decodedVd.HasEffectiveTime) { isoMetadata.AppendFormat("Volume effective date: {0}", decodedVd.EffectiveTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume has always been effective.").AppendLine(); } isoMetadata.AppendFormat("Volume has {0} blocks of {1} bytes each", decodedVd.Blocks, decodedVd.BlockSize). AppendLine(); if (jolietvd != null) { isoMetadata.AppendLine("-------------------------------------"); isoMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:"); isoMetadata.AppendLine("-------------------------------------"); isoMetadata.AppendFormat("System identifier: {0}", decodedJolietVd.SystemIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVd.VolumeIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVd.VolumeSetIdentifier). AppendLine(); isoMetadata.AppendFormat("Publisher identifier: {0}", decodedJolietVd.PublisherIdentifier).AppendLine(); isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedJolietVd.DataPreparerIdentifier). AppendLine(); isoMetadata.AppendFormat("Application identifier: {0}", decodedJolietVd.ApplicationIdentifier). AppendLine(); isoMetadata.AppendFormat("Volume creation date: {0}", decodedJolietVd.CreationTime).AppendLine(); if (decodedJolietVd.HasModificationTime) { isoMetadata.AppendFormat("Volume modification date: {0}", decodedJolietVd.ModificationTime). AppendLine(); } else { isoMetadata.AppendFormat("Volume has not been modified.").AppendLine(); } if (decodedJolietVd.HasExpirationTime) { isoMetadata.AppendFormat("Volume expiration date: {0}", decodedJolietVd.ExpirationTime). AppendLine(); } else { isoMetadata.AppendFormat("Volume does not expire.").AppendLine(); } if (decodedJolietVd.HasEffectiveTime) { isoMetadata.AppendFormat("Volume effective date: {0}", decodedJolietVd.EffectiveTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume has always been effective.").AppendLine(); } } if (torito != null) { vdSector = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start); int toritoOff = 0; if (vdSector[toritoOff] != 1) { goto exit_torito; } ElToritoValidationEntry valentry = Marshal.ByteArrayToStructureLittleEndian <ElToritoValidationEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); if (valentry.signature != EL_TORITO_MAGIC) { goto exit_torito; } toritoOff = EL_TORITO_ENTRY_SIZE; ElToritoInitialEntry initialEntry = Marshal.ByteArrayToStructureLittleEndian <ElToritoInitialEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); initialEntry.boot_type = (ElToritoEmulation)((byte)initialEntry.boot_type & 0xF); AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.load_rba = {0}", initialEntry.load_rba); AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.sector_count = {0}", initialEntry.sector_count); byte[] bootImage = (initialEntry.load_rba + partition.Start + initialEntry.sector_count) - 1 <= partition.End ? imagePlugin.ReadSectors(initialEntry.load_rba + partition.Start, initialEntry.sector_count) : null; isoMetadata.AppendLine("----------------------"); isoMetadata.AppendLine("EL TORITO INFORMATION:"); isoMetadata.AppendLine("----------------------"); isoMetadata.AppendLine("Initial entry:"); isoMetadata.AppendFormat("\tDeveloper ID: {0}", Encoding.GetString(valentry.developer_id)).AppendLine(); if (initialEntry.bootable == ElToritoIndicator.Bootable) { isoMetadata.AppendFormat("\tBootable on {0}", valentry.platform_id).AppendLine(); isoMetadata.AppendFormat("\tBootable image starts at sector {0} and runs for {1} sectors", initialEntry.load_rba, initialEntry.sector_count).AppendLine(); if (valentry.platform_id == ElToritoPlatform.x86) { isoMetadata.AppendFormat("\tBootable image will be loaded at segment {0:X4}h", initialEntry.load_seg == 0 ? 0x7C0 : initialEntry.load_seg). AppendLine(); } else { isoMetadata.AppendFormat("\tBootable image will be loaded at 0x{0:X8}", (uint)initialEntry.load_seg * 10).AppendLine(); } switch (initialEntry.boot_type) { case ElToritoEmulation.None: isoMetadata.AppendLine("\tImage uses no emulation"); break; case ElToritoEmulation.Md2Hd: isoMetadata.AppendLine("\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); break; case ElToritoEmulation.Mf2Hd: isoMetadata.AppendLine("\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); break; case ElToritoEmulation.Mf2Ed: isoMetadata.AppendLine("\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); break; default: isoMetadata.AppendFormat("\tImage uses unknown emulation type {0}", (byte)initialEntry.boot_type).AppendLine(); break; } isoMetadata.AppendFormat("\tSystem type: 0x{0:X2}", initialEntry.system_type).AppendLine(); if (bootImage != null) { isoMetadata.AppendFormat("\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)). AppendLine(); } } else { isoMetadata.AppendLine("\tNot bootable"); } toritoOff += EL_TORITO_ENTRY_SIZE; const int sectionCounter = 2; while (toritoOff < vdSector.Length && (vdSector[toritoOff] == (byte)ElToritoIndicator.Header || vdSector[toritoOff] == (byte)ElToritoIndicator.LastHeader)) { ElToritoSectionHeaderEntry sectionHeader = Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionHeaderEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; isoMetadata.AppendFormat("Boot section {0}:", sectionCounter); isoMetadata.AppendFormat("\tSection ID: {0}", Encoding.GetString(sectionHeader.identifier)). AppendLine(); for (int entryCounter = 1; entryCounter <= sectionHeader.entries && toritoOff < vdSector.Length; entryCounter++) { ElToritoSectionEntry sectionEntry = Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; isoMetadata.AppendFormat("\tEntry {0}:", entryCounter); if (sectionEntry.bootable == ElToritoIndicator.Bootable) { bootImage = (sectionEntry.load_rba + partition.Start + sectionEntry.sector_count) - 1 <= partition.End ? imagePlugin.ReadSectors(sectionEntry.load_rba + partition.Start, sectionEntry.sector_count) : null; isoMetadata.AppendFormat("\t\tBootable on {0}", sectionHeader.platform_id).AppendLine(); isoMetadata.AppendFormat("\t\tBootable image starts at sector {0} and runs for {1} sectors", sectionEntry.load_rba, sectionEntry.sector_count).AppendLine(); if (valentry.platform_id == ElToritoPlatform.x86) { isoMetadata.AppendFormat("\t\tBootable image will be loaded at segment {0:X4}h", sectionEntry.load_seg == 0 ? 0x7C0 : sectionEntry.load_seg). AppendLine(); } else { isoMetadata.AppendFormat("\t\tBootable image will be loaded at 0x{0:X8}", (uint)sectionEntry.load_seg * 10).AppendLine(); } switch ((ElToritoEmulation)((byte)sectionEntry.boot_type & 0xF)) { case ElToritoEmulation.None: isoMetadata.AppendLine("\t\tImage uses no emulation"); break; case ElToritoEmulation.Md2Hd: isoMetadata. AppendLine("\t\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); break; case ElToritoEmulation.Mf2Hd: isoMetadata. AppendLine("\t\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); break; case ElToritoEmulation.Mf2Ed: isoMetadata. AppendLine("\t\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); break; default: isoMetadata.AppendFormat("\t\tImage uses unknown emulation type {0}", (byte)initialEntry.boot_type).AppendLine(); break; } isoMetadata.AppendFormat("\t\tSelection criteria type: {0}", sectionEntry.selection_criteria_type).AppendLine(); isoMetadata.AppendFormat("\t\tSystem type: 0x{0:X2}", sectionEntry.system_type). AppendLine(); if (bootImage != null) { isoMetadata.AppendFormat("\t\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)).AppendLine(); } } else { isoMetadata.AppendLine("\t\tNot bootable"); } var flags = (ElToritoFlags)((byte)sectionEntry.boot_type & 0xF0); if (flags.HasFlag(ElToritoFlags.ATAPI)) { isoMetadata.AppendLine("\t\tImage contains ATAPI drivers"); } if (flags.HasFlag(ElToritoFlags.SCSI)) { isoMetadata.AppendLine("\t\tImage contains SCSI drivers"); } if (!flags.HasFlag(ElToritoFlags.Continued)) { continue; } while (toritoOff < vdSector.Length) { ElToritoSectionEntryExtension sectionExtension = Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntryExtension>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; if (!sectionExtension.extension_flags.HasFlag(ElToritoFlags.Continued)) { break; } } } if (sectionHeader.header_id == ElToritoIndicator.LastHeader) { break; } } } exit_torito: if (refareas.Count > 0) { isoMetadata.Append(suspInformation); } XmlFsType.Type = fsFormat; if (jolietvd != null) { XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; if (string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; } else { XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null : decodedJolietVd.SystemIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) { XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; } else { XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null : decodedJolietVd.VolumeSetIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) { XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; } else { XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null : decodedJolietVd.PublisherIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) { XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; } else { XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) ? null : decodedJolietVd.DataPreparerIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) { XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; } else { XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null : decodedJolietVd.ApplicationIdentifier; } XmlFsType.CreationDate = decodedJolietVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedJolietVd.HasModificationTime) { XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedJolietVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedJolietVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } } else { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; XmlFsType.VolumeName = decodedVd.VolumeIdentifier; XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; XmlFsType.CreationDate = decodedVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedVd.HasModificationTime) { XmlFsType.ModificationDate = decodedVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } } XmlFsType.Bootable |= bvd != null || segaCd != null || saturn != null || dreamcast != null; XmlFsType.Clusters = decodedVd.Blocks; XmlFsType.ClusterSize = decodedVd.BlockSize; information = isoMetadata.ToString(); }
public Errno ReadDir(string path, out List <string> contents) { contents = null; if (!_mounted) { return(Errno.AccessDenied); } if (string.IsNullOrWhiteSpace(path) || path == "/") { contents = _rootDirectory.Keys.ToList(); return(Errno.NoError); } string cutPath = path.StartsWith('/') ? path.Substring(1).ToLower(_cultureInfo) : path.ToLower(_cultureInfo); if (_directoryCache.TryGetValue(cutPath, out Dictionary <string, DirectoryEntry> currentDirectory)) { contents = currentDirectory.Keys.ToList(); return(Errno.NoError); } string[] pieces = cutPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); KeyValuePair <string, DirectoryEntry> entry = _rootDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[0]); if (string.IsNullOrEmpty(entry.Key)) { return(Errno.NoSuchFile); } if (!entry.Value.attributes.HasFlag(Attributes.Directory)) { return(Errno.NotDirectory); } string currentPath = pieces[0]; currentDirectory = _rootDirectory; for (int p = 0; p < pieces.Length; p++) { entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[p]); if (string.IsNullOrEmpty(entry.Key)) { return(Errno.NoSuchFile); } if (!entry.Value.attributes.HasFlag(Attributes.Directory)) { return(Errno.NotDirectory); } currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; uint currentCluster = entry.Value.firstCluster; if (_directoryCache.TryGetValue(currentPath, out currentDirectory)) { continue; } uint[] clusters = GetClusters(currentCluster); if (clusters is null) { return(Errno.InvalidArgument); } byte[] directoryBuffer = new byte[_bytesPerCluster * clusters.Length]; for (int i = 0; i < clusters.Length; i++) { byte[] buffer = _imagePlugin.ReadSectors(_firstClusterSector + ((clusters[i] - 1) * _sectorsPerCluster), _sectorsPerCluster); Array.Copy(buffer, 0, directoryBuffer, i * _bytesPerCluster, _bytesPerCluster); } currentDirectory = new Dictionary <string, DirectoryEntry>(); int pos = 0; while (pos < directoryBuffer.Length) { DirectoryEntry dirent = _littleEndian ? Marshal. ByteArrayToStructureLittleEndian <DirectoryEntry >(directoryBuffer, pos, Marshal.SizeOf <DirectoryEntry>()) : Marshal.ByteArrayToStructureBigEndian <DirectoryEntry>(directoryBuffer, pos, Marshal.SizeOf <DirectoryEntry>()); pos += Marshal.SizeOf <DirectoryEntry>(); if (dirent.filenameSize == UNUSED_DIRENTRY || dirent.filenameSize == FINISHED_DIRENTRY) { break; } if (dirent.filenameSize == DELETED_DIRENTRY || dirent.filenameSize > MAX_FILENAME) { continue; } string filename = Encoding.GetString(dirent.filename, 0, dirent.filenameSize); currentDirectory.Add(filename, dirent); } _directoryCache.Add(currentPath, currentDirectory); } contents = currentDirectory?.Keys.ToList(); 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); }
static BpbKind DetectBpbKind(byte[] bpbSector, IMediaImage imagePlugin, Partition partition, out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, out byte minBootNearJump, out bool andosOemCorrect, out bool bootable) { fakeBpb = new BiosParameterBlockEbpb(); minBootNearJump = 0; andosOemCorrect = false; bootable = false; humanBpb = Marshal.ByteArrayToStructureBigEndian <HumanParameterBlock>(bpbSector); atariBpb = Marshal.ByteArrayToStructureLittleEndian <AtariParameterBlock>(bpbSector); ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0; // Check clusters for Human68k are correct bool humanClustersCorrect = humanBpb.clusters == 0 ? humanBpb.big_clusters == expectedClusters : humanBpb.clusters == expectedClusters; // Check OEM for Human68k is correct bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 && bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 && bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 && bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 && bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 && bpbSector[17] >= 0x20; // Check correct branch for Human68k bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x1C && bpbSector[1] < 0xFE; AaruConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect); AaruConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect); AaruConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect); // If all Human68k checks are correct, it is a Human68k FAT16 bool useHumanBpb = humanClustersCorrect && humanOemCorrect && humanBranchCorrect && expectedClusters > 0; if (useHumanBpb) { AaruConsole.DebugWriteLine("FAT plugin", "Using Human68k BPB"); fakeBpb.jump = humanBpb.jump; fakeBpb.oem_name = humanBpb.oem_name; fakeBpb.bps = (ushort)imagePlugin.Info.SectorSize; fakeBpb.spc = (byte)(humanBpb.bpc / fakeBpb.bps); fakeBpb.fats_no = 2; fakeBpb.root_ent = humanBpb.root_ent; fakeBpb.media = humanBpb.media; fakeBpb.spfat = (ushort)(humanBpb.cpfat * fakeBpb.spc); fakeBpb.boot_code = humanBpb.boot_code; fakeBpb.sectors = humanBpb.clusters; fakeBpb.big_sectors = humanBpb.big_clusters; fakeBpb.rsectors = 1; return(BpbKind.Human); } var msxBpb = new MsxParameterBlock(); var dos2Bpb = new BiosParameterBlock2(); var dos30Bpb = new BiosParameterBlock30(); var dos32Bpb = new BiosParameterBlock32(); var dos33Bpb = new BiosParameterBlock33(); var shortEbpb = new BiosParameterBlockShortEbpb(); var ebpb = new BiosParameterBlockEbpb(); var apricotBpb = new ApricotLabel(); bool useAtariBpb = false; bool useMsxBpb = false; bool useDos2Bpb = false; bool useDos3Bpb = false; bool useDos32Bpb = false; bool useDos33Bpb = false; bool userShortExtendedBpb = false; bool useExtendedBpb = false; bool useShortFat32 = false; bool useLongFat32 = false; bool useApricotBpb = false; bool useDecRainbowBpb = false; if (imagePlugin.Info.SectorSize >= 256) { msxBpb = Marshal.ByteArrayToStructureLittleEndian <MsxParameterBlock>(bpbSector); dos2Bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock2>(bpbSector); dos30Bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock30>(bpbSector); dos32Bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock32>(bpbSector); dos33Bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock33>(bpbSector); shortEbpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlockShortEbpb>(bpbSector); ebpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlockEbpb>(bpbSector); Fat32ParameterBlockShort shortFat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlockShort>(bpbSector); Fat32ParameterBlock fat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlock>(bpbSector); apricotBpb = Marshal.ByteArrayToStructureLittleEndian <ApricotLabel>(bpbSector); int bitsInBpsMsx = CountBits.Count(msxBpb.bps); int bitsInBpsDos33 = CountBits.Count(dos33Bpb.bps); int bitsInBpsDos40 = CountBits.Count(ebpb.bps); int bitsInBpsFat32Short = CountBits.Count(shortFat32Bpb.bps); int bitsInBpsFat32 = CountBits.Count(fat32Bpb.bps); int bitsInBpsApricot = CountBits.Count(apricotBpb.mainBPB.bps); bool correctSpcMsx = msxBpb.spc == 1 || msxBpb.spc == 2 || msxBpb.spc == 4 || msxBpb.spc == 8 || msxBpb.spc == 16 || msxBpb.spc == 32 || msxBpb.spc == 64; bool correctSpcDos33 = dos33Bpb.spc == 1 || dos33Bpb.spc == 2 || dos33Bpb.spc == 4 || dos33Bpb.spc == 8 || dos33Bpb.spc == 16 || dos33Bpb.spc == 32 || dos33Bpb.spc == 64; bool correctSpcDos40 = ebpb.spc == 1 || ebpb.spc == 2 || ebpb.spc == 4 || ebpb.spc == 8 || ebpb.spc == 16 || ebpb.spc == 32 || ebpb.spc == 64; bool correctSpcFat32Short = shortFat32Bpb.spc == 1 || shortFat32Bpb.spc == 2 || shortFat32Bpb.spc == 4 || shortFat32Bpb.spc == 8 || shortFat32Bpb.spc == 16 || shortFat32Bpb.spc == 32 || shortFat32Bpb.spc == 64; bool correctSpcFat32 = fat32Bpb.spc == 1 || fat32Bpb.spc == 2 || fat32Bpb.spc == 4 || fat32Bpb.spc == 8 || fat32Bpb.spc == 16 || fat32Bpb.spc == 32 || fat32Bpb.spc == 64; bool correctSpcApricot = apricotBpb.mainBPB.spc == 1 || apricotBpb.mainBPB.spc == 2 || apricotBpb.mainBPB.spc == 4 || apricotBpb.mainBPB.spc == 8 || apricotBpb.mainBPB.spc == 16 || apricotBpb.mainBPB.spc == 32 || apricotBpb.mainBPB.spc == 64; // This is to support FAT partitions on hybrid ISO/USB images if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { atariBpb.sectors /= 4; msxBpb.sectors /= 4; dos2Bpb.sectors /= 4; dos30Bpb.sectors /= 4; dos32Bpb.sectors /= 4; dos33Bpb.sectors /= 4; dos33Bpb.big_sectors /= 4; shortEbpb.sectors /= 4; shortEbpb.big_sectors /= 4; ebpb.sectors /= 4; ebpb.big_sectors /= 4; shortFat32Bpb.sectors /= 4; shortFat32Bpb.big_sectors /= 4; shortFat32Bpb.huge_sectors /= 4; fat32Bpb.sectors /= 4; fat32Bpb.big_sectors /= 4; apricotBpb.mainBPB.sectors /= 4; } andosOemCorrect = dos33Bpb.oem_name[0] < 0x20 && dos33Bpb.oem_name[1] >= 0x20 && dos33Bpb.oem_name[2] >= 0x20 && dos33Bpb.oem_name[3] >= 0x20 && dos33Bpb.oem_name[4] >= 0x20 && dos33Bpb.oem_name[5] >= 0x20 && dos33Bpb.oem_name[6] >= 0x20 && dos33Bpb.oem_name[7] >= 0x20; if (bitsInBpsFat32 == 1 && correctSpcFat32 && fat32Bpb.fats_no <= 2 && fat32Bpb.sectors == 0 && fat32Bpb.spfat == 0 && fat32Bpb.signature == 0x29 && Encoding.ASCII.GetString(fat32Bpb.fs_type) == "FAT32 ") { AaruConsole.DebugWriteLine("FAT plugin", "Using FAT32 BPB"); minBootNearJump = 0x58; return(BpbKind.LongFat32); } if (bitsInBpsFat32Short == 1 && correctSpcFat32Short && shortFat32Bpb.fats_no <= 2 && shortFat32Bpb.sectors == 0 && shortFat32Bpb.spfat == 0 && shortFat32Bpb.signature == 0x28) { AaruConsole.DebugWriteLine("FAT plugin", "Using short FAT32 BPB"); minBootNearJump = 0x57; return(BpbKind.ShortFat32); } if (bitsInBpsMsx == 1 && correctSpcMsx && msxBpb.fats_no <= 2 && msxBpb.root_ent > 0 && msxBpb.sectors <= (partition.End - partition.Start) + 1 && msxBpb.spfat > 0 && Encoding.ASCII.GetString(msxBpb.vol_id) == "VOL_ID") { AaruConsole.DebugWriteLine("FAT plugin", "Using MSX BPB"); useMsxBpb = true; } else if (bitsInBpsApricot == 1 && correctSpcApricot && apricotBpb.mainBPB.fats_no <= 2 && apricotBpb.mainBPB.root_ent > 0 && apricotBpb.mainBPB.sectors <= (partition.End - partition.Start) + 1 && apricotBpb.mainBPB.spfat > 0 && apricotBpb.partitionCount == 0) { AaruConsole.DebugWriteLine("FAT plugin", "Using Apricot BPB"); useApricotBpb = true; } else if (bitsInBpsDos40 == 1 && correctSpcDos40 && ebpb.fats_no <= 2 && ebpb.root_ent > 0 && ebpb.spfat > 0 && (ebpb.signature == 0x28 || ebpb.signature == 0x29 || andosOemCorrect)) { if (ebpb.sectors == 0) { if (ebpb.big_sectors <= (partition.End - partition.Start) + 1) { if (ebpb.signature == 0x29 || andosOemCorrect) { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); useExtendedBpb = true; minBootNearJump = 0x3C; } else { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); userShortExtendedBpb = true; minBootNearJump = 0x29; } } } else if (ebpb.sectors <= (partition.End - partition.Start) + 1) { if (ebpb.signature == 0x29 || andosOemCorrect) { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); useExtendedBpb = true; minBootNearJump = 0x3C; } else { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); userShortExtendedBpb = true; minBootNearJump = 0x29; } } } else if (bitsInBpsDos33 == 1 && correctSpcDos33 && dos33Bpb.rsectors < partition.End - partition.Start && dos33Bpb.fats_no <= 2 && dos33Bpb.root_ent > 0 && dos33Bpb.spfat > 0) { if (dos33Bpb.sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.big_sectors > 0 && dos33Bpb.big_sectors <= (partition.End - partition.Start) + 1) { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); useDos33Bpb = true; minBootNearJump = 0x22; } else if (dos33Bpb.big_sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.sectors > 0 && dos33Bpb.sectors <= (partition.End - partition.Start) + 1) { if (atariBpb.jump[0] == 0x60 || (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ")) { AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); useAtariBpb = true; } else { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); useDos33Bpb = true; minBootNearJump = 0x22; } } else { if (dos32Bpb.hsectors <= partition.Start && dos32Bpb.hsectors + dos32Bpb.sectors == dos32Bpb.total_sectors) { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.2 BPB"); useDos32Bpb = true; minBootNearJump = 0x1E; } else if (dos30Bpb.sptrk > 0 && dos30Bpb.sptrk < 64 && dos30Bpb.heads > 0 && dos30Bpb.heads < 256) { if (atariBpb.jump[0] == 0x60 || (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ")) { AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); useAtariBpb = true; } else { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.0 BPB"); useDos3Bpb = true; minBootNearJump = 0x1C; } } else { if (atariBpb.jump[0] == 0x60 || (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ")) { AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); useAtariBpb = true; } else { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 2.0 BPB"); useDos2Bpb = true; minBootNearJump = 0x16; } } } } } // DEC Rainbow, lacks a BPB but has a very concrete structure... if (imagePlugin.Info.Sectors == 800 && imagePlugin.Info.SectorSize == 512 && !useAtariBpb && !useMsxBpb && !useDos2Bpb && !useDos3Bpb && !useDos32Bpb && !useDos33Bpb && !userShortExtendedBpb && !useExtendedBpb && !useShortFat32 && !useLongFat32 && !useApricotBpb) { // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) byte z80Di = bpbSector[0]; // First FAT1 sector resides at LBA 0x14 byte[] fat1Sector0 = imagePlugin.ReadSector(0x14); // First FAT2 sector resides at LBA 0x1A byte[] fat2Sector0 = imagePlugin.ReadSector(0x1A); bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; // Volume is software interleaved 2:1 var rootMs = new MemoryStream(); foreach (byte[] tmp in from ulong rootSector in new[] { 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 } select imagePlugin.ReadSector(rootSector)) { rootMs.Write(tmp, 0, tmp.Length); } byte[] rootDir = rootMs.ToArray(); bool validRootDir = true; // Iterate all root directory for (int e = 0; e < 96 * 32; e += 32) { for (int c = 0; c < 11; c++) { if ((rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05) || rootDir[c + e] == 0xFF || rootDir[c + e] == 0x2E) { validRootDir = false; break; } } if (!validRootDir) { break; } } if (z80Di == 0xF3 && equalFatIds && (fat1Sector0[0] & 0xF0) == 0xF0 && fat1Sector0[1] == 0xFF && validRootDir) { useDecRainbowBpb = true; AaruConsole.DebugWriteLine("FAT plugin", "Using DEC Rainbow hardcoded BPB."); fakeBpb.bps = 512; fakeBpb.spc = 1; fakeBpb.rsectors = 20; fakeBpb.fats_no = 2; fakeBpb.root_ent = 96; fakeBpb.sectors = 800; fakeBpb.media = 0xFA; fakeBpb.sptrk = 10; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 3; bootable = true; fakeBpb.boot_code = bpbSector; return(BpbKind.DecRainbow); } } if (!useAtariBpb && !useMsxBpb && !useDos2Bpb && !useDos3Bpb && !useDos32Bpb && !useDos33Bpb && !useHumanBpb && !userShortExtendedBpb && !useExtendedBpb && !useShortFat32 && !useLongFat32 && !useApricotBpb && !useDecRainbowBpb) { byte[] fatSector = imagePlugin.ReadSector(1 + partition.Start); switch (fatSector[0]) { case 0xE5: if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 4; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 64; fakeBpb.sectors = 2002; fakeBpb.media = 0xE5; fakeBpb.sptrk = 26; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 1; } break; case 0xFD: if (imagePlugin.Info.Sectors == 4004 && imagePlugin.Info.SectorSize == 128) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 4; fakeBpb.rsectors = 4; fakeBpb.fats_no = 2; fakeBpb.root_ent = 68; fakeBpb.sectors = 4004; fakeBpb.media = 0xFD; fakeBpb.sptrk = 26; fakeBpb.heads = 2; fakeBpb.hsectors = 0; fakeBpb.spfat = 6; } else if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 4; fakeBpb.rsectors = 4; fakeBpb.fats_no = 2; fakeBpb.root_ent = 68; fakeBpb.sectors = 2002; fakeBpb.media = 0xFD; fakeBpb.sptrk = 26; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 6; } break; case 0xFE: if (imagePlugin.Info.Sectors == 320 && imagePlugin.Info.SectorSize == 512) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" SSDD."); fakeBpb.bps = 512; fakeBpb.spc = 1; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 64; fakeBpb.sectors = 320; fakeBpb.media = 0xFE; fakeBpb.sptrk = 8; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 1; } else if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 4; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 68; fakeBpb.sectors = 2002; fakeBpb.media = 0xFE; fakeBpb.sptrk = 26; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 6; } else if (imagePlugin.Info.Sectors == 1232 && imagePlugin.Info.SectorSize == 1024) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 1024; fakeBpb.spc = 1; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 192; fakeBpb.sectors = 1232; fakeBpb.media = 0xFE; fakeBpb.sptrk = 8; fakeBpb.heads = 2; fakeBpb.hsectors = 0; fakeBpb.spfat = 2; } else if (imagePlugin.Info.Sectors == 616 && imagePlugin.Info.SectorSize == 1024) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 1024; fakeBpb.spc = 1; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 6192; fakeBpb.sectors = 616; fakeBpb.media = 0xFE; fakeBpb.sptrk = 8; fakeBpb.heads = 2; fakeBpb.hsectors = 0; } else if (imagePlugin.Info.Sectors == 720 && imagePlugin.Info.SectorSize == 128) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 2; fakeBpb.rsectors = 54; fakeBpb.fats_no = 2; fakeBpb.root_ent = 64; fakeBpb.sectors = 720; fakeBpb.media = 0xFE; fakeBpb.sptrk = 18; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 4; } else if (imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); fakeBpb.bps = 512; fakeBpb.spc = 2; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 112; fakeBpb.sectors = 640; fakeBpb.media = 0xFF; fakeBpb.sptrk = 8; fakeBpb.heads = 2; fakeBpb.hsectors = 0; fakeBpb.spfat = 1; } break; case 0xFF: if (imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) { AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); fakeBpb.bps = 512; fakeBpb.spc = 2; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 112; fakeBpb.sectors = 640; fakeBpb.media = 0xFF; fakeBpb.sptrk = 8; fakeBpb.heads = 2; fakeBpb.hsectors = 0; fakeBpb.spfat = 1; } break; } // This assumes a bootable sector will jump somewhere or disable interrupts in x86 code bootable |= bpbSector[0] == 0xFA || (bpbSector[0] == 0xEB && bpbSector[1] <= 0x7F) || (bpbSector[0] == 0xE9 && BitConverter.ToUInt16(bpbSector, 1) <= 0x1FC); fakeBpb.boot_code = bpbSector; return(BpbKind.Hardcoded); } if (useExtendedBpb) { fakeBpb = ebpb; return(BpbKind.Extended); } if (userShortExtendedBpb) { fakeBpb.jump = shortEbpb.jump; fakeBpb.oem_name = shortEbpb.oem_name; fakeBpb.bps = shortEbpb.bps; fakeBpb.spc = shortEbpb.spc; fakeBpb.rsectors = shortEbpb.rsectors; fakeBpb.fats_no = shortEbpb.fats_no; fakeBpb.root_ent = shortEbpb.root_ent; fakeBpb.sectors = shortEbpb.sectors; fakeBpb.media = shortEbpb.media; fakeBpb.spfat = shortEbpb.spfat; fakeBpb.sptrk = shortEbpb.sptrk; fakeBpb.heads = shortEbpb.heads; fakeBpb.hsectors = shortEbpb.hsectors; fakeBpb.big_sectors = shortEbpb.big_sectors; fakeBpb.drive_no = shortEbpb.drive_no; fakeBpb.flags = shortEbpb.flags; fakeBpb.signature = shortEbpb.signature; fakeBpb.serial_no = shortEbpb.serial_no; fakeBpb.boot_code = shortEbpb.boot_code; fakeBpb.boot_signature = shortEbpb.boot_signature; return(BpbKind.ShortExtended); } if (useDos33Bpb) { fakeBpb.jump = dos33Bpb.jump; fakeBpb.oem_name = dos33Bpb.oem_name; fakeBpb.bps = dos33Bpb.bps; fakeBpb.spc = dos33Bpb.spc; fakeBpb.rsectors = dos33Bpb.rsectors; fakeBpb.fats_no = dos33Bpb.fats_no; fakeBpb.root_ent = dos33Bpb.root_ent; fakeBpb.sectors = dos33Bpb.sectors; fakeBpb.media = dos33Bpb.media; fakeBpb.spfat = dos33Bpb.spfat; fakeBpb.sptrk = dos33Bpb.sptrk; fakeBpb.heads = dos33Bpb.heads; fakeBpb.hsectors = dos33Bpb.hsectors; fakeBpb.big_sectors = dos33Bpb.big_sectors; fakeBpb.boot_code = dos33Bpb.boot_code; fakeBpb.boot_signature = dos33Bpb.boot_signature; return(BpbKind.Dos33); } if (useDos32Bpb) { fakeBpb.jump = dos32Bpb.jump; fakeBpb.oem_name = dos32Bpb.oem_name; fakeBpb.bps = dos32Bpb.bps; fakeBpb.spc = dos32Bpb.spc; fakeBpb.rsectors = dos32Bpb.rsectors; fakeBpb.fats_no = dos32Bpb.fats_no; fakeBpb.root_ent = dos32Bpb.root_ent; fakeBpb.sectors = dos32Bpb.sectors; fakeBpb.media = dos32Bpb.media; fakeBpb.spfat = dos32Bpb.spfat; fakeBpb.sptrk = dos32Bpb.sptrk; fakeBpb.heads = dos32Bpb.heads; fakeBpb.hsectors = dos32Bpb.hsectors; fakeBpb.boot_code = dos32Bpb.boot_code; fakeBpb.boot_signature = dos32Bpb.boot_signature; return(BpbKind.Dos32); } if (useDos3Bpb) { fakeBpb.jump = dos30Bpb.jump; fakeBpb.oem_name = dos30Bpb.oem_name; fakeBpb.bps = dos30Bpb.bps; fakeBpb.spc = dos30Bpb.spc; fakeBpb.rsectors = dos30Bpb.rsectors; fakeBpb.fats_no = dos30Bpb.fats_no; fakeBpb.root_ent = dos30Bpb.root_ent; fakeBpb.sectors = dos30Bpb.sectors; fakeBpb.media = dos30Bpb.media; fakeBpb.spfat = dos30Bpb.spfat; fakeBpb.sptrk = dos30Bpb.sptrk; fakeBpb.heads = dos30Bpb.heads; fakeBpb.hsectors = dos30Bpb.hsectors; fakeBpb.boot_code = dos30Bpb.boot_code; fakeBpb.boot_signature = dos30Bpb.boot_signature; return(BpbKind.Dos3); } if (useDos2Bpb) { fakeBpb.jump = dos2Bpb.jump; fakeBpb.oem_name = dos2Bpb.oem_name; fakeBpb.bps = dos2Bpb.bps; fakeBpb.spc = dos2Bpb.spc; fakeBpb.rsectors = dos2Bpb.rsectors; fakeBpb.fats_no = dos2Bpb.fats_no; fakeBpb.root_ent = dos2Bpb.root_ent; fakeBpb.sectors = dos2Bpb.sectors; fakeBpb.media = dos2Bpb.media; fakeBpb.spfat = dos2Bpb.spfat; fakeBpb.boot_code = dos2Bpb.boot_code; fakeBpb.boot_signature = dos2Bpb.boot_signature; return(BpbKind.Dos2); } if (useMsxBpb) { fakeBpb.jump = msxBpb.jump; fakeBpb.oem_name = msxBpb.oem_name; fakeBpb.bps = msxBpb.bps; fakeBpb.spc = msxBpb.spc; fakeBpb.rsectors = msxBpb.rsectors; fakeBpb.fats_no = msxBpb.fats_no; fakeBpb.root_ent = msxBpb.root_ent; fakeBpb.sectors = msxBpb.sectors; fakeBpb.media = msxBpb.media; fakeBpb.spfat = msxBpb.spfat; fakeBpb.sptrk = msxBpb.sptrk; fakeBpb.heads = msxBpb.heads; fakeBpb.hsectors = msxBpb.hsectors; fakeBpb.boot_code = msxBpb.boot_code; fakeBpb.boot_signature = msxBpb.boot_signature; fakeBpb.serial_no = msxBpb.serial_no; // TODO: Is there any way to check this? bootable = true; return(BpbKind.Msx); } if (useAtariBpb) { fakeBpb.jump = atariBpb.jump; fakeBpb.oem_name = atariBpb.oem_name; fakeBpb.bps = atariBpb.bps; fakeBpb.spc = atariBpb.spc; fakeBpb.rsectors = atariBpb.rsectors; fakeBpb.fats_no = atariBpb.fats_no; fakeBpb.root_ent = atariBpb.root_ent; fakeBpb.sectors = atariBpb.sectors; fakeBpb.media = atariBpb.media; fakeBpb.spfat = atariBpb.spfat; fakeBpb.sptrk = atariBpb.sptrk; fakeBpb.heads = atariBpb.heads; fakeBpb.boot_code = atariBpb.boot_code; return(BpbKind.Atari); } if (useApricotBpb) { fakeBpb.bps = apricotBpb.mainBPB.bps; fakeBpb.spc = apricotBpb.mainBPB.spc; fakeBpb.rsectors = apricotBpb.mainBPB.rsectors; fakeBpb.fats_no = apricotBpb.mainBPB.fats_no; fakeBpb.root_ent = apricotBpb.mainBPB.root_ent; fakeBpb.sectors = apricotBpb.mainBPB.sectors; fakeBpb.media = apricotBpb.mainBPB.media; fakeBpb.spfat = apricotBpb.mainBPB.spfat; fakeBpb.sptrk = apricotBpb.spt; bootable = apricotBpb.bootType > 0; if (apricotBpb.bootLocation > 0 && apricotBpb.bootLocation + apricotBpb.bootSize < imagePlugin.Info.Sectors) { fakeBpb.boot_code = imagePlugin.ReadSectors(apricotBpb.bootLocation, (uint)(apricotBpb.sectorSize * apricotBpb.bootSize) / imagePlugin.Info.SectorSize); } return(BpbKind.Apricot); } return(BpbKind.None); }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { // TODO: Find correct default encoding Encoding = Encoding.ASCII; if (options == null) { options = GetDefaultOptions(); } if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out debug); } byte[] sbSector = imagePlugin.ReadSector(0 + partition.Start); SuperBlock sb = Marshal.ByteArrayToStructureBigEndian <SuperBlock>(sbSector); if (sb.record_type != 1 || sb.record_version != 1) { return(Errno.InvalidArgument); } if (Encoding.ASCII.GetString(sb.sync_bytes) != SYNC) { return(Errno.InvalidArgument); } if (imagePlugin.Info.SectorSize == 2336 || imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448) { volumeBlockSizeRatio = sb.block_size / 2048; } else { volumeBlockSizeRatio = sb.block_size / imagePlugin.Info.SectorSize; } XmlFsType = new FileSystemType { Type = "Opera", VolumeName = StringHandlers.CToString(sb.volume_label, Encoding), ClusterSize = sb.block_size, Clusters = sb.block_count, Bootable = true, VolumeSerial = $"{sb.volume_id:X8}" }; statfs = new FileSystemInfo { Blocks = sb.block_count, FilenameLength = MAX_NAME, FreeBlocks = 0, Id = new FileSystemId { IsInt = true, Serial32 = sb.volume_id }, PluginId = Id, Type = "Opera" }; image = imagePlugin; int firstRootBlock = BigEndianBitConverter.ToInt32(sbSector, Marshal.SizeOf <SuperBlock>()); rootDirectoryCache = DecodeDirectory(firstRootBlock); directoryCache = new Dictionary <string, Dictionary <string, DirectoryEntryWithPointers> >(); mounted = true; return(Errno.NoError); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (2 + partition.Start >= partition.End) { return(false); } ushort bps; byte spc; byte numberOfFats; ushort reservedSecs; ushort rootEntries; ushort sectors; byte mediaDescriptor; ushort fatSectors; uint bigSectors; byte bpbSignature; byte fat32Signature; ulong hugeSectors; byte[] fat32Id = new byte[8]; byte[] msxId = new byte[6]; byte fatId; byte[] dosOem = new byte[8]; byte[] atariOem = new byte[6]; ushort bootable = 0; uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; byte[] bpbSector = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb); byte[] fatSector = imagePlugin.ReadSector(sectorsPerBpb + partition.Start); HumanParameterBlock humanBpb = Marshal.ByteArrayToStructureBigEndian <HumanParameterBlock>(bpbSector); ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0; AaruConsole.DebugWriteLine("FAT plugin", "Human bpc = {0}", humanBpb.bpc); AaruConsole.DebugWriteLine("FAT plugin", "Human clusters = {0}", humanBpb.clusters); AaruConsole.DebugWriteLine("FAT plugin", "Human big_clusters = {0}", humanBpb.big_clusters); AaruConsole.DebugWriteLine("FAT plugin", "Human expected clusters = {0}", expectedClusters); // Check clusters for Human68k are correct bool humanClustersCorrect = humanBpb.clusters == 0 ? humanBpb.big_clusters == expectedClusters : humanBpb.clusters == expectedClusters; // Check OEM for Human68k is correct bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 && bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 && bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 && bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 && bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 && bpbSector[17] >= 0x20; // Check correct branch for Human68k bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x20 && bpbSector[1] < 0xFE; AaruConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect); AaruConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect); AaruConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect); // If all Human68k checks are correct, it is a Human68k FAT16 if (humanClustersCorrect && humanOemCorrect && humanBranchCorrect && expectedClusters > 0) { return(true); } Array.Copy(bpbSector, 0x02, atariOem, 0, 6); Array.Copy(bpbSector, 0x03, dosOem, 0, 8); bps = BitConverter.ToUInt16(bpbSector, 0x00B); spc = bpbSector[0x00D]; reservedSecs = BitConverter.ToUInt16(bpbSector, 0x00E); numberOfFats = bpbSector[0x010]; rootEntries = BitConverter.ToUInt16(bpbSector, 0x011); sectors = BitConverter.ToUInt16(bpbSector, 0x013); mediaDescriptor = bpbSector[0x015]; fatSectors = BitConverter.ToUInt16(bpbSector, 0x016); Array.Copy(bpbSector, 0x052, msxId, 0, 6); bigSectors = BitConverter.ToUInt32(bpbSector, 0x020); bpbSignature = bpbSector[0x026]; fat32Signature = bpbSector[0x042]; Array.Copy(bpbSector, 0x052, fat32Id, 0, 8); hugeSectors = BitConverter.ToUInt64(bpbSector, 0x052); fatId = fatSector[0]; int bitsInBps = CountBits.Count(bps); if (imagePlugin.Info.SectorSize >= 512) { bootable = BitConverter.ToUInt16(bpbSector, 0x1FE); } bool correctSpc = spc == 1 || spc == 2 || spc == 4 || spc == 8 || spc == 16 || spc == 32 || spc == 64; string msxString = Encoding.ASCII.GetString(msxId); string fat32String = Encoding.ASCII.GetString(fat32Id); bool atariOemCorrect = atariOem[0] >= 0x20 && atariOem[1] >= 0x20 && atariOem[2] >= 0x20 && atariOem[3] >= 0x20 && atariOem[4] >= 0x20 && atariOem[5] >= 0x20; bool dosOemCorrect = dosOem[0] >= 0x20 && dosOem[1] >= 0x20 && dosOem[2] >= 0x20 && dosOem[3] >= 0x20 && dosOem[4] >= 0x20 && dosOem[5] >= 0x20 && dosOem[6] >= 0x20 && dosOem[7] >= 0x20; string atariString = Encoding.ASCII.GetString(atariOem); string oemString = Encoding.ASCII.GetString(dosOem); AaruConsole.DebugWriteLine("FAT plugin", "atari_oem_correct = {0}", atariOemCorrect); AaruConsole.DebugWriteLine("FAT plugin", "dos_oem_correct = {0}", dosOemCorrect); AaruConsole.DebugWriteLine("FAT plugin", "bps = {0}", bps); AaruConsole.DebugWriteLine("FAT plugin", "bits in bps = {0}", bitsInBps); AaruConsole.DebugWriteLine("FAT plugin", "spc = {0}", spc); AaruConsole.DebugWriteLine("FAT plugin", "correct_spc = {0}", correctSpc); AaruConsole.DebugWriteLine("FAT plugin", "reserved_secs = {0}", reservedSecs); AaruConsole.DebugWriteLine("FAT plugin", "fats_no = {0}", numberOfFats); AaruConsole.DebugWriteLine("FAT plugin", "root_entries = {0}", rootEntries); AaruConsole.DebugWriteLine("FAT plugin", "sectors = {0}", sectors); AaruConsole.DebugWriteLine("FAT plugin", "media_descriptor = 0x{0:X2}", mediaDescriptor); AaruConsole.DebugWriteLine("FAT plugin", "fat_sectors = {0}", fatSectors); AaruConsole.DebugWriteLine("FAT plugin", "msx_id = \"{0}\"", msxString); AaruConsole.DebugWriteLine("FAT plugin", "big_sectors = {0}", bigSectors); AaruConsole.DebugWriteLine("FAT plugin", "bpb_signature = 0x{0:X2}", bpbSignature); AaruConsole.DebugWriteLine("FAT plugin", "fat32_signature = 0x{0:X2}", fat32Signature); AaruConsole.DebugWriteLine("FAT plugin", "fat32_id = \"{0}\"", fat32String); AaruConsole.DebugWriteLine("FAT plugin", "huge_sectors = {0}", hugeSectors); AaruConsole.DebugWriteLine("FAT plugin", "fat_id = 0x{0:X2}", fatId); ushort apricotBps = BitConverter.ToUInt16(bpbSector, 0x50); byte apricotSpc = bpbSector[0x52]; ushort apricotReservedSecs = BitConverter.ToUInt16(bpbSector, 0x53); byte apricotFatsNo = bpbSector[0x55]; ushort apricotRootEntries = BitConverter.ToUInt16(bpbSector, 0x56); ushort apricotSectors = BitConverter.ToUInt16(bpbSector, 0x58); byte apricotMediaDescriptor = bpbSector[0x5A]; ushort apricotFatSectors = BitConverter.ToUInt16(bpbSector, 0x5B); bool apricotCorrectSpc = apricotSpc == 1 || apricotSpc == 2 || apricotSpc == 4 || apricotSpc == 8 || apricotSpc == 16 || apricotSpc == 32 || apricotSpc == 64; int bitsInApricotBps = CountBits.Count(apricotBps); byte apricotPartitions = bpbSector[0x0C]; AaruConsole.DebugWriteLine("FAT plugin", "apricot_bps = {0}", apricotBps); AaruConsole.DebugWriteLine("FAT plugin", "apricot_spc = {0}", apricotSpc); AaruConsole.DebugWriteLine("FAT plugin", "apricot_correct_spc = {0}", apricotCorrectSpc); AaruConsole.DebugWriteLine("FAT plugin", "apricot_reserved_secs = {0}", apricotReservedSecs); AaruConsole.DebugWriteLine("FAT plugin", "apricot_fats_no = {0}", apricotFatsNo); AaruConsole.DebugWriteLine("FAT plugin", "apricot_root_entries = {0}", apricotRootEntries); AaruConsole.DebugWriteLine("FAT plugin", "apricot_sectors = {0}", apricotSectors); AaruConsole.DebugWriteLine("FAT plugin", "apricot_media_descriptor = 0x{0:X2}", apricotMediaDescriptor); AaruConsole.DebugWriteLine("FAT plugin", "apricot_fat_sectors = {0}", apricotFatSectors); // This is to support FAT partitions on hybrid ISO/USB images if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { sectors /= 4; bigSectors /= 4; hugeSectors /= 4; } switch (oemString) { // exFAT case "EXFAT ": return(false); // NTFS case "NTFS " when bootable == 0xAA55 && numberOfFats == 0 && fatSectors == 0: return(false); // QNX4 case "FQNX4FS ": return(false); } // HPFS if (16 + partition.Start <= partition.End) { byte[] hpfsSbSector = imagePlugin.ReadSector(16 + partition.Start); // Seek to superblock, on logical sector 16 uint hpfsMagic1 = BitConverter.ToUInt32(hpfsSbSector, 0x000); uint hpfsMagic2 = BitConverter.ToUInt32(hpfsSbSector, 0x004); if (hpfsMagic1 == 0xF995E849 && hpfsMagic2 == 0xFA53E9C5) { return(false); } } switch (bitsInBps) { // FAT32 for sure case 1 when correctSpc && numberOfFats <= 2 && sectors == 0 && fatSectors == 0 && fat32Signature == 0x29 && fat32String == "FAT32 ": return(true); // short FAT32 case 1 when correctSpc && numberOfFats <= 2 && sectors == 0 && fatSectors == 0 && fat32Signature == 0x28: return(bigSectors == 0 ? hugeSectors <= (partition.End - partition.Start) + 1 : bigSectors <= (partition.End - partition.Start) + 1); // MSX-DOS FAT12 case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 && sectors <= (partition.End - partition.Start) + 1 && fatSectors > 0 && msxString == "VOL_ID": return(true); // EBPB case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 && fatSectors > 0 && (bpbSignature == 0x28 || bpbSignature == 0x29): return(sectors == 0 ? bigSectors <= (partition.End - partition.Start) + 1 : sectors <= (partition.End - partition.Start) + 1); // BPB case 1 when correctSpc && reservedSecs < partition.End - partition.Start && numberOfFats <= 2 && rootEntries > 0 && fatSectors > 0: return(sectors == 0 ? bigSectors <= (partition.End - partition.Start) + 1 : sectors <= (partition.End - partition.Start) + 1); } // Apricot BPB if (bitsInApricotBps == 1 && apricotCorrectSpc && apricotReservedSecs < partition.End - partition.Start && apricotFatsNo <= 2 && apricotRootEntries > 0 && apricotFatSectors > 0 && apricotSectors <= (partition.End - partition.Start) + 1 && apricotPartitions == 0) { return(true); } // All FAT12 without BPB can only be used on floppies, without partitions. if (partition.Start != 0) { return(false); } // DEC Rainbow, lacks a BPB but has a very concrete structure... if (imagePlugin.Info.Sectors == 800 && imagePlugin.Info.SectorSize == 512) { // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) byte z80Di = bpbSector[0]; // First FAT1 sector resides at LBA 0x14 byte[] fat1Sector0 = imagePlugin.ReadSector(0x14); // First FAT2 sector resides at LBA 0x1A byte[] fat2Sector0 = imagePlugin.ReadSector(0x1A); bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; // Volume is software interleaved 2:1 var rootMs = new MemoryStream(); foreach (byte[] tmp in from ulong rootSector in new ulong[] { 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 } select imagePlugin.ReadSector(rootSector)) { rootMs.Write(tmp, 0, tmp.Length); } byte[] rootDir = rootMs.ToArray(); bool validRootDir = true; // Iterate all root directory for (int e = 0; e < 96 * 32; e += 32) { for (int c = 0; c < 11; c++) { if ((rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05) || rootDir[c + e] == 0xFF || rootDir[c + e] == 0x2E) { validRootDir = false; break; } } if (!validRootDir) { break; } } if (z80Di == 0xF3 && equalFatIds && (fat1Sector0[0] & 0xF0) == 0xF0 && fat1Sector0[1] == 0xFF && validRootDir) { return(true); } } byte fat2 = fatSector[1]; byte fat3 = fatSector[2]; ushort fat2ndCluster = (ushort)(((fat2 << 8) + fat3) & 0xFFF); AaruConsole.DebugWriteLine("FAT plugin", "1st fat cluster 1 = {0:X3}", fat2ndCluster); if (fat2ndCluster < 0xFF0) { return(false); } ulong fat2SectorNo = 0; switch (fatId) { case 0xE5: if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) { fat2SectorNo = 2; } break; case 0xFD: if (imagePlugin.Info.Sectors == 4004 && imagePlugin.Info.SectorSize == 128) { fat2SectorNo = 7; } else if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) { fat2SectorNo = 7; } break; case 0xFE: if (imagePlugin.Info.Sectors == 320 && imagePlugin.Info.SectorSize == 512) { fat2SectorNo = 2; } else if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) { fat2SectorNo = 7; } else if (imagePlugin.Info.Sectors == 1232 && imagePlugin.Info.SectorSize == 1024) { fat2SectorNo = 3; } else if (imagePlugin.Info.Sectors == 616 && imagePlugin.Info.SectorSize == 1024) { fat2SectorNo = 2; } else if (imagePlugin.Info.Sectors == 720 && imagePlugin.Info.SectorSize == 128) { fat2SectorNo = 5; } else if (imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) { fat2SectorNo = 2; } break; case 0xFF: if (imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) { fat2SectorNo = 2; } break; default: if (fatId < 0xE8) { return(false); } fat2SectorNo = 2; break; } if (fat2SectorNo > partition.End || fat2SectorNo == 0) { return(false); } AaruConsole.DebugWriteLine("FAT plugin", "2nd fat starts at = {0}", fat2SectorNo); byte[] fat2Sector = imagePlugin.ReadSector(fat2SectorNo); fat2 = fat2Sector[1]; fat3 = fat2Sector[2]; fat2ndCluster = (ushort)(((fat2 << 8) + fat3) & 0xFFF); if (fat2ndCluster < 0xFF0) { return(false); } return(fatId == fat2Sector[0]); }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { Encoding = encoding ?? Encoding.GetEncoding(1252); byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" if (options == null) { options = GetDefaultOptions(); } if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out debug); } if (options.TryGetValue("use_path_table", out string usePathTableString)) { bool.TryParse(usePathTableString, out usePathTable); } if (options.TryGetValue("use_trans_tbl", out string useTransTblString)) { bool.TryParse(useTransTblString, out useTransTbl); } if (options.TryGetValue("use_evd", out string useEvdString)) { bool.TryParse(useEvdString, out useEvd); } // Default namespace if (@namespace is null) { @namespace = "joliet"; } switch (@namespace.ToLowerInvariant()) { case "normal": this.@namespace = Namespace.Normal; break; case "vms": this.@namespace = Namespace.Vms; break; case "joliet": this.@namespace = Namespace.Joliet; break; case "rrip": this.@namespace = Namespace.Rrip; break; case "romeo": this.@namespace = Namespace.Romeo; break; default: return(Errno.InvalidArgument); } PrimaryVolumeDescriptor?pvd = null; PrimaryVolumeDescriptor?jolietvd = null; BootRecord?bvd = null; HighSierraPrimaryVolumeDescriptor?hsvd = null; FileStructureVolumeDescriptor? fsvd = null; // ISO9660 is designed for 2048 bytes/sector devices if (imagePlugin.Info.SectorSize < 2048) { return(Errno.InvalidArgument); } // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. if (partition.End < 16) { return(Errno.InvalidArgument); } ulong counter = 0; byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start); int xaOff = vdSector.Length == 2336 ? 8 : 0; Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; int hsOff = 0; if (highSierra) { hsOff = 8; } cdi = false; List <ulong> bvdSectors = new List <ulong>(); List <ulong> pvdSectors = new List <ulong>(); List <ulong> svdSectors = new List <ulong>(); List <ulong> evdSectors = new List <ulong>(); List <ulong> vpdSectors = new List <ulong>(); while (true) { DicConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); // Seek to Volume Descriptor DicConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); byte[] vdSectorTmp = imagePlugin.ReadSector(16 + counter + partition.Start); vdSector = new byte[vdSectorTmp.Length - xaOff]; Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. DicConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); if (vdType == 255) // Supposedly we are in the PVD. { if (counter == 0) { return(Errno.InvalidArgument); } break; } Array.Copy(vdSector, 0x001, vdMagic, 0, 5); Array.Copy(vdSector, 0x009, hsMagic, 0, 5); if (Encoding.GetString(vdMagic) != ISO_MAGIC && Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && Encoding.GetString(vdMagic) != CDI_MAGIC ) // Recognized, it is an ISO9660, now check for rest of data. { if (counter == 0) { return(Errno.InvalidArgument); } break; } cdi |= Encoding.GetString(vdMagic) == CDI_MAGIC; switch (vdType) { case 0: { if (debug) { bvdSectors.Add(16 + counter + partition.Start); } break; } case 1: { if (highSierra) { hsvd = Marshal .ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector); } else if (cdi) { fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector); } else { pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); } if (debug) { pvdSectors.Add(16 + counter + partition.Start); } break; } case 2: { PrimaryVolumeDescriptor svd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); // TODO: Other escape sequences // Check if this is Joliet if (svd.version == 1) { if (svd.escape_sequences[0] == '%' && svd.escape_sequences[1] == '/') { if (svd.escape_sequences[2] == '@' || svd.escape_sequences[2] == 'C' || svd.escape_sequences[2] == 'E') { jolietvd = svd; } else { DicConsole.WriteLine("ISO9660 plugin", "Found unknown supplementary volume descriptor"); } } if (debug) { svdSectors.Add(16 + counter + partition.Start); } } else { if (debug) { evdSectors.Add(16 + counter + partition.Start); } if (useEvd) { // Basically until escape sequences are implemented, let the user chose the encoding. // This is the same as user chosing Romeo namespace, but using the EVD instead of the PVD this.@namespace = Namespace.Romeo; pvd = svd; } } break; } case 3: { if (debug) { vpdSectors.Add(16 + counter + partition.Start); } break; } } counter++; } DecodedVolumeDescriptor decodedVd; DecodedVolumeDescriptor decodedJolietVd = new DecodedVolumeDescriptor(); XmlFsType = new FileSystemType(); if (pvd == null && hsvd == null && fsvd == null) { DicConsole.ErrorWriteLine("ERROR: Could not find primary volume descriptor"); return(Errno.InvalidArgument); } if (highSierra) { decodedVd = DecodeVolumeDescriptor(hsvd.Value); } else if (cdi) { decodedVd = DecodeVolumeDescriptor(fsvd.Value); } else { decodedVd = DecodeVolumeDescriptor(pvd.Value); } if (jolietvd != null) { decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); } if (this.@namespace != Namespace.Romeo) { Encoding = Encoding.ASCII; } string fsFormat; byte[] pathTableData; uint pathTableSizeInSectors; uint pathTableMsbLocation; uint pathTableLsbLocation = 0; // Initialize to 0 as ignored in CD-i image = imagePlugin; if (highSierra) { pathTableSizeInSectors = hsvd.Value.path_table_size / 2048; if (hsvd.Value.path_table_size % 2048 > 0) { pathTableSizeInSectors++; } pathTableData = ReadSectors(Swapping.Swap(hsvd.Value.mandatory_path_table_msb), pathTableSizeInSectors); fsFormat = "High Sierra Format"; pathTableMsbLocation = hsvd.Value.mandatory_path_table_msb; pathTableLsbLocation = hsvd.Value.mandatory_path_table_lsb; } else if (cdi) { pathTableSizeInSectors = fsvd.Value.path_table_size / 2048; if (fsvd.Value.path_table_size % 2048 > 0) { pathTableSizeInSectors++; } pathTableData = ReadSectors(fsvd.Value.path_table_addr, pathTableSizeInSectors); fsFormat = "CD-i"; pathTableMsbLocation = fsvd.Value.path_table_addr; // TODO: Until escape sequences are implemented this is the default CD-i encoding. Encoding = Encoding.GetEncoding("iso8859-1"); } else { pathTableSizeInSectors = pvd.Value.path_table_size / 2048; if (pvd.Value.path_table_size % 2048 > 0) { pathTableSizeInSectors++; } pathTableData = ReadSectors(Swapping.Swap(pvd.Value.type_m_path_table), pathTableSizeInSectors); fsFormat = "ISO9660"; pathTableMsbLocation = pvd.Value.type_m_path_table; pathTableLsbLocation = pvd.Value.type_l_path_table; } pathTable = highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData); // High Sierra and CD-i do not support Joliet or RRIP if ((highSierra || cdi) && this.@namespace != Namespace.Normal && this.@namespace != Namespace.Vms) { this.@namespace = Namespace.Normal; } if (jolietvd is null && this.@namespace == Namespace.Joliet) { this.@namespace = Namespace.Normal; } uint rootLocation; uint rootSize; byte rootXattrLength = 0; if (!cdi) { rootLocation = highSierra ? hsvd.Value.root_directory_record.extent : pvd.Value.root_directory_record.extent; rootXattrLength = highSierra ? hsvd.Value.root_directory_record.xattr_len : pvd.Value.root_directory_record.xattr_len; if (highSierra) { rootSize = hsvd.Value.root_directory_record.size / hsvd.Value.logical_block_size; if (hsvd.Value.root_directory_record.size % hsvd.Value.logical_block_size > 0) { rootSize++; } } else { rootSize = pvd.Value.root_directory_record.size / pvd.Value.logical_block_size; if (pvd.Value.root_directory_record.size % pvd.Value.logical_block_size > 0) { rootSize++; } } } else { rootLocation = pathTable[0].Extent; byte[] firstRootSector = ReadSectors(rootLocation, 1); CdiDirectoryRecord rootEntry = Marshal.ByteArrayToStructureBigEndian <CdiDirectoryRecord>(firstRootSector); rootSize = rootEntry.size / fsvd.Value.logical_block_size; if (rootEntry.size % fsvd.Value.logical_block_size > 0) { rootSize++; } usePathTable = usePathTable || pathTable.Length == 1; useTransTbl = false; } // In case the path table is incomplete if (usePathTable && pathTableData.Length == 1) { usePathTable = false; } if (rootLocation + rootSize >= imagePlugin.Info.Sectors) { return(Errno.InvalidArgument); } byte[] rootDir = ReadSectors(rootLocation, rootSize); byte[] ipbinSector = ReadSectors(partition.Start, 1); CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); Dreamcast.IPBin?dreamcast = Dreamcast.DecodeIPBin(ipbinSector); if (this.@namespace == Namespace.Joliet || this.@namespace == Namespace.Rrip) { usePathTable = false; useTransTbl = false; } // Cannot traverse path table if we substitute the names for the ones in TRANS.TBL if (useTransTbl) { usePathTable = false; } if (this.@namespace != Namespace.Joliet) { rootDirectoryCache = cdi ? DecodeCdiDirectory(rootLocation, rootSize, rootXattrLength) : highSierra ? DecodeHighSierraDirectory(rootLocation, rootSize, rootXattrLength) : DecodeIsoDirectory(rootLocation, rootSize, rootXattrLength); } XmlFsType.Type = fsFormat; if (debug) { rootDirectoryCache.Add("$", new DecodedDirectoryEntry { Extents = new List <(uint extent, uint size)> { (rootLocation, (uint)rootDir.Length) }, Filename = "$", Size = (uint)rootDir.Length, Timestamp = decodedVd.CreationTime });