2016-07-28 18:13:49 +01:00
|
|
|
// /***************************************************************************
|
|
|
|
|
// The Disc Image Chef
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Filename : TeleDisk.cs
|
|
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
|
|
|
//
|
2017-12-19 03:50:57 +00:00
|
|
|
// Component : Disk image plugins.
|
2016-07-28 18:13:49 +01:00
|
|
|
//
|
|
|
|
|
// --[ Description ] ----------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Manages Sydex TeleDisk disk images.
|
|
|
|
|
//
|
|
|
|
|
// --[ License ] --------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// This library is free software; you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU Lesser General Public License as
|
|
|
|
|
// published by the Free Software Foundation; either version 2.1 of the
|
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This library is distributed in the hope that it will be useful, but
|
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2017-12-19 03:50:57 +00:00
|
|
|
// Copyright © 2011-2018 Natalia Portillo
|
2016-07-28 18:13:49 +01:00
|
|
|
// ****************************************************************************/
|
2014-04-19 21:21:08 +01:00
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2017-12-19 19:33:46 +00:00
|
|
|
using System.IO;
|
2017-12-21 14:30:38 +00:00
|
|
|
using System.Text;
|
2015-11-23 21:44:58 +00:00
|
|
|
using DiscImageChef.CommonTypes;
|
2018-06-25 19:08:16 +01:00
|
|
|
using DiscImageChef.CommonTypes.Enums;
|
|
|
|
|
using DiscImageChef.CommonTypes.Exceptions;
|
|
|
|
|
using DiscImageChef.CommonTypes.Interfaces;
|
|
|
|
|
using DiscImageChef.CommonTypes.Structs;
|
2018-01-21 12:37:32 +00:00
|
|
|
using DiscImageChef.Compression;
|
2017-12-19 19:33:46 +00:00
|
|
|
using DiscImageChef.Console;
|
2018-01-28 20:29:46 +00:00
|
|
|
using Schemas;
|
2015-10-18 22:04:03 +01:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
namespace DiscImageChef.DiscImages
|
2014-04-19 21:21:08 +01:00
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
// Created following notes from Dave Dunfield
|
|
|
|
|
// http://www.classiccmp.org/dunfield/img54306/td0notes.txt
|
2017-12-26 06:05:12 +00:00
|
|
|
public class TeleDisk : IMediaImage
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
// "TD" as little endian uint.
|
2017-12-20 17:15:26 +00:00
|
|
|
const ushort TD_MAGIC = 0x4454;
|
2017-12-19 20:33:03 +00:00
|
|
|
// "td" as little endian uint. Means whole file is compressed (aka Advanced Compression)
|
2017-12-20 17:15:26 +00:00
|
|
|
const ushort TD_ADV_COMP_MAGIC = 0x6474;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// DataRates
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte DATA_RATE_250KBPS = 0x00;
|
|
|
|
|
const byte DATA_RATE_300KBPS = 0x01;
|
|
|
|
|
const byte DATA_RATE_500KBPS = 0x02;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// TeleDisk drive types
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte DRIVE_TYPE_525_HD_DD_DISK = 0x00;
|
2018-01-28 20:29:46 +00:00
|
|
|
const byte DRIVE_TYPE_525_HD = 0x01;
|
|
|
|
|
const byte DRIVE_TYPE_525_DD = 0x02;
|
|
|
|
|
const byte DRIVE_TYPE_35_DD = 0x03;
|
|
|
|
|
const byte DRIVE_TYPE_35_HD = 0x04;
|
|
|
|
|
const byte DRIVE_TYPE_8_INCH = 0x05;
|
|
|
|
|
const byte DRIVE_TYPE_35_ED = 0x06;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// Stepping
|
2018-01-28 20:29:46 +00:00
|
|
|
const byte STEPPING_SINGLE = 0x00;
|
|
|
|
|
const byte STEPPING_DOUBLE = 0x01;
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte STEPPING_EVEN_ONLY = 0x02;
|
2017-12-19 20:33:03 +00:00
|
|
|
// If this bit is set, there is a comment block
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte COMMENT_BLOCK_PRESENT = 0x80;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// CRC polynomial
|
2017-12-20 17:15:26 +00:00
|
|
|
const ushort TELE_DISK_CRC_POLY = 0xA097;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// Sector sizes table
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte SECTOR_SIZE_128 = 0x00;
|
|
|
|
|
const byte SECTOR_SIZE_256 = 0x01;
|
|
|
|
|
const byte SECTOR_SIZE_512 = 0x02;
|
2018-01-28 20:29:46 +00:00
|
|
|
const byte SECTOR_SIZE_1K = 0x03;
|
|
|
|
|
const byte SECTOR_SIZE_2K = 0x04;
|
|
|
|
|
const byte SECTOR_SIZE_4K = 0x05;
|
|
|
|
|
const byte SECTOR_SIZE_8K = 0x06;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// Flags
|
|
|
|
|
// Address mark repeats inside same track
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte FLAGS_SECTOR_DUPLICATE = 0x01;
|
2017-12-19 20:33:03 +00:00
|
|
|
// Sector gave CRC error on reading
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte FLAGS_SECTOR_CRC_ERROR = 0x02;
|
2017-12-19 20:33:03 +00:00
|
|
|
// Address mark indicates deleted sector
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte FLAGS_SECTOR_DELETED = 0x04;
|
2017-12-19 20:33:03 +00:00
|
|
|
// Sector skipped as FAT said it's unused
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte FLAGS_SECTOR_SKIPPED = 0x10;
|
2017-12-19 20:33:03 +00:00
|
|
|
// There was an address mark, but no data following
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte FLAGS_SECTOR_DATALESS = 0x20;
|
2017-12-19 20:33:03 +00:00
|
|
|
// There was data without address mark
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte FLAGS_SECTOR_NO_ID = 0x40;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// Data block encodings
|
|
|
|
|
// Data is copied as is
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte DATA_BLOCK_COPY = 0x00;
|
2017-12-19 20:33:03 +00:00
|
|
|
// Data is encoded as a pair of len.value uint16s
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte DATA_BLOCK_PATTERN = 0x01;
|
2017-12-19 20:33:03 +00:00
|
|
|
// Data is encoded as RLE
|
2017-12-20 17:15:26 +00:00
|
|
|
const byte DATA_BLOCK_RLE = 0x02;
|
2018-01-28 20:29:46 +00:00
|
|
|
|
|
|
|
|
const int BUFSZ = 512;
|
|
|
|
|
bool aDiskCrcHasFailed;
|
|
|
|
|
byte[] commentBlock;
|
2017-12-24 00:12:31 +00:00
|
|
|
TeleDiskCommentBlockHeader commentHeader;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
TeleDiskHeader header;
|
2018-01-28 20:29:46 +00:00
|
|
|
ImageInfo imageInfo;
|
|
|
|
|
Stream inStream;
|
|
|
|
|
byte[] leadOut;
|
2017-12-24 00:12:31 +00:00
|
|
|
// Cylinder by head, sector data matrix
|
|
|
|
|
byte[][][][] sectorsData;
|
2018-01-28 20:29:46 +00:00
|
|
|
List<ulong> sectorsWhereCrcHasFailed;
|
2017-12-24 00:12:31 +00:00
|
|
|
// LBA, data
|
|
|
|
|
uint totalDiskSize;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
public TeleDisk()
|
|
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo = new ImageInfo
|
2017-12-22 06:55:04 +00:00
|
|
|
{
|
2018-01-28 20:29:46 +00:00
|
|
|
ReadableSectorTags = new List<SectorTagType>(),
|
|
|
|
|
ReadableMediaTags = new List<MediaTagType>(),
|
|
|
|
|
HasPartitions = false,
|
|
|
|
|
HasSessions = false,
|
|
|
|
|
Application = "Sydex TeleDisk",
|
|
|
|
|
Comments = null,
|
|
|
|
|
Creator = null,
|
|
|
|
|
MediaManufacturer = null,
|
|
|
|
|
MediaModel = null,
|
|
|
|
|
MediaSerialNumber = null,
|
|
|
|
|
MediaBarcode = null,
|
|
|
|
|
MediaPartNumber = null,
|
|
|
|
|
MediaSequence = 0,
|
|
|
|
|
LastMediaSequence = 0,
|
|
|
|
|
DriveManufacturer = null,
|
|
|
|
|
DriveModel = null,
|
|
|
|
|
DriveSerialNumber = null,
|
2017-12-22 06:55:04 +00:00
|
|
|
DriveFirmwareRevision = null
|
|
|
|
|
};
|
2018-01-28 20:29:46 +00:00
|
|
|
aDiskCrcHasFailed = false;
|
2017-12-20 17:15:26 +00:00
|
|
|
sectorsWhereCrcHasFailed = new List<ulong>();
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public ImageInfo Info => imageInfo;
|
2017-12-26 02:51:10 +00:00
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public string Name => "Sydex TeleDisk";
|
2018-01-28 20:29:46 +00:00
|
|
|
public Guid Id => new Guid("0240B7B1-E959-4CDC-B0BD-386D6E467B88");
|
2017-12-26 06:05:12 +00:00
|
|
|
|
2017-12-28 19:56:36 +00:00
|
|
|
public string Format => "Sydex TeleDisk";
|
2017-12-26 06:05:12 +00:00
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public List<Partition> Partitions =>
|
2017-12-26 02:51:10 +00:00
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public List<Track> Tracks =>
|
2017-12-26 02:51:10 +00:00
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public List<Session> Sessions =>
|
2017-12-26 02:51:10 +00:00
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
|
2017-12-28 19:56:36 +00:00
|
|
|
public bool Identify(IFilter imageFilter)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2018-06-22 08:08:38 +01:00
|
|
|
header = new TeleDiskHeader();
|
2017-12-19 20:33:03 +00:00
|
|
|
byte[] headerBytes = new byte[12];
|
2018-01-28 20:29:46 +00:00
|
|
|
Stream stream = imageFilter.GetDataForkStream();
|
2017-12-19 20:33:03 +00:00
|
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
|
|
|
|
|
stream.Read(headerBytes, 0, 12);
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
header.Signature = BitConverter.ToUInt16(headerBytes, 0);
|
|
|
|
|
|
|
|
|
|
if(header.Signature != TD_MAGIC && header.Signature != TD_ADV_COMP_MAGIC) return false;
|
|
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
header.Sequence = headerBytes[2];
|
|
|
|
|
header.DiskSet = headerBytes[3];
|
|
|
|
|
header.Version = headerBytes[4];
|
|
|
|
|
header.DataRate = headerBytes[5];
|
|
|
|
|
header.DriveType = headerBytes[6];
|
|
|
|
|
header.Stepping = headerBytes[7];
|
2017-12-20 17:15:26 +00:00
|
|
|
header.DosAllocation = headerBytes[8];
|
2018-01-28 20:29:46 +00:00
|
|
|
header.Sides = headerBytes[9];
|
|
|
|
|
header.Crc = BitConverter.ToUInt16(headerBytes, 10);
|
2017-12-20 17:15:26 +00:00
|
|
|
|
|
|
|
|
byte[] headerBytesForCrc = new byte[10];
|
|
|
|
|
Array.Copy(headerBytes, headerBytesForCrc, 10);
|
|
|
|
|
ushort calculatedHeaderCrc = TeleDiskCrc(0x0000, headerBytesForCrc);
|
|
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.signature = 0x{0:X4}", header.Signature);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.sequence = 0x{0:X2}", header.Sequence);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.diskSet = 0x{0:X2}", header.DiskSet);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.version = 0x{0:X2}", header.Version);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.dataRate = 0x{0:X2}", header.DataRate);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.driveType = 0x{0:X2}", header.DriveType);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.stepping = 0x{0:X2}", header.Stepping);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.dosAllocation = 0x{0:X2}", header.DosAllocation);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.sides = 0x{0:X2}", header.Sides);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.crc = 0x{0:X4}", header.Crc);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "calculated header crc = 0x{0:X4}", calculatedHeaderCrc);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// We need more checks as the magic is too simply.
|
|
|
|
|
// This may deny legal images
|
|
|
|
|
|
|
|
|
|
// That would be much of a coincidence
|
2017-12-20 17:15:26 +00:00
|
|
|
if(header.Crc == calculatedHeaderCrc) return true;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(header.Sequence != 0x00) return false;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(header.DataRate != DATA_RATE_250KBPS && header.DataRate != DATA_RATE_300KBPS &&
|
|
|
|
|
header.DataRate != DATA_RATE_500KBPS) return false;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
return header.DriveType == DRIVE_TYPE_35_DD || header.DriveType == DRIVE_TYPE_35_ED ||
|
|
|
|
|
header.DriveType == DRIVE_TYPE_35_HD || header.DriveType == DRIVE_TYPE_525_DD ||
|
2017-12-24 00:12:31 +00:00
|
|
|
header.DriveType == DRIVE_TYPE_525_HD || header.DriveType == DRIVE_TYPE_525_HD_DD_DISK ||
|
|
|
|
|
header.DriveType == DRIVE_TYPE_8_INCH;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-28 19:56:36 +00:00
|
|
|
public bool Open(IFilter imageFilter)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2018-06-22 08:08:38 +01:00
|
|
|
header = new TeleDiskHeader();
|
|
|
|
|
byte[] headerBytes = new byte[12];
|
|
|
|
|
inStream = imageFilter.GetDataForkStream();
|
2017-12-19 20:33:03 +00:00
|
|
|
MemoryStream stream = new MemoryStream();
|
|
|
|
|
inStream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
|
|
|
|
|
inStream.Read(headerBytes, 0, 12);
|
|
|
|
|
stream.Write(headerBytes, 0, 12);
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
header.Signature = BitConverter.ToUInt16(headerBytes, 0);
|
|
|
|
|
|
|
|
|
|
if(header.Signature != TD_MAGIC && header.Signature != TD_ADV_COMP_MAGIC) return false;
|
|
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
header.Sequence = headerBytes[2];
|
|
|
|
|
header.DiskSet = headerBytes[3];
|
|
|
|
|
header.Version = headerBytes[4];
|
|
|
|
|
header.DataRate = headerBytes[5];
|
|
|
|
|
header.DriveType = headerBytes[6];
|
|
|
|
|
header.Stepping = headerBytes[7];
|
2017-12-20 17:15:26 +00:00
|
|
|
header.DosAllocation = headerBytes[8];
|
2018-01-28 20:29:46 +00:00
|
|
|
header.Sides = headerBytes[9];
|
|
|
|
|
header.Crc = BitConverter.ToUInt16(headerBytes, 10);
|
2017-12-20 17:15:26 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
|
|
|
|
|
imageInfo.Version = $"{(header.Version & 0xF0) >> 4}.{header.Version & 0x0F}";
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.Application = imageInfo.Version;
|
2017-12-20 17:15:26 +00:00
|
|
|
|
|
|
|
|
byte[] headerBytesForCrc = new byte[10];
|
|
|
|
|
Array.Copy(headerBytes, headerBytesForCrc, 10);
|
|
|
|
|
ushort calculatedHeaderCrc = TeleDiskCrc(0x0000, headerBytesForCrc);
|
|
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.signature = 0x{0:X4}", header.Signature);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.sequence = 0x{0:X2}", header.Sequence);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.diskSet = 0x{0:X2}", header.DiskSet);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.version = 0x{0:X2}", header.Version);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.dataRate = 0x{0:X2}", header.DataRate);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.driveType = 0x{0:X2}", header.DriveType);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.stepping = 0x{0:X2}", header.Stepping);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.dosAllocation = 0x{0:X2}", header.DosAllocation);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.sides = 0x{0:X2}", header.Sides);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "header.crc = 0x{0:X4}", header.Crc);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "calculated header crc = 0x{0:X4}", calculatedHeaderCrc);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// We need more checks as the magic is too simply.
|
|
|
|
|
// This may deny legal images
|
|
|
|
|
|
|
|
|
|
// That would be much of a coincidence
|
2017-12-20 17:15:26 +00:00
|
|
|
if(header.Crc != calculatedHeaderCrc)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
aDiskCrcHasFailed = true;
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Calculated CRC does not coincide with stored one.");
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(header.Sequence != 0x00) return false;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(header.DataRate != DATA_RATE_250KBPS && header.DataRate != DATA_RATE_300KBPS &&
|
|
|
|
|
header.DataRate != DATA_RATE_500KBPS) return false;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
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 &&
|
2017-12-20 17:15:26 +00:00
|
|
|
header.DriveType != DRIVE_TYPE_525_HD && header.DriveType != DRIVE_TYPE_525_HD_DD_DISK &&
|
|
|
|
|
header.DriveType != DRIVE_TYPE_8_INCH) return false;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(header.Signature == TD_ADV_COMP_MAGIC)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
int rd;
|
|
|
|
|
inStream.Seek(12, SeekOrigin.Begin);
|
|
|
|
|
stream.Seek(12, SeekOrigin.Begin);
|
2018-01-21 12:37:32 +00:00
|
|
|
TeleDiskLzh lzh = new TeleDiskLzh(inStream);
|
2018-01-28 20:29:46 +00:00
|
|
|
do
|
|
|
|
|
if((rd = lzh.Decode(out byte[] obuf, BUFSZ)) > 0)
|
|
|
|
|
stream.Write(obuf, 0, rd);
|
2017-12-19 20:33:03 +00:00
|
|
|
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);
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.CreationTime = DateTime.MinValue;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if((header.Stepping & COMMENT_BLOCK_PRESENT) == COMMENT_BLOCK_PRESENT)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
commentHeader = new TeleDiskCommentBlockHeader();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
byte[] commentHeaderBytes = new byte[10];
|
|
|
|
|
|
|
|
|
|
stream.Read(commentHeaderBytes, 0, 10);
|
2018-01-28 20:29:46 +00:00
|
|
|
commentHeader.Crc = BitConverter.ToUInt16(commentHeaderBytes, 0);
|
2017-12-20 17:15:26 +00:00
|
|
|
commentHeader.Length = BitConverter.ToUInt16(commentHeaderBytes, 2);
|
2018-01-28 20:29:46 +00:00
|
|
|
commentHeader.Year = commentHeaderBytes[4];
|
|
|
|
|
commentHeader.Month = commentHeaderBytes[5];
|
|
|
|
|
commentHeader.Day = commentHeaderBytes[6];
|
|
|
|
|
commentHeader.Hour = commentHeaderBytes[7];
|
2017-12-20 17:15:26 +00:00
|
|
|
commentHeader.Minute = commentHeaderBytes[8];
|
|
|
|
|
commentHeader.Second = commentHeaderBytes[9];
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
commentBlock = new byte[commentHeader.Length];
|
|
|
|
|
stream.Read(commentBlock, 0, commentHeader.Length);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
byte[] commentBlockForCrc = new byte[commentHeader.Length + 8];
|
2017-12-20 17:15:26 +00:00
|
|
|
Array.Copy(commentHeaderBytes, 2, commentBlockForCrc, 0, 8);
|
2018-01-28 20:29:46 +00:00
|
|
|
Array.Copy(commentBlock, 0, commentBlockForCrc, 8, commentHeader.Length);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
ushort cmtcrc = TeleDiskCrc(0, commentBlockForCrc);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Comment header");
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.crc = 0x{0:X4}", commentHeader.Crc);
|
2018-01-28 20:29:46 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tCalculated CRC = 0x{0:X4}", cmtcrc);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.length = {0} bytes",
|
2017-12-20 17:15:26 +00:00
|
|
|
commentHeader.Length);
|
2018-01-28 20:29:46 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.year = {0}", commentHeader.Year);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.month = {0}", commentHeader.Month);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.day = {0}", commentHeader.Day);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.hour = {0}", commentHeader.Hour);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.minute = {0}", commentHeader.Minute);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.second = {0}", commentHeader.Second);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
aDiskCrcHasFailed |= cmtcrc != commentHeader.Crc;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
for(int i = 0; i < commentBlock.Length; i++)
|
2017-12-24 00:12:31 +00:00
|
|
|
// Replace NULLs, used by TeleDisk as newline markers, with UNIX newline marker
|
2018-01-28 20:29:46 +00:00
|
|
|
if(commentBlock[i] == 0x00)
|
|
|
|
|
commentBlock[i] = 0x0A;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.Comments = Encoding.ASCII.GetString(commentBlock);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Comment");
|
2017-12-26 06:05:12 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "{0}", imageInfo.Comments);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.CreationTime = new DateTime(commentHeader.Year + 1900, commentHeader.Month + 1,
|
|
|
|
|
commentHeader.Day, commentHeader.Hour, commentHeader.Minute,
|
|
|
|
|
commentHeader.Second, DateTimeKind.Unspecified);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(imageInfo.CreationTime == DateTime.MinValue) imageInfo.CreationTime = imageFilter.GetCreationTime();
|
2018-06-22 08:08:38 +01:00
|
|
|
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Image created on {0}", imageInfo.CreationTime);
|
2017-12-26 06:05:12 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Image modified on {0}", imageInfo.LastModificationTime);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Parsing image");
|
|
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
totalDiskSize = 0;
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.ImageSize = 0;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-06-22 08:08:38 +01:00
|
|
|
int totalCylinders = -1;
|
|
|
|
|
int totalHeads = -1;
|
|
|
|
|
int maxSector = -1;
|
|
|
|
|
int totalSectors = 0;
|
|
|
|
|
long currentPos = stream.Position;
|
2018-01-28 20:29:46 +00:00
|
|
|
imageInfo.SectorSize = uint.MaxValue;
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.SectorsPerTrack = uint.MaxValue;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// Count cylinders
|
|
|
|
|
while(true)
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
TeleDiskTrackHeader teleDiskTrack = new TeleDiskTrackHeader
|
|
|
|
|
{
|
2018-01-28 20:29:46 +00:00
|
|
|
Sectors = (byte)stream.ReadByte(),
|
2017-12-22 06:55:04 +00:00
|
|
|
Cylinder = (byte)stream.ReadByte(),
|
2018-01-28 20:29:46 +00:00
|
|
|
Head = (byte)stream.ReadByte(),
|
|
|
|
|
Crc = (byte)stream.ReadByte()
|
2017-12-22 06:55:04 +00:00
|
|
|
};
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(teleDiskTrack.Cylinder > totalCylinders) totalCylinders = teleDiskTrack.Cylinder;
|
2018-01-28 20:29:46 +00:00
|
|
|
if(teleDiskTrack.Head > totalHeads) totalHeads = teleDiskTrack.Head;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(teleDiskTrack.Sectors == 0xFF) // End of disk image
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
for(byte processedSectors = 0; processedSectors < teleDiskTrack.Sectors; processedSectors++)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
TeleDiskSectorHeader teleDiskSector = new TeleDiskSectorHeader();
|
2018-01-28 20:29:46 +00:00
|
|
|
TeleDiskDataHeader teleDiskData = new TeleDiskDataHeader();
|
|
|
|
|
byte[] dataSizeBytes = new byte[2];
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
teleDiskSector.Cylinder = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskSector.Head = (byte)stream.ReadByte();
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskSector.SectorNumber = (byte)stream.ReadByte();
|
2018-01-28 20:29:46 +00:00
|
|
|
teleDiskSector.SectorSize = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskSector.Flags = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskSector.Crc = (byte)stream.ReadByte();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(teleDiskSector.SectorNumber > maxSector) maxSector = teleDiskSector.SectorNumber;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if((teleDiskSector.Flags & FLAGS_SECTOR_DATALESS) != FLAGS_SECTOR_DATALESS &&
|
2018-01-28 20:29:46 +00:00
|
|
|
(teleDiskSector.Flags & FLAGS_SECTOR_SKIPPED) != FLAGS_SECTOR_SKIPPED)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
stream.Read(dataSizeBytes, 0, 2);
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskData.DataSize = BitConverter.ToUInt16(dataSizeBytes, 0);
|
|
|
|
|
teleDiskData.DataSize--; // Sydex decided to including dataEncoding byte as part of it
|
|
|
|
|
teleDiskData.DataEncoding = (byte)stream.ReadByte();
|
2018-06-22 08:08:38 +01:00
|
|
|
byte[] data = new byte[teleDiskData.DataSize];
|
2017-12-20 17:15:26 +00:00
|
|
|
stream.Read(data, 0, teleDiskData.DataSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-22 08:08:38 +01:00
|
|
|
if(128 << teleDiskSector.SectorSize < imageInfo.SectorSize)
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.SectorSize = (uint)(128 << teleDiskSector.SectorSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
totalSectors++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
totalCylinders++;
|
|
|
|
|
totalHeads++;
|
|
|
|
|
|
|
|
|
|
if(totalCylinders <= 0 || totalHeads <= 0)
|
|
|
|
|
throw new ImageNotSupportedException("No cylinders or heads found");
|
|
|
|
|
|
|
|
|
|
bool hasLeadOutOnHead0 = false;
|
|
|
|
|
bool hasLeadOutOnHead1 = false;
|
2018-06-22 08:08:38 +01:00
|
|
|
imageInfo.Cylinders = (ushort)totalCylinders;
|
|
|
|
|
imageInfo.Heads = (byte)totalHeads;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// Count sectors per track
|
|
|
|
|
stream.Seek(currentPos, SeekOrigin.Begin);
|
|
|
|
|
while(true)
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
TeleDiskTrackHeader teleDiskTrack = new TeleDiskTrackHeader
|
|
|
|
|
{
|
2018-01-28 20:29:46 +00:00
|
|
|
Sectors = (byte)stream.ReadByte(),
|
2017-12-22 06:55:04 +00:00
|
|
|
Cylinder = (byte)stream.ReadByte(),
|
2018-01-28 20:29:46 +00:00
|
|
|
Head = (byte)stream.ReadByte(),
|
|
|
|
|
Crc = (byte)stream.ReadByte()
|
2017-12-22 06:55:04 +00:00
|
|
|
};
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(teleDiskTrack.Sectors == 0xFF) // End of disk image
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
|
2018-06-22 08:08:38 +01:00
|
|
|
if(teleDiskTrack.Sectors < imageInfo.SectorsPerTrack)
|
2017-12-20 17:15:26 +00:00
|
|
|
if(teleDiskTrack.Cylinder + 1 == totalCylinders)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
hasLeadOutOnHead0 |= teleDiskTrack.Head == 0;
|
|
|
|
|
hasLeadOutOnHead1 |= teleDiskTrack.Head == 1;
|
2018-06-22 08:08:38 +01:00
|
|
|
if(imageInfo.Cylinders == totalCylinders) imageInfo.Cylinders--;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
2018-01-28 20:29:46 +00:00
|
|
|
else
|
|
|
|
|
imageInfo.SectorsPerTrack = teleDiskTrack.Sectors;
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
for(byte processedSectors = 0; processedSectors < teleDiskTrack.Sectors; processedSectors++)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
TeleDiskSectorHeader teleDiskSector = new TeleDiskSectorHeader();
|
2018-01-28 20:29:46 +00:00
|
|
|
TeleDiskDataHeader teleDiskData = new TeleDiskDataHeader();
|
|
|
|
|
byte[] dataSizeBytes = new byte[2];
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
teleDiskSector.Cylinder = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskSector.Head = (byte)stream.ReadByte();
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskSector.SectorNumber = (byte)stream.ReadByte();
|
2018-01-28 20:29:46 +00:00
|
|
|
teleDiskSector.SectorSize = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskSector.Flags = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskSector.Crc = (byte)stream.ReadByte();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
if((teleDiskSector.Flags & FLAGS_SECTOR_DATALESS) == FLAGS_SECTOR_DATALESS ||
|
2018-01-28 20:29:46 +00:00
|
|
|
(teleDiskSector.Flags & FLAGS_SECTOR_SKIPPED) == FLAGS_SECTOR_SKIPPED) continue;
|
2017-12-21 06:06:19 +00:00
|
|
|
|
|
|
|
|
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();
|
2018-06-22 08:08:38 +01:00
|
|
|
byte[] data = new byte[teleDiskData.DataSize];
|
2017-12-21 06:06:19 +00:00
|
|
|
stream.Read(data, 0, teleDiskData.DataSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sectorsData = new byte[totalCylinders][][][];
|
|
|
|
|
// Total sectors per track
|
|
|
|
|
uint[][] spts = new uint[totalCylinders][];
|
|
|
|
|
|
|
|
|
|
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][][];
|
2018-01-28 20:29:46 +00:00
|
|
|
spts[i] = new uint[totalHeads];
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
for(int j = 0; j < totalHeads; j++) sectorsData[i][j] = new byte[maxSector + 1][];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Decode the image
|
|
|
|
|
stream.Seek(currentPos, SeekOrigin.Begin);
|
|
|
|
|
while(true)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
TeleDiskTrackHeader teleDiskTrack = new TeleDiskTrackHeader();
|
2018-01-28 20:29:46 +00:00
|
|
|
byte[] tdTrackForCrc = new byte[3];
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
teleDiskTrack.Sectors = (byte)stream.ReadByte();
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskTrack.Cylinder = (byte)stream.ReadByte();
|
2018-01-28 20:29:46 +00:00
|
|
|
teleDiskTrack.Head = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskTrack.Crc = (byte)stream.ReadByte();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
tdTrackForCrc[0] = teleDiskTrack.Sectors;
|
|
|
|
|
tdTrackForCrc[1] = teleDiskTrack.Cylinder;
|
|
|
|
|
tdTrackForCrc[2] = teleDiskTrack.Head;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
byte tdTrackCalculatedCrc = (byte)(TeleDiskCrc(0, tdTrackForCrc) & 0xFF);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Track follows");
|
2018-01-28 20:29:46 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tTrack cylinder: {0}\t", teleDiskTrack.Cylinder);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tTrack head: {0}\t", teleDiskTrack.Head);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tSectors in track: {0}\t", teleDiskTrack.Sectors);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tTrack header CRC: 0x{0:X2} (calculated 0x{1:X2})\t",
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskTrack.Crc, tdTrackCalculatedCrc);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
aDiskCrcHasFailed |= tdTrackCalculatedCrc != teleDiskTrack.Crc;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(teleDiskTrack.Sectors == 0xFF) // End of disk image
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "End of disk image arrived");
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Total of {0} data sectors, for {1} bytes",
|
|
|
|
|
totalSectors, totalDiskSize);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
for(byte processedSectors = 0; processedSectors < teleDiskTrack.Sectors; processedSectors++)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
TeleDiskSectorHeader teleDiskSector = new TeleDiskSectorHeader();
|
2018-01-28 20:29:46 +00:00
|
|
|
TeleDiskDataHeader teleDiskData = new TeleDiskDataHeader();
|
|
|
|
|
byte[] dataSizeBytes = new byte[2];
|
|
|
|
|
byte[] decodedData;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
teleDiskSector.Cylinder = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskSector.Head = (byte)stream.ReadByte();
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskSector.SectorNumber = (byte)stream.ReadByte();
|
2018-01-28 20:29:46 +00:00
|
|
|
teleDiskSector.SectorSize = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskSector.Flags = (byte)stream.ReadByte();
|
|
|
|
|
teleDiskSector.Crc = (byte)stream.ReadByte();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\tSector follows");
|
2017-12-24 00:12:31 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tAddressMark cylinder: {0}",
|
|
|
|
|
teleDiskSector.Cylinder);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tAddressMark head: {0}", teleDiskSector.Head);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tAddressMark sector number: {0}",
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskSector.SectorNumber);
|
2018-01-28 20:29:46 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector size: {0}",
|
|
|
|
|
teleDiskSector.SectorSize);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector flags: 0x{0:X2}", teleDiskSector.Flags);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector CRC (plus headers): 0x{0:X2}",
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskSector.Crc);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
uint lba = (uint)(teleDiskSector.Cylinder * header.Sides * imageInfo.SectorsPerTrack +
|
2018-06-22 08:08:38 +01:00
|
|
|
teleDiskSector.Head * imageInfo.SectorsPerTrack +
|
|
|
|
|
(teleDiskSector.SectorNumber - 1));
|
2017-12-20 17:15:26 +00:00
|
|
|
if((teleDiskSector.Flags & FLAGS_SECTOR_DATALESS) != FLAGS_SECTOR_DATALESS &&
|
2018-01-28 20:29:46 +00:00
|
|
|
(teleDiskSector.Flags & FLAGS_SECTOR_SKIPPED) != FLAGS_SECTOR_SKIPPED)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
stream.Read(dataSizeBytes, 0, 2);
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskData.DataSize = BitConverter.ToUInt16(dataSizeBytes, 0);
|
|
|
|
|
teleDiskData.DataSize--; // Sydex decided to including dataEncoding byte as part of it
|
2018-01-28 20:29:46 +00:00
|
|
|
imageInfo.ImageSize += teleDiskData.DataSize;
|
|
|
|
|
teleDiskData.DataEncoding = (byte)stream.ReadByte();
|
2018-06-22 08:08:38 +01:00
|
|
|
byte[] data = new byte[teleDiskData.DataSize];
|
2017-12-20 17:15:26 +00:00
|
|
|
stream.Read(data, 0, teleDiskData.DataSize);
|
2017-12-24 00:12:31 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tData size (in-image): {0}",
|
|
|
|
|
teleDiskData.DataSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tData encoding: 0x{0:X2}",
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskData.DataEncoding);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
decodedData = DecodeTeleDiskData(teleDiskSector.SectorSize, teleDiskData.DataEncoding, data);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
byte tdSectorCalculatedCrc = (byte)(TeleDiskCrc(0, decodedData) & 0xFF);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(tdSectorCalculatedCrc != teleDiskSector.Crc)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin",
|
|
|
|
|
"Sector {0}:{3}:{4} calculated CRC 0x{1:X2} differs from stored CRC 0x{2:X2}",
|
2017-12-20 17:15:26 +00:00
|
|
|
teleDiskTrack.Cylinder, tdSectorCalculatedCrc, teleDiskSector.Crc,
|
|
|
|
|
teleDiskTrack.Cylinder, teleDiskSector.SectorNumber);
|
2017-12-24 00:12:31 +00:00
|
|
|
if((teleDiskSector.Flags & FLAGS_SECTOR_NO_ID) != FLAGS_SECTOR_NO_ID)
|
|
|
|
|
sectorsWhereCrcHasFailed.Add(lba);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
else decodedData = new byte[128 << teleDiskSector.SectorSize];
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tLBA: {0}", lba);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
if((teleDiskSector.Flags & FLAGS_SECTOR_NO_ID) == FLAGS_SECTOR_NO_ID) continue;
|
|
|
|
|
|
|
|
|
|
if(sectorsData[teleDiskTrack.Cylinder][teleDiskTrack.Head][teleDiskSector.SectorNumber] != null)
|
2017-12-22 06:55:04 +00:00
|
|
|
DicConsole.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);
|
2017-12-21 06:06:19 +00:00
|
|
|
else
|
|
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
sectorsData[teleDiskTrack.Cylinder][teleDiskTrack.Head][teleDiskSector.SectorNumber] =
|
|
|
|
|
decodedData;
|
2017-12-21 06:06:19 +00:00
|
|
|
totalDiskSize += (uint)decodedData.Length;
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemoryStream leadOutMs = new MemoryStream();
|
|
|
|
|
if(hasLeadOutOnHead0)
|
2018-06-22 08:08:38 +01:00
|
|
|
for(int i = 0; i < sectorsData[totalCylinders - 1][0].Length; i++)
|
|
|
|
|
if(sectorsData[totalCylinders - 1][0][i] != null)
|
2017-12-19 20:33:03 +00:00
|
|
|
leadOutMs.Write(sectorsData[totalCylinders - 1][0][i], 0,
|
|
|
|
|
sectorsData[totalCylinders - 1][0][i].Length);
|
|
|
|
|
if(hasLeadOutOnHead1)
|
2018-06-22 08:08:38 +01:00
|
|
|
for(int i = 0; i < sectorsData[totalCylinders - 1][1].Length; i++)
|
|
|
|
|
if(sectorsData[totalCylinders - 1][1][i] != null)
|
2017-12-19 20:33:03 +00:00
|
|
|
leadOutMs.Write(sectorsData[totalCylinders - 1][1][i], 0,
|
|
|
|
|
sectorsData[totalCylinders - 1][1][i].Length);
|
|
|
|
|
|
|
|
|
|
if(leadOutMs.Length != 0)
|
|
|
|
|
{
|
|
|
|
|
leadOut = leadOutMs.ToArray();
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.ReadableMediaTags.Add(MediaTagType.Floppy_LeadOut);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
imageInfo.Sectors = imageInfo.Cylinders * imageInfo.Heads * imageInfo.SectorsPerTrack;
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.MediaType = DecodeTeleDiskDiskType();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
DicConsole.VerboseWriteLine("TeleDisk image contains a disk of type {0}", imageInfo.MediaType);
|
|
|
|
|
if(!string.IsNullOrEmpty(imageInfo.Comments))
|
|
|
|
|
DicConsole.VerboseWriteLine("TeleDisk comments: {0}", imageInfo.Comments);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
inStream.Dispose();
|
|
|
|
|
stream.Dispose();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSector(ulong sectorAddress)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
(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];
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectors(ulong sectorAddress, uint length)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress > imageInfo.Sectors - 1)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress + length > imageInfo.Sectors)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
|
|
|
|
|
|
|
|
|
|
MemoryStream buffer = new MemoryStream();
|
|
|
|
|
for(uint i = 0; i < length; i++)
|
|
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
byte[] sector = ReadSector(sectorAddress + i) ?? new byte[imageInfo.SectorSize];
|
2017-12-19 20:33:03 +00:00
|
|
|
buffer.Write(sector, 0, sector.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return buffer.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorLong(ulong sectorAddress)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
return ReadSectors(sectorAddress, 1);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
return ReadSectors(sectorAddress, length);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool? VerifySector(ulong sectorAddress)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
return !sectorsWhereCrcHasFailed.Contains(sectorAddress);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool? VerifySector(ulong sectorAddress, uint track)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-22 08:08:38 +01:00
|
|
|
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
|
|
|
|
|
out List<ulong> unknownLbas)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
failingLbas = new List<ulong>();
|
|
|
|
|
unknownLbas = new List<ulong>();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
for(ulong i = sectorAddress; i < sectorAddress + length; i++)
|
2018-01-28 20:29:46 +00:00
|
|
|
if(sectorsWhereCrcHasFailed.Contains(sectorAddress))
|
|
|
|
|
failingLbas.Add(sectorAddress);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
return failingLbas.Count <= 0;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-22 08:08:38 +01:00
|
|
|
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
|
|
|
|
|
out List<ulong> unknownLbas)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
failingLbas = new List<ulong>();
|
|
|
|
|
unknownLbas = new List<ulong>();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool? VerifyMediaImage()
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
return aDiskCrcHasFailed;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
public List<DumpHardwareType> DumpHardware => null;
|
|
|
|
|
public CICMMetadataType CicmMetadata => null;
|
|
|
|
|
|
|
|
|
|
public byte[] ReadDiskTag(MediaTagType tag)
|
|
|
|
|
{
|
|
|
|
|
if(tag != MediaTagType.Floppy_LeadOut)
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
|
|
|
|
|
if(leadOut != null) return leadOut;
|
|
|
|
|
|
|
|
|
|
throw new FeatureNotPresentImageException("Lead-out not present in disk image");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<Track> GetSessionTracks(Session session)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<Track> GetSessionTracks(ushort session)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ReadSector(ulong sectorAddress, uint track)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
|
|
|
|
|
{
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(ushort cylinder, byte head, byte sector) LbaToChs(ulong lba)
|
|
|
|
|
{
|
2018-06-22 08:08:38 +01:00
|
|
|
ushort cylinder = (ushort)(lba / (imageInfo.Heads * imageInfo.SectorsPerTrack));
|
|
|
|
|
byte head = (byte)(lba / imageInfo.SectorsPerTrack % imageInfo.Heads);
|
|
|
|
|
byte sector = (byte)(lba % imageInfo.SectorsPerTrack + 1);
|
2018-01-28 20:29:46 +00:00
|
|
|
|
|
|
|
|
return (cylinder, head, sector);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
static ushort TeleDiskCrc(ushort crc, byte[] buffer)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
int counter = 0;
|
|
|
|
|
|
|
|
|
|
while(counter < buffer.Length)
|
|
|
|
|
{
|
|
|
|
|
crc ^= (ushort)((buffer[counter] & 0xFF) << 8);
|
|
|
|
|
|
2018-06-22 08:08:38 +01:00
|
|
|
for(int i = 0; i < 8; i++)
|
2018-01-28 20:29:46 +00:00
|
|
|
if((crc & 0x8000) > 0)
|
|
|
|
|
crc = (ushort)((crc << 1) ^ TELE_DISK_CRC_POLY);
|
|
|
|
|
else
|
|
|
|
|
crc = (ushort)(crc << 1);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return crc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static byte[] DecodeTeleDiskData(byte sectorSize, byte encodingType, byte[] encodedData)
|
|
|
|
|
{
|
|
|
|
|
byte[] decodedData;
|
|
|
|
|
switch(sectorSize)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case SECTOR_SIZE_128:
|
2017-12-19 20:33:03 +00:00
|
|
|
decodedData = new byte[128];
|
|
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SECTOR_SIZE_256:
|
2017-12-19 20:33:03 +00:00
|
|
|
decodedData = new byte[256];
|
|
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SECTOR_SIZE_512:
|
2017-12-19 20:33:03 +00:00
|
|
|
decodedData = new byte[512];
|
|
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SECTOR_SIZE_1K:
|
2017-12-19 20:33:03 +00:00
|
|
|
decodedData = new byte[1024];
|
|
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SECTOR_SIZE_2K:
|
2017-12-19 20:33:03 +00:00
|
|
|
decodedData = new byte[2048];
|
|
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SECTOR_SIZE_4K:
|
2017-12-19 20:33:03 +00:00
|
|
|
decodedData = new byte[4096];
|
|
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SECTOR_SIZE_8K:
|
2017-12-19 20:33:03 +00:00
|
|
|
decodedData = new byte[8192];
|
|
|
|
|
break;
|
2017-12-24 00:12:31 +00:00
|
|
|
default: throw new ImageNotSupportedException($"Sector size {sectorSize} is incorrect.");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(encodingType)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case DATA_BLOCK_COPY:
|
2017-12-19 20:33:03 +00:00
|
|
|
Array.Copy(encodedData, decodedData, decodedData.Length);
|
|
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case DATA_BLOCK_PATTERN:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2018-01-28 20:29:46 +00:00
|
|
|
int ins = 0;
|
2017-12-19 20:33:03 +00:00
|
|
|
int outs = 0;
|
|
|
|
|
while(ins < encodedData.Length)
|
|
|
|
|
{
|
|
|
|
|
byte[] repeatValue = new byte[2];
|
|
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
ushort repeatNumber = BitConverter.ToUInt16(encodedData, ins);
|
2017-12-19 20:33:03 +00:00
|
|
|
Array.Copy(encodedData, ins + 2, repeatValue, 0, 2);
|
|
|
|
|
byte[] decodedPiece = new byte[repeatNumber * 2];
|
|
|
|
|
ArrayHelpers.ArrayFill(decodedPiece, repeatValue);
|
|
|
|
|
Array.Copy(decodedPiece, 0, decodedData, outs, decodedPiece.Length);
|
2018-01-28 20:29:46 +00:00
|
|
|
ins += 4;
|
2017-12-19 20:33:03 +00:00
|
|
|
outs += decodedPiece.Length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "(Block pattern decoder): Input data size: {0} bytes",
|
|
|
|
|
encodedData.Length);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "(Block pattern decoder): Processed input: {0} bytes",
|
|
|
|
|
ins);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "(Block pattern decoder): Output data size: {0} bytes",
|
|
|
|
|
decodedData.Length);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "(Block pattern decoder): Processed Output: {0} bytes",
|
|
|
|
|
outs);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case DATA_BLOCK_RLE:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2018-01-28 20:29:46 +00:00
|
|
|
int ins = 0;
|
2017-12-19 20:33:03 +00:00
|
|
|
int outs = 0;
|
|
|
|
|
while(ins < encodedData.Length)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
byte length;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-28 20:29:46 +00:00
|
|
|
byte encoding = encodedData[ins];
|
2017-12-20 17:15:26 +00:00
|
|
|
if(encoding == 0x00)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2018-06-22 08:08:38 +01:00
|
|
|
length = encodedData[ins + 1];
|
2017-12-20 17:15:26 +00:00
|
|
|
Array.Copy(encodedData, ins + 2, decodedData, outs, length);
|
2018-06-22 08:08:38 +01:00
|
|
|
ins += 2 + length;
|
2017-12-20 17:15:26 +00:00
|
|
|
outs += length;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-06-22 08:08:38 +01:00
|
|
|
length = (byte)(encoding * 2);
|
2018-01-28 20:29:46 +00:00
|
|
|
byte run = encodedData[ins + 1];
|
2017-12-20 17:15:26 +00:00
|
|
|
byte[] part = new byte[length];
|
|
|
|
|
Array.Copy(encodedData, ins + 2, part, 0, length);
|
2017-12-22 06:55:04 +00:00
|
|
|
byte[] piece = new byte[length * run];
|
2017-12-20 17:15:26 +00:00
|
|
|
ArrayHelpers.ArrayFill(piece, part);
|
|
|
|
|
Array.Copy(piece, 0, decodedData, outs, piece.Length);
|
2018-01-28 20:29:46 +00:00
|
|
|
ins += 2 + length;
|
2017-12-20 17:15:26 +00:00
|
|
|
outs += piece.Length;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "(RLE decoder): Input data size: {0} bytes",
|
|
|
|
|
encodedData.Length);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "(RLE decoder): Processed input: {0} bytes", ins);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "(RLE decoder): Output data size: {0} bytes",
|
|
|
|
|
decodedData.Length);
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "(RLE decoder): Processed Output: {0} bytes", outs);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-12-24 00:12:31 +00:00
|
|
|
default: throw new ImageNotSupportedException($"Data encoding {encodingType} is incorrect.");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return decodedData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MediaType DecodeTeleDiskDiskType()
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
switch(header.DriveType)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case DRIVE_TYPE_525_DD:
|
|
|
|
|
case DRIVE_TYPE_525_HD_DD_DISK:
|
|
|
|
|
case DRIVE_TYPE_525_HD:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
switch(totalDiskSize)
|
|
|
|
|
{
|
|
|
|
|
case 163840:
|
|
|
|
|
{
|
|
|
|
|
// Acorn disk uses 256 bytes/sector
|
2017-12-26 06:05:12 +00:00
|
|
|
return imageInfo.SectorSize == 256
|
2017-12-24 00:12:31 +00:00
|
|
|
? MediaType.ACORN_525_SS_DD_40
|
|
|
|
|
: MediaType.DOS_525_SS_DD_8;
|
2018-01-28 20:29:46 +00:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
// DOS disks use 512 bytes/sector
|
|
|
|
|
}
|
|
|
|
|
case 184320:
|
|
|
|
|
{
|
|
|
|
|
// Atari disk uses 256 bytes/sector
|
2017-12-26 06:05:12 +00:00
|
|
|
return imageInfo.SectorSize == 256 ? MediaType.ATARI_525_DD : MediaType.DOS_525_SS_DD_9;
|
2018-01-28 20:29:46 +00:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
// DOS disks use 512 bytes/sector
|
|
|
|
|
}
|
|
|
|
|
case 327680:
|
|
|
|
|
{
|
|
|
|
|
// Acorn disk uses 256 bytes/sector
|
2017-12-26 06:05:12 +00:00
|
|
|
return imageInfo.SectorSize == 256
|
2017-12-24 00:12:31 +00:00
|
|
|
? MediaType.ACORN_525_SS_DD_80
|
|
|
|
|
: MediaType.DOS_525_DS_DD_8;
|
2018-01-28 20:29:46 +00:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
// DOS disks use 512 bytes/sector
|
|
|
|
|
}
|
2018-01-28 20:29:46 +00:00
|
|
|
case 368640: return MediaType.DOS_525_DS_DD_9;
|
2017-12-19 20:33:03 +00:00
|
|
|
case 1228800: return MediaType.DOS_525_HD;
|
2018-01-28 20:29:46 +00:00
|
|
|
case 102400: return MediaType.ACORN_525_SS_SD_40;
|
|
|
|
|
case 204800: return MediaType.ACORN_525_SS_SD_80;
|
|
|
|
|
case 655360: return MediaType.ACORN_525_DS_DD;
|
|
|
|
|
case 92160: return MediaType.ATARI_525_SD;
|
|
|
|
|
case 133120: return MediaType.ATARI_525_ED;
|
2017-12-19 20:33:03 +00:00
|
|
|
case 1310720: return MediaType.NEC_525_HD;
|
|
|
|
|
case 1261568: return MediaType.SHARP_525;
|
2018-01-28 20:29:46 +00:00
|
|
|
case 839680: return MediaType.FDFORMAT_525_DD;
|
2017-12-19 20:33:03 +00:00
|
|
|
case 1304320: return MediaType.ECMA_99_8;
|
|
|
|
|
case 1223424: return MediaType.ECMA_99_15;
|
|
|
|
|
case 1061632: return MediaType.ECMA_99_26;
|
2018-01-28 20:29:46 +00:00
|
|
|
case 80384: return MediaType.ECMA_66;
|
|
|
|
|
case 325632: return MediaType.ECMA_70;
|
|
|
|
|
case 653312: return MediaType.ECMA_78;
|
|
|
|
|
case 737280: return MediaType.ECMA_78_2;
|
2017-12-19 20:33:03 +00:00
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Unknown 5,25\" disk with {0} bytes",
|
|
|
|
|
totalDiskSize);
|
|
|
|
|
return MediaType.Unknown;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case DRIVE_TYPE_35_DD:
|
|
|
|
|
case DRIVE_TYPE_35_ED:
|
|
|
|
|
case DRIVE_TYPE_35_HD:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
switch(totalDiskSize)
|
|
|
|
|
{
|
2018-01-28 20:29:46 +00:00
|
|
|
case 322560: return MediaType.Apricot_35;
|
|
|
|
|
case 327680: return MediaType.DOS_35_SS_DD_8;
|
|
|
|
|
case 368640: return MediaType.DOS_35_SS_DD_9;
|
|
|
|
|
case 655360: return MediaType.DOS_35_DS_DD_8;
|
|
|
|
|
case 737280: return MediaType.DOS_35_DS_DD_9;
|
2017-12-19 20:33:03 +00:00
|
|
|
case 1474560: return MediaType.DOS_35_HD;
|
|
|
|
|
case 2949120: return MediaType.DOS_35_ED;
|
|
|
|
|
case 1720320: return MediaType.DMF;
|
|
|
|
|
case 1763328: return MediaType.DMF_82;
|
|
|
|
|
case 1884160: // Irreal size, seen as BIOS with TSR, 23 sectors/track
|
|
|
|
|
case 1860608: // Real data size, sum of all sectors
|
|
|
|
|
return MediaType.XDF_35;
|
2018-01-28 20:29:46 +00:00
|
|
|
case 819200: return MediaType.CBM_35_DD;
|
|
|
|
|
case 901120: return MediaType.CBM_AMIGA_35_DD;
|
2017-12-19 20:33:03 +00:00
|
|
|
case 1802240: return MediaType.CBM_AMIGA_35_HD;
|
|
|
|
|
case 1310720: return MediaType.NEC_35_HD_8;
|
|
|
|
|
case 1228800: return MediaType.NEC_35_HD_15;
|
|
|
|
|
case 1261568: return MediaType.SHARP_35;
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Unknown 3,5\" disk with {0} bytes",
|
|
|
|
|
totalDiskSize);
|
|
|
|
|
return MediaType.Unknown;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case DRIVE_TYPE_8_INCH:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
switch(totalDiskSize)
|
|
|
|
|
{
|
2018-01-28 20:29:46 +00:00
|
|
|
case 81664: return MediaType.IBM23FD;
|
|
|
|
|
case 242944: return MediaType.IBM33FD_128;
|
|
|
|
|
case 287488: return MediaType.IBM33FD_256;
|
|
|
|
|
case 306432: return MediaType.IBM33FD_512;
|
|
|
|
|
case 499200: return MediaType.IBM43FD_128;
|
|
|
|
|
case 574976: return MediaType.IBM43FD_256;
|
|
|
|
|
case 995072: return MediaType.IBM53FD_256;
|
2017-12-19 20:33:03 +00:00
|
|
|
case 1146624: return MediaType.IBM53FD_512;
|
|
|
|
|
case 1222400: return MediaType.IBM53FD_1024;
|
|
|
|
|
case 256256:
|
|
|
|
|
// Same size, with same disk geometry, for DEC RX01, NEC and ECMA, return ECMA
|
|
|
|
|
return MediaType.ECMA_54;
|
|
|
|
|
case 512512:
|
|
|
|
|
{
|
|
|
|
|
// DEC disk uses 256 bytes/sector
|
2017-12-26 06:05:12 +00:00
|
|
|
return imageInfo.SectorSize == 256 ? MediaType.RX02 : MediaType.ECMA_59;
|
2018-01-28 20:29:46 +00:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
// ECMA disks use 128 bytes/sector
|
|
|
|
|
}
|
|
|
|
|
case 1261568: return MediaType.NEC_8_DD;
|
|
|
|
|
case 1255168: return MediaType.ECMA_69_8;
|
|
|
|
|
case 1177344: return MediaType.ECMA_69_15;
|
|
|
|
|
case 1021696: return MediaType.ECMA_69_26;
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Unknown 8\" disk with {0} bytes",
|
|
|
|
|
totalDiskSize);
|
|
|
|
|
return MediaType.Unknown;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
DicConsole.DebugWriteLine("TeleDisk plugin", "Unknown drive type {1} with {0} bytes", totalDiskSize,
|
2017-12-20 17:15:26 +00:00
|
|
|
header.DriveType);
|
2017-12-19 20:33:03 +00:00
|
|
|
return MediaType.Unknown;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
struct TeleDiskHeader
|
|
|
|
|
{
|
|
|
|
|
/// <summary>"TD" or "td" depending on compression</summary>
|
|
|
|
|
public ushort Signature;
|
|
|
|
|
/// <summary>Sequence, but TeleDisk seems to complaing if != 0</summary>
|
|
|
|
|
public byte Sequence;
|
|
|
|
|
/// <summary>Random, same byte for all disks in the same set</summary>
|
|
|
|
|
public byte DiskSet;
|
|
|
|
|
/// <summary>TeleDisk version, major in high nibble, minor in low nibble</summary>
|
|
|
|
|
public byte Version;
|
|
|
|
|
/// <summary>Data rate</summary>
|
|
|
|
|
public byte DataRate;
|
|
|
|
|
/// <summary>BIOS drive type</summary>
|
|
|
|
|
public byte DriveType;
|
|
|
|
|
/// <summary>Stepping used</summary>
|
|
|
|
|
public byte Stepping;
|
|
|
|
|
/// <summary>If set means image only allocates sectors marked in-use by FAT12</summary>
|
|
|
|
|
public byte DosAllocation;
|
|
|
|
|
/// <summary>Sides of disk</summary>
|
|
|
|
|
public byte Sides;
|
|
|
|
|
/// <summary>CRC of all the previous</summary>
|
|
|
|
|
public ushort Crc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct TeleDiskCommentBlockHeader
|
|
|
|
|
{
|
|
|
|
|
/// <summary>CRC of comment block after crc field</summary>
|
|
|
|
|
public ushort Crc;
|
|
|
|
|
/// <summary>Length of comment</summary>
|
|
|
|
|
public ushort Length;
|
2018-06-22 08:08:38 +01:00
|
|
|
public byte Year;
|
|
|
|
|
public byte Month;
|
|
|
|
|
public byte Day;
|
|
|
|
|
public byte Hour;
|
|
|
|
|
public byte Minute;
|
|
|
|
|
public byte Second;
|
2017-12-24 00:12:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct TeleDiskTrackHeader
|
|
|
|
|
{
|
|
|
|
|
/// <summary>Sectors in the track, 0xFF if end of disk image (there is no spoon)</summary>
|
|
|
|
|
public byte Sectors;
|
|
|
|
|
/// <summary>Cylinder the head was on</summary>
|
|
|
|
|
public byte Cylinder;
|
|
|
|
|
/// <summary>Head/side used</summary>
|
|
|
|
|
public byte Head;
|
|
|
|
|
/// <summary>Lower byte of CRC of previous fields</summary>
|
|
|
|
|
public byte Crc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct TeleDiskSectorHeader
|
|
|
|
|
{
|
|
|
|
|
/// <summary>Cylinder as stored on sector address mark</summary>
|
|
|
|
|
public byte Cylinder;
|
|
|
|
|
/// <summary>Head as stored on sector address mark</summary>
|
|
|
|
|
public byte Head;
|
|
|
|
|
/// <summary>Sector number as stored on sector address mark</summary>
|
|
|
|
|
public byte SectorNumber;
|
|
|
|
|
/// <summary>Sector size</summary>
|
|
|
|
|
public byte SectorSize;
|
|
|
|
|
/// <summary>Sector flags</summary>
|
|
|
|
|
public byte Flags;
|
|
|
|
|
/// <summary>Lower byte of TeleDisk CRC of sector header, data header and data block</summary>
|
|
|
|
|
public byte Crc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct TeleDiskDataHeader
|
|
|
|
|
{
|
|
|
|
|
/// <summary>Size of all data (encoded) + next field (1)</summary>
|
|
|
|
|
public ushort DataSize;
|
|
|
|
|
/// <summary>Encoding used for data block</summary>
|
|
|
|
|
public byte DataEncoding;
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
2017-09-27 14:54:27 +01:00
|
|
|
}
|