mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
The pregaps were not calculated properly when starting each subsequent file, causing the start position of subsequent files to be offset by an amount equal to the cumulative pregaps of all the previous tracks
259 lines
8.3 KiB
C++
259 lines
8.3 KiB
C++
#include "pch.h"
|
|
#include "Shared/CdReader.h"
|
|
#include "Shared/MessageManager.h"
|
|
#include "Utilities/StringUtilities.h"
|
|
#include "Utilities/FolderUtilities.h"
|
|
#include "Utilities/magic_enum.hpp"
|
|
|
|
struct CueIndexEntry
|
|
{
|
|
uint32_t Number;
|
|
DiscPosition Position;
|
|
};
|
|
|
|
struct CueGapEntry
|
|
{
|
|
bool HasGap = false;
|
|
DiscPosition Length;
|
|
};
|
|
|
|
struct CueTrackEntry
|
|
{
|
|
uint32_t Number;
|
|
string Format;
|
|
CueGapEntry PreGap = {};
|
|
vector<CueIndexEntry> Indexes;
|
|
};
|
|
|
|
struct CueFileEntry
|
|
{
|
|
string Filename;
|
|
vector<CueTrackEntry> Tracks;
|
|
};
|
|
|
|
bool CdReader::LoadCue(VirtualFile& cueFile, DiscInfo& disc)
|
|
{
|
|
vector<CueFileEntry> files;
|
|
|
|
stringstream ss;
|
|
cueFile.ReadFile(ss);
|
|
|
|
string line;
|
|
while(std::getline(ss, line)) {
|
|
line = StringUtilities::TrimLeft(StringUtilities::TrimRight(line));
|
|
|
|
if(line.substr(0, 4) == string("FILE")) {
|
|
size_t start = line.find_first_of('"');
|
|
size_t end = line.find_last_of('"');
|
|
|
|
string filename;
|
|
if(start != end && start != string::npos && end != string::npos) {
|
|
filename = line.substr(start + 1, end - start - 1);
|
|
} else {
|
|
//No quotes found, use first and last space as delimiters
|
|
start = line.find_first_of(' ');
|
|
end = line.find_last_of(' ');
|
|
if(end == start && start != string::npos) {
|
|
//only 1 space found, use end of string instead
|
|
end = line.size();
|
|
}
|
|
if(start != end && start != string::npos && end != string::npos) {
|
|
filename = line.substr(start + 1, end - start - 1);
|
|
}
|
|
}
|
|
|
|
filename = StringUtilities::Trim(filename);
|
|
if(!filename.empty()) {
|
|
VirtualFile dataFile = cueFile.GetFolderPath() + filename;
|
|
if(cueFile.IsArchive()) {
|
|
dataFile = VirtualFile(cueFile.GetFilePath(), filename);
|
|
}
|
|
files.push_back({ dataFile });
|
|
} else {
|
|
MessageManager::Log("[CUE] Invalid FILE entry");
|
|
return false;
|
|
}
|
|
} else if(line.substr(0, 5) == string("TRACK")) {
|
|
if(files.size() == 0) {
|
|
MessageManager::Log("[CUE] Unexpected TRACK entry");
|
|
return false;
|
|
}
|
|
|
|
vector<string> entry = StringUtilities::Split(line, ' ');
|
|
|
|
if(entry.size() < 3) {
|
|
MessageManager::Log("[CUE] Invalid TRACK entry");
|
|
return false;
|
|
}
|
|
|
|
CueTrackEntry trk = {};
|
|
try {
|
|
trk.Number = std::stoi(entry[1]);
|
|
} catch(const std::exception&) {
|
|
MessageManager::Log("[CUE] Invalid TRACK number");
|
|
return false;
|
|
}
|
|
|
|
trk.Format = entry[2];
|
|
files[files.size() - 1].Tracks.push_back(trk);
|
|
} else if(line.substr(0, 6) == string("PREGAP")) {
|
|
if(files.empty() || files[files.size() - 1].Tracks.empty()) {
|
|
MessageManager::Log("[CUE] Unexpected PREGAP entry");
|
|
return false;
|
|
}
|
|
|
|
vector<string> entry = StringUtilities::Split(line, ' ');
|
|
|
|
CueGapEntry gap = {};
|
|
|
|
vector<string> lengthParts = StringUtilities::Split(entry[1], ':');
|
|
if(lengthParts.size() != 3) {
|
|
MessageManager::Log("[CUE] Invalid PREGAP time format");
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
gap.Length.Minutes = std::stoi(lengthParts[0]);
|
|
gap.Length.Seconds = std::stoi(lengthParts[1]);
|
|
gap.Length.Frames = std::stoi(lengthParts[2]);
|
|
gap.HasGap = true;
|
|
} catch(const std::exception&) {
|
|
MessageManager::Log("[CUE] Invalid PREGAP time format");
|
|
return false;
|
|
}
|
|
|
|
files[files.size() - 1].Tracks[files[files.size() - 1].Tracks.size() - 1].PreGap = gap;
|
|
} else if(line.substr(0, 5) == string("INDEX")) {
|
|
if(files.empty() || files[files.size() - 1].Tracks.empty()) {
|
|
MessageManager::Log("[CUE] Unexpected INDEX entry");
|
|
return false;
|
|
}
|
|
|
|
vector<string> entry = StringUtilities::Split(line, ' ');
|
|
CueIndexEntry idx = {};
|
|
try {
|
|
idx.Number = std::stoi(entry[1]);
|
|
} catch(const std::exception&) {
|
|
MessageManager::Log("[CUE] Invalid INDEX number");
|
|
return false;
|
|
}
|
|
|
|
vector<string> lengthParts = StringUtilities::Split(entry[2], ':');
|
|
if(lengthParts.size() != 3) {
|
|
MessageManager::Log("[CUE] Invalid INDEX time format");
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
idx.Position.Minutes = std::stoi(lengthParts[0]);
|
|
idx.Position.Seconds = std::stoi(lengthParts[1]);
|
|
idx.Position.Frames = std::stoi(lengthParts[2]);
|
|
} catch(const std::exception&) {
|
|
MessageManager::Log("[CUE] Invalid INDEX time format");
|
|
return false;
|
|
}
|
|
|
|
files[files.size() - 1].Tracks[files[files.size() - 1].Tracks.size() - 1].Indexes.push_back(idx);
|
|
}
|
|
}
|
|
|
|
uint32_t totalPregapLbaLength = 0;
|
|
for(size_t i = 0; i < files.size(); i++) {
|
|
VirtualFile physicalFile = files[i].Filename;
|
|
if(!physicalFile.IsValid()) {
|
|
MessageManager::Log("[CUE] Missing or invalid file: " + files[i].Filename);
|
|
return false;
|
|
}
|
|
|
|
disc.Files.push_back(files[i].Filename);
|
|
int startSector = i == 0 ? 0 : (disc.Tracks[disc.Tracks.size() - 1].LastSector + 1);
|
|
for(size_t j = 0; j < files[i].Tracks.size(); j++) {
|
|
CueTrackEntry entry = files[i].Tracks[j];
|
|
TrackInfo trk = {};
|
|
|
|
if(entry.PreGap.HasGap) {
|
|
totalPregapLbaLength += entry.PreGap.Length.ToLba();
|
|
}
|
|
|
|
DiscPosition startPos;
|
|
for(CueIndexEntry& idx : entry.Indexes) {
|
|
if(idx.Number == 0) {
|
|
trk.HasLeadIn = true;
|
|
trk.LeadInPosition = DiscPosition::FromLba(idx.Position.ToLba() + startSector);
|
|
} else if(idx.Number == 1) {
|
|
if(entry.PreGap.HasGap) {
|
|
trk.HasLeadIn = true;
|
|
trk.LeadInPosition = DiscPosition::FromLba(idx.Position.ToLba() + totalPregapLbaLength - entry.PreGap.Length.ToLba() + startSector);
|
|
}
|
|
trk.StartPosition = DiscPosition::FromLba(idx.Position.ToLba() + totalPregapLbaLength + startSector);
|
|
startPos = idx.Position;
|
|
} else {
|
|
MessageManager::Log("[CUE] Unsupported index number: " + std::to_string(idx.Number));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(entry.Format == "AUDIO") {
|
|
trk.Format = TrackFormat::Audio;
|
|
} else if(entry.Format == "MODE1/2352") {
|
|
trk.Format = TrackFormat::Mode1_2352;
|
|
} else if(entry.Format == "MODE1/2048") {
|
|
trk.Format = TrackFormat::Mode1_2048;
|
|
} else {
|
|
MessageManager::Log("[CUE] Unsupported track format: " + entry.Format);
|
|
return false;
|
|
}
|
|
|
|
trk.FirstSector = trk.StartPosition.ToLba();
|
|
|
|
uint32_t currentFileOffset = 0;
|
|
if(disc.Tracks.size() > 0) {
|
|
TrackInfo& prvTrk = disc.Tracks[disc.Tracks.size() - 1];
|
|
if(prvTrk.Size == 0) {
|
|
//Update end position for this file's previous track based on the start of the current track
|
|
prvTrk.EndPosition = DiscPosition::FromLba((trk.HasLeadIn ? trk.LeadInPosition.ToLba() : trk.FirstSector) - 1);
|
|
prvTrk.LastSector = prvTrk.EndPosition.ToLba();
|
|
prvTrk.SectorCount = prvTrk.LastSector - prvTrk.FirstSector + 1;
|
|
prvTrk.Size = prvTrk.SectorCount * trk.GetSectorSize();
|
|
currentFileOffset = prvTrk.FileOffset + prvTrk.Size;
|
|
}
|
|
}
|
|
|
|
trk.FileOffset = currentFileOffset;
|
|
if(trk.HasLeadIn && !entry.PreGap.HasGap) {
|
|
trk.FileOffset += (trk.StartPosition.ToLba() - trk.LeadInPosition.ToLba()) * trk.GetSectorSize();
|
|
}
|
|
trk.FileIndex = (uint32_t)disc.Files.size() - 1;
|
|
|
|
disc.Tracks.push_back(trk);
|
|
}
|
|
|
|
//Set end position for last track to be the end of the current file
|
|
TrackInfo& lastTrk = disc.Tracks[disc.Tracks.size() - 1];
|
|
lastTrk.Size = (uint32_t)((disc.Files[lastTrk.FileIndex].GetSize() - lastTrk.FileOffset) / lastTrk.GetSectorSize() * lastTrk.GetSectorSize());
|
|
lastTrk.SectorCount = lastTrk.Size / lastTrk.GetSectorSize();
|
|
lastTrk.EndPosition = DiscPosition::FromLba(lastTrk.FirstSector + lastTrk.SectorCount - 1);
|
|
lastTrk.LastSector = lastTrk.EndPosition.ToLba();
|
|
}
|
|
|
|
TrackInfo& discLastTrk = disc.Tracks[disc.Tracks.size() - 1];
|
|
disc.DiscSize = discLastTrk.FileOffset + discLastTrk.Size;
|
|
disc.DiscSectorCount = discLastTrk.LastSector + 1;
|
|
disc.EndPosition = DiscPosition::FromLba(disc.DiscSectorCount + 2 * 75);
|
|
|
|
MessageManager::Log("---- DISC TRACKS ----");
|
|
int i = 1;
|
|
for(TrackInfo& trk : disc.Tracks) {
|
|
MessageManager::Log("Track " + std::to_string(i) + " (" + string(magic_enum::enum_name(trk.Format)) + ")");
|
|
if(trk.HasLeadIn) {
|
|
MessageManager::Log(" Lead-in: " + trk.LeadInPosition.ToString());
|
|
}
|
|
MessageManager::Log(" Time: " + trk.StartPosition.ToString() + " - " + trk.EndPosition.ToString());
|
|
MessageManager::Log(" Sectors: " + std::to_string(trk.FirstSector) + " - " + std::to_string(trk.LastSector));
|
|
MessageManager::Log(" File offset: " + std::to_string(trk.FileOffset) + " - " + std::to_string(trk.FileOffset+trk.Size-1));
|
|
i++;
|
|
}
|
|
MessageManager::Log("---- END TRACKS ----");
|
|
|
|
return disc.Tracks.size() > 0;
|
|
}
|