Corrected detection and reading of misaligned partitions on

optical media (e.g. map says sector 17 on 512 byte units, that
	would fall on sector 4.25 on 2048 units).
This commit is contained in:
2017-07-20 13:14:12 +01:00
parent 290cb74540
commit 9df8dee93d
3 changed files with 129 additions and 69 deletions

View File

@@ -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)
{

View File

@@ -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<HFSPlusVolumeHeader>(vh_sector);
if(HPVH.version == 4 || HPVH.version == 5)

View File

@@ -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;