Update to bsnes v107r4 beta release.

byuu says:

  - bsnes: added video filters from bsnes v082
  - bsnes: added ZSNES snow effect option when games paused or unloaded
    (no, I'm not joking)
  - bsnes: added 7-zip support (LZMA 19.00 SDK)

[Recent higan WIPs have also mentioned bsnes changes, although the higan code
no longer includes the bsnes code. These changes include:

  - higan, bsnes: added EXLOROM, EXLOROM-RAM, EXHIROM mappings
  - higan, bsnes: focus the viewport after leaving fullscreen exclusive
    mode
  - bsnes: re-added mightymo's cheat code database
  - bsnes: improved make install rules for the game and cheat code
    databases
  - bsnes: delayed construction of hiro::Window objects to properly show
    bsnes window icons

- Ed.]
This commit is contained in:
Tim Allen
2019-07-07 19:44:09 +10:00
parent becbca47d4
commit d87a0f633d
280 changed files with 120826 additions and 1521 deletions

18
nall/cd/crc16.hpp Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
//CRC-16/KERMIT
namespace nall::CD {
inline auto CRC16(array_view<uint8_t> data) -> uint16_t {
uint16_t crc = 0;
while(data) {
crc ^= *data++ << 8;
for(uint bit : range(8)) {
crc = crc << 1 ^ (crc & 0x8000 ? 0x1021 : 0);
}
}
return ~crc;
}
}

65
nall/cd/edc.hpp Normal file
View File

@@ -0,0 +1,65 @@
#pragma once
//error detection code
namespace nall::CD::EDC {
//polynomial(x) = (x^16 + x^15 + x^2 + 1) * (x^16 + x^2 + x + 1)
inline auto polynomial(uint8_t x) -> uint32_t {
static uint32_t lookup[256]{};
static bool once = false;
if(!once) { once = true;
for(uint n : range(256)) {
uint32_t edc = n;
for(uint b : range(8)) edc = edc >> 1 ^ (edc & 1 ? 0xd8018001 : 0);
lookup[n] = edc;
}
}
return lookup[x];
}
//
inline auto create(array_view<uint8_t> input) -> uint32_t {
uint32_t sum = 0;
for(auto& byte : input) sum = sum >> 8 ^ polynomial(sum ^ byte);
return sum;
}
inline auto create(array_view<uint8_t> input, array_span<uint8_t> output) -> bool {
if(output.size() != 4) return false;
auto sum = create(input);
output[0] = sum >> 0;
output[1] = sum >> 8;
output[2] = sum >> 16;
output[3] = sum >> 24;
return true;
}
inline auto createMode1(array_span<uint8_t> sector) -> bool {
if(sector.size() != 2352) return false;
return create({sector, 2064}, {sector + 2064, 4});
}
//
inline auto verify(array_view<uint8_t> input, uint32_t edc) -> bool {
return edc == create(input);
}
inline auto verify(array_view<uint8_t> input, array_view<uint8_t> compare) -> bool {
if(compare.size() != 4) return false;
auto sum = create(input);
if(compare[0] != uint8_t(sum >> 0)) return false;
if(compare[1] != uint8_t(sum >> 8)) return false;
if(compare[2] != uint8_t(sum >> 16)) return false;
if(compare[3] != uint8_t(sum >> 24)) return false;
return true;
}
inline auto verifyMode1(array_view<uint8_t> sector) -> bool {
if(sector.size() != 2352) return false;
return verify({sector, 2064}, {sector + 2064, 4});
}
}

68
nall/cd/efm.hpp Normal file
View File

@@ -0,0 +1,68 @@
#pragma once
//eight-to-fourteen modulation:
//separates each 1-bit by at least two 0-bits and at most ten 0-bits
namespace nall::CD::EFM {
//the algorithm to generate this table is unknown
inline auto lookup(uint8_t index) -> uint16_t {
static const uint16_t lookup[256] = {
0x1220, 0x2100, 0x2420, 0x2220, 0x1100, 0x0110, 0x0420, 0x0900,
0x1240, 0x2040, 0x2440, 0x2240, 0x1040, 0x0040, 0x0440, 0x0840,
0x2020, 0x2080, 0x2480, 0x0820, 0x1080, 0x0080, 0x0480, 0x0880,
0x1210, 0x2010, 0x2410, 0x2210, 0x1010, 0x0210, 0x0410, 0x0810,
0x0020, 0x2108, 0x0220, 0x0920, 0x1108, 0x0108, 0x1020, 0x0908,
0x1248, 0x2048, 0x2448, 0x2248, 0x1048, 0x0048, 0x0448, 0x0848,
0x0100, 0x2088, 0x2488, 0x2110, 0x1088, 0x0088, 0x0488, 0x0888,
0x1208, 0x2008, 0x2408, 0x2208, 0x1008, 0x0208, 0x0408, 0x0808,
0x1224, 0x2124, 0x2424, 0x2224, 0x1124, 0x0024, 0x0424, 0x0924,
0x1244, 0x2044, 0x2444, 0x2244, 0x1044, 0x0044, 0x0444, 0x0844,
0x2024, 0x2084, 0x2484, 0x0824, 0x1084, 0x0084, 0x0484, 0x0884,
0x1204, 0x2004, 0x2404, 0x2204, 0x1004, 0x0204, 0x0404, 0x0804,
0x1222, 0x2122, 0x2422, 0x2222, 0x1122, 0x0022, 0x1024, 0x0922,
0x1242, 0x2042, 0x2442, 0x2242, 0x1042, 0x0042, 0x0442, 0x0842,
0x2022, 0x2082, 0x2482, 0x0822, 0x1082, 0x0082, 0x0482, 0x0882,
0x1202, 0x0248, 0x2402, 0x2202, 0x1002, 0x0202, 0x0402, 0x0802,
0x1221, 0x2121, 0x2421, 0x2221, 0x1121, 0x0021, 0x0421, 0x0921,
0x1241, 0x2041, 0x2441, 0x2241, 0x1041, 0x0041, 0x0441, 0x0841,
0x2021, 0x2081, 0x2481, 0x0821, 0x1081, 0x0081, 0x0481, 0x0881,
0x1201, 0x2090, 0x2401, 0x2201, 0x1090, 0x0201, 0x0401, 0x0890,
0x0221, 0x2109, 0x1110, 0x0121, 0x1109, 0x0109, 0x1021, 0x0909,
0x1249, 0x2049, 0x2449, 0x2249, 0x1049, 0x0049, 0x0449, 0x0849,
0x0120, 0x2089, 0x2489, 0x0910, 0x1089, 0x0089, 0x0489, 0x0889,
0x1209, 0x2009, 0x2409, 0x2209, 0x1009, 0x0209, 0x0409, 0x0809,
0x1120, 0x2111, 0x2490, 0x0224, 0x1111, 0x0111, 0x0490, 0x0911,
0x0241, 0x2101, 0x0244, 0x0240, 0x1101, 0x0101, 0x0090, 0x0901,
0x0124, 0x2091, 0x2491, 0x2120, 0x1091, 0x0091, 0x0491, 0x0891,
0x1211, 0x2011, 0x2411, 0x2211, 0x1011, 0x0211, 0x0411, 0x0811,
0x1102, 0x0102, 0x2112, 0x0902, 0x1112, 0x0112, 0x1022, 0x0912,
0x2102, 0x2104, 0x0249, 0x0242, 0x1104, 0x0104, 0x0422, 0x0904,
0x0122, 0x2092, 0x2492, 0x0222, 0x1092, 0x0092, 0x0492, 0x0892,
0x1212, 0x2012, 0x2412, 0x2212, 0x1012, 0x0212, 0x0412, 0x0812,
};
return lookup[index];
}
//
inline auto encode(uint8_t data) -> uint16_t {
return lookup(data);
}
//
inline auto decode(uint16_t data) -> maybe<uint8_t> {
static uint16_t table[1 << 14];
static bool once = true;
if(once) {
once = false;
for(uint n : range(1 << 14)) table[n] = 0xffff;
for(uint n : range(1 << 8)) table[lookup(n)] = n;
}
uint16_t result = table[data & 0x3fff];
if(result == 0xffff) return {};
return (uint8_t)result;
}
}

128
nall/cd/rspc.hpp Normal file
View File

@@ -0,0 +1,128 @@
#pragma once
//reed-solomon product code
namespace nall::CD::RSPC {
inline auto encodeP(array_view<uint8_t> input, array_span<uint8_t> parity) -> bool {
ReedSolomon<26,24> s;
uint lo = 0, hi = 43 * 2;
for(uint x : range(43)) {
for(uint w : range(2)) { //16-bit words
uint z = 0;
for(uint y : range(24)) {
s[z++] = input[(y * 43 + x) * 2 + w];
}
s.generateParity();
parity[lo++] = s[z++];
parity[hi++] = s[z++];
}
}
return true;
}
inline auto encodeQ(array_view<uint8_t> input, array_span<uint8_t> parity) -> bool {
ReedSolomon<45,43> s;
uint lo = 0, hi = 26 * 2;
for(uint y : range(26)) {
for(uint w : range(2)) {
uint z = 0;
for(uint x : range(43)) {
s[z++] = input[((x * 44 + y * 43) * 2 + w) % (26 * 43 * 2)];
}
s.generateParity();
parity[lo++] = s[z++];
parity[hi++] = s[z++];
}
}
return true;
}
inline auto encodeMode1(array_span<uint8_t> sector) -> bool {
if(sector.size() != 2352) return false;
if(!encodeP({sector + 12, 2064}, {sector + 2076, 172})) return false;
if(!encodeQ({sector + 12, 2236}, {sector + 2248, 104})) return false;
return true;
}
//
inline auto decodeP(array_span<uint8_t> input, array_span<uint8_t> parity) -> int {
bool success = false;
bool failure = false;
ReedSolomon<26,24> s;
uint lo = 0, hi = 43 * 2;
for(uint x : range(43)) {
for(uint w : range(2)) {
uint z = 0;
for(uint y : range(24)) {
s[z++] = input[(y * 43 + x) * 2 + w];
}
s[z++] = parity[lo++];
s[z++] = parity[hi++];
auto count = s.correctErrors();
if(count < 0) {
failure = true;
}
if(count > 0) {
success = true;
z = 0;
for(uint y : range(24)) {
input[(y * 43 + x) * 2 + w] = s[z++];
}
parity[lo - 1] = s[z++];
parity[hi - 1] = s[z++];
}
}
}
if(!success && !failure) return 0; //no errors remaining
return success ? 1 : -1; //return success even if there are some failures
}
inline auto decodeQ(array_span<uint8_t> input, array_span<uint8_t> parity) -> int {
bool success = false;
bool failure = false;
ReedSolomon<45,43> s;
uint lo = 0, hi = 26 * 2;
for(uint y : range(26)) {
for(uint w : range(2)) {
uint z = 0;
for(uint x : range(43)) {
s[z++] = input[((x * 44 + y * 43) * 2 + w) % (26 * 43 * 2)];
}
s[z++] = parity[lo++];
s[z++] = parity[hi++];
auto count = s.correctErrors();
if(count < 0) {
failure = true;
}
if(count > 0) {
success = true;
z = 0;
for(uint x : range(43)) {
input[((x * 44 + y * 43) * 2 + w) % (26 * 43 * 2)] = s[z++];
}
parity[lo - 1] = s[z++];
parity[hi - 1] = s[z++];
}
}
}
if(!success && !failure) return 0;
return success ? 1 : -1;
}
inline auto decodeMode1(array_span<uint8_t> sector) -> bool {
if(sector.size() != 2352) return false;
//P corrections can allow Q corrections that previously failed to succeed, and vice versa.
//the more iterations, the more chances to correct errors, but the more computationally expensive it is.
//there must be a limit on the amount of retries, or this function may get stuck in an infinite loop.
for(uint attempt : range(4)) {
auto p = decodeP({sector + 12, 2064}, {sector + 2076, 172});
auto q = decodeQ({sector + 12, 2236}, {sector + 2248, 104});
if(p == 0 && q == 0) return true; //no errors remaining
if(p < 0 && q < 0) return false; //no more errors correctable
}
return false; //exhausted all retries with errors remaining
}
}

35
nall/cd/scrambler.hpp Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
namespace nall::CD::Scrambler {
//polynomial(x) = x^15 + x + 1
inline auto polynomial(uint x) -> uint8_t {
static uint8_t lookup[2340]{};
static bool once = false;
if(!once) { once = true;
uint16_t shift = 0x0001;
for(uint n : range(2340)) {
lookup[n] = shift;
for(uint b : range(8)) {
bool carry = shift & 1 ^ shift >> 1 & 1;
shift = (carry << 15 | shift) >> 1;
}
}
}
return lookup[x];
}
//
inline auto transform(array_span<uint8_t> sector) -> bool {
if(sector.size() == 2352) sector += 12; //header is not scrambled
if(sector.size() != 2340) return false; //F1 frames only
for(uint index : range(2340)) {
sector[index] ^= polynomial(index);
}
return true;
}
}

478
nall/cd/session.hpp Normal file
View File

@@ -0,0 +1,478 @@
#pragma once
//subchannel processor
//note: this code is not tolerant to subchannel data that violates the Redbook standard
namespace nall::CD {
enum : int { InvalidLBA = 100 * 60 * 75 };
struct BCD {
static auto encode(uint8_t value) -> uint8_t { return value / 10 << 4 | value % 10; }
static auto decode(uint8_t value) -> uint8_t { return (value >> 4) * 10 + (value & 15); }
};
struct MSF {
uint8_t minute; //00-99
uint8_t second; //00-59
uint8_t frame = -1; //00-74
MSF() = default;
MSF(uint8_t m, uint8_t s, uint8_t f) : minute(m), second(s), frame(f) {}
MSF(int lba) { *this = fromLBA(lba); }
explicit operator bool() const {
return minute <= 99 && second <= 59 && frame <= 74;
}
static auto fromBCD(uint8_t minute, uint8_t second, uint8_t frame) -> MSF {
return {BCD::decode(minute), BCD::decode(second), BCD::decode(frame)};
}
static auto fromLBA(int lba) -> MSF {
if(lba < 0) lba = 100 * 60 * 75 + lba;
if(lba >= 100 * 60 * 75) return {};
uint8_t minute = lba / 75 / 60 % 100;
uint8_t second = lba / 75 % 60;
uint8_t frame = lba % 75;
return {minute, second, frame};
}
auto toLBA() const -> int {
int lba = minute * 60 * 75 + second * 75 + frame;
if(minute < 90) return lba;
return -(100 * 60 * 75 - lba);
}
//for debugging purposes
auto toString() const -> string {
if(!operator bool()) return "??:??:??";
return {pad(minute, 2, '0'), ":", pad(second, 2, '0'), ":", pad(frame, 2, '0')};
}
};
struct Index {
int lba = InvalidLBA;
int end = InvalidLBA; //inclusive range
explicit operator bool() const {
return lba != InvalidLBA;
}
auto inRange(int sector) const -> bool {
if(lba == InvalidLBA || end == InvalidLBA) return false;
return sector >= lba && sector <= end;
}
};
struct Track {
uint8_t control = 0b1111; //4-bit
uint8_t address = 0b1111; //4-bit
Index indices[100];
uint8_t firstIndex = -1;
uint8_t lastIndex = -1;
explicit operator bool() const {
return (bool)indices[1];
}
auto emphasis() const -> bool {
return control & 1;
}
auto copyable() const -> bool {
return control & 2;
}
auto channels() const -> uint {
if((control & 0b1100) == 0b0000) return 2;
if((control & 0b1100) == 0b1000) return 4;
return 0; //data track or reserved
}
auto pregap() const -> int {
if(!indices[0] || !indices[1]) return InvalidLBA;
return indices[1].lba - indices[0].lba;
}
auto isAudio() const -> bool {
return channels() != 0;
}
auto isData() const -> bool {
return (control & 0b1100) == 0b0100;
}
auto inIndex(int lba) const -> maybe<uint8_t> {
for(uint8_t index : range(100)) {
if(indices[index].inRange(lba)) return index;
}
return {};
}
auto inRange(int lba) const -> bool {
if(firstIndex > 99 || lastIndex > 99) return false;
return lba >= indices[firstIndex].lba && lba <= indices[lastIndex].end;
}
};
struct Session {
Index leadIn; //00
Track tracks[100]; //01-99
Index leadOut; //aa
uint8_t firstTrack = -1;
uint8_t lastTrack = -1;
auto inLeadIn(int lba) const -> bool {
return lba < 0;
}
auto inTrack(int lba) const -> maybe<uint8_t> {
for(uint8_t trackID : range(100)) {
auto& track = tracks[trackID];
if(track && track.inRange(lba)) return trackID;
}
return {};
}
auto inLeadOut(int lba) const -> bool {
return lba >= leadOut.lba;
}
auto encode(uint sectors) const -> vector<uint8_t> {
if(sectors < abs(leadIn.lba) + leadOut.lba) return {}; //not enough sectors
vector<uint8_t> data;
data.resize(sectors * 96 + 96); //add one sector for P shift
auto toP = [&](int lba) -> array_span<uint8_t> {
//P is encoded one sector later than Q
return {&data[(lba + abs(leadIn.lba) + 1) * 96], 12};
};
auto toQ = [&](int lba) -> array_span<uint8_t> {
return {&data[(lba + abs(leadIn.lba)) * 96 + 12], 12};
};
//lead-in
int lba = leadIn.lba;
while(lba < 0) {
//tracks
for(uint trackID : range(100)) {
for(uint repeat : range(3)) {
auto& track = tracks[trackID];
if(!track) continue;
auto q = toQ(lba);
q[0] = track.control << 4 | track.address << 0;
q[1] = 0x00;
q[2] = BCD::encode(trackID);
auto msf = MSF(lba);
q[3] = BCD::encode(msf.minute);
q[4] = BCD::encode(msf.second);
q[5] = BCD::encode(msf.frame);
q[6] = 0x00;
msf = MSF(track.indices[1].lba);
q[7] = BCD::encode(msf.minute);
q[8] = BCD::encode(msf.second);
q[9] = BCD::encode(msf.frame);
auto crc16 = CRC16({q, 10});
q[10] = crc16 >> 8;
q[11] = crc16 >> 0;
if(++lba >= 0) break;
}}if( lba >= 0) break;
//first track
for(uint repeat : range(3)) {
auto q = toQ(lba);
q[0] = 0x01; //control value unverified; address = 1
q[1] = 0x00; //track# = 00 (TOC)
q[2] = 0xa0; //first track
auto msf = MSF(lba);
q[3] = BCD::encode(msf.minute);
q[4] = BCD::encode(msf.second);
q[5] = BCD::encode(msf.frame);
q[6] = 0x00;
q[7] = BCD::encode(firstTrack);
q[8] = 0x00;
q[9] = 0x00;
auto crc16 = CRC16({q, 10});
q[10] = crc16 >> 8;
q[11] = crc16 >> 0;
if(++lba >= 0) break;
} if( lba >= 0) break;
//last track
for(uint repeat : range(3)) {
auto q = toQ(lba);
q[0] = 0x01;
q[1] = 0x00;
q[2] = 0xa1; //last track
auto msf = MSF(lba);
q[3] = BCD::encode(msf.minute);
q[4] = BCD::encode(msf.second);
q[5] = BCD::encode(msf.frame);
q[6] = 0x00;
q[7] = BCD::encode(lastTrack);
q[8] = 0x00;
q[9] = 0x00;
auto crc16 = CRC16({q, 10});
q[10] = crc16 >> 8;
q[11] = crc16 >> 0;
if(++lba >= 0) break;
} if( lba >= 0) break;
//lead-out point
for(uint repeat : range(3)) {
auto q = toQ(lba);
q[0] = 0x01;
q[1] = 0x00;
q[2] = 0xa2; //lead-out point
auto msf = MSF(lba);
q[3] = BCD::encode(msf.minute);
q[4] = BCD::encode(msf.second);
q[5] = BCD::encode(msf.frame);
q[6] = 0x00;
msf = MSF(leadOut.lba);
q[7] = BCD::encode(msf.minute);
q[8] = BCD::encode(msf.second);
q[9] = BCD::encode(msf.frame);
auto crc16 = CRC16({q, 10});
q[10] = crc16 >> 8;
q[11] = crc16 >> 0;
if(++lba >= 0) break;
} if( lba >= 0) break;
}
//tracks
int end = leadOut.lba;
for(uint8_t trackID : reverse(range(100))) {
auto& track = tracks[trackID];
if(!track) continue;
//indices
for(uint8_t indexID : reverse(range(100))) {
auto& index = track.indices[indexID];
if(!index) continue;
for(int lba = index.lba; lba < end; lba++) {
auto p = toP(lba);
uint8_t byte = indexID == 0 ? 0xff : 0x00;
for(uint index : range(12)) p[index] = byte;
auto q = toQ(lba);
q[0] = track.control << 4 | track.address << 0;
q[1] = BCD::encode(trackID);
q[2] = BCD::encode(indexID);
auto msf = MSF(lba - track.indices[1].lba);
q[3] = BCD::encode(msf.minute);
q[4] = BCD::encode(msf.second);
q[5] = BCD::encode(msf.frame);
q[6] = 0x00;
msf = MSF(lba);
q[7] = BCD::encode(msf.minute);
q[8] = BCD::encode(msf.second);
q[9] = BCD::encode(msf.frame);
auto crc16 = CRC16({q, 10});
q[10] = crc16 >> 8;
q[11] = crc16 >> 0;
}
end = index.lba;
}
}
//lead-out
for(int lba : range(sectors - abs(leadIn.lba) - leadOut.lba)) {
auto p = toP(leadOut.lba + lba);
uint8_t byte;
if(lba < 150) {
//2s start (standard specifies 2-3s start)
byte = 0x00;
} else {
//2hz duty cycle; rounded downward (standard specifies 2% tolerance)
byte = (lba - 150) / (75 >> 1) & 1 ? 0xff : 0x00;
}
for(uint index : range(12)) p[index] = byte;
auto q = toQ(leadOut.lba + lba);
q[0] = 0x01;
q[1] = 0xaa; //lead-out track#
q[2] = 0x01; //lead-out index#
auto msf = MSF(lba);
q[3] = BCD::encode(msf.minute);
q[4] = BCD::encode(msf.second);
q[5] = BCD::encode(msf.frame);
q[6] = 0x00;
msf = MSF(leadOut.lba + lba);
q[7] = BCD::encode(msf.minute);
q[8] = BCD::encode(msf.second);
q[9] = BCD::encode(msf.frame);
auto crc16 = CRC16({q, 10});
q[10] = crc16 >> 8;
q[11] = crc16 >> 0;
}
data.resize(data.size() - 96); //remove padding for P shift
return data;
}
auto decode(array_view<uint8_t> data, uint size, uint leadOutSectors = 0) -> bool {
*this = {}; //reset session
//three data[] types supported: subcode Q only, subcode P-W only, data+subcode complete image
if(size != 12 && size != 96 && size != 2448) return false;
//determine lead-in sector count
for(int lba : range(7500)) { //7500 max sectors scanned
uint offset = lba * size;
if(size == 96) offset += 12;
if(size == 2448) offset += 12 + 2352;
if(offset + 12 > data.size()) break;
auto q = array_view<uint8_t>{&data[offset], 12};
auto crc16 = CRC16({q, 10});
if(q[10] != uint8_t(crc16 >> 8)) continue;
if(q[11] != uint8_t(crc16 >> 0)) continue;
uint8_t control = q[0] >> 4;
uint8_t address = q[0] & 15;
uint8_t trackID = q[1];
if(address != 1) continue;
if(trackID != 0) continue;
auto msf = MSF::fromBCD(q[3], q[4], q[5]);
leadIn.lba = msf.toLBA() - lba;
break;
}
if(leadIn.lba == InvalidLBA || leadIn.lba >= 0) return false;
auto toQ = [&](int lba) -> array_view<uint8_t> {
uint offset = (lba + abs(leadIn.lba)) * size;
if(size == 96) offset += 12;
if(size == 2448) offset += 12 + 2352;
if(offset + 12 > data.size()) return {};
return {&data[offset], 12};
};
//lead-in
for(int lba = leadIn.lba; lba < 0; lba++) {
auto q = toQ(lba);
if(!q) break;
auto crc16 = CRC16({q, 10});
if(q[10] != uint8_t(crc16 >> 8)) continue;
if(q[11] != uint8_t(crc16 >> 0)) continue;
uint8_t control = q[0] >> 4;
uint8_t address = q[0] & 15;
uint8_t trackID = q[1];
if(address != 1) continue;
if(trackID != 0) continue;
trackID = BCD::decode(q[2]);
if(trackID <= 99) { //00-99
auto& track = tracks[trackID];
track.control = control;
track.address = address;
track.indices[1].lba = MSF::fromBCD(q[7], q[8], q[9]).toLBA();
}
if(trackID == 100) { //a0
firstTrack = BCD::decode(q[7]);
}
if(trackID == 101) { //a1
lastTrack = BCD::decode(q[7]);
}
if(trackID == 102) { //a2
leadOut.lba = MSF::fromBCD(q[7], q[8], q[9]).toLBA();
}
}
if(leadOut.lba == InvalidLBA) return false;
//tracks
for(int lba = 0; lba < leadOut.lba; lba++) {
auto q = toQ(lba);
if(!q) break;
auto crc16 = CRC16({q, 10});
if(q[10] != uint8_t(crc16 >> 8)) continue;
if(q[11] != uint8_t(crc16 >> 0)) continue;
uint8_t control = q[0] >> 4;
uint8_t address = q[0] & 15;
uint8_t trackID = BCD::decode(q[1]);
uint8_t indexID = BCD::decode(q[2]);
if(address != 1) continue;
if(trackID > 99) continue;
if(indexID > 99) continue;
auto& track = tracks[trackID];
if(!track) continue; //track not found?
auto& index = track.indices[indexID];
if(index) continue; //index already decoded?
index.lba = MSF::fromBCD(q[7], q[8], q[9]).toLBA();
}
synchronize(leadOutSectors);
return true;
}
//calculates Index::end variables:
//needed for Session::isTrack() and Track::isIndex() to function.
auto synchronize(uint leadOutSectors = 0) -> void {
leadIn.end = -1;
int end = leadOut.lba - 1;
for(uint trackID : reverse(range(100))) {
auto& track = tracks[trackID];
if(!track) continue;
for(uint indexID : reverse(range(100))) {
auto& index = track.indices[indexID];
if(!index) continue;
index.end = end;
end = index.lba - 1;
}
for(uint indexID : range(100)) {
auto& index = track.indices[indexID];
if(index) { track.firstIndex = indexID; break; }
}
for(uint indexID : reverse(range(100))) {
auto& index = track.indices[indexID];
if(index) { track.lastIndex = indexID; break; }
}
}
leadOut.end = leadOut.lba + leadOutSectors - 1;
}
//for diagnostic use only
auto serialize() const -> string {
string s;
s.append("session\n");
s.append(" leadIn: ");
s.append(MSF(leadIn.lba).toString(), " - ", MSF(leadIn.end).toString(), "\n");
for(uint trackID : range(100)) {
auto& track = tracks[trackID];
if(!track) continue;
s.append(" track", pad(trackID, 2, '0'));
if(trackID == firstTrack) s.append(" first");
if(trackID == lastTrack) s.append( " last");
s.append("\n");
s.append(" control: ", binary(track.control, 4, '0'), "\n");
s.append(" address: ", binary(track.address, 4, '0'), "\n");
for(uint indexID : range(100)) {
auto& index = track.indices[indexID];
if(!index) continue;
s.append(" index", pad(indexID, 2, '0'), ": ");
s.append(MSF(index.lba).toString(), " - ", MSF(index.end).toString(), "\n");
}
}
s.append(" leadout: ");
s.append(MSF(leadOut.lba).toString(), " - ", MSF(leadOut.end).toString(), "\n");
return s;
}
};
}

27
nall/cd/sync.hpp Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
namespace nall::CD::Sync {
inline auto create(array_span<uint8_t> sector) -> bool {
if(sector.size() != 12 && sector.size() != 2352) return false;
for(uint n : range(12)) {
sector[n] = (n == 0 || n == 11) ? 0x00 : 0xff;
}
return true;
}
//
inline auto verify(array_view<uint8_t> sector) -> bool {
if(sector.size() != 12 && sector.size() != 2352) return false;
for(uint n : range(12)) {
if(sector[n] != (n == 0 || n == 11) ? 0x00 : 0xff) return false;
}
return true;
}
}