Leaked source code of windows server 2003
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.

2752 lines
77 KiB

  1. /*
  2. * cdio.c
  3. *
  4. *
  5. * This module provides a C interface to the CD-ROM device
  6. * drivers to make the audio control a little simpler for
  7. * the rest of the driver.
  8. *
  9. * 21-Jun-91 NigelT
  10. * 10-Mar-92 RobinSp - Catch up with windows 3.1
  11. *
  12. * Copyright (c) 1990-1998 Microsoft Corporation
  13. *
  14. */
  15. #include <windows.h>
  16. #include <devioctl.h>
  17. #include <mmsystem.h>
  18. #include <tchar.h>
  19. #include "mcicda.h"
  20. #include "cda.h"
  21. #include "cdio.h"
  22. //#include <ntstatus.h>
  23. #ifndef STATUS_VERIFY_REQUIRED
  24. #define STATUS_VERIFY_REQUIRED ((DWORD)0x80000016L)
  25. #endif
  26. //
  27. // 7/13/2001
  28. // if this value is set to 1, MCICDA will no longer hide data
  29. // tracks on Blue Book (CD+) cds, and will properly munge the
  30. // tracklength for the final audio track. unfortunately, this
  31. // cannot be enabled currently because both WinAMP 2.76 and
  32. // WMP 8.0 have unique bugs that occur when this is done.
  33. // see Windows Bugs bug #436523 for details.
  34. // apps can use IOCTL_CDROM_READ_TOC to get the full TOC when
  35. // they want the real information for a disc, but that is much
  36. // more work than most want to do.
  37. //
  38. #define USE_PROPER_BLUE_BOOK_HACK 0
  39. //
  40. // Private constants
  41. //
  42. //
  43. // Local functions (cd prefix, globals have Cd)
  44. //
  45. HANDLE cdOpenDeviceDriver(TCHAR szAnsiDeviceName, DWORD Access);
  46. void cdCloseDeviceDriver(HANDLE hDevice);
  47. DWORD cdGetDiskInfo(LPCDINFO lpInfo);
  48. DWORD cdIoctl(LPCDINFO lpInfo, DWORD Request, PVOID lpData, DWORD size);
  49. DWORD cdIoctlData(LPCDINFO lpInfo, DWORD Request, PVOID lpData,
  50. DWORD size, PVOID lpOutput, DWORD OutputSize);
  51. void CdSetAudioStatus (HCD hCD, UCHAR fStatus);
  52. BOOL CdGetAudioStatus (HCD hCD, UCHAR fStatus, DWORD * pStatus);
  53. BOOL CdGetTrack(LPCDINFO lpInfo, MSF msfPos, UCHAR * pTrack, MSF * pmsfStart);
  54. /***************************************************************************
  55. @doc EXTERNAL
  56. @api MSF | CdFindAudio | Given a position to start playing find
  57. the next audio track (if this one isn't) if any.
  58. @parm LPCDINFO | lpInfo | Pointer to CD info including track data.
  59. @parm MSF | msfStart | Position to start looking.
  60. @rdesc A new MSF to play from / seek to within an audio track or
  61. the end of the CD if none was found.
  62. ***************************************************************************/
  63. MSF CdFindAudio(LPCDINFO lpInfo, MSF msfStart)
  64. {
  65. UINT tracknum;
  66. MSF lastaudio = lpInfo->msfEnd;
  67. //
  68. // If we don't have a valid TOC then just obey - they may know
  69. // what they're doing.
  70. //
  71. dprintf2(("CdFindAudio"));
  72. if (!lpInfo->bTOCValid) {
  73. dprintf2(("CdFindAudio - No valid TOC"));
  74. return msfStart;
  75. }
  76. //
  77. // If we're being asked to play a data track then move forward
  78. // to the next audio track if there is one
  79. //
  80. //
  81. // Search for the track which ends after ours and is audio
  82. //
  83. for (tracknum = 0; ;tracknum++) {
  84. //
  85. // Note that some CDs return positions outside the playing range
  86. // sometimes (notably 0) so msfStart may be less than the first
  87. // track start
  88. //
  89. //
  90. // If we're beyond the start of the track and before the start
  91. // of the next track then this is the track we want.
  92. //
  93. // We assume we're always beyond the start of the first track
  94. // and we check that if we're looking at the last track then
  95. // we check we're before the end of the CD.
  96. //
  97. if (!(lpInfo->Track[tracknum].Ctrl & IS_DATA_TRACK)) {
  98. // Remember the last audio track. The MCI CDAudio spec
  99. // for Seek to end says we position at the last audio track
  100. // which is not necessarily the last track on the disc
  101. lastaudio = lpInfo->Track[tracknum].msfStart;
  102. }
  103. if ((msfStart >= lpInfo->Track[tracknum].msfStart || tracknum == 0)
  104. &&
  105. #ifdef OLD
  106. (tracknum + lpInfo->FirstTrack == lpInfo->LastTrack &&
  107. msfStart < lpInfo->msfEnd ||
  108. tracknum + lpInfo->FirstTrack != lpInfo->LastTrack &&
  109. msfStart < lpInfo->Track[tracknum + 1].msfStart)) {
  110. #else
  111. // Simplify the logic. When reviewed to the extent that the
  112. // reviewer is convinced the test below is identical to the
  113. // test above the old code can be deleted.
  114. (tracknum + lpInfo->FirstTrack == lpInfo->LastTrack
  115. ? msfStart <= lpInfo->msfEnd
  116. : msfStart < lpInfo->Track[tracknum + 1].msfStart)
  117. ) {
  118. #endif
  119. if (!(lpInfo->Track[tracknum].Ctrl & IS_DATA_TRACK)) {
  120. return msfStart;
  121. }
  122. //
  123. // Move to next track if there is one and this one is a
  124. // data track
  125. //
  126. if (tracknum + lpInfo->FirstTrack >= lpInfo->LastTrack) {
  127. //
  128. // Didn't find a suitable start point so return end of CD
  129. //
  130. return lpInfo->msfEnd;
  131. } else {
  132. //
  133. // We don't get here if this was already the last track
  134. //
  135. msfStart = lpInfo->Track[tracknum + 1].msfStart;
  136. }
  137. }
  138. //
  139. // Exhausted all tracks ?
  140. //
  141. if (tracknum + lpInfo->FirstTrack >= lpInfo->LastTrack) {
  142. return lastaudio;
  143. }
  144. }
  145. }
  146. /***************************************************************************
  147. @doc EXTERNAL
  148. @api WORD | CdGetNumDrives | Get the number of CD-ROM drives in
  149. the system.
  150. @rdesc The return value is the number of drives available.
  151. @comm It is assumed that all CD-ROM drives have audio capability,
  152. but this may not be true and consequently later calls which
  153. try to play audio CDs on those drives may fail. It takes a
  154. fairly bad user to put an audio CD in a drive not connected
  155. up to play audio.
  156. ***************************************************************************/
  157. int CdGetNumDrives(void)
  158. {
  159. TCHAR cDrive;
  160. LPCDINFO lpInfo;
  161. TCHAR szName[ANSI_NAME_SIZE];
  162. DWORD dwLogicalDrives;
  163. dprintf2(("CdGetNumDrives"));
  164. if (NumDrives == 0) {
  165. //
  166. // We start with the name A: and work up to Z: or until we have
  167. // accumulated MCIRBOOK_MAX_DRIVES drives.
  168. //
  169. lpInfo = CdInfo;
  170. lstrcpy(szName, TEXT("?:\\"));
  171. for (cDrive = TEXT('A'), dwLogicalDrives = GetLogicalDrives();
  172. NumDrives < MCIRBOOK_MAX_DRIVES && cDrive <= TEXT('Z');
  173. cDrive++) {
  174. szName[0] = cDrive;
  175. if (dwLogicalDrives & (1 << (cDrive - TEXT('A'))) &&
  176. GetDriveType(szName) == DRIVE_CDROM)
  177. {
  178. lpInfo->cDrive = cDrive;
  179. NumDrives++;
  180. lpInfo++; // Move on to next device info structure
  181. dprintf2(("CdGetNumDrives - %c: is a CDROM drive", cDrive));
  182. }
  183. }
  184. }
  185. return NumDrives;
  186. }
  187. /***************************************************************************
  188. @doc EXTERNAL
  189. @api HCD | CdOpen | Open a drive.
  190. @parm int | Drive | The drive number to open.
  191. @rdesc
  192. If the drive exists and is available then the return value is TRUE.
  193. If no drive exists, it is unavavilable, already open or an error
  194. occurs then the return value is set to FALSE.
  195. @comm
  196. The CdInfo slot for this drive is initialized if the open is
  197. successful.
  198. ***************************************************************************/
  199. BOOL CdOpen(int Drive)
  200. {
  201. LPCDINFO lpInfo;
  202. TCHAR szName[ANSI_NAME_SIZE];
  203. //
  204. // Check the drive number is valid
  205. //
  206. if (Drive > NumDrives || Drive < 0) {
  207. dprintf1(("Drive %u is invalid", Drive));
  208. return FALSE;
  209. }
  210. lpInfo = &CdInfo[Drive];
  211. //
  212. // See if it's already open
  213. // CONSIDER: Do shareable support code here
  214. //
  215. if (lpInfo->hDevice != NULL) {
  216. dprintf2(("Drive %u (%c) is being opened recursively - %d users",
  217. Drive, (char)(lpInfo->cDrive), lpInfo->NumberOfUsers + 1));
  218. lpInfo->NumberOfUsers++;
  219. return TRUE;
  220. }
  221. //
  222. // Make sure it really is a CDROM drive
  223. //
  224. lstrcpy(szName, TEXT("?:\\"));
  225. szName[0] = lpInfo->cDrive;
  226. if (GetDriveType(szName) != DRIVE_CDROM)
  227. {
  228. dprintf2(("CdOpen - Error, Drive %u, Letter = %c: is not a CDROM drive",
  229. Drive, (char)(lpInfo->cDrive)));
  230. return FALSE;
  231. }
  232. //
  233. // open the device driver
  234. //
  235. lpInfo->hDevice = cdOpenDeviceDriver(lpInfo->cDrive, GENERIC_READ);
  236. if (lpInfo->hDevice == NULL) {
  237. dprintf2(("Failed to open %c:", (char)(lpInfo->cDrive)));
  238. return FALSE;
  239. }
  240. //
  241. // reset the TOC valid indicator
  242. //
  243. lpInfo->bTOCValid = FALSE;
  244. lpInfo->fPrevStatus = 0;
  245. lpInfo->fPrevSeekTime = 0;
  246. lpInfo->VolChannels[0] = 0xFF;
  247. lpInfo->VolChannels[1] = 0xFF;
  248. lpInfo->VolChannels[2] = 0xFF;
  249. lpInfo->VolChannels[3] = 0xFF;
  250. //
  251. // Device now in use
  252. //
  253. lpInfo->NumberOfUsers = 1;
  254. #if 0 // unnecessary and slows down media player startup
  255. //
  256. // Get the TOC if it's available (helps with the problems with the
  257. // Pioneer DRM-600 drive not being ready until the TOC has been read).
  258. //
  259. cdGetDiskInfo(lpInfo);
  260. #endif
  261. return TRUE;
  262. }
  263. /***************************************************************************
  264. @doc EXTERNAL
  265. @api BOOL | CdClose | Close a drive.
  266. @parm HCD | hCD | The handle of a currently open drive.
  267. @rdesc The return value is TRUE if the drive is closed, FALSE
  268. if the drive was not open or some other error occured.
  269. ***************************************************************************/
  270. BOOL CdClose(HCD hCD)
  271. {
  272. LPCDINFO lpInfo;
  273. lpInfo = (LPCDINFO) hCD;
  274. dprintf2(("CdClose(%08XH)", hCD));
  275. if (lpInfo == NULL) {
  276. dprintf1(("CdClose, NULL info pointer"));
  277. return FALSE;
  278. }
  279. lpInfo->fPrevStatus = 0;
  280. if (lpInfo->hDevice == NULL) {
  281. dprintf1(("CdClose, Attempt to close unopened device"));
  282. return FALSE;
  283. }
  284. if (lpInfo->NumberOfUsers == 0)
  285. {
  286. dprintf2(("CdClose (%c), number of users already = 0",
  287. (char)(lpInfo->cDrive)));
  288. }
  289. else if (--lpInfo->NumberOfUsers == 0)
  290. {
  291. cdCloseDeviceDriver(lpInfo->hDevice);
  292. lpInfo->hDevice = (HANDLE) 0;
  293. }
  294. else
  295. {
  296. dprintf2(("CdClose (%c), Device still open with %d users",
  297. (char)(lpInfo->cDrive), lpInfo->NumberOfUsers));
  298. }
  299. return TRUE;
  300. }
  301. /***************************************************************************
  302. @doc EXTERNAL
  303. @api BOOL | CdReload | Reload Device
  304. @parm HCD | hCD | The handle of a currently open drive.
  305. @rdesc The return value is TRUE if the drive tray is reloaded
  306. ***************************************************************************/
  307. BOOL CdReload (LPCDINFO lpInfo)
  308. {
  309. DWORD ii;
  310. DWORD index;
  311. HANDLE hNewDevice;
  312. if (!lpInfo)
  313. {
  314. dprintf2(("CdReload, NULL info pointer"));
  315. return FALSE;
  316. }
  317. //
  318. // Reload Device
  319. // Note: Don't close old device til we have a new device
  320. // so we don't hose any users out there
  321. //
  322. EnterCrit (lpInfo->DeviceCritSec);
  323. // Make sure we have an open device
  324. if (NULL == lpInfo->hDevice)
  325. {
  326. dprintf2(("CdReload, Attempt to reload unopened device"));
  327. LeaveCrit (lpInfo->DeviceCritSec);
  328. return FALSE;
  329. }
  330. // Open New Device
  331. hNewDevice = cdOpenDeviceDriver(lpInfo->cDrive, GENERIC_READ);
  332. if (NULL == hNewDevice)
  333. {
  334. dprintf2(("CdReload, Failed to reload driver"));
  335. LeaveCrit (lpInfo->DeviceCritSec);
  336. return FALSE;
  337. }
  338. // Close Old Device
  339. cdCloseDeviceDriver(lpInfo->hDevice);
  340. // Assign New Device
  341. lpInfo->hDevice = hNewDevice;
  342. //lpInfo->fPrevStatus = 0;
  343. //
  344. // reset the TOC valid indicator
  345. //
  346. lpInfo->bTOCValid = FALSE;
  347. LeaveCrit (lpInfo->DeviceCritSec);
  348. // Succesfully Reloaded
  349. return TRUE;
  350. } // End CdReload
  351. /***************************************************************************
  352. @doc EXTERNAL
  353. @api BOOL | CdReady | Test if a CD is ready.
  354. @parm HCD | hCD | The handle of a currently open drive.
  355. @rdesc The return value is TRUE if the drive has a disk in it
  356. and we have read the TOC. It is FALSE if the drive is not
  357. ready or we cannot read the TOC.
  358. ***************************************************************************/
  359. BOOL CdReady(HCD hCD)
  360. {
  361. LPCDINFO lpInfo;
  362. dprintf2(("CdReady(%08XH)", hCD));
  363. lpInfo = (LPCDINFO) hCD;
  364. //
  365. // Check a disk is in the drive and the door is shut and
  366. // we have a valid table of contents
  367. //
  368. return ERROR_SUCCESS == cdIoctl(lpInfo,
  369. IOCTL_CDROM_CHECK_VERIFY,
  370. NULL,
  371. 0);
  372. }
  373. /***************************************************************************
  374. @doc EXTERNAL
  375. @api BOOL | CdTrayClosed | Test what state a CD is in.
  376. @parm HCD | hCD | The handle of a currently open drive.
  377. @rdesc The return value is TRUE if the drive tray is closed
  378. ***************************************************************************/
  379. BOOL CdTrayClosed(HCD hCD)
  380. {
  381. LPCDINFO lpInfo;
  382. DWORD dwError;
  383. dprintf2(("CdTrayClosed(%08XH)", hCD));
  384. lpInfo = (LPCDINFO) hCD;
  385. //
  386. // Check a disk is in the drive and the door is shut.
  387. //
  388. dwError = cdIoctl(lpInfo, IOCTL_CDROM_CHECK_VERIFY, NULL, 0);
  389. switch (dwError)
  390. {
  391. case ERROR_NO_MEDIA_IN_DRIVE:
  392. case ERROR_UNRECOGNIZED_MEDIA:
  393. case ERROR_NOT_READY:
  394. return FALSE;
  395. default:
  396. return TRUE;
  397. }
  398. }
  399. /***************************************************************************
  400. @doc INTERNAL
  401. @api DWORD | cdGetDiskInfo | Read the disk info and TOC
  402. @parm LPCDINFO | lpInfo | Pointer to a CDINFO structure.
  403. @rdesc The return value is ERROR_SUCCESS if the info is read ok,
  404. otherwise the NT status code if not.
  405. ***************************************************************************/
  406. DWORD cdGetDiskInfo(LPCDINFO lpInfo)
  407. {
  408. CDROM_TOC Toc;
  409. int i;
  410. PTRACK_DATA pTocTrack;
  411. LPTRACK_INFO pLocalTrack;
  412. DWORD Status;
  413. UCHAR TrackNumber;
  414. dprintf2(("cdGetDiskInfo(%08XH)", lpInfo));
  415. #if 0 // If the app doesn't poll we may miss a disk change
  416. //
  417. // If the TOC is valid already then don't read it
  418. //
  419. if (lpInfo->bTOCValid) {
  420. return TRUE;
  421. }
  422. #endif
  423. //
  424. // Read the table of contents (TOC)
  425. //
  426. FillMemory(&Toc, sizeof(Toc), 0xFF);
  427. Status = cdIoctl(lpInfo, IOCTL_CDROM_READ_TOC, &Toc, sizeof(Toc));
  428. if (ERROR_SUCCESS != Status) {
  429. dprintf2(("cdGetDiskInfo - Failed to read TOC"));
  430. return Status;
  431. }
  432. #ifdef DBG
  433. dprintf4((" TOC..."));
  434. dprintf4((" Length[0] %02XH", Toc.Length[0]));
  435. dprintf4((" Length[1] %02XH", Toc.Length[1]));
  436. dprintf4((" FirstTrack %u", Toc.FirstTrack));
  437. dprintf4((" LastTrack %u", Toc.LastTrack));
  438. dprintf4((" Track info..."));
  439. for (i=0; i<20; i++) {
  440. dprintf4((" Track: %03u, Ctrl: %02XH, MSF: %02d %02d %02d",
  441. Toc.TrackData[i].TrackNumber,
  442. Toc.TrackData[i].Control,
  443. Toc.TrackData[i].Address[1],
  444. Toc.TrackData[i].Address[2],
  445. Toc.TrackData[i].Address[3]));
  446. }
  447. #endif
  448. //
  449. // Avoid problems with bad CDs
  450. //
  451. if (Toc.FirstTrack == 0) {
  452. return ERROR_INVALID_DATA;
  453. }
  454. if (Toc.LastTrack > MAXIMUM_NUMBER_TRACKS - 1) {
  455. Toc.LastTrack = MAXIMUM_NUMBER_TRACKS - 1;
  456. }
  457. #if USE_PROPER_BLUE_BOOK_HACK
  458. #else // !(USE_PROPER_BLUE_BOOK_HACK)
  459. //
  460. // hide the data track on Enhanced CDs (CD+).
  461. //
  462. for (i=0; i < (Toc.LastTrack - Toc.FirstTrack + 1); i++) {
  463. if (Toc.TrackData[i].Control & AUDIO_DATA_TRACK) {
  464. //
  465. // if first track, just exit out.
  466. //
  467. if (i == 0) {
  468. i = Toc.LastTrack+1;
  469. } else {
  470. //
  471. // remove one track from the TOC
  472. //
  473. Toc.LastTrack -= 1;
  474. //
  475. // knock 2.5 minutes off the current track to
  476. // hide the final leadin and make it the lead-out
  477. // track
  478. //
  479. Toc.TrackData[i].Address[1] -= 2;
  480. Toc.TrackData[i].Address[2] += 30;
  481. if (Toc.TrackData[i].Address[2] < 60) {
  482. Toc.TrackData[i].Address[1] -= 1;
  483. } else {
  484. Toc.TrackData[i].Address[2] -= 60;
  485. }
  486. Toc.TrackData[i].TrackNumber = 0xAA;
  487. }
  488. }
  489. }
  490. #ifdef DBG
  491. dprintf4((" TOC (munged)..."));
  492. dprintf4((" Length[0] %02XH", Toc.Length[0]));
  493. dprintf4((" Length[1] %02XH", Toc.Length[1]));
  494. dprintf4((" FirstTrack %u", Toc.FirstTrack));
  495. dprintf4((" LastTrack %u", Toc.LastTrack));
  496. dprintf4((" Track info..."));
  497. for (i=0; i<20; i++) {
  498. dprintf4((" Track: %03u, Ctrl: %02XH, MSF: %02d %02d %02d",
  499. Toc.TrackData[i].TrackNumber,
  500. Toc.TrackData[i].Control,
  501. Toc.TrackData[i].Address[1],
  502. Toc.TrackData[i].Address[2],
  503. Toc.TrackData[i].Address[3]));
  504. }
  505. #endif
  506. #endif // USE_PROPER_BLUE_BOOK_HACK
  507. //
  508. // Copy the data we got back to our own cache in the format
  509. // we like it. We copy all the tracks and then use the next track
  510. // data as the end of the disk. (Lead out info).
  511. //
  512. lpInfo->FirstTrack = Toc.FirstTrack;
  513. lpInfo->LastTrack = Toc.LastTrack;
  514. pTocTrack = &Toc.TrackData[0];
  515. pLocalTrack = &(lpInfo->Track[0]);
  516. TrackNumber = lpInfo->FirstTrack;
  517. while (TrackNumber <= Toc.LastTrack) {
  518. pLocalTrack->TrackNumber = TrackNumber;
  519. if (TrackNumber != pTocTrack->TrackNumber) {
  520. dprintf2(("Track data not correct in TOC"));
  521. return ERROR_INVALID_DATA;
  522. }
  523. pLocalTrack->msfStart = MAKERED(pTocTrack->Address[1],
  524. pTocTrack->Address[2],
  525. pTocTrack->Address[3]);
  526. pLocalTrack->Ctrl = pTocTrack->Control;
  527. pTocTrack++;
  528. pLocalTrack++;
  529. TrackNumber++;
  530. }
  531. //
  532. // Save the leadout for the disc id algorithm
  533. //
  534. lpInfo->leadout = MAKERED(pTocTrack->Address[1],
  535. pTocTrack->Address[2],
  536. pTocTrack->Address[3]);
  537. //
  538. // Some CD Rom drives don't like to go right to the end
  539. // so we fake it to be 1 frames earlier
  540. //
  541. lpInfo->msfEnd = reddiff(lpInfo->leadout, 1);
  542. lpInfo->bTOCValid = TRUE;
  543. return ERROR_SUCCESS;
  544. }
  545. /***************************************************************************
  546. @doc INTERNAL
  547. @api HANDLE | cdOpenDeviceDriver | Open a device driver.
  548. @parm LPSTR | szAnsiDeviceName | The name of the device to open.
  549. @parm DWORD | Access | Access to use to open the file. This
  550. will be one of:
  551. GENERIC_READ if we want to actually operate the device
  552. FILE_READ_ATTRIBTES if we just want to see if it's there. This
  553. prevents the device from being mounted and speeds things up.
  554. @rdesc The return value is the handle to the open device or
  555. NULL if the device driver could not be opened.
  556. ***************************************************************************/
  557. HANDLE cdOpenDeviceDriver(TCHAR cDrive, DWORD Access)
  558. {
  559. HANDLE hDevice;
  560. TCHAR szDeviceName[7]; // "\\\\.\\?:"
  561. DWORD dwErr;
  562. dprintf2(("cdOpenDeviceDriver"));
  563. wsprintf(szDeviceName, TEXT("\\\\.\\%c:"), cDrive);
  564. //
  565. // have a go at opening the driver
  566. //
  567. {
  568. UINT OldErrorMode;
  569. //
  570. // We don't want to see hard error popups
  571. //
  572. OldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  573. hDevice = CreateFile( szDeviceName,
  574. Access,
  575. FILE_SHARE_READ|FILE_SHARE_WRITE,
  576. NULL,
  577. OPEN_EXISTING,
  578. FILE_ATTRIBUTE_NORMAL,
  579. NULL );
  580. if (hDevice == INVALID_HANDLE_VALUE) {
  581. hDevice = (HANDLE) 0;
  582. dwErr = GetLastError ();
  583. dprintf1(("cdOpenDeviceDriver - Failed to open device driver %c: code %d", cDrive, dwErr));
  584. }
  585. //
  586. // Restore the error mode
  587. //
  588. SetErrorMode(OldErrorMode);
  589. }
  590. return hDevice;
  591. }
  592. /***************************************************************************
  593. @doc INTERNAL
  594. @api void | cdCloseDeviceDriver | Close a device driver.
  595. @parm HANDLE | hDevice | Handle of the device to close.
  596. @rdesc There is no return value.
  597. ***************************************************************************/
  598. void cdCloseDeviceDriver(HANDLE hDevice)
  599. {
  600. DWORD status;
  601. dprintf2(("cdCloseDeviceDriver"));
  602. if (hDevice == NULL) {
  603. dprintf1(("Attempt to close NULL handle"));
  604. }
  605. status = CloseHandle(hDevice);
  606. if (!status) {
  607. dprintf1(("cdCloseDeviceDriver - Failed to close device. Error %08XH", GetLastError()));
  608. }
  609. }
  610. /***************************************************************************
  611. @doc INTERNAL
  612. @api DWORD | cdIoctl | Send an IOCTL request to the device driver.
  613. @parm LPCDINFO | lpInfo | Pointer to a CDINFO structure.
  614. @parm DWORD | Request | The IOCTL request code.
  615. @parm PVOID | lpData | Pointer to a data structure to be passed.
  616. @parm DWORD | dwSize | The length of the data strucure.
  617. @comm This function returns the disk status
  618. @rdesc The return value is the status value returned from the
  619. call to DeviceIoControl
  620. ***************************************************************************/
  621. DWORD cdIoctl(LPCDINFO lpInfo, DWORD Request, PVOID lpData, DWORD dwSize)
  622. {
  623. DWORD Status;
  624. Status = cdIoctlData(lpInfo, Request, lpData, dwSize, lpData, dwSize);
  625. //if (ERROR_SUCCESS != Status && Request == IOCTL_CDROM_CHECK_VERIFY) {
  626. // lpInfo->bTOCValid = FALSE;
  627. //}
  628. return Status;
  629. }
  630. /***************************************************************************
  631. @doc INTERNAL
  632. @api DWORD | cdIoctlData | Send an IOCTL request to the device driver.
  633. @parm LPCDINFO | lpInfo | Pointer to a CDINFO structure.
  634. @parm DWORD | Request | The IOCTL request code.
  635. @parm PVOID | lpData | Pointer to a data structure to be passed.
  636. @parm DWORD | dwSize | The length of the data strucure.
  637. @parm PVOID | lpOutput | Our output data
  638. @parm DWORD | OutputSize | Our output data (maximum) size
  639. @comm This function returns the disk status
  640. @rdesc The return value is the status value returned from the
  641. call to DeviceIoControl
  642. ***************************************************************************/
  643. DWORD cdIoctlData(LPCDINFO lpInfo, DWORD Request, PVOID lpData,
  644. DWORD dwSize, PVOID lpOutput, DWORD OutputSize)
  645. {
  646. BOOL status;
  647. UINT OldErrorMode;
  648. DWORD BytesReturned;
  649. DWORD dwError = ERROR_SUCCESS;
  650. BOOL fTryAgain;
  651. dprintf3(("cdIoctl(%08XH, %08XH, %08XH, %08XH", lpInfo, Request, lpData, dwSize));
  652. if (!lpInfo->hDevice) {
  653. dprintf1(("cdIoctlData - Device not open"));
  654. return ERROR_INVALID_FUNCTION;
  655. }
  656. #if DBG
  657. switch (Request) {
  658. case IOCTL_CDROM_READ_TOC:
  659. dprintf3(("IOCTL_CDROM_READ_TOC"));
  660. break;
  661. case IOCTL_CDROM_PLAY_AUDIO_MSF:
  662. dprintf3(("IOCTL_CDROM_PLAY_AUDIO_MSF"));
  663. break;
  664. case IOCTL_CDROM_SEEK_AUDIO_MSF:
  665. dprintf3(("IOCTL_CDROM_SEEK_AUDIO_MSF"));
  666. break;
  667. case IOCTL_CDROM_STOP_AUDIO:
  668. dprintf3(("IOCTL_CDROM_STOP_AUDIO"));
  669. break;
  670. case IOCTL_CDROM_PAUSE_AUDIO:
  671. dprintf3(("IOCTL_CDROM_PAUSE_AUDIO"));
  672. break;
  673. case IOCTL_CDROM_RESUME_AUDIO:
  674. dprintf3(("IOCTL_CDROM_RESUME_AUDIO"));
  675. break;
  676. case IOCTL_CDROM_GET_VOLUME:
  677. dprintf3(("IOCTL_CDROM_SET_VOLUME"));
  678. break;
  679. case IOCTL_CDROM_SET_VOLUME:
  680. dprintf3(("IOCTL_CDROM_GET_VOLUME"));
  681. break;
  682. case IOCTL_CDROM_READ_Q_CHANNEL:
  683. dprintf3(("IOCTL_CDROM_READ_Q_CHANNEL"));
  684. break;
  685. case IOCTL_CDROM_CHECK_VERIFY:
  686. dprintf3(("IOCTL_CDROM_CHECK_VERIFY"));
  687. break;
  688. }
  689. #endif // DBG
  690. //
  691. // We don't want to see hard error popups
  692. //
  693. fTryAgain = TRUE;
  694. while (fTryAgain)
  695. {
  696. fTryAgain = FALSE;
  697. OldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  698. status = DeviceIoControl(lpInfo->hDevice,
  699. Request,
  700. lpData,
  701. dwSize,
  702. lpOutput,
  703. OutputSize,
  704. &BytesReturned,
  705. NULL);
  706. //
  707. // Restore the error mode
  708. //
  709. SetErrorMode(OldErrorMode);
  710. // Check for Failure
  711. if (!status)
  712. {
  713. dwError = GetLastError();
  714. if (dwError == ERROR_MEDIA_CHANGED)
  715. {
  716. dprintf2(("Error Media Changed"));
  717. }
  718. //
  719. // Treat anything bad as invalidating our TOC. Some of the things
  720. // we call are invalid on some devices so don't count those. Also
  721. // the device can be 'busy' while playing so don't count that case
  722. // either.
  723. //
  724. if (Request == IOCTL_CDROM_CHECK_VERIFY)
  725. {
  726. lpInfo->bTOCValid = FALSE;
  727. switch (dwError)
  728. {
  729. case ERROR_MEDIA_CHANGED:
  730. dprintf2(("Error Media Changed, Reloading Device"));
  731. // Reload new Device
  732. if (CdReload (lpInfo))
  733. fTryAgain = TRUE;
  734. break;
  735. case ERROR_INVALID_FUNCTION:
  736. case ERROR_BUSY:
  737. default:
  738. break;
  739. }
  740. }
  741. #if DBG
  742. dprintf1(("IOCTL %8XH Status: %08XH", Request, dwError));
  743. #endif
  744. }
  745. } // End While (fTryAgain)
  746. return dwError;
  747. }
  748. /***************************************************************************
  749. @doc EXTERNAL
  750. @api BOOL | CdPlay | Play a section of the CD
  751. @parm HCD | hCD | The handle of a currently open drive.
  752. @parm MSF | msfStart | Where to start
  753. @parm MSF | msfEnd | Where to end
  754. @rdesc The return value is TRUE if the drive is play is started,
  755. FALSE if not.
  756. ***************************************************************************/
  757. BOOL CdPlay(HCD hCD, MSF msfStart, MSF msfEnd)
  758. {
  759. LPCDINFO lpInfo;
  760. CDROM_PLAY_AUDIO_MSF msfPlay;
  761. BOOL fResult;
  762. dprintf2(("CdPlay(%08XH, %08XH, %08XH)", hCD, msfStart, msfEnd));
  763. lpInfo = (LPCDINFO) hCD;
  764. msfStart = CdFindAudio(lpInfo, msfStart);
  765. //
  766. // If the start is now beyond the end then don't play anything
  767. //
  768. if (msfStart > msfEnd) {
  769. return TRUE;
  770. }
  771. //
  772. // Set up the data for the call to the driver
  773. //
  774. msfPlay.StartingM = REDMINUTE(msfStart);
  775. msfPlay.StartingS = REDSECOND(msfStart);
  776. msfPlay.StartingF = REDFRAME(msfStart);
  777. msfPlay.EndingM = REDMINUTE(msfEnd);
  778. msfPlay.EndingS = REDSECOND(msfEnd);
  779. msfPlay.EndingF = REDFRAME(msfEnd);
  780. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo,
  781. IOCTL_CDROM_PLAY_AUDIO_MSF,
  782. &msfPlay,
  783. sizeof(msfPlay)));
  784. if (fResult)
  785. {
  786. lpInfo->fPrevSeekTime = msfStart;
  787. }
  788. return fResult;
  789. }
  790. /***************************************************************************
  791. @doc EXTERNAL
  792. @api BOOL | CdSeek | Seek to a given part of the CD
  793. @parm HCD | hCD | The handle of a currently open drive.
  794. @parm MSF | msf | Where to seek to
  795. @rdesc The return value is TRUE if the seek is successful,
  796. FALSE if not.
  797. ***************************************************************************/
  798. BOOL CdSeek(HCD hCD, MSF msf, BOOL fForceAudio)
  799. {
  800. LPCDINFO lpInfo;
  801. CDROM_SEEK_AUDIO_MSF msfSeek;
  802. BOOL fResult;
  803. dprintf2(("CdSeek(%08XH, %08XH) Forcing search for audio: %d", hCD, msf, fForceAudio));
  804. lpInfo = (LPCDINFO) hCD;
  805. //
  806. // Only seek to audio
  807. //
  808. if (fForceAudio) { // On a seek to END or seek to START command
  809. msf = CdFindAudio(lpInfo, msf);
  810. dprintf2(("Cd Seek changed msf to %08XH", msf));
  811. } else {
  812. if (msf != CdFindAudio(lpInfo, msf)) {
  813. return TRUE;
  814. }
  815. }
  816. #if 1
  817. msfSeek.M = REDMINUTE(msf);
  818. msfSeek.S = REDSECOND(msf);
  819. msfSeek.F = REDFRAME(msf);
  820. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_SEEK_AUDIO_MSF,
  821. &msfSeek, sizeof(msfSeek)));
  822. if (fResult)
  823. {
  824. lpInfo->fPrevSeekTime = msf;
  825. }
  826. return fResult;
  827. #else
  828. //
  829. // This is a hideous hack to make more drives work. It uses the
  830. // method originally used by Cd player to seek - viz play from
  831. // the requested position and immediatly pause
  832. //
  833. return CdPlay(hCD, msf, redadd(lpInfo->msfEnd,1)) || CdPause(hCD);
  834. #endif
  835. }
  836. /***************************************************************************
  837. @doc EXTERNAL
  838. @api MSF | CdTrackStart | Get the start time of a track.
  839. @parm HCD | hCD | The handle of a currently open drive.
  840. @parm UCHAR | Track | The track number.
  841. @rdesc The return value is the start time of the track expressed
  842. in MSF or INVALID_TRACK if the track number is not in the TOC.
  843. ***************************************************************************/
  844. MSF CdTrackStart(HCD hCD, UCHAR Track)
  845. {
  846. LPCDINFO lpInfo;
  847. LPTRACK_INFO lpTrack;
  848. dprintf2(("CdTrackStart(%08XH, %u)", hCD, Track));
  849. lpInfo = (LPCDINFO) hCD;
  850. //
  851. // We may need to read the TOC because we're not doing it on open
  852. // any more
  853. //
  854. if (!lpInfo->bTOCValid && CdNumTracks(hCD) == 0) {
  855. dprintf1(("TOC not valid"));
  856. return INVALID_TRACK;
  857. }
  858. if ((Track < lpInfo->FirstTrack) || (Track > lpInfo->LastTrack)) {
  859. dprintf1(("Track number out of range"));
  860. return INVALID_TRACK;
  861. }
  862. // search for the track info in the TOC
  863. lpTrack = lpInfo->Track;
  864. while (lpTrack->TrackNumber != Track) lpTrack++;
  865. return lpTrack->msfStart;
  866. }
  867. /***************************************************************************
  868. @doc EXTERNAL
  869. @api MSF | CdTrackLength | Get the length of a track.
  870. @parm HCD | hCD | The handle of a currently open drive.
  871. @parm UCHAR | Track | The track number.
  872. @rdesc The return value is the start time of the track expressed
  873. in MSF or INVALID_TRACK if the track number is not in the TOC.
  874. ***************************************************************************/
  875. MSF CdTrackLength(HCD hCD, UCHAR Track)
  876. {
  877. LPCDINFO lpInfo;
  878. MSF TrackStart;
  879. MSF NextTrackStart;
  880. lpInfo = (LPCDINFO) hCD;
  881. dprintf2(("CdTrackLength(%08XH, %u)", hCD, Track));
  882. //
  883. // Get the start of this track
  884. //
  885. TrackStart = CdTrackStart(hCD, Track);
  886. if (TrackStart == INVALID_TRACK) {
  887. return INVALID_TRACK;
  888. }
  889. if (Track == lpInfo->LastTrack) {
  890. return reddiff(lpInfo->msfEnd, TrackStart);
  891. } else {
  892. NextTrackStart = CdTrackStart(hCD, (UCHAR)(Track + 1));
  893. if (NextTrackStart == INVALID_TRACK) {
  894. return INVALID_TRACK;
  895. } else {
  896. MSF length = reddiff(NextTrackStart, TrackStart);
  897. #if USE_PROPER_BLUE_BOOK_HACK
  898. if ((CdTrackType(hCD, Track) == MCI_CDA_TRACK_AUDIO) &&
  899. (CdTrackType(hCD, Track+1) != MCI_CDA_TRACK_AUDIO)) {
  900. //
  901. // Blue Book CD HACK -- subtract the 2:30 of lead-in that
  902. // precedes the data track to get the proper track length
  903. // for Blue Book CDs.
  904. //
  905. length = reddiff(length, MAKERED(2,30,0));
  906. }
  907. #endif // USE_PROPER_BLUE_BOOK_HACK
  908. return length;
  909. }
  910. }
  911. }
  912. /***************************************************************************
  913. @doc EXTERNAL
  914. @api MSF | CdTrackType | Get the type of a track.
  915. @parm HCD | hCD | The handle of a currently open drive.
  916. @parm UCHAR | Track | The track number.
  917. @rdesc The return value is either MCI_TRACK_AUDIO or MCI_TRACK_OTHER.
  918. ***************************************************************************/
  919. int CdTrackType(HCD hCD, UCHAR Track)
  920. {
  921. LPCDINFO lpInfo;
  922. lpInfo = (LPCDINFO) hCD;
  923. dprintf2(("CdTrackType(%08XH, %u)", hCD, Track));
  924. if ( INVALID_TRACK == CdTrackStart(hCD, (UCHAR)Track) ) {
  925. return INVALID_TRACK;
  926. }
  927. if ( lpInfo->Track[Track-lpInfo->FirstTrack].Ctrl & IS_DATA_TRACK) {
  928. return MCI_CDA_TRACK_OTHER;
  929. }
  930. return MCI_CDA_TRACK_AUDIO;
  931. }
  932. /***************************************************************************
  933. @doc EXTERNAL
  934. @api MSF | CdPosition | Get the current position.
  935. @parm HCD | hCD | The handle of a currently open drive.
  936. @parm MSF * | tracktime | position in MSF (track relative)
  937. @parm MSF * | disktime | position in MSF (disk relative)
  938. @rdesc TRUE if position returned correctly (in tracktime and disktime).
  939. FALSE otherwise.
  940. If the device does not support query of the position then
  941. we return position 0.
  942. ***************************************************************************/
  943. BOOL CdPosition(HCD hCD, MSF *tracktime, MSF *disktime)
  944. {
  945. LPCDINFO lpInfo;
  946. SUB_Q_CHANNEL_DATA sqd;
  947. CDROM_SUB_Q_DATA_FORMAT Format;
  948. MSF msfPos;
  949. int tries;
  950. DWORD dwReturn;
  951. UCHAR fStatus;
  952. UCHAR fCode;
  953. UCHAR cTrack;
  954. dprintf2(("CdPosition(%08XH)", hCD));
  955. Format.Format = IOCTL_CDROM_CURRENT_POSITION;
  956. lpInfo = (LPCDINFO) hCD;
  957. for (tries=0; tries<10; tries++)
  958. {
  959. memset(&sqd, 0xFF, sizeof(sqd));
  960. dwReturn = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  961. &Format, sizeof(Format), &sqd, sizeof(sqd));
  962. switch (dwReturn)
  963. {
  964. case ERROR_SUCCESS:
  965. fStatus = sqd.CurrentPosition.Header.AudioStatus;
  966. fCode = sqd.CurrentPosition.FormatCode;
  967. cTrack = sqd.CurrentPosition.TrackNumber;
  968. // Save previous audio status to prevent bug
  969. CdSetAudioStatus (hCD, fStatus);
  970. // If the track > 100 (outside spec'ed range)
  971. // OR track > last track number
  972. // then display an error message
  973. if ((fCode == 0x01) &&
  974. ( (100 < cTrack) ||
  975. ((lpInfo->bTOCValid) && (lpInfo->LastTrack < cTrack))) &&
  976. (tries<9)) {
  977. // Always display this message on checked builds.
  978. // We need some feeling for how often this happens
  979. // It should NEVER happen, but (at least for NEC
  980. // drives) we see it after a seek to end
  981. dprintf1(("CDIoctlData returned track==%d, retrying", cTrack));
  982. continue;
  983. }
  984. break;
  985. case ERROR_INVALID_FUNCTION:
  986. dprintf2(("CdPositon - Error Invalid Function"));
  987. *tracktime = REDTH(0, 1);
  988. *disktime = REDTH(0, 0);
  989. return TRUE;
  990. default:
  991. dprintf1(("CdPosition - Failed to get Q channel data"));
  992. return FALSE;
  993. }
  994. dprintf4(("Status = %02X, Length[0] = %02X, Length[1] = %02X",
  995. fStatus,
  996. sqd.CurrentPosition.Header.DataLength[0],
  997. sqd.CurrentPosition.Header.DataLength[1]));
  998. dprintf4((" Format %02XH", fCode));
  999. dprintf4((" Absolute Address %02X%02X%02X%02XH",
  1000. sqd.CurrentPosition.AbsoluteAddress[0],
  1001. sqd.CurrentPosition.AbsoluteAddress[1],
  1002. sqd.CurrentPosition.AbsoluteAddress[2],
  1003. sqd.CurrentPosition.AbsoluteAddress[3]));
  1004. dprintf4((" Relative Address %02X%02X%02X%02XH",
  1005. sqd.CurrentPosition.TrackRelativeAddress[0],
  1006. sqd.CurrentPosition.TrackRelativeAddress[1],
  1007. sqd.CurrentPosition.TrackRelativeAddress[2],
  1008. sqd.CurrentPosition.TrackRelativeAddress[3]));
  1009. if (fCode == 0x01) { // MSF format ?
  1010. // data is current position
  1011. msfPos = MAKERED(sqd.CurrentPosition.AbsoluteAddress[1],
  1012. sqd.CurrentPosition.AbsoluteAddress[2],
  1013. sqd.CurrentPosition.AbsoluteAddress[3]);
  1014. if (msfPos == 0)
  1015. {
  1016. //
  1017. // If position is 0 (this seems to happen on the Toshiba) then
  1018. // we'll return our last valid seek position
  1019. //
  1020. MSF msfStart;
  1021. MSF msfRel;
  1022. msfPos = lpInfo->fPrevSeekTime;
  1023. if (CdGetTrack (lpInfo, msfPos, &cTrack, &msfStart))
  1024. {
  1025. if (msfStart <= msfPos)
  1026. msfRel = msfPos - msfStart;
  1027. else
  1028. msfRel = 0;
  1029. *disktime = REDTH(msfPos, cTrack);
  1030. *tracktime = REDTH(msfRel, cTrack);
  1031. return TRUE;
  1032. }
  1033. else
  1034. {
  1035. continue;
  1036. }
  1037. }
  1038. else
  1039. {
  1040. dprintf4(("CdPosition - MSF disk pos: %u, %u, %u",
  1041. REDMINUTE(msfPos), REDSECOND(msfPos), REDFRAME(msfPos)));
  1042. *disktime = REDTH(msfPos, cTrack);
  1043. // data is current position
  1044. msfPos = MAKERED(sqd.CurrentPosition.TrackRelativeAddress[1],
  1045. sqd.CurrentPosition.TrackRelativeAddress[2],
  1046. sqd.CurrentPosition.TrackRelativeAddress[3]);
  1047. dprintf4(("CdPosition - MSF track pos (t,m,s,f): %u, %u, %u, %u",
  1048. cTrack, REDMINUTE(msfPos), REDSECOND(msfPos), REDFRAME(msfPos)));
  1049. *tracktime = REDTH(msfPos, cTrack);
  1050. return TRUE;
  1051. }
  1052. }
  1053. }
  1054. dprintf1(("CdPosition - Failed to read cd position"));
  1055. return FALSE;
  1056. }
  1057. /***************************************************************************
  1058. @doc EXTERNAL
  1059. @api MSF | CdDiskEnd | Get the position of the end of the disk.
  1060. @parm HCD | hCD | The handle of a currently open drive.
  1061. @rdesc The return value is the end position expressed
  1062. in MSF or INVALID_TRACK if an error occurs.
  1063. ***************************************************************************/
  1064. MSF CdDiskEnd(HCD hCD)
  1065. {
  1066. LPCDINFO lpInfo;
  1067. lpInfo = (LPCDINFO) hCD;
  1068. return lpInfo->msfEnd;
  1069. }
  1070. /***************************************************************************
  1071. @doc EXTERNAL
  1072. @api MSF | CdDiskLength | Get the length of the disk.
  1073. @parm HCD | hCD | The handle of a currently open drive.
  1074. @rdesc The return value is the length expressed
  1075. in MSF or INVALID_TRACK if an error occurs.
  1076. ***************************************************************************/
  1077. MSF CdDiskLength(HCD hCD)
  1078. {
  1079. LPCDINFO lpInfo;
  1080. MSF FirstTrackStart;
  1081. lpInfo = (LPCDINFO) hCD;
  1082. FirstTrackStart = CdTrackStart(hCD, lpInfo->FirstTrack);
  1083. if (FirstTrackStart == INVALID_TRACK) {
  1084. return INVALID_TRACK;
  1085. } else {
  1086. return reddiff(lpInfo->msfEnd, FirstTrackStart);
  1087. }
  1088. }
  1089. /***************************************************************************
  1090. @doc EXTERNAL
  1091. @api DWORD | CdStatus | Get the disk status.
  1092. @parm HCD | hCD | The handle of a currently open drive.
  1093. @rdesc The return value is the current audio status.
  1094. ***************************************************************************/
  1095. DWORD CdStatus(HCD hCD)
  1096. {
  1097. LPCDINFO lpInfo;
  1098. SUB_Q_CHANNEL_DATA sqd;
  1099. CDROM_SUB_Q_DATA_FORMAT Format;
  1100. DWORD CheckStatus;
  1101. DWORD ReadStatus;
  1102. UCHAR fStatus;
  1103. lpInfo = (LPCDINFO) hCD;
  1104. dprintf2(("CdStatus(%08XH)", hCD));
  1105. Format.Format = IOCTL_CDROM_CURRENT_POSITION;
  1106. FillMemory((PVOID)&sqd, sizeof(sqd), 0xFF);
  1107. //
  1108. // Check the disk status as well because IOCTL_CDROM_READ_Q_CHANNEL
  1109. // can return ERROR_SUCCESS even if there's no disk (I don't know why - or
  1110. // whether it's software bug in NT, hardware bug or valid!).
  1111. //
  1112. CheckStatus = cdIoctl(lpInfo, IOCTL_CDROM_CHECK_VERIFY, NULL, 0);
  1113. if (ERROR_SUCCESS != CheckStatus) {
  1114. return DISC_NOT_READY;
  1115. }
  1116. ReadStatus = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  1117. &Format, sizeof(Format),
  1118. &sqd, sizeof(sqd));
  1119. if (ReadStatus == ERROR_NOT_READY) {
  1120. if (ERROR_SUCCESS == cdGetDiskInfo(lpInfo)) {
  1121. ReadStatus = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  1122. &Format, sizeof(Format),
  1123. &sqd, sizeof(sqd));
  1124. }
  1125. }
  1126. if (ERROR_SUCCESS != ReadStatus) {
  1127. //
  1128. // The read Q channel command is optional
  1129. //
  1130. dprintf1(("CdStatus - Failed to get Q channel data"));
  1131. return DISC_NOT_READY;
  1132. }
  1133. // Save previous audio status to prevent bug
  1134. fStatus = sqd.CurrentPosition.Header.AudioStatus;
  1135. CdSetAudioStatus (hCD, fStatus);
  1136. dprintf4(("CdStatus - Status %02XH", fStatus));
  1137. switch (fStatus)
  1138. {
  1139. case AUDIO_STATUS_IN_PROGRESS:
  1140. dprintf4(("CdStatus - Playing"));
  1141. return DISC_PLAYING;
  1142. case AUDIO_STATUS_PAUSED:
  1143. dprintf4(("CdStatus - Paused"));
  1144. return DISC_PAUSED;
  1145. case AUDIO_STATUS_PLAY_COMPLETE:
  1146. dprintf4(("CdStatus - Stopped"));
  1147. return DISC_READY;
  1148. case AUDIO_STATUS_NO_STATUS:
  1149. // Grab previous status instead
  1150. switch (lpInfo->fPrevStatus)
  1151. {
  1152. #if 0
  1153. // NOTE: Be very careful before uncommenting the
  1154. // following 3 lines, they basically cause
  1155. // Play & wait to spin forever breaking
  1156. // "Continous Play", "Random Order" in CDPLAYER
  1157. // and MCI Play & wait commands.
  1158. // Basically, I didn't have time to track down
  1159. // the real problem. Apparently, the driver
  1160. // Is not returning AUDIO_STATUS_PLAY_COMPLETE
  1161. // when the CD reaches the end of the current
  1162. // play command. From my end, MCICDA is not
  1163. // receiving this. ChuckP says the lowlevel
  1164. // driver is generating this status correctly.
  1165. // Which I have verified
  1166. // So, the question is how is it getting lost?
  1167. case AUDIO_STATUS_IN_PROGRESS:
  1168. dprintf4(("CdStatus - Playing (Prev)"));
  1169. return DISC_PLAYING;
  1170. #endif
  1171. case AUDIO_STATUS_PAUSED:
  1172. dprintf4(("CdStatus - Paused (Prev)"));
  1173. return DISC_PAUSED;
  1174. case AUDIO_STATUS_PLAY_COMPLETE:
  1175. dprintf4(("CdStatus - Stopped (Prev)"));
  1176. return DISC_READY;
  1177. case AUDIO_STATUS_NO_STATUS:
  1178. default:
  1179. break;
  1180. } // End switch
  1181. dprintf4(("CdStatus - No status, assume stopped (Prev)"));
  1182. return DISC_READY;
  1183. //
  1184. // Some drives just return 0 sometimes - so we rely on the results of
  1185. // CHECK_VERIFY in this case
  1186. //
  1187. default:
  1188. dprintf4(("CdStatus - No status, assume Stopped"));
  1189. return DISC_READY;
  1190. }
  1191. } // End CdStatus
  1192. /***************************************************************************
  1193. @doc EXTERNAL
  1194. @api BOOL | CdEject | Eject the disk
  1195. @parm HCD | hCD | The handle of a currently open drive.
  1196. @rdesc The return value is the current status.
  1197. ***************************************************************************/
  1198. BOOL CdEject(HCD hCD)
  1199. {
  1200. LPCDINFO lpInfo;
  1201. BOOL fResult;
  1202. lpInfo = (LPCDINFO) hCD;
  1203. dprintf2(("CdEject(%08XH)", hCD));
  1204. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_EJECT_MEDIA, NULL, 0));
  1205. //if (fResult) {
  1206. // Save Audio Status to prevent bug
  1207. //lpInfo->fPrevStatus = 0;
  1208. //}
  1209. return fResult;
  1210. }
  1211. /***************************************************************************
  1212. @doc EXTERNAL
  1213. @api BOOL | CdPause | Pause the playing
  1214. @parm HCD | hCD | The handle of a currently open drive.
  1215. @rdesc The return value is the current status.
  1216. ***************************************************************************/
  1217. BOOL CdPause(HCD hCD)
  1218. {
  1219. LPCDINFO lpInfo;
  1220. lpInfo = (LPCDINFO) hCD;
  1221. dprintf2(("CdPause(%08XH)", hCD));
  1222. cdIoctl(lpInfo, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0);
  1223. //
  1224. // Ignore the return - we may have been paused or stopped already
  1225. //
  1226. return TRUE;
  1227. }
  1228. /***************************************************************************
  1229. @doc EXTERNAL
  1230. @api BOOL | CdResume | Resume the playing
  1231. @parm HCD | hCD | The handle of a currently open drive.
  1232. @rdesc The return value is the current status.
  1233. ***************************************************************************/
  1234. BOOL CdResume(HCD hCD)
  1235. {
  1236. LPCDINFO lpInfo;
  1237. lpInfo = (LPCDINFO) hCD;
  1238. dprintf2(("CdResume(%08XH)", hCD));
  1239. return ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_RESUME_AUDIO, NULL, 0);
  1240. }
  1241. /***************************************************************************
  1242. @doc EXTERNAL
  1243. @api BOOL | CdStop | Stop playing
  1244. @parm HCD | hCD | The handle of a currently open drive.
  1245. @rdesc The return value is the current status. Note that not
  1246. all devices support stop
  1247. ***************************************************************************/
  1248. BOOL CdStop(HCD hCD)
  1249. {
  1250. LPCDINFO lpInfo;
  1251. BOOL fResult;
  1252. lpInfo = (LPCDINFO) hCD;
  1253. dprintf2(("CdStop(%08XH)", hCD));
  1254. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_STOP_AUDIO, NULL, 0));
  1255. if (fResult)
  1256. {
  1257. lpInfo->fPrevStatus = AUDIO_STATUS_PLAY_COMPLETE;
  1258. }
  1259. return fResult;
  1260. }
  1261. /***************************************************************************
  1262. @doc EXTERNAL
  1263. @api BOOL | CdSetVolumeAll | Set the playing volume for all channels
  1264. @parm HCD | hCD | The handle of a currently open drive.
  1265. @parm UCHAR | Volume | The volume to set (FF = max)
  1266. @rdesc The return value is the current status.
  1267. ***************************************************************************/
  1268. BOOL CdSetVolumeAll(HCD hCD, UCHAR Volume)
  1269. {
  1270. LPCDINFO lpInfo;
  1271. VOLUME_CONTROL VolumeControl;
  1272. DWORD dwErr;
  1273. lpInfo = (LPCDINFO) hCD;
  1274. dprintf4(("CdSetVolumeAll(%08XH), Volume %u", hCD, Volume));
  1275. //
  1276. // Read the old values
  1277. //
  1278. dwErr = cdIoctl(lpInfo,
  1279. IOCTL_CDROM_GET_VOLUME,
  1280. (PVOID)&VolumeControl,
  1281. sizeof(VolumeControl));
  1282. if (dwErr != ERROR_SUCCESS)
  1283. {
  1284. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Error = %lx",
  1285. hCD, Volume, dwErr));
  1286. return FALSE;
  1287. }
  1288. // Set all channels to new volume
  1289. VolumeControl.PortVolume[0] = Volume;
  1290. VolumeControl.PortVolume[1] = Volume;
  1291. VolumeControl.PortVolume[2] = Volume;
  1292. VolumeControl.PortVolume[3] = Volume;
  1293. // Save what we think it should be
  1294. lpInfo->VolChannels[0] = Volume;
  1295. lpInfo->VolChannels[1] = Volume;
  1296. lpInfo->VolChannels[2] = Volume;
  1297. lpInfo->VolChannels[3] = Volume;
  1298. //
  1299. // Not all CDs support volume setting so don't check the return code here
  1300. //
  1301. dwErr = cdIoctl(lpInfo, IOCTL_CDROM_SET_VOLUME,
  1302. (PVOID)&VolumeControl,
  1303. sizeof(VolumeControl));
  1304. if (dwErr != ERROR_SUCCESS)
  1305. {
  1306. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Set Volume Failed (%08XH)",
  1307. hCD, Volume, dwErr));
  1308. }
  1309. #ifdef DEBUG
  1310. // Double Check
  1311. if (ERROR_SUCCESS != cdIoctl(lpInfo,
  1312. IOCTL_CDROM_GET_VOLUME,
  1313. (PVOID)&VolumeControl,
  1314. sizeof(VolumeControl)))
  1315. {
  1316. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Get Volume Failed (%08XH)",
  1317. hCD, Volume, dwErr));
  1318. }
  1319. // Compare actual to what we think it should be
  1320. if ((VolumeControl.PortVolume[0] != lpInfo->VolChannels[0]) ||
  1321. (VolumeControl.PortVolume[1] != lpInfo->VolChannels[1]) ||
  1322. (VolumeControl.PortVolume[2] != lpInfo->VolChannels[2]) ||
  1323. (VolumeControl.PortVolume[3] != lpInfo->VolChannels[3]))
  1324. {
  1325. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Channels don't match [%lx,%lx,%lx,%lx] != [%lx,%lx,%lx,%lx]",
  1326. hCD, Volume,
  1327. (DWORD)VolumeControl.PortVolume[0],
  1328. (DWORD)VolumeControl.PortVolume[1],
  1329. (DWORD)VolumeControl.PortVolume[2],
  1330. (DWORD)VolumeControl.PortVolume[3],
  1331. (DWORD)lpInfo->VolChannels[0],
  1332. (DWORD)lpInfo->VolChannels[1],
  1333. (DWORD)lpInfo->VolChannels[2],
  1334. (DWORD)lpInfo->VolChannels[3]
  1335. ));
  1336. }
  1337. #endif
  1338. return TRUE;
  1339. }
  1340. /***************************************************************************
  1341. @doc EXTERNAL
  1342. @api BOOL | CdSetVolume | Set the playing volume for one channel
  1343. @parm HCD | hCD | The handle of a currently open drive.
  1344. @parm int | Channel | The channel to set
  1345. @parm UCHAR | Volume | The volume to set (FF = max)
  1346. @rdesc The return value is the current status.
  1347. ***************************************************************************/
  1348. BOOL CdSetVolume(HCD hCD, int Channel, UCHAR Volume)
  1349. {
  1350. LPCDINFO lpInfo;
  1351. VOLUME_CONTROL VolumeControl;
  1352. DWORD dwErr;
  1353. lpInfo = (LPCDINFO) hCD;
  1354. dprintf4(("CdSetVolume(%08XH), Channel %d Volume %u", hCD, Channel, Volume));
  1355. VolumeControl.PortVolume[0] = lpInfo->VolChannels[0];
  1356. VolumeControl.PortVolume[1] = lpInfo->VolChannels[1];
  1357. VolumeControl.PortVolume[2] = lpInfo->VolChannels[2];
  1358. VolumeControl.PortVolume[3] = lpInfo->VolChannels[3];
  1359. //
  1360. // Read the old values
  1361. //
  1362. dwErr = cdIoctl(lpInfo,
  1363. IOCTL_CDROM_GET_VOLUME,
  1364. (PVOID)&VolumeControl,
  1365. sizeof(VolumeControl));
  1366. if (dwErr != ERROR_SUCCESS)
  1367. {
  1368. dprintf2(("CdSetVolume(%08XH), Channel %u, Volume %u, Error = %lx",
  1369. hCD, Channel, Volume, dwErr));
  1370. return FALSE;
  1371. }
  1372. // Check if it is already the correct value
  1373. if (VolumeControl.PortVolume[Channel] == Volume)
  1374. {
  1375. // Nothing to do
  1376. dprintf2(("CdSetVolume(%08XH), Channel %u, Volume %u, Already this volume!!!",
  1377. hCD, Channel, Volume));
  1378. return TRUE;
  1379. }
  1380. lpInfo->VolChannels[Channel] = Volume;
  1381. VolumeControl.PortVolume[Channel] = Volume;
  1382. //
  1383. // Not all CDs support volume setting so don't check the return code here
  1384. //
  1385. dwErr = cdIoctl(lpInfo, IOCTL_CDROM_SET_VOLUME,
  1386. (PVOID)&VolumeControl,
  1387. sizeof(VolumeControl));
  1388. if (dwErr != ERROR_SUCCESS)
  1389. {
  1390. dprintf2(("CdSetVolume(%08XH), Channel %d, Volume %u, Set Volume Failed (%08XH)",
  1391. hCD, Channel, Volume, dwErr));
  1392. }
  1393. #ifdef DEBUG
  1394. //
  1395. // Double Check our results
  1396. //
  1397. if (ERROR_SUCCESS != cdIoctl(lpInfo,
  1398. IOCTL_CDROM_GET_VOLUME,
  1399. (PVOID)&VolumeControl,
  1400. sizeof(VolumeControl)))
  1401. {
  1402. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Get Volume Failed (%08XH)",
  1403. hCD, Volume, dwErr));
  1404. }
  1405. // Compare actual to what we think it should be
  1406. if ((VolumeControl.PortVolume[0] != lpInfo->VolChannels[0]) ||
  1407. (VolumeControl.PortVolume[1] != lpInfo->VolChannels[1]) ||
  1408. (VolumeControl.PortVolume[2] != lpInfo->VolChannels[2]) ||
  1409. (VolumeControl.PortVolume[3] != lpInfo->VolChannels[3]))
  1410. {
  1411. dprintf2(("CdSetVolume (%08XH), Channel %u, Volume %u, Channels don't match [%lx,%lx,%lx,%lx] != [%lx,%lx,%lx,%lx]",
  1412. hCD, Channel, Volume,
  1413. (DWORD)VolumeControl.PortVolume[0],
  1414. (DWORD)VolumeControl.PortVolume[1],
  1415. (DWORD)VolumeControl.PortVolume[2],
  1416. (DWORD)VolumeControl.PortVolume[3],
  1417. (DWORD)lpInfo->VolChannels[0],
  1418. (DWORD)lpInfo->VolChannels[1],
  1419. (DWORD)lpInfo->VolChannels[2],
  1420. (DWORD)lpInfo->VolChannels[3]
  1421. ));
  1422. }
  1423. #endif
  1424. return TRUE;
  1425. }
  1426. /***************************************************************************
  1427. @doc EXTERNAL
  1428. @api BOOL | CdCloseTray | Close the tray
  1429. @parm HCD | hCD | The handle of a currently open drive.
  1430. @rdesc The return value is the current status.
  1431. ***************************************************************************/
  1432. BOOL CdCloseTray(HCD hCD)
  1433. {
  1434. LPCDINFO lpInfo;
  1435. BOOL fResult;
  1436. lpInfo = (LPCDINFO) hCD;
  1437. dprintf2(("CdCloseTray(%08XH)", hCD));
  1438. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_LOAD_MEDIA, NULL, 0));
  1439. //if (fResult) {
  1440. // Save Audio Status to prevent bug
  1441. // lpInfo->fPrevStatus = 0;
  1442. //}
  1443. return fResult;
  1444. }
  1445. /***************************************************************************
  1446. @doc EXTERNAL
  1447. @api BOOL | CdNumTracks | Return the number of tracks and check
  1448. ready (updating TOC) as a side-effect.
  1449. @parm HCD | hCD | The handle of a currently open drive.
  1450. @rdesc The return value is the current status.
  1451. ***************************************************************************/
  1452. int CdNumTracks(HCD hCD)
  1453. {
  1454. LPCDINFO lpInfo;
  1455. DWORD Status;
  1456. MSF Position;
  1457. MSF Temp;
  1458. lpInfo = (LPCDINFO) hCD;
  1459. dprintf2(("CdNumTracks(%08XH)", hCD));
  1460. //
  1461. // If the driver does NOT cache the table of contents then we may
  1462. // fail if a play is in progress
  1463. //
  1464. // However, if we don't have a valid TOC to work with then we'll just
  1465. // have to blow away the play anyway.
  1466. //
  1467. if (!lpInfo->bTOCValid) {
  1468. Status = cdGetDiskInfo(lpInfo);
  1469. if (ERROR_SUCCESS != Status) {
  1470. //
  1471. // See if we failed because it's playing
  1472. //
  1473. if (Status == ERROR_BUSY) {
  1474. if (!lpInfo->bTOCValid) {
  1475. int i;
  1476. //
  1477. // Stop it one way or another
  1478. // Note that pause is no good because in a paused
  1479. // state we may still not be able to read the TOC
  1480. //
  1481. if (!CdPosition(hCD, &Temp, &Position)) {
  1482. CdStop(hCD);
  1483. } else {
  1484. //
  1485. // Can't call CdPlay because this needs a valid
  1486. // position!
  1487. //
  1488. CDROM_PLAY_AUDIO_MSF msfPlay;
  1489. //
  1490. // Set up the data for the call to the driver
  1491. //
  1492. msfPlay.StartingM = REDMINUTE(Position);
  1493. msfPlay.StartingS = REDSECOND(Position);
  1494. msfPlay.StartingF = REDFRAME(Position);
  1495. msfPlay.EndingM = REDMINUTE(Position);
  1496. msfPlay.EndingS = REDSECOND(Position);
  1497. msfPlay.EndingF = REDFRAME(Position);
  1498. cdIoctl(lpInfo, IOCTL_CDROM_PLAY_AUDIO_MSF, &msfPlay,
  1499. sizeof(msfPlay));
  1500. }
  1501. //
  1502. // Make sure the driver knows it's stopped and
  1503. // give it a chance to stop.
  1504. // (NOTE - Sony drive can take around 70ms)
  1505. //
  1506. for (i = 0; i < 60; i++) {
  1507. if (CdStatus(hCD) == DISC_PLAYING) {
  1508. Sleep(10);
  1509. } else {
  1510. break;
  1511. }
  1512. }
  1513. dprintf2(("Took %d tries to stop it!", i));
  1514. //
  1515. // Have another go
  1516. //
  1517. if (ERROR_SUCCESS != cdGetDiskInfo(lpInfo)) {
  1518. return 0;
  1519. }
  1520. }
  1521. } else {
  1522. return 0;
  1523. }
  1524. }
  1525. }
  1526. return lpInfo->LastTrack - lpInfo->FirstTrack + 1;
  1527. }
  1528. /***************************************************************************
  1529. @doc EXTERNAL
  1530. @api DWORD | CdDiskID | Return the disk id
  1531. @parm HCD | hCD | The handle of a currently open drive.
  1532. @rdesc The return value is the disk id or -1 if it can't be found.
  1533. ***************************************************************************/
  1534. DWORD CdDiskID(HCD hCD)
  1535. {
  1536. LPCDINFO lpInfo;
  1537. UINT i;
  1538. DWORD id;
  1539. dprintf2(("CdDiskID"));
  1540. lpInfo = (LPCDINFO) hCD;
  1541. if (!lpInfo->bTOCValid) {
  1542. if (ERROR_SUCCESS != cdGetDiskInfo(lpInfo))
  1543. {
  1544. dprintf2(("CdDiskID - Invalid TOC"));
  1545. return (DWORD)-1;
  1546. }
  1547. }
  1548. for (i = 0, id = 0;
  1549. i < (UINT)(lpInfo->LastTrack - lpInfo->FirstTrack + 1);
  1550. i++) {
  1551. id += lpInfo->Track[i].msfStart & 0x00FFFFFF;
  1552. }
  1553. if (lpInfo->LastTrack - lpInfo->FirstTrack + 1 <= 2) {
  1554. id += CDA_red2bin(reddiff(lpInfo->leadout, lpInfo->Track[0].msfStart));
  1555. }
  1556. return id;
  1557. }
  1558. /***************************************************************************
  1559. @doc EXTERNAL
  1560. @api BOOL | CdDiskUPC | Return the disk UPC code
  1561. @parm HCD | hCD | The handle of a currently open drive.
  1562. @parm LPTSTR | upc | Where to put the upc
  1563. @rdesc TRUE or FALSE if failed
  1564. ***************************************************************************/
  1565. BOOL CdDiskUPC(HCD hCD, LPTSTR upc)
  1566. {
  1567. LPCDINFO lpInfo;
  1568. SUB_Q_CHANNEL_DATA sqd;
  1569. CDROM_SUB_Q_DATA_FORMAT Format;
  1570. DWORD Status;
  1571. UINT i;
  1572. dprintf2(("CdDiskUPC"));
  1573. Format.Format = IOCTL_CDROM_MEDIA_CATALOG;
  1574. Format.Track = 0;
  1575. lpInfo = (LPCDINFO) hCD;
  1576. Status = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  1577. &Format, sizeof(Format),
  1578. &sqd, sizeof(SUB_Q_MEDIA_CATALOG_NUMBER));
  1579. if (ERROR_SUCCESS != Status) {
  1580. return FALSE;
  1581. }
  1582. // Save previous audio status to prevent bug
  1583. CdSetAudioStatus (hCD, sqd.CurrentPosition.Header.AudioStatus);
  1584. //
  1585. // See if there's anything there
  1586. //
  1587. if (!sqd.MediaCatalog.Mcval ||
  1588. sqd.MediaCatalog.FormatCode != IOCTL_CDROM_MEDIA_CATALOG) {
  1589. return FALSE;
  1590. }
  1591. //
  1592. // Check the upc format :
  1593. //
  1594. // 1. ASCII at least 12 ASCII digits
  1595. // 2. packed bcd 6 packed BCD digits
  1596. // 3. unpacked upc
  1597. //
  1598. if (sqd.MediaCatalog.MediaCatalog[9] >= TEXT('0')) {
  1599. for (i = 0; i < 12; i++) {
  1600. if (sqd.MediaCatalog.MediaCatalog[i] < TEXT('0') ||
  1601. sqd.MediaCatalog.MediaCatalog[i] > TEXT('9')) {
  1602. return FALSE;
  1603. }
  1604. }
  1605. wsprintf(upc, TEXT("%12.12hs"), sqd.MediaCatalog.MediaCatalog);
  1606. return TRUE;
  1607. }
  1608. //
  1609. // See if it's packed or unpacked.
  1610. //
  1611. for (i = 0; i < 6; i++) {
  1612. if (sqd.MediaCatalog.MediaCatalog[i] > 9) {
  1613. //
  1614. // Packed - unpack
  1615. //
  1616. for (i = 6; i > 0; i --) {
  1617. UCHAR uBCD;
  1618. uBCD = sqd.MediaCatalog.MediaCatalog[i - 1];
  1619. sqd.MediaCatalog.MediaCatalog[i * 2 - 2] = (UCHAR)(uBCD >> 4);
  1620. sqd.MediaCatalog.MediaCatalog[i * 2 - 1] = (UCHAR)(uBCD & 0x0F);
  1621. }
  1622. break;
  1623. }
  1624. }
  1625. //
  1626. // Check everything is in range
  1627. //
  1628. for (i = 0; i < 12; i++) {
  1629. if (sqd.MediaCatalog.MediaCatalog[i] > 9) {
  1630. return FALSE;
  1631. }
  1632. }
  1633. for (i = 0; i < 12; i++) {
  1634. if (sqd.MediaCatalog.MediaCatalog[i] != 0) {
  1635. //
  1636. // There is a real media catalog
  1637. //
  1638. for (i = 0 ; i < 12; i++) {
  1639. wsprintf(upc + i, TEXT("%01X"), sqd.MediaCatalog.MediaCatalog[i]);
  1640. }
  1641. return TRUE;
  1642. }
  1643. }
  1644. return FALSE;
  1645. }
  1646. /***************************************************************************
  1647. @doc EXTERNAL
  1648. @api BOOL | CdGetDrive | Return the drive id if matches one in our
  1649. list
  1650. @parm LPTSTR | lpstrDeviceName | Name of the device
  1651. @parm DID * | pdid | Where to put the id
  1652. @rdesc TRUE or FALSE if failed
  1653. @comm We allow both the device form and drive form eg:
  1654. f:
  1655. \\.\f:
  1656. ***************************************************************************/
  1657. BOOL CdGetDrive(LPCTSTR lpstrDeviceName, DID * pdid)
  1658. {
  1659. DID didSearch;
  1660. TCHAR szDeviceName[10];
  1661. TCHAR szDeviceNameOnly[10];
  1662. dprintf2(("CdGetDrive"));
  1663. for (didSearch = 0; didSearch < NumDrives; didSearch++) {
  1664. wsprintf(szDeviceNameOnly, TEXT("%c:"), lpstrDeviceName[0]);
  1665. wsprintf(szDeviceName, TEXT("%c:"), CdInfo[didSearch].cDrive);
  1666. if (lstrcmpi(szDeviceName, szDeviceNameOnly) == 0) {
  1667. *pdid = didSearch;
  1668. return TRUE;
  1669. }
  1670. wsprintf(szDeviceNameOnly, TEXT("\\\\.\\%c:"), lpstrDeviceName[0]);
  1671. wsprintf(szDeviceName, TEXT("\\\\.\\%c:"), CdInfo[didSearch].cDrive);
  1672. if (lstrcmpi(szDeviceName, szDeviceNameOnly) == 0) {
  1673. *pdid = didSearch;
  1674. return TRUE;
  1675. }
  1676. }
  1677. return FALSE;
  1678. }
  1679. /***************************************************************************
  1680. @doc EXTERNAL
  1681. @api DWORD | CdStatusTrackPos | Get the disk status,track,and pos.
  1682. @parm HCD | hCD | The handle of a currently open drive.
  1683. @parm DWORD * | pStatus | return status code here
  1684. @parm MSF * | pTrackTime | return Track Time here
  1685. @parm MSF * | pDiscTime | return Track Time here
  1686. @rdesc The return value is success/failure.
  1687. This function does an end run around to get past the MCI functionality
  1688. In other words it is a major HACK. The only compelling reason for
  1689. this function is that it reduces the number of IOCTL's that CDPLAYER
  1690. generates every 1/2 second from ~15 to 1 on average. Reducing system
  1691. traffic for SCSI drives by a substantial factor
  1692. ***************************************************************************/
  1693. BOOL CdStatusTrackPos (
  1694. HCD hCD,
  1695. DWORD * pStatus,
  1696. MSF * pTrackTime,
  1697. MSF * pDiscTime)
  1698. {
  1699. LPCDINFO lpInfo;
  1700. SUB_Q_CHANNEL_DATA sqd;
  1701. CDROM_SUB_Q_DATA_FORMAT Format;
  1702. DWORD CheckStatus;
  1703. DWORD ReadStatus;
  1704. MSF msfPos;
  1705. BOOL fTryAgain = TRUE;
  1706. UCHAR fStatus;
  1707. UCHAR fCode;
  1708. UCHAR cTrack;
  1709. // Check hCD
  1710. lpInfo = (LPCDINFO) hCD;
  1711. if (!lpInfo)
  1712. {
  1713. dprintf2(("CdStatusTrackPos(%08LX), invalid hCD", hCD));
  1714. return FALSE;
  1715. }
  1716. // Check parameters
  1717. if ((!pStatus) || (!pTrackTime) || (!pDiscTime))
  1718. {
  1719. dprintf2(("CdStatusTrackPos(%c), invalid parameters", (char)(lpInfo->cDrive)));
  1720. return FALSE;
  1721. }
  1722. dprintf2(("CdStatusTrackPos(%08LX, %c), Enter",
  1723. hCD, (char)(lpInfo->cDrive)));
  1724. lblTRYAGAIN:
  1725. *pStatus = DISC_NOT_READY;
  1726. *pTrackTime = REDTH(0, 1);
  1727. *pDiscTime = REDTH(0, 0);
  1728. Format.Format = IOCTL_CDROM_CURRENT_POSITION;
  1729. FillMemory((PVOID)&sqd, sizeof(sqd), 0xFF);
  1730. //
  1731. // Read position and status
  1732. //
  1733. ReadStatus = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  1734. &Format, sizeof(Format), &sqd, sizeof(sqd));
  1735. switch (ReadStatus)
  1736. {
  1737. case ERROR_NOT_READY:
  1738. // Don't give up yet
  1739. if (fTryAgain)
  1740. {
  1741. if (ERROR_SUCCESS == cdGetDiskInfo(lpInfo))
  1742. {
  1743. // Try one more time before admitting defeat
  1744. fTryAgain = FALSE;
  1745. goto lblTRYAGAIN;
  1746. }
  1747. }
  1748. // Give up !!!
  1749. dprintf2(("CdStatusTrackPos(%08LX, %c), ReadQChannel = ERROR_NOT_READY",
  1750. hCD, (char)(lpInfo->cDrive)));
  1751. return FALSE;
  1752. case STATUS_VERIFY_REQUIRED:
  1753. // Check if disk is still in drive
  1754. CheckStatus = cdIoctl (lpInfo, IOCTL_CDROM_CHECK_VERIFY, NULL, 0);
  1755. switch (CheckStatus)
  1756. {
  1757. *pStatus = DISC_NOT_READY;
  1758. case ERROR_NO_MEDIA_IN_DRIVE:
  1759. case ERROR_UNRECOGNIZED_MEDIA:
  1760. case ERROR_NOT_READY:
  1761. default:
  1762. return FALSE;
  1763. case ERROR_SUCCESS:
  1764. if (fTryAgain)
  1765. {
  1766. // Try one more time
  1767. fTryAgain = FALSE;
  1768. goto lblTRYAGAIN;
  1769. }
  1770. return FALSE;
  1771. }
  1772. break;
  1773. case ERROR_INVALID_FUNCTION:
  1774. dprintf2(("CdStatusTrackPos(%08LX, %c), ReadQChannel = ERROR_INVALID_FUNCTION failed",
  1775. hCD, (char)(lpInfo->cDrive)));
  1776. *pTrackTime = REDTH(0, 1);
  1777. *pDiscTime = REDTH(0, 0);
  1778. CdGetAudioStatus (hCD, 0, pStatus);
  1779. return TRUE;
  1780. case ERROR_SUCCESS:
  1781. // Success, fall through
  1782. fStatus = sqd.CurrentPosition.Header.AudioStatus;
  1783. fCode = sqd.CurrentPosition.FormatCode;
  1784. cTrack = sqd.CurrentPosition.TrackNumber;
  1785. break;
  1786. default:
  1787. // Failed
  1788. dprintf2(("CdStatusTrackPos(%08LX, %c), ReadQChannel = unknown error (%08LX) failed",
  1789. hCD, (char)(lpInfo->cDrive), (DWORD)ReadStatus));
  1790. return FALSE;
  1791. }
  1792. // Save previous audio status to prevent bug
  1793. CdSetAudioStatus (hCD, fStatus);
  1794. // Translate status code
  1795. CdGetAudioStatus (hCD, fStatus, pStatus);
  1796. dprintf2(("CdStatusTrackPos - Status %02XH", fStatus));
  1797. //
  1798. // Get Position
  1799. //
  1800. // If the track > 100 (outside spec'ed range)
  1801. // OR track > last track number
  1802. // then display an error message
  1803. if ((fCode == 0x01) &&
  1804. ((100 < cTrack) ||
  1805. ((lpInfo->bTOCValid) &&
  1806. (lpInfo->LastTrack < cTrack))))
  1807. {
  1808. // Always display this message on checked builds.
  1809. // We need some feeling for how often this happens
  1810. // It should NEVER happen, but (at least for NEC
  1811. // drives) we see it after a seek to end
  1812. dprintf1(("CDIoctlData returned track==%d, retrying", cTrack));
  1813. if (fTryAgain)
  1814. {
  1815. // Try one more time
  1816. fTryAgain = FALSE;
  1817. goto lblTRYAGAIN;
  1818. }
  1819. }
  1820. dprintf4(("Status = %02X, Length[0] = %02X, Length[1] = %02X",
  1821. fStatus,
  1822. sqd.CurrentPosition.Header.DataLength[0],
  1823. sqd.CurrentPosition.Header.DataLength[1]));
  1824. dprintf4((" Format %02XH", fCode));
  1825. dprintf4((" Absolute Address %02X%02X%02X%02XH",
  1826. sqd.CurrentPosition.AbsoluteAddress[0],
  1827. sqd.CurrentPosition.AbsoluteAddress[1],
  1828. sqd.CurrentPosition.AbsoluteAddress[2],
  1829. sqd.CurrentPosition.AbsoluteAddress[3]));
  1830. dprintf4((" Relative Address %02X%02X%02X%02XH",
  1831. sqd.CurrentPosition.TrackRelativeAddress[0],
  1832. sqd.CurrentPosition.TrackRelativeAddress[1],
  1833. sqd.CurrentPosition.TrackRelativeAddress[2],
  1834. sqd.CurrentPosition.TrackRelativeAddress[3]));
  1835. if (fCode == 0x01)
  1836. {
  1837. // MSF format ?
  1838. // data is current position
  1839. msfPos = MAKERED(sqd.CurrentPosition.AbsoluteAddress[1],
  1840. sqd.CurrentPosition.AbsoluteAddress[2],
  1841. sqd.CurrentPosition.AbsoluteAddress[3]);
  1842. //
  1843. // If position is 0 (this seems to happen on the Toshiba) then
  1844. // we'll try again
  1845. //
  1846. if (msfPos == 0)
  1847. {
  1848. if (fTryAgain)
  1849. {
  1850. fTryAgain = FALSE;
  1851. goto lblTRYAGAIN;
  1852. }
  1853. dprintf3(("CdStatusTrackPos(%08LX, %c), Position = 0",
  1854. hCD, (char)(lpInfo->cDrive), (DWORD)ReadStatus));
  1855. return FALSE;
  1856. }
  1857. dprintf4(("CdStatusTrackPos - MSF disk pos: %u, %u, %u",
  1858. REDMINUTE(msfPos), REDSECOND(msfPos), REDFRAME(msfPos)));
  1859. *pDiscTime = REDTH(msfPos, cTrack);
  1860. // data is current position
  1861. msfPos = MAKERED(sqd.CurrentPosition.TrackRelativeAddress[1],
  1862. sqd.CurrentPosition.TrackRelativeAddress[2],
  1863. sqd.CurrentPosition.TrackRelativeAddress[3]);
  1864. dprintf4(("CdStatusTrackPos - MSF track pos (t,m,s,f): %u, %u, %u, %u",
  1865. cTrack, REDMINUTE(msfPos), REDSECOND(msfPos), REDFRAME(msfPos)));
  1866. *pTrackTime = REDTH(msfPos, cTrack);
  1867. return TRUE;
  1868. }
  1869. dprintf1(("CdStatusTrackPos - Failed to read cd position"));
  1870. return FALSE;
  1871. }
  1872. /***************************************************************************
  1873. @doc INTERNAL
  1874. @api BOOL | CdSetAudioStatus |
  1875. @rdesc TRUE or FALSE if failed
  1876. ***************************************************************************/
  1877. void CdSetAudioStatus (HCD hCD, UCHAR fStatus)
  1878. {
  1879. LPCDINFO lpInfo;
  1880. if (! hCD)
  1881. return;
  1882. lpInfo = (LPCDINFO)hCD;
  1883. // Save Old status
  1884. switch (fStatus)
  1885. {
  1886. case AUDIO_STATUS_NO_STATUS:
  1887. // Do nothing
  1888. break;
  1889. case AUDIO_STATUS_NOT_SUPPORTED:
  1890. case AUDIO_STATUS_IN_PROGRESS:
  1891. case AUDIO_STATUS_PAUSED:
  1892. case AUDIO_STATUS_PLAY_COMPLETE:
  1893. case AUDIO_STATUS_PLAY_ERROR:
  1894. default:
  1895. // Save previous state
  1896. lpInfo->fPrevStatus = fStatus;
  1897. break;
  1898. }
  1899. } // End CdSetAudioStatus
  1900. /***************************************************************************
  1901. @doc INTERNAL
  1902. @api BOOL | CdGetAudioStatus |
  1903. @rdesc TRUE or FALSE if failed
  1904. ***************************************************************************/
  1905. BOOL CdGetAudioStatus (HCD hCD, UCHAR fStatus, DWORD * pStatus)
  1906. {
  1907. LPCDINFO lpInfo;
  1908. DWORD CheckStatus;
  1909. if ((! hCD) || (!pStatus))
  1910. return FALSE;
  1911. lpInfo = (LPCDINFO)hCD;
  1912. // Get status code
  1913. switch (fStatus)
  1914. {
  1915. case AUDIO_STATUS_IN_PROGRESS:
  1916. *pStatus = DISC_PLAYING;
  1917. break;
  1918. case AUDIO_STATUS_PAUSED:
  1919. *pStatus = DISC_PAUSED;
  1920. break;
  1921. case AUDIO_STATUS_PLAY_COMPLETE:
  1922. *pStatus = DISC_READY;
  1923. break;
  1924. case AUDIO_STATUS_NO_STATUS:
  1925. // Grab previous status instead
  1926. switch (lpInfo->fPrevStatus)
  1927. {
  1928. #if 0
  1929. // NOTE: Be very careful before uncommenting
  1930. // The following 3 lines, they basically
  1931. // Cause Play & wait on IDE CD-ROMS to
  1932. // spin forever breaking "Continous Play",
  1933. // "Random Order" in CDPLAYER and
  1934. // MCI Play & wait commands.
  1935. // Basically, I didn't have time to track down
  1936. // the real problem. Apparently, the driver
  1937. // Is not returning AUDIO_STATUS_PLAY_COMPLETE
  1938. // when the CD reaches the end of the current
  1939. // play command. From my end, MCICDA is not
  1940. // receiving this. ChuckP says the lowlevel
  1941. // driver is generating this status correctly.
  1942. // Which I have verified
  1943. // So, the question is how is it getting lost?
  1944. case AUDIO_STATUS_IN_PROGRESS:
  1945. *pStatus = DISC_PLAYING;
  1946. break;
  1947. #endif
  1948. case AUDIO_STATUS_PAUSED:
  1949. *pStatus = DISC_PAUSED;
  1950. break;
  1951. case AUDIO_STATUS_PLAY_COMPLETE:
  1952. *pStatus = DISC_READY;
  1953. break;
  1954. default:
  1955. // We are in a broken state, so just
  1956. // assume we are stopped, it's the safest
  1957. *pStatus = DISC_READY;
  1958. break;
  1959. } // End switch
  1960. break;
  1961. //
  1962. // Some drives just return 0 sometimes - so we rely on the results of
  1963. // CHECK_VERIFY in this case
  1964. //
  1965. default:
  1966. // Check if disk is still in drive
  1967. CheckStatus = cdIoctl (lpInfo, IOCTL_CDROM_CHECK_VERIFY, NULL, 0);
  1968. if (ERROR_SUCCESS != CheckStatus)
  1969. {
  1970. *pStatus = DISC_NOT_READY;
  1971. }
  1972. else
  1973. {
  1974. *pStatus = DISC_READY;
  1975. }
  1976. break;
  1977. } // End Switch
  1978. // Success
  1979. return TRUE;
  1980. } // CdGetAudioStatus
  1981. /***************************************************************************
  1982. @doc EXTERNAL
  1983. @api MSF | CdGetTrack | Given a position to find the corresponding track
  1984. if any
  1985. @parm LPCDINFO | lpInfo | Pointer to CD info including track data.
  1986. @parm MSF | msfStart | Position to start looking.
  1987. @rdesc A new MSF to play from / seek to within an audio track or
  1988. the end of the CD if none was found.
  1989. ***************************************************************************/
  1990. BOOL CdGetTrack(
  1991. LPCDINFO lpInfo,
  1992. MSF msfPos,
  1993. UCHAR * pTrack,
  1994. MSF * pmsfStart)
  1995. {
  1996. UINT tracknum;
  1997. MSF lastaudio = lpInfo->msfEnd;
  1998. //
  1999. // Search for the track which ends after ours and is audio
  2000. //
  2001. for (tracknum = 0; ;tracknum++) {
  2002. //
  2003. // Note that some CDs return positions outside the playing range
  2004. // sometimes (notably 0) so msfStart may be less than the first
  2005. // track start
  2006. //
  2007. //
  2008. // If we're beyond the start of the track and before the start
  2009. // of the next track then this is the track we want.
  2010. //
  2011. // We assume we're always beyond the start of the first track
  2012. // and we check that if we're looking at the last track then
  2013. // we check we're before the end of the CD.
  2014. //
  2015. if ((msfPos >= lpInfo->Track[tracknum].msfStart || tracknum == 0)
  2016. &&
  2017. (tracknum + lpInfo->FirstTrack == lpInfo->LastTrack
  2018. ? msfPos <= lpInfo->msfEnd
  2019. : msfPos < lpInfo->Track[tracknum + 1].msfStart))
  2020. {
  2021. if (!(lpInfo->Track[tracknum].Ctrl & IS_DATA_TRACK))
  2022. {
  2023. *pTrack = lpInfo->Track[tracknum].TrackNumber;
  2024. *pmsfStart = lpInfo->Track[tracknum].msfStart;
  2025. return TRUE;
  2026. }
  2027. }
  2028. //
  2029. // Exhausted all tracks ?
  2030. //
  2031. if (tracknum + lpInfo->FirstTrack >= lpInfo->LastTrack)
  2032. {
  2033. return FALSE;
  2034. }
  2035. }
  2036. }