/******************************Module*Header*******************************\ * Module Name: scan.c * * Code for scanning the available CD Rom devices. * * * Created: 02-11-93 * Author: Stephen Estrop [StephenE] * * Copyright (c) 1993 Microsoft Corporation \**************************************************************************/ #pragma warning( once : 4201 4214 ) #define NOOLE #include /* required for all Windows applications */ #include #include #include /* contains portable ascii/unicode macros */ #include "playres.h" #include "cdplayer.h" #include "cdapi.h" #include "scan.h" #include "trklst.h" #include "database.h" /*****************************Private*Routine******************************\ * ScanForCdromDevices * * Returns the number of CD-ROM devices installed in the system. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ int ScanForCdromDevices( void ) { DWORD dwDrives; TCHAR chDrive[] = TEXT("A:\\"); int iNumDrives; iNumDrives = 0; for (dwDrives = GetLogicalDrives(); dwDrives != 0; dwDrives >>= 1 ) { /* ** Is there a logical drive ?? */ if (dwDrives & 1) { if ( GetDriveType(chDrive) == DRIVE_CDROM ) { g_Devices[iNumDrives] = (CDROM*)AllocMemory( sizeof(CDROM) ); g_Devices[iNumDrives]->drive = chDrive[0]; g_Devices[iNumDrives]->State = CD_BEING_SCANNED; iNumDrives++; } } /* ** Go look at the next drive */ chDrive[0] = chDrive[0] + 1; } return iNumDrives; } /******************************Public*Routine******************************\ * RescanDevice * * * This routine is called to scan the disc in a given cdrom by * reading its table of contents. If the cdrom is playing the user is * notified that the music will stop. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void RescanDevice( HWND hwndNotify, int cdrom ) { TOC_THREAD_PARMS *ptoc; HWND hwndButton; int iMsgBoxRtn; if ( g_Devices[cdrom]->State & CD_PLAYING ) { TCHAR s1[256]; TCHAR s2[256]; _tcscpy( s1, IdStr( STR_CANCEL_PLAY ) ); _tcscpy( s2, IdStr( STR_RESCAN ) ); iMsgBoxRtn = MessageBox( g_hwndApp, s1, s2, MB_APPLMODAL | MB_DEFBUTTON1 | MB_ICONQUESTION | MB_YESNO); if ( iMsgBoxRtn == IDYES ) { hwndButton = g_hwndControls[INDEX(IDM_PLAYBAR_STOP)]; SendMessage( hwndButton, WM_LBUTTONDOWN, 0, 0L ); SendMessage( hwndButton, WM_LBUTTONUP, 0, 0L ); } else { return; } } /* ** Attempt to read table of contents of disc in this drive. We ** now spawn off a separate thread to do this. Note that the child ** thread frees the storage allocated below. */ ptoc = (TOC_THREAD_PARMS*)AllocMemory( sizeof(TOC_THREAD_PARMS) ); ptoc->hwndNotify = hwndNotify; ptoc->cdrom = cdrom; ReadTableOfContents( ptoc ); } /*****************************Private*Routine******************************\ * ReadTableofContents * * This function reads in the table of contents (TOC) for the specified cdrom. * All TOC's are read on a worker thread. The hi-word of thread_info variable * is a boolean that states if the display should been updated after the TOC * has been reads. The lo-word of thread_info is the id of the cdrom device * to be read. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void ReadTableOfContents( TOC_THREAD_PARMS *ptoc ) { DWORD dwThreadId; int cdrom; cdrom = ptoc->cdrom; g_Devices[ cdrom ]->fIsTocValid = FALSE; g_Devices[cdrom]->fShowLeadIn = FALSE; g_Devices[cdrom]->fProcessingLeadIn = FALSE; if (g_Devices[ cdrom ]->hThreadToc != NULL) { /* ** We have a thread TOC handle see if the thread is ** still running. If so just return, otherwise */ switch ( WaitForSingleObject(g_Devices[ cdrom ]->hThreadToc, 0L) ) { /* ** Thread has finished to continue */ case WAIT_OBJECT_0: break; /* ** The thread is still running so just return */ case WAIT_TIMEOUT: default: return; } CloseHandle( g_Devices[ cdrom ]->hThreadToc ); } g_Devices[ cdrom ]->hThreadToc = CreateThread( NULL, 0L, (LPTHREAD_START_ROUTINE)TableOfContentsThread, (LPVOID)ptoc, 0L, &dwThreadId ); /* ** For now I will kill the app if I cannot create the ** ReadTableOfContents thread. This is probably a bit ** harsh. */ if (g_Devices[ cdrom ]->hThreadToc == NULL) { FatalApplicationError( STR_NO_RES, GetLastError() ); } } /*****************************Private*Routine******************************\ * TableOfContentsThread * * This is the worker thread that reads the table of contents for the * specified cdrom. * * Before the thread exits we post a message to the UI threads main window to * notify it that the TOC for this cdrom has been updated. It then examines the * database to determine if this cdrom is known and updates the screen ccordingly. * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void TableOfContentsThread( TOC_THREAD_PARMS *ptoc ) { DWORD status; UCHAR num, numaudio; int cdrom; HWND hwndNotify; // This serializes access to this function // between multiple threads and the CDPlayer_OnTocRead // function on the main thread. // This prevents resource contention on CDROM Multi-changers EnterCriticalSection (&g_csTOCSerialize); cdrom = ptoc->cdrom; hwndNotify = ptoc->hwndNotify; LocalFree( ptoc ); /* ** Try to read the TOC from the drive. */ #ifdef USE_IOCTLS status = GetCdromTOC( g_Devices[cdrom]->hCd, &(g_Devices[cdrom]->toc) ); num = g_Devices[cdrom]->toc.LastTrack - g_Devices[cdrom]->toc.FirstTrack+1; { int i; numaudio = 0; /* ** Look for audio tracks... */ for( i = 0; i < num; i++ ) { if ( (g_Devices[cdrom]->toc.TrackData[i].Control & TRACK_TYPE_MASK ) == AUDIO_TRACK ) { numaudio++; } } } /* ** Need to check if we got data tracks or audio ** tracks back...if there is a mix, strip out ** the data tracks... */ if (status == ERROR_SUCCESS) { /* ** If there aren't any audio tracks, then we (most likely) ** have a data CD loaded. */ if (numaudio == 0) { status == ERROR_UNRECOGNIZED_MEDIA; g_Devices[cdrom]->State = CD_DATA_CD_LOADED | CD_STOPPED; } else { g_Devices[cdrom]->State = CD_LOADED | CD_STOPPED; } } else { g_Devices[cdrom]->State = CD_NO_CD | CD_STOPPED; } #else { MCIDEVICEID wDeviceID; DWORD dwCDPlayerMode = 0L; OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(os); GetVersionEx(&os); if (os.dwPlatformId != VER_PLATFORM_WIN32_NT) { if (g_Devices[cdrom]->hCd == 0) { g_Devices[cdrom]->hCd = OpenCdRom( g_Devices[cdrom]->drive, &status ); } wDeviceID = g_Devices[cdrom]->hCd; } else { wDeviceID = OpenCdRom( g_Devices[cdrom]->drive, &status ); } if ( wDeviceID != 0 ) { int i; numaudio = 0; status = GetCdromTOC( wDeviceID, &(g_Devices[cdrom]->toc) ); /* ** Need to check if we got data tracks or audio ** tracks back...if there is a mix, strip out ** the data tracks... */ if ( status == ERROR_SUCCESS) { num = g_Devices[cdrom]->toc.LastTrack - g_Devices[cdrom]->toc.FirstTrack + 1; for( i = 0; i < num; i++ ) { if ( IsCdromTrackAudio(wDeviceID, i) ) { numaudio++; } } } dwCDPlayerMode = GetCdromMode( wDeviceID ); OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(os); GetVersionEx(&os); if (os.dwPlatformId == VER_PLATFORM_WIN32_NT) { CloseCdRom( wDeviceID ); } } /* ** Need to check if we got data tracks or audio ** tracks back...if there is a mix, strip out ** the data tracks... */ if (status == ERROR_SUCCESS) { /* ** If there aren't any audio tracks, then we (most likely) ** have a data CD loaded. */ if (numaudio == 0) { g_Devices[cdrom]->State = CD_DATA_CD_LOADED | CD_STOPPED; } else { g_Devices[cdrom]->State = CD_LOADED; switch (dwCDPlayerMode) { case MCI_MODE_PAUSE: g_Devices[cdrom]->State |= CD_PAUSED; break; case MCI_MODE_PLAY: g_Devices[cdrom]->State |= CD_PLAYING; break; default: g_Devices[cdrom]->State |= CD_STOPPED; break; } } } else { if (status == (DWORD)MCIERR_MUST_USE_SHAREABLE) { g_Devices[cdrom]->State = CD_IN_USE; } if (g_Devices[cdrom]->State != CD_IN_USE) { g_Devices[cdrom]->State = CD_NO_CD | CD_STOPPED; } } } #endif /* ** Notify the UI thread that a TOC has been read and then terminate the ** thread. */ PostMessage( hwndNotify, WM_NOTIFY_TOC_READ, (WPARAM)cdrom, (LPARAM)numaudio ); LeaveCriticalSection (&g_csTOCSerialize); ExitThread( 1L ); }