Avocado/src/disc/format/chd_format.cpp
Jakub Czekański fab6bd3881 disc: reworked track handling
getTrackBegin returns the first position of the track, whereas getTrackStart returns index1 of that track
That with other smaller bugfixes should play CDDA tracks from the beginning (it was 2 seconds into the track before).
Also, pregap wasn't handled properly.
2021-10-19 03:33:16 +02:00

152 lines
4.6 KiB
C++

#include "chd_format.h"
#include <fmt/core.h>
#include <cstring>
#include "disc/track.h"
#include "utils/file.h"
namespace disc {
namespace format {
std::unique_ptr<Chd> Chd::open(const std::string& path) {
chd_file* chdFile;
auto err = chd_open(path.c_str(), CHD_OPEN_READ, nullptr, &chdFile);
if (err != CHDERR_NONE) {
fmt::print("[CHD] Unable to load file {} (error: {})\n", path, err);
return {};
}
// I'm not using make_unique - I want the constructor to be private
auto chd = std::unique_ptr<Chd>(new Chd(path, chdFile));
const chd_header* header = chd_get_header(chdFile);
chd->hunkSize = header->hunkbytes;
chd->lastHunk = std::vector<uint8_t>(chd->hunkSize);
chd->lastHunkId = 0xfffffff;
if ((chd->hunkSize % chd->sectorSize) != 0) {
fmt::print("[CHD] Image uses invalid hunkSize: {}\n", chd->hunkSize);
return {};
}
char metadata[512];
for (;;) {
int trackNum;
char type[64];
char subtype[64];
int frames;
int pregap = 0;
char pgtype[32];
char pgsub[32];
int postgap = 0;
if (chd_get_metadata(chdFile, CDROM_TRACK_METADATA2_TAG, chd->tracks.size(), metadata, sizeof(metadata), NULL, NULL, NULL)
== CHDERR_NONE) {
sscanf(metadata, CDROM_TRACK_METADATA2_FORMAT, &trackNum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap);
} else if (chd_get_metadata(chdFile, CDROM_TRACK_METADATA_TAG, chd->tracks.size(), metadata, sizeof(metadata), NULL, NULL, NULL)
== CHDERR_NONE) {
sscanf(metadata, CDROM_TRACK_METADATA_FORMAT, &trackNum, type, subtype, &frames);
} else {
if (chd->tracks.size() != 0) {
break;
}
fmt::print("[CHD] No valid metadata found, are you using correct .chd file?\n");
return {};
}
Track track;
if (strcmp(type, "MODE2_RAW") == 0 || strcmp(type, "MODE2") == 0) {
track.type = disc::TrackType::DATA;
} else if (strcmp(type, "AUDIO") == 0) {
track.type = disc::TrackType::AUDIO;
} else {
track.type = disc::TrackType::INVALID;
fmt::print("[CHD] Unsupported track type {}\n", type);
return {};
}
track.number = trackNum;
track.frames = frames;
track.pregap = Position::fromLba(pregap);
track.index0 = Position::fromLba(0);
track.index1 = Position::fromLba(pregap);
track.postgap = Position::fromLba(postgap);
chd->tracks.push_back(track);
}
chd->loadSubchannel(path);
return chd;
}
Chd::Chd(const std::string& path, chd_file* chdFile) : path(path), chdFile(chdFile) {}
Chd::~Chd() { chd_close(chdFile); }
Sector Chd::read(Position pos) {
int lba = (pos - Position{0, 2, 0}).toLba();
size_t hunk = (lba * sectorSize) / hunkSize;
size_t offset = (lba * sectorSize) % hunkSize;
if (hunk != lastHunkId || lastHunk.empty()) {
chd_read(chdFile, hunk, lastHunk.data());
lastHunkId = hunk;
}
std::vector<uint8_t> data(Track::SECTOR_SIZE);
std::copy(lastHunk.begin() + offset, lastHunk.begin() + offset + Track::SECTOR_SIZE, data.begin());
disc::TrackType type = disc::TrackType::DATA;
int trackN = getTrackByPosition(pos);
if (trackN != -1) {
type = tracks[trackN].type;
}
return std::make_pair(data, type);
}
std::string Chd::getFile() const { return path; }
size_t Chd::getTrackCount() const { return tracks.size(); }
int Chd::getTrackByPosition(Position pos) const {
size_t posFrames = (pos - Position(0, 2, 0)).toLba();
size_t frames = 0;
for (size_t i = 0; i < tracks.size(); i++) {
if (posFrames > frames && posFrames < frames + tracks[i].frames) {
return i;
}
frames += tracks[i].frames;
}
return 0;
}
Position Chd::getTrackBegin(int track) const {
size_t frames = 75 * 2;
if ((unsigned)track < tracks.size()) {
for (int i = 0; i < track; i++) {
frames += tracks[i].frames;
}
}
return Position::fromLba(frames);
}
Position Chd::getTrackStart(int track) const { return getTrackBegin(track) + tracks[track].start(); }
Position Chd::getTrackLength(int track) const { return Position::fromLba(tracks[track].pregap.toLba() + tracks[track].frames); }
Position Chd::getDiskSize() const {
size_t frames = 75 * 2;
for (auto t : tracks) {
frames += t.pregap.toLba() + t.frames;
}
return Position::fromLba(frames);
}
} // namespace format
} // namespace disc