From c963a56af37210a44acd0a94ddf6bac50e7b13e0 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Mon, 6 May 2019 20:21:57 +0100 Subject: [PATCH] Implement CopyTape reading. --- .../CopyTape/Constants.cs | 7 +- DiscImageChef.DiscImages/CopyTape/CopyTape.cs | 7 +- .../CopyTape/Properties.cs | 6 +- DiscImageChef.DiscImages/CopyTape/Read.cs | 160 +++++++++++++++++- 4 files changed, 170 insertions(+), 10 deletions(-) diff --git a/DiscImageChef.DiscImages/CopyTape/Constants.cs b/DiscImageChef.DiscImages/CopyTape/Constants.cs index 6911d553c..98a2cc803 100644 --- a/DiscImageChef.DiscImages/CopyTape/Constants.cs +++ b/DiscImageChef.DiscImages/CopyTape/Constants.cs @@ -34,8 +34,9 @@ namespace DiscImageChef.DiscImages.CopyTape { public partial class CopyTape { - const string BlockRegex = @"^CPTP:BLK (?\d{6})\n$"; - const string FilemarkRegex = @"^CPTP:MRK\n$"; - const string EndOfTapeRegex = @"^CPTP:EOT\n$"; + const string BlockRegex = @"^CPTP:BLK (?\d{6})\n$"; + const string PartialBlockRegex = @"^CPTP:BLK $"; + const string FilemarkRegex = @"^CPTP:MRK\n$"; + const string EndOfTapeRegex = @"^CPTP:EOT\n$"; } } \ No newline at end of file diff --git a/DiscImageChef.DiscImages/CopyTape/CopyTape.cs b/DiscImageChef.DiscImages/CopyTape/CopyTape.cs index f2b17c662..ba5241d93 100644 --- a/DiscImageChef.DiscImages/CopyTape/CopyTape.cs +++ b/DiscImageChef.DiscImages/CopyTape/CopyTape.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using DiscImageChef.CommonTypes.Enums; using DiscImageChef.CommonTypes.Interfaces; using DiscImageChef.CommonTypes.Structs; @@ -7,9 +8,13 @@ namespace DiscImageChef.DiscImages.CopyTape { public partial class CopyTape : ITapeImage { + long[] blockPositionCache; + ImageInfo imageInfo; + Stream imageStream; + public CopyTape() { - Info = new ImageInfo + imageInfo = new ImageInfo { ReadableSectorTags = new List(), ReadableMediaTags = new List(), diff --git a/DiscImageChef.DiscImages/CopyTape/Properties.cs b/DiscImageChef.DiscImages/CopyTape/Properties.cs index 98a65b799..86413b356 100644 --- a/DiscImageChef.DiscImages/CopyTape/Properties.cs +++ b/DiscImageChef.DiscImages/CopyTape/Properties.cs @@ -39,15 +39,15 @@ namespace DiscImageChef.DiscImages.CopyTape { public partial class CopyTape { - public ImageInfo Info { get; } + public ImageInfo Info => imageInfo; public string Name => "CopyTape"; public Guid Id => new Guid("C537D41E-D6A7-4922-9AA9-8E8442D0E340"); public string Author => "Natalia Portillo"; public string Format => "CopyTape"; public List DumpHardware => null; public CICMMetadataType CicmMetadata => null; - public List Files { get; } - public List TapePartitions { get; } + public List Files { get; private set; } + public List TapePartitions { get; set; } public bool IsTape => true; } } \ No newline at end of file diff --git a/DiscImageChef.DiscImages/CopyTape/Read.cs b/DiscImageChef.DiscImages/CopyTape/Read.cs index 5aa55b297..aa731b2e6 100644 --- a/DiscImageChef.DiscImages/CopyTape/Read.cs +++ b/DiscImageChef.DiscImages/CopyTape/Read.cs @@ -31,16 +31,170 @@ // ****************************************************************************/ 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.Structs; namespace DiscImageChef.DiscImages.CopyTape { public partial class CopyTape { - public bool Open(IFilter imageFilter) => throw new NotImplementedException(); + public bool Open(IFilter imageFilter) + { + List blockPositions = new List(); + 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(); + + 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 + { + 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(); + } } } \ No newline at end of file