syncing 2.3.0

parent 4074da44
/*
* UAE
*
* CD image file support
*
* - iso (2048/2352 block size)
* - cue/bin, cue/bin/wav, cue/bin/mp3
* - ccd/img and ccd/img/sub
*
* Copyright 2010 Toni Wilen
*
*/
#include "sysconfig.h"
#include "sysdeps.h"
#include "options.h"
#include "blkdev.h"
#include "zfile.h"
#include "gui.h"
#include "fsdb.h"
#include "threaddep/thread.h"
#include "scsidev.h"
#include <mp3decoder.h>
#include <memory.h>
#ifdef RETROPLATFORM
#include "rp.h"
#endif
#define scsi_log write_log
#define CDDA_BUFFERS 6
enum audenc { AUDENC_NONE, AUDENC_PCM, AUDENC_MP3, AUDENC_FLAC };
struct cdtoc
{
struct zfile *handle;
int offset;
uae_u8 *data;
struct zfile *subhandle;
int suboffset;
uae_u8 *subdata;
int filesize;
TCHAR *fname;
int address;
uae_u8 adr, ctrl;
int track;
int size;
int skipsize; // bytes to skip after each block
audenc enctype;
int writeoffset;
int subcode;
};
struct cdunit {
bool enabled;
bool open;
uae_u8 buffer[2352];
struct cdtoc toc[102];
int tracks;
uae_u64 cdsize;
int blocksize;
int cdda_play_finished;
int cdda_play;
int cdda_paused;
int cdda_volume[2];
int cdda_scan;
int cd_last_pos;
int cdda_start, cdda_end;
play_subchannel_callback cdda_subfunc;
bool slowunit;
int imagechange;
TCHAR newfile[MAX_DPATH];
uae_sem_t sub_sem;
struct device_info di;
};
static struct cdunit cdunits[MAX_TOTAL_SCSI_DEVICES];
static int bus_open;
static volatile int cdimage_unpack_thread, cdimage_unpack_active;
static smp_comm_pipe unpack_pipe;
static struct cdunit *unitisopen (int unitnum)
{
struct cdunit *cdu = &cdunits[unitnum];
if (cdu->open)
return cdu;
return NULL;
}
static struct cdtoc *findtoc (struct cdunit *cdu, int *sectorp)
{
int i;
int sector;
sector = *sectorp;
for (i = 0; i <= cdu->tracks; i++) {
struct cdtoc *t = &cdu->toc[i];
if (t->address > sector) {
if (i == 0) {
*sectorp = 0;
return t;
}
t--;
sector -= t->address;
*sectorp = sector;
return t;
}
}
return NULL;
}
// WOHOO, library that supports virtual file access functions. Perfect!
static void flac_metadata_callback (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
{
struct cdtoc *t = (struct cdtoc*)client_data;
if (t->data)
return;
if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
t->filesize = metadata->data.stream_info.total_samples * (metadata->data.stream_info.bits_per_sample / 8) * metadata->data.stream_info.channels;
}
}
static void flac_error_callback (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
return;
}
static FLAC__StreamDecoderWriteStatus flac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
{
struct cdtoc *t = (struct cdtoc*)client_data;
uae_u16 *p = (uae_u16*)(t->data + t->writeoffset);
int size = 4;
for (int i = 0; i < frame->header.blocksize && t->writeoffset < t->filesize - size; i++, t->writeoffset += size) {
*p++ = (FLAC__int16)buffer[0][i];
*p++ = (FLAC__int16)buffer[1][i];
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
static FLAC__StreamDecoderReadStatus file_read_callback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
{
struct cdtoc *t = (struct cdtoc*)client_data;
if (zfile_ftell (t->handle) >= zfile_size (t->handle))
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
return zfile_fread (buffer, *bytes, 1, t->handle) ? FLAC__STREAM_DECODER_READ_STATUS_CONTINUE : FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}
static FLAC__StreamDecoderSeekStatus file_seek_callback (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
{
struct cdtoc *t = (struct cdtoc*)client_data;
zfile_fseek (t->handle, absolute_byte_offset, SEEK_SET);
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
}
static FLAC__StreamDecoderTellStatus file_tell_callback (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
{
struct cdtoc *t = (struct cdtoc*)client_data;
*absolute_byte_offset = zfile_ftell (t->handle);
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}
static FLAC__StreamDecoderLengthStatus file_len_callback (const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
{
struct cdtoc *t = (struct cdtoc*)client_data;
*stream_length = zfile_size (t->handle);
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
static FLAC__bool file_eof_callback (const FLAC__StreamDecoder *decoder, void *client_data)
{
struct cdtoc *t = (struct cdtoc*)client_data;
return zfile_ftell (t->handle) >= zfile_size (t->handle);
}
static void flac_get_size (struct cdtoc *t)
{
FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new ();
if (decoder) {
FLAC__stream_decoder_set_md5_checking (decoder, false);
int init_status = FLAC__stream_decoder_init_stream (decoder,
&file_read_callback, &file_seek_callback, &file_tell_callback,
&file_len_callback, &file_eof_callback,
&flac_write_callback, &flac_metadata_callback, &flac_error_callback, t);
FLAC__stream_decoder_process_until_end_of_metadata (decoder);
FLAC__stream_decoder_delete (decoder);
}
}
static uae_u8 *flac_get_data (struct cdtoc *t)
{
write_log ("FLAC: unpacking '%s'..\n", zfile_getname (t->handle));
t->writeoffset = 0;
FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new ();
if (decoder) {
FLAC__stream_decoder_set_md5_checking (decoder, false);
int init_status = FLAC__stream_decoder_init_stream (decoder,
&file_read_callback, &file_seek_callback, &file_tell_callback,
&file_len_callback, &file_eof_callback,
&flac_write_callback, &flac_metadata_callback, &flac_error_callback, t);
FLAC__stream_decoder_process_until_end_of_stream (decoder);
FLAC__stream_decoder_delete (decoder);
write_log ("FLAC: %s unpacked\n", zfile_getname (t->handle));
}
return t->data;
}
#ifdef _WIN32
static HWAVEOUT cdda_wavehandle;
static void cdda_closewav (void)
{
if (cdda_wavehandle != NULL)
waveOutClose (cdda_wavehandle);
cdda_wavehandle = NULL;
}
static int cdda_openwav (void)
{
WAVEFORMATEX wav = { 0 };
MMRESULT mmr;
wav.cbSize = 0;
wav.nChannels = 2;
wav.nSamplesPerSec = 44100;
wav.wBitsPerSample = 16;
wav.nBlockAlign = wav.wBitsPerSample / 8 * wav.nChannels;
wav.nAvgBytesPerSec = wav.nBlockAlign * wav.nSamplesPerSec;
wav.wFormatTag = WAVE_FORMAT_PCM;
mmr = waveOutOpen (&cdda_wavehandle, WAVE_MAPPER, &wav, 0, 0, WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT);
if (mmr != MMSYSERR_NOERROR) {
write_log ("IMAGE CDDA: wave open %d\n", mmr);
cdda_closewav ();
return 0;
}
return 1;
}
static void sub_to_interleaved (const uae_u8 *s, uae_u8 *d)
{
for (int i = 0; i < 8 * 12; i ++) {
int dmask = 0x80;
int smask = 1 << (7 - (i & 7));
(*d) = 0;
for (int j = 0; j < 8; j++) {
(*d) |= (s[(i / 8) + j * 12] & smask) ? dmask : 0;
dmask >>= 1;
}
d++;
}
}
static void sub_to_deinterleaved (const uae_u8 *s, uae_u8 *d)
{
for (int i = 0; i < 8 * 12; i ++) {
int dmask = 0x80;
int smask = 1 << (7 - (i / 12));
(*d) = 0;
for (int j = 0; j < 8; j++) {
(*d) |= (s[(i % 12) * 8 + j] & smask) ? dmask : 0;
dmask >>= 1;
}
d++;
}
}
static int getsub (uae_u8 *dst, struct cdunit *cdu, struct cdtoc *t, int sector)
{
int ret = 0;
uae_sem_wait (&cdu->sub_sem);
if (t->subcode) {
if (t->subhandle) {
int offset = 0;
int totalsize = SUB_CHANNEL_SIZE;
if (t->skipsize) {
totalsize += t->size;
offset = t->size;
}
zfile_fseek (t->subhandle, sector * totalsize + t->suboffset + offset, SEEK_SET);
if (zfile_fread (dst, SUB_CHANNEL_SIZE, 1, t->subhandle) > 0)
ret = t->subcode;
} else {
memcpy (dst, t->subdata + sector * SUB_CHANNEL_SIZE + t->suboffset, SUB_CHANNEL_SIZE);
ret = t->subcode;
}
}
if (!ret) {
memset (dst, 0, SUB_CHANNEL_SIZE);
// regenerate Q-subchannel
uae_u8 *s = dst + 12;
s[0] = (t->ctrl << 4) | (t->adr << 0);
s[1] = tobcd (t - &cdu->toc[0] + 1);
s[2] = tobcd (1);
int msf = lsn2msf (sector);
tolongbcd (s + 7, msf);
msf = lsn2msf (sector - t->address - 150);
tolongbcd (s + 3, msf);
ret = 2;
}
if (ret == 1) {
uae_u8 tmp[SUB_CHANNEL_SIZE];
memcpy (tmp, dst, SUB_CHANNEL_SIZE);
sub_to_deinterleaved (tmp, dst);
ret = 2;
}
uae_sem_post (&cdu->sub_sem);
return ret;
}
static void dosub (struct cdunit *cdu, struct cdtoc *t, int sector)
{
uae_u8 *d;
uae_u8 subbuf[SUB_CHANNEL_SIZE];
uae_u8 subbuf2[SUB_CHANNEL_SIZE];
if (!cdu->cdda_subfunc)
return;
if (!t) {
memset (subbuf, 0, sizeof subbuf);
cdu->cdda_subfunc (subbuf, 1);
return;
}
memset (subbuf, 0, SUB_CHANNEL_SIZE);
int mode = getsub (subbuf, cdu, t, sector);
if (mode == 2) { // deinterleaved -> interleaved
sub_to_interleaved (subbuf, subbuf2);
d = subbuf2;
} else {
d = subbuf;
}
cdu->cdda_subfunc (d, 1);
}
static void *cdda_unpack_func (void *v)
{
cdimage_unpack_thread = 1;
mp3decoder *mp3dec = NULL;
for (;;) {
uae_u32 cduidx = read_comm_pipe_u32_blocking (&unpack_pipe);
if (cdimage_unpack_thread == 0)
break;
uae_u32 tocidx = read_comm_pipe_u32_blocking (&unpack_pipe);
struct cdunit *cdu = &cdunits[cduidx];
struct cdtoc *t = &cdu->toc[tocidx];
if (t->handle) {
// force unpack if handle points to delayed zipped file
uae_s64 pos = zfile_ftell (t->handle);
zfile_fseek (t->handle, -1, SEEK_END);
uae_u8 b;
zfile_fread (&b, 1, 1, t->handle);
zfile_fseek (t->handle, pos, SEEK_SET);
if (!t->data && (t->enctype == AUDENC_MP3 || t->enctype == AUDENC_FLAC)) {
t->data = xcalloc (uae_u8, t->filesize + 2352);
cdimage_unpack_active = 1;
if (t->data) {
if (t->enctype == AUDENC_MP3) {
if (!mp3dec) {
try {
mp3dec = new mp3decoder();
} catch (exception) { };
}
if (mp3dec)
t->data = mp3dec->get (t->handle, t->data, t->filesize);
} else if (t->enctype == AUDENC_FLAC) {
flac_get_data (t);
}
}
}
}
cdimage_unpack_active = 2;
}
delete mp3dec;
cdimage_unpack_thread = -1;
return 0;
}
static void *cdda_play_func (void *v)
{
int cdda_pos;
int num_sectors = CDDA_BUFFERS;
int quit = 0;
int bufnum;
int buffered;
uae_u8 *px[2], *p;
int bufon[2];
int i;
WAVEHDR whdr[2];
MMRESULT mmr;
int volume[2], volume_main;
int oldplay;
struct cdunit *cdu = (struct cdunit*)v;
int firstloops;
for (i = 0; i < 2; i++) {
memset (&whdr[i], 0, sizeof (WAVEHDR));
whdr[i].dwFlags = WHDR_DONE;
}
while (cdu->cdda_play == 0)
Sleep (10);
oldplay = -1;
p = xmalloc (uae_u8, 2 * num_sectors * 4096);
px[0] = p;
px[1] = p + num_sectors * 4096;
bufon[0] = bufon[1] = 0;
bufnum = 0;
buffered = 0;
volume[0] = volume[1] = -1;
volume_main = -1;
if (cdda_openwav ()) {
for (i = 0; i < 2; i++) {
memset (&whdr[i], 0, sizeof (WAVEHDR));
whdr[i].dwBufferLength = 2352 * num_sectors;
whdr[i].lpData = (LPSTR)px[i];
mmr = waveOutPrepareHeader (cdda_wavehandle, &whdr[i], sizeof (WAVEHDR));
if (mmr != MMSYSERR_NOERROR) {
write_log ("CDDA: waveOutPrepareHeader %d:%d\n", i, mmr);
goto end;
}
whdr[i].dwFlags |= WHDR_DONE;
}
while (cdu->cdda_play > 0) {
if (oldplay != cdu->cdda_play) {
struct cdtoc *t;
int sector;
struct _timeb tb;
_ftime (&tb);
cdda_pos = cdu->cdda_start;
oldplay = cdu->cdda_play;
cdu->cd_last_pos = cdda_pos;
sector = cdu->cdda_start;
t = findtoc (cdu, &sector);
if (!t) {
write_log ("IMAGE CDDA: illegal sector number %d\n", cdu->cdda_start);
} else {
write_log ("IMAGE CDDA: playing from %d to %d, track %d ('%s', offset %d, secoffset %d)\n",
cdu->cdda_start, cdu->cdda_end, t->track, t->fname, t->offset, sector);
// do this even if audio is not compressed, t->handle also could be
// compressed and we want to unpack it in background too
while (cdimage_unpack_active == 1)
Sleep (10);
cdimage_unpack_active = 0;
write_comm_pipe_u32 (&unpack_pipe, cdu - &cdunits[0], 0);
write_comm_pipe_u32 (&unpack_pipe, t - &cdu->toc[0], 1);
while (cdimage_unpack_active == 0)
Sleep (10);
}
firstloops = cdu->slowunit ? 150 : 30;
while (cdu->cdda_paused && cdu->cdda_play > 0) {
Sleep (10);
firstloops = -1;
}
if (firstloops > 0)
firstloops /= num_sectors;
}
while (!(whdr[bufnum].dwFlags & WHDR_DONE)) {
Sleep (10);
if (!cdu->cdda_play)
goto end;
}
bufon[bufnum] = 0;
if (!isaudiotrack (&cdu->di.toc, cdda_pos))
goto end; // data track?
if ((cdda_pos < cdu->cdda_end || cdu->cdda_end == 0xffffffff) && !cdu->cdda_paused && cdu->cdda_play > 0) {
struct cdtoc *t;
int sector, cnt;
int dofinish = 0;
gui_flicker_led (LED_CD, cdu->di.unitnum - 1, LED_CD_AUDIO);
memset (px[bufnum], 0, num_sectors * 2352);
if (firstloops > 0) {
firstloops--;
for (cnt = 0; cnt < num_sectors; cnt++)
dosub (cdu, NULL, -1);
} else {
for (cnt = 0; cnt < num_sectors; cnt++) {
sector = cdda_pos;
if (cdu->cdda_scan) {
cdda_pos += cdu->cdda_scan;
if (cdda_pos < 0)
cdda_pos = 0;
} else {
cdda_pos++;
}
if (cdda_pos - num_sectors < cdu->cdda_end && cdda_pos >= cdu->cdda_end)
dofinish = 1;
t = findtoc (cdu, &sector);
if (t) {
if (t->handle && !(t->ctrl & 4)) {
uae_u8 *dst = px[bufnum] + cnt * t->size;
int totalsize = t->size + t->skipsize;
if ((t->enctype == AUDENC_MP3 || t->enctype == AUDENC_FLAC) && t->data) {
if (t->filesize >= sector * totalsize + t->offset + t->size)
memcpy (dst, t->data + sector * totalsize + t->offset, t->size);
} else if (t->enctype == AUDENC_PCM) {
if (sector * totalsize + t->offset + totalsize < t->filesize) {
zfile_fseek (t->handle, sector * totalsize + t->offset, SEEK_SET);
zfile_fread (dst, t->size, 1, t->handle);
}
}
}
dosub (cdu, t, cdda_pos);
}
}
}
volume_main = currprefs.sound_volume;
int vol_mult[2];
for (int j = 0; j < 2; j++) {
volume[j] = cdu->cdda_volume[j];
vol_mult[j] = (100 - volume_main) * volume[j] / 100;
if (vol_mult[j])
vol_mult[j]++;
if (vol_mult[j] >= 32768)
vol_mult[j] = 32768;
}
uae_s16 *p = (uae_s16*)(px[bufnum]);
for (i = 0; i < num_sectors * 2352 / 4; i++) {
p[i * 2 + 0] = p[i * 2 + 0] * vol_mult[0] / 32768;
p[i * 2 + 1] = p[i * 2 + 1] * vol_mult[1] / 32768;
}
bufon[bufnum] = 1;
mmr = waveOutWrite (cdda_wavehandle, &whdr[bufnum], sizeof (WAVEHDR));
if (mmr != MMSYSERR_NOERROR) {
write_log ("IMAGE CDDA: waveOutWrite %d\n", mmr);
break;
}
cdu->cd_last_pos = cdda_pos;
if (dofinish) {
cdu->cdda_play_finished = 1;
cdu->cdda_play = -1;
cdda_pos = cdu->cdda_end + 1;
}
}
if (bufon[0] == 0 && bufon[1] == 0) {
while (!(whdr[0].dwFlags & WHDR_DONE) || !(whdr[1].dwFlags & WHDR_DONE))
Sleep (10);
while (cdu->cdda_paused && cdu->cdda_play > 0)
Sleep (10);
}
bufnum = 1 - bufnum;
}
}
end:
while (!(whdr[0].dwFlags & WHDR_DONE) || !(whdr[1].dwFlags & WHDR_DONE))
Sleep (10);
for (i = 0; i < 2; i++)
waveOutUnprepareHeader (cdda_wavehandle, &whdr[i], sizeof (WAVEHDR));
while (cdimage_unpack_active == 1)
Sleep (10);
cdda_closewav ();
xfree (p);
cdu->cdda_play = 0;
write_log ("IMAGE CDDA: thread killed\n");
return NULL;
}
#endif
static void cdda_stop (struct cdunit *cdu)
{
if (cdu->cdda_play > 0) {
cdu->cdda_play = -1;
while (cdu->cdda_play) {
Sleep (10);
}
}
cdu->cdda_play_finished = 0;
cdu->cdda_paused = 0;
}
static int command_pause (int unitnum, int paused)
{
struct cdunit *cdu = unitisopen (unitnum);
if (!cdu)
return -1;
int old = cdu->cdda_paused;
cdu->cdda_paused = paused;
return old;
}
static int command_stop (int unitnum)
{
struct cdunit *cdu = unitisopen (unitnum);
if (!cdu)
return 0;
cdda_stop (cdu);
return 1;
}
static int command_play (int unitnum, int startlsn, int endlsn, int scan, play_subchannel_callback subfunc)
{
struct cdunit *cdu = unitisopen (unitnum);
if (!cdu)
return 0;
if (!isaudiotrack (&cdu->di.toc, startlsn))
return 0;
cdu->cdda_play_finished = 0;
cdu->cd_last_pos = startlsn;
cdu->cdda_start = startlsn;
cdu->cdda_end = endlsn;
cdu->cdda_subfunc = subfunc;
cdu->cdda_scan = scan > 0 ? 10 : (scan < 0 ? 10 : 0);
if (!cdu->cdda_play)
uae_start_thread ("cdimage_cdda_play", cdda_play_func, cdu, NULL);
cdu->cdda_play++;
return 1;
}
static int command_qcode (int unitnum, uae_u8 *buf, int sector)
{
struct cdunit *cdu = unitisopen (unitnum);
if (!cdu)
return 0;
uae_u8 subbuf[SUB_CHANNEL_SIZE];
uae_u8 *p;
int trk;
int pos;
int status;
memset (buf, 0, SUBQ_SIZE);
p = buf;
status = AUDIO_STATUS_NO_STATUS;
if (cdu->cdda_play > 0) {
status = AUDIO_STATUS_IN_PROGRESS;
if (cdu->cdda_paused)
status = AUDIO_STATUS_PAUSED;
} else if (cdu->cdda_play_finished) {
status = AUDIO_STATUS_PLAY_COMPLETE;
}
if (sector < 0)
pos = cdu->cd_last_pos;
else
pos = sector;
p[1] = status;
p[3] = 12;
p = buf + 4;
struct cdtoc *td = NULL;
for (trk = 0; trk <= cdu->tracks; trk++) {
td = &cdu->toc[trk];
if (pos < td->address) {
if (trk > 0)
td--;
break;
}
if (pos >= td->address && pos < td[1].address)
break;
}
if (!td)
return 0;
getsub (subbuf, cdu, td, pos);
memcpy (p, subbuf + 12, 12);
// write_log ("%6d %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x\n",
// pos, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11]);
return 1;
}
static uae_u32 command_volume (int unitnum, uae_u16 volume_left, uae_u16 volume_right)
{
struct cdunit *cdu = unitisopen (unitnum);
if (!cdu)
return -1;
uae_u32 old = (cdu->cdda_volume[1] << 16) | (cdu->cdda_volume[0] << 0);
cdu->cdda_volume[0] = volume_left;
cdu->cdda_volume[1] = volume_right;
return old;
}
extern void encode_l2 (uae_u8 *p, int address);
static int command_rawread (int unitnum, uae_u8 *data, int sector, int size, int sectorsize, uae_u32 extra)
{
int ret = 0;
struct cdunit *cdu = unitisopen (unitnum);
if (!cdu)
return 0;
struct cdtoc *t = findtoc (cdu, &sector);
if (!t || t->handle == NULL)
return 0;
cdda_stop (cdu);
if (sectorsize > 0) {
if (sectorsize == 2352 && t->size == 2048) {
// 2048 -> 2352
while (size-- > 0) {
memset (data, 0, 16);
zfile_fseek (t->handle, t->offset + sector * t->size, SEEK_SET);
zfile_fread (data + 16, t->size, 1, t->handle);
encode_l2 (data, sector + 150);
sector++;
data += sectorsize;
}
} else if (sectorsize == 2048 && t->size == 2352) {
// 2352 -> 2048
while (size-- > 0) {
zfile_fseek (t->handle, t->offset + sector * t->size + 16, SEEK_SET);
zfile_fread (data, sectorsize, 1, t->handle);
sector++;
data += sectorsize;
}
} else if (sectorsize == 2336 && t->size == 2352) {
// 2352 -> 2336
while (size-- > 0) {
zfile_fseek (t->handle, t->offset + sector * t->size + 16, SEEK_SET);
zfile_fread (data, sectorsize, 1, t->handle);
sector++;
data += sectorsize;
}
} else if (sectorsize == t->size) {
// no change
zfile_fseek (t->handle, t->offset + sector * t->size, SEEK_SET);
zfile_fread (data, sectorsize, size, t->handle);
sector += size;
}
cdu->cd_last_pos = sector;
ret = sectorsize * size;
} else {
uae_u8 sectortype = extra >> 16;
uae_u8 cmd9 = extra >> 8;
int sync = (cmd9 >> 7) & 1;
int headercodes = (cmd9 >> 5) & 3;
int userdata = (cmd9 >> 4) & 1;
int edcecc = (cmd9 >> 3) & 1;
int errorfield = (cmd9 >> 1) & 3;
uae_u8 subs = extra & 7;
if (subs != 0 && subs != 1 && subs != 2 && subs != 4)
return -1;
if (isaudiotrack (&cdu->di.toc, sector)) {
if (sectortype != 0 && sectortype != 1)
return -2;
if (t->size != 2352)
return -1;
for (int i = 0; i < size; i++) {
zfile_fseek (t->handle, t->offset + sector * t->size, SEEK_SET);
zfile_fread (data, t->size, 1, t->handle);
uae_u8 *p = data + t->size;
if (subs) {
uae_u8 subdata[SUB_CHANNEL_SIZE];
getsub (subdata, cdu, t, sector);
if (subs == 4) { // all, de-interleaved
memcpy (p, subdata, SUB_CHANNEL_SIZE);
p += SUB_CHANNEL_SIZE;
} else if (subs == 2) { // q-only
memcpy (p, subdata + SUB_ENTRY_SIZE, SUB_ENTRY_SIZE);
p += SUB_ENTRY_SIZE;
} else if (subs == 1) { // all, interleaved
sub_to_interleaved (subdata, p);
p += SUB_CHANNEL_SIZE;
}
}
ret += p - data;
data = p;
sector++;
}
}
}
return ret;
}
// this only supports 2048 byte sectors
static int command_read (int unitnum, uae_u8 *data, int sector, int size)
{
struct cdunit *cdu = unitisopen (unitnum);
if (!cdu)
return 0;
struct cdtoc *t = findtoc (cdu, &sector);
int offset;
if (!t || t->handle == NULL)
return NULL;
cdda_stop (cdu);
if (t->size == 2048) {
int offset = 0;
zfile_fseek (t->handle, t->offset + sector * t->size + offset, SEEK_SET);
zfile_fread (data, size, 2048, t->handle);
sector += size;
} else {
offset = 16;
zfile_fseek (t->handle, t->offset + sector * t->size + offset, SEEK_SET);
zfile_fread (data, size, 2048, t->handle);
cdu->cd_last_pos = sector;
return 1;
}
static int command_toc (int unitnum, struct cd_toc_head *th)
{
struct cdunit *cdu = unitisopen (unitnum);
if (!cdu)
return 0;
int i;
memset (&cdu->di.toc, 0, sizeof (struct cd_toc_head));
if (!cdu->tracks)
return 0;
memset (th, 0, sizeof (struct cd_toc_head));
struct cd_toc *toc = &th->toc[0];
th->first_track = 1;
th->last_track = cdu->tracks;
th->points = cdu->tracks + 3;
th->tracks = cdu->tracks;
th->lastaddress = cdu->toc[cdu->tracks].address;
toc->adr = 1;
toc->point = 0xa0;
toc->track = th->first_track;
toc++;
th->first_track_offset = 1;
for (i = 0; i < cdu->tracks; i++) {
toc->adr = cdu->toc[i].adr;
toc->control = cdu->toc[i].ctrl;
toc->track = i + 1;
toc->point = i + 1;
toc->paddress = cdu->toc[i].address;
toc++;
}
th->last_track_offset = cdu->tracks;
toc->adr = 1;
toc->point = 0xa1;
toc->track = th->last_track;
toc++;
toc->adr = 1;
toc->point = 0xa2;
toc->paddress = th->lastaddress;
toc++;
memcpy (&cdu->di.toc, th, sizeof (struct cd_toc_head));
gui_flicker_led (LED_CD, unitnum, 1);
return 1;
}
static void skipspace (TCHAR **s)
{
while (_istspace (**s))
(*s)++;
}
static void skipnspace (TCHAR **s)
{
while (!_istspace (**s))
(*s)++;
}
static TCHAR *nextstring (TCHAR **sp)
{
TCHAR *s;
TCHAR *out = NULL;
skipspace (sp);
s = *sp;
if (*s == '\"') {
s++;
out = s;
while (*s && *s != '\"')
s++;
*s++ = 0;
} else if (*s) {
out = s;
skipnspace (&s);
*s++ = 0;
}
*sp = s;
return out;
}
static int readval (const TCHAR *s)
{
int base = 10;
TCHAR *endptr;
if (s[0] == '0' && _totupper (s[1]) == 'X')
s += 2, base = 16;
return _tcstol (s, &endptr, base);
}
#define MEDIA_DESCRIPTOR "MEDIA DESCRIPTOR"
/* MDS spec structures from cdemu */
#define MDS_MEDIUM_CD 0x00 /* CD-ROM */
#define MDS_MEDIUM_CD_R 0x01 /* CD-R */
#define MDS_MEDIUM_CD_RW 0x02 /* CD-RW */
#define MDS_MEDIUM_DVD 0x10 /* DVD-ROM */
#define MDS_MEDIUM_DVD_MINUS_R 0x12 /* DVD-R */
#define MDS_TRACKMODE_UNKNOWN 0x00
#define MDS_TRACKMODE_AUDIO 0xA9 /* sector size = 2352 */
#define MDS_TRACKMODE_MODE1 0xAA /* sector size = 2048 */
#define MDS_TRACKMODE_MODE2 0xAB /* sector size = 2336 */
#define MDS_TRACKMODE_MODE2_FORM1 0xAC /* sector size = 2048 */
#define MDS_TRACKMODE_MODE2_FORM2 0xAD /* sector size = 2324 (+4) */
#define MDS_SUBCHAN_NONE 0x00 /* no subchannel */
#define MDS_SUBCHAN_PW_INTERLEAVED 0x08 /* 96-byte PW subchannel, interleaved */
#define MDS_POINT_TRACK_FIRST 0xA0 /* info about first track */
#define MDS_POINT_TRACK_LAST 0xA1 /* info about last track */
#define MDS_POINT_TRACK_LEADOUT 0xA2 /* info about lead-out */
#pragma pack(1)
typedef struct {
uae_u8 signature[16]; /* "MEDIA DESCRIPTOR" */
uae_u8 version[2]; /* Version ? */
uae_u16 medium_type; /* Medium type */
uae_u16 num_sessions; /* Number of sessions */
uae_u16 __dummy1__[2]; /* Wish I knew... */
uae_u16 bca_len; /* Length of BCA data (DVD-ROM) */
uae_u32 __dummy2__[2];
uae_u32 bca_data_offset; /* Offset to BCA data (DVD-ROM) */
uae_u32 __dummy3__[6]; /* Probably more offsets */
uae_u32 disc_structures_offset; /* Offset to disc structures */
uae_u32 __dummy4__[3]; /* Probably more offsets */
uae_u32 sessions_blocks_offset; /* Offset to session blocks */
uae_u32 dpm_blocks_offset; /* offset to DPM data blocks */
} MDS_Header; /* length: 88 bytes */
typedef struct {
uae_s32 session_start; /* Session's start address */
uae_s32 session_end; /* Session's end address */
uae_u16 session_number; /* (Unknown) */
uae_u8 num_all_blocks; /* Number of all data blocks. */
uae_u8 num_nontrack_blocks; /* Number of lead-in data blocks */
uae_u16 first_track; /* Total number of sessions in image? */
uae_u16 last_track; /* Number of regular track data blocks. */
uae_u32 __dummy2__; /* (unknown) */
uae_u32 tracks_blocks_offset; /* Offset of lead-in+regular track data blocks. */
} MDS_SessionBlock; /* length: 24 bytes */
typedef struct {
uae_u8 mode; /* Track mode */
uae_u8 subchannel; /* Subchannel mode */
uae_u8 adr_ctl; /* Adr/Ctl */
uae_u8 __dummy2__; /* Track flags? */
uae_u8 point; /* Track number. (>0x99 is lead-in track) */
uae_u32 __dummy3__;
uae_u8 min; /* Min */
uae_u8 sec; /* Sec */
uae_u8 frame; /* Frame */
uae_u32 extra_offset; /* Start offset of this track's extra block. */
uae_u16 sector_size; /* Sector size. */
uae_u8 __dummy4__[18];
uae_u32 start_sector; /* Track start sector (PLBA). */
uae_u64 start_offset; /* Track start offset. */
uae_u8 session; /* Session or index? */
uae_u8 __dummy5__[3];
uae_u32 footer_offset; /* Start offset of footer. */
uae_u8 __dummy6__[24];
} MDS_TrackBlock; /* length: 80 bytes */
typedef struct {
uae_u32 pregap; /* Number of sectors in pregap. */
uae_u32 length; /* Number of sectors in track. */
} MDS_TrackExtraBlock; /* length: 8 bytes */
typedef struct {
uae_u32 filename_offset; /* Start offset of image filename. */
uae_u32 widechar_filename; /* Seems to be set to 1 if widechar filename is used */
uae_u32 __dummy1__;
uae_u32 __dummy2__;
} MDS_Footer; /* length: 16 bytes */
#pragma pack()
static int parsemds (struct cdunit *cdu, struct zfile *zmds, const TCHAR *img)
{
MDS_Header *head;
struct cdtoc *t;
uae_u8 *mds = NULL;
write_log ("MDS TOC: '%s'\n", img);
int size = zfile_size (zmds);
mds = xmalloc (uae_u8, size);
if (!mds)
goto end;
if (zfile_fread (mds, size, 1, zmds) != 1)
goto end;
head = (MDS_Header*)mds;
if (!memcmp (&head, MEDIA_DESCRIPTOR, strlen (MEDIA_DESCRIPTOR)))
goto end;
if (head->version[0] != 1) {
write_log ("unsupported MDS version %d, only v.1 supported\n", head->version[0]);
goto end;
}
MDS_SessionBlock *sb = (MDS_SessionBlock*)(mds + head->sessions_blocks_offset);
cdu->tracks = sb->last_track - sb->first_track + 1;
for (int i = 0; i < sb->num_all_blocks; i++) {
MDS_TrackBlock *tb = (MDS_TrackBlock*)(mds + sb->tracks_blocks_offset + i * sizeof MDS_TrackBlock);
int point = tb->point;
int tracknum = -1;
if (point == 0xa2)
tracknum = cdu->tracks;
else if (point >= 1 && point <= 99)
tracknum = point - 1;
if (tracknum >= 0) {
MDS_Footer *footer = tb->footer_offset == 0 ? NULL : (MDS_Footer*)(mds + tb->footer_offset);
MDS_TrackExtraBlock *teb = tb->extra_offset == 0 ? NULL : (MDS_TrackExtraBlock*)(mds + tb->extra_offset);
t = &cdu->toc[tracknum];
t->adr = tb->adr_ctl >> 4;
t->ctrl = tb->adr_ctl & 15;
if (point == 0xa2)
t->address = sb->session_end;
else
t->address = tb->start_sector;
t->track = point;
t->offset = tb->start_offset;
t->size = tb->sector_size;
if (footer) {
TCHAR *fname = NULL;
if (footer->widechar_filename == 0)
fname = au ((char*)(mds + footer->filename_offset));
else
fname = my_strdup ((wchar_t*)(mds + footer->filename_offset));
if (fname[0] == '*' && fname[1] == '.') {
TCHAR newname[MAX_DPATH];
_tcscpy (newname, img);
TCHAR *ext = _tcsrchr (newname, '.');
if (ext)
_tcscpy (ext, fname + 1);
xfree (fname);
fname = my_strdup (newname);
}
t->handle = zfile_fopen (fname, "rb", ZFD_NORMAL);
t->fname = my_strdup (fname);
if (t->handle)
t->filesize = zfile_size (t->handle);
}
if (tb->subchannel && t->handle) {
t->suboffset = t->offset + t->size;
t->subcode = 1; // interleaved
t->subhandle = zfile_dup (t->handle);
t->skipsize = SUB_CHANNEL_SIZE;
t->size -= SUB_CHANNEL_SIZE;
if ((t->ctrl & 0x0c) != 4)
t->enctype = AUDENC_PCM;
}
}
}
end:
xfree (mds);
return cdu->tracks;
}
static int parseccd (struct cdunit *cdu, struct zfile *zcue, const TCHAR *img)
{
int mode;
int num, tracknum, trackmode;
int adr, control, lba;
bool gotlba;
struct cdtoc *t;
struct zfile *zimg, *zsub;
TCHAR fname[MAX_DPATH];
write_log ("CCD TOC: '%s'\n", img);
_tcscpy (fname, img);
TCHAR *ext = _tcsrchr (fname, '.');
if (ext)
*ext = 0;
_tcscat (fname, ".img");
zimg = zfile_fopen (fname, "rb", ZFD_NORMAL);
if (!zimg) {
write_log ("CCD: can't open '%s'\n", fname);
//return 0;
}
ext = _tcsrchr (fname, '.');
if (ext)
*ext = 0;
_tcscat (fname, ".sub");
zsub = zfile_fopen (fname, "rb", ZFD_NORMAL);
if (zsub)
write_log ("CCD: '%s' detected\n", fname);
num = -1;
mode = -1;
for (;;) {
TCHAR buf[MAX_DPATH], *p;
if (!zfile_fgets (buf, sizeof buf / sizeof (TCHAR), zcue))
break;
p = buf;
skipspace (&p);
if (!_tcsnicmp (p, "[DISC]", 6)) {
mode = 1;
} else if (!_tcsnicmp (p, "[ENTRY ", 7)) {
t = NULL;
mode = 2;
num = readval (p + 7);
if (num < 0)
break;
adr = control = -1;
gotlba = false;
} else if (!_tcsnicmp (p, "[TRACK ", 7)) {
mode = 3;
tracknum = readval (p + 7);
trackmode = -1;
if (tracknum <= 0 || tracknum > 99)
break;
t = &cdu->toc[tracknum - 1];
}
if (mode < 0)
continue;
if (mode == 1) {
if (!_tcsnicmp (p, "TocEntries=", 11)) {
cdu->tracks = readval (p + 11) - 3;
if (cdu->tracks <= 0 || cdu->tracks > 99)
break;
}
continue;
}
if (cdu->tracks <= 0)
break;
if (mode == 2) {
if (!_tcsnicmp (p, "SESSION=", 8)) {
if (readval (p + 8) != 1)
mode = -1;
continue;
} else if (!_tcsnicmp (p, "POINT=", 6)) {
tracknum = readval (p + 6);
if (tracknum <= 0)
break;
if (tracknum >= 0xa0 && tracknum != 0xa2) {
mode = -1;
continue;
}
if (tracknum == 0xa2)
tracknum = cdu->tracks + 1;
t = &cdu->toc[tracknum - 1];
continue;
}
if (!_tcsnicmp (p, "ADR=", 4))
adr = readval (p + 4);
if (!_tcsnicmp (p, "CONTROL=", 8))
control = readval (p + 8);
if (!_tcsnicmp (p, "PLBA=", 5)) {
lba = readval (p + 5);
gotlba = true;
}
if (gotlba && adr >= 0 && control >= 0) {
t->adr = adr;
t->ctrl = control;
t->address = lba;
t->offset = 0;
t->size = 2352;
t->offset = lba * t->size;
t->track = tracknum;
if ((control & 0x0c) != 4)
t->enctype = AUDENC_PCM;
if (zsub) {
t->subcode = 2;
t->subhandle = zfile_dup (zsub);
t->suboffset = 0;
}
if (zimg) {
t->handle = zfile_dup (zimg);
t->fname = my_strdup (zfile_getname (zimg));
}
mode = -1;
}
} else if (mode == 3) {
if (!_tcsnicmp (p, "MODE=", 5))
trackmode = _tstol (p + 5);
if (trackmode < 0 || trackmode > 2)
continue;
}
}
return cdu->tracks;
}
static int parsecue (struct cdunit *cdu, struct zfile *zcue, const TCHAR *img)
{
int tracknum, index0, pregap;
int offset, secoffset, newfile;
TCHAR *fname, *fnametype;
audenc fnametypeid;
int ctrl;
mp3decoder *mp3dec = NULL;
fname = NULL;
fnametype = NULL;
tracknum = 0;
offset = 0;
secoffset = 0;
newfile = 0;
ctrl = 0;
index0 = -1;
pregap = 0;
fnametypeid = AUDENC_NONE;
write_log ("CUE TOC: '%s'\n", img);
for (;;) {
TCHAR buf[MAX_DPATH], *p;
if (!zfile_fgets (buf, sizeof buf / sizeof (TCHAR), zcue))
break;
p = buf;
skipspace (&p);
if (!_tcsnicmp (p, "FILE", 4)) {
p += 4;
xfree (fname);
fname = my_strdup (nextstring (&p));
fnametype = nextstring (&p);
fnametypeid = AUDENC_NONE;
if (!fnametype)
break;
if (_tcsicmp (fnametype, "BINARY") && _tcsicmp (fnametype, "WAVE") && _tcsicmp (fnametype, "MP3")) {
write_log ("CUE: unknown file type '%s' ('%s')\n", fnametype, fname);
}
fnametypeid = AUDENC_PCM;
if (!_tcsicmp (fnametype, "MP3"))
fnametypeid = AUDENC_MP3;
else if (!_tcsicmp (fnametype, "FLAC"))
fnametypeid = AUDENC_FLAC;
offset = 0;
newfile = 1;
ctrl = 0;
} else if (!_tcsnicmp (p, "FLAGS", 5)) {
ctrl &= ~(1 | 2 | 8);
for (;;) {
TCHAR *f = nextstring (&p);
if (!f)
break;
if (!_tcsicmp (f, "PRE"))
ctrl |= 1;
if (!_tcsicmp (f, "DCP"))
ctrl |= 2;
if (!_tcsicmp (f, "4CH"))
ctrl |= 8;
}
} else if (!_tcsnicmp (p, "TRACK", 5)) {
int size;
TCHAR *tracktype;
p += 5;
//pregap = 0;
index0 = -1;
tracknum = _tstoi (nextstring (&p));
tracktype = nextstring (&p);
if (!tracktype)
break;
size = 2352;
if (!_tcsicmp (tracktype, "AUDIO")) {
ctrl &= ~4;
} else {
ctrl |= 4;
if (!_tcsicmp (tracktype, "MODE1/2048"))
size = 2048;
else if (!_tcsicmp (tracktype, "MODE1/2352"))
size = 2352;
else if (!_tcsicmp (tracktype, "MODE2/2336") || !_tcsicmp (tracktype, "CDI/2336"))
size = 2336;
else if (!_tcsicmp (tracktype, "MODE2/2352") || !_tcsicmp (tracktype, "CDI/2352"))
size = 2352;
else {
write_log ("CUE: unknown tracktype '%s' ('%s')\n", tracktype, fname);
}
}
if (tracknum >= 1 && tracknum <= 99) {
struct cdtoc *t = &cdu->toc[tracknum - 1];
struct zfile *ztrack;
if (tracknum > 1 && newfile) {
t--;
secoffset += t->filesize / t->size;
t++;
}
newfile = 0;
ztrack = zfile_fopen (fname, "rb", ZFD_ARCHIVE | ZFD_DELAYEDOPEN);
if (!ztrack) {
TCHAR tmp[MAX_DPATH];
_tcscpy (tmp, fname);
p = tmp + _tcslen (tmp);
while (p > tmp) {
if (*p == '/' || *p == '\\') {
ztrack = zfile_fopen (p + 1, "rb", ZFD_ARCHIVE | ZFD_DELAYEDOPEN);
if (ztrack) {
xfree (fname);
fname = my_strdup (p + 1);
}
break;
}
p--;
}
}
if (!ztrack) {
TCHAR tmp[MAX_DPATH];
TCHAR *s2;
_tcscpy (tmp, zfile_getname (zcue));
s2 = _tcsrchr (tmp, '\\');
if (!s2)
s2 = _tcsrchr (tmp, '/');
if (s2) {
s2[0] = 0;
_tcscat (tmp, "\\");
_tcscat (tmp, fname);
ztrack = zfile_fopen (tmp, "rb", ZFD_ARCHIVE | ZFD_DELAYEDOPEN);
}
}
t->track = tracknum;
t->ctrl = ctrl;
t->adr = 1;
t->handle = ztrack;
t->size = size;
t->fname = my_strdup (fname);
if (tracknum > cdu->tracks)
cdu->tracks = tracknum;
if (t->handle)
t->filesize = zfile_size (t->handle);
}
} else if (!_tcsnicmp (p, "PREGAP", 6)) {
TCHAR *tt;
int tn;
p += 6;
tt = nextstring (&p);
tn = _tstoi (tt) * 60 * 75;
tn += _tstoi (tt + 3) * 75;
tn += _tstoi (tt + 6);
pregap += tn;
} else if (!_tcsnicmp (p, "INDEX", 5)) {
int idxnum;
int tn = 0;
TCHAR *tt;
p += 5;
idxnum = _tstoi (nextstring (&p));
tt = nextstring (&p);
tn = _tstoi (tt) * 60 * 75;
tn += _tstoi (tt + 3) * 75;
tn += _tstoi (tt + 6);
if (idxnum == 0) {
index0 = tn;
} else if (idxnum == 1 && tracknum >= 1 && tracknum <= 99) {
struct cdtoc *t = &cdu->toc[tracknum - 1];
if (!t->address) {
t->address = tn + secoffset;
t->address += pregap;
if (tracknum > 1) {
offset += t->address - t[-1].address;
} else {
offset += t->address;
}
if (!secoffset)
t->offset = offset * t->size;
if (fnametypeid == AUDENC_PCM && t->handle) {
struct zfile *zf = t->handle;
uae_u8 buf[16] = { 0 };
zfile_fread (buf, 12, 1, zf);
if (!memcmp (buf, "RIFF", 4) && !memcmp (buf + 8, "WAVE", 4)) {
int size;
for (;;) {
memset (buf, 0, sizeof buf);
if (zfile_fread (buf, 8, 1, zf) != 1)
break;
size = (buf[4] << 0) | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
if (!memcmp (buf, "data", 4))
break;
if (size <= 0)
break;
zfile_fseek (zf, size, SEEK_CUR);
}
t->offset += zfile_ftell (zf);
t->filesize = size;
}
t->enctype = fnametypeid;
} else if (fnametypeid == AUDENC_MP3 && t->handle) {
if (!mp3dec) {
try {
mp3dec = new mp3decoder();
} catch (exception) { }
}
if (mp3dec) {
t->offset = 0;
t->filesize = mp3dec->getsize (t->handle);
if (t->filesize)
t->enctype = fnametypeid;
}
} else if (fnametypeid == AUDENC_FLAC && t->handle) {
flac_get_size (t);
if (t->filesize)
t->enctype = fnametypeid;
}
}
}
}
}
struct cdtoc *t = &cdu->toc[cdu->tracks - 1];
int size = t->filesize;
if (!secoffset)
size -= offset * t->size;
if (size < 0)
size = 0;
cdu->toc[cdu->tracks].address = t->address + size / t->size;
xfree (fname);
delete mp3dec;
return cdu->tracks;
}
static int parse_image (struct cdunit *cdu, const TCHAR *img)
{
struct zfile *zcue;
int i;
const TCHAR *ext;
int secoffset;
secoffset = 0;
cdu->tracks = 0;
if (!img)
return 0;
zcue = zfile_fopen (img, "rb", ZFD_ARCHIVE | ZFD_CD | ZFD_DELAYEDOPEN);
if (!zcue)
return 0;
ext = _tcsrchr (zfile_getname (zcue), '.');
if (ext) {
TCHAR curdir[MAX_DPATH];
TCHAR oldcurdir[MAX_DPATH], *p;
ext++;
oldcurdir[0] = 0;
_tcscpy (curdir, img);
p = curdir + _tcslen (curdir);
while (p > curdir) {
if (*p == '/' || *p == '\\')
break;
p--;
}
*p = 0;
if (p > curdir)
my_setcurrentdir (curdir, oldcurdir);
if (!_tcsicmp (ext, "cue"))
parsecue (cdu, zcue, img);
else if (!_tcsicmp (ext, "ccd"))
parseccd (cdu, zcue, img);
else if (!_tcsicmp (ext, "mds"))
parsemds (cdu, zcue, img);
if (oldcurdir[0])
my_setcurrentdir (oldcurdir, NULL);
}
if (!cdu->tracks) {
uae_u64 siz = zfile_size (zcue);
if (siz >= 16384 && (siz % 2048) == 0 || (siz % 2352) == 0) {
struct cdtoc *t = &cdu->toc[0];
cdu->tracks = 1;
t->ctrl = 4;
t->adr = 1;
t->fname = my_strdup (img);
t->handle = zcue;
t->size = (siz % 2048) == 0 ? 2048 : 2352;
t->filesize = siz;
write_log ("CUE: plain CD image mounted!\n");
cdu->toc[1].address = t->address + t->filesize / t->size;
zcue = NULL;
}
}
for (i = 0; i <= cdu->tracks; i++) {
struct cdtoc *t = &cdu->toc[i];
uae_u32 msf = lsn2msf (t->address);
if (i < cdu->tracks)
write_log ("%2d: ", i + 1);
else
write_log (" ");
write_log ("%7d %02d:%02d:%02d",
t->address, (msf >> 16) & 0xff, (msf >> 8) & 0xff, (msf >> 0) & 0xff);
if (i < cdu->tracks)
write_log (" %s %x %10d %10d %s", (t->ctrl & 4) ? "DATA " : (t->subcode ? "CDA+SUB" : "CDA "),
t->ctrl, t->offset, t->filesize, t->handle == NULL ? "[FILE ERROR]" : "");
write_log ("\n");
if (i < cdu->tracks)
write_log (" - %s\n", t->fname);
if (t->handle && !t->filesize)
t->filesize = zfile_size (t->handle);
}
cdu->blocksize = 2048;
cdu->cdsize = cdu->toc[cdu->tracks].address * cdu->blocksize;
zfile_fclose (zcue);
return 1;
}
static int ismedia (int unitnum, int quick)
{
struct cdunit *cdu = &cdunits[unitnum];
if (!cdu->enabled)
return -1;
return cdu->tracks > 0 ? 1 : 0;
}
static struct device_info *info_device (int unitnum, struct device_info *di, int quick)
{
struct cdunit *cdu = &cdunits[unitnum];
memset (di, 0, sizeof (struct device_info));
if (!cdu->enabled)
return 0;
di->open = cdu->open;
di->slow_unit = cdu->slowunit;
di->removable = 1;
di->bus = unitnum;
di->target = 0;
di->lun = 0;
di->media_inserted = 0;
di->bytespersector = 2048;
di->mediapath[0] = 0;
di->cylinders = 1;
di->trackspercylinder = 1;
di->sectorspertrack = cdu->cdsize / di->bytespersector;
if (ismedia (unitnum, 1)) {
di->media_inserted = 1;
_tcscpy (di->mediapath, currprefs.cdslots[unitnum].name);
}
memset (&di->toc, 0, sizeof (struct cd_toc_head));
command_toc (unitnum, &di->toc);
di->write_protected = 1;
di->type = INQ_ROMD;
di->unitnum = unitnum + 1;
if (di->mediapath[0]) {
_tcscpy (di->label, "IMG:");
_tcscat (di->label, di->mediapath);
} else {
_tcscpy (di->label, "IMG:<EMPTY>");
}
di->backend = "IMAGE";
return di;
}
static void unload_image (struct cdunit *cdu)
{
int i;
for (i = 0; i < cdu->tracks; i++) {
struct cdtoc *t = &cdu->toc[i];
zfile_fclose (t->handle);
if (t->handle != t->subhandle)
zfile_fclose (t->subhandle);
xfree (t->fname);
xfree (t->data);
xfree (t->subdata);
}
memset (cdu->toc, 0, sizeof cdu->toc);
cdu->tracks = 0;
cdu->cdsize = 0;
}
static int open_device (int unitnum, const TCHAR *ident, int flags)
{
struct cdunit *cdu = &cdunits[unitnum];
if (cdu->open)
return 0;
uae_sem_init (&cdu->sub_sem, 0, 1);
parse_image (cdu, ident);
cdu->open = true;
cdu->enabled = true;
cdu->cdda_volume[0] = 0x7fff;
cdu->cdda_volume[1] = 0x7fff;
cdu->slowunit = (flags & 1) != 0;
blkdev_cd_change (unitnum, currprefs.cdslots[unitnum].name);
if (cdimage_unpack_thread == 0) {
init_comm_pipe (&unpack_pipe, 10, 1);
uae_start_thread ("cdimage_unpack", cdda_unpack_func, NULL, NULL);
while (cdimage_unpack_thread == 0)
Sleep (10);
}
return 1;
}
static void close_device (int unitnum)
{
struct cdunit *cdu = &cdunits[unitnum];
if (cdu->open == false)
return;
cdda_stop (cdu);
unload_image (cdu);
uae_sem_destroy (&cdu->sub_sem);
cdu->open = false;
blkdev_cd_change (unitnum, currprefs.cdslots[unitnum].name);
if (cdimage_unpack_thread) {
cdimage_unpack_thread = 0;
write_comm_pipe_u32 (&unpack_pipe, -1, 0);
write_comm_pipe_u32 (&unpack_pipe, -1, 1);
while (cdimage_unpack_thread == 0)
Sleep (10);
cdimage_unpack_thread = 0;
destroy_comm_pipe (&unpack_pipe);
}
}
static void close_bus (void)
{
if (!bus_open) {
write_log ("IMAGE close_bus() when already closed!\n");
return;
}
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
struct cdunit *cdu = &cdunits[i];
if (cdu->open)
close_device (i);
cdu->enabled = false;
}
bus_open = 0;
write_log ("IMAGE driver closed.\n");
}
static int open_bus (int flags)
{
if (bus_open) {
write_log ("IOCTL open_bus() more than once!\n");
return 1;
}
bus_open = 1;
write_log ("Image driver open.\n");
return 1;
}
struct device_functions devicefunc_cdimage = {
L"IMAGE",
open_bus, close_bus, open_device, close_device, info_device,
0, 0, 0,
command_pause, command_stop, command_play, command_volume, command_qcode,
command_toc, command_read, command_rawread, 0,
0, ismedia
};
static uae_u8 *inprec_buffer, *inprec_p;
static struct zfile *inprec_zf;
static int inprec_size;
int input_recording = 0;
static uae_u8 *inprec_plast, *inprec_plastptr;
static int inprec_div;
static uae_u32 oldbuttons[4];
static uae_u16 oldjoy[2];
int inprec_open(char *fname, int record)
{
uae_u32 t = (uae_u32)time(0);
int i;
inprec_close();
inprec_zf = zfile_fopen(fname, record > 0 ? "wb" : "rb");
if (inprec_zf == NULL)
return 0;
inprec_size = 10000;
inprec_div = 1;
if (record < 0) {
uae_u32 id;
zfile_fseek (inprec_zf, 0, SEEK_END);
inprec_size = zfile_ftell (inprec_zf);
zfile_fseek (inprec_zf, 0, SEEK_SET);
inprec_buffer = inprec_p = (uae_u8*)xmalloc (inprec_size);
zfile_fread (inprec_buffer, inprec_size, 1, inprec_zf);
inprec_plastptr = inprec_buffer;
id = inprec_pu32();
if (id != 'UAE\0') {
inprec_close();
return 0;
}
inprec_pu32();
t = inprec_pu32();
i = inprec_pu32();
while (i-- > 0)
inprec_pu8();
inprec_p = inprec_plastptr;
oldbuttons[0] = oldbuttons[1] = oldbuttons[2] = oldbuttons[3] = 0;
oldjoy[0] = oldjoy[1] = 0;
if (record < -1)
inprec_div = maxvpos;
} else if (record > 0) {
inprec_buffer = inprec_p = (uae_u8*)xmalloc (inprec_size);
inprec_ru32('UAE\0');
inprec_ru8(1);
inprec_ru8(UAEMAJOR);
inprec_ru8(UAEMINOR);
inprec_ru8(UAESUBREV);
inprec_ru32(t);
inprec_ru32(0); // extra header size
} else {
return 0;
}
input_recording = record;
srand(t);
CIA_inprec_prepare();
write_log ("inprec initialized '%s', mode=%d\n", fname, input_recording);
return 1;
}
void inprec_close(void)
{
if (!inprec_zf)
return;
if (inprec_buffer && input_recording > 0) {
hsync_counter++;
inprec_rstart(INPREC_END);
inprec_rend();
hsync_counter--;
zfile_fwrite (inprec_buffer, inprec_p - inprec_buffer, 1, inprec_zf);
inprec_p = inprec_buffer;
}
zfile_fclose (inprec_zf);
inprec_zf = NULL;
xfree (inprec_buffer);
inprec_buffer = NULL;
input_recording = 0;
write_log ("inprec finished\n");
}
void inprec_ru8(uae_u8 v)
{
*inprec_p++= v;
}
void inprec_ru16(uae_u16 v)
{
inprec_ru8((uae_u8)(v >> 8));
inprec_ru8((uae_u8)v);
}
void inprec_ru32(uae_u32 v)
{
inprec_ru16((uae_u16)(v >> 16));
inprec_ru16((uae_u16)v);
}
void inprec_rstr(const char *s)
{
while(*s) {
inprec_ru8(*s);
s++;
}
inprec_ru8(0);
}
void inprec_rstart(uae_u8 type)
{
write_log ("INPREC: %08X: %d\n", hsync_counter, type);
inprec_ru32(hsync_counter);
inprec_ru8(0);
inprec_plast = inprec_p;
inprec_ru8(0xff);
inprec_ru8(type);
}
void inprec_rend(void)
{
*inprec_plast = inprec_p - (inprec_plast + 2);
if (inprec_p >= inprec_buffer + inprec_size - 256) {
zfile_fwrite (inprec_buffer, inprec_p - inprec_buffer, 1, inprec_zf);
inprec_p = inprec_buffer;
}
}
int inprec_pstart(uae_u8 type)
{
uae_u8 *p = inprec_p;
uae_u32 hc = hsync_counter;
static uae_u8 *lastp;
uae_u32 hc_orig, hc2_orig;
if (savestate_state)
return 0;
if (p[5 + 1] == INPREC_END) {
inprec_close();
return 0;
}
hc_orig = hc;
hc /= inprec_div;
hc *= inprec_div;
for (;;) {
uae_u32 hc2 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
if (p > lastp) {
write_log ("INPREC: Next %08x (%08x=%d): %d (%d)\n", hc2, hc, hc2 - hc, p[5 + 1], p[5]);
lastp = p;
}
hc2_orig = hc2;
hc2 /= inprec_div;
hc2 *= inprec_div;
if (hc > hc2) {
write_log ("INPREC: %08x > %08x: %d (%d) missed!\n", hc, hc2, p[5 + 1], p[5]);
inprec_close();
return 0;
}
if (hc2 != hc) {
lastp = p;
break;
}
if (p[5 + 1] == type) {
write_log ("INPREC: %08x: %d (%d) (%+d)\n", hc, type, p[5], hc_orig - hc2_orig);
inprec_plast = p;
inprec_plastptr = p + 5 + 2;
return 1;
}
p += 5 + 2 + p[5];
}
inprec_plast = NULL;
return 0;
}
void inprec_pend(void)
{
uae_u8 *p = inprec_p;
uae_u32 hc = hsync_counter;
if (!inprec_plast)
return;
inprec_plast[5 + 1] = 0;
inprec_plast = NULL;
inprec_plastptr = NULL;
hc /= inprec_div;
hc *= inprec_div;
for (;;) {
uae_u32 hc2 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
hc2 /= inprec_div;
hc2 *= inprec_div;
if (hc2 != hc)
break;
if (p[5 + 1] != 0)
return;
p += 5 + 2 + p[5];
}
inprec_p = p;
if (p[5 + 1] == INPREC_END)
inprec_close();
}
uae_u8 inprec_pu8(void)
{
return *inprec_plastptr++;
}
uae_u16 inprec_pu16(void)
{
uae_u16 v = inprec_pu8() << 8;
v |= inprec_pu8();
return v;
}
uae_u32 inprec_pu32(void)
{
uae_u32 v = inprec_pu16() << 16;
v |= inprec_pu16();
return v;
}
int inprec_pstr(char *s)
{
int len = 0;
for(;;) {
uae_u8 v = inprec_pu8();
*s++ = v;
if (!v)
break;
len++;
}
return len;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment