diff --git a/DiscImageChef.Filesystems/AppleHFS.cs b/DiscImageChef.Filesystems/AppleHFS.cs index 89380e4bc..b4b854a2a 100644 --- a/DiscImageChef.Filesystems/AppleHFS.cs +++ b/DiscImageChef.Filesystems/AppleHFS.cs @@ -81,25 +81,17 @@ namespace DiscImageChef.Filesystems if(imagePlugin.GetSectorSize() == 2352 || imagePlugin.GetSectorSize() == 2448 || imagePlugin.GetSectorSize() == 2048) { - mdb_sector = imagePlugin.ReadSector(2 + partition.Start); - drSigWord = BigEndianBitConverter.ToUInt16(mdb_sector, 0); + mdb_sector = imagePlugin.ReadSectors(partition.Start, 2); - if(drSigWord == HFS_MAGIC) + foreach(int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) { - drSigWord = BigEndianBitConverter.ToUInt16(mdb_sector, 0x7C); // Seek to embedded HFS+ signature + drSigWord = BigEndianBitConverter.ToUInt16(mdb_sector, offset); + if(drSigWord == HFS_MAGIC) + { + drSigWord = BigEndianBitConverter.ToUInt16(mdb_sector, offset + 0x7C); // Seek to embedded HFS+ signature - return drSigWord != HFSP_MAGIC; - } - mdb_sector = Read2048SectorAs512(imagePlugin, 2 + partition.Start * 4); - drSigWord = BigEndianBitConverter.ToUInt16(mdb_sector, 0); - - if(drSigWord == HFS_MAGIC) - { - DicConsole.DebugWriteLine("HFS plugin", "HFS sector size is 512 bytes, but device's 2048"); - - drSigWord = BigEndianBitConverter.ToUInt16(mdb_sector, 0x7C); // Seek to embedded HFS+ signature - - return drSigWord != HFSP_MAGIC; + return drSigWord != HFSP_MAGIC; + } } } else @@ -126,36 +118,34 @@ namespace DiscImageChef.Filesystems HFS_MasterDirectoryBlock MDB = new HFS_MasterDirectoryBlock(); HFS_BootBlock BB = new HFS_BootBlock(); - byte[] pString; - - byte[] bb_sector; - byte[] mdb_sector; + byte[] bb_sector = null; + byte[] mdb_sector = null; ushort drSigWord; bool APMFromHDDOnCD = false; + // TODO: I don't like this, I can do better if(imagePlugin.GetSectorSize() == 2352 || imagePlugin.GetSectorSize() == 2448 || imagePlugin.GetSectorSize() == 2048) { - mdb_sector = imagePlugin.ReadSector(2 + partition.Start); - drSigWord = BigEndianBitConverter.ToUInt16(mdb_sector, 0); + byte[] tmp_sector = imagePlugin.ReadSectors(partition.Start, 2); - if(drSigWord == HFS_MAGIC) + foreach(int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) { - bb_sector = imagePlugin.ReadSector(partition.Start); - } - else - { - mdb_sector = Read2048SectorAs512(imagePlugin, 2 + partition.Start * 4); - drSigWord = BigEndianBitConverter.ToUInt16(mdb_sector, 0); - + drSigWord = BigEndianBitConverter.ToUInt16(tmp_sector, offset); if(drSigWord == HFS_MAGIC) { - bb_sector = Read2048SectorAs512(imagePlugin, partition.Start * 4); + bb_sector = new byte[1024]; + mdb_sector = new byte[512]; + if(offset >= 0x400) + Array.Copy(tmp_sector, offset - 0x400, bb_sector, 0, 1024); + Array.Copy(tmp_sector, offset, mdb_sector, 0, 512); APMFromHDDOnCD = true; + break; } - else - return; } + + if(!APMFromHDDOnCD) + return; } else { @@ -174,7 +164,7 @@ namespace DiscImageChef.Filesystems sb.AppendLine("Apple Hierarchical File System"); sb.AppendLine(); if(APMFromHDDOnCD) - sb.AppendLine("HFS uses 512 bytes/sector while devices uses 2048 bytes/sector.").AppendLine(); + 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(); @@ -235,7 +225,8 @@ namespace DiscImageChef.Filesystems 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(); - sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", MDB.drFndrInfo6, MDB.drFndrInfo7).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) { diff --git a/DiscImageChef.Filesystems/AppleHFSPlus.cs b/DiscImageChef.Filesystems/AppleHFSPlus.cs index 345c9a320..82665c929 100644 --- a/DiscImageChef.Filesystems/AppleHFSPlus.cs +++ b/DiscImageChef.Filesystems/AppleHFSPlus.cs @@ -35,6 +35,7 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using DiscImageChef.CommonTypes; +using DiscImageChef.Console; namespace DiscImageChef.Filesystems { @@ -82,23 +83,27 @@ namespace DiscImageChef.Filesystems byte[] vh_sector; ulong hfsp_offset; - vh_sector = imagePlugin.ReadSector(2 + partition.Start); // Read volume header, of HFS Wrapper MDB + uint sectorsToRead = 0x800 / imagePlugin.ImageInfo.sectorSize; + if(0x800 % imagePlugin.ImageInfo.sectorSize > 0) + sectorsToRead++; - drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0); // Check for HFS Wrapper MDB + vh_sector = imagePlugin.ReadSectors(partition.Start, sectorsToRead); // Read volume header, of HFS Wrapper MDB + + drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0x400); // Check for HFS Wrapper MDB if(drSigWord == HFS_MAGIC) // "BD" { - drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0x07C); // Read embedded HFS+ signature + drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0x47C); // Read embedded HFS+ signature if(drSigWord == HFSP_MAGIC) // "H+" { - xdrStABNt = BigEndianBitConverter.ToUInt16(vh_sector, 0x07E); // Starting block number of embedded HFS+ volume + xdrStABNt = BigEndianBitConverter.ToUInt16(vh_sector, 0x47E); // Starting block number of embedded HFS+ volume - drAlBlkSiz = BigEndianBitConverter.ToUInt32(vh_sector, 0x014); // Block size + drAlBlkSiz = BigEndianBitConverter.ToUInt32(vh_sector, 0x414); // Block size - drAlBlSt = BigEndianBitConverter.ToUInt16(vh_sector, 0x01C); // Start of allocated blocks (in 512-byte/block) + drAlBlSt = BigEndianBitConverter.ToUInt16(vh_sector, 0x41C); // Start of allocated blocks (in 512-byte/block) - hfsp_offset = (drAlBlSt + xdrStABNt * (drAlBlkSiz / 512)) * (imagePlugin.GetSectorSize() / 512); + hfsp_offset = (ulong)(((drAlBlSt * 512) + (xdrStABNt * drAlBlkSiz)) / imagePlugin.GetSectorSize()); } else { @@ -110,9 +115,9 @@ namespace DiscImageChef.Filesystems hfsp_offset = 0; } - vh_sector = imagePlugin.ReadSector(2 + partition.Start + hfsp_offset); // Read volume header + vh_sector = imagePlugin.ReadSectors(partition.Start + hfsp_offset, sectorsToRead); // Read volume header - drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0); + drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0x400); if(drSigWord == HFSP_MAGIC || drSigWord == HFSX_MAGIC) return true; return false; @@ -132,23 +137,27 @@ namespace DiscImageChef.Filesystems bool wrapped; byte[] vh_sector; - vh_sector = imagePlugin.ReadSector(2 + partition.Start); // Read volume header, of HFS Wrapper MDB + uint sectorsToRead = 0x800 / imagePlugin.ImageInfo.sectorSize; + if(0x800 % imagePlugin.ImageInfo.sectorSize > 0) + sectorsToRead++; - drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0); // Check for HFS Wrapper MDB + vh_sector = imagePlugin.ReadSectors(partition.Start, sectorsToRead); // Read volume header, of HFS Wrapper MDB + + drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0x400); // Check for HFS Wrapper MDB if(drSigWord == HFS_MAGIC) // "BD" { - drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0x07C); // Read embedded HFS+ signature + drSigWord = BigEndianBitConverter.ToUInt16(vh_sector, 0x47C); // Read embedded HFS+ signature if(drSigWord == HFSP_MAGIC) // "H+" { - xdrStABNt = BigEndianBitConverter.ToUInt16(vh_sector, 0x07E); // Starting block number of embedded HFS+ volume + xdrStABNt = BigEndianBitConverter.ToUInt16(vh_sector, 0x47E); // Starting block number of embedded HFS+ volume - drAlBlkSiz = BigEndianBitConverter.ToUInt32(vh_sector, 0x014); // Block size + drAlBlkSiz = BigEndianBitConverter.ToUInt32(vh_sector, 0x414); // Block size - drAlBlSt = BigEndianBitConverter.ToUInt16(vh_sector, 0x01C); // Start of allocated blocks (in 512-byte/block) + drAlBlSt = BigEndianBitConverter.ToUInt16(vh_sector, 0x41C); // Start of allocated blocks (in 512-byte/block) - hfsp_offset = (drAlBlSt + xdrStABNt * (drAlBlkSiz / 512)) * (imagePlugin.GetSectorSize() / 512); + hfsp_offset = (ulong)(((drAlBlSt * 512) + (xdrStABNt * drAlBlkSiz)) / imagePlugin.GetSectorSize()); wrapped = true; } else @@ -163,9 +172,9 @@ namespace DiscImageChef.Filesystems wrapped = false; } - vh_sector = imagePlugin.ReadSector(2 + partition.Start + hfsp_offset); // Read volume header + vh_sector = imagePlugin.ReadSectors(partition.Start + hfsp_offset, sectorsToRead); // Read volume header - HPVH.signature = BigEndianBitConverter.ToUInt16(vh_sector, 0x000); + HPVH.signature = BigEndianBitConverter.ToUInt16(vh_sector, 0x400); if(HPVH.signature == HFSP_MAGIC || HPVH.signature == HFSX_MAGIC) { StringBuilder sb = new StringBuilder(); @@ -177,6 +186,10 @@ namespace DiscImageChef.Filesystems if(wrapped) sb.AppendLine("Volume is wrapped inside an HFS volume."); + byte[] tmp = new byte[0x400]; + Array.Copy(vh_sector, 0x400, tmp, 0, 0x400); + vh_sector = tmp; + HPVH = BigEndianMarshal.ByteArrayToStructureBigEndian(vh_sector); if(HPVH.version == 4 || HPVH.version == 5) diff --git a/DiscImageChef.Filesystems/ProDOS.cs b/DiscImageChef.Filesystems/ProDOS.cs index 07a8e9a24..dfa58e842 100644 --- a/DiscImageChef.Filesystems/ProDOS.cs +++ b/DiscImageChef.Filesystems/ProDOS.cs @@ -101,6 +101,25 @@ namespace DiscImageChef.Filesystems // Blocks 0 and 1 are boot code byte[] rootDirectoryKeyBlock = imagePlugin.ReadSector(2 + partition.Start); + bool APMFromHDDOnCD = false; + + if(imagePlugin.GetSectorSize() == 2352 || imagePlugin.GetSectorSize() == 2448 || imagePlugin.GetSectorSize() == 2048) + { + byte[] tmp = imagePlugin.ReadSectors(partition.Start, 2); + + foreach(int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) + { + if(BitConverter.ToUInt16(tmp, offset) == 0 && + (byte)((tmp[offset + 0x04] & ProDOSStorageTypeMask) >> 4) == RootDirectoryType && + tmp[offset + 0x23] == ProDOSEntryLength && + tmp[offset + 0x24] == ProDOSEntriesPerBlock) + { + Array.Copy(tmp, offset, rootDirectoryKeyBlock, 0, 0x200); + APMFromHDDOnCD = true; + break; + } + } + } ushort prePointer = BitConverter.ToUInt16(rootDirectoryKeyBlock, 0); DicConsole.DebugWriteLine("ProDOS plugin", "prePointer = {0}", prePointer); @@ -128,6 +147,9 @@ namespace DiscImageChef.Filesystems return false; ushort total_blocks = BitConverter.ToUInt16(rootDirectoryKeyBlock, 0x29); + if(APMFromHDDOnCD) + total_blocks /= 4; + DicConsole.DebugWriteLine("ProDOS plugin", "{0} <= ({1} - {2} + 1)? {3}", total_blocks, partition.End, partition.Start, total_blocks <= (partition.End - partition.Start + 1)); return total_blocks <= (partition.End - partition.Start + 1); } @@ -139,6 +161,26 @@ namespace DiscImageChef.Filesystems // Blocks 0 and 1 are boot code byte[] rootDirectoryKeyBlockBytes = imagePlugin.ReadSector(2 + partition.Start); + bool APMFromHDDOnCD = false; + + if(imagePlugin.GetSectorSize() == 2352 || imagePlugin.GetSectorSize() == 2448 || imagePlugin.GetSectorSize() == 2048) + { + byte[] tmp = imagePlugin.ReadSectors(partition.Start, 2); + + foreach(int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) + { + if(BitConverter.ToUInt16(tmp, offset) == 0 && + (byte)((tmp[offset + 0x04] & ProDOSStorageTypeMask) >> 4) == RootDirectoryType && + tmp[offset + 0x23] == ProDOSEntryLength && + tmp[offset + 0x24] == ProDOSEntriesPerBlock) + { + Array.Copy(tmp, offset, rootDirectoryKeyBlockBytes, 0, 0x200); + APMFromHDDOnCD = true; + break; + } + } + } + ProDOSRootDirectoryKeyBlock rootDirectoryKeyBlock = new ProDOSRootDirectoryKeyBlock(); rootDirectoryKeyBlock.header = new ProDOSRootDirectoryHeader(); @@ -158,22 +200,32 @@ namespace DiscImageChef.Filesystems temp_timestamp_left = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x1C); temp_timestamp_right = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x1E); - temp_timestamp = (uint)((temp_timestamp_left << 16) + temp_timestamp_right); - year = (int)((temp_timestamp & ProDOSYearMask) >> 25); - month = (int)((temp_timestamp & ProDOSMonthMask) >> 21); - day = (int)((temp_timestamp & ProDOSDayMask) >> 16); - hour = (int)((temp_timestamp & ProDOSHourMask) >> 8); - minute = (int)(temp_timestamp & ProDOSMinuteMask); - year += 1900; - if(year < 1940) - year += 100; - DicConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp_left = 0x{0:X4}", temp_timestamp_left); - DicConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp_right = 0x{0:X4}", temp_timestamp_right); - DicConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp = 0x{0:X8}", temp_timestamp); - DicConsole.DebugWriteLine("ProDOS plugin", "Datetime field year {0}, month {1}, day {2}, hour {3}, minute {4}.", year, month, day, hour, minute); + bool dateCorrect; + try + { + temp_timestamp = (uint)((temp_timestamp_left << 16) + temp_timestamp_right); + year = (int)((temp_timestamp & ProDOSYearMask) >> 25); + month = (int)((temp_timestamp & ProDOSMonthMask) >> 21); + day = (int)((temp_timestamp & ProDOSDayMask) >> 16); + hour = (int)((temp_timestamp & ProDOSHourMask) >> 8); + minute = (int)(temp_timestamp & ProDOSMinuteMask); + year += 1900; + if(year < 1940) + year += 100; - rootDirectoryKeyBlock.header.creation_time = new DateTime(year, month, day, hour, minute, 0); + DicConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp_left = 0x{0:X4}", temp_timestamp_left); + DicConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp_right = 0x{0:X4}", temp_timestamp_right); + DicConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp = 0x{0:X8}", temp_timestamp); + DicConsole.DebugWriteLine("ProDOS plugin", "Datetime field year {0}, month {1}, day {2}, hour {3}, minute {4}.", year, month, day, hour, minute); + + rootDirectoryKeyBlock.header.creation_time = new DateTime(year, month, day, hour, minute, 0); + dateCorrect = true; + } + catch(ArgumentOutOfRangeException) + { + dateCorrect = false; + } rootDirectoryKeyBlock.header.version = rootDirectoryKeyBlockBytes[0x20]; rootDirectoryKeyBlock.header.min_version = rootDirectoryKeyBlockBytes[0x21]; @@ -185,6 +237,9 @@ namespace DiscImageChef.Filesystems rootDirectoryKeyBlock.header.bit_map_pointer = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x27); rootDirectoryKeyBlock.header.total_blocks = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x29); + if(APMFromHDDOnCD) + sbInformation.AppendLine("ProDOS uses 512 bytes/sector while devices uses 2048 bytes/sector.").AppendLine(); + if(rootDirectoryKeyBlock.header.version != ProDOSVersion1 || rootDirectoryKeyBlock.header.min_version != ProDOSVersion1) { sbInformation.AppendLine("Warning! Detected unknown ProDOS version ProDOS filesystem."); @@ -202,7 +257,8 @@ namespace DiscImageChef.Filesystems sbInformation.AppendFormat("Unknown ProDOS version with field {0} is at least required for reading this volume.", rootDirectoryKeyBlock.header.min_version).AppendLine(); sbInformation.AppendFormat("Volume name is {0}", rootDirectoryKeyBlock.header.volume_name).AppendLine(); - sbInformation.AppendFormat("Volume created on {0}", rootDirectoryKeyBlock.header.creation_time).AppendLine(); + if(dateCorrect) + sbInformation.AppendFormat("Volume created on {0}", rootDirectoryKeyBlock.header.creation_time).AppendLine(); sbInformation.AppendFormat("{0} bytes per directory entry", rootDirectoryKeyBlock.header.entry_length).AppendLine(); sbInformation.AppendFormat("{0} entries per directory block", rootDirectoryKeyBlock.header.entries_per_block).AppendLine(); sbInformation.AppendFormat("{0} files in root directory", rootDirectoryKeyBlock.header.file_count).AppendLine(); @@ -227,7 +283,7 @@ namespace DiscImageChef.Filesystems xmlFSType = new Schemas.FileSystemType(); xmlFSType.VolumeName = rootDirectoryKeyBlock.header.volume_name; - if(year != 0 || month != 0 || day != 0 || hour != 0 || minute != 0) + if(dateCorrect) { xmlFSType.CreationDate = rootDirectoryKeyBlock.header.creation_time; xmlFSType.CreationDateSpecified = true;