mirror of
https://github.com/stenzek/duckstation.git
synced 2026-07-11 01:24:11 +02:00
CueParser: Handle rarely-used commands
This commit is contained in:
@@ -96,7 +96,13 @@ TEST(CueParser, DifferentTrackModes)
|
||||
"TRACK 04 MODE2/2048\n"
|
||||
"INDEX 01 30:10:00\n"
|
||||
"TRACK 05 MODE2/2352\n"
|
||||
"INDEX 01 45:00:00\n";
|
||||
"INDEX 01 45:00:00\n"
|
||||
"TRACK 06 MODE2/2324\n"
|
||||
"INDEX 01 60:00:00\n"
|
||||
"TRACK 07 MODE1_RAW\n"
|
||||
"INDEX 01 75:00:00\n"
|
||||
"TRACK 08 MODE2_RAW\n"
|
||||
"INDEX 01 90:00:00\n";
|
||||
|
||||
CueParser::File cue_file;
|
||||
Error error;
|
||||
@@ -125,6 +131,18 @@ TEST(CueParser, DifferentTrackModes)
|
||||
const CueParser::Track* track5 = cue_file.GetTrack(5);
|
||||
ASSERT_NE(track5, nullptr);
|
||||
EXPECT_EQ(track5->mode, CueParser::TrackMode::Mode2Raw);
|
||||
|
||||
const CueParser::Track* track6 = cue_file.GetTrack(6);
|
||||
ASSERT_NE(track6, nullptr);
|
||||
EXPECT_EQ(track6->mode, CueParser::TrackMode::Mode2Form2);
|
||||
|
||||
const CueParser::Track* track7 = cue_file.GetTrack(7);
|
||||
ASSERT_NE(track7, nullptr);
|
||||
EXPECT_EQ(track7->mode, CueParser::TrackMode::Mode1Raw);
|
||||
|
||||
const CueParser::Track* track8 = cue_file.GetTrack(8);
|
||||
ASSERT_NE(track8, nullptr);
|
||||
EXPECT_EQ(track8->mode, CueParser::TrackMode::Mode2Raw);
|
||||
}
|
||||
|
||||
// Test track flags
|
||||
@@ -269,13 +287,19 @@ TEST(CueParser, InvalidIndexNumber)
|
||||
// Test invalid MSF format
|
||||
TEST(CueParser, InvalidMSFFormat)
|
||||
{
|
||||
const std::string cue = "FILE \"game.bin\" BINARY\n"
|
||||
"TRACK 01 AUDIO\n"
|
||||
"INDEX 01 00:99:00\n";
|
||||
const std::string invalid_second = "FILE \"game.bin\" BINARY\n"
|
||||
"TRACK 01 AUDIO\n"
|
||||
"INDEX 01 00:60:00\n";
|
||||
const std::string invalid_frame = "FILE \"game.bin\" BINARY\n"
|
||||
"TRACK 01 AUDIO\n"
|
||||
"INDEX 01 00:00:75\n";
|
||||
|
||||
CueParser::File cue_file;
|
||||
Error error;
|
||||
ASSERT_FALSE(cue_file.Parse(cue, &error));
|
||||
CueParser::File cue_file;
|
||||
ASSERT_FALSE(cue_file.Parse(invalid_second, &error));
|
||||
|
||||
cue_file = CueParser::File();
|
||||
ASSERT_FALSE(cue_file.Parse(invalid_frame, &error));
|
||||
}
|
||||
|
||||
// Test duplicate index
|
||||
@@ -308,6 +332,9 @@ TEST(CueParser, ReverseIndexOrder)
|
||||
TEST(CueParser, CommentsAndWhitespace)
|
||||
{
|
||||
const std::string cue = "REM This is a comment\n"
|
||||
"; This is also a comment\n"
|
||||
"// cdrdao comment\n"
|
||||
"CD_ROM_XA\n"
|
||||
" FILE \"game.bin\" BINARY \n"
|
||||
"\n"
|
||||
"REM Another comment\n"
|
||||
@@ -324,6 +351,46 @@ TEST(CueParser, CommentsAndWhitespace)
|
||||
EXPECT_EQ(track->mode, CueParser::TrackMode::Mode2Raw);
|
||||
}
|
||||
|
||||
TEST(CueParser, CdrdaoTrackWithoutNumber)
|
||||
{
|
||||
const std::string cue = "FILE \"game.bin\" BINARY\n"
|
||||
"TRACK MODE1_RAW\n"
|
||||
"INDEX 01 00:00:00\n";
|
||||
|
||||
CueParser::File cue_file;
|
||||
Error error;
|
||||
|
||||
ASSERT_TRUE(cue_file.Parse(cue, &error)) << error.GetDescription();
|
||||
|
||||
const CueParser::Track* track = cue_file.GetTrack(1);
|
||||
ASSERT_NE(track, nullptr);
|
||||
EXPECT_EQ(track->mode, CueParser::TrackMode::Mode1Raw);
|
||||
}
|
||||
|
||||
TEST(CueParser, StandaloneFlagCommands)
|
||||
{
|
||||
const std::string cue = "FILE \"game.bin\" BINARY\n"
|
||||
"TRACK 01 AUDIO\n"
|
||||
"COPY\n"
|
||||
"PRE_EMPHASIS\n"
|
||||
"FOUR_CHANNEL_AUDIO\n"
|
||||
"NO COPY\n"
|
||||
"NO PRE_EMPHASIS\n"
|
||||
"TWO_CHANNEL_AUDIO\n"
|
||||
"INDEX 01 00:00:00\n";
|
||||
|
||||
CueParser::File cue_file;
|
||||
Error error;
|
||||
|
||||
ASSERT_TRUE(cue_file.Parse(cue, &error)) << error.GetDescription();
|
||||
|
||||
const CueParser::Track* track = cue_file.GetTrack(1);
|
||||
ASSERT_NE(track, nullptr);
|
||||
EXPECT_FALSE(track->HasFlag(CueParser::TrackFlag::CopyPermitted));
|
||||
EXPECT_FALSE(track->HasFlag(CueParser::TrackFlag::PreEmphasis));
|
||||
EXPECT_FALSE(track->HasFlag(CueParser::TrackFlag::FourChannelAudio));
|
||||
}
|
||||
|
||||
// Test handling of multiple files
|
||||
TEST(CueParser, MultipleFiles)
|
||||
{
|
||||
|
||||
@@ -9,6 +9,15 @@ bool Core::GetBaseBoolSettingValue(const char* section, const char* key, bool de
|
||||
return default_value;
|
||||
}
|
||||
|
||||
SmallString Host::TranslatePluralToSmallString(const char* context, const char* msg, const char* disambiguation,
|
||||
int count)
|
||||
{
|
||||
TinyString count_string = TinyString::from_format("{}", count);
|
||||
SmallString ret(msg);
|
||||
ret.replace("%n", count_string.c_str());
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 Host::Internal::GetTranslatedStringImpl(std::string_view context, std::string_view msg,
|
||||
std::string_view disambiguation, char* tbuf, size_t tbuf_space)
|
||||
{
|
||||
|
||||
+100
-12
@@ -148,7 +148,7 @@ std::string_view CueParser::File::GetToken(const char*& line)
|
||||
|
||||
std::optional<CueParser::MSF> CueParser::File::GetMSF(std::string_view token)
|
||||
{
|
||||
static const s32 max_values[] = {std::numeric_limits<s32>::max(), 60, 75};
|
||||
static const s32 max_values[] = {std::numeric_limits<s32>::max(), 59, 74};
|
||||
|
||||
u32 parts[3] = {};
|
||||
u32 part = 0;
|
||||
@@ -197,7 +197,7 @@ bool CueParser::File::ParseLine(const char* line, u32 line_number, Error* error)
|
||||
if (command.empty())
|
||||
return true;
|
||||
|
||||
if (TokenMatch(command, "REM"))
|
||||
if (command[0] == ';' || command.starts_with("//") || TokenMatch(command, "REM"))
|
||||
{
|
||||
// comment, eat it
|
||||
return true;
|
||||
@@ -213,6 +213,22 @@ bool CueParser::File::ParseLine(const char* line, u32 line_number, Error* error)
|
||||
return HandlePregapCommand(line, line_number, error);
|
||||
else if (TokenMatch(command, "FLAGS"))
|
||||
return HandleFlagCommand(line, line_number, error);
|
||||
else if (TokenMatch(command, "COPY"))
|
||||
return HandleCopyCommand(line_number, true, error);
|
||||
else if (TokenMatch(command, "PRE_EMPHASIS"))
|
||||
return HandlePreEmphasisCommand(line_number, true, error);
|
||||
else if (TokenMatch(command, "FOUR_CHANNEL_AUDIO"))
|
||||
return HandleFourChannelAudioCommand(line_number, true, error);
|
||||
else if (TokenMatch(command, "TWO_CHANNEL_AUDIO"))
|
||||
return HandleFourChannelAudioCommand(line_number, false, error);
|
||||
else if (TokenMatch(command, "NO"))
|
||||
{
|
||||
const std::string_view subcommand(GetToken(line));
|
||||
if (TokenMatch(subcommand, "COPY"))
|
||||
return HandleCopyCommand(line_number, false, error);
|
||||
else if (TokenMatch(subcommand, "PRE_EMPHASIS"))
|
||||
return HandlePreEmphasisCommand(line_number, false, error);
|
||||
}
|
||||
|
||||
if (TokenMatch(command, "POSTGAP"))
|
||||
{
|
||||
@@ -221,12 +237,15 @@ bool CueParser::File::ParseLine(const char* line, u32 line_number, Error* error)
|
||||
}
|
||||
|
||||
// stuff we definitely ignore
|
||||
if (TokenMatch(command, "CATALOG") || TokenMatch(command, "CDTEXTFILE") || TokenMatch(command, "ISRC") ||
|
||||
TokenMatch(command, "TRACK_ISRC") || TokenMatch(command, "TITLE") || TokenMatch(command, "PERFORMER") ||
|
||||
TokenMatch(command, "SONGWRITER") || TokenMatch(command, "COMPOSER") || TokenMatch(command, "ARRANGER") ||
|
||||
TokenMatch(command, "MESSAGE") || TokenMatch(command, "DISC_ID") || TokenMatch(command, "GENRE") ||
|
||||
TokenMatch(command, "TOC_INFO1") || TokenMatch(command, "TOC_INFO2") || TokenMatch(command, "UPC_EAN") ||
|
||||
TokenMatch(command, "SIZE_INFO"))
|
||||
if (TokenMatch(command, "CATALOG") || TokenMatch(command, "CD_DA") || TokenMatch(command, "CD_ROM") ||
|
||||
TokenMatch(command, "CD_ROM_XA") || TokenMatch(command, "CD_TEXT") || TokenMatch(command, "CDTEXTFILE") ||
|
||||
TokenMatch(command, "ISRC") || TokenMatch(command, "TRACK_ISRC") || TokenMatch(command, "TITLE") ||
|
||||
TokenMatch(command, "PERFORMER") || TokenMatch(command, "SONGWRITER") || TokenMatch(command, "COMPOSER") ||
|
||||
TokenMatch(command, "ARRANGER") || TokenMatch(command, "MESSAGE") || TokenMatch(command, "DISC_ID") ||
|
||||
TokenMatch(command, "GENRE") || TokenMatch(command, "TOC_INFO1") || TokenMatch(command, "TOC_INFO2") ||
|
||||
TokenMatch(command, "UPC_EAN") || TokenMatch(command, "SIZE_INFO") || TokenMatch(command, "DATAFILE") ||
|
||||
TokenMatch(command, "FIFO") || TokenMatch(command, "SILENCE") || TokenMatch(command, "START") ||
|
||||
TokenMatch(command, "ZERO"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -278,33 +297,54 @@ bool CueParser::File::HandleTrackCommand(const char* line, u32 line_number, Erro
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string_view track_number_str(GetToken(line));
|
||||
std::string_view track_number_str(GetToken(line));
|
||||
if (track_number_str.empty())
|
||||
{
|
||||
SetError(line_number, error, "Missing track number");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::optional<s32> track_number = StringUtil::FromChars<s32>(track_number_str);
|
||||
std::optional<s32> track_number = StringUtil::FromChars<s32>(track_number_str);
|
||||
std::string_view mode_str;
|
||||
if (track_number.has_value())
|
||||
{
|
||||
mode_str = GetToken(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
// cdrdao can omit the track number for single-track CD_ROM CUE sheets.
|
||||
if (!m_tracks.empty())
|
||||
{
|
||||
SetError(line_number, error, "Missing track number");
|
||||
return false;
|
||||
}
|
||||
|
||||
track_number = 1;
|
||||
mode_str = track_number_str;
|
||||
}
|
||||
|
||||
if (track_number.value_or(0) < MIN_TRACK_NUMBER || track_number.value_or(0) > MAX_TRACK_NUMBER)
|
||||
{
|
||||
SetError(line_number, error, "Invalid track number {}", track_number.value_or(0));
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string_view mode_str = GetToken(line);
|
||||
TrackMode mode;
|
||||
if (TokenMatch(mode_str, "AUDIO"))
|
||||
mode = TrackMode::Audio;
|
||||
else if (TokenMatch(mode_str, "MODE1_RAW"))
|
||||
mode = TrackMode::Mode1Raw;
|
||||
else if (TokenMatch(mode_str, "MODE1/2048"))
|
||||
mode = TrackMode::Mode1;
|
||||
else if (TokenMatch(mode_str, "MODE1/2352"))
|
||||
mode = TrackMode::Mode1Raw;
|
||||
else if (TokenMatch(mode_str, "MODE2_RAW"))
|
||||
mode = TrackMode::Mode2Raw;
|
||||
else if (TokenMatch(mode_str, "MODE2/2336"))
|
||||
mode = TrackMode::Mode2;
|
||||
else if (TokenMatch(mode_str, "MODE2/2048"))
|
||||
mode = TrackMode::Mode2Form1;
|
||||
else if (TokenMatch(mode_str, "MODE2/2342"))
|
||||
else if (TokenMatch(mode_str, "MODE2/2324") || TokenMatch(mode_str, "MODE2/2342"))
|
||||
mode = TrackMode::Mode2Form2;
|
||||
else if (TokenMatch(mode_str, "MODE2/2332"))
|
||||
mode = TrackMode::Mode2FormMix;
|
||||
@@ -324,6 +364,54 @@ bool CueParser::File::HandleTrackCommand(const char* line, u32 line_number, Erro
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CueParser::File::HandleCopyCommand(u32 line_number, bool enabled, Error* error)
|
||||
{
|
||||
if (!m_current_track.has_value())
|
||||
{
|
||||
SetError(line_number, error, "Copy command outside of track");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
m_current_track->SetFlag(TrackFlag::CopyPermitted);
|
||||
else
|
||||
m_current_track->RemoveFlag(TrackFlag::CopyPermitted);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CueParser::File::HandlePreEmphasisCommand(u32 line_number, bool enabled, Error* error)
|
||||
{
|
||||
if (!m_current_track.has_value())
|
||||
{
|
||||
SetError(line_number, error, "Pre-emphasis command outside of track");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
m_current_track->SetFlag(TrackFlag::PreEmphasis);
|
||||
else
|
||||
m_current_track->RemoveFlag(TrackFlag::PreEmphasis);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CueParser::File::HandleFourChannelAudioCommand(u32 line_number, bool enabled, Error* error)
|
||||
{
|
||||
if (!m_current_track.has_value())
|
||||
{
|
||||
SetError(line_number, error, "Channel audio command outside of track");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
m_current_track->SetFlag(TrackFlag::FourChannelAudio);
|
||||
else
|
||||
m_current_track->RemoveFlag(TrackFlag::FourChannelAudio);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CueParser::File::HandleIndexCommand(const char* line, u32 line_number, Error* error)
|
||||
{
|
||||
if (!m_current_track.has_value())
|
||||
|
||||
@@ -88,6 +88,9 @@ private:
|
||||
bool HandleIndexCommand(const char* line, u32 line_number, Error* error);
|
||||
bool HandlePregapCommand(const char* line, u32 line_number, Error* error);
|
||||
bool HandleFlagCommand(const char* line, u32 line_number, Error* error);
|
||||
bool HandleCopyCommand(u32 line_number, bool enabled, Error* error);
|
||||
bool HandlePreEmphasisCommand(u32 line_number, bool enabled, Error* error);
|
||||
bool HandleFourChannelAudioCommand(u32 line_number, bool enabled, Error* error);
|
||||
|
||||
bool CompleteLastTrack(u32 line_number, Error* error);
|
||||
bool SetTrackLengths(u32 line_number, Error* error);
|
||||
|
||||
Reference in New Issue
Block a user