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

2483 lines
82 KiB

  1. /****************************************************************************
  2. *
  3. * mcipionr.c
  4. *
  5. * Copyright (c) 1991-1993 Microsoft Corporation. All Rights Reserved
  6. *
  7. * MCI Device Driver for the Pioneer 4200 Videodisc Player
  8. *
  9. * MCI Module - MCI commands and interface to device
  10. *
  11. ***************************************************************************/
  12. /* Known problems in this driver:
  13. *
  14. * 1) Play succeeds with no disc in the drive
  15. * 2) The command 'spin down' does not notify when completed
  16. * 3) The first 'status mode' after a 'spin down' followed by a
  17. * 'seek' to an invalid adress can return 'play' instead of
  18. * 'stopped'
  19. * 4) A 'spin down notify' command followed by a 'play' command
  20. * will return MCI_NOTIFY_FAILURE instead of MCI_NOTIFY_ABORTED
  21. * 5) The first 'status time format' command sent after a new
  22. * disk is inserted can result in a return value of '0' instead
  23. * of a legal value like MCI_FORMAT_HMS
  24. * 6) Multi-process device sharing for Windows NT is not addressed.
  25. * The sharing will work correctly for 16-bit applications.
  26. */
  27. #define USECOMM
  28. #include <windows.h>
  29. #include <windowsx.h>
  30. #include <mmsystem.h>
  31. #include <mmddk.h>
  32. #include "mcipionr.h"
  33. #include "comm.h"
  34. /* Maximum number of supported comports */
  35. #define PIONEER_MAX_COMPORTS 4
  36. /* Time in milliseconds that the driver will normally wait for the device */
  37. /* to return data */
  38. #define PIONEER_READCHAR_TIMEOUT 1000 /* Minimum for 10Mhz 286 */
  39. /* Time in milliseconds that the driver will wait for the device to return */
  40. /* data when the device is busy (i.e. seeking) */
  41. #define PIONEER_MAX_BUSY_TIME 30000
  42. /* Maximum length of the videodisc's buffer */
  43. #define VDISC_BUFFER_LENGTH 20
  44. /* Macro to determine if the given videodisc frame is valid */
  45. #define VALID_FRAME(n) ((DWORD)(n) <= (DWORD)99999)
  46. /* Maximum number of frames on a CAV disc */
  47. #define CAV_MAX_DISC_FRAMES 54000
  48. /* Frame rate of a CAV disc */
  49. #define CAV_FRAMES_PER_SECOND 30
  50. /* Flags a to position as invalid */
  51. #define NO_TO_POSITION 0xFFFFFFFF
  52. /* Flags a time mode as invalid */
  53. #define NO_TIME_MODE 0xFFFFFFFF
  54. /* Flags a comport as invalid */
  55. #define NO_COMPORT -1
  56. /* Internal play directions */
  57. /* Set at least two bits so they'll never equal MCI_PLAY_VD_REVERSE */
  58. #define PION_PLAY_FORWARD 3
  59. #define PION_PLAY_NO_DIRECTION 7
  60. /* Polling period in milliseconds used for setting timer */
  61. #define TIMER_POLLING_PERIOD 100
  62. /* Convert an HMS address to Seconds */
  63. #define HMSTOSEC(hms) (MCI_HMS_HOUR(hms) * 3600 + MCI_HMS_MINUTE(hms) * 60 + \
  64. MCI_HMS_SECOND(hms))
  65. /* Difference in seconds between two HMS addresses */
  66. #define HMS_DIFF(h2, h1) (HMSTOSEC(h2) - HMSTOSEC(h1))
  67. /* Absolute value macro */
  68. #define abs(a) ((a) < 0 ? -(a) : (a))
  69. #ifdef WIN32
  70. #define AnsiUpperChar(c) ((TCHAR)CharUpper((LPTSTR)(DWORD)(c)))
  71. #else
  72. #define wsprintfA wsprintf
  73. #define lstrlenA lstrlen
  74. #define AnsiUpperChar(c) ((char)(WORD)(DWORD)AnsiUpper((LPSTR)(DWORD)(WORD)(char)(c)))
  75. #endif /* WIN32 */
  76. /* Module instance handle */
  77. HINSTANCE hInstance;
  78. /* ID of the timer used for polling */
  79. static int wTimerID;
  80. /* Number of channels which are using the timer */
  81. static int nWaitingChannels;
  82. /* Set to FALSE inside TimerProc to prevent re-entrant disasters when */
  83. /* trying to yield */
  84. static BOOL bYieldWhenReading = TRUE;
  85. /* Forward declarations */
  86. static DWORD PASCAL NEAR spinupdown(UINT wDeviceID, int nPort, DWORD dwFlag, BOOL bWait);
  87. static DWORD PASCAL NEAR status(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_STATUS_PARMS lpStatus);
  88. /* Data for each comm port - Port 0 is com1, port 1 is com2, etc... */
  89. static struct {
  90. int nCommID; /* The ID returned by OpenComm */
  91. UINT Rate; /* Baud rate */
  92. HWND hCallback; /* Handle to window function to call back */
  93. BOOL bPlayerBusy; /* TRUE if the player is seeking or transitioning */
  94. /* between park and random access mode */
  95. BOOL bDoorAction; /* TRUE if door opening or closing */
  96. BOOL bPlayTo; /* TRUE if playing to a particular frame */
  97. DWORD dwPlayTo; /* The frame being played to */
  98. DWORD dwToTimeMode; /* Time mode of the dwPlayTo position */
  99. UINT wAudioChannels; /* Bit 0 is ch1 status, bit 1 is ch2 */
  100. BOOL bTimerSet; /* TRUE when there is a timer for this channel */
  101. DWORD dwTimeMode; /* One of MCI_FORMAT_MILLISECONDS, */
  102. /* MCI_FORMAT_HMS or */
  103. /* MCI_FORMAT_FRAMES or */
  104. /* MCI_VD_FORMAT_TRACK */
  105. BOOL bCAVDisc; /* True if a CAV disc is inserted */
  106. UINT wDeviceID; /* MCI device ID */
  107. int nUseCount;
  108. BOOL bShareable;
  109. BOOL bResponding;
  110. DWORD dwDirection; /* The current direction. One of: */
  111. /* PION_PLAY_FORWARD */
  112. /* PION_PLAY_NO_DIRECTION */
  113. /* MCI_VD_PLAY_REVERSE */
  114. DWORD dwBusyStart; /* The time the timer loop started waiting */
  115. #ifdef WIN32
  116. CRITICAL_SECTION
  117. DeviceCritSec; /* Only one person accesses a device at a time */
  118. #endif /* WIN32 */
  119. } comport[PIONEER_MAX_COMPORTS];
  120. #if DBG
  121. /* Amount of information (if any) to dump out the debug port */
  122. static UINT wDebugLevel = 0;
  123. #endif
  124. #ifdef WIN32
  125. #define SZCODE CHAR
  126. #define SZTCODE TCHAR
  127. #else
  128. #define SZCODE CHAR _based(_segname("_CODE"))
  129. #define SZTCODE TCHAR _based(_segname("_CODE"))
  130. #endif /* WIN32 */
  131. static SZTCODE aszComm[] = TEXT("com");
  132. static SZTCODE aszCommOpenFormat[] = TEXT("%s%1d");
  133. static SZTCODE aszCommSetupFormat[] = TEXT("%s%1d:%d,n,8,1");
  134. static SZCODE aszFrameFormat[] = "%lu";
  135. static SZCODE aszTrackFormat[] = "%u";
  136. static SZCODE aszHMSFormat[] = "%1u%2u%2u";
  137. static SZCODE aszCLVLength[] = "10000";
  138. static SZCODE aszNull[] = "";
  139. static SZCODE aszQueryPlaying[] = "?P";
  140. static SZCODE aszQueryMedia[] = "?D";
  141. static SZCODE aszQueryTrack[] = "?C";
  142. static SZCODE aszQueryFormat[] = "?F";
  143. static SZCODE aszQueryTime[] = "?T";
  144. static SZCODE aszFormat[] = "FR";
  145. static SZCODE aszTime[] = "TM";
  146. static SZCODE aszCheck[] = "CH";
  147. static SZCODE aszClose[] = "0KL";
  148. static SZCODE aszAudioOn[] = "3AD";
  149. static SZCODE aszSpinUp[] = "SA";
  150. static SZCODE aszStopMarker[] = "SM";
  151. static SZCODE aszSeekTo[] = "SE";
  152. static SZCODE aszSeekToFormat[] = "%uSE";
  153. static SZCODE aszSeekStart[] = "0SE";
  154. static SZCODE aszSeekEnd[] = "99999SE";
  155. static SZCODE aszSeekSetSpeed[] = "255SP";
  156. static SZCODE aszFastSetSpeed[] = "180SP";
  157. static SZCODE aszSlowSetSpeed[] = "20SP";
  158. static SZCODE aszMediumSetSpeed[] = "60SP";
  159. static SZCODE aszSetSpeedFormat[] = "%uSP";
  160. static SZCODE aszMediaReverse[] = "MR";
  161. static SZCODE aszMediaForward[] = "MF";
  162. static SZCODE aszPlay[] = "PL";
  163. static SZCODE aszPause[] = "PA";
  164. static SZCODE aszStop[] = "ST";
  165. static SZCODE aszReject[] = "RJ";
  166. static SZCODE aszStepReverse[] = "SR";
  167. static SZCODE aszStepForward[] = "SF";
  168. static SZCODE aszOpenDoor[] = "OP";
  169. static SZCODE aszCommandOff[] = "2CM";
  170. static SZCODE aszCommandOn[] = "3CM";
  171. static SZCODE aszIndexOff[] = "0DS";
  172. static SZCODE aszIndexOn[] = "1DS";
  173. static SZCODE aszKeyLockOff[] = "0KL";
  174. static SZCODE aszKeyLockOn[] = "1KL";
  175. /****************************************************************************
  176. * @doc INTERNAL MCI
  177. *
  178. * @api UINT | getchars | Read "n" characters into "buf". Wait up to
  179. * PIONEER_READCHAR_TIMEOUT milliseconds.
  180. *
  181. * @parm int | nPort | Port number.
  182. *
  183. * @parm LPSTR | lpstrBuf | Buffer to fill.
  184. *
  185. * @parm int | n | Number of characters to read.
  186. *
  187. * @parm UINT | wWait | Number of milliseconds to wait before timing out
  188. * If 0 then wait for PIONEER_READCHAR_TIMEOUT milliseconds.
  189. *
  190. * @rdesc Number of characters actually read.
  191. ***************************************************************************/
  192. static UINT PASCAL NEAR getchars(UINT wDeviceID, int nPort, LPSTR lpstrBuf, int n, UINT wWait)
  193. {
  194. int nRead, FirstTry = TRUE;
  195. DWORD dwTime0, dwTime;
  196. int nToRead = n;
  197. int nCommID = comport[nPort].nCommID;
  198. #if DBG
  199. LPSTR lpstrStart = lpstrBuf;
  200. #endif
  201. if (wWait == 0)
  202. wWait = PIONEER_READCHAR_TIMEOUT;
  203. while (n > 0) {
  204. /* Try to read the number of characters that are left to read */
  205. nRead = ReadComm(nCommID, lpstrBuf, n);
  206. /* Some characters were read */
  207. if (nRead > 0) {
  208. n -= nRead;
  209. lpstrBuf += nRead;
  210. }
  211. else {
  212. /* Either 0 characters read or an error occured */
  213. if (nRead < 0) {
  214. DOUT("mcipionr: Comm error");
  215. return MCIERR_HARDWARE;
  216. }
  217. if (nRead == 0) {
  218. COMSTAT comstat;
  219. if (GetCommError(nCommID, &comstat) != 0) {
  220. DOUT("mcipionr: Comm error");
  221. return MCIERR_HARDWARE;
  222. }
  223. }
  224. }
  225. /* If not all characters were read */
  226. if (n > 0) {
  227. /* If first failure then initialize time base */
  228. if (FirstTry) {
  229. dwTime0 = GetCurrentTime();
  230. FirstTry = FALSE;
  231. }
  232. /* If subsequent failure then check for timeout */
  233. else {
  234. dwTime = GetCurrentTime();
  235. /* Check timer rollover case */
  236. if (dwTime < dwTime0 &&
  237. (dwTime + (0xFFFFFFFF - dwTime0)) > wWait) {
  238. DOUT("mcipionr: getchars timeout!");
  239. break;
  240. }
  241. /* Normal case */
  242. if (dwTime - dwTime0 > wWait) {
  243. DOUT("mcipionr: getchars timeout!");
  244. break;
  245. }
  246. if (bYieldWhenReading)
  247. pionDriverYield(wDeviceID, nPort);
  248. #ifdef WIN32
  249. Sleep(10);
  250. #endif /* WIN32 */
  251. }
  252. }
  253. }
  254. #if DBG
  255. {
  256. CHAR temp[50];
  257. LPSTR lpstrTemp = temp;
  258. nRead = nToRead - n;
  259. if (nRead > sizeof(temp) / sizeof(CHAR) - 1)
  260. nRead = sizeof(temp) / sizeof(CHAR) - 1;
  261. lpstrBuf = lpstrStart;
  262. while (nRead-- > 0)
  263. *lpstrTemp++ = *lpstrBuf++;
  264. *lpstrTemp = '\0';
  265. DOUT("MCIPIONR received:");
  266. DOUTX(temp);
  267. }
  268. #endif
  269. return nToRead - n;
  270. }
  271. /****************************************************************************
  272. * @doc INTERNAL MCI
  273. *
  274. * @api int | GetCompletionCode | Read either a completion code ("R<CR>")
  275. * or an error ("E0x<CR>").
  276. *
  277. * @parm UINT | wDeviceID | Device ID to use.
  278. *
  279. * @parm int | nPort | Port number.
  280. *
  281. * @parm UINT | wWait | Number of milliseconds to wait before timing out
  282. * If 0 then wait for PIONEER_READCHAR_TIMEOUT milliseconds.
  283. *
  284. * @rdesc Returns zero if no error.
  285. ***************************************************************************/
  286. static int PASCAL NEAR GetCompletionCode(UINT wDeviceID, int nPort, UINT wWait)
  287. {
  288. CHAR buf[4]; /* This must be larger than 2 because of Win 3.1 Comm bug */
  289. DOUT("Get completion:");
  290. if (getchars(wDeviceID, nPort, buf, 2, wWait) != 2)
  291. return MCIERR_HARDWARE;
  292. if (buf[0] == 'E') {
  293. /* Empty the buffer of the error condition */
  294. getchars(wDeviceID, nPort, buf, 2, wWait);
  295. comport[nPort].bPlayerBusy = FALSE;
  296. return MCIERR_HARDWARE;
  297. }
  298. DOUT("True");
  299. return 0;
  300. }
  301. /****************************************************************************
  302. * @doc INTERNAL MCI
  303. *
  304. * @api static int | putchars | Send "n" characters from "lpstrS" to the
  305. * port specified by "nPort".
  306. *
  307. * @parm UINT | wDeviceID | Device ID to use.
  308. *
  309. * @parm int | nPort | The port number.
  310. *
  311. * @parm LPSTR | lpstrS | The string to send.
  312. *
  313. * @parm int | bReturn | If TRUE then the function waits for a
  314. * completion code (or an error) to be returned or until a timeout
  315. * (PIONEER_GETCHAR_TIMEOUT msecs). The timeout generates an error.
  316. *
  317. * @rdesc Returns zero if no error.
  318. ***************************************************************************/
  319. static int PASCAL NEAR putchars(UINT wDeviceID, int nPort, LPSTR lpstrS, int bReturn)
  320. {
  321. int nCommID = comport[nPort].nCommID;
  322. static CHAR c = 0xd;
  323. int n = 0;
  324. #if DBG
  325. DOUT("MCIPIONR sent:");
  326. DOUTX(lpstrS);
  327. #endif
  328. n = lstrlenA(lpstrS);
  329. /* Send string to player */
  330. if (WriteComm(nCommID, lpstrS, n) != n ||
  331. WriteComm(nCommID, &c, 1) != 1) {
  332. DOUT("mcipionr: Hardware error on output");
  333. return MCIERR_HARDWARE;
  334. }
  335. if (bReturn)
  336. /* Get completion code */
  337. return GetCompletionCode(wDeviceID, nPort, 0);
  338. else
  339. return 0;
  340. }
  341. /****************************************************************************
  342. * Get player status - Returns 1 if it is PLAY or MULTI-SPEED, -1 if there
  343. * is a hardware error, and 0 otherwise.
  344. ***************************************************************************/
  345. static int PASCAL NEAR IsPlaying(UINT wDeviceID, int nPort)
  346. {
  347. CHAR buf[8];
  348. putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
  349. if (getchars(wDeviceID, nPort, buf, 4, 0) != 4)
  350. return -1;
  351. return buf[0] == 'P' && (buf[2] == '4' || buf[2] == '9');
  352. }
  353. /****************************************************************************
  354. * @doc INTERNAL MCI
  355. *
  356. * @api void | cancel_notify | Cancel any active notification for this port.
  357. *
  358. * @parm int | nPort | The port number to check.
  359. *
  360. * @parm UINT | wStatus | The notification status to return.
  361. *
  362. * @rdesc There is no return value.
  363. ***************************************************************************/
  364. static void PASCAL NEAR cancel_notify(int nPort, UINT wStatus)
  365. {
  366. if (comport[nPort].bTimerSet) {
  367. comport[nPort].bTimerSet = FALSE;
  368. if (--nWaitingChannels <= 0)
  369. KillTimer(NULL, wTimerID);
  370. mciDriverNotify(comport[nPort].hCallback,
  371. comport[nPort].wDeviceID, wStatus);
  372. }
  373. }
  374. /****************************************************************************
  375. * Check if the disc is spinning and return 0 if it is
  376. ***************************************************************************/
  377. static DWORD PASCAL NEAR IsDiscSpinning(UINT wDeviceID, UINT nPort)
  378. {
  379. CHAR buf[8];
  380. putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
  381. if (getchars(wDeviceID, nPort, buf, 4, 0) != 4)
  382. return MCIERR_HARDWARE;
  383. if (buf[0] != 'P') {
  384. DOUT("mcipionr: bad mode info from player");
  385. return MCIERR_HARDWARE;
  386. }
  387. if (buf[1] != '0')
  388. return MCIERR_PIONEER_NOT_SPINNING;
  389. if (buf[2] == '0' || buf[2] == '1')
  390. return MCIERR_PIONEER_NOT_SPINNING;
  391. else
  392. return 0;
  393. }
  394. /****************************************************************************
  395. * Get the media type
  396. ***************************************************************************/
  397. /* If a CLV disc has time mode set to FRAMES, set the time mode to HMS */
  398. /* because frames are illegal for CLV. */
  399. static DWORD PASCAL NEAR get_media_type(
  400. UINT wDeviceID,
  401. UINT nPort,
  402. DWORD FAR* pdwMediaType)
  403. {
  404. CHAR buf[VDISC_BUFFER_LENGTH];
  405. putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
  406. if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
  407. return MCIERR_HARDWARE;
  408. if (buf[0] != '0') {
  409. if (buf[1] == '0') {
  410. comport[nPort].bCAVDisc = TRUE;
  411. *pdwMediaType = MCI_VD_MEDIA_CAV;
  412. return 0;
  413. }
  414. if (buf[1] == '1') {
  415. comport[nPort].bCAVDisc = FALSE;
  416. if (comport[nPort].dwTimeMode == MCI_FORMAT_FRAMES)
  417. comport[nPort].dwTimeMode = MCI_FORMAT_HMS;
  418. *pdwMediaType = MCI_VD_MEDIA_CLV;
  419. return 0;
  420. }
  421. }
  422. return MCIERR_PIONEER_NOT_SPINNING;
  423. }
  424. /****************************************************************************
  425. * Set the proper default time mode for the currently loaded media type
  426. ***************************************************************************/
  427. static void PASCAL NEAR set_time_mode(UINT wDeviceID, UINT nPort)
  428. {
  429. DWORD dwMediaType;
  430. if (!get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CAV)) {
  431. comport[nPort].dwTimeMode = MCI_FORMAT_FRAMES;
  432. putchars(wDeviceID, nPort, aszFormat, TRUE);
  433. } else {
  434. comport[nPort].dwTimeMode = MCI_FORMAT_HMS;
  435. putchars(wDeviceID, nPort, aszTime, TRUE);
  436. }
  437. }
  438. /****************************************************************************
  439. * See if the player has arrived at the proper 'to' position
  440. ***************************************************************************/
  441. static BOOL PASCAL NEAR check_arrival(int nPort)
  442. {
  443. MCI_STATUS_PARMS Status;
  444. BOOL bWasTracks = FALSE, bResult = TRUE;
  445. if (comport[nPort].dwPlayTo == NO_TO_POSITION)
  446. return TRUE;
  447. if (comport[nPort].dwTimeMode == MCI_VD_FORMAT_TRACK &&
  448. comport[nPort].dwToTimeMode != MCI_VD_FORMAT_TRACK) {
  449. bWasTracks = TRUE;
  450. set_time_mode(comport[nPort].wDeviceID, nPort);
  451. }
  452. Status.dwItem = MCI_STATUS_POSITION;
  453. if (LOWORD(status(comport[nPort].wDeviceID, nPort, MCI_STATUS_ITEM,
  454. (LPMCI_STATUS_PARMS)&Status)) != 0) {
  455. bResult = FALSE;
  456. goto reset_mode;
  457. }
  458. switch (comport[nPort].dwTimeMode) {
  459. case MCI_FORMAT_HMS:
  460. { int n;
  461. n = HMS_DIFF(Status.dwReturn, comport[nPort].dwPlayTo);
  462. if (abs(n) > 2)
  463. bResult = FALSE;
  464. break;
  465. }
  466. case MCI_FORMAT_FRAMES:
  467. if (abs((long)Status.dwReturn - (long)comport[nPort].dwPlayTo) > 2)
  468. bResult = FALSE;
  469. break;
  470. case MCI_FORMAT_MILLISECONDS:
  471. if (abs((long)Status.dwReturn - (long)comport[nPort].dwPlayTo) > 2000)
  472. bResult = FALSE;
  473. break;
  474. case MCI_VD_FORMAT_TRACK:
  475. if (abs((long)Status.dwReturn - (long)comport[nPort].dwPlayTo) > 1)
  476. bResult = FALSE;
  477. break;
  478. default:
  479. bResult = FALSE;
  480. break;
  481. }
  482. reset_mode:;
  483. if (bWasTracks) {
  484. putchars(comport[nPort].wDeviceID, nPort, aszCheck, TRUE);
  485. comport[nPort].dwTimeMode = MCI_VD_FORMAT_TRACK;
  486. }
  487. return bResult;
  488. }
  489. /****************************************************************************
  490. * The timer is active if any comm port controlled by this driver is
  491. * waiting to reach a point on the disc (comport[nPort].bPlayTo == TRUE)
  492. * or if it is seeking or spinning up or down
  493. * (comport[nPort].bBusy == TRUE) AND if "notify <x>" was specified for
  494. * the command
  495. ***************************************************************************/
  496. void FAR PASCAL _LOADDS TimerProc(HWND hwnd, UINT uMessage, UINT uTimer, DWORD dwParam)
  497. {
  498. int nCommID;
  499. int nPort;
  500. bYieldWhenReading = FALSE;
  501. /* Loop through all channels */
  502. for (nPort = 0; nPort < PIONEER_MAX_COMPORTS; LeaveCrit(nPort), nPort++) {
  503. /* Serialize access to this device */
  504. EnterCrit(nPort);
  505. nCommID = comport[nPort].nCommID;
  506. /* If this channel is not waiting, skip it */
  507. if (comport[nPort].bPlayerBusy) {
  508. int nRetCode;
  509. nRetCode = GetCompletionCode(comport[nPort].wDeviceID, nPort, 0);
  510. /* If ok, or got valid error return from port */
  511. if (!nRetCode || !comport[nPort].bPlayerBusy) {
  512. comport[nPort].dwBusyStart = 0;
  513. comport[nPort].bPlayerBusy = FALSE;
  514. /* Unless the channel is waiting for a frame to be reached (play to x) */
  515. /* notify the application */
  516. if (!comport[nPort].bPlayTo)
  517. cancel_notify(nPort, nRetCode == 0 ?
  518. MCI_NOTIFY_SUCCESSFUL :
  519. MCI_NOTIFY_FAILURE);
  520. }
  521. else {
  522. /* No completion code so skip play */
  523. if (GetCommError(nCommID, NULL) != 0)
  524. cancel_notify(nPort, MCI_NOTIFY_FAILURE);
  525. if (comport[nPort].dwBusyStart != 0) {
  526. if (GetCurrentTime() >
  527. comport[nPort].dwBusyStart + PIONEER_MAX_BUSY_TIME)
  528. cancel_notify(nPort, MCI_NOTIFY_FAILURE);
  529. } else
  530. comport[nPort].dwBusyStart = GetCurrentTime();
  531. continue;
  532. }
  533. }
  534. if (comport[nPort].bPlayTo) {
  535. int nPlay;
  536. if ((nPlay = IsPlaying(comport[nPort].wDeviceID, nPort)) == 0)
  537. cancel_notify(nPort, check_arrival(nPort) ?
  538. MCI_NOTIFY_SUCCESSFUL :
  539. MCI_NOTIFY_FAILURE);
  540. else if (nPlay == -1)
  541. cancel_notify(nPort, MCI_NOTIFY_FAILURE);
  542. }
  543. }
  544. bYieldWhenReading = TRUE;
  545. }
  546. /****************************************************************************
  547. * @doc INTERNAL
  548. *
  549. * @api int | LibMain | Library initialization code.
  550. *
  551. * @parm HINSTANCE | hModule | Our instance handle.
  552. *
  553. * @parm UINT | cbHeap | The heap size from the .def file.
  554. *
  555. * @parm LPCSTR | lpszCmdLine | The command line.
  556. *
  557. * @rdesc Returns 1 if the initialization was successful and 0 otherwise.
  558. ***************************************************************************/
  559. int PASCAL NEAR LibMain(HINSTANCE hModule, UINT cbHeap, LPCSTR lpszCmdLine)
  560. {
  561. int nPort;
  562. hInstance = hModule;
  563. /* Port 0 is com1, port 1 is com2, etc... */
  564. for (nPort = 0; nPort < PIONEER_MAX_COMPORTS; nPort++)
  565. comport[nPort].nCommID = NO_COMPORT;
  566. #if DBG
  567. wDebugLevel = GetProfileInt(TEXT("mmdebug"), TEXT("mcipionr"), wDebugLevel);
  568. #endif
  569. return TRUE;
  570. }
  571. /****************************************************************************
  572. * Compare up to "n" characters in two strings. Comparison is case insensitive.
  573. * Returns 0 if they match, and non-zero otherwise.
  574. ***************************************************************************/
  575. static UINT PASCAL NEAR vdisc_lstrncmp_nocase(LPTSTR lpS1, LPTSTR lpS2, int n)
  576. {
  577. while (*lpS1 && *lpS2 && n--)
  578. {
  579. if (AnsiUpperChar(*lpS1) != AnsiUpperChar(*lpS2))
  580. break;
  581. lpS1++;
  582. lpS2++;
  583. }
  584. return n;
  585. }
  586. /****************************************************************************
  587. * Send a successful notify if wErr is 0 and the MCI_NOTIFY flag is set
  588. * and supersede any active notification
  589. ***************************************************************************/
  590. static void PASCAL NEAR notify(UINT wErr, DWORD dwFlags, UINT wDeviceID, LPMCI_GENERIC_PARMS lpParms, int nPort)
  591. {
  592. if (wErr == 0 && dwFlags & MCI_NOTIFY) {
  593. cancel_notify(nPort, MCI_NOTIFY_SUPERSEDED);
  594. mciDriverNotify((HWND)(UINT)lpParms->dwCallback, wDeviceID,
  595. MCI_NOTIFY_SUCCESSFUL);
  596. }
  597. }
  598. /****************************************************************************
  599. * Process the MCI_NOTIFY and MCI_WAIT flags
  600. *
  601. * If MCI_NOTIFY then start the timer going (if not started)
  602. *
  603. * If MCI_WAIT then wait until completion if seeking or until the
  604. * player is stopped if "play to <x>" is the command
  605. *
  606. * Otherwise, if "play to <x>" was the command then just clear the
  607. * bPlayTo flag
  608. ***************************************************************************/
  609. static DWORD PASCAL NEAR process_delay(UINT wDeviceID, int nPort, DWORD dwFlags, DWORD dwCb)
  610. {
  611. int nCommID;
  612. nCommID = comport[nPort].nCommID;
  613. if (dwFlags & MCI_WAIT) {
  614. if (comport[nPort].bPlayerBusy) {
  615. comport[nPort].bPlayerBusy = FALSE;
  616. if (GetCompletionCode(wDeviceID, nPort, PIONEER_MAX_BUSY_TIME)
  617. != 0) {
  618. if (dwFlags & MCI_NOTIFY)
  619. mciDriverNotify((HANDLE)dwCb, wDeviceID,
  620. MCI_NOTIFY_FAILURE);
  621. return MCIERR_HARDWARE;
  622. }
  623. }
  624. /* if (comport[nPort].bPlayTo) */
  625. {
  626. int nPlay;
  627. while ((nPlay = IsPlaying(wDeviceID, nPort)) == 1) {
  628. /* If the operation should be aborted */
  629. if (pionDriverYield(wDeviceID, nPort) != 0)
  630. return process_delay(wDeviceID, nPort, dwFlags & ~MCI_WAIT, dwCb);
  631. #ifdef WIN32
  632. Sleep(10);
  633. #endif /* WIN32 */
  634. }
  635. comport[nPort].bPlayTo = FALSE;
  636. if (nPlay == -1) {
  637. if (dwFlags & MCI_NOTIFY)
  638. mciDriverNotify((HANDLE)dwCb,
  639. wDeviceID, MCI_NOTIFY_FAILURE);
  640. return MCIERR_HARDWARE;
  641. }
  642. }
  643. if (dwFlags & MCI_NOTIFY)
  644. mciDriverNotify((HANDLE)dwCb, wDeviceID, MCI_NOTIFY_SUCCESSFUL);
  645. }
  646. else if (dwFlags & MCI_NOTIFY && (HANDLE)dwCb != NULL) {
  647. comport[nPort].hCallback = (HANDLE)dwCb;
  648. if (!comport[nPort].bTimerSet) {
  649. comport[nPort].bTimerSet = TRUE;
  650. comport[nPort].wDeviceID = wDeviceID;
  651. if (nWaitingChannels++ == 0)
  652. if ((wTimerID = SetTimer(NULL, 1, TIMER_POLLING_PERIOD,
  653. (TIMERPROC)TimerProc)) == 0)
  654. return MCIERR_PIONEER_NO_TIMERS;
  655. }
  656. } else
  657. comport[nPort].bPlayTo = FALSE;
  658. return 0;
  659. }
  660. /****************************************************************************
  661. * Concatenate lpIN onto lpOut but don't exceed wLen in length, including
  662. * terminating null. Returns the total length not including the terminating
  663. * null or 0 on error or overflow.
  664. ***************************************************************************/
  665. static UINT PASCAL NEAR catstring(LPSTR lpOut, LPSTR lpIn, int nLen)
  666. {
  667. int nSize = 0;
  668. if (lpOut == NULL || lpIn == NULL)
  669. return 0;
  670. /* search to end of lpOut */
  671. while (*lpOut != '\0') {
  672. ++lpOut;
  673. ++nSize;
  674. }
  675. /* concatenate */
  676. while (nSize++ < nLen && *lpIn != '\0')
  677. *lpOut++ = *lpIn++;
  678. *lpOut = '\0';
  679. if (*lpIn != '\0')
  680. return 0;
  681. return nSize - 1;
  682. }
  683. /****************************************************************************
  684. * Convert the input string to a DWORD
  685. ***************************************************************************/
  686. static DWORD PASCAL NEAR vdisc_atodw(LPSTR lpstrInput)
  687. {
  688. DWORD dwRet = 0;
  689. while (*lpstrInput >= '0' && *lpstrInput <= '9')
  690. dwRet = dwRet * 10 + *lpstrInput++ - '0';
  691. return dwRet;
  692. }
  693. /****************************************************************************
  694. * Shut down the device and release the com port
  695. ***************************************************************************/
  696. static void PASCAL NEAR vdisc_close(UINT wDeviceID, int nPort)
  697. {
  698. int nCommID = comport[nPort].nCommID;
  699. #if DBG
  700. CHAR buf[100];
  701. wsprintfA(buf, "port=%d commid=%d", nPort, nCommID);
  702. DOUT(buf);
  703. #endif
  704. DOUT("vdisc_close");
  705. if (nCommID != NO_COMPORT) {
  706. /* Unlock front panel */
  707. DOUT("unlock");
  708. /* Don't allow a yield because auto-close will be messed up */
  709. bYieldWhenReading = FALSE;
  710. putchars(wDeviceID, nPort, aszClose, TRUE);
  711. bYieldWhenReading = TRUE;
  712. DOUT("CloseComm");
  713. CloseComm(nCommID);
  714. }
  715. }
  716. /****************************************************************************
  717. * Switch to frame or time mode whichever is appropriate for the disk type
  718. ***************************************************************************/
  719. static int PASCAL NEAR unset_chapter_mode(UINT wDeviceID, int nPort)
  720. {
  721. CHAR buf[8];
  722. putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
  723. if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
  724. return MCIERR_HARDWARE;
  725. if (buf[1] == '0')
  726. putchars(wDeviceID, nPort, aszFormat, TRUE);
  727. else
  728. putchars(wDeviceID, nPort, aszTime, TRUE);
  729. return 0;
  730. }
  731. /****************************************************************************
  732. * Read the comport number for the input in the form "com<x>"
  733. ***************************************************************************/
  734. void PASCAL FAR pionGetComportAndRate(LPTSTR lpstrBuf, PUINT pPort, PUINT pRate)
  735. {
  736. LPTSTR pszChar;
  737. *pPort = 0;
  738. *pRate = DEFAULT_BAUD_RATE;
  739. if (lpstrBuf != NULL) {
  740. while (*lpstrBuf == ' ')
  741. ++lpstrBuf;
  742. if (!vdisc_lstrncmp_nocase(lpstrBuf, aszComm, sizeof(aszComm) / sizeof(TCHAR) - 1))
  743. if (lpstrBuf[sizeof(aszComm) / sizeof(TCHAR) -1] >= '1' &&
  744. lpstrBuf[sizeof(aszComm) / sizeof(TCHAR)-1] <=
  745. '0' + PIONEER_MAX_COMPORTS) {
  746. UINT wPort;
  747. if ((wPort = lpstrBuf[sizeof(aszComm)/sizeof(TCHAR)-1] - '1') < PIONEER_MAX_COMPORTS) {
  748. *pPort = wPort;
  749. }
  750. }
  751. /*
  752. * Baud rate (if any - default is 4800) is after ','
  753. */
  754. for (pszChar = lpstrBuf;
  755. *pszChar != TEXT(',') && *pszChar != TEXT('\0');
  756. pszChar++);
  757. if (*pszChar == TEXT(',')) {
  758. pszChar++;
  759. }
  760. /* Remove blanks */
  761. for (; *pszChar == TEXT(' '); pszChar++);
  762. if (*pszChar != TEXT('\0')) {
  763. UINT Rate = 0;
  764. /*
  765. * Extract the rate
  766. */
  767. while (*pszChar >= '0' && *pszChar <= '9') {
  768. Rate = Rate * 10 + (*pszChar - TEXT('0'));
  769. pszChar++;
  770. }
  771. if (Rate != 0) {
  772. *pRate = Rate;
  773. }
  774. }
  775. }
  776. }
  777. /****************************************************************************
  778. * Set the rate for the port
  779. ***************************************************************************/
  780. void pionSetBaudRate(UINT nPort, UINT nRate)
  781. {
  782. comport[nPort].Rate = nRate;
  783. }
  784. /****************************************************************************
  785. * Initialize the player
  786. ***************************************************************************/
  787. static DWORD PASCAL NEAR init_player(UINT wDeviceID, int nPort)
  788. {
  789. CHAR buf[VDISC_BUFFER_LENGTH];
  790. BOOL bPlayerSpinning = FALSE;
  791. /* Set the audio channels to a known state (both on) */
  792. putchars(wDeviceID, nPort, aszAudioOn, TRUE);
  793. comport[nPort].wAudioChannels = 3;
  794. /* See if the disk is spinning */
  795. putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
  796. getchars(wDeviceID, nPort, buf, 4, 0);
  797. if (buf[0] == 'P' && buf[2] != '0' && buf[2] != '1') {
  798. bPlayerSpinning = TRUE;
  799. /* What kind of disc is this (CAV or CLV)? */
  800. putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
  801. if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
  802. return MCIERR_HARDWARE;
  803. if (buf[1] != '1') {
  804. comport[nPort].dwTimeMode = MCI_FORMAT_FRAMES;
  805. comport[nPort].bCAVDisc = TRUE;
  806. /* Set mode to frames */
  807. putchars(wDeviceID, nPort, aszFormat, TRUE);
  808. }
  809. else {
  810. comport[nPort].dwTimeMode = MCI_FORMAT_HMS;
  811. comport[nPort].bCAVDisc = FALSE;
  812. /* Set mode to hms */
  813. putchars(wDeviceID, nPort, aszTime, TRUE);
  814. }
  815. }
  816. else {
  817. bPlayerSpinning = FALSE;
  818. comport[nPort].dwTimeMode = NO_TIME_MODE;
  819. }
  820. comport[nPort].bPlayerBusy = FALSE;
  821. comport[nPort].bDoorAction = FALSE;
  822. comport[nPort].bPlayTo = FALSE;
  823. comport[nPort].bTimerSet = FALSE;
  824. comport[nPort].dwBusyStart = 0;
  825. comport[nPort].dwDirection = PION_PLAY_NO_DIRECTION;
  826. if (!bPlayerSpinning && comport[nPort].bResponding
  827. && spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, FALSE) != 0)
  828. return MCIERR_HARDWARE;
  829. else
  830. return 0;
  831. }
  832. /****************************************************************************
  833. * Process the MCI_OPEN_DRIVER message
  834. ***************************************************************************/
  835. static DWORD PASCAL NEAR open(UINT wDeviceID, int nPort, DWORD dwFlags)
  836. {
  837. DCB dcb;
  838. TCHAR strDescription [20];
  839. int nCommID;
  840. if (dwFlags & MCI_OPEN_ELEMENT)
  841. return MCIERR_NO_ELEMENT_ALLOWED;
  842. /* See if a com port was specified in the SYSTEM.INI parameters */
  843. wsprintf(strDescription, aszCommOpenFormat, (LPSTR)aszComm, nPort + 1);
  844. /* Try to open the com port */
  845. if ((nCommID = OpenComm(strDescription, 100, 100)) < 0)
  846. return MCIERR_HARDWARE;
  847. /* Set up the com port, 4800 baud (switch S7 UP) or 9600 baud is assumed */
  848. wsprintf(strDescription, aszCommSetupFormat, (LPTSTR)aszComm, nPort + 1,
  849. comport[nPort].Rate);
  850. /*
  851. * need to initialise state of dcb first since BuildCommDCB only sets
  852. * some fields
  853. */
  854. GetCommState((HANDLE)nCommID, &dcb);
  855. BuildCommDCB(strDescription, &dcb);
  856. if (!SetCommState((HANDLE)nCommID, &dcb)) {
  857. CloseComm(nCommID);
  858. return MCIERR_HARDWARE;
  859. }
  860. /* Set up the channel description */
  861. comport[nPort].nCommID = nCommID;
  862. if (dwFlags & MCI_OPEN_SHAREABLE)
  863. comport[nPort].bShareable = TRUE;
  864. else
  865. comport[nPort].bShareable = FALSE;
  866. /* Don't make the user wait at this point to test if the device responds -
  867. they can wait when they really try to use the device */
  868. comport[nPort].bResponding = FALSE;
  869. return 0;
  870. }
  871. /****************************************************************************
  872. * Convert the given position dwPos in the current time format into the units
  873. * appropriate for the disk type puting the result in buf
  874. ***************************************************************************/
  875. static DWORD PASCAL NEAR encode_position(UINT wDeviceID, LPSTR buf, DWORD dwPos, int nPort)
  876. {
  877. BYTE h, m, s;
  878. /* Allow frame 0 */
  879. if (dwPos == 0
  880. && comport[nPort].dwTimeMode != MCI_FORMAT_HMS
  881. && comport[nPort].dwTimeMode != MCI_VD_FORMAT_TRACK)
  882. dwPos = 1;
  883. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  884. set_time_mode(wDeviceID, nPort);
  885. switch (comport[nPort].dwTimeMode) {
  886. case MCI_FORMAT_FRAMES:
  887. /* Ensure frame is at most five characters */
  888. if (!VALID_FRAME(dwPos))
  889. return MCIERR_OUTOFRANGE;
  890. wsprintfA(buf, aszFrameFormat, dwPos);
  891. break;
  892. case MCI_FORMAT_HMS:
  893. h = MCI_HMS_HOUR(dwPos);
  894. m = MCI_HMS_MINUTE(dwPos);
  895. s = MCI_HMS_SECOND(dwPos);
  896. if (h > 9 || m > 59 || s > 59)
  897. return MCIERR_OUTOFRANGE;
  898. if (comport[nPort].bCAVDisc)
  899. wsprintfA(buf, aszFrameFormat, (DWORD)(((h * 60) + m) * 60 + s) *
  900. CAV_FRAMES_PER_SECOND);
  901. else {
  902. wsprintfA(buf, aszHMSFormat, h, m, s);
  903. if (m < 10)
  904. buf[1] = '0';
  905. if (s < 10)
  906. buf[3] = '0';
  907. }
  908. break;
  909. case MCI_FORMAT_MILLISECONDS:
  910. if (comport[nPort].bCAVDisc) {
  911. dwPos = (dwPos * 3) / 100; /* 30 frames/second */
  912. wsprintfA(buf, aszFrameFormat, dwPos);
  913. }
  914. else {
  915. UINT wX;
  916. dwPos /= 1000; /* ignore fractions of a second */
  917. /* Number of minutes leftover from hours */
  918. wX = (UINT)(dwPos % 3600);
  919. h = (CHAR)((dwPos - wX) / 3600);
  920. if (h > 9)
  921. return MCIERR_OUTOFRANGE;
  922. dwPos = wX;
  923. s = (CHAR)(dwPos % 60);
  924. m = (CHAR)((dwPos - s) / 60);
  925. wsprintfA(buf, aszHMSFormat, h, m, s);
  926. /* Fill in leading zero's */
  927. if (m < 10)
  928. buf[1] = '0';
  929. if (s < 10)
  930. buf[3] = '0';
  931. }
  932. break;
  933. case MCI_VD_FORMAT_TRACK:
  934. if (dwPos > 99)
  935. return MCIERR_OUTOFRANGE;
  936. wsprintfA(buf, aszTrackFormat, dwPos);
  937. break;
  938. }
  939. return 0L;
  940. }
  941. /****************************************************************************
  942. * Convert frames to the output representation for the current time mode
  943. ***************************************************************************/
  944. static DWORD PASCAL NEAR convert_frames(UINT wDeviceID, int nPort, DWORD dwFrames, LPDWORD lpdwReturn)
  945. {
  946. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  947. set_time_mode(wDeviceID, nPort);
  948. switch (comport[nPort].dwTimeMode) {
  949. case MCI_FORMAT_FRAMES:
  950. *lpdwReturn = dwFrames;
  951. break;
  952. case MCI_FORMAT_MILLISECONDS:
  953. *lpdwReturn = (dwFrames * 100) / 3; /* 30 frames per second */
  954. break;
  955. case MCI_FORMAT_HMS:
  956. {
  957. DWORD dwSeconds = dwFrames / CAV_FRAMES_PER_SECOND;
  958. *lpdwReturn = MCI_MAKE_HMS(dwSeconds / 3600,
  959. (dwSeconds % 3600) / 60,
  960. dwSeconds % 60);
  961. return MCI_COLONIZED3_RETURN;
  962. }
  963. }
  964. return 0;
  965. }
  966. /****************************************************************************
  967. * Convert hms to the output representation for the current time mode
  968. ***************************************************************************/
  969. static DWORD PASCAL NEAR convert_hms(UINT wDeviceID, int nPort, LPSTR buf, LPDWORD lpdwReturn)
  970. {
  971. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  972. set_time_mode(wDeviceID, nPort);
  973. if (comport[nPort].dwTimeMode == MCI_FORMAT_HMS) {
  974. UINT wTemp;
  975. *lpdwReturn = MCI_MAKE_HMS(buf[0] - '0',
  976. (buf[1] - '0') * 10 + buf[2] - '0',
  977. (buf[3] - '0') * 10 + buf[4] - '0');
  978. return MCI_COLONIZED3_RETURN;
  979. }
  980. else if (comport[nPort].dwTimeMode == MCI_FORMAT_MILLISECONDS) {
  981. *lpdwReturn =
  982. (buf[0] - '0') * 3600000L +
  983. ((buf[1] - '0') * 10 + (buf[2] - '0')) * 60000L +
  984. ((buf[3] - '0') * 10 + (buf[4] - '0')) * 1000L;
  985. return 0;
  986. } else
  987. return MCIERR_HARDWARE;
  988. }
  989. /****************************************************************************/
  990. static int PASCAL NEAR DeviceStatusMode(
  991. UINT wDeviceID,
  992. int nPort)
  993. {
  994. #define PIONEER_MODE_OPEN '0'
  995. #define PIONEER_MODE_PARK '1'
  996. #define PIONEER_MODE_PLAY '4'
  997. #define PIONEER_MODE_STILL '5'
  998. #define PIONEER_MODE_PAUSE '6'
  999. #define PIONEER_MODE_MULTI '9'
  1000. CHAR buf[VDISC_BUFFER_LENGTH];
  1001. UINT wPos;
  1002. putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
  1003. if (getchars(wDeviceID, nPort, buf, 4, 0) == 0)
  1004. return MCI_MODE_NOT_READY;
  1005. /* This is done to remove the spurious return character generated */
  1006. /* if this command is sent after power is cut and restored to the player */
  1007. if (buf[0] != 'P' && buf[1] == 'P') {
  1008. wPos = 3;
  1009. getchars(wDeviceID, nPort, buf, 1, 200);
  1010. }
  1011. else
  1012. wPos = 2;
  1013. switch (buf[wPos]) {
  1014. case PIONEER_MODE_OPEN:
  1015. return MCI_MODE_OPEN;
  1016. case PIONEER_MODE_PARK:
  1017. return MCI_VD_MODE_PARK;
  1018. case PIONEER_MODE_PLAY:
  1019. return MCI_MODE_PLAY;
  1020. case PIONEER_MODE_STILL:
  1021. return MCI_MODE_PAUSE;
  1022. case PIONEER_MODE_PAUSE:
  1023. return MCI_MODE_STOP;
  1024. case PIONEER_MODE_MULTI:
  1025. return MCI_MODE_PLAY;
  1026. default:
  1027. return MCI_MODE_NOT_READY;
  1028. }
  1029. }
  1030. /****************************************************************************
  1031. * Process the MCI_STATUS message
  1032. ***************************************************************************/
  1033. static DWORD PASCAL NEAR status(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_STATUS_PARMS lpStatus)
  1034. {
  1035. CHAR buf[VDISC_BUFFER_LENGTH];
  1036. int n;
  1037. DWORD dwRet;
  1038. DWORD dwMediaType;
  1039. if (!(dwFlags & MCI_STATUS_ITEM))
  1040. return MCIERR_MISSING_PARAMETER;
  1041. switch (lpStatus->dwItem) {
  1042. case MCI_STATUS_MODE:
  1043. {
  1044. n = DeviceStatusMode(wDeviceID, nPort);
  1045. if (n == MCI_MODE_NOT_READY)
  1046. n = DeviceStatusMode(wDeviceID, nPort);
  1047. lpStatus->dwReturn = MAKEMCIRESOURCE(n, n);
  1048. return MCI_RESOURCE_RETURNED;
  1049. }
  1050. case MCI_STATUS_POSITION:
  1051. putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
  1052. getchars(wDeviceID, nPort, buf, 4, 0);
  1053. if (buf[0] == 'P' && buf[1] == '0' && buf[2] == '0')
  1054. return MCIERR_HARDWARE;
  1055. dwRet = spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, TRUE);
  1056. if (dwRet != 0)
  1057. return dwRet;
  1058. if (dwFlags & MCI_TRACK)
  1059. return MCIERR_UNSUPPORTED_FUNCTION;
  1060. if (dwFlags & MCI_STATUS_START) {
  1061. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  1062. set_time_mode(wDeviceID, nPort);
  1063. switch (comport[nPort].dwTimeMode) {
  1064. case MCI_VD_FORMAT_TRACK:
  1065. lpStatus->dwReturn = 0;
  1066. return 0;
  1067. case MCI_FORMAT_FRAMES:
  1068. lpStatus->dwReturn = 1;
  1069. return 0;
  1070. case MCI_FORMAT_HMS:
  1071. lpStatus->dwReturn = 0;
  1072. return MCI_COLONIZED3_RETURN;
  1073. case MCI_FORMAT_MILLISECONDS:
  1074. if (comport[nPort].bCAVDisc)
  1075. lpStatus->dwReturn = 1000 / CAV_FRAMES_PER_SECOND;
  1076. else
  1077. lpStatus->dwReturn = 0;
  1078. return 0;
  1079. default:
  1080. return MCIERR_HARDWARE;
  1081. }
  1082. }
  1083. if (comport[nPort].dwTimeMode == MCI_VD_FORMAT_TRACK) {
  1084. putchars(wDeviceID, nPort, aszQueryTrack, FALSE);
  1085. n = getchars(wDeviceID, nPort, buf, 3, 0);
  1086. buf[n-1] = '\0';
  1087. if (buf[0] == 'E')
  1088. return MCIERR_HARDWARE;
  1089. lpStatus->dwReturn = vdisc_atodw(buf);
  1090. return 0;
  1091. }
  1092. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  1093. set_time_mode(wDeviceID, nPort);
  1094. else
  1095. get_media_type(wDeviceID, nPort, &dwMediaType);
  1096. if (comport[nPort].bCAVDisc) {
  1097. /* Try FRAMES */
  1098. putchars(wDeviceID, nPort, aszQueryFormat, FALSE);
  1099. n = getchars(wDeviceID, nPort, buf, 6, 0);
  1100. buf[n - 1] = '\0';
  1101. /* If no error then convert from frames */
  1102. if (buf[0] != 'E')
  1103. return convert_frames(wDeviceID, nPort, vdisc_atodw(buf),
  1104. &lpStatus->dwReturn);
  1105. else
  1106. return MCIERR_HARDWARE;
  1107. }
  1108. else {
  1109. /* Try TIME */
  1110. putchars(wDeviceID, nPort, aszQueryTime, FALSE);
  1111. n = getchars(wDeviceID, nPort, buf, 6, 0);
  1112. buf[n - 1] = '\0';
  1113. if (buf[0] == 'E') {
  1114. DOUT("mcipionr: error returning HMS position");
  1115. return MCIERR_HARDWARE;
  1116. }
  1117. if (comport[nPort].dwTimeMode == MCI_FORMAT_FRAMES)
  1118. return MCIERR_HARDWARE;
  1119. else
  1120. return convert_hms(wDeviceID, nPort, buf, &lpStatus->dwReturn);
  1121. }
  1122. case MCI_STATUS_MEDIA_PRESENT:
  1123. putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
  1124. if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
  1125. return MCIERR_HARDWARE;
  1126. if (buf[0] == '1')
  1127. lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  1128. else
  1129. lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  1130. return MCI_RESOURCE_RETURNED;
  1131. case MCI_VD_STATUS_SPEED:
  1132. return MCIERR_UNSUPPORTED_FUNCTION;
  1133. case MCI_VD_STATUS_MEDIA_TYPE:
  1134. {
  1135. dwRet = get_media_type(wDeviceID, nPort, &dwMediaType);
  1136. if (dwRet)
  1137. return dwRet;
  1138. n = LOWORD(dwMediaType);
  1139. lpStatus->dwReturn = MAKEMCIRESOURCE(n, n);
  1140. return MCI_RESOURCE_RETURNED;
  1141. }
  1142. case MCI_VD_STATUS_SIDE:
  1143. {
  1144. DWORD dwRet = IsDiscSpinning(wDeviceID, nPort);
  1145. if (dwRet != 0)
  1146. return dwRet;
  1147. putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
  1148. if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
  1149. return MCIERR_HARDWARE;
  1150. if (buf[0] == '0')
  1151. return MCIERR_PIONEER_NOT_SPINNING;
  1152. if (buf[3] == '0')
  1153. lpStatus->dwReturn = 1;
  1154. else
  1155. lpStatus->dwReturn = 2;
  1156. return 0;
  1157. }
  1158. case MCI_VD_STATUS_DISC_SIZE:
  1159. {
  1160. DWORD dwRet = IsDiscSpinning(wDeviceID, nPort);
  1161. if (dwRet != 0)
  1162. return dwRet;
  1163. putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
  1164. if (getchars(wDeviceID, nPort, buf, 6, 0) != 6)
  1165. return MCIERR_HARDWARE;
  1166. if (buf[0] == '0')
  1167. return MCIERR_PIONEER_NOT_SPINNING;
  1168. if (buf[2] == '0')
  1169. lpStatus->dwReturn = 12;
  1170. else
  1171. lpStatus->dwReturn = 8;
  1172. return 0;
  1173. }
  1174. case MCI_STATUS_READY:
  1175. putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
  1176. if (getchars(wDeviceID, nPort, buf, 4, 0) != 4)
  1177. lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  1178. else
  1179. lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  1180. return MCI_RESOURCE_RETURNED;
  1181. case MCI_STATUS_LENGTH:
  1182. {
  1183. if (dwFlags & MCI_TRACK)
  1184. return MCIERR_UNSUPPORTED_FUNCTION;
  1185. putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
  1186. getchars(wDeviceID, nPort, buf, 4, 0);
  1187. if (buf[0] == 'P' && buf[1] == '0' && buf[2] == '0')
  1188. return MCIERR_HARDWARE;
  1189. dwRet = spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, TRUE);
  1190. if (dwRet != 0)
  1191. return dwRet;
  1192. if (comport[nPort].dwTimeMode == MCI_VD_FORMAT_TRACK)
  1193. return MCIERR_BAD_TIME_FORMAT;
  1194. dwRet = get_media_type(wDeviceID, nPort, &dwMediaType);
  1195. if (dwRet)
  1196. return dwRet;
  1197. if (dwMediaType == MCI_VD_MEDIA_CAV)
  1198. return convert_frames(wDeviceID, nPort, CAV_MAX_DISC_FRAMES,
  1199. &lpStatus->dwReturn);
  1200. return convert_hms(wDeviceID, nPort, aszCLVLength, &lpStatus->dwReturn);
  1201. }
  1202. case MCI_STATUS_TIME_FORMAT:
  1203. putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
  1204. getchars(wDeviceID, nPort, buf, 4, 0);
  1205. if (buf[0] == 'P' && buf[1] == '0' && buf[2] == '0')
  1206. return MCIERR_HARDWARE;
  1207. dwRet = spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, TRUE);
  1208. if (dwRet != 0)
  1209. return dwRet;
  1210. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  1211. set_time_mode(nPort, wDeviceID);
  1212. n = LOWORD(comport[nPort].dwTimeMode);
  1213. if (n == MCI_VD_FORMAT_TRACK)
  1214. lpStatus->dwReturn = MAKEMCIRESOURCE(MCI_VD_FORMAT_TRACK,
  1215. MCI_VD_FORMAT_TRACK_S);
  1216. else
  1217. lpStatus->dwReturn =
  1218. MAKEMCIRESOURCE(n, n + MCI_FORMAT_RETURN_BASE);
  1219. return MCI_RESOURCE_RETURNED;
  1220. case MCI_STATUS_CURRENT_TRACK:
  1221. putchars(wDeviceID, nPort, aszQueryPlaying, FALSE);
  1222. getchars(wDeviceID, nPort, buf, 4, 0);
  1223. if (buf[0] == 'P' && buf[1] == '0' && buf[2] == '0')
  1224. return MCIERR_HARDWARE;
  1225. dwRet = spinupdown(wDeviceID, nPort, MCI_VD_SPIN_UP, TRUE);
  1226. if (dwRet != 0)
  1227. return dwRet;
  1228. putchars(wDeviceID, nPort, aszQueryTrack, FALSE);
  1229. n = getchars(wDeviceID, nPort, buf, 3, 0);
  1230. buf[n-1] = '\0';
  1231. if (buf[0] == 'E') {
  1232. /* Flush buffer */
  1233. getchars(wDeviceID, nPort, buf, 2, 0);
  1234. /* See if the problem is no chapter support */
  1235. putchars(wDeviceID, nPort, aszQueryMedia, FALSE);
  1236. if (getchars(wDeviceID, nPort, buf, 6, 0) != 6 ||
  1237. buf[4] == '1')
  1238. return MCIERR_HARDWARE;
  1239. else
  1240. return MCIERR_PIONEER_NO_CHAPTERS;
  1241. }
  1242. lpStatus->dwReturn = vdisc_atodw(buf);
  1243. return 0;
  1244. }
  1245. return MCIERR_UNSUPPORTED_FUNCTION;
  1246. }
  1247. /****************************************************************************
  1248. * Process the MCI_SEEK message
  1249. ***************************************************************************/
  1250. static DWORD PASCAL NEAR seek(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_SEEK_PARMS lpSeek)
  1251. {
  1252. int comlen;
  1253. CHAR buf[VDISC_BUFFER_LENGTH];
  1254. DWORD dwErr;
  1255. DWORD dwMediaType;
  1256. buf[0] = '\0';
  1257. /* Position for the wsprintf to start */
  1258. comlen = 0;
  1259. if (IsDiscSpinning(wDeviceID, nPort) != 0) {
  1260. if (dwFlags & MCI_TO) {
  1261. /* Must spin up NOW if a to position needs to be converted */
  1262. putchars(wDeviceID, nPort, aszSpinUp, FALSE);
  1263. GetCompletionCode(wDeviceID, nPort, PIONEER_MAX_BUSY_TIME);
  1264. } else
  1265. {
  1266. comlen = 2;
  1267. catstring(buf, aszSpinUp, VDISC_BUFFER_LENGTH);
  1268. /* 2 characters possible here */
  1269. comport[nPort].bPlayerBusy = TRUE;
  1270. }
  1271. }
  1272. if (dwFlags & (MCI_TO | MCI_SEEK_TO_START | MCI_SEEK_TO_END)) {
  1273. if (dwFlags & MCI_SEEK_TO_START) {
  1274. if (dwFlags & MCI_SEEK_TO_END)
  1275. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1276. catstring (buf, aszSeekStart, VDISC_BUFFER_LENGTH);
  1277. }
  1278. else if (dwFlags & MCI_SEEK_TO_END) {
  1279. catstring (buf, aszSeekEnd, VDISC_BUFFER_LENGTH);
  1280. }
  1281. else if (dwFlags & MCI_TO) {
  1282. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  1283. set_time_mode(wDeviceID, nPort);
  1284. if ((dwErr = encode_position(wDeviceID, &buf[comlen], lpSeek->dwTo, nPort))
  1285. != 0)
  1286. return dwErr;
  1287. catstring(buf, aszSeekTo, VDISC_BUFFER_LENGTH);
  1288. }
  1289. putchars(wDeviceID, nPort, buf, FALSE);
  1290. comport[nPort].bPlayerBusy = TRUE;
  1291. }
  1292. else {
  1293. catstring(buf, aszSeekSetSpeed, VDISC_BUFFER_LENGTH);
  1294. if (dwFlags & MCI_VD_SEEK_REVERSE) {
  1295. dwErr = get_media_type(wDeviceID, nPort, &dwMediaType);
  1296. if (dwErr)
  1297. return dwErr;
  1298. if (dwMediaType == MCI_VD_MEDIA_CAV)
  1299. catstring(buf, aszMediaReverse, VDISC_BUFFER_LENGTH);
  1300. else
  1301. return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
  1302. }
  1303. else
  1304. catstring(buf, aszMediaForward, VDISC_BUFFER_LENGTH);
  1305. putchars(wDeviceID, nPort, buf, TRUE);
  1306. if (dwFlags & MCI_NOTIFY) {
  1307. comport[nPort].bPlayTo = TRUE;
  1308. comport[nPort].dwPlayTo = NO_TO_POSITION;
  1309. }
  1310. }
  1311. cancel_notify(nPort, MCI_NOTIFY_ABORTED);
  1312. process_delay(wDeviceID, nPort, dwFlags, lpSeek->dwCallback);
  1313. return 0;
  1314. }
  1315. /****************************************************************************
  1316. * Process the MCI_PLAY message
  1317. ***************************************************************************/
  1318. static DWORD PASCAL NEAR play(UINT wDeviceID, int nPort, DWORD dwFlags, MCI_VD_PLAY_PARMS FAR *lpPlay)
  1319. {
  1320. LPSTR compart;
  1321. int comlen;
  1322. DWORD dwErr;
  1323. DWORD dwMediaType;
  1324. BOOL bNormalSpeed = FALSE;
  1325. CHAR buf[VDISC_BUFFER_LENGTH];
  1326. DWORD dwOldToPosition = comport[nPort].bPlayTo ? comport[nPort].dwPlayTo
  1327. : NO_TO_POSITION;
  1328. DWORD dwOldDirection = comport[nPort].dwDirection;
  1329. BOOL bPlayerSpinning;
  1330. BOOL bGoingToBeBusy = FALSE;
  1331. buf[0] = '\0';
  1332. /* Convert a 'play x to x' into 'seek to x' if the positions are equal or */
  1333. /* for milliseconds if they are within the same frame or second */
  1334. if (dwFlags & MCI_FROM && dwFlags & MCI_TO &&
  1335. (lpPlay->dwTo == lpPlay->dwFrom ||
  1336. (comport[nPort].dwTimeMode == MCI_FORMAT_MILLISECONDS &&
  1337. lpPlay->dwTo - lpPlay->dwFrom <
  1338. (DWORD)(comport[nPort].bCAVDisc ? 40 : 1000))))
  1339. {
  1340. MCI_SEEK_PARMS Seek;
  1341. /* Preserve NOTIFY and WAIT and set TO flag */
  1342. DWORD dwSeekFlags;
  1343. dwSeekFlags = ((dwFlags & (MCI_NOTIFY | MCI_WAIT)) | MCI_TO);
  1344. Seek.dwTo = lpPlay->dwFrom;
  1345. Seek.dwCallback = lpPlay->dwCallback;
  1346. return seek(wDeviceID, nPort, dwSeekFlags,
  1347. (LPMCI_SEEK_PARMS)&Seek);
  1348. }
  1349. /* Build a command string to send to the player */
  1350. /* Position for the wsprintf to start */
  1351. comlen = 0;
  1352. #define PLAY_SPEED_FLAGS (MCI_VD_PLAY_FAST | MCI_VD_PLAY_SLOW | \
  1353. MCI_VD_PLAY_SPEED | MCI_VD_PLAY_SCAN)
  1354. /* Determine speed */
  1355. if (dwFlags & MCI_VD_PLAY_FAST) {
  1356. if ((dwFlags & PLAY_SPEED_FLAGS) != MCI_VD_PLAY_FAST)
  1357. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1358. /* PLAY FAST */
  1359. compart = aszFastSetSpeed;
  1360. }
  1361. else if (dwFlags & MCI_VD_PLAY_SLOW) {
  1362. if ((dwFlags & PLAY_SPEED_FLAGS) != MCI_VD_PLAY_SLOW)
  1363. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1364. /* PLAY SLOW */
  1365. compart = aszSlowSetSpeed;
  1366. }
  1367. else if (dwFlags & MCI_VD_PLAY_SPEED &&
  1368. lpPlay->dwSpeed != CAV_FRAMES_PER_SECOND) {
  1369. if ((dwFlags & PLAY_SPEED_FLAGS) != MCI_VD_PLAY_SPEED)
  1370. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1371. if (lpPlay->dwSpeed > 127)
  1372. return MCIERR_OUTOFRANGE;
  1373. wsprintfA(buf, aszSetSpeedFormat, (UINT)lpPlay->dwSpeed * 2);
  1374. compart = buf;
  1375. }
  1376. else if (dwFlags & MCI_VD_PLAY_SCAN) {
  1377. wsprintfA(buf, aszSetSpeedFormat, 255);
  1378. compart = buf;
  1379. }
  1380. else {
  1381. /* PLAY NORMAL */
  1382. compart = aszNull;
  1383. if (dwFlags & MCI_VD_PLAY_REVERSE)
  1384. compart = aszMediumSetSpeed;
  1385. bNormalSpeed = TRUE;
  1386. }
  1387. if (compart[0] != '\0')
  1388. putchars(wDeviceID, nPort, compart, TRUE);
  1389. if (!(bPlayerSpinning = !IsDiscSpinning(wDeviceID, nPort))) {
  1390. if ((dwFlags & (MCI_TO | MCI_FROM)) != 0 || !bNormalSpeed) {
  1391. /* Must spin up NOW if a position needs to be converted */
  1392. putchars(wDeviceID, nPort, aszSpinUp, FALSE);
  1393. GetCompletionCode(wDeviceID, nPort, PIONEER_MAX_BUSY_TIME);
  1394. }
  1395. else {
  1396. catstring(buf, aszSpinUp, VDISC_BUFFER_LENGTH);
  1397. comlen = 2;
  1398. /* 2 characters possible here */
  1399. bGoingToBeBusy = TRUE;
  1400. }
  1401. }
  1402. if (!bNormalSpeed &&
  1403. !get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CLV))
  1404. return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
  1405. /* If FROM was specified */
  1406. if (dwFlags & MCI_FROM) {
  1407. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  1408. set_time_mode(wDeviceID, nPort);
  1409. if ((dwErr = encode_position(wDeviceID, &buf[comlen], lpPlay->dwFrom, nPort))
  1410. != 0)
  1411. return dwErr;
  1412. /* 5 characters possible here, total of 7 */
  1413. catstring(buf, aszSeekTo, VDISC_BUFFER_LENGTH);
  1414. /* 2 characters possible here, total of 9 */
  1415. bGoingToBeBusy = TRUE;
  1416. }
  1417. /* If TO was specified */
  1418. if (dwFlags & MCI_TO) {
  1419. CHAR tobuf[10];
  1420. DWORD dwFrom;
  1421. if (dwFlags & MCI_VD_PLAY_REVERSE)
  1422. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1423. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  1424. set_time_mode(wDeviceID, nPort);
  1425. if ((dwErr = encode_position(wDeviceID, tobuf, lpPlay->dwTo, nPort)) != 0)
  1426. return dwErr;
  1427. catstring(buf, tobuf, VDISC_BUFFER_LENGTH);
  1428. /* 5 characters possible here, total of 14 */
  1429. catstring(buf, aszStopMarker, VDISC_BUFFER_LENGTH);
  1430. /* 2 characters possible here, total of 16 */
  1431. comport[nPort].bPlayTo = TRUE;
  1432. comport[nPort].dwPlayTo = lpPlay->dwTo;
  1433. comport[nPort].dwToTimeMode = comport[nPort].dwTimeMode;
  1434. /* If to is less than from then go in reverse */
  1435. if (dwFlags & MCI_FROM)
  1436. dwFrom = lpPlay->dwFrom;
  1437. else {
  1438. if (!bPlayerSpinning)
  1439. dwFrom = 0;
  1440. else {
  1441. MCI_STATUS_PARMS Status;
  1442. Status.dwItem = MCI_STATUS_POSITION;
  1443. if ((dwErr =
  1444. status(wDeviceID, nPort, MCI_STATUS_ITEM,
  1445. (LPMCI_STATUS_PARMS)&Status)) != 0)
  1446. return dwErr;
  1447. dwFrom = Status.dwReturn;
  1448. }
  1449. }
  1450. /* Compare from and to positions */
  1451. if (comport[nPort].dwTimeMode == MCI_FORMAT_HMS) {
  1452. /* Account for slop */
  1453. DWORD dwTo = lpPlay->dwTo;
  1454. if (MCI_HMS_HOUR(dwTo) < MCI_HMS_HOUR(dwFrom))
  1455. dwFlags |= MCI_VD_PLAY_REVERSE;
  1456. else if (MCI_HMS_HOUR(dwTo) == MCI_HMS_HOUR(dwFrom)) {
  1457. if (MCI_HMS_MINUTE(dwTo) < MCI_HMS_MINUTE(dwFrom))
  1458. dwFlags |= MCI_VD_PLAY_REVERSE;
  1459. else if (MCI_HMS_MINUTE(dwTo) == MCI_HMS_MINUTE(dwFrom)) {
  1460. int nDelta = MCI_HMS_SECOND(dwTo) - MCI_HMS_SECOND(dwFrom);
  1461. /* Position is plus or minus 1 second from HMS */
  1462. if (nDelta <= 1 && nDelta >= -1)
  1463. dwFrom = lpPlay->dwTo;
  1464. else if (nDelta < 0)
  1465. dwFlags |= MCI_VD_PLAY_REVERSE;
  1466. }
  1467. }
  1468. }
  1469. else if (comport[nPort].dwTimeMode == MCI_FORMAT_MILLISECONDS) {
  1470. /* Account for slop */
  1471. long lDelta = lpPlay->dwTo - dwFrom;
  1472. if (lDelta < 0) {
  1473. lDelta = -lDelta;
  1474. dwFlags |= MCI_VD_PLAY_REVERSE;
  1475. }
  1476. if (comport[nPort].bCAVDisc &&
  1477. lDelta < 1000 / CAV_FRAMES_PER_SECOND)
  1478. dwFrom = lpPlay->dwTo;
  1479. else if (lDelta < 1000)
  1480. dwFrom = lpPlay->dwTo;
  1481. } else if (lpPlay->dwTo < dwFrom)
  1482. dwFlags |= MCI_VD_PLAY_REVERSE;
  1483. if (!comport[nPort].bCAVDisc && dwFlags & MCI_VD_PLAY_REVERSE)
  1484. return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
  1485. /* If from == to then do nothing */
  1486. if (lpPlay->dwTo == dwFrom) {
  1487. notify(0, dwFlags, wDeviceID,
  1488. (LPMCI_GENERIC_PARMS)lpPlay, nPort);
  1489. return 0;
  1490. }
  1491. }
  1492. else {
  1493. comport[nPort].bPlayTo = TRUE;
  1494. comport[nPort].dwPlayTo = NO_TO_POSITION;
  1495. }
  1496. /* Determine direction */
  1497. if (dwFlags & MCI_VD_PLAY_REVERSE)
  1498. /* PLAY REVERSE */
  1499. compart = aszMediaReverse;
  1500. else if (bNormalSpeed)
  1501. /* PLAY FORWARD NORMAL SPEED */
  1502. /* The PL command is used here instead of 60SPMF because only PL is */
  1503. /* legal for CLV discs */
  1504. compart = aszPlay;
  1505. else
  1506. /* PLAY FORWARD */
  1507. compart = aszMediaForward;
  1508. catstring(buf, compart, VDISC_BUFFER_LENGTH);
  1509. /* 2 characters possible here, total of 18, buffer size is 20 */
  1510. if (putchars(wDeviceID, nPort, buf, !bGoingToBeBusy) != 0)
  1511. return MCIERR_HARDWARE;
  1512. if (bGoingToBeBusy)
  1513. comport[nPort].bPlayerBusy = TRUE;
  1514. if (dwFlags & MCI_VD_PLAY_REVERSE)
  1515. comport[nPort].dwDirection = MCI_VD_PLAY_REVERSE;
  1516. else
  1517. comport[nPort].dwDirection = PION_PLAY_FORWARD;
  1518. /* If a from position is specified or a to position is specified with
  1519. * a different position than the active notify or if a new direction is
  1520. * specified then cancel notify;
  1521. */
  1522. if (dwFlags & MCI_FROM ||
  1523. comport[nPort].dwPlayTo != dwOldToPosition ||
  1524. (dwOldDirection != PION_PLAY_NO_DIRECTION &&
  1525. comport[nPort].dwDirection != dwOldDirection))
  1526. cancel_notify(nPort, MCI_NOTIFY_ABORTED);
  1527. else if (dwFlags & MCI_NOTIFY)
  1528. cancel_notify(nPort, MCI_NOTIFY_SUPERSEDED);
  1529. /* if (comport[nPort].bPlayerBusy || comport[nPort].bPlayTo) */
  1530. process_delay(wDeviceID, nPort, dwFlags, lpPlay->dwCallback);
  1531. return 0;
  1532. }
  1533. /****************************************************************************
  1534. * Process the MCI_STOP and MCI_PAUSE messages
  1535. ***************************************************************************/
  1536. static DWORD PASCAL NEAR stop_pause(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_GENERIC_PARMS lpGeneric, UINT wCommand)
  1537. {
  1538. DWORD dwErr;
  1539. DWORD dwMediaType;
  1540. /* Note, "ST" stands for 'still', "PA" stands for 'pause' but */
  1541. /* in MCI lingo "stop" uses the "PA" command and "pause" uses "ST" */
  1542. if ((dwErr = IsDiscSpinning(wDeviceID, nPort)) == 0) {
  1543. dwErr = (DWORD)putchars(wDeviceID, nPort,
  1544. wCommand == MCI_STOP ? aszPause : aszStop,
  1545. TRUE);
  1546. /* If error and CLV disc then try "stop" instead */
  1547. if (dwErr != 0 && wCommand == MCI_PAUSE)
  1548. if (!get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CLV))
  1549. dwErr = (DWORD)putchars(wDeviceID, nPort, aszPause, TRUE);
  1550. }
  1551. if (dwErr == 0)
  1552. cancel_notify(nPort, MCI_NOTIFY_ABORTED);
  1553. notify(LOWORD(dwErr), dwFlags, wDeviceID, lpGeneric, nPort);
  1554. return dwErr;
  1555. }
  1556. /****************************************************************************
  1557. * Spin the player up or down depending on dwFlag
  1558. ***************************************************************************/
  1559. static DWORD PASCAL NEAR spinupdown(UINT wDeviceID, int nPort, DWORD dwFlag, BOOL bWait)
  1560. {
  1561. if (dwFlag & MCI_VD_SPIN_UP) {
  1562. if (IsDiscSpinning(wDeviceID, nPort) != 0) {
  1563. DWORD dwErr;
  1564. if (!bWait) {
  1565. comport[nPort].bPlayerBusy = TRUE;
  1566. comport[nPort].bDoorAction = TRUE;
  1567. }
  1568. if (putchars(wDeviceID, nPort, aszSpinUp, FALSE) != 0)
  1569. return MCIERR_HARDWARE;
  1570. if (bWait && (dwErr =
  1571. GetCompletionCode(wDeviceID, nPort, PIONEER_MAX_BUSY_TIME)) != 0)
  1572. return dwErr;
  1573. }
  1574. }
  1575. else if (dwFlag & MCI_VD_SPIN_DOWN) {
  1576. if (IsDiscSpinning(wDeviceID, nPort) == 0) {
  1577. comport[nPort].bPlayerBusy = TRUE;
  1578. comport[nPort].dwDirection = PION_PLAY_NO_DIRECTION;
  1579. cancel_notify(nPort, MCI_NOTIFY_ABORTED);
  1580. if (putchars(wDeviceID, nPort, aszReject, bWait) != 0)
  1581. return MCIERR_HARDWARE;
  1582. }
  1583. } else
  1584. return MCIERR_MISSING_PARAMETER;
  1585. return 0;
  1586. }
  1587. /****************************************************************************
  1588. * Process the MCI_SPIN message
  1589. ***************************************************************************/
  1590. static DWORD PASCAL NEAR spin(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_GENERIC_PARMS lpGeneric)
  1591. {
  1592. DWORD dwErr;
  1593. dwErr = spinupdown(wDeviceID, nPort, dwFlags, FALSE);
  1594. if (dwErr != 0)
  1595. return dwErr;
  1596. if (comport[nPort].bPlayerBusy)
  1597. process_delay(wDeviceID, nPort, dwFlags, lpGeneric->dwCallback);
  1598. else
  1599. notify(0, dwFlags, wDeviceID, lpGeneric,
  1600. nPort);
  1601. return 0;
  1602. }
  1603. /****************************************************************************
  1604. * Process the MCI_STEP message
  1605. ***************************************************************************/
  1606. static DWORD PASCAL NEAR step(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_VD_STEP_PARMS lpStep)
  1607. {
  1608. DWORD dwFrame, dwErr;
  1609. int n;
  1610. CHAR buf[VDISC_BUFFER_LENGTH];
  1611. DWORD dwMediaType;
  1612. if (dwFlags & MCI_VD_STEP_FRAMES) {
  1613. if (comport[nPort].dwTimeMode == NO_TIME_MODE)
  1614. set_time_mode(wDeviceID, nPort);
  1615. /* Must get current position and go from there */
  1616. if (putchars(wDeviceID, nPort, aszQueryFormat, FALSE) != 0)
  1617. return MCIERR_HARDWARE;
  1618. if ((n = getchars(wDeviceID, nPort, buf, 6, 0)) != 6
  1619. || buf[0] == 'E')
  1620. goto step_error;
  1621. buf[n - 1] = '\0';
  1622. dwFrame = vdisc_atodw(buf);
  1623. if (dwFlags & MCI_VD_STEP_REVERSE)
  1624. dwFrame -= lpStep->dwFrames;
  1625. else
  1626. dwFrame += lpStep->dwFrames;
  1627. wsprintfA(buf, aszSeekToFormat, (UINT)dwFrame);
  1628. comport[nPort].bPlayerBusy = TRUE;
  1629. if (putchars(wDeviceID, nPort, buf, FALSE) != 0)
  1630. return MCIERR_HARDWARE;
  1631. process_delay(wDeviceID, nPort, dwFlags, lpStep->dwCallback);
  1632. return 0;
  1633. }
  1634. else {
  1635. if (dwFlags & MCI_VD_STEP_REVERSE)
  1636. dwErr = putchars(wDeviceID, nPort, aszStepReverse, TRUE);
  1637. else
  1638. dwErr = putchars(wDeviceID, nPort, aszStepForward, TRUE);
  1639. if (dwErr == 0)
  1640. return 0;
  1641. else
  1642. goto step_error;
  1643. }
  1644. step_error:
  1645. dwErr = get_media_type(wDeviceID, nPort, &dwMediaType);
  1646. if (dwErr)
  1647. return dwErr;
  1648. if (dwMediaType == MCI_VD_MEDIA_CLV)
  1649. return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
  1650. return MCIERR_HARDWARE;
  1651. }
  1652. /****************************************************************************
  1653. * Process the MCI_GETDEVCAPS message
  1654. ***************************************************************************/
  1655. static DWORD PASCAL NEAR getdevcaps(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpCaps)
  1656. {
  1657. BOOL bCLV = FALSE;
  1658. DWORD dwMediaType;
  1659. /* Info is for CAV unless CLV specified or current disc is CLV */
  1660. if (dwFlags & MCI_VD_GETDEVCAPS_CLV)
  1661. bCLV = TRUE;
  1662. else if (!(dwFlags & MCI_VD_GETDEVCAPS_CAV) &&
  1663. !get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CLV))
  1664. bCLV = TRUE;
  1665. if (!(MCI_GETDEVCAPS_ITEM))
  1666. return MCIERR_MISSING_PARAMETER;
  1667. switch (lpCaps->dwItem) {
  1668. case MCI_GETDEVCAPS_CAN_RECORD:
  1669. case MCI_GETDEVCAPS_CAN_SAVE:
  1670. case MCI_GETDEVCAPS_USES_FILES:
  1671. case MCI_GETDEVCAPS_COMPOUND_DEVICE:
  1672. lpCaps->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  1673. return MCI_RESOURCE_RETURNED;
  1674. case MCI_GETDEVCAPS_HAS_AUDIO:
  1675. case MCI_GETDEVCAPS_HAS_VIDEO:
  1676. case MCI_GETDEVCAPS_CAN_EJECT:
  1677. case MCI_GETDEVCAPS_CAN_PLAY:
  1678. lpCaps->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  1679. return MCI_RESOURCE_RETURNED;
  1680. case MCI_VD_GETDEVCAPS_CAN_REVERSE:
  1681. if (bCLV)
  1682. lpCaps->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  1683. else
  1684. lpCaps->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  1685. return MCI_RESOURCE_RETURNED;
  1686. case MCI_GETDEVCAPS_DEVICE_TYPE:
  1687. lpCaps->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_VIDEODISC,
  1688. MCI_DEVTYPE_VIDEODISC);
  1689. return MCI_RESOURCE_RETURNED;
  1690. case MCI_VD_GETDEVCAPS_NORMAL_RATE:
  1691. lpCaps->dwReturn = CAV_FRAMES_PER_SECOND;
  1692. return 0;
  1693. case MCI_VD_GETDEVCAPS_SLOW_RATE:
  1694. case MCI_VD_GETDEVCAPS_CLV:
  1695. if (bCLV)
  1696. lpCaps->dwReturn = 0;
  1697. else
  1698. lpCaps->dwReturn = CAV_FRAMES_PER_SECOND / 3;
  1699. return 0;
  1700. case MCI_VD_GETDEVCAPS_FAST_RATE:
  1701. if (bCLV)
  1702. lpCaps->dwReturn = 0;
  1703. else
  1704. lpCaps->dwReturn = CAV_FRAMES_PER_SECOND * 3;
  1705. return 0;
  1706. }
  1707. return MCIERR_MISSING_PARAMETER;
  1708. }
  1709. /****************************************************************************
  1710. * Process the MCI_SET message
  1711. ***************************************************************************/
  1712. static DWORD PASCAL NEAR set(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_SET_PARMS lpSet)
  1713. {
  1714. CHAR strCommand[4];
  1715. UINT wMask;
  1716. DWORD dwMediaType;
  1717. if (dwFlags & MCI_SET_AUDIO) {
  1718. if (dwFlags & MCI_SET_VIDEO)
  1719. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1720. strCommand[0] = '0';
  1721. strCommand[1] = 'A';
  1722. strCommand[2] = 'D';
  1723. strCommand[3] = '\0';
  1724. if (lpSet->dwAudio == MCI_SET_AUDIO_LEFT)
  1725. wMask = 1;
  1726. else if (lpSet->dwAudio == MCI_SET_AUDIO_RIGHT)
  1727. wMask = 2;
  1728. else if (lpSet->dwAudio == MCI_SET_AUDIO_ALL)
  1729. wMask = 3;
  1730. else
  1731. return MCIERR_OUTOFRANGE;
  1732. if (dwFlags & MCI_SET_ON) {
  1733. if (dwFlags & MCI_SET_OFF)
  1734. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1735. comport[nPort].wAudioChannels |= wMask;
  1736. }
  1737. else if (dwFlags & MCI_SET_OFF)
  1738. comport[nPort].wAudioChannels &= ~wMask;
  1739. else
  1740. return MCIERR_MISSING_PARAMETER;
  1741. strCommand[0] = (CHAR)(comport[nPort].wAudioChannels + '0');
  1742. putchars(wDeviceID, nPort, strCommand, TRUE);
  1743. }
  1744. else if (dwFlags & MCI_SET_TIME_FORMAT) {
  1745. switch (lpSet->dwTimeFormat) {
  1746. case MCI_FORMAT_MILLISECONDS:
  1747. comport[nPort].dwTimeMode = MCI_FORMAT_MILLISECONDS;
  1748. break;
  1749. case MCI_FORMAT_HMS:
  1750. comport[nPort].dwTimeMode = MCI_FORMAT_HMS;
  1751. break;
  1752. case MCI_FORMAT_FRAMES:
  1753. if (!get_media_type(wDeviceID, nPort, &dwMediaType) && (dwMediaType == MCI_VD_MEDIA_CLV))
  1754. return MCIERR_PIONEER_ILLEGAL_FOR_CLV;
  1755. comport[nPort].dwTimeMode = MCI_FORMAT_FRAMES;
  1756. break;
  1757. case MCI_VD_FORMAT_TRACK:
  1758. if (putchars(wDeviceID, nPort, aszCheck, TRUE) != 0)
  1759. return MCIERR_HARDWARE;
  1760. comport[nPort].dwTimeMode = MCI_VD_FORMAT_TRACK;
  1761. break;
  1762. default:
  1763. return MCIERR_BAD_TIME_FORMAT;
  1764. }
  1765. if (lpSet->dwTimeFormat != MCI_VD_FORMAT_TRACK) {
  1766. DWORD dwErr;
  1767. if ((dwErr = unset_chapter_mode(wDeviceID, nPort)) != 0)
  1768. return dwErr;
  1769. }
  1770. }
  1771. else if (dwFlags & MCI_SET_VIDEO) {
  1772. strCommand[1] = 'V';
  1773. strCommand[2] = 'D';
  1774. strCommand[3] = '\0';
  1775. if (dwFlags & MCI_SET_ON) {
  1776. if (dwFlags & MCI_SET_OFF)
  1777. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1778. strCommand[0] = '1';
  1779. } else if (dwFlags & MCI_SET_OFF)
  1780. strCommand[0] = '0';
  1781. else
  1782. return MCIERR_MISSING_PARAMETER;
  1783. if (putchars(wDeviceID, nPort, strCommand, TRUE) != 0)
  1784. return MCIERR_HARDWARE;
  1785. }
  1786. else if (dwFlags & MCI_SET_DOOR_OPEN) {
  1787. if (putchars(wDeviceID, nPort, aszOpenDoor, FALSE) != 0)
  1788. return MCIERR_HARDWARE;
  1789. comport[nPort].bPlayerBusy = TRUE;
  1790. comport[nPort].bDoorAction = TRUE;
  1791. comport[nPort].dwTimeMode = NO_TIME_MODE;
  1792. process_delay(wDeviceID, nPort, dwFlags, lpSet->dwCallback);
  1793. return 0;
  1794. }
  1795. else if (dwFlags & MCI_SET_DOOR_CLOSED) {
  1796. /* Don't use spinupdown() because it won't work right for notification */
  1797. if (IsDiscSpinning(wDeviceID, nPort) != 0) {
  1798. comport[nPort].bPlayerBusy = TRUE;
  1799. comport[nPort].bDoorAction = TRUE;
  1800. if (putchars(wDeviceID, nPort, aszSpinUp, FALSE) != 0)
  1801. return MCIERR_HARDWARE;
  1802. process_delay(wDeviceID, nPort, dwFlags, lpSet->dwCallback);
  1803. return 0;
  1804. }
  1805. } else
  1806. return MCIERR_MISSING_PARAMETER;
  1807. notify(0, dwFlags, wDeviceID, (LPMCI_GENERIC_PARMS)lpSet, nPort);
  1808. return 0;
  1809. }
  1810. /****************************************************************************
  1811. * Process the MCI_ESCAPE message
  1812. ***************************************************************************/
  1813. static DWORD PASCAL NEAR command(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_VD_ESCAPE_PARMS lpCommand)
  1814. {
  1815. DWORD dwErr, dwReturn = 0;
  1816. if (!(dwFlags & MCI_VD_ESCAPE_STRING) || lpCommand->lpstrCommand == NULL)
  1817. return MCIERR_MISSING_PARAMETER;
  1818. /* Turn off return codes -- this command has no return code */
  1819. if ((dwErr = putchars(wDeviceID, nPort, aszCommandOff, FALSE)) != 0)
  1820. return dwErr;
  1821. /* Send application's string */
  1822. if ((dwErr = putchars(wDeviceID, nPort, (LPSTR)lpCommand->lpstrCommand, FALSE))
  1823. != 0)
  1824. dwReturn = dwErr;
  1825. /* Turn on return codes -- this command has a return code */
  1826. if ((dwErr = putchars(wDeviceID, nPort, aszCommandOn, TRUE)) != 0 &&
  1827. dwReturn == 0)
  1828. dwReturn = dwErr;
  1829. return dwReturn;
  1830. }
  1831. /****************************************************************************
  1832. * Process the MCI_VDISC_INDEX message
  1833. ***************************************************************************/
  1834. static DWORD PASCAL NEAR index(UINT wDeviceID, int nPort, DWORD dwFlags)
  1835. {
  1836. return putchars(wDeviceID, nPort,
  1837. dwFlags & VDISC_FLAG_ON ? aszIndexOn : aszIndexOff, TRUE);
  1838. }
  1839. /****************************************************************************
  1840. * Process the MCI_VDISC_KEYLOCK message
  1841. ***************************************************************************/
  1842. static DWORD PASCAL NEAR keylock(UINT wDeviceID, int nPort, DWORD dwFlags)
  1843. {
  1844. /* Ensure that one and only flag is set */
  1845. if (dwFlags & VDISC_FLAG_ON && dwFlags & VDISC_FLAG_OFF)
  1846. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1847. if (!(dwFlags & (VDISC_FLAG_ON | VDISC_FLAG_OFF)))
  1848. return MCIERR_MISSING_PARAMETER;
  1849. return putchars(wDeviceID, nPort,
  1850. dwFlags & VDISC_FLAG_ON ? aszKeyLockOn : aszKeyLockOff,
  1851. TRUE);
  1852. }
  1853. /****************************************************************************
  1854. * Process the MCI_INFO message
  1855. ***************************************************************************/
  1856. static DWORD PASCAL NEAR info(UINT wDeviceID, int nPort, DWORD dwFlags, LPMCI_INFO_PARMS lpInfo)
  1857. {
  1858. DWORD dwErr;
  1859. if (dwFlags & MCI_INFO_PRODUCT) {
  1860. if (lpInfo->lpstrReturn == NULL ||
  1861. !LOWORD(lpInfo->dwRetSize))
  1862. dwErr = MCIERR_PARAM_OVERFLOW;
  1863. else {
  1864. UINT wReturnBufferLength;
  1865. wReturnBufferLength = LOWORD(lpInfo->dwRetSize);
  1866. *(lpInfo->lpstrReturn + wReturnBufferLength - 1) = '\0';
  1867. lpInfo->dwRetSize = LoadString(hInstance, IDS_PRODUCTNAME, lpInfo->lpstrReturn, wReturnBufferLength);
  1868. if (*(lpInfo->lpstrReturn + wReturnBufferLength - 1) != '\0')
  1869. dwErr = MCIERR_PARAM_OVERFLOW;
  1870. else
  1871. dwErr = 0;
  1872. }
  1873. } else
  1874. dwErr = MCIERR_MISSING_PARAMETER;
  1875. return dwErr;
  1876. }
  1877. /****************************************************************************
  1878. * Process the MCI_CLOSE message
  1879. ***************************************************************************/
  1880. static void PASCAL NEAR close(UINT wDeviceID, int nPort)
  1881. {
  1882. DOUT("Closing...");
  1883. if (--comport[nPort].nUseCount == 0) {
  1884. DOUT("comport...");
  1885. vdisc_close(wDeviceID, nPort);
  1886. comport[nPort].nCommID = NO_COMPORT;
  1887. if (comport[nPort].bTimerSet) {
  1888. if (--nWaitingChannels == 0) {
  1889. KillTimer(NULL, wTimerID);
  1890. mciDriverNotify(comport[nPort].hCallback,
  1891. comport[nPort].wDeviceID,
  1892. MCI_NOTIFY_ABORTED);
  1893. }
  1894. }
  1895. }
  1896. }
  1897. /****************************************************************************
  1898. * Process all MCI specific message
  1899. ***************************************************************************/
  1900. DWORD FAR PASCAL mciDriverEntry(UINT wDeviceID, UINT message, LPARAM lParam1, LPARAM lParam2)
  1901. {
  1902. int nCommID, nPort;
  1903. DWORD dwErr = 0;
  1904. LPMCI_GENERIC_PARMS lpGeneric = (LPMCI_GENERIC_PARMS)lParam2;
  1905. #if DBG
  1906. CHAR buf[100];
  1907. #endif
  1908. /* Catch these here to avoid a mandatory wait */
  1909. switch (message) {
  1910. case MCI_RECORD:
  1911. case MCI_LOAD:
  1912. case MCI_SAVE:
  1913. case MCI_RESUME:
  1914. return MCIERR_UNSUPPORTED_FUNCTION;
  1915. }
  1916. if (lpGeneric == NULL)
  1917. return MCIERR_NULL_PARAMETER_BLOCK;
  1918. /* Find the channel number given the device ID */
  1919. nPort = (UINT)mciGetDriverData(wDeviceID);
  1920. /* Serialize all access to this com port. When we yield we release this
  1921. cricical section */
  1922. EnterCrit(nPort);
  1923. /* Find the actual comm port handle */
  1924. nCommID = comport[nPort].nCommID;
  1925. #if DBG
  1926. wsprintfA(buf, "port=%d commid=%d", nPort, nCommID);
  1927. DOUT(buf);
  1928. #endif
  1929. /* If the device is busy then wait for completion before sending any commands */
  1930. if (message != MCI_OPEN_DRIVER) {
  1931. if (comport[nPort].bPlayerBusy) {
  1932. /* If the command is 'status mode' */
  1933. if (message == MCI_STATUS && (DWORD)lParam1 & MCI_STATUS_ITEM &&
  1934. ((LPMCI_STATUS_PARMS)lParam2)->dwItem == MCI_STATUS_MODE) {
  1935. /* Seek if the device is done seeking */
  1936. if (GetCompletionCode(wDeviceID, nPort, 200) == 0) {
  1937. comport[nPort].bPlayerBusy = FALSE;
  1938. comport[nPort].bDoorAction = FALSE;
  1939. }
  1940. else {
  1941. /* If not then return MCI_MODE_SEEK */
  1942. UINT wMode;
  1943. wMode = (comport[nPort].bDoorAction? MCI_MODE_NOT_READY :
  1944. MCI_MODE_SEEK);
  1945. ((LPMCI_STATUS_PARMS)lParam2)->dwReturn =
  1946. MAKEMCIRESOURCE(wMode, wMode);
  1947. notify(LOWORD(dwErr), (DWORD)lParam1, wDeviceID,
  1948. (LPMCI_GENERIC_PARMS)lParam2, nPort);
  1949. LeaveCrit(nPort);
  1950. return MCI_RESOURCE_RETURNED;
  1951. }
  1952. }
  1953. else {
  1954. /* Wait up to 25 seconds for the ongoing command to complete */
  1955. GetCompletionCode(wDeviceID, nPort, 25000);
  1956. comport[nPort].bPlayerBusy = FALSE;
  1957. }
  1958. }
  1959. /* If the device has not yet responded try to see if it's alive */
  1960. if (!comport[nPort].bResponding && message != MCI_CLOSE_DRIVER &&
  1961. message != MCI_GETDEVCAPS)
  1962. if (putchars(wDeviceID, nPort, aszKeyLockOn, TRUE) != 0) {
  1963. LeaveCrit(nPort);
  1964. return MCIERR_HARDWARE;
  1965. }
  1966. else {
  1967. comport[nPort].bResponding = TRUE;
  1968. init_player(wDeviceID, nPort);
  1969. }
  1970. }
  1971. /* These commands will abort notification or are otherwise strange */
  1972. switch (message) {
  1973. case MCI_PLAY:
  1974. dwErr = play(wDeviceID, nPort, (DWORD)lParam1, (MCI_VD_PLAY_PARMS FAR *)lParam2);
  1975. LeaveCrit(nPort);
  1976. return dwErr;
  1977. case MCI_SEEK:
  1978. dwErr = seek(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_SEEK_PARMS)lParam2);
  1979. LeaveCrit(nPort);
  1980. return dwErr;
  1981. case MCI_SET:
  1982. dwErr = set(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_SET_PARMS)lParam2);
  1983. LeaveCrit(nPort);
  1984. return dwErr;
  1985. case MCI_SPIN:
  1986. dwErr = spin(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_GENERIC_PARMS)lParam2);
  1987. LeaveCrit(nPort);
  1988. return dwErr;
  1989. case MCI_CLOSE_DRIVER:
  1990. close(wDeviceID, nPort);
  1991. notify(0, (DWORD)lParam1, wDeviceID, (LPMCI_GENERIC_PARMS)lParam2,
  1992. nPort);
  1993. LeaveCrit(nPort);
  1994. return 0;
  1995. case MCI_STOP:
  1996. /* Fall through */
  1997. case MCI_PAUSE:
  1998. dwErr = stop_pause(wDeviceID, nPort,
  1999. (DWORD)lParam1, lpGeneric, message);
  2000. LeaveCrit(nPort);
  2001. return dwErr;
  2002. }
  2003. /* These commands will NOT abort notification */
  2004. switch (message) {
  2005. case MCI_OPEN_DRIVER:
  2006. /* If the port is in use then shareable must be specified now and with */
  2007. /* the previous open */
  2008. if (comport[nPort].nUseCount != 0)
  2009. if (!((DWORD)lParam1 & MCI_OPEN_SHAREABLE) ||
  2010. !comport[nPort].bShareable) {
  2011. LeaveCrit(nPort);
  2012. return MCIERR_MUST_USE_SHAREABLE;
  2013. }
  2014. ++comport[nPort].nUseCount;
  2015. if (comport[nPort].nCommID == NO_COMPORT)
  2016. dwErr = open(wDeviceID, nPort, (DWORD)lParam1);
  2017. break;
  2018. case VDISC_INDEX:
  2019. dwErr = index(wDeviceID, nPort, (DWORD)lParam1);
  2020. break;
  2021. case VDISC_KEYLOCK:
  2022. dwErr = keylock(wDeviceID, nPort, (DWORD)lParam1);
  2023. break;
  2024. case MCI_ESCAPE:
  2025. dwErr = command(wDeviceID, nPort, (DWORD)lParam1,
  2026. (LPMCI_VD_ESCAPE_PARMS)lParam2);
  2027. break;
  2028. /* The MCI_STEP message should really be in the above list of message */
  2029. /* which can abort notification. In this driver, MCI_STEP never */
  2030. /* aborts notification which is an error */
  2031. case MCI_STEP:
  2032. dwErr = step(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_VD_STEP_PARMS)lParam2);
  2033. break;
  2034. case MCI_GETDEVCAPS:
  2035. dwErr = getdevcaps(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_GETDEVCAPS_PARMS)lParam2);
  2036. break;
  2037. case MCI_STATUS:
  2038. dwErr = status(wDeviceID, nPort, (DWORD)lParam1, (LPMCI_STATUS_PARMS)lParam2);
  2039. break;
  2040. case MCI_INFO:
  2041. dwErr = info(wDeviceID, nPort, (DWORD)lParam1,
  2042. (LPMCI_INFO_PARMS)lParam2);
  2043. break;
  2044. default:
  2045. dwErr = MCIERR_UNRECOGNIZED_COMMAND;
  2046. break;
  2047. } /* switch */
  2048. notify(LOWORD(dwErr), (DWORD)lParam1, wDeviceID, (LPMCI_GENERIC_PARMS)lParam2,
  2049. nPort);
  2050. LeaveCrit(nPort);
  2051. return dwErr;
  2052. }
  2053. /****************************************************************************
  2054. * Library exit function
  2055. ***************************************************************************/
  2056. BOOL PASCAL FAR _LOADDS WEP(BOOL fSystemExit)
  2057. {
  2058. int n;
  2059. for (n = 0; n < PIONEER_MAX_COMPORTS; n++)
  2060. if (comport[n].nCommID != NO_COMPORT)
  2061. /* We use device id 0 which is OK because it's not used anyway
  2062. since the device id is only used for Yielding and we don't
  2063. yield during close */
  2064. vdisc_close(0, n);
  2065. return TRUE;
  2066. }
  2067. #ifdef WIN32
  2068. /****************************************************************************
  2069. * @doc EXTERNAL
  2070. *
  2071. * @api int | DllInstanceInit | Library initialization code.
  2072. *
  2073. * @parm HINSTANCE | hModule | Our instance handle.
  2074. *
  2075. * @parm ULONG | Reason | Reason for being called.
  2076. *
  2077. * @parm PCONTEXT | pContext | Context
  2078. *
  2079. * @rdesc Returns 1 if the initialization was successful and 0 otherwise.
  2080. ***************************************************************************/
  2081. BOOL DllInstanceInit(PVOID hModule, ULONG Reason, PCONTEXT pContext)
  2082. {
  2083. UNREFERENCED_PARAMETER(pContext);
  2084. if (Reason == DLL_PROCESS_ATTACH) {
  2085. int i;
  2086. for (i = 0; i < PIONEER_MAX_COMPORTS; i++) {
  2087. InitializeCriticalSection(&comport[i].DeviceCritSec);
  2088. }
  2089. return LibMain(hModule, 0, NULL);
  2090. } else {
  2091. if (Reason == DLL_PROCESS_DETACH) {
  2092. return WEP(FALSE);
  2093. } else {
  2094. return TRUE;
  2095. }
  2096. }
  2097. }
  2098. /****************************************************************************
  2099. * @doc INTERNAL
  2100. *
  2101. * @api UINT | pionDriverYield | Yield or yield simulation.
  2102. *
  2103. * @parm UINT | wDeviceId | Device id yielding.
  2104. *
  2105. * @parm UINT | nPort | logical device yielding.
  2106. *
  2107. * @rdesc Returns code returned by mciDriverYield.
  2108. *
  2109. ***************************************************************************/
  2110. UINT pionDriverYield(UINT wDeviceId, UINT nPort)
  2111. {
  2112. UINT rc;
  2113. LeaveCrit(nPort);
  2114. rc = mciDriverYield(wDeviceId);
  2115. /* Let someone else have a go */
  2116. Sleep(10);
  2117. EnterCrit(nPort);
  2118. return rc;
  2119. }
  2120. #endif /* WIN32 */