🐛Correct track flags on CD images.

This commit is contained in:
2018-01-01 21:32:12 +00:00
parent 4756fd8e7c
commit aa493b53e5
6 changed files with 1171 additions and 1117 deletions

View File

@@ -70,8 +70,7 @@ namespace DiscImageChef.DiscImages
const string REGEX_DISCTYPE = @"^\s*(?<type>(CD_DA|CD_ROM_XA|CD_ROM|CD_I))"; const string REGEX_DISCTYPE = @"^\s*(?<type>(CD_DA|CD_ROM_XA|CD_ROM|CD_I))";
const string REGEX_EMPHASIS = @"^\s*(?<no>NO)?\s*PRE_EMPHASIS"; const string REGEX_EMPHASIS = @"^\s*(?<no>NO)?\s*PRE_EMPHASIS";
const string REGEX_FILE_AUDIO = const string REGEX_FILE_AUDIO =
@"^\s*(AUDIO)?FILE\s*""(?<filename>.+)""\s*(#(?<base_offset>\d+))?\s*((?<start>[\d]+:[\d]+:[\d]+)|(?<start_num>\d+))\s*(?<length>[\d]+:[\d]+:[\d]+)?" @"^\s*(AUDIO)?FILE\s*""(?<filename>.+)""\s*(#(?<base_offset>\d+))?\s*((?<start>[\d]+:[\d]+:[\d]+)|(?<start_num>\d+))\s*(?<length>[\d]+:[\d]+:[\d]+)?";
;
const string REGEX_FILE_DATA = const string REGEX_FILE_DATA =
@"^\s*DATAFILE\s*""(?<filename>.+)""\s*(#(?<base_offset>\d+))?\s*(?<length>[\d]+:[\d]+:[\d]+)?"; @"^\s*DATAFILE\s*""(?<filename>.+)""\s*(#(?<base_offset>\d+))?\s*(?<length>[\d]+:[\d]+:[\d]+)?";
const string REGEX_INDEX = @"^\s*INDEX\s*(?<address>\d+:\d+:\d+)"; const string REGEX_INDEX = @"^\s*INDEX\s*(?<address>\d+:\d+:\d+)";
@@ -80,8 +79,7 @@ namespace DiscImageChef.DiscImages
const string REGEX_PREGAP = @"^\s*START\s*(?<address>\d+:\d+:\d+)?"; const string REGEX_PREGAP = @"^\s*START\s*(?<address>\d+:\d+:\d+)?";
const string REGEX_STEREO = @"^\s*(?<num>(TWO|FOUR))_CHANNEL_AUDIO"; const string REGEX_STEREO = @"^\s*(?<num>(TWO|FOUR))_CHANNEL_AUDIO";
const string REGEX_TRACK = const string REGEX_TRACK =
@"^\s*TRACK\s*(?<type>(AUDIO|MODE1_RAW|MODE1|MODE2_FORM1|MODE2_FORM2|MODE2_FORM_MIX|MODE2_RAW|MODE2))\s*(?<subchan>(RW_RAW|RW))?" @"^\s*TRACK\s*(?<type>(AUDIO|MODE1_RAW|MODE1|MODE2_FORM1|MODE2_FORM2|MODE2_FORM_MIX|MODE2_RAW|MODE2))\s*(?<subchan>(RW_RAW|RW))?";
;
const string REGEX_ZERO_AUDIO = @"^\s*SILENCE\s*(?<length>\d+:\d+:\d+)"; const string REGEX_ZERO_AUDIO = @"^\s*SILENCE\s*(?<length>\d+:\d+:\d+)";
const string REGEX_ZERO_DATA = @"^\s*ZERO\s*(?<length>\d+:\d+:\d+)"; const string REGEX_ZERO_DATA = @"^\s*ZERO\s*(?<length>\d+:\d+:\d+)";
const string REGEX_ZERO_PREGAP = @"^\s*PREGAP\s*(?<length>\d+:\d+:\d+)"; const string REGEX_ZERO_PREGAP = @"^\s*PREGAP\s*(?<length>\d+:\d+:\d+)";
@@ -109,7 +107,6 @@ namespace DiscImageChef.DiscImages
Stream imageStream; Stream imageStream;
/// <summary>Dictionary, index is track #, value is TrackFile</summary> /// <summary>Dictionary, index is track #, value is TrackFile</summary>
Dictionary<uint, ulong> offsetmap; Dictionary<uint, ulong> offsetmap;
List<Partition> partitions;
StreamReader tocStream; StreamReader tocStream;
public Cdrdao() public Cdrdao()
@@ -143,7 +140,7 @@ namespace DiscImageChef.DiscImages
public string Format => "CDRDAO tocfile"; public string Format => "CDRDAO tocfile";
public List<Partition> Partitions => partitions; public List<Partition> Partitions { get; set; }
public List<Track> Tracks public List<Track> Tracks
{ {
@@ -429,6 +426,7 @@ namespace DiscImageChef.DiscImages
currenttrack = new CdrdaoTrack {Indexes = new Dictionary<int, ulong>(), Pregap = 0}; currenttrack = new CdrdaoTrack {Indexes = new Dictionary<int, ulong>(), Pregap = 0};
nextindex = 2; nextindex = 2;
} }
currentTrackNumber++; currentTrackNumber++;
intrack = true; intrack = true;
@@ -506,7 +504,8 @@ namespace DiscImageChef.DiscImages
string[] lengthString = matchFile.Groups["length"].Value.Split(':'); string[] lengthString = matchFile.Groups["length"].Value.Split(':');
ulong nextIndexPos = ulong.Parse(lengthString[0]) * 60 * 75 + ulong nextIndexPos = ulong.Parse(lengthString[0]) * 60 * 75 +
ulong.Parse(lengthString[1]) * 75 + ulong.Parse(lengthString[2]); ulong.Parse(lengthString[1]) * 75 +
ulong.Parse(lengthString[2]);
currenttrack.Indexes.Add(nextindex, currenttrack.Indexes.Add(nextindex,
nextIndexPos + currenttrack.Pregap + currenttrack.StartSector); nextIndexPos + currenttrack.Pregap + currenttrack.StartSector);
} }
@@ -520,7 +519,8 @@ namespace DiscImageChef.DiscImages
{ {
string[] lengthString = matchPregap.Groups["address"].Value.Split(':'); string[] lengthString = matchPregap.Groups["address"].Value.Split(':');
currenttrack.Pregap = ulong.Parse(lengthString[0]) * 60 * 75 + currenttrack.Pregap = ulong.Parse(lengthString[0]) * 60 * 75 +
ulong.Parse(lengthString[1]) * 75 + ulong.Parse(lengthString[2]); ulong.Parse(lengthString[1]) * 75 +
ulong.Parse(lengthString[2]);
} }
else currenttrack.Pregap = currenttrack.Sectors; else currenttrack.Pregap = currenttrack.Sectors;
} }
@@ -567,7 +567,8 @@ namespace DiscImageChef.DiscImages
{ {
string[] startString = matchAudioFile.Groups["start"].Value.Split(':'); string[] startString = matchAudioFile.Groups["start"].Value.Split(':');
startSectors = ulong.Parse(startString[0]) * 60 * 75 + startSectors = ulong.Parse(startString[0]) * 60 * 75 +
ulong.Parse(startString[1]) * 75 + ulong.Parse(startString[2]); ulong.Parse(startString[1]) * 75 +
ulong.Parse(startString[2]);
} }
currenttrack.Trackfile.Offset += startSectors * currenttrack.Bps; currenttrack.Trackfile.Offset += startSectors * currenttrack.Bps;
@@ -576,7 +577,8 @@ namespace DiscImageChef.DiscImages
{ {
string[] lengthString = matchAudioFile.Groups["length"].Value.Split(':'); string[] lengthString = matchAudioFile.Groups["length"].Value.Split(':');
currenttrack.Sectors = ulong.Parse(lengthString[0]) * 60 * 75 + currenttrack.Sectors = ulong.Parse(lengthString[0]) * 60 * 75 +
ulong.Parse(lengthString[1]) * 75 + ulong.Parse(lengthString[2]); ulong.Parse(lengthString[1]) * 75 +
ulong.Parse(lengthString[2]);
} }
else else
currenttrack.Sectors = currenttrack.Sectors =
@@ -606,7 +608,8 @@ namespace DiscImageChef.DiscImages
{ {
string[] lengthString = matchFile.Groups["length"].Value.Split(':'); string[] lengthString = matchFile.Groups["length"].Value.Split(':');
currenttrack.Sectors = ulong.Parse(lengthString[0]) * 60 * 75 + currenttrack.Sectors = ulong.Parse(lengthString[0]) * 60 * 75 +
ulong.Parse(lengthString[1]) * 75 + ulong.Parse(lengthString[2]); ulong.Parse(lengthString[1]) * 75 +
ulong.Parse(lengthString[2]);
} }
else else
currenttrack.Sectors = currenttrack.Sectors =
@@ -673,6 +676,7 @@ namespace DiscImageChef.DiscImages
else if(line == "") // Empty line, ignore it else if(line == "") // Empty line, ignore it
{ } { }
} }
// TODO: Regex CD-TEXT SIZE_INFO // TODO: Regex CD-TEXT SIZE_INFO
/* /*
else // Non-empty unknown field else // Non-empty unknown field
@@ -693,26 +697,43 @@ namespace DiscImageChef.DiscImages
discimage.Comment = commentBuilder.ToString(); discimage.Comment = commentBuilder.ToString();
// DEBUG information // DEBUG information
DicConsole.DebugWriteLine("CDRDAO plugin", "Disc image parsing results"); DicConsole.DebugWriteLine("CDRDAO plugin",
"Disc image parsing results");
DicConsole.DebugWriteLine("CDRDAO plugin", "Disc CD-TEXT:"); DicConsole.DebugWriteLine("CDRDAO plugin", "Disc CD-TEXT:");
if(discimage.Arranger == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tArranger is not set."); if(discimage.Arranger == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tArranger is not set.");
else DicConsole.DebugWriteLine("CDRDAO plugin", "\tArranger: {0}", discimage.Arranger); else
DicConsole.DebugWriteLine("CDRDAO plugin", "\tArranger: {0}",
discimage.Arranger);
if(discimage.Composer == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tComposer is not set."); if(discimage.Composer == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tComposer is not set.");
else DicConsole.DebugWriteLine("CDRDAO plugin", "\tComposer: {0}", discimage.Composer); else
DicConsole.DebugWriteLine("CDRDAO plugin", "\tComposer: {0}",
discimage.Composer);
if(discimage.Performer == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tPerformer is not set."); if(discimage.Performer == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tPerformer is not set.");
else DicConsole.DebugWriteLine("CDRDAO plugin", "\tPerformer: {0}", discimage.Performer); else
DicConsole.DebugWriteLine("CDRDAO plugin", "\tPerformer: {0}",
discimage.Performer);
if(discimage.Songwriter == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tSongwriter is not set."); if(discimage.Songwriter == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tSongwriter is not set.");
else DicConsole.DebugWriteLine("CDRDAO plugin", "\tSongwriter: {0}", discimage.Songwriter); else
DicConsole.DebugWriteLine("CDRDAO plugin", "\tSongwriter: {0}",
discimage.Songwriter);
if(discimage.Title == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tTitle is not set."); if(discimage.Title == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tTitle is not set.");
else DicConsole.DebugWriteLine("CDRDAO plugin", "\tTitle: {0}", discimage.Title); else
DicConsole.DebugWriteLine("CDRDAO plugin", "\tTitle: {0}",
discimage.Title);
DicConsole.DebugWriteLine("CDRDAO plugin", "Disc information:"); DicConsole.DebugWriteLine("CDRDAO plugin", "Disc information:");
DicConsole.DebugWriteLine("CDRDAO plugin", "\tGuessed disk type: {0}", discimage.Disktype); DicConsole.DebugWriteLine("CDRDAO plugin", "\tGuessed disk type: {0}",
discimage.Disktype);
if(discimage.Barcode == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tBarcode not set."); if(discimage.Barcode == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tBarcode not set.");
else DicConsole.DebugWriteLine("CDRDAO plugin", "\tBarcode: {0}", discimage.Barcode); else
DicConsole.DebugWriteLine("CDRDAO plugin", "\tBarcode: {0}",
discimage.Barcode);
if(discimage.DiskId == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tDisc ID not set."); if(discimage.DiskId == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tDisc ID not set.");
else DicConsole.DebugWriteLine("CDRDAO plugin", "\tDisc ID: {0}", discimage.DiskId); else
DicConsole.DebugWriteLine("CDRDAO plugin", "\tDisc ID: {0}",
discimage.DiskId);
if(discimage.Mcn == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tMCN not set."); if(discimage.Mcn == null) DicConsole.DebugWriteLine("CDRDAO plugin", "\tMCN not set.");
else DicConsole.DebugWriteLine("CDRDAO plugin", "\tMCN: {0}", discimage.Mcn); else
DicConsole.DebugWriteLine("CDRDAO plugin", "\tMCN: {0}", discimage.Mcn);
if(string.IsNullOrEmpty(discimage.Comment)) if(string.IsNullOrEmpty(discimage.Comment))
DicConsole.DebugWriteLine("CDRDAO plugin", "\tComment not set."); DicConsole.DebugWriteLine("CDRDAO plugin", "\tComment not set.");
else DicConsole.DebugWriteLine("CDRDAO plugin", "\tComment: \"{0}\"", discimage.Comment); else DicConsole.DebugWriteLine("CDRDAO plugin", "\tComment: \"{0}\"", discimage.Comment);
@@ -774,7 +795,7 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("CDRDAO plugin", "Building offset map"); DicConsole.DebugWriteLine("CDRDAO plugin", "Building offset map");
partitions = new List<Partition>(); Partitions = new List<Partition>();
offsetmap = new Dictionary<uint, ulong>(); offsetmap = new Dictionary<uint, ulong>();
ulong byteOffset = 0; ulong byteOffset = 0;
@@ -815,16 +836,17 @@ namespace DiscImageChef.DiscImages
} }
} }
partitions.Add(partition); Partitions.Add(partition);
} }
// Print partition map // Print partition map
DicConsole.DebugWriteLine("CDRDAO plugin", "printing partition map"); DicConsole.DebugWriteLine("CDRDAO plugin", "printing partition map");
foreach(Partition partition in partitions) foreach(Partition partition in Partitions)
{ {
DicConsole.DebugWriteLine("CDRDAO plugin", "Partition sequence: {0}", partition.Sequence); DicConsole.DebugWriteLine("CDRDAO plugin", "Partition sequence: {0}", partition.Sequence);
DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition name: {0}", partition.Name); DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition name: {0}", partition.Name);
DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition description: {0}", partition.Description); DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition description: {0}",
partition.Description);
DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition type: {0}", partition.Type); DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition type: {0}", partition.Type);
DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition starting sector: {0}", partition.Start); DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition starting sector: {0}", partition.Start);
DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition sectors: {0}", partition.Length); DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition sectors: {0}", partition.Length);
@@ -842,9 +864,10 @@ namespace DiscImageChef.DiscImages
discimage.Disktype == MediaType.CDMIDI) discimage.Disktype == MediaType.CDMIDI)
imageInfo.SectorSize = 2448; // CD+G subchannels ARE user data, as CD+G are useless without them imageInfo.SectorSize = 2448; // CD+G subchannels ARE user data, as CD+G are useless without them
else if(discimage.Disktype != MediaType.CDROMXA && discimage.Disktype != MediaType.CDDA && else if(discimage.Disktype != MediaType.CDROMXA && discimage.Disktype != MediaType.CDDA &&
discimage.Disktype != MediaType.CDI && discimage.Disktype != MediaType.CDI && discimage.Disktype != MediaType.CDPLUS)
discimage.Disktype != MediaType.CDPLUS) imageInfo.SectorSize = 2048; // Only data tracks imageInfo.SectorSize = 2048; // Only data tracks
else imageInfo.SectorSize = 2352; // All others else
imageInfo.SectorSize = 2352; // All others
if(discimage.Mcn != null) imageInfo.ReadableMediaTags.Add(MediaTagType.CD_MCN); if(discimage.Mcn != null) imageInfo.ReadableMediaTags.Add(MediaTagType.CD_MCN);
@@ -1122,17 +1145,17 @@ namespace DiscImageChef.DiscImages
case SectorTagType.CdSectorSync: break; case SectorTagType.CdSectorSync: break;
case SectorTagType.CdTrackFlags: case SectorTagType.CdTrackFlags:
{ {
byte[] flags = new byte[1]; CdFlags flags = 0;
if(dicTrack.Tracktype != CDRDAO_TRACK_TYPE_AUDIO) flags[0] += 0x40; if(dicTrack.Tracktype != CDRDAO_TRACK_TYPE_AUDIO) flags |= CdFlags.DataTrack;
if(dicTrack.FlagDcp) flags[0] += 0x20; if(dicTrack.FlagDcp) flags |= CdFlags.CopyPermitted;
if(dicTrack.FlagPre) flags[0] += 0x10; if(dicTrack.FlagPre) flags |= CdFlags.PreEmphasis;
if(dicTrack.Flag_4Ch) flags[0] += 0x80; if(dicTrack.Flag_4Ch) flags |= CdFlags.FourChannel;
return flags; return new[] {(byte)flags};
} }
case SectorTagType.CdTrackIsrc: return Encoding.UTF8.GetBytes(dicTrack.Isrc); case SectorTagType.CdTrackIsrc: return Encoding.UTF8.GetBytes(dicTrack.Isrc);
default: throw new ArgumentException("Unsupported tag requested", nameof(tag)); default: throw new ArgumentException("Unsupported tag requested", nameof(tag));
@@ -1510,7 +1533,7 @@ namespace DiscImageChef.DiscImages
public string Filetype; public string Filetype;
} }
#pragma warning disable 169 #pragma warning disable 169
[SuppressMessage("ReSharper", "NotAccessedField.Local")] [SuppressMessage("ReSharper", "NotAccessedField.Local")]
struct CdrdaoTrack struct CdrdaoTrack
{ {
@@ -1590,6 +1613,6 @@ namespace DiscImageChef.DiscImages
/// <summary>Disk comment</summary> /// <summary>Disk comment</summary>
public string Comment; public string Comment;
} }
#pragma warning restore 169 #pragma warning restore 169
} }
} }

View File

@@ -1438,18 +1438,18 @@ namespace DiscImageChef.DiscImages
case SectorTagType.CdSectorSync: break; case SectorTagType.CdSectorSync: break;
case SectorTagType.CdTrackFlags: case SectorTagType.CdTrackFlags:
{ {
byte[] flags = new byte[1]; CdFlags flags = 0;
if(dicTrack.Tracktype != CDRWIN_TRACK_TYPE_AUDIO && dicTrack.Tracktype != CDRWIN_TRACK_TYPE_CDG) if(dicTrack.Tracktype != CDRWIN_TRACK_TYPE_AUDIO && dicTrack.Tracktype != CDRWIN_TRACK_TYPE_CDG)
flags[0] += 0x40; flags |= CdFlags.DataTrack;
if(dicTrack.FlagDcp) flags[0] += 0x20; if(dicTrack.FlagDcp) flags |= CdFlags.CopyPermitted;
if(dicTrack.FlagPre) flags[0] += 0x10; if(dicTrack.FlagPre) flags |= CdFlags.PreEmphasis;
if(dicTrack.Flag4ch) flags[0] += 0x80; if(dicTrack.Flag4ch) flags |= CdFlags.FourChannel;
return flags; return new[] {(byte)flags};
} }
case SectorTagType.CdTrackIsrc: return Encoding.UTF8.GetBytes(dicTrack.Isrc); case SectorTagType.CdTrackIsrc: return Encoding.UTF8.GetBytes(dicTrack.Isrc);
case SectorTagType.CdTrackText: case SectorTagType.CdTrackText:

View File

@@ -85,12 +85,10 @@ namespace DiscImageChef.DiscImages
byte[] fulltoc; byte[] fulltoc;
ImageInfo imageInfo; ImageInfo imageInfo;
Dictionary<uint, ulong> offsetmap; Dictionary<uint, ulong> offsetmap;
List<Partition> partitions;
bool scrambled; bool scrambled;
List<Session> sessions;
IFilter subFilter; IFilter subFilter;
Stream subStream; Stream subStream;
List<Track> tracks; Dictionary<byte, byte> trackFlags;
public CloneCd() public CloneCd()
{ {
@@ -123,11 +121,11 @@ namespace DiscImageChef.DiscImages
public string Format => "CloneCD"; public string Format => "CloneCD";
public List<Partition> Partitions => partitions; public List<Partition> Partitions { get; set; }
public List<Track> Tracks => tracks; public List<Track> Tracks { get; set; }
public List<Session> Sessions => sessions; public List<Session> Sessions { get; set; }
public bool Identify(IFilter imageFilter) public bool Identify(IFilter imageFilter)
{ {
@@ -368,7 +366,8 @@ namespace DiscImageChef.DiscImages
if(entSessMatch.Success) if(entSessMatch.Success)
{ {
DicConsole.DebugWriteLine("CloneCD plugin", "Found Session at line {0}", lineNumber); DicConsole.DebugWriteLine("CloneCD plugin", "Found Session at line {0}", lineNumber);
currentEntry.SessionNumber = Convert.ToByte(entSessMatch.Groups["value"].Value, 10); currentEntry.SessionNumber =
Convert.ToByte(entSessMatch.Groups["value"].Value, 10);
if(currentEntry.SessionNumber < minSession) minSession = currentEntry.SessionNumber; if(currentEntry.SessionNumber < minSession) minSession = currentEntry.SessionNumber;
if(currentEntry.SessionNumber > maxSession) maxSession = currentEntry.SessionNumber; if(currentEntry.SessionNumber > maxSession) maxSession = currentEntry.SessionNumber;
} }
@@ -484,11 +483,12 @@ namespace DiscImageChef.DiscImages
int curSessionNo = 0; int curSessionNo = 0;
Track currentTrack = new Track(); Track currentTrack = new Track();
bool firstTrackInSession = true; bool firstTrackInSession = true;
tracks = new List<Track>(); Tracks = new List<Track>();
ulong leadOutStart = 0; ulong leadOutStart = 0;
dataStream = dataFilter.GetDataForkStream(); dataStream = dataFilter.GetDataForkStream();
if(subFilter != null) subStream = subFilter.GetDataForkStream(); if(subFilter != null) subStream = subFilter.GetDataForkStream();
trackFlags = new Dictionary<byte, byte>();
foreach(FullTOC.TrackDataDescriptor descriptor in entries) foreach(FullTOC.TrackDataDescriptor descriptor in entries)
{ {
@@ -498,8 +498,9 @@ namespace DiscImageChef.DiscImages
if(!firstTrackInSession) if(!firstTrackInSession)
{ {
currentTrack.TrackEndSector = leadOutStart - 1; currentTrack.TrackEndSector = leadOutStart - 1;
tracks.Add(currentTrack); Tracks.Add(currentTrack);
} }
firstTrackInSession = true; firstTrackInSession = true;
} }
@@ -525,7 +526,7 @@ namespace DiscImageChef.DiscImages
currentTrack.TrackEndSector = currentTrack.TrackEndSector =
GetLba(descriptor.PHOUR, descriptor.PMIN, descriptor.PSEC, GetLba(descriptor.PHOUR, descriptor.PMIN, descriptor.PSEC,
descriptor.PFRAME) - 1; descriptor.PFRAME) - 1;
tracks.Add(currentTrack); Tracks.Add(currentTrack);
} }
else firstTrackInSession = false; else firstTrackInSession = false;
@@ -550,6 +551,9 @@ namespace DiscImageChef.DiscImages
currentTrack.TrackType = TrackType.Data; currentTrack.TrackType = TrackType.Data;
else currentTrack.TrackType = TrackType.Audio; else currentTrack.TrackType = TrackType.Audio;
if(!trackFlags.ContainsKey(descriptor.POINT))
trackFlags.Add(descriptor.POINT, descriptor.CONTROL);
if(subFilter != null) if(subFilter != null)
{ {
currentTrack.TrackSubchannelFile = subFilter.GetFilename(); currentTrack.TrackSubchannelFile = subFilter.GetFilename();
@@ -608,8 +612,7 @@ namespace DiscImageChef.DiscImages
currentTrack.TrackBytesPerSector = 2324; currentTrack.TrackBytesPerSector = 2324;
currentTrack.TrackType = TrackType.CdMode2Form2; currentTrack.TrackType = TrackType.CdMode2Form2;
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType if(!imageInfo.ReadableSectorTags.Contains(SectorTagType
.CdSectorSync) .CdSectorSync))
)
imageInfo.ReadableSectorTags.Add(SectorTagType imageInfo.ReadableSectorTags.Add(SectorTagType
.CdSectorSync); .CdSectorSync);
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType if(!imageInfo.ReadableSectorTags.Contains(SectorTagType
@@ -633,8 +636,7 @@ namespace DiscImageChef.DiscImages
currentTrack.TrackBytesPerSector = 2048; currentTrack.TrackBytesPerSector = 2048;
currentTrack.TrackType = TrackType.CdMode2Form1; currentTrack.TrackType = TrackType.CdMode2Form1;
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType if(!imageInfo.ReadableSectorTags.Contains(SectorTagType
.CdSectorSync) .CdSectorSync))
)
imageInfo.ReadableSectorTags.Add(SectorTagType imageInfo.ReadableSectorTags.Add(SectorTagType
.CdSectorSync); .CdSectorSync);
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType if(!imageInfo.ReadableSectorTags.Contains(SectorTagType
@@ -652,13 +654,11 @@ namespace DiscImageChef.DiscImages
imageInfo.ReadableSectorTags.Add(SectorTagType imageInfo.ReadableSectorTags.Add(SectorTagType
.CdSectorEcc); .CdSectorEcc);
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType if(!imageInfo.ReadableSectorTags.Contains(SectorTagType
.CdSectorEccP) .CdSectorEccP))
)
imageInfo.ReadableSectorTags.Add(SectorTagType imageInfo.ReadableSectorTags.Add(SectorTagType
.CdSectorEccP); .CdSectorEccP);
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType if(!imageInfo.ReadableSectorTags.Contains(SectorTagType
.CdSectorEccQ) .CdSectorEccQ))
)
imageInfo.ReadableSectorTags.Add(SectorTagType imageInfo.ReadableSectorTags.Add(SectorTagType
.CdSectorEccQ); .CdSectorEccQ);
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType if(!imageInfo.ReadableSectorTags.Contains(SectorTagType
@@ -684,8 +684,12 @@ namespace DiscImageChef.DiscImages
} }
} }
} }
else { if(imageInfo.SectorSize < 2352) imageInfo.SectorSize = 2352; } else
{
if(imageInfo.SectorSize < 2352) imageInfo.SectorSize = 2352;
} }
}
break; break;
} }
@@ -705,6 +709,7 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("CloneCD plugin", "Disc manufactured by: {0}", DicConsole.DebugWriteLine("CloneCD plugin", "Disc manufactured by: {0}",
imageInfo.MediaManufacturer); imageInfo.MediaManufacturer);
} }
break; break;
} }
@@ -722,23 +727,25 @@ namespace DiscImageChef.DiscImages
if(!firstTrackInSession) if(!firstTrackInSession)
{ {
currentTrack.TrackEndSector = leadOutStart - 1; currentTrack.TrackEndSector = leadOutStart - 1;
tracks.Add(currentTrack); Tracks.Add(currentTrack);
} }
if(subFilter != null && !imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel)) if(subFilter != null && !imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel))
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubchannel); imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubchannel);
sessions = new List<Session>(); imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackFlags);
Sessions = new List<Session>();
Session currentSession = new Session Session currentSession = new Session
{ {
EndTrack = uint.MinValue, EndTrack = uint.MinValue,
StartTrack = uint.MaxValue, StartTrack = uint.MaxValue,
SessionSequence = 1 SessionSequence = 1
}; };
partitions = new List<Partition>(); Partitions = new List<Partition>();
offsetmap = new Dictionary<uint, ulong>(); offsetmap = new Dictionary<uint, ulong>();
foreach(Track track in tracks) foreach(Track track in Tracks)
{ {
if(track.TrackSession == currentSession.SessionSequence) if(track.TrackSession == currentSession.SessionSequence)
{ {
@@ -756,7 +763,7 @@ namespace DiscImageChef.DiscImages
} }
else else
{ {
sessions.Add(currentSession); Sessions.Add(currentSession);
currentSession = new Session currentSession = new Session
{ {
EndTrack = uint.MinValue, EndTrack = uint.MinValue,
@@ -769,7 +776,8 @@ namespace DiscImageChef.DiscImages
{ {
Description = track.TrackDescription, Description = track.TrackDescription,
Size = Size =
(track.TrackEndSector - track.TrackStartSector + 1) * (ulong)track.TrackRawBytesPerSector, (track.TrackEndSector - track.TrackStartSector + 1) *
(ulong)track.TrackRawBytesPerSector,
Length = track.TrackEndSector - track.TrackStartSector + 1, Length = track.TrackEndSector - track.TrackStartSector + 1,
Sequence = track.TrackSequence, Sequence = track.TrackSequence,
Offset = track.TrackFileOffset, Offset = track.TrackFileOffset,
@@ -777,7 +785,7 @@ namespace DiscImageChef.DiscImages
Type = track.TrackType.ToString() Type = track.TrackType.ToString()
}; };
imageInfo.Sectors += partition.Length; imageInfo.Sectors += partition.Length;
partitions.Add(partition); Partitions.Add(partition);
offsetmap.Add(track.TrackSequence, track.TrackStartSector); offsetmap.Add(track.TrackSequence, track.TrackStartSector);
} }
@@ -787,21 +795,21 @@ namespace DiscImageChef.DiscImages
bool firstdata = false; bool firstdata = false;
bool audio = false; bool audio = false;
for(int i = 0; i < tracks.Count; i++) for(int i = 0; i < Tracks.Count; i++)
{ {
// First track is audio // First track is audio
firstaudio |= i == 0 && tracks[i].TrackType == TrackType.Audio; firstaudio |= i == 0 && Tracks[i].TrackType == TrackType.Audio;
// First track is data // First track is data
firstdata |= i == 0 && tracks[i].TrackType != TrackType.Audio; firstdata |= i == 0 && Tracks[i].TrackType != TrackType.Audio;
// Any non first track is data // Any non first track is data
data |= i != 0 && tracks[i].TrackType != TrackType.Audio; data |= i != 0 && Tracks[i].TrackType != TrackType.Audio;
// Any non first track is audio // Any non first track is audio
audio |= i != 0 && tracks[i].TrackType == TrackType.Audio; audio |= i != 0 && Tracks[i].TrackType == TrackType.Audio;
switch(tracks[i].TrackType) switch(Tracks[i].TrackType)
{ {
case TrackType.CdMode2Form1: case TrackType.CdMode2Form1:
case TrackType.CdMode2Form2: case TrackType.CdMode2Form2:
@@ -815,10 +823,14 @@ namespace DiscImageChef.DiscImages
cdtext = cdtMs.ToArray(); cdtext = cdtMs.ToArray();
if(!data && !firstdata) imageInfo.MediaType = MediaType.CDDA; if(!data && !firstdata) imageInfo.MediaType = MediaType.CDDA;
else if(firstaudio && data && sessions.Count > 1 && mode2) imageInfo.MediaType = MediaType.CDPLUS; else if(firstaudio && data && Sessions.Count > 1 && mode2)
else if(firstdata && audio || mode2) imageInfo.MediaType = MediaType.CDROMXA; imageInfo.MediaType = MediaType.CDPLUS;
else if(!audio) imageInfo.MediaType = MediaType.CDROM; else if(firstdata && audio || mode2)
else imageInfo.MediaType = MediaType.CD; imageInfo.MediaType = MediaType.CDROMXA;
else if(!audio)
imageInfo.MediaType = MediaType.CDROM;
else
imageInfo.MediaType = MediaType.CD;
imageInfo.Application = "CloneCD"; imageInfo.Application = "CloneCD";
imageInfo.ImageSize = (ulong)imageFilter.GetDataForkLength(); imageInfo.ImageSize = (ulong)imageFilter.GetDataForkLength();
@@ -837,19 +849,11 @@ namespace DiscImageChef.DiscImages
} }
} }
static ulong GetLba(int hour, int minute, int second, int frame)
{
return (ulong)(hour * 60 * 60 * 75 + minute * 60 * 75 + second * 75 + frame - 150);
}
public byte[] ReadDiskTag(MediaTagType tag) public byte[] ReadDiskTag(MediaTagType tag)
{ {
switch(tag) switch(tag)
{ {
case MediaTagType.CD_FullTOC: case MediaTagType.CD_FullTOC: { return fulltoc; }
{
return fulltoc;
}
case MediaTagType.CD_TEXT: case MediaTagType.CD_TEXT:
{ {
if(cdtext != null && cdtext.Length > 0) return cdtext; if(cdtext != null && cdtext.Length > 0) return cdtext;
@@ -885,7 +889,7 @@ namespace DiscImageChef.DiscImages
{ {
foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap
where sectorAddress >= kvp.Value where sectorAddress >= kvp.Value
from track in tracks from track in Tracks
where track.TrackSequence == kvp.Key where track.TrackSequence == kvp.Key
where sectorAddress <= track.TrackEndSector where sectorAddress <= track.TrackEndSector
select kvp) select kvp)
@@ -898,7 +902,7 @@ namespace DiscImageChef.DiscImages
{ {
foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap
where sectorAddress >= kvp.Value where sectorAddress >= kvp.Value
from track in tracks from track in Tracks
where track.TrackSequence == kvp.Key where track.TrackSequence == kvp.Key
where sectorAddress <= track.TrackEndSector where sectorAddress <= track.TrackEndSector
select kvp) select kvp)
@@ -911,7 +915,7 @@ namespace DiscImageChef.DiscImages
{ {
Track dicTrack = new Track {TrackSequence = 0}; Track dicTrack = new Track {TrackSequence = 0};
foreach(Track linqTrack in tracks.Where(linqTrack => linqTrack.TrackSequence == track)) foreach(Track linqTrack in Tracks.Where(linqTrack => linqTrack.TrackSequence == track))
{ {
dicTrack = linqTrack; dicTrack = linqTrack;
break; break;
@@ -988,12 +992,11 @@ namespace DiscImageChef.DiscImages
return buffer; return buffer;
} }
// TODO: Flags
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag) public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{ {
Track dicTrack = new Track {TrackSequence = 0}; Track dicTrack = new Track {TrackSequence = 0};
foreach(Track linqTrack in tracks.Where(linqTrack => linqTrack.TrackSequence == track)) foreach(Track linqTrack in Tracks.Where(linqTrack => linqTrack.TrackSequence == track))
{ {
dicTrack = linqTrack; dicTrack = linqTrack;
break; break;
@@ -1020,6 +1023,10 @@ namespace DiscImageChef.DiscImages
case SectorTagType.CdSectorHeader: case SectorTagType.CdSectorHeader:
case SectorTagType.CdSectorSubHeader: case SectorTagType.CdSectorSubHeader:
case SectorTagType.CdSectorSync: break; case SectorTagType.CdSectorSync: break;
case SectorTagType.CdTrackFlags:
return !trackFlags.TryGetValue((byte)dicTrack.TrackSequence, out byte flags)
? new[] {flags}
: new byte[1];
case SectorTagType.CdSectorSubchannel: case SectorTagType.CdSectorSubchannel:
buffer = new byte[96 * length]; buffer = new byte[96 * length];
subStream.Seek((long)(dicTrack.TrackSubchannelOffset + sectorAddress * 96), SeekOrigin.Begin); subStream.Seek((long)(dicTrack.TrackSubchannelOffset + sectorAddress * 96), SeekOrigin.Begin);
@@ -1205,11 +1212,9 @@ namespace DiscImageChef.DiscImages
} }
break; break;
case TrackType.Audio: case TrackType.Audio: { throw new ArgumentException("Unsupported tag requested", nameof(tag)); }
{ default:
throw new ArgumentException("Unsupported tag requested", nameof(tag)); throw new FeatureSupportedButNotImplementedImageException("Unsupported track type");
}
default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type");
} }
buffer = new byte[sectorSize * length]; buffer = new byte[sectorSize * length];
@@ -1243,7 +1248,7 @@ namespace DiscImageChef.DiscImages
{ {
foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap
where sectorAddress >= kvp.Value where sectorAddress >= kvp.Value
from track in tracks from track in Tracks
where track.TrackSequence == kvp.Key where track.TrackSequence == kvp.Key
where sectorAddress - kvp.Value < where sectorAddress - kvp.Value <
track.TrackEndSector - track.TrackStartSector + 1 track.TrackEndSector - track.TrackStartSector + 1
@@ -1257,7 +1262,7 @@ namespace DiscImageChef.DiscImages
{ {
Track dicTrack = new Track {TrackSequence = 0}; Track dicTrack = new Track {TrackSequence = 0};
foreach(Track linqTrack in tracks.Where(linqTrack => linqTrack.TrackSequence == track)) foreach(Track linqTrack in Tracks.Where(linqTrack => linqTrack.TrackSequence == track))
{ {
dicTrack = linqTrack; dicTrack = linqTrack;
break; break;
@@ -1280,14 +1285,14 @@ namespace DiscImageChef.DiscImages
public List<Track> GetSessionTracks(Session session) public List<Track> GetSessionTracks(Session session)
{ {
if(sessions.Contains(session)) return GetSessionTracks(session.SessionSequence); if(Sessions.Contains(session)) return GetSessionTracks(session.SessionSequence);
throw new ImageNotSupportedException("Session does not exist in disc image"); throw new ImageNotSupportedException("Session does not exist in disc image");
} }
public List<Track> GetSessionTracks(ushort session) public List<Track> GetSessionTracks(ushort session)
{ {
return tracks.Where(track => track.TrackSession == session).ToList(); return Tracks.Where(track => track.TrackSession == session).ToList();
} }
public bool? VerifySector(ulong sectorAddress) public bool? VerifySector(ulong sectorAddress)
@@ -1366,5 +1371,10 @@ namespace DiscImageChef.DiscImages
{ {
return null; return null;
} }
static ulong GetLba(int hour, int minute, int second, int frame)
{
return (ulong)(hour * 60 * 60 * 75 + minute * 60 * 75 + second * 75 + frame - 150);
}
} }
} }

View File

@@ -281,12 +281,12 @@ namespace DiscImageChef.DiscImages
public enum CdFlags : byte public enum CdFlags : byte
{ {
/// <summary>Track is quadraphonic.</summary> /// <summary>Track is quadraphonic.</summary>
FourChannel = 0x20, FourChannel = 0x08,
/// <summary>Track is non-audio (data).</summary> /// <summary>Track is non-audio (data).</summary>
DataTrack = 0x10, DataTrack = 0x04,
/// <summary>Track is copy protected.</summary> /// <summary>Track is copy protected.</summary>
CopyPrevent = 0x08, CopyPermitted = 0x02,
/// <summary>Track has pre-emphasis.</summary> /// <summary>Track has pre-emphasis.</summary>
PreEmphasis = 0x04 PreEmphasis = 0x01
} }
} }

View File

@@ -56,7 +56,6 @@ namespace DiscImageChef.DiscImages
Stream imageStream; Stream imageStream;
/// <summary>Dictionary, index is track #, value is track number, or 0 if a TOC</summary> /// <summary>Dictionary, index is track #, value is track number, or 0 if a TOC</summary>
Dictionary<uint, ulong> offsetmap; Dictionary<uint, ulong> offsetmap;
List<Partition> partitions;
public Gdi() public Gdi()
{ {
@@ -88,7 +87,7 @@ namespace DiscImageChef.DiscImages
public string Format => "Dreamcast GDI image"; public string Format => "Dreamcast GDI image";
public List<Partition> Partitions => partitions; public List<Partition> Partitions { get; set; }
public List<Track> Tracks public List<Track> Tracks
{ {
@@ -164,7 +163,10 @@ namespace DiscImageChef.DiscImages
lineNumber++; lineNumber++;
string line = gdiStream.ReadLine(); string line = gdiStream.ReadLine();
if(lineNumber == 1) { if(!int.TryParse(line, out tracks)) return false; } if(lineNumber == 1)
{
if(!int.TryParse(line, out tracks)) return false;
}
else else
{ {
Regex regexTrack = new Regex(REGEX_TRACK); Regex regexTrack = new Regex(REGEX_TRACK);
@@ -241,7 +243,7 @@ namespace DiscImageChef.DiscImages
GdiTrack currentTrack = new GdiTrack GdiTrack currentTrack = new GdiTrack
{ {
Bps = ushort.Parse(trackMatch.Groups["type"].Value), Bps = ushort.Parse(trackMatch.Groups["type"].Value),
Flags = (byte)(byte.Parse(trackMatch.Groups["flags"].Value) * 0x10), Flags = byte.Parse(trackMatch.Groups["flags"].Value),
Offset = long.Parse(trackMatch.Groups["offset"].Value), Offset = long.Parse(trackMatch.Groups["offset"].Value),
Sequence = uint.Parse(trackMatch.Groups["track"].Value), Sequence = uint.Parse(trackMatch.Groups["track"].Value),
StartSector = ulong.Parse(trackMatch.Groups["start"].Value), StartSector = ulong.Parse(trackMatch.Groups["start"].Value),
@@ -277,7 +279,7 @@ namespace DiscImageChef.DiscImages
currentTrack.HighDensity = highDensity; currentTrack.HighDensity = highDensity;
currentTrack.Tracktype = currentTrack.Tracktype =
(currentTrack.Flags & 0x40) == 0x40 ? TrackType.CdMode1 : TrackType.Audio; (currentTrack.Flags & 0x4) == 0x4 ? TrackType.CdMode1 : TrackType.Audio;
discimage.Tracks.Add(currentTrack); discimage.Tracks.Add(currentTrack);
} }
@@ -292,7 +294,8 @@ namespace DiscImageChef.DiscImages
foreach(GdiTrack trk in discimage.Tracks.Where(trk => !trk.HighDensity)) foreach(GdiTrack trk in discimage.Tracks.Where(trk => !trk.HighDensity))
{ {
if(sessions[s].StartTrack == 0) sessions[s].StartTrack = trk.Sequence; if(sessions[s].StartTrack == 0) sessions[s].StartTrack = trk.Sequence;
else if(sessions[s].StartTrack > trk.Sequence) sessions[s].StartTrack = trk.Sequence; else if(sessions[s].StartTrack > trk.Sequence)
sessions[s].StartTrack = trk.Sequence;
if(sessions[s].EndTrack < trk.Sequence) sessions[s].EndTrack = trk.Sequence; if(sessions[s].EndTrack < trk.Sequence) sessions[s].EndTrack = trk.Sequence;
@@ -309,7 +312,8 @@ namespace DiscImageChef.DiscImages
foreach(GdiTrack trk in discimage.Tracks.Where(trk => trk.HighDensity)) foreach(GdiTrack trk in discimage.Tracks.Where(trk => trk.HighDensity))
{ {
if(sessions[s].StartTrack == 0) sessions[s].StartTrack = trk.Sequence; if(sessions[s].StartTrack == 0) sessions[s].StartTrack = trk.Sequence;
else if(sessions[s].StartTrack > trk.Sequence) sessions[s].StartTrack = trk.Sequence; else if(sessions[s].StartTrack > trk.Sequence)
sessions[s].StartTrack = trk.Sequence;
if(sessions[s].EndTrack < trk.Sequence) sessions[s].EndTrack = trk.Sequence; if(sessions[s].EndTrack < trk.Sequence) sessions[s].EndTrack = trk.Sequence;
@@ -349,13 +353,13 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("GDI plugin", "\t\t{0} bytes per sector", discimage.Tracks[i].Bps); DicConsole.DebugWriteLine("GDI plugin", "\t\t{0} bytes per sector", discimage.Tracks[i].Bps);
DicConsole.DebugWriteLine("GDI plugin", "\t\tPregap: {0} sectors", discimage.Tracks[i].Pregap); DicConsole.DebugWriteLine("GDI plugin", "\t\tPregap: {0} sectors", discimage.Tracks[i].Pregap);
if((discimage.Tracks[i].Flags & 0x80) == 0x80) if((discimage.Tracks[i].Flags & 0x8) == 0x8)
DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack is flagged as quadraphonic"); DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack is flagged as quadraphonic");
if((discimage.Tracks[i].Flags & 0x40) == 0x40) if((discimage.Tracks[i].Flags & 0x4) == 0x4)
DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack is data"); DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack is data");
if((discimage.Tracks[i].Flags & 0x20) == 0x20) if((discimage.Tracks[i].Flags & 0x2) == 0x2)
DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack allows digital copy"); DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack allows digital copy");
if((discimage.Tracks[i].Flags & 0x10) == 0x10) if((discimage.Tracks[i].Flags & 0x1) == 0x1)
DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack has pre-emphasis applied"); DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack has pre-emphasis applied");
DicConsole.DebugWriteLine("GDI plugin", DicConsole.DebugWriteLine("GDI plugin",
@@ -366,7 +370,7 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("GDI plugin", "Building offset map"); DicConsole.DebugWriteLine("GDI plugin", "Building offset map");
partitions = new List<Partition>(); Partitions = new List<Partition>();
ulong byteOffset = 0; ulong byteOffset = 0;
for(int i = 0; i < discimage.Tracks.Count; i++) for(int i = 0; i < discimage.Tracks.Count; i++)
@@ -389,7 +393,7 @@ namespace DiscImageChef.DiscImages
byteOffset += partition.Size; byteOffset += partition.Size;
offsetmap.Add(discimage.Tracks[i].Sequence, partition.Start); offsetmap.Add(discimage.Tracks[i].Sequence, partition.Start);
partitions.Add(partition); Partitions.Add(partition);
} }
foreach(GdiTrack track in discimage.Tracks) imageInfo.ImageSize += track.Bps * track.Sectors; foreach(GdiTrack track in discimage.Tracks) imageInfo.ImageSize += track.Bps * track.Sectors;
@@ -399,8 +403,8 @@ namespace DiscImageChef.DiscImages
imageInfo.SectorSize = 2352; // All others imageInfo.SectorSize = 2352; // All others
foreach(GdiTrack unused in foreach(GdiTrack unused in discimage.Tracks.Where(track => (track.Flags & 0x4) == 0x4 &&
discimage.Tracks.Where(track => (track.Flags & 0x40) == 0x40 && track.Bps == 2352)) track.Bps == 2352))
{ {
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync); imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync);
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader); imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader);
@@ -470,7 +474,8 @@ namespace DiscImageChef.DiscImages
offsetmap.TryGetValue(0, out ulong transitionStart); offsetmap.TryGetValue(0, out ulong transitionStart);
if(sectorAddress >= transitionStart && sectorAddress < densitySeparationSectors + transitionStart) if(sectorAddress >= transitionStart && sectorAddress < densitySeparationSectors + transitionStart)
return ReadSectors(sectorAddress - transitionStart, length, 0); return ReadSectors(sectorAddress - transitionStart, length,
0);
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found"); throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
} }
@@ -487,7 +492,8 @@ namespace DiscImageChef.DiscImages
offsetmap.TryGetValue(0, out ulong transitionStart); offsetmap.TryGetValue(0, out ulong transitionStart);
if(sectorAddress >= transitionStart && sectorAddress < densitySeparationSectors + transitionStart) if(sectorAddress >= transitionStart && sectorAddress < densitySeparationSectors + transitionStart)
return ReadSectorsTag(sectorAddress - transitionStart, length, 0, tag); return ReadSectorsTag(sectorAddress - transitionStart, length,
0, tag);
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found"); throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
} }
@@ -545,6 +551,7 @@ namespace DiscImageChef.DiscImages
sectorSize = 2048; sectorSize = 2048;
sectorSkip = 0; sectorSkip = 0;
} }
break; break;
} }
default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type");
@@ -824,6 +831,7 @@ namespace DiscImageChef.DiscImages
sectorSize = 2048; sectorSize = 2048;
sectorSkip = 0; sectorSkip = 0;
} }
break; break;
} }
default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type");

View File

@@ -95,13 +95,10 @@ namespace DiscImageChef.DiscImages
// "END!" // "END!"
const uint NERO_END = 0x454E4421; const uint NERO_END = 0x454E4421;
bool imageNewFormat; bool imageNewFormat;
List<Partition> imagePartitions;
List<Session> imageSessions;
Stream imageStream; Stream imageStream;
ImageInfo imageInfo; ImageInfo imageInfo;
public ImageInfo Info => imageInfo; public ImageInfo Info => imageInfo;
List<Track> imageTracks;
NeroCdText neroCdtxt; NeroCdText neroCdtxt;
NeroV1Cuesheet neroCuesheetV1; NeroV1Cuesheet neroCuesheetV1;
NeroV2Cuesheet neroCuesheetV2; NeroV2Cuesheet neroCuesheetV2;
@@ -135,8 +132,8 @@ namespace DiscImageChef.DiscImages
neroSessions = new Dictionary<ushort, uint>(); neroSessions = new Dictionary<ushort, uint>();
neroTracks = new Dictionary<uint, NeroTrack>(); neroTracks = new Dictionary<uint, NeroTrack>();
offsetmap = new Dictionary<uint, ulong>(); offsetmap = new Dictionary<uint, ulong>();
imageSessions = new List<Session>(); Sessions = new List<Session>();
imagePartitions = new List<Partition>(); Partitions = new List<Partition>();
} }
// Due to .cue format, this method must parse whole file, ignoring errors (those will be thrown by OpenImage()). // Due to .cue format, this method must parse whole file, ignoring errors (those will be thrown by OpenImage()).
@@ -218,7 +215,7 @@ namespace DiscImageChef.DiscImages
ushort currentsession = 1; ushort currentsession = 1;
uint currenttrack = 1; uint currenttrack = 1;
imageTracks = new List<Track>(); Tracks = new List<Track>();
trackIsrCs = new Dictionary<uint, byte[]>(); trackIsrCs = new Dictionary<uint, byte[]>();
imageInfo.MediaType = MediaType.CD; imageInfo.MediaType = MediaType.CD;
@@ -272,7 +269,8 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].TrackNumber = {1:X2}", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].TrackNumber = {1:X2}",
i / 8 + 1, entry.TrackNumber); i / 8 + 1, entry.TrackNumber);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].IndexNumber = {1:X2}", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].IndexNumber = {1:X2}",
i / 8 + 1, entry.IndexNumber); i / 8 + 1,
entry.IndexNumber);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Dummy = {1:X4}", i / 8 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Dummy = {1:X4}", i / 8 + 1,
entry.Dummy); entry.Dummy);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Minute = {1:X2}", i / 8 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Minute = {1:X2}", i / 8 + 1,
@@ -316,7 +314,8 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].TrackNumber = {1:X2}", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].TrackNumber = {1:X2}",
i / 8 + 1, entry.TrackNumber); i / 8 + 1, entry.TrackNumber);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].IndexNumber = {1:X2}", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].IndexNumber = {1:X2}",
i / 8 + 1, entry.IndexNumber); i / 8 + 1,
entry.IndexNumber);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Dummy = {1:X2}", i / 8 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Dummy = {1:X2}", i / 8 + 1,
entry.Dummy); entry.Dummy);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].LBAStart = {1}", i / 8 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].LBAStart = {1}", i / 8 + 1,
@@ -383,7 +382,8 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Mode = {1} (0x{2:X4})", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Mode = {1} (0x{2:X4})",
i / 32 + 1, (DaoMode)entry.Mode, entry.Mode); i / 32 + 1, (DaoMode)entry.Mode, entry.Mode);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Unknown = 0x{1:X4}", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Unknown = 0x{1:X4}",
i / 32 + 1, entry.Unknown); i / 32 + 1,
entry.Unknown);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Index0 = {1}", i / 32 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Index0 = {1}", i / 32 + 1,
entry.Index0); entry.Index0);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Index1 = {1}", i / 32 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Index1 = {1}", i / 32 + 1,
@@ -475,7 +475,8 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].SectorSize = {1}", i / 32 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].SectorSize = {1}", i / 32 + 1,
entry.SectorSize); entry.SectorSize);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Mode = {1} (0x{2:X4})", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Mode = {1} (0x{2:X4})",
i / 32 + 1, (DaoMode)entry.Mode, entry.Mode); i / 32 + 1,
(DaoMode)entry.Mode, entry.Mode);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Unknown = {1:X2}", i / 32 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Unknown = {1:X2}", i / 32 + 1,
entry.Unknown); entry.Unknown);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Index0 = {1}", i / 32 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Index0 = {1}", i / 32 + 1,
@@ -550,7 +551,8 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].PackNumber = 0x{1:X2}", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].PackNumber = 0x{1:X2}",
i / 18 + 1, entry.PackNumber); i / 18 + 1, entry.PackNumber);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].BlockNumber = 0x{1:X2}", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].BlockNumber = 0x{1:X2}",
i / 18 + 1, entry.BlockNumber); i / 18 + 1,
entry.BlockNumber);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Text = \"{1}\"", i / 18 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Text = \"{1}\"", i / 18 + 1,
StringHandlers.CToString(entry.Text)); StringHandlers.CToString(entry.Text));
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].CRC = 0x{1:X4}", i / 18 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].CRC = 0x{1:X4}", i / 18 + 1,
@@ -591,7 +593,8 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Length = {1} bytes", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Length = {1} bytes",
i / 20 + 1, entry.Length); i / 20 + 1, entry.Length);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Mode = {1} (0x{2:X4})", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Mode = {1} (0x{2:X4})",
i / 20 + 1, (DaoMode)entry.Mode, entry.Mode); i / 20 + 1,
(DaoMode)entry.Mode, entry.Mode);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].StartLBA = {1}", i / 20 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].StartLBA = {1}", i / 20 + 1,
entry.StartLba); entry.StartLba);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Unknown = 0x{1:X4}", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Unknown = 0x{1:X4}",
@@ -657,11 +660,13 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Length = {1} bytes", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Length = {1} bytes",
i / 32 + 1, entry.Length); i / 32 + 1, entry.Length);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Mode = {1} (0x{2:X4})", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Mode = {1} (0x{2:X4})",
i / 32 + 1, (DaoMode)entry.Mode, entry.Mode); i / 32 + 1,
(DaoMode)entry.Mode, entry.Mode);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].StartLBA = {1}", i / 32 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].StartLBA = {1}", i / 32 + 1,
entry.StartLba); entry.StartLba);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Unknown = 0x{1:X4}", DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Unknown = 0x{1:X4}",
i / 32 + 1, entry.Unknown); i / 32 + 1,
entry.Unknown);
DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Sectors = {1}", i / 32 + 1, DicConsole.DebugWriteLine("Nero plugin", "\t _entry[{0}].Sectors = {1}", i / 32 + 1,
entry.Sectors); entry.Sectors);
@@ -852,7 +857,8 @@ namespace DiscImageChef.DiscImages
track.Indexes.Add(1, neroTrack.Index1 / neroTrack.SectorSize); track.Indexes.Add(1, neroTrack.Index1 / neroTrack.SectorSize);
track.TrackDescription = StringHandlers.CToString(neroTrack.Isrc); track.TrackDescription = StringHandlers.CToString(neroTrack.Isrc);
track.TrackEndSector = neroTrack.Length / neroTrack.SectorSize + neroTrack.StartLba - 1; track.TrackEndSector = neroTrack.Length / neroTrack.SectorSize + neroTrack.StartLba - 1;
track.TrackPregap = (neroTrack.Index1 - neroTrack.Index0) / neroTrack.SectorSize; track.TrackPregap = (neroTrack.Index1 - neroTrack.Index0) /
neroTrack.SectorSize;
track.TrackSequence = neroTrack.Sequence; track.TrackSequence = neroTrack.Sequence;
track.TrackSession = currentsession; track.TrackSession = currentsession;
track.TrackStartSector = neroTrack.StartLba; track.TrackStartSector = neroTrack.StartLba;
@@ -909,7 +915,7 @@ namespace DiscImageChef.DiscImages
track.TrackSubchannelOffset = neroTrack.Offset; track.TrackSubchannelOffset = neroTrack.Offset;
} }
imageTracks.Add(track); Tracks.Add(track);
DicConsole.DebugWriteLine("Nero plugin", "\t\t _track.TrackDescription = {0}", DicConsole.DebugWriteLine("Nero plugin", "\t\t _track.TrackDescription = {0}",
track.TrackDescription); track.TrackDescription);
@@ -936,7 +942,7 @@ namespace DiscImageChef.DiscImages
currentsessioncurrenttrack = 1; currentsessioncurrenttrack = 1;
currentsessionstruct.EndTrack = track.TrackSequence; currentsessionstruct.EndTrack = track.TrackSequence;
currentsessionstruct.EndSector = track.TrackEndSector; currentsessionstruct.EndSector = track.TrackEndSector;
imageSessions.Add(currentsessionstruct); Sessions.Add(currentsessionstruct);
} }
if(i == neroTracks.Count) if(i == neroTracks.Count)
@@ -945,7 +951,7 @@ namespace DiscImageChef.DiscImages
currentsessioncurrenttrack = 1; currentsessioncurrenttrack = 1;
currentsessionstruct.EndTrack = track.TrackSequence; currentsessionstruct.EndTrack = track.TrackSequence;
currentsessionstruct.EndSector = track.TrackEndSector; currentsessionstruct.EndSector = track.TrackEndSector;
imageSessions.Add(currentsessionstruct); Sessions.Add(currentsessionstruct);
} }
offsetmap.Add(track.TrackSequence, track.TrackStartSector); offsetmap.Add(track.TrackSequence, track.TrackStartSector);
@@ -978,7 +984,7 @@ namespace DiscImageChef.DiscImages
Type = NeroTrackModeToTrackType((DaoMode)neroTrack.Mode).ToString() Type = NeroTrackModeToTrackType((DaoMode)neroTrack.Mode).ToString()
}; };
partition.Length = partition.Size / neroTrack.SectorSize; partition.Length = partition.Size / neroTrack.SectorSize;
imagePartitions.Add(partition); Partitions.Add(partition);
partitionSequence++; partitionSequence++;
partitionStartByte += partition.Size; partitionStartByte += partition.Size;
} }
@@ -996,19 +1002,23 @@ namespace DiscImageChef.DiscImages
for(int i = 0; i < neroTracks.Count; i++) for(int i = 0; i < neroTracks.Count; i++)
{ {
// First track is audio // First track is audio
firstaudio |= i == 0 && ((DaoMode)neroTracks.ElementAt(i).Value.Mode == DaoMode.Audio || firstaudio |= i == 0 &&
((DaoMode)neroTracks.ElementAt(i).Value.Mode == DaoMode.Audio ||
(DaoMode)neroTracks.ElementAt(i).Value.Mode == DaoMode.AudioSub); (DaoMode)neroTracks.ElementAt(i).Value.Mode == DaoMode.AudioSub);
// First track is data // First track is data
firstdata |= i == 0 && (DaoMode)neroTracks.ElementAt(i).Value.Mode != DaoMode.Audio && firstdata |= i == 0 &&
(DaoMode)neroTracks.ElementAt(i).Value.Mode != DaoMode.Audio &&
(DaoMode)neroTracks.ElementAt(i).Value.Mode != DaoMode.AudioSub; (DaoMode)neroTracks.ElementAt(i).Value.Mode != DaoMode.AudioSub;
// Any non first track is data // Any non first track is data
data |= i != 0 && (DaoMode)neroTracks.ElementAt(i).Value.Mode != DaoMode.Audio && data |= i != 0 &&
(DaoMode)neroTracks.ElementAt(i).Value.Mode != DaoMode.Audio &&
(DaoMode)neroTracks.ElementAt(i).Value.Mode != DaoMode.AudioSub; (DaoMode)neroTracks.ElementAt(i).Value.Mode != DaoMode.AudioSub;
// Any non first track is audio // Any non first track is audio
audio |= i != 0 && ((DaoMode)neroTracks.ElementAt(i).Value.Mode == DaoMode.Audio || audio |= i != 0 &&
((DaoMode)neroTracks.ElementAt(i).Value.Mode == DaoMode.Audio ||
(DaoMode)neroTracks.ElementAt(i).Value.Mode == DaoMode.AudioSub); (DaoMode)neroTracks.ElementAt(i).Value.Mode == DaoMode.AudioSub);
switch((DaoMode)neroTracks.ElementAt(i).Value.Mode) switch((DaoMode)neroTracks.ElementAt(i).Value.Mode)
@@ -1023,11 +1033,14 @@ namespace DiscImageChef.DiscImages
} }
if(!data && !firstdata) imageInfo.MediaType = MediaType.CDDA; if(!data && !firstdata) imageInfo.MediaType = MediaType.CDDA;
else if(firstaudio && data && imageSessions.Count > 1 && mode2) else if(firstaudio && data && Sessions.Count > 1 && mode2)
imageInfo.MediaType = MediaType.CDPLUS; imageInfo.MediaType = MediaType.CDPLUS;
else if(firstdata && audio || mode2) imageInfo.MediaType = MediaType.CDROMXA; else if(firstdata && audio || mode2)
else if(!audio) imageInfo.MediaType = MediaType.CDROM; imageInfo.MediaType = MediaType.CDROMXA;
else imageInfo.MediaType = MediaType.CD; else if(!audio)
imageInfo.MediaType = MediaType.CDROM;
else
imageInfo.MediaType = MediaType.CD;
} }
imageInfo.XmlMediaType = XmlMediaType.OpticalDisc; imageInfo.XmlMediaType = XmlMediaType.OpticalDisc;
@@ -1077,7 +1090,7 @@ namespace DiscImageChef.DiscImages
{ {
foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap
where sectorAddress >= kvp.Value where sectorAddress >= kvp.Value
from track in imageTracks from track in Tracks
where track.TrackSequence == kvp.Key where track.TrackSequence == kvp.Key
where sectorAddress - kvp.Value < where sectorAddress - kvp.Value <
track.TrackEndSector - track.TrackStartSector track.TrackEndSector - track.TrackStartSector
@@ -1091,7 +1104,7 @@ namespace DiscImageChef.DiscImages
{ {
foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap
where sectorAddress >= kvp.Value where sectorAddress >= kvp.Value
from track in imageTracks from track in Tracks
where track.TrackSequence == kvp.Key where track.TrackSequence == kvp.Key
where sectorAddress - kvp.Value < where sectorAddress - kvp.Value <
track.TrackEndSector - track.TrackStartSector track.TrackEndSector - track.TrackStartSector
@@ -1225,7 +1238,7 @@ namespace DiscImageChef.DiscImages
flags[0] = 0x00; flags[0] = 0x00;
if((DaoMode)dicTrack.Mode != DaoMode.Audio && (DaoMode)dicTrack.Mode != DaoMode.AudioSub) if((DaoMode)dicTrack.Mode != DaoMode.Audio && (DaoMode)dicTrack.Mode != DaoMode.AudioSub)
flags[0] += 0x40; flags[0] += 0x4;
return flags; return flags;
} }
@@ -1433,7 +1446,7 @@ namespace DiscImageChef.DiscImages
{ {
foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap foreach(KeyValuePair<uint, ulong> kvp in from kvp in offsetmap
where sectorAddress >= kvp.Value where sectorAddress >= kvp.Value
from track in imageTracks from track in Tracks
where track.TrackSequence == kvp.Key where track.TrackSequence == kvp.Key
where sectorAddress - kvp.Value < where sectorAddress - kvp.Value <
track.TrackEndSector - track.TrackStartSector track.TrackEndSector - track.TrackStartSector
@@ -1518,9 +1531,9 @@ namespace DiscImageChef.DiscImages
public string Format => "Nero Burning ROM"; public string Format => "Nero Burning ROM";
public List<Partition> Partitions => imagePartitions; public List<Partition> Partitions { get; }
public List<Track> Tracks => imageTracks; public List<Track> Tracks { get; set; }
public List<Track> GetSessionTracks(Session session) public List<Track> GetSessionTracks(Session session)
{ {
@@ -1529,10 +1542,10 @@ namespace DiscImageChef.DiscImages
public List<Track> GetSessionTracks(ushort session) public List<Track> GetSessionTracks(ushort session)
{ {
return imageTracks.Where(track => track.TrackSession == session).ToList(); return Tracks.Where(track => track.TrackSession == session).ToList();
} }
public List<Session> Sessions => imageSessions; public List<Session> Sessions { get; }
public bool? VerifySector(ulong sectorAddress) public bool? VerifySector(ulong sectorAddress)
{ {