mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2025-01-26 01:51:15 +01:00
[Core/CD] added support for platform-configurable CD file virtualization
This commit is contained in:
parent
4017d379e6
commit
2a19462f0d
174
core/cd_hw/cdd.c
174
core/cd_hw/cdd.c
@ -2,7 +2,7 @@
|
|||||||
* Genesis Plus
|
* Genesis Plus
|
||||||
* CD drive processor & CD-DA fader
|
* CD drive processor & CD-DA fader
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012-2016 Eke-Eke (Genesis Plus GX)
|
* Copyright (C) 2012-2017 Eke-Eke (Genesis Plus GX)
|
||||||
*
|
*
|
||||||
* Redistribution and use of this code or any derivative works are permitted
|
* Redistribution and use of this code or any derivative works are permitted
|
||||||
* provided that the following conditions are met:
|
* provided that the following conditions are met:
|
||||||
@ -157,6 +157,19 @@ static const char extensions[SUPPORTED_EXT][16] =
|
|||||||
};
|
};
|
||||||
|
|
||||||
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
||||||
|
|
||||||
|
static int seek64_wrap(void *f,ogg_int64_t off,int whence){
|
||||||
|
return cdStreamSeek(f,off,whence);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ov_callbacks cb =
|
||||||
|
{
|
||||||
|
(size_t (*)(void *, size_t, size_t, void *)) cdStreamRead,
|
||||||
|
(int (*)(void *, ogg_int64_t, int)) seek64_wrap,
|
||||||
|
(int (*)(void *)) cdStreamClose,
|
||||||
|
(long (*)(void *)) cdStreamTell
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef DISABLE_MANY_OGG_OPEN_FILES
|
#ifdef DISABLE_MANY_OGG_OPEN_FILES
|
||||||
static void ogg_free(int i)
|
static void ogg_free(int i)
|
||||||
{
|
{
|
||||||
@ -170,9 +183,10 @@ static void ogg_free(int i)
|
|||||||
cdd.toc.tracks[i].vf.seekable = 1;
|
cdd.toc.tracks[i].vf.seekable = 1;
|
||||||
|
|
||||||
/* reset file reading position */
|
/* reset file reading position */
|
||||||
fseek(cdd.toc.tracks[i].fd, 0, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[i].fd, 0, SEEK_SET);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void cdd_init(int samplerate)
|
void cdd_init(int samplerate)
|
||||||
@ -255,21 +269,21 @@ int cdd_context_load(uint8 *state)
|
|||||||
if (cdd.toc.sub)
|
if (cdd.toc.sub)
|
||||||
{
|
{
|
||||||
/* 96 bytes per sector */
|
/* 96 bytes per sector */
|
||||||
fseek(cdd.toc.sub, lba * 96, SEEK_SET);
|
cdStreamSeek(cdd.toc.sub, lba * 96, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* seek to current track position */
|
/* seek to current track position */
|
||||||
if (cdd.toc.tracks[cdd.index].type)
|
if (cdd.toc.tracks[cdd.index].type)
|
||||||
{
|
{
|
||||||
/* DATA track */
|
/* DATA track */
|
||||||
fseek(cdd.toc.tracks[cdd.index].fd, lba * cdd.sectorSize, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[cdd.index].fd, lba * cdd.sectorSize, SEEK_SET);
|
||||||
}
|
}
|
||||||
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
||||||
else if (cdd.toc.tracks[cdd.index].vf.seekable)
|
else if (cdd.toc.tracks[cdd.index].vf.seekable)
|
||||||
{
|
{
|
||||||
#ifdef DISABLE_MANY_OGG_OPEN_FILES
|
#ifdef DISABLE_MANY_OGG_OPEN_FILES
|
||||||
/* VORBIS file need to be opened first */
|
/* VORBIS file need to be opened first */
|
||||||
ov_open(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0);
|
ov_open_callbacks(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0,cb);
|
||||||
#endif
|
#endif
|
||||||
/* VORBIS AUDIO track */
|
/* VORBIS AUDIO track */
|
||||||
ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, (lba * 588) - cdd.toc.tracks[cdd.index].offset);
|
ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, (lba * 588) - cdd.toc.tracks[cdd.index].offset);
|
||||||
@ -278,7 +292,7 @@ int cdd_context_load(uint8 *state)
|
|||||||
else if (cdd.toc.tracks[cdd.index].fd)
|
else if (cdd.toc.tracks[cdd.index].fd)
|
||||||
{
|
{
|
||||||
/* PCM AUDIO track */
|
/* PCM AUDIO track */
|
||||||
fseek(cdd.toc.tracks[cdd.index].fd, (lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[cdd.index].fd, (lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bufferptr;
|
return bufferptr;
|
||||||
@ -289,7 +303,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
char fname[256+10];
|
char fname[256+10];
|
||||||
char line[128];
|
char line[128];
|
||||||
char *ptr, *lptr;
|
char *ptr, *lptr;
|
||||||
FILE *fd;
|
cdStream *fd;
|
||||||
|
|
||||||
/* assume CD image file by default */
|
/* assume CD image file by default */
|
||||||
int isCDfile = 1;
|
int isCDfile = 1;
|
||||||
@ -298,7 +312,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
cdd_unload();
|
cdd_unload();
|
||||||
|
|
||||||
/* open file */
|
/* open file */
|
||||||
fd = fopen(filename, "rb");
|
fd = cdStreamOpen(filename);
|
||||||
if (!fd) return (-1);
|
if (!fd) return (-1);
|
||||||
|
|
||||||
/* save a copy of base filename */
|
/* save a copy of base filename */
|
||||||
@ -310,7 +324,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
int len;
|
int len;
|
||||||
|
|
||||||
/* read first 16 bytes */
|
/* read first 16 bytes */
|
||||||
fread(header, 0x10, 1, fd);
|
cdStreamRead(header, 0x10, 1, fd);
|
||||||
|
|
||||||
/* look for valid CD image identifier */
|
/* look for valid CD image identifier */
|
||||||
if (!memcmp("SEGADISCSYSTEM", header, 14))
|
if (!memcmp("SEGADISCSYSTEM", header, 14))
|
||||||
@ -321,7 +335,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* read next 16 bytes */
|
/* read next 16 bytes */
|
||||||
fread(header, 0x10, 1, fd);
|
cdStreamRead(header, 0x10, 1, fd);
|
||||||
|
|
||||||
/* look for valid CD image identifier */
|
/* look for valid CD image identifier */
|
||||||
if (!memcmp("SEGADISCSYSTEM", header, 14))
|
if (!memcmp("SEGADISCSYSTEM", header, 14))
|
||||||
@ -335,7 +349,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
if (cdd.sectorSize)
|
if (cdd.sectorSize)
|
||||||
{
|
{
|
||||||
/* read CD image header + security code */
|
/* read CD image header + security code */
|
||||||
fread(header + 0x10, 0x200, 1, fd);
|
cdStreamRead(header + 0x10, 0x200, 1, fd);
|
||||||
|
|
||||||
/* initialize first track file descriptor */
|
/* initialize first track file descriptor */
|
||||||
cdd.toc.tracks[0].fd = fd;
|
cdd.toc.tracks[0].fd = fd;
|
||||||
@ -344,8 +358,8 @@ int cdd_load(char *filename, char *header)
|
|||||||
cdd.toc.tracks[0].type = TYPE_CDROM;
|
cdd.toc.tracks[0].type = TYPE_CDROM;
|
||||||
|
|
||||||
/* DATA track end LBA (based on DATA file length) */
|
/* DATA track end LBA (based on DATA file length) */
|
||||||
fseek(fd, 0, SEEK_END);
|
cdStreamSeek(fd, 0, SEEK_END);
|
||||||
cdd.toc.tracks[0].end = ftell(fd) / cdd.sectorSize;
|
cdd.toc.tracks[0].end = cdStreamTell(fd) / cdd.sectorSize;
|
||||||
|
|
||||||
/* DATA track length should be at least 2s (BIOS requirement) */
|
/* DATA track length should be at least 2s (BIOS requirement) */
|
||||||
if (cdd.toc.tracks[0].end < 150)
|
if (cdd.toc.tracks[0].end < 150)
|
||||||
@ -354,7 +368,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* DATA track start LBA (logical block 0) */
|
/* DATA track start LBA (logical block 0) */
|
||||||
fseek(fd, 0, SEEK_SET);
|
cdStreamSeek(fd, 0, SEEK_SET);
|
||||||
cdd.toc.tracks[0].start = 0;
|
cdd.toc.tracks[0].start = 0;
|
||||||
|
|
||||||
/* initialize TOC */
|
/* initialize TOC */
|
||||||
@ -367,14 +381,14 @@ int cdd_load(char *filename, char *header)
|
|||||||
isCDfile = 0;
|
isCDfile = 0;
|
||||||
|
|
||||||
/* close file */
|
/* close file */
|
||||||
fclose(fd);
|
cdStreamClose(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* automatically try to mount CD associated CUE file */
|
/* automatically try to mount CD associated CUE file */
|
||||||
len = strlen(fname);
|
len = strlen(fname);
|
||||||
while ((len && (fname[len] != '.')) || (len > 251)) len--;
|
while ((len && (fname[len] != '.')) || (len > 251)) len--;
|
||||||
strcpy(&fname[len], ".cue");
|
strcpy(&fname[len], ".cue");
|
||||||
fd = fopen(fname, "rb");
|
fd = cdStreamOpen(fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parse CUE file */
|
/* parse CUE file */
|
||||||
@ -386,7 +400,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
if (cdd.toc.last)
|
if (cdd.toc.last)
|
||||||
{
|
{
|
||||||
/* skip first track */
|
/* skip first track */
|
||||||
while (fgets(line, 128, fd))
|
while (cdStreamGets(line, 128, fd))
|
||||||
{
|
{
|
||||||
if (strstr(line, "INDEX 01") && !strstr(line, "INDEX 1"))
|
if (strstr(line, "INDEX 01") && !strstr(line, "INDEX 1"))
|
||||||
break;
|
break;
|
||||||
@ -394,7 +408,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* read lines until end of file */
|
/* read lines until end of file */
|
||||||
while (fgets(line, 128, fd))
|
while (cdStreamGets(line, 128, fd))
|
||||||
{
|
{
|
||||||
/* skip any SPACE characters */
|
/* skip any SPACE characters */
|
||||||
lptr = line;
|
lptr = line;
|
||||||
@ -431,7 +445,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
*ptr = 0;
|
*ptr = 0;
|
||||||
|
|
||||||
/* open current track file descriptor */
|
/* open current track file descriptor */
|
||||||
cdd.toc.tracks[cdd.toc.last].fd = fopen(fname, "rb");
|
cdd.toc.tracks[cdd.toc.last].fd = cdStreamOpen(fname);
|
||||||
if (!cdd.toc.tracks[cdd.toc.last].fd)
|
if (!cdd.toc.tracks[cdd.toc.last].fd)
|
||||||
{
|
{
|
||||||
/* error opening file */
|
/* error opening file */
|
||||||
@ -449,32 +463,32 @@ int cdd_load(char *filename, char *header)
|
|||||||
{
|
{
|
||||||
/* read file header */
|
/* read file header */
|
||||||
unsigned char head[28];
|
unsigned char head[28];
|
||||||
fseek(cdd.toc.tracks[cdd.toc.last].fd, 8, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[cdd.toc.last].fd, 8, SEEK_SET);
|
||||||
fread(head, 28, 1, cdd.toc.tracks[cdd.toc.last].fd);
|
cdStreamRead(head, 28, 1, cdd.toc.tracks[cdd.toc.last].fd);
|
||||||
fseek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET);
|
||||||
|
|
||||||
/* autodetect WAVE file header (44.1KHz 16-bit stereo format only) */
|
/* autodetect WAVE file header (44.1KHz 16-bit stereo format only) */
|
||||||
if (!memcmp(head, waveHeader, 28))
|
if (!memcmp(head, waveHeader, 28))
|
||||||
{
|
{
|
||||||
/* look for 'data' chunk id */
|
/* look for 'data' chunk id */
|
||||||
int dataOffset = 0;
|
int dataOffset = 0;
|
||||||
fseek(cdd.toc.tracks[cdd.toc.last].fd, 36, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[cdd.toc.last].fd, 36, SEEK_SET);
|
||||||
while (fread(head, 4, 1, cdd.toc.tracks[cdd.toc.last].fd))
|
while (cdStreamRead(head, 4, 1, cdd.toc.tracks[cdd.toc.last].fd))
|
||||||
{
|
{
|
||||||
if (!memcmp(head, "data", 4))
|
if (!memcmp(head, "data", 4))
|
||||||
{
|
{
|
||||||
dataOffset = ftell(cdd.toc.tracks[cdd.toc.last].fd) + 4;
|
dataOffset = cdStreamTell(cdd.toc.tracks[cdd.toc.last].fd) + 4;
|
||||||
fseek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fseek(cdd.toc.tracks[cdd.toc.last].fd, -2, SEEK_CUR);
|
cdStreamSeek(cdd.toc.tracks[cdd.toc.last].fd, -2, SEEK_CUR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if 'data' chunk has not been found */
|
/* check if 'data' chunk has not been found */
|
||||||
if (!dataOffset)
|
if (!dataOffset)
|
||||||
{
|
{
|
||||||
/* invalid WAVE file */
|
/* invalid WAVE file */
|
||||||
fclose(cdd.toc.tracks[cdd.toc.last].fd);
|
cdStreamClose(cdd.toc.tracks[cdd.toc.last].fd);
|
||||||
cdd.toc.tracks[cdd.toc.last].fd = 0;
|
cdd.toc.tracks[cdd.toc.last].fd = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -483,7 +497,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
cdd.toc.tracks[cdd.toc.last].offset -= dataOffset;
|
cdd.toc.tracks[cdd.toc.last].offset -= dataOffset;
|
||||||
}
|
}
|
||||||
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
||||||
else if (!ov_open(cdd.toc.tracks[cdd.toc.last].fd,&cdd.toc.tracks[cdd.toc.last].vf,0,0))
|
else if (!ov_open_callbacks(cdd.toc.tracks[cdd.toc.last].fd,&cdd.toc.tracks[cdd.toc.last].vf,0,0,cb))
|
||||||
{
|
{
|
||||||
/* retrieve stream infos */
|
/* retrieve stream infos */
|
||||||
vorbis_info *info = ov_info(&cdd.toc.tracks[cdd.toc.last].vf,-1);
|
vorbis_info *info = ov_info(&cdd.toc.tracks[cdd.toc.last].vf,-1);
|
||||||
@ -499,7 +513,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* unsupported audio file */
|
/* unsupported audio file */
|
||||||
fclose(cdd.toc.tracks[cdd.toc.last].fd);
|
cdStreamClose(cdd.toc.tracks[cdd.toc.last].fd);
|
||||||
cdd.toc.tracks[cdd.toc.last].fd = 0;
|
cdd.toc.tracks[cdd.toc.last].fd = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -515,7 +529,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
/* close any opened file */
|
/* close any opened file */
|
||||||
if (cdd.toc.tracks[cdd.toc.last].fd)
|
if (cdd.toc.tracks[cdd.toc.last].fd)
|
||||||
{
|
{
|
||||||
fclose(cdd.toc.tracks[cdd.toc.last].fd);
|
cdStreamClose(cdd.toc.tracks[cdd.toc.last].fd);
|
||||||
cdd.toc.tracks[cdd.toc.last].fd = 0;
|
cdd.toc.tracks[cdd.toc.last].fd = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,7 +552,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
cdd.sectorSize = 2352;
|
cdd.sectorSize = 2352;
|
||||||
|
|
||||||
/* skip 16-byte header */
|
/* skip 16-byte header */
|
||||||
fseek(cdd.toc.tracks[0].fd, 0x10, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[0].fd, 0x10, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cdd.sectorSize)
|
if (cdd.sectorSize)
|
||||||
@ -547,8 +561,8 @@ int cdd_load(char *filename, char *header)
|
|||||||
cdd.toc.tracks[0].type = TYPE_CDROM;
|
cdd.toc.tracks[0].type = TYPE_CDROM;
|
||||||
|
|
||||||
/* read CD image header + security code */
|
/* read CD image header + security code */
|
||||||
fread(header, 0x210, 1, cdd.toc.tracks[0].fd);
|
cdStreamRead(header, 0x210, 1, cdd.toc.tracks[0].fd);
|
||||||
fseek(cdd.toc.tracks[0].fd, 0, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[0].fd, 0, SEEK_SET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -638,18 +652,18 @@ int cdd_load(char *filename, char *header)
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
/* current track end time */
|
/* current track end time */
|
||||||
fseek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_END);
|
cdStreamSeek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_END);
|
||||||
if (cdd.toc.tracks[cdd.toc.last].type)
|
if (cdd.toc.tracks[cdd.toc.last].type)
|
||||||
{
|
{
|
||||||
/* DATA track length */
|
/* DATA track length */
|
||||||
cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((ftell(cdd.toc.tracks[cdd.toc.last].fd) + cdd.sectorSize - 1) / cdd.sectorSize);
|
cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((cdStreamTell(cdd.toc.tracks[cdd.toc.last].fd) + cdd.sectorSize - 1) / cdd.sectorSize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* AUDIO track length */
|
/* AUDIO track length */
|
||||||
cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((ftell(cdd.toc.tracks[cdd.toc.last].fd) + 2351) / 2352);
|
cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((cdStreamTell(cdd.toc.tracks[cdd.toc.last].fd) + 2351) / 2352);
|
||||||
}
|
}
|
||||||
fseek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* adjust track start time (based on current file start time + index absolute time) */
|
/* adjust track start time (based on current file start time + index absolute time) */
|
||||||
@ -687,11 +701,11 @@ int cdd_load(char *filename, char *header)
|
|||||||
#endif
|
#endif
|
||||||
if (cdd.toc.tracks[cdd.toc.last].fd)
|
if (cdd.toc.tracks[cdd.toc.last].fd)
|
||||||
{
|
{
|
||||||
fclose(cdd.toc.tracks[cdd.toc.last].fd);
|
cdStreamClose(cdd.toc.tracks[cdd.toc.last].fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* close CUE file */
|
/* close CUE file */
|
||||||
fclose(fd);
|
cdStreamClose(fd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -705,7 +719,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
{
|
{
|
||||||
/* auto-detect wrong initial track index */
|
/* auto-detect wrong initial track index */
|
||||||
sprintf(ptr, extensions[i], cdd.toc.last);
|
sprintf(ptr, extensions[i], cdd.toc.last);
|
||||||
fd = fopen(fname, "rb");
|
fd = cdStreamOpen(fname);
|
||||||
if (fd)
|
if (fd)
|
||||||
{
|
{
|
||||||
offset = 0;
|
offset = 0;
|
||||||
@ -713,7 +727,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sprintf(ptr, extensions[i], cdd.toc.last + 1);
|
sprintf(ptr, extensions[i], cdd.toc.last + 1);
|
||||||
fd = fopen(fname, "rb");
|
fd = cdStreamOpen(fname);
|
||||||
if (fd) break;
|
if (fd) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -722,31 +736,31 @@ int cdd_load(char *filename, char *header)
|
|||||||
{
|
{
|
||||||
/* read file HEADER */
|
/* read file HEADER */
|
||||||
unsigned char head[28];
|
unsigned char head[28];
|
||||||
fseek(fd, 8, SEEK_SET);
|
cdStreamSeek(fd, 8, SEEK_SET);
|
||||||
fread(head, 28, 1, fd);
|
cdStreamRead(head, 28, 1, fd);
|
||||||
fseek(fd, 0, SEEK_SET);
|
cdStreamSeek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
/* check if this is a valid WAVE file (44.1KHz 16-bit stereo format only) */
|
/* check if this is a valid WAVE file (44.1KHz 16-bit stereo format only) */
|
||||||
if (!memcmp(head, waveHeader, 28))
|
if (!memcmp(head, waveHeader, 28))
|
||||||
{
|
{
|
||||||
/* look for 'data' chunk id */
|
/* look for 'data' chunk id */
|
||||||
int dataOffset = 0;
|
int dataOffset = 0;
|
||||||
fseek(fd, 36, SEEK_SET);
|
cdStreamSeek(fd, 36, SEEK_SET);
|
||||||
while (fread(head, 4, 1, fd))
|
while (cdStreamRead(head, 4, 1, fd))
|
||||||
{
|
{
|
||||||
if (!memcmp(head, "data", 4))
|
if (!memcmp(head, "data", 4))
|
||||||
{
|
{
|
||||||
dataOffset = ftell(fd) + 4;
|
dataOffset = cdStreamTell(fd) + 4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fseek(fd, -2, SEEK_CUR);
|
cdStreamSeek(fd, -2, SEEK_CUR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if 'data' chunk has not been found */
|
/* check if 'data' chunk has not been found */
|
||||||
if (!dataOffset)
|
if (!dataOffset)
|
||||||
{
|
{
|
||||||
/* invalid WAVE file */
|
/* invalid WAVE file */
|
||||||
fclose(fd);
|
cdStreamClose(fd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,16 +774,16 @@ int cdd_load(char *filename, char *header)
|
|||||||
cdd.toc.tracks[cdd.toc.last].start += 150;
|
cdd.toc.tracks[cdd.toc.last].start += 150;
|
||||||
|
|
||||||
/* current track end time */
|
/* current track end time */
|
||||||
fseek(fd, 0, SEEK_END);
|
cdStreamSeek(fd, 0, SEEK_END);
|
||||||
cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((ftell(fd) - dataOffset + 2351) / 2352);
|
cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((cdStreamTell(fd) - dataOffset + 2351) / 2352);
|
||||||
|
|
||||||
/* initialize file read offset for current track */
|
/* initialize file read offset for current track */
|
||||||
cdd.toc.tracks[cdd.toc.last].offset = cdd.toc.tracks[cdd.toc.last].start * 2352;
|
cdd.toc.tracks[cdd.toc.last].offset = cdd.toc.tracks[cdd.toc.last].start * 2352;
|
||||||
|
|
||||||
/* auto-detect PAUSE within audio files */
|
/* auto-detect PAUSE within audio files */
|
||||||
fseek(fd, 100 * 2352, SEEK_SET);
|
cdStreamSeek(fd, 100 * 2352, SEEK_SET);
|
||||||
fread(head, 4, 1, fd);
|
cdStreamRead(head, 4, 1, fd);
|
||||||
fseek(fd, 0, SEEK_SET);
|
cdStreamSeek(fd, 0, SEEK_SET);
|
||||||
if (*(int32 *)head == 0)
|
if (*(int32 *)head == 0)
|
||||||
{
|
{
|
||||||
/* assume 2s PAUSE is included at the beginning of the file */
|
/* assume 2s PAUSE is included at the beginning of the file */
|
||||||
@ -787,7 +801,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
cdd.toc.last++;
|
cdd.toc.last++;
|
||||||
}
|
}
|
||||||
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
||||||
else if (!ov_open(fd,&cdd.toc.tracks[cdd.toc.last].vf,0,0))
|
else if (!ov_open_callbacks(fd,&cdd.toc.tracks[cdd.toc.last].vf,0,0,cb))
|
||||||
{
|
{
|
||||||
/* retrieve stream infos */
|
/* retrieve stream infos */
|
||||||
vorbis_info *info = ov_info(&cdd.toc.tracks[cdd.toc.last].vf,-1);
|
vorbis_info *info = ov_info(&cdd.toc.tracks[cdd.toc.last].vf,-1);
|
||||||
@ -852,7 +866,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* unsupported audio file format */
|
/* unsupported audio file format */
|
||||||
fclose(fd);
|
cdStreamClose(fd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -861,7 +875,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
|
|
||||||
/* try to open next audio track file */
|
/* try to open next audio track file */
|
||||||
sprintf(ptr, extensions[i], cdd.toc.last + offset);
|
sprintf(ptr, extensions[i], cdd.toc.last + offset);
|
||||||
fd = fopen(fname, "rb");
|
fd = cdStreamOpen(fname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -976,7 +990,7 @@ int cdd_load(char *filename, char *header)
|
|||||||
|
|
||||||
/* Automatically try to open associated subcode data file */
|
/* Automatically try to open associated subcode data file */
|
||||||
strncpy(&fname[strlen(fname) - 4], ".sub", 4);
|
strncpy(&fname[strlen(fname) - 4], ".sub", 4);
|
||||||
cdd.toc.sub = fopen(fname, "rb");
|
cdd.toc.sub = cdStreamOpen(fname);
|
||||||
|
|
||||||
/* return 1 if loaded file is CD image file */
|
/* return 1 if loaded file is CD image file */
|
||||||
return (isCDfile);
|
return (isCDfile);
|
||||||
@ -1014,13 +1028,13 @@ void cdd_unload(void)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* close file */
|
/* close file */
|
||||||
fclose(cdd.toc.tracks[i].fd);
|
cdStreamClose(cdd.toc.tracks[i].fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* close any opened subcode file */
|
/* close any opened subcode file */
|
||||||
if (cdd.toc.sub) fclose(cdd.toc.sub);
|
if (cdd.toc.sub) cdStreamClose(cdd.toc.sub);
|
||||||
|
|
||||||
/* CD unloaded */
|
/* CD unloaded */
|
||||||
cdd.loaded = 0;
|
cdd.loaded = 0;
|
||||||
@ -1042,16 +1056,16 @@ void cdd_read_data(uint8 *dst)
|
|||||||
if (cdd.sectorSize == 2048)
|
if (cdd.sectorSize == 2048)
|
||||||
{
|
{
|
||||||
/* Mode 1 COOKED data (ISO) */
|
/* Mode 1 COOKED data (ISO) */
|
||||||
fseek(cdd.toc.tracks[0].fd, cdd.lba * 2048, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[0].fd, cdd.lba * 2048, SEEK_SET);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Mode 1 RAW data (skip 16-byte header) */
|
/* Mode 1 RAW data (skip 16-byte header) */
|
||||||
fseek(cdd.toc.tracks[0].fd, cdd.lba * 2352 + 16, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[0].fd, cdd.lba * 2352 + 16, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read sector data (Mode 1 = 2048 bytes) */
|
/* read sector data (Mode 1 = 2048 bytes) */
|
||||||
fread(dst, 2048, 1, cdd.toc.tracks[0].fd);
|
cdStreamRead(dst, 2048, 1, cdd.toc.tracks[0].fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1139,7 +1153,7 @@ void cdd_read_audio(unsigned int samples)
|
|||||||
#else
|
#else
|
||||||
uint8 *ptr = cdc.ram;
|
uint8 *ptr = cdc.ram;
|
||||||
#endif
|
#endif
|
||||||
fread(cdc.ram, 1, samples * 4, cdd.toc.tracks[cdd.index].fd);
|
cdStreamRead(cdc.ram, 1, samples * 4, cdd.toc.tracks[cdd.index].fd);
|
||||||
|
|
||||||
/* process 16-bit (little-endian) stereo samples */
|
/* process 16-bit (little-endian) stereo samples */
|
||||||
for (i=0; i<samples; i++)
|
for (i=0; i<samples; i++)
|
||||||
@ -1217,7 +1231,7 @@ static void cdd_read_subcode(void)
|
|||||||
index = (scd.regs[0x68>>1].byte.l + 0x100) >> 1;
|
index = (scd.regs[0x68>>1].byte.l + 0x100) >> 1;
|
||||||
|
|
||||||
/* read interleaved subcode data from .sub file (12 x 8-bit of P subchannel first, then Q subchannel, etc) */
|
/* read interleaved subcode data from .sub file (12 x 8-bit of P subchannel first, then Q subchannel, etc) */
|
||||||
fread(subc, 1, 96, cdd.toc.sub);
|
cdStreamRead(subc, 1, 96, cdd.toc.sub);
|
||||||
|
|
||||||
/* convert back to raw subcode format (96 bytes with 8 x P-W subchannel bits per byte) */
|
/* convert back to raw subcode format (96 bytes with 8 x P-W subchannel bits per byte) */
|
||||||
for (i=0; i<96; i+=2)
|
for (i=0; i<96; i+=2)
|
||||||
@ -1345,7 +1359,7 @@ void cdd_update(void)
|
|||||||
{
|
{
|
||||||
#ifdef DISABLE_MANY_OGG_OPEN_FILES
|
#ifdef DISABLE_MANY_OGG_OPEN_FILES
|
||||||
/* VORBIS file need to be opened first */
|
/* VORBIS file need to be opened first */
|
||||||
ov_open(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0);
|
ov_open_callbacks(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0,cb);
|
||||||
#endif
|
#endif
|
||||||
ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, (cdd.toc.tracks[cdd.index].start * 588) - cdd.toc.tracks[cdd.index].offset);
|
ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, (cdd.toc.tracks[cdd.index].start * 588) - cdd.toc.tracks[cdd.index].offset);
|
||||||
}
|
}
|
||||||
@ -1353,7 +1367,7 @@ void cdd_update(void)
|
|||||||
#endif
|
#endif
|
||||||
if (cdd.toc.tracks[cdd.index].fd)
|
if (cdd.toc.tracks[cdd.index].fd)
|
||||||
{
|
{
|
||||||
fseek(cdd.toc.tracks[cdd.index].fd, (cdd.toc.tracks[cdd.index].start * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[cdd.index].fd, (cdd.toc.tracks[cdd.index].start * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1431,14 +1445,14 @@ void cdd_update(void)
|
|||||||
/* seek to current subcode position */
|
/* seek to current subcode position */
|
||||||
if (cdd.toc.sub)
|
if (cdd.toc.sub)
|
||||||
{
|
{
|
||||||
fseek(cdd.toc.sub, cdd.lba * 96, SEEK_SET);
|
cdStreamSeek(cdd.toc.sub, cdd.lba * 96, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* seek to current track position */
|
/* seek to current track position */
|
||||||
if (cdd.toc.tracks[cdd.index].type)
|
if (cdd.toc.tracks[cdd.index].type)
|
||||||
{
|
{
|
||||||
/* DATA track */
|
/* DATA track */
|
||||||
fseek(cdd.toc.tracks[0].fd, cdd.lba * cdd.sectorSize, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[0].fd, cdd.lba * cdd.sectorSize, SEEK_SET);
|
||||||
}
|
}
|
||||||
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
||||||
else if (cdd.toc.tracks[cdd.index].vf.seekable)
|
else if (cdd.toc.tracks[cdd.index].vf.seekable)
|
||||||
@ -1448,7 +1462,7 @@ void cdd_update(void)
|
|||||||
if (!cdd.toc.tracks[cdd.index].vf.datasource)
|
if (!cdd.toc.tracks[cdd.index].vf.datasource)
|
||||||
{
|
{
|
||||||
/* VORBIS file need to be opened first */
|
/* VORBIS file need to be opened first */
|
||||||
ov_open(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0);
|
ov_open_callbacks(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0,cb);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* VORBIS AUDIO track */
|
/* VORBIS AUDIO track */
|
||||||
@ -1458,7 +1472,7 @@ void cdd_update(void)
|
|||||||
else if (cdd.toc.tracks[cdd.index].fd)
|
else if (cdd.toc.tracks[cdd.index].fd)
|
||||||
{
|
{
|
||||||
/* PCM AUDIO track */
|
/* PCM AUDIO track */
|
||||||
fseek(cdd.toc.tracks[cdd.index].fd, (cdd.lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[cdd.index].fd, (cdd.lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1641,7 +1655,7 @@ void cdd_process(void)
|
|||||||
/* open current track VORBIS file */
|
/* open current track VORBIS file */
|
||||||
if (cdd.toc.tracks[index].vf.seekable)
|
if (cdd.toc.tracks[index].vf.seekable)
|
||||||
{
|
{
|
||||||
ov_open(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0);
|
ov_open_callbacks(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0,cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1659,14 +1673,14 @@ void cdd_process(void)
|
|||||||
/* seek to current subcode position */
|
/* seek to current subcode position */
|
||||||
if (cdd.toc.sub)
|
if (cdd.toc.sub)
|
||||||
{
|
{
|
||||||
fseek(cdd.toc.sub, lba * 96, SEEK_SET);
|
cdStreamSeek(cdd.toc.sub, lba * 96, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* seek to current track position */
|
/* seek to current track position */
|
||||||
if (cdd.toc.tracks[index].type)
|
if (cdd.toc.tracks[index].type)
|
||||||
{
|
{
|
||||||
/* DATA track */
|
/* DATA track */
|
||||||
fseek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET);
|
||||||
}
|
}
|
||||||
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
||||||
else if (cdd.toc.tracks[index].vf.seekable)
|
else if (cdd.toc.tracks[index].vf.seekable)
|
||||||
@ -1678,7 +1692,7 @@ void cdd_process(void)
|
|||||||
else if (cdd.toc.tracks[index].fd)
|
else if (cdd.toc.tracks[index].fd)
|
||||||
{
|
{
|
||||||
/* PCM AUDIO track */
|
/* PCM AUDIO track */
|
||||||
fseek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* no audio track playing (yet) */
|
/* no audio track playing (yet) */
|
||||||
@ -1739,7 +1753,7 @@ void cdd_process(void)
|
|||||||
/* open current track VORBIS file */
|
/* open current track VORBIS file */
|
||||||
if (cdd.toc.tracks[index].vf.seekable)
|
if (cdd.toc.tracks[index].vf.seekable)
|
||||||
{
|
{
|
||||||
ov_open(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0);
|
ov_open_callbacks(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0,cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1758,7 +1772,7 @@ void cdd_process(void)
|
|||||||
if (cdd.toc.tracks[index].type)
|
if (cdd.toc.tracks[index].type)
|
||||||
{
|
{
|
||||||
/* DATA track */
|
/* DATA track */
|
||||||
fseek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET);
|
||||||
}
|
}
|
||||||
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
||||||
else if (cdd.toc.tracks[index].vf.seekable)
|
else if (cdd.toc.tracks[index].vf.seekable)
|
||||||
@ -1770,13 +1784,13 @@ void cdd_process(void)
|
|||||||
else if (cdd.toc.tracks[index].fd)
|
else if (cdd.toc.tracks[index].fd)
|
||||||
{
|
{
|
||||||
/* PCM AUDIO track */
|
/* PCM AUDIO track */
|
||||||
fseek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET);
|
cdStreamSeek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* seek to current subcode position */
|
/* seek to current subcode position */
|
||||||
if (cdd.toc.sub)
|
if (cdd.toc.sub)
|
||||||
{
|
{
|
||||||
fseek(cdd.toc.sub, lba * 96, SEEK_SET);
|
cdStreamSeek(cdd.toc.sub, lba * 96, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* no audio track playing */
|
/* no audio track playing */
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* Genesis Plus
|
* Genesis Plus
|
||||||
* CD drive processor & CD-DA fader
|
* CD drive processor & CD-DA fader
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012-2016 Eke-Eke (Genesis Plus GX)
|
* Copyright (C) 2012-2017 Eke-Eke (Genesis Plus GX)
|
||||||
*
|
*
|
||||||
* Redistribution and use of this code or any derivative works are permitted
|
* Redistribution and use of this code or any derivative works are permitted
|
||||||
* provided that the following conditions are met:
|
* provided that the following conditions are met:
|
||||||
@ -64,7 +64,7 @@
|
|||||||
/* CD track */
|
/* CD track */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
FILE *fd;
|
cdStream *fd;
|
||||||
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
|
||||||
OggVorbis_File vf;
|
OggVorbis_File vf;
|
||||||
#endif
|
#endif
|
||||||
@ -80,7 +80,7 @@ typedef struct
|
|||||||
int end;
|
int end;
|
||||||
int last;
|
int last;
|
||||||
track_t tracks[CD_MAX_TRACKS];
|
track_t tracks[CD_MAX_TRACKS];
|
||||||
FILE *sub;
|
cdStream *sub;
|
||||||
} toc_t;
|
} toc_t;
|
||||||
|
|
||||||
/* CDD hardware */
|
/* CDD hardware */
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
/* Set to your compiler's static inline keyword to enable it, or
|
/* Set to your compiler's static inline keyword to enable it, or
|
||||||
* set it to blank to disable it.
|
* set it to blank to disable it.
|
||||||
* If you define INLINE in the makefile, it will override this value.
|
* If you define INLINE in makefile or osd.h, it will override this value.
|
||||||
* NOTE: not enabling inline functions will SEVERELY slow down emulation.
|
* NOTE: not enabling inline functions will SEVERELY slow down emulation.
|
||||||
*/
|
*/
|
||||||
#ifndef INLINE
|
#ifndef INLINE
|
||||||
@ -53,4 +53,18 @@
|
|||||||
#define ALIGNED_(x) __attribute__ ((aligned(x)))
|
#define ALIGNED_(x) __attribute__ ((aligned(x)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Default CD image file access (read-only) functions */
|
||||||
|
/* If you need to override default stdio.h functions with custom filesystem API,
|
||||||
|
redefine following macros in platform specific include file (osd.h) or Makefile
|
||||||
|
*/
|
||||||
|
#ifndef cdStream
|
||||||
|
#define cdStream FILE
|
||||||
|
#define cdStreamOpen(fname) fopen(fname, "rb")
|
||||||
|
#define cdStreamClose fclose
|
||||||
|
#define cdStreamRead fread
|
||||||
|
#define cdStreamSeek fseek
|
||||||
|
#define cdStreamTell ftell
|
||||||
|
#define cdStreamGets fgets
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _MACROS_H_ */
|
#endif /* _MACROS_H_ */
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "macros.h"
|
|
||||||
#include "osd.h"
|
#include "osd.h"
|
||||||
|
#include "macros.h"
|
||||||
#include "loadrom.h"
|
#include "loadrom.h"
|
||||||
#include "m68k.h"
|
#include "m68k.h"
|
||||||
#include "z80.h"
|
#include "z80.h"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user