Implement CopyTape reading.

This commit is contained in:
2019-05-06 20:21:57 +01:00
parent ca1a226c50
commit c963a56af3
4 changed files with 170 additions and 10 deletions

View File

@@ -35,6 +35,7 @@ namespace DiscImageChef.DiscImages.CopyTape
public partial class CopyTape public partial class CopyTape
{ {
const string BlockRegex = @"^CPTP:BLK (?<blockSize>\d{6})\n$"; const string BlockRegex = @"^CPTP:BLK (?<blockSize>\d{6})\n$";
const string PartialBlockRegex = @"^CPTP:BLK $";
const string FilemarkRegex = @"^CPTP:MRK\n$"; const string FilemarkRegex = @"^CPTP:MRK\n$";
const string EndOfTapeRegex = @"^CPTP:EOT\n$"; const string EndOfTapeRegex = @"^CPTP:EOT\n$";
} }

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums; using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces; using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs; using DiscImageChef.CommonTypes.Structs;
@@ -7,9 +8,13 @@ namespace DiscImageChef.DiscImages.CopyTape
{ {
public partial class CopyTape : ITapeImage public partial class CopyTape : ITapeImage
{ {
long[] blockPositionCache;
ImageInfo imageInfo;
Stream imageStream;
public CopyTape() public CopyTape()
{ {
Info = new ImageInfo imageInfo = new ImageInfo
{ {
ReadableSectorTags = new List<SectorTagType>(), ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(), ReadableMediaTags = new List<MediaTagType>(),

View File

@@ -39,15 +39,15 @@ namespace DiscImageChef.DiscImages.CopyTape
{ {
public partial class CopyTape public partial class CopyTape
{ {
public ImageInfo Info { get; } public ImageInfo Info => imageInfo;
public string Name => "CopyTape"; public string Name => "CopyTape";
public Guid Id => new Guid("C537D41E-D6A7-4922-9AA9-8E8442D0E340"); public Guid Id => new Guid("C537D41E-D6A7-4922-9AA9-8E8442D0E340");
public string Author => "Natalia Portillo"; public string Author => "Natalia Portillo";
public string Format => "CopyTape"; public string Format => "CopyTape";
public List<DumpHardwareType> DumpHardware => null; public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null; public CICMMetadataType CicmMetadata => null;
public List<TapeFile> Files { get; } public List<TapeFile> Files { get; private set; }
public List<TapePartition> TapePartitions { get; } public List<TapePartition> TapePartitions { get; set; }
public bool IsTape => true; public bool IsTape => true;
} }
} }

View File

@@ -31,16 +31,170 @@
// ****************************************************************************/ // ****************************************************************************/
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces; using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages.CopyTape namespace DiscImageChef.DiscImages.CopyTape
{ {
public partial class CopyTape public partial class CopyTape
{ {
public bool Open(IFilter imageFilter) => throw new NotImplementedException(); public bool Open(IFilter imageFilter)
{
List<long> blockPositions = new List<long>();
Regex partialBlockRx = new Regex(PartialBlockRegex);
Regex blockRx = new Regex(BlockRegex);
Regex filemarkRx = new Regex(FilemarkRegex);
Regex eotRx = new Regex(EndOfTapeRegex);
public byte[] ReadSector(ulong sectorAddress) => throw new NotImplementedException(); if(imageFilter.GetDataForkLength() <= 16) return false;
public byte[] ReadSectors(ulong sectorAddress, uint length) => throw new NotImplementedException(); imageStream = imageFilter.GetDataForkStream();
imageStream.Position = 0;
byte[] header = new byte[9];
byte[] blockHeader = new byte[16];
ulong currentBlock = 0;
uint currentFile = 0;
ulong currentFileStart = 0;
bool inFile = false;
Files = new List<TapeFile>();
while(imageStream.Position + 9 < imageStream.Length)
{
imageStream.Read(header, 0, 9);
string mark = Encoding.ASCII.GetString(header);
Match partialBlockMt = partialBlockRx.Match(mark);
Match filemarkMt = filemarkRx.Match(mark);
Match eotMt = eotRx.Match(mark);
if(eotMt.Success) break;
if(filemarkMt.Success)
{
Files.Add(new TapeFile
{
File = currentFile,
FirstBlock = currentFileStart,
LastBlock = currentBlock - 1,
Partition = 0
});
inFile = false;
currentFile++;
continue;
}
if(!partialBlockMt.Success) throw new ArgumentException("Found unhandled header, cannot open.");
imageStream.Position -= 9;
if(!inFile)
{
currentFileStart = currentBlock;
inFile = true;
}
imageStream.Read(blockHeader, 0, 16);
mark = Encoding.ASCII.GetString(blockHeader);
Match blockMt = blockRx.Match(mark);
if(!blockMt.Success) throw new ArgumentException("Cannot decode block header, cannot open.");
string blkSize = blockMt.Groups["blockSize"].Value;
if(string.IsNullOrWhiteSpace(blkSize))
throw new ArgumentException("Cannot decode block header, cannot open.");
if(!uint.TryParse(blkSize, out uint blockSize))
throw new ArgumentException("Cannot decode block header, cannot open.");
if(blockSize == 0 || blockSize + 17 > imageFilter.GetDataForkLength())
throw new ArgumentException("Cannot decode block header, cannot open.");
imageStream.Position += blockSize;
int newLine = imageStream.ReadByte();
if(newLine != 0x0A) throw new ArgumentException("Cannot decode block header, cannot open.");
blockPositions.Add(imageStream.Position - blockSize - 17);
currentBlock++;
imageInfo.ImageSize += blockSize;
if(imageInfo.SectorSize < blockSize) imageInfo.SectorSize = blockSize;
}
blockPositionCache = blockPositions.ToArray();
TapePartitions = new List<TapePartition>
{
new TapePartition {FirstBlock = 0, LastBlock = currentBlock - 1, Number = 0}
};
imageInfo.Sectors = (ulong)blockPositionCache.LongLength;
imageInfo.MediaType = MediaType.UnknownTape;
imageInfo.Application = "CopyTape";
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
if(sectorAddress >= (ulong)blockPositionCache.LongLength)
throw new ArgumentOutOfRangeException(nameof(sectorAddress),
$"Sector address {sectorAddress} not found");
imageStream.Position = blockPositionCache[sectorAddress];
byte[] blockHeader = new byte[16];
Regex blockRx = new Regex(BlockRegex);
imageStream.Read(blockHeader, 0, 16);
string mark = Encoding.ASCII.GetString(blockHeader);
Match blockMt = blockRx.Match(mark);
if(!blockMt.Success) throw new ArgumentException("Cannot decode block header, cannot read.");
string blkSize = blockMt.Groups["blockSize"].Value;
if(string.IsNullOrWhiteSpace(blkSize))
throw new ArgumentException("Cannot decode block header, cannot read.");
if(!uint.TryParse(blkSize, out uint blockSize))
throw new ArgumentException("Cannot decode block header, cannot read.");
if(blockSize == 0 || blockSize + 17 > imageStream.Length)
throw new ArgumentException("Cannot decode block header, cannot read.");
byte[] data = new byte[blockSize];
imageStream.Read(data, 0, (int)blockSize);
if(imageStream.ReadByte() != 0x0A) throw new ArgumentException("Cannot decode block header, cannot read.");
return data;
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
MemoryStream dataMs = new MemoryStream();
for(uint i = 0; i < length; i++)
{
byte[] data = ReadSector(sectorAddress + i);
dataMs.Write(data, 0, data.Length);
}
return dataMs.ToArray();
}
} }
} }