mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 07:17:45 -04:00
core, groundwork for chd support
still needs some scrutiny, and build integration is missing
This commit is contained in:
parent
4da84f9454
commit
15ca715228
18 changed files with 411 additions and 100 deletions
200
pico/cart.c
200
pico/cart.c
|
@ -16,6 +16,11 @@
|
|||
#include "file_stream_transforms.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_LIBCHDR)
|
||||
#include "libchdr/chd.h"
|
||||
#include "libchdr/cdrom.h"
|
||||
#endif
|
||||
|
||||
static int rom_alloc_size;
|
||||
static const char *rom_exts[] = { "bin", "gen", "smd", "iso", "sms", "gg", "sg" };
|
||||
|
||||
|
@ -102,6 +107,19 @@ struct zip_file {
|
|||
unsigned int pos;
|
||||
};
|
||||
|
||||
#if defined(USE_LIBCHDR)
|
||||
struct chd_struct {
|
||||
pm_file file;
|
||||
int fpos;
|
||||
int sectorsize;
|
||||
chd_file *chd;
|
||||
int unitbytes;
|
||||
int hunkunits;
|
||||
u8 *hunk;
|
||||
int hunknum;
|
||||
};
|
||||
#endif
|
||||
|
||||
pm_file *pm_open(const char *path)
|
||||
{
|
||||
pm_file *file = NULL;
|
||||
|
@ -228,6 +246,50 @@ cso_failed:
|
|||
if (f != NULL) fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
#if defined(USE_LIBCHDR)
|
||||
else if (strcasecmp(ext, "chd") == 0)
|
||||
{
|
||||
struct chd_struct *chd = NULL;
|
||||
chd_file *cf = NULL;
|
||||
const chd_header *head;
|
||||
|
||||
if (chd_open(path, CHD_OPEN_READ, NULL, &cf) != CHDERR_NONE)
|
||||
goto chd_failed;
|
||||
|
||||
// sanity check
|
||||
head = chd_get_header(cf);
|
||||
if ((head->hunkbytes == 0) || (head->hunkbytes % CD_FRAME_SIZE))
|
||||
goto chd_failed;
|
||||
|
||||
chd = calloc(1, sizeof(*chd));
|
||||
if (chd == NULL)
|
||||
goto chd_failed;
|
||||
chd->hunk = (u8 *)malloc(head->hunkbytes);
|
||||
if (!chd->hunk)
|
||||
goto chd_failed;
|
||||
|
||||
chd->chd = cf;
|
||||
chd->unitbytes = head->unitbytes;
|
||||
chd->hunkunits = head->hunkbytes / head->unitbytes;
|
||||
chd->sectorsize = CD_MAX_SECTOR_DATA; // default to RAW mode
|
||||
|
||||
chd->fpos = 0;
|
||||
chd->hunknum = -1;
|
||||
|
||||
chd->file.file = chd;
|
||||
chd->file.type = PMT_CHD;
|
||||
// subchannel data is skipped, remove it from total size
|
||||
chd->file.size = chd_get_header(cf)->logicalbytes / CD_FRAME_SIZE * CD_MAX_SECTOR_DATA;
|
||||
strncpy(chd->file.ext, ext, sizeof(chd->file.ext) - 1);
|
||||
return &chd->file;
|
||||
|
||||
chd_failed:
|
||||
/* invalid CHD file */
|
||||
if (chd != NULL) free(chd);
|
||||
if (cf != NULL) chd_close(cf);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* not a zip, treat as uncompressed file */
|
||||
f = fopen(path, "rb");
|
||||
|
@ -255,6 +317,88 @@ cso_failed:
|
|||
return file;
|
||||
}
|
||||
|
||||
void pm_sectorsize(int length, pm_file *stream)
|
||||
{
|
||||
// CHD reading needs to know how much binary data is in one data sector(=unit)
|
||||
#if defined(USE_LIBCHDR)
|
||||
if (stream->type == PMT_CHD) {
|
||||
struct chd_struct *chd = stream->file;
|
||||
chd->sectorsize = length;
|
||||
if (chd->sectorsize > chd->unitbytes)
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "cd: sector size %d too large for unit %d", chd->sectorsize, chd->unitbytes);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USE_LIBCHDR)
|
||||
static size_t _pm_read_chd(void *ptr, size_t bytes, pm_file *stream, int is_audio)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (stream->type == PMT_CHD) {
|
||||
struct chd_struct *chd = stream->file;
|
||||
// calculate sector and offset in sector
|
||||
int sectsz = is_audio ? CD_MAX_SECTOR_DATA : chd->sectorsize;
|
||||
int sector = chd->fpos / sectsz;
|
||||
int offset = chd->fpos - (sector * sectsz);
|
||||
// calculate hunk and sector offset in hunk
|
||||
int hunknum = sector / chd->hunkunits;
|
||||
int hunksec = sector - (hunknum * chd->hunkunits);
|
||||
int hunkofs = hunksec * chd->unitbytes;
|
||||
|
||||
while (bytes != 0) {
|
||||
// data left in current sector
|
||||
int len = sectsz - offset;
|
||||
|
||||
// update hunk cache if needed
|
||||
if (hunknum != chd->hunknum) {
|
||||
chd_read(chd->chd, hunknum, chd->hunk);
|
||||
chd->hunknum = hunknum;
|
||||
}
|
||||
if (len > bytes)
|
||||
len = bytes;
|
||||
|
||||
#ifdef CPU_IS_LE
|
||||
if (is_audio) {
|
||||
// convert big endian audio samples
|
||||
u16 *dst = ptr, v;
|
||||
u8 *src = chd->hunk + hunkofs + offset;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
v = *src++ << 8; *dst++ = v | *src++;
|
||||
v = *src++ << 8; *dst++ = v | *src++;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
memcpy(ptr, chd->hunk + hunkofs + offset, len);
|
||||
|
||||
// house keeping
|
||||
ret += len;
|
||||
chd->fpos += len;
|
||||
bytes -= len;
|
||||
|
||||
// no need to advance internals if there's no more data to read
|
||||
if (bytes) {
|
||||
ptr += len;
|
||||
offset = 0;
|
||||
|
||||
sector ++;
|
||||
hunksec ++;
|
||||
hunkofs += chd->unitbytes;
|
||||
if (hunksec >= chd->hunkunits) {
|
||||
hunksec = 0;
|
||||
hunkofs = 0;
|
||||
hunknum ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t pm_read(void *ptr, size_t bytes, pm_file *stream)
|
||||
{
|
||||
int ret;
|
||||
|
@ -355,12 +499,46 @@ size_t pm_read(void *ptr, size_t bytes, pm_file *stream)
|
|||
index_end = cso->index[block+1];
|
||||
}
|
||||
}
|
||||
#if defined(USE_LIBCHDR)
|
||||
else if (stream->type == PMT_CHD)
|
||||
{
|
||||
ret = _pm_read_chd(ptr, bytes, stream, 0);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t pm_read_audio(void *ptr, size_t bytes, pm_file *stream)
|
||||
{
|
||||
#if !(CPU_IS_LE)
|
||||
if (stream->type == PMT_UNCOMPRESSED)
|
||||
{
|
||||
// convert little endian audio samples from WAV file
|
||||
int ret = pm_read(ptr, bytes, stream);
|
||||
u16 *dst = ptr, v;
|
||||
u8 *src = ptr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ret; i += 4) {
|
||||
v = *src++; *dst++ = v | (*src++ << 8);
|
||||
v = *src++; *dst++ = v | (*src++ << 8);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#if defined(USE_LIBCHDR)
|
||||
if (stream->type == PMT_CHD)
|
||||
{
|
||||
return _pm_read_chd(ptr, bytes, stream, 1);
|
||||
}
|
||||
#endif
|
||||
return pm_read(ptr, bytes, stream);
|
||||
}
|
||||
|
||||
int pm_seek(pm_file *stream, long offset, int whence)
|
||||
{
|
||||
if (stream->type == PMT_UNCOMPRESSED)
|
||||
|
@ -421,6 +599,19 @@ int pm_seek(pm_file *stream, long offset, int whence)
|
|||
}
|
||||
return cso->fpos_out;
|
||||
}
|
||||
#if defined(USE_LIBCHDR)
|
||||
else if (stream->type == PMT_CHD)
|
||||
{
|
||||
struct chd_struct *chd = stream->file;
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_CUR: chd->fpos += offset; break;
|
||||
case SEEK_SET: chd->fpos = offset; break;
|
||||
case SEEK_END: chd->fpos = stream->size - offset; break;
|
||||
}
|
||||
return chd->fpos;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
@ -446,6 +637,15 @@ int pm_close(pm_file *fp)
|
|||
free(fp->param);
|
||||
fclose(fp->file);
|
||||
}
|
||||
#if defined(USE_LIBCHDR)
|
||||
else if (fp->type == PMT_CHD)
|
||||
{
|
||||
struct chd_struct *chd = fp->file;
|
||||
chd_close(chd->chd);
|
||||
if (chd->hunk)
|
||||
free(chd->hunk);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
ret = EOF;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue