mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Support writing logically block addressable tapes in dicformat.
This commit is contained in:
@@ -122,6 +122,7 @@ namespace DiscImageChef.DiscImages
|
||||
List<IndexEntry> index;
|
||||
/// <summary>If set to <c>true</c>, the DDT entries are in-memory.</summary>
|
||||
bool inMemoryDdt;
|
||||
bool isTape;
|
||||
ulong lastWrittenBlock;
|
||||
/// <summary>LZMA stream.</summary>
|
||||
LzmaStream lzmaBlockStream;
|
||||
@@ -154,6 +155,7 @@ namespace DiscImageChef.DiscImages
|
||||
byte[] structureBytes;
|
||||
/// <summary>Cache for pointer for marshaling structures.</summary>
|
||||
IntPtr structurePointer;
|
||||
Dictionary<ulong, ulong> tapeDdt;
|
||||
/// <summary>Cache of CompactDisc track's flags</summary>
|
||||
Dictionary<byte, byte> trackFlags;
|
||||
/// <summary>Cache of CompactDisc track's ISRC</summary>
|
||||
|
||||
@@ -239,8 +239,10 @@ namespace DiscImageChef.DiscImages
|
||||
ParentBlock = 0x50524E54,
|
||||
/// <summary>Block containing an array of hardware used to create the image</summary>
|
||||
DumpHardwareBlock = 0x2A504D44,
|
||||
/// <summary>TODO: Block containing list of files for a tape image</summary>
|
||||
TapeFileBlock = 0x454C4654
|
||||
/// <summary>Block containing list of files for a tape image</summary>
|
||||
TapeFileBlock = 0x454C4654,
|
||||
/// <summary>Block containing list of partitions for a tape image</summary>
|
||||
TapePartitionBlock = 0x54425054
|
||||
}
|
||||
|
||||
enum ChecksumAlgorithm : byte
|
||||
|
||||
@@ -107,8 +107,7 @@ namespace DiscImageChef.DiscImages
|
||||
string[] separated = identify.Model.Split(' ');
|
||||
|
||||
if(separated.Length == 1)
|
||||
if(string.IsNullOrWhiteSpace(imageInfo.DriveModel))
|
||||
imageInfo.DriveModel = separated[0];
|
||||
if(string.IsNullOrWhiteSpace(imageInfo.DriveModel)) imageInfo.DriveModel = separated[0];
|
||||
else
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(imageInfo.DriveManufacturer))
|
||||
@@ -249,7 +248,8 @@ namespace DiscImageChef.DiscImages
|
||||
{
|
||||
if(inMemoryDdt)
|
||||
{
|
||||
userDataDdt[sectorAddress] = pointer;
|
||||
if(isTape) tapeDdt[sectorAddress] = pointer;
|
||||
else userDataDdt[sectorAddress] = pointer;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace DiscImageChef.DiscImages
|
||||
/// <summary>CRC64-ECMA of the compressed DDT</summary>
|
||||
public ulong cmpCrc64;
|
||||
/// <summary>CRC64-ECMA of the uncompressed DDT</summary>
|
||||
public ulong crc64;
|
||||
public readonly ulong crc64;
|
||||
}
|
||||
|
||||
/// <summary>Header for the index, followed by entries</summary>
|
||||
@@ -314,5 +314,81 @@ namespace DiscImageChef.DiscImages
|
||||
/// <summary>Length in bytes of checksum that follows this structure</summary>
|
||||
public uint length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tape file block, contains a list of all files in a tape
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct TapeFileHeader
|
||||
{
|
||||
/// <summary>Identifier, <see cref="BlockType.TapeFileBlock" /></summary>
|
||||
public BlockType identifier;
|
||||
/// <summary>How many entries follow this header</summary>
|
||||
public uint entries;
|
||||
/// <summary>Size of the whole block, not including this header, in bytes</summary>
|
||||
public ulong length;
|
||||
/// <summary>CRC64-ECMA of the block</summary>
|
||||
public ulong crc64;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tape file entry
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct TapeFileEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// File number
|
||||
/// </summary>
|
||||
public uint File;
|
||||
/// <summary>
|
||||
/// Partition number
|
||||
/// </summary>
|
||||
public readonly byte Partition;
|
||||
/// <summary>
|
||||
/// First block, inclusive, of the file
|
||||
/// </summary>
|
||||
public ulong FirstBlock;
|
||||
/// <summary>
|
||||
/// Last block, inclusive, of the file
|
||||
/// </summary>
|
||||
public ulong LastBlock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tape partition block, contains a list of all partitions in a tape
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct TapePartitionHeader
|
||||
{
|
||||
/// <summary>Identifier, <see cref="BlockType.TapePartitionBlock" /></summary>
|
||||
public BlockType identifier;
|
||||
/// <summary>How many entries follow this header</summary>
|
||||
public byte entries;
|
||||
/// <summary>Size of the whole block, not including this header, in bytes</summary>
|
||||
public ulong length;
|
||||
/// <summary>CRC64-ECMA of the block</summary>
|
||||
public ulong crc64;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tape partition entry
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct TapePartitionEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Partition number
|
||||
/// </summary>
|
||||
public byte Number;
|
||||
/// <summary>
|
||||
/// First block, inclusive, of the partition
|
||||
/// </summary>
|
||||
public ulong FirstBlock;
|
||||
/// <summary>
|
||||
/// Last block, inclusive, of the partition
|
||||
/// </summary>
|
||||
public ulong LastBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,22 +30,46 @@
|
||||
// Copyright © 2011-2019 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DiscImageChef.CommonTypes.Interfaces;
|
||||
using System.Linq;
|
||||
using DiscImageChef.CommonTypes.Structs;
|
||||
|
||||
namespace DiscImageChef.DiscImages
|
||||
{
|
||||
public partial class DiscImageChef
|
||||
{
|
||||
public List<TapeFile> Files { get; }
|
||||
List<TapePartition> ITapeImage.Partitions { get; }
|
||||
public List<TapeFile> Files { get; private set; }
|
||||
public List<TapePartition> TapePartitions { get; private set; }
|
||||
|
||||
public bool AddFile(TapeFile file) => throw new NotImplementedException();
|
||||
public bool AddFile(TapeFile file)
|
||||
{
|
||||
if(Files.Any(f => f.File == file.File))
|
||||
{
|
||||
TapeFile removeMe = Files.FirstOrDefault(f => f.File == file.File);
|
||||
Files.Remove(removeMe);
|
||||
}
|
||||
|
||||
public bool AddPartition(TapePartition partition) => throw new NotImplementedException();
|
||||
Files.Add(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetTape() => throw new NotImplementedException();
|
||||
public bool AddPartition(TapePartition partition)
|
||||
{
|
||||
if(TapePartitions.Any(f => f.Number == partition.Number))
|
||||
{
|
||||
TapePartition removeMe = TapePartitions.FirstOrDefault(f => f.Number == partition.Number);
|
||||
TapePartitions.Remove(removeMe);
|
||||
}
|
||||
|
||||
TapePartitions.Add(partition);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetTape()
|
||||
{
|
||||
Files = new List<TapeFile>();
|
||||
TapePartitions = new List<TapePartition>();
|
||||
return isTape = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -688,7 +688,11 @@ namespace DiscImageChef.DiscImages
|
||||
inMemoryDdt = sectors <= maxDdtSize * 1024 * 1024 / sizeof(ulong);
|
||||
|
||||
// If in memory, easy
|
||||
if(inMemoryDdt) userDataDdt = new ulong[sectors];
|
||||
if(inMemoryDdt)
|
||||
{
|
||||
if(isTape) tapeDdt = new Dictionary<ulong, ulong>();
|
||||
else userDataDdt = new ulong[sectors];
|
||||
}
|
||||
// If not, create the block, add to index, and enlarge the file to allow the DDT to exist on-disk
|
||||
else
|
||||
{
|
||||
@@ -798,7 +802,7 @@ namespace DiscImageChef.DiscImages
|
||||
return false;
|
||||
}
|
||||
|
||||
if(sectorAddress >= Info.Sectors)
|
||||
if(sectorAddress >= Info.Sectors && !isTape)
|
||||
{
|
||||
ErrorMessage = "Tried to write past image size";
|
||||
return false;
|
||||
@@ -1854,6 +1858,96 @@ namespace DiscImageChef.DiscImages
|
||||
}
|
||||
}
|
||||
|
||||
if(isTape)
|
||||
{
|
||||
ulong latestBlock = tapeDdt.Max(b => b.Key);
|
||||
|
||||
userDataDdt = new ulong[latestBlock + 1];
|
||||
foreach(KeyValuePair<ulong, ulong> block in tapeDdt) userDataDdt[block.Key] = block.Value;
|
||||
|
||||
inMemoryDdt = true;
|
||||
tapeDdt.Clear();
|
||||
|
||||
idxEntry = new IndexEntry
|
||||
{
|
||||
blockType = BlockType.TapePartitionBlock,
|
||||
dataType = DataType.UserData,
|
||||
offset = (ulong)imageStream.Position
|
||||
};
|
||||
|
||||
DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing tape partitions to position {0}",
|
||||
idxEntry.offset);
|
||||
|
||||
TapePartitionEntry[] tapePartitionEntries = new TapePartitionEntry[TapePartitions.Count];
|
||||
for(int t = 0; t < TapePartitions.Count; t++)
|
||||
{
|
||||
tapePartitionEntries[t] = new TapePartitionEntry();
|
||||
tapePartitionEntries[t].Number = TapePartitions[t].Number;
|
||||
tapePartitionEntries[t].FirstBlock = TapePartitions[t].FirstBlock;
|
||||
tapePartitionEntries[t].LastBlock = TapePartitions[t].LastBlock;
|
||||
}
|
||||
|
||||
byte[] tapePartitionEntriesData =
|
||||
MemoryMarshal.Cast<TapePartitionEntry, byte>(tapePartitionEntries).ToArray();
|
||||
|
||||
TapePartitionHeader tapePartitionHeader = new TapePartitionHeader();
|
||||
tapePartitionHeader.identifier = BlockType.TapePartitionBlock;
|
||||
tapePartitionHeader.entries = (byte)tapePartitionEntries.Length;
|
||||
tapePartitionHeader.length = (ulong)tapePartitionEntriesData.Length;
|
||||
|
||||
crc64 = new Crc64Context();
|
||||
crc64.Update(tapePartitionEntriesData);
|
||||
tapePartitionHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0);
|
||||
|
||||
structureBytes = new byte[Marshal.SizeOf<TapePartitionHeader>()];
|
||||
MemoryMarshal.Write(structureBytes, ref tapePartitionHeader);
|
||||
imageStream.Write(structureBytes, 0, structureBytes.Length);
|
||||
structureBytes = null;
|
||||
imageStream.Write(tapePartitionEntriesData, 0, tapePartitionEntriesData.Length);
|
||||
|
||||
index.RemoveAll(t => t.blockType == BlockType.TapePartitionBlock && t.dataType == DataType.UserData);
|
||||
index.Add(idxEntry);
|
||||
|
||||
idxEntry = new IndexEntry
|
||||
{
|
||||
blockType = BlockType.TapeFileBlock,
|
||||
dataType = DataType.UserData,
|
||||
offset = (ulong)imageStream.Position
|
||||
};
|
||||
|
||||
DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing tape files to position {0}",
|
||||
idxEntry.offset);
|
||||
|
||||
TapeFileEntry[] tapeFileEntries = new TapeFileEntry[Files.Count];
|
||||
for(int t = 0; t < Files.Count; t++)
|
||||
tapeFileEntries[t] = new TapeFileEntry
|
||||
{
|
||||
File = Files[t].File, FirstBlock = Files[t].FirstBlock, LastBlock = Files[t].LastBlock
|
||||
};
|
||||
|
||||
byte[] tapeFileEntriesData = MemoryMarshal.Cast<TapeFileEntry, byte>(tapeFileEntries).ToArray();
|
||||
|
||||
TapeFileHeader tapeFileHeader = new TapeFileHeader
|
||||
{
|
||||
identifier = BlockType.TapeFileBlock,
|
||||
entries = (uint)tapeFileEntries.Length,
|
||||
length = (ulong)tapeFileEntriesData.Length
|
||||
};
|
||||
|
||||
crc64 = new Crc64Context();
|
||||
crc64.Update(tapeFileEntriesData);
|
||||
tapeFileHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0);
|
||||
|
||||
structureBytes = new byte[Marshal.SizeOf<TapeFileHeader>()];
|
||||
MemoryMarshal.Write(structureBytes, ref tapeFileHeader);
|
||||
imageStream.Write(structureBytes, 0, structureBytes.Length);
|
||||
structureBytes = null;
|
||||
imageStream.Write(tapeFileEntriesData, 0, tapeFileEntriesData.Length);
|
||||
|
||||
index.RemoveAll(t => t.blockType == BlockType.TapeFileBlock && t.dataType == DataType.UserData);
|
||||
index.Add(idxEntry);
|
||||
}
|
||||
|
||||
// If the DDT is in-memory, write it to disk
|
||||
if(inMemoryDdt)
|
||||
{
|
||||
@@ -2949,11 +3043,13 @@ namespace DiscImageChef.DiscImages
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case SectorTagType.CdTrackIsrc:
|
||||
{
|
||||
if(data != null) trackIsrcs.Add((byte)track.TrackSequence, Encoding.UTF8.GetString(data));
|
||||
return true;
|
||||
}
|
||||
|
||||
case SectorTagType.CdSectorSubchannel:
|
||||
{
|
||||
if(data.Length != 96)
|
||||
@@ -2968,6 +3064,7 @@ namespace DiscImageChef.DiscImages
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
ErrorMessage = $"Don't know how to write sector tag type {tag}";
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user