mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 16:09:47 +00:00
add hashing support for PSX cheevos (bin/cue, chd, or real CD)
This commit is contained in:
parent
08ac3ed925
commit
1185433374
@ -177,6 +177,7 @@ OBJ += frontend/frontend_driver.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/interface_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/memory_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.o \
|
||||
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation_cdrom.o \
|
||||
$(LIBRETRO_COMM_DIR)/media/media_detect_cd.o \
|
||||
$(LIBRETRO_COMM_DIR)/lists/string_list.o \
|
||||
$(LIBRETRO_COMM_DIR)/string/stdstring.o \
|
||||
@ -1668,8 +1669,7 @@ ifeq ($(HAVE_CDROM), 1)
|
||||
endif
|
||||
|
||||
DEFINES += -DHAVE_CDROM
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/cdrom/cdrom.o \
|
||||
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation_cdrom.o
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/cdrom/cdrom.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RTGA), 1)
|
||||
|
@ -1118,6 +1118,8 @@ typedef struct
|
||||
const char *path;
|
||||
const char *ext;
|
||||
intfstream_t *stream;
|
||||
intfstream_t *cdrom_stream;
|
||||
intfstream_t *cdrom_track_stream;
|
||||
rcheevos_cheevo_t *cheevo;
|
||||
settings_t *settings;
|
||||
struct http_connection_t *conn;
|
||||
@ -1146,7 +1148,8 @@ enum
|
||||
RCHEEVOS_HTTP_GET = -13,
|
||||
RCHEEVOS_DEACTIVATE = -14,
|
||||
RCHEEVOS_PLAYING = -15,
|
||||
RCHEEVOS_DELAY = -16
|
||||
RCHEEVOS_DELAY = -16,
|
||||
RCHEEVOS_PSX_MD5 = -17
|
||||
};
|
||||
|
||||
static int rcheevos_iterate(rcheevos_coro_t* coro)
|
||||
@ -1155,8 +1158,14 @@ static int rcheevos_iterate(rcheevos_coro_t* coro)
|
||||
const int lynx_header_len = 0x40;
|
||||
ssize_t num_read = 0;
|
||||
size_t to_read = 4096;
|
||||
uint8_t *buffer = NULL;
|
||||
uint8_t *ptr = NULL;
|
||||
const char *end = NULL;
|
||||
const char *ext = NULL;
|
||||
size_t exe_name_size = 0;
|
||||
char exe_name_buffer[32];
|
||||
char *exe_name = NULL;
|
||||
char *scan = NULL;
|
||||
char buffer[2048];
|
||||
|
||||
static const uint32_t genesis_exts[] =
|
||||
{
|
||||
@ -1192,12 +1201,24 @@ static int rcheevos_iterate(rcheevos_coro_t* coro)
|
||||
0
|
||||
};
|
||||
|
||||
static const uint32_t psx_exts[] =
|
||||
{
|
||||
0x0b886782U, /* cue */
|
||||
0x0b88899aU, /* m3u */
|
||||
/*0x0b88af0bU,* toc */
|
||||
/*0x0b88652fU,* ccd */
|
||||
/*0x0b889c67U,* pbp */
|
||||
0x0b8865d4U, /* chd */
|
||||
0
|
||||
};
|
||||
|
||||
static rcheevos_finder_t finders[] =
|
||||
{
|
||||
{RCHEEVOS_SNES_MD5, "SNES (discards header)", snes_exts},
|
||||
{RCHEEVOS_GENESIS_MD5, "Genesis (6Mb padding)", genesis_exts},
|
||||
{RCHEEVOS_LYNX_MD5, "Atari Lynx (discards header)", lynx_exts},
|
||||
{RCHEEVOS_LYNX_MD5, "Atari Lynx (discards header)", lynx_exts},
|
||||
{RCHEEVOS_NES_MD5, "NES (discards header)", NULL},
|
||||
{RCHEEVOS_PSX_MD5, "Playstation (main executable)", psx_exts},
|
||||
{RCHEEVOS_GENERIC_MD5, "Generic (plain content)", NULL},
|
||||
{RCHEEVOS_FILENAME_MD5, "Generic (filename)", NULL}
|
||||
};
|
||||
@ -1243,14 +1264,13 @@ static int rcheevos_iterate(rcheevos_coro_t* coro)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
buffer = (uint8_t*)coro->data + coro->len;
|
||||
ptr = (uint8_t*)coro->data + coro->len;
|
||||
to_read = 4096;
|
||||
|
||||
if (to_read > coro->count)
|
||||
to_read = coro->count;
|
||||
|
||||
num_read = intfstream_read(coro->stream,
|
||||
(void*)buffer, to_read);
|
||||
num_read = intfstream_read(coro->stream, (void*)ptr, to_read);
|
||||
|
||||
if (num_read <= 0)
|
||||
break;
|
||||
@ -1545,6 +1565,186 @@ found:
|
||||
MD5_Final(coro->hash, &coro->md5);
|
||||
CORO_GOTO(RCHEEVOS_GET_GAMEID);
|
||||
|
||||
/**************************************************************************
|
||||
* Info Tries to identify a Playstation game
|
||||
* Input CHEEVOS_VAR_INFO the content info
|
||||
* Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found
|
||||
*************************************************************************/
|
||||
CORO_SUB(RCHEEVOS_PSX_MD5)
|
||||
{
|
||||
ext = path_get_extension(coro->path);
|
||||
|
||||
MD5_Init(&coro->md5);
|
||||
|
||||
/* if we're looking at an m3u file, get the first disc from the playlist */
|
||||
if (string_is_equal_noncase(ext, "m3u"))
|
||||
{
|
||||
coro->cdrom_stream = intfstream_open_file(coro->path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (coro->cdrom_stream)
|
||||
{
|
||||
char disc_path[PATH_MAX_LENGTH];
|
||||
char* tmp;
|
||||
|
||||
intfstream_read(coro->cdrom_stream, buffer, sizeof(buffer));
|
||||
intfstream_close(coro->cdrom_stream);
|
||||
CHEEVOS_FREE(coro->cdrom_stream);
|
||||
coro->cdrom_stream = NULL;
|
||||
|
||||
tmp = buffer;
|
||||
while (*tmp && *tmp != '\n')
|
||||
++tmp;
|
||||
if (tmp > buffer && tmp[-1] == '\r')
|
||||
--tmp;
|
||||
*tmp = '\0';
|
||||
|
||||
fill_pathname_basedir(disc_path, coro->path, sizeof(disc_path));
|
||||
strlcat(disc_path, buffer, sizeof(disc_path));
|
||||
|
||||
free((void*)coro->path);
|
||||
coro->path = strdup(disc_path);
|
||||
ext = path_get_extension(coro->path);
|
||||
}
|
||||
}
|
||||
|
||||
if (string_is_equal_noncase(ext, "chd"))
|
||||
{
|
||||
coro->cdrom_track_stream = intfstream_open_chd_track(coro->path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
coro->cdrom_stream = intfstream_open_file(coro->path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (!coro->cdrom_stream)
|
||||
CORO_STOP();
|
||||
|
||||
coro->cdrom_track_stream = intfstream_open_file_child(coro->cdrom_stream, "TRACK 01");
|
||||
}
|
||||
|
||||
if (coro->cdrom_track_stream)
|
||||
{
|
||||
coro->stream = intfstream_open_file_child(coro->cdrom_track_stream, "SYSTEM.CNF");
|
||||
if (coro->stream)
|
||||
{
|
||||
intfstream_read(coro->stream, buffer, sizeof(buffer));
|
||||
intfstream_close(coro->stream);
|
||||
CHEEVOS_FREE(coro->stream);
|
||||
coro->stream = NULL;
|
||||
|
||||
for (scan = buffer; scan < &buffer[sizeof(buffer)] && *scan; ++scan)
|
||||
{
|
||||
if (strncmp(scan, "BOOT", 4) == 0)
|
||||
{
|
||||
exe_name = scan + 4;
|
||||
while (isspace(*exe_name))
|
||||
++exe_name;
|
||||
if (*exe_name == '=')
|
||||
{
|
||||
++exe_name;
|
||||
while (isspace(*exe_name))
|
||||
++exe_name;
|
||||
|
||||
if (strncmp(exe_name, "cdrom:", 6) == 0)
|
||||
exe_name += 6;
|
||||
if (*exe_name == '\\')
|
||||
++exe_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (*scan && *scan != '\n')
|
||||
++scan;
|
||||
}
|
||||
|
||||
if (exe_name)
|
||||
{
|
||||
scan = exe_name;
|
||||
while (*scan != '\n' && *scan != ';' && *scan != ' ')
|
||||
++scan;
|
||||
*scan = '\0';
|
||||
|
||||
exe_name_size = scan - exe_name;
|
||||
if (exe_name_size < sizeof(exe_name_buffer))
|
||||
{
|
||||
strcpy(exe_name_buffer, exe_name);
|
||||
coro->stream = intfstream_open_file_child(coro->cdrom_track_stream, exe_name);
|
||||
}
|
||||
|
||||
if (coro->stream)
|
||||
{
|
||||
intfstream_read(coro->stream, buffer, sizeof(buffer));
|
||||
|
||||
/* the PSX-E header specifies the executable size as a 4-byte value 28 bytes into the header, which doesn't
|
||||
* include the header itself. We want to include the header in the hash, so append another 2048 to that value.
|
||||
* ASSERT: this results in the same value as coro->stream->file.fp->size */
|
||||
coro->count = 2048 + (((uint8_t)buffer[28 + 3] << 24) | ((uint8_t)buffer[28 + 2] << 16) |
|
||||
((uint8_t)buffer[28 + 1] << 8) | (uint8_t)buffer[28]);
|
||||
|
||||
if (coro->count < CHEEVOS_MB(16)) /* sanity check */
|
||||
{
|
||||
/* there's also a few games that are use a singular engine and only differ via their data files.
|
||||
* luckily, they have unique serial numbers, and use the serial number as the boot file in the
|
||||
* standard way. include the boot executable name in the hash */
|
||||
coro->count += exe_name_size;
|
||||
|
||||
free(coro->data);
|
||||
coro->data = (uint8_t*)malloc(coro->count);
|
||||
memcpy(coro->data, exe_name_buffer, exe_name_size);
|
||||
coro->len = exe_name_size;
|
||||
|
||||
do
|
||||
{
|
||||
to_read = coro->count - coro->len;
|
||||
if (to_read > sizeof(buffer))
|
||||
to_read = sizeof(buffer);
|
||||
|
||||
memcpy((uint8_t*)coro->data + coro->len, buffer, to_read);
|
||||
coro->len += to_read;
|
||||
|
||||
if (coro->len == coro->count)
|
||||
break;
|
||||
|
||||
CORO_YIELD();
|
||||
|
||||
intfstream_read(coro->stream, buffer, sizeof(buffer));
|
||||
} while (true);
|
||||
|
||||
CORO_GOSUB(RCHEEVOS_EVAL_MD5);
|
||||
MD5_Final(coro->hash, &coro->md5);
|
||||
|
||||
intfstream_close(coro->stream);
|
||||
CHEEVOS_FREE(coro->stream);
|
||||
|
||||
intfstream_close(coro->cdrom_track_stream);
|
||||
CHEEVOS_FREE(coro->cdrom_track_stream);
|
||||
|
||||
if (coro->cdrom_stream)
|
||||
{
|
||||
intfstream_close(coro->cdrom_stream);
|
||||
CHEEVOS_FREE(coro->cdrom_stream);
|
||||
}
|
||||
|
||||
CORO_GOTO(RCHEEVOS_GET_GAMEID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intfstream_close(coro->stream);
|
||||
CHEEVOS_FREE(coro->stream);
|
||||
}
|
||||
|
||||
intfstream_close(coro->cdrom_track_stream);
|
||||
CHEEVOS_FREE(coro->cdrom_track_stream);
|
||||
}
|
||||
|
||||
if (coro->cdrom_stream)
|
||||
{
|
||||
intfstream_close(coro->cdrom_stream);
|
||||
CHEEVOS_FREE(coro->cdrom_stream);
|
||||
}
|
||||
|
||||
coro->gameid = 0;
|
||||
CORO_RET();
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Info Tries to identify a "generic" game
|
||||
* Input CHEEVOS_VAR_INFO the content info
|
||||
@ -1640,6 +1840,11 @@ found:
|
||||
|
||||
{
|
||||
int size;
|
||||
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "checking hash %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
|
||||
coro->hash[ 0], coro->hash[ 1], coro->hash[ 2], coro->hash[ 3], coro->hash[ 4], coro->hash[ 5], coro->hash[ 6], coro->hash[ 7],
|
||||
coro->hash[ 8], coro->hash[ 9], coro->hash[10], coro->hash[11], coro->hash[12], coro->hash[13], coro->hash[14], coro->hash[15]);
|
||||
|
||||
size = rc_url_get_gameid(coro->url, sizeof(coro->url), coro->hash);
|
||||
|
||||
if (size < 0)
|
||||
|
@ -550,8 +550,8 @@ Frees the patchdata
|
||||
void rcheevos_free_patchdata(rcheevos_rapatchdata_t* patchdata)
|
||||
{
|
||||
unsigned i = 0, count = 0;
|
||||
const rcheevos_racheevo_t* cheevo = NULL;
|
||||
const rcheevos_ralboard_t* lboard = NULL;
|
||||
rcheevos_racheevo_t* cheevo = NULL;
|
||||
rcheevos_ralboard_t* lboard = NULL;
|
||||
|
||||
cheevo = patchdata->core;
|
||||
|
||||
|
@ -32,7 +32,12 @@ End of setup
|
||||
*****************************************************************************/
|
||||
|
||||
#define RCHEEVOS_TAG "[RCHEEVOS]: "
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define CHEEVOS_FREE(p) do { void* q = (void*)p; p = NULL; if (q != NULL) free(q); } while (0)
|
||||
#else
|
||||
#define CHEEVOS_FREE(p) do { void* q = (void*)p; if (q != NULL) free(q); } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef CHEEVOS_VERBOSE
|
||||
|
||||
|
@ -973,11 +973,11 @@ FILE
|
||||
#include "../libretro-common/streams/memory_stream.c"
|
||||
#ifndef __WINRT__
|
||||
#include "../libretro-common/vfs/vfs_implementation.c"
|
||||
#include "../libretro-common/vfs/vfs_implementation_cdrom.c"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CDROM
|
||||
#include "../libretro-common/cdrom/cdrom.c"
|
||||
#include "../libretro-common/vfs/vfs_implementation_cdrom.c"
|
||||
#include "../libretro-common/media/media_detect_cd.c"
|
||||
#endif
|
||||
|
||||
|
@ -301,7 +301,7 @@ static int cdrom_send_command_win32(const libretro_vfs_implementation_file *stre
|
||||
|
||||
memcpy(sptd.s.Cdb, cmd, cmd_len);
|
||||
|
||||
ioctl_rv = DeviceIoControl(stream->fh, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd,
|
||||
ioctl_rv = DeviceIoControl(stream->parent ? stream->parent->fh : stream->fh, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd,
|
||||
sizeof(sptd), &sptd, sizeof(sptd), &ioctl_bytes, NULL);
|
||||
|
||||
#ifdef CDROM_DEBUG
|
||||
@ -313,7 +313,8 @@ static int cdrom_send_command_win32(const libretro_vfs_implementation_file *stre
|
||||
if (cmd[0] == 0xB9)
|
||||
{
|
||||
double time_taken = (double)(((clock() - t) * 1000) / CLOCKS_PER_SEC);
|
||||
printf("time taken %f ms for DT received length %ld of %" PRId64 " for %02d:%02d:%02d to %02d:%02d:%02d%s req %d cur %d cur_lba %d\n", time_taken, sptd.s.DataTransferLength, len, cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], extra, lba_req, lba_cur, stream->cdrom.cur_lba);
|
||||
printf("time taken %f ms for DT received length %ld of %" PRId64 " for %02d:%02d:%02d to %02d:%02d:%02d%s req %d cur %d cur_lba %d\n",
|
||||
time_taken, sptd.s.DataTransferLength, len, cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], extra, lba_req, lba_cur, stream->track ? stream->track->cur_lba : -1);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
@ -359,7 +360,7 @@ static int cdrom_send_command_linux(const libretro_vfs_implementation_file *stre
|
||||
sgio.mx_sb_len = sense_len;
|
||||
sgio.timeout = 5000;
|
||||
|
||||
rv = ioctl(fileno(stream->fp), SG_IO, &sgio);
|
||||
rv = ioctl(fileno(stream->parent ? stream->parent->fp : stream->fp), SG_IO, &sgio);
|
||||
|
||||
if (rv == -1 || sgio.info & SG_INFO_CHECK)
|
||||
return 1;
|
||||
@ -442,7 +443,7 @@ static int cdrom_send_command(libretro_vfs_implementation_file *stream, CDROM_CM
|
||||
|
||||
lba_req = cdrom_msf_to_lba(cmd[3], cmd[4], cmd[5]);
|
||||
|
||||
if (stream->cdrom.last_frame_valid && lba_req == stream->cdrom.last_frame_lba)
|
||||
if (lba_req == stream->track->last_frame_lba)
|
||||
{
|
||||
/* use cached frame */
|
||||
cached_read = true;
|
||||
@ -451,7 +452,7 @@ static int cdrom_send_command(libretro_vfs_implementation_file *stream, CDROM_CM
|
||||
fflush(stdout);
|
||||
#endif
|
||||
/* assumes request_len is always equal to the size of last_frame */
|
||||
memcpy(xfer_buf_pos, stream->cdrom.last_frame, sizeof(stream->cdrom.last_frame));
|
||||
memcpy(xfer_buf_pos, stream->track->last_frame, sizeof(stream->track->last_frame));
|
||||
}
|
||||
|
||||
}
|
||||
@ -497,18 +498,20 @@ retry:
|
||||
memcpy((char*)buf + copied_bytes, xfer_buf_pos + skip, copy_len);
|
||||
copied_bytes += copy_len;
|
||||
|
||||
if (read_cd && !cached_read && request_len >= 2352)
|
||||
if (stream->track)
|
||||
{
|
||||
unsigned frame_end = cdrom_msf_to_lba(cmd[6], cmd[7], cmd[8]);
|
||||
if (read_cd && !cached_read && request_len >= 2352)
|
||||
{
|
||||
unsigned frame_end = cdrom_msf_to_lba(cmd[6], cmd[7], cmd[8]);
|
||||
|
||||
/* cache the last received frame */
|
||||
memcpy(stream->cdrom.last_frame, xfer_buf_pos, sizeof(stream->cdrom.last_frame));
|
||||
stream->cdrom.last_frame_valid = true;
|
||||
/* the ending frame is never actually read, so what we really just read is the one right before that */
|
||||
stream->cdrom.last_frame_lba = frame_end - 1;
|
||||
/* cache the last received frame */
|
||||
memcpy(stream->track->last_frame, xfer_buf_pos, sizeof(stream->track->last_frame));
|
||||
/* the ending frame is never actually read, so what we really just read is the one right before that */
|
||||
stream->track->last_frame_lba = frame_end - 1;
|
||||
}
|
||||
else
|
||||
stream->track->last_frame_lba = (unsigned)-1;
|
||||
}
|
||||
else
|
||||
stream->cdrom.last_frame_valid = false;
|
||||
|
||||
#if 0
|
||||
printf("Frame %d, adding %" PRId64 " to buf_pos, is now %" PRId64 ". skip is %" PRId64 "\n", i, request_len, (xfer_buf_pos + request_len) - xfer_buf, skip);
|
||||
@ -1231,7 +1234,7 @@ int cdrom_read(libretro_vfs_implementation_file *stream, cdrom_group_timeouts_t
|
||||
|
||||
if (rv)
|
||||
{
|
||||
stream->cdrom.last_frame_valid = false;
|
||||
stream->track->last_frame_lba = (unsigned)-1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1428,7 +1431,7 @@ struct string_list* cdrom_get_available_drives(void)
|
||||
for (i = 0; i < sizeof(DWORD) * 8; i++)
|
||||
{
|
||||
char path[] = {"a:\\"};
|
||||
char cdrom_path[] = {"cdrom://a:/drive-track01.bin"};
|
||||
char cdrom_path[] = {"cdrom://a:/drive.cue"};
|
||||
|
||||
path[0] += i;
|
||||
cdrom_path[8] += i;
|
||||
@ -1444,16 +1447,16 @@ struct string_list* cdrom_get_available_drives(void)
|
||||
char drive_model[32] = {0};
|
||||
char drive_string[33] = {0};
|
||||
union string_list_elem_attr attr = {0};
|
||||
RFILE *file = filestream_open(cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0);
|
||||
libretro_vfs_implementation_file *stream;
|
||||
RFILE *cdrom_file = filestream_open(cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0);
|
||||
bool is_cdrom = false;
|
||||
|
||||
if (!file)
|
||||
continue;
|
||||
if (cdrom_file)
|
||||
{
|
||||
libretro_vfs_implementation_file* stream = filestream_get_vfs_handle(cdrom_file);
|
||||
cdrom_get_inquiry(stream, drive_model, sizeof(drive_model), &is_cdrom);
|
||||
|
||||
stream = filestream_get_vfs_handle(file);
|
||||
cdrom_get_inquiry(stream, drive_model, sizeof(drive_model), &is_cdrom);
|
||||
filestream_close(file);
|
||||
filestream_close(cdrom_file);
|
||||
}
|
||||
|
||||
if (!is_cdrom)
|
||||
continue;
|
||||
|
@ -1037,6 +1037,10 @@ void chd_close(chd_file *chd)
|
||||
for (i = 0 ; i < 4 ; i++)
|
||||
{
|
||||
void* codec = NULL;
|
||||
|
||||
if (!chd->codecintf[i])
|
||||
continue;
|
||||
|
||||
switch (chd->codecintf[i]->compression)
|
||||
{
|
||||
case CHD_CODEC_CD_LZMA:
|
||||
|
@ -45,16 +45,28 @@ void chdstream_close(chdstream_t *stream);
|
||||
|
||||
ssize_t chdstream_read(chdstream_t *stream, void *data, size_t bytes);
|
||||
|
||||
ssize_t chdstream_read_file(chdstream_t *stream, int64_t file_start, void *data, size_t bytes);
|
||||
|
||||
int chdstream_getc(chdstream_t *stream);
|
||||
|
||||
int chdstream_getc_file(chdstream_t *stream, int64_t file_start);
|
||||
|
||||
char *chdstream_gets(chdstream_t *stream, char *buffer, size_t len);
|
||||
|
||||
char *chdstream_gets_file(chdstream_t *stream, int64_t file_start, char *buffer, size_t len);
|
||||
|
||||
uint64_t chdstream_tell(chdstream_t *stream);
|
||||
|
||||
uint64_t chdstream_tell_file(chdstream_t *stream, int64_t file_start);
|
||||
|
||||
void chdstream_rewind(chdstream_t *stream);
|
||||
|
||||
void chdstream_rewind_file(chdstream_t *stream, int64_t file_start);
|
||||
|
||||
int64_t chdstream_seek(chdstream_t *stream, int64_t offset, int whence);
|
||||
|
||||
int64_t chdstream_seek_file(chdstream_t* stream, const char* filename);
|
||||
|
||||
ssize_t chdstream_get_size(chdstream_t *stream);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
@ -63,6 +63,8 @@ int64_t filestream_truncate(RFILE *stream, int64_t length);
|
||||
**/
|
||||
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints);
|
||||
|
||||
RFILE* filestream_open_child(RFILE *stream, const char* path);
|
||||
|
||||
int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position);
|
||||
|
||||
int64_t filestream_read(RFILE *stream, void *data, int64_t len);
|
||||
|
@ -36,7 +36,8 @@ enum intfstream_type
|
||||
{
|
||||
INTFSTREAM_FILE = 0,
|
||||
INTFSTREAM_MEMORY,
|
||||
INTFSTREAM_CHD
|
||||
INTFSTREAM_CHD,
|
||||
INTFSTREAM_CHD_FILE
|
||||
};
|
||||
|
||||
typedef struct intfstream_internal intfstream_internal_t, intfstream_t;
|
||||
@ -97,6 +98,9 @@ int intfstream_flush(intfstream_internal_t *intf);
|
||||
intfstream_t* intfstream_open_file(const char *path,
|
||||
unsigned mode, unsigned hints);
|
||||
|
||||
intfstream_t* intfstream_open_file_child(intfstream_internal_t *intf,
|
||||
const char* path);
|
||||
|
||||
intfstream_t *intfstream_open_memory(void *data,
|
||||
unsigned mode, unsigned hints, uint64_t size);
|
||||
|
||||
|
@ -38,28 +38,40 @@ RETRO_BEGIN_DECLS
|
||||
typedef void* HANDLE;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CDROM
|
||||
typedef struct
|
||||
{
|
||||
char *cue_buf;
|
||||
char* cue_buf;
|
||||
size_t cue_len;
|
||||
int64_t byte_pos;
|
||||
size_t cue_pos;
|
||||
char drive;
|
||||
} vfs_cdrom_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int64_t byte_pos;
|
||||
unsigned cur_lba;
|
||||
unsigned char cur_track;
|
||||
unsigned char cur_min;
|
||||
unsigned char cur_sec;
|
||||
unsigned char cur_frame;
|
||||
unsigned char cur_track;
|
||||
unsigned cur_lba;
|
||||
|
||||
unsigned sector_size;
|
||||
unsigned char sector_header_size;
|
||||
unsigned char mode;
|
||||
|
||||
unsigned last_frame_lba;
|
||||
unsigned char last_frame[2352];
|
||||
bool last_frame_valid;
|
||||
} vfs_cdrom_t;
|
||||
#endif
|
||||
} vfs_cdrom_track_t;
|
||||
|
||||
enum vfs_scheme
|
||||
{
|
||||
VFS_SCHEME_NONE = 0,
|
||||
VFS_SCHEME_CDROM
|
||||
VFS_SCHEME_CDROM,
|
||||
VFS_SCHEME_CDROM_TRACK,
|
||||
VFS_SCHEME_CDROM_FILE,
|
||||
VFS_SCHEME_CUE,
|
||||
VFS_SCHEME_CUE_BIN,
|
||||
VFS_SCHEME_CUE_BIN_FILE
|
||||
};
|
||||
|
||||
#ifndef __WINRT__
|
||||
@ -81,10 +93,18 @@ struct libretro_vfs_implementation_file
|
||||
uint64_t mappos;
|
||||
uint64_t mapsize;
|
||||
uint8_t *mapped;
|
||||
enum vfs_scheme scheme;
|
||||
#ifdef HAVE_CDROM
|
||||
vfs_cdrom_t cdrom;
|
||||
|
||||
#ifdef VFS_FRONTEND
|
||||
struct retro_vfs_file_handle* parent;
|
||||
#else
|
||||
struct libretro_vfs_implementation_file* parent;
|
||||
#endif
|
||||
uint64_t parent_offset;
|
||||
|
||||
enum vfs_scheme scheme;
|
||||
|
||||
vfs_cdrom_t cdrom;
|
||||
vfs_cdrom_track_t* track;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -24,13 +24,16 @@
|
||||
#define __LIBRETRO_SDK_VFS_IMPLEMENTATION_CDROM_H
|
||||
|
||||
#include <vfs/vfs.h>
|
||||
|
||||
#ifdef HAVE_CDROM
|
||||
#include <cdrom/cdrom.h>
|
||||
#endif
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
int64_t retro_vfs_file_seek_cdrom(libretro_vfs_implementation_file *stream, int64_t offset, int whence);
|
||||
|
||||
void retro_vfs_file_open_cdrom(
|
||||
bool retro_vfs_file_open_cdrom(
|
||||
libretro_vfs_implementation_file *stream,
|
||||
const char *path, unsigned mode, unsigned hints);
|
||||
|
||||
@ -43,9 +46,30 @@ int64_t retro_vfs_file_read_cdrom(libretro_vfs_implementation_file *stream,
|
||||
|
||||
int retro_vfs_file_error_cdrom(libretro_vfs_implementation_file *stream);
|
||||
|
||||
#ifdef HAVE_CDROM
|
||||
const cdrom_toc_t* retro_vfs_file_get_cdrom_toc(void);
|
||||
#endif
|
||||
|
||||
const vfs_cdrom_t* retro_vfs_file_get_cdrom_position(const libretro_vfs_implementation_file *stream);
|
||||
libretro_vfs_implementation_file* retro_vfs_file_open_cdrom_track(
|
||||
libretro_vfs_implementation_file* stream, const char* track);
|
||||
|
||||
int retro_vfs_file_close_cdrom_track(libretro_vfs_implementation_file *stream);
|
||||
|
||||
int64_t retro_vfs_file_seek_cdrom_track(libretro_vfs_implementation_file *stream, int64_t offset, int whence);
|
||||
|
||||
int64_t retro_vfs_file_read_cdrom_track(libretro_vfs_implementation_file *stream, void *s, uint64_t len);
|
||||
|
||||
int64_t retro_vfs_file_tell_cdrom_track(libretro_vfs_implementation_file *stream);
|
||||
|
||||
|
||||
libretro_vfs_implementation_file* retro_vfs_file_open_cdrom_file(
|
||||
libretro_vfs_implementation_file* stream, const char* path);
|
||||
|
||||
int64_t retro_vfs_file_seek_cdrom_file(libretro_vfs_implementation_file *stream, int64_t offset, int whence);
|
||||
|
||||
int64_t retro_vfs_file_read_cdrom_file(libretro_vfs_implementation_file *stream, void *s, uint64_t len);
|
||||
|
||||
int64_t retro_vfs_file_tell_cdrom_file(libretro_vfs_implementation_file *stream);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
|
@ -26,8 +26,6 @@
|
||||
#include <file/file_path.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
/*#define MEDIA_CUE_PARSE_DEBUG*/
|
||||
|
||||
static void media_zero_trailing_spaces(char *buf, size_t len)
|
||||
{
|
||||
int i;
|
||||
@ -65,203 +63,6 @@ static bool media_skip_spaces(const char **buf, size_t len)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fill in "info" with detected CD info. Use this when you have a cue file and want it parsed to find the first data track and any pregap info. */
|
||||
bool media_detect_cd_info_cue(const char *path, media_detect_cd_info_t *info)
|
||||
{
|
||||
RFILE *file = NULL;
|
||||
char *line = NULL;
|
||||
char track_path[PATH_MAX_LENGTH] = {0};
|
||||
char track_abs_path[PATH_MAX_LENGTH] = {0};
|
||||
char track_mode[11] = {0};
|
||||
bool found_file = false;
|
||||
bool found_track = false;
|
||||
unsigned first_data_track = 0;
|
||||
uint64_t data_track_pregap_bytes = 0;
|
||||
|
||||
if (string_is_empty(path) || !info)
|
||||
return false;
|
||||
|
||||
file = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ, 0);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
printf("[MEDIA] Could not open cue path for reading: %s\n", path);
|
||||
fflush(stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!filestream_eof(file) && (line = filestream_getline(file)))
|
||||
{
|
||||
size_t len = 0;
|
||||
const char *command = NULL;
|
||||
|
||||
if (string_is_empty(line))
|
||||
{
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
len = strlen(line);
|
||||
|
||||
command = line;
|
||||
|
||||
media_skip_spaces(&command, len);
|
||||
|
||||
if (!found_file && !strncasecmp(command, "FILE", 4))
|
||||
{
|
||||
const char *file = command + 4;
|
||||
media_skip_spaces(&file, len - 4);
|
||||
|
||||
if (!string_is_empty(file))
|
||||
{
|
||||
const char *file_end = NULL;
|
||||
size_t file_len = 0;
|
||||
bool quoted = false;
|
||||
|
||||
if (file[0] == '"')
|
||||
{
|
||||
quoted = true;
|
||||
file++;
|
||||
}
|
||||
|
||||
if (quoted)
|
||||
file_end = strchr(file, '\"');
|
||||
else
|
||||
file_end = strchr(file, ' ');
|
||||
|
||||
if (file_end)
|
||||
{
|
||||
file_len = file_end - file;
|
||||
memcpy(track_path, file, file_len);
|
||||
found_file = true;
|
||||
#ifdef MEDIA_CUE_PARSE_DEBUG
|
||||
printf("Found file: %s\n", track_path);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (found_file && !found_track && !strncasecmp(command, "TRACK", 5))
|
||||
{
|
||||
const char *track = command + 5;
|
||||
media_skip_spaces(&track, len - 5);
|
||||
|
||||
if (!string_is_empty(track))
|
||||
{
|
||||
unsigned track_number = 0;
|
||||
sscanf(track, "%d", &track_number);
|
||||
#ifdef MEDIA_CUE_PARSE_DEBUG
|
||||
printf("Found track: %d\n", track_number);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
track++;
|
||||
|
||||
if (track[0] && track[0] != ' ' && track[0] != '\t')
|
||||
track++;
|
||||
|
||||
if (!string_is_empty(track))
|
||||
{
|
||||
media_skip_spaces(&track, strlen(track));
|
||||
#ifdef MEDIA_CUE_PARSE_DEBUG
|
||||
printf("Found track type: %s\n", track);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
if (!strncasecmp(track, "MODE", 4))
|
||||
{
|
||||
first_data_track = track_number;
|
||||
found_track = true;
|
||||
strlcpy(track_mode, track, sizeof(track_mode));
|
||||
}
|
||||
else
|
||||
found_file = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (found_file && found_track && first_data_track && !strncasecmp(command, "INDEX", 5))
|
||||
{
|
||||
const char *index = command + 5;
|
||||
media_skip_spaces(&index, len - 5);
|
||||
|
||||
if (!string_is_empty(index))
|
||||
{
|
||||
unsigned index_number = 0;
|
||||
sscanf(index, "%d", &index_number);
|
||||
|
||||
if (index_number == 1)
|
||||
{
|
||||
const char *pregap = index + 1;
|
||||
|
||||
if (pregap[0] && pregap[0] != ' ' && pregap[0] != '\t')
|
||||
pregap++;
|
||||
|
||||
if (!string_is_empty(pregap))
|
||||
{
|
||||
media_skip_spaces(&pregap, strlen(pregap));
|
||||
found_file = false;
|
||||
found_track = false;
|
||||
|
||||
if (first_data_track && !string_is_empty(track_mode))
|
||||
{
|
||||
unsigned track_sector_size = 0;
|
||||
unsigned track_mode_number = 0;
|
||||
|
||||
if (strlen(track_mode) == 10)
|
||||
{
|
||||
sscanf(track_mode, "MODE%d/%d", &track_mode_number, &track_sector_size);
|
||||
#ifdef MEDIA_CUE_PARSE_DEBUG
|
||||
printf("Found track mode %d with sector size %d\n", track_mode_number, track_sector_size);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
if ((track_mode_number == 1 || track_mode_number == 2) && track_sector_size)
|
||||
{
|
||||
unsigned min = 0;
|
||||
unsigned sec = 0;
|
||||
unsigned frame = 0;
|
||||
sscanf(pregap, "%02d:%02d:%02d", &min, &sec, &frame);
|
||||
|
||||
if (min || sec || frame || strstr(pregap, "00:00:00"))
|
||||
{
|
||||
data_track_pregap_bytes = ((min * 60 + sec) * 75 + frame) * track_sector_size;
|
||||
#ifdef MEDIA_CUE_PARSE_DEBUG
|
||||
printf("Found pregap of %02d:%02d:%02d (bytes: %" PRIu64 ")\n", min, sec, frame, data_track_pregap_bytes);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
filestream_close(file);
|
||||
|
||||
if (!string_is_empty(track_path))
|
||||
{
|
||||
if (strstr(track_path, "/") || strstr(track_path, "\\"))
|
||||
{
|
||||
printf("using path %s\n", track_path);
|
||||
fflush(stdout);
|
||||
return media_detect_cd_info(track_path, data_track_pregap_bytes, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
fill_pathname_basedir(track_abs_path, path, sizeof(track_abs_path));
|
||||
strlcat(track_abs_path, track_path, sizeof(track_abs_path));
|
||||
printf("using abs path %s\n", track_abs_path);
|
||||
fflush(stdout);
|
||||
return media_detect_cd_info(track_abs_path, data_track_pregap_bytes, info);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Fill in "info" with detected CD info. Use this when you want to open a specific track file directly, and the pregap is known. */
|
||||
bool media_detect_cd_info(const char *path, uint64_t pregap_bytes, media_detect_cd_info_t *info)
|
||||
{
|
||||
|
@ -43,6 +43,8 @@ struct chdstream
|
||||
uint32_t frame_size;
|
||||
/* Offset of data within frame */
|
||||
uint32_t frame_offset;
|
||||
/* Size of frame header */
|
||||
uint32_t frame_header_size;
|
||||
/* Number of frames per hunk */
|
||||
uint32_t frames_per_hunk;
|
||||
/* First frame of track in chd */
|
||||
@ -360,11 +362,62 @@ ssize_t chdstream_read(chdstream_t *stream, void *data, size_t bytes)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
ssize_t chdstream_read_file(chdstream_t *stream, int64_t file_start, void *data, size_t bytes)
|
||||
{
|
||||
uint8_t buffer[SECTOR_SIZE];
|
||||
int64_t file_frame = (stream->offset - file_start) / stream->frame_size;
|
||||
int64_t file_frame_offset = (stream->offset - file_start) - (file_frame * stream->frame_size) - stream->frame_header_size;
|
||||
ssize_t bytes_read = 0;
|
||||
|
||||
if (file_frame_offset >= stream->frame_header_size + 2048)
|
||||
{
|
||||
++file_frame;
|
||||
file_frame_offset = -1;
|
||||
}
|
||||
if (file_frame_offset < 0)
|
||||
{
|
||||
stream->offset = file_start + (file_frame * stream->frame_size) + stream->frame_header_size;
|
||||
file_frame_offset = 0;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
const int64_t remaining = 2048 - file_frame_offset;
|
||||
if (bytes < remaining)
|
||||
{
|
||||
if (bytes > 0)
|
||||
{
|
||||
chdstream_read(stream, data, bytes);
|
||||
bytes_read += bytes;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
chdstream_read(stream, data, remaining);
|
||||
bytes_read += remaining;
|
||||
bytes -= remaining;
|
||||
|
||||
++file_frame;
|
||||
stream->offset = file_start + (file_frame * stream->frame_size) + stream->frame_header_size;
|
||||
file_frame_offset = 0;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
int chdstream_getc(chdstream_t *stream)
|
||||
{
|
||||
char c = 0;
|
||||
|
||||
if (chdstream_read(stream, &c, sizeof(c) != sizeof(c)))
|
||||
if (chdstream_read(stream, &c, sizeof(c)) != sizeof(c))
|
||||
return EOF;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int chdstream_getc_file(chdstream_t *stream, int64_t file_start)
|
||||
{
|
||||
char c = 0;
|
||||
|
||||
if (chdstream_read_file(stream, file_start, &c, sizeof(c)) != sizeof(c))
|
||||
return EOF;
|
||||
|
||||
return c;
|
||||
@ -385,16 +438,43 @@ char *chdstream_gets(chdstream_t *stream, char *buffer, size_t len)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *chdstream_gets_file(chdstream_t *stream, int64_t file_start, char *buffer, size_t len)
|
||||
{
|
||||
int c;
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
while (offset < len && (c = chdstream_getc_file(stream, file_start)) != EOF)
|
||||
buffer[offset++] = c;
|
||||
|
||||
if (offset < len)
|
||||
buffer[offset] = '\0';
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
uint64_t chdstream_tell(chdstream_t *stream)
|
||||
{
|
||||
return stream->offset;
|
||||
}
|
||||
|
||||
uint64_t chdstream_tell_file(chdstream_t *stream, int64_t file_start)
|
||||
{
|
||||
const int64_t file_frame = (stream->offset - file_start) / stream->frame_size;
|
||||
const int64_t file_frame_offset = (stream->offset - file_start) - (file_frame * stream->frame_size) - stream->frame_header_size;
|
||||
return (file_frame * 2048) + file_frame_offset;
|
||||
}
|
||||
|
||||
void chdstream_rewind(chdstream_t *stream)
|
||||
{
|
||||
stream->offset = 0;
|
||||
}
|
||||
|
||||
void chdstream_rewind_file(chdstream_t *stream, int64_t file_start)
|
||||
{
|
||||
stream->offset = file_start;
|
||||
}
|
||||
|
||||
int64_t chdstream_seek(chdstream_t *stream, int64_t offset, int whence)
|
||||
{
|
||||
int64_t new_offset;
|
||||
@ -424,6 +504,74 @@ int64_t chdstream_seek(chdstream_t *stream, int64_t offset, int whence)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t chdstream_seek_file(chdstream_t* stream, const char* path)
|
||||
{
|
||||
uint8_t buffer[SECTOR_SIZE], *tmp;
|
||||
int sector, path_length;
|
||||
|
||||
const char* slash = strrchr(path, '\\');
|
||||
if (slash)
|
||||
{
|
||||
/* navigate the path to the directory record for the file */
|
||||
const int dir_length = (int)(slash - path);
|
||||
memcpy(buffer, path, dir_length);
|
||||
buffer[dir_length] = '\0';
|
||||
|
||||
if (chdstream_seek_file(stream, (const char*)buffer) < 0)
|
||||
return -1;
|
||||
|
||||
path += dir_length + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The boot record or primary volume descriptor is always at sector 16 and will contain a "CD001" marker */
|
||||
chdstream_seek(stream, 16 * stream->frame_size, SEEK_SET);
|
||||
chdstream_read(stream, buffer, sizeof(buffer));
|
||||
|
||||
if (stream->frame_header_size == 0)
|
||||
{
|
||||
if (memcmp(&buffer[25], "CD001", 5) == 0)
|
||||
stream->frame_header_size = 24;
|
||||
else
|
||||
stream->frame_header_size = 16;
|
||||
}
|
||||
|
||||
/* the directory_record starts at 156 bytes into the sector.
|
||||
* the sector containing the table of contents is a 3 byte value that is 2 bytes into the directory_record. */
|
||||
{
|
||||
const int offset = stream->frame_header_size + 156 + 2;
|
||||
sector = buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16);
|
||||
}
|
||||
chdstream_seek(stream, sector * stream->frame_size, SEEK_SET);
|
||||
}
|
||||
|
||||
/* process the table of contents */
|
||||
chdstream_read(stream, buffer, sizeof(buffer));
|
||||
|
||||
path_length = strlen(path);
|
||||
tmp = buffer + stream->frame_header_size;
|
||||
while (tmp < buffer + sizeof(buffer))
|
||||
{
|
||||
/* the first byte of the record is the length of the record - if 0, we reached the end of the data */
|
||||
if (!*tmp)
|
||||
break;
|
||||
|
||||
/* filename is 33 bytes into the record and the format is "FILENAME;version" or "DIRECTORY" */
|
||||
if ((tmp[33 + path_length] == ';' || tmp[33 + path_length] == '\0') &&
|
||||
strncasecmp((const char*)(tmp + 33), path, path_length) == 0)
|
||||
{
|
||||
/* the file contents are in the sector identified in bytes 2-4 of the record */
|
||||
sector = tmp[2] | (tmp[3] << 8) | (tmp[4] << 16);
|
||||
return chdstream_seek(stream, sector * stream->frame_size, SEEK_SET);
|
||||
}
|
||||
|
||||
/* the first byte of the record is the length of the record */
|
||||
tmp += tmp[0];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t chdstream_get_size(chdstream_t *stream)
|
||||
{
|
||||
return stream->track_end;
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <streams/file_stream.h>
|
||||
#define VFS_FRONTEND
|
||||
#include <vfs/vfs_implementation.h>
|
||||
#include <vfs/vfs_implementation_cdrom.h>
|
||||
|
||||
static const int64_t vfs_error_return_value = -1;
|
||||
|
||||
@ -176,6 +177,37 @@ RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
|
||||
return output;
|
||||
}
|
||||
|
||||
RFILE* filestream_open_child(RFILE *stream, const char* path)
|
||||
{
|
||||
struct retro_vfs_file_handle *fp = NULL;
|
||||
RFILE* output = NULL;
|
||||
|
||||
switch (stream->hfile->scheme)
|
||||
{
|
||||
case VFS_SCHEME_CDROM:
|
||||
case VFS_SCHEME_CUE:
|
||||
fp = retro_vfs_file_open_cdrom_track(stream->hfile, path);
|
||||
break;
|
||||
|
||||
case VFS_SCHEME_CDROM_TRACK:
|
||||
case VFS_SCHEME_CUE_BIN:
|
||||
fp = retro_vfs_file_open_cdrom_file(stream->hfile, path);
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
output = (RFILE*)malloc(sizeof(RFILE));
|
||||
output->error_flag = false;
|
||||
output->eof_flag = false;
|
||||
output->hfile = fp;
|
||||
return output;
|
||||
}
|
||||
|
||||
char* filestream_gets(RFILE *stream, char *s, size_t len)
|
||||
{
|
||||
int c = 0;
|
||||
|
@ -52,6 +52,7 @@ struct intfstream_internal
|
||||
struct
|
||||
{
|
||||
int32_t track;
|
||||
int32_t file_start;
|
||||
chdstream_t *fp;
|
||||
} chd;
|
||||
#endif
|
||||
@ -68,12 +69,12 @@ int64_t intfstream_get_size(intfstream_internal_t *intf)
|
||||
return filestream_get_size(intf->file.fp);
|
||||
case INTFSTREAM_MEMORY:
|
||||
return intf->memory.buf.size;
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
return chdstream_get_size(intf->chd.fp);
|
||||
#else
|
||||
break;
|
||||
case INTFSTREAM_CHD:
|
||||
return chdstream_get_size(intf->chd.fp);
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -95,9 +96,11 @@ bool intfstream_resize(intfstream_internal_t *intf, intfstream_info_t *info)
|
||||
memstream_set_buffer(intf->memory.buf.data,
|
||||
intf->memory.buf.size);
|
||||
break;
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@ -122,15 +125,15 @@ bool intfstream_open(intfstream_internal_t *intf, const char *path,
|
||||
if (!intf->memory.fp)
|
||||
return false;
|
||||
break;
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
intf->chd.fp = chdstream_open(path, intf->chd.track);
|
||||
if (!intf->chd.fp)
|
||||
return false;
|
||||
break;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -149,6 +152,8 @@ int intfstream_flush(intfstream_internal_t *intf)
|
||||
case INTFSTREAM_CHD:
|
||||
/* Should we stub this for these interfaces? */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -169,12 +174,17 @@ int intfstream_close(intfstream_internal_t *intf)
|
||||
if (intf->memory.fp)
|
||||
memstream_close(intf->memory.fp);
|
||||
return 0;
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
if (intf->chd.fp)
|
||||
chdstream_close(intf->chd.fp);
|
||||
#endif
|
||||
return 0;
|
||||
case INTFSTREAM_CHD_FILE:
|
||||
/* handle owned by INTFSTREAM_CHD */
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -202,13 +212,13 @@ void *intfstream_init(intfstream_info_t *info)
|
||||
if (!intfstream_resize(intf, info))
|
||||
goto error;
|
||||
break;
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
intf->chd.track = info->chd.track;
|
||||
break;
|
||||
#else
|
||||
goto error;
|
||||
#endif
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
return intf;
|
||||
@ -246,12 +256,12 @@ int64_t intfstream_seek(intfstream_internal_t *intf, int64_t offset, int whence)
|
||||
}
|
||||
case INTFSTREAM_MEMORY:
|
||||
return (int64_t)memstream_seek(intf->memory.fp, offset, whence);
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
return (int64_t)chdstream_seek(intf->chd.fp, offset, whence);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -268,12 +278,14 @@ int64_t intfstream_read(intfstream_internal_t *intf, void *s, uint64_t len)
|
||||
return filestream_read(intf->file.fp, s, len);
|
||||
case INTFSTREAM_MEMORY:
|
||||
return memstream_read(intf->memory.fp, s, len);
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
return chdstream_read(intf->chd.fp, s, len);
|
||||
#else
|
||||
break;
|
||||
case INTFSTREAM_CHD_FILE:
|
||||
return chdstream_read_file(intf->chd.fp, intf->chd.file_start, s, len);
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -292,6 +304,7 @@ int64_t intfstream_write(intfstream_internal_t *intf,
|
||||
case INTFSTREAM_MEMORY:
|
||||
return memstream_write(intf->memory.fp, s, len);
|
||||
case INTFSTREAM_CHD:
|
||||
case INTFSTREAM_CHD_FILE:
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -312,12 +325,14 @@ char *intfstream_gets(intfstream_internal_t *intf,
|
||||
case INTFSTREAM_MEMORY:
|
||||
return memstream_gets(intf->memory.fp,
|
||||
buffer, (size_t)len);
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
return chdstream_gets(intf->chd.fp, buffer, len);
|
||||
#else
|
||||
break;
|
||||
case INTFSTREAM_CHD_FILE:
|
||||
return chdstream_gets_file(intf->chd.fp, intf->chd.file_start, buffer, len);
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -334,12 +349,14 @@ int intfstream_getc(intfstream_internal_t *intf)
|
||||
return filestream_getc(intf->file.fp);
|
||||
case INTFSTREAM_MEMORY:
|
||||
return memstream_getc(intf->memory.fp);
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
return chdstream_getc(intf->chd.fp);
|
||||
#else
|
||||
break;
|
||||
case INTFSTREAM_CHD_FILE:
|
||||
return chdstream_getc_file(intf->chd.fp, intf->chd.file_start);
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -356,12 +373,14 @@ int64_t intfstream_tell(intfstream_internal_t *intf)
|
||||
return (int64_t)filestream_tell(intf->file.fp);
|
||||
case INTFSTREAM_MEMORY:
|
||||
return (int64_t)memstream_pos(intf->memory.fp);
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
return (int64_t)chdstream_tell(intf->chd.fp);
|
||||
#else
|
||||
break;
|
||||
case INTFSTREAM_CHD_FILE:
|
||||
return (int64_t)chdstream_tell_file(intf->chd.fp, intf->chd.file_start);
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -377,10 +396,15 @@ void intfstream_rewind(intfstream_internal_t *intf)
|
||||
case INTFSTREAM_MEMORY:
|
||||
memstream_rewind(intf->memory.fp);
|
||||
break;
|
||||
case INTFSTREAM_CHD:
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
chdstream_rewind(intf->chd.fp);
|
||||
break;
|
||||
case INTFSTREAM_CHD_FILE:
|
||||
chdstream_rewind_file(intf->chd.fp, intf->chd.file_start);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -398,7 +422,7 @@ void intfstream_putc(intfstream_internal_t *intf, int c)
|
||||
case INTFSTREAM_MEMORY:
|
||||
memstream_putc(intf->memory.fp, c);
|
||||
break;
|
||||
case INTFSTREAM_CHD:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -429,6 +453,55 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
intfstream_t* intfstream_open_file_child(intfstream_internal_t* intf, const char* path)
|
||||
{
|
||||
intfstream_info_t info;
|
||||
intfstream_t *fd = NULL;
|
||||
|
||||
if (!intf)
|
||||
return NULL;
|
||||
|
||||
info.type = INTFSTREAM_FILE;
|
||||
fd = (intfstream_t*)intfstream_init(&info);
|
||||
|
||||
if (!fd)
|
||||
return NULL;
|
||||
|
||||
switch (intf->type)
|
||||
{
|
||||
case INTFSTREAM_FILE:
|
||||
fd->file.fp = filestream_open_child(intf->file.fp, path);
|
||||
break;
|
||||
case INTFSTREAM_MEMORY:
|
||||
break;
|
||||
#ifdef HAVE_CHD
|
||||
case INTFSTREAM_CHD:
|
||||
if (chdstream_seek_file(intf->chd.fp, path) < 0)
|
||||
goto error;
|
||||
|
||||
fd->type = INTFSTREAM_CHD_FILE;
|
||||
fd->chd.fp = intf->chd.fp;
|
||||
fd->chd.file_start = chdstream_tell(intf->chd.fp);
|
||||
return fd;
|
||||
#endif
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!fd->file.fp)
|
||||
goto error;
|
||||
|
||||
return fd;
|
||||
|
||||
error:
|
||||
if (fd)
|
||||
{
|
||||
intfstream_close(fd);
|
||||
free(fd);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
intfstream_t *intfstream_open_memory(void *data,
|
||||
unsigned mode, unsigned hints, uint64_t size)
|
||||
{
|
||||
|
@ -179,16 +179,13 @@
|
||||
#endif
|
||||
|
||||
#include <vfs/vfs_implementation.h>
|
||||
#include <vfs/vfs_implementation_cdrom.h>
|
||||
#include <libretro.h>
|
||||
#include <memmap.h>
|
||||
#include <encodings/utf.h>
|
||||
#include <compat/fopen_utf8.h>
|
||||
#include <file/file_path.h>
|
||||
|
||||
#ifdef HAVE_CDROM
|
||||
#include <vfs/vfs_implementation_cdrom.h>
|
||||
#endif
|
||||
|
||||
#define RFILE_HINT_UNBUFFERED (1 << 8)
|
||||
|
||||
int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, int64_t offset, int whence)
|
||||
@ -198,10 +195,25 @@ int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, i
|
||||
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
switch (stream->scheme)
|
||||
{
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
return retro_vfs_file_seek_cdrom(stream, offset, whence);
|
||||
case VFS_SCHEME_CDROM:
|
||||
return retro_vfs_file_seek_cdrom(stream, offset, whence);
|
||||
case VFS_SCHEME_CDROM_TRACK:
|
||||
return retro_vfs_file_seek_cdrom_track(stream, offset, whence);
|
||||
case VFS_SCHEME_CDROM_FILE:
|
||||
return retro_vfs_file_seek_cdrom_file(stream, offset, whence);
|
||||
#endif
|
||||
case VFS_SCHEME_CUE:
|
||||
break;
|
||||
case VFS_SCHEME_CUE_BIN:
|
||||
return retro_vfs_file_seek_cdrom_track(stream, offset, whence);
|
||||
case VFS_SCHEME_CUE_BIN_FILE:
|
||||
return retro_vfs_file_seek_cdrom_file(stream, offset, whence);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* VC2005 and up have a special 64-bit fseek */
|
||||
#ifdef ATLEAST_VC2005
|
||||
return _fseeki64(stream->fp, offset, whence);
|
||||
@ -403,28 +415,28 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
}
|
||||
stream->fd = fd;
|
||||
#else
|
||||
FILE *fp;
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
{
|
||||
retro_vfs_file_open_cdrom(stream, path, mode, hints);
|
||||
#if defined(_WIN32) && !defined(_XBOX)
|
||||
if (!stream->fh)
|
||||
if (!retro_vfs_file_open_cdrom(stream, path, mode, hints))
|
||||
goto error;
|
||||
#else
|
||||
if (!stream->fp)
|
||||
goto error;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
FILE *fp;
|
||||
const char* ext;
|
||||
|
||||
fp = (FILE*)fopen_utf8(path, mode_str);
|
||||
|
||||
if (!fp)
|
||||
goto error;
|
||||
|
||||
stream->fp = fp;
|
||||
|
||||
ext = path_get_extension(path);
|
||||
if (string_is_equal_case_insensitive(ext, "cue"))
|
||||
stream->scheme = VFS_SCHEME_CUE;
|
||||
}
|
||||
/* Regarding setvbuf:
|
||||
*
|
||||
@ -437,11 +449,17 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
*/
|
||||
/* TODO: this is only useful for a few platforms, find which and add ifdef */
|
||||
#if !defined(PS2) && !defined(PSP)
|
||||
if (stream->scheme != VFS_SCHEME_CDROM)
|
||||
switch (stream->scheme)
|
||||
{
|
||||
stream->buf = (char*)calloc(1, 0x4000);
|
||||
if (stream->fp)
|
||||
setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
|
||||
case VFS_SCHEME_CDROM:
|
||||
case VFS_SCHEME_CDROM_FILE:
|
||||
case VFS_SCHEME_CDROM_TRACK:
|
||||
break;
|
||||
default:
|
||||
stream->buf = (char*)calloc(1, 0x4000);
|
||||
if (stream->fp)
|
||||
setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@ -491,18 +509,6 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
stream->size = orbisLseek(stream->fd, 0, SEEK_END);
|
||||
orbisLseek(stream->fd, 0, SEEK_SET);
|
||||
#else
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
{
|
||||
retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET);
|
||||
retro_vfs_file_seek_cdrom(stream, 0, SEEK_END);
|
||||
|
||||
stream->size = retro_vfs_file_tell_impl(stream);
|
||||
|
||||
retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
|
||||
retro_vfs_file_seek_internal(stream, 0, SEEK_END);
|
||||
@ -524,14 +530,28 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
switch (stream->scheme)
|
||||
{
|
||||
retro_vfs_file_close_cdrom(stream);
|
||||
goto end;
|
||||
}
|
||||
#ifdef HAVE_CDROM
|
||||
case VFS_SCHEME_CDROM:
|
||||
retro_vfs_file_close_cdrom(stream);
|
||||
goto end;
|
||||
|
||||
case VFS_SCHEME_CDROM_TRACK:
|
||||
retro_vfs_file_close_cdrom_track(stream);
|
||||
goto end;
|
||||
|
||||
case VFS_SCHEME_CDROM_FILE:
|
||||
break;
|
||||
#endif
|
||||
|
||||
case VFS_SCHEME_CUE_BIN:
|
||||
retro_vfs_file_close_cdrom_track(stream);
|
||||
goto end;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
if (stream->fp)
|
||||
@ -556,8 +576,9 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
|
||||
close(stream->fd);
|
||||
#endif
|
||||
}
|
||||
#ifdef HAVE_CDROM
|
||||
|
||||
end:
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->cdrom.cue_buf)
|
||||
free(stream->cdrom.cue_buf);
|
||||
#endif
|
||||
@ -616,10 +637,23 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
|
||||
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
switch (stream->scheme)
|
||||
{
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
return retro_vfs_file_tell_cdrom(stream);
|
||||
case VFS_SCHEME_CDROM:
|
||||
return retro_vfs_file_tell_cdrom(stream);
|
||||
case VFS_SCHEME_CDROM_TRACK:
|
||||
return retro_vfs_file_tell_cdrom_track(stream);
|
||||
case VFS_SCHEME_CDROM_FILE:
|
||||
return retro_vfs_file_tell_cdrom_file(stream);
|
||||
#endif
|
||||
case VFS_SCHEME_CUE_BIN:
|
||||
return retro_vfs_file_tell_cdrom_track(stream);
|
||||
case VFS_SCHEME_CUE_BIN_FILE:
|
||||
return retro_vfs_file_tell_cdrom_file(stream);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#ifdef ORBIS
|
||||
{
|
||||
int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR);
|
||||
@ -676,10 +710,25 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
|
||||
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
switch (stream->scheme)
|
||||
{
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
return retro_vfs_file_read_cdrom(stream, s, len);
|
||||
case VFS_SCHEME_CDROM:
|
||||
return retro_vfs_file_read_cdrom(stream, s, len);
|
||||
case VFS_SCHEME_CDROM_TRACK:
|
||||
return retro_vfs_file_read_cdrom_track(stream, s, len);
|
||||
case VFS_SCHEME_CDROM_FILE:
|
||||
return retro_vfs_file_read_cdrom_file(stream, s, len);
|
||||
#endif
|
||||
case VFS_SCHEME_CUE:
|
||||
break;
|
||||
case VFS_SCHEME_CUE_BIN:
|
||||
return retro_vfs_file_read_cdrom_track(stream, s, len);
|
||||
case VFS_SCHEME_CUE_BIN_FILE:
|
||||
return retro_vfs_file_read_cdrom_file(stream, s, len);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#ifdef ORBIS
|
||||
if (orbisRead(stream->fd, s, (size_t)len) < 0)
|
||||
return -1;
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user