Mesen2/Core/Shared/CdReader.cpp

294 lines
9.4 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();
if(disc.Tracks.size() > 0) {
uint32_t currentFileOffset = 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;
} else {
trk.FileOffset = trk.FirstSector * trk.GetSectorSize();
}
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 ----");
LoadSubcodeFile(cueFile, disc);
return disc.Tracks.size() > 0;
}
void CdReader::LoadSubcodeFile(VirtualFile& cueFile, DiscInfo& disc)
{
VirtualFile subFile = FolderUtilities::CombinePath(FolderUtilities::GetFolderName(cueFile.GetFilePath()), FolderUtilities::GetFilename(cueFile.GetFileName(), false)) + ".sub";
if(subFile.IsValid()) {
vector<uint8_t>& subCode = disc.DecodedSubCode;
subFile.ReadFile(subCode);
for(int i = 0; i < disc.DecodedSubCode.size() / 96; i++) {
disc.SubCode.push_back(0x00);
disc.SubCode.push_back(0x80);
for(int j = 0; j < 12; j++) {
for(int k = 7; k >= 0; k--) {
uint8_t encoded = (
(((subCode[i * 96 + j + 0] >> k) & 0x01) << 7) |
(((subCode[i * 96 + j + 12] >> k) & 0x01) << 6) |
(((subCode[i * 96 + j + 24] >> k) & 0x01) << 5) |
(((subCode[i * 96 + j + 36] >> k) & 0x01) << 4) |
(((subCode[i * 96 + j + 48] >> k) & 0x01) << 3) |
(((subCode[i * 96 + j + 60] >> k) & 0x01) << 2) |
(((subCode[i * 96 + j + 72] >> k) & 0x01) << 1) |
(((subCode[i * 96 + j + 84] >> k) & 0x01) << 0)
);
disc.SubCode.push_back(encoded);
}
}
}
}
}