/***************************************************************************
FileSystem identifier and checker
----------------------------------------------------------------------------
Filename : TeleDisk.cs
Version : 1.0
Author(s) : Natalia Portillo
Component : Disk image plugins
Revision : $Revision$
Last change by : $Author$
Date : $Date$
--[ Description ] ----------------------------------------------------------
Manages Sydex TeleDisk disk images.
--[ License ] --------------------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
----------------------------------------------------------------------------
Copyright (C) 2011-2014 Claunia.com
****************************************************************************/
//$Id$
using System;
using System.IO;
using System.Collections.Generic;
// Created following notes from Dave Dunfield
namespace FileSystemIDandChk.ImagePlugins
{
class TeleDisk : ImagePlugin
{
#region Internal Structures
struct TD0Header
{
// "TD" or "td" depending on compression
public UInt16 signature;
// Sequence, but TeleDisk seems to complaing if != 0
public byte sequence;
// Random, same byte for all disks in the same set
public byte diskSet;
// TeleDisk version, major in high nibble, minor in low nibble
public byte version;
// Data rate
public byte dataRate;
// BIOS drive type
public byte driveType;
// Stepping used
public byte stepping;
// If set means image only allocates sectors marked in-use by FAT12
public byte dosAllocation;
// Sides of disk
public byte sides;
// CRC of all the previous
public UInt16 crc;
}
struct TDCommentBlockHeader
{
// CRC of comment block after crc field
public UInt16 crc;
// Length of comment
public UInt16 length;
public byte year;
public byte month;
public byte day;
public byte hour;
public byte minute;
public byte second;
}
struct TDTrackHeader
{
// Sectors in the track, 0xFF if end of disk image (there is no spoon)
public byte sectors;
// Cylinder the head was on
public byte cylinder;
// Head/side used
public byte head;
// Lower byte of CRC of previous fields
public byte crc;
}
struct TDSectorHeader
{
// Cylinder as stored on sector address mark
public byte cylinder;
// Head as stored on sector address mark
public byte head;
// Sector number as stored on sector address mark
public byte sectorNumber;
// Sector size
public byte sectorSize;
// Sector flags
public byte flags;
// Lower byte of TeleDisk CRC of sector header, data header and data block
public byte crc;
}
struct TDDataHeader
{
// Size of all data (encoded) + next field (1)
public UInt16 dataSize;
// Encoding used for data block
public byte dataEncoding;
}
#endregion
#region Internal Constants
// "TD" as little endian uint.
const UInt16 tdMagic = 0x4454;
// "td" as little endian uint. Means whole file is compressed (aka Advanced Compression)
const UInt16 tdAdvCompMagic = 0x6474;
// DataRates
const byte DataRate250kbps = 0x00;
const byte DataRate300kbps = 0x01;
const byte DataRate500kbps = 0x02;
// TeleDisk drive types
const byte DriveType525HD_DDDisk = 0x00;
const byte DriveType525HD = 0x01;
const byte DriveType525DD = 0x02;
const byte DriveType35DD = 0x03;
const byte DriveType35HD = 0x04;
const byte DriveType8inch = 0x05;
const byte DriveType35ED = 0x06;
// Stepping
const byte SteppingSingle = 0x00;
const byte SteppingDouble = 0x01;
const byte SteppingEvenOnly = 0x02;
// If this bit is set, there is a comment block
const byte CommentBlockPresent = 0x80;
// CRC polynomial
const UInt16 TeleDiskCRCPoly = 0xA097;
// Sector sizes table
const byte SectorSize128 = 0x00;
const byte SectorSize256 = 0x01;
const byte SectorSize512 = 0x02;
const byte SectorSize1K = 0x03;
const byte SectorSize2K = 0x04;
const byte SectorSize4K = 0x05;
const byte SectorSize8K = 0x06;
// Flags
// Address mark repeats inside same track
const byte FlagsSectorDuplicate = 0x01;
// Sector gave CRC error on reading
const byte FlagsSectorCRCError = 0x02;
// Address mark indicates deleted sector
const byte FlagsSectorDeleted = 0x04;
// Sector skipped as FAT said it's unused
const byte FlagsSectorSkipped = 0x10;
// There was an address mark, but no data following
const byte FlagsSectorDataless = 0x20;
// There was data without address mark
const byte FlagsSectorNoID = 0x40;
// Data block encodings
// Data is copied as is
const byte dataBlockCopy = 0x00;
// Data is encoded as a pair of len.value uint16s
const byte dataBlockPattern = 0x01;
// Data is encoded as RLE
const byte dataBlockRLE = 0x02;
#endregion
#region Internal variables
TD0Header header;
TDCommentBlockHeader commentHeader;
byte[] commentBlock;
string comment;
#endregion
public TeleDisk(PluginBase Core)
{
Name = "Sydex TeleDisk";
PluginUUID = new Guid("0240B7B1-E959-4CDC-B0BD-386D6E467B88");
}
public override bool IdentifyImage(string imagePath)
{
header = new TD0Header();
byte[] headerBytes = new byte[12];
FileStream stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read);
stream.Read(headerBytes, 0, 12);
header.signature = BitConverter.ToUInt16(headerBytes, 0);
if (header.signature != tdMagic && header.signature != tdAdvCompMagic)
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);
byte[] headerBytesForCRC = new byte[10];
Array.Copy(headerBytes, headerBytesForCRC, 10);
UInt16 calculatedHeaderCRC = TeleDiskCRC(0x0000, headerBytesForCRC);
if (MainClass.isDebug)
{
Console.WriteLine("DEBUG (TeleDisk plugin): header.signature = 0x{0:X4}", header.signature);
Console.WriteLine("DEBUG (TeleDisk plugin): header.sequence = 0x{0:X2}", header.sequence);
Console.WriteLine("DEBUG (TeleDisk plugin): header.diskSet = 0x{0:X2}", header.diskSet);
Console.WriteLine("DEBUG (TeleDisk plugin): header.version = 0x{0:X2}", header.version);
Console.WriteLine("DEBUG (TeleDisk plugin): header.dataRate = 0x{0:X2}", header.dataRate);
Console.WriteLine("DEBUG (TeleDisk plugin): header.driveType = 0x{0:X2}", header.driveType);
Console.WriteLine("DEBUG (TeleDisk plugin): header.stepping = 0x{0:X2}", header.stepping);
Console.WriteLine("DEBUG (TeleDisk plugin): header.dosAllocation = 0x{0:X2}", header.dosAllocation);
Console.WriteLine("DEBUG (TeleDisk plugin): header.sides = 0x{0:X2}", header.sides);
Console.WriteLine("DEBUG (TeleDisk plugin): header.crc = 0x{0:X4}", header.crc);
Console.WriteLine("DEBUG (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)
return true;
if (header.sequence != 0x00)
return false;
if (header.dataRate != DataRate250kbps && header.dataRate != DataRate300kbps && header.dataRate != DataRate500kbps)
return false;
if (header.driveType != DriveType35DD && header.driveType != DriveType35ED && header.driveType != DriveType35HD && header.driveType != DriveType525DD &&
header.driveType != DriveType525HD && header.driveType != DriveType525HD_DDDisk && header.driveType != DriveType8inch)
return false;
return true;
}
public override bool OpenImage(string imagePath)
{
throw new NotImplementedException("Not yet implemented.");
}
public override bool ImageHasPartitions()
{
return false;
}
public override UInt64 GetImageSize()
{
throw new NotImplementedException("Not yet implemented.");
}
public override UInt64 GetSectors()
{
throw new NotImplementedException("Not yet implemented.");
}
public override UInt32 GetSectorSize()
{
throw new NotImplementedException("Not yet implemented.");
}
public override byte[] ReadSector(UInt64 sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public override byte[] ReadSectors(UInt64 sectorAddress, UInt32 length)
{
throw new NotImplementedException("Not yet implemented.");
}
public override byte[] ReadSectorLong(UInt64 sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public override byte[] ReadSectorsLong(UInt64 sectorAddress, UInt32 length)
{
return ReadSectors(sectorAddress, length);
}
public override string GetImageFormat()
{
return "Sydex TeleDisk";
}
public override string GetImageVersion()
{
throw new NotImplementedException("Not yet implemented.");
}
public override string GetImageApplication()
{
return "Sydex TeleDisk";
}
public override string GetImageApplicationVersion()
{
throw new NotImplementedException("Not yet implemented.");
}
public override DateTime GetImageCreationTime()
{
throw new NotImplementedException("Not yet implemented.");
}
public override DateTime GetImageLastModificationTime()
{
throw new NotImplementedException("Not yet implemented.");
}
public override string GetImageName()
{
throw new NotImplementedException("Not yet implemented.");
}
public override DiskType GetDiskType()
{
throw new NotImplementedException("Not yet implemented.");
}
#region Private methods
static UInt16 TeleDiskCRC(UInt16 crc, byte[] buffer)
{
int counter = 0;
while (counter < buffer.Length)
{
crc ^= (UInt16)((buffer[counter] & 0xFF) << 8);
for (int i = 0; i < 8; i++)
{
if ((crc & 0x8000) > 0)
crc = (UInt16)((crc << 1) ^ TeleDiskCRCPoly);
else
crc = (UInt16)(crc << 1);
}
counter++;
}
return crc;
}
#endregion
#region Unsupported features
public override byte[] ReadSectorTag(UInt64 sectorAddress, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override byte[] ReadSectorsTag(UInt64 sectorAddress, UInt32 length, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override byte[] ReadDiskTag(DiskTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetImageCreator()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetImageComments()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetDiskManufacturer()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetDiskModel()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetDiskSerialNumber()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetDiskBarcode()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetDiskPartNumber()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override int GetDiskSequence()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override int GetLastDiskSequence()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetDriveManufacturer()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetDriveModel()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override string GetDriveSerialNumber()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override List GetPartitions()
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public override List