mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 11:14:25 +00:00
1602 lines
65 KiB
C#
1602 lines
65 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Filename : Read.cs
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// Component : Disk image plugins.
|
|
//
|
|
// --[ Description ] ----------------------------------------------------------
|
|
//
|
|
// Reads cdrdao cuesheets (toc/bin).
|
|
//
|
|
// --[ License ] --------------------------------------------------------------
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as
|
|
// published by the Free Software Foundation; either version 2.1 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful, but
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
// Copyright © 2011-2025 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using Aaru.CommonTypes;
|
|
using Aaru.CommonTypes.Enums;
|
|
using Aaru.CommonTypes.Interfaces;
|
|
using Aaru.CommonTypes.Structs;
|
|
using Aaru.Decoders.CD;
|
|
using Aaru.Logging;
|
|
using Session = Aaru.CommonTypes.Structs.Session;
|
|
|
|
namespace Aaru.Images;
|
|
|
|
public sealed partial class Cdrdao
|
|
{
|
|
#region IWritableOpticalImage Members
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber Open(IFilter imageFilter)
|
|
{
|
|
if(imageFilter == null) return ErrorNumber.NoSuchFile;
|
|
|
|
_cdrdaoFilter = imageFilter;
|
|
|
|
try
|
|
{
|
|
imageFilter.GetDataForkStream().Seek(0, SeekOrigin.Begin);
|
|
_tocStream = new StreamReader(imageFilter.GetDataForkStream());
|
|
var inTrack = false;
|
|
|
|
// Initialize all RegExs
|
|
Regex regexComment = CommentRegex();
|
|
Regex regexDiskType = DiscTypeRegex();
|
|
Regex regexMcn = McnRegex();
|
|
Regex regexTrack = TrackRegex();
|
|
Regex regexCopy = CopyRegex();
|
|
Regex regexEmphasis = EmphasisRegex();
|
|
Regex regexStereo = StereoRegex();
|
|
Regex regexIsrc = IsrcRegex();
|
|
Regex regexIndex = IndexRegex();
|
|
Regex regexPregap = PregapRegex();
|
|
Regex regexZeroPregap = ZeroPregapRegex();
|
|
Regex regexZeroData = ZeroDataRegex();
|
|
Regex regexZeroAudio = ZeroAudioRegex();
|
|
Regex regexAudioFile = FileAudioRegex();
|
|
Regex regexFile = FileDataRegex();
|
|
Regex regexTitle = TitleRegex();
|
|
Regex regexPerformer = PerformerRegex();
|
|
Regex regexSongwriter = SongwriterRegex();
|
|
Regex regexComposer = ComposerRegex();
|
|
Regex regexArranger = ArrangerRegex();
|
|
Regex regexMessage = MessageRegex();
|
|
Regex regexDiscId = DiscIdRegex();
|
|
Regex regexUpc = UpcRegex();
|
|
Regex regexCdText = CdTextRegex();
|
|
Regex regexLanguage = LanguageRegex();
|
|
Regex regexClosure = ClosureRegex();
|
|
Regex regexLanguageMap = LanguageMapRegex();
|
|
Regex regexLanguageMapping = LanguageMappingRegex();
|
|
|
|
// Initialize all RegEx matches
|
|
Match matchComment;
|
|
Match matchDiskType;
|
|
|
|
// Initialize disc
|
|
_discimage = new CdrdaoDisc
|
|
{
|
|
Tracks = [],
|
|
Comment = ""
|
|
};
|
|
|
|
var currentTrack = new CdrdaoTrack();
|
|
uint currentTrackNumber = 0;
|
|
currentTrack.Indexes = new Dictionary<int, ulong>();
|
|
currentTrack.Pregap = 0;
|
|
ulong currentSector = 0;
|
|
var nextIndex = 2;
|
|
var commentBuilder = new StringBuilder();
|
|
|
|
_tocStream = new StreamReader(_cdrdaoFilter.GetDataForkStream());
|
|
string line;
|
|
var lineNumber = 0;
|
|
|
|
while(_tocStream.Peek() >= 0)
|
|
{
|
|
lineNumber++;
|
|
line = _tocStream.ReadLine();
|
|
|
|
matchDiskType = regexDiskType.Match(line ?? "");
|
|
matchComment = regexComment.Match(line);
|
|
|
|
// Skip comments at start of file
|
|
if(matchComment.Success) continue;
|
|
|
|
if(!matchDiskType.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Not_a_CDRDAO_TOC_or_TOC_type_not_in_line_0, lineNumber);
|
|
|
|
return ErrorNumber.InvalidArgument;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
_tocStream = new StreamReader(_cdrdaoFilter.GetDataForkStream());
|
|
lineNumber = 0;
|
|
|
|
_tocStream.BaseStream.Position = 0;
|
|
|
|
while(_tocStream.Peek() >= 0)
|
|
{
|
|
lineNumber++;
|
|
line = _tocStream.ReadLine();
|
|
|
|
matchComment = regexComment.Match(line ?? "");
|
|
matchDiskType = regexDiskType.Match(line);
|
|
Match matchMcn = regexMcn.Match(line);
|
|
Match matchTrack = regexTrack.Match(line);
|
|
Match matchCopy = regexCopy.Match(line);
|
|
Match matchEmphasis = regexEmphasis.Match(line);
|
|
Match matchStereo = regexStereo.Match(line);
|
|
Match matchIsrc = regexIsrc.Match(line);
|
|
Match matchIndex = regexIndex.Match(line);
|
|
Match matchPregap = regexPregap.Match(line);
|
|
Match matchZeroPregap = regexZeroPregap.Match(line);
|
|
Match matchZeroData = regexZeroData.Match(line);
|
|
Match matchZeroAudio = regexZeroAudio.Match(line);
|
|
Match matchAudioFile = regexAudioFile.Match(line);
|
|
Match matchFile = regexFile.Match(line);
|
|
Match matchTitle = regexTitle.Match(line);
|
|
Match matchPerformer = regexPerformer.Match(line);
|
|
Match matchSongwriter = regexSongwriter.Match(line);
|
|
Match matchComposer = regexComposer.Match(line);
|
|
Match matchArranger = regexArranger.Match(line);
|
|
Match matchMessage = regexMessage.Match(line);
|
|
Match matchDiscId = regexDiscId.Match(line);
|
|
Match matchUpc = regexUpc.Match(line);
|
|
Match matchCdText = regexCdText.Match(line);
|
|
Match matchLanguage = regexLanguage.Match(line);
|
|
Match matchClosure = regexClosure.Match(line);
|
|
Match matchLanguageMap = regexLanguageMap.Match(line);
|
|
Match matchLanguageMapping = regexLanguageMapping.Match(line);
|
|
|
|
if(matchComment.Success)
|
|
{
|
|
// Ignore "// Track X" comments
|
|
if(matchComment.Groups["comment"].Value.StartsWith(" Track ", StringComparison.Ordinal)) continue;
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_comment_1_at_line_0,
|
|
lineNumber,
|
|
matchComment.Groups["comment"].Value.Trim());
|
|
|
|
commentBuilder.AppendLine(matchComment.Groups["comment"].Value.Trim());
|
|
}
|
|
else if(matchDiskType.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_1_at_line_0,
|
|
lineNumber,
|
|
matchDiskType.Groups["type"].Value);
|
|
|
|
_discimage.Disktypestr = matchDiskType.Groups["type"].Value;
|
|
|
|
_discimage.Disktype = matchDiskType.Groups["type"].Value switch
|
|
{
|
|
"CD_DA" => MediaType.CDDA,
|
|
"CD_ROM" => MediaType.CDROM,
|
|
"CD_ROM_XA" => MediaType.CDROMXA,
|
|
"CD_I" => MediaType.CDI,
|
|
_ => MediaType.CD
|
|
};
|
|
}
|
|
else if(matchMcn.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_CATALOG_1_at_line_0,
|
|
lineNumber,
|
|
matchMcn.Groups["catalog"].Value);
|
|
|
|
_discimage.Mcn = matchMcn.Groups["catalog"].Value;
|
|
}
|
|
else if(matchTrack.Success)
|
|
{
|
|
if(matchTrack.Groups["subchan"].Value == "")
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_TRACK_type_1_with_no_subchannel_at_line_0,
|
|
lineNumber,
|
|
matchTrack.Groups["type"].Value);
|
|
}
|
|
else
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_TRACK_type_1_subchannel_2_at_line_0,
|
|
lineNumber,
|
|
matchTrack.Groups["type"].Value,
|
|
matchTrack.Groups["subchan"].Value);
|
|
}
|
|
|
|
if(inTrack)
|
|
{
|
|
currentSector += currentTrack.Sectors;
|
|
|
|
if(currentTrack.Pregap != currentTrack.Sectors)
|
|
currentTrack.Indexes.TryAdd(1, currentTrack.StartSector + currentTrack.Pregap);
|
|
|
|
_discimage.Tracks.Add(currentTrack);
|
|
|
|
currentTrack = new CdrdaoTrack
|
|
{
|
|
Indexes = new Dictionary<int, ulong>(),
|
|
Pregap = 0
|
|
};
|
|
|
|
nextIndex = 2;
|
|
}
|
|
|
|
currentTrackNumber++;
|
|
inTrack = true;
|
|
|
|
switch(matchTrack.Groups["type"].Value)
|
|
{
|
|
case "AUDIO":
|
|
case "MODE1_RAW":
|
|
case "MODE2_RAW":
|
|
currentTrack.Bps = 2352;
|
|
|
|
break;
|
|
case "MODE1":
|
|
case "MODE2_FORM1":
|
|
currentTrack.Bps = 2048;
|
|
|
|
break;
|
|
case "MODE2_FORM2":
|
|
currentTrack.Bps = 2324;
|
|
|
|
break;
|
|
case "MODE2":
|
|
case "MODE2_FORM_MIX":
|
|
currentTrack.Bps = 2336;
|
|
|
|
break;
|
|
default:
|
|
{
|
|
AaruLogging.Error(string.Format(Localization.Track_mode_0_is_unsupported,
|
|
matchTrack.Groups["type"].Value));
|
|
|
|
return ErrorNumber.NotSupported;
|
|
}
|
|
}
|
|
|
|
switch(matchTrack.Groups["subchan"].Value)
|
|
{
|
|
case "":
|
|
break;
|
|
case "RW":
|
|
currentTrack.Packedsubchannel = true;
|
|
goto case "RW_RAW";
|
|
case "RW_RAW":
|
|
currentTrack.Subchannel = true;
|
|
|
|
break;
|
|
default:
|
|
{
|
|
AaruLogging.Error(string.Format(Localization.Track_subchannel_mode_0_is_unsupported,
|
|
matchTrack.Groups["subchan"].Value));
|
|
|
|
return ErrorNumber.NotSupported;
|
|
}
|
|
}
|
|
|
|
currentTrack.Tracktype = matchTrack.Groups["type"].Value;
|
|
|
|
currentTrack.Sequence = currentTrackNumber;
|
|
currentTrack.StartSector = currentSector;
|
|
}
|
|
else if(matchCopy.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_1_COPY_at_line_0,
|
|
lineNumber,
|
|
matchCopy.Groups["no"].Value);
|
|
|
|
currentTrack.FlagDcp |= inTrack && matchCopy.Groups["no"].Value == "";
|
|
}
|
|
else if(matchEmphasis.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_1_PRE_EMPHASIS_at_line_0,
|
|
lineNumber,
|
|
matchEmphasis.Groups["no"].Value);
|
|
|
|
currentTrack.FlagPre |= inTrack && matchEmphasis.Groups["no"].Value == "";
|
|
}
|
|
else if(matchStereo.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_1_CHANNEL_AUDIO_at_line_0,
|
|
lineNumber,
|
|
matchStereo.Groups["num"].Value);
|
|
|
|
currentTrack.Flag4Ch |= inTrack && matchStereo.Groups["num"].Value == "FOUR";
|
|
}
|
|
else if(matchIsrc.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_ISRC_1_at_line_0,
|
|
lineNumber,
|
|
matchIsrc.Groups["isrc"].Value);
|
|
|
|
if(inTrack) currentTrack.Isrc = matchIsrc.Groups["isrc"].Value;
|
|
}
|
|
else if(matchIndex.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_INDEX_1_at_line_0,
|
|
lineNumber,
|
|
matchIndex.Groups["address"].Value);
|
|
|
|
string[] lengthString = matchFile.Groups["length"].Value.Split(':');
|
|
|
|
ulong nextIndexPos = ulong.Parse(lengthString[0]) * 60 * 75 +
|
|
ulong.Parse(lengthString[1]) * 75 +
|
|
ulong.Parse(lengthString[2]);
|
|
|
|
currentTrack.Indexes.Add(nextIndex, nextIndexPos + currentTrack.Pregap + currentTrack.StartSector);
|
|
}
|
|
else if(matchPregap.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_START_1_at_line_0,
|
|
lineNumber,
|
|
matchPregap.Groups["address"].Value);
|
|
|
|
currentTrack.Indexes.Add(0, currentTrack.StartSector);
|
|
|
|
if(matchPregap.Groups["address"].Value != "")
|
|
{
|
|
string[] lengthString = matchPregap.Groups["address"].Value.Split(':');
|
|
|
|
currentTrack.Pregap = ulong.Parse(lengthString[0]) * 60 * 75 +
|
|
ulong.Parse(lengthString[1]) * 75 +
|
|
ulong.Parse(lengthString[2]);
|
|
}
|
|
else
|
|
currentTrack.Pregap = currentTrack.Sectors;
|
|
}
|
|
else if(matchZeroPregap.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_PREGAP_1_at_line_0,
|
|
lineNumber,
|
|
matchZeroPregap.Groups["length"].Value);
|
|
|
|
currentTrack.Indexes.Add(0, currentTrack.StartSector);
|
|
string[] lengthString = matchZeroPregap.Groups["length"].Value.Split(':');
|
|
|
|
currentTrack.Pregap = ulong.Parse(lengthString[0]) * 60 * 75 +
|
|
ulong.Parse(lengthString[1]) * 75 +
|
|
ulong.Parse(lengthString[2]);
|
|
}
|
|
else if(matchZeroData.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_ZERO_1_at_line_0,
|
|
lineNumber,
|
|
matchZeroData.Groups["length"].Value);
|
|
}
|
|
else if(matchZeroAudio.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_SILENCE_1_at_line_0,
|
|
lineNumber,
|
|
matchZeroAudio.Groups["length"].Value);
|
|
}
|
|
else
|
|
{
|
|
if(matchAudioFile.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_AUDIOFILE_1_at_line_0,
|
|
lineNumber,
|
|
matchAudioFile.Groups["filename"].Value);
|
|
|
|
|
|
currentTrack.Trackfile = new CdrdaoTrackFile
|
|
{
|
|
Datafilter =
|
|
PluginRegister.Singleton.GetFilter(Path.Combine(imageFilter.ParentFolder,
|
|
matchAudioFile.Groups["filename"]
|
|
.Value)),
|
|
Datafile = matchAudioFile.Groups["filename"].Value,
|
|
Offset = matchAudioFile.Groups["base_offset"].Value != ""
|
|
? ulong.Parse(matchAudioFile.Groups["base_offset"].Value)
|
|
: 0,
|
|
Filetype = "BINARY",
|
|
Sequence = currentTrackNumber
|
|
};
|
|
|
|
ulong startSectors = 0;
|
|
|
|
if(matchAudioFile.Groups["start"].Value != "")
|
|
{
|
|
string[] startString = matchAudioFile.Groups["start"].Value.Split(':');
|
|
|
|
startSectors = ulong.Parse(startString[0]) * 60 * 75 +
|
|
ulong.Parse(startString[1]) * 75 +
|
|
ulong.Parse(startString[2]);
|
|
}
|
|
|
|
currentTrack.Trackfile.Offset += startSectors * currentTrack.Bps;
|
|
|
|
if(matchAudioFile.Groups["length"].Value != "")
|
|
{
|
|
string[] lengthString = matchAudioFile.Groups["length"].Value.Split(':');
|
|
|
|
currentTrack.Sectors = ulong.Parse(lengthString[0]) * 60 * 75 +
|
|
ulong.Parse(lengthString[1]) * 75 +
|
|
ulong.Parse(lengthString[2]);
|
|
}
|
|
else
|
|
{
|
|
currentTrack.Sectors =
|
|
((ulong)currentTrack.Trackfile.Datafilter.DataForkLength -
|
|
currentTrack.Trackfile.Offset) /
|
|
currentTrack.Bps;
|
|
}
|
|
}
|
|
else if(matchFile.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_DATAFILE_1_at_line_0,
|
|
lineNumber,
|
|
matchFile.Groups["filename"].Value);
|
|
|
|
currentTrack.Trackfile = new CdrdaoTrackFile
|
|
{
|
|
Datafilter =
|
|
PluginRegister.Singleton.GetFilter(Path.Combine(imageFilter.ParentFolder,
|
|
matchFile.Groups["filename"]
|
|
.Value)),
|
|
Datafile = matchAudioFile.Groups["filename"].Value,
|
|
Offset = matchFile.Groups["base_offset"].Value != ""
|
|
? ulong.Parse(matchFile.Groups["base_offset"].Value)
|
|
: 0,
|
|
Filetype = "BINARY",
|
|
Sequence = currentTrackNumber
|
|
};
|
|
|
|
if(matchFile.Groups["length"].Value != "")
|
|
{
|
|
string[] lengthString = matchFile.Groups["length"].Value.Split(':');
|
|
|
|
currentTrack.Sectors = ulong.Parse(lengthString[0]) * 60 * 75 +
|
|
ulong.Parse(lengthString[1]) * 75 +
|
|
ulong.Parse(lengthString[2]);
|
|
}
|
|
else
|
|
{
|
|
currentTrack.Sectors =
|
|
((ulong)currentTrack.Trackfile.Datafilter.DataForkLength -
|
|
currentTrack.Trackfile.Offset) /
|
|
currentTrack.Bps;
|
|
}
|
|
}
|
|
else if(matchTitle.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_TITLE_1_at_line_0,
|
|
lineNumber,
|
|
matchTitle.Groups["title"].Value);
|
|
|
|
if(inTrack)
|
|
currentTrack.Title = matchTitle.Groups["title"].Value;
|
|
else
|
|
_discimage.Title = matchTitle.Groups["title"].Value;
|
|
}
|
|
else if(matchPerformer.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_PERFORMER_1_at_line_0,
|
|
lineNumber,
|
|
matchPerformer.Groups["performer"].Value);
|
|
|
|
if(inTrack)
|
|
currentTrack.Performer = matchPerformer.Groups["performer"].Value;
|
|
else
|
|
_discimage.Performer = matchPerformer.Groups["performer"].Value;
|
|
}
|
|
else if(matchSongwriter.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_SONGWRITER_1_at_line_0,
|
|
lineNumber,
|
|
matchSongwriter.Groups["songwriter"].Value);
|
|
|
|
if(inTrack)
|
|
currentTrack.Songwriter = matchSongwriter.Groups["songwriter"].Value;
|
|
else
|
|
_discimage.Songwriter = matchSongwriter.Groups["songwriter"].Value;
|
|
}
|
|
else if(matchComposer.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_COMPOSER_1_at_line_0,
|
|
lineNumber,
|
|
matchComposer.Groups["composer"].Value);
|
|
|
|
if(inTrack)
|
|
currentTrack.Composer = matchComposer.Groups["composer"].Value;
|
|
else
|
|
_discimage.Composer = matchComposer.Groups["composer"].Value;
|
|
}
|
|
else if(matchArranger.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_ARRANGER_1_at_line_0,
|
|
lineNumber,
|
|
matchArranger.Groups["arranger"].Value);
|
|
|
|
if(inTrack)
|
|
currentTrack.Arranger = matchArranger.Groups["arranger"].Value;
|
|
else
|
|
_discimage.Arranger = matchArranger.Groups["arranger"].Value;
|
|
}
|
|
else if(matchMessage.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_MESSAGE_1_at_line_0,
|
|
lineNumber,
|
|
matchMessage.Groups["message"].Value);
|
|
|
|
if(inTrack)
|
|
currentTrack.Message = matchMessage.Groups["message"].Value;
|
|
else
|
|
_discimage.Message = matchMessage.Groups["message"].Value;
|
|
}
|
|
else if(matchDiscId.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_DISC_ID_1_at_line_0,
|
|
lineNumber,
|
|
matchDiscId.Groups["discid"].Value);
|
|
|
|
if(!inTrack) _discimage.DiskId = matchDiscId.Groups["discid"].Value;
|
|
}
|
|
else if(matchUpc.Success)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Found_UPC_EAN_1_at_line_0,
|
|
lineNumber,
|
|
matchUpc.Groups["catalog"].Value);
|
|
|
|
if(!inTrack) _discimage.Barcode = matchUpc.Groups["catalog"].Value;
|
|
}
|
|
|
|
// Ignored fields
|
|
else if(matchCdText.Success ||
|
|
matchLanguage.Success ||
|
|
matchClosure.Success ||
|
|
matchLanguageMap.Success ||
|
|
matchLanguageMapping.Success) {}
|
|
else if(line == "") // Empty line, ignore it
|
|
{}
|
|
}
|
|
|
|
// TODO: Regex CD-TEXT SIZE_INFO
|
|
/*
|
|
else // Non-empty unknown field
|
|
{
|
|
AaruLogging.ErrorWriteLine(string.Format("Found unknown field defined at line {0}: \"{1}\"", line, _line));
|
|
return ErrorNumber.NotSupported;
|
|
}
|
|
*/
|
|
}
|
|
|
|
if(currentTrack.Sequence != 0)
|
|
{
|
|
if(currentTrack.Pregap != currentTrack.Sectors)
|
|
currentTrack.Indexes.TryAdd(1, currentTrack.StartSector + currentTrack.Pregap);
|
|
|
|
_discimage.Tracks.Add(currentTrack);
|
|
}
|
|
|
|
_discimage.Comment = commentBuilder.ToString();
|
|
|
|
// DEBUG information
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Disc_image_parsing_results);
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Disc_CD_TEXT);
|
|
|
|
if(_discimage.Arranger == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Arranger_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Arranger_0, _discimage.Arranger);
|
|
|
|
if(_discimage.Composer == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Composer_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Composer_0, _discimage.Composer);
|
|
|
|
if(_discimage.Performer == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Performer_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Performer_0, _discimage.Performer);
|
|
|
|
if(_discimage.Songwriter == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Songwriter_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Songwriter_0, _discimage.Songwriter);
|
|
|
|
if(_discimage.Title == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Title_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Title_0, _discimage.Title);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Disc_information);
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Guessed_disk_type_0, _discimage.Disktype);
|
|
|
|
if(_discimage.Barcode == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Barcode_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Barcode_0, _discimage.Barcode);
|
|
|
|
if(_discimage.DiskId == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Disc_ID_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Disc_ID_0, _discimage.DiskId);
|
|
|
|
if(_discimage.Mcn == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.MCN_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.MCN_0, _discimage.Mcn);
|
|
|
|
if(string.IsNullOrEmpty(_discimage.Comment))
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Comment_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Comment_0, _discimage.Comment);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Track_information);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Disc_contains_0_tracks, _discimage.Tracks.Count);
|
|
|
|
for(var i = 0; i < _discimage.Tracks.Count; i++)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Track_0_information, _discimage.Tracks[i].Sequence);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization._0_bytes_per_sector, _discimage.Tracks[i].Bps);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Pregap_0_sectors, _discimage.Tracks[i].Pregap);
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
"\t\t" + Localization.Data_0_sectors_starting_at_sector_1,
|
|
_discimage.Tracks[i].Sectors,
|
|
_discimage.Tracks[i].StartSector);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Postgap_0_sectors, _discimage.Tracks[i].Postgap);
|
|
|
|
if(_discimage.Tracks[i].Flag4Ch)
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Track_is_flagged_as_quadraphonic);
|
|
|
|
if(_discimage.Tracks[i].FlagDcp)
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Track_allows_digital_copy);
|
|
|
|
if(_discimage.Tracks[i].FlagPre)
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Track_has_pre_emphasis_applied);
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
"\t\t" + Localization.Track_resides_in_file_0_type_defined_as_1_starting_at_byte_2,
|
|
_discimage.Tracks[i].Trackfile.Datafilter.Filename,
|
|
_discimage.Tracks[i].Trackfile.Filetype,
|
|
_discimage.Tracks[i].Trackfile.Offset);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Indexes);
|
|
|
|
foreach(KeyValuePair<int, ulong> kvp in _discimage.Tracks[i].Indexes)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
"\t\t\t" + Localization.Index_0_starts_at_sector_1,
|
|
kvp.Key,
|
|
kvp.Value);
|
|
}
|
|
|
|
if(_discimage.Tracks[i].Isrc == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.ISRC_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.ISRC_0, _discimage.Tracks[i].Isrc);
|
|
|
|
if(_discimage.Tracks[i].Arranger == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Arranger_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Arranger_0, _discimage.Tracks[i].Arranger);
|
|
|
|
if(_discimage.Tracks[i].Composer == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Composer_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Composer_0, _discimage.Tracks[i].Composer);
|
|
|
|
if(_discimage.Tracks[i].Performer == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Performer_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Performer_0, _discimage.Tracks[i].Performer);
|
|
|
|
if(_discimage.Tracks[i].Songwriter == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Songwriter_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Songwriter_0, _discimage.Tracks[i].Songwriter);
|
|
|
|
if(_discimage.Tracks[i].Title == null)
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Title_is_not_set);
|
|
else
|
|
AaruLogging.Debug(MODULE_NAME, "\t\t" + Localization.Title_0, _discimage.Tracks[i].Title);
|
|
}
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Building_offset_map);
|
|
|
|
Partitions = [];
|
|
_offsetmap = new Dictionary<uint, ulong>();
|
|
|
|
ulong byteOffset = 0;
|
|
ulong partitionSequence = 0;
|
|
|
|
for(var i = 0; i < _discimage.Tracks.Count; i++)
|
|
{
|
|
if(_discimage.Tracks[i].Sequence == 1 && i != 0)
|
|
{
|
|
AaruLogging.Error(Localization.Unordered_tracks);
|
|
|
|
return ErrorNumber.NotSupported;
|
|
}
|
|
|
|
// Index 01
|
|
var partition = new Partition
|
|
{
|
|
Description = string.Format(Localization.Track_0, _discimage.Tracks[i].Sequence),
|
|
Name = _discimage.Tracks[i].Title,
|
|
Start = _discimage.Tracks[i].StartSector,
|
|
Size = _discimage.Tracks[i].Sectors * _discimage.Tracks[i].Bps,
|
|
Length = _discimage.Tracks[i].Sectors,
|
|
Sequence = partitionSequence,
|
|
Offset = byteOffset,
|
|
Type = _discimage.Tracks[i].Tracktype
|
|
};
|
|
|
|
byteOffset += partition.Size;
|
|
partitionSequence++;
|
|
|
|
if(!_offsetmap.ContainsKey(_discimage.Tracks[i].Sequence))
|
|
_offsetmap.Add(_discimage.Tracks[i].Sequence, partition.Start);
|
|
else
|
|
{
|
|
_offsetmap.TryGetValue(_discimage.Tracks[i].Sequence, out ulong oldStart);
|
|
|
|
if(partition.Start < oldStart)
|
|
{
|
|
_offsetmap.Remove(_discimage.Tracks[i].Sequence);
|
|
_offsetmap.Add(_discimage.Tracks[i].Sequence, partition.Start);
|
|
}
|
|
}
|
|
|
|
Partitions.Add(partition);
|
|
}
|
|
|
|
// Print partition map
|
|
AaruLogging.Debug(MODULE_NAME, Localization.printing_partition_map);
|
|
|
|
foreach(Partition partition in Partitions)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Partition_sequence_0, partition.Sequence);
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Partition_name_0, partition.Name);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Partition_description_0, partition.Description);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Partition_type_0, partition.Type);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Partition_starting_sector_0, partition.Start);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Partition_sectors_0, partition.Length);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Partition_starting_offset_0, partition.Offset);
|
|
|
|
AaruLogging.Debug(MODULE_NAME, "\t" + Localization.Partition_size_in_bytes_0, partition.Size);
|
|
}
|
|
|
|
foreach(CdrdaoTrack track in _discimage.Tracks)
|
|
{
|
|
_imageInfo.ImageSize += track.Bps * track.Sectors;
|
|
_imageInfo.Sectors += track.Sectors;
|
|
}
|
|
|
|
if(_discimage.Disktype != MediaType.CDG &&
|
|
_discimage.Disktype != MediaType.CDEG &&
|
|
_discimage.Disktype != MediaType.CDMIDI &&
|
|
_discimage.Disktype != MediaType.CDROMXA &&
|
|
_discimage.Disktype != MediaType.CDDA &&
|
|
_discimage.Disktype != MediaType.CDI &&
|
|
_discimage.Disktype != MediaType.CDPLUS)
|
|
_imageInfo.SectorSize = 2048; // Only data tracks
|
|
else
|
|
_imageInfo.SectorSize = 2352; // All others
|
|
|
|
if(_discimage.Mcn != null) _imageInfo.ReadableMediaTags.Add(MediaTagType.CD_MCN);
|
|
|
|
_imageInfo.Application = "CDRDAO";
|
|
|
|
_imageInfo.CreationTime = imageFilter.CreationTime;
|
|
_imageInfo.LastModificationTime = imageFilter.LastWriteTime;
|
|
|
|
_imageInfo.Comments = _discimage.Comment;
|
|
_imageInfo.MediaSerialNumber = _discimage.Mcn;
|
|
_imageInfo.MediaBarcode = _discimage.Barcode;
|
|
_imageInfo.MediaType = _discimage.Disktype;
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackFlags);
|
|
|
|
foreach(CdrdaoTrack track in _discimage.Tracks)
|
|
{
|
|
if(track.Subchannel)
|
|
{
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubchannel);
|
|
}
|
|
|
|
switch(track.Tracktype)
|
|
{
|
|
case CDRDAO_TRACK_TYPE_AUDIO:
|
|
{
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackIsrc))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackIsrc);
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2:
|
|
case CDRDAO_TRACK_TYPE_MODE2_MIX:
|
|
{
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEdc))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc);
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2_RAW:
|
|
{
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEdc))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc);
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE1_RAW:
|
|
{
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEcc))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEcc);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccP))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccP);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccQ))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccQ);
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEdc))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
_imageInfo.MetadataMediaType = MetadataMediaType.OpticalDisc;
|
|
|
|
AaruLogging.Verbose(Localization.CDRDAO_image_describes_a_disc_of_type_0, _imageInfo.MediaType);
|
|
|
|
if(!string.IsNullOrEmpty(_imageInfo.Comments))
|
|
AaruLogging.Verbose(Localization.CDRDAO_comments_0, _imageInfo.Comments);
|
|
|
|
_sectorBuilder = new SectorBuilder();
|
|
|
|
return ErrorNumber.NoError;
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
AaruLogging.Error(Localization.Exception_trying_to_identify_image_file_0, imageFilter);
|
|
AaruLogging.Exception(ex, Localization.Exception_trying_to_identify_image_file_0, imageFilter.Filename);
|
|
|
|
return ErrorNumber.UnexpectedException;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer)
|
|
{
|
|
buffer = null;
|
|
|
|
switch(tag)
|
|
{
|
|
case MediaTagType.CD_MCN:
|
|
{
|
|
if(_discimage.Mcn == null) return ErrorNumber.NoData;
|
|
|
|
buffer = Encoding.ASCII.GetBytes(_discimage.Mcn);
|
|
|
|
return ErrorNumber.NoError;
|
|
}
|
|
default:
|
|
return ErrorNumber.NotSupported;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSector(ulong sectorAddress, bool negative, out byte[] buffer, out SectorStatus sectorStatus)
|
|
{
|
|
sectorStatus = SectorStatus.Dumped;
|
|
|
|
return ReadSectors(sectorAddress, negative, 1, out buffer, out _);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectorTag(ulong sectorAddress, bool negative, SectorTagType tag, out byte[] buffer) =>
|
|
ReadSectorsTag(sectorAddress, negative, 1, tag, out buffer);
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSector(ulong sectorAddress, uint track, out byte[] buffer, out SectorStatus sectorStatus)
|
|
{
|
|
sectorStatus = SectorStatus.Dumped;
|
|
|
|
return ReadSectors(sectorAddress, 1, track, out buffer, out _);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag, out byte[] buffer) =>
|
|
ReadSectorsTag(sectorAddress, 1, track, tag, out buffer);
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectors(ulong sectorAddress, bool negative, uint length, out byte[] buffer,
|
|
out SectorStatus[] sectorStatus)
|
|
{
|
|
buffer = null;
|
|
sectorStatus = null;
|
|
|
|
if(negative) return ErrorNumber.NotSupported;
|
|
|
|
foreach(KeyValuePair<uint, ulong> kvp in from kvp in _offsetmap
|
|
where sectorAddress >= kvp.Value
|
|
from cdrdaoTrack in _discimage.Tracks
|
|
where cdrdaoTrack.Sequence == kvp.Key
|
|
where sectorAddress - kvp.Value < cdrdaoTrack.Sectors
|
|
select kvp)
|
|
return ReadSectors(sectorAddress - kvp.Value, length, kvp.Key, out buffer, out sectorStatus);
|
|
|
|
return ErrorNumber.SectorNotFound;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectorsTag(ulong sectorAddress, bool negative, uint length, SectorTagType tag,
|
|
out byte[] buffer)
|
|
{
|
|
buffer = null;
|
|
|
|
if(negative) return ErrorNumber.NotSupported;
|
|
|
|
foreach(KeyValuePair<uint, ulong> kvp in from kvp in _offsetmap
|
|
where sectorAddress >= kvp.Value
|
|
from cdrdaoTrack in _discimage.Tracks
|
|
where cdrdaoTrack.Sequence == kvp.Key
|
|
where sectorAddress - kvp.Value < cdrdaoTrack.Sectors
|
|
select kvp)
|
|
return ReadSectorsTag(sectorAddress - kvp.Value, length, kvp.Key, tag, out buffer);
|
|
|
|
return ErrorNumber.SectorNotFound;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectors(ulong sectorAddress, uint length, uint track, out byte[] buffer,
|
|
out SectorStatus[] sectorStatus)
|
|
{
|
|
buffer = null;
|
|
sectorStatus = null;
|
|
|
|
var aaruTrack = new CdrdaoTrack
|
|
{
|
|
Sequence = 0
|
|
};
|
|
|
|
foreach(CdrdaoTrack cdrdaoTrack in _discimage.Tracks.Where(cdrdaoTrack => cdrdaoTrack.Sequence == track))
|
|
{
|
|
aaruTrack = cdrdaoTrack;
|
|
|
|
break;
|
|
}
|
|
|
|
if(aaruTrack.Sequence == 0) return ErrorNumber.SectorNotFound;
|
|
|
|
if(length > aaruTrack.Sectors) return ErrorNumber.OutOfRange;
|
|
|
|
sectorStatus = new SectorStatus[length];
|
|
for(uint i = 0; i < length; i++) sectorStatus[i] = SectorStatus.Dumped;
|
|
|
|
uint sectorOffset;
|
|
uint sectorSize;
|
|
uint sectorSkip;
|
|
var mode2 = false;
|
|
|
|
switch(aaruTrack.Tracktype)
|
|
{
|
|
case CDRDAO_TRACK_TYPE_MODE1:
|
|
case CDRDAO_TRACK_TYPE_MODE2_FORM1:
|
|
{
|
|
sectorOffset = 0;
|
|
sectorSize = 2048;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2_FORM2:
|
|
{
|
|
sectorOffset = 0;
|
|
sectorSize = 2324;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2:
|
|
case CDRDAO_TRACK_TYPE_MODE2_MIX:
|
|
{
|
|
mode2 = true;
|
|
sectorOffset = 0;
|
|
sectorSize = 2336;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_AUDIO:
|
|
{
|
|
sectorOffset = 0;
|
|
sectorSize = 2352;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE1_RAW:
|
|
{
|
|
sectorOffset = 16;
|
|
sectorSize = 2048;
|
|
sectorSkip = 288;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2_RAW:
|
|
{
|
|
mode2 = true;
|
|
sectorOffset = 0;
|
|
sectorSize = 2352;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
return ErrorNumber.NotSupported;
|
|
}
|
|
|
|
if(aaruTrack.Subchannel) sectorSkip += 96;
|
|
|
|
buffer = new byte[sectorSize * length];
|
|
|
|
_imageStream = aaruTrack.Trackfile.Datafilter.GetDataForkStream();
|
|
var br = new BinaryReader(_imageStream);
|
|
|
|
br.BaseStream.Seek((long)aaruTrack.Trackfile.Offset +
|
|
(long)(sectorAddress * (sectorOffset + sectorSize + sectorSkip)),
|
|
SeekOrigin.Begin);
|
|
|
|
if(mode2)
|
|
{
|
|
var mode2Ms = new MemoryStream((int)(sectorSize * length));
|
|
|
|
buffer = br.ReadBytes((int)((sectorSize + sectorSkip) * length));
|
|
|
|
for(var i = 0; i < length; i++)
|
|
{
|
|
var sector = new byte[sectorSize];
|
|
Array.Copy(buffer, (sectorSize + sectorSkip) * i, sector, 0, sectorSize);
|
|
sector = Sector.GetUserDataFromMode2(sector);
|
|
mode2Ms.Write(sector, 0, sector.Length);
|
|
}
|
|
|
|
buffer = mode2Ms.ToArray();
|
|
}
|
|
else if(sectorOffset == 0 && sectorSkip == 0)
|
|
buffer = br.ReadBytes((int)(sectorSize * length));
|
|
else
|
|
{
|
|
for(var i = 0; i < length; i++)
|
|
{
|
|
br.BaseStream.Seek(sectorOffset, SeekOrigin.Current);
|
|
byte[] sector = br.ReadBytes((int)sectorSize);
|
|
br.BaseStream.Seek(sectorSkip, SeekOrigin.Current);
|
|
Array.Copy(sector, 0, buffer, i * sectorSize, sectorSize);
|
|
}
|
|
}
|
|
|
|
// cdrdao audio tracks are endian swapped corresponding to Aaru
|
|
if(aaruTrack.Tracktype != CDRDAO_TRACK_TYPE_AUDIO) return ErrorNumber.NoError;
|
|
|
|
var swapped = new byte[buffer.Length];
|
|
|
|
for(long i = 0; i < buffer.Length; i += 2)
|
|
{
|
|
swapped[i] = buffer[i + 1];
|
|
swapped[i + 1] = buffer[i];
|
|
}
|
|
|
|
buffer = swapped;
|
|
|
|
return ErrorNumber.NoError;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag,
|
|
out byte[] buffer)
|
|
{
|
|
buffer = null;
|
|
|
|
if(tag is SectorTagType.CdTrackFlags or SectorTagType.CdTrackIsrc) track = (uint)sectorAddress;
|
|
|
|
var aaruTrack = new CdrdaoTrack
|
|
{
|
|
Sequence = 0
|
|
};
|
|
|
|
foreach(CdrdaoTrack cdrdaoTrack in _discimage.Tracks.Where(cdrdaoTrack => cdrdaoTrack.Sequence == track))
|
|
{
|
|
aaruTrack = cdrdaoTrack;
|
|
|
|
break;
|
|
}
|
|
|
|
if(aaruTrack.Sequence == 0) return ErrorNumber.SectorNotFound;
|
|
|
|
if(length > aaruTrack.Sectors) return ErrorNumber.OutOfRange;
|
|
|
|
uint sectorOffset = 0;
|
|
uint sectorSize = 0;
|
|
uint sectorSkip = 0;
|
|
|
|
if(!aaruTrack.Subchannel && tag == SectorTagType.CdSectorSubchannel) return ErrorNumber.NoData;
|
|
|
|
switch(tag)
|
|
{
|
|
case SectorTagType.CdSectorEcc:
|
|
case SectorTagType.CdSectorEccP:
|
|
case SectorTagType.CdSectorEccQ:
|
|
case SectorTagType.CdSectorEdc:
|
|
case SectorTagType.CdSectorHeader:
|
|
case SectorTagType.CdSectorSubchannel:
|
|
case SectorTagType.CdSectorSubHeader:
|
|
case SectorTagType.CdSectorSync:
|
|
break;
|
|
case SectorTagType.CdTrackFlags:
|
|
{
|
|
CdFlags flags = 0;
|
|
|
|
if(aaruTrack.Tracktype != CDRDAO_TRACK_TYPE_AUDIO) flags |= CdFlags.DataTrack;
|
|
|
|
if(aaruTrack.FlagDcp) flags |= CdFlags.CopyPermitted;
|
|
|
|
if(aaruTrack.FlagPre) flags |= CdFlags.PreEmphasis;
|
|
|
|
if(aaruTrack.Flag4Ch) flags |= CdFlags.FourChannel;
|
|
|
|
buffer = [(byte)flags];
|
|
|
|
return ErrorNumber.NoError;
|
|
}
|
|
case SectorTagType.CdTrackIsrc:
|
|
if(aaruTrack.Isrc == null) return ErrorNumber.NoData;
|
|
|
|
buffer = Encoding.UTF8.GetBytes(aaruTrack.Isrc);
|
|
|
|
return ErrorNumber.NoError;
|
|
default:
|
|
return ErrorNumber.NotSupported;
|
|
}
|
|
|
|
switch(aaruTrack.Tracktype)
|
|
{
|
|
case CDRDAO_TRACK_TYPE_MODE1:
|
|
case CDRDAO_TRACK_TYPE_MODE2_FORM1:
|
|
if(tag != SectorTagType.CdSectorSubchannel) return ErrorNumber.NoData;
|
|
|
|
sectorOffset = 2048;
|
|
sectorSize = 96;
|
|
|
|
break;
|
|
case CDRDAO_TRACK_TYPE_MODE2_FORM2:
|
|
case CDRDAO_TRACK_TYPE_MODE2_MIX:
|
|
if(tag != SectorTagType.CdSectorSubchannel) return ErrorNumber.NoData;
|
|
|
|
sectorOffset = 2336;
|
|
sectorSize = 96;
|
|
|
|
break;
|
|
case CDRDAO_TRACK_TYPE_AUDIO:
|
|
if(tag != SectorTagType.CdSectorSubchannel) return ErrorNumber.NoData;
|
|
|
|
sectorOffset = 2352;
|
|
sectorSize = 96;
|
|
|
|
break;
|
|
case CDRDAO_TRACK_TYPE_MODE1_RAW:
|
|
{
|
|
switch(tag)
|
|
{
|
|
case SectorTagType.CdSectorSync:
|
|
{
|
|
sectorOffset = 0;
|
|
sectorSize = 12;
|
|
sectorSkip = 2340;
|
|
|
|
break;
|
|
}
|
|
case SectorTagType.CdSectorHeader:
|
|
{
|
|
sectorOffset = 12;
|
|
sectorSize = 4;
|
|
sectorSkip = 2336;
|
|
|
|
break;
|
|
}
|
|
case SectorTagType.CdSectorSubchannel:
|
|
{
|
|
sectorOffset = 2352;
|
|
sectorSize = 96;
|
|
|
|
break;
|
|
}
|
|
case SectorTagType.CdSectorSubHeader:
|
|
return ErrorNumber.NotSupported;
|
|
case SectorTagType.CdSectorEcc:
|
|
{
|
|
sectorOffset = 2076;
|
|
sectorSize = 276;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
case SectorTagType.CdSectorEccP:
|
|
{
|
|
sectorOffset = 2076;
|
|
sectorSize = 172;
|
|
sectorSkip = 104;
|
|
|
|
break;
|
|
}
|
|
case SectorTagType.CdSectorEccQ:
|
|
{
|
|
sectorOffset = 2248;
|
|
sectorSize = 104;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
case SectorTagType.CdSectorEdc:
|
|
{
|
|
sectorOffset = 2064;
|
|
sectorSize = 4;
|
|
sectorSkip = 284;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2_RAW: // Requires reading sector
|
|
if(tag != SectorTagType.CdSectorSubchannel) return ErrorNumber.NotImplemented;
|
|
|
|
sectorOffset = 2352;
|
|
sectorSize = 96;
|
|
|
|
break;
|
|
default:
|
|
return ErrorNumber.NotSupported;
|
|
}
|
|
|
|
buffer = new byte[sectorSize * length];
|
|
|
|
_imageStream = aaruTrack.Trackfile.Datafilter.GetDataForkStream();
|
|
var br = new BinaryReader(_imageStream);
|
|
|
|
br.BaseStream.Seek((long)aaruTrack.Trackfile.Offset +
|
|
(long)(sectorAddress * (sectorOffset + sectorSize + sectorSkip)),
|
|
SeekOrigin.Begin);
|
|
|
|
if(sectorOffset == 0 && sectorSkip == 0)
|
|
buffer = br.ReadBytes((int)(sectorSize * length));
|
|
else
|
|
{
|
|
for(var i = 0; i < length; i++)
|
|
{
|
|
br.BaseStream.Seek(sectorOffset, SeekOrigin.Current);
|
|
byte[] sector = br.ReadBytes((int)sectorSize);
|
|
br.BaseStream.Seek(sectorSkip, SeekOrigin.Current);
|
|
Array.Copy(sector, 0, buffer, i * sectorSize, sectorSize);
|
|
}
|
|
}
|
|
|
|
return ErrorNumber.NoError;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectorLong(ulong sectorAddress, bool negative, out byte[] buffer,
|
|
out SectorStatus sectorStatus)
|
|
{
|
|
sectorStatus = SectorStatus.Dumped;
|
|
|
|
return ReadSectorsLong(sectorAddress, negative, 1, out buffer, out _);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectorLong(ulong sectorAddress, uint track, out byte[] buffer, out SectorStatus sectorStatus)
|
|
{
|
|
sectorStatus = SectorStatus.Dumped;
|
|
|
|
return ReadSectorsLong(sectorAddress, 1, track, out buffer, out _);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectorsLong(ulong sectorAddress, bool negative, uint length, out byte[] buffer,
|
|
out SectorStatus[] sectorStatus)
|
|
{
|
|
buffer = null;
|
|
sectorStatus = null;
|
|
|
|
if(negative) return ErrorNumber.NotSupported;
|
|
|
|
foreach(KeyValuePair<uint, ulong> kvp in from kvp in _offsetmap
|
|
where sectorAddress >= kvp.Value
|
|
from cdrdaoTrack in _discimage.Tracks
|
|
where cdrdaoTrack.Sequence == kvp.Key
|
|
where sectorAddress - kvp.Value < cdrdaoTrack.Sectors
|
|
select kvp)
|
|
return ReadSectorsLong(sectorAddress - kvp.Value, length, kvp.Key, out buffer, out sectorStatus);
|
|
|
|
return ErrorNumber.SectorNotFound;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ErrorNumber ReadSectorsLong(ulong sectorAddress, uint length, uint track, out byte[] buffer,
|
|
out SectorStatus[] sectorStatus)
|
|
{
|
|
buffer = null;
|
|
sectorStatus = null;
|
|
|
|
var aaruTrack = new CdrdaoTrack
|
|
{
|
|
Sequence = 0
|
|
};
|
|
|
|
foreach(CdrdaoTrack cdrdaoTrack in _discimage.Tracks.Where(cdrdaoTrack => cdrdaoTrack.Sequence == track))
|
|
{
|
|
aaruTrack = cdrdaoTrack;
|
|
|
|
break;
|
|
}
|
|
|
|
if(aaruTrack.Sequence == 0) return ErrorNumber.SectorNotFound;
|
|
|
|
if(length > aaruTrack.Sectors) return ErrorNumber.OutOfRange;
|
|
|
|
sectorStatus = new SectorStatus[length];
|
|
for(uint i = 0; i < length; i++) sectorStatus[i] = SectorStatus.Dumped;
|
|
|
|
uint sectorOffset;
|
|
uint sectorSize;
|
|
uint sectorSkip;
|
|
|
|
switch(aaruTrack.Tracktype)
|
|
{
|
|
case CDRDAO_TRACK_TYPE_MODE1:
|
|
case CDRDAO_TRACK_TYPE_MODE2_FORM1:
|
|
{
|
|
sectorOffset = 0;
|
|
sectorSize = 2048;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2_FORM2:
|
|
{
|
|
sectorOffset = 0;
|
|
sectorSize = 2324;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2:
|
|
case CDRDAO_TRACK_TYPE_MODE2_MIX:
|
|
{
|
|
sectorOffset = 0;
|
|
sectorSize = 2336;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE1_RAW:
|
|
case CDRDAO_TRACK_TYPE_MODE2_RAW:
|
|
case CDRDAO_TRACK_TYPE_AUDIO:
|
|
{
|
|
sectorOffset = 0;
|
|
sectorSize = 2352;
|
|
sectorSkip = 0;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
return ErrorNumber.NotSupported;
|
|
}
|
|
|
|
if(aaruTrack.Subchannel) sectorSkip += 96;
|
|
|
|
buffer = new byte[sectorSize * length];
|
|
|
|
_imageStream = aaruTrack.Trackfile.Datafilter.GetDataForkStream();
|
|
var br = new BinaryReader(_imageStream);
|
|
|
|
br.BaseStream.Seek((long)aaruTrack.Trackfile.Offset + (long)(sectorAddress * (sectorSize + sectorSkip)),
|
|
SeekOrigin.Begin);
|
|
|
|
if(sectorSkip == 0)
|
|
buffer = br.ReadBytes((int)(sectorSize * length));
|
|
else
|
|
{
|
|
for(var i = 0; i < length; i++)
|
|
{
|
|
br.BaseStream.Seek(sectorOffset, SeekOrigin.Current);
|
|
byte[] sector = br.ReadBytes((int)sectorSize);
|
|
br.BaseStream.Seek(sectorSkip, SeekOrigin.Current);
|
|
|
|
Array.Copy(sector, 0, buffer, i * sectorSize, sectorSize);
|
|
}
|
|
}
|
|
|
|
switch(aaruTrack.Tracktype)
|
|
{
|
|
case CDRDAO_TRACK_TYPE_MODE1:
|
|
{
|
|
var fullSector = new byte[2352];
|
|
var fullBuffer = new byte[2352 * length];
|
|
|
|
for(uint i = 0; i < length; i++)
|
|
{
|
|
Array.Copy(buffer, i * 2048, fullSector, 16, 2048);
|
|
_sectorBuilder.ReconstructPrefix(ref fullSector, TrackType.CdMode1, (long)(sectorAddress + i));
|
|
_sectorBuilder.ReconstructEcc(ref fullSector, TrackType.CdMode1);
|
|
Array.Copy(fullSector, 0, fullBuffer, i * 2352, 2352);
|
|
}
|
|
|
|
buffer = fullBuffer;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2_FORM1:
|
|
{
|
|
var fullSector = new byte[2352];
|
|
var fullBuffer = new byte[2352 * length];
|
|
|
|
for(uint i = 0; i < length; i++)
|
|
{
|
|
Array.Copy(buffer, i * 2048, fullSector, 24, 2048);
|
|
|
|
_sectorBuilder.ReconstructPrefix(ref fullSector, TrackType.CdMode2Form1, (long)(sectorAddress + i));
|
|
|
|
_sectorBuilder.ReconstructEcc(ref fullSector, TrackType.CdMode2Form1);
|
|
Array.Copy(fullSector, 0, fullBuffer, i * 2352, 2352);
|
|
}
|
|
|
|
buffer = fullBuffer;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2_FORM2:
|
|
{
|
|
var fullSector = new byte[2352];
|
|
var fullBuffer = new byte[2352 * length];
|
|
|
|
for(uint i = 0; i < length; i++)
|
|
{
|
|
Array.Copy(buffer, i * 2324, fullSector, 24, 2324);
|
|
|
|
_sectorBuilder.ReconstructPrefix(ref fullSector, TrackType.CdMode2Form2, (long)(sectorAddress + i));
|
|
|
|
_sectorBuilder.ReconstructEcc(ref fullSector, TrackType.CdMode2Form2);
|
|
Array.Copy(fullSector, 0, fullBuffer, i * 2352, 2352);
|
|
}
|
|
|
|
buffer = fullBuffer;
|
|
|
|
break;
|
|
}
|
|
case CDRDAO_TRACK_TYPE_MODE2:
|
|
case CDRDAO_TRACK_TYPE_MODE2_MIX:
|
|
{
|
|
var fullSector = new byte[2352];
|
|
var fullBuffer = new byte[2352 * length];
|
|
|
|
for(uint i = 0; i < length; i++)
|
|
{
|
|
_sectorBuilder.ReconstructPrefix(ref fullSector,
|
|
TrackType.CdMode2Formless,
|
|
(long)(sectorAddress + i));
|
|
|
|
Array.Copy(buffer, i * 2336, fullSector, 16, 2336);
|
|
Array.Copy(fullSector, 0, fullBuffer, i * 2352, 2352);
|
|
}
|
|
|
|
buffer = fullBuffer;
|
|
|
|
break;
|
|
}
|
|
|
|
// cdrdao audio tracks are endian swapped corresponding to Aaru
|
|
case CDRDAO_TRACK_TYPE_AUDIO:
|
|
{
|
|
var swapped = new byte[buffer.Length];
|
|
|
|
for(long i = 0; i < buffer.Length; i += 2)
|
|
{
|
|
swapped[i] = buffer[i + 1];
|
|
swapped[i + 1] = buffer[i];
|
|
}
|
|
|
|
buffer = swapped;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ErrorNumber.NoError;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public List<Track> GetSessionTracks(Session session) => GetSessionTracks(session.Sequence);
|
|
|
|
/// <inheritdoc />
|
|
public List<Track> GetSessionTracks(ushort session) => session == 1 ? Tracks : null;
|
|
|
|
#endregion
|
|
} |