diff --git a/BasiliskII/src/SDL/audio_sdl.cpp b/BasiliskII/src/SDL/audio_sdl.cpp index 2a6f4b74..81fff018 100644 --- a/BasiliskII/src/SDL/audio_sdl.cpp +++ b/BasiliskII/src/SDL/audio_sdl.cpp @@ -32,6 +32,10 @@ #define DEBUG 0 #include "debug.h" +#if defined(BINCUE) +#include "bincue_unix.h" +#endif + // The currently selected audio parameters (indices in audio_sample_rates[] etc. vectors) static int audio_sample_rate_index = 0; @@ -91,6 +95,11 @@ static bool open_sdl_audio(void) return false; } +#if defined(BINCUE) + OpenAudio_bincue(audio_spec.freq, audio_spec.format, audio_spec.channels, + audio_spec.silence); +#endif + char driver_name[32]; printf("Using SDL/%s audio output\n", SDL_AudioDriverName(driver_name, sizeof(driver_name) - 1)); silence_byte = audio_spec.silence; @@ -218,6 +227,9 @@ static void stream_func(void *arg, uint8 *stream, int stream_len) // Audio not active, play silence silence: memset(stream, silence_byte, stream_len); } +#if defined(BINCUE) + MixAudio_bincue(stream, stream_len); +#endif } diff --git a/BasiliskII/src/Unix/bincue_unix.cpp b/BasiliskII/src/Unix/bincue_unix.cpp new file mode 100644 index 00000000..11c93548 --- /dev/null +++ b/BasiliskII/src/Unix/bincue_unix.cpp @@ -0,0 +1,834 @@ +/* + * Copyright (C) 2002-2010 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Geoffrey Brown 2010 + * Includes ideas from dosbox src/dos/cdrom_image.cpp + * + * Limitations: 1) cue files must reference single bin file + * 2) only supports raw mode1 data and audio + * 3) no support for audio flags + * 4) requires SDL audio or OS X core audio + * 5) limited cue file keyword support + * + * Creating cue/bin files: + * cdrdao read-cd --read-raw --paranoia 3 foo.toc + * toc2cue foo.toc + */ + +#include "sysdeps.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OSX_CORE_AUDIO +#include "../MacOSX/MacOSX_sound_if.h" +static int bincue_core_audio_callback(void); +#endif + +#ifdef USE_SDL_AUDIO +#include +#include +#endif + +#include "bincue_unix.h" +#define DEBUG 0 +#include "debug.h" + +#define MAXTRACK 100 +#define MAXLINE 512 +#define CD_FRAMES 75 +#define RAW_SECTOR_SIZE 2352 +#define COOKED_SECTOR_SIZE 2048 + +// Bits of Track Control Field -- These are standard for scsi cd players + +#define PREMPHASIS 0x1 +#define COPY 0x2 +#define DATA 0x4 +#define AUDIO 0 +#define FOURTRACK 0x8 + +// Audio status -- These are standard for scsi cd players + +#define CDROM_AUDIO_INVALID 0x00 +#define CDROM_AUDIO_PLAY 0x11 +#define CDROM_AUDIO_PAUSED 0x12 +#define CDROM_AUDIO_COMPLETED 0x13 +#define CDROM_AUDIO_ERROR 0x14 +#define CDROM_AUDIO_NO_STATUS 0x15 + +typedef unsigned char uint8; + +// cuefiles can be challenging as some information is +// implied. For example, there may a pregap (also postgap) +// of silence that must be generated. Here we implement +// only the pregap. + +typedef struct { + int number; + unsigned int start; // Track start in frames + unsigned int length; // Track length in frames + loff_t fileoffset; // Track frame start within file + unsigned int pregap; // Silence in frames to generate + unsigned char tcf; // Track control field +} Track; + +typedef struct { + char *binfile; // Binary file name + unsigned int length; // file length in frames + int binfh; // binary file handle + int tcnt; // number of tracks + Track tracks[MAXTRACK]; +} CueSheet; + +typedef struct { + CueSheet *cs; // cue sheet to play from + int audiofh; // file handle for audio data + unsigned int audioposition; // current position from audiostart (bytes) + unsigned int audiostart; // start position if playing (frame) + unsigned int audioend; // end position if playing (frames) + unsigned int silence; // pregap (silence) bytes + unsigned char audiostatus; // See defines above for status + loff_t fileoffset; // offset from file beginning to audiostart +#ifdef OSX_CORE_AUDIO + OSXsoundOutput soundoutput; +#endif +} CDPlayer; + +// Minute,Second,Frame data type + +typedef struct { + int m, s, f; // note size matters since we scan for %d ! +} MSF; + +// Parser State + +static unsigned int totalPregap; +static unsigned int prestart; + +// Audio System State + +static bool audio_enabled = false; +static uint8 silence_byte; + + +// CD Player state. Note only one player is supported ! + +static CDPlayer player; + +static void FramesToMSF(unsigned int frames, MSF *msf) +{ + msf->m = frames/(60 * CD_FRAMES); + frames = frames%(60 * CD_FRAMES); + msf->s = frames/CD_FRAMES; + msf->f = frames%CD_FRAMES; +} + +static int MSFToFrames(MSF msf) +{ + return (msf.m * 60 * CD_FRAMES) + (msf.s * CD_FRAMES) + msf.f; +} + + +static int PositionToTrack(CueSheet *cs, unsigned int position) +{ + int i; + MSF msf; + + FramesToMSF(position, &msf); + + for (i = 0; i < cs->tcnt; i++) { + if ((position >= cs->tracks[i].start) && + (position <= (cs->tracks[i].start + cs->tracks[i].length))) + break; + } + return i; +} + +static bool AddTrack(CueSheet *cs) +{ + int skip = prestart; + Track *prev; + Track *curr = &(cs->tracks[cs->tcnt]); + + prestart = 0; + + if (skip > 0) { + if (skip > curr->start) { + D(bug("AddTrack: prestart > start\n")); + return false; + } + } + + curr->fileoffset = curr->start * RAW_SECTOR_SIZE; + + // now we patch up the indicated time + + curr->start += totalPregap; + + // curr->pregap is supposed to be part of this track, but it + // must be generated as silence + + totalPregap += curr->pregap; + + if (cs->tcnt == 0) { + if (curr->number != 1) { + D(bug("AddTrack: number != 1\n")); + return false; + } + cs->tcnt++; + return true; + } + + prev = &(cs->tracks[cs->tcnt - 1]); + + if (prev->start < skip) + prev->length = skip - prev->start - curr->pregap; + else + prev->length = curr->start - prev->start - curr->pregap; + + // error checks + + if (curr->number <= 1) { + D(bug("Bad track number %d\n", curr->number)); + return false; + } + if ((prev->number + 1 != curr->number) && (curr->number != 0xAA)) { + D(bug("Bad track number %d\n", curr->number)); + return false; + } + if (curr->start < prev->start + prev->length) { + D(bug("unexpected start %d\n", curr->start)); + return false; + } + + cs->tcnt++; + return true; +} + +static bool ParseCueSheet(FILE *fh, CueSheet *cs, const char *cuefile) +{ + bool seen1st = false; + char line[MAXLINE]; + unsigned int i_line=0; + char *keyword; + + totalPregap = 0; + prestart = 0; + + while (fgets(line, MAXLINE, fh) != NULL) { + Track *curr = &cs->tracks[cs->tcnt]; + + // check for CUE file + + if (!i_line && (strncmp("FILE", line, 4) != 0)) { + return false; + } + i_line++; + + // extract keyword + + if (NULL != (keyword = strtok(line, " \t\n\t"))) { + if (!strcmp("FILE", keyword)) { + char *filename; + char *filetype; + + if (i_line > 1) { + D(bug("More than one FILE token\n")); + goto fail; + } + filename = strtok(NULL, "\"\t\n\r"); + filetype = strtok(NULL, " \"\t\n\r"); + if (strcmp("BINARY", filetype)) { + D(bug("Not binary file %s", filetype)); + goto fail; + } + else { + char *tmp = strdup(cuefile); + char *b = dirname(tmp); + cs->binfile = (char *) malloc(strlen(b) + strlen(filename) + 2); + sprintf(cs->binfile, "%s/%s", b, filename); + free(tmp); + } + } else if (!strcmp("TRACK", keyword)) { + char *field; + int i_track; + + if (seen1st) { + if (!AddTrack(cs)){ + D(bug("AddTrack failed \n")); + goto fail; + } + curr = &cs->tracks[cs->tcnt]; + } + + seen1st = true; + + // parse track number + + field = strtok(NULL, " \t\n\r"); + if (1 != sscanf(field, "%d", &i_track)) { + D(bug("Expected track number\n")); + goto fail; + } + curr->number = i_track; + + // parse track type + + field = strtok(NULL, " \t\n\r"); + if (!strcmp("MODE1/2352", field)) { + curr->tcf = DATA; + } else if (!strcmp("AUDIO", field)) { + curr->tcf = AUDIO; + } else { + D(bug("Unexpected track type %s", field)); + goto fail; + } + + } else if (!strcmp("INDEX", keyword)) { + char *field; + int i_index; + MSF msf; + + // parse INDEX number + + field = strtok(NULL, " \t\n\r"); + if (1 != sscanf(field, "%d", &i_index)) { + D(bug("Expected index number")); + goto fail; + } + + // parse INDEX start + + field = strtok(NULL, " \t\n\r"); + if (3 != sscanf(field, "%d:%d:%d", + &msf.m, &msf.s, &msf.f)) { + D(bug("Expected index start frame\n")); + goto fail; + } + + if (i_index == 1) + curr->start = MSFToFrames(msf); + else if (i_index == 0) + prestart = MSFToFrames(msf); + } else if (!strcmp("PREGAP", keyword)) { + MSF msf; + char *field = strtok(NULL, " \t\n\r"); + if (3 != sscanf(field, "%d:%d:%d", + &msf.m, &msf.s, &msf.f)) { + D(bug("Expected pregap frame\n")); + goto fail; + } + curr->pregap = MSFToFrames(msf); + + // Ignored directives + + } else if (!strcmp("TITLE", keyword)) { + } else if (!strcmp("PERFORMER", keyword)) { + } else if (!strcmp("REM", keyword)) { + } else if (!strcmp("ISRC", keyword)) { + } else if (!strcmp("SONGWRITER", keyword)) { + } else { + D(bug("Unexpected keyword %s\n", keyword)); + goto fail; + } + } + } + + AddTrack(cs); // add final track + return true; + fail: + return false; +} + +static bool LoadCueSheet(const char *cuefile, CueSheet *cs) +{ + FILE *fh = NULL; + int binfh = -1; + struct stat buf; + Track *tlast = NULL; + + if (cs) { + bzero(cs, sizeof(*cs)); + if (!(fh = fopen(cuefile, "r"))) + return false; + + if (!ParseCueSheet(fh, cs, cuefile)) goto fail; + + // Open bin file and find length + + if ((binfh = open(cs->binfile,O_RDONLY)) < 0) { + D(bug("Can't read bin file %s\n", cs->binfile)); + goto fail; + } + + if (fstat(binfh, &buf)) { + D(bug("fstat returned error\n")); + goto fail; + } + + // compute length of final track + + + tlast = &cs->tracks[cs->tcnt - 1]; + tlast->length = buf.st_size/RAW_SECTOR_SIZE + - tlast->start + totalPregap; + + if (tlast->length < 0) { + D(bug("Binary file too short \n")); + goto fail; + } + + // save bin file length and pointer + + cs->length = buf.st_size/RAW_SECTOR_SIZE; + cs->binfh = binfh; + + fclose(fh); + return true; + + fail: + if (binfh >= 0) + close(binfh); + fclose(fh); + free(cs->binfile); + return false; + + } + return false; +} + + + +void *open_bincue(const char *name) +{ + CueSheet *cs; + + if (player.cs == NULL) { + cs = (CueSheet *) malloc(sizeof(CueSheet)); + if (!cs) { + D(bug("malloc failed\n")); + return NULL; + } + + if (LoadCueSheet(name, cs)) { + player.cs = cs; +#ifdef OSX_CORE_AUDIO + audio_enabled = true; +#endif + if (audio_enabled) + player.audiostatus = CDROM_AUDIO_NO_STATUS; + else + player.audiostatus = CDROM_AUDIO_INVALID; + player.audiofh = dup(cs->binfh); + return cs; + } + else + free(cs); + } + return NULL; +} + +void close_bincue(void *fh) +{ + + +} + +/* + * File read (cooked) + * Data are stored in raw sectors of which only COOKED_SECTOR_SIZE + * bytes are valid -- the remaining include 16 bytes at the beginning + * of each raw sector and RAW_SECTOR_SIZE - COOKED_SECTOR_SIZE - bytes + * at the end + * + * We assume that a read request can land in the middle of + * sector. We compute the byte address of that sector (sec) + * and the offset of the first byte we want within that sector (secoff) + * + * Reading is performed one raw sector at a time, extracting as many + * valid bytes as possible from that raw sector (available) + */ + +size_t read_bincue(void *fh, void *b, loff_t offset, size_t len) +{ + size_t bytes_read = 0; // bytes read so far + unsigned char *buf = (unsigned char *) b; // target buffer + unsigned char secbuf[RAW_SECTOR_SIZE]; // temporary buffer + + off_t sec = ((offset/COOKED_SECTOR_SIZE) * RAW_SECTOR_SIZE); + off_t secoff = offset % COOKED_SECTOR_SIZE; + + // sec contains location (in bytes) of next raw sector to read + // secoff contains offset within that sector at which to start + // reading since we can request a read that starts in the middle + // of a sector + + CueSheet *cs = (CueSheet *) fh; + + if (cs == NULL || lseek(cs->binfh, sec, SEEK_SET) < 0) { + return -1; + } + while (len) { + + // bytes available in next raw sector or len (bytes) + // we want whichever is less + + size_t available = COOKED_SECTOR_SIZE - secoff; + available = (available > len) ? len : available; + + // read the next raw sector + + if (read(cs->binfh, secbuf, RAW_SECTOR_SIZE) != RAW_SECTOR_SIZE) { + return bytes_read; + } + + // copy cooked sector bytes (skip first 16) + // we want out of those available + + bcopy(&secbuf[16+secoff], &buf[bytes_read], available); + + // next sector we start at the beginning + + secoff = 0; + + // increment running count decrement request + + bytes_read += available; + len -= available; + } + return bytes_read; +} + +loff_t size_bincue(void *fh) +{ + if (fh) { + return ((CueSheet *)fh)->length * COOKED_SECTOR_SIZE; + } +} + +bool readtoc_bincue(void *fh, unsigned char *toc) +{ + CueSheet *cs = (CueSheet *) fh; + if (cs) { + + MSF msf; + unsigned char *p = toc + 2; + *p++ = cs->tracks[0].number; + *p++ = cs->tracks[cs->tcnt - 1].number; + for (int i = 0; i < cs->tcnt; i++) { + + FramesToMSF(cs->tracks[i].start, &msf); + *p++ = 0; + *p++ = 0x10 | cs->tracks[i].tcf; + *p++ = cs->tracks[i].number; + *p++ = 0; + *p++ = 0; + *p++ = msf.m; + *p++ = msf.s; + *p++ = msf.f; + } + FramesToMSF(cs->length, &msf); + *p++ = 0; + *p++ = 0x14; + *p++ = 0xAA; + *p++ = 0; + *p++ = 0; + *p++ = msf.m; + *p++ = msf.s; + *p++ = msf.f; + + int toc_size = p - toc; + *toc++ = toc_size >> 8; + *toc++ = toc_size & 0xff; + return true; + } +} + +bool GetPosition_bincue(void *fh, uint8 *pos) +{ + CueSheet *cs = (CueSheet *) fh; + if (cs && player.cs == cs) { + MSF abs, rel; + int fpos = player.audioposition / RAW_SECTOR_SIZE + player.audiostart; + int trackno = PositionToTrack(cs, fpos); + + if (!audio_enabled) + return false; + + FramesToMSF(fpos, &abs); + if (trackno < cs->tcnt) { + // compute position relative to start of frame + + unsigned int position = player.audioposition/RAW_SECTOR_SIZE + + player.audiostart - player.cs->tracks[trackno].start; + + FramesToMSF(position, &rel); + } + else + FramesToMSF(0, &rel); + + *pos++ = 0; + *pos++ = player.audiostatus; + *pos++ = 0; + *pos++ = 12; // Sub-Q data length + *pos++ = 0; + if (trackno < cs->tcnt) + *pos++ = 0x10 | cs->tracks[trackno].tcf; + *pos++ = (trackno < cs->tcnt) ? cs->tracks[trackno].number : 0xAA; + *pos++ = 1; // track index + *pos++ = 0; + *pos++ = abs.m; + *pos++ = abs.s; + *pos++ = abs.f; + *pos++ = 0; + *pos++ = rel.m; + *pos++ = rel.s; + *pos++ = rel.f; + *pos++ = 0; +// D(bug("CDROM position %02d:%02d:%02d track %02d\n", abs.m, abs.s, abs.f, trackno)); + return true; + } + else + return false; +} + +bool CDPause_bincue(void *fh) +{ + CueSheet *cs = (CueSheet *) fh; + if (cs && cs == player.cs) { + if (player.audiostatus == CDROM_AUDIO_PLAY) { + player.audiostatus = CDROM_AUDIO_PAUSED; + return true; + } + } + return false; +} + +bool CDStop_bincue(void *fh) +{ + CueSheet *cs = (CueSheet *) fh; + + if (cs && cs == player.cs) { +#ifdef OSX_CORE_AUDIO + player.soundoutput.stop(); +#endif + if (player.audiostatus != CDROM_AUDIO_INVALID) + player.audiostatus = CDROM_AUDIO_NO_STATUS; + return true; + } + return false; +} + +bool CDResume_bincue(void *fh) +{ + CueSheet *cs = (CueSheet *) fh; + if (cs && cs == player.cs) { + if (player.audiostatus == CDROM_AUDIO_PAUSED) { + player.audiostatus = CDROM_AUDIO_PLAY; + return true; + } + } + return false; +} + +bool CDPlay_bincue(void *fh, uint8 start_m, uint8 start_s, uint8 start_f, + uint8 end_m, uint8 end_s, uint8 end_f) +{ + CueSheet *cs = (CueSheet *)fh; + if (cs && cs == player.cs) { + int track; + MSF msf; + +#ifdef USE_SDL_AUDIO + SDL_LockAudio(); +#endif + + player.audiostatus = CDROM_AUDIO_NO_STATUS; + + player.audiostart = (start_m * 60 * CD_FRAMES) + + (start_s * CD_FRAMES) + start_f; + player.audioend = (end_m * 60 * CD_FRAMES) + (end_s * CD_FRAMES) + end_f; + + track = PositionToTrack(player.cs, player.audiostart); + + if (track < player.cs->tcnt) { + player.audioposition = 0; + + // here we need to compute silence + + if (player.audiostart - player.cs->tracks[track].start > + player.cs->tracks[track].pregap) + player.silence = 0; + else + player.silence = (player.cs->tracks[track].pregap - + player.audiostart + + player.cs->tracks[track].start) * RAW_SECTOR_SIZE; + + player.fileoffset = player.cs->tracks[track].fileoffset; + + D(bug("file offset %d\n", (unsigned int) player.fileoffset)); + + // fix up file offset if beyond the silence bytes + + if (!player.silence) // not at the beginning + player.fileoffset += (player.audiostart - + player.cs->tracks[track].start - + player.cs->tracks[track].pregap) * RAW_SECTOR_SIZE; + + FramesToMSF(player.cs->tracks[track].start, &msf); + D(bug("CDPlay_bincue track %02d start %02d:%02d:%02d silence %d", + player.cs->tracks[track].number, msf.m, msf.s, msf.f, + player.silence/RAW_SECTOR_SIZE)); + D(bug(" Stop %02u:%02u:%02u\n", end_m, end_s, end_f)); + } + else + D(bug("CDPlay_bincue: play beyond last track !\n")); + +#ifdef USE_SDL_AUDIO + SDL_UnlockAudio(); +#endif + + if (audio_enabled) { + player.audiostatus = CDROM_AUDIO_PLAY; +#ifdef OSX_CORE_AUDIO + D(bug("starting os x sound")); + player.soundoutput.setCallback(bincue_core_audio_callback); + // should be from current track ! + player.soundoutput.start(16, 2, 44100); +#endif + return true; + } + } + return false; +} + +static uint8 *fill_buffer(int stream_len) +{ + static uint8 *buf = 0; + static int bufsize = 0; + int offset = 0; + + if (bufsize < stream_len) { + free(buf); + buf = (uint8 *) malloc(stream_len); + if (buf) { + bufsize = stream_len; + } + else { + D(bug("malloc failed \n")); + return NULL; + } + } + + memset(buf, silence_byte, stream_len); + + if (player.audiostatus == CDROM_AUDIO_PLAY) { + int remaining_silence = player.silence - player.audioposition; + + if (player.audiostart + player.audioposition/RAW_SECTOR_SIZE + >= player.audioend) { + player.audiostatus = CDROM_AUDIO_COMPLETED; + return buf; + } + + if (remaining_silence >= stream_len) { + player.audioposition += stream_len; + return buf; + } + + if (remaining_silence > 0) { + offset += remaining_silence; + player.audioposition += remaining_silence; + } + + int ret = 0; + int available = ((player.audioend - player.audiostart) * + RAW_SECTOR_SIZE) - player.audioposition; + if (available > (stream_len - offset)) + available = stream_len - offset; + + if (lseek(player.audiofh, + player.fileoffset + player.audioposition - player.silence, + SEEK_SET) < 0) + return NULL; + + if (available < 0) { + player.audioposition += available; // correct end !; + available = 0; + } + + if ((ret = read(player.audiofh, &buf[offset], available)) >= 0) { + player.audioposition += ret; + offset += ret; + available -= ret; + } + + while (offset < stream_len) { + buf[offset++] = silence_byte; + if (available-- > 0){ + player.audioposition++; + } + } + } + return buf; +} + + +#ifdef USE_SDL_AUDIO +void MixAudio_bincue(uint8 *stream, int stream_len) +{ + uint8 *buf; + if (audio_enabled && (player.audiostatus == CDROM_AUDIO_PLAY)) { + if (buf = fill_buffer(stream_len)) + SDL_MixAudio(stream, buf, stream_len, SDL_MIX_MAXVOLUME); + } +} + +void OpenAudio_bincue(int freq, int format, int channels, uint8 silence) +{ + if (freq == 44100 && format == AUDIO_S16MSB && channels == 2) { + audio_enabled = true; + silence_byte = silence; + } + else { + D(bug("unexpected frequency %d , format %d, or channels %d\n", + freq, format, channels)); + } +} +#endif + +#ifdef OSX_CORE_AUDIO +static int bincue_core_audio_callback(void) +{ + int frames = player.soundoutput.bufferSizeFrames(); + uint8 *buf = fill_buffer(frames*4); + + // D(bug("Audio request %d\n", stream_len)); + + player.soundoutput.sendAudioBuffer((void *) buf, (buf ? frames : 0)); + + return 1; +} +#endif diff --git a/BasiliskII/src/Unix/bincue_unix.h b/BasiliskII/src/Unix/bincue_unix.h new file mode 100644 index 00000000..dbf5d8b5 --- /dev/null +++ b/BasiliskII/src/Unix/bincue_unix.h @@ -0,0 +1,43 @@ +/* + * bincue_unix.h -- support for cdrom image files in bin/cue format + * + * (C) 2010 Geoffrey Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef BINCUE_H +#define BINCUE_H + +extern void *open_bincue(const char *name); +extern bool readtoc_bincue(void *, uint8 *); +extern size_t read_bincue(void *, void *, loff_t, size_t); +extern loff_t size_bincue(void *); +extern void close_bincue(void *); + +extern bool GetPosition_bincue(void *, uint8 *); + +extern bool CDPlay_bincue(void *, uint8, uint8, + uint8, uint8, uint8, uint8); +extern bool CDPause_bincue(void *); +extern bool CDResume_bincue(void *); +extern bool CDStop_bincue(void *); + +#ifdef USE_SDL_AUDIO +extern void OpenAudio_bincue(int, int, int, uint8); +extern void MixAudio_bincue(uint8 *, int); +#endif + +#endif diff --git a/BasiliskII/src/Unix/configure.ac b/BasiliskII/src/Unix/configure.ac index e831792c..63219dc6 100644 --- a/BasiliskII/src/Unix/configure.ac +++ b/BasiliskII/src/Unix/configure.ac @@ -74,6 +74,9 @@ AC_ARG_WITH(gtk, [ --with-gtk use GTK user interface [ [WANT_GTK="gtk2 gtk"]) AC_ARG_WITH(mon, [ --with-mon use mon as debugger [default=yes]], [WANT_MON=$withval], [WANT_MON=yes]) +AC_ARG_WITH(bincue, + AS_HELP_STRING([--with-bincue], [Allow cdrom image files in bin/cue mode])) + dnl Canonical system information. AC_CANONICAL_HOST AC_CANONICAL_TARGET @@ -248,6 +251,17 @@ if [[ "x$WANT_SDL" = "xno" ]]; then LIBS="$LIBS $X_PRE_LIBS $X_LIBS -lX11 -lXext $X_EXTRA_LIBS" fi +dnl BINCUE +AS_IF([test "x$with_bincue" = "xyes" ], [have_bincue=yes], [have_bincue=no]) +AS_IF([test "x$have_bincue" = "xyes" ], [ + if [[ "x$WANT_SDL_AUDIO" = "xyes" ]]; then + DEFINES="$DEFINES -DBINCUE" + else + AC_MSG_ERROR([You need SDL Audio to use BINCUE support.]) + fi +]) + + dnl We want pthreads. Try libpthread first, then libc_r (FreeBSD), then PTL. HAVE_PTHREADS=yes AC_CHECK_LIB(pthread, pthread_create, , [ @@ -697,6 +711,12 @@ if [[ "x$WANT_SDL_AUDIO" = "xyes" ]]; then AUDIOSRC="../SDL/audio_sdl.cpp" fi +dnl BINCUE overrides + +if [[ "x$have_bincue" = "xyes" ]]; then + EXTRASYSSRCS="$EXTRASYSSRCS bincue_unix.cpp" +fi + dnl Use 68k CPU natively? WANT_NATIVE_M68K=no if [[ "x$HAVE_M68K" = "xyes" -a "x$CAN_NATIVE_M68K" = "xyes" ]]; then @@ -1633,6 +1653,7 @@ echo echo Basilisk II configuration summary: echo echo SDL support ............................ : $SDL_SUPPORT +echo BINCUE support ......................... : $have_bincue echo XFree86 DGA support .................... : $WANT_XF86_DGA echo XFree86 VidMode support ................ : $WANT_XF86_VIDMODE echo fbdev DGA support ...................... : $WANT_FBDEV_DGA diff --git a/BasiliskII/src/Unix/sys_unix.cpp b/BasiliskII/src/Unix/sys_unix.cpp index 11dbfbd8..8ac4bc0a 100644 --- a/BasiliskII/src/Unix/sys_unix.cpp +++ b/BasiliskII/src/Unix/sys_unix.cpp @@ -57,19 +57,25 @@ #include "user_strings.h" #include "sys.h" +#if defined(BINCUE) +#include "bincue_unix.h" +#endif + #define DEBUG 0 #include "debug.h" - // File handles are pointers to these structures struct file_handle { - char *name; // Copy of device/file name + char *name; // Copy of device/file name int fd; bool is_file; // Flag: plain file or /dev/something? bool is_floppy; // Flag: floppy device bool is_cdrom; // Flag: CD-ROM device +#if defined(BINCUE) + bool is_bincue; // Flag: BIN CUE file +#endif bool read_only; // Copy of Sys_open() flag - loff_t start_byte; // Size of file header (if any) + loff_t start_byte; // Size of file header (if any) loff_t file_size; // Size of file data (only valid if is_file is true) bool is_media_present; // Flag: media is inserted and available @@ -78,8 +84,12 @@ struct file_handle { #elif defined(__FreeBSD__) struct ioc_capability cdrom_cap; #elif defined(__APPLE__) && defined(__MACH__) - char *ioctl_name; // For CDs on OS X - a device for special ioctls - int ioctl_fd; + char *ioctl_name; // For CDs on OS X - a device for special ioctls + int ioctl_fd; +#endif + +#if defined(BINCUE) + void *bincue_fd; #endif }; @@ -438,6 +448,7 @@ bool cdrom_open(file_handle *fh, const char *path) void cdrom_close(file_handle *fh) { + if (fh->fd >= 0) { close(fh->fd); fh->fd = -1; @@ -549,6 +560,31 @@ void *Sys_open(const char *name, bool read_only) } // Open file/device + +#if defined(BINCUE) + + void *binfd = open_bincue(name); + if (binfd) { + file_handle *fh = new file_handle; + fh->fd = 0; + fh->is_file = false; + fh->name = strdup(name); + fh->bincue_fd = binfd; + fh->is_bincue = true; + fh->read_only = true; + fh->start_byte = 0; + fh->is_floppy = false; + fh->is_cdrom = false; + fh->is_media_present = true; +#if defined __MACOSX__ + fh->ioctl_fd = -1; + fh->ioctl_name = NULL; +#endif + sys_add_file_handle(fh); + return fh; + } +#endif + #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACOSX__) int fd = open(name, (read_only ? O_RDONLY : O_RDWR) | (is_cdrom ? O_NONBLOCK : 0)); #else @@ -569,9 +605,16 @@ void *Sys_open(const char *name, bool read_only) fh->is_floppy = is_floppy; fh->is_cdrom = is_cdrom; fh->is_media_present = false; +#if defined __linux__ + fh->cdrom_cap = 0; +#endif #if defined __MACOSX__ fh->ioctl_fd = -1; fh->ioctl_name = NULL; +#endif +#if defined(BINCUE) + fh->is_bincue = false; + fh->bincue_fd = NULL; #endif if (fh->is_file) { fh->is_media_present = true; @@ -646,6 +689,13 @@ void Sys_close(void *arg) sys_remove_file_handle(fh); +#if defined(BINCUE) + if (fh->is_bincue) { + close_bincue(fh->bincue_fd); + fh->bincue_fd = NULL; + } +#endif + if (fh->is_cdrom) cdrom_close(fh); if (fh->fd >= 0) @@ -667,6 +717,12 @@ size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length) if (!fh) return 0; +#if defined(BINCUE) + if (fh->is_bincue) { + return read_bincue(fh->bincue_fd, buffer, offset, length); + } +#endif + // Seek to position if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0) return 0; @@ -709,8 +765,14 @@ loff_t SysGetFileSize(void *arg) if (fh->is_file) return fh->file_size; else { -#if defined(__linux__) long blocks; +#if defined(BINCUE) + if (fh->is_bincue) { + return size_bincue(fh->bincue_fd); + } +#endif +#if defined(__linux__) + if (ioctl(fh->fd, BLKGETSIZE, &blocks) < 0) return 0; D(bug(" BLKGETSIZE returns %d blocks\n", blocks)); @@ -951,7 +1013,14 @@ bool SysCDReadTOC(void *arg, uint8 *toc) if (!fh) return false; +#if defined(BINCUE) + if (fh->is_bincue){ + return readtoc_bincue(fh->bincue_fd, toc); + } +#endif + if (fh->is_cdrom) { + #if defined(__linux__) uint8 *p = toc + 2; @@ -1092,6 +1161,13 @@ bool SysCDGetPosition(void *arg, uint8 *pos) if (!fh) return false; +#if defined(BINCUE) + if (fh->is_bincue) { + + return GetPosition_bincue(fh->bincue_fd, pos); + } +#endif + if (fh->is_cdrom) { #if defined(__linux__) cdrom_subchnl chan; @@ -1157,6 +1233,13 @@ bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end if (!fh) return false; +#if defined(BINCUE) + if (fh->is_bincue) { + return CDPlay_bincue(fh->bincue_fd, start_m, start_s, + start_f, end_m, end_s, end_f); + } +#endif + if (fh->is_cdrom) { #if defined(__linux__) cdrom_msf play; @@ -1194,6 +1277,12 @@ bool SysCDPause(void *arg) if (!fh) return false; +#if defined(BINCUE) + if (fh->is_bincue){ + return CDPause_bincue(fh->bincue_fd); + } +#endif + if (fh->is_cdrom) { #if defined(__linux__) return ioctl(fh->fd, CDROMPAUSE) == 0; @@ -1217,6 +1306,13 @@ bool SysCDResume(void *arg) if (!fh) return false; +#if defined(BINCUE) + if (fh->is_bincue) { + return CDResume_bincue(fh->bincue_fd); + } +#endif + + if (fh->is_cdrom) { #if defined(__linux__) return ioctl(fh->fd, CDROMRESUME) == 0; @@ -1240,6 +1336,13 @@ bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f) if (!fh) return false; +#if defined(BINCUE) + if (fh->is_bincue) { + return CDStop_bincue(fh->bincue_fd); + } +#endif + + if (fh->is_cdrom) { #if defined(__linux__) return ioctl(fh->fd, CDROMSTOP) == 0;