mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-31 02:00:27 +02:00
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:
@@ -69,7 +69,7 @@ endif
|
||||
|
||||
# build optimization levels
|
||||
ifeq ($(build),debug)
|
||||
flags += -Og -DBUILD_DEBUG
|
||||
flags += -Og -g -DBUILD_DEBUG
|
||||
else ifeq ($(build),stable)
|
||||
flags += -O1 -DBUILD_STABLE
|
||||
else ifeq ($(build),size)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <nall/array-span.hpp>
|
||||
#include <nall/array-view.hpp>
|
||||
#include <nall/range.hpp>
|
||||
#include <nall/view.hpp>
|
||||
@@ -19,6 +20,10 @@ template<typename T, uint Size> struct array<T[Size]> {
|
||||
}
|
||||
}
|
||||
|
||||
operator array_span<T>() {
|
||||
return {data(), size()};
|
||||
}
|
||||
|
||||
operator array_view<T>() const {
|
||||
return {data(), size()};
|
||||
}
|
||||
|
@@ -59,7 +59,7 @@ namespace bit {
|
||||
}
|
||||
|
||||
//count number of bits set in a byte
|
||||
inline auto count(uintmax x) -> uint {
|
||||
constexpr inline auto count(uintmax x) -> uint {
|
||||
uint count = 0;
|
||||
while(x) x &= x - 1, count++; //clear the least significant bit
|
||||
return count;
|
||||
@@ -67,7 +67,7 @@ namespace bit {
|
||||
|
||||
//return index of the first bit set (or zero of no bits are set)
|
||||
//first(0b1000) == 3
|
||||
inline auto first(uintmax x) -> uint {
|
||||
constexpr inline auto first(uintmax x) -> uint {
|
||||
uint first = 0;
|
||||
while(x) { if(x & 1) break; x >>= 1; first++; }
|
||||
return first;
|
||||
@@ -75,7 +75,7 @@ namespace bit {
|
||||
|
||||
//round up to next highest single bit:
|
||||
//round(15) == 16, round(16) == 16, round(17) == 32
|
||||
inline auto round(uintmax x) -> uintmax {
|
||||
constexpr inline auto round(uintmax x) -> uintmax {
|
||||
if((x & (x - 1)) == 0) return x;
|
||||
while(x & (x - 1)) x &= x - 1;
|
||||
return x << 1;
|
||||
|
31
nall/cd.hpp
Normal file
31
nall/cd.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
/* CD-ROM sector functions.
|
||||
*
|
||||
* Implemented:
|
||||
* eight-to-fourteen modulation (encoding and decoding)
|
||||
* sync header creation and verification
|
||||
* error detection code creation and verification
|
||||
* reed-solomon product-code creation and verification
|
||||
* sector scrambling and descrambling (currently unverified)
|
||||
*
|
||||
* Unimplemented:
|
||||
* reed-solomon product-code correction
|
||||
* cross-interleave reed-solomon creation, verification, and correction
|
||||
* CD-ROM XA mode 2 forms 1 & 2 support
|
||||
* subcode insertion and removal
|
||||
* subcode decoding from CUE files
|
||||
* channel frame expansion and reduction
|
||||
*/
|
||||
|
||||
#include <nall/galois-field.hpp>
|
||||
#include <nall/matrix.hpp>
|
||||
#include <nall/reed-solomon.hpp>
|
||||
|
||||
#include <nall/cd/crc16.hpp>
|
||||
#include <nall/cd/efm.hpp>
|
||||
#include <nall/cd/sync.hpp>
|
||||
#include <nall/cd/edc.hpp>
|
||||
#include <nall/cd/rspc.hpp>
|
||||
#include <nall/cd/scrambler.hpp>
|
||||
#include <nall/cd/session.hpp>
|
18
nall/cd/crc16.hpp
Normal file
18
nall/cd/crc16.hpp
Normal 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
65
nall/cd/edc.hpp
Normal 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
68
nall/cd/efm.hpp
Normal 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
128
nall/cd/rspc.hpp
Normal 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
35
nall/cd/scrambler.hpp
Normal 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
478
nall/cd/session.hpp
Normal 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
27
nall/cd/sync.hpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@@ -20,6 +20,7 @@ struct Biquad {
|
||||
inline auto reset(Type type, double cutoffFrequency, double samplingFrequency, double quality, double gain = 0.0) -> void;
|
||||
inline auto process(double in) -> double; //normalized sample (-1.0 to +1.0)
|
||||
|
||||
inline static auto shelf(double gain, double slope) -> double;
|
||||
inline static auto butterworth(uint order, uint phase) -> double;
|
||||
|
||||
private:
|
||||
@@ -105,37 +106,37 @@ auto Biquad::reset(Type type, double cutoffFrequency, double samplingFrequency,
|
||||
|
||||
case Type::LowShelf:
|
||||
if(gain >= 0) {
|
||||
n = 1 / (1 + sqrt(2) * k + k * k);
|
||||
a0 = (1 + sqrt(2 * v) * k + v * k * k) * n;
|
||||
n = 1 / (1 + k / q + k * k);
|
||||
a0 = (1 + sqrt(v) / q * k + v * k * k) * n;
|
||||
a1 = 2 * (v * k * k - 1) * n;
|
||||
a2 = (1 - sqrt(2 * v) * k + v * k * k) * n;
|
||||
a2 = (1 - sqrt(v) / q * k + v * k * k) * n;
|
||||
b1 = 2 * (k * k - 1) * n;
|
||||
b2 = (1 - sqrt(2) * k + k * k) * n;
|
||||
b2 = (1 - k / q + k * k) * n;
|
||||
} else {
|
||||
n = 1 / (1 + sqrt(2 * v) * k + v * k * k);
|
||||
a0 = (1 + sqrt(2) * k + k * k) * n;
|
||||
n = 1 / (1 + sqrt(v) / q * k + v * k * k);
|
||||
a0 = (1 + k / q + k * k) * n;
|
||||
a1 = 2 * (k * k - 1) * n;
|
||||
a2 = (1 - sqrt(2) * k + k * k) * n;
|
||||
a2 = (1 - k / q + k * k) * n;
|
||||
b1 = 2 * (v * k * k - 1) * n;
|
||||
b2 = (1 - sqrt(2 * v) * k + v * k * k) * n;
|
||||
b2 = (1 - sqrt(v) / q * k + v * k * k) * n;
|
||||
}
|
||||
break;
|
||||
|
||||
case Type::HighShelf:
|
||||
if(gain >= 0) {
|
||||
n = 1 / (1 + sqrt(2) * k + k * k);
|
||||
a0 = (v + sqrt(2 * v) * k + k * k) * n;
|
||||
n = 1 / (1 + k / q + k * k);
|
||||
a0 = (v + sqrt(v) / q * k + k * k) * n;
|
||||
a1 = 2 * (k * k - v) * n;
|
||||
a2 = (v - sqrt(2 * v) * k + k * k) * n;
|
||||
a2 = (v - sqrt(v) / q * k + k * k) * n;
|
||||
b1 = 2 * (k * k - 1) * n;
|
||||
b2 = (1 - sqrt(2) * k + k * k) * n;
|
||||
b2 = (1 - k / q + k * k) * n;
|
||||
} else {
|
||||
n = 1 / (v + sqrt(2 * v) * k + k * k);
|
||||
a0 = (1 + sqrt(2) * k + k * k) * n;
|
||||
n = 1 / (v + sqrt(v) / q * k + k * k);
|
||||
a0 = (1 + k / q + k * k) * n;
|
||||
a1 = 2 * (k * k - 1) * n;
|
||||
a2 = (1 - sqrt(2) * k + k * k) * n;
|
||||
a2 = (1 - k / q + k * k) * n;
|
||||
b1 = 2 * (k * k - v) * n;
|
||||
b2 = (v - sqrt(2 * v) * k + k * k) * n;
|
||||
b2 = (v - sqrt(v) / q * k + k * k) * n;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -149,7 +150,13 @@ auto Biquad::process(double in) -> double {
|
||||
return out;
|
||||
}
|
||||
|
||||
//compute Q values for N-order butterworth filtering
|
||||
//compute Q values for low-shelf and high-shelf filtering
|
||||
auto Biquad::shelf(double gain, double slope) -> double {
|
||||
double a = pow(10, gain / 40);
|
||||
return 1 / sqrt((a + 1 / a) * (1 / slope - 1) + 2);
|
||||
}
|
||||
|
||||
//compute Q values for Nth-order butterworth filtering
|
||||
auto Biquad::butterworth(uint order, uint phase) -> double {
|
||||
return -0.5 / cos(Math::Pi * (phase + order + 0.5) / order);
|
||||
}
|
||||
|
70
nall/galois-field.hpp
Normal file
70
nall/galois-field.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
//table-driven galois field modulo 2
|
||||
//do not use with GF(2^17) or larger
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<typename field, uint Elements, uint Polynomial>
|
||||
struct GaloisField {
|
||||
using type = GaloisField;
|
||||
|
||||
GaloisField(uint x = 0) : x(x) {}
|
||||
operator field() const { return x; }
|
||||
|
||||
auto operator^(field y) const -> type { return x ^ y; }
|
||||
auto operator+(field y) const -> type { return x ^ y; }
|
||||
auto operator-(field y) const -> type { return x ^ y; }
|
||||
auto operator*(field y) const -> type { return x && y ? exp(log(x) + log(y)) : 0; }
|
||||
auto operator/(field y) const -> type { return x && y ? exp(log(x) + Elements - log(y)) : 0; }
|
||||
|
||||
auto& operator =(field y) { return x = y, *this; }
|
||||
auto& operator^=(field y) { return x = operator^(y), *this; }
|
||||
auto& operator+=(field y) { return x = operator^(y), *this; }
|
||||
auto& operator-=(field y) { return x = operator^(y), *this; }
|
||||
auto& operator*=(field y) { return x = operator*(y), *this; }
|
||||
auto& operator/=(field y) { return x = operator/(y), *this; }
|
||||
|
||||
auto pow(field y) const -> type { return exp(log(x) * y); }
|
||||
auto inv() const -> type { return exp(Elements - log(x)); } // 1/x
|
||||
|
||||
static auto log(uint x) -> uint {
|
||||
enum : uint { Size = bit::round(Elements), Mask = Size - 1 };
|
||||
static array<field[Size]> log = [] {
|
||||
uint shift = 0, polynomial = Polynomial;
|
||||
while(polynomial >>= 1) shift++;
|
||||
shift--;
|
||||
|
||||
array<field[Size]> log;
|
||||
field x = 1;
|
||||
for(uint n : range(Elements)) {
|
||||
log[x] = n;
|
||||
x = x << 1 ^ (x >> shift ? Polynomial : 0);
|
||||
}
|
||||
log[0] = 0; //-inf (undefined)
|
||||
return log;
|
||||
}();
|
||||
return log[x & Mask];
|
||||
}
|
||||
|
||||
static auto exp(uint x) -> uint {
|
||||
static array<field[Elements]> exp = [] {
|
||||
uint shift = 0, polynomial = Polynomial;
|
||||
while(polynomial >>= 1) shift++;
|
||||
shift--;
|
||||
|
||||
array<field[Elements]> exp;
|
||||
field x = 1;
|
||||
for(uint n : range(Elements)) {
|
||||
exp[n] = x;
|
||||
x = x << 1 ^ (x >> shift ? Polynomial : 0);
|
||||
}
|
||||
return exp;
|
||||
}();
|
||||
return exp[x % Elements];
|
||||
}
|
||||
|
||||
field x;
|
||||
};
|
||||
|
||||
}
|
@@ -52,7 +52,7 @@ inline auto prefix(string_view self) -> string {
|
||||
const char* p = self.data() + self.size() - 1, *last = p;
|
||||
for(int offset = self.size() - 1, suffix = -1; offset >= 0; offset--, p--) {
|
||||
if(*p == '/' && p == last) continue;
|
||||
if(*p == '/') return slice(self, offset + 1, suffix >= 0 ? suffix - offset - 1 : 0).trimRight("/");
|
||||
if(*p == '/') return slice(self, offset + 1, (suffix >= 0 ? suffix : self.size()) - offset - 1).trimRight("/");
|
||||
if(*p == '.' && suffix == -1) { suffix = offset; continue; }
|
||||
if(offset == 0) return slice(self, offset, suffix).trimRight("/");
|
||||
}
|
||||
|
36
nall/matrix-multiply.hpp
Normal file
36
nall/matrix-multiply.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
//matrix multiplication primitives
|
||||
//used in: ruby/opengl/quark
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<typename T> inline auto MatrixMultiply(
|
||||
T* output,
|
||||
const T* xdata, uint xrows, uint xcols,
|
||||
const T* ydata, uint yrows, uint ycols
|
||||
) -> void {
|
||||
if(xcols != yrows) return;
|
||||
|
||||
for(uint y : range(xrows)) {
|
||||
for(uint x : range(ycols)) {
|
||||
T sum = 0;
|
||||
for(uint z : range(xcols)) {
|
||||
sum += xdata[y * xcols + z] * ydata[z * ycols + x];
|
||||
}
|
||||
*output++ = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> inline auto MatrixMultiply(
|
||||
const T* xdata, uint xrows, uint xcols,
|
||||
const T* ydata, uint yrows, uint ycols
|
||||
) -> vector<T> {
|
||||
vector<T> output;
|
||||
output.resize(xrows * ycols);
|
||||
MatrixMultiply(output.data(), xdata, xrows, xcols, ydata, yrows, ycols);
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
231
nall/matrix.hpp
231
nall/matrix.hpp
@@ -1,36 +1,213 @@
|
||||
#pragma once
|
||||
|
||||
//matrix multiplication primitives
|
||||
//used in: ruby/opengl/quark
|
||||
namespace nall {
|
||||
|
||||
namespace nall::Matrix {
|
||||
template<typename T, uint Rows, uint Cols>
|
||||
struct Matrix {
|
||||
static_assert(Rows > 0 && Cols > 0);
|
||||
|
||||
template<typename T> inline auto Multiply(
|
||||
T* output,
|
||||
const T* xdata, uint xrows, uint xcols,
|
||||
const T* ydata, uint yrows, uint ycols
|
||||
) -> void {
|
||||
if(xcols != yrows) return;
|
||||
|
||||
for(uint y : range(xrows)) {
|
||||
for(uint x : range(ycols)) {
|
||||
T sum = 0;
|
||||
for(uint z : range(xcols)) {
|
||||
sum += xdata[y * xcols + z] * ydata[z * ycols + x];
|
||||
}
|
||||
*output++ = sum;
|
||||
Matrix() = default;
|
||||
Matrix(const Matrix&) = default;
|
||||
Matrix(const initializer_list<T>& source) {
|
||||
uint index = 0;
|
||||
for(auto& value : source) {
|
||||
if(index >= Rows * Cols) break;
|
||||
values[index / Cols][index % Cols] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> inline auto Multiply(
|
||||
const T* xdata, uint xrows, uint xcols,
|
||||
const T* ydata, uint yrows, uint ycols
|
||||
) -> vector<T> {
|
||||
vector<T> output;
|
||||
output.resize(xrows * ycols);
|
||||
Multiply(output.data(), xdata, xrows, xcols, ydata, yrows, ycols);
|
||||
return output;
|
||||
}
|
||||
operator array_span<T>() { return {values, Rows * Cols}; }
|
||||
operator array_view<T>() const { return {values, Rows * Cols}; }
|
||||
|
||||
//1D matrices (for polynomials, etc)
|
||||
auto operator[](uint row) -> T& { return values[row][0]; }
|
||||
auto operator[](uint row) const -> T { return values[row][0]; }
|
||||
|
||||
//2D matrices
|
||||
auto operator()(uint row, uint col) -> T& { return values[row][col]; }
|
||||
auto operator()(uint row, uint col) const -> T { return values[row][col]; }
|
||||
|
||||
//operators
|
||||
auto operator+() const -> Matrix {
|
||||
Matrix result;
|
||||
for(uint row : range(Rows)) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(row, col) = +target(row, col);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto operator-() const -> Matrix {
|
||||
Matrix result;
|
||||
for(uint row : range(Rows)) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(row, col) = -target(row, col);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto operator+(const Matrix& source) const -> Matrix {
|
||||
Matrix result;
|
||||
for(uint row : range(Rows)) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(row, col) = target(row, col) + source(row, col);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto operator-(const Matrix& source) const -> Matrix {
|
||||
Matrix result;
|
||||
for(uint row : range(Rows)) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(row, col) = target(row, col) - source(row, col);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto operator*(T source) const -> Matrix {
|
||||
Matrix result;
|
||||
for(uint row : range(Rows)) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(row, col) = target(row, col) * source;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto operator/(T source) const -> Matrix {
|
||||
Matrix result;
|
||||
for(uint row : range(Rows)) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(row, col) = target(row, col) / source;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//warning: matrix multiplication is not commutative!
|
||||
template<uint SourceRows, uint SourceCols>
|
||||
auto operator*(const Matrix<T, SourceRows, SourceCols>& source) const -> Matrix<T, Rows, SourceCols> {
|
||||
static_assert(Cols == SourceRows);
|
||||
Matrix<T, Rows, SourceCols> result;
|
||||
for(uint y : range(Rows)) {
|
||||
for(uint x : range(SourceCols)) {
|
||||
T sum{};
|
||||
for(uint z : range(Cols)) {
|
||||
sum += target(y, z) * source(z, x);
|
||||
}
|
||||
result(y, x) = sum;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<uint SourceRows, uint SourceCols>
|
||||
auto operator/(const Matrix<T, SourceRows, SourceCols>& source) const -> maybe<Matrix<T, Rows, SourceCols>> {
|
||||
static_assert(Cols == SourceRows && SourceRows == SourceCols);
|
||||
if(auto inverted = source.invert()) return operator*(inverted());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& operator+=(const Matrix& source) { return *this = operator+(source); }
|
||||
auto& operator-=(const Matrix& source) { return *this = operator-(source); }
|
||||
auto& operator*=(T source) { return *this = operator*(source); }
|
||||
auto& operator/=(T source) { return *this = operator/(source); }
|
||||
template<uint SourceRows, uint SourceCols>
|
||||
auto& operator*=(const Matrix<T, SourceRows, SourceCols>& source) { return *this = operator*(source); }
|
||||
//matrix division is not always possible (when matrix cannot be inverted), so operator/= is not provided
|
||||
|
||||
//algorithm: Gauss-Jordan
|
||||
auto invert() const -> maybe<Matrix> {
|
||||
static_assert(Rows == Cols);
|
||||
Matrix source = *this;
|
||||
Matrix result = identity();
|
||||
|
||||
const auto add = [&](uint targetRow, uint sourceRow, T factor = 1) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(targetRow, col) += result(sourceRow, col) * factor;
|
||||
source(targetRow, col) += source(sourceRow, col) * factor;
|
||||
}
|
||||
};
|
||||
|
||||
const auto sub = [&](uint targetRow, uint sourceRow, T factor = 1) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(targetRow, col) -= result(sourceRow, col) * factor;
|
||||
source(targetRow, col) -= source(sourceRow, col) * factor;
|
||||
}
|
||||
};
|
||||
|
||||
const auto mul = [&](uint row, T factor) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(row, col) *= factor;
|
||||
source(row, col) *= factor;
|
||||
}
|
||||
};
|
||||
|
||||
for(uint i : range(Cols)) {
|
||||
if(source(i, i) == 0) {
|
||||
for(uint row : range(Rows)) {
|
||||
if(source(row, i) != 0) {
|
||||
add(i, row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//matrix is not invertible:
|
||||
if(source(i, i) == 0) return {};
|
||||
}
|
||||
|
||||
mul(i, T{1} / source(i, i));
|
||||
for(uint row : range(Rows)) {
|
||||
if(row == i) continue;
|
||||
sub(row, i, source(row, i));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto transpose() const -> Matrix<T, Cols, Rows> {
|
||||
Matrix<T, Cols, Rows> result;
|
||||
for(uint row : range(Rows)) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(col, row) = target(row, col);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static auto identity() -> Matrix {
|
||||
static_assert(Rows == Cols);
|
||||
Matrix result;
|
||||
for(uint row : range(Rows)) {
|
||||
for(uint col : range(Cols)) {
|
||||
result(row, col) = row == col;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//debugging function: do not use in production code
|
||||
template<uint Pad = 0>
|
||||
auto _print() const -> void {
|
||||
for(uint row : range(Rows)) {
|
||||
nall::print("[ ");
|
||||
for(uint col : range(Cols)) {
|
||||
nall::print(pad(target(row, col), Pad, ' '), " ");
|
||||
}
|
||||
nall::print("]\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
//same as operator(), but with easier to read syntax inside Matrix class
|
||||
auto target(uint row, uint col) -> T& { return values[row][col]; }
|
||||
auto target(uint row, uint col) const -> T { return values[row][col]; }
|
||||
|
||||
T values[Rows][Cols]{};
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@
|
||||
#include <nall/file-buffer.hpp>
|
||||
#include <nall/file-map.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/galois-field.hpp>
|
||||
#include <nall/hashset.hpp>
|
||||
#include <nall/hid.hpp>
|
||||
#include <nall/image.hpp>
|
||||
@@ -45,6 +46,7 @@
|
||||
#include <nall/location.hpp>
|
||||
#include <nall/map.hpp>
|
||||
#include <nall/matrix.hpp>
|
||||
#include <nall/matrix-multiply.hpp>
|
||||
#include <nall/maybe.hpp>
|
||||
#include <nall/memory.hpp>
|
||||
#include <nall/merge-sort.hpp>
|
||||
@@ -54,6 +56,7 @@
|
||||
#include <nall/queue.hpp>
|
||||
#include <nall/random.hpp>
|
||||
#include <nall/range.hpp>
|
||||
#include <nall/reed-solomon.hpp>
|
||||
#include <nall/run.hpp>
|
||||
#include <nall/serializer.hpp>
|
||||
#include <nall/set.hpp>
|
||||
|
@@ -39,6 +39,7 @@ namespace Math {
|
||||
#include <time.h>
|
||||
#include <utime.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -77,7 +78,6 @@ namespace Math {
|
||||
inline auto putenv(const char* value) -> int { return _wputenv(nall::utf16_t(value)); }
|
||||
inline auto realpath(const char* file_name, char* resolved_name) -> char* { wchar_t wfile_name[PATH_MAX] = L""; if(!_wfullpath(wfile_name, nall::utf16_t(file_name), PATH_MAX)) return nullptr; strcpy(resolved_name, nall::utf8_t(wfile_name)); return resolved_name; }
|
||||
inline auto rename(const char* oldname, const char* newname) -> int { return _wrename(nall::utf16_t(oldname), nall::utf16_t(newname)); }
|
||||
inline auto usleep(unsigned milliseconds) -> void { Sleep(milliseconds / 1000); }
|
||||
|
||||
namespace nall {
|
||||
//network functions take void*, not char*. this allows them to be used without casting
|
||||
|
@@ -40,6 +40,12 @@ template<uint Precision> struct Integer {
|
||||
template<typename T> inline auto& operator ^=(const T& value) { data = mask(data ^ value); return *this; }
|
||||
template<typename T> inline auto& operator |=(const T& value) { data = mask(data | value); return *this; }
|
||||
|
||||
inline auto operator()(int index) -> BitRange<Precision> { return {(utype&)data, index, index}; }
|
||||
inline auto operator()(int lo, int hi) -> BitRange<Precision> { return {(utype&)data, lo, hi}; }
|
||||
|
||||
inline auto operator()(int index) const -> const BitRange<Precision> { return {(utype&)data, index, index}; }
|
||||
inline auto operator()(int lo, int hi) const -> const BitRange<Precision> { return {(utype&)data, lo, hi}; }
|
||||
|
||||
inline auto bits(int lo, int hi) -> BitRange<Precision> { return {(utype&)data, lo, hi}; }
|
||||
inline auto bit(int index) -> BitRange<Precision> { return {(utype&)data, index, index}; }
|
||||
inline auto byte(int index) -> BitRange<Precision> { return {(utype&)data, index * 8 + 0, index * 8 + 7}; }
|
||||
|
@@ -38,6 +38,12 @@ template<uint Precision> struct Natural {
|
||||
template<typename T> inline auto& operator ^=(const T& value) { data = mask(data ^ value); return *this; }
|
||||
template<typename T> inline auto& operator |=(const T& value) { data = mask(data | value); return *this; }
|
||||
|
||||
inline auto operator()(int index) -> BitRange<Precision> { return {(utype&)data, index, index}; }
|
||||
inline auto operator()(int lo, int hi) -> BitRange<Precision> { return {(utype&)data, lo, hi}; }
|
||||
|
||||
inline auto operator()(int index) const -> const BitRange<Precision> { return {(utype&)data, index, index}; }
|
||||
inline auto operator()(int lo, int hi) const -> const BitRange<Precision> { return {(utype&)data, lo, hi}; }
|
||||
|
||||
inline auto bits(int lo, int hi) -> BitRange<Precision> { return {(utype&)data, lo, hi}; }
|
||||
inline auto bit(int index) -> BitRange<Precision> { return {(utype&)data, index, index}; }
|
||||
inline auto byte(int index) -> BitRange<Precision> { return {(utype&)data, index * 8 + 0, index * 8 + 7}; }
|
||||
|
218
nall/reed-solomon.hpp
Normal file
218
nall/reed-solomon.hpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#pragma once
|
||||
|
||||
namespace nall {
|
||||
|
||||
//RS(n,k) = ReedSolomon<Length, Inputs>
|
||||
template<uint Length, uint Inputs>
|
||||
struct ReedSolomon {
|
||||
enum : uint { Parity = Length - Inputs };
|
||||
static_assert(Length <= 255 && Length > 0);
|
||||
static_assert(Parity <= 32 && Parity > 0);
|
||||
|
||||
using Field = GaloisField<uint8_t, 255, 0x11d>;
|
||||
template<uint Rows, uint Cols = 1> using Polynomial = Matrix<Field, Rows, Cols>;
|
||||
|
||||
template<uint Size>
|
||||
static auto shift(Polynomial<Size> polynomial) -> Polynomial<Size> {
|
||||
for(int n = Size - 1; n > 0; n--) polynomial[n] = polynomial[n - 1];
|
||||
polynomial[0] = 0;
|
||||
return polynomial;
|
||||
}
|
||||
|
||||
template<uint Size>
|
||||
static auto degree(const Polynomial<Size>& polynomial) -> uint {
|
||||
for(int n = Size; n > 0; n--) {
|
||||
if(polynomial[n - 1] != 0) return n - 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<uint Size>
|
||||
static auto evaluate(const Polynomial<Size>& polynomial, Field field) -> Field {
|
||||
Field sum = 0;
|
||||
for(uint n : range(Size)) sum += polynomial[n] * field.pow(n);
|
||||
return sum;
|
||||
}
|
||||
|
||||
Polynomial<Length> message;
|
||||
Polynomial<Parity> syndromes;
|
||||
Polynomial<Parity + 1> locators;
|
||||
|
||||
ReedSolomon() = default;
|
||||
ReedSolomon(const ReedSolomon&) = default;
|
||||
|
||||
ReedSolomon(const initializer_list<uint8_t>& source) {
|
||||
uint index = 0;
|
||||
for(auto& value : source) {
|
||||
if(index >= Length) break;
|
||||
message[index++] = value;
|
||||
}
|
||||
}
|
||||
|
||||
auto operator[](uint index) -> Field& { return message[index]; }
|
||||
auto operator[](uint index) const -> Field { return message[index]; }
|
||||
|
||||
auto calculateSyndromes() -> void {
|
||||
static const Polynomial<Parity> bases = [] {
|
||||
Polynomial<Parity> bases;
|
||||
for(uint n : range(Parity)) {
|
||||
bases[n] = Field::exp(n);
|
||||
}
|
||||
return bases;
|
||||
}();
|
||||
|
||||
syndromes = {};
|
||||
for(uint m : range(Length)) {
|
||||
for(uint p : range(Parity)) {
|
||||
syndromes[p] *= bases[p];
|
||||
syndromes[p] += message[m];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto generateParity() -> void {
|
||||
static const Polynomial<Parity, Parity> matrix = [] {
|
||||
Polynomial<Parity, Parity> matrix{};
|
||||
for(uint row : range(Parity)) {
|
||||
for(uint col : range(Parity)) {
|
||||
matrix(row, col) = Field::exp(row * col);
|
||||
}
|
||||
}
|
||||
if(auto result = matrix.invert()) return *result;
|
||||
throw; //should never occur
|
||||
}();
|
||||
|
||||
for(uint p : range(Parity)) message[Inputs + p] = 0;
|
||||
calculateSyndromes();
|
||||
auto parity = matrix * syndromes;
|
||||
for(uint p : range(Parity)) message[Inputs + p] = parity[Parity - (p + 1)];
|
||||
}
|
||||
|
||||
auto syndromesAreZero() -> bool {
|
||||
for(uint p : range(Parity)) {
|
||||
if(syndromes[p]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//algorithm: Berlekamp-Massey
|
||||
auto calculateLocators() -> void {
|
||||
Polynomial<Parity + 1> history{1};
|
||||
locators = history;
|
||||
uint errors = 0;
|
||||
|
||||
for(uint n : range(Parity)) {
|
||||
Field discrepancy = 0;
|
||||
for(uint l : range(errors + 1)) {
|
||||
discrepancy += locators[l] * syndromes[n - l];
|
||||
}
|
||||
|
||||
history = shift(history);
|
||||
if(discrepancy) {
|
||||
auto located = locators - history * discrepancy;
|
||||
if(errors * 2 <= n) {
|
||||
errors = (n + 1) - errors;
|
||||
history = locators * discrepancy.inv();
|
||||
}
|
||||
locators = located;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//algorithm: brute force
|
||||
//todo: implement Chien search here
|
||||
auto calculateErrors() -> vector<uint8_t> {
|
||||
calculateSyndromes();
|
||||
if(syndromesAreZero()) return {}; //no errors detected
|
||||
calculateLocators();
|
||||
vector<uint8_t> errors;
|
||||
for(uint n : range(Length)) {
|
||||
if(evaluate(locators, Field{2}.pow(255 - n))) continue;
|
||||
errors.append(Length - (n + 1));
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
template<uint Size>
|
||||
static auto calculateErasures(array_view<uint8_t> errors) -> maybe<Polynomial<Size, Size>> {
|
||||
Polynomial<Size, Size> matrix{};
|
||||
for(uint row : range(Size)) {
|
||||
for(uint col : range(Size)) {
|
||||
uint index = Length - (errors[col] + 1);
|
||||
matrix(row, col) = Field::exp(row * index);
|
||||
}
|
||||
}
|
||||
return matrix.invert();
|
||||
}
|
||||
|
||||
template<uint Size>
|
||||
auto correctErasures(array_view<uint8_t> errors) -> int {
|
||||
calculateSyndromes();
|
||||
if(syndromesAreZero()) return 0; //no errors detected
|
||||
if(auto matrix = calculateErasures<Size>(errors)) {
|
||||
Polynomial<Size> factors;
|
||||
for(uint n : range(Size)) factors[n] = syndromes[n];
|
||||
auto errata = matrix() * factors;
|
||||
for(uint m : range(Size)) {
|
||||
message[errors[m]] += errata[m];
|
||||
}
|
||||
calculateSyndromes();
|
||||
if(syndromesAreZero()) return Size; //corrected Size errors
|
||||
return -Size; //failed to correct Size errors
|
||||
}
|
||||
return -Size; //should never occur, but might ...
|
||||
}
|
||||
|
||||
//note: the erasure matrix is generated as a Polynomial<NxN>, where N is the number of errors to correct.
|
||||
//because this is a template parameter, and the actual number of errors may very, this function is needed.
|
||||
//the alternative would be to convert Matrix<Rows, Cols> to a dynamically sized Matrix(Rows, Cols) type,
|
||||
//but this would require heap memory allocations and would be a massive performance penalty.
|
||||
auto correctErrata(array_view<uint8_t> errors) -> int {
|
||||
if(errors.size() >= Parity) return -errors.size(); //too many errors to be correctable
|
||||
|
||||
switch(errors.size()) {
|
||||
case 0: return 0;
|
||||
case 1: return correctErasures< 1>(errors);
|
||||
case 2: return correctErasures< 2>(errors);
|
||||
case 3: return correctErasures< 3>(errors);
|
||||
case 4: return correctErasures< 4>(errors);
|
||||
case 5: return correctErasures< 5>(errors);
|
||||
case 6: return correctErasures< 6>(errors);
|
||||
case 7: return correctErasures< 7>(errors);
|
||||
case 8: return correctErasures< 8>(errors);
|
||||
case 9: return correctErasures< 9>(errors);
|
||||
case 10: return correctErasures<10>(errors);
|
||||
case 11: return correctErasures<11>(errors);
|
||||
case 12: return correctErasures<12>(errors);
|
||||
case 13: return correctErasures<13>(errors);
|
||||
case 14: return correctErasures<14>(errors);
|
||||
case 15: return correctErasures<15>(errors);
|
||||
case 16: return correctErasures<16>(errors);
|
||||
case 17: return correctErasures<17>(errors);
|
||||
case 18: return correctErasures<18>(errors);
|
||||
case 19: return correctErasures<19>(errors);
|
||||
case 20: return correctErasures<20>(errors);
|
||||
case 21: return correctErasures<21>(errors);
|
||||
case 22: return correctErasures<22>(errors);
|
||||
case 23: return correctErasures<23>(errors);
|
||||
case 24: return correctErasures<24>(errors);
|
||||
case 25: return correctErasures<25>(errors);
|
||||
case 26: return correctErasures<26>(errors);
|
||||
case 27: return correctErasures<27>(errors);
|
||||
case 28: return correctErasures<28>(errors);
|
||||
case 29: return correctErasures<29>(errors);
|
||||
case 30: return correctErasures<30>(errors);
|
||||
case 31: return correctErasures<31>(errors);
|
||||
case 32: return correctErasures<32>(errors);
|
||||
}
|
||||
return -errors.size(); //it's possible to correct more errors if the above switch were extended ...
|
||||
}
|
||||
|
||||
//convenience function for when erasures aren't needed
|
||||
auto correctErrors() -> int {
|
||||
auto errors = calculateErrors();
|
||||
return correctErrata(errors);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@@ -51,7 +51,7 @@ struct serializer {
|
||||
return _capacity;
|
||||
}
|
||||
|
||||
template<typename T> auto floatingpoint(T& value) -> serializer& {
|
||||
template<typename T> auto real(T& value) -> serializer& {
|
||||
enum : uint { size = sizeof(T) };
|
||||
//this is rather dangerous, and not cross-platform safe;
|
||||
//but there is no standardized way to export FP-values
|
||||
@@ -108,7 +108,7 @@ struct serializer {
|
||||
|
||||
template<typename T> auto operator()(T& value, typename std::enable_if<has_serialize<T>::value>::type* = 0) -> serializer& { value.serialize(*this); return *this; }
|
||||
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_integral<T>::value>::type* = 0) -> serializer& { return integer(value); }
|
||||
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) -> serializer& { return floatingpoint(value); }
|
||||
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) -> serializer& { return real(value); }
|
||||
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_array<T>::value>::type* = 0) -> serializer& { return array(value); }
|
||||
template<typename T> auto operator()(T& value, uint size, typename std::enable_if<std::is_pointer<T>::value>::type* = 0) -> serializer& { return array(value, size); }
|
||||
|
||||
|
@@ -188,6 +188,10 @@ struct shared_pointer {
|
||||
return manager && manager->strong == 1;
|
||||
}
|
||||
|
||||
auto references() const -> uint {
|
||||
return manager ? manager->strong : 0;
|
||||
}
|
||||
|
||||
auto reset() -> void {
|
||||
if(manager && manager->strong) {
|
||||
//pointer may contain weak references; if strong==0 it may destroy manager
|
||||
|
@@ -194,11 +194,9 @@ public:
|
||||
template<typename... P> inline auto assign(P&&...) -> type&;
|
||||
template<typename T, typename... P> inline auto prepend(const T&, P&&...) -> type&;
|
||||
template<typename... P> inline auto prepend(const nall::string_format&, P&&...) -> type&;
|
||||
inline auto prepend() -> type&;
|
||||
template<typename T> inline auto _prepend(const stringify<T>&) -> type&;
|
||||
template<typename T, typename... P> inline auto append(const T&, P&&...) -> type&;
|
||||
template<typename... P> inline auto append(const nall::string_format&, P&&...) -> type&;
|
||||
inline auto append() -> type&;
|
||||
template<typename T> inline auto _append(const stringify<T>&) -> type&;
|
||||
inline auto length() const -> uint;
|
||||
|
||||
|
@@ -34,19 +34,15 @@ template<typename... P> auto string::assign(P&&... p) -> string& {
|
||||
}
|
||||
|
||||
template<typename T, typename... P> auto string::prepend(const T& value, P&&... p) -> string& {
|
||||
prepend(forward<P>(p)...);
|
||||
if constexpr(sizeof...(p)) prepend(forward<P>(p)...);
|
||||
return _prepend(make_string(value));
|
||||
}
|
||||
|
||||
template<typename... P> auto string::prepend(const nall::string_format& value, P&&... p) -> string& {
|
||||
prepend(forward<P>(p)...);
|
||||
if constexpr(sizeof...(p)) prepend(forward<P>(p)...);
|
||||
return format(value);
|
||||
}
|
||||
|
||||
auto string::prepend() -> string& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> auto string::_prepend(const stringify<T>& source) -> string& {
|
||||
resize(source.size() + size());
|
||||
memory::move(get() + source.size(), get(), size() - source.size());
|
||||
@@ -56,15 +52,13 @@ template<typename T> auto string::_prepend(const stringify<T>& source) -> string
|
||||
|
||||
template<typename T, typename... P> auto string::append(const T& value, P&&... p) -> string& {
|
||||
_append(make_string(value));
|
||||
return append(forward<P>(p)...);
|
||||
if constexpr(sizeof...(p)) append(forward<P>(p)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... P> auto string::append(const nall::string_format& value, P&&... p) -> string& {
|
||||
format(value);
|
||||
return append(forward<P>(p)...);
|
||||
}
|
||||
|
||||
auto string::append() -> string& {
|
||||
if constexpr(sizeof...(p)) append(forward<P>(p)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@@ -66,15 +66,15 @@ struct Node {
|
||||
auto text() const -> nall::string { return value().strip(); }
|
||||
auto string() const -> nall::string { return value().strip(); }
|
||||
auto boolean() const -> bool { return text() == "true"; }
|
||||
auto integer() const -> intmax { return text().integer(); }
|
||||
auto natural() const -> uintmax { return text().natural(); }
|
||||
auto integer() const -> int64_t { return text().integer(); }
|
||||
auto natural() const -> uint64_t { return text().natural(); }
|
||||
auto real() const -> double { return text().real(); }
|
||||
|
||||
auto text(const nall::string& fallback) const -> nall::string { return bool(*this) ? text() : fallback; }
|
||||
auto string(const nall::string& fallback) const -> nall::string { return bool(*this) ? string() : fallback; }
|
||||
auto boolean(bool fallback) const -> bool { return bool(*this) ? boolean() : fallback; }
|
||||
auto integer(intmax fallback) const -> intmax { return bool(*this) ? integer() : fallback; }
|
||||
auto natural(uintmax fallback) const -> uintmax { return bool(*this) ? natural() : fallback; }
|
||||
auto integer(int64_t fallback) const -> int64_t { return bool(*this) ? integer() : fallback; }
|
||||
auto natural(uint64_t fallback) const -> uint64_t { return bool(*this) ? natural() : fallback; }
|
||||
auto real(double fallback) const -> double { return bool(*this) ? real() : fallback; }
|
||||
|
||||
auto setName(const nall::string& name = "") -> Node& { shared->_name = name; return *this; }
|
||||
|
Reference in New Issue
Block a user