126 lines
3.7 KiB
C
126 lines
3.7 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "assetdolphindvd.h"
|
|
#include "asset/asset.h"
|
|
#include "util/string.h"
|
|
#include "util/memory.h"
|
|
|
|
#define ISO_SECTOR_SIZE 2048u
|
|
#define ISO_PVD_SECTOR 16u
|
|
|
|
errorret_t assetInitDolphinDVD(void) {
|
|
DVD_Init();
|
|
DVD_Mount();
|
|
|
|
// ISO 9660 Primary Volume Descriptor is at sector 16.
|
|
u8 *pvd = (u8 *)assetDolphinDVDRead(
|
|
(s64)ISO_PVD_SECTOR * ISO_SECTOR_SIZE, ISO_SECTOR_SIZE
|
|
);
|
|
if(!pvd) errorThrow("Failed to read ISO 9660 PVD.");
|
|
|
|
// Sanity-check: type=1, identifier="CD001"
|
|
if(pvd[0] != 1 || pvd[1] != 'C' || pvd[2] != 'D' ||
|
|
pvd[3] != '0' || pvd[4] != '0' || pvd[5] != '1') {
|
|
memoryFree(pvd);
|
|
errorThrow("Not a valid ISO 9660 disc.");
|
|
}
|
|
|
|
// Root Directory Record starts at PVD+156.
|
|
// ISO 9660 stores multi-byte fields in both byte orders; use the BE copies
|
|
// (offset +6 for LBA, +14 for size) since the GameCube is big-endian.
|
|
u32 rootLBA = assetDolphinDVDReadBigEndian32(pvd + 156 + 6);
|
|
u32 rootSize = assetDolphinDVDReadBigEndian32(pvd + 156 + 14);
|
|
memoryFree(pvd);
|
|
|
|
u8 *dir = (u8 *)assetDolphinDVDRead(
|
|
(s64)rootLBA * ISO_SECTOR_SIZE, rootSize
|
|
);
|
|
if(!dir) errorThrow("Failed to read ISO 9660 root directory.");
|
|
|
|
// Scan directory records for dusk.dsk.
|
|
// ISO 9660 level-1 names are uppercase with a ";1" version suffix, e.g.
|
|
// "DUSK.DSK;1". We strip the suffix before comparing case-insensitively.
|
|
u32 fileLBA = 0, fileSize = 0;
|
|
u32 pos = 0;
|
|
while(pos < rootSize) {
|
|
u8 recLen = dir[pos];
|
|
if(recLen == 0) {
|
|
// Sector padding — skip to the start of the next sector.
|
|
pos = (pos + (ISO_SECTOR_SIZE - 1u)) & ~(ISO_SECTOR_SIZE - 1u);
|
|
continue;
|
|
}
|
|
|
|
u8 flags = dir[pos + 25];
|
|
u8 nameLen = dir[pos + 32];
|
|
|
|
if(!(flags & 0x02) && nameLen > 1) { // skip directories and "." / ".."
|
|
const char_t *isoName = (const char_t *)(dir + pos + 33);
|
|
|
|
// Build a null-terminated copy of the base name (strip ";N" version).
|
|
char_t baseName[32];
|
|
u8 baseLen = 0;
|
|
while(baseLen < nameLen && isoName[baseLen] != ';' && baseLen < 31) {
|
|
baseName[baseLen] = isoName[baseLen];
|
|
baseLen++;
|
|
}
|
|
baseName[baseLen] = '\0';
|
|
|
|
if(stringCompareInsensitive(baseName, ASSET_FILE_NAME) == 0) {
|
|
fileLBA = assetDolphinDVDReadBigEndian32(dir + pos + 6);
|
|
fileSize = assetDolphinDVDReadBigEndian32(dir + pos + 14);
|
|
break;
|
|
}
|
|
}
|
|
pos += recLen;
|
|
}
|
|
memoryFree(dir);
|
|
|
|
if(!fileLBA) errorThrow("Failed to find asset file on ISO.");
|
|
|
|
u8 *data = (u8 *)assetDolphinDVDRead(
|
|
(s64)fileLBA * ISO_SECTOR_SIZE, fileSize
|
|
);
|
|
if(!data) errorThrow("Failed to read asset file from ISO.");
|
|
|
|
zip_error_t zerr;
|
|
zip_source_t *src = zip_source_buffer_create(data, fileSize, 1, &zerr);
|
|
if(!src) {
|
|
memoryFree(data);
|
|
errorThrow("Failed to create zip source from DVD buffer.");
|
|
}
|
|
|
|
ASSET.zip = zip_open_from_source(src, ZIP_RDONLY, &zerr);
|
|
if(!ASSET.zip) {
|
|
zip_source_free(src);
|
|
errorThrow("Failed to open asset zip from DVD.");
|
|
}
|
|
|
|
errorOk();
|
|
}
|
|
|
|
void *assetDolphinDVDRead(const s64 offset, const u32 size) {
|
|
u32 padded = ASSET_DOLPHIN_DVD_ALIGN_UP(size);
|
|
void *buf = memoryAlign(ASSET_DOLPHIN_DVD_ALIGN, padded);
|
|
if(!buf) return NULL;
|
|
DCInvalidateRange(buf, padded);
|
|
static dvdcmdblk block;
|
|
if(DVD_ReadAbs(&block, buf, padded, offset) <= 0) {
|
|
memoryFree(buf);
|
|
return NULL;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
u32 assetDolphinDVDReadBigEndian32(const u8 *p) {
|
|
return ((u32)p[0] << 24) | ((u32)p[1] << 16) | ((u32)p[2] << 8) | (u32)p[3];
|
|
}
|
|
|
|
errorret_t assetDisposeDolphinDVD(void) {
|
|
errorOk();
|
|
}
|