view src/cdrom/macos/SDL_syscdrom.c @ 1662:782fd950bd46 SDL-1.3

Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API. WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid. The code is now run through a consistent indent format: indent -i4 -nut -nsc -br -ce The headers are being converted to automatically generate doxygen documentation.
author Sam Lantinga <slouken@libsdl.org>
date Sun, 28 May 2006 13:04:16 +0000
parents 92947e3a18db
children 4da1ee79c9af
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

#ifdef SDL_CDROM_MACOS

/* MacOS functions for system-level CD-ROM audio control */

#include <Devices.h>
#include <Files.h>
#include <LowMem.h>             /* Use entry table macros, not functions in InterfaceLib  */

#include "SDL_cdrom.h"
#include "../SDL_syscdrom.h"
#include "SDL_syscdrom_c.h"

/* Added by Matt Slot */
#if !defined(LMGetUnitTableEntryCount)
#define LMGetUnitTableEntryCount()   *(short *)0x01D2
#endif

/* The maximum number of CD-ROM drives we'll detect */
#define MAX_DRIVES	26

/* A list of available CD-ROM drives */
static long SDL_cdversion = 0;
static struct
{
    short dRefNum;
    short driveNum;
    long frames;
    char name[256];
    Boolean hasAudio;
} SDL_cdlist[MAX_DRIVES];
static StringPtr gDriverName = "\p.AppleCD";

/* The system-dependent CD control functions */
static const char *SDL_SYS_CDName (int drive);
static int SDL_SYS_CDOpen (int drive);
static int SDL_SYS_CDGetTOC (SDL_CD * cdrom);
static CDstatus SDL_SYS_CDStatus (SDL_CD * cdrom, int *position);
static int SDL_SYS_CDPlay (SDL_CD * cdrom, int start, int length);
static int SDL_SYS_CDPause (SDL_CD * cdrom);
static int SDL_SYS_CDResume (SDL_CD * cdrom);
static int SDL_SYS_CDStop (SDL_CD * cdrom);
static int SDL_SYS_CDEject (SDL_CD * cdrom);
static void SDL_SYS_CDClose (SDL_CD * cdrom);

static short
SDL_SYS_ShortToBCD (short value)
{
    return ((value % 10) + (value / 10) * 0x10);        /* Convert value to BCD */
}

static short
SDL_SYS_BCDToShort (short value)
{
    return ((value % 0x10) + (value / 0x10) * 10);      /* Convert value from BCD */
}

int
SDL_SYS_CDInit (void)
{
    SInt16 dRefNum = 0;
    SInt16 first, last;

    SDL_numcds = 0;

    /* Check that the software is available */
    if (Gestalt (kGestaltAudioCDSelector, &SDL_cdversion) || !SDL_cdversion)
        return (0);

    /* Fill in our driver capabilities */
    SDL_CDcaps.Name = SDL_SYS_CDName;
    SDL_CDcaps.Open = SDL_SYS_CDOpen;
    SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
    SDL_CDcaps.Status = SDL_SYS_CDStatus;
    SDL_CDcaps.Play = SDL_SYS_CDPlay;
    SDL_CDcaps.Pause = SDL_SYS_CDPause;
    SDL_CDcaps.Resume = SDL_SYS_CDResume;
    SDL_CDcaps.Stop = SDL_SYS_CDStop;
    SDL_CDcaps.Eject = SDL_SYS_CDEject;
    SDL_CDcaps.Close = SDL_SYS_CDClose;

    /* Walk the list, count each AudioCD driver, and save the refnums */
    first = -1;
    last = 0 - LMGetUnitTableEntryCount ();
    for (dRefNum = first; dRefNum >= last; dRefNum--) {
        Str255 driverName;
        StringPtr namePtr;
        DCtlHandle deviceEntry;

        deviceEntry = GetDCtlEntry (dRefNum);
        if (!deviceEntry)
            continue;

        /* Is this an .AppleCD ? */
        namePtr = (*deviceEntry)->dCtlFlags & (1L << dRAMBased) ?
            ((StringPtr) ((DCtlPtr) deviceEntry)->dCtlDriver + 18) :
            ((StringPtr) (*deviceEntry)->dCtlDriver + 18);
        BlockMoveData (namePtr, driverName, namePtr[0] + 1);
        if (driverName[0] > gDriverName[0])
            driverName[0] = gDriverName[0];
        if (!EqualString (driverName, gDriverName, false, false))
            continue;

        /* Record the basic info for each drive */
        SDL_cdlist[SDL_numcds].dRefNum = dRefNum;
        BlockMoveData (namePtr + 1, SDL_cdlist[SDL_numcds].name, namePtr[0]);
        SDL_cdlist[SDL_numcds].name[namePtr[0]] = 0;
        SDL_cdlist[SDL_numcds].hasAudio = false;
        SDL_numcds++;
    }
    return (0);
}

static const char *
SDL_SYS_CDName (int drive)
{
    return (SDL_cdlist[drive].name);
}

static int
get_drivenum (int drive)
{
    QHdr *driveQ = GetDrvQHdr ();
    DrvQEl *driveElem;

    /* Update the drive number */
    SDL_cdlist[drive].driveNum = 0;
    if (driveQ->qTail) {
        driveQ->qTail->qLink = 0;
    }
    for (driveElem = (DrvQEl *) driveQ->qHead; driveElem;
         driveElem = (DrvQEl *) driveElem->qLink) {
        if (driveElem->dQRefNum == SDL_cdlist[drive].dRefNum) {
            SDL_cdlist[drive].driveNum = driveElem->dQDrive;
            break;
        }
    }
    return (SDL_cdlist[drive].driveNum);
}

static int
SDL_SYS_CDOpen (int drive)
{
    return (drive);
}

static int
SDL_SYS_CDGetTOC (SDL_CD * cdrom)
{
    CDCntrlParam cdpb;
    CDTrackData tracks[SDL_MAX_TRACKS];
    long i, leadout;

    /* Get the number of tracks on the CD by examining the TOC */
    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kReadTOC;
    cdpb.csParam.words[0] = kGetTrackRange;
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }

    cdrom->numtracks =
        SDL_SYS_BCDToShort (cdpb.csParam.bytes[1]) -
        SDL_SYS_BCDToShort (cdpb.csParam.bytes[0]) + 1;
    if (cdrom->numtracks > SDL_MAX_TRACKS)
        cdrom->numtracks = SDL_MAX_TRACKS;
    cdrom->status = CD_STOPPED;
    cdrom->cur_track = 0;       /* Apparently these are set elsewhere */
    cdrom->cur_frame = 0;       /* Apparently these are set elsewhere */


    /* Get the lead out area of the CD by examining the TOC */
    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kReadTOC;
    cdpb.csParam.words[0] = kGetLeadOutArea;
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }

    leadout = MSF_TO_FRAMES (SDL_SYS_BCDToShort (cdpb.csParam.bytes[0]),
                             SDL_SYS_BCDToShort (cdpb.csParam.bytes[1]),
                             SDL_SYS_BCDToShort (cdpb.csParam.bytes[2]));

    /* Get an array of track locations by examining the TOC */
    SDL_memset (tracks, 0, sizeof (tracks));
    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kReadTOC;
    cdpb.csParam.words[0] = kGetTrackEntries;   /* Type of Query */
    *((long *) (cdpb.csParam.words + 1)) = (long) tracks;
    cdpb.csParam.words[3] = cdrom->numtracks * sizeof (tracks[0]);
    *((char *) (cdpb.csParam.words + 4)) = 1;   /* First track */
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }

    /* Read all the track TOC entries */
    SDL_cdlist[cdrom->id].hasAudio = false;
    for (i = 0; i < cdrom->numtracks; ++i) {
        cdrom->track[i].id = i + 1;
        if (tracks[i].entry.control & kDataTrackMask)
            cdrom->track[i].type = SDL_DATA_TRACK;
        else {
            cdrom->track[i].type = SDL_AUDIO_TRACK;
            SDL_cdlist[SDL_numcds].hasAudio = true;
        }

        cdrom->track[i].offset =
            MSF_TO_FRAMES (SDL_SYS_BCDToShort (tracks[i].entry.min),
                           SDL_SYS_BCDToShort (tracks[i].entry.min),
                           SDL_SYS_BCDToShort (tracks[i].entry.frame));
        cdrom->track[i].length =
            MSF_TO_FRAMES (SDL_SYS_BCDToShort (tracks[i + 1].entry.min),
                           SDL_SYS_BCDToShort (tracks[i + 1].entry.min),
                           SDL_SYS_BCDToShort (tracks[i + 1].entry.frame)) -
            cdrom->track[i].offset;
    }

    /* Apparently SDL wants a fake last entry */
    cdrom->track[i].offset = leadout;
    cdrom->track[i].length = 0;

    return (0);
}

/* Get CD-ROM status */
static CDstatus
SDL_SYS_CDStatus (SDL_CD * cdrom, int *position)
{
    CDCntrlParam cdpb;
    CDstatus status = CD_ERROR;
    Boolean spinning = false;

    if (position)
        *position = 0;

    /* Get the number of tracks on the CD by examining the TOC */
    if (!get_drivenum (cdrom->id)) {
        return (CD_TRAYEMPTY);
    }
    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kReadTOC;
    cdpb.csParam.words[0] = kGetTrackRange;
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (CD_ERROR);
    }

    cdrom->numtracks =
        SDL_SYS_BCDToShort (cdpb.csParam.bytes[1]) -
        SDL_SYS_BCDToShort (cdpb.csParam.bytes[0]) + 1;
    if (cdrom->numtracks > SDL_MAX_TRACKS)
        cdrom->numtracks = SDL_MAX_TRACKS;
    cdrom->cur_track = 0;       /* Apparently these are set elsewhere */
    cdrom->cur_frame = 0;       /* Apparently these are set elsewhere */


    if (1 || SDL_cdlist[cdrom->id].hasAudio) {
        /* Get the current playback status */
        SDL_memset (&cdpb, 0, sizeof (cdpb));
        cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
        cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
        cdpb.csCode = kAudioStatus;
        if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
            SDL_SetError ("PBControlSync() failed");
            return (-1);
        }

        switch (cdpb.csParam.cd.status) {
        case kStatusPlaying:
            status = CD_PLAYING;
            spinning = true;
            break;
        case kStatusPaused:
            status = CD_PAUSED;
            spinning = true;
            break;
        case kStatusMuted:
            status = CD_PLAYING;        /* What should I do here? */
            spinning = true;
            break;
        case kStatusDone:
            status = CD_STOPPED;
            spinning = true;
            break;
        case kStatusStopped:
            status = CD_STOPPED;
            spinning = false;
            break;
        case kStatusError:
        default:
            status = CD_ERROR;
            spinning = false;
            break;
        }

        if (spinning && position)
            *position =
                MSF_TO_FRAMES (SDL_SYS_BCDToShort (cdpb.csParam.cd.minute),
                               SDL_SYS_BCDToShort (cdpb.csParam.cd.second),
                               SDL_SYS_BCDToShort (cdpb.csParam.cd.frame));
    } else
        status = CD_ERROR;      /* What should I do here? */

    return (status);
}

/* Start play */
static int
SDL_SYS_CDPlay (SDL_CD * cdrom, int start, int length)
{
    CDCntrlParam cdpb;

    /* Pause the current audio playback to avoid audible artifacts */
    if (SDL_SYS_CDPause (cdrom) < 0) {
        return (-1);
    }

    /* Specify the AudioCD playback mode */
    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kSetPlayMode;
    cdpb.csParam.bytes[0] = false;      /* Repeat? */
    cdpb.csParam.bytes[1] = kPlayModeSequential;        /* Play mode */
    /* ¥¥¥ÊTreat as soft error, NEC Drive doesnt support this call ¥¥¥ */
    PBControlSync ((ParmBlkPtr) & cdpb);

#if 1
    /* Specify the end of audio playback */
    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kAudioStop;
    cdpb.csParam.words[0] = kBlockPosition;     /* Position Mode */
    *(long *) (cdpb.csParam.words + 1) = start + length - 1;    /* Search Address */
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }

    /* Specify the start of audio playback, and start it */
    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kAudioPlay;
    cdpb.csParam.words[0] = kBlockPosition;     /* Position Mode */
    *(long *) (cdpb.csParam.words + 1) = start + 1;     /* Search Address */
    cdpb.csParam.words[3] = false;      /* Stop address? */
    cdpb.csParam.words[4] = kStereoPlayMode;    /* Audio Play Mode */
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }
#else
    /* Specify the end of audio playback */
    FRAMES_TO_MSF (start + length, &m, &s, &f);
    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kAudioStop;
    cdpb.csParam.words[0] = kTrackPosition;     /* Position Mode */
    cdpb.csParam.words[1] = 0;  /* Search Address (hiword) */
    cdpb.csParam.words[2] =     /* Search Address (loword) */
        SDL_SYS_ShortToBCD (cdrom->numtracks);
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }

    /* Specify the start of audio playback, and start it */
    FRAMES_TO_MSF (start, &m, &s, &f);
    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kAudioPlay;
    cdpb.csParam.words[0] = kTrackPosition;     /* Position Mode */
    cdpb.csParam.words[1] = 0;  /* Search Address (hiword) */
    cdpb.csParam.words[2] = SDL_SYS_ShortToBCD (1);     /* Search Address (loword) */
    cdpb.csParam.words[3] = false;      /* Stop address? */
    cdpb.csParam.words[4] = kStereoPlayMode;    /* Audio Play Mode */
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }
#endif

    return (0);
}

/* Pause play */
static int
SDL_SYS_CDPause (SDL_CD * cdrom)
{
    CDCntrlParam cdpb;

    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kAudioPause;
    cdpb.csParam.words[0] = 0;  /* Pause/Continue Flag (hiword) */
    cdpb.csParam.words[1] = 1;  /* Pause/Continue Flag (loword) */
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }
    return (0);
}

/* Resume play */
static int
SDL_SYS_CDResume (SDL_CD * cdrom)
{
    CDCntrlParam cdpb;

    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kAudioPause;
    cdpb.csParam.words[0] = 0;  /* Pause/Continue Flag (hiword) */
    cdpb.csParam.words[1] = 0;  /* Pause/Continue Flag (loword) */
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }
    return (0);
}

/* Stop play */
static int
SDL_SYS_CDStop (SDL_CD * cdrom)
{
    CDCntrlParam cdpb;

    SDL_memset (&cdpb, 0, sizeof (cdpb));
    cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    cdpb.csCode = kAudioStop;
    cdpb.csParam.words[0] = 0;  /* Position Mode */
    cdpb.csParam.words[1] = 0;  /* Search Address (hiword) */
    cdpb.csParam.words[2] = 0;  /* Search Address (loword) */
    if (PBControlSync ((ParmBlkPtr) & cdpb) != noErr) {
        SDL_SetError ("PBControlSync() failed");
        return (-1);
    }
    return (0);
}

/* Eject the CD-ROM */
static int
SDL_SYS_CDEject (SDL_CD * cdrom)
{
    Boolean disk = false;
    QHdr *driveQ = GetDrvQHdr ();
    DrvQEl *driveElem;
    HParamBlockRec hpb;
    ParamBlockRec cpb;

    for (driveElem = (DrvQEl *) driveQ->qHead; driveElem; driveElem =
         (driveElem) ? ((DrvQEl *) driveElem->qLink) :
         ((DrvQEl *) driveQ->qHead)) {
        if (driveQ->qTail) {
            driveQ->qTail->qLink = 0;
        }
        if (driveElem->dQRefNum != SDL_cdlist[cdrom->id].dRefNum) {
            continue;
        }

        /* Does drive contain mounted volume? If not, skip */
        SDL_memset (&hpb, 0, sizeof (hpb));
        hpb.volumeParam.ioVRefNum = driveElem->dQDrive;
        if (PBHGetVInfoSync (&hpb) != noErr) {
            continue;
        }
        if ((UnmountVol (0, driveElem->dQDrive) == noErr) &&
            (Eject (0, driveElem->dQDrive) == noErr)) {
            driveElem = 0;      /* Clear pointer to reset our loop */
            disk = true;
        }
    }

    /* If no disk is present, just eject the tray */
    if (!disk) {
        SDL_memset (&cpb, 0, sizeof (cpb));
        cpb.cntrlParam.ioVRefNum = 0;   /* No Drive */
        cpb.cntrlParam.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
        cpb.cntrlParam.csCode = kEjectTheDisc;
        if (PBControlSync ((ParmBlkPtr) & cpb) != noErr) {
            SDL_SetError ("PBControlSync() failed");
            return (-1);
        }
    }
    return (0);
}

/* Close the CD-ROM handle */
static void
SDL_SYS_CDClose (SDL_CD * cdrom)
{
    return;
}

void
SDL_SYS_CDQuit (void)
{
    while (SDL_numcds--)
        SDL_memset (SDL_cdlist + SDL_numcds, 0, sizeof (SDL_cdlist[0]));
}

#endif /* SDL_CDROM_MACOS */
/* vi: set ts=4 sw=4 expandtab: */