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.

3858 lines
88 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: cdplayer.c
  3. *
  4. * CD Playing application
  5. *
  6. *
  7. * Created: 02-11-93
  8. * Author: Stephen Estrop [StephenE]
  9. *
  10. * Copyright (c) 1993 - 1995 Microsoft Corporation. All rights reserved.
  11. \**************************************************************************/
  12. #pragma warning( once : 4201 4214 )
  13. #include <windows.h> /* required for all Windows applications */
  14. #include <shellapi.h>
  15. #include <windowsx.h>
  16. #include <ole2.h>
  17. #include <shlobj.h>
  18. #include <dbt.h>
  19. #include "..\main\mmfw.h"
  20. #include "..\main\resource.h"
  21. #include "..\main\mmenu.h"
  22. #include "..\cdopt\cdopt.h"
  23. #include "..\cdnet\cdnet.h"
  24. #define NOMENUHELP
  25. #define NOBTNLIST
  26. #define NOTRACKBAR
  27. #define NODRAGLIST
  28. #define NOUPDOWN
  29. #include <commctrl.h> /* want toolbar and status bar */
  30. #include <string.h>
  31. #include <stdio.h>
  32. #include <tchar.h> /* contains portable ascii/unicode macros */
  33. #include <stdarg.h>
  34. #include <stdlib.h>
  35. #define GLOBAL /* This allocates storage for the public globals */
  36. #include "playres.h"
  37. #include "cdplayer.h"
  38. #include "ledwnd.h"
  39. #include "cdapi.h"
  40. #include "scan.h"
  41. #include "trklst.h"
  42. #include "database.h"
  43. #include "commands.h"
  44. #include "literals.h"
  45. //#ifndef WM_CDPLAYER_COPYDATA
  46. #define WM_CDPLAYER_COPYDATA (WM_USER+0x100)
  47. //#endif
  48. IMMFWNotifySink* g_pSink;
  49. ATOM g_atomCDClass = NULL;
  50. extern HINSTANCE g_hInst;
  51. void GetTOC(int cdrom, TCHAR* szNetQuery);
  52. /* -------------------------------------------------------------------------
  53. ** Private functions
  54. ** -------------------------------------------------------------------------
  55. */
  56. int
  57. CopyWord(
  58. TCHAR *szWord,
  59. TCHAR *szSource
  60. );
  61. void
  62. AppendTrackToPlayList(
  63. PTRACK_PLAY pHead,
  64. PTRACK_PLAY pInsert
  65. );
  66. BOOL
  67. IsTrackFileNameValid(
  68. LPTSTR lpstFileName,
  69. int *piCdRomIndex,
  70. int *piTrackIndex,
  71. BOOL fScanningTracks,
  72. BOOL fQuiet
  73. );
  74. TCHAR *
  75. ParseTrackList(
  76. TCHAR *szTrackList,
  77. int *piCdRomIndex
  78. );
  79. int
  80. ParseCommandLine(
  81. LPTSTR lpstr,
  82. int *piTrackToSeekTo,
  83. BOOL fQuiet
  84. );
  85. void
  86. HandlePassedCommandLine(
  87. LPTSTR lpCmdLine,
  88. BOOL fCheckCDRom
  89. );
  90. int
  91. FindMostSuitableDrive(
  92. void
  93. );
  94. void
  95. AskUserToInsertCorrectDisc(
  96. DWORD dwID
  97. );
  98. #ifndef USE_IOCTLS
  99. BOOL CheckMCICDA (TCHAR chDrive);
  100. #endif // ! USE_IOCTLS
  101. BOOL
  102. CDPlay_CopyData(
  103. HWND hwnd,
  104. PCOPYDATASTRUCT lpcpds
  105. );
  106. /* -------------------------------------------------------------------------
  107. ** Private Globals
  108. ** -------------------------------------------------------------------------
  109. */
  110. HBRUSH g_hBrushBkgd;
  111. TCHAR g_szTimeSep[10];
  112. int g_AcceleratorCount;
  113. BOOL g_fInCopyData = FALSE;
  114. HCURSOR g_hcurs = NULL;
  115. //---------------------------------------------------------------------------
  116. // Stuff required to make drag/dropping of a shortcut file work on Chicago
  117. //---------------------------------------------------------------------------
  118. BOOL
  119. ResolveLink(
  120. TCHAR * szFileName
  121. );
  122. BOOL g_fOleInitialized = FALSE;
  123. /*
  124. ** these values are defined by the UI gods...
  125. */
  126. const int dxButton = 24;
  127. const int dyButton = 22;
  128. const int dxBitmap = 16;
  129. const int dyBitmap = 16;
  130. const int xFirstButton = 8;
  131. /******************************Public*Routine******************************\
  132. * WinMain
  133. *
  134. *
  135. * Windows recognizes this function by name as the initial entry point
  136. * for the program. This function calls the application initialization
  137. * routine, if no other instance of the program is running, and always
  138. * calls the instance initialization routine. It then executes a message
  139. * retrieval and dispatch loop that is the top-level control structure
  140. * for the remainder of execution. The loop is terminated when a WM_QUIT
  141. * message is received, at which time this function exits the application
  142. * instance by returning the value passed by PostQuitMessage().
  143. *
  144. * If this function must abort before entering the message loop, it
  145. * returns the conventional value NULL.
  146. *
  147. *
  148. * History:
  149. * 18-11-93 - StephenE - Created
  150. *
  151. \**************************************************************************/
  152. HWND PASCAL
  153. WinFake(
  154. HINSTANCE hInstance,
  155. HINSTANCE hPrevInstance,
  156. LPSTR lpCmdLine,
  157. int nCmdShow,
  158. HWND hwndMain,
  159. IMMFWNotifySink* pSink
  160. )
  161. {
  162. g_pSink = pSink;
  163. g_fBlockNetPrompt = FALSE;
  164. g_fSelectedOrder = TRUE;
  165. g_fIntroPlay = FALSE;
  166. g_fContinuous = FALSE;
  167. g_fRepeatSingle = FALSE;
  168. #ifdef DBG
  169. /*
  170. ** This removes the Gdi batch feature. It ensures that the screen
  171. ** is updated after every gdi call - very useful for debugging.
  172. */
  173. GdiSetBatchLimit(1);
  174. #endif
  175. /*
  176. ** Save the instance handle in static variable, which will be used in
  177. ** many subsequence calls from this application to Windows.
  178. */
  179. g_hInst = hInstance;
  180. g_lpCmdLine = lpCmdLine;
  181. InitializeCriticalSection (&g_csTOCSerialize);
  182. /*
  183. ** Initialize the cdplayer application.
  184. */
  185. CdPlayerStartUp(hwndMain);
  186. return g_hwndApp;
  187. }
  188. /*****************************Private*Routine******************************\
  189. * InitInstance
  190. *
  191. *
  192. * This function is called at initialization time for every instance of
  193. * this application. This function performs initialization tasks that
  194. * cannot be shared by multiple instances.
  195. *
  196. * In this case, we save the instance handle in a static variable and
  197. * create and display the main program window.
  198. *
  199. * History:
  200. * 18-11-93 - StephenE - Created
  201. *
  202. \**************************************************************************/
  203. BOOL
  204. InitInstance(
  205. HANDLE hInstance,
  206. HWND hwndMain
  207. )
  208. {
  209. HWND hwnd;
  210. /*
  211. ** Load in some strings
  212. */
  213. _tcscpy( g_szArtistTxt, IdStr( STR_HDR_ARTIST ) );
  214. _tcscpy( g_szTitleTxt, IdStr( STR_HDR_TITLE ) );
  215. _tcscpy( g_szUnknownTxt, IdStr( STR_UNKNOWN ) );
  216. _tcscpy( g_szTrackTxt, IdStr( STR_HDR_TRACK ) );
  217. g_szTimeSep[0] = TEXT(':');
  218. g_szTimeSep[1] = g_chNULL;
  219. GetLocaleInfo( GetUserDefaultLCID(), LOCALE_STIME, g_szTimeSep, 10 );
  220. /*
  221. ** Initialize the my classes. We do this here because the dialog
  222. ** that we are about to create contains two windows on my class.
  223. ** The dialog would fail to be created if the classes was not registered.
  224. */
  225. g_fDisplayT = TRUE;
  226. InitLEDClass( g_hInst );
  227. Init_SJE_TextClass( g_hInst );
  228. WNDCLASS cls;
  229. cls.lpszClassName = g_szSJE_CdPlayerClass;
  230. cls.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW);
  231. cls.hIcon = NULL;
  232. cls.lpszMenuName = NULL;
  233. cls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  234. cls.hInstance = (HINSTANCE)hInstance;
  235. cls.style = CS_DBLCLKS;
  236. cls.lpfnWndProc = DefDlgProc;
  237. cls.cbClsExtra = 0;
  238. cls.cbWndExtra = DLGWINDOWEXTRA;
  239. if ( !RegisterClass(&cls) )
  240. {
  241. return FALSE;
  242. }
  243. g_hcurs = LoadCursor(g_hInst,MAKEINTRESOURCE(IDC_CURSOR_HAND));
  244. /*
  245. ** Create a main window for this application instance.
  246. */
  247. hwnd = CreateDialog( g_hInst, MAKEINTRESOURCE(IDR_CDPLAYER),
  248. hwndMain, (DLGPROC)MainWndProc );
  249. /*
  250. ** If window could not be created, return "failure"
  251. */
  252. if ( !hwnd )
  253. {
  254. return FALSE;
  255. }
  256. g_hwndApp = hwnd;
  257. return TRUE;
  258. }
  259. /*****************************Private*Routine******************************\
  260. * CdPlayerStartUp
  261. *
  262. *
  263. *
  264. * History:
  265. * dd-mm-95 - StephenE - Created
  266. *
  267. \**************************************************************************/
  268. void
  269. CdPlayerStartUp(
  270. HWND hwndMain
  271. )
  272. {
  273. /*
  274. ** Reseed random generator
  275. */
  276. srand( GetTickCount() );
  277. /*
  278. ** Set error mode popups for critical errors (like
  279. ** no disc in drive) OFF.
  280. */
  281. SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );
  282. /*
  283. ** Scan device chain for CDROM devices... Terminate if none found.
  284. */
  285. g_NumCdDevices = ScanForCdromDevices( );
  286. if ( g_NumCdDevices == 0 )
  287. {
  288. LPTSTR lpstrTitle;
  289. LPTSTR lpstrText;
  290. lpstrTitle = (TCHAR*)AllocMemory( STR_MAX_STRING_LEN * sizeof(TCHAR) );
  291. lpstrText = (TCHAR*)AllocMemory( STR_MAX_STRING_LEN * sizeof(TCHAR) );
  292. _tcscpy( lpstrText, IdStr(STR_NO_CDROMS) );
  293. _tcscpy( lpstrTitle, IdStr(STR_CDPLAYER) );
  294. MessageBox( NULL, lpstrText, lpstrTitle,
  295. MB_APPLMODAL | MB_ICONINFORMATION |
  296. MB_OK | MB_SETFOREGROUND );
  297. LocalFree( (HLOCAL)lpstrText );
  298. LocalFree( (HLOCAL)lpstrTitle );
  299. ExitProcess( (UINT)-1 );
  300. }
  301. #ifndef USE_IOCTLS
  302. // Make sure we have a functional MCI (CD Audio)
  303. OSVERSIONINFO os;
  304. os.dwOSVersionInfoSize = sizeof(os);
  305. GetVersionEx(&os);
  306. if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
  307. {
  308. if (! CheckMCICDA (g_Devices[0]->drive))
  309. {
  310. ExitProcess( (UINT)-1 );
  311. }
  312. }
  313. #endif // ! USE_IOCTLS
  314. /*
  315. ** Perform initializations that apply to a specific instance
  316. ** This function actually creates the CdPlayer window. (Note that it is
  317. ** not visible yet). If we get here we know that there is a least one
  318. ** cdrom device detected which may have a music cd in it. If it does
  319. ** contain a music cdrom the table of contents will have been read and
  320. ** cd database queryed to determine if the music cd is known. Therefore
  321. ** on the WM_INITDIALOG message we should update the "Artist", "Title" and
  322. ** "Track" fields of the track info display and adjust the enable state
  323. ** of the play buttons.
  324. */
  325. if ( !InitInstance( g_hInst, hwndMain ) )
  326. {
  327. FatalApplicationError( STR_TERMINATE );
  328. }
  329. /*
  330. ** Restore ourselves from the ini file
  331. */
  332. ReadSettings(NULL);
  333. /*
  334. ** Scan command the command line. If we were given any valid commandline
  335. ** args we have to adjust the nCmdShow parameter. (ie. start minimized
  336. ** if the user just wants us to play a certain track. ScanCommandLine can
  337. ** overide the default playlist for all the cd-rom devices installed. It
  338. ** modifies the global flag g_fPlay and returns the index of the first
  339. ** CD-Rom that should be played.
  340. */
  341. g_CurrCdrom = g_LastCdrom = 0;
  342. }
  343. /*****************************Private*Routine******************************\
  344. * CompleteCdPlayerStartUp
  345. *
  346. *
  347. *
  348. * History:
  349. * dd-mm-95 - StephenE - Created
  350. *
  351. \**************************************************************************/
  352. void
  353. CompleteCdPlayerStartUp(
  354. void
  355. )
  356. {
  357. int iTrackToSeekTo = -1;
  358. int i;
  359. g_fStartedInTray = FALSE;
  360. /*
  361. ** Scan command the command line. If we were given any valid
  362. ** commandline args we have to adjust the nCmdShow parameter. (ie.
  363. ** start minimized if the user just wants us to play a certain
  364. ** track. ScanCommandLine can overide the default playlist for all
  365. ** the cd-rom devices installed. It modifies the global flag
  366. ** g_fPlay and returns the index of the first CD-Rom that should be
  367. ** played.
  368. **
  369. */
  370. g_CurrCdrom = g_LastCdrom = ParseCommandLine( GetCommandLine(),
  371. &iTrackToSeekTo, FALSE );
  372. /*
  373. ** If the message box prompting the user to insert the correct cd disc in
  374. ** the drive was displayed, ParseCommandLine will return -1, in which case
  375. ** find the most suitable drive, also make sure that we don't come up
  376. ** playing.
  377. */
  378. if (g_LastCdrom == -1)
  379. {
  380. g_fPlay = FALSE;
  381. g_CurrCdrom = g_LastCdrom = FindMostSuitableDrive();
  382. }
  383. for ( i = 0; i < g_NumCdDevices; i++) {
  384. TimeAdjustInitialize( i );
  385. }
  386. /*
  387. ** All the rescan threads are either dead or in the act of dying.
  388. ** It is now safe to initalize the time information for each
  389. ** cdrom drive.
  390. */
  391. if ( iTrackToSeekTo != -1 ) {
  392. PTRACK_PLAY tr;
  393. tr = PLAYLIST( g_CurrCdrom );
  394. if ( tr != NULL ) {
  395. for( i = 0; i < iTrackToSeekTo; i++, tr = tr->nextplay );
  396. TimeAdjustSkipToTrack( g_CurrCdrom, tr );
  397. }
  398. }
  399. /*
  400. ** if we are in random mode, then we need to shuffle the play lists.
  401. */
  402. if (!g_fSelectedOrder)
  403. {
  404. ComputeAndUseShufflePlayLists();
  405. }
  406. SetPlayButtonsEnableState();
  407. /*
  408. ** Start the heart beat time. This timer is responsible for:
  409. ** 1. detecting new or ejected cdroms.
  410. ** 2. flashing the LED display if we are in paused mode.
  411. ** 3. Incrementing the LED display if we are in play mode.
  412. */
  413. UINT_PTR timerid = SetTimer( g_hwndApp, HEARTBEAT_TIMER_ID, HEARTBEAT_TIMER_RATE,
  414. (TIMERPROC)HeartBeatTimerProc );
  415. if (!g_fPlay)
  416. {
  417. //"play" wasn't on the command line, but maybe the user wants it on startup anyway
  418. HKEY hKey;
  419. LONG lRet;
  420. lRet = RegOpenKey( HKEY_CURRENT_USER, g_szRegistryKey, &hKey );
  421. if ( (lRet == ERROR_SUCCESS) )
  422. {
  423. DWORD dwType, dwLen;
  424. dwLen = sizeof( g_fPlay );
  425. if ( ERROR_SUCCESS != RegQueryValueEx(hKey, g_szStartCDPlayingOnStart,
  426. 0L, &dwType, (LPBYTE)&g_fPlay,
  427. &dwLen) )
  428. {
  429. g_fPlay = FALSE; //default to not playing
  430. }
  431. RegCloseKey(hKey);
  432. }
  433. }
  434. //Don't start if player was started in tray mode.
  435. //This prevents the user from getting an unexpected blast on boot.
  436. if (( g_fPlay ) && (!g_fStartedInTray))
  437. {
  438. CdPlayerPlayCmd();
  439. }
  440. if (g_CurrCdrom != 0)
  441. {
  442. //didn't use the default player, so jump to the new one
  443. MMONDISCCHANGED mmOnDisc;
  444. mmOnDisc.nNewDisc = g_CurrCdrom;
  445. mmOnDisc.fDisplayVolChange = FALSE;
  446. g_pSink->OnEvent(MMEVENT_ONDISCCHANGED,&mmOnDisc);
  447. }
  448. if (g_Devices[g_CurrCdrom]->State & CD_LOADED)
  449. {
  450. //need to set track button on main ui
  451. HWND hwndTrackButton = GetDlgItem(GetParent(g_hwndApp),IDB_TRACK);
  452. if (hwndTrackButton)
  453. {
  454. EnableWindow(hwndTrackButton,TRUE);
  455. }
  456. }
  457. //cd was already playing; let's update the main ui
  458. if (g_Devices[g_CurrCdrom]->State & CD_PLAYING)
  459. {
  460. g_pSink->OnEvent(MMEVENT_ONPLAY,NULL);
  461. }
  462. }
  463. /******************************Public*Routine******************************\
  464. * MainWndProc
  465. *
  466. * Use the message crackers to dispatch the dialog messages to appropirate
  467. * message handlers. The message crackers are portable between 16 and 32
  468. * bit versions of Windows.
  469. *
  470. * History:
  471. * 18-11-93 - StephenE - Created
  472. *
  473. \**************************************************************************/
  474. INT_PTR CALLBACK
  475. MainWndProc(
  476. HWND hwnd,
  477. UINT message,
  478. WPARAM wParam,
  479. LPARAM lParam
  480. )
  481. {
  482. switch ( message ) {
  483. HANDLE_MSG( hwnd, WM_INITDIALOG, CDPlay_OnInitDialog );
  484. HANDLE_MSG( hwnd, WM_DRAWITEM, CDPlay_OnDrawItem );
  485. HANDLE_MSG( hwnd, WM_COMMAND, CDPlay_OnCommand );
  486. HANDLE_MSG( hwnd, WM_DESTROY, CDPlay_OnDestroy );
  487. HANDLE_MSG( hwnd, WM_SIZE, CDPlay_OnSize );
  488. HANDLE_MSG( hwnd, WM_ENDSESSION, CDPlay_OnEndSession );
  489. HANDLE_MSG( hwnd, WM_WININICHANGE, CDPlay_OnWinIniChange );
  490. HANDLE_MSG( hwnd, WM_CTLCOLORSTATIC, Common_OnCtlColor );
  491. HANDLE_MSG( hwnd, WM_CTLCOLORDLG, Common_OnCtlColor );
  492. HANDLE_MSG( hwnd, WM_MEASUREITEM, Common_OnMeasureItem );
  493. HANDLE_MSG( hwnd, WM_NOTIFY, CDPlay_OnNotify );
  494. HANDLE_MSG( hwnd, WM_DROPFILES, CDPlay_OnDropFiles );
  495. case WM_DEVICECHANGE:
  496. return CDPlay_OnDeviceChange (hwnd, wParam, lParam);
  497. case WM_SETFOCUS :
  498. {
  499. //move focus to next window in tab order
  500. HWND hwndNext = GetNextDlgTabItem(GetParent(hwnd),hwnd,FALSE);
  501. //if the next window just lost focus, we need to go the other way
  502. if (hwndNext == (HWND)wParam)
  503. {
  504. hwndNext = GetNextDlgTabItem(GetParent(hwnd),hwnd,TRUE);
  505. }
  506. SetFocus(hwndNext);
  507. return 0;
  508. }
  509. break;
  510. case WM_ERASEBKGND:
  511. return 1;
  512. case WM_CLOSE:
  513. return CDPlay_OnClose(hwnd, FALSE);
  514. case WM_COPYDATA:
  515. return CDPlay_CopyData( hwnd, (PCOPYDATASTRUCT)lParam );
  516. case WM_CDPLAYER_COPYDATA:
  517. return CDPlay_OnCopyData( hwnd, (PCOPYDATASTRUCT)lParam );
  518. case WM_NOTIFY_TOC_READ:
  519. return CDPlay_OnTocRead( (int)wParam );
  520. case WM_NOTIFY_FIRST_SCAN:
  521. {
  522. for ( int i = 0; i < g_NumCdDevices; i++ )
  523. {
  524. RescanDevice( hwnd, i );
  525. }
  526. return TRUE;
  527. }
  528. break;
  529. default:
  530. return FALSE;
  531. }
  532. }
  533. /*****************************Private*Routine******************************\
  534. * CDPlay_OnInitDialog
  535. *
  536. *
  537. *
  538. * History:
  539. * 18-11-93 - StephenE - Created
  540. *
  541. \**************************************************************************/
  542. BOOL
  543. CDPlay_OnInitDialog(
  544. HWND hwnd,
  545. HWND hwndFocus,
  546. LPARAM lParam
  547. )
  548. {
  549. int i;
  550. g_hBrushBkgd = CreateSolidBrush( GetSysColor(COLOR_BTNFACE) );
  551. EnumChildWindows( hwnd, ChildEnumProc, (LPARAM)hwnd );
  552. DragAcceptFiles( hwnd, TRUE );
  553. /*
  554. ** Initialize and read the TOC for all the detected CD-ROMS
  555. */
  556. SetPlayButtonsEnableState();
  557. for ( i = 0; i < g_NumCdDevices; i++ )
  558. {
  559. ASSERT(g_Devices[i]->State == CD_BEING_SCANNED);
  560. ASSERT(g_Devices[i]->hCd == 0L);
  561. TimeAdjustInitialize( i );
  562. g_Devices[i]->State = CD_NO_CD;
  563. }
  564. PostMessage(hwnd,WM_NOTIFY_FIRST_SCAN,0,0);
  565. return FALSE;
  566. }
  567. /*****************************Private*Routine******************************\
  568. * CDPlay_OnWinIniChange
  569. *
  570. * Updates the time format separator and the LED display
  571. *
  572. * History:
  573. * 29-09-94 - StephenE - Created
  574. *
  575. \**************************************************************************/
  576. void
  577. CDPlay_OnWinIniChange(
  578. HWND hwnd,
  579. LPCTSTR lpszSectionName
  580. )
  581. {
  582. GetLocaleInfo( GetUserDefaultLCID(), LOCALE_STIME, g_szTimeSep, 10 );
  583. UpdateDisplay( DISPLAY_UPD_LED | DISPLAY_UPD_DISC_TIME | DISPLAY_UPD_TRACK_TIME );
  584. }
  585. /*****************************Private*Routine******************************\
  586. * CDPlay_OnDrawItem
  587. *
  588. *
  589. *
  590. * History:
  591. * 18-11-93 - StephenE - Created
  592. *
  593. \**************************************************************************/
  594. BOOL
  595. CDPlay_OnDrawItem(
  596. HWND hwnd,
  597. const DRAWITEMSTRUCT *lpdis
  598. )
  599. {
  600. if (lpdis->CtlType == ODT_MENU)
  601. {
  602. return FALSE;
  603. }
  604. int i;
  605. i = INDEX(lpdis->CtlID);
  606. switch (lpdis->CtlType) {
  607. case ODT_BUTTON:
  608. /*
  609. ** See if the fast foreward or backward buttons has been pressed or
  610. ** released. If so execute the seek command here. Do nothing on
  611. ** the WM_COMMAND message.
  612. */
  613. if ( lpdis->CtlID == IDM_PLAYBAR_SKIPBACK
  614. || lpdis->CtlID == IDM_PLAYBAR_SKIPFORE ) {
  615. if (lpdis->itemAction & ODA_SELECT ) {
  616. g_AcceleratorCount = 0;
  617. CdPlayerSeekCmd( hwnd, (lpdis->itemState & ODS_SELECTED),
  618. lpdis->CtlID );
  619. }
  620. }
  621. /*
  622. ** Now draw the button according to the buttons state information.
  623. */
  624. /*
  625. case ODT_COMBOBOX:
  626. if (lpdis->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) {
  627. switch (lpdis->CtlID) {
  628. case IDC_ARTIST_NAME:
  629. DrawDriveItem( lpdis->hDC, &lpdis->rcItem,
  630. lpdis->itemData,
  631. (ODS_SELECTED & lpdis->itemState) );
  632. break;
  633. }
  634. }
  635. */
  636. return TRUE;
  637. }
  638. return FALSE;
  639. }
  640. /*****************************Private*Routine******************************\
  641. * CDPlay_OnCommand
  642. *
  643. *
  644. *
  645. * History:
  646. * 18-11-93 - StephenE - Created
  647. *
  648. \**************************************************************************/
  649. void
  650. CDPlay_OnCommand(
  651. HWND hwnd,
  652. int id,
  653. HWND hwndCtl,
  654. UINT codeNotify
  655. )
  656. {
  657. switch( id )
  658. {
  659. case IDM_NET_CD:
  660. {
  661. MMNET* pNet = (MMNET*)hwndCtl;
  662. if (pNet->discid==0)
  663. {
  664. //if disc id is 0, then we want to manually get the info for the current cd
  665. GetInternetDatabase(g_CurrCdrom,g_Devices[g_CurrCdrom]->CdInfo.Id,TRUE,TRUE,pNet->hwndCallback,NULL);
  666. }
  667. else if ((pNet->discid==-1) || (pNet->fForceNet))
  668. {
  669. //if disc id is -1, then we want to get just the batches
  670. int cdrom = g_CurrCdrom;
  671. if (pNet->fForceNet)
  672. {
  673. //try to find the correct cdrom for this guy
  674. for(int i = 0; i < g_NumCdDevices; i++)
  675. {
  676. if (pNet->discid == g_Devices[i]->CdInfo.Id)
  677. {
  678. //if the id was found in the player, physically rescan it
  679. pNet->pData2 = NULL;
  680. cdrom = i;
  681. break;
  682. }
  683. } //end for
  684. } //end if force net
  685. GetInternetDatabase(cdrom,pNet->fForceNet ? pNet->discid : 0,TRUE,TRUE,pNet->hwndCallback,pNet->pData2);
  686. }
  687. else
  688. {
  689. for(int i = 0; i < g_NumCdDevices; i++)
  690. {
  691. if (pNet->discid == g_Devices[i]->CdInfo.Id)
  692. {
  693. //don't hit the net, just scan the entry
  694. GetInternetDatabase(i,g_Devices[i]->CdInfo.Id,FALSE,TRUE,pNet->hwndCallback,pNet->pData);
  695. UpdateDisplay(DISPLAY_UPD_TITLE_NAME|DISPLAY_UPD_LED);
  696. }
  697. } //end for
  698. }
  699. }
  700. break;
  701. case IDM_OPTIONS_NORMAL :
  702. {
  703. //turn randomness off if it is on
  704. if (!g_fSelectedOrder)
  705. {
  706. if ( LockALLTableOfContents() )
  707. {
  708. FlipBetweenShuffleAndOrder();
  709. }
  710. }
  711. g_fRepeatSingle = FALSE;
  712. g_fIntroPlay = FALSE;
  713. g_fSelectedOrder = TRUE;
  714. g_fContinuous = FALSE;
  715. }
  716. break;
  717. case IDM_OPTIONS_RANDOM:
  718. if ( LockALLTableOfContents() )
  719. {
  720. g_fSelectedOrder = FALSE;
  721. ComputeAndUseShufflePlayLists();
  722. g_fIntroPlay = FALSE;
  723. g_fContinuous = TRUE;
  724. g_fRepeatSingle = FALSE;
  725. }
  726. break;
  727. //case IDM_OPTIONS_MULTI:
  728. //g_fSingleDisk = !g_fSingleDisk;
  729. //break;
  730. case IDM_OPTIONS_REPEAT_SINGLE :
  731. {
  732. //turn randomness off if it is on
  733. if (!g_fSelectedOrder)
  734. {
  735. if ( LockALLTableOfContents() )
  736. {
  737. FlipBetweenShuffleAndOrder();
  738. }
  739. }
  740. g_fRepeatSingle = TRUE;
  741. g_fIntroPlay = FALSE;
  742. g_fSelectedOrder = TRUE;
  743. g_fContinuous = FALSE;
  744. }
  745. break;
  746. case IDM_OPTIONS_INTRO:
  747. //turn randomness off if it is on
  748. if (!g_fSelectedOrder)
  749. {
  750. if ( LockALLTableOfContents() )
  751. {
  752. FlipBetweenShuffleAndOrder();
  753. }
  754. }
  755. g_fIntroPlay = TRUE;
  756. g_fSelectedOrder = TRUE;
  757. g_fContinuous = FALSE;
  758. g_fRepeatSingle = FALSE;
  759. break;
  760. case IDM_OPTIONS_CONTINUOUS:
  761. //turn randomness off if it is on
  762. if (!g_fSelectedOrder)
  763. {
  764. if ( LockALLTableOfContents() )
  765. {
  766. FlipBetweenShuffleAndOrder();
  767. }
  768. }
  769. g_fContinuous = TRUE;
  770. g_fIntroPlay = FALSE;
  771. g_fSelectedOrder = TRUE;
  772. g_fRepeatSingle = FALSE;
  773. break;
  774. case IDM_TIME_REMAINING:
  775. g_fDisplayT = TRUE;
  776. g_fDisplayD = g_fDisplayTr = g_fDisplayDr = FALSE;
  777. UpdateDisplay( DISPLAY_UPD_LED );
  778. break;
  779. case IDM_TRACK_REMAINING:
  780. g_fDisplayTr = TRUE;
  781. g_fDisplayD = g_fDisplayDr = g_fDisplayT = FALSE;
  782. UpdateDisplay( DISPLAY_UPD_LED );
  783. break;
  784. case IDM_DISC_REMAINING:
  785. g_fDisplayDr = TRUE;
  786. g_fDisplayD = g_fDisplayTr = g_fDisplayT = FALSE;
  787. UpdateDisplay( DISPLAY_UPD_LED );
  788. break;
  789. case IDM_PLAYBAR_EJECT:
  790. CdPlayerEjectCmd();
  791. break;
  792. case IDM_PLAYBAR_PLAY:
  793. /*
  794. ** If we currently in PLAY mode and the command came from
  795. ** a keyboard accelerator then assume that the user really
  796. ** means Pause. This is because the Ctrl-P key sequence
  797. ** is a toggle between Play and Paused. codeNotify is 1 when
  798. ** the WM_COMMAND message came from an accelerator and 0 when
  799. ** it cam from a menu.
  800. */
  801. if ((g_State & CD_PLAYING) && (codeNotify == 1))
  802. {
  803. CdPlayerPauseCmd();
  804. }
  805. else
  806. {
  807. CdPlayerPlayCmd();
  808. }
  809. break;
  810. case IDM_PLAYBAR_PAUSE:
  811. CdPlayerPauseCmd();
  812. break;
  813. case IDM_PLAYBAR_STOP:
  814. CdPlayerStopCmd();
  815. break;
  816. case IDM_PLAYBAR_PREVTRACK:
  817. CdPlayerPrevTrackCmd();
  818. break;
  819. case IDM_PLAYBAR_NEXTTRACK:
  820. CdPlayerNextTrackCmd();
  821. break;
  822. case IDM_DATABASE_EXIT:
  823. PostMessage( hwnd, WM_CLOSE, 0, 0L );
  824. break;
  825. }
  826. }
  827. /******************************Public*Routine******************************\
  828. * CDPlay_OnDestroy
  829. *
  830. *
  831. *
  832. * History:
  833. * dd-mm-93 - StephenE - Created
  834. *
  835. \**************************************************************************/
  836. void
  837. CDPlay_OnDestroy(
  838. HWND hwnd
  839. )
  840. {
  841. int i;
  842. for ( i = 0; i < g_NumCdDevices; i++ ) {
  843. if (g_fStopCDOnExit) {
  844. if ( g_Devices[i]->State & CD_PLAYING
  845. || g_Devices[i]->State & CD_PAUSED ) {
  846. StopTheCdromDrive( i );
  847. }
  848. }
  849. #ifdef USE_IOCTLS
  850. if ( g_Devices[i]->hCd != NULL ) {
  851. CloseHandle( g_Devices[i]->hCd );
  852. }
  853. #else
  854. if ( g_Devices[i]->hCd != 0L ) {
  855. CloseCdRom( g_Devices[i]->hCd );
  856. g_Devices[i]->hCd = 0L;
  857. }
  858. #endif
  859. ErasePlayList(i);
  860. EraseSaveList(i);
  861. EraseTrackList(i);
  862. LocalFree( (HLOCAL) g_Devices[i] );
  863. }
  864. if (g_hBrushBkgd) {
  865. DeleteObject( g_hBrushBkgd );
  866. }
  867. WinHelp( hwnd, g_HelpFileName, HELP_QUIT, 0 );
  868. PostQuitMessage( 0 );
  869. }
  870. /******************************Public*Routine******************************\
  871. * CDPlay_OnClose
  872. *
  873. *
  874. *
  875. * History:
  876. * dd-mm-93 - StephenE - Created
  877. *
  878. \**************************************************************************/
  879. BOOL
  880. CDPlay_OnClose(
  881. HWND hwnd,
  882. BOOL fShuttingDown
  883. )
  884. {
  885. /*
  886. ** If we are playing or paused and the brain "don't stop playing
  887. ** on exit" flag set, then we need to tell the user that he is about
  888. ** to go into stupid mode. Basically CD Player can only perform as expected
  889. ** if the user has not mucked about with the play list, hasn't put the
  890. ** app into random mode or intro play mode or continuous play mode or
  891. ** multi-disc mode.
  892. */
  893. if ( !fShuttingDown && !g_fStopCDOnExit
  894. && (g_State & (CD_PLAYING | CD_PAUSED) ) ) {
  895. if ( !g_fSelectedOrder || g_fIntroPlay || g_fContinuous
  896. || !g_fSingleDisk || !PlayListMatchesAvailList() ) {
  897. TCHAR s1[256];
  898. TCHAR s2[256];
  899. int iMsgBoxRtn;
  900. _tcscpy( s1, IdStr( STR_EXIT_MESSAGE ) );
  901. _tcscpy( s2, IdStr( STR_CDPLAYER ) );
  902. iMsgBoxRtn = MessageBox( g_hwndApp, s1, s2,
  903. MB_APPLMODAL | MB_DEFBUTTON1 |
  904. MB_ICONQUESTION | MB_YESNO);
  905. if ( iMsgBoxRtn == IDNO ) {
  906. return TRUE;
  907. }
  908. }
  909. }
  910. //WriteSettings();
  911. return DestroyWindow( hwnd );
  912. }
  913. /*****************************Private*Routine******************************\
  914. * CDPlay_OnEndSession
  915. *
  916. * If the session is really ending make sure that we stop the CD Player
  917. * from playing and that all the ini file stuff is saved away.
  918. *
  919. * History:
  920. * dd-mm-93 - StephenE - Created
  921. *
  922. \**************************************************************************/
  923. void
  924. CDPlay_OnEndSession(
  925. HWND hwnd,
  926. BOOL fEnding
  927. )
  928. {
  929. if ( fEnding ) {
  930. CDPlay_OnClose( hwnd, fEnding );
  931. }
  932. }
  933. /******************************Public*Routine******************************\
  934. * CDPlay_OnSize
  935. *
  936. *
  937. *
  938. * History:
  939. * dd-mm-93 - StephenE - Created
  940. *
  941. \**************************************************************************/
  942. void
  943. CDPlay_OnSize(
  944. HWND hwnd,
  945. UINT state,
  946. int cx,
  947. int cy
  948. )
  949. {
  950. if (g_fIsIconic && (state != SIZE_MINIMIZED)) {
  951. SetWindowText( hwnd, IdStr( STR_CDPLAYER ) );
  952. }
  953. g_fIsIconic = (state == SIZE_MINIMIZED);
  954. SetWindowPos(GetDlgItem(g_hwndApp,IDC_LED),
  955. hwnd,
  956. 0,
  957. 0,
  958. cx,
  959. cy,
  960. SWP_NOACTIVATE);
  961. }
  962. /*
  963. * NormalizeNameForMenuDisplay
  964. This function turns a string like "Twist & Shout" into
  965. "Twist && Shout" because otherwise it will look like
  966. "Twist _Shout" in the menu due to the accelerator char
  967. */
  968. extern "C" void NormalizeNameForMenuDisplay(TCHAR* szInput, TCHAR* szOutput, DWORD cbLen)
  969. {
  970. ZeroMemory(szOutput,cbLen);
  971. WORD index1 = 0;
  972. WORD index2 = 0;
  973. for (; index1 < _tcslen(szInput); index1++)
  974. {
  975. szOutput[index2] = szInput[index1];
  976. if (szOutput[index2] == TEXT('&'))
  977. {
  978. szOutput[++index2] = TEXT('&');
  979. }
  980. index2++;
  981. }
  982. }
  983. /*****************************Private*Routine******************************\
  984. * CDPlay_OnNotify
  985. *
  986. * Time to display the little tool tips. Also, change the status bar
  987. * so that it displays a longer version of the tool tip text.
  988. *
  989. * History:
  990. * dd-mm-94 - StephenE - Created
  991. *
  992. \**************************************************************************/
  993. LRESULT
  994. CDPlay_OnNotify(
  995. HWND hwnd,
  996. int idFrom,
  997. NMHDR *pnmhdr
  998. )
  999. {
  1000. return TRUE;
  1001. }
  1002. BOOL
  1003. CDPlay_CopyData(
  1004. HWND hwnd,
  1005. PCOPYDATASTRUCT lpcpds
  1006. )
  1007. {
  1008. LPTSTR lpCmdLine;
  1009. // Make a copy of the passed command line as we are not supposed
  1010. // to write into the one passed in the WM_COPYDATA message.
  1011. lpCmdLine = (TCHAR*)AllocMemory( lpcpds->cbData );
  1012. _tcscpy( lpCmdLine, (LPCTSTR)lpcpds->lpData );
  1013. PostMessage (hwnd, WM_CDPLAYER_COPYDATA, 0, (LPARAM)(LPVOID)lpCmdLine);
  1014. return TRUE;
  1015. } // End CopyData
  1016. /*****************************Private*Routine******************************\
  1017. * CDPlay_OnCopyData
  1018. *
  1019. * Handles command lines passed from other intances of CD Player
  1020. *
  1021. * History:
  1022. * dd-mm-94 - StephenE - Created
  1023. *
  1024. \**************************************************************************/
  1025. BOOL
  1026. CDPlay_OnCopyData(
  1027. HWND hwnd,
  1028. PCOPYDATASTRUCT lpcpds
  1029. )
  1030. {
  1031. LPTSTR lpCmdLine;
  1032. BOOL fWasPlaying = FALSE;
  1033. BOOL fUpdate;
  1034. int iTrack = -1;
  1035. int iCdRom;
  1036. // Prevent Re-entrancy while
  1037. // we are opening/closing CD's
  1038. if (g_fInCopyData)
  1039. return FALSE;
  1040. g_fInCopyData = TRUE;
  1041. /*
  1042. ** Make a copy of the passed command line as we are not supposed
  1043. ** to write into the one passed in the WM_COPYDATA message.
  1044. */
  1045. //lpCmdLine = AllocMemory( lpcpds->cbData );
  1046. //_tcscpy( lpCmdLine, (LPCTSTR)lpcpds->lpData );
  1047. lpCmdLine = (LPTSTR)(LPVOID)lpcpds;
  1048. if (lpCmdLine == NULL)
  1049. {
  1050. g_fInCopyData = FALSE;
  1051. return 0L;
  1052. }
  1053. iCdRom = ParseCommandLine( lpCmdLine, &iTrack, FALSE );
  1054. if (iCdRom < 0 && iTrack < 0) {
  1055. LocalFree( (HLOCAL)lpCmdLine );
  1056. g_fInCopyData = FALSE;
  1057. return 0L;
  1058. }
  1059. // Check if it is just an update command?!?
  1060. fUpdate = IsUpdateOptionGiven (lpCmdLine);
  1061. if ((fUpdate) && (iTrack == -1))
  1062. {
  1063. if ((iCdRom >= 0) && (iCdRom < g_NumCdDevices))
  1064. {
  1065. CheckUnitCdrom(iCdRom, TRUE);
  1066. }
  1067. LocalFree( (HLOCAL)lpCmdLine );
  1068. g_fInCopyData = FALSE;
  1069. return 0L;
  1070. }
  1071. /*
  1072. ** Remember our current playing state as we need to temporarly
  1073. ** stop the CD if it is currently playing.
  1074. */
  1075. if ( g_State & (CD_PLAYING | CD_PAUSED) )
  1076. {
  1077. #ifdef DBG
  1078. dprintf(TEXT("Auto Stopping"));
  1079. #endif
  1080. while( !LockALLTableOfContents() )
  1081. {
  1082. MSG msg;
  1083. #if DBG
  1084. dprintf(TEXT("Busy waiting for TOC to become valid!"));
  1085. #endif
  1086. GetMessage( &msg, NULL, WM_NOTIFY_TOC_READ, WM_NOTIFY_TOC_READ );
  1087. DispatchMessage( &msg );
  1088. }
  1089. CdPlayerStopCmd();
  1090. fWasPlaying = TRUE;
  1091. }
  1092. /*
  1093. ** Figure what has been passed and act on it accordingly.
  1094. */
  1095. HandlePassedCommandLine( lpCmdLine, FALSE );
  1096. /*
  1097. ** If we were playing make sure that we are still playing the
  1098. ** new track(s)
  1099. */
  1100. if ( fWasPlaying || g_fPlay )
  1101. {
  1102. #ifdef DBG
  1103. dprintf(TEXT("Trying to autoplay"));
  1104. #endif
  1105. while( !LockTableOfContents(g_CurrCdrom) )
  1106. {
  1107. MSG msg;
  1108. #ifdef DBG
  1109. dprintf(TEXT("Busy waiting for TOC to become valid!"));
  1110. #endif
  1111. GetMessage( &msg, NULL, WM_NOTIFY_TOC_READ, WM_NOTIFY_TOC_READ );
  1112. DispatchMessage( &msg );
  1113. }
  1114. CdPlayerPlayCmd();
  1115. }
  1116. /*
  1117. ** Free the local copy of the command line.
  1118. */
  1119. LocalFree( (HLOCAL)lpCmdLine );
  1120. g_fInCopyData = FALSE;
  1121. return 0L;
  1122. }
  1123. /*****************************Private*Routine******************************\
  1124. * CDPlay_OnTocRead
  1125. *
  1126. *
  1127. *
  1128. * History:
  1129. * dd-mm-94 - StephenE - Created
  1130. *
  1131. \**************************************************************************/
  1132. BOOL
  1133. CDPlay_OnTocRead(
  1134. int iDriveRead
  1135. )
  1136. {
  1137. static int iNumRead = 0;
  1138. // This serializes processing between this
  1139. // function and the various Table of Content Threads
  1140. // Preventing resource contention on CDROM Multi-Changers.
  1141. EnterCriticalSection (&g_csTOCSerialize);
  1142. /*
  1143. ** Have we finished the initial read of the CD-Rom TOCs ?
  1144. ** If so we have to re-open the device. We only need to do this
  1145. ** on Daytona because MCI device handles are not shared between threads.
  1146. */
  1147. iNumRead++;
  1148. #ifndef USE_IOCTLS
  1149. OSVERSIONINFO os;
  1150. os.dwOSVersionInfoSize = sizeof(os);
  1151. GetVersionEx(&os);
  1152. if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
  1153. {
  1154. if (iNumRead <= g_NumCdDevices) {
  1155. /*
  1156. ** Now, open the cdrom device on the UI thread.
  1157. */
  1158. g_Devices[iDriveRead]->hCd =
  1159. OpenCdRom( g_Devices[iDriveRead]->drive, NULL );
  1160. }
  1161. }
  1162. #endif
  1163. /*
  1164. ** This means that one of the threads dedicated to reading the
  1165. ** toc has finished. iDriveRead contains the relevant cdrom id.
  1166. */
  1167. LockALLTableOfContents();
  1168. if ( g_Devices[iDriveRead]->State & CD_LOADED )
  1169. {
  1170. /*
  1171. ** We have a CD loaded, so generate unique ID
  1172. ** based on TOC information.
  1173. */
  1174. g_Devices[iDriveRead]->CdInfo.Id = ComputeNewDiscId( iDriveRead );
  1175. /*
  1176. ** Check database for this compact disc
  1177. */
  1178. AddFindEntry( iDriveRead, g_Devices[iDriveRead]->CdInfo.Id,
  1179. &(g_Devices[iDriveRead]->toc) );
  1180. //plop this into the punit table
  1181. //try to find the drive in the unit table
  1182. if (g_pSink)
  1183. {
  1184. LPCDOPT pOpt = (LPCDOPT)g_pSink->GetOptions();
  1185. LPCDOPTIONS pCDOpts = NULL;
  1186. LPCDUNIT pUnit = NULL;
  1187. if (pOpt)
  1188. {
  1189. pCDOpts = pOpt->GetCDOpts();
  1190. }
  1191. if (pCDOpts)
  1192. {
  1193. pUnit = pCDOpts->pCDUnitList;
  1194. }
  1195. //scan the list to find the one we want
  1196. for (int index = 0; index < iDriveRead; index++)
  1197. {
  1198. if (pUnit)
  1199. {
  1200. pUnit = pUnit->pNext;
  1201. }
  1202. }
  1203. if (pUnit)
  1204. {
  1205. pUnit->dwTitleID = g_Devices[iDriveRead]->CdInfo.Id;
  1206. pUnit->dwNumTracks = g_Devices[iDriveRead]->CdInfo.NumTracks;
  1207. GetTOC(iDriveRead,pUnit->szNetQuery);
  1208. pOpt->DiscChanged(pUnit);
  1209. }
  1210. } //end if gpsink
  1211. }
  1212. /*
  1213. ** If we have completed the initialization of the Cd-Rom drives we can
  1214. ** now complete the startup processing of the application.
  1215. */
  1216. if (iNumRead == g_NumCdDevices)
  1217. {
  1218. CompleteCdPlayerStartUp();
  1219. }
  1220. else {
  1221. /*
  1222. ** if we are in random mode, then we need to shuffle the play lists.
  1223. ** but only if we can lock all the cd devices.
  1224. */
  1225. TimeAdjustInitialize( iDriveRead );
  1226. if ( g_fSelectedOrder == FALSE ) {
  1227. if ( LockALLTableOfContents() ) {
  1228. ComputeAndUseShufflePlayLists();
  1229. }
  1230. }
  1231. ComputeDriveComboBox();
  1232. if (iDriveRead == g_CurrCdrom)
  1233. {
  1234. if (g_fPlay)
  1235. {
  1236. CdPlayerPlayCmd();
  1237. }
  1238. SetPlayButtonsEnableState();
  1239. }
  1240. }
  1241. LeaveCriticalSection (&g_csTOCSerialize);
  1242. return TRUE;
  1243. }
  1244. /*****************************Private*Routine******************************\
  1245. * CDPlay_OnDeviceChange
  1246. *
  1247. *
  1248. *
  1249. * History:
  1250. * dd-mm-94 - StephenE - Created
  1251. *
  1252. \**************************************************************************/
  1253. BOOL
  1254. CDPlay_OnDeviceChange(
  1255. HWND hwnd,
  1256. WPARAM wParam,
  1257. LPARAM lParam)
  1258. {
  1259. UINT uiEvent = (UINT)wParam;
  1260. DWORD dwData = (DWORD)lParam;
  1261. switch (uiEvent)
  1262. {
  1263. case DBT_DEVICEARRIVAL: // Insertion
  1264. case DBT_DEVICEREMOVECOMPLETE: // Ejection
  1265. if ((PDEV_BROADCAST_HDR)dwData)
  1266. {
  1267. switch (((PDEV_BROADCAST_HDR)dwData)->dbch_devicetype)
  1268. {
  1269. case DBT_DEVTYP_VOLUME:
  1270. {
  1271. TCHAR chDrive[4] = TEXT("A:\\");
  1272. INT i,j,drive;
  1273. DWORD dwCurr;
  1274. PDEV_BROADCAST_VOLUME pdbv;
  1275. DWORD dwMask, dwDrives;
  1276. pdbv = (PDEV_BROADCAST_VOLUME)dwData;
  1277. dwMask = pdbv->dbcv_unitmask;
  1278. dwDrives = GetLogicalDrives();
  1279. dwMask &= dwDrives;
  1280. if (dwMask)
  1281. {
  1282. // Check all drives for match
  1283. for (i = 0; i < 32; i++)
  1284. {
  1285. dwCurr = 1 << i;
  1286. if (dwCurr & dwMask)
  1287. {
  1288. // Check drive
  1289. chDrive[0] = TEXT('A') + i;
  1290. if ( GetDriveType(chDrive) == DRIVE_CDROM )
  1291. {
  1292. // Find Associated Drive structure
  1293. drive = -1;
  1294. for (j = 0; j < g_NumCdDevices; j++)
  1295. {
  1296. if (g_Devices[j]->drive == chDrive[0])
  1297. drive = j;
  1298. }
  1299. // Structure not found, make one
  1300. if (drive == -1)
  1301. {
  1302. #ifdef DBG
  1303. dprintf (TEXT("CDPlay_OnDeviceChange - didn't find drive"));
  1304. #endif
  1305. if (g_NumCdDevices > MAX_CD_DEVICES)
  1306. {
  1307. // Error - not enough device slots
  1308. break;
  1309. }
  1310. g_Devices[g_NumCdDevices] = (CDROM*)AllocMemory( sizeof(CDROM) );
  1311. if (NULL == g_Devices[g_NumCdDevices])
  1312. {
  1313. // Error - unable to get enough memory
  1314. break;
  1315. }
  1316. g_Devices[g_NumCdDevices]->drive = chDrive[0];
  1317. drive = g_NumCdDevices;
  1318. g_NumCdDevices++;
  1319. }
  1320. // Insert/Eject new drive
  1321. if (uiEvent == DBT_DEVICEARRIVAL)
  1322. {
  1323. // Drive has been inserted
  1324. // The Shell should inform us using
  1325. // the AUTOPLAY through WM_COPYDDATA
  1326. //This is only necessary to detect discs with
  1327. //more than just redbook audio on them ...
  1328. //to prevent a double-scan of any discs that
  1329. //are normal audio, we need to block the "get it now"
  1330. //net prompt when scanning this way
  1331. g_fBlockNetPrompt = TRUE;
  1332. CheckUnitCdrom(drive,TRUE);
  1333. g_fBlockNetPrompt = FALSE;
  1334. return FALSE;
  1335. }
  1336. else
  1337. {
  1338. NoMediaUpdate (drive);
  1339. }
  1340. }
  1341. }
  1342. }
  1343. }
  1344. break;
  1345. }
  1346. default:
  1347. // Not a logical volume message
  1348. break;
  1349. }
  1350. }
  1351. break;
  1352. case DBT_DEVICEQUERYREMOVE: // Permission to remove a device is requested.
  1353. case DBT_DEVICEQUERYREMOVEFAILED: // Request to remove a device has been canceled.
  1354. case DBT_DEVICEREMOVEPENDING: // Device is about to be removed. Can not be denied.
  1355. case DBT_DEVICETYPESPECIFIC: // Device-specific event.
  1356. case DBT_CONFIGCHANGED: // Current configuration has changed.
  1357. default:
  1358. break;
  1359. }
  1360. return TRUE;
  1361. } // End CDPlay_OnDeviceChange
  1362. /*****************************Private*Routine******************************\
  1363. * CDPlay_OnDropFiles
  1364. *
  1365. *
  1366. *
  1367. * History:
  1368. * dd-mm-94 - StephenE - Created
  1369. *
  1370. \**************************************************************************/
  1371. void
  1372. CDPlay_OnDropFiles(
  1373. HWND hwnd,
  1374. HDROP hdrop
  1375. )
  1376. {
  1377. int cFiles;
  1378. int cGoodFiles;
  1379. int iTextLen;
  1380. int i;
  1381. TCHAR szFileName[MAX_PATH+3];
  1382. LPTSTR lpCommandLine;
  1383. BOOL fWasPlaying = FALSE;
  1384. // Prevent Re-entrancy while we are
  1385. // Opening and closing CD's
  1386. if (g_fInCopyData)
  1387. return;
  1388. g_fInCopyData = TRUE;
  1389. /*
  1390. ** Determine how many files were passed to us.
  1391. */
  1392. cFiles = DragQueryFile( hdrop, (UINT)-1, (LPTSTR)NULL, 0 );
  1393. /*
  1394. ** Calculate the length of the command line each filename should be
  1395. ** separated by a space character
  1396. */
  1397. iTextLen = _tcslen( g_szCdplayer );
  1398. iTextLen += _tcslen( g_szPlayOption );
  1399. iTextLen += _tcslen( g_szTrackOption );
  1400. for ( cGoodFiles = cFiles, i = 0; i < cFiles; i++ ) {
  1401. int unused1, unused2;
  1402. DragQueryFile( hdrop, i, szFileName, MAX_PATH );
  1403. if (IsTrackFileNameValid( szFileName, &unused1,
  1404. &unused2, TRUE, FALSE )) {
  1405. // Add on 3 extra characters - one for the space and
  1406. // two for quote marks, we do this because the filename
  1407. // given may contain space characters.
  1408. iTextLen += _tcslen( szFileName ) + 2 + 1;
  1409. }
  1410. else {
  1411. cGoodFiles--;
  1412. }
  1413. }
  1414. /*
  1415. ** If the none of the dropped files are valid tracks just return
  1416. */
  1417. if (cGoodFiles < 1) {
  1418. g_fInCopyData = FALSE;
  1419. return;
  1420. }
  1421. /*
  1422. ** Allocate a chunk of memory big enough for all the options and
  1423. ** filenames. Don't forget the NULL.
  1424. */
  1425. lpCommandLine = (TCHAR*)AllocMemory(sizeof(TCHAR) * (iTextLen + 1));
  1426. /*
  1427. ** Add a dummy intial command line arg. This is because the
  1428. ** first arg is always the name of the invoked application. We ignore
  1429. ** this paramter. Also if we are currently playing we need to
  1430. ** add the -play option to command line as well as stop the CD.
  1431. */
  1432. _tcscpy( lpCommandLine, g_szCdplayer );
  1433. if ( g_State & (CD_PLAYING | CD_PAUSED) ) {
  1434. CdPlayerStopCmd();
  1435. fWasPlaying = TRUE;
  1436. _tcscat( lpCommandLine, g_szPlayOption );
  1437. }
  1438. /*
  1439. ** If there is more than one file name specified then we should constuct
  1440. ** a new playlist from the given files.
  1441. */
  1442. if ( cGoodFiles > 1) {
  1443. _tcscat( lpCommandLine, g_szTrackOption );
  1444. }
  1445. /*
  1446. ** Build up the command line.
  1447. */
  1448. for ( i = 0; i < cFiles; i++ ) {
  1449. int unused1, unused2;
  1450. DragQueryFile( hdrop, i, szFileName, MAX_PATH );
  1451. if (IsTrackFileNameValid( szFileName, &unused1,
  1452. &unused2, TRUE, TRUE )) {
  1453. _tcscat( lpCommandLine, TEXT("\'") );
  1454. _tcscat( lpCommandLine, szFileName );
  1455. _tcscat( lpCommandLine, TEXT("\'") );
  1456. _tcscat( lpCommandLine, g_szBlank );
  1457. }
  1458. }
  1459. /*
  1460. ** now process the newly constructed command line.
  1461. */
  1462. HandlePassedCommandLine( lpCommandLine, FALSE );
  1463. /*
  1464. ** If we were playing make sure that we are still playing the
  1465. ** new track(s)
  1466. */
  1467. if ( fWasPlaying ) {
  1468. CdPlayerPlayCmd();
  1469. }
  1470. LocalFree( lpCommandLine );
  1471. DragFinish( hdrop );
  1472. g_fInCopyData = FALSE;
  1473. }
  1474. /*****************************Private*Routine******************************\
  1475. * ResolveLink
  1476. *
  1477. * Takes the shortcut (shell link) file pointed to be szFileName and
  1478. * resolves the link returning the linked file name in szFileName.
  1479. *
  1480. * szFileName must point to at least MAX_PATH amount of TCHARS. The function
  1481. * return TRUE if the link was successfully resolved and FALSE otherwise.
  1482. *
  1483. * History:
  1484. * dd-mm-94 - StephenE - Created
  1485. * 03-11-95 - ShawnB - Unicode enabled
  1486. *
  1487. \**************************************************************************/
  1488. BOOL
  1489. ResolveLink(
  1490. TCHAR *szFileName
  1491. )
  1492. {
  1493. return FALSE;
  1494. }
  1495. //#endif
  1496. /******************************Public*Routine******************************\
  1497. * FatalApplicationError
  1498. *
  1499. * Call this function if something "bad" happens to the application. It
  1500. * displays an error message and then kills itself.
  1501. *
  1502. * History:
  1503. * 18-11-93 - StephenE - Created
  1504. *
  1505. \**************************************************************************/
  1506. void
  1507. FatalApplicationError(
  1508. INT uIdStringResource,
  1509. ...
  1510. )
  1511. {
  1512. va_list va;
  1513. TCHAR chBuffer1[ STR_MAX_STRING_LEN ];
  1514. TCHAR chBuffer2[ STR_MAX_STRING_LEN ];
  1515. /*
  1516. ** Load the relevant messages
  1517. */
  1518. va_start(va, uIdStringResource);
  1519. wvsprintf(chBuffer1, IdStr(uIdStringResource), va);
  1520. va_end(va);
  1521. _tcscpy( chBuffer2, IdStr(STR_FATAL_ERROR) ); /*"CD Player: Fatal Error"*/
  1522. /*
  1523. ** How much of the application do we need to kill
  1524. */
  1525. if (g_hwndApp) {
  1526. if ( IsWindowVisible(g_hwndApp) ) {
  1527. BringWindowToTop(g_hwndApp);
  1528. }
  1529. MessageBox( g_hwndApp, chBuffer1, chBuffer2,
  1530. MB_ICONSTOP | MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
  1531. DestroyWindow( g_hwndApp );
  1532. }
  1533. else {
  1534. MessageBox( NULL, chBuffer1, chBuffer2,
  1535. MB_APPLMODAL | MB_ICONSTOP | MB_OK | MB_SETFOREGROUND );
  1536. }
  1537. ExitProcess( (UINT)-1 );
  1538. }
  1539. /******************************Public*Routine******************************\
  1540. * IdStr
  1541. *
  1542. * Loads the given string resource ID into the passed storage.
  1543. *
  1544. * History:
  1545. * 18-11-93 - StephenE - Created
  1546. *
  1547. \**************************************************************************/
  1548. LPTSTR
  1549. IdStr(
  1550. int idResource
  1551. )
  1552. {
  1553. static TCHAR chBuffer[ STR_MAX_STRING_LEN ];
  1554. if (LoadString(g_hInst, idResource, chBuffer, STR_MAX_STRING_LEN) == 0)
  1555. {
  1556. return TEXT("");
  1557. }
  1558. return chBuffer;
  1559. }
  1560. /******************************Public*Routine******************************\
  1561. * CheckMenuItemIfTrue
  1562. *
  1563. * If "flag" TRUE the given menu item is checked, otherwise it is unchecked.
  1564. *
  1565. * History:
  1566. * 18-11-93 - StephenE - Created
  1567. *
  1568. \**************************************************************************/
  1569. void
  1570. CheckMenuItemIfTrue(
  1571. HMENU hMenu,
  1572. UINT idItem,
  1573. BOOL flag
  1574. )
  1575. {
  1576. UINT uFlags;
  1577. if (flag) {
  1578. uFlags = MF_CHECKED | MF_BYCOMMAND;
  1579. }
  1580. else {
  1581. uFlags = MF_UNCHECKED | MF_BYCOMMAND;
  1582. }
  1583. CheckMenuItem( hMenu, idItem, uFlags );
  1584. }
  1585. /******************************Public*Routine******************************\
  1586. * ReadSettings
  1587. *
  1588. * Read app settings from ini file.
  1589. *
  1590. * History:
  1591. * 18-11-93 - StephenE - Created
  1592. *
  1593. \**************************************************************************/
  1594. void
  1595. ReadSettings(
  1596. void* pData
  1597. )
  1598. {
  1599. LPCDOPT pOpt = NULL;
  1600. LPCDOPTDATA pOptionData = (LPCDOPTDATA)pData;
  1601. //if no option data, get some!
  1602. if (pOptionData == NULL)
  1603. {
  1604. pOpt = (LPCDOPT)g_pSink->GetOptions();
  1605. if( pOpt )
  1606. {
  1607. LPCDOPTIONS pOptions = pOpt->GetCDOpts();
  1608. pOptionData = pOptions->pCDData;
  1609. }
  1610. }
  1611. //if we still don't have it, bail out!
  1612. if (pOptionData == NULL)
  1613. {
  1614. return;
  1615. }
  1616. g_fStopCDOnExit = pOptionData->fExitStop;
  1617. //if being called because of user dialog setting, reset the play mode flag
  1618. if (pData != NULL)
  1619. {
  1620. g_fPlay = pOptionData->fStartPlay;
  1621. }
  1622. if ( g_NumCdDevices < 2 )
  1623. {
  1624. g_fMultiDiskAvailable = FALSE;
  1625. g_fSingleDisk = TRUE;
  1626. }
  1627. else {
  1628. g_fMultiDiskAvailable = TRUE;
  1629. g_fSingleDisk = FALSE;
  1630. }
  1631. g_fDisplayD = FALSE;
  1632. g_fDisplayDr = FALSE;
  1633. g_fDisplayT = FALSE;
  1634. g_fDisplayTr = FALSE;
  1635. switch (pOptionData->fDispMode)
  1636. {
  1637. case CDDISP_CDTIME :
  1638. {
  1639. g_fDisplayD = TRUE;
  1640. }
  1641. break;
  1642. case CDDISP_CDREMAIN :
  1643. {
  1644. g_fDisplayDr = TRUE;
  1645. }
  1646. break;
  1647. case CDDISP_TRACKTIME :
  1648. {
  1649. g_fDisplayT = TRUE;
  1650. }
  1651. break;
  1652. case CDDISP_TRACKREMAIN :
  1653. {
  1654. g_fDisplayTr = TRUE;
  1655. }
  1656. break;
  1657. }
  1658. g_IntroPlayLength = pOptionData->dwIntroTime;
  1659. //set into correct mode
  1660. g_fSelectedOrder = (pOptionData->dwPlayMode != IDM_MODE_RANDOM);
  1661. /*
  1662. ** Make sure that the LED display format is correct
  1663. */
  1664. if ( g_fDisplayT == FALSE && g_fDisplayTr == FALSE
  1665. && g_fDisplayDr == FALSE && g_fDisplayD == FALSE)
  1666. {
  1667. g_fDisplayT = TRUE;
  1668. }
  1669. }
  1670. /******************************Public*Routine******************************\
  1671. * LockTableOfContents
  1672. *
  1673. * This function is used to determine if it is valid for the UI thread
  1674. * to access the table of contents for the specified CD Rom. If this
  1675. * function returns FALSE the UI thread should NOT touch the table of
  1676. * contents for this CD Rom.
  1677. *
  1678. * History:
  1679. * 18-11-93 - StephenE - Created
  1680. *
  1681. \**************************************************************************/
  1682. BOOL
  1683. LockTableOfContents(
  1684. int cdrom
  1685. )
  1686. {
  1687. DWORD dwRet;
  1688. if (g_Devices[cdrom]->fIsTocValid) {
  1689. return TRUE;
  1690. }
  1691. if (g_Devices[cdrom]->hThreadToc == NULL) {
  1692. return FALSE;
  1693. }
  1694. dwRet = WaitForSingleObject(g_Devices[cdrom]->hThreadToc, 0L );
  1695. if (dwRet == WAIT_OBJECT_0) {
  1696. GetExitCodeThread( g_Devices[cdrom]->hThreadToc, &dwRet );
  1697. g_Devices[cdrom]->fIsTocValid = (BOOL)dwRet;
  1698. CloseHandle( g_Devices[cdrom]->hThreadToc );
  1699. g_Devices[cdrom]->hThreadToc = NULL;
  1700. }
  1701. return g_Devices[cdrom]->fIsTocValid;
  1702. }
  1703. /******************************Public*Routine******************************\
  1704. * LockAllTableOfContents
  1705. *
  1706. * This function is used to determine if it is valid for the UI thread
  1707. * to access the table of contents for the ALL the cdroms devices.
  1708. * The function returns FALSE the UI thread should NOT touch the table of
  1709. * contents for any CD Rom.
  1710. *
  1711. * History:
  1712. * 18-11-93 - StephenE - Created
  1713. *
  1714. \**************************************************************************/
  1715. BOOL
  1716. LockALLTableOfContents(
  1717. void
  1718. )
  1719. {
  1720. BOOL fLock;
  1721. int i;
  1722. for (i = 0, fLock = TRUE; fLock && (i < g_NumCdDevices); i++) {
  1723. fLock = LockTableOfContents(i);
  1724. }
  1725. return fLock;
  1726. }
  1727. /******************************Public*Routine******************************\
  1728. * AllocMemory
  1729. *
  1730. * Allocates a memory of the given size. This function will terminate the
  1731. * application if the allocation failed. Memory allocated by this function
  1732. * must be freed with LocalFree. The memory should not be locked or unlocked.
  1733. *
  1734. * History:
  1735. * 18-11-93 - StephenE - Created
  1736. *
  1737. \**************************************************************************/
  1738. LPVOID
  1739. AllocMemory(
  1740. UINT uSize
  1741. )
  1742. {
  1743. LPVOID lp;
  1744. lp = LocalAlloc( LPTR, uSize );
  1745. if (lp == NULL) {
  1746. /*
  1747. ** No memory - no application, simple !
  1748. */
  1749. FatalApplicationError( STR_FAIL_INIT );
  1750. }
  1751. return lp;
  1752. }
  1753. /******************************Public*Routine******************************\
  1754. * SetPlayButtonsEnableState
  1755. *
  1756. * Sets the play buttons enable state to match the state of the current
  1757. * cdrom device. See below...
  1758. *
  1759. *
  1760. * CDPlayer buttons enable state table
  1761. * Ŀ
  1762. * E=Enabled D=Disabled Play Pause Eject Stop Other DataB
  1763. * Ĵ
  1764. * Disk in use D D D D D D
  1765. * Ĵ
  1766. * No music cd or data cdrom D D E D D D
  1767. * Ĵ
  1768. * Music cd (playing) D E E E E E
  1769. * Ĵ
  1770. * Music cd (paused) E E E E E E
  1771. * Ĵ
  1772. * Music cd (stopped) E D E D E E
  1773. *
  1774. *
  1775. *
  1776. * History:
  1777. * 18-11-93 - StephenE - Created
  1778. *
  1779. \**************************************************************************/
  1780. void
  1781. SetPlayButtonsEnableState(
  1782. void
  1783. )
  1784. {
  1785. BOOL fMusicCdLoaded;
  1786. BOOL fActivePlayList;
  1787. int i;
  1788. /*
  1789. ** Do we have a music cd loaded.
  1790. */
  1791. if (g_State & (CD_BEING_SCANNED | CD_NO_CD | CD_DATA_CD_LOADED | CD_IN_USE))
  1792. {
  1793. fMusicCdLoaded = FALSE;
  1794. }
  1795. else
  1796. {
  1797. fMusicCdLoaded = TRUE;
  1798. }
  1799. /*
  1800. ** Is there an active playlist
  1801. */
  1802. if ( (CDTIME(g_CurrCdrom).TotalMin == 0) && (CDTIME(g_CurrCdrom).TotalSec == 0) )
  1803. {
  1804. fActivePlayList = FALSE;
  1805. }
  1806. else
  1807. {
  1808. fActivePlayList = TRUE;
  1809. }
  1810. //tell the main UI about the track button
  1811. HWND hwndTrackButton = GetDlgItem(GetParent(g_hwndApp),IDB_TRACK);
  1812. if (hwndTrackButton)
  1813. {
  1814. EnableWindow(hwndTrackButton,(fMusicCdLoaded & fActivePlayList));
  1815. }
  1816. //just turn off all "old" cdplayer buttons, since they are not used in this app
  1817. EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_PLAY)], FALSE );
  1818. EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)], FALSE );
  1819. EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_PAUSE)], FALSE );
  1820. /*
  1821. ** Do the remaining buttons
  1822. */
  1823. for ( i = IDM_PLAYBAR_PREVTRACK; i <= IDM_PLAYBAR_NEXTTRACK; i++ )
  1824. {
  1825. EnableWindow( g_hwndControls[INDEX(i)], FALSE );
  1826. }
  1827. /*
  1828. ** If the drive is in use then we must diable the eject button.
  1829. */
  1830. EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_EJECT)], FALSE );
  1831. }
  1832. /******************************Public*Routine******************************\
  1833. * HeartBeatTimerProc
  1834. *
  1835. * This function is responsible for.
  1836. *
  1837. * 1. detecting new or ejected cdroms.
  1838. * 2. flashing the LED display if we are in paused mode.
  1839. * 3. Incrementing the LED display if we are in play mode.
  1840. *
  1841. * History:
  1842. * 18-11-93 - StephenE - Created
  1843. *
  1844. \**************************************************************************/
  1845. void CALLBACK
  1846. HeartBeatTimerProc(
  1847. HWND hwnd,
  1848. UINT uMsg,
  1849. UINT idEvent,
  1850. DWORD dwTime
  1851. )
  1852. {
  1853. static DWORD dwTickCount;
  1854. DWORD dwMod;
  1855. ++dwTickCount;
  1856. dwMod = (dwTickCount % 6);
  1857. /*
  1858. ** Check for "letting go" of drive every 3 seconds
  1859. */
  1860. if ( 0 == dwMod )
  1861. {
  1862. for (int i = 0; i < g_NumCdDevices; i++)
  1863. {
  1864. if ( (!(g_Devices[i]->State & CD_EDITING))
  1865. && (!(g_Devices[i]->State & CD_PLAYING)) )
  1866. {
  1867. CheckUnitCdrom(i,FALSE);
  1868. }
  1869. }
  1870. }
  1871. if ( g_State & CD_PLAYING ) {
  1872. if ( LockALLTableOfContents() ) {
  1873. SyncDisplay();
  1874. }
  1875. }
  1876. /*
  1877. ** If we are paused and NOT skipping flash the display.
  1878. */
  1879. else if ((g_State & CD_PAUSED) && !(g_State & CD_SEEKING)) {
  1880. HWND hwnd;
  1881. switch ( dwMod ) {
  1882. case 2:
  1883. case 5:
  1884. case 8:
  1885. case 11:
  1886. if ( g_fIsIconic ) {
  1887. //Next two lines removed to fix tooltip bug:<mwetzel 08.26.97>
  1888. //SetWindowText( g_hwndApp, g_szBlank );
  1889. //UpdateWindow( g_hwndApp );
  1890. }
  1891. else {
  1892. hwnd = g_hwndControls[INDEX(IDC_LED)];
  1893. g_fFlashLed = TRUE;
  1894. //SetWindowText( hwnd, g_szBlank );
  1895. g_fFlashLed = FALSE;
  1896. }
  1897. break;
  1898. case 0:
  1899. case 3:
  1900. case 6:
  1901. case 9:
  1902. UpdateDisplay( DISPLAY_UPD_LED );
  1903. break;
  1904. }
  1905. }
  1906. }
  1907. /******************************Public*Routine******************************\
  1908. * SkipBeatTimerProc
  1909. *
  1910. * This function is responsible for advancing or retreating the current
  1911. * playing position.
  1912. *
  1913. *
  1914. * History:
  1915. * 18-11-93 - StephenE - Created
  1916. *
  1917. \**************************************************************************/
  1918. void CALLBACK
  1919. SkipBeatTimerProc(
  1920. HWND hwnd,
  1921. UINT uMsg,
  1922. UINT idEvent,
  1923. DWORD dwTime
  1924. )
  1925. {
  1926. /*
  1927. ** Deteremine if it is time to accelerate the skipping frequency.
  1928. */
  1929. switch (++g_AcceleratorCount) {
  1930. case SKIP_ACCELERATOR_LIMIT1:
  1931. KillTimer( hwnd, idEvent );
  1932. SetTimer( hwnd, idEvent, SKIPBEAT_TIMER_RATE2, (TIMERPROC)SkipBeatTimerProc );
  1933. break;
  1934. case SKIP_ACCELERATOR_LIMIT2:
  1935. KillTimer( hwnd, idEvent );
  1936. SetTimer( hwnd, idEvent, SKIPBEAT_TIMER_RATE3, (TIMERPROC)SkipBeatTimerProc );
  1937. break;
  1938. }
  1939. if ( LockALLTableOfContents() ) {
  1940. if ( idEvent == IDM_PLAYBAR_SKIPFORE) {
  1941. TimeAdjustIncSecond( g_CurrCdrom );
  1942. /*
  1943. ** When TimeAjustIncSecond gets to the end of the last track
  1944. ** it sets CURRTRACK(g_CurrCdrom) equal to NULL. When this
  1945. ** occurs we effectively reset the CD Player
  1946. */
  1947. if ( CURRTRACK(g_CurrCdrom) == NULL ) {
  1948. if ( g_State & (CD_WAS_PLAYING | CD_PAUSED) ) {
  1949. SendMessage( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)],
  1950. WM_LBUTTONDOWN, 0, 0L );
  1951. SendMessage( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)],
  1952. WM_LBUTTONUP, 0, 0L );
  1953. }
  1954. else {
  1955. /*
  1956. ** Seek to the first playable track.
  1957. */
  1958. CURRTRACK(g_CurrCdrom) = FindFirstTrack( g_CurrCdrom );
  1959. if ( CURRTRACK(g_CurrCdrom) != NULL ) {
  1960. TimeAdjustSkipToTrack( g_CurrCdrom,
  1961. CURRTRACK(g_CurrCdrom) );
  1962. UpdateDisplay( DISPLAY_UPD_LED | DISPLAY_UPD_TRACK_TIME |
  1963. DISPLAY_UPD_TRACK_NAME );
  1964. SetPlayButtonsEnableState();
  1965. }
  1966. }
  1967. }
  1968. }
  1969. else {
  1970. TimeAdjustDecSecond( g_CurrCdrom );
  1971. }
  1972. }
  1973. }
  1974. /******************************Public*Routine******************************\
  1975. * UpdateDisplay
  1976. *
  1977. * This routine updates the display according to the flags that
  1978. * are passed in. The display consists of the LED display, the
  1979. * track and title names, the disc and track lengths and the cdrom
  1980. * combo-box.
  1981. *
  1982. * History:
  1983. * 18-11-93 - StephenE - Created
  1984. *
  1985. \**************************************************************************/
  1986. void
  1987. UpdateDisplay(
  1988. DWORD Flags
  1989. )
  1990. {
  1991. TCHAR lpsz[55];
  1992. TCHAR lpszIcon[75];
  1993. PTRACK_PLAY tr;
  1994. int track;
  1995. int mtemp, stemp, m, s;
  1996. /*
  1997. ** Check for valid flags
  1998. */
  1999. if ( Flags == 0 ) {
  2000. return;
  2001. }
  2002. ZeroMemory(lpsz,sizeof(lpsz));
  2003. /*
  2004. ** Grab current track information
  2005. */
  2006. if (CURRTRACK(g_CurrCdrom) != NULL) {
  2007. track = CURRTRACK(g_CurrCdrom)->TocIndex + FIRSTTRACK(g_CurrCdrom);
  2008. }
  2009. else {
  2010. track = 0;
  2011. }
  2012. /*
  2013. ** Update the LED box?
  2014. */
  2015. if (Flags & DISPLAY_UPD_LED) {
  2016. /*
  2017. ** Update LED box
  2018. */
  2019. if (g_fDisplayT) {
  2020. if (Flags & DISPLAY_UPD_LEADOUT_TIME) {
  2021. wsprintf( lpsz, TRACK_TIME_LEADOUT_FORMAT,
  2022. //track,
  2023. CDTIME(g_CurrCdrom).TrackCurMin,
  2024. g_szTimeSep,
  2025. CDTIME(g_CurrCdrom).TrackCurSec );
  2026. }
  2027. else {
  2028. wsprintf( lpsz, TRACK_TIME_FORMAT,
  2029. //track,
  2030. CDTIME(g_CurrCdrom).TrackCurMin,
  2031. g_szTimeSep,
  2032. CDTIME(g_CurrCdrom).TrackCurSec );
  2033. }
  2034. }
  2035. if (g_fDisplayTr) {
  2036. if (Flags & DISPLAY_UPD_LEADOUT_TIME) {
  2037. wsprintf( lpsz, TRACK_REM_FORMAT, //track - 1,
  2038. CDTIME(g_CurrCdrom).TrackCurMin,
  2039. g_szTimeSep,
  2040. CDTIME(g_CurrCdrom).TrackCurSec );
  2041. }
  2042. else {
  2043. wsprintf( lpsz, TRACK_REM_FORMAT, //track,
  2044. CDTIME(g_CurrCdrom).TrackRemMin,
  2045. g_szTimeSep,
  2046. CDTIME(g_CurrCdrom).TrackRemSec );
  2047. }
  2048. }
  2049. if (g_fDisplayD)
  2050. {
  2051. /*
  2052. ** Compute remaining time, then sub from total time
  2053. */
  2054. mtemp = stemp = m = s =0;
  2055. if (CURRTRACK(g_CurrCdrom) != NULL)
  2056. {
  2057. for ( tr = CURRTRACK(g_CurrCdrom)->nextplay;
  2058. tr != NULL;
  2059. tr = tr->nextplay )
  2060. {
  2061. FigureTrackTime( g_CurrCdrom, tr->TocIndex, &mtemp, &stemp );
  2062. m+=mtemp;
  2063. s+=stemp;
  2064. }
  2065. m+= CDTIME(g_CurrCdrom).TrackRemMin;
  2066. s+= CDTIME(g_CurrCdrom).TrackRemSec;
  2067. }
  2068. m += (s / 60);
  2069. s = (s % 60);
  2070. CDTIME(g_CurrCdrom).RemMin = m;
  2071. CDTIME(g_CurrCdrom).RemSec = s;
  2072. //convert to a total number of seconds remaining
  2073. s = (m*60) + s;
  2074. //convert total time to a number of seconds
  2075. DWORD stotal = (CDTIME(g_CurrCdrom).TotalMin*60) + CDTIME(g_CurrCdrom).TotalSec;
  2076. //subtract time remaining from total time
  2077. stotal = stotal - s;
  2078. m = (stotal / 60);
  2079. s = (stotal % 60);
  2080. wsprintf( lpsz, DISC_TIME_FORMAT,
  2081. m,
  2082. g_szTimeSep,
  2083. s);
  2084. }
  2085. if (g_fDisplayDr) {
  2086. /*
  2087. ** Compute remaining time
  2088. */
  2089. mtemp = stemp = m = s =0;
  2090. if (CURRTRACK(g_CurrCdrom) != NULL) {
  2091. for ( tr = CURRTRACK(g_CurrCdrom)->nextplay;
  2092. tr != NULL;
  2093. tr = tr->nextplay ) {
  2094. FigureTrackTime( g_CurrCdrom, tr->TocIndex, &mtemp, &stemp );
  2095. m+=mtemp;
  2096. s+=stemp;
  2097. }
  2098. m+= CDTIME(g_CurrCdrom).TrackRemMin;
  2099. s+= CDTIME(g_CurrCdrom).TrackRemSec;
  2100. }
  2101. m+= (s / 60);
  2102. s = (s % 60);
  2103. CDTIME(g_CurrCdrom).RemMin = m;
  2104. CDTIME(g_CurrCdrom).RemSec = s;
  2105. wsprintf( lpsz, DISC_REM_FORMAT,
  2106. CDTIME(g_CurrCdrom).RemMin,
  2107. g_szTimeSep,
  2108. CDTIME(g_CurrCdrom).RemSec );
  2109. }
  2110. SetWindowText( g_hwndControls[INDEX(IDC_LED)], lpsz );
  2111. if (Flags != DISPLAY_UPD_LED)
  2112. {
  2113. InvalidateRect(g_hwndControls[INDEX(IDC_LED)],NULL,FALSE);
  2114. UpdateWindow(g_hwndControls[INDEX(IDC_LED)]);
  2115. }
  2116. if (g_fIsIconic) {
  2117. //Next four lines changed to fix tooltip bugs: <mwetzel 08.26.97>
  2118. if( g_Devices[g_CurrCdrom]->State & CD_PAUSED )
  2119. wsprintf( lpszIcon, IdStr( STR_CDPLAYER_PAUSED), lpsz );
  2120. else
  2121. wsprintf( lpszIcon, IdStr( STR_CDPLAYER_TIME ), lpsz );
  2122. SetWindowText( g_hwndApp, lpszIcon );
  2123. }
  2124. }
  2125. //update the framework window to show the time
  2126. if ((CURRTRACK(g_CurrCdrom)) && (lpsz[0] != TEXT('\0')))
  2127. {
  2128. //might already be pre-pending track number
  2129. if (lpsz[0] != TEXT('['))
  2130. {
  2131. wsprintf(lpszIcon,TEXT("[%i] %s"),CURRTRACK(g_CurrCdrom)->TocIndex+1,lpsz);
  2132. }
  2133. else
  2134. {
  2135. _tcscpy(lpszIcon,lpsz);
  2136. }
  2137. MMSETTITLE mmTitle;
  2138. mmTitle.mmInfoText = MMINFOTEXT_DESCRIPTION;
  2139. mmTitle.szTitle = lpszIcon;
  2140. g_pSink->OnEvent(MMEVENT_SETTITLE,&mmTitle);
  2141. }
  2142. /*
  2143. ** Update Title?
  2144. */
  2145. if (Flags & DISPLAY_UPD_TITLE_NAME)
  2146. {
  2147. ComputeDriveComboBox( );
  2148. SetWindowText( g_hwndControls[INDEX(IDC_TITLE_NAME)],
  2149. (LPCTSTR)TITLE(g_CurrCdrom) );
  2150. //update the framework window to show the title
  2151. MMSETTITLE mmTitle;
  2152. mmTitle.mmInfoText = MMINFOTEXT_TITLE;
  2153. mmTitle.szTitle = TITLE(g_CurrCdrom);
  2154. g_pSink->OnEvent(MMEVENT_SETTITLE,&mmTitle);
  2155. }
  2156. }
  2157. /******************************Public*Routine******************************\
  2158. * Common_OnCtlColor
  2159. *
  2160. * Here we return a brush to paint the background with. The brush is the same
  2161. * color as the face of a button. We also set the text background color so
  2162. * that static controls draw correctly. This function is shared with the
  2163. * disk info/editing dialog box.
  2164. *
  2165. * History:
  2166. * dd-mm-93 - StephenE - Created
  2167. *
  2168. \**************************************************************************/
  2169. HBRUSH
  2170. Common_OnCtlColor(
  2171. HWND hwnd,
  2172. HDC hdc,
  2173. HWND hwndChild,
  2174. int type
  2175. )
  2176. {
  2177. SetBkColor( hdc, GetSysColor(COLOR_BTNFACE) );
  2178. return g_hBrushBkgd;
  2179. }
  2180. /******************************Public*Routine******************************\
  2181. * Common_OnMeasureItem
  2182. *
  2183. * All items are the same height and width.
  2184. *
  2185. * We only have to update the height field for owner draw combo boxes and
  2186. * list boxes. This function is shared with the disk edit/info dialog box.
  2187. *
  2188. * History:
  2189. * dd-mm-93 - StephenE - Created
  2190. *
  2191. \**************************************************************************/
  2192. BOOL
  2193. Common_OnMeasureItem(
  2194. HWND hwnd,
  2195. MEASUREITEMSTRUCT *lpMeasureItem
  2196. )
  2197. {
  2198. if (lpMeasureItem->CtlType == ODT_MENU)
  2199. {
  2200. return FALSE;
  2201. }
  2202. HFONT hFont;
  2203. int cyBorder, cyDelta;
  2204. LOGFONT lf;
  2205. hFont = GetWindowFont( hwnd );
  2206. if ( hFont != NULL ) {
  2207. GetObject( hFont, sizeof(lf), &lf );
  2208. }
  2209. else {
  2210. SystemParametersInfo( SPI_GETICONTITLELOGFONT,
  2211. sizeof(lf), (LPVOID)&lf, 0 );
  2212. }
  2213. cyDelta = ABS( lf.lfHeight ) / 2;
  2214. cyBorder = GetSystemMetrics( SM_CYBORDER );
  2215. //
  2216. // Ensure enough room between chars.
  2217. //
  2218. if (cyDelta < 4 * cyBorder) {
  2219. cyDelta = 4 * cyBorder;
  2220. }
  2221. lpMeasureItem->itemHeight = ABS( lf.lfHeight ) + cyDelta;
  2222. return TRUE;
  2223. }
  2224. /******************************Public*Routine******************************\
  2225. * DrawTrackItem
  2226. *
  2227. * This routine draws the information in a cell of the track name
  2228. * combo box.
  2229. *
  2230. * History:
  2231. * 18-11-93 - StephenE - Created
  2232. *
  2233. \**************************************************************************/
  2234. void
  2235. DrawTrackItem(
  2236. HDC hdc,
  2237. const RECT *r,
  2238. DWORD item,
  2239. BOOL selected
  2240. )
  2241. {
  2242. SIZE si;
  2243. int i;
  2244. int cxTrk;
  2245. PTRACK_INF t;
  2246. TCHAR s[ARTIST_LENGTH];
  2247. TCHAR szTrk[16];
  2248. /*
  2249. ** Check for invalid items
  2250. */
  2251. if ( item == (DWORD)-1 ) {
  2252. return;
  2253. }
  2254. if ( ALLTRACKS(g_CurrCdrom) == NULL ) {
  2255. return;
  2256. }
  2257. /*
  2258. ** Check selection status, and set up to draw correctly
  2259. */
  2260. if ( selected ) {
  2261. SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
  2262. SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
  2263. }
  2264. else {
  2265. SetBkColor( hdc, GetSysColor(COLOR_WINDOW));
  2266. SetTextColor( hdc, GetSysColor(COLOR_WINDOWTEXT));
  2267. }
  2268. /*
  2269. ** Get track info
  2270. */
  2271. t = FindTrackNodeFromTocIndex( item, ALLTRACKS( g_CurrCdrom ) );
  2272. if ( (t != NULL) && (t->name != NULL) ) {
  2273. /*
  2274. ** Do we need to munge track name (clip to listbox)?
  2275. */
  2276. wsprintf(szTrk, TEXT("<%02d> "), t->TocIndex + FIRSTTRACK(g_CurrCdrom));
  2277. GetTextExtentPoint( hdc, szTrk, _tcslen(szTrk), &si );
  2278. cxTrk = si.cx;
  2279. i = _tcslen( t->name ) + 1;
  2280. do {
  2281. GetTextExtentPoint( hdc, t->name, --i, &si );
  2282. } while( si.cx > (r->right - r->left - cxTrk) );
  2283. ZeroMemory( s, TRACK_TITLE_LENGTH * sizeof( TCHAR ) );
  2284. _tcsncpy( s, t->name, i );
  2285. }
  2286. else {
  2287. _tcscpy( s, g_szBlank );
  2288. i = 1;
  2289. }
  2290. /*
  2291. ** Draw track name
  2292. */
  2293. ExtTextOut( hdc, r->left, r->top,
  2294. ETO_OPAQUE | ETO_CLIPPED,
  2295. r, s, i, NULL );
  2296. /*
  2297. ** draw track number
  2298. */
  2299. if ( t != NULL ) {
  2300. ExtTextOut( hdc, r->right - cxTrk, r->top, ETO_CLIPPED,
  2301. r, szTrk, _tcslen( szTrk ), NULL );
  2302. }
  2303. }
  2304. /*****************************Private*Routine******************************\
  2305. * HandlePassedCommandLine
  2306. *
  2307. * This function gets called to handle command line options that are passed to
  2308. * CDPlayer as the result of the WM_DROPFILES or WM_COPYDATA messages.
  2309. *
  2310. * History:
  2311. * dd-mm-94 - StephenE - Created
  2312. *
  2313. \**************************************************************************/
  2314. void
  2315. HandlePassedCommandLine(
  2316. LPTSTR lpCmdLine,
  2317. BOOL fCheckCDRom
  2318. )
  2319. {
  2320. int i;
  2321. int iTrack = -1, iCDrom;
  2322. iCDrom = ParseCommandLine( lpCmdLine, &iTrack, TRUE );
  2323. if ((iCDrom < 0) || (iCDrom >= g_NumCdDevices))
  2324. return;
  2325. // CheckUnitCDRom to reload Table of Contents
  2326. if ( fCheckCDRom )
  2327. {
  2328. CheckUnitCdrom(iCDrom, TRUE);
  2329. while( !LockTableOfContents(iCDrom) )
  2330. {
  2331. MSG msg;
  2332. GetMessage( &msg, NULL, WM_NOTIFY_TOC_READ, WM_NOTIFY_TOC_READ );
  2333. DispatchMessage( &msg );
  2334. }
  2335. }
  2336. #if 0
  2337. if (iCDrom != g_CurrCdrom)
  2338. {
  2339. HWND hwndBtn = g_hwndControls[INDEX(IDC_ARTIST_NAME)];
  2340. SwitchToCdrom( iCDrom, TRUE );
  2341. SetPlayButtonsEnableState();
  2342. SendMessage( hwndBtn, CB_SETCURSEL, (WPARAM)iCDrom, 0 );
  2343. }
  2344. #endif
  2345. /*
  2346. ** Initialize the new play list for each drive.
  2347. */
  2348. for ( i = 0; i < g_NumCdDevices; i++)
  2349. {
  2350. TimeAdjustInitialize( i );
  2351. }
  2352. // Set Current Track to specified track
  2353. if ( iTrack != -1 )
  2354. {
  2355. PTRACK_PLAY tr;
  2356. tr = PLAYLIST( iCDrom );
  2357. if ( tr != NULL )
  2358. {
  2359. for( i = 0; i < iTrack; i++, tr = tr->nextplay );
  2360. TimeAdjustSkipToTrack( iCDrom, tr );
  2361. }
  2362. }
  2363. }
  2364. /******************************Public*Routine******************************\
  2365. * IsUpdateOptionGiven
  2366. *
  2367. * Checks the command line to see if the "-update" option has been passed.
  2368. *
  2369. * History:
  2370. * dd-mm-95 - StephenE - Created
  2371. *
  2372. \**************************************************************************/
  2373. BOOL
  2374. IsUpdateOptionGiven(
  2375. LPTSTR lpstr
  2376. )
  2377. {
  2378. TCHAR chOption[MAX_PATH];
  2379. /*
  2380. ** Start by converting everything to upper case.
  2381. */
  2382. CharUpperBuff( lpstr, _tcslen(lpstr) );
  2383. /*
  2384. ** The first parameter on the command line is always the
  2385. ** string used invoke the program. ie cdplayer.exe
  2386. */
  2387. lpstr += _tcsspn( lpstr, g_szBlank );
  2388. lpstr += CopyWord( chOption, lpstr );
  2389. /*
  2390. ** Remove leading space
  2391. */
  2392. lpstr += _tcsspn( lpstr, g_szBlank );
  2393. /*
  2394. ** process any command line options
  2395. */
  2396. while ( (*lpstr == g_chOptionHyphen) || (*lpstr == g_chOptionSlash) ) {
  2397. /*
  2398. ** pass over the option delimeter
  2399. */
  2400. lpstr++;
  2401. /*
  2402. ** Copy option and skip over it.
  2403. */
  2404. lpstr += CopyWord( chOption, lpstr );
  2405. /*
  2406. ** Is this the play option ?? If so, don't bother processing anymore
  2407. ** options.
  2408. */
  2409. if ( 0 == _tcscmp( chOption, g_szUpdate ) ) {
  2410. return TRUE;
  2411. }
  2412. /*
  2413. ** Remove leading space
  2414. */
  2415. lpstr += _tcsspn( lpstr, g_szBlank );
  2416. }
  2417. return FALSE;
  2418. }
  2419. /*****************************Private*Routine******************************\
  2420. * ParseCommandLine
  2421. *
  2422. * Here we look to see if we have been asked to play a particular track.
  2423. * The format of the command line is:
  2424. *
  2425. *
  2426. * CDPlayer command options.
  2427. *
  2428. * CDPLAYER {Options}
  2429. *
  2430. * Options : -play | {Sub-Options}
  2431. * Sub-Options : {-track tracklist} | trackname
  2432. * trackname : filename | drive letter
  2433. * tracklist : filename+
  2434. *
  2435. * -track A track list if a list of tracks that the user wants
  2436. * to play. It overides any play list that may already be stored
  2437. * for the current cd.
  2438. *
  2439. * -play Start playing the current play list. If -play is not specified
  2440. * CD Player seeks to the first track in the play list.
  2441. *
  2442. *
  2443. * On Windows NT the format of [file] is:
  2444. * d:\track(nn).cda
  2445. *
  2446. * where d: is the drive letter of the cdrom that you want to play
  2447. * and \track(nn) is the track number that you want to play.
  2448. *
  2449. * Therefore to play track 8 from a cd-rom drive mapped to e:
  2450. *
  2451. * cdplayer /play e:\track08.cda
  2452. *
  2453. * On Chicago the file is actually a riff CDDA file which contains
  2454. * all the info required to locate the disc and track.
  2455. *
  2456. * Returns the index of the CD-Rom drive which should be played first. Can return
  2457. * -1 iff the caller passed FALSE for the fQuiet parameter and the message box was
  2458. * actually displayed. This should only occur when the user trys to start a new
  2459. * copy of cdplayer passing it the track.cda file of a disk that is not inserted
  2460. * in any of the current CD-Drives attached to the machine.
  2461. *
  2462. * History:
  2463. * dd-mm-94 - StephenE - Created
  2464. *
  2465. \**************************************************************************/
  2466. int
  2467. ParseCommandLine(
  2468. LPTSTR lpstr,
  2469. int *piTrackToSeekTo,
  2470. BOOL fQuiet
  2471. )
  2472. {
  2473. TCHAR chOption[MAX_PATH];
  2474. BOOL fTrack = FALSE;
  2475. int iCdRomIndex = -1; // Assume Failure until proven otherwise
  2476. int ii;
  2477. for (ii = 0; ii < g_NumCdDevices; ii++) {
  2478. g_Devices[ii]->fKilledPlayList = FALSE;
  2479. }
  2480. /*
  2481. ** Start by converting everything to upper case.
  2482. */
  2483. CharUpperBuff( lpstr, _tcslen(lpstr) );
  2484. #if DBG
  2485. #ifdef UNICODE
  2486. dprintf( TEXT("CD Player Command line : %ls"), lpstr );
  2487. #else
  2488. dprintf( "CD Player Command line : %s", lpstr );
  2489. #endif
  2490. #endif
  2491. /*
  2492. ** The first parameter on the command line is always the
  2493. ** string used invoke the program. ie cdplayer.exe
  2494. */
  2495. lpstr += _tcsspn( lpstr, g_szBlank );
  2496. lpstr += CopyWord( chOption, lpstr );
  2497. /*
  2498. ** Remove leading space
  2499. */
  2500. lpstr += _tcsspn( lpstr, g_szBlank );
  2501. /*
  2502. ** process any command line options
  2503. */
  2504. while ( (*lpstr == g_chOptionHyphen) || (*lpstr == g_chOptionSlash) ) {
  2505. /*
  2506. ** pass over the option delimeter
  2507. */
  2508. lpstr++;
  2509. /*
  2510. ** Copy option and skip over it.
  2511. */
  2512. lpstr += CopyWord( chOption, lpstr );
  2513. /*
  2514. ** Is this a command line option we understand - ignore ones
  2515. ** we don't understand.
  2516. */
  2517. if ( 0 == _tcscmp( chOption, g_szTrack ) )
  2518. {
  2519. if ( !fTrack )
  2520. {
  2521. lpstr = ParseTrackList( lpstr, &iCdRomIndex );
  2522. fTrack = TRUE;
  2523. }
  2524. }
  2525. else if ( 0 == _tcscmp( chOption, g_szPlay ) )
  2526. {
  2527. g_fPlay = TRUE;
  2528. }
  2529. else if ( 0 == _tcscmp( chOption, g_szTray) )
  2530. {
  2531. g_fStartedInTray = TRUE;
  2532. }
  2533. else
  2534. {
  2535. #if DBG
  2536. #ifdef UNICODE
  2537. dprintf(TEXT("Ignoring unknown option %ls\n"), chOption );
  2538. #else
  2539. dprintf("Ignoring unknown option %s\n", chOption );
  2540. #endif
  2541. #endif
  2542. }
  2543. /*
  2544. ** Remove leading space
  2545. */
  2546. lpstr += _tcsspn( lpstr, g_szBlank );
  2547. }
  2548. /*
  2549. ** parse remaining command line parameters
  2550. */
  2551. if ( (*lpstr != g_chNULL) && !fTrack) {
  2552. /*
  2553. ** Copy track name and skip over it. Sometimes the shell
  2554. ** gives us quoted strings and sometimes it doesn't. If the
  2555. ** string is not quoted assume that remainder of the command line
  2556. ** is the track name.
  2557. */
  2558. if ( (*lpstr == TEXT('\'')) || (*lpstr == TEXT('\"')) ) {
  2559. lpstr += CopyWord( chOption, lpstr );
  2560. }
  2561. else {
  2562. _tcscpy(chOption, lpstr);
  2563. }
  2564. if ( IsTrackFileNameValid( chOption, &iCdRomIndex,
  2565. piTrackToSeekTo, FALSE, fQuiet ) )
  2566. {
  2567. //if the user passed in a track, turn off start-up random mode
  2568. if (!g_fSelectedOrder)
  2569. {
  2570. g_fSelectedOrder = TRUE;
  2571. SendMessage(GetParent(g_hwndApp),WM_COMMAND,MAKEWPARAM(IDM_MODE_NORMAL,0),(LPARAM)0);
  2572. }
  2573. ResetPlayList( iCdRomIndex );
  2574. }
  2575. #if DBG
  2576. #ifdef UNICODE
  2577. dprintf(TEXT("Seeking to track %ls\n"), chOption );
  2578. #else
  2579. dprintf("Seeking to track %s\n", chOption );
  2580. #endif
  2581. #endif
  2582. }
  2583. return iCdRomIndex;
  2584. }
  2585. /*****************************Private*Routine******************************\
  2586. * ParseTrackList
  2587. *
  2588. * Each track is separated by a ' ' character. The track list is terminated
  2589. * by the NULL, '/' or '-' character.
  2590. *
  2591. * History:
  2592. * dd-mm-94 - StephenE - Created
  2593. *
  2594. \**************************************************************************/
  2595. TCHAR *
  2596. ParseTrackList(
  2597. TCHAR *szTrackList,
  2598. int *piCdRomIndex
  2599. )
  2600. {
  2601. TCHAR chTrack[MAX_PATH];
  2602. int iTrackIndex;
  2603. int iCdRom = -1; // Assume failure, until proven otherwise
  2604. BOOL fPlayListErased = FALSE;
  2605. /*
  2606. ** Remove any stray white space
  2607. */
  2608. szTrackList += _tcsspn( szTrackList, g_szBlank );
  2609. /*
  2610. ** While there are still valid characters to process
  2611. */
  2612. while ( (*szTrackList != g_chNULL)
  2613. && (*szTrackList != g_chOptionHyphen)
  2614. && (*szTrackList != g_chOptionSlash) ) {
  2615. /*
  2616. ** Copy the track name and skip over it.
  2617. */
  2618. szTrackList += CopyWord( chTrack, szTrackList );
  2619. /*
  2620. ** Now check that we have been given a valid filename
  2621. */
  2622. if ( IsTrackFileNameValid( chTrack, &iCdRom, &iTrackIndex, TRUE, FALSE ) ) {
  2623. PTRACK_PLAY pt;
  2624. /*
  2625. ** If this is the first valid file given nuke the
  2626. ** existing play lists and prepare for a new list. Note that
  2627. ** things are complicated by the fact that we could be given
  2628. ** files from more than one CD-Rom drive.
  2629. */
  2630. if (! g_Devices[iCdRom]->fKilledPlayList)
  2631. {
  2632. /*
  2633. ** Kill the old play and save lists.
  2634. */
  2635. ErasePlayList( iCdRom );
  2636. EraseSaveList( iCdRom );
  2637. PLAYLIST( iCdRom ) = NULL;
  2638. SAVELIST( iCdRom ) = NULL;
  2639. fPlayListErased = TRUE;
  2640. g_Devices[iCdRom]->fKilledPlayList = TRUE;
  2641. *piCdRomIndex = iCdRom;
  2642. }
  2643. pt = (TRACK_PLAY*)AllocMemory( sizeof(TRACK_PLAY) );
  2644. pt->TocIndex = iTrackIndex;
  2645. pt->min = 0;
  2646. pt->sec = 0;
  2647. /*
  2648. ** Is this the first track on this devices play list ?
  2649. */
  2650. if ( PLAYLIST(iCdRom) == NULL ) {
  2651. PLAYLIST(iCdRom) = pt;
  2652. pt->nextplay = pt->prevplay = NULL;
  2653. }
  2654. else {
  2655. /*
  2656. ** append this track to the end of the current play list
  2657. */
  2658. AppendTrackToPlayList( PLAYLIST(iCdRom), pt );
  2659. }
  2660. }
  2661. else {
  2662. /*
  2663. ** Put up a message box warning the user that the given
  2664. ** track name is invalid and that we can't play it.
  2665. */
  2666. ;
  2667. }
  2668. /*
  2669. ** Remove any stray white space
  2670. */
  2671. szTrackList += _tcsspn( szTrackList, g_szBlank );
  2672. }
  2673. /*
  2674. ** If we have erased the play list we have to go off and reset the
  2675. ** saved play list.
  2676. */
  2677. if ( fPlayListErased ) {
  2678. SAVELIST( iCdRom ) = CopyPlayList( PLAYLIST(iCdRom) );
  2679. }
  2680. return szTrackList;
  2681. }
  2682. /*****************************Private*Routine******************************\
  2683. * CopyWord
  2684. *
  2685. * Copies one from szSource to szWord - assumes that words are delimited
  2686. * by ' ' characters. szSource MUST point to the begining of the word.
  2687. *
  2688. * Returns length of word copied.
  2689. *
  2690. * History:
  2691. * dd-mm-94 - StephenE - Created
  2692. *
  2693. \**************************************************************************/
  2694. int
  2695. CopyWord(
  2696. TCHAR *szWord,
  2697. TCHAR *szSource
  2698. )
  2699. {
  2700. int n, nReturn;
  2701. /*
  2702. ** Copy the track name
  2703. */
  2704. if ( (*szSource == TEXT('\'')) || (*szSource == TEXT('\"')) ) {
  2705. TCHAR ch = *szSource;
  2706. /*
  2707. ** Remember which quote character it was
  2708. ** According to the DOCS " is invalid in a filename...
  2709. */
  2710. n = 0;
  2711. /*
  2712. ** Move over the initial quote, then copy the filename
  2713. */
  2714. while ( *++szSource && *szSource != ch ) {
  2715. szWord[n++] = *szSource;
  2716. }
  2717. nReturn = n + (*szSource == ch ? 2 : 1);
  2718. }
  2719. else {
  2720. n = _tcscspn( szSource, g_szBlank );
  2721. _tcsncpy( szWord, szSource, n );
  2722. nReturn = n;
  2723. }
  2724. szWord[n] = g_chNULL;
  2725. return nReturn;
  2726. }
  2727. /*****************************Private*Routine******************************\
  2728. * IsTrackFileNameValid
  2729. *
  2730. * This function returns true if the specified filename is a valid CD track.
  2731. * On NT track filenames must be of the form:
  2732. * d:\track(n).cda where d: is the CD-Rom device and \track(n).cda
  2733. * is the index of the track to be played (starting from 1).
  2734. *
  2735. * On Chicago the track filename is actually a riff CDDA file which contains
  2736. * the track info that we require.
  2737. *
  2738. * If the filename is valid the function true and sets piCdromIndex and
  2739. * piTrackIndex to the correct values.
  2740. *
  2741. * History:
  2742. * 29-09-94 - StephenE - Created
  2743. *
  2744. \**************************************************************************/
  2745. BOOL
  2746. IsTrackFileNameValid(
  2747. LPTSTR lpstFileName,
  2748. int *piCdRomIndex,
  2749. int *piTrackIndex,
  2750. BOOL fScanningTracks,
  2751. BOOL fQuiet
  2752. )
  2753. {
  2754. #define RIFF_RIFF 0x46464952
  2755. #define RIFF_CDDA 0x41444443
  2756. RIFFCDA cda;
  2757. HANDLE hFile;
  2758. TCHAR chDriveLetter;
  2759. int i;
  2760. TCHAR szFileName[MAX_PATH];
  2761. TCHAR szPath[MAX_PATH];
  2762. SHFILEINFO shInfo;
  2763. DWORD cbRead;
  2764. //
  2765. // If we are not constructing a track play list it is valid to just specify
  2766. // a drive letter, in which case we select that drive and start playing
  2767. // at the first track on it. All the tracks are played in sequential
  2768. // order.
  2769. //
  2770. if ( !fScanningTracks) {
  2771. //
  2772. // Map the drive letter onto the internal CD-Rom index used by CDPlayer.
  2773. //
  2774. chDriveLetter = *lpstFileName;
  2775. for ( i = 0; i < g_NumCdDevices; i++ ) {
  2776. if (g_Devices[i]->drive == chDriveLetter) {
  2777. *piCdRomIndex = i;
  2778. break;
  2779. }
  2780. }
  2781. //
  2782. // If we mapped the drive OK check to see if we should play all
  2783. // the tracks or just the current play list for that drive. If we
  2784. // didn't map the drive OK assume that its the first part of a
  2785. // RIFF filename and fall through to the code below that opens the
  2786. // RIFF file and parses its contents.
  2787. //
  2788. if ( i != g_NumCdDevices ) {
  2789. //
  2790. // If next character is only a colon ':' then play the
  2791. // the entire disk starting from the first track.
  2792. //
  2793. if ( 0 == _tcscmp(lpstFileName + 1, g_szColon) ) {
  2794. *piTrackIndex = 0;
  2795. return TRUE;
  2796. }
  2797. //
  2798. // If the next two characters are colon backslash ":\" then
  2799. // we seek to the specified drive and play only those tracks that
  2800. // are in the default playlist for the current disk in that drive.
  2801. // All we need to do to achive this is return FALSE.
  2802. //
  2803. if ( 0 == _tcscmp(lpstFileName + 1, g_szColonBackSlash) ) {
  2804. return FALSE;
  2805. }
  2806. }
  2807. }
  2808. //
  2809. // Otherwise, open the file and read the CDA info. The file name may be a
  2810. // link to .cda in which case we need to get the shell to resolve the link for
  2811. // us. We take a copy of the file name because the ResolveLink function
  2812. // modifies the file name string in place.
  2813. //
  2814. _tcscpy(szFileName, lpstFileName);
  2815. if (0L == SHGetFileInfo( szFileName, 0L, &shInfo,
  2816. sizeof(shInfo), SHGFI_ATTRIBUTES)) {
  2817. return FALSE;
  2818. }
  2819. if ((shInfo.dwAttributes & SFGAO_LINK) == SFGAO_LINK) {
  2820. if (!g_fOleInitialized)
  2821. {
  2822. g_fOleInitialized = SUCCEEDED(OleInitialize(NULL));
  2823. }
  2824. if (!ResolveLink(szFileName)) {
  2825. return FALSE;
  2826. }
  2827. }
  2828. // Make sure file exists
  2829. if (GetFileAttributes (szFileName) == ((DWORD)-1)) {
  2830. // Get Full path to file
  2831. if (0 == SearchPath (NULL, szFileName, NULL,
  2832. MAX_PATH, szPath, NULL)) {
  2833. return FALSE;
  2834. }
  2835. } else {
  2836. _tcscpy (szPath, szFileName);
  2837. }
  2838. // Open file and read in CDA info
  2839. hFile = CreateFile (szFileName, GENERIC_READ,
  2840. FILE_SHARE_READ, NULL,
  2841. OPEN_EXISTING, 0, NULL);
  2842. if (INVALID_HANDLE_VALUE == hFile) {
  2843. return FALSE;
  2844. }
  2845. ReadFile(hFile, &cda, sizeof(cda), &cbRead, NULL);
  2846. CloseHandle (hFile);
  2847. //
  2848. // Make sure its a RIFF CDDA file
  2849. //
  2850. if ( (cda.dwRIFF != RIFF_RIFF) || (cda.dwCDDA != RIFF_CDDA) ) {
  2851. return FALSE;
  2852. }
  2853. //
  2854. // Make sure that we have this disc loaded.
  2855. //
  2856. for ( i = 0; i < g_NumCdDevices; i++ ) {
  2857. if (g_Devices[i]->CdInfo.Id == cda.DiscID) {
  2858. *piCdRomIndex = i;
  2859. break;
  2860. }
  2861. }
  2862. //
  2863. // If we didn't map the drive OK return FALSE AND set the
  2864. // returned CD-ROM index to -1 but only if the caller asked us
  2865. // to complain about an incorrect CD being inserted in the drive.
  2866. //
  2867. if ( i == g_NumCdDevices ) {
  2868. if (!fQuiet) {
  2869. AskUserToInsertCorrectDisc(cda.DiscID);
  2870. *piCdRomIndex = -1;
  2871. }
  2872. return FALSE;
  2873. }
  2874. *piTrackIndex = cda.wTrack - 1;
  2875. return TRUE;
  2876. }
  2877. /*****************************Private*Routine******************************\
  2878. * AppendTrackToPlayList
  2879. *
  2880. * Appends the TRACK_PLAY record pointed to by pAppend to the end of the
  2881. * double linked list pointed to by pHead.
  2882. *
  2883. *
  2884. * History:
  2885. * dd-mm-94 - StephenE - Created
  2886. *
  2887. \**************************************************************************/
  2888. void
  2889. AppendTrackToPlayList(
  2890. PTRACK_PLAY pHead,
  2891. PTRACK_PLAY pAppend
  2892. )
  2893. {
  2894. PTRACK_PLAY pp = pHead;
  2895. while (pp->nextplay != NULL) {
  2896. pp = pp->nextplay;
  2897. }
  2898. pp->nextplay = pAppend;
  2899. pAppend->prevplay = pp;
  2900. pAppend->nextplay = NULL;
  2901. }
  2902. /*****************************Private*Routine******************************\
  2903. * FindMostSuitableDrive
  2904. *
  2905. * Tries to determine the best drive to make the current drive. Returns the
  2906. * drive.
  2907. *
  2908. * We should choose the first disc that is playing if any are playing.
  2909. *
  2910. * New dstewart: Else choose the drive that is selected in the CDUNIT table
  2911. *
  2912. * Else we should choose the first disc with a music disk in it if there
  2913. * any drives with music discs in them.
  2914. *
  2915. * Else we should chose the first drive that is available if any of the
  2916. * drives are available.
  2917. *
  2918. * Else just choose the first (ie. zeroth) drive.
  2919. *
  2920. * History:
  2921. * dd-mm-94 - StephenE - Created
  2922. *
  2923. \**************************************************************************/
  2924. int
  2925. FindMostSuitableDrive(
  2926. void
  2927. )
  2928. {
  2929. int iDisc;
  2930. /*
  2931. ** Check for a playing drive
  2932. */
  2933. for ( iDisc = 0; iDisc < g_NumCdDevices; iDisc++ ) {
  2934. if ( g_Devices[iDisc]->State & (CD_PLAYING | CD_PAUSED) ) {
  2935. return iDisc;
  2936. }
  2937. }
  2938. //check the current default drive
  2939. LPCDOPT pOpt = (LPCDOPT)g_pSink->GetOptions();
  2940. LPCDOPTIONS pCDOpts = NULL;
  2941. LPCDUNIT pUnit = NULL;
  2942. int iDefDrive = 0;
  2943. if (pOpt)
  2944. {
  2945. pCDOpts = pOpt->GetCDOpts();
  2946. }
  2947. if (pCDOpts)
  2948. {
  2949. pUnit = pCDOpts->pCDUnitList;
  2950. }
  2951. //scan the list to find the one we want
  2952. for (int index = 0; index < g_NumCdDevices; index++)
  2953. {
  2954. if (pUnit)
  2955. {
  2956. if (pUnit->fDefaultDrive)
  2957. {
  2958. iDefDrive = index;
  2959. //if this is the default AND it has a disc loaded, go for it
  2960. if ( g_Devices[index]->State & CD_LOADED )
  2961. {
  2962. return index;
  2963. }
  2964. }
  2965. pUnit = pUnit->pNext;
  2966. }
  2967. }
  2968. /*
  2969. ** Check for a drive with a music disk in it
  2970. */
  2971. for ( iDisc = 0; iDisc < g_NumCdDevices; iDisc++ )
  2972. {
  2973. if ( g_Devices[iDisc]->State & CD_LOADED )
  2974. {
  2975. return iDisc;
  2976. }
  2977. }
  2978. /*
  2979. ** If the default drive is not in use, use it
  2980. */
  2981. if ( (g_Devices[iDefDrive]->State & (CD_BEING_SCANNED | CD_IN_USE)) == 0 )
  2982. {
  2983. return iDefDrive;
  2984. }
  2985. /*
  2986. ** Check for any drive that is not in use
  2987. */
  2988. for ( iDisc = 0; iDisc < g_NumCdDevices; iDisc++ )
  2989. {
  2990. if ( (g_Devices[iDisc]->State & (CD_BEING_SCANNED | CD_IN_USE)) == 0 )
  2991. {
  2992. return iDisc;
  2993. }
  2994. }
  2995. /*
  2996. ** Ok, no disc are loaded, but all disc are in use, just use the default
  2997. */
  2998. return iDefDrive;
  2999. }
  3000. /*****************************Private*Routine******************************\
  3001. * AskUserToInsertCorrectDisc
  3002. *
  3003. *
  3004. *
  3005. * History:
  3006. * dd-mm-94 - StephenE - Created
  3007. *
  3008. \**************************************************************************/
  3009. void
  3010. AskUserToInsertCorrectDisc(
  3011. DWORD dwID
  3012. )
  3013. {
  3014. TCHAR szMsgBoxTitle[32];
  3015. TCHAR szDiskTitle[TITLE_LENGTH];
  3016. TCHAR szArtistName[ARTIST_LENGTH];
  3017. TCHAR szFormat[STR_MAX_STRING_LEN];
  3018. TCHAR szText[STR_MAX_STRING_LEN + TITLE_LENGTH];
  3019. LPCDDATA pData = (LPCDDATA)g_pSink->GetData();
  3020. _tcscpy(szDiskTitle,g_szNothingThere);
  3021. if(pData)
  3022. {
  3023. //
  3024. // Try to read in title from the options database
  3025. //
  3026. if (pData->QueryTitle(dwID))
  3027. {
  3028. //
  3029. // We found an entry for this disc, so copy all the information
  3030. // from the title database
  3031. LPCDTITLE pCDTitle = NULL;
  3032. if (pData->LockTitle(&pCDTitle,dwID))
  3033. {
  3034. _tcscpy(szDiskTitle,pCDTitle->szTitle);
  3035. _tcscpy(szArtistName,pCDTitle->szArtist);
  3036. pData->UnlockTitle(pCDTitle,FALSE);
  3037. } //end if title locked
  3038. } //end if title found
  3039. }
  3040. /*
  3041. ** If the disk title was found in the database display it.
  3042. */
  3043. if (_tcscmp(szDiskTitle, g_szNothingThere) != 0)
  3044. {
  3045. _tcscpy( szFormat, IdStr(STR_DISK_NOT_THERE_K) );
  3046. wsprintf(szText, szFormat, szDiskTitle, szArtistName);
  3047. }
  3048. else
  3049. {
  3050. _tcscpy( szText, IdStr(STR_DISK_NOT_THERE) );
  3051. }
  3052. //
  3053. // If CD Player is minimized make sure it is restored
  3054. // before displaying the MessageBox
  3055. //
  3056. if (IsIconic(g_hwndApp)) {
  3057. WINDOWPLACEMENT wndpl;
  3058. wndpl.length = sizeof(WINDOWPLACEMENT);
  3059. GetWindowPlacement(g_hwndApp, &wndpl);
  3060. wndpl.showCmd = SW_RESTORE;
  3061. SetWindowPlacement(g_hwndApp, &wndpl);
  3062. }
  3063. _tcscpy( szMsgBoxTitle, IdStr(STR_CDPLAYER) );
  3064. MessageBox( g_hwndApp, szText, szMsgBoxTitle,
  3065. MB_SETFOREGROUND | MB_ICONINFORMATION | MB_APPLMODAL | MB_OK);
  3066. }
  3067. #ifndef USE_IOCTLS
  3068. BOOL CheckMCICDA (TCHAR chDrive)
  3069. {
  3070. DWORD cchLen;
  3071. DWORD dwResult;
  3072. DWORD dwErr;
  3073. CDHANDLE hCD;
  3074. TCHAR szPath[MAX_PATH];
  3075. TCHAR szText[512];
  3076. TCHAR szTitle[MAX_PATH];
  3077. // Make sure the mcicda.dll exists
  3078. cchLen = NUMELEMS(szPath);
  3079. dwResult = SearchPath (NULL, TEXT ("mcicda.dll"), NULL,
  3080. cchLen, szPath, NULL);
  3081. if ((! dwResult) ||
  3082. (0xFFFFFFFF == GetFileAttributes (szPath)))
  3083. {
  3084. // Give Missing MCICDA.DLL error message
  3085. GetSystemDirectory (szPath, cchLen);
  3086. _tcscpy( szTitle, IdStr( STR_MCICDA_MISSING ) );
  3087. wsprintf (szText, szTitle, szPath);
  3088. _tcscpy( szTitle, IdStr( STR_CDPLAYER ) );
  3089. MessageBox( NULL, szText, szTitle,
  3090. MB_APPLMODAL | MB_ICONINFORMATION |
  3091. MB_OK | MB_SETFOREGROUND );
  3092. return FALSE;
  3093. }
  3094. // Make sure mcicda.dll service is up and running
  3095. hCD = OpenCdRom (chDrive, &dwErr);
  3096. if (! hCD)
  3097. {
  3098. // Error loading media device driver.
  3099. _tcscpy( szText, IdStr( STR_MCICDA_NOT_WORKING ) );
  3100. _tcscpy( szTitle, IdStr( STR_CDPLAYER ) );
  3101. MessageBox( NULL, szText, szTitle,
  3102. MB_APPLMODAL | MB_ICONINFORMATION |
  3103. MB_OK | MB_SETFOREGROUND );
  3104. return FALSE;
  3105. }
  3106. // Close Device
  3107. CloseCdRom (hCD);
  3108. return TRUE;
  3109. }
  3110. #endif // ! USE_IOCTLS
  3111. #if DBG
  3112. /******************************Public*Routine******************************\
  3113. * CDAssert
  3114. *
  3115. *
  3116. * History:
  3117. * 18-11-93 - StephenE - Created
  3118. *
  3119. \**************************************************************************/
  3120. void
  3121. CDAssert(
  3122. LPSTR x,
  3123. LPSTR file,
  3124. int line
  3125. )
  3126. {
  3127. TCHAR buff[128];
  3128. wsprintf( buff, TEXT("%s \nat line %d of %s"), x, line, file );
  3129. MessageBox( NULL, buff, TEXT("Assertion Failure:"), MB_APPLMODAL | MB_OK );
  3130. }
  3131. /******************************Public*Routine******************************\
  3132. * dprintf
  3133. *
  3134. *
  3135. *
  3136. * History:
  3137. * dd-mm-94 - StephenE - Created
  3138. *
  3139. \**************************************************************************/
  3140. void
  3141. dprintf(
  3142. TCHAR *lpszFormat,
  3143. ...
  3144. )
  3145. {
  3146. TCHAR buf[512];
  3147. UINT n;
  3148. va_list va;
  3149. static int iPrintOutput = -1;
  3150. if (iPrintOutput == -1) {
  3151. iPrintOutput = GetProfileInt( TEXT("MMDEBUG"), TEXT("CdPlayer"), 0);
  3152. }
  3153. if (iPrintOutput) {
  3154. n = wsprintf(buf, TEXT("CdPlayer: <%d>"), GetCurrentThreadId() );
  3155. va_start(va, lpszFormat);
  3156. n += wvsprintf(buf+n, lpszFormat, va);
  3157. va_end(va);
  3158. buf[n++] = '\n';
  3159. buf[n] = 0;
  3160. OutputDebugString(buf);
  3161. }
  3162. }
  3163. #endif // End #ifdef DBG
  3164. /******************************Public*Routine******************************\
  3165. * ChildEnumProc
  3166. *
  3167. * Gets the position of each child control window. As saves the associated
  3168. * window handle for later use.
  3169. *
  3170. * History:
  3171. * 18-11-93 - StephenE - Created
  3172. *
  3173. \**************************************************************************/
  3174. BOOL CALLBACK
  3175. ChildEnumProc(
  3176. HWND hwndChild,
  3177. LPARAM hwndParent
  3178. )
  3179. {
  3180. int index = 0;
  3181. index = INDEX(GetDlgCtrlID( hwndChild ));
  3182. if ((index > -1) && (index < NUM_OF_CONTROLS))
  3183. {
  3184. g_hwndControls[index] = hwndChild;
  3185. }
  3186. return TRUE;
  3187. }