public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); // Even if disk name is supposedly ASCII, I'm pretty sure most emulators allow Shift-JIS to be used :p var shiftjis = Encoding.GetEncoding("shift_jis"); if (stream.Length < Marshal.SizeOf <D88Header>()) { return(false); } byte[] hdrB = new byte[Marshal.SizeOf <D88Header>()]; stream.Read(hdrB, 0, hdrB.Length); D88Header d88Hdr = Marshal.ByteArrayToStructureLittleEndian <D88Header>(hdrB); AaruConsole.DebugWriteLine("D88 plugin", "d88hdr.name = \"{0}\"", StringHandlers.CToString(d88Hdr.name, shiftjis)); AaruConsole.DebugWriteLine("D88 plugin", "d88hdr.reserved is empty? = {0}", d88Hdr.reserved.SequenceEqual(_reservedEmpty)); AaruConsole.DebugWriteLine("D88 plugin", "d88hdr.write_protect = 0x{0:X2}", d88Hdr.write_protect); AaruConsole.DebugWriteLine("D88 plugin", "d88hdr.disk_type = {0} ({1})", d88Hdr.disk_type, (byte)d88Hdr.disk_type); AaruConsole.DebugWriteLine("D88 plugin", "d88hdr.disk_size = {0}", d88Hdr.disk_size); if (d88Hdr.disk_size != stream.Length) { return(false); } if (d88Hdr.disk_type != DiskType.D2 && d88Hdr.disk_type != DiskType.Dd2 && d88Hdr.disk_type != DiskType.Hd2) { return(false); } if (!d88Hdr.reserved.SequenceEqual(_reservedEmpty)) { return(false); } int trkCounter = 0; foreach (int t in d88Hdr.track_table) { if (t > 0) { trkCounter++; } if (t < 0 || t > stream.Length) { return(false); } } AaruConsole.DebugWriteLine("D88 plugin", "{0} tracks", trkCounter); if (trkCounter == 0) { return(false); } hdrB = new byte[Marshal.SizeOf <SectorHeader>()]; stream.Seek(d88Hdr.track_table[0], SeekOrigin.Begin); stream.Read(hdrB, 0, hdrB.Length); SectorHeader sechdr = Marshal.ByteArrayToStructureLittleEndian <SectorHeader>(hdrB); AaruConsole.DebugWriteLine("D88 plugin", "sechdr.c = {0}", sechdr.c); AaruConsole.DebugWriteLine("D88 plugin", "sechdr.h = {0}", sechdr.h); AaruConsole.DebugWriteLine("D88 plugin", "sechdr.r = {0}", sechdr.r); AaruConsole.DebugWriteLine("D88 plugin", "sechdr.n = {0}", sechdr.n); AaruConsole.DebugWriteLine("D88 plugin", "sechdr.spt = {0}", sechdr.spt); AaruConsole.DebugWriteLine("D88 plugin", "sechdr.density = {0}", sechdr.density); AaruConsole.DebugWriteLine("D88 plugin", "sechdr.deleted_mark = {0}", sechdr.deleted_mark); AaruConsole.DebugWriteLine("D88 plugin", "sechdr.status = {0}", sechdr.status); AaruConsole.DebugWriteLine("D88 plugin", "sechdr.size_of_data = {0}", sechdr.size_of_data); short spt = sechdr.spt; IBMSectorSizeCode bps = sechdr.n; bool allEqual = true; _sectorsData = new List <byte[]>(); for (int i = 0; i < trkCounter; i++) { stream.Seek(d88Hdr.track_table[i], SeekOrigin.Begin); stream.Read(hdrB, 0, hdrB.Length); SortedDictionary <byte, byte[]> sectors = new SortedDictionary <byte, byte[]>(); sechdr = Marshal.ByteArrayToStructureLittleEndian <SectorHeader>(hdrB); if (sechdr.spt != spt || sechdr.n != bps) { AaruConsole.DebugWriteLine("D88 plugin", "Disk tracks are not same size. spt = {0} (expected {1}), bps = {2} (expected {3}) at track {4} sector {5}", sechdr.spt, spt, sechdr.n, bps, i, 0); allEqual = false; } short maxJ = sechdr.spt; byte[] secB; for (short j = 1; j < maxJ; j++) { secB = new byte[sechdr.size_of_data]; stream.Read(secB, 0, secB.Length); sectors.Add(sechdr.r, secB); stream.Read(hdrB, 0, hdrB.Length); sechdr = Marshal.ByteArrayToStructureLittleEndian <SectorHeader>(hdrB); if (sechdr.spt == spt && sechdr.n == bps) { continue; } AaruConsole.DebugWriteLine("D88 plugin", "Disk tracks are not same size. spt = {0} (expected {1}), bps = {2} (expected {3}) at track {4} sector {5}", sechdr.spt, spt, sechdr.n, bps, i, j, sechdr.deleted_mark); allEqual = false; } secB = new byte[sechdr.size_of_data]; stream.Read(secB, 0, secB.Length); sectors.Add(sechdr.r, secB); foreach (KeyValuePair <byte, byte[]> kvp in sectors) { _sectorsData.Add(kvp.Value); } } AaruConsole.DebugWriteLine("D88 plugin", "{0} sectors", _sectorsData.Count); _imageInfo.MediaType = MediaType.Unknown; if (allEqual) { if (trkCounter == 154 && spt == 26 && bps == IBMSectorSizeCode.EighthKilo) { _imageInfo.MediaType = MediaType.NEC_8_SD; } else if (bps == IBMSectorSizeCode.QuarterKilo) { switch (trkCounter) { case 80 when spt == 16: _imageInfo.MediaType = MediaType.NEC_525_SS; break; case 154 when spt == 26: _imageInfo.MediaType = MediaType.NEC_8_DD; break; case 160 when spt == 16: _imageInfo.MediaType = MediaType.NEC_525_DS; break; } } else if (trkCounter == 154 && spt == 8 && bps == IBMSectorSizeCode.Kilo) { _imageInfo.MediaType = MediaType.NEC_525_HD; } else if (bps == IBMSectorSizeCode.HalfKilo) { switch (d88Hdr.track_table.Length) { case 40: { switch (spt) { case 8: _imageInfo.MediaType = MediaType.DOS_525_SS_DD_8; break; case 9: _imageInfo.MediaType = MediaType.DOS_525_SS_DD_9; break; } } break; case 80: { switch (spt) { case 8: _imageInfo.MediaType = MediaType.DOS_525_DS_DD_8; break; case 9: _imageInfo.MediaType = MediaType.DOS_525_DS_DD_9; break; } } break; case 160: { switch (spt) { case 15: _imageInfo.MediaType = MediaType.NEC_35_HD_15; break; case 9: _imageInfo.MediaType = MediaType.DOS_35_DS_DD_9; break; case 18: _imageInfo.MediaType = MediaType.DOS_35_HD; break; case 36: _imageInfo.MediaType = MediaType.DOS_35_ED; break; } } break; case 480: if (spt == 38) { _imageInfo.MediaType = MediaType.NEC_35_TD; } break; } } } AaruConsole.DebugWriteLine("D88 plugin", "MediaType: {0}", _imageInfo.MediaType); _imageInfo.ImageSize = (ulong)d88Hdr.disk_size; _imageInfo.CreationTime = imageFilter.GetCreationTime(); _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); _imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); _imageInfo.Sectors = (ulong)_sectorsData.Count; _imageInfo.Comments = StringHandlers.CToString(d88Hdr.name, shiftjis); _imageInfo.XmlMediaType = XmlMediaType.BlockMedia; _imageInfo.SectorSize = (uint)(128 << (int)bps); switch (_imageInfo.MediaType) { case MediaType.NEC_525_SS: _imageInfo.Cylinders = 80; _imageInfo.Heads = 1; _imageInfo.SectorsPerTrack = 16; break; case MediaType.NEC_8_SD: case MediaType.NEC_8_DD: _imageInfo.Cylinders = 77; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 26; break; case MediaType.NEC_525_DS: _imageInfo.Cylinders = 80; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 16; break; case MediaType.NEC_525_HD: _imageInfo.Cylinders = 77; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 8; break; case MediaType.DOS_525_SS_DD_8: _imageInfo.Cylinders = 40; _imageInfo.Heads = 1; _imageInfo.SectorsPerTrack = 8; break; case MediaType.DOS_525_SS_DD_9: _imageInfo.Cylinders = 40; _imageInfo.Heads = 1; _imageInfo.SectorsPerTrack = 9; break; case MediaType.DOS_525_DS_DD_8: _imageInfo.Cylinders = 40; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 8; break; case MediaType.DOS_525_DS_DD_9: _imageInfo.Cylinders = 40; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 9; break; case MediaType.NEC_35_HD_15: _imageInfo.Cylinders = 80; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 15; break; case MediaType.DOS_35_DS_DD_9: _imageInfo.Cylinders = 80; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 9; break; case MediaType.DOS_35_HD: _imageInfo.Cylinders = 80; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 18; break; case MediaType.DOS_35_ED: _imageInfo.Cylinders = 80; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 36; break; case MediaType.NEC_35_TD: _imageInfo.Cylinders = 240; _imageInfo.Heads = 2; _imageInfo.SectorsPerTrack = 38; break; } return(true); }
public bool Open(IFilter imageFilter) { _header = new Header(); byte[] headerBytes = new byte[12]; _inStream = imageFilter.GetDataForkStream(); var stream = new MemoryStream(); _inStream.Seek(0, SeekOrigin.Begin); _inStream.Read(headerBytes, 0, 12); stream.Write(headerBytes, 0, 12); _header.Signature = BitConverter.ToUInt16(headerBytes, 0); if (_header.Signature != TD_MAGIC && _header.Signature != TD_ADV_COMP_MAGIC) { return(false); } _header.Sequence = headerBytes[2]; _header.DiskSet = headerBytes[3]; _header.Version = headerBytes[4]; _header.DataRate = headerBytes[5]; _header.DriveType = headerBytes[6]; _header.Stepping = headerBytes[7]; _header.DosAllocation = headerBytes[8]; _header.Sides = headerBytes[9]; _header.Crc = BitConverter.ToUInt16(headerBytes, 10); _imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); _imageInfo.Version = $"{(_header.Version & 0xF0) >> 4}.{_header.Version & 0x0F}"; _imageInfo.Application = _imageInfo.Version; byte[] headerBytesForCrc = new byte[10]; Array.Copy(headerBytes, headerBytesForCrc, 10); ushort calculatedHeaderCrc = TeleDiskCrc(0x0000, headerBytesForCrc); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.signature = 0x{0:X4}", _header.Signature); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.sequence = 0x{0:X2}", _header.Sequence); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.diskSet = 0x{0:X2}", _header.DiskSet); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.version = 0x{0:X2}", _header.Version); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.dataRate = 0x{0:X2}", _header.DataRate); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.driveType = 0x{0:X2}", _header.DriveType); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.stepping = 0x{0:X2}", _header.Stepping); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.dosAllocation = 0x{0:X2}", _header.DosAllocation); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.sides = 0x{0:X2}", _header.Sides); AaruConsole.DebugWriteLine("TeleDisk plugin", "header.crc = 0x{0:X4}", _header.Crc); AaruConsole.DebugWriteLine("TeleDisk plugin", "calculated header crc = 0x{0:X4}", calculatedHeaderCrc); // We need more checks as the magic is too simply. // This may deny legal images // That would be much of a coincidence if (_header.Crc != calculatedHeaderCrc) { _aDiskCrcHasFailed = true; AaruConsole.DebugWriteLine("TeleDisk plugin", "Calculated CRC does not coincide with stored one."); } if (_header.Sequence != 0x00) { return(false); } if (_header.DataRate != DATA_RATE_250KBPS && _header.DataRate != DATA_RATE_300KBPS && _header.DataRate != DATA_RATE_500KBPS) { return(false); } if (_header.DriveType != DRIVE_TYPE_35_DD && _header.DriveType != DRIVE_TYPE_35_ED && _header.DriveType != DRIVE_TYPE_35_HD && _header.DriveType != DRIVE_TYPE_525_DD && _header.DriveType != DRIVE_TYPE_525_HD && _header.DriveType != DRIVE_TYPE_525_HD_DD_DISK && _header.DriveType != DRIVE_TYPE_8_INCH) { return(false); } if (_header.Signature == TD_ADV_COMP_MAGIC) { int rd; _inStream.Seek(12, SeekOrigin.Begin); stream.Seek(12, SeekOrigin.Begin); var lzh = new TeleDiskLzh(_inStream); do { if ((rd = lzh.Decode(out byte[] obuf, BUFSZ)) > 0) { stream.Write(obuf, 0, rd); } }while(rd == BUFSZ); } else { // Not using Stream.CopyTo() because it's failing with LZIP byte[] copybuf = new byte[_inStream.Length]; _inStream.Seek(0, SeekOrigin.Begin); _inStream.Read(copybuf, 0, copybuf.Length); stream.Seek(0, SeekOrigin.Begin); stream.Write(copybuf, 0, copybuf.Length); } stream.Seek(12, SeekOrigin.Begin); _imageInfo.CreationTime = DateTime.MinValue; if ((_header.Stepping & COMMENT_BLOCK_PRESENT) == COMMENT_BLOCK_PRESENT) { _commentHeader = new CommentBlockHeader(); byte[] commentHeaderBytes = new byte[10]; stream.Read(commentHeaderBytes, 0, 10); _commentHeader.Crc = BitConverter.ToUInt16(commentHeaderBytes, 0); _commentHeader.Length = BitConverter.ToUInt16(commentHeaderBytes, 2); _commentHeader.Year = commentHeaderBytes[4]; _commentHeader.Month = commentHeaderBytes[5]; _commentHeader.Day = commentHeaderBytes[6]; _commentHeader.Hour = commentHeaderBytes[7]; _commentHeader.Minute = commentHeaderBytes[8]; _commentHeader.Second = commentHeaderBytes[9]; _commentBlock = new byte[_commentHeader.Length]; stream.Read(_commentBlock, 0, _commentHeader.Length); byte[] commentBlockForCrc = new byte[_commentHeader.Length + 8]; Array.Copy(commentHeaderBytes, 2, commentBlockForCrc, 0, 8); Array.Copy(_commentBlock, 0, commentBlockForCrc, 8, _commentHeader.Length); ushort cmtcrc = TeleDiskCrc(0, commentBlockForCrc); AaruConsole.DebugWriteLine("TeleDisk plugin", "Comment header"); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.crc = 0x{0:X4}", _commentHeader.Crc); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tCalculated CRC = 0x{0:X4}", cmtcrc); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.length = {0} bytes", _commentHeader.Length); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.year = {0}", _commentHeader.Year); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.month = {0}", _commentHeader.Month); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.day = {0}", _commentHeader.Day); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.hour = {0}", _commentHeader.Hour); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.minute = {0}", _commentHeader.Minute); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.second = {0}", _commentHeader.Second); _aDiskCrcHasFailed |= cmtcrc != _commentHeader.Crc; for (int i = 0; i < _commentBlock.Length; i++) { // Replace NULLs, used by TeleDisk as newline markers, with UNIX newline marker if (_commentBlock[i] == 0x00) { _commentBlock[i] = 0x0A; } } _imageInfo.Comments = Encoding.ASCII.GetString(_commentBlock); AaruConsole.DebugWriteLine("TeleDisk plugin", "Comment"); AaruConsole.DebugWriteLine("TeleDisk plugin", "{0}", _imageInfo.Comments); _imageInfo.CreationTime = new DateTime(_commentHeader.Year + 1900, _commentHeader.Month + 1, _commentHeader.Day, _commentHeader.Hour, _commentHeader.Minute, _commentHeader.Second, DateTimeKind.Unspecified); } if (_imageInfo.CreationTime == DateTime.MinValue) { _imageInfo.CreationTime = imageFilter.GetCreationTime(); } _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); AaruConsole.DebugWriteLine("TeleDisk plugin", "Image created on {0}", _imageInfo.CreationTime); AaruConsole.DebugWriteLine("TeleDisk plugin", "Image modified on {0}", _imageInfo.LastModificationTime); AaruConsole.DebugWriteLine("TeleDisk plugin", "Parsing image"); _totalDiskSize = 0; _imageInfo.ImageSize = 0; int totalCylinders = -1; 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) { var teleDiskTrack = new TrackHeader { Sectors = (byte)stream.ReadByte(), Cylinder = (byte)stream.ReadByte(), Head = (byte)stream.ReadByte(), Crc = (byte)stream.ReadByte() }; if (teleDiskTrack.Cylinder > totalCylinders) { totalCylinders = teleDiskTrack.Cylinder; } if (teleDiskTrack.Head > totalHeads) { totalHeads = teleDiskTrack.Head; } if (teleDiskTrack.Sectors == 0xFF) // End of disk image { break; } for (byte processedSectors = 0; processedSectors < teleDiskTrack.Sectors; processedSectors++) { var teleDiskSector = new SectorHeader(); var teleDiskData = new DataHeader(); byte[] dataSizeBytes = new byte[2]; teleDiskSector.Cylinder = (byte)stream.ReadByte(); teleDiskSector.Head = (byte)stream.ReadByte(); teleDiskSector.SectorNumber = (byte)stream.ReadByte(); teleDiskSector.SectorSize = (byte)stream.ReadByte(); teleDiskSector.Flags = (byte)stream.ReadByte(); teleDiskSector.Crc = (byte)stream.ReadByte(); if (teleDiskSector.SectorNumber > maxSector) { maxSector = teleDiskSector.SectorNumber; } if ((teleDiskSector.Flags & FLAGS_SECTOR_DATALESS) != FLAGS_SECTOR_DATALESS && (teleDiskSector.Flags & FLAGS_SECTOR_SKIPPED) != FLAGS_SECTOR_SKIPPED) { stream.Read(dataSizeBytes, 0, 2); teleDiskData.DataSize = BitConverter.ToUInt16(dataSizeBytes, 0); teleDiskData.DataSize--; // Sydex decided to including dataEncoding byte as part of it teleDiskData.DataEncoding = (byte)stream.ReadByte(); byte[] data = new byte[teleDiskData.DataSize]; stream.Read(data, 0, teleDiskData.DataSize); } if (128 << teleDiskSector.SectorSize < _imageInfo.SectorSize) { _imageInfo.SectorSize = (uint)(128 << teleDiskSector.SectorSize); } totalSectors++; } } totalCylinders++; totalHeads++; if (totalCylinders <= 0 || totalHeads <= 0) { throw new ImageNotSupportedException("No cylinders or heads found"); } bool hasLeadOutOnHead0 = false; bool hasLeadOutOnHead1 = false; _imageInfo.Cylinders = (ushort)totalCylinders; _imageInfo.Heads = (byte)totalHeads; // Count sectors per track stream.Seek(currentPos, SeekOrigin.Begin); while (true) { var teleDiskTrack = new TrackHeader { Sectors = (byte)stream.ReadByte(), Cylinder = (byte)stream.ReadByte(), Head = (byte)stream.ReadByte(), Crc = (byte)stream.ReadByte() }; if (teleDiskTrack.Sectors == 0xFF) // End of disk image { break; } if (teleDiskTrack.Sectors < _imageInfo.SectorsPerTrack) { if (teleDiskTrack.Cylinder + 1 == totalCylinders) { hasLeadOutOnHead0 |= teleDiskTrack.Head == 0; hasLeadOutOnHead1 |= teleDiskTrack.Head == 1; if (_imageInfo.Cylinders == totalCylinders) { _imageInfo.Cylinders--; } } else { _imageInfo.SectorsPerTrack = teleDiskTrack.Sectors; } } for (byte processedSectors = 0; processedSectors < teleDiskTrack.Sectors; processedSectors++) { var teleDiskSector = new SectorHeader(); var teleDiskData = new DataHeader(); byte[] dataSizeBytes = new byte[2]; teleDiskSector.Cylinder = (byte)stream.ReadByte(); teleDiskSector.Head = (byte)stream.ReadByte(); teleDiskSector.SectorNumber = (byte)stream.ReadByte(); teleDiskSector.SectorSize = (byte)stream.ReadByte(); teleDiskSector.Flags = (byte)stream.ReadByte(); teleDiskSector.Crc = (byte)stream.ReadByte(); if ((teleDiskSector.Flags & FLAGS_SECTOR_DATALESS) == FLAGS_SECTOR_DATALESS || (teleDiskSector.Flags & FLAGS_SECTOR_SKIPPED) == FLAGS_SECTOR_SKIPPED) { continue; } stream.Read(dataSizeBytes, 0, 2); teleDiskData.DataSize = BitConverter.ToUInt16(dataSizeBytes, 0); teleDiskData.DataSize--; // Sydex decided to including dataEncoding byte as part of it teleDiskData.DataEncoding = (byte)stream.ReadByte(); byte[] data = new byte[teleDiskData.DataSize]; stream.Read(data, 0, teleDiskData.DataSize); } } _sectorsData = new byte[totalCylinders][][][]; // Total sectors per track uint[][] spts = new uint[totalCylinders][]; AaruConsole.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) { var teleDiskTrack = new TrackHeader(); byte[] tdTrackForCrc = new byte[3]; teleDiskTrack.Sectors = (byte)stream.ReadByte(); teleDiskTrack.Cylinder = (byte)stream.ReadByte(); teleDiskTrack.Head = (byte)stream.ReadByte(); teleDiskTrack.Crc = (byte)stream.ReadByte(); tdTrackForCrc[0] = teleDiskTrack.Sectors; tdTrackForCrc[1] = teleDiskTrack.Cylinder; tdTrackForCrc[2] = teleDiskTrack.Head; byte tdTrackCalculatedCrc = (byte)(TeleDiskCrc(0, tdTrackForCrc) & 0xFF); AaruConsole.DebugWriteLine("TeleDisk plugin", "Track follows"); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tTrack cylinder: {0}\t", teleDiskTrack.Cylinder); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tTrack head: {0}\t", teleDiskTrack.Head); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tSectors in track: {0}\t", teleDiskTrack.Sectors); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tTrack header CRC: 0x{0:X2} (calculated 0x{1:X2})\t", teleDiskTrack.Crc, tdTrackCalculatedCrc); _aDiskCrcHasFailed |= tdTrackCalculatedCrc != teleDiskTrack.Crc; if (teleDiskTrack.Sectors == 0xFF) // End of disk image { AaruConsole.DebugWriteLine("TeleDisk plugin", "End of disk image arrived"); AaruConsole.DebugWriteLine("TeleDisk plugin", "Total of {0} data sectors, for {1} bytes", totalSectors, _totalDiskSize); break; } for (byte processedSectors = 0; processedSectors < teleDiskTrack.Sectors; processedSectors++) { var teleDiskSector = new SectorHeader(); var teleDiskData = new DataHeader(); byte[] dataSizeBytes = new byte[2]; byte[] decodedData; teleDiskSector.Cylinder = (byte)stream.ReadByte(); teleDiskSector.Head = (byte)stream.ReadByte(); teleDiskSector.SectorNumber = (byte)stream.ReadByte(); teleDiskSector.SectorSize = (byte)stream.ReadByte(); teleDiskSector.Flags = (byte)stream.ReadByte(); teleDiskSector.Crc = (byte)stream.ReadByte(); AaruConsole.DebugWriteLine("TeleDisk plugin", "\tSector follows"); AaruConsole.DebugWriteLine("TeleDisk plugin", "\t\tAddressMark cylinder: {0}", teleDiskSector.Cylinder); AaruConsole.DebugWriteLine("TeleDisk plugin", "\t\tAddressMark head: {0}", teleDiskSector.Head); AaruConsole.DebugWriteLine("TeleDisk plugin", "\t\tAddressMark sector number: {0}", teleDiskSector.SectorNumber); AaruConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector size: {0}", teleDiskSector.SectorSize); AaruConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector flags: 0x{0:X2}", teleDiskSector.Flags); AaruConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector CRC (plus headers): 0x{0:X2}", teleDiskSector.Crc); uint lba = (uint)((teleDiskSector.Cylinder * _header.Sides * _imageInfo.SectorsPerTrack) + (teleDiskSector.Head * _imageInfo.SectorsPerTrack) + (teleDiskSector.SectorNumber - 1)); if ((teleDiskSector.Flags & FLAGS_SECTOR_DATALESS) != FLAGS_SECTOR_DATALESS && (teleDiskSector.Flags & FLAGS_SECTOR_SKIPPED) != FLAGS_SECTOR_SKIPPED) { stream.Read(dataSizeBytes, 0, 2); teleDiskData.DataSize = BitConverter.ToUInt16(dataSizeBytes, 0); teleDiskData.DataSize--; // Sydex decided to including dataEncoding byte as part of it _imageInfo.ImageSize += teleDiskData.DataSize; teleDiskData.DataEncoding = (byte)stream.ReadByte(); byte[] data = new byte[teleDiskData.DataSize]; stream.Read(data, 0, teleDiskData.DataSize); AaruConsole.DebugWriteLine("TeleDisk plugin", "\t\tData size (in-image): {0}", teleDiskData.DataSize); AaruConsole.DebugWriteLine("TeleDisk plugin", "\t\tData encoding: 0x{0:X2}", teleDiskData.DataEncoding); decodedData = DecodeTeleDiskData(teleDiskSector.SectorSize, teleDiskData.DataEncoding, data); byte tdSectorCalculatedCrc = (byte)(TeleDiskCrc(0, decodedData) & 0xFF); if (tdSectorCalculatedCrc != teleDiskSector.Crc) { AaruConsole.DebugWriteLine("TeleDisk plugin", "Sector {0}:{3}:{4} calculated CRC 0x{1:X2} differs from stored CRC 0x{2:X2}", teleDiskTrack.Cylinder, tdSectorCalculatedCrc, teleDiskSector.Crc, teleDiskTrack.Cylinder, teleDiskSector.SectorNumber); if ((teleDiskSector.Flags & FLAGS_SECTOR_NO_ID) != FLAGS_SECTOR_NO_ID) { _sectorsWhereCrcHasFailed.Add(lba); } } } else { decodedData = new byte[128 << teleDiskSector.SectorSize]; } AaruConsole.DebugWriteLine("TeleDisk plugin", "\t\tLBA: {0}", lba); if ((teleDiskSector.Flags & FLAGS_SECTOR_NO_ID) == FLAGS_SECTOR_NO_ID) { continue; } if (_sectorsData[teleDiskTrack.Cylinder][teleDiskTrack.Head][teleDiskSector.SectorNumber] != null) { AaruConsole.DebugWriteLine("TeleDisk plugin", (teleDiskSector.Flags & FLAGS_SECTOR_DUPLICATE) == FLAGS_SECTOR_DUPLICATE ? "\t\tSector {0} on cylinder {1} head {2} is duplicate, and marked so" : "\t\tSector {0} on cylinder {1} head {2} is duplicate, but is not marked so", teleDiskSector.SectorNumber, teleDiskSector.Cylinder, teleDiskSector.Head); } else { _sectorsData[teleDiskTrack.Cylinder][teleDiskTrack.Head][teleDiskSector.SectorNumber] = decodedData; _totalDiskSize += (uint)decodedData.Length; } } } var leadOutMs = new MemoryStream(); if (hasLeadOutOnHead0) { for (int i = 0; i < _sectorsData[totalCylinders - 1][0].Length; i++) { if (_sectorsData[totalCylinders - 1][0][i] != null) { leadOutMs.Write(_sectorsData[totalCylinders - 1][0][i], 0, _sectorsData[totalCylinders - 1][0][i].Length); } } } if (hasLeadOutOnHead1) { for (int i = 0; i < _sectorsData[totalCylinders - 1][1].Length; i++) { if (_sectorsData[totalCylinders - 1][1][i] != null) { leadOutMs.Write(_sectorsData[totalCylinders - 1][1][i], 0, _sectorsData[totalCylinders - 1][1][i].Length); } } } if (leadOutMs.Length != 0) { _leadOut = leadOutMs.ToArray(); _imageInfo.ReadableMediaTags.Add(MediaTagType.Floppy_LeadOut); } _imageInfo.Sectors = _imageInfo.Cylinders * _imageInfo.Heads * _imageInfo.SectorsPerTrack; _imageInfo.MediaType = DecodeTeleDiskDiskType(); _imageInfo.XmlMediaType = XmlMediaType.BlockMedia; AaruConsole.VerboseWriteLine("TeleDisk image contains a disk of type {0}", _imageInfo.MediaType); if (!string.IsNullOrEmpty(_imageInfo.Comments)) { AaruConsole.VerboseWriteLine("TeleDisk comments: {0}", _imageInfo.Comments); } _inStream.Dispose(); stream.Dispose(); return(true); }
private ArrayList readTrackHeader(Stream f) { byte[] buf = new byte[7]; ArrayList sectorHeaderList = new ArrayList(); f.Read(buf, 0, 4); // data offset in data block int dataOffset = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); f.Read(buf, 0, 2); // reserved f.Read(buf, 0, 1); // sector count int sectorCount = buf[0]; for (int i = 0; i < sectorCount; i++) { f.Read(buf, 0, 7); SectorHeader sh = new SectorHeader(); sh.C = buf[0]; sh.H = buf[1]; sh.R = buf[2]; sh.N = buf[3]; sh.Flags = buf[4]; sh.DataOffset = dataOffset + (buf[5] | (buf[6] << 8)); sectorHeaderList.Add(sh); } return sectorHeaderList; }
public bool Open(IFilter imageFilter) { string comments = string.Empty; Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); byte[] header = new byte[32]; stream.Read(header, 0, 32); FileHeader fheader = Marshal.ByteArrayToStructureLittleEndian <FileHeader>(header); AaruConsole.DebugWriteLine("d2f plugin", "Detected WC DISK IMAGE with {0} heads, {1} tracks and {2} sectors per track.", fheader.heads, fheader.cylinders, fheader.sectorsPerTrack); _imageInfo.Cylinders = fheader.cylinders; _imageInfo.SectorsPerTrack = fheader.sectorsPerTrack; _imageInfo.SectorSize = 512; // only 512 bytes per sector supported _imageInfo.Heads = fheader.heads; _imageInfo.Sectors = _imageInfo.Heads * _imageInfo.Cylinders * _imageInfo.SectorsPerTrack; _imageInfo.ImageSize = _imageInfo.Sectors * _imageInfo.SectorSize; _imageInfo.XmlMediaType = XmlMediaType.BlockMedia; _imageInfo.CreationTime = imageFilter.GetCreationTime(); _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); _imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); _imageInfo.MediaType = Geometry.GetMediaType(((ushort)_imageInfo.Cylinders, (byte)_imageInfo.Heads, (ushort)_imageInfo.SectorsPerTrack, 512, MediaEncoding.MFM, false)); /* buffer the entire disk in memory */ for (int cyl = 0; cyl < _imageInfo.Cylinders; cyl++) { for (int head = 0; head < _imageInfo.Heads; head++) { ReadTrack(stream, cyl, head); } } /* if there are extra tracks, read them as well */ if (fheader.extraTracks[0] == 1) { AaruConsole.DebugWriteLine("d2f plugin", "Extra track 1 (head 0) present, reading"); ReadTrack(stream, (int)_imageInfo.Cylinders, 0); } if (fheader.extraTracks[1] == 1) { AaruConsole.DebugWriteLine("d2f plugin", "Extra track 1 (head 1) present, reading"); ReadTrack(stream, (int)_imageInfo.Cylinders, 1); } if (fheader.extraTracks[2] == 1) { AaruConsole.DebugWriteLine("d2f plugin", "Extra track 2 (head 0) present, reading"); ReadTrack(stream, (int)_imageInfo.Cylinders + 1, 0); } if (fheader.extraTracks[3] == 1) { AaruConsole.DebugWriteLine("d2f plugin", "Extra track 2 (head 1) present, reading"); ReadTrack(stream, (int)_imageInfo.Cylinders + 1, 1); } /* adjust number of cylinders */ if (fheader.extraTracks[0] == 1 || fheader.extraTracks[1] == 1) { _imageInfo.Cylinders++; } if (fheader.extraTracks[2] == 1 || fheader.extraTracks[3] == 1) { _imageInfo.Cylinders++; } /* read the comment and directory data if present */ if (fheader.extraFlags.HasFlag(ExtraFlag.Comment)) { AaruConsole.DebugWriteLine("d2f plugin", "Comment present, reading"); byte[] sheaderBuffer = new byte[6]; stream.Read(sheaderBuffer, 0, 6); SectorHeader sheader = Marshal.ByteArrayToStructureLittleEndian <SectorHeader>(sheaderBuffer); if (sheader.flag != SectorFlag.Comment) { throw new InvalidDataException($"Invalid sector type '{sheader.flag.ToString()}' encountered"); } byte[] comm = new byte[sheader.crc]; stream.Read(comm, 0, sheader.crc); comments += Encoding.ASCII.GetString(comm) + Environment.NewLine; } if (fheader.extraFlags.HasFlag(ExtraFlag.Directory)) { AaruConsole.DebugWriteLine("d2f plugin", "Directory listing present, reading"); byte[] sheaderBuffer = new byte[6]; stream.Read(sheaderBuffer, 0, 6); SectorHeader sheader = Marshal.ByteArrayToStructureLittleEndian <SectorHeader>(sheaderBuffer); if (sheader.flag != SectorFlag.Directory) { throw new InvalidDataException($"Invalid sector type '{sheader.flag.ToString()}' encountered"); } byte[] dir = new byte[sheader.crc]; stream.Read(dir, 0, sheader.crc); comments += Encoding.ASCII.GetString(dir); } if (comments.Length > 0) { _imageInfo.Comments = comments; } // save some variables for later use _fileHeader = fheader; _wcImageFilter = imageFilter; return(true); }