Added support for variable sectors per track disks.

This commit is contained in:
2017-09-27 12:37:03 +01:00
parent 6f8027a44b
commit fbf260d6e3

View File

@@ -190,15 +190,16 @@ namespace DiscImageChef.ImagePlugins
TD0Header header; TD0Header header;
TDCommentBlockHeader commentHeader; TDCommentBlockHeader commentHeader;
byte[] commentBlock; byte[] commentBlock;
Dictionary<uint, byte[]> sectorsData;
// LBA, data // LBA, data
uint totalDiskSize; uint totalDiskSize;
bool ADiskCRCHasFailed; bool ADiskCRCHasFailed;
List<ulong> SectorsWhereCRCHasFailed; List<ulong> SectorsWhereCRCHasFailed;
// Cylinder by head, sector data matrix
byte[][][][] sectorsData;
#endregion #endregion
public TeleDisk() public TeleDisk()
{ {
Name = "Sydex TeleDisk"; Name = "Sydex TeleDisk";
PluginUUID = new Guid("0240B7B1-E959-4CDC-B0BD-386D6E467B88"); PluginUUID = new Guid("0240B7B1-E959-4CDC-B0BD-386D6E467B88");
@@ -419,11 +420,101 @@ namespace DiscImageChef.ImagePlugins
DicConsole.DebugWriteLine("TeleDisk plugin", "Parsing image"); DicConsole.DebugWriteLine("TeleDisk plugin", "Parsing image");
totalDiskSize = 0; totalDiskSize = 0;
byte spt = 0;
ImageInfo.imageSize = 0; ImageInfo.imageSize = 0;
sectorsData = new Dictionary<uint, byte[]>();
ImageInfo.sectorSize = 0; int totalCylinders = -1;
while(true) int totalHeads = -1;
int maxSector = -1;
int totalSectors = 0;
long currentPos = stream.Position;
ImageInfo.sectorSize = uint.MaxValue;
ImageInfo.sectorsPerTrack = uint.MaxValue;
// Count cylinders
while(true)
{
TDTrackHeader TDTrack = new TDTrackHeader();
TDTrack.sectors = (byte)stream.ReadByte();
TDTrack.cylinder = (byte)stream.ReadByte();
TDTrack.head = (byte)stream.ReadByte();
TDTrack.crc = (byte)stream.ReadByte();
if(TDTrack.cylinder > totalCylinders)
totalCylinders = TDTrack.cylinder;
if(TDTrack.head > totalHeads)
totalHeads = TDTrack.head;
if(TDTrack.sectors == 0xFF) // End of disk image
break;
if(TDTrack.sectors < ImageInfo.sectorsPerTrack)
ImageInfo.sectorsPerTrack = TDTrack.sectors;
for(byte processedSectors = 0; processedSectors < TDTrack.sectors; processedSectors++)
{
TDSectorHeader TDSector = new TDSectorHeader();
TDDataHeader TDData = new TDDataHeader();
byte[] dataSizeBytes = new byte[2];
byte[] data;
byte[] decodedData;
TDSector.cylinder = (byte)stream.ReadByte();
TDSector.head = (byte)stream.ReadByte();
TDSector.sectorNumber = (byte)stream.ReadByte();
TDSector.sectorSize = (byte)stream.ReadByte();
TDSector.flags = (byte)stream.ReadByte();
TDSector.crc = (byte)stream.ReadByte();
if(TDSector.sectorNumber > maxSector)
maxSector = TDSector.sectorNumber;
if((TDSector.flags & FlagsSectorDataless) != FlagsSectorDataless && (TDSector.flags & FlagsSectorSkipped) != FlagsSectorSkipped)
{
stream.Read(dataSizeBytes, 0, 2);
TDData.dataSize = BitConverter.ToUInt16(dataSizeBytes, 0);
TDData.dataSize--; // Sydex decided to including dataEncoding byte as part of it
ImageInfo.imageSize += TDData.dataSize;
TDData.dataEncoding = (byte)stream.ReadByte();
data = new byte[TDData.dataSize];
stream.Read(data, 0, TDData.dataSize);
}
if(128 << TDSector.sectorSize < ImageInfo.sectorSize)
ImageInfo.sectorSize = (uint)(128 << TDSector.sectorSize);
totalSectors++;
}
}
totalCylinders++;
totalHeads++;
if(totalCylinders <= 0 || totalHeads <= 0)
throw new ImageNotSupportedException("No cylinders or heads found");
sectorsData = new byte[totalCylinders][][][];
// Total sectors per track
uint[][] spts = new uint[totalCylinders][];
ImageInfo.cylinders = (ushort)totalCylinders;
ImageInfo.heads = (byte)totalHeads;
DicConsole.DebugWriteLine("TeleDisk plugin", "Found {0} cylinders and {1} heads with a maximum sector number of {2}", totalCylinders, totalHeads, maxSector);
// Create heads
for(int i = 0; i < totalCylinders; i++)
{
sectorsData[i] = new byte[totalHeads][][];
spts[i] = new uint[totalHeads];
for(int j = 0; j < totalHeads; j++)
sectorsData[i][j] = new byte[maxSector + 1][];
}
// Decode the image
stream.Seek(currentPos, SeekOrigin.Begin);
while(true)
{ {
TDTrackHeader TDTrack = new TDTrackHeader(); TDTrackHeader TDTrack = new TDTrackHeader();
byte[] TDTrackForCRC = new byte[3]; byte[] TDTrackForCRC = new byte[3];
@@ -451,18 +542,11 @@ namespace DiscImageChef.ImagePlugins
if(TDTrack.sectors == 0xFF) // End of disk image if(TDTrack.sectors == 0xFF) // End of disk image
{ {
DicConsole.DebugWriteLine("TeleDisk plugin", "End of disk image arrived"); DicConsole.DebugWriteLine("TeleDisk plugin", "End of disk image arrived");
DicConsole.DebugWriteLine("TeleDisk plugin", "Total of {0} data sectors, for {1} bytes", sectorsData.Count, totalDiskSize); DicConsole.DebugWriteLine("TeleDisk plugin", "Total of {0} data sectors, for {1} bytes", totalSectors, totalDiskSize);
break; break;
} }
if(spt != TDTrack.sectors && TDTrack.sectors > 0)
{
if(spt != 0)
throw new FeatureUnsupportedImageException("Variable number of sectors per track. This kind of image is not yet supported");
spt = TDTrack.sectors;
}
for(byte processedSectors = 0; processedSectors < TDTrack.sectors; processedSectors++) for(byte processedSectors = 0; processedSectors < TDTrack.sectors; processedSectors++)
{ {
TDSectorHeader TDSector = new TDSectorHeader(); TDSectorHeader TDSector = new TDSectorHeader();
@@ -486,67 +570,38 @@ namespace DiscImageChef.ImagePlugins
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector flags: 0x{0:X2}", TDSector.flags); DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector flags: 0x{0:X2}", TDSector.flags);
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector CRC (plus headers): 0x{0:X2}", TDSector.crc); DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector CRC (plus headers): 0x{0:X2}", TDSector.crc);
uint LBA = (uint)((TDSector.cylinder * header.sides * spt) + (TDSector.head * spt) + (TDSector.sectorNumber - 1)); uint LBA = (uint)((TDSector.cylinder * header.sides * ImageInfo.sectorsPerTrack) + (TDSector.head * ImageInfo.sectorsPerTrack) + (TDSector.sectorNumber - 1));
if((TDSector.flags & FlagsSectorDataless) != FlagsSectorDataless && (TDSector.flags & FlagsSectorSkipped) != FlagsSectorSkipped) if((TDSector.flags & FlagsSectorDataless) != FlagsSectorDataless && (TDSector.flags & FlagsSectorSkipped) != FlagsSectorSkipped)
{ {
stream.Read(dataSizeBytes, 0, 2); stream.Read(dataSizeBytes, 0, 2);
TDData.dataSize = BitConverter.ToUInt16(dataSizeBytes, 0); TDData.dataSize = BitConverter.ToUInt16(dataSizeBytes, 0);
TDData.dataSize--; // Sydex decided to including dataEncoding byte as part of it TDData.dataSize--; // Sydex decided to including dataEncoding byte as part of it
ImageInfo.imageSize += TDData.dataSize; ImageInfo.imageSize += TDData.dataSize;
TDData.dataEncoding = (byte)stream.ReadByte(); TDData.dataEncoding = (byte)stream.ReadByte();
data = new byte[TDData.dataSize]; data = new byte[TDData.dataSize];
stream.Read(data, 0, TDData.dataSize); stream.Read(data, 0, TDData.dataSize);
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tData size (in-image): {0}", TDData.dataSize); DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tData size (in-image): {0}", TDData.dataSize);
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tData encoding: 0x{0:X2}", TDData.dataEncoding); DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tData encoding: 0x{0:X2}", TDData.dataEncoding);
decodedData = DecodeTeleDiskData(TDSector.sectorSize, TDData.dataEncoding, data); decodedData = DecodeTeleDiskData(TDSector.sectorSize, TDData.dataEncoding, data);
byte TDSectorCalculatedCRC = (byte)(TeleDiskCRC(0, decodedData) & 0xFF); byte TDSectorCalculatedCRC = (byte)(TeleDiskCRC(0, decodedData) & 0xFF);
if(TDSectorCalculatedCRC != TDSector.crc) if(TDSectorCalculatedCRC != TDSector.crc)
{ {
DicConsole.DebugWriteLine("TeleDisk plugin", "Sector LBA {0} calculated CRC 0x{1:X2} differs from stored CRC 0x{2:X2}", LBA, TDSectorCalculatedCRC, TDSector.crc); DicConsole.DebugWriteLine("TeleDisk plugin", "Sector {0}:{3}:{4} calculated CRC 0x{1:X2} differs from stored CRC 0x{2:X2}", TDTrack.cylinder, TDSectorCalculatedCRC, TDSector.crc, TDTrack.cylinder, TDSector.sectorNumber);
if((TDSector.flags & FlagsSectorNoID) != FlagsSectorNoID) if((TDSector.flags & FlagsSectorNoID) != FlagsSectorNoID)
if(!sectorsData.ContainsKey(LBA) && (TDSector.flags & FlagsSectorDuplicate) != FlagsSectorDuplicate) SectorsWhereCRCHasFailed.Add(LBA);
SectorsWhereCRCHasFailed.Add(LBA); }
} }
} else
else decodedData = new byte[128 << TDSector.sectorSize];
{
switch(TDSector.sectorSize) DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tLBA: {0}", LBA);
{
case SectorSize128:
decodedData = new byte[128];
break;
case SectorSize256:
decodedData = new byte[256];
break;
case SectorSize512:
decodedData = new byte[512];
break;
case SectorSize1K:
decodedData = new byte[1024];
break;
case SectorSize2K:
decodedData = new byte[2048];
break;
case SectorSize4K:
decodedData = new byte[4096];
break;
case SectorSize8K:
decodedData = new byte[8192];
break;
default:
throw new ImageNotSupportedException(string.Format("Sector size {0} for cylinder {1} head {2} sector {3} is incorrect.",
TDSector.sectorSize, TDSector.cylinder, TDSector.head, TDSector.sectorNumber));
}
ArrayHelpers.ArrayFill(decodedData, (byte)0);
}
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tLBA: {0}", LBA);
if((TDSector.flags & FlagsSectorNoID) != FlagsSectorNoID) if((TDSector.flags & FlagsSectorNoID) != FlagsSectorNoID)
{ {
if(sectorsData.ContainsKey(LBA)) if(sectorsData[TDTrack.cylinder][TDTrack.head][TDSector.sectorNumber] != null)
{ {
if((TDSector.flags & FlagsSectorDuplicate) == FlagsSectorDuplicate) if((TDSector.flags & FlagsSectorDuplicate) == FlagsSectorDuplicate)
{ {
@@ -561,16 +616,14 @@ namespace DiscImageChef.ImagePlugins
} }
else else
{ {
sectorsData.Add(LBA, decodedData); sectorsData[TDTrack.cylinder][TDTrack.head][TDSector.sectorNumber] = decodedData;
totalDiskSize += (uint)decodedData.Length; totalDiskSize += (uint)decodedData.Length;
} }
} }
if(decodedData.Length > ImageInfo.sectorSize)
ImageInfo.sectorSize = (uint)decodedData.Length;
} }
} }
ImageInfo.sectors = (ulong)sectorsData.Count; ImageInfo.sectors = ImageInfo.cylinders * ImageInfo.heads * ImageInfo.sectorsPerTrack;
ImageInfo.mediaType = DecodeTeleDiskDiskType(); ImageInfo.mediaType = DecodeTeleDiskDiskType();
ImageInfo.xmlMediaType = XmlMediaType.BlockMedia; ImageInfo.xmlMediaType = XmlMediaType.BlockMedia;
@@ -579,10 +632,6 @@ namespace DiscImageChef.ImagePlugins
if(!string.IsNullOrEmpty(ImageInfo.imageComments)) if(!string.IsNullOrEmpty(ImageInfo.imageComments))
DicConsole.VerboseWriteLine("TeleDisk comments: {0}", ImageInfo.imageComments); DicConsole.VerboseWriteLine("TeleDisk comments: {0}", ImageInfo.imageComments);
ImageInfo.heads = header.sides;
ImageInfo.sectorsPerTrack = spt;
ImageInfo.cylinders = (uint)((ImageInfo.sectors / header.sides) / spt);
return true; return true;
} }
@@ -608,49 +657,48 @@ namespace DiscImageChef.ImagePlugins
public override byte[] ReadSector(ulong sectorAddress) public override byte[] ReadSector(ulong sectorAddress)
{ {
return ReadSectors(sectorAddress, 1); (ushort cylinder, byte head, byte sector) = LbaToChs(sectorAddress);
}
if(cylinder >= sectorsData.Length)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(head >= sectorsData[cylinder].Length)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sector > sectorsData[cylinder][head].Length)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
return sectorsData[cylinder][head][sector];
}
public override byte[] ReadSectors(ulong sectorAddress, uint length) public override byte[] ReadSectors(ulong sectorAddress, uint length)
{ {
if(sectorAddress > (ulong)sectorsData.Count - 1) if(sectorAddress > ImageInfo.sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found"); throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > (ulong)sectorsData.Count) if(sectorAddress + length > ImageInfo.sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available"); throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] data = new byte[1]; // To make compiler happy MemoryStream buffer = new MemoryStream();
bool first = true; for(uint i = 0; i < length; i++)
int dataPosition = 0; {
byte[] sector = ReadSector(sectorAddress + i);
buffer.Write(sector, 0, sector.Length);
}
for(ulong i = sectorAddress; i < (sectorAddress + length); i++) return buffer.ToArray();
{ }
if(!sectorsData.ContainsKey((uint)i))
throw new ImageNotSupportedException(string.Format("Requested sector {0} not found", i));
byte[] sector; (ushort cylinder, byte head, byte sector) LbaToChs(ulong lba)
{
ushort cylinder = (ushort)(lba / (ImageInfo.heads * ImageInfo.sectorsPerTrack));
byte head = (byte)((lba / ImageInfo.sectorsPerTrack) % ImageInfo.heads);
byte sector = (byte)((lba % ImageInfo.sectorsPerTrack) + 1);
if(!sectorsData.TryGetValue((uint)i, out sector)) return (cylinder, head, sector);
throw new ImageNotSupportedException(string.Format("Error reading sector {0}", i)); }
if(first) public override byte[] ReadSectorLong(ulong sectorAddress)
{
data = new byte[sector.Length];
Array.Copy(sector, data, sector.Length);
first = false;
}
else
{
Array.Resize(ref data, dataPosition + sector.Length);
Array.Copy(sector, 0, data, dataPosition, sector.Length);
}
dataPosition += sector.Length;
}
return data;
}
public override byte[] ReadSectorLong(ulong sectorAddress)
{ {
return ReadSectors(sectorAddress, 1); return ReadSectors(sectorAddress, 1);
} }