Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2483 lines
82 KiB

/****************************************************************************
*
* mcipionr.c
*
* Copyright (c) 1991-1993 Microsoft Corporation. All Rights Reserved
*
* MCI Device Driver for the Pioneer 4200 Videodisc Player
*
* MCI Module - MCI commands and interface to device
*
***************************************************************************/
/* Known problems in this driver:
*
* 1) Play succeeds with no disc in the drive
* 2) The command 'spin down' does not notify when completed
* 3) The first 'status mode' after a 'spin down' followed by a
* 'seek' to an invalid adress can return 'play' instead of
* 'stopped'
* 4) A 'spin down notify' command followed by a 'play' command
* will return MCI_NOTIFY_FAILURE instead of MCI_NOTIFY_ABORTED
* 5) The first 'status time format' command sent after a new
* disk is inserted can result in a return value of '0' instead
* of a legal value like MCI_FORMAT_HMS
* 6) Multi-process device sharing for Windows NT is not addressed.
* The sharing will work correctly for 16-bit applications.
*/
#define USECOMM
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <mmddk.h>
#include "mcipionr.h"
#include "comm.h"
/* Maximum number of supported comports */
#define PIONEER_MAX_COMPORTS 4
/* Time in milliseconds that the driver will normally wait for the device */
/* to return data */
#define PIONEER_READCHAR_TIMEOUT 1000 /* Minimum for 10Mhz 286 */
/* Time in milliseconds that the driver will wait for the device to return */
/* data when the device is busy (i.e. seeking) */
#define PIONEER_MAX_BUSY_TIME 30000
/* Maximum length of the videodisc's buffer */
#define VDISC_BUFFER_LENGTH 20
/* Macro to determine if the given videodisc frame is valid */
#define VALID_FRAME(n) ((DWORD)(n) <= (DWORD)99999)
/* Maximum number of frames on a CAV disc */
#define CAV_MAX_DISC_FRAMES 54000
/* Frame rate of a CAV disc */
#define CAV_FRAMES_PER_SECOND 30
/* Flags a to position as invalid */
#define NO_TO_POSITION 0xFFFFFFFF
/* Flags a time mode as invalid */
#define NO_TIME_MODE 0xFFFFFFFF
/* Flags a comport as invalid */
#define NO_COMPORT -1
/* Internal play directions */
/* Set at least two bits so they'll never equal MCI_PLAY_VD_REVERSE */
#define PION_PLAY_FORWARD 3
#define PION_PLAY_NO_DIRECTION 7
/* Polling period in milliseconds used for setting timer */
#define TIMER_POLLING_PERIOD 100
/* Convert an HMS address to Seconds */
#define HMSTOSEC(hms) (MCI_HMS_HOUR(hms) * 3600 + MCI_HMS_MINUTE(hms) * 60 + \
MCI_HMS_SECOND(hms))
/* Difference in seconds between two HMS addresses */
#define HMS_DIFF(h2, h1) (HMSTOSEC(h2) - HMSTOSEC(h1))
/* Absolute value macro */
#define abs(a) ((a) < 0 ? -(a) : (a))
#ifdef WIN32
#define AnsiUpperChar(c) ((TCHAR)CharUpper((LPTSTR)(DWORD)(c)))
#else
#define wsprintfA wsprintf
#define lstrlenA lstrlen
#define AnsiUpperChar(c) ((char)(WORD)(DWORD)AnsiUpper((LPSTR)(DWORD)(WORD)(char)(c)))
#endif /* WIN32 */
/* Module instance handle */
HINSTANCE hInstance;
/* ID of the timer used for polling */
static int wTimerID;
/* Number of channels which are using the timer */
static int nWaitingChannels;
/* Set to FALSE inside TimerProc to prevent re-entrant disasters when */
/* trying to yield */
static BOOL bYieldWhenReading = TRUE;
/* Forward declarations */
static DWORD PASCAL NEAR spinupdown(UINT wDeviceID, int nPort, DWORD dwFlag, BOOL bWait);
static DWORD PASCAL NEAR status(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_STATUS_PARMS lpStatus);
/* Data for each comm port - Port 0 is com1, port 1 is com2, etc... */
static struct {
int nCommID; /* The ID returned by OpenComm */
UINT Rate; /* Baud rate */
HWND hCallback; /* Handle to window function to call back */
BOOL bPlayerBusy; /* TRUE if the player is seeking or transitioning */
/* between park and random access mode */
BOOL bDoorAction; /* TRUE if door opening or closing */
BOOL bPlayTo; /* TRUE if playing to a particular frame */
DWORD dwPlayTo; /* The frame being played to */
DWORD dwToTimeMode; /* Time mode of the dwPlayTo position */
UINT wAudioChannels; /* Bit 0 is ch1 status, bit 1 is ch2 */
BOOL bTimerSet; /* TRUE when there is a timer for this channel */
DWORD dwTimeMode; /* One of MCI_FORMAT_MILLISECONDS, */
/* MCI_FORMAT_HMS or */
/* MCI_FORMAT_FRAMES or */
/* MCI_VD_FORMAT_TRACK */
BOOL bCAVDisc; /* True if a CAV disc is inserted */
UINT wDeviceID; /* MCI device ID */
int nUseCount;
BOOL bShareable;
BOOL bResponding;
DWORD dwDirection; /* The current direction. One of: */
/* PION_PLAY_FORWARD */
/* PION_PLAY_NO_DIRECTION */
/* MCI_VD_PLAY_REVERSE */
DWORD dwBusyStart; /* The time the timer loop started waiting */
#ifdef WIN32
CRITICAL_SECTION
DeviceCritSec; /* Only one person accesses a device at a time */
#endif /* WIN32 */
} comport[PIONEER_MAX_COMPORTS];
#if DBG
/* Amount of information (if any) to dump out the debug port */
static UINT wDebugLevel = 0;
#endif
#ifdef WIN32
#define SZCODE CHAR
#define SZTCODE TCHAR
#else
#define SZCODE CHAR _based(_segname("_CODE"))
#define SZTCODE TCHAR _based(_segname("_CODE"))
#endif /* WIN32 */
static SZTCODE aszComm[] = TEXT("com");
static SZTCODE aszCommOpenFormat[] = TEXT("%s%1d");
static SZTCODE aszCommSetupFormat[] = TEXT("%s%1d:%d,n,8,1");
static SZCODE aszFrameFormat[] = "%lu";
static SZCODE aszTrackFormat[] = "%u";
static SZCODE aszHMSFormat[] = "%1u%2u%2u";
static SZCODE aszCLVLength[] = "10000";
static SZCODE aszNull[] = "";
static SZCODE aszQueryPlaying[] = "?P";
static SZCODE aszQueryMedia[] = "?D";
static SZCODE aszQueryTrack[] = "?C";
static SZCODE aszQueryFormat[] = "?F";
static SZCODE aszQueryTime[] = "?T";
static SZCODE aszFormat[] = "FR";
static SZCODE aszTime[] = "TM";
static SZCODE aszCheck[] = "CH";
static SZCODE aszClose[] = "0KL";
static SZCODE aszAudioOn[] = "3AD";
static SZCODE aszSpinUp[] = "SA";
static SZCODE aszStopMarker[] = "SM";
static SZCODE aszSeekTo[] = "SE";
static SZCODE aszSeekToFormat[] = "%uSE";
static SZCODE aszSeekStart[] = "0SE";
static SZCODE aszSeekEnd[] = "99999SE";
static SZCODE aszSeekSetSpeed[] = "255SP";
static SZCODE aszFastSetSpeed[] = "180SP";
static SZCODE aszSlowSetSpeed[] = "20SP";
static SZCODE aszMediumSetSpeed[] = "60SP";
static SZCODE aszSetSpeedFormat[] = "%uSP";
static SZCODE aszMediaReverse[] = "MR";
static SZCODE aszMediaForward[] = "MF";
static SZCODE aszPlay[] = "PL";
static SZCODE aszPause[] = "PA";
static SZCODE aszStop[] = "ST";
static SZCODE aszReject[] = "RJ";
static SZCODE aszStepReverse[] = "SR";
static SZCODE aszStepForward[] = "SF";
static SZCODE aszOpenDoor[] = "OP";
static SZCODE aszCommandOff[] = "2CM";
static SZCODE aszCommandOn[] = "3CM";
static SZCODE aszIndexOff[] = "0DS";
static SZCODE aszIndexOn[] = "1DS";
static SZCODE aszKeyLockOff[] = "0KL";
static SZCODE aszKeyLockOn[] = "1KL";
/****************************************************************************
* @doc INTERNAL MCI
*
* @api UINT | getchars | Read "n" characters into "buf". Wait up to
* PIONEER_READCHAR_TIMEOUT milliseconds.
*
* @parm int | nPort | Port number.
*
* @parm LPSTR | lpstrBuf | Buffer to fill.
*
* @parm int | n | Number of characters to read.
*
* @parm UINT | wWait | Number of milliseconds to wait before timing out
* If 0 then wait for PIONEER_READCHAR_TIMEOUT milliseconds.
*
* @rdesc Number of characters actually read.
***************************************************************************/
static UINT PASCAL NEAR getchars(UINT wDeviceID, int nPort, LPSTR lpstrBuf, int n, UINT wWait)
{
int nRead, FirstTry = TRUE;
DWORD dwTime0, dwTime;
int nToRead = n;
int nCommID = comport[nPort].nCommID;
#if DBG
LPSTR lpstrStart = lpstrBuf;
#endif
if (wWait == 0)
wWait = PIONEER_READCHAR_TIMEOUT;
while (n > 0) {
/* Try to read the number of characters that are left to read */
nRead = ReadComm(nCommID, lpstrBuf, n);
/* Some characters were read */
if (nRead > 0) {
n -= nRead;
lpstrBuf += nRead;
}
else {
/* Either 0 characters read or an error occured */
if (nRead < 0) {
DOUT("mcipionr: Comm error");
return MCIERR_HARDWARE;
}
if (nRead == 0) {
COMSTAT comstat;
if (GetCommError(nCommID, &comstat) != 0) {
DOUT("mcipionr: Comm error");
return MCIERR_HARDWARE;
}
}
}
/* If not all characters were read */
if (n > 0) {
/* If first failure then initialize time base */
if (FirstTry) {
dwTime0 = GetCurrentTime();
FirstTry = FALSE;
}
/* If subsequent failure then check for timeout */
else {
dwTime = GetCurrentTime();
/* Check timer rollover case */
if (dwTime < dwTime0 &&
(dwTime + (0xFFFFFFFF - dwTime0)) > wWait) {
DOUT("mcipionr: getchars timeout!");
break;
}
/* Normal case */
if (dwTime - dwTime0 > wWait) {
DOUT("mcipionr: getchars timeout!");
break;
}
if (bYieldWhenReading)
pionDriverYield(wDeviceID, nPort);
#ifdef WIN32
Sleep(10);
#endif /* WIN32 */
}
}
}
#if DBG
{
CHAR temp[50];
LPSTR lpstrTemp = temp;
nRead = nToRead - n;
if (nRead > sizeof(temp) / sizeof(CHAR) - 1)
nRead = sizeof(temp) / sizeof(CHAR) - 1;
lpstrBuf = lpstrStart;
while (nRead-- > 0)
*lpstrTemp++ = *lpstrBuf++;
*lpstrTemp = '\0';
DOUT("MCIPIONR received:");
DOUTX(temp);
}
#endif
return nToRead - n;
}
/****************************************************************************
* @doc INTERNAL MCI
*
* @api int | GetCompletionCode | Read either a completion code ("R<CR>")
* or an error ("E0x<CR>").
*
* @parm UINT | wDeviceID | Device ID to use.
*
* @parm int | nPort | Port number.
*
* @parm UINT | wWait | Number of milliseconds to wait before timing out
* If 0 then wait for PIONEER_READCHAR_TIMEOUT milliseconds.
*
* @rdesc Returns zero if no error.
***************************************************************************/
static int PASCAL NEAR GetCompletionCode(UINT wDeviceID, int nPort, UINT wWait)
{
CHAR buf[4]; /* This must be larger than 2 because of Win 3.1 Comm bug */
DOUT("Get completion:");
if (getchars(wDeviceID, nPort, buf, 2, wWait) != 2)
return MCIERR_HARDWARE;
if (buf[0] == 'E') {
/* Empty the buffer of the error condition */
getchars(wDeviceID, nPort, buf, 2, wWait);
comport[nPort].bPlayerBusy = FALSE;
return MCIERR_HARDWARE;
}
DOUT("True");
return 0;
}
/****************************************************************************
* @doc INTERNAL MCI
*
* @api static int | putchars | Send "n" characters from "lpstrS" to the
* port specified by "nPort".
*
* @parm UINT | wDeviceID | Device ID to use.
*
* @parm int | nPort | The port number.
*
* @parm LPSTR | lpstrS | The string to send.
*
* @parm int | bReturn | If TRUE then the function waits for a
* completion code (or an error) to be returned or until a timeout
* (PIONEER_GETCHAR_TIMEOUT msecs). The timeout generates an error.
*
* @rdesc Returns zero if no error.
***************************************************************************/
static int PASCAL NEAR putchars(UINT wDeviceID, int nPort, LPSTR lpstrS, int bReturn)
{
int nCommID = comport[nPort].nCommID;
static CHAR c = 0xd;
int n = 0;
#if DBG
DOUT("MCIPIONR sent:");
DOUTX(lpstrS);
#endif
n = lstrlenA(lpstrS);
/* Send string to player */
if (WriteComm(nCommID, lpstrS, n) != n ||
WriteComm(nCommID, &c, 1) != 1) {
DOUT("mcipionr: Hardware error on output");
return MCIERR_HARDWARE;
}
if (bReturn)
/* Get completion code */
return GetCompletionCode(wDeviceID, nPort, 0);
else
return 0;
}
/****************************************************************************
* Get player status - Returns 1 if it is PLAY or MULTI-SPEED, -1 if there
* is a hardware error, and 0 otherwise.
***************************************************************************/
static int PASCAL NEAR IsPlaying(UINT wDeviceID, int nPort)
{
CHAR buf[8];
putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
if (getchars(wDeviceID, nPort, buf, 4, 0) != 4)
return -1;
return buf[0] == 'P' && (buf[2] == '4' || buf[2] == '9');
}
/****************************************************************************
* @doc INTERNAL MCI
*
* @api void | cancel_notify | Cancel any active notification for this port.
*
* @parm int | nPort | The port number to check.
*
* @parm UINT | wStatus | The notification status to return.
*
* @rdesc There is no return value.
***************************************************************************/
static void PASCAL NEAR cancel_notify(int nPort, UINT wStatus)
{
if (comport[nPort].bTimerSet) {
comport[nPort].bTimerSet = FALSE;
if (--nWaitingChannels <= 0)
KillTimer(NULL, wTimerID);
mciDriverNotify(comport[nPort].hCallback,
comport[nPort].wDeviceID, wStatus);
}
}
/****************************************************************************
* Check if the disc is spinning and return 0 if it is
***************************************************************************/
static DWORD PASCAL NEAR IsDiscSpinning(UINT wDeviceID, UINT nPort)
{
CHAR buf[8];
putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
if (getchars(wDeviceID, nPort, buf, 4, 0) != 4)
return MCIERR_HARDWARE;
if (buf[0] != 'P') {
DOUT("mcipionr: bad mode info from player");
return MCIERR_HARDWARE;
}
if (buf[1] != '0')
return MCIERR_PIONEER_NOT_SPINNING;
if (buf[2] == '0' || buf[2] == '1')
return MCIERR_PIONEER_NOT_SPINNING;
else
return 0;
}
/****************************************************************************
* Get the media type
***************************************************************************/
/* If a CLV disc has time mode set to FRAMES, set the time mode to HMS */
/* because frames are illegal for CLV. */
static DWORD PASCAL NEAR get_media_type(
UINT wDeviceID,
UINT nPort,
DWORD FAR* pdwMediaType)
{
CHAR buf[VDISC_BUFFER_LENGTH];
putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
return MCIERR_HARDWARE;
if (buf[0] != '0') {
if (buf[1] == '0') {
comport[nPort].bCAVDisc = TRUE;
*pdwMediaType = MCI_VD_MEDIA_CAV;
return 0;
}
if (buf[1] == '1') {
comport[nPort].bCAVDisc = FALSE;
if (comport[nPort].dwTimeMode == MCI_FORMAT_FRAMES)
comport[nPort].dwTimeMode = MCI_FORMAT_HMS;
*pdwMediaType = MCI_VD_MEDIA_CLV;
return 0;
}
}
return MCIERR_PIONEER_NOT_SPINNING;
}
/****************************************************************************
* Set the proper default time mode for the currently loaded media type
***************************************************************************/
static void PASCAL NEAR set_time_mode(UINT wDeviceID, UINT nPort)
{
DWORD dwMediaType;
if (!get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CAV)) {
comport[nPort].dwTimeMode = MCI_FORMAT_FRAMES;
putchars(wDeviceID, nPort, aszFormat, TRUE);
} else {
comport[nPort].dwTimeMode = MCI_FORMAT_HMS;
putchars(wDeviceID, nPort, aszTime, TRUE);
}
}
/****************************************************************************
* See if the player has arrived at the proper 'to' position
***************************************************************************/
static BOOL PASCAL NEAR check_arrival(int nPort)
{
MCI_STATUS_PARMS Status;
BOOL bWasTracks = FALSE, bResult = TRUE;
if (comport[nPort].dwPlayTo == NO_TO_POSITION)
return TRUE;
if (comport[nPort].dwTimeMode == MCI_VD_FORMAT_TRACK &&
comport[nPort].dwToTimeMode != MCI_VD_FORMAT_TRACK) {
bWasTracks = TRUE;
set_time_mode(comport[nPort].wDeviceID, nPort);
}
Status.dwItem = MCI_STATUS_POSITION;
if (LOWORD(status(comport[nPort].wDeviceID, nPort, MCI_STATUS_ITEM,
(LPMCI_STATUS_PARMS)&Status)) != 0) {
bResult = FALSE;
goto reset_mode;
}
switch (comport[nPort].dwTimeMode) {
case MCI_FORMAT_HMS:
{ int n;
n = HMS_DIFF(Status.dwReturn, comport[nPort].dwPlayTo);
if (abs(n) > 2)
bResult = FALSE;
break;
}
case MCI_FORMAT_FRAMES:
if (abs((long)Status.dwReturn - (long)comport[nPort].dwPlayTo) > 2)
bResult = FALSE;
break;
case MCI_FORMAT_MILLISECONDS:
if (abs((long)Status.dwReturn - (long)comport[nPort].dwPlayTo) > 2000)
bResult = FALSE;
break;
case MCI_VD_FORMAT_TRACK:
if (abs((long)Status.dwReturn - (long)comport[nPort].dwPlayTo) > 1)
bResult = FALSE;
break;
default:
bResult = FALSE;
break;
}
reset_mode:;
if (bWasTracks) {
putchars(comport[nPort].wDeviceID, nPort, aszCheck, TRUE);
comport[nPort].dwTimeMode = MCI_VD_FORMAT_TRACK;
}
return bResult;
}
/****************************************************************************
* The timer is active if any comm port controlled by this driver is
* waiting to reach a point on the disc (comport[nPort].bPlayTo == TRUE)
* or if it is seeking or spinning up or down
* (comport[nPort].bBusy == TRUE) AND if "notify <x>" was specified for
* the command
***************************************************************************/
void FAR PASCAL _LOADDS TimerProc(HWND hwnd, UINT uMessage, UINT uTimer, DWORD dwParam)
{
int nCommID;
int nPort;
bYieldWhenReading = FALSE;
/* Loop through all channels */
for (nPort = 0; nPort < PIONEER_MAX_COMPORTS; LeaveCrit(nPort), nPort++) {
/* Serialize access to this device */
EnterCrit(nPort);
nCommID = comport[nPort].nCommID;
/* If this channel is not waiting, skip it */
if (comport[nPort].bPlayerBusy) {
int nRetCode;
nRetCode = GetCompletionCode(comport[nPort].wDeviceID, nPort, 0);
/* If ok, or got valid error return from port */
if (!nRetCode || !comport[nPort].bPlayerBusy) {
comport[nPort].dwBusyStart = 0;
comport[nPort].bPlayerBusy = FALSE;
/* Unless the channel is waiting for a frame to be reached (play to x) */
/* notify the application */
if (!comport[nPort].bPlayTo)
cancel_notify(nPort, nRetCode == 0 ?
MCI_NOTIFY_SUCCESSFUL :
MCI_NOTIFY_FAILURE);
}
else {
/* No completion code so skip play */
if (GetCommError(nCommID, NULL) != 0)
cancel_notify(nPort, MCI_NOTIFY_FAILURE);
if (comport[nPort].dwBusyStart != 0) {
if (GetCurrentTime() >
comport[nPort].dwBusyStart + PIONEER_MAX_BUSY_TIME)
cancel_notify(nPort, MCI_NOTIFY_FAILURE);
} else
comport[nPort].dwBusyStart = GetCurrentTime();
continue;
}
}
if (comport[nPort].bPlayTo) {
int nPlay;
if ((nPlay = IsPlaying(comport[nPort].wDeviceID, nPort)) == 0)
cancel_notify(nPort, check_arrival(nPort) ?
MCI_NOTIFY_SUCCESSFUL :
MCI_NOTIFY_FAILURE);
else if (nPlay == -1)
cancel_notify(nPort, MCI_NOTIFY_FAILURE);
}
}
bYieldWhenReading = TRUE;
}
/****************************************************************************
* @doc INTERNAL
*
* @api int | LibMain | Library initialization code.
*
* @parm HINSTANCE | hModule | Our instance handle.
*
* @parm UINT | cbHeap | The heap size from the .def file.
*
* @parm LPCSTR | lpszCmdLine | The command line.
*
* @rdesc Returns 1 if the initialization was successful and 0 otherwise.
***************************************************************************/
int PASCAL NEAR LibMain(HINSTANCE hModule, UINT cbHeap, LPCSTR lpszCmdLine)
{
int nPort;
hInstance = hModule;
/* Port 0 is com1, port 1 is com2, etc... */
for (nPort = 0; nPort < PIONEER_MAX_COMPORTS; nPort++)
comport[nPort].nCommID = NO_COMPORT;
#if DBG
wDebugLevel = GetProfileInt(TEXT("mmdebug"), TEXT("mcipionr"), wDebugLevel);
#endif
return TRUE;
}
/****************************************************************************
* Compare up to "n" characters in two strings. Comparison is case insensitive.
* Returns 0 if they match, and non-zero otherwise.
***************************************************************************/
static UINT PASCAL NEAR vdisc_lstrncmp_nocase(LPTSTR lpS1, LPTSTR lpS2, int n)
{
while (*lpS1 && *lpS2 && n--)
{
if (AnsiUpperChar(*lpS1) != AnsiUpperChar(*lpS2))
break;
lpS1++;
lpS2++;
}
return n;
}
/****************************************************************************
* Send a successful notify if wErr is 0 and the MCI_NOTIFY flag is set
* and supersede any active notification
***************************************************************************/
static void PASCAL NEAR notify(UINT wErr, DWORD dwFlags, UINT wDeviceID, LPMCI_GENERIC_PARMS lpParms, int nPort)
{
if (wErr == 0 && dwFlags & MCI_NOTIFY) {
cancel_notify(nPort, MCI_NOTIFY_SUPERSEDED);
mciDriverNotify((HWND)(UINT)lpParms->dwCallback, wDeviceID,
MCI_NOTIFY_SUCCESSFUL);
}
}
/****************************************************************************
* Process the MCI_NOTIFY and MCI_WAIT flags
*
* If MCI_NOTIFY then start the timer going (if not started)
*
* If MCI_WAIT then wait until completion if seeking or until the
* player is stopped if "play to <x>" is the command
*
* Otherwise, if "play to <x>" was the command then just clear the
* bPlayTo flag
***************************************************************************/
static DWORD PASCAL NEAR process_delay(UINT wDeviceID, int nPort, DWORD dwFlags, DWORD dwCb)
{
int nCommID;
nCommID = comport[nPort].nCommID;
if (dwFlags & MCI_WAIT) {
if (comport[nPort].bPlayerBusy) {
comport[nPort].bPlayerBusy = FALSE;
if (GetCompletionCode(wDeviceID, nPort, PIONEER_MAX_BUSY_TIME)
!= 0) {
if (dwFlags & MCI_NOTIFY)
mciDriverNotify((HANDLE)dwCb, wDeviceID,
MCI_NOTIFY_FAILURE);
return MCIERR_HARDWARE;
}
}
/* if (comport[nPort].bPlayTo) */
{
int nPlay;
while ((nPlay = IsPlaying(wDeviceID, nPort)) == 1) {
/* If the operation should be aborted */
if (pionDriverYield(wDeviceID, nPort) != 0)
return process_delay(wDeviceID, nPort, dwFlags & ~MCI_WAIT, dwCb);
#ifdef WIN32
Sleep(10);
#endif /* WIN32 */
}
comport[nPort].bPlayTo = FALSE;
if (nPlay == -1) {
if (dwFlags & MCI_NOTIFY)
mciDriverNotify((HANDLE)dwCb,
wDeviceID, MCI_NOTIFY_FAILURE);
return MCIERR_HARDWARE;
}
}
if (dwFlags & MCI_NOTIFY)
mciDriverNotify((HANDLE)dwCb, wDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
else if (dwFlags & MCI_NOTIFY && (HANDLE)dwCb != NULL) {
comport[nPort].hCallback = (HANDLE)dwCb;
if (!comport[nPort].bTimerSet) {
comport[nPort].bTimerSet = TRUE;
comport[nPort].wDeviceID = wDeviceID;
if (nWaitingChannels++ == 0)
if ((wTimerID = SetTimer(NULL, 1, TIMER_POLLING_PERIOD,
(TIMERPROC)TimerProc)) == 0)
return MCIERR_PIONEER_NO_TIMERS;
}
} else
comport[nPort].bPlayTo = FALSE;
return 0;
}
/****************************************************************************
* Concatenate lpIN onto lpOut but don't exceed wLen in length, including
* terminating null. Returns the total length not including the terminating
* null or 0 on error or overflow.
***************************************************************************/
static UINT PASCAL NEAR catstring(LPSTR lpOut, LPSTR lpIn, int nLen)
{
int nSize = 0;
if (lpOut == NULL || lpIn == NULL)
return 0;
/* search to end of lpOut */
while (*lpOut != '\0') {
++lpOut;
++nSize;
}
/* concatenate */
while (nSize++ < nLen && *lpIn != '\0')
*lpOut++ = *lpIn++;
*lpOut = '\0';
if (*lpIn != '\0')
return 0;
return nSize - 1;
}
/****************************************************************************
* Convert the input string to a DWORD
***************************************************************************/
static DWORD PASCAL NEAR vdisc_atodw(LPSTR lpstrInput)
{
DWORD dwRet = 0;
while (*lpstrInput >= '0' && *lpstrInput <= '9')
dwRet = dwRet * 10 + *lpstrInput++ - '0';
return dwRet;
}
/****************************************************************************
* Shut down the device and release the com port
***************************************************************************/
static void PASCAL NEAR vdisc_close(UINT wDeviceID, int nPort)
{
int nCommID = comport[nPort].nCommID;
#if DBG
CHAR buf[100];
wsprintfA(buf, "port=%d commid=%d", nPort, nCommID);
DOUT(buf);
#endif
DOUT("vdisc_close");
if (nCommID != NO_COMPORT) {
/* Unlock front panel */
DOUT("unlock");
/* Don't allow a yield because auto-close will be messed up */
bYieldWhenReading = FALSE;
putchars(wDeviceID, nPort, aszClose, TRUE);
bYieldWhenReading = TRUE;
DOUT("CloseComm");
CloseComm(nCommID);
}
}
/****************************************************************************
* Switch to frame or time mode whichever is appropriate for the disk type
***************************************************************************/
static int PASCAL NEAR unset_chapter_mode(UINT wDeviceID, int nPort)
{
CHAR buf[8];
putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
return MCIERR_HARDWARE;
if (buf[1] == '0')
putchars(wDeviceID, nPort, aszFormat, TRUE);
else
putchars(wDeviceID, nPort, aszTime, TRUE);
return 0;
}
/****************************************************************************
* Read the comport number for the input in the form "com<x>"
***************************************************************************/
void PASCAL FAR pionGetComportAndRate(LPTSTR lpstrBuf, PUINT pPort, PUINT pRate)
{
LPTSTR pszChar;
*pPort = 0;
*pRate = DEFAULT_BAUD_RATE;
if (lpstrBuf != NULL) {
while (*lpstrBuf == ' ')
++lpstrBuf;
if (!vdisc_lstrncmp_nocase(lpstrBuf, aszComm, sizeof(aszComm) / sizeof(TCHAR) - 1))
if (lpstrBuf[sizeof(aszComm) / sizeof(TCHAR) -1] >= '1' &&
lpstrBuf[sizeof(aszComm) / sizeof(TCHAR)-1] <=
'0' + PIONEER_MAX_COMPORTS) {
UINT wPort;
if ((wPort = lpstrBuf[sizeof(aszComm)/sizeof(TCHAR)-1] - '1') < PIONEER_MAX_COMPORTS) {
*pPort = wPort;
}
}
/*
* Baud rate (if any - default is 4800) is after ','
*/
for (pszChar = lpstrBuf;
*pszChar != TEXT(',') && *pszChar != TEXT('\0');
pszChar++);
if (*pszChar == TEXT(',')) {
pszChar++;
}
/* Remove blanks */
for (; *pszChar == TEXT(' '); pszChar++);
if (*pszChar != TEXT('\0')) {
UINT Rate = 0;
/*
* Extract the rate
*/
while (*pszChar >= '0' && *pszChar <= '9') {
Rate = Rate * 10 + (*pszChar - TEXT('0'));
pszChar++;
}
if (Rate != 0) {
*pRate = Rate;
}
}
}
}
/****************************************************************************
* Set the rate for the port
***************************************************************************/
void pionSetBaudRate(UINT nPort, UINT nRate)
{
comport[nPort].Rate = nRate;
}
/****************************************************************************
* Initialize the player
***************************************************************************/
static DWORD PASCAL NEAR init_player(UINT wDeviceID, int nPort)
{
CHAR buf[VDISC_BUFFER_LENGTH];
BOOL bPlayerSpinning = FALSE;
/* Set the audio channels to a known state (both on) */
putchars(wDeviceID, nPort, aszAudioOn, TRUE);
comport[nPort].wAudioChannels = 3;
/* See if the disk is spinning */
putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
getchars(wDeviceID, nPort, buf, 4, 0);
if (buf[0] == 'P' && buf[2] != '0' && buf[2] != '1') {
bPlayerSpinning = TRUE;
/* What kind of disc is this (CAV or CLV)? */
putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
return MCIERR_HARDWARE;
if (buf[1] != '1') {
comport[nPort].dwTimeMode = MCI_FORMAT_FRAMES;
comport[nPort].bCAVDisc = TRUE;
/* Set mode to frames */
putchars(wDeviceID, nPort, aszFormat, TRUE);
}
else {
comport[nPort].dwTimeMode = MCI_FORMAT_HMS;
comport[nPort].bCAVDisc = FALSE;
/* Set mode to hms */
putchars(wDeviceID, nPort, aszTime, TRUE);
}
}
else {
bPlayerSpinning = FALSE;
comport[nPort].dwTimeMode = NO_TIME_MODE;
}
comport[nPort].bPlayerBusy = FALSE;
comport[nPort].bDoorAction = FALSE;
comport[nPort].bPlayTo = FALSE;
comport[nPort].bTimerSet = FALSE;
comport[nPort].dwBusyStart = 0;
comport[nPort].dwDirection = PION_PLAY_NO_DIRECTION;
if (!bPlayerSpinning && comport[nPort].bResponding
&& spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, FALSE) != 0)
return MCIERR_HARDWARE;
else
return 0;
}
/****************************************************************************
* Process the MCI_OPEN_DRIVER message
***************************************************************************/
static DWORD PASCAL NEAR open(UINT wDeviceID, int nPort, DWORD dwFlags)
{
DCB dcb;
TCHAR strDescription [20];
int nCommID;
if (dwFlags & MCI_OPEN_ELEMENT)
return MCIERR_NO_ELEMENT_ALLOWED;
/* See if a com port was specified in the SYSTEM.INI parameters */
wsprintf(strDescription, aszCommOpenFormat, (LPSTR)aszComm, nPort + 1);
/* Try to open the com port */
if ((nCommID = OpenComm(strDescription, 100, 100)) < 0)
return MCIERR_HARDWARE;
/* Set up the com port, 4800 baud (switch S7 UP) or 9600 baud is assumed */
wsprintf(strDescription, aszCommSetupFormat, (LPTSTR)aszComm, nPort + 1,
comport[nPort].Rate);
/*
* need to initialise state of dcb first since BuildCommDCB only sets
* some fields
*/
GetCommState((HANDLE)nCommID, &dcb);
BuildCommDCB(strDescription, &dcb);
if (!SetCommState((HANDLE)nCommID, &dcb)) {
CloseComm(nCommID);
return MCIERR_HARDWARE;
}
/* Set up the channel description */
comport[nPort].nCommID = nCommID;
if (dwFlags & MCI_OPEN_SHAREABLE)
comport[nPort].bShareable = TRUE;
else
comport[nPort].bShareable = FALSE;
/* Don't make the user wait at this point to test if the device responds -
they can wait when they really try to use the device */
comport[nPort].bResponding = FALSE;
return 0;
}
/****************************************************************************
* Convert the given position dwPos in the current time format into the units
* appropriate for the disk type puting the result in buf
***************************************************************************/
static DWORD PASCAL NEAR encode_position(UINT wDeviceID, LPSTR buf, DWORD dwPos, int nPort)
{
BYTE h, m, s;
/* Allow frame 0 */
if (dwPos == 0
&& comport[nPort].dwTimeMode != MCI_FORMAT_HMS
&& comport[nPort].dwTimeMode != MCI_VD_FORMAT_TRACK)
dwPos = 1;
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(wDeviceID, nPort);
switch (comport[nPort].dwTimeMode) {
case MCI_FORMAT_FRAMES:
/* Ensure frame is at most five characters */
if (!VALID_FRAME(dwPos))
return MCIERR_OUTOFRANGE;
wsprintfA(buf, aszFrameFormat, dwPos);
break;
case MCI_FORMAT_HMS:
h = MCI_HMS_HOUR(dwPos);
m = MCI_HMS_MINUTE(dwPos);
s = MCI_HMS_SECOND(dwPos);
if (h > 9 || m > 59 || s > 59)
return MCIERR_OUTOFRANGE;
if (comport[nPort].bCAVDisc)
wsprintfA(buf, aszFrameFormat, (DWORD)(((h * 60) + m) * 60 + s) *
CAV_FRAMES_PER_SECOND);
else {
wsprintfA(buf, aszHMSFormat, h, m, s);
if (m < 10)
buf[1] = '0';
if (s < 10)
buf[3] = '0';
}
break;
case MCI_FORMAT_MILLISECONDS:
if (comport[nPort].bCAVDisc) {
dwPos = (dwPos * 3) / 100; /* 30 frames/second */
wsprintfA(buf, aszFrameFormat, dwPos);
}
else {
UINT wX;
dwPos /= 1000; /* ignore fractions of a second */
/* Number of minutes leftover from hours */
wX = (UINT)(dwPos % 3600);
h = (CHAR)((dwPos - wX) / 3600);
if (h > 9)
return MCIERR_OUTOFRANGE;
dwPos = wX;
s = (CHAR)(dwPos % 60);
m = (CHAR)((dwPos - s) / 60);
wsprintfA(buf, aszHMSFormat, h, m, s);
/* Fill in leading zero's */
if (m < 10)
buf[1] = '0';
if (s < 10)
buf[3] = '0';
}
break;
case MCI_VD_FORMAT_TRACK:
if (dwPos > 99)
return MCIERR_OUTOFRANGE;
wsprintfA(buf, aszTrackFormat, dwPos);
break;
}
return 0L;
}
/****************************************************************************
* Convert frames to the output representation for the current time mode
***************************************************************************/
static DWORD PASCAL NEAR convert_frames(UINT wDeviceID, int nPort, DWORD dwFrames, LPDWORD lpdwReturn)
{
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(wDeviceID, nPort);
switch (comport[nPort].dwTimeMode) {
case MCI_FORMAT_FRAMES:
*lpdwReturn = dwFrames;
break;
case MCI_FORMAT_MILLISECONDS:
*lpdwReturn = (dwFrames * 100) / 3; /* 30 frames per second */
break;
case MCI_FORMAT_HMS:
{
DWORD dwSeconds = dwFrames / CAV_FRAMES_PER_SECOND;
*lpdwReturn = MCI_MAKE_HMS(dwSeconds / 3600,
(dwSeconds % 3600) / 60,
dwSeconds % 60);
return MCI_COLONIZED3_RETURN;
}
}
return 0;
}
/****************************************************************************
* Convert hms to the output representation for the current time mode
***************************************************************************/
static DWORD PASCAL NEAR convert_hms(UINT wDeviceID, int nPort, LPSTR buf, LPDWORD lpdwReturn)
{
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(wDeviceID, nPort);
if (comport[nPort].dwTimeMode == MCI_FORMAT_HMS) {
UINT wTemp;
*lpdwReturn = MCI_MAKE_HMS(buf[0] - '0',
(buf[1] - '0') * 10 + buf[2] - '0',
(buf[3] - '0') * 10 + buf[4] - '0');
return MCI_COLONIZED3_RETURN;
}
else if (comport[nPort].dwTimeMode == MCI_FORMAT_MILLISECONDS) {
*lpdwReturn =
(buf[0] - '0') * 3600000L +
((buf[1] - '0') * 10 + (buf[2] - '0')) * 60000L +
((buf[3] - '0') * 10 + (buf[4] - '0')) * 1000L;
return 0;
} else
return MCIERR_HARDWARE;
}
/****************************************************************************/
static int PASCAL NEAR DeviceStatusMode(
UINT wDeviceID,
int nPort)
{
#define PIONEER_MODE_OPEN '0'
#define PIONEER_MODE_PARK '1'
#define PIONEER_MODE_PLAY '4'
#define PIONEER_MODE_STILL '5'
#define PIONEER_MODE_PAUSE '6'
#define PIONEER_MODE_MULTI '9'
CHAR buf[VDISC_BUFFER_LENGTH];
UINT wPos;
putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
if (getchars(wDeviceID, nPort, buf, 4, 0) == 0)
return MCI_MODE_NOT_READY;
/* This is done to remove the spurious return character generated */
/* if this command is sent after power is cut and restored to the player */
if (buf[0] != 'P' && buf[1] == 'P') {
wPos = 3;
getchars(wDeviceID, nPort, buf, 1, 200);
}
else
wPos = 2;
switch (buf[wPos]) {
case PIONEER_MODE_OPEN:
return MCI_MODE_OPEN;
case PIONEER_MODE_PARK:
return MCI_VD_MODE_PARK;
case PIONEER_MODE_PLAY:
return MCI_MODE_PLAY;
case PIONEER_MODE_STILL:
return MCI_MODE_PAUSE;
case PIONEER_MODE_PAUSE:
return MCI_MODE_STOP;
case PIONEER_MODE_MULTI:
return MCI_MODE_PLAY;
default:
return MCI_MODE_NOT_READY;
}
}
/****************************************************************************
* Process the MCI_STATUS message
***************************************************************************/
static DWORD PASCAL NEAR status(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_STATUS_PARMS lpStatus)
{
CHAR buf[VDISC_BUFFER_LENGTH];
int n;
DWORD dwRet;
DWORD dwMediaType;
if (!(dwFlags & MCI_STATUS_ITEM))
return MCIERR_MISSING_PARAMETER;
switch (lpStatus->dwItem) {
case MCI_STATUS_MODE:
{
n = DeviceStatusMode(wDeviceID, nPort);
if (n == MCI_MODE_NOT_READY)
n = DeviceStatusMode(wDeviceID, nPort);
lpStatus->dwReturn = MAKEMCIRESOURCE(n, n);
return MCI_RESOURCE_RETURNED;
}
case MCI_STATUS_POSITION:
putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
getchars(wDeviceID, nPort, buf, 4, 0);
if (buf[0] == 'P' && buf[1] == '0' && buf[2] == '0')
return MCIERR_HARDWARE;
dwRet = spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, TRUE);
if (dwRet != 0)
return dwRet;
if (dwFlags & MCI_TRACK)
return MCIERR_UNSUPPORTED_FUNCTION;
if (dwFlags & MCI_STATUS_START) {
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(wDeviceID, nPort);
switch (comport[nPort].dwTimeMode) {
case MCI_VD_FORMAT_TRACK:
lpStatus->dwReturn = 0;
return 0;
case MCI_FORMAT_FRAMES:
lpStatus->dwReturn = 1;
return 0;
case MCI_FORMAT_HMS:
lpStatus->dwReturn = 0;
return MCI_COLONIZED3_RETURN;
case MCI_FORMAT_MILLISECONDS:
if (comport[nPort].bCAVDisc)
lpStatus->dwReturn = 1000 / CAV_FRAMES_PER_SECOND;
else
lpStatus->dwReturn = 0;
return 0;
default:
return MCIERR_HARDWARE;
}
}
if (comport[nPort].dwTimeMode == MCI_VD_FORMAT_TRACK) {
putchars(wDeviceID, nPort, aszQueryTrack, FALSE);
n = getchars(wDeviceID, nPort, buf, 3, 0);
buf[n-1] = '\0';
if (buf[0] == 'E')
return MCIERR_HARDWARE;
lpStatus->dwReturn = vdisc_atodw(buf);
return 0;
}
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(wDeviceID, nPort);
else
get_media_type(wDeviceID, nPort, &dwMediaType);
if (comport[nPort].bCAVDisc) {
/* Try FRAMES */
putchars(wDeviceID, nPort, aszQueryFormat, FALSE);
n = getchars(wDeviceID, nPort, buf, 6, 0);
buf[n - 1] = '\0';
/* If no error then convert from frames */
if (buf[0] != 'E')
return convert_frames(wDeviceID, nPort, vdisc_atodw(buf),
&lpStatus->dwReturn);
else
return MCIERR_HARDWARE;
}
else {
/* Try TIME */
putchars(wDeviceID, nPort, aszQueryTime, FALSE);
n = getchars(wDeviceID, nPort, buf, 6, 0);
buf[n - 1] = '\0';
if (buf[0] == 'E') {
DOUT("mcipionr: error returning HMS position");
return MCIERR_HARDWARE;
}
if (comport[nPort].dwTimeMode == MCI_FORMAT_FRAMES)
return MCIERR_HARDWARE;
else
return convert_hms(wDeviceID, nPort, buf, &lpStatus->dwReturn);
}
case MCI_STATUS_MEDIA_PRESENT:
putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
return MCIERR_HARDWARE;
if (buf[0] == '1')
lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
else
lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
return MCI_RESOURCE_RETURNED;
case MCI_VD_STATUS_SPEED:
return MCIERR_UNSUPPORTED_FUNCTION;
case MCI_VD_STATUS_MEDIA_TYPE:
{
dwRet = get_media_type(wDeviceID, nPort, &dwMediaType);
if (dwRet)
return dwRet;
n = LOWORD(dwMediaType);
lpStatus->dwReturn = MAKEMCIRESOURCE(n, n);
return MCI_RESOURCE_RETURNED;
}
case MCI_VD_STATUS_SIDE:
{
DWORD dwRet = IsDiscSpinning(wDeviceID, nPort);
if (dwRet != 0)
return dwRet;
putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
return MCIERR_HARDWARE;
if (buf[0] == '0')
return MCIERR_PIONEER_NOT_SPINNING;
if (buf[3] == '0')
lpStatus->dwReturn = 1;
else
lpStatus->dwReturn = 2;
return 0;
}
case MCI_VD_STATUS_DISC_SIZE:
{
DWORD dwRet = IsDiscSpinning(wDeviceID, nPort);
if (dwRet != 0)
return dwRet;
putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
return MCIERR_HARDWARE;
if (buf[0] == '0')
return MCIERR_PIONEER_NOT_SPINNING;
if (buf[2] == '0')
lpStatus->dwReturn = 12;
else
lpStatus->dwReturn = 8;
return 0;
}
case MCI_STATUS_READY:
putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
if (getchars(wDeviceID, nPort, buf, 4, 0) != 4)
lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
else
lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
return MCI_RESOURCE_RETURNED;
case MCI_STATUS_LENGTH:
{
if (dwFlags & MCI_TRACK)
return MCIERR_UNSUPPORTED_FUNCTION;
putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
getchars(wDeviceID, nPort, buf, 4, 0);
if (buf[0] == 'P' && buf[1] == '0' && buf[2] == '0')
return MCIERR_HARDWARE;
dwRet = spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, TRUE);
if (dwRet != 0)
return dwRet;
if (comport[nPort].dwTimeMode == MCI_VD_FORMAT_TRACK)
return MCIERR_BAD_TIME_FORMAT;
dwRet = get_media_type(wDeviceID, nPort, &dwMediaType);
if (dwRet)
return dwRet;
if (dwMediaType == MCI_VD_MEDIA_CAV)
return convert_frames(wDeviceID, nPort, CAV_MAX_DISC_FRAMES,
&lpStatus->dwReturn);
return convert_hms(wDeviceID, nPort, aszCLVLength, &lpStatus->dwReturn);
}
case MCI_STATUS_TIME_FORMAT:
putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
getchars(wDeviceID, nPort, buf, 4, 0);
if (buf[0] == 'P' && buf[1] == '0' && buf[2] == '0')
return MCIERR_HARDWARE;
dwRet = spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, TRUE);
if (dwRet != 0)
return dwRet;
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(nPort, wDeviceID);
n = LOWORD(comport[nPort].dwTimeMode);
if (n == MCI_VD_FORMAT_TRACK)
lpStatus->dwReturn = MAKEMCIRESOURCE(MCI_VD_FORMAT_TRACK,
MCI_VD_FORMAT_TRACK_S);
else
lpStatus->dwReturn =
MAKEMCIRESOURCE(n, n + MCI_FORMAT_RETURN_BASE);
return MCI_RESOURCE_RETURNED;
case MCI_STATUS_CURRENT_TRACK:
putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
getchars(wDeviceID, nPort, buf, 4, 0);
if (buf[0] == 'P' && buf[1] == '0' && buf[2] == '0')
return MCIERR_HARDWARE;
dwRet = spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, TRUE);
if (dwRet != 0)
return dwRet;
putchars(wDeviceID, nPort, aszQueryTrack, FALSE);
n = getchars(wDeviceID, nPort, buf, 3, 0);
buf[n-1] = '\0';
if (buf[0] == 'E') {
/* Flush buffer */
getchars(wDeviceID, nPort, buf, 2, 0);
/* See if the problem is no chapter support */
putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
if (getchars(wDeviceID, nPort, buf, 6, 0) != 6 ||
buf[4] == '1')
return MCIERR_HARDWARE;
else
return MCIERR_PIONEER_NO_CHAPTERS;
}
lpStatus->dwReturn = vdisc_atodw(buf);
return 0;
}
return MCIERR_UNSUPPORTED_FUNCTION;
}
/****************************************************************************
* Process the MCI_SEEK message
***************************************************************************/
static DWORD PASCAL NEAR seek(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_SEEK_PARMS lpSeek)
{
int comlen;
CHAR buf[VDISC_BUFFER_LENGTH];
DWORD dwErr;
DWORD dwMediaType;
buf[0] = '\0';
/* Position for the wsprintf to start */
comlen = 0;
if (IsDiscSpinning(wDeviceID, nPort) != 0) {
if (dwFlags & MCI_TO) {
/* Must spin up NOW if a to position needs to be converted */
putchars(wDeviceID, nPort, aszSpinUp, FALSE);
GetCompletionCode(wDeviceID, nPort, PIONEER_MAX_BUSY_TIME);
} else
{
comlen = 2;
catstring(buf, aszSpinUp, VDISC_BUFFER_LENGTH);
/* 2 characters possible here */
comport[nPort].bPlayerBusy = TRUE;
}
}
if (dwFlags & (MCI_TO | MCI_SEEK_TO_START | MCI_SEEK_TO_END)) {
if (dwFlags & MCI_SEEK_TO_START) {
if (dwFlags & MCI_SEEK_TO_END)
return MCIERR_FLAGS_NOT_COMPATIBLE;
catstring (buf, aszSeekStart, VDISC_BUFFER_LENGTH);
}
else if (dwFlags & MCI_SEEK_TO_END) {
catstring (buf, aszSeekEnd, VDISC_BUFFER_LENGTH);
}
else if (dwFlags & MCI_TO) {
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(wDeviceID, nPort);
if ((dwErr = encode_position(wDeviceID, &buf[comlen], lpSeek->dwTo, nPort))
!= 0)
return dwErr;
catstring(buf, aszSeekTo, VDISC_BUFFER_LENGTH);
}
putchars(wDeviceID, nPort, buf, FALSE);
comport[nPort].bPlayerBusy = TRUE;
}
else {
catstring(buf, aszSeekSetSpeed, VDISC_BUFFER_LENGTH);
if (dwFlags & MCI_VD_SEEK_REVERSE) {
dwErr = get_media_type(wDeviceID, nPort, &dwMediaType);
if (dwErr)
return dwErr;
if (dwMediaType == MCI_VD_MEDIA_CAV)
catstring(buf, aszMediaReverse, VDISC_BUFFER_LENGTH);
else
return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
}
else
catstring(buf, aszMediaForward, VDISC_BUFFER_LENGTH);
putchars(wDeviceID, nPort, buf, TRUE);
if (dwFlags & MCI_NOTIFY) {
comport[nPort].bPlayTo = TRUE;
comport[nPort].dwPlayTo = NO_TO_POSITION;
}
}
cancel_notify(nPort, MCI_NOTIFY_ABORTED);
process_delay(wDeviceID, nPort, dwFlags, lpSeek->dwCallback);
return 0;
}
/****************************************************************************
* Process the MCI_PLAY message
***************************************************************************/
static DWORD PASCAL NEAR play(UINT wDeviceID, int nPort, DWORD dwFlags, MCI_VD_PLAY_PARMS FAR *lpPlay)
{
LPSTR compart;
int comlen;
DWORD dwErr;
DWORD dwMediaType;
BOOL bNormalSpeed = FALSE;
CHAR buf[VDISC_BUFFER_LENGTH];
DWORD dwOldToPosition = comport[nPort].bPlayTo ? comport[nPort].dwPlayTo
: NO_TO_POSITION;
DWORD dwOldDirection = comport[nPort].dwDirection;
BOOL bPlayerSpinning;
BOOL bGoingToBeBusy = FALSE;
buf[0] = '\0';
/* Convert a 'play x to x' into 'seek to x' if the positions are equal or */
/* for milliseconds if they are within the same frame or second */
if (dwFlags & MCI_FROM && dwFlags & MCI_TO &&
(lpPlay->dwTo == lpPlay->dwFrom ||
(comport[nPort].dwTimeMode == MCI_FORMAT_MILLISECONDS &&
lpPlay->dwTo - lpPlay->dwFrom <
(DWORD)(comport[nPort].bCAVDisc ? 40 : 1000))))
{
MCI_SEEK_PARMS Seek;
/* Preserve NOTIFY and WAIT and set TO flag */
DWORD dwSeekFlags;
dwSeekFlags = ((dwFlags & (MCI_NOTIFY | MCI_WAIT)) | MCI_TO);
Seek.dwTo = lpPlay->dwFrom;
Seek.dwCallback = lpPlay->dwCallback;
return seek(wDeviceID, nPort, dwSeekFlags,
(LPMCI_SEEK_PARMS)&Seek);
}
/* Build a command string to send to the player */
/* Position for the wsprintf to start */
comlen = 0;
#define PLAY_SPEED_FLAGS (MCI_VD_PLAY_FAST | MCI_VD_PLAY_SLOW | \
MCI_VD_PLAY_SPEED | MCI_VD_PLAY_SCAN)
/* Determine speed */
if (dwFlags & MCI_VD_PLAY_FAST) {
if ((dwFlags & PLAY_SPEED_FLAGS) != MCI_VD_PLAY_FAST)
return MCIERR_FLAGS_NOT_COMPATIBLE;
/* PLAY FAST */
compart = aszFastSetSpeed;
}
else if (dwFlags & MCI_VD_PLAY_SLOW) {
if ((dwFlags & PLAY_SPEED_FLAGS) != MCI_VD_PLAY_SLOW)
return MCIERR_FLAGS_NOT_COMPATIBLE;
/* PLAY SLOW */
compart = aszSlowSetSpeed;
}
else if (dwFlags & MCI_VD_PLAY_SPEED &&
lpPlay->dwSpeed != CAV_FRAMES_PER_SECOND) {
if ((dwFlags & PLAY_SPEED_FLAGS) != MCI_VD_PLAY_SPEED)
return MCIERR_FLAGS_NOT_COMPATIBLE;
if (lpPlay->dwSpeed > 127)
return MCIERR_OUTOFRANGE;
wsprintfA(buf, aszSetSpeedFormat, (UINT)lpPlay->dwSpeed * 2);
compart = buf;
}
else if (dwFlags & MCI_VD_PLAY_SCAN) {
wsprintfA(buf, aszSetSpeedFormat, 255);
compart = buf;
}
else {
/* PLAY NORMAL */
compart = aszNull;
if (dwFlags & MCI_VD_PLAY_REVERSE)
compart = aszMediumSetSpeed;
bNormalSpeed = TRUE;
}
if (compart[0] != '\0')
putchars(wDeviceID, nPort, compart, TRUE);
if (!(bPlayerSpinning = !IsDiscSpinning(wDeviceID, nPort))) {
if ((dwFlags & (MCI_TO | MCI_FROM)) != 0 || !bNormalSpeed) {
/* Must spin up NOW if a position needs to be converted */
putchars(wDeviceID, nPort, aszSpinUp, FALSE);
GetCompletionCode(wDeviceID, nPort, PIONEER_MAX_BUSY_TIME);
}
else {
catstring(buf, aszSpinUp, VDISC_BUFFER_LENGTH);
comlen = 2;
/* 2 characters possible here */
bGoingToBeBusy = TRUE;
}
}
if (!bNormalSpeed &&
!get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CLV))
return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
/* If FROM was specified */
if (dwFlags & MCI_FROM) {
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(wDeviceID, nPort);
if ((dwErr = encode_position(wDeviceID, &buf[comlen], lpPlay->dwFrom, nPort))
!= 0)
return dwErr;
/* 5 characters possible here, total of 7 */
catstring(buf, aszSeekTo, VDISC_BUFFER_LENGTH);
/* 2 characters possible here, total of 9 */
bGoingToBeBusy = TRUE;
}
/* If TO was specified */
if (dwFlags & MCI_TO) {
CHAR tobuf[10];
DWORD dwFrom;
if (dwFlags & MCI_VD_PLAY_REVERSE)
return MCIERR_FLAGS_NOT_COMPATIBLE;
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(wDeviceID, nPort);
if ((dwErr = encode_position(wDeviceID, tobuf, lpPlay->dwTo, nPort)) != 0)
return dwErr;
catstring(buf, tobuf, VDISC_BUFFER_LENGTH);
/* 5 characters possible here, total of 14 */
catstring(buf, aszStopMarker, VDISC_BUFFER_LENGTH);
/* 2 characters possible here, total of 16 */
comport[nPort].bPlayTo = TRUE;
comport[nPort].dwPlayTo = lpPlay->dwTo;
comport[nPort].dwToTimeMode = comport[nPort].dwTimeMode;
/* If to is less than from then go in reverse */
if (dwFlags & MCI_FROM)
dwFrom = lpPlay->dwFrom;
else {
if (!bPlayerSpinning)
dwFrom = 0;
else {
MCI_STATUS_PARMS Status;
Status.dwItem = MCI_STATUS_POSITION;
if ((dwErr =
status(wDeviceID, nPort, MCI_STATUS_ITEM,
(LPMCI_STATUS_PARMS)&Status)) != 0)
return dwErr;
dwFrom = Status.dwReturn;
}
}
/* Compare from and to positions */
if (comport[nPort].dwTimeMode == MCI_FORMAT_HMS) {
/* Account for slop */
DWORD dwTo = lpPlay->dwTo;
if (MCI_HMS_HOUR(dwTo) < MCI_HMS_HOUR(dwFrom))
dwFlags |= MCI_VD_PLAY_REVERSE;
else if (MCI_HMS_HOUR(dwTo) == MCI_HMS_HOUR(dwFrom)) {
if (MCI_HMS_MINUTE(dwTo) < MCI_HMS_MINUTE(dwFrom))
dwFlags |= MCI_VD_PLAY_REVERSE;
else if (MCI_HMS_MINUTE(dwTo) == MCI_HMS_MINUTE(dwFrom)) {
int nDelta = MCI_HMS_SECOND(dwTo) - MCI_HMS_SECOND(dwFrom);
/* Position is plus or minus 1 second from HMS */
if (nDelta <= 1 && nDelta >= -1)
dwFrom = lpPlay->dwTo;
else if (nDelta < 0)
dwFlags |= MCI_VD_PLAY_REVERSE;
}
}
}
else if (comport[nPort].dwTimeMode == MCI_FORMAT_MILLISECONDS) {
/* Account for slop */
long lDelta = lpPlay->dwTo - dwFrom;
if (lDelta < 0) {
lDelta = -lDelta;
dwFlags |= MCI_VD_PLAY_REVERSE;
}
if (comport[nPort].bCAVDisc &&
lDelta < 1000 / CAV_FRAMES_PER_SECOND)
dwFrom = lpPlay->dwTo;
else if (lDelta < 1000)
dwFrom = lpPlay->dwTo;
} else if (lpPlay->dwTo < dwFrom)
dwFlags |= MCI_VD_PLAY_REVERSE;
if (!comport[nPort].bCAVDisc && dwFlags & MCI_VD_PLAY_REVERSE)
return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
/* If from == to then do nothing */
if (lpPlay->dwTo == dwFrom) {
notify(0, dwFlags, wDeviceID,
(LPMCI_GENERIC_PARMS)lpPlay, nPort);
return 0;
}
}
else {
comport[nPort].bPlayTo = TRUE;
comport[nPort].dwPlayTo = NO_TO_POSITION;
}
/* Determine direction */
if (dwFlags & MCI_VD_PLAY_REVERSE)
/* PLAY REVERSE */
compart = aszMediaReverse;
else if (bNormalSpeed)
/* PLAY FORWARD NORMAL SPEED */
/* The PL command is used here instead of 60SPMF because only PL is */
/* legal for CLV discs */
compart = aszPlay;
else
/* PLAY FORWARD */
compart = aszMediaForward;
catstring(buf, compart, VDISC_BUFFER_LENGTH);
/* 2 characters possible here, total of 18, buffer size is 20 */
if (putchars(wDeviceID, nPort, buf, !bGoingToBeBusy) != 0)
return MCIERR_HARDWARE;
if (bGoingToBeBusy)
comport[nPort].bPlayerBusy = TRUE;
if (dwFlags & MCI_VD_PLAY_REVERSE)
comport[nPort].dwDirection = MCI_VD_PLAY_REVERSE;
else
comport[nPort].dwDirection = PION_PLAY_FORWARD;
/* If a from position is specified or a to position is specified with
* a different position than the active notify or if a new direction is
* specified then cancel notify;
*/
if (dwFlags & MCI_FROM ||
comport[nPort].dwPlayTo != dwOldToPosition ||
(dwOldDirection != PION_PLAY_NO_DIRECTION &&
comport[nPort].dwDirection != dwOldDirection))
cancel_notify(nPort, MCI_NOTIFY_ABORTED);
else if (dwFlags & MCI_NOTIFY)
cancel_notify(nPort, MCI_NOTIFY_SUPERSEDED);
/* if (comport[nPort].bPlayerBusy || comport[nPort].bPlayTo) */
process_delay(wDeviceID, nPort, dwFlags, lpPlay->dwCallback);
return 0;
}
/****************************************************************************
* Process the MCI_STOP and MCI_PAUSE messages
***************************************************************************/
static DWORD PASCAL NEAR stop_pause(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_GENERIC_PARMS lpGeneric, UINT wCommand)
{
DWORD dwErr;
DWORD dwMediaType;
/* Note, "ST" stands for 'still', "PA" stands for 'pause' but */
/* in MCI lingo "stop" uses the "PA" command and "pause" uses "ST" */
if ((dwErr = IsDiscSpinning(wDeviceID, nPort)) == 0) {
dwErr = (DWORD)putchars(wDeviceID, nPort,
wCommand == MCI_STOP ? aszPause : aszStop,
TRUE);
/* If error and CLV disc then try "stop" instead */
if (dwErr != 0 && wCommand == MCI_PAUSE)
if (!get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CLV))
dwErr = (DWORD)putchars(wDeviceID, nPort, aszPause, TRUE);
}
if (dwErr == 0)
cancel_notify(nPort, MCI_NOTIFY_ABORTED);
notify(LOWORD(dwErr), dwFlags, wDeviceID, lpGeneric, nPort);
return dwErr;
}
/****************************************************************************
* Spin the player up or down depending on dwFlag
***************************************************************************/
static DWORD PASCAL NEAR spinupdown(UINT wDeviceID, int nPort, DWORD dwFlag, BOOL bWait)
{
if (dwFlag & MCI_VD_SPIN_UP) {
if (IsDiscSpinning(wDeviceID, nPort) != 0) {
DWORD dwErr;
if (!bWait) {
comport[nPort].bPlayerBusy = TRUE;
comport[nPort].bDoorAction = TRUE;
}
if (putchars(wDeviceID, nPort, aszSpinUp, FALSE) != 0)
return MCIERR_HARDWARE;
if (bWait && (dwErr =
GetCompletionCode(wDeviceID, nPort, PIONEER_MAX_BUSY_TIME)) != 0)
return dwErr;
}
}
else if (dwFlag & MCI_VD_SPIN_DOWN) {
if (IsDiscSpinning(wDeviceID, nPort) == 0) {
comport[nPort].bPlayerBusy = TRUE;
comport[nPort].dwDirection = PION_PLAY_NO_DIRECTION;
cancel_notify(nPort, MCI_NOTIFY_ABORTED);
if (putchars(wDeviceID, nPort, aszReject, bWait) != 0)
return MCIERR_HARDWARE;
}
} else
return MCIERR_MISSING_PARAMETER;
return 0;
}
/****************************************************************************
* Process the MCI_SPIN message
***************************************************************************/
static DWORD PASCAL NEAR spin(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_GENERIC_PARMS lpGeneric)
{
DWORD dwErr;
dwErr = spinupdown(wDeviceID, nPort, dwFlags, FALSE);
if (dwErr != 0)
return dwErr;
if (comport[nPort].bPlayerBusy)
process_delay(wDeviceID, nPort, dwFlags, lpGeneric->dwCallback);
else
notify(0, dwFlags, wDeviceID, lpGeneric,
nPort);
return 0;
}
/****************************************************************************
* Process the MCI_STEP message
***************************************************************************/
static DWORD PASCAL NEAR step(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_VD_STEP_PARMS lpStep)
{
DWORD dwFrame, dwErr;
int n;
CHAR buf[VDISC_BUFFER_LENGTH];
DWORD dwMediaType;
if (dwFlags & MCI_VD_STEP_FRAMES) {
if (comport[nPort].dwTimeMode == NO_TIME_MODE)
set_time_mode(wDeviceID, nPort);
/* Must get current position and go from there */
if (putchars(wDeviceID, nPort, aszQueryFormat, FALSE) != 0)
return MCIERR_HARDWARE;
if ((n = getchars(wDeviceID, nPort, buf, 6, 0)) != 6
|| buf[0] == 'E')
goto step_error;
buf[n - 1] = '\0';
dwFrame = vdisc_atodw(buf);
if (dwFlags & MCI_VD_STEP_REVERSE)
dwFrame -= lpStep->dwFrames;
else
dwFrame += lpStep->dwFrames;
wsprintfA(buf, aszSeekToFormat, (UINT)dwFrame);
comport[nPort].bPlayerBusy = TRUE;
if (putchars(wDeviceID, nPort, buf, FALSE) != 0)
return MCIERR_HARDWARE;
process_delay(wDeviceID, nPort, dwFlags, lpStep->dwCallback);
return 0;
}
else {
if (dwFlags & MCI_VD_STEP_REVERSE)
dwErr = putchars(wDeviceID, nPort, aszStepReverse, TRUE);
else
dwErr = putchars(wDeviceID, nPort, aszStepForward, TRUE);
if (dwErr == 0)
return 0;
else
goto step_error;
}
step_error:
dwErr = get_media_type(wDeviceID, nPort, &dwMediaType);
if (dwErr)
return dwErr;
if (dwMediaType == MCI_VD_MEDIA_CLV)
return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
return MCIERR_HARDWARE;
}
/****************************************************************************
* Process the MCI_GETDEVCAPS message
***************************************************************************/
static DWORD PASCAL NEAR getdevcaps(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpCaps)
{
BOOL bCLV = FALSE;
DWORD dwMediaType;
/* Info is for CAV unless CLV specified or current disc is CLV */
if (dwFlags & MCI_VD_GETDEVCAPS_CLV)
bCLV = TRUE;
else if (!(dwFlags & MCI_VD_GETDEVCAPS_CAV) &&
!get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CLV))
bCLV = TRUE;
if (!(MCI_GETDEVCAPS_ITEM))
return MCIERR_MISSING_PARAMETER;
switch (lpCaps->dwItem) {
case MCI_GETDEVCAPS_CAN_RECORD:
case MCI_GETDEVCAPS_CAN_SAVE:
case MCI_GETDEVCAPS_USES_FILES:
case MCI_GETDEVCAPS_COMPOUND_DEVICE:
lpCaps->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
return MCI_RESOURCE_RETURNED;
case MCI_GETDEVCAPS_HAS_AUDIO:
case MCI_GETDEVCAPS_HAS_VIDEO:
case MCI_GETDEVCAPS_CAN_EJECT:
case MCI_GETDEVCAPS_CAN_PLAY:
lpCaps->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
return MCI_RESOURCE_RETURNED;
case MCI_VD_GETDEVCAPS_CAN_REVERSE:
if (bCLV)
lpCaps->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
else
lpCaps->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
return MCI_RESOURCE_RETURNED;
case MCI_GETDEVCAPS_DEVICE_TYPE:
lpCaps->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_VIDEODISC,
MCI_DEVTYPE_VIDEODISC);
return MCI_RESOURCE_RETURNED;
case MCI_VD_GETDEVCAPS_NORMAL_RATE:
lpCaps->dwReturn = CAV_FRAMES_PER_SECOND;
return 0;
case MCI_VD_GETDEVCAPS_SLOW_RATE:
case MCI_VD_GETDEVCAPS_CLV:
if (bCLV)
lpCaps->dwReturn = 0;
else
lpCaps->dwReturn = CAV_FRAMES_PER_SECOND / 3;
return 0;
case MCI_VD_GETDEVCAPS_FAST_RATE:
if (bCLV)
lpCaps->dwReturn = 0;
else
lpCaps->dwReturn = CAV_FRAMES_PER_SECOND * 3;
return 0;
}
return MCIERR_MISSING_PARAMETER;
}
/****************************************************************************
* Process the MCI_SET message
***************************************************************************/
static DWORD PASCAL NEAR set(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_SET_PARMS lpSet)
{
CHAR strCommand[4];
UINT wMask;
DWORD dwMediaType;
if (dwFlags & MCI_SET_AUDIO) {
if (dwFlags & MCI_SET_VIDEO)
return MCIERR_FLAGS_NOT_COMPATIBLE;
strCommand[0] = '0';
strCommand[1] = 'A';
strCommand[2] = 'D';
strCommand[3] = '\0';
if (lpSet->dwAudio == MCI_SET_AUDIO_LEFT)
wMask = 1;
else if (lpSet->dwAudio == MCI_SET_AUDIO_RIGHT)
wMask = 2;
else if (lpSet->dwAudio == MCI_SET_AUDIO_ALL)
wMask = 3;
else
return MCIERR_OUTOFRANGE;
if (dwFlags & MCI_SET_ON) {
if (dwFlags & MCI_SET_OFF)
return MCIERR_FLAGS_NOT_COMPATIBLE;
comport[nPort].wAudioChannels |= wMask;
}
else if (dwFlags & MCI_SET_OFF)
comport[nPort].wAudioChannels &= ~wMask;
else
return MCIERR_MISSING_PARAMETER;
strCommand[0] = (CHAR)(comport[nPort].wAudioChannels + '0');
putchars(wDeviceID, nPort, strCommand, TRUE);
}
else if (dwFlags & MCI_SET_TIME_FORMAT) {
switch (lpSet->dwTimeFormat) {
case MCI_FORMAT_MILLISECONDS:
comport[nPort].dwTimeMode = MCI_FORMAT_MILLISECONDS;
break;
case MCI_FORMAT_HMS:
comport[nPort].dwTimeMode = MCI_FORMAT_HMS;
break;
case MCI_FORMAT_FRAMES:
if (!get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CLV))
return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
comport[nPort].dwTimeMode = MCI_FORMAT_FRAMES;
break;
case MCI_VD_FORMAT_TRACK:
if (putchars(wDeviceID, nPort, aszCheck, TRUE) != 0)
return MCIERR_HARDWARE;
comport[nPort].dwTimeMode = MCI_VD_FORMAT_TRACK;
break;
default:
return MCIERR_BAD_TIME_FORMAT;
}
if (lpSet->dwTimeFormat != MCI_VD_FORMAT_TRACK) {
DWORD dwErr;
if ((dwErr = unset_chapter_mode(wDeviceID, nPort)) != 0)
return dwErr;
}
}
else if (dwFlags & MCI_SET_VIDEO) {
strCommand[1] = 'V';
strCommand[2] = 'D';
strCommand[3] = '\0';
if (dwFlags & MCI_SET_ON) {
if (dwFlags & MCI_SET_OFF)
return MCIERR_FLAGS_NOT_COMPATIBLE;
strCommand[0] = '1';
} else if (dwFlags & MCI_SET_OFF)
strCommand[0] = '0';
else
return MCIERR_MISSING_PARAMETER;
if (putchars(wDeviceID, nPort, strCommand, TRUE) != 0)
return MCIERR_HARDWARE;
}
else if (dwFlags & MCI_SET_DOOR_OPEN) {
if (putchars(wDeviceID, nPort, aszOpenDoor, FALSE) != 0)
return MCIERR_HARDWARE;
comport[nPort].bPlayerBusy = TRUE;
comport[nPort].bDoorAction = TRUE;
comport[nPort].dwTimeMode = NO_TIME_MODE;
process_delay(wDeviceID, nPort, dwFlags, lpSet->dwCallback);
return 0;
}
else if (dwFlags & MCI_SET_DOOR_CLOSED) {
/* Don't use spinupdown() because it won't work right for notification */
if (IsDiscSpinning(wDeviceID, nPort) != 0) {
comport[nPort].bPlayerBusy = TRUE;
comport[nPort].bDoorAction = TRUE;
if (putchars(wDeviceID, nPort, aszSpinUp, FALSE) != 0)
return MCIERR_HARDWARE;
process_delay(wDeviceID, nPort, dwFlags, lpSet->dwCallback);
return 0;
}
} else
return MCIERR_MISSING_PARAMETER;
notify(0, dwFlags, wDeviceID, (LPMCI_GENERIC_PARMS)lpSet, nPort);
return 0;
}
/****************************************************************************
* Process the MCI_ESCAPE message
***************************************************************************/
static DWORD PASCAL NEAR command(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_VD_ESCAPE_PARMS lpCommand)
{
DWORD dwErr, dwReturn = 0;
if (!(dwFlags & MCI_VD_ESCAPE_STRING) || lpCommand->lpstrCommand == NULL)
return MCIERR_MISSING_PARAMETER;
/* Turn off return codes -- this command has no return code */
if ((dwErr = putchars(wDeviceID, nPort, aszCommandOff, FALSE)) != 0)
return dwErr;
/* Send application's string */
if ((dwErr = putchars(wDeviceID, nPort, (LPSTR)lpCommand->lpstrCommand, FALSE))
!= 0)
dwReturn = dwErr;
/* Turn on return codes -- this command has a return code */
if ((dwErr = putchars(wDeviceID, nPort, aszCommandOn, TRUE)) != 0 &&
dwReturn == 0)
dwReturn = dwErr;
return dwReturn;
}
/****************************************************************************
* Process the MCI_VDISC_INDEX message
***************************************************************************/
static DWORD PASCAL NEAR index(UINT wDeviceID, int nPort, DWORD dwFlags)
{
return putchars(wDeviceID, nPort,
dwFlags & VDISC_FLAG_ON ? aszIndexOn : aszIndexOff, TRUE);
}
/****************************************************************************
* Process the MCI_VDISC_KEYLOCK message
***************************************************************************/
static DWORD PASCAL NEAR keylock(UINT wDeviceID, int nPort, DWORD dwFlags)
{
/* Ensure that one and only flag is set */
if (dwFlags & VDISC_FLAG_ON && dwFlags & VDISC_FLAG_OFF)
return MCIERR_FLAGS_NOT_COMPATIBLE;
if (!(dwFlags & (VDISC_FLAG_ON | VDISC_FLAG_OFF)))
return MCIERR_MISSING_PARAMETER;
return putchars(wDeviceID, nPort,
dwFlags & VDISC_FLAG_ON ? aszKeyLockOn : aszKeyLockOff,
TRUE);
}
/****************************************************************************
* Process the MCI_INFO message
***************************************************************************/
static DWORD PASCAL NEAR info(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_INFO_PARMS lpInfo)
{
DWORD dwErr;
if (dwFlags & MCI_INFO_PRODUCT) {
if (lpInfo->lpstrReturn == NULL ||
!LOWORD(lpInfo->dwRetSize))
dwErr = MCIERR_PARAM_OVERFLOW;
else {
UINT wReturnBufferLength;
wReturnBufferLength = LOWORD(lpInfo->dwRetSize);
*(lpInfo->lpstrReturn + wReturnBufferLength - 1) = '\0';
lpInfo->dwRetSize = LoadString(hInstance, IDS_PRODUCTNAME, lpInfo->lpstrReturn, wReturnBufferLength);
if (*(lpInfo->lpstrReturn + wReturnBufferLength - 1) != '\0')
dwErr = MCIERR_PARAM_OVERFLOW;
else
dwErr = 0;
}
} else
dwErr = MCIERR_MISSING_PARAMETER;
return dwErr;
}
/****************************************************************************
* Process the MCI_CLOSE message
***************************************************************************/
static void PASCAL NEAR close(UINT wDeviceID, int nPort)
{
DOUT("Closing...");
if (--comport[nPort].nUseCount == 0) {
DOUT("comport...");
vdisc_close(wDeviceID, nPort);
comport[nPort].nCommID = NO_COMPORT;
if (comport[nPort].bTimerSet) {
if (--nWaitingChannels == 0) {
KillTimer(NULL, wTimerID);
mciDriverNotify(comport[nPort].hCallback,
comport[nPort].wDeviceID,
MCI_NOTIFY_ABORTED);
}
}
}
}
/****************************************************************************
* Process all MCI specific message
***************************************************************************/
DWORD FAR PASCAL mciDriverEntry(UINT wDeviceID, UINT message, LPARAM lParam1, LPARAM lParam2)
{
int nCommID, nPort;
DWORD dwErr = 0;
LPMCI_GENERIC_PARMS lpGeneric = (LPMCI_GENERIC_PARMS)lParam2;
#if DBG
CHAR buf[100];
#endif
/* Catch these here to avoid a mandatory wait */
switch (message) {
case MCI_RECORD:
case MCI_LOAD:
case MCI_SAVE:
case MCI_RESUME:
return MCIERR_UNSUPPORTED_FUNCTION;
}
if (lpGeneric == NULL)
return MCIERR_NULL_PARAMETER_BLOCK;
/* Find the channel number given the device ID */
nPort = (UINT)mciGetDriverData(wDeviceID);
/* Serialize all access to this com port. When we yield we release this
cricical section */
EnterCrit(nPort);
/* Find the actual comm port handle */
nCommID = comport[nPort].nCommID;
#if DBG
wsprintfA(buf, "port=%d commid=%d", nPort, nCommID);
DOUT(buf);
#endif
/* If the device is busy then wait for completion before sending any commands */
if (message != MCI_OPEN_DRIVER) {
if (comport[nPort].bPlayerBusy) {
/* If the command is 'status mode' */
if (message == MCI_STATUS && (DWORD)lParam1 & MCI_STATUS_ITEM &&
((LPMCI_STATUS_PARMS)lParam2)->dwItem == MCI_STATUS_MODE) {
/* Seek if the device is done seeking */
if (GetCompletionCode(wDeviceID, nPort, 200) == 0) {
comport[nPort].bPlayerBusy = FALSE;
comport[nPort].bDoorAction = FALSE;
}
else {
/* If not then return MCI_MODE_SEEK */
UINT wMode;
wMode = (comport[nPort].bDoorAction? MCI_MODE_NOT_READY :
MCI_MODE_SEEK);
((LPMCI_STATUS_PARMS)lParam2)->dwReturn =
MAKEMCIRESOURCE(wMode, wMode);
notify(LOWORD(dwErr), (DWORD)lParam1, wDeviceID,
(LPMCI_GENERIC_PARMS)lParam2, nPort);
LeaveCrit(nPort);
return MCI_RESOURCE_RETURNED;
}
}
else {
/* Wait up to 25 seconds for the ongoing command to complete */
GetCompletionCode(wDeviceID, nPort, 25000);
comport[nPort].bPlayerBusy = FALSE;
}
}
/* If the device has not yet responded try to see if it's alive */
if (!comport[nPort].bResponding && message != MCI_CLOSE_DRIVER &&
message != MCI_GETDEVCAPS)
if (putchars(wDeviceID, nPort, aszKeyLockOn, TRUE) != 0) {
LeaveCrit(nPort);
return MCIERR_HARDWARE;
}
else {
comport[nPort].bResponding = TRUE;
init_player(wDeviceID, nPort);
}
}
/* These commands will abort notification or are otherwise strange */
switch (message) {
case MCI_PLAY:
dwErr = play(wDeviceID, nPort, (DWORD)lParam1, (MCI_VD_PLAY_PARMS FAR *)lParam2);
LeaveCrit(nPort);
return dwErr;
case MCI_SEEK:
dwErr = seek(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_SEEK_PARMS)lParam2);
LeaveCrit(nPort);
return dwErr;
case MCI_SET:
dwErr = set(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_SET_PARMS)lParam2);
LeaveCrit(nPort);
return dwErr;
case MCI_SPIN:
dwErr = spin(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_GENERIC_PARMS)lParam2);
LeaveCrit(nPort);
return dwErr;
case MCI_CLOSE_DRIVER:
close(wDeviceID, nPort);
notify(0, (DWORD)lParam1, wDeviceID, (LPMCI_GENERIC_PARMS)lParam2,
nPort);
LeaveCrit(nPort);
return 0;
case MCI_STOP:
/* Fall through */
case MCI_PAUSE:
dwErr = stop_pause(wDeviceID, nPort,
(DWORD)lParam1, lpGeneric, message);
LeaveCrit(nPort);
return dwErr;
}
/* These commands will NOT abort notification */
switch (message) {
case MCI_OPEN_DRIVER:
/* If the port is in use then shareable must be specified now and with */
/* the previous open */
if (comport[nPort].nUseCount != 0)
if (!((DWORD)lParam1 & MCI_OPEN_SHAREABLE) ||
!comport[nPort].bShareable) {
LeaveCrit(nPort);
return MCIERR_MUST_USE_SHAREABLE;
}
++comport[nPort].nUseCount;
if (comport[nPort].nCommID == NO_COMPORT)
dwErr = open(wDeviceID, nPort, (DWORD)lParam1);
break;
case VDISC_INDEX:
dwErr = index(wDeviceID, nPort, (DWORD)lParam1);
break;
case VDISC_KEYLOCK:
dwErr = keylock(wDeviceID, nPort, (DWORD)lParam1);
break;
case MCI_ESCAPE:
dwErr = command(wDeviceID, nPort, (DWORD)lParam1,
(LPMCI_VD_ESCAPE_PARMS)lParam2);
break;
/* The MCI_STEP message should really be in the above list of message */
/* which can abort notification. In this driver, MCI_STEP never */
/* aborts notification which is an error */
case MCI_STEP:
dwErr = step(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_VD_STEP_PARMS)lParam2);
break;
case MCI_GETDEVCAPS:
dwErr = getdevcaps(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_GETDEVCAPS_PARMS)lParam2);
break;
case MCI_STATUS:
dwErr = status(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_STATUS_PARMS)lParam2);
break;
case MCI_INFO:
dwErr = info(wDeviceID, nPort, (DWORD)lParam1,
(LPMCI_INFO_PARMS)lParam2);
break;
default:
dwErr = MCIERR_UNRECOGNIZED_COMMAND;
break;
} /* switch */
notify(LOWORD(dwErr), (DWORD)lParam1, wDeviceID, (LPMCI_GENERIC_PARMS)lParam2,
nPort);
LeaveCrit(nPort);
return dwErr;
}
/****************************************************************************
* Library exit function
***************************************************************************/
BOOL PASCAL FAR _LOADDS WEP(BOOL fSystemExit)
{
int n;
for (n = 0; n < PIONEER_MAX_COMPORTS; n++)
if (comport[n].nCommID != NO_COMPORT)
/* We use device id 0 which is OK because it's not used anyway
since the device id is only used for Yielding and we don't
yield during close */
vdisc_close(0, n);
return TRUE;
}
#ifdef WIN32
/****************************************************************************
* @doc EXTERNAL
*
* @api int | DllInstanceInit | Library initialization code.
*
* @parm HINSTANCE | hModule | Our instance handle.
*
* @parm ULONG | Reason | Reason for being called.
*
* @parm PCONTEXT | pContext | Context
*
* @rdesc Returns 1 if the initialization was successful and 0 otherwise.
***************************************************************************/
BOOL DllInstanceInit(PVOID hModule, ULONG Reason, PCONTEXT pContext)
{
UNREFERENCED_PARAMETER(pContext);
if (Reason == DLL_PROCESS_ATTACH) {
int i;
for (i = 0; i < PIONEER_MAX_COMPORTS; i++) {
InitializeCriticalSection(&comport[i].DeviceCritSec);
}
return LibMain(hModule, 0, NULL);
} else {
if (Reason == DLL_PROCESS_DETACH) {
return WEP(FALSE);
} else {
return TRUE;
}
}
}
/****************************************************************************
* @doc INTERNAL
*
* @api UINT | pionDriverYield | Yield or yield simulation.
*
* @parm UINT | wDeviceId | Device id yielding.
*
* @parm UINT | nPort | logical device yielding.
*
* @rdesc Returns code returned by mciDriverYield.
*
***************************************************************************/
UINT pionDriverYield(UINT wDeviceId, UINT nPort)
{
UINT rc;
LeaveCrit(nPort);
rc = mciDriverYield(wDeviceId);
/* Let someone else have a go */
Sleep(10);
EnterCrit(nPort);
return rc;
}
#endif /* WIN32 */