public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { _device = imagePlugin; Encoding = encoding ?? new Apple2(); options ??= GetDefaultOptions(); if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out _debug); } if (_device.Info.Sectors < 3) { return(Errno.InvalidArgument); } _multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); // Blocks 0 and 1 are boot code _catalogBlocks = _device.ReadSectors(_multiplier * 2, _multiplier); // On Apple //, it's little endian // TODO: Fix //BigEndianBitConverter.IsLittleEndian = // multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; _mountedVolEntry.FirstBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x00); _mountedVolEntry.LastBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x02); _mountedVolEntry.EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(_catalogBlocks, 0x04); _mountedVolEntry.VolumeName = new byte[8]; Array.Copy(_catalogBlocks, 0x06, _mountedVolEntry.VolumeName, 0, 8); _mountedVolEntry.Blocks = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x0E); _mountedVolEntry.Files = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x10); _mountedVolEntry.Dummy = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x12); _mountedVolEntry.LastBoot = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x14); _mountedVolEntry.Tail = BigEndianBitConverter.ToInt32(_catalogBlocks, 0x16); if (_mountedVolEntry.FirstBlock != 0 || _mountedVolEntry.LastBlock <= _mountedVolEntry.FirstBlock || (ulong)_mountedVolEntry.LastBlock > (_device.Info.Sectors / _multiplier) - 2 || (_mountedVolEntry.EntryType != PascalFileKind.Volume && _mountedVolEntry.EntryType != PascalFileKind.Secure) || _mountedVolEntry.VolumeName[0] > 7 || _mountedVolEntry.Blocks < 0 || (ulong)_mountedVolEntry.Blocks != _device.Info.Sectors / _multiplier || _mountedVolEntry.Files < 0) { return(Errno.InvalidArgument); } _catalogBlocks = _device.ReadSectors(_multiplier * 2, (uint)(_mountedVolEntry.LastBlock - _mountedVolEntry.FirstBlock - 2) * _multiplier); int offset = 26; _fileEntries = new List <PascalFileEntry>(); while (offset + 26 < _catalogBlocks.Length) { var entry = new PascalFileEntry { Filename = new byte[16], FirstBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x00), LastBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x02), EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x04), LastBytes = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x16), ModificationTime = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x18) }; Array.Copy(_catalogBlocks, offset + 0x06, entry.Filename, 0, 16); if (entry.Filename[0] <= 15 && entry.Filename[0] > 0) { _fileEntries.Add(entry); } offset += 26; } _bootBlocks = _device.ReadSectors(0, 2 * _multiplier); XmlFsType = new FileSystemType { Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(_bootBlocks), Clusters = (ulong)_mountedVolEntry.Blocks, ClusterSize = _device.Info.SectorSize, Files = (ulong)_mountedVolEntry.Files, FilesSpecified = true, Type = "UCSD Pascal", VolumeName = StringHandlers.PascalToString(_mountedVolEntry.VolumeName, Encoding) }; _mounted = true; return(Errno.NoError); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (partition.Length < 3) { return(false); } multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); // Blocks 0 and 1 are boot code byte[] volBlock = imagePlugin.ReadSectors(multiplier * 2 + partition.Start, multiplier); PascalVolumeEntry volEntry = new PascalVolumeEntry(); // On Apple II, it's little endian BigEndianBitConverter.IsLittleEndian = multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; volEntry.FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00); volEntry.LastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02); volEntry.EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04); volEntry.VolumeName = new byte[8]; Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8); volEntry.Blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E); volEntry.Files = BigEndianBitConverter.ToInt16(volBlock, 0x10); volEntry.Dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12); volEntry.LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14); volEntry.Tail = BigEndianBitConverter.ToInt32(volBlock, 0x16); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.firstBlock = {0}", volEntry.FirstBlock); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBlock = {0}", volEntry.LastBlock); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.entryType = {0}", volEntry.EntryType); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.volumeName = {0}", volEntry.VolumeName); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.blocks = {0}", volEntry.Blocks); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.files = {0}", volEntry.Files); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.dummy = {0}", volEntry.Dummy); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBoot = {0}", volEntry.LastBoot); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.tail = {0}", volEntry.Tail); // First block is always 0 (even is it's sector 2) if (volEntry.FirstBlock != 0) { return(false); } // Last volume record block must be after first block, and before end of device if (volEntry.LastBlock <= volEntry.FirstBlock || (ulong)volEntry.LastBlock > imagePlugin.Info.Sectors / multiplier - 2) { return(false); } // Volume record entry type must be volume or secure if (volEntry.EntryType != PascalFileKind.Volume && volEntry.EntryType != PascalFileKind.Secure) { return(false); } // Volume name is max 7 characters if (volEntry.VolumeName[0] > 7) { return(false); } // Volume blocks is equal to volume sectors if (volEntry.Blocks < 0 || (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / multiplier) { return(false); } // There can be not less than zero files return(volEntry.Files >= 0); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.UTF8; information = ""; uint sbSize = (uint)(Marshal.SizeOf <VolumeHeader>() / imagePlugin.Info.SectorSize); if (Marshal.SizeOf <VolumeHeader>() % imagePlugin.Info.SectorSize != 0) { sbSize++; } if (partition.Start + sbSize >= partition.End) { return; } byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize); if (sector.Length < Marshal.SizeOf <VolumeHeader>()) { return; } VolumeHeader vhdr = Marshal.ByteArrayToStructureLittleEndian <VolumeHeader>(sector); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.jump empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(vhdr.jump)); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.signature = {0}", StringHandlers.CToString(vhdr.signature)); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.mustBeZero empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero)); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.identifier = {0}", StringHandlers.CToString(BitConverter.GetBytes(vhdr.identifier))); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.length = {0}", vhdr.length); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.checksum = 0x{0:X4}", vhdr.checksum); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectors = {0}", vhdr.sectors); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.bytesPerSector = {0}", vhdr.bytesPerSector); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectorsPerCluster = {0}", vhdr.sectorsPerCluster); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown1 zero? = {0}", vhdr.unknown1 == 0); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown2 zero? = {0}", vhdr.unknown2 == 0); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown3 zero? = {0}", vhdr.unknown3 == 0); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown4 zero? = {0}", vhdr.unknown4 == 0); AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown5 empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(vhdr.unknown5)); if (vhdr.identifier != FSRS || !ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero) || !vhdr.signature.SequenceEqual(_signature)) { return; } var sb = new StringBuilder(); sb.AppendLine("Microsoft Resilient File System"); sb.AppendFormat("Volume uses {0} bytes per sector", vhdr.bytesPerSector).AppendLine(); sb.AppendFormat("Volume uses {0} sectors per cluster ({1} bytes)", vhdr.sectorsPerCluster, vhdr.sectorsPerCluster * vhdr.bytesPerSector).AppendLine(); sb.AppendFormat("Volume has {0} sectors ({1} bytes)", vhdr.sectors, vhdr.sectors * vhdr.bytesPerSector). AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "Resilient File System", ClusterSize = vhdr.bytesPerSector * vhdr.sectorsPerCluster, Clusters = vhdr.sectors / vhdr.sectorsPerCluster }; }
public bool Identify(IMediaImage imagePlugin, Partition partition) { try { if (imagePlugin.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true) { return(false); } // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors if (imagePlugin.Info.Sectors < 800) { return(false); } int beforeMddf = -1; // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors for (int i = 0; i < 100; i++) { DecodeTag(imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag), out LisaTag.PriamTag searchTag); AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); if (beforeMddf == -1 && searchTag.FileId == FILEID_LOADER_SIGNED) { beforeMddf = i - 1; } if (searchTag.FileId != FILEID_MDDF) { continue; } byte[] sector = imagePlugin.ReadSector((ulong)i); var infoMddf = new MDDF { mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C), volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70), volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74), vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78), blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C), datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E) }; AaruConsole.DebugWriteLine("LisaFS plugin", "Current sector = {0}", i); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.mddf_block = {0}", infoMddf.mddf_block); AaruConsole.DebugWriteLine("LisaFS plugin", "Disk size = {0} sectors", imagePlugin.Info.Sectors); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size = {0} sectors", infoMddf.vol_size); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size - 1 = {0}", infoMddf.volsize_minus_one); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size - mddf.mddf_block -1 = {0}", infoMddf.volsize_minus_mddf_minus_one); AaruConsole.DebugWriteLine("LisaFS plugin", "Disk sector = {0} bytes", imagePlugin.Info.SectorSize); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.blocksize = {0} bytes", infoMddf.blocksize); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.datasize = {0} bytes", infoMddf.datasize); if (infoMddf.mddf_block != i - beforeMddf) { return(false); } if (infoMddf.vol_size > imagePlugin.Info.Sectors) { return(false); } if (infoMddf.vol_size - 1 != infoMddf.volsize_minus_one) { return(false); } if (infoMddf.vol_size - i - 1 != infoMddf.volsize_minus_mddf_minus_one - beforeMddf) { return(false); } if (infoMddf.datasize > infoMddf.blocksize) { return(false); } if (infoMddf.blocksize < imagePlugin.Info.SectorSize) { return(false); } return(infoMddf.datasize == imagePlugin.Info.SectorSize); } return(false); } catch (Exception ex) { AaruConsole.ErrorWriteLine("Exception {0}, {1}, {2}", ex.Message, ex.InnerException, ex.StackTrace); return(false); } }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; var sb = new StringBuilder(); if (imagePlugin.Info.SectorSize < 512) { return; } ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; uint sbOff = SB_POS % imagePlugin.Info.SectorSize; if (sbSectorOff + partition.Start >= partition.End) { return; } byte[] sblock = imagePlugin.ReadSector(sbSectorOff + partition.Start); byte[] sbSector = new byte[512]; Array.Copy(sblock, sbOff, sbSector, 0, 512); var extSb = new SuperBlock { inodes = BitConverter.ToUInt32(sbSector, 0x000), zones = BitConverter.ToUInt32(sbSector, 0x004), firstfreeblk = BitConverter.ToUInt32(sbSector, 0x008), freecountblk = BitConverter.ToUInt32(sbSector, 0x00C), firstfreeind = BitConverter.ToUInt32(sbSector, 0x010), freecountind = BitConverter.ToUInt32(sbSector, 0x014), firstdatazone = BitConverter.ToUInt32(sbSector, 0x018), logzonesize = BitConverter.ToUInt32(sbSector, 0x01C), maxsize = BitConverter.ToUInt32(sbSector, 0x020) }; sb.AppendLine("ext filesystem"); sb.AppendFormat("{0} zones on volume", extSb.zones); sb.AppendFormat("{0} free blocks ({1} bytes)", extSb.freecountblk, extSb.freecountblk * 1024); sb.AppendFormat("{0} inodes on volume, {1} free ({2}%)", extSb.inodes, extSb.freecountind, (extSb.freecountind * 100) / extSb.inodes); sb.AppendFormat("First free inode is {0}", extSb.firstfreeind); sb.AppendFormat("First free block is {0}", extSb.firstfreeblk); sb.AppendFormat("First data zone is {0}", extSb.firstdatazone); sb.AppendFormat("Log zone size: {0}", extSb.logzonesize); sb.AppendFormat("Max zone size: {0}", extSb.maxsize); XmlFsType = new FileSystemType { Type = "ext", FreeClusters = extSb.freecountblk, FreeClustersSpecified = true, ClusterSize = 1024, Clusters = (((partition.End - partition.Start) + 1) * imagePlugin.Info.SectorSize) / 1024 }; information = sb.ToString(); }
public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset) { partitions = new List <Partition>(); byte[] sector = imagePlugin.ReadSector(sectorOffset); if (sector.Length < 512) { return(false); } var table = new AtariTable { boot = new byte[342], icdEntries = new AtariEntry[8], unused = new byte[12], entries = new AtariEntry[4] }; Array.Copy(sector, 0, table.boot, 0, 342); for (int i = 0; i < 8; i++) { table.icdEntries[i].type = BigEndianBitConverter.ToUInt32(sector, 342 + (i * 12) + 0); table.icdEntries[i].start = BigEndianBitConverter.ToUInt32(sector, 342 + (i * 12) + 4); table.icdEntries[i].length = BigEndianBitConverter.ToUInt32(sector, 342 + (i * 12) + 8); } Array.Copy(sector, 438, table.unused, 0, 12); table.size = BigEndianBitConverter.ToUInt32(sector, 450); for (int i = 0; i < 4; i++) { table.entries[i].type = BigEndianBitConverter.ToUInt32(sector, 454 + (i * 12) + 0); table.entries[i].start = BigEndianBitConverter.ToUInt32(sector, 454 + (i * 12) + 4); table.entries[i].length = BigEndianBitConverter.ToUInt32(sector, 454 + (i * 12) + 8); } table.badStart = BigEndianBitConverter.ToUInt32(sector, 502); table.badLength = BigEndianBitConverter.ToUInt32(sector, 506); table.checksum = BigEndianBitConverter.ToUInt16(sector, 510); var sha1Ctx = new Sha1Context(); sha1Ctx.Update(table.boot); AaruConsole.DebugWriteLine("Atari partition plugin", "Boot code SHA1: {0}", sha1Ctx.End()); for (int i = 0; i < 8; i++) { AaruConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].flag = 0x{1:X2}", i, (table.icdEntries[i].type & 0xFF000000) >> 24); AaruConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].type = 0x{1:X6}", i, table.icdEntries[i].type & 0x00FFFFFF); AaruConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].start = {1}", i, table.icdEntries[i].start); AaruConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].length = {1}", i, table.icdEntries[i].length); } AaruConsole.DebugWriteLine("Atari partition plugin", "table.size = {0}", table.size); for (int i = 0; i < 4; i++) { AaruConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].flag = 0x{1:X2}", i, (table.entries[i].type & 0xFF000000) >> 24); AaruConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].type = 0x{1:X6}", i, table.entries[i].type & 0x00FFFFFF); AaruConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].start = {1}", i, table.entries[i].start); AaruConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].length = {1}", i, table.entries[i].length); } AaruConsole.DebugWriteLine("Atari partition plugin", "table.badStart = {0}", table.badStart); AaruConsole.DebugWriteLine("Atari partition plugin", "table.badLength = {0}", table.badLength); AaruConsole.DebugWriteLine("Atari partition plugin", "table.checksum = 0x{0:X4}", table.checksum); bool validTable = false; ulong partitionSequence = 0; for (int i = 0; i < 4; i++) { uint type = table.entries[i].type & 0x00FFFFFF; switch (type) { case TypeGEMDOS: case TypeBigGEMDOS: case TypeLinux: case TypeSwap: case TypeRAW: case TypeNetBSD: case TypeNetBSDSwap: case TypeSysV: case TypeMac: case TypeMinix: case TypeMinix2: validTable = true; if (table.entries[i].start <= imagePlugin.Info.Sectors) { if (table.entries[i].start + table.entries[i].length > imagePlugin.Info.Sectors) { AaruConsole.DebugWriteLine("Atari partition plugin", "WARNING: End of partition goes beyond device size"); } ulong sectorSize = imagePlugin.Info.SectorSize; if (sectorSize == 2448 || sectorSize == 2352) { sectorSize = 2048; } byte[] partType = new byte[3]; partType[0] = (byte)((type & 0xFF0000) >> 16); partType[1] = (byte)((type & 0x00FF00) >> 8); partType[2] = (byte)(type & 0x0000FF); var part = new Partition { Size = table.entries[i].length * sectorSize, Length = table.entries[i].length, Sequence = partitionSequence, Name = "", Offset = table.entries[i].start * sectorSize, Start = table.entries[i].start, Type = Encoding.ASCII.GetString(partType), Scheme = Name }; switch (type) { case TypeGEMDOS: part.Description = "Atari GEMDOS partition"; break; case TypeBigGEMDOS: part.Description = "Atari GEMDOS partition bigger than 32 MiB"; break; case TypeLinux: part.Description = "Linux partition"; break; case TypeSwap: part.Description = "Swap partition"; break; case TypeRAW: part.Description = "RAW partition"; break; case TypeNetBSD: part.Description = "NetBSD partition"; break; case TypeNetBSDSwap: part.Description = "NetBSD swap partition"; break; case TypeSysV: part.Description = "Atari UNIX partition"; break; case TypeMac: part.Description = "Macintosh partition"; break; case TypeMinix: case TypeMinix2: part.Description = "MINIX partition"; break; default: part.Description = "Unknown partition type"; break; } partitions.Add(part); partitionSequence++; } break; case TypeExtended: byte[] extendedSector = imagePlugin.ReadSector(table.entries[i].start); var extendedTable = new AtariTable(); extendedTable.entries = new AtariEntry[4]; for (int j = 0; j < 4; j++) { extendedTable.entries[j].type = BigEndianBitConverter.ToUInt32(extendedSector, 454 + (j * 12) + 0); extendedTable.entries[j].start = BigEndianBitConverter.ToUInt32(extendedSector, 454 + (j * 12) + 4); extendedTable.entries[j].length = BigEndianBitConverter.ToUInt32(extendedSector, 454 + (j * 12) + 8); } for (int j = 0; j < 4; j++) { uint extendedType = extendedTable.entries[j].type & 0x00FFFFFF; if (extendedType != TypeGEMDOS && extendedType != TypeBigGEMDOS && extendedType != TypeLinux && extendedType != TypeSwap && extendedType != TypeRAW && extendedType != TypeNetBSD && extendedType != TypeNetBSDSwap && extendedType != TypeSysV && extendedType != TypeMac && extendedType != TypeMinix && extendedType != TypeMinix2) { continue; } validTable = true; if (extendedTable.entries[j].start > imagePlugin.Info.Sectors) { continue; } if (extendedTable.entries[j].start + extendedTable.entries[j].length > imagePlugin.Info.Sectors) { AaruConsole.DebugWriteLine("Atari partition plugin", "WARNING: End of partition goes beyond device size"); } ulong sectorSize = imagePlugin.Info.SectorSize; if (sectorSize == 2448 || sectorSize == 2352) { sectorSize = 2048; } byte[] partType = new byte[3]; partType[0] = (byte)((extendedType & 0xFF0000) >> 16); partType[1] = (byte)((extendedType & 0x00FF00) >> 8); partType[2] = (byte)(extendedType & 0x0000FF); var part = new Partition { Size = extendedTable.entries[j].length * sectorSize, Length = extendedTable.entries[j].length, Sequence = partitionSequence, Name = "", Offset = extendedTable.entries[j].start * sectorSize, Start = extendedTable.entries[j].start, Type = Encoding.ASCII.GetString(partType), Scheme = Name }; switch (extendedType) { case TypeGEMDOS: part.Description = "Atari GEMDOS partition"; break; case TypeBigGEMDOS: part.Description = "Atari GEMDOS partition bigger than 32 MiB"; break; case TypeLinux: part.Description = "Linux partition"; break; case TypeSwap: part.Description = "Swap partition"; break; case TypeRAW: part.Description = "RAW partition"; break; case TypeNetBSD: part.Description = "NetBSD partition"; break; case TypeNetBSDSwap: part.Description = "NetBSD swap partition"; break; case TypeSysV: part.Description = "Atari UNIX partition"; break; case TypeMac: part.Description = "Macintosh partition"; break; case TypeMinix: case TypeMinix2: part.Description = "MINIX partition"; break; default: part.Description = "Unknown partition type"; break; } partitions.Add(part); partitionSequence++; } break; } } if (!validTable) { return(partitions.Count > 0); } for (int i = 0; i < 8; i++) { uint type = table.icdEntries[i].type & 0x00FFFFFF; if (type != TypeGEMDOS && type != TypeBigGEMDOS && type != TypeLinux && type != TypeSwap && type != TypeRAW && type != TypeNetBSD && type != TypeNetBSDSwap && type != TypeSysV && type != TypeMac && type != TypeMinix && type != TypeMinix2) { continue; } if (table.icdEntries[i].start > imagePlugin.Info.Sectors) { continue; } if (table.icdEntries[i].start + table.icdEntries[i].length > imagePlugin.Info.Sectors) { AaruConsole.DebugWriteLine("Atari partition plugin", "WARNING: End of partition goes beyond device size"); } ulong sectorSize = imagePlugin.Info.SectorSize; if (sectorSize == 2448 || sectorSize == 2352) { sectorSize = 2048; } byte[] partType = new byte[3]; partType[0] = (byte)((type & 0xFF0000) >> 16); partType[1] = (byte)((type & 0x00FF00) >> 8); partType[2] = (byte)(type & 0x0000FF); var part = new Partition { Size = table.icdEntries[i].length * sectorSize, Length = table.icdEntries[i].length, Sequence = partitionSequence, Name = "", Offset = table.icdEntries[i].start * sectorSize, Start = table.icdEntries[i].start, Type = Encoding.ASCII.GetString(partType), Scheme = Name }; switch (type) { case TypeGEMDOS: part.Description = "Atari GEMDOS partition"; break; case TypeBigGEMDOS: part.Description = "Atari GEMDOS partition bigger than 32 MiB"; break; case TypeLinux: part.Description = "Linux partition"; break; case TypeSwap: part.Description = "Swap partition"; break; case TypeRAW: part.Description = "RAW partition"; break; case TypeNetBSD: part.Description = "NetBSD partition"; break; case TypeNetBSDSwap: part.Description = "NetBSD swap partition"; break; case TypeSysV: part.Description = "Atari UNIX partition"; break; case TypeMac: part.Description = "Macintosh partition"; break; case TypeMinix: case TypeMinix2: part.Description = "MINIX partition"; break; default: part.Description = "Unknown partition type"; break; } partitions.Add(part); partitionSequence++; } return(partitions.Count > 0); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("macintosh"); information = ""; StringBuilder sb = new StringBuilder(); byte[] bbSector = null; byte[] mdbSector = null; ushort drSigWord; bool apmFromHddOnCd = false; if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 || imagePlugin.Info.SectorSize == 2048) { byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2); foreach (int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) { drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); if (drSigWord != HFS_MAGIC) { continue; } bbSector = new byte[1024]; mdbSector = new byte[512]; if (offset >= 0x400) { Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024); } Array.Copy(tmpSector, offset, mdbSector, 0, 512); apmFromHddOnCd = true; break; } if (!apmFromHddOnCd) { return; } } else { mdbSector = imagePlugin.ReadSector(2 + partition.Start); drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); if (drSigWord == HFS_MAGIC) { bbSector = imagePlugin.ReadSector(partition.Start); } else { return; } } HfsMasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian <HfsMasterDirectoryBlock>(mdbSector); HfsBootBlock bb = Marshal.ByteArrayToStructureBigEndian <HfsBootBlock>(bbSector); sb.AppendLine("Apple Hierarchical File System"); sb.AppendLine(); if (apmFromHddOnCd) { sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine(); } sb.AppendLine("Master Directory Block:"); sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine(); if (mdb.drVolBkUp > 0) { sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine(); sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine(); } else { sb.AppendLine("Volume has never been backed up"); } if ((mdb.drAtrb & 0x80) == 0x80) { sb.AppendLine("Volume is locked by hardware."); } sb.AppendLine((mdb.drAtrb & 0x100) == 0x100 ? "Volume was unmonted." : "Volume is mounted."); if ((mdb.drAtrb & 0x200) == 0x200) { sb.AppendLine("Volume has spared bad blocks."); } if ((mdb.drAtrb & 0x400) == 0x400) { sb.AppendLine("Volume does not need cache."); } if ((mdb.drAtrb & 0x800) == 0x800) { sb.AppendLine("Boot volume is inconsistent."); } if ((mdb.drAtrb & 0x1000) == 0x1000) { sb.AppendLine("There are reused CNIDs."); } if ((mdb.drAtrb & 0x2000) == 0x2000) { sb.AppendLine("Volume is journaled."); } if ((mdb.drAtrb & 0x4000) == 0x4000) { sb.AppendLine("Volume is seriously inconsistent."); } if ((mdb.drAtrb & 0x8000) == 0x8000) { sb.AppendLine("Volume is locked by software."); } sb.AppendFormat("{0} files on root directory", mdb.drNmFls).AppendLine(); sb.AppendFormat("{0} directories on root directory", mdb.drNmRtDirs).AppendLine(); sb.AppendFormat("{0} files on volume", mdb.drFilCnt).AppendLine(); sb.AppendFormat("{0} directories on volume", mdb.drDirCnt).AppendLine(); sb.AppendFormat("Volume write count: {0}", mdb.drWrCnt).AppendLine(); sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", mdb.drVBMSt).AppendLine(); sb.AppendFormat("Next allocation block: {0}.", mdb.drAllocPtr).AppendLine(); sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks).AppendLine(); sb.AppendFormat("{0} bytes per allocation block.", mdb.drAlBlkSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a file.", mdb.drClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", mdb.drXTClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", mdb.drCTClpSiz).AppendLine(); sb.AppendFormat("Sector of first allocation block: {0}", mdb.drAlBlSt).AppendLine(); sb.AppendFormat("Next unused CNID: {0}", mdb.drNxtCNID).AppendLine(); sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); sb.AppendFormat("{0} bytes in the Extents B-Tree", mdb.drXTFlSize).AppendLine(); sb.AppendFormat("{0} bytes in the Catalog B-Tree", mdb.drCTFlSize).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(mdb.drVN, Encoding)).AppendLine(); sb.AppendLine("Finder info:"); sb.AppendFormat("CNID of bootable system's directory: {0}", mdb.drFndrInfo0).AppendLine(); sb.AppendFormat("CNID of first-run application's directory: {0}", mdb.drFndrInfo1).AppendLine(); sb.AppendFormat("CNID of previously opened directory: {0}", mdb.drFndrInfo2).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", mdb.drFndrInfo3).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", mdb.drFndrInfo5).AppendLine(); if (mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0) { sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", mdb.drFndrInfo6, mdb.drFndrInfo7).AppendLine(); } if (mdb.drEmbedSigWord == HFSP_MAGIC) { sb.AppendLine("Volume wraps a HFS+ volume."); sb.AppendFormat("Starting block of the HFS+ volume: {0}", mdb.xdrStABNt).AppendLine(); sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", mdb.xdrNumABlks).AppendLine(); } else { sb.AppendFormat("{0} blocks in volume cache", mdb.drVCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume bitmap cache", mdb.drVBMCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume common cache", mdb.drCtlCSize).AppendLine(); } if (bb.signature == HFSBB_MAGIC) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine("Boot Block:"); if ((bb.boot_flags & 0x40) == 0x40) { sb.AppendLine("Boot block should be executed."); } if ((bb.boot_flags & 0x80) == 0x80) { sb.AppendLine("Boot block is in new unknown format."); } else { if (bb.boot_flags > 0) { sb.AppendLine("Allocate secondary sound buffer at boot."); } else if (bb.boot_flags < 0) { sb.AppendLine("Allocate secondary sound and video buffers at boot."); } sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.system_name, Encoding)) .AppendLine(); sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.finder_name, Encoding)) .AppendLine(); sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.debug_name, Encoding)) .AppendLine(); sb.AppendFormat("Disassembler filename: {0}", StringHandlers.PascalToString(bb.disasm_name, Encoding)).AppendLine(); sb.AppendFormat("Startup screen filename: {0}", StringHandlers.PascalToString(bb.stupscr_name, Encoding)).AppendLine(); sb.AppendFormat("First program to execute at boot: {0}", StringHandlers.PascalToString(bb.bootup_name, Encoding)).AppendLine(); sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(bb.clipbrd_name, Encoding)) .AppendLine(); sb.AppendFormat("Maximum opened files: {0}", bb.max_files * 4).AppendLine(); sb.AppendFormat("Event queue size: {0}", bb.queue_size).AppendLine(); sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.heap_128k).AppendLine(); sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.heap_256k).AppendLine(); sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.heap_512k).AppendLine(); } } else if (mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0) { sb.AppendLine("Volume is bootable."); } else { sb.AppendLine("Volume is not bootable."); } information = sb.ToString(); XmlFsType = new FileSystemType(); if (mdb.drVolBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drVolBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bb.signature == HFSBB_MAGIC || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0; XmlFsType.Clusters = mdb.drNmAlBlks; XmlFsType.ClusterSize = mdb.drAlBlkSiz; if (mdb.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = (mdb.drAtrb & 0x100) != 0x100; XmlFsType.Files = mdb.drFilCnt; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = mdb.drFreeBks; XmlFsType.FreeClustersSpecified = true; if (mdb.drLsMod > 0) { XmlFsType.ModificationDate = DateHandlers.MacToDateTime(mdb.drLsMod); XmlFsType.ModificationDateSpecified = true; } XmlFsType.Type = "HFS"; XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding); if (mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0) { XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}"; } }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; StringBuilder sb = new StringBuilder(); HammerSuperBlock hammerSb; uint run = HAMMER_VOLHDR_SIZE / imagePlugin.Info.SectorSize; if (HAMMER_VOLHDR_SIZE % imagePlugin.Info.SectorSize > 0) { run++; } byte[] sbSector = imagePlugin.ReadSectors(partition.Start, run); ulong magic = BitConverter.ToUInt64(sbSector, 0); if (magic == HAMMER_FSBUF_VOLUME) { hammerSb = Marshal.ByteArrayToStructureLittleEndian <HammerSuperBlock>(sbSector); } else { hammerSb = Marshal.ByteArrayToStructureBigEndian <HammerSuperBlock>(sbSector); } sb.AppendLine("HAMMER filesystem"); sb.AppendFormat("Volume version: {0}", hammerSb.vol_version).AppendLine(); sb.AppendFormat("Volume {0} of {1} on this filesystem", hammerSb.vol_no + 1, hammerSb.vol_count) .AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(hammerSb.vol_label, Encoding)).AppendLine(); sb.AppendFormat("Volume serial: {0}", hammerSb.vol_fsid).AppendLine(); sb.AppendFormat("Filesystem type: {0}", hammerSb.vol_fstype).AppendLine(); sb.AppendFormat("Boot area starts at {0}", hammerSb.vol_bot_beg).AppendLine(); sb.AppendFormat("Memory log starts at {0}", hammerSb.vol_mem_beg).AppendLine(); sb.AppendFormat("First volume buffer starts at {0}", hammerSb.vol_buf_beg).AppendLine(); sb.AppendFormat("Volume ends at {0}", hammerSb.vol_buf_end).AppendLine(); XmlFsType = new FileSystemType { Clusters = partition.Size / HAMMER_BIGBLOCK_SIZE, ClusterSize = HAMMER_BIGBLOCK_SIZE, Dirty = false, Type = "HAMMER", VolumeName = StringHandlers.CToString(hammerSb.vol_label, Encoding), VolumeSerial = hammerSb.vol_fsid.ToString() }; if (hammerSb.vol_no == hammerSb.vol_rootvol) { sb.AppendFormat("Filesystem contains {0} \"big-blocks\" ({1} bytes)", hammerSb.vol0_stat_bigblocks, hammerSb.vol0_stat_bigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); sb.AppendFormat("Filesystem has {0} \"big-blocks\" free ({1} bytes)", hammerSb.vol0_stat_freebigblocks, hammerSb.vol0_stat_freebigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); sb.AppendFormat("Filesystem has {0} inode used", hammerSb.vol0_stat_inodes).AppendLine(); XmlFsType.Clusters = (ulong)hammerSb.vol0_stat_bigblocks; XmlFsType.FreeClusters = (ulong)hammerSb.vol0_stat_freebigblocks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Files = (ulong)hammerSb.vol0_stat_inodes; XmlFsType.FilesSpecified = true; } // 0 ? //sb.AppendFormat("Volume header CRC: 0x{0:X8}", afs_sb.vol_crc).AppendLine(); information = sb.ToString(); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("shift_jis"); var sbInformation = new StringBuilder(); information = ""; XmlFsType = new FileSystemType(); var fields = new NintendoFields(); byte[] header = imagePlugin.ReadSectors(0, 0x50000 / imagePlugin.Info.SectorSize); bool wii = false; uint magicGc = BigEndianBitConverter.ToUInt32(header, 0x1C); uint magicWii = BigEndianBitConverter.ToUInt32(header, 0x18); if (magicWii == 0x5D1C9EA3) { wii = true; } else if (magicGc != 0xC2339F3D) { return; } fields.DiscType = Encoding.ASCII.GetString(header, 0, 1); fields.GameCode = Encoding.ASCII.GetString(header, 1, 2); fields.RegionCode = Encoding.ASCII.GetString(header, 3, 1); fields.PublisherCode = Encoding.ASCII.GetString(header, 4, 2); fields.DiscId = Encoding.ASCII.GetString(header, 0, 6); fields.DiscNumber = header[6]; fields.DiscVersion = header[7]; fields.Streaming |= header[8] > 0; fields.StreamBufferSize = header[9]; byte[] temp = new byte[64]; Array.Copy(header, 0x20, temp, 0, 64); fields.Title = StringHandlers.CToString(temp, Encoding); if (!wii) { fields.DebugOff = BigEndianBitConverter.ToUInt32(header, 0x0400); fields.DebugAddr = BigEndianBitConverter.ToUInt32(header, 0x0404); fields.DolOff = BigEndianBitConverter.ToUInt32(header, 0x0420); fields.FstOff = BigEndianBitConverter.ToUInt32(header, 0x0424); fields.FstSize = BigEndianBitConverter.ToUInt32(header, 0x0428); fields.FstMax = BigEndianBitConverter.ToUInt32(header, 0x042C); } if (wii) { uint offset1 = BigEndianBitConverter.ToUInt32(header, 0x40004) << 2; uint offset2 = BigEndianBitConverter.ToUInt32(header, 0x4000C) << 2; uint offset3 = BigEndianBitConverter.ToUInt32(header, 0x40014) << 2; uint offset4 = BigEndianBitConverter.ToUInt32(header, 0x4001C) << 2; fields.FirstPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40000)]; fields.SecondPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40008)]; fields.ThirdPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40010)]; fields.FourthPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40018)]; for (int i = 0; i < fields.FirstPartitions.Length; i++) { if (offset1 + (i * 8) + 8 < 0x50000) { fields.FirstPartitions[i].Offset = BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 0)) << 2; fields.FirstPartitions[i].Type = BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 4)); } } for (int i = 0; i < fields.SecondPartitions.Length; i++) { if (offset1 + (i * 8) + 8 < 0x50000) { fields.FirstPartitions[i].Offset = BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 0)) << 2; fields.FirstPartitions[i].Type = BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 4)); } } for (int i = 0; i < fields.ThirdPartitions.Length; i++) { if (offset1 + (i * 8) + 8 < 0x50000) { fields.FirstPartitions[i].Offset = BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 0)) << 2; fields.FirstPartitions[i].Type = BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 4)); } } for (int i = 0; i < fields.FourthPartitions.Length; i++) { if (offset1 + (i * 8) + 8 < 0x50000) { fields.FirstPartitions[i].Offset = BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 0)) << 2; fields.FirstPartitions[i].Type = BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 4)); } } fields.Region = header[0x4E000]; fields.JapanAge = header[0x4E010]; fields.UsaAge = header[0x4E011]; fields.GermanAge = header[0x4E013]; fields.PegiAge = header[0x4E014]; fields.FinlandAge = header[0x4E015]; fields.PortugalAge = header[0x4E016]; fields.UkAge = header[0x4E017]; fields.AustraliaAge = header[0x4E018]; fields.KoreaAge = header[0x4E019]; } else { fields.FirstPartitions = new NintendoPartition[0]; fields.SecondPartitions = new NintendoPartition[0]; fields.ThirdPartitions = new NintendoPartition[0]; fields.FourthPartitions = new NintendoPartition[0]; } AaruConsole.DebugWriteLine("Nintendo plugin", "discType = {0}", fields.DiscType); AaruConsole.DebugWriteLine("Nintendo plugin", "gameCode = {0}", fields.GameCode); AaruConsole.DebugWriteLine("Nintendo plugin", "regionCode = {0}", fields.RegionCode); AaruConsole.DebugWriteLine("Nintendo plugin", "publisherCode = {0}", fields.PublisherCode); AaruConsole.DebugWriteLine("Nintendo plugin", "discID = {0}", fields.DiscId); AaruConsole.DebugWriteLine("Nintendo plugin", "discNumber = {0}", fields.DiscNumber); AaruConsole.DebugWriteLine("Nintendo plugin", "discVersion = {0}", fields.DiscVersion); AaruConsole.DebugWriteLine("Nintendo plugin", "streaming = {0}", fields.Streaming); AaruConsole.DebugWriteLine("Nintendo plugin", "streamBufferSize = {0}", fields.StreamBufferSize); AaruConsole.DebugWriteLine("Nintendo plugin", "title = \"{0}\"", fields.Title); AaruConsole.DebugWriteLine("Nintendo plugin", "debugOff = 0x{0:X8}", fields.DebugOff); AaruConsole.DebugWriteLine("Nintendo plugin", "debugAddr = 0x{0:X8}", fields.DebugAddr); AaruConsole.DebugWriteLine("Nintendo plugin", "dolOff = 0x{0:X8}", fields.DolOff); AaruConsole.DebugWriteLine("Nintendo plugin", "fstOff = 0x{0:X8}", fields.FstOff); AaruConsole.DebugWriteLine("Nintendo plugin", "fstSize = {0}", fields.FstSize); AaruConsole.DebugWriteLine("Nintendo plugin", "fstMax = {0}", fields.FstMax); for (int i = 0; i < fields.FirstPartitions.Length; i++) { AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].offset = {0}", fields.FirstPartitions[i].Offset, i); AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].type = {0}", fields.FirstPartitions[i].Type, i); } for (int i = 0; i < fields.SecondPartitions.Length; i++) { AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].offset = {0}", fields.SecondPartitions[i].Offset, i); AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].type = {0}", fields.SecondPartitions[i].Type, i); } for (int i = 0; i < fields.ThirdPartitions.Length; i++) { AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].offset = {0}", fields.ThirdPartitions[i].Offset, i); AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].type = {0}", fields.ThirdPartitions[i].Type, i); } for (int i = 0; i < fields.FourthPartitions.Length; i++) { AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].offset = {0}", fields.FourthPartitions[i].Offset, i); AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].type = {0}", fields.FourthPartitions[i].Type, i); } AaruConsole.DebugWriteLine("Nintendo plugin", "region = {0}", fields.Region); AaruConsole.DebugWriteLine("Nintendo plugin", "japanAge = {0}", fields.JapanAge); AaruConsole.DebugWriteLine("Nintendo plugin", "usaAge = {0}", fields.UsaAge); AaruConsole.DebugWriteLine("Nintendo plugin", "germanAge = {0}", fields.GermanAge); AaruConsole.DebugWriteLine("Nintendo plugin", "pegiAge = {0}", fields.PegiAge); AaruConsole.DebugWriteLine("Nintendo plugin", "finlandAge = {0}", fields.FinlandAge); AaruConsole.DebugWriteLine("Nintendo plugin", "portugalAge = {0}", fields.PortugalAge); AaruConsole.DebugWriteLine("Nintendo plugin", "ukAge = {0}", fields.UkAge); AaruConsole.DebugWriteLine("Nintendo plugin", "australiaAge = {0}", fields.AustraliaAge); AaruConsole.DebugWriteLine("Nintendo plugin", "koreaAge = {0}", fields.KoreaAge); sbInformation.AppendLine("Nintendo optical filesystem"); sbInformation.AppendLine(wii ? "Nintendo Wii Optical Disc" : "Nintendo GameCube Optical Disc"); sbInformation.AppendFormat("Disc ID is {0}", fields.DiscId).AppendLine(); sbInformation.AppendFormat("Disc is a {0} disc", DiscTypeToString(fields.DiscType)).AppendLine(); sbInformation.AppendFormat("Disc region is {0}", RegionCodeToString(fields.RegionCode)).AppendLine(); sbInformation.AppendFormat("Published by {0}", PublisherCodeToString(fields.PublisherCode)).AppendLine(); if (fields.DiscNumber > 0) { sbInformation.AppendFormat("Disc number {0} of a multi-disc set", fields.DiscNumber + 1).AppendLine(); } if (fields.Streaming) { sbInformation.AppendLine("Disc is prepared for audio streaming"); } if (fields.StreamBufferSize > 0) { sbInformation.AppendFormat("Audio streaming buffer size is {0} bytes", fields.StreamBufferSize). AppendLine(); } sbInformation.AppendFormat("Title: {0}", fields.Title).AppendLine(); if (wii) { for (int i = 0; i < fields.FirstPartitions.Length; i++) { sbInformation.AppendFormat("First {0} partition starts at sector {1}", PartitionTypeToString(fields.FirstPartitions[i].Type), fields.FirstPartitions[i].Offset / 2048).AppendLine(); } for (int i = 0; i < fields.SecondPartitions.Length; i++) { sbInformation.AppendFormat("Second {0} partition starts at sector {1}", PartitionTypeToString(fields.SecondPartitions[i].Type), fields.SecondPartitions[i].Offset / 2048).AppendLine(); } for (int i = 0; i < fields.ThirdPartitions.Length; i++) { sbInformation.AppendFormat("Third {0} partition starts at sector {1}", PartitionTypeToString(fields.ThirdPartitions[i].Type), fields.ThirdPartitions[i].Offset / 2048).AppendLine(); } for (int i = 0; i < fields.FourthPartitions.Length; i++) { sbInformation.AppendFormat("Fourth {0} partition starts at sector {1}", PartitionTypeToString(fields.FourthPartitions[i].Type), fields.FourthPartitions[i].Offset / 2048).AppendLine(); } // sbInformation.AppendFormat("Region byte is {0}", fields.region).AppendLine(); if ((fields.JapanAge & 0x80) != 0x80) { sbInformation.AppendFormat("Japan age rating is {0}", fields.JapanAge).AppendLine(); } if ((fields.UsaAge & 0x80) != 0x80) { sbInformation.AppendFormat("ESRB age rating is {0}", fields.UsaAge).AppendLine(); } if ((fields.GermanAge & 0x80) != 0x80) { sbInformation.AppendFormat("German age rating is {0}", fields.GermanAge).AppendLine(); } if ((fields.PegiAge & 0x80) != 0x80) { sbInformation.AppendFormat("PEGI age rating is {0}", fields.PegiAge).AppendLine(); } if ((fields.FinlandAge & 0x80) != 0x80) { sbInformation.AppendFormat("Finland age rating is {0}", fields.FinlandAge).AppendLine(); } if ((fields.PortugalAge & 0x80) != 0x80) { sbInformation.AppendFormat("Portugal age rating is {0}", fields.PortugalAge).AppendLine(); } if ((fields.UkAge & 0x80) != 0x80) { sbInformation.AppendFormat("UK age rating is {0}", fields.UkAge).AppendLine(); } if ((fields.AustraliaAge & 0x80) != 0x80) { sbInformation.AppendFormat("Australia age rating is {0}", fields.AustraliaAge).AppendLine(); } if ((fields.KoreaAge & 0x80) != 0x80) { sbInformation.AppendFormat("Korea age rating is {0}", fields.KoreaAge).AppendLine(); } } else { sbInformation.AppendFormat("FST starts at {0} and has {1} bytes", fields.FstOff, fields.FstSize). AppendLine(); } information = sbInformation.ToString(); XmlFsType.Bootable = true; XmlFsType.Clusters = (imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize) / 2048; XmlFsType.ClusterSize = 2048; XmlFsType.Type = wii ? "Nintendo Wii filesystem" : "Nintendo Gamecube filesystem"; XmlFsType.VolumeName = fields.Title; XmlFsType.VolumeSerial = fields.DiscId; }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options) { device = imagePlugin; this.partition = partition; Encoding = encoding ?? Encoding.GetEncoding("IBM437"); // As the identification is so complex, just call Identify() and relay on its findings if (!Identify(device, partition) || !cpmFound || workingDefinition == null || dpb == null) { return(Errno.InvalidArgument); } // Build the software interleaving sector mask if (workingDefinition.sides == 1) { sectorMask = new int[workingDefinition.side1.sectorIds.Length]; for (int m = 0; m < sectorMask.Length; m++) { sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0]; } } else { // Head changes after every track if (string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0) { sectorMask = new int[workingDefinition.side1.sectorIds.Length + workingDefinition.side2.sectorIds.Length]; for (int m = 0; m < workingDefinition.side1.sectorIds.Length; m++) { sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0]; } // Skip first track (first side) for (int m = 0; m < workingDefinition.side2.sectorIds.Length; m++) { sectorMask[m + workingDefinition.side1.sectorIds.Length] = workingDefinition.side2.sectorIds[m] - workingDefinition.side2.sectorIds[0] + workingDefinition.side1.sectorIds.Length; } } // Head changes after whole side else if (string.Compare(workingDefinition.order, "CYLINDERS", StringComparison.InvariantCultureIgnoreCase) == 0) { for (int m = 0; m < workingDefinition.side1.sectorIds.Length; m++) { sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0]; } // Skip first track (first side) and first track (second side) for (int m = 0; m < workingDefinition.side1.sectorIds.Length; m++) { sectorMask[m + workingDefinition.side1.sectorIds.Length] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0] + workingDefinition.side1.sectorIds.Length + workingDefinition.side2.sectorIds.Length; } // TODO: Implement CYLINDERS ordering DicConsole.DebugWriteLine("CP/M Plugin", "CYLINDERS ordering not yet implemented."); return(Errno.NotImplemented); } // TODO: Implement COLUMBIA ordering else if (string.Compare(workingDefinition.order, "COLUMBIA", StringComparison.InvariantCultureIgnoreCase) == 0) { DicConsole.DebugWriteLine("CP/M Plugin", "Don't know how to handle COLUMBIA ordering, not proceeding with this definition."); return(Errno.NotImplemented); } // TODO: Implement EAGLE ordering else if (string.Compare(workingDefinition.order, "EAGLE", StringComparison.InvariantCultureIgnoreCase) == 0) { DicConsole.DebugWriteLine("CP/M Plugin", "Don't know how to handle EAGLE ordering, not proceeding with this definition."); return(Errno.NotImplemented); } else { DicConsole.DebugWriteLine("CP/M Plugin", "Unknown order type \"{0}\", not proceeding with this definition.", workingDefinition.order); return(Errno.NotSupported); } } // Deinterleave whole volume Dictionary <ulong, byte[]> deinterleavedSectors = new Dictionary <ulong, byte[]>(); if (workingDefinition.sides == 1 || string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0) { DicConsole.DebugWriteLine("CP/M Plugin", "Deinterleaving whole volume."); for (int p = 0; p <= (int)(partition.End - partition.Start); p++) { byte[] readSector = device.ReadSector((ulong)((int)partition.Start + p / sectorMask.Length * sectorMask.Length + sectorMask[p % sectorMask.Length])); if (workingDefinition.complement) { for (int b = 0; b < readSector.Length; b++) { readSector[b] = (byte)(~readSector[b] & 0xFF); } } deinterleavedSectors.Add((ulong)p, readSector); } } int blockSize = 128 << dpb.bsh; MemoryStream blockMs = new MemoryStream(); ulong blockNo = 0; int sectorsPerBlock = 0; Dictionary <ulong, byte[]> allocationBlocks = new Dictionary <ulong, byte[]>(); DicConsole.DebugWriteLine("CP/M Plugin", "Creating allocation blocks."); // For each volume sector for (ulong a = 0; a < (ulong)deinterleavedSectors.Count; a++) { deinterleavedSectors.TryGetValue(a, out byte[] sector); // May it happen? Just in case, CP/M blocks are smaller than physical sectors if (sector.Length > blockSize) { for (int i = 0; i < sector.Length / blockSize; i++) { byte[] tmp = new byte[blockSize]; Array.Copy(sector, blockSize * i, tmp, 0, blockSize); allocationBlocks.Add(blockNo++, tmp); } } // CP/M blocks are larger than physical sectors else if (sector.Length < blockSize) { blockMs.Write(sector, 0, sector.Length); sectorsPerBlock++; if (sectorsPerBlock != blockSize / sector.Length) { continue; } allocationBlocks.Add(blockNo++, blockMs.ToArray()); sectorsPerBlock = 0; blockMs = new MemoryStream(); } // CP/M blocks are same size than physical sectors else { allocationBlocks.Add(blockNo++, sector); } } DicConsole.DebugWriteLine("CP/M Plugin", "Reading directory."); int dirOff; int dirSectors = (dpb.drm + 1) * 32 / workingDefinition.bytesPerSector; if (workingDefinition.sofs > 0) { dirOff = workingDefinition.sofs; } else { dirOff = workingDefinition.ofs * workingDefinition.sectorsPerTrack; } // Read the whole directory blocks MemoryStream dirMs = new MemoryStream(); for (int d = 0; d < dirSectors; d++) { deinterleavedSectors.TryGetValue((ulong)(d + dirOff), out byte[] sector); dirMs.Write(sector, 0, sector.Length); } byte[] directory = dirMs.ToArray(); if (directory == null) { return(Errno.InvalidArgument); } int dirCnt = 0; string file1 = null; string file2 = null; string file3 = null; Dictionary <string, Dictionary <int, List <ushort> > > fileExtents = new Dictionary <string, Dictionary <int, List <ushort> > >(); statCache = new Dictionary <string, FileEntryInfo>(); cpmStat = new FileSystemInfo(); bool atime = false; dirList = new List <string>(); labelCreationDate = null; labelUpdateDate = null; passwordCache = new Dictionary <string, byte[]>(); DicConsole.DebugWriteLine("CP/M Plugin", "Traversing directory."); IntPtr dirPtr; // For each directory entry for (int dOff = 0; dOff < directory.Length; dOff += 32) { // Describes a file (does not support PDOS entries with user >= 16, because they're identical to password entries if ((directory[dOff] & 0x7F) < 0x10) { if (allocationBlocks.Count > 256) { dirPtr = Marshal.AllocHGlobal(32); Marshal.Copy(directory, dOff, dirPtr, 32); DirectoryEntry16 entry = (DirectoryEntry16)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry16)); Marshal.FreeHGlobal(dirPtr); bool hidden = (entry.statusUser & 0x80) == 0x80; bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80; bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80; //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80; int user = entry.statusUser & 0x0F; bool validEntry = true; for (int i = 0; i < 8; i++) { entry.filename[i] &= 0x7F; validEntry &= entry.filename[i] >= 0x20; } for (int i = 0; i < 3; i++) { entry.extension[i] &= 0x7F; validEntry &= entry.extension[i] >= 0x20; } if (!validEntry) { continue; } string filename = Encoding.ASCII.GetString(entry.filename).Trim(); string extension = Encoding.ASCII.GetString(entry.extension).Trim(); // If user is != 0, append user to name to have identical filenames if (user > 0) { filename = $"{user:X1}:{filename}"; } if (!string.IsNullOrEmpty(extension)) { filename = filename + "." + extension; } int entryNo = (32 * entry.extentCounter + entry.extentCounterHigh) / (dpb.exm + 1); // Do we have a stat for the file already? if (statCache.TryGetValue(filename, out FileEntryInfo fInfo)) { statCache.Remove(filename); } else { fInfo = new FileEntryInfo { Attributes = new FileAttributes() } }; // And any extent? if (fileExtents.TryGetValue(filename, out Dictionary <int, List <ushort> > extentBlocks)) { fileExtents.Remove(filename); } else { extentBlocks = new Dictionary <int, List <ushort> >(); } // Do we already have this extent? Should never happen if (extentBlocks.TryGetValue(entryNo, out List <ushort> blocks)) { extentBlocks.Remove(entryNo); } else { blocks = new List <ushort>(); } // Attributes if (hidden) { fInfo.Attributes |= FileAttributes.Hidden; } if (rdOnly) { fInfo.Attributes |= FileAttributes.ReadOnly; } if (system) { fInfo.Attributes |= FileAttributes.System; } // Supposedly there is a value in the directory entry telling how many blocks are designated in // this entry. However some implementations tend to do whatever they wish, but none will ever // allocate block 0 for a file because that's where the directory resides. // There is also a field telling how many bytes are used in the last block, but its meaning is // non-standard so we must ignore it. foreach (ushort blk in entry.allocations.Where(blk => !blocks.Contains(blk) && blk != 0)) { blocks.Add(blk); } // Save the file fInfo.UID = (ulong)user; extentBlocks.Add(entryNo, blocks); fileExtents.Add(filename, extentBlocks); statCache.Add(filename, fInfo); // Add the file to the directory listing if (!dirList.Contains(filename)) { dirList.Add(filename); } // Count entries 3 by 3 for timestamps switch (dirCnt % 3) { case 0: file1 = filename; break; case 1: file2 = filename; break; case 2: file3 = filename; break; } dirCnt++; } else { dirPtr = Marshal.AllocHGlobal(32); Marshal.Copy(directory, dOff, dirPtr, 32); DirectoryEntry entry = (DirectoryEntry)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry)); Marshal.FreeHGlobal(dirPtr); bool hidden = (entry.statusUser & 0x80) == 0x80; bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80; bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80; //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80; int user = entry.statusUser & 0x0F; bool validEntry = true; for (int i = 0; i < 8; i++) { entry.filename[i] &= 0x7F; validEntry &= entry.filename[i] >= 0x20; } for (int i = 0; i < 3; i++) { entry.extension[i] &= 0x7F; validEntry &= entry.extension[i] >= 0x20; } if (!validEntry) { continue; } string filename = Encoding.ASCII.GetString(entry.filename).Trim(); string extension = Encoding.ASCII.GetString(entry.extension).Trim(); // If user is != 0, append user to name to have identical filenames if (user > 0) { filename = $"{user:X1}:{filename}"; } if (!string.IsNullOrEmpty(extension)) { filename = filename + "." + extension; } int entryNo = (32 * entry.extentCounterHigh + entry.extentCounter) / (dpb.exm + 1); // Do we have a stat for the file already? if (statCache.TryGetValue(filename, out FileEntryInfo fInfo)) { statCache.Remove(filename); } else { fInfo = new FileEntryInfo { Attributes = new FileAttributes() } }; // And any extent? if (fileExtents.TryGetValue(filename, out Dictionary <int, List <ushort> > extentBlocks)) { fileExtents.Remove(filename); } else { extentBlocks = new Dictionary <int, List <ushort> >(); } // Do we already have this extent? Should never happen if (extentBlocks.TryGetValue(entryNo, out List <ushort> blocks)) { extentBlocks.Remove(entryNo); } else { blocks = new List <ushort>(); } // Attributes if (hidden) { fInfo.Attributes |= FileAttributes.Hidden; } if (rdOnly) { fInfo.Attributes |= FileAttributes.ReadOnly; } if (system) { fInfo.Attributes |= FileAttributes.System; } // Supposedly there is a value in the directory entry telling how many blocks are designated in // this entry. However some implementations tend to do whatever they wish, but none will ever // allocate block 0 for a file because that's where the directory resides. // There is also a field telling how many bytes are used in the last block, but its meaning is // non-standard so we must ignore it. foreach (ushort blk in entry.allocations.Cast <ushort>() .Where(blk => !blocks.Contains(blk) && blk != 0)) { blocks.Add(blk); } // Save the file fInfo.UID = (ulong)user; extentBlocks.Add(entryNo, blocks); fileExtents.Add(filename, extentBlocks); statCache.Add(filename, fInfo); // Add the file to the directory listing if (!dirList.Contains(filename)) { dirList.Add(filename); } // Count entries 3 by 3 for timestamps switch (dirCnt % 3) { case 0: file1 = filename; break; case 1: file2 = filename; break; case 2: file3 = filename; break; } dirCnt++; } } // A password entry (or a file entry in PDOS, but this does not handle that case) else if ((directory[dOff] & 0x7F) >= 0x10 && (directory[dOff] & 0x7F) < 0x20) { dirPtr = Marshal.AllocHGlobal(32); Marshal.Copy(directory, dOff, dirPtr, 32); PasswordEntry entry = (PasswordEntry)Marshal.PtrToStructure(dirPtr, typeof(PasswordEntry)); Marshal.FreeHGlobal(dirPtr); int user = entry.userNumber & 0x0F; for (int i = 0; i < 8; i++) { entry.filename[i] &= 0x7F; } for (int i = 0; i < 3; i++) { entry.extension[i] &= 0x7F; } string filename = Encoding.ASCII.GetString(entry.filename).Trim(); string extension = Encoding.ASCII.GetString(entry.extension).Trim(); // If user is != 0, append user to name to have identical filenames if (user > 0) { filename = $"{user:X1}:{filename}"; } if (!string.IsNullOrEmpty(extension)) { filename = filename + "." + extension; } // Do not repeat passwords if (passwordCache.ContainsKey(filename)) { passwordCache.Remove(filename); } // Copy whole password entry byte[] tmp = new byte[32]; Array.Copy(directory, dOff, tmp, 0, 32); passwordCache.Add(filename, tmp); // Count entries 3 by 3 for timestamps switch (dirCnt % 3) { case 0: file1 = filename; break; case 1: file2 = filename; break; case 2: file3 = filename; break; } dirCnt++; } // Volume label and password entry. Volume password is ignored. else { switch (directory[dOff] & 0x7F) { case 0x20: LabelEntry labelEntry; dirPtr = Marshal.AllocHGlobal(32); Marshal.Copy(directory, dOff, dirPtr, 32); labelEntry = (LabelEntry)Marshal.PtrToStructure(dirPtr, typeof(LabelEntry)); Marshal.FreeHGlobal(dirPtr); // The volume label defines if one of the fields in CP/M 3 timestamp is a creation or an // access time atime |= (labelEntry.flags & 0x40) == 0x40; label = Encoding.ASCII.GetString(directory, dOff + 1, 11).Trim(); labelCreationDate = new byte[4]; labelUpdateDate = new byte[4]; Array.Copy(directory, dOff + 24, labelCreationDate, 0, 4); Array.Copy(directory, dOff + 28, labelUpdateDate, 0, 4); // Count entries 3 by 3 for timestamps switch (dirCnt % 3) { case 0: file1 = null; break; case 1: file2 = null; break; case 2: file3 = null; break; } dirCnt++; break; case 0x21: if (directory[dOff + 10] == 0x00 && directory[dOff + 20] == 0x00 && directory[dOff + 30] == 0x00 && directory[dOff + 31] == 0x00) { dirPtr = Marshal.AllocHGlobal(32); Marshal.Copy(directory, dOff, dirPtr, 32); DateEntry dateEntry = (DateEntry)Marshal.PtrToStructure(dirPtr, typeof(DateEntry)); Marshal.FreeHGlobal(dirPtr); FileEntryInfo fInfo; // Entry contains timestamps for last 3 entries, whatever the kind they are. if (!string.IsNullOrEmpty(file1)) { if (statCache.TryGetValue(file1, out fInfo)) { statCache.Remove(file1); } else { fInfo = new FileEntryInfo(); } if (atime) { fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date1); } else { fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date1); } fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date2); statCache.Add(file1, fInfo); } if (!string.IsNullOrEmpty(file2)) { if (statCache.TryGetValue(file2, out fInfo)) { statCache.Remove(file2); } else { fInfo = new FileEntryInfo(); } if (atime) { fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date3); } else { fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date3); } fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date4); statCache.Add(file2, fInfo); } if (!string.IsNullOrEmpty(file3)) { if (statCache.TryGetValue(file3, out fInfo)) { statCache.Remove(file3); } else { fInfo = new FileEntryInfo(); } if (atime) { fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date5); } else { fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date5); } fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date6); statCache.Add(file3, fInfo); } file1 = null; file2 = null; file3 = null; dirCnt = 0; } // However, if this byte is 0, timestamp is in Z80DOS or DOS+ format else if (directory[dOff + 1] == 0x00) { dirPtr = Marshal.AllocHGlobal(32); Marshal.Copy(directory, dOff, dirPtr, 32); TrdPartyDateEntry trdPartyDateEntry = (TrdPartyDateEntry)Marshal.PtrToStructure(dirPtr, typeof(TrdPartyDateEntry)); Marshal.FreeHGlobal(dirPtr); FileEntryInfo fInfo; // Entry contains timestamps for last 3 entries, whatever the kind they are. if (!string.IsNullOrEmpty(file1)) { if (statCache.TryGetValue(file1, out fInfo)) { statCache.Remove(file1); } else { fInfo = new FileEntryInfo(); } byte[] ctime = new byte[4]; ctime[0] = trdPartyDateEntry.create1[0]; ctime[1] = trdPartyDateEntry.create1[1]; fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access1); fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify1); statCache.Add(file1, fInfo); } if (!string.IsNullOrEmpty(file2)) { if (statCache.TryGetValue(file2, out fInfo)) { statCache.Remove(file2); } else { fInfo = new FileEntryInfo(); } byte[] ctime = new byte[4]; ctime[0] = trdPartyDateEntry.create2[0]; ctime[1] = trdPartyDateEntry.create2[1]; fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access2); fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify2); statCache.Add(file2, fInfo); } if (!string.IsNullOrEmpty(file3)) { if (statCache.TryGetValue(file1, out fInfo)) { statCache.Remove(file3); } else { fInfo = new FileEntryInfo(); } byte[] ctime = new byte[4]; ctime[0] = trdPartyDateEntry.create3[0]; ctime[1] = trdPartyDateEntry.create3[1]; fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access3); fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify3); statCache.Add(file3, fInfo); } file1 = null; file2 = null; file3 = null; dirCnt = 0; } break; } } } // Cache all files. As CP/M maximum volume size is 8 Mib // this should not be a problem DicConsole.DebugWriteLine("CP/M Plugin", "Reading files."); long usedBlocks = 0; fileCache = new Dictionary <string, byte[]>(); foreach (string filename in dirList) { MemoryStream fileMs = new MemoryStream(); if (statCache.TryGetValue(filename, out FileEntryInfo fInfo)) { statCache.Remove(filename); } fInfo.Blocks = 0; if (fileExtents.TryGetValue(filename, out Dictionary <int, List <ushort> > extents)) { for (int ex = 0; ex < extents.Count; ex++) { if (!extents.TryGetValue(ex, out List <ushort> alBlks)) { continue; } foreach (ushort alBlk in alBlks) { allocationBlocks.TryGetValue(alBlk, out byte[] blk); fileMs.Write(blk, 0, blk.Length); fInfo.Blocks++; } } } // If you insist to call CP/M "extent based" fInfo.Attributes |= FileAttributes.Extents; fInfo.BlockSize = blockSize; fInfo.Length = fileMs.Length; cpmStat.Files++; usedBlocks += fInfo.Blocks; statCache.Add(filename, fInfo); fileCache.Add(filename, fileMs.ToArray()); } decodedPasswordCache = new Dictionary <string, byte[]>(); // For each stored password, store a decoded version of it if (passwordCache.Count > 0) { foreach (KeyValuePair <string, byte[]> kvp in passwordCache) { byte[] tmp = new byte[8]; Array.Copy(kvp.Value, 16, tmp, 0, 8); for (int t = 0; t < 8; t++) { tmp[t] ^= kvp.Value[13]; } decodedPasswordCache.Add(kvp.Key, tmp); } } // Generate statfs. cpmStat.Blocks = dpb.dsm + 1; cpmStat.FilenameLength = 11; cpmStat.Files = (ulong)fileCache.Count; cpmStat.FreeBlocks = cpmStat.Blocks - usedBlocks; cpmStat.PluginId = Id; cpmStat.Type = "CP/M filesystem"; // Generate XML info XmlFsType = new FileSystemType { Clusters = cpmStat.Blocks, ClusterSize = blockSize, Files = fileCache.Count, FilesSpecified = true, FreeClusters = cpmStat.FreeBlocks, FreeClustersSpecified = true, Type = "CP/M filesystem" }; if (labelCreationDate != null) { XmlFsType.CreationDate = DateHandlers.CpmToDateTime(labelCreationDate); XmlFsType.CreationDateSpecified = true; } if (labelUpdateDate != null) { XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(labelUpdateDate); XmlFsType.ModificationDateSpecified = true; } if (!string.IsNullOrEmpty(label)) { XmlFsType.VolumeName = label; } mounted = true; return(Errno.NoError); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? new MacRoman(); information = ""; StringBuilder sb = new StringBuilder(); MFS_MasterDirectoryBlock mdb = new MFS_MasterDirectoryBlock(); MFS_BootBlock bb = new MFS_BootBlock(); byte[] pString = new byte[16]; byte[] mdbSector = imagePlugin.ReadSector(2 + partition.Start); byte[] bbSector = imagePlugin.ReadSector(0 + partition.Start); mdb.drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x000); if (mdb.drSigWord != MFS_MAGIC) { return; } mdb.drCrDate = BigEndianBitConverter.ToUInt32(mdbSector, 0x002); mdb.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbSector, 0x006); mdb.drAtrb = BigEndianBitConverter.ToUInt16(mdbSector, 0x00A); mdb.drNmFls = BigEndianBitConverter.ToUInt16(mdbSector, 0x00C); mdb.drDirSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x00E); mdb.drBlLen = BigEndianBitConverter.ToUInt16(mdbSector, 0x010); mdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbSector, 0x012); mdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x014); mdb.drClpSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x018); mdb.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x01C); mdb.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbSector, 0x01E); mdb.drFreeBks = BigEndianBitConverter.ToUInt16(mdbSector, 0x022); mdb.drVNSiz = mdbSector[0x024]; byte[] variableSize = new byte[mdb.drVNSiz + 1]; Array.Copy(mdbSector, 0x024, variableSize, 0, mdb.drVNSiz + 1); mdb.drVN = StringHandlers.PascalToString(variableSize, Encoding); bb.signature = BigEndianBitConverter.ToUInt16(bbSector, 0x000); if (bb.signature == MFSBB_MAGIC) { bb.branch = BigEndianBitConverter.ToUInt32(bbSector, 0x002); bb.boot_flags = bbSector[0x006]; bb.boot_version = bbSector[0x007]; bb.sec_sv_pages = BigEndianBitConverter.ToInt16(bbSector, 0x008); Array.Copy(mdbSector, 0x00A, pString, 0, 16); bb.system_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x01A, pString, 0, 16); bb.finder_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x02A, pString, 0, 16); bb.debug_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x03A, pString, 0, 16); bb.disasm_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x04A, pString, 0, 16); bb.stupscr_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x05A, pString, 0, 16); bb.bootup_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x06A, pString, 0, 16); bb.clipbrd_name = StringHandlers.PascalToString(pString, Encoding); bb.max_files = BigEndianBitConverter.ToUInt16(bbSector, 0x07A); bb.queue_size = BigEndianBitConverter.ToUInt16(bbSector, 0x07C); bb.heap_128k = BigEndianBitConverter.ToUInt32(bbSector, 0x07E); bb.heap_256k = BigEndianBitConverter.ToUInt32(bbSector, 0x082); bb.heap_512k = BigEndianBitConverter.ToUInt32(bbSector, 0x086); } else { bb.signature = 0x0000; } sb.AppendLine("Apple Macintosh File System"); sb.AppendLine(); sb.AppendLine("Master Directory Block:"); sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drLsBkUp)).AppendLine(); if ((mdb.drAtrb & 0x80) == 0x80) { sb.AppendLine("Volume is locked by hardware."); } if ((mdb.drAtrb & 0x8000) == 0x8000) { sb.AppendLine("Volume is locked by software."); } sb.AppendFormat("{0} files on volume", mdb.drNmFls).AppendLine(); sb.AppendFormat("First directory sector: {0}", mdb.drDirSt).AppendLine(); sb.AppendFormat("{0} sectors in directory.", mdb.drBlLen).AppendLine(); sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks + 1).AppendLine(); sb.AppendFormat("Size of allocation blocks: {0} bytes", mdb.drAlBlkSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate.", mdb.drClpSiz).AppendLine(); sb.AppendFormat("First allocation block (#2) starts in sector {0}.", mdb.drAlBlSt).AppendLine(); sb.AppendFormat("Next unused file number: {0}", mdb.drNxtFNum).AppendLine(); sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); sb.AppendFormat("Volume name: {0}", mdb.drVN).AppendLine(); if (bb.signature == MFSBB_MAGIC) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine("Boot Block:"); if ((bb.boot_flags & 0x40) == 0x40) { sb.AppendLine("Boot block should be executed."); } if ((bb.boot_flags & 0x80) == 0x80) { sb.AppendLine("Boot block is in new unknown format."); } else { if (bb.sec_sv_pages > 0) { sb.AppendLine("Allocate secondary sound buffer at boot."); } else if (bb.sec_sv_pages < 0) { sb.AppendLine("Allocate secondary sound and video buffers at boot."); } sb.AppendFormat("System filename: {0}", bb.system_name).AppendLine(); sb.AppendFormat("Finder filename: {0}", bb.finder_name).AppendLine(); sb.AppendFormat("Debugger filename: {0}", bb.debug_name).AppendLine(); sb.AppendFormat("Disassembler filename: {0}", bb.disasm_name).AppendLine(); sb.AppendFormat("Startup screen filename: {0}", bb.stupscr_name).AppendLine(); sb.AppendFormat("First program to execute at boot: {0}", bb.bootup_name).AppendLine(); sb.AppendFormat("Clipboard filename: {0}", bb.clipbrd_name).AppendLine(); sb.AppendFormat("Maximum opened files: {0}", bb.max_files * 4).AppendLine(); sb.AppendFormat("Event queue size: {0}", bb.queue_size).AppendLine(); sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.heap_128k).AppendLine(); sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.heap_256k).AppendLine(); sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.heap_512k).AppendLine(); } } else { sb.AppendLine("Volume is not bootable."); } information = sb.ToString(); XmlFsType = new FileSystemType(); if (mdb.drLsBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drLsBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bb.signature == MFSBB_MAGIC; XmlFsType.Clusters = mdb.drNmAlBlks; XmlFsType.ClusterSize = mdb.drAlBlkSiz; if (mdb.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Files = mdb.drNmFls; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = mdb.drFreeBks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "MFS"; XmlFsType.VolumeName = mdb.drVN; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { // ZFS is always UTF-8 Encoding = Encoding.UTF8; information = ""; if (imagePlugin.Info.SectorSize < 512) { return; } byte[] sector; ulong magic; ulong nvlistOff = 32; uint nvlistLen = 114688 / imagePlugin.Info.SectorSize; if (partition.Start + 31 < partition.End) { sector = imagePlugin.ReadSector(partition.Start + 31); magic = BitConverter.ToUInt64(sector, 0x1D8); if (magic == ZEC_MAGIC || magic == ZEC_CIGAM) { nvlistOff = 32; } } if (partition.Start + 16 < partition.End) { sector = imagePlugin.ReadSector(partition.Start + 16); magic = BitConverter.ToUInt64(sector, 0x1D8); if (magic == ZEC_MAGIC || magic == ZEC_CIGAM) { nvlistOff = 17; } } StringBuilder sb = new StringBuilder(); sb.AppendLine("ZFS filesystem"); byte[] nvlist = imagePlugin.ReadSectors(partition.Start + nvlistOff, nvlistLen); sb.AppendLine(!DecodeNvList(nvlist, out Dictionary <string, NVS_Item> decodedNvList) ? "Could not decode nvlist" : PrintNvList(decodedNvList)); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "ZFS filesystem" }; if (decodedNvList.TryGetValue("name", out NVS_Item tmpObj)) { XmlFsType.VolumeName = (string)tmpObj.value; } if (decodedNvList.TryGetValue("guid", out tmpObj)) { XmlFsType.VolumeSerial = $"{(ulong)tmpObj.value}"; } if (decodedNvList.TryGetValue("pool_guid", out tmpObj)) { XmlFsType.VolumeSetIdentifier = $"{(ulong)tmpObj.value}"; } }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); information = ""; var sb = new StringBuilder(); byte[] hbSector = imagePlugin.ReadSector(1 + partition.Start); HomeBlock homeblock = Marshal.ByteArrayToStructureLittleEndian <HomeBlock>(hbSector); // Optical disc if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc && StringHandlers.CToString(homeblock.format) != "DECFILE11A " && StringHandlers.CToString(homeblock.format) != "DECFILE11B ") { if (hbSector.Length < 0x400) { return; } byte[] tmp = imagePlugin.ReadSector(partition.Start); hbSector = new byte[0x200]; Array.Copy(tmp, 0x200, hbSector, 0, 0x200); homeblock = Marshal.ByteArrayToStructureLittleEndian <HomeBlock>(hbSector); if (StringHandlers.CToString(homeblock.format) != "DECFILE11A " && StringHandlers.CToString(homeblock.format) != "DECFILE11B ") { return; } } if ((homeblock.struclev & 0xFF00) != 0x0200 || (homeblock.struclev & 0xFF) != 1 || StringHandlers.CToString(homeblock.format) != "DECFILE11B ") { sb.AppendLine("The following information may be incorrect for this volume."); } if (homeblock.resfiles < 5 || homeblock.devtype != 0) { sb.AppendLine("This volume may be corrupted."); } sb.AppendFormat("Volume format is {0}", StringHandlers.SpacePaddedToString(homeblock.format, Encoding)). AppendLine(); sb.AppendFormat("Volume is Level {0} revision {1}", (homeblock.struclev & 0xFF00) >> 8, homeblock.struclev & 0xFF).AppendLine(); sb.AppendFormat("Lowest structure in the volume is Level {0}, revision {1}", (homeblock.lowstruclev & 0xFF00) >> 8, homeblock.lowstruclev & 0xFF).AppendLine(); sb.AppendFormat("Highest structure in the volume is Level {0}, revision {1}", (homeblock.highstruclev & 0xFF00) >> 8, homeblock.highstruclev & 0xFF).AppendLine(); sb.AppendFormat("{0} sectors per cluster ({1} bytes)", homeblock.cluster, homeblock.cluster * 512). AppendLine(); sb.AppendFormat("This home block is on sector {0} (VBN {1})", homeblock.homelbn, homeblock.homevbn). AppendLine(); sb.AppendFormat("Secondary home block is on sector {0} (VBN {1})", homeblock.alhomelbn, homeblock.alhomevbn).AppendLine(); sb.AppendFormat("Volume bitmap starts in sector {0} (VBN {1})", homeblock.ibmaplbn, homeblock.ibmapvbn). AppendLine(); sb.AppendFormat("Volume bitmap runs for {0} sectors ({1} bytes)", homeblock.ibmapsize, homeblock.ibmapsize * 512).AppendLine(); sb.AppendFormat("Backup INDEXF.SYS;1 is in sector {0} (VBN {1})", homeblock.altidxlbn, homeblock.altidxvbn). AppendLine(); sb.AppendFormat("{0} maximum files on the volume", homeblock.maxfiles).AppendLine(); sb.AppendFormat("{0} reserved files", homeblock.resfiles).AppendLine(); if (homeblock.rvn > 0 && homeblock.setcount > 0 && StringHandlers.CToString(homeblock.strucname) != " ") { sb.AppendFormat("Volume is {0} of {1} in set \"{2}\".", homeblock.rvn, homeblock.setcount, StringHandlers.SpacePaddedToString(homeblock.strucname, Encoding)).AppendLine(); } sb.AppendFormat("Volume owner is \"{0}\" (ID 0x{1:X8})", StringHandlers.SpacePaddedToString(homeblock.ownername, Encoding), homeblock.volowner). AppendLine(); sb.AppendFormat("Volume label: \"{0}\"", StringHandlers.SpacePaddedToString(homeblock.volname, Encoding)). AppendLine(); sb.AppendFormat("Drive serial number: 0x{0:X8}", homeblock.serialnum).AppendLine(); sb.AppendFormat("Volume was created on {0}", DateHandlers.VmsToDateTime(homeblock.credate)).AppendLine(); if (homeblock.revdate > 0) { sb.AppendFormat("Volume was last modified on {0}", DateHandlers.VmsToDateTime(homeblock.revdate)). AppendLine(); } if (homeblock.copydate > 0) { sb.AppendFormat("Volume copied on {0}", DateHandlers.VmsToDateTime(homeblock.copydate)).AppendLine(); } sb.AppendFormat("Checksums: 0x{0:X4} and 0x{1:X4}", homeblock.checksum1, homeblock.checksum2).AppendLine(); sb.AppendLine("Flags:"); sb.AppendFormat("Window: {0}", homeblock.window).AppendLine(); sb.AppendFormat("Cached directores: {0}", homeblock.lru_lim).AppendLine(); sb.AppendFormat("Default allocation: {0} blocks", homeblock.extend).AppendLine(); if ((homeblock.volchar & 0x01) == 0x01) { sb.AppendLine("Readings should be verified"); } if ((homeblock.volchar & 0x02) == 0x02) { sb.AppendLine("Writings should be verified"); } if ((homeblock.volchar & 0x04) == 0x04) { sb.AppendLine("Files should be erased or overwritten when deleted"); } if ((homeblock.volchar & 0x08) == 0x08) { sb.AppendLine("Highwater mark is to be disabled"); } if ((homeblock.volchar & 0x10) == 0x10) { sb.AppendLine("Classification checks are enabled"); } sb.AppendLine("Volume permissions (r = read, w = write, c = create, d = delete)"); sb.AppendLine("System, owner, group, world"); // System sb.Append((homeblock.protect & 0x1000) == 0x1000 ? "-" : "r"); sb.Append((homeblock.protect & 0x2000) == 0x2000 ? "-" : "w"); sb.Append((homeblock.protect & 0x4000) == 0x4000 ? "-" : "c"); sb.Append((homeblock.protect & 0x8000) == 0x8000 ? "-" : "d"); // Owner sb.Append((homeblock.protect & 0x100) == 0x100 ? "-" : "r"); sb.Append((homeblock.protect & 0x200) == 0x200 ? "-" : "w"); sb.Append((homeblock.protect & 0x400) == 0x400 ? "-" : "c"); sb.Append((homeblock.protect & 0x800) == 0x800 ? "-" : "d"); // Group sb.Append((homeblock.protect & 0x10) == 0x10 ? "-" : "r"); sb.Append((homeblock.protect & 0x20) == 0x20 ? "-" : "w"); sb.Append((homeblock.protect & 0x40) == 0x40 ? "-" : "c"); sb.Append((homeblock.protect & 0x80) == 0x80 ? "-" : "d"); // World (other) sb.Append((homeblock.protect & 0x1) == 0x1 ? "-" : "r"); sb.Append((homeblock.protect & 0x2) == 0x2 ? "-" : "w"); sb.Append((homeblock.protect & 0x4) == 0x4 ? "-" : "c"); sb.Append((homeblock.protect & 0x8) == 0x8 ? "-" : "d"); sb.AppendLine(); sb.AppendLine("Unknown structures:"); sb.AppendFormat("Security mask: 0x{0:X8}", homeblock.sec_mask).AppendLine(); sb.AppendFormat("File protection: 0x{0:X4}", homeblock.fileprot).AppendLine(); sb.AppendFormat("Record protection: 0x{0:X4}", homeblock.recprot).AppendLine(); XmlFsType = new FileSystemType { Type = "FILES-11", ClusterSize = (uint)(homeblock.cluster * 512), Clusters = partition.Size / (ulong)(homeblock.cluster * 512), VolumeName = StringHandlers.SpacePaddedToString(homeblock.volname, Encoding), VolumeSerial = $"{homeblock.serialnum:X8}" }; if (homeblock.credate > 0) { XmlFsType.CreationDate = DateHandlers.VmsToDateTime(homeblock.credate); XmlFsType.CreationDateSpecified = true; } if (homeblock.revdate > 0) { XmlFsType.ModificationDate = DateHandlers.VmsToDateTime(homeblock.revdate); XmlFsType.ModificationDateSpecified = true; } information = sb.ToString(); }
public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset) { partitions = new List <Partition>(); uint nSectors = 2048 / imagePlugin.Info.SectorSize; if (2048 % imagePlugin.Info.SectorSize > 0) { nSectors++; } if (sectorOffset + nSectors >= imagePlugin.Info.Sectors) { return(false); } byte[] sectors = imagePlugin.ReadSectors(sectorOffset, nSectors); if (sectors.Length < 2048) { return(false); } IntPtr labelPtr = Marshal.AllocHGlobal(2048); Marshal.Copy(sectors, 0, labelPtr, 2048); Disklabel64 disklabel = (Disklabel64)Marshal.PtrToStructure(labelPtr, typeof(Disklabel64)); Marshal.FreeHGlobal(labelPtr); if (disklabel.d_magic != 0xC4464C59) { return(false); } ulong counter = 0; foreach (Partition64 entry in disklabel.d_partitions) { Partition part = new Partition { Start = entry.p_boffset / imagePlugin.Info.SectorSize + sectorOffset, Offset = entry.p_boffset + sectorOffset * imagePlugin.Info.SectorSize, Size = entry.p_bsize, Length = entry.p_bsize / imagePlugin.Info.SectorSize, Name = entry.p_stor_uuid.ToString(), Sequence = counter, Scheme = Name, Type = (BSD.fsType)entry.p_fstype == BSD.fsType.Other ? entry.p_type_uuid.ToString() : BSD.fsTypeToString((BSD.fsType)entry.p_fstype) }; if (entry.p_bsize % imagePlugin.Info.SectorSize > 0) { part.Length++; } if (entry.p_bsize <= 0 || entry.p_boffset <= 0) { continue; } partitions.Add(part); counter++; } return(true); }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { device = imagePlugin; partitionStart = partition.Start; Encoding = encoding ?? Encoding.GetEncoding("macintosh"); if (options == null) { options = GetDefaultOptions(); } if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out debug); } volMDB = new MFS_MasterDirectoryBlock(); mdbBlocks = device.ReadSector(2 + partitionStart); bootBlocks = device.ReadSector(0 + partitionStart); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; volMDB.drSigWord = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x000); if (volMDB.drSigWord != MFS_MAGIC) { return(Errno.InvalidArgument); } volMDB.drCrDate = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x002); volMDB.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x006); volMDB.drAtrb = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00A); volMDB.drNmFls = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00C); volMDB.drDirSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00E); volMDB.drBlLen = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x010); volMDB.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x012); volMDB.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x014); volMDB.drClpSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x018); volMDB.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x01C); volMDB.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x01E); volMDB.drFreeBks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x022); volMDB.drVNSiz = mdbBlocks[0x024]; byte[] variableSize = new byte[volMDB.drVNSiz + 1]; Array.Copy(mdbBlocks, 0x024, variableSize, 0, volMDB.drVNSiz + 1); volMDB.drVN = StringHandlers.PascalToString(variableSize, Encoding); directoryBlocks = device.ReadSectors(volMDB.drDirSt + partitionStart, volMDB.drBlLen); int bytesInBlockMap = volMDB.drNmAlBlks * 12 / 8 + volMDB.drNmAlBlks * 12 % 8; const int BYTES_BEFORE_BLOCK_MAP = 64; int bytesInWholeMdb = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP; int sectorsInWholeMdb = bytesInWholeMdb / (int)device.Info.SectorSize + bytesInWholeMdb % (int)device.Info.SectorSize; byte[] wholeMdb = device.ReadSectors(partitionStart + 2, (uint)sectorsInWholeMdb); blockMapBytes = new byte[bytesInBlockMap]; Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, blockMapBytes, 0, blockMapBytes.Length); int offset = 0; blockMap = new uint[volMDB.drNmAlBlks + 2 + 1]; for (int i = 2; i < volMDB.drNmAlBlks + 2; i += 8) { uint tmp1 = 0; uint tmp2 = 0; uint tmp3 = 0; if (offset + 4 <= blockMapBytes.Length) { tmp1 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset); } if (offset + 4 + 4 <= blockMapBytes.Length) { tmp2 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 4); } if (offset + 8 + 4 <= blockMapBytes.Length) { tmp3 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 8); } if (i < blockMap.Length) { blockMap[i] = (tmp1 & 0xFFF00000) >> 20; } if (i + 2 < blockMap.Length) { blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8; } if (i + 3 < blockMap.Length) { blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28); } if (i + 4 < blockMap.Length) { blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16; } if (i + 5 < blockMap.Length) { blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4; } if (i + 6 < blockMap.Length) { blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24); } if (i + 7 < blockMap.Length) { blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12; } if (i + 8 < blockMap.Length) { blockMap[i + 7] = tmp3 & 0xFFF; } offset += 12; } if (device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) { mdbTags = device.ReadSectorTag(2 + partitionStart, SectorTagType.AppleSectorTag); bootTags = device.ReadSectorTag(0 + partitionStart, SectorTagType.AppleSectorTag); directoryTags = device.ReadSectorsTag(volMDB.drDirSt + partitionStart, volMDB.drBlLen, SectorTagType.AppleSectorTag); bitmapTags = device.ReadSectorsTag(partitionStart + 2, (uint)sectorsInWholeMdb, SectorTagType.AppleSectorTag); } sectorsPerBlock = (int)(volMDB.drAlBlkSiz / device.Info.SectorSize); if (!FillDirectory()) { return(Errno.InvalidArgument); } mounted = true; ushort bbSig = BigEndianBitConverter.ToUInt16(bootBlocks, 0x000); if (bbSig != MFSBB_MAGIC) { bootBlocks = null; } XmlFsType = new FileSystemType(); if (volMDB.drLsBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(volMDB.drLsBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bbSig == MFSBB_MAGIC; XmlFsType.Clusters = volMDB.drNmAlBlks; XmlFsType.ClusterSize = volMDB.drAlBlkSiz; if (volMDB.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(volMDB.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Files = volMDB.drNmFls; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = volMDB.drFreeBks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "MFS"; XmlFsType.VolumeName = volMDB.drVN; return(Errno.NoError); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? 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(); }
public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset) { partitions = new List <Partition>(); ulong sbSector; // RISC OS always checks for the partition on 0. Afaik no emulator chains it. if (sectorOffset != 0) { return(false); } if (imagePlugin.Info.SectorSize > ADFS_SB_POS) { sbSector = 0; } else { sbSector = ADFS_SB_POS / imagePlugin.Info.SectorSize; } byte[] sector = imagePlugin.ReadSector(sbSector); if (sector.Length < 512) { return(false); } IntPtr bbPtr = Marshal.AllocHGlobal(512); Marshal.Copy(sector, 0, bbPtr, 512); AcornBootBlock bootBlock = (AcornBootBlock)Marshal.PtrToStructure(bbPtr, typeof(AcornBootBlock)); Marshal.FreeHGlobal(bbPtr); int checksum = 0; for (int i = 0; i < 0x1FF; i++) { checksum = (checksum & 0xFF) + (checksum >> 8) + sector[i]; } int heads = bootBlock.discRecord.heads + ((bootBlock.discRecord.lowsector >> 6) & 1); int secCyl = bootBlock.discRecord.spt * heads; int mapSector = bootBlock.startCylinder * secCyl; if ((ulong)mapSector >= imagePlugin.Info.Sectors) { return(false); } byte[] map = imagePlugin.ReadSector((ulong)mapSector); ulong counter = 0; if (checksum == bootBlock.checksum) { Partition part = new Partition { Size = (ulong)bootBlock.discRecord.disc_size_high * 0x100000000 + bootBlock.discRecord.disc_size, Length = ((ulong)bootBlock.discRecord.disc_size_high * 0x100000000 + bootBlock.discRecord.disc_size) / imagePlugin.Info.SectorSize, Type = "ADFS", Name = StringHandlers.CToString(bootBlock.discRecord.disc_name, Encoding.GetEncoding("iso-8859-1")) }; if (part.Size > 0) { partitions.Add(part); counter++; } } switch (bootBlock.flags & TYPE_MASK) { case TYPE_LINUX: { IntPtr tablePtr = Marshal.AllocHGlobal(512); Marshal.Copy(map, 0, tablePtr, 512); LinuxTable table = (LinuxTable)Marshal.PtrToStructure(tablePtr, typeof(LinuxTable)); Marshal.FreeHGlobal(tablePtr); foreach (LinuxEntry entry in table.entries) { Partition part = new Partition { Start = (ulong)(mapSector + entry.start), Size = entry.size, Length = (ulong)(entry.size * sector.Length), Sequence = counter, Scheme = Name }; part.Offset = part.Start * (ulong)sector.Length; if (entry.magic != LINUX_MAGIC && entry.magic != SWAP_MAGIC) { continue; } partitions.Add(part); counter++; } break; } case TYPE_RISCIX_MFM: case TYPE_RISCIX_SCSI: { IntPtr tablePtr = Marshal.AllocHGlobal(512); Marshal.Copy(map, 0, tablePtr, 512); RiscIxTable table = (RiscIxTable)Marshal.PtrToStructure(tablePtr, typeof(RiscIxTable)); Marshal.FreeHGlobal(tablePtr); if (table.magic == RISCIX_MAGIC) { foreach (RiscIxEntry entry in table.partitions) { Partition part = new Partition { Start = (ulong)(mapSector + entry.start), Size = entry.length, Length = (ulong)(entry.length * sector.Length), Name = StringHandlers.CToString(entry.name, Encoding.GetEncoding("iso-8859-1")), Sequence = counter, Scheme = Name }; part.Offset = part.Start * (ulong)sector.Length; if (entry.length <= 0) { continue; } partitions.Add(part); counter++; } } break; } } return(partitions.Count != 0); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; var sb = new StringBuilder(); var besb = new SuperBlock(); byte[] sbSector = imagePlugin.ReadSector(0 + partition.Start); bool littleEndian; besb.magic1 = BigEndianBitConverter.ToUInt32(sbSector, 0x20); if (besb.magic1 == BEFS_MAGIC1 || besb.magic1 == BEFS_CIGAM1) // Magic is at offset { littleEndian = besb.magic1 == BEFS_CIGAM1; } else { sbSector = imagePlugin.ReadSector(1 + partition.Start); besb.magic1 = BigEndianBitConverter.ToUInt32(sbSector, 0x20); if (besb.magic1 == BEFS_MAGIC1 || besb.magic1 == BEFS_CIGAM1) // There is a boot sector { littleEndian = besb.magic1 == BEFS_CIGAM1; } else if (sbSector.Length >= 0x400) { byte[] temp = imagePlugin.ReadSector(0 + partition.Start); besb.magic1 = BigEndianBitConverter.ToUInt32(temp, 0x220); if (besb.magic1 == BEFS_MAGIC1 || besb.magic1 == BEFS_CIGAM1) // There is a boot sector { littleEndian = besb.magic1 == BEFS_CIGAM1; sbSector = new byte[0x200]; Array.Copy(temp, 0x200, sbSector, 0, 0x200); } else { return; } } else { return; } } besb = littleEndian ? Marshal.ByteArrayToStructureLittleEndian <SuperBlock>(sbSector) : Marshal.ByteArrayToStructureBigEndian <SuperBlock>(sbSector); sb.AppendLine(littleEndian ? "Little-endian BeFS" : "Big-endian BeFS"); if (besb.magic1 != BEFS_MAGIC1 || besb.fs_byte_order != BEFS_ENDIAN || besb.magic2 != BEFS_MAGIC2 || besb.magic3 != BEFS_MAGIC3 || besb.root_dir_len != 1 || besb.indices_len != 1 || 1 << (int)besb.block_shift != besb.block_size) { sb.AppendLine("Superblock seems corrupt, following information may be incorrect"); sb.AppendFormat("Magic 1: 0x{0:X8} (Should be 0x42465331)", besb.magic1).AppendLine(); sb.AppendFormat("Magic 2: 0x{0:X8} (Should be 0xDD121031)", besb.magic2).AppendLine(); sb.AppendFormat("Magic 3: 0x{0:X8} (Should be 0x15B6830E)", besb.magic3).AppendLine(); sb.AppendFormat("Filesystem endianness: 0x{0:X8} (Should be 0x42494745)", besb.fs_byte_order). AppendLine(); sb.AppendFormat("Root folder's i-node size: {0} blocks (Should be 1)", besb.root_dir_len).AppendLine(); sb.AppendFormat("Indices' i-node size: {0} blocks (Should be 1)", besb.indices_len).AppendLine(); sb.AppendFormat("1 << block_shift == block_size => 1 << {0} == {1} (Should be {2})", besb.block_shift, 1 << (int)besb.block_shift, besb.block_size).AppendLine(); } switch (besb.flags) { case BEFS_CLEAN: sb.AppendLine(besb.log_start == besb.log_end ? "Filesystem is clean" : "Filesystem is dirty"); break; case BEFS_DIRTY: sb.AppendLine("Filesystem is dirty"); break; default: sb.AppendFormat("Unknown flags: {0:X8}", besb.flags).AppendLine(); break; } sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(besb.name, Encoding)).AppendLine(); sb.AppendFormat("{0} bytes per block", besb.block_size).AppendLine(); sb.AppendFormat("{0} blocks in volume ({1} bytes)", besb.num_blocks, besb.num_blocks * besb.block_size). AppendLine(); sb.AppendFormat("{0} used blocks ({1} bytes)", besb.used_blocks, besb.used_blocks * besb.block_size). AppendLine(); sb.AppendFormat("{0} bytes per i-node", besb.inode_size).AppendLine(); sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", besb.blocks_per_ag, besb.blocks_per_ag * besb.block_size).AppendLine(); sb.AppendFormat("{0} allocation groups in volume", besb.num_ags).AppendLine(); sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", besb.log_blocks_start, besb.log_blocks_ag, besb.log_blocks_len, besb.log_blocks_len * besb.block_size).AppendLine(); sb.AppendFormat("Journal starts in byte {0} and ends in byte {1}", besb.log_start, besb.log_end). AppendLine(); sb. AppendFormat("Root folder's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", besb.root_dir_start, besb.root_dir_ag, besb.root_dir_len, besb.root_dir_len * besb.block_size).AppendLine(); sb. AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", besb.indices_start, besb.indices_ag, besb.indices_len, besb.indices_len * besb.block_size). AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Clusters = (ulong)besb.num_blocks, ClusterSize = besb.block_size, Dirty = besb.flags == BEFS_DIRTY, FreeClusters = (ulong)(besb.num_blocks - besb.used_blocks), FreeClustersSpecified = true, Type = "BeFS", VolumeName = StringHandlers.CToString(besb.name, Encoding) }; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; byte[] sector = imagePlugin.ReadSector(partition.Start + 1); if (sector.Length < 512) { return; } QNX4_Superblock qnxSb = Marshal.ByteArrayToStructureLittleEndian <QNX4_Superblock>(sector); // Too much useless information /* * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_fname = {0}", CurrentEncoding.GetString(qnxSb.rootDir.di_fname)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_size = {0}", qnxSb.rootDir.di_size); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_first_xtnt.block = {0}", qnxSb.rootDir.di_first_xtnt.block); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_first_xtnt.length = {0}", qnxSb.rootDir.di_first_xtnt.length); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_xblk = {0}", qnxSb.rootDir.di_xblk); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_ftime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_mtime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_atime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_ctime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_num_xtnts = {0}", qnxSb.rootDir.di_num_xtnts); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_mode = {0}", Convert.ToString(qnxSb.rootDir.di_mode, 8)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_uid = {0}", qnxSb.rootDir.di_uid); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_gid = {0}", qnxSb.rootDir.di_gid); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_nlink = {0}", qnxSb.rootDir.di_nlink); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_zero = {0}", qnxSb.rootDir.di_zero); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_type = {0}", qnxSb.rootDir.di_type); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_status = {0}", qnxSb.rootDir.di_status); * * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_fname = {0}", CurrentEncoding.GetString(qnxSb.inode.di_fname)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_size = {0}", qnxSb.inode.di_size); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_first_xtnt.block = {0}", qnxSb.inode.di_first_xtnt.block); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_first_xtnt.length = {0}", qnxSb.inode.di_first_xtnt.length); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_xblk = {0}", qnxSb.inode.di_xblk); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_ftime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_mtime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_atime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_ctime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_num_xtnts = {0}", qnxSb.inode.di_num_xtnts); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_mode = {0}", Convert.ToString(qnxSb.inode.di_mode, 8)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_uid = {0}", qnxSb.inode.di_uid); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_gid = {0}", qnxSb.inode.di_gid); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_nlink = {0}", qnxSb.inode.di_nlink); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_zero = {0}", qnxSb.inode.di_zero); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_type = {0}", qnxSb.inode.di_type); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_status = {0}", qnxSb.inode.di_status); * * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_fname = {0}", CurrentEncoding.GetString(qnxSb.boot.di_fname)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_size = {0}", qnxSb.boot.di_size); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_first_xtnt.block = {0}", qnxSb.boot.di_first_xtnt.block); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_first_xtnt.length = {0}", qnxSb.boot.di_first_xtnt.length); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_xblk = {0}", qnxSb.boot.di_xblk); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_ftime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_mtime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_atime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_ctime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_num_xtnts = {0}", qnxSb.boot.di_num_xtnts); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_mode = {0}", Convert.ToString(qnxSb.boot.di_mode, 8)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_uid = {0}", qnxSb.boot.di_uid); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_gid = {0}", qnxSb.boot.di_gid); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_nlink = {0}", qnxSb.boot.di_nlink); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_zero = {0}", qnxSb.boot.di_zero); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_type = {0}", qnxSb.boot.di_type); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_status = {0}", qnxSb.boot.di_status); * * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_fname = {0}", CurrentEncoding.GetString(qnxSb.altBoot.di_fname)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_size = {0}", qnxSb.altBoot.di_size); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_first_xtnt.block = {0}", qnxSb.altBoot.di_first_xtnt.block); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_first_xtnt.length = {0}", qnxSb.altBoot.di_first_xtnt.length); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_xblk = {0}", qnxSb.altBoot.di_xblk); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_ftime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_mtime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_atime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_ctime)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_num_xtnts = {0}", qnxSb.altBoot.di_num_xtnts); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_mode = {0}", Convert.ToString(qnxSb.altBoot.di_mode, 8)); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_uid = {0}", qnxSb.altBoot.di_uid); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_gid = {0}", qnxSb.altBoot.di_gid); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_nlink = {0}", qnxSb.altBoot.di_nlink); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_zero = {0}", qnxSb.altBoot.di_zero); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_type = {0}", qnxSb.altBoot.di_type); * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_status = {0}", qnxSb.altBoot.di_status); */ information = $"QNX4 filesystem\nCreated on {DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_ftime)}\n"; XmlFsType = new FileSystemType { Type = "QNX4 filesystem", Clusters = partition.Length, ClusterSize = 512, CreationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_ftime), CreationDateSpecified = true, ModificationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_mtime), ModificationDateSpecified = true }; XmlFsType.Bootable |= qnxSb.boot.di_size != 0 || qnxSb.altBoot.di_size != 0; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("IBM437"); information = ""; var sb = new StringBuilder(); XmlFsType = new FileSystemType(); uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; byte[] bpbSector = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb); BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition, out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, out byte minBootNearJump, out bool andosOemCorrect, out bool bootable); bool isFat12 = false; bool isFat16 = false; bool isFat32 = false; ulong rootDirectorySector = 0; string extraInfo = null; string bootChk = null; XmlFsType.Bootable = bootable; // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field. uint sectorsPerRealSector; // This is needed because some OSes don't put volume label as first entry in the root directory uint sectorsForRootDirectory = 0; switch (bpbKind) { case BpbKind.DecRainbow: case BpbKind.Hardcoded: case BpbKind.Msx: case BpbKind.Apricot: isFat12 = true; break; case BpbKind.ShortFat32: case BpbKind.LongFat32: { isFat32 = true; Fat32ParameterBlock fat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlock>(bpbSector); Fat32ParameterBlockShort shortFat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlockShort>(bpbSector); // This is to support FAT partitions on hybrid ISO/USB images if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { fat32Bpb.bps *= 4; fat32Bpb.spc /= 4; fat32Bpb.big_spfat /= 4; fat32Bpb.hsectors /= 4; fat32Bpb.sptrk /= 4; } if (fat32Bpb.version != 0) { sb.AppendLine("FAT+"); XmlFsType.Type = "FAT+"; } else { sb.AppendLine("Microsoft FAT32"); XmlFsType.Type = "FAT32"; } if (fat32Bpb.oem_name != null) { if (fat32Bpb.oem_name[5] == 0x49 && fat32Bpb.oem_name[6] == 0x48 && fat32Bpb.oem_name[7] == 0x43) { sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); } else { XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name); } } if (!string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) { sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); } sb.AppendFormat("{0} bytes per sector.", fat32Bpb.bps).AppendLine(); sb.AppendFormat("{0} sectors per cluster.", fat32Bpb.spc).AppendLine(); XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc); sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fat32Bpb.rsectors).AppendLine(); if (fat32Bpb.big_sectors == 0 && fat32Bpb.signature == 0x28) { sb.AppendFormat("{0} sectors on volume ({1} bytes).", shortFat32Bpb.huge_sectors, shortFat32Bpb.huge_sectors * shortFat32Bpb.bps).AppendLine(); XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc; } else { sb.AppendFormat("{0} sectors on volume ({1} bytes).", fat32Bpb.big_sectors, fat32Bpb.big_sectors * fat32Bpb.bps).AppendLine(); XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc; } sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); sb.AppendFormat("Media descriptor: 0x{0:X2}", fat32Bpb.media).AppendLine(); sb.AppendFormat("{0} sectors per FAT.", fat32Bpb.big_spfat).AppendLine(); sb.AppendFormat("{0} sectors per track.", fat32Bpb.sptrk).AppendLine(); sb.AppendFormat("{0} heads.", fat32Bpb.heads).AppendLine(); sb.AppendFormat("{0} hidden sectors before BPB.", fat32Bpb.hsectors).AppendLine(); sb.AppendFormat("Cluster of root directory: {0}", fat32Bpb.root_cluster).AppendLine(); sb.AppendFormat("Sector of FSINFO structure: {0}", fat32Bpb.fsinfo_sector).AppendLine(); sb.AppendFormat("Sector of backup FAT32 parameter block: {0}", fat32Bpb.backup_sector).AppendLine(); sb.AppendFormat("Drive number: 0x{0:X2}", fat32Bpb.drive_no).AppendLine(); sb.AppendFormat("Volume Serial Number: 0x{0:X8}", fat32Bpb.serial_no).AppendLine(); XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}"; if ((fat32Bpb.flags & 0xF8) == 0x00) { if ((fat32Bpb.flags & 0x01) == 0x01) { sb.AppendLine("Volume should be checked on next mount."); XmlFsType.Dirty = true; } if ((fat32Bpb.flags & 0x02) == 0x02) { sb.AppendLine("Disk surface should be on next mount."); } } if ((fat32Bpb.mirror_flags & 0x80) == 0x80) { sb.AppendFormat("FATs are out of sync. FAT #{0} is in use.", fat32Bpb.mirror_flags & 0xF). AppendLine(); } else { sb.AppendLine("All copies of FAT are the same."); } if ((fat32Bpb.mirror_flags & 0x6F20) == 0x6F20) { sb.AppendLine("DR-DOS will boot this FAT32 using CHS."); } else if ((fat32Bpb.mirror_flags & 0x4F20) == 0x4F20) { sb.AppendLine("DR-DOS will boot this FAT32 using LBA."); } if (fat32Bpb.signature == 0x29) { XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fat32Bpb.volume_label, Encoding); XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fat32Bpb.fs_type)). AppendLine(); bootChk = Sha1Context.Data(fat32Bpb.boot_code, out _); } else { bootChk = Sha1Context.Data(shortFat32Bpb.boot_code, out _); } // Check that jumps to a correct boot code position and has boot signature set. // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... XmlFsType.Bootable = (fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80) || (fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 && BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump && BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC); sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize; // First root directory sector rootDirectorySector = (ulong)(((fat32Bpb.root_cluster - 2) * fat32Bpb.spc) + (fat32Bpb.big_spfat * fat32Bpb.fats_no) + fat32Bpb.rsectors) * sectorsPerRealSector; sectorsForRootDirectory = 1; if (fat32Bpb.fsinfo_sector + partition.Start <= partition.End) { byte[] fsinfoSector = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start); FsInfoSector fsInfo = Marshal.ByteArrayToStructureLittleEndian <FsInfoSector>(fsinfoSector); if (fsInfo.signature1 == FSINFO_SIGNATURE1 && fsInfo.signature2 == FSINFO_SIGNATURE2 && fsInfo.signature3 == FSINFO_SIGNATURE3) { if (fsInfo.free_clusters < 0xFFFFFFFF) { sb.AppendFormat("{0} free clusters", fsInfo.free_clusters).AppendLine(); XmlFsType.FreeClusters = fsInfo.free_clusters; XmlFsType.FreeClustersSpecified = true; } if (fsInfo.last_cluster > 2 && fsInfo.last_cluster < 0xFFFFFFFF) { sb.AppendFormat("Last allocated cluster {0}", fsInfo.last_cluster).AppendLine(); } } } break; } // Some fields could overflow fake BPB, those will be handled below case BpbKind.Atari: { ushort sum = 0; for (int i = 0; i < bpbSector.Length; i += 2) { sum += BigEndianBitConverter.ToUInt16(bpbSector, i); } // TODO: Check this if (sum == 0x1234) { XmlFsType.Bootable = true; var atariSb = new StringBuilder(); atariSb.AppendFormat("cmdload will be loaded with value {0:X4}h", BigEndianBitConverter.ToUInt16(bpbSector, 0x01E)).AppendLine(); atariSb.AppendFormat("Boot program will be loaded at address {0:X4}h", atariBpb.ldaaddr). AppendLine(); atariSb.AppendFormat("FAT and directory will be cached at address {0:X4}h", atariBpb.fatbuf). AppendLine(); if (atariBpb.ldmode == 0) { byte[] tmp = new byte[8]; Array.Copy(atariBpb.fname, 0, tmp, 0, 8); string fname = Encoding.ASCII.GetString(tmp).Trim(); tmp = new byte[3]; Array.Copy(atariBpb.fname, 8, tmp, 0, 3); string extension = Encoding.ASCII.GetString(tmp).Trim(); string filename; if (string.IsNullOrEmpty(extension)) { filename = fname; } else { filename = fname + "." + extension; } atariSb.AppendFormat("Boot program resides in file \"{0}\"", filename).AppendLine(); } else { atariSb. AppendFormat("Boot program starts in sector {0} and is {1} sectors long ({2} bytes)", atariBpb.ssect, atariBpb.sectcnt, atariBpb.sectcnt * atariBpb.bps). AppendLine(); } extraInfo = atariSb.ToString(); } break; } case BpbKind.Human: XmlFsType.Bootable = true; break; } if (!isFat32) { // This is to support FAT partitions on hybrid ISO/USB images if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { fakeBpb.bps *= 4; fakeBpb.spc /= 4; fakeBpb.spfat /= 4; fakeBpb.hsectors /= 4; fakeBpb.sptrk /= 4; fakeBpb.rsectors /= 4; if (fakeBpb.spc == 0) { fakeBpb.spc = 1; } } // This assumes no sane implementation will violate cluster size rules // However nothing prevents this to happen // If first file on disk uses only one cluster there is absolutely no way to differentiate between FAT12 and FAT16, // so let's hope implementations use common sense? if (!isFat12 && !isFat16) { ulong clusters; if (fakeBpb.sectors == 0) { clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc; } else { clusters = fakeBpb.spc == 0 ? fakeBpb.sectors : (ulong)fakeBpb.sectors / fakeBpb.spc; } if (clusters < 4089) { isFat12 = true; } else { isFat16 = true; } } if (isFat12) { switch (bpbKind) { case BpbKind.Atari: sb.AppendLine("Atari FAT12"); break; case BpbKind.Apricot: sb.AppendLine("Apricot FAT12"); break; case BpbKind.Human: sb.AppendLine("Human68k FAT12"); break; default: sb.AppendLine("Microsoft FAT12"); break; } XmlFsType.Type = "FAT12"; } else if (isFat16) { sb.AppendLine(bpbKind == BpbKind.Atari ? "Atari FAT16" : bpbKind == BpbKind.Human ? "Human68k FAT16" : "Microsoft FAT16"); XmlFsType.Type = "FAT16"; } if (bpbKind == BpbKind.Atari) { if (atariBpb.serial_no[0] == 0x49 && atariBpb.serial_no[1] == 0x48 && atariBpb.serial_no[2] == 0x43) { sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); } else { XmlFsType.VolumeSerial = $"{atariBpb.serial_no[0]:X2}{atariBpb.serial_no[1]:X2}{atariBpb.serial_no[2]:X2}"; } XmlFsType.SystemIdentifier = StringHandlers.CToString(atariBpb.oem_name); if (string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) { XmlFsType.SystemIdentifier = null; } } else if (fakeBpb.oem_name != null) { if (fakeBpb.oem_name[5] == 0x49 && fakeBpb.oem_name[6] == 0x48 && fakeBpb.oem_name[7] == 0x43) { sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); } else { // Later versions of Windows create a DOS 3 BPB without OEM name on 8 sectors/track floppies // OEM ID should be ASCII, otherwise ignore it if (fakeBpb.oem_name[0] >= 0x20 && fakeBpb.oem_name[0] <= 0x7F && fakeBpb.oem_name[1] >= 0x20 && fakeBpb.oem_name[1] <= 0x7F && fakeBpb.oem_name[2] >= 0x20 && fakeBpb.oem_name[2] <= 0x7F && fakeBpb.oem_name[3] >= 0x20 && fakeBpb.oem_name[3] <= 0x7F && fakeBpb.oem_name[4] >= 0x20 && fakeBpb.oem_name[4] <= 0x7F && fakeBpb.oem_name[5] >= 0x20 && fakeBpb.oem_name[5] <= 0x7F && fakeBpb.oem_name[6] >= 0x20 && fakeBpb.oem_name[6] <= 0x7F && fakeBpb.oem_name[7] >= 0x20 && fakeBpb.oem_name[7] <= 0x7F) { XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name); } else if (fakeBpb.oem_name[0] < 0x20 && fakeBpb.oem_name[1] >= 0x20 && fakeBpb.oem_name[1] <= 0x7F && fakeBpb.oem_name[2] >= 0x20 && fakeBpb.oem_name[2] <= 0x7F && fakeBpb.oem_name[3] >= 0x20 && fakeBpb.oem_name[3] <= 0x7F && fakeBpb.oem_name[4] >= 0x20 && fakeBpb.oem_name[4] <= 0x7F && fakeBpb.oem_name[5] >= 0x20 && fakeBpb.oem_name[5] <= 0x7F && fakeBpb.oem_name[6] >= 0x20 && fakeBpb.oem_name[6] <= 0x7F && fakeBpb.oem_name[7] >= 0x20 && fakeBpb.oem_name[7] <= 0x7F) { XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name, Encoding, start: 1); } } if (fakeBpb.signature == 0x28 || fakeBpb.signature == 0x29) { XmlFsType.VolumeSerial = $"{fakeBpb.serial_no:X8}"; } } if (XmlFsType.SystemIdentifier != null) { sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); } sb.AppendFormat("{0} bytes per sector.", fakeBpb.bps).AppendLine(); if (bpbKind != BpbKind.Human) { if (fakeBpb.sectors == 0) { sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.big_sectors, fakeBpb.big_sectors * fakeBpb.bps).AppendLine(); XmlFsType.Clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc; } else { sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.sectors, fakeBpb.sectors * fakeBpb.bps).AppendLine(); XmlFsType.Clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.sectors : fakeBpb.sectors / fakeBpb.spc); } } else { XmlFsType.Clusters = humanBpb.clusters == 0 ? humanBpb.big_clusters : humanBpb.clusters; sb.AppendFormat("{0} sectors on volume ({1} bytes).", (XmlFsType.Clusters * humanBpb.bpc) / imagePlugin.Info.SectorSize, XmlFsType.Clusters * humanBpb.bpc).AppendLine(); } sb.AppendFormat("{0} sectors per cluster.", fakeBpb.spc).AppendLine(); sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); XmlFsType.ClusterSize = (uint)(fakeBpb.bps * fakeBpb.spc); sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fakeBpb.rsectors).AppendLine(); sb.AppendFormat("{0} FATs.", fakeBpb.fats_no).AppendLine(); sb.AppendFormat("{0} entries on root directory.", fakeBpb.root_ent).AppendLine(); if (fakeBpb.media > 0) { sb.AppendFormat("Media descriptor: 0x{0:X2}", fakeBpb.media).AppendLine(); } sb.AppendFormat("{0} sectors per FAT.", fakeBpb.spfat).AppendLine(); if (fakeBpb.sptrk > 0 && fakeBpb.sptrk < 64 && fakeBpb.heads > 0 && fakeBpb.heads < 256) { sb.AppendFormat("{0} sectors per track.", fakeBpb.sptrk).AppendLine(); sb.AppendFormat("{0} heads.", fakeBpb.heads).AppendLine(); } if (fakeBpb.hsectors <= partition.Start) { sb.AppendFormat("{0} hidden sectors before BPB.", fakeBpb.hsectors).AppendLine(); } if (fakeBpb.signature == 0x28 || fakeBpb.signature == 0x29 || andosOemCorrect) { sb.AppendFormat("Drive number: 0x{0:X2}", fakeBpb.drive_no).AppendLine(); if (XmlFsType.VolumeSerial != null) { sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine(); } if ((fakeBpb.flags & 0xF8) == 0x00) { if ((fakeBpb.flags & 0x01) == 0x01) { sb.AppendLine("Volume should be checked on next mount."); XmlFsType.Dirty = true; } if ((fakeBpb.flags & 0x02) == 0x02) { sb.AppendLine("Disk surface should be on next mount."); } } if (fakeBpb.signature == 0x29 || andosOemCorrect) { XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fakeBpb.volume_label, Encoding); XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fakeBpb.fs_type)).AppendLine(); } } else if (bpbKind == BpbKind.Atari && XmlFsType.VolumeSerial != null) { sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine(); } bootChk = Sha1Context.Data(fakeBpb.boot_code, out _); // Workaround that PCExchange jumps into "FAT16 "... if (XmlFsType.SystemIdentifier == "PCX 2.0 ") { fakeBpb.jump[1] += 8; } // Check that jumps to a correct boot code position and has boot signature set. // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... if (XmlFsType.Bootable == false && fakeBpb.jump != null) { XmlFsType.Bootable |= (fakeBpb.jump[0] == 0xEB && fakeBpb.jump[1] >= minBootNearJump && fakeBpb.jump[1] < 0x80) || (fakeBpb.jump[0] == 0xE9 && fakeBpb.jump.Length >= 3 && BitConverter.ToUInt16(fakeBpb.jump, 1) >= minBootNearJump && BitConverter.ToUInt16(fakeBpb.jump, 1) <= 0x1FC); } sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; // First root directory sector rootDirectorySector = (ulong)((fakeBpb.spfat * fakeBpb.fats_no) + fakeBpb.rsectors) * sectorsPerRealSector; sectorsForRootDirectory = (uint)((fakeBpb.root_ent * 32) / imagePlugin.Info.SectorSize); } if (extraInfo != null) { sb.Append(extraInfo); } if (rootDirectorySector + partition.Start < partition.End && imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) { byte[] rootDirectory = imagePlugin.ReadSectors(rootDirectorySector + partition.Start, sectorsForRootDirectory); if (bpbKind == BpbKind.DecRainbow) { 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); } rootDirectory = rootMs.ToArray(); } for (int i = 0; i < rootDirectory.Length; i += 32) { // Not a correct entry if (rootDirectory[i] < DIRENT_MIN && rootDirectory[i] != DIRENT_E5) { continue; } // Deleted or subdirectory entry if (rootDirectory[i] == DIRENT_SUBDIR || rootDirectory[i] == DIRENT_DELETED) { continue; } // Not a volume label if (rootDirectory[i + 0x0B] != 0x08 && rootDirectory[i + 0x0B] != 0x28) { continue; } DirectoryEntry entry = Marshal.ByteArrayToStructureLittleEndian <DirectoryEntry>(rootDirectory, i, 32); byte[] fullname = new byte[11]; Array.Copy(entry.filename, 0, fullname, 0, 8); Array.Copy(entry.extension, 0, fullname, 8, 3); string volname = Encoding.GetString(fullname).Trim(); if (!string.IsNullOrEmpty(volname)) { XmlFsType.VolumeName = entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) ? volname.ToLower() : volname; } if (entry.ctime > 0 && entry.cdate > 0) { XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); if (entry.ctime_ms > 0) { XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); } XmlFsType.CreationDateSpecified = true; sb.AppendFormat("Volume created on {0}", XmlFsType.CreationDate).AppendLine(); } if (entry.mtime > 0 && entry.mdate > 0) { XmlFsType.ModificationDate = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); XmlFsType.ModificationDateSpecified = true; sb.AppendFormat("Volume last modified on {0}", XmlFsType.ModificationDate).AppendLine(); } if (entry.adate > 0) { sb.AppendFormat("Volume last accessed on {0:d}", DateHandlers.DosToDateTime(entry.adate, 0)). AppendLine(); } break; } } if (!string.IsNullOrEmpty(XmlFsType.VolumeName)) { sb.AppendFormat("Volume label: {0}", XmlFsType.VolumeName).AppendLine(); } if (XmlFsType.Bootable) { // Intel short jump if (bpbSector[0] == 0xEB && bpbSector[1] < 0x80) { int sigSize = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0; byte[] bootCode = new byte[512 - sigSize - bpbSector[1] - 2]; Array.Copy(bpbSector, bpbSector[1] + 2, bootCode, 0, bootCode.Length); Sha1Context.Data(bootCode, out _); } // Intel big jump else if (bpbSector[0] == 0xE9 && BitConverter.ToUInt16(bpbSector, 1) < 0x1FC) { int sigSize = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0; byte[] bootCode = new byte[512 - sigSize - BitConverter.ToUInt16(bpbSector, 1) - 3]; Array.Copy(bpbSector, BitConverter.ToUInt16(bpbSector, 1) + 3, bootCode, 0, bootCode.Length); Sha1Context.Data(bootCode, out _); } sb.AppendLine("Volume is bootable"); sb.AppendFormat("Boot code's SHA1: {0}", bootChk).AppendLine(); string bootName = _knownBootHashes.FirstOrDefault(t => t.hash == bootChk).name; if (string.IsNullOrWhiteSpace(bootName)) { sb.AppendLine("Unknown boot code."); } else { sb.AppendFormat("Boot code corresponds to {0}", bootName).AppendLine(); } } information = sb.ToString(); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = new LisaRoman(); information = ""; var sb = new StringBuilder(); try { if (imagePlugin.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true) { return; } // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors if (imagePlugin.Info.Sectors < 800) { return; } int beforeMddf = -1; // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors for (int i = 0; i < 100; i++) { DecodeTag(imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag), out LisaTag.PriamTag searchTag); AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); if (beforeMddf == -1 && searchTag.FileId == FILEID_LOADER_SIGNED) { beforeMddf = i - 1; } if (searchTag.FileId != FILEID_MDDF) { continue; } byte[] sector = imagePlugin.ReadSector((ulong)i); var infoMddf = new MDDF(); byte[] pString = new byte[33]; infoMddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00); infoMddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02); infoMddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A); Array.Copy(sector, 0x0C, pString, 0, 33); infoMddf.volname = StringHandlers.PascalToString(pString, Encoding); infoMddf.unknown1 = sector[0x2D]; Array.Copy(sector, 0x2E, pString, 0, 33); // Prevent garbage infoMddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : ""; infoMddf.unknown2 = sector[0x4F]; infoMddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50); infoMddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54); uint lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58); infoMddf.dtvc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C); infoMddf.dtcc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60); infoMddf.dtvb = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64); infoMddf.dtvs = DateHandlers.LisaToDateTime(lisaTime); infoMddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68); infoMddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C); infoMddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70); infoMddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74); infoMddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78); infoMddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C); infoMddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E); infoMddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80); infoMddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82); infoMddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86); infoMddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A); infoMddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C); infoMddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90); infoMddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94); infoMddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98); infoMddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A); infoMddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C); infoMddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0); infoMddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4); infoMddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8); infoMddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC); infoMddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0); infoMddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2); infoMddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6); infoMddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA); infoMddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE); infoMddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0); infoMddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4); infoMddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC); infoMddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0); infoMddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4); infoMddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8); infoMddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC); infoMddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0); infoMddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4); infoMddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8); infoMddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC); infoMddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0); infoMddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4); infoMddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8); infoMddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC); infoMddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100); infoMddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104); infoMddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108); infoMddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C); infoMddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110); infoMddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114); infoMddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118); infoMddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120); infoMddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122); infoMddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124); infoMddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126); infoMddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C); infoMddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A); infoMddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E); infoMddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132); infoMddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136); infoMddf.vol_left_mounted = sector[0x138]; AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown1 = 0x{0:X2} ({0})", infoMddf.unknown1); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown2 = 0x{0:X2} ({0})", infoMddf.unknown2); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown3 = 0x{0:X8} ({0})", infoMddf.unknown3); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown4 = 0x{0:X4} ({0})", infoMddf.unknown4); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown5 = 0x{0:X8} ({0})", infoMddf.unknown5); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown6 = 0x{0:X8} ({0})", infoMddf.unknown6); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown7 = 0x{0:X8} ({0})", infoMddf.unknown7); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown9 = 0x{0:X4} ({0})", infoMddf.unknown9); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown10 = 0x{0:X8} ({0})", infoMddf.unknown10); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown11 = 0x{0:X8} ({0})", infoMddf.unknown11); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown12 = 0x{0:X8} ({0})", infoMddf.unknown12); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown13 = 0x{0:X8} ({0})", infoMddf.unknown13); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown14 = 0x{0:X8} ({0})", infoMddf.unknown14); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown15 = 0x{0:X8} ({0})", infoMddf.unknown15); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown16 = 0x{0:X8} ({0})", infoMddf.unknown16); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown17 = 0x{0:X4} ({0})", infoMddf.unknown17); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown18 = 0x{0:X8} ({0})", infoMddf.unknown18); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown19 = 0x{0:X8} ({0})", infoMddf.unknown19); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown20 = 0x{0:X8} ({0})", infoMddf.unknown20); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown21 = 0x{0:X8} ({0})", infoMddf.unknown21); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown22 = 0x{0:X8} ({0})", infoMddf.unknown22); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown23 = 0x{0:X8} ({0})", infoMddf.unknown23); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown24 = 0x{0:X8} ({0})", infoMddf.unknown24); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown25 = 0x{0:X8} ({0})", infoMddf.unknown25); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown26 = 0x{0:X8} ({0})", infoMddf.unknown26); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown27 = 0x{0:X8} ({0})", infoMddf.unknown27); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown28 = 0x{0:X8} ({0})", infoMddf.unknown28); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown29 = 0x{0:X8} ({0})", infoMddf.unknown29); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown30 = 0x{0:X8} ({0})", infoMddf.unknown30); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown31 = 0x{0:X8} ({0})", infoMddf.unknown31); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown32 = 0x{0:X8} ({0})", infoMddf.unknown32); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown33 = 0x{0:X8} ({0})", infoMddf.unknown33); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown34 = 0x{0:X8} ({0})", infoMddf.unknown34); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown35 = 0x{0:X8} ({0})", infoMddf.unknown35); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown36 = 0x{0:X8} ({0})", infoMddf.unknown36); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown37 = 0x{0:X8} ({0})", infoMddf.unknown37); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown38 = 0x{0:X8} ({0})", infoMddf.unknown38); AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown_timestamp = 0x{0:X8} ({0}, {1})", infoMddf.unknown_timestamp, DateHandlers.LisaToDateTime(infoMddf.unknown_timestamp)); if (infoMddf.mddf_block != i - beforeMddf) { return; } if (infoMddf.vol_size > imagePlugin.Info.Sectors) { return; } if (infoMddf.vol_size - 1 != infoMddf.volsize_minus_one) { return; } if (infoMddf.vol_size - i - 1 != infoMddf.volsize_minus_mddf_minus_one - beforeMddf) { return; } if (infoMddf.datasize > infoMddf.blocksize) { return; } if (infoMddf.blocksize < imagePlugin.Info.SectorSize) { return; } if (infoMddf.datasize != imagePlugin.Info.SectorSize) { return; } switch (infoMddf.fsversion) { case LISA_V1: sb.AppendLine("LisaFS v1"); break; case LISA_V2: sb.AppendLine("LisaFS v2"); break; case LISA_V3: sb.AppendLine("LisaFS v3"); break; default: sb.AppendFormat("Unknown LisaFS version {0}", infoMddf.fsversion).AppendLine(); break; } sb.AppendFormat("Volume name: \"{0}\"", infoMddf.volname).AppendLine(); sb.AppendFormat("Volume password: \"{0}\"", infoMddf.password).AppendLine(); sb.AppendFormat("Volume ID: 0x{0:X16}", infoMddf.volid).AppendLine(); sb.AppendFormat("Backup volume ID: 0x{0:X16}", infoMddf.backup_volid).AppendLine(); sb.AppendFormat("Master copy ID: 0x{0:X8}", infoMddf.master_copy_id).AppendLine(); sb.AppendFormat("Volume is number {0} of {1}", infoMddf.volnum, infoMddf.vol_sequence).AppendLine(); sb.AppendFormat("Serial number of Lisa computer that created this volume: {0}", infoMddf.machine_id).AppendLine(); sb.AppendFormat("Serial number of Lisa computer that can use this volume's software {0}", infoMddf.serialization).AppendLine(); sb.AppendFormat("Volume created on {0}", infoMddf.dtvc).AppendLine(); sb.AppendFormat("Some timestamp, says {0}", infoMddf.dtcc).AppendLine(); sb.AppendFormat("Volume backed up on {0}", infoMddf.dtvb).AppendLine(); sb.AppendFormat("Volume scavenged on {0}", infoMddf.dtvs).AppendLine(); sb.AppendFormat("MDDF is in block {0}", infoMddf.mddf_block + beforeMddf).AppendLine(); sb.AppendFormat("There are {0} reserved blocks before volume", beforeMddf).AppendLine(); sb.AppendFormat("{0} blocks minus one", infoMddf.volsize_minus_one).AppendLine(); sb.AppendFormat("{0} blocks minus one minus MDDF offset", infoMddf.volsize_minus_mddf_minus_one). AppendLine(); sb.AppendFormat("{0} blocks in volume", infoMddf.vol_size).AppendLine(); sb.AppendFormat("{0} bytes per sector (uncooked)", infoMddf.blocksize).AppendLine(); sb.AppendFormat("{0} bytes per sector", infoMddf.datasize).AppendLine(); sb.AppendFormat("{0} blocks per cluster", infoMddf.clustersize).AppendLine(); sb.AppendFormat("{0} blocks in filesystem", infoMddf.fs_size).AppendLine(); sb.AppendFormat("{0} files in volume", infoMddf.filecount).AppendLine(); sb.AppendFormat("{0} blocks free", infoMddf.freecount).AppendLine(); sb.AppendFormat("{0} bytes in LisaInfo", infoMddf.label_size).AppendLine(); sb.AppendFormat("Filesystem overhead: {0}", infoMddf.fs_overhead).AppendLine(); sb.AppendFormat("Scavenger result code: 0x{0:X8}", infoMddf.result_scavenge).AppendLine(); sb.AppendFormat("Boot code: 0x{0:X8}", infoMddf.boot_code).AppendLine(); sb.AppendFormat("Boot environment: 0x{0:X8}", infoMddf.boot_environ).AppendLine(); sb.AppendFormat("Overmount stamp: 0x{0:X16}", infoMddf.overmount_stamp).AppendLine(); sb.AppendFormat("S-Records start at {0} and spans for {1} blocks", infoMddf.srec_ptr + infoMddf.mddf_block + beforeMddf, infoMddf.srec_len). AppendLine(); sb.AppendLine(infoMddf.vol_left_mounted == 0 ? "Volume is clean" : "Volume is dirty"); information = sb.ToString(); XmlFsType = new FileSystemType(); if (DateTime.Compare(infoMddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0) { XmlFsType.BackupDate = infoMddf.dtvb; XmlFsType.BackupDateSpecified = true; } XmlFsType.Clusters = infoMddf.vol_size; XmlFsType.ClusterSize = (uint)(infoMddf.clustersize * infoMddf.datasize); if (DateTime.Compare(infoMddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0) { XmlFsType.CreationDate = infoMddf.dtvc; XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = infoMddf.vol_left_mounted != 0; XmlFsType.Files = infoMddf.filecount; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = infoMddf.freecount; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "LisaFS"; XmlFsType.VolumeName = infoMddf.volname; XmlFsType.VolumeSerial = $"{infoMddf.volid:X16}"; return; } } catch (Exception ex) { AaruConsole.ErrorWriteLine("Exception {0}, {1}, {2}", ex.Message, ex.InnerException, ex.StackTrace); } }
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 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 bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset) { partitions = new List <Partition>(); uint run = (MAX_LABEL_SIZE + labelOffsets.Last()) / imagePlugin.Info.SectorSize; if ((MAX_LABEL_SIZE + labelOffsets.Last()) % imagePlugin.Info.SectorSize > 0) { run++; } DiskLabel dl = new DiskLabel(); bool found = false; foreach (ulong location in labelLocations) { if (location + run + sectorOffset >= imagePlugin.Info.Sectors) { return(false); } byte[] tmp = imagePlugin.ReadSectors(location + sectorOffset, run); foreach (uint offset in labelOffsets) { byte[] sector = new byte[MAX_LABEL_SIZE]; Array.Copy(tmp, offset, sector, 0, MAX_LABEL_SIZE); dl = GetDiskLabel(sector); DicConsole.DebugWriteLine("BSD plugin", "dl.magic on sector {0} at offset {1} = 0x{2:X8} (expected 0x{3:X8})", location + sectorOffset, offset, dl.d_magic, DISKMAGIC); if ((dl.d_magic != DISKMAGIC || dl.d_magic2 != DISKMAGIC) && (dl.d_magic != DISKCIGAM || dl.d_magic2 != DISKCIGAM)) { continue; } found = true; break; } if (found) { break; } } if (!found) { return(false); } if (dl.d_magic == DISKCIGAM && dl.d_magic2 == DISKCIGAM) { dl = SwapDiskLabel(dl); } DicConsole.DebugWriteLine("BSD plugin", "dl.d_type = {0}", dl.d_type); DicConsole.DebugWriteLine("BSD plugin", "dl.d_subtype = {0}", dl.d_subtype); DicConsole.DebugWriteLine("BSD plugin", "dl.d_typename = {0}", StringHandlers.CToString(dl.d_typename)); DicConsole.DebugWriteLine("BSD plugin", "dl.d_packname = {0}", StringHandlers.CToString(dl.d_packname)); DicConsole.DebugWriteLine("BSD plugin", "dl.d_secsize = {0}", dl.d_secsize); DicConsole.DebugWriteLine("BSD plugin", "dl.d_nsectors = {0}", dl.d_nsectors); DicConsole.DebugWriteLine("BSD plugin", "dl.d_ntracks = {0}", dl.d_ntracks); DicConsole.DebugWriteLine("BSD plugin", "dl.d_ncylinders = {0}", dl.d_ncylinders); DicConsole.DebugWriteLine("BSD plugin", "dl.d_secpercyl = {0}", dl.d_secpercyl); DicConsole.DebugWriteLine("BSD plugin", "dl.d_secperunit = {0}", dl.d_secperunit); DicConsole.DebugWriteLine("BSD plugin", "dl.d_sparespertrack = {0}", dl.d_sparespertrack); DicConsole.DebugWriteLine("BSD plugin", "dl.d_sparespercyl = {0}", dl.d_sparespercyl); DicConsole.DebugWriteLine("BSD plugin", "dl.d_acylinders = {0}", dl.d_acylinders); DicConsole.DebugWriteLine("BSD plugin", "dl.d_rpm = {0}", dl.d_rpm); DicConsole.DebugWriteLine("BSD plugin", "dl.d_interleave = {0}", dl.d_interleave); DicConsole.DebugWriteLine("BSD plugin", "dl.d_trackskew = {0}", dl.d_trackskew); DicConsole.DebugWriteLine("BSD plugin", "dl.d_cylskeew = {0}", dl.d_cylskeew); DicConsole.DebugWriteLine("BSD plugin", "dl.d_headswitch = {0}", dl.d_headswitch); DicConsole.DebugWriteLine("BSD plugin", "dl.d_trkseek = {0}", dl.d_trkseek); DicConsole.DebugWriteLine("BSD plugin", "dl.d_flags = {0}", dl.d_flags); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[0] = {0}", dl.d_drivedata[0]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[1] = {0}", dl.d_drivedata[1]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[2] = {0}", dl.d_drivedata[2]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[3] = {0}", dl.d_drivedata[3]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[4] = {0}", dl.d_drivedata[4]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[0] = {0}", dl.d_spare[0]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[1] = {0}", dl.d_spare[1]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[2] = {0}", dl.d_spare[2]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[3] = {0}", dl.d_spare[3]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[4] = {0}", dl.d_spare[4]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_magic2 = 0x{0:X8}", dl.d_magic2); DicConsole.DebugWriteLine("BSD plugin", "dl.d_checksum = 0x{0:X8}", dl.d_checksum); DicConsole.DebugWriteLine("BSD plugin", "dl.d_npartitions = {0}", dl.d_npartitions); DicConsole.DebugWriteLine("BSD plugin", "dl.d_bbsize = {0}", dl.d_bbsize); DicConsole.DebugWriteLine("BSD plugin", "dl.d_sbsize = {0}", dl.d_sbsize); ulong counter = 0; bool addSectorOffset = false; for (int i = 0; i < dl.d_npartitions && i < 22; i++) { DicConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_offset = {0}", dl.d_partitions[i].p_offset); DicConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_size = {0}", dl.d_partitions[i].p_size); DicConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_fstype = {0} ({1})", dl.d_partitions[i].p_fstype, fsTypeToString(dl.d_partitions[i].p_fstype)); Partition part = new Partition { Start = dl.d_partitions[i].p_offset * dl.d_secsize / imagePlugin.Info.SectorSize, Offset = dl.d_partitions[i].p_offset * dl.d_secsize, Length = dl.d_partitions[i].p_size * dl.d_secsize / imagePlugin.Info.SectorSize, Size = dl.d_partitions[i].p_size * dl.d_secsize, Type = fsTypeToString(dl.d_partitions[i].p_fstype), Sequence = counter, Scheme = Name }; if (dl.d_partitions[i].p_fstype == fsType.Unused) { continue; } // Crude and dirty way to know if the disklabel is relative to its parent partition... if (dl.d_partitions[i].p_offset < sectorOffset && !addSectorOffset) { addSectorOffset = true; } if (addSectorOffset) { part.Start += sectorOffset; part.Offset += sectorOffset * imagePlugin.Info.SectorSize; } DicConsole.DebugWriteLine("BSD plugin", "part.start = {0}", part.Start); DicConsole.DebugWriteLine("BSD plugin", "Adding it..."); partitions.Add(part); counter++; } return(partitions.Count > 0); }
public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset) { partitions = new List <Partition>(); Partition[] parts; if (sectorOffset != 0) { return(false); } switch (imagePlugin.Info.MediaType) { case MediaType.RA60: parts = RA60; break; case MediaType.RA80: parts = RA80; break; case MediaType.RA81: parts = RA81; break; case MediaType.RC25: parts = RC25; break; case MediaType.RD31: parts = RD31; break; case MediaType.RD32: parts = RD32; break; case MediaType.RD51: parts = RD51; break; case MediaType.RD52: parts = RD52; break; case MediaType.RD53: parts = RD53; break; case MediaType.RD54: parts = RD54; break; case MediaType.RK06: parts = RK06; break; case MediaType.RK07: parts = RK07; break; case MediaType.RM02: case MediaType.RM03: parts = RM02; break; case MediaType.RM05: parts = RM05; break; case MediaType.RP02: parts = RP02; break; case MediaType.RP03: parts = RP03; break; case MediaType.RP04: case MediaType.RP05: parts = RP04; break; case MediaType.RP06: parts = RP06; break; default: return(false); } for (int i = 0; i < parts.Length; i++) { parts[i].Scheme = ""; } partitions = parts.ToList(); return(partitions.Count > 0); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? new Apple2(); StringBuilder sbInformation = new StringBuilder(); information = ""; multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); if (imagePlugin.Info.Sectors < 3) { return; } // Blocks 0 and 1 are boot code byte[] volBlock = imagePlugin.ReadSectors(multiplier * 2 + partition.Start, multiplier); PascalVolumeEntry volEntry = new PascalVolumeEntry(); // On Apple //, it's little endian BigEndianBitConverter.IsLittleEndian = multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; volEntry.FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00); volEntry.LastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02); volEntry.EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04); volEntry.VolumeName = new byte[8]; Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8); volEntry.Blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E); volEntry.Files = BigEndianBitConverter.ToInt16(volBlock, 0x10); volEntry.Dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12); volEntry.LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14); volEntry.Tail = BigEndianBitConverter.ToInt32(volBlock, 0x16); // First block is always 0 (even is it's sector 2) if (volEntry.FirstBlock != 0) { return; } // Last volume record block must be after first block, and before end of device if (volEntry.LastBlock <= volEntry.FirstBlock || (ulong)volEntry.LastBlock > imagePlugin.Info.Sectors / multiplier - 2) { return; } // Volume record entry type must be volume or secure if (volEntry.EntryType != PascalFileKind.Volume && volEntry.EntryType != PascalFileKind.Secure) { return; } // Volume name is max 7 characters if (volEntry.VolumeName[0] > 7) { return; } // Volume blocks is equal to volume sectors if (volEntry.Blocks < 0 || (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / multiplier) { return; } // There can be not less than zero files if (volEntry.Files < 0) { return; } sbInformation.AppendFormat("Volume record spans from block {0} to block {1}", volEntry.FirstBlock, volEntry.LastBlock).AppendLine(); sbInformation.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(volEntry.VolumeName, Encoding)) .AppendLine(); sbInformation.AppendFormat("Volume has {0} blocks", volEntry.Blocks).AppendLine(); sbInformation.AppendFormat("Volume has {0} files", volEntry.Files).AppendLine(); sbInformation .AppendFormat("Volume last booted at {0}", DateHandlers.UcsdPascalToDateTime(volEntry.LastBoot)) .AppendLine(); information = sbInformation.ToString(); XmlFsType = new FileSystemType { Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(imagePlugin.ReadSectors(partition.Start, multiplier * 2)), Clusters = (ulong)volEntry.Blocks, ClusterSize = imagePlugin.Info.SectorSize, Files = (ulong)volEntry.Files, FilesSpecified = true, Type = "UCSD Pascal", VolumeName = StringHandlers.PascalToString(volEntry.VolumeName, Encoding) }; }
internal static void DoVerify(VerifyOptions options) { DicConsole.DebugWriteLine("Verify command", "--debug={0}", options.Debug); DicConsole.DebugWriteLine("Verify command", "--verbose={0}", options.Verbose); DicConsole.DebugWriteLine("Verify command", "--input={0}", options.InputFile); DicConsole.DebugWriteLine("Verify command", "--verify-disc={0}", options.VerifyDisc); DicConsole.DebugWriteLine("Verify command", "--verify-sectors={0}", options.VerifySectors); FiltersList filtersList = new FiltersList(); IFilter inputFilter = filtersList.GetFilter(options.InputFile); if (inputFilter == null) { DicConsole.ErrorWriteLine("Cannot open specified file."); return; } IMediaImage inputFormat = ImageFormat.Detect(inputFilter); if (inputFormat == null) { DicConsole.ErrorWriteLine("Unable to recognize image format, not verifying"); return; } inputFormat.Open(inputFilter); Core.Statistics.AddMediaFormat(inputFormat.Format); Core.Statistics.AddMedia(inputFormat.Info.MediaType, false); Core.Statistics.AddFilter(inputFilter.Name); bool?correctDisc = null; long totalSectors = 0; long errorSectors = 0; long correctSectors = 0; long unknownSectors = 0; if (options.VerifyDisc) { DateTime startCheck = DateTime.UtcNow; bool? discCheckStatus = inputFormat.VerifyMediaImage(); DateTime endCheck = DateTime.UtcNow; TimeSpan checkTime = endCheck - startCheck; switch (discCheckStatus) { case true: DicConsole.WriteLine("Disc image checksums are correct"); break; case false: DicConsole.WriteLine("Disc image checksums are incorrect"); break; case null: DicConsole.WriteLine("Disc image does not contain checksums"); break; } correctDisc = discCheckStatus; DicConsole.VerboseWriteLine("Checking disc image checksums took {0} seconds", checkTime.TotalSeconds); } if (options.VerifySectors) { bool formatHasTracks; try { List <Track> inputTracks = inputFormat.Tracks; formatHasTracks = inputTracks.Count > 0; } catch { formatHasTracks = false; } DateTime startCheck; DateTime endCheck; List <ulong> failingLbas = new List <ulong>(); List <ulong> unknownLbas = new List <ulong>(); if (formatHasTracks) { List <Track> inputTracks = inputFormat.Tracks; ulong currentSectorAll = 0; startCheck = DateTime.UtcNow; foreach (Track currentTrack in inputTracks) { ulong remainingSectors = currentTrack.TrackEndSector - currentTrack.TrackStartSector; ulong currentSector = 0; while (remainingSectors > 0) { DicConsole.Write("\rChecking sector {0} of {1}, on track {2}", currentSectorAll, inputFormat.Info.Sectors, currentTrack.TrackSequence); List <ulong> tempfailingLbas; List <ulong> tempunknownLbas; if (remainingSectors < 512) { inputFormat.VerifySectors(currentSector, (uint)remainingSectors, currentTrack.TrackSequence, out tempfailingLbas, out tempunknownLbas); } else { inputFormat.VerifySectors(currentSector, 512, currentTrack.TrackSequence, out tempfailingLbas, out tempunknownLbas); } failingLbas.AddRange(tempfailingLbas); unknownLbas.AddRange(tempunknownLbas); if (remainingSectors < 512) { currentSector += remainingSectors; currentSectorAll += remainingSectors; remainingSectors = 0; } else { currentSector += 512; currentSectorAll += 512; remainingSectors -= 512; } } } endCheck = DateTime.UtcNow; } else { ulong remainingSectors = inputFormat.Info.Sectors; ulong currentSector = 0; startCheck = DateTime.UtcNow; while (remainingSectors > 0) { DicConsole.Write("\rChecking sector {0} of {1}", currentSector, inputFormat.Info.Sectors); List <ulong> tempfailingLbas; List <ulong> tempunknownLbas; if (remainingSectors < 512) { inputFormat.VerifySectors(currentSector, (uint)remainingSectors, out tempfailingLbas, out tempunknownLbas); } else { inputFormat.VerifySectors(currentSector, 512, out tempfailingLbas, out tempunknownLbas); } failingLbas.AddRange(tempfailingLbas); unknownLbas.AddRange(tempunknownLbas); if (remainingSectors < 512) { currentSector += remainingSectors; remainingSectors = 0; } else { currentSector += 512; remainingSectors -= 512; } } endCheck = DateTime.UtcNow; } TimeSpan checkTime = endCheck - startCheck; DicConsole.Write("\r" + new string(' ', System.Console.WindowWidth - 1) + "\r"); if (unknownSectors > 0) { DicConsole.WriteLine("There is at least one sector that does not contain a checksum"); } if (errorSectors > 0) { DicConsole.WriteLine("There is at least one sector with incorrect checksum or errors"); } if (unknownSectors == 0 && errorSectors == 0) { DicConsole.WriteLine("All sector checksums are correct"); } DicConsole.VerboseWriteLine("Checking sector checksums took {0} seconds", checkTime.TotalSeconds); if (options.Verbose) { DicConsole.VerboseWriteLine("LBAs with error:"); if (failingLbas.Count == (int)inputFormat.Info.Sectors) { DicConsole.VerboseWriteLine("\tall sectors."); } else { foreach (ulong t in failingLbas) { DicConsole.VerboseWriteLine("\t{0}", t); } } DicConsole.WriteLine("LBAs without checksum:"); if (unknownLbas.Count == (int)inputFormat.Info.Sectors) { DicConsole.VerboseWriteLine("\tall sectors."); } else { foreach (ulong t in unknownLbas) { DicConsole.VerboseWriteLine("\t{0}", t); } } } DicConsole.WriteLine("Total sectors........... {0}", inputFormat.Info.Sectors); DicConsole.WriteLine("Total errors............ {0}", failingLbas.Count); DicConsole.WriteLine("Total unknowns.......... {0}", unknownLbas.Count); DicConsole.WriteLine("Total errors+unknowns... {0}", failingLbas.Count + unknownLbas.Count); totalSectors = (long)inputFormat.Info.Sectors; errorSectors = failingLbas.Count; unknownSectors = unknownLbas.Count; correctSectors = totalSectors - errorSectors - unknownSectors; } Core.Statistics.AddCommand("verify"); Core.Statistics.AddVerify(correctDisc, correctSectors, errorSectors, unknownSectors, totalSectors); }
public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset) { partitions = new List <Partition>(); byte[] sector = imagePlugin.ReadSector(sectorOffset); if (sector.Length < 512) { return(false); } SGILabel dvh = Marshal.ByteArrayToStructureBigEndian <SGILabel>(sector); for (int i = 0; i < dvh.volume.Length; i++) { dvh.volume[i] = (SGIVolume)Marshal.SwapStructureMembersEndian(dvh.volume[i]); } for (int i = 0; i < dvh.partitions.Length; i++) { dvh.partitions[i] = (SGIPartition)Marshal.SwapStructureMembersEndian(dvh.partitions[i]); } DicConsole.DebugWriteLine("SGIVH plugin", "dvh.magic = 0x{0:X8} (should be 0x{1:X8})", dvh.magic, SGI_MAGIC); if (dvh.magic != SGI_MAGIC) { return(false); } DicConsole.DebugWriteLine("SGIVH plugin", "dvh.root_part_num = {0}", dvh.root_part_num); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.swap_part_num = {0}", dvh.swap_part_num); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.boot_file = \"{0}\"", StringHandlers.CToString(dvh.boot_file)); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_skew = {0}", dvh.device_params.dp_skew); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_gap1 = {0}", dvh.device_params.dp_gap1); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_gap2 = {0}", dvh.device_params.dp_gap2); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_spares_cyl = {0}", dvh.device_params.dp_spares_cyl); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_cyls = {0}", dvh.device_params.dp_cyls); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_shd0 = {0}", dvh.device_params.dp_shd0); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_trks0 = {0}", dvh.device_params.dp_trks0); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_ctq_depth = {0}", dvh.device_params.dp_ctq_depth); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_cylshi = {0}", dvh.device_params.dp_cylshi); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_secs = {0}", dvh.device_params.dp_secs); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_secbytes = {0}", dvh.device_params.dp_secbytes); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_interleave = {0}", dvh.device_params.dp_interleave); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_flags = {0}", dvh.device_params.dp_flags); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_datarate = {0}", dvh.device_params.dp_datarate); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_nretries = {0}", dvh.device_params.dp_nretries); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_mspw = {0}", dvh.device_params.dp_mspw); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xgap1 = {0}", dvh.device_params.dp_xgap1); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xsync = {0}", dvh.device_params.dp_xsync); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xrdly = {0}", dvh.device_params.dp_xrdly); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xgap2 = {0}", dvh.device_params.dp_xgap2); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xrgate = {0}", dvh.device_params.dp_xrgate); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xwcont = {0}", dvh.device_params.dp_xwcont); ulong counter = 0; for (int i = 0; i < dvh.partitions.Length; i++) { DicConsole.DebugWriteLine("SGIVH plugin", "dvh.partitions[{0}].num_blocks = {1}", i, dvh.partitions[i].num_blocks); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.partitions[{0}].first_block = {1}", i, dvh.partitions[i].first_block); // TODO: Solve big endian marshal with enumerations dvh.partitions[i].type = (SGIType)Swapping.Swap((uint)dvh.partitions[i].type); DicConsole.DebugWriteLine("SGIVH plugin", "dvh.partitions[{0}].type = {1}", i, dvh.partitions[i].type); Partition part = new Partition { Start = dvh.partitions[i].first_block * dvh.device_params.dp_secbytes / imagePlugin.Info.SectorSize, Offset = dvh.partitions[i].first_block * dvh.device_params.dp_secbytes, Length = dvh.partitions[i].num_blocks * dvh.device_params.dp_secbytes / imagePlugin.Info.SectorSize, Size = dvh.partitions[i].num_blocks * dvh.device_params.dp_secbytes, Type = TypeToString(dvh.partitions[i].type), Sequence = counter, Scheme = Name }; if (part.Size <= 0 || dvh.partitions[i].type == SGIType.Header || dvh.partitions[i].type == SGIType.Volume) { continue; } partitions.Add(part); counter++; } return(true); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = new PETSCII(); byte[] sector; StringBuilder sbInformation = new StringBuilder(); sbInformation.AppendLine("Commodore file system"); XmlFsType = new FileSystemType { Type = "Commodore file system", Clusters = (long)imagePlugin.Info.Sectors, ClusterSize = 256 }; if (imagePlugin.Info.Sectors == 3200) { sector = imagePlugin.ReadSector(1560); CommodoreHeader cbmHdr = new CommodoreHeader(); IntPtr cbmHdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cbmHdr)); Marshal.Copy(sector, 0, cbmHdrPtr, Marshal.SizeOf(cbmHdr)); cbmHdr = (CommodoreHeader)Marshal.PtrToStructure(cbmHdrPtr, typeof(CommodoreHeader)); Marshal.FreeHGlobal(cbmHdrPtr); sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmHdr.directoryTrack, cbmHdr.directorySector).AppendLine(); sbInformation .AppendFormat("Disk DOS Version: {0}", Encoding.ASCII.GetString(new[] { cbmHdr.diskDosVersion })) .AppendLine(); sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] { cbmHdr.dosVersion })) .AppendLine(); sbInformation.AppendFormat("Disk Version: {0}", Encoding.ASCII.GetString(new[] { cbmHdr.diskVersion })) .AppendLine(); sbInformation.AppendFormat("Disk ID: {0}", cbmHdr.diskId).AppendLine(); sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmHdr.name, Encoding)) .AppendLine(); XmlFsType.VolumeName = StringHandlers.CToString(cbmHdr.name, Encoding); XmlFsType.VolumeSerial = $"{cbmHdr.diskId}"; } else { sector = imagePlugin.ReadSector(357); CommodoreBam cbmBam = new CommodoreBam(); IntPtr cbmBamPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cbmBam)); Marshal.Copy(sector, 0, cbmBamPtr, Marshal.SizeOf(cbmBam)); cbmBam = (CommodoreBam)Marshal.PtrToStructure(cbmBamPtr, typeof(CommodoreBam)); Marshal.FreeHGlobal(cbmBamPtr); sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmBam.directoryTrack, cbmBam.directorySector).AppendLine(); sbInformation.AppendFormat("Disk DOS type: {0}", Encoding.ASCII.GetString(BitConverter.GetBytes(cbmBam.dosType))) .AppendLine(); sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] { cbmBam.dosVersion })) .AppendLine(); sbInformation.AppendFormat("Disk ID: {0}", cbmBam.diskId).AppendLine(); sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmBam.name, Encoding)) .AppendLine(); XmlFsType.VolumeName = StringHandlers.CToString(cbmBam.name, Encoding); XmlFsType.VolumeSerial = $"{cbmBam.diskId}"; } information = sbInformation.ToString(); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.BigEndianUnicode; information = ""; HfsPlusVolumeHeader vh = new HfsPlusVolumeHeader(); ulong hfspOffset; bool wrapped; uint sectorsToRead = 0x800 / imagePlugin.Info.SectorSize; if (0x800 % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } byte[] vhSector = imagePlugin.ReadSectors(partition.Start, sectorsToRead); ushort drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400); if (drSigWord == HFS_MAGIC) // "BD" { drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature if (drSigWord == HFSP_MAGIC) // "H+" { ushort xdrStABNt = BigEndianBitConverter.ToUInt16(vhSector, 0x47E); uint drAlBlkSiz = BigEndianBitConverter.ToUInt32(vhSector, 0x414); ushort drAlBlSt = BigEndianBitConverter.ToUInt16(vhSector, 0x41C); hfspOffset = (ulong)((drAlBlSt * 512 + xdrStABNt * drAlBlkSiz) / imagePlugin.Info.SectorSize); wrapped = true; } else { hfspOffset = 0; wrapped = false; } } else { hfspOffset = 0; wrapped = false; } vhSector = imagePlugin.ReadSectors(partition.Start + hfspOffset, sectorsToRead); // Read volume header vh.signature = BigEndianBitConverter.ToUInt16(vhSector, 0x400); if (vh.signature != HFSP_MAGIC && vh.signature != HFSX_MAGIC) { return; } StringBuilder sb = new StringBuilder(); if (vh.signature == 0x482B) { sb.AppendLine("HFS+ filesystem."); } if (vh.signature == 0x4858) { sb.AppendLine("HFSX filesystem."); } if (wrapped) { sb.AppendLine("Volume is wrapped inside an HFS volume."); } byte[] tmp = new byte[0x400]; Array.Copy(vhSector, 0x400, tmp, 0, 0x400); vhSector = tmp; vh = BigEndianMarshal.ByteArrayToStructureBigEndian <HfsPlusVolumeHeader>(vhSector); if (vh.version == 4 || vh.version == 5) { sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); if ((vh.attributes & 0x80) == 0x80) { sb.AppendLine("Volume is locked on hardware."); } if ((vh.attributes & 0x100) == 0x100) { sb.AppendLine("Volume is unmounted."); } if ((vh.attributes & 0x200) == 0x200) { sb.AppendLine("There are bad blocks in the extents file."); } if ((vh.attributes & 0x400) == 0x400) { sb.AppendLine("Volume does not require cache."); } if ((vh.attributes & 0x800) == 0x800) { sb.AppendLine("Volume state is inconsistent."); } if ((vh.attributes & 0x1000) == 0x1000) { sb.AppendLine("CNIDs are reused."); } if ((vh.attributes & 0x2000) == 0x2000) { sb.AppendLine("Volume is journaled."); } if ((vh.attributes & 0x8000) == 0x8000) { sb.AppendLine("Volume is locked on software."); } sb.AppendFormat("Implementation that last mounted the volume: \"{0}\".", Encoding.ASCII.GetString(vh.lastMountedVersion)).AppendLine(); if ((vh.attributes & 0x2000) == 0x2000) { sb.AppendFormat("Journal starts at allocation block {0}.", vh.journalInfoBlock).AppendLine(); } sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(vh.createDate)).AppendLine(); sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(vh.modifyDate)).AppendLine(); if (vh.backupDate > 0) { sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(vh.backupDate)).AppendLine(); } else { sb.AppendLine("Volume has never been backed up"); } if (vh.backupDate > 0) { sb.AppendFormat("Last check date: {0}", DateHandlers.MacToDateTime(vh.checkedDate)).AppendLine(); } else { sb.AppendLine("Volume has never been checked up"); } sb.AppendFormat("{0} files on volume.", vh.fileCount).AppendLine(); sb.AppendFormat("{0} folders on volume.", vh.folderCount).AppendLine(); sb.AppendFormat("{0} bytes per allocation block.", vh.blockSize).AppendLine(); sb.AppendFormat("{0} allocation blocks.", vh.totalBlocks).AppendLine(); sb.AppendFormat("{0} free blocks.", vh.freeBlocks).AppendLine(); sb.AppendFormat("Next allocation block: {0}.", vh.nextAllocation).AppendLine(); sb.AppendFormat("Resource fork clump size: {0} bytes.", vh.rsrcClumpSize).AppendLine(); sb.AppendFormat("Data fork clump size: {0} bytes.", vh.dataClumpSize).AppendLine(); sb.AppendFormat("Next unused CNID: {0}.", vh.nextCatalogID).AppendLine(); sb.AppendFormat("Volume has been mounted writable {0} times.", vh.writeCount).AppendLine(); sb.AppendFormat("Allocation File is {0} bytes.", vh.allocationFile_logicalSize).AppendLine(); sb.AppendFormat("Extents File is {0} bytes.", vh.extentsFile_logicalSize).AppendLine(); sb.AppendFormat("Catalog File is {0} bytes.", vh.catalogFile_logicalSize).AppendLine(); sb.AppendFormat("Attributes File is {0} bytes.", vh.attributesFile_logicalSize).AppendLine(); sb.AppendFormat("Startup File is {0} bytes.", vh.startupFile_logicalSize).AppendLine(); sb.AppendLine("Finder info:"); sb.AppendFormat("CNID of bootable system's directory: {0}", vh.drFndrInfo0).AppendLine(); sb.AppendFormat("CNID of first-run application's directory: {0}", vh.drFndrInfo1).AppendLine(); sb.AppendFormat("CNID of previously opened directory: {0}", vh.drFndrInfo2).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", vh.drFndrInfo3).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", vh.drFndrInfo5).AppendLine(); if (vh.drFndrInfo6 != 0 && vh.drFndrInfo7 != 0) { sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", vh.drFndrInfo6, vh.drFndrInfo7).AppendLine(); } XmlFsType = new FileSystemType(); if (vh.backupDate > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(vh.backupDate); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable |= vh.drFndrInfo0 != 0 || vh.drFndrInfo3 != 0 || vh.drFndrInfo5 != 0; XmlFsType.Clusters = vh.totalBlocks; XmlFsType.ClusterSize = (int)vh.blockSize; if (vh.createDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(vh.createDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = (vh.attributes & 0x100) != 0x100; XmlFsType.Files = vh.fileCount; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = vh.freeBlocks; XmlFsType.FreeClustersSpecified = true; if (vh.modifyDate > 0) { XmlFsType.ModificationDate = DateHandlers.MacToDateTime(vh.modifyDate); XmlFsType.ModificationDateSpecified = true; } if (vh.signature == 0x482B) { XmlFsType.Type = "HFS+"; } if (vh.signature == 0x4858) { XmlFsType.Type = "HFSX"; } if (vh.drFndrInfo6 != 0 && vh.drFndrInfo7 != 0) { XmlFsType.VolumeSerial = $"{vh.drFndrInfo6:X8}{vh.drFndrInfo7:X8}"; } XmlFsType.SystemIdentifier = Encoding.ASCII.GetString(vh.lastMountedVersion); } else { sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); sb.AppendLine("This version is not supported yet."); } information = sb.ToString(); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.Unicode; information = ""; if (imagePlugin.Info.SectorSize < F2FS_MIN_SECTOR || imagePlugin.Info.SectorSize > F2FS_MAX_SECTOR) { return; } uint sbAddr = F2FS_SUPER_OFFSET / imagePlugin.Info.SectorSize; if (sbAddr == 0) { sbAddr = 1; } uint sbSize = (uint)(Marshal.SizeOf <Superblock>() / imagePlugin.Info.SectorSize); if (Marshal.SizeOf <Superblock>() % imagePlugin.Info.SectorSize != 0) { sbSize++; } byte[] sector = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize); if (sector.Length < Marshal.SizeOf <Superblock>()) { return; } Superblock f2fsSb = Marshal.ByteArrayToStructureLittleEndian <Superblock>(sector); if (f2fsSb.magic != F2FS_MAGIC) { return; } var sb = new StringBuilder(); sb.AppendLine("F2FS filesystem"); sb.AppendFormat("Version {0}.{1}", f2fsSb.major_ver, f2fsSb.minor_ver).AppendLine(); sb.AppendFormat("{0} bytes per sector", 1 << (int)f2fsSb.log_sectorsize).AppendLine(); sb.AppendFormat("{0} sectors ({1} bytes) per block", 1 << (int)f2fsSb.log_sectors_per_block, 1 << (int)f2fsSb.log_blocksize).AppendLine(); sb.AppendFormat("{0} blocks per segment", f2fsSb.log_blocks_per_seg).AppendLine(); sb.AppendFormat("{0} blocks in volume", f2fsSb.block_count).AppendLine(); sb.AppendFormat("{0} segments per section", f2fsSb.segs_per_sec).AppendLine(); sb.AppendFormat("{0} sections per zone", f2fsSb.secs_per_zone).AppendLine(); sb.AppendFormat("{0} sections", f2fsSb.section_count).AppendLine(); sb.AppendFormat("{0} segments", f2fsSb.segment_count).AppendLine(); sb.AppendFormat("Root directory resides on inode {0}", f2fsSb.root_ino).AppendLine(); sb.AppendFormat("Volume UUID: {0}", f2fsSb.uuid).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true)). AppendLine(); sb.AppendFormat("Volume last mounted on kernel version: {0}", StringHandlers.CToString(f2fsSb.version)). AppendLine(); sb.AppendFormat("Volume created on kernel version: {0}", StringHandlers.CToString(f2fsSb.init_version)). AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "F2FS filesystem", SystemIdentifier = Encoding.ASCII.GetString(f2fsSb.version), Clusters = f2fsSb.block_count, ClusterSize = (uint)(1 << (int)f2fsSb.log_blocksize), DataPreparerIdentifier = Encoding.ASCII.GetString(f2fsSb.init_version), VolumeName = StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true), VolumeSerial = f2fsSb.uuid.ToString() }; }