mirror of https://github.com/tongzx/nt5src
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
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 */
|