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;
|
List<IndexEntry> index;
|
||||||
/// <summary>If set to <c>true</c>, the DDT entries are in-memory.</summary>
|
/// <summary>If set to <c>true</c>, the DDT entries are in-memory.</summary>
|
||||||
bool inMemoryDdt;
|
bool inMemoryDdt;
|
||||||
|
bool isTape;
|
||||||
ulong lastWrittenBlock;
|
ulong lastWrittenBlock;
|
||||||
/// <summary>LZMA stream.</summary>
|
/// <summary>LZMA stream.</summary>
|
||||||
LzmaStream lzmaBlockStream;
|
LzmaStream lzmaBlockStream;
|
||||||
@@ -154,6 +155,7 @@ namespace DiscImageChef.DiscImages
|
|||||||
byte[] structureBytes;
|
byte[] structureBytes;
|
||||||
/// <summary>Cache for pointer for marshaling structures.</summary>
|
/// <summary>Cache for pointer for marshaling structures.</summary>
|
||||||
IntPtr structurePointer;
|
IntPtr structurePointer;
|
||||||
|
Dictionary<ulong, ulong> tapeDdt;
|
||||||
/// <summary>Cache of CompactDisc track's flags</summary>
|
/// <summary>Cache of CompactDisc track's flags</summary>
|
||||||
Dictionary<byte, byte> trackFlags;
|
Dictionary<byte, byte> trackFlags;
|
||||||
/// <summary>Cache of CompactDisc track's ISRC</summary>
|
/// <summary>Cache of CompactDisc track's ISRC</summary>
|
||||||
|
|||||||
@@ -239,8 +239,10 @@ namespace DiscImageChef.DiscImages
|
|||||||
ParentBlock = 0x50524E54,
|
ParentBlock = 0x50524E54,
|
||||||
/// <summary>Block containing an array of hardware used to create the image</summary>
|
/// <summary>Block containing an array of hardware used to create the image</summary>
|
||||||
DumpHardwareBlock = 0x2A504D44,
|
DumpHardwareBlock = 0x2A504D44,
|
||||||
/// <summary>TODO: Block containing list of files for a tape image</summary>
|
/// <summary>Block containing list of files for a tape image</summary>
|
||||||
TapeFileBlock = 0x454C4654
|
TapeFileBlock = 0x454C4654,
|
||||||
|
/// <summary>Block containing list of partitions for a tape image</summary>
|
||||||
|
TapePartitionBlock = 0x54425054
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChecksumAlgorithm : byte
|
enum ChecksumAlgorithm : byte
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ namespace DiscImageChef.DiscImages
|
|||||||
string[] separated = identify.Model.Split(' ');
|
string[] separated = identify.Model.Split(' ');
|
||||||
|
|
||||||
if(separated.Length == 1)
|
if(separated.Length == 1)
|
||||||
if(string.IsNullOrWhiteSpace(imageInfo.DriveModel))
|
if(string.IsNullOrWhiteSpace(imageInfo.DriveModel)) imageInfo.DriveModel = separated[0];
|
||||||
imageInfo.DriveModel = separated[0];
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(imageInfo.DriveManufacturer))
|
if(string.IsNullOrWhiteSpace(imageInfo.DriveManufacturer))
|
||||||
@@ -249,7 +248,8 @@ namespace DiscImageChef.DiscImages
|
|||||||
{
|
{
|
||||||
if(inMemoryDdt)
|
if(inMemoryDdt)
|
||||||
{
|
{
|
||||||
userDataDdt[sectorAddress] = pointer;
|
if(isTape) tapeDdt[sectorAddress] = pointer;
|
||||||
|
else userDataDdt[sectorAddress] = pointer;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ namespace DiscImageChef.DiscImages
|
|||||||
/// <summary>CRC64-ECMA of the compressed DDT</summary>
|
/// <summary>CRC64-ECMA of the compressed DDT</summary>
|
||||||
public ulong cmpCrc64;
|
public ulong cmpCrc64;
|
||||||
/// <summary>CRC64-ECMA of the uncompressed DDT</summary>
|
/// <summary>CRC64-ECMA of the uncompressed DDT</summary>
|
||||||
public ulong crc64;
|
public readonly ulong crc64;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Header for the index, followed by entries</summary>
|
/// <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>
|
/// <summary>Length in bytes of checksum that follows this structure</summary>
|
||||||
public uint length;
|
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
|
// Copyright © 2011-2019 Natalia Portillo
|
||||||
// ****************************************************************************/
|
// ****************************************************************************/
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using DiscImageChef.CommonTypes.Interfaces;
|
using System.Linq;
|
||||||
using DiscImageChef.CommonTypes.Structs;
|
using DiscImageChef.CommonTypes.Structs;
|
||||||
|
|
||||||
namespace DiscImageChef.DiscImages
|
namespace DiscImageChef.DiscImages
|
||||||
{
|
{
|
||||||
public partial class DiscImageChef
|
public partial class DiscImageChef
|
||||||
{
|
{
|
||||||
public List<TapeFile> Files { get; }
|
public List<TapeFile> Files { get; private set; }
|
||||||
List<TapePartition> ITapeImage.Partitions { get; }
|
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);
|
inMemoryDdt = sectors <= maxDdtSize * 1024 * 1024 / sizeof(ulong);
|
||||||
|
|
||||||
// If in memory, easy
|
// 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
|
// If not, create the block, add to index, and enlarge the file to allow the DDT to exist on-disk
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -798,7 +802,7 @@ namespace DiscImageChef.DiscImages
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sectorAddress >= Info.Sectors)
|
if(sectorAddress >= Info.Sectors && !isTape)
|
||||||
{
|
{
|
||||||
ErrorMessage = "Tried to write past image size";
|
ErrorMessage = "Tried to write past image size";
|
||||||
return false;
|
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 the DDT is in-memory, write it to disk
|
||||||
if(inMemoryDdt)
|
if(inMemoryDdt)
|
||||||
{
|
{
|
||||||
@@ -2949,11 +3043,13 @@ namespace DiscImageChef.DiscImages
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SectorTagType.CdTrackIsrc:
|
case SectorTagType.CdTrackIsrc:
|
||||||
{
|
{
|
||||||
if(data != null) trackIsrcs.Add((byte)track.TrackSequence, Encoding.UTF8.GetString(data));
|
if(data != null) trackIsrcs.Add((byte)track.TrackSequence, Encoding.UTF8.GetString(data));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SectorTagType.CdSectorSubchannel:
|
case SectorTagType.CdSectorSubchannel:
|
||||||
{
|
{
|
||||||
if(data.Length != 96)
|
if(data.Length != 96)
|
||||||
@@ -2968,6 +3064,7 @@ namespace DiscImageChef.DiscImages
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ErrorMessage = $"Don't know how to write sector tag type {tag}";
|
ErrorMessage = $"Don't know how to write sector tag type {tag}";
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user