For DiscImageChef format, add support for tracks.

This commit is contained in:
2018-01-24 17:56:50 +00:00
parent 18067f8485
commit db281a0986
2 changed files with 400 additions and 26 deletions

View File

@@ -113,6 +113,8 @@ namespace DiscImageChef.DiscImages
byte shift;
byte[] structureBytes;
IntPtr structurePointer;
Dictionary<byte, byte> trackFlags;
Dictionary<byte, string> trackIsrcs;
ulong[] userDataDdt;
public DiscImageChef()
@@ -146,9 +148,9 @@ namespace DiscImageChef.DiscImages
public string Name => "DiscImageChef format";
public Guid Id => new Guid("49360069-1784-4A2F-B723-0C844D610B0A");
public string Format => "DiscImageChef";
public List<Partition> Partitions { get; }
public List<Partition> Partitions { get; private set; }
public List<Track> Tracks { get; private set; }
public List<Session> Sessions { get; }
public List<Session> Sessions { get; private set; }
public bool Identify(IFilter imageFilter)
{
@@ -568,6 +570,59 @@ namespace DiscImageChef.DiscImages
imageInfo.DriveFirmwareRevision);
}
break;
case BlockType.TracksBlock:
TracksHeader tracksHeader = new TracksHeader();
structureBytes = new byte[Marshal.SizeOf(tracksHeader)];
imageStream.Read(structureBytes, 0, structureBytes.Length);
structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(tracksHeader));
Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(tracksHeader));
tracksHeader = (TracksHeader)Marshal.PtrToStructure(structurePointer, typeof(TracksHeader));
Marshal.FreeHGlobal(structurePointer);
if(tracksHeader.identifier != BlockType.TracksBlock)
{
DicConsole.DebugWriteLine("DiscImageChef format plugin",
"Incorrect identifier for tracks block at position {0}",
entry.offset);
break;
}
// TODO: Check CRC64
Tracks = new List<Track>();
trackFlags = new Dictionary<byte, byte>();
trackIsrcs = new Dictionary<byte, string>();
DicConsole.DebugWriteLine("DiscImageChef format plugin", "Found {0} tracks at position {0}",
tracksHeader.entries, entry.offset);
for(ushort i = 0; i < tracksHeader.entries; i++)
{
TrackEntry trackEntry = new TrackEntry();
structureBytes = new byte[Marshal.SizeOf(trackEntry)];
imageStream.Read(structureBytes, 0, structureBytes.Length);
structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(trackEntry));
Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(trackEntry));
trackEntry = (TrackEntry)Marshal.PtrToStructure(structurePointer, typeof(TrackEntry));
Marshal.FreeHGlobal(structurePointer);
Tracks.Add(new Track
{
TrackSequence = trackEntry.sequence,
TrackType = trackEntry.type,
TrackStartSector = (ulong)trackEntry.start,
TrackEndSector = (ulong)trackEntry.end,
TrackPregap = (ulong)trackEntry.pregap,
TrackSession = trackEntry.session,
TrackFile = imageFilter.GetFilename(),
TrackFileType = "BINARY",
TrackFilter = imageFilter
});
trackFlags.Add(trackEntry.sequence, trackEntry.flags);
trackIsrcs.Add(trackEntry.sequence, trackEntry.isrc);
}
break;
}
}
@@ -596,6 +651,79 @@ namespace DiscImageChef.DiscImages
currentCacheSize = 0;
if(!inMemoryDdt) ddtEntryCache = new Dictionary<ulong, ulong>();
if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc)
{
if(Tracks == null || Tracks.Count == 0)
{
Tracks = new List<Track>
{
new Track
{
Indexes = new Dictionary<int, ulong>(),
TrackBytesPerSector = (int)imageInfo.SectorSize,
TrackEndSector = imageInfo.Sectors - 1,
TrackFile = imageFilter.GetFilename(),
TrackFileType = "BINARY",
TrackFilter = imageFilter,
TrackRawBytesPerSector = (int)imageInfo.SectorSize,
TrackSession = 1,
TrackSequence = 1,
TrackType = TrackType.Data
}
};
trackFlags = new Dictionary<byte, byte> {{1, (byte)CdFlags.DataTrack}};
trackIsrcs = new Dictionary<byte, string>();
}
Sessions = new List<Session>();
for(int i = 1; i <= Tracks.Max(t => t.TrackSession); i++)
Sessions.Add(new Session
{
SessionSequence = (ushort)i,
StartTrack = Tracks.Where(t => t.TrackSession == i).Max(t => t.TrackSequence),
EndTrack = Tracks.Where(t => t.TrackSession == i).Min(t => t.TrackSequence),
StartSector = Tracks.Where(t => t.TrackSession == i).Min(t => t.TrackStartSector),
EndSector = Tracks.Where(t => t.TrackSession == i).Max(t => t.TrackEndSector)
});
ulong currentTrackOffset = 0;
Partitions = new List<Partition>();
foreach(Track track in Tracks.OrderBy(t => t.TrackStartSector))
{
Partitions.Add(new Partition
{
Sequence = track.TrackSequence,
Type = track.TrackType.ToString(),
Name = $"Track {track.TrackSequence}",
Offset = currentTrackOffset,
Start = track.TrackStartSector,
Size = (track.TrackEndSector - track.TrackStartSector + 1) *
(ulong)track.TrackBytesPerSector,
Length = track.TrackEndSector - track.TrackStartSector + 1,
Scheme = "Optical disc track"
});
currentTrackOffset += (track.TrackEndSector - track.TrackStartSector + 1) *
(ulong)track.TrackBytesPerSector;
}
Track[] tracks = Tracks.ToArray();
for(int i = 0; i < tracks.Length; i++)
{
byte[] sector = ReadSector(tracks[i].TrackStartSector);
tracks[i].TrackBytesPerSector = sector.Length;
tracks[i].TrackRawBytesPerSector = sector.Length;
}
Tracks = tracks.ToList();
}
else
{
Tracks = null;
Sessions = null;
Partitions = null;
}
return true;
}
@@ -686,12 +814,26 @@ namespace DiscImageChef.DiscImages
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new NotImplementedException();
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
throw new FeatureNotPresentImageException("Feature not present in image");
Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track);
if(trk.TrackSequence != track)
throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image");
return ReadSector(trk.TrackStartSector + sectorAddress);
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new NotImplementedException();
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
throw new FeatureNotPresentImageException("Feature not present in image");
Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track);
if(trk.TrackSequence != track)
throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image");
return ReadSectorTag(trk.TrackStartSector + sectorAddress, tag);
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
@@ -721,12 +863,34 @@ namespace DiscImageChef.DiscImages
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new NotImplementedException();
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
throw new FeatureNotPresentImageException("Feature not present in image");
Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track);
if(trk.TrackSequence != track)
throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image");
if(trk.TrackStartSector + sectorAddress + length > trk.TrackEndSector + 1)
throw new ArgumentOutOfRangeException(nameof(length),
$"Requested more sectors ({length + sectorAddress}) than present in track ({trk.TrackEndSector - trk.TrackStartSector + 1}), won't cross tracks");
return ReadSectors(trk.TrackStartSector + sectorAddress, length);
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new NotImplementedException();
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
throw new FeatureNotPresentImageException("Feature not present in image");
Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track);
if(trk.TrackSequence != track)
throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image");
if(trk.TrackStartSector + sectorAddress + length > trk.TrackEndSector + 1)
throw new ArgumentOutOfRangeException(nameof(length),
$"Requested more sectors ({length + sectorAddress}) than present in track ({trk.TrackEndSector - trk.TrackStartSector + 1}), won't cross tracks");
return ReadSectorsTag(trk.TrackStartSector + sectorAddress, length, tag);
}
public byte[] ReadSectorLong(ulong sectorAddress)
@@ -736,7 +900,14 @@ namespace DiscImageChef.DiscImages
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new NotImplementedException();
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
throw new FeatureNotPresentImageException("Feature not present in image");
Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track);
if(trk.TrackSequence != track)
throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image");
return ReadSectorLong(trk.TrackStartSector + sectorAddress);
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
@@ -746,7 +917,18 @@ namespace DiscImageChef.DiscImages
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new NotImplementedException();
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
throw new FeatureNotPresentImageException("Feature not present in image");
Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track);
if(trk.TrackSequence != track)
throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image");
if(trk.TrackStartSector + sectorAddress + length > trk.TrackEndSector + 1)
throw new ArgumentOutOfRangeException(nameof(length),
$"Requested more sectors ({length + sectorAddress}) than present in track ({trk.TrackEndSector - trk.TrackStartSector + 1}), won't cross tracks");
return ReadSectorsLong(trk.TrackStartSector + sectorAddress, length);
}
public List<Track> GetSessionTracks(Session session)
@@ -766,7 +948,14 @@ namespace DiscImageChef.DiscImages
public bool? VerifySector(ulong sectorAddress, uint track)
{
throw new NotImplementedException();
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
throw new FeatureNotPresentImageException("Feature not present in image");
Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track);
if(trk.TrackSequence != track)
throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image");
return VerifySector(trk.TrackStartSector + sectorAddress);
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
@@ -778,7 +967,18 @@ namespace DiscImageChef.DiscImages
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
throw new NotImplementedException();
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
throw new FeatureNotPresentImageException("Feature not present in image");
Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track);
if(trk.TrackSequence != track)
throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image");
if(trk.TrackStartSector + sectorAddress + length > trk.TrackEndSector + 1)
throw new ArgumentOutOfRangeException(nameof(length),
$"Requested more sectors ({length + sectorAddress}) than present in track ({trk.TrackEndSector - trk.TrackStartSector + 1}), won't cross tracks");
return VerifySectors(trk.TrackStartSector + sectorAddress, length, out failingLbas, out unknownLbas);
}
public bool? VerifyMediaImage()
@@ -837,7 +1037,13 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("DiscImageChef format plugin", "Got a shift of {0} for {1} sectors per block",
shift, oldSectorsPerBlock);
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
imageInfo = new ImageInfo
{
MediaType = mediaType,
SectorSize = sectorSize,
Sectors = sectors,
XmlMediaType = GetXmlMediaType(mediaType)
};
try { imageStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
@@ -906,6 +1112,8 @@ namespace DiscImageChef.DiscImages
mediaTags = new Dictionary<MediaTagType, byte[]>();
checksumProvider = SHA256.Create();
deduplicationTable = new Dictionary<byte[], ulong>();
trackIsrcs = new Dictionary<byte, string>();
trackFlags = new Dictionary<byte, byte>();
IsWriting = true;
ErrorMessage = null;
@@ -970,7 +1178,7 @@ namespace DiscImageChef.DiscImages
dataType = DataType.UserData,
offset = (ulong)imageStream.Position
});
structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(currentBlockHeader));
structureBytes = new byte[Marshal.SizeOf(currentBlockHeader)];
Marshal.StructureToPtr(currentBlockHeader, structurePointer, true);
@@ -1055,6 +1263,12 @@ namespace DiscImageChef.DiscImages
public bool SetTracks(List<Track> tracks)
{
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
{
ErrorMessage = "Unsupported feature";
return false;
}
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
@@ -1245,6 +1459,72 @@ namespace DiscImageChef.DiscImages
index.Add(idxEntry);
}
if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc && Tracks != null && Tracks.Count > 0)
{
List<TrackEntry> trackEntries = new List<TrackEntry>();
foreach(Track track in Tracks)
{
trackFlags.TryGetValue((byte)track.TrackSequence, out byte flags);
trackIsrcs.TryGetValue((byte)track.TrackSequence, out string isrc);
if((flags & (int)CdFlags.DataTrack) == 0 && track.TrackType != TrackType.Audio)
flags += (byte)CdFlags.DataTrack;
trackEntries.Add(new TrackEntry
{
sequence = (byte)track.TrackSequence,
type = track.TrackType,
start = (long)track.TrackStartSector,
end = (long)track.TrackEndSector,
pregap = (long)track.TrackPregap,
session = (byte)track.TrackSession,
isrc = isrc,
flags = flags
});
}
if(trackEntries.Count > 0)
{
blockStream = new MemoryStream();
foreach(TrackEntry entry in trackEntries)
{
structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(entry));
structureBytes = new byte[Marshal.SizeOf(entry)];
Marshal.StructureToPtr(entry, structurePointer, true);
Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length);
Marshal.FreeHGlobal(structurePointer);
blockStream.Write(structureBytes, 0, structureBytes.Length);
}
Crc64Context.Data(blockStream.ToArray(), out byte[] trksCrc);
TracksHeader trkHeader = new TracksHeader
{
identifier = BlockType.TracksBlock,
entries = (ushort)trackEntries.Count,
crc64 = BitConverter.ToUInt64(trksCrc, 0)
};
DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing tracks to position {0}",
imageStream.Position);
index.Add(new IndexEntry
{
blockType = BlockType.TracksBlock,
dataType = DataType.NoData,
offset = (ulong)imageStream.Position
});
structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(trkHeader));
structureBytes = new byte[Marshal.SizeOf(trkHeader)];
Marshal.StructureToPtr(trkHeader, structurePointer, true);
Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length);
Marshal.FreeHGlobal(structurePointer);
imageStream.Write(structureBytes, 0, structureBytes.Length);
imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length);
}
}
MetadataBlock metadataBlock = new MetadataBlock();
blockStream = new MemoryStream();
blockStream.Write(new byte[Marshal.SizeOf(metadataBlock)], 0, Marshal.SizeOf(metadataBlock));
@@ -1489,18 +1769,85 @@ namespace DiscImageChef.DiscImages
return true;
}
// TODO: Implement
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not yet implemented.";
return false;
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(sectorAddress >= imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
Track track = new Track();
switch(tag)
{
case SectorTagType.CdTrackFlags:
case SectorTagType.CdTrackIsrc:
if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
{
ErrorMessage = "Incorrect tag for disk type";
return false;
}
track = Tracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
break;
}
switch(tag)
{
case SectorTagType.CdTrackFlags:
{
if(data.Length != 1)
{
ErrorMessage = "Incorrect data size for track flags";
return false;
}
trackFlags.Add((byte)track.TrackSequence, data[0]);
return true;
}
case SectorTagType.CdTrackIsrc:
{
if(data != null) trackIsrcs.Add((byte)track.TrackSequence, Encoding.UTF8.GetString(data));
return true;
}
default: throw new NotImplementedException();
}
}
// TODO: Implement
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not yet implemented.";
return false;
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
switch(tag)
{
case SectorTagType.CdTrackFlags:
case SectorTagType.CdTrackIsrc: return WriteSectorTag(data, sectorAddress, tag);
default: throw new NotImplementedException();
}
}
static XmlMediaType GetXmlMediaType(MediaType type)
@@ -1860,7 +2207,8 @@ namespace DiscImageChef.DiscImages
DeDuplicationTable = 0x48544444,
Index = 0x48584449,
GeometryBlock = 0x4D4F4547,
MetadataBlock = 0x5444545D
MetadataBlock = 0x5444545D,
TracksBlock = 0x534B5254
}
/// <summary>Header, at start of file</summary>
@@ -1975,6 +2323,7 @@ namespace DiscImageChef.DiscImages
public uint sectorsPerTrack;
}
/// <summary>Metadata block, contains metadata</summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MetadataBlock
{
@@ -2035,5 +2384,30 @@ namespace DiscImageChef.DiscImages
/// <summary>Length in bytes of the null-terminated UTF-16LE creator string</summary>
public uint driveFirmwareRevisionLength;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TracksHeader
{
/// <summary>Identifier, <see cref="BlockType.TracksBlock" /></summary>
public BlockType identifier;
/// <summary>How many entries follow this header</summary>
public ushort entries;
/// <summary>CRC64-ECMA of the block</summary>
public ulong crc64;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
struct TrackEntry
{
public byte sequence;
public TrackType type;
public long start;
public long end;
public long pregap;
public byte session;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
public string isrc;
public byte flags;
}
}
}