283 lines
7.3 KiB
D
283 lines
7.3 KiB
D
module usbextreme;
|
|
import std.stdint;
|
|
|
|
enum USBEXTREME_NAME_LENGTH = 32;
|
|
enum USBEXTREME_ID_LENGTH = 15;
|
|
enum USBEXTREME_NAME_EXT_LENGTH = 10;
|
|
enum USBEXTREME_MAGIC = 0x08;
|
|
enum USBEXTREME_HEADER_SIZE = UsbExtremeBase.sizeof;
|
|
enum USBEXTREME_FILESTAT_NAME_LENGTH = USBEXTREME_NAME_LENGTH + USBEXTREME_NAME_EXT_LENGTH;
|
|
enum USBEXTREME_PREFIX = "ul.";
|
|
enum USBEXTREME_CRC32_LENGTH = 8;
|
|
enum USBEXTREME_FILENAME_LENGTH = USBEXTREME_ID_LENGTH + USBEXTREME_CRC32_LENGTH + 1;
|
|
enum USBEXTREME_PART_SIZE_V0 = 0x40000000;
|
|
|
|
align(1) struct UsbExtremeBase {
|
|
align(1):
|
|
uint8_t[6 + USBEXTREME_ID_LENGTH + USBEXTREME_NAME_LENGTH] empty;
|
|
uint8_t magic;
|
|
uint8_t[10] empty2;
|
|
}
|
|
|
|
align(1) struct UsbExtremeV0 {
|
|
align(1):
|
|
char[USBEXTREME_NAME_LENGTH] name;
|
|
char[USBEXTREME_ID_LENGTH] id;
|
|
uint8_t parts;
|
|
SCECdvdMediaType type;
|
|
uint8_t[4] empty;
|
|
uint8_t magic;
|
|
uint8_t[USBEXTREME_NAME_EXT_LENGTH] empty2;
|
|
}
|
|
|
|
align(1) struct UsbExtremeV1 {
|
|
align(1):
|
|
char[USBEXTREME_NAME_LENGTH] name;
|
|
char[USBEXTREME_ID_LENGTH] id;
|
|
uint8_t parts;
|
|
SCECdvdMediaType type;
|
|
uint16_t partSize;
|
|
uint8_t videoMode;
|
|
UsbExtremeVersion usbExtremeVersion;
|
|
uint8_t magic;
|
|
char[USBEXTREME_NAME_EXT_LENGTH] nameExt;
|
|
}
|
|
|
|
struct UsbExtremeHeaders {
|
|
const(void)* firstHeader;
|
|
const(UsbExtremeBase)[] headers;
|
|
size_t numHeaders;
|
|
size_t headersLen;
|
|
UsbExtremeVersion oueVersion;
|
|
}
|
|
|
|
struct UsbExtremeFilestat {
|
|
size_t offset;
|
|
char[USBEXTREME_FILESTAT_NAME_LENGTH] name;
|
|
SCECdvdMediaType type;
|
|
size_t partSize;
|
|
uint8_t videoMode;
|
|
UsbExtremeVersion usbExtremeVersion;
|
|
}
|
|
|
|
enum UsbExtremeVersion : uint8_t {
|
|
V0 = 0x00,
|
|
V1,
|
|
Unknown
|
|
}
|
|
|
|
enum SCECdvdMediaType : uint8_t {
|
|
SCECdNODISC = 0x00,
|
|
SCECdDETCT,
|
|
SCECdDETCTCD,
|
|
SCECdDETCTDVDS,
|
|
SCECdDETCTDVDD,
|
|
SCECdUNKNOWN,
|
|
|
|
SCECdPSCD = 0x10,
|
|
SCECdPSCDDA,
|
|
SCECdPS2CD,
|
|
SCECdPS2CDDA,
|
|
SCECdPS2DVD,
|
|
|
|
SCECdCDDA = 0xFD,
|
|
SCECdDVDV,
|
|
SCECdIllegalMediaoffset
|
|
}
|
|
|
|
extern(D) bool isOue(const(void)[] headers) {
|
|
auto headersOeu = castArray!(const UsbExtremeBase)(headers);
|
|
|
|
foreach (header; headersOeu) {
|
|
if (header.magic != USBEXTREME_MAGIC) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
extern(D) UsbExtremeVersion getVersion(UsbExtremeVersion usbExtremeVersion) {
|
|
switch (usbExtremeVersion) {
|
|
case UsbExtremeVersion.V0: {
|
|
return UsbExtremeVersion.V0;
|
|
}
|
|
|
|
case UsbExtremeVersion.V1: {
|
|
return UsbExtremeVersion.V1;
|
|
}
|
|
|
|
default: {
|
|
return UsbExtremeVersion.V0;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern(D) size_t oueNumHeaders(const(void)[] headers) {
|
|
auto headersLen = castArray!(UsbExtremeBase)(headers).length;
|
|
|
|
if (!isOue(headers)) {
|
|
return -1;
|
|
}
|
|
|
|
return headersLen;
|
|
}
|
|
|
|
extern(D) size_t ouePointHeaders(ref const(UsbExtremeBase)[] headers, const(void)[] rawHeaders) {
|
|
auto headersLen = oueNumHeaders(rawHeaders);
|
|
|
|
if (headersLen <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
headers = castArray!(const(UsbExtremeBase))(rawHeaders)[];
|
|
return headersLen;
|
|
}
|
|
|
|
extern(D) UsbExtremeVersion oueHeadersVersion(const(void)[] headers) {
|
|
auto headersOeu = castArray!(UsbExtremeV1)(headers);
|
|
auto firstVersion = UsbExtremeVersion.V0;
|
|
|
|
if (!isOue(headers)) {
|
|
return UsbExtremeVersion.Unknown;
|
|
}
|
|
|
|
foreach (i, header; headersOeu) {
|
|
if (i == 0) {
|
|
firstVersion = getVersion(header.usbExtremeVersion);
|
|
} else {
|
|
if (firstVersion != getVersion(header.usbExtremeVersion)) {
|
|
return UsbExtremeVersion.V0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return firstVersion;
|
|
}
|
|
|
|
extern(D) int oueReadHeaders(ref UsbExtremeHeaders headers, const(void)[] rawHeaders) {
|
|
auto oueVersion = oueHeadersVersion(rawHeaders);
|
|
auto numHeaders = oueNumHeaders(rawHeaders);
|
|
|
|
if (!isOue(rawHeaders)) {
|
|
return -1;
|
|
}
|
|
|
|
if (oueVersion == UsbExtremeVersion.Unknown) {
|
|
return -1;
|
|
}
|
|
|
|
auto headersArr = castArray!(UsbExtremeBase)(rawHeaders);
|
|
headers = UsbExtremeHeaders(rawHeaders.ptr,
|
|
headersArr,
|
|
numHeaders,
|
|
rawHeaders.length,
|
|
oueVersion);
|
|
return 1;
|
|
}
|
|
|
|
extern(D) UsbExtremeFilestat[] oueRead(UsbExtremeFilestat[] filestats, const(UsbExtremeHeaders) headers) {
|
|
auto headersFull = castArray!(UsbExtremeV1)(headers.headers);
|
|
auto headersLength = headersFull.length;
|
|
int fileStatsLength = 0;
|
|
|
|
foreach (i, ref filestat; filestats) {
|
|
if (headersLength == 0) {
|
|
return filestats[0..i];
|
|
}
|
|
|
|
auto header = headersFull[i];
|
|
oueGetName(filestat.name, headers, i);
|
|
auto headerVersion = getVersion(header.usbExtremeVersion);
|
|
auto partSize = ouePartSize(headers, i);
|
|
uint8_t videoMode = 0;
|
|
|
|
if (headerVersion >= 1) {
|
|
videoMode = header.videoMode;
|
|
}
|
|
|
|
filestat.partSize = partSize;
|
|
filestat.type = header.type;
|
|
filestat.offset = i;
|
|
filestat.videoMode = videoMode;
|
|
filestat.usbExtremeVersion = headerVersion;
|
|
headersLength -= 1;
|
|
fileStatsLength += 1;
|
|
}
|
|
|
|
return filestats[0..fileStatsLength];
|
|
}
|
|
|
|
extern(D) void oueGetName(char[] dest, const(UsbExtremeHeaders) headers, size_t offset) {
|
|
auto headersFull = castArray!(UsbExtremeV1)(headers.headers);
|
|
auto header = headersFull[offset];
|
|
auto headerVersion = getVersion(header.usbExtremeVersion);
|
|
|
|
dest[0..USBEXTREME_NAME_LENGTH] = header.name[0..$];
|
|
|
|
if (headerVersion >= 1) {
|
|
dest[USBEXTREME_NAME_LENGTH..USBEXTREME_NAME_LENGTH + USBEXTREME_NAME_EXT_LENGTH] = header.nameExt[0..$];
|
|
}
|
|
}
|
|
|
|
extern(D) char[] oueFilename(char[] buffer, const(UsbExtremeHeaders) headers, size_t offset) {
|
|
import core.stdc.stdio : snprintf;
|
|
|
|
char[USBEXTREME_FILESTAT_NAME_LENGTH] name;
|
|
auto headersFull = castArray!(UsbExtremeV1)(headers.headers);
|
|
auto header = headersFull[offset];
|
|
auto startupId = header.id[3..$];
|
|
|
|
oueGetName(name, headers, offset);
|
|
|
|
auto crc = crc32(name);
|
|
|
|
snprintf(buffer.ptr, buffer.length, "%s%08X.%s", USBEXTREME_PREFIX.ptr, crc, startupId.ptr);
|
|
return buffer;
|
|
}
|
|
|
|
extern(D) size_t ouePartSize(const(UsbExtremeHeaders) headers, size_t offset) {
|
|
auto headersFull = castArray!(UsbExtremeV1)(headers.headers);
|
|
auto header = headersFull[offset];
|
|
auto headerVersion = getVersion(header.usbExtremeVersion);
|
|
|
|
if (headerVersion >= 1) {
|
|
return header.partSize * (2 ^^ 20);
|
|
} else {
|
|
return USBEXTREME_PART_SIZE_V0;
|
|
}
|
|
}
|
|
|
|
private R[] castArray(R, T) (T[] array) { // Workaround for https://issues.dlang.org/show_bug.cgi?id=20088
|
|
auto ptr = array.ptr;
|
|
auto castPtr = cast(R*) ptr;
|
|
return castPtr[0..((array.length * T.sizeof) / R.sizeof)];
|
|
}
|
|
|
|
__gshared uint[1024] crcTable;
|
|
private uint crc32(char[] name) {
|
|
int crc;
|
|
int count;
|
|
|
|
foreach (table; 0 .. 256) {
|
|
crc = table << 24;
|
|
|
|
for (count = 8; count > 0; count--) {
|
|
if (crc < 0) {
|
|
crc = crc << 1;
|
|
} else {
|
|
crc = (crc << 1) ^ 0x04C11DB7;
|
|
}
|
|
}
|
|
|
|
crcTable[255 - table] = crc;
|
|
}
|
|
|
|
do {
|
|
auto singleChar = name[count++];
|
|
crc = crcTable[singleChar ^ ((crc >> 24) & 0xFF)] ^ ((crc << 8) & 0xFFFFFF00);
|
|
} while (name[count - 1] != 0);
|
|
|
|
return crc;
|
|
}
|