/**************************************************************************** * * 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 #include #include #include #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") * or an error ("E0x"). * * @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 " 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 " is the command * * Otherwise, if "play to " 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" ***************************************************************************/ 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 */