Exemplo n.º 1
0
BOOL CP_BuildDirectory(CP_COMPOSITEFILE hComposite)
{
	CPs_CompositeContext* pContext = (CPs_CompositeContext*)hComposite;
	unsigned int iOffset;
	CP_CHECKOBJECT(pContext);
	
	// Initialise structure
	pContext->m_pFirstSubFile = NULL;
	
	// Scan the composite for the file headers (ignore the end directory stuff)
	iOffset = 0;
	
	while (((iOffset + sizeof(CPs_PKFILE_HEADER)) < pContext->m_dwFileSize)
			&& *(DWORD*)(pContext->m_pFileBase + iOffset) != CPC_PKFILE_DIRMAGIC)
	{
		CPs_PKFILE_HEADER* pHeader = (CPs_PKFILE_HEADER*)(pContext->m_pFileBase + iOffset);
		CPs_SubFile* pNewSubFile;
		
		if (pHeader->m_dwSig != CPC_PKFILE_MAGIC
				|| (pHeader->m_wBITs & CPC_PKFILE_BITS_ENCRYPTED)
				|| (pHeader->m_wBITs & CPC_PKFILE_BITS_STREAMED)
				|| (pHeader->m_wMethod != CPC_PKFILE_METHOD_STORED && pHeader->m_wMethod != CPC_PKFILE_METHOD_DEFLATED))
		{
			CP_TRACE0("ZIP format not understood");
			return FALSE;
		}
		
		// Create subfile object
		pNewSubFile = (CPs_SubFile*)malloc(sizeof(*pNewSubFile));
		
		pNewSubFile->m_pNext = pContext->m_pFirstSubFile;
		
		pContext->m_pFirstSubFile = pNewSubFile;
		
		// Init subfile members
		pNewSubFile->m_pcName = (char*)malloc(pHeader->m_wFilenameLen + 1);
		memcpy(pNewSubFile->m_pcName, pContext->m_pFileBase + iOffset + sizeof(*pHeader), pHeader->m_wFilenameLen);
		
		pNewSubFile->m_pcName[pHeader->m_wFilenameLen] = '\0';
		pNewSubFile->m_wMethod = pHeader->m_wMethod;
		pNewSubFile->m_dwCRC32 = pHeader->m_dwCRC32;
		pNewSubFile->m_iCompressedSize = pHeader->m_dwCompressedSize;
		pNewSubFile->m_iUncompressedSize = pHeader->m_dwDecompressedSize;
		pNewSubFile->m_iFileOffset = iOffset + sizeof(*pHeader) + pHeader->m_wFilenameLen + pHeader->m_wExtraFieldLen;
		CP_TRACE1("SubFile:\"%s\"", pNewSubFile->m_pcName);
		
		// Skip to next file
		iOffset += sizeof(*pHeader)
				   + pHeader->m_dwCompressedSize
				   + pHeader->m_wFilenameLen
				   + pHeader->m_wExtraFieldLen;
	}
	
	
	return TRUE;
}
Exemplo n.º 2
0
void CPP_OMFL_Uninitialise(CPs_OutputModule* pModule)
{
	CPs_OutputContext_File* pContext = (CPs_OutputContext_File*)pModule->m_pModuleCookie;
	CP_CHECKOBJECT(pContext);
	CP_TRACE0("Wave out shutting down");
	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
	
	// If there is a File handle
	
	if (pContext->m_hFile)
	{
		// Stop any pending playing
		fclose(pContext->m_hFile);
		pContext->m_hFile = NULL;
		// Clean up
		CloseHandle(pModule->m_evtBlockFree);
	}
	
	free(pContext);
	
	pModule->m_pModuleCookie = NULL;
}
Exemplo n.º 3
0
void CPP_OMFL_Initialise(CPs_OutputModule* pModule, const CPs_FileInfo* pFileInfo, CP_HEQUALISER hEqualiser)
{

	// This is called when some playing is required.
	// Do all allocation here so that we do not hold
	// resources while we are just sitting waiting for
	// something to happen.
	
	// Create a context
	CPs_OutputContext_File* pContext;
	CP_ASSERT(pModule->m_pModuleCookie == NULL);
	pContext = (CPs_OutputContext_File*)malloc(sizeof(CPs_OutputContext_File));
	pModule->m_pModuleCookie = pContext;
	CP_TRACE0("File out initialising");
	
	// Create sync object
	pModule->m_evtBlockFree = CreateEvent(NULL, FALSE, FALSE, NULL);
	
	// Setup thread prioity to lowest
	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
	pModule->m_pEqualiser = hEqualiser;
	pContext->m_bPaused = FALSE;
	pContext->m_hFile = NULL;
}
Exemplo n.º 4
0
DWORD WINAPI CPI_Player__EngineEP(void* pCookie)
{
	BOOL bTerminateThread = FALSE;
	HRESULT hr_ComState;
	CPs_PlayerContext playercontext;
	
	playercontext.m_pBaseEngineParams = (CPs_PlayEngine*)pCookie;
	playercontext.m_bOutputActive = FALSE;
	playercontext.m_iProportion_TrackLength = 0;
	playercontext.m_iLastSentTime_Secs = -1;
	playercontext.m_iLastSentTime_Proportion = -1;
	playercontext.m_iInternalVolume = 100;
	CP_CHECKOBJECT(playercontext.m_pBaseEngineParams);
	
	CP_TRACE0("Cooler Engine Startup");
	hr_ComState = CoInitialize(NULL);
		
	// Initialise CoDecs
	CP_InitialiseCodec_MPEG(&playercontext.m_CoDecs[CP_CODEC_MPEG]);
	CP_InitialiseCodec_WAV(&playercontext.m_CoDecs[CP_CODEC_WAV]);
	CP_InitialiseCodec_OGG(&playercontext.m_CoDecs[CP_CODEC_OGG]);
	CP_InitialiseCodec_WinAmpPlugin(&playercontext.m_CoDecs[CP_CODEC_WINAMPPLUGIN]);
	
	// Initialise output module
	
	if (options.decoder_output_mode > CP_OUTPUT_last)
		options.decoder_output_mode = CP_OUTPUT_last;
		
	playercontext.m_dwCurrentOutputModule = options.decoder_output_mode;
	
	CPI_Player_Output_Initialise_WaveMapper(&playercontext.m_OutputModules[CP_OUTPUT_WAVE]);
	CPI_Player_Output_Initialise_DirectSound(&playercontext.m_OutputModules[CP_OUTPUT_DIRECTSOUND]);
	CPI_Player_Output_Initialise_File(&playercontext.m_OutputModules[CP_OUTPUT_FILE]);
	
	playercontext.m_pCurrentOutputModule = &playercontext.m_OutputModules[playercontext.m_dwCurrentOutputModule];
	
	// Initialise EQ
	CPI_Player_Equaliser_Initialise_Basic(&playercontext.m_Equaliser);
	
	{
		CPs_PlayEngine* player = (CPs_PlayEngine*)pCookie;
		player->m_pContext = &playercontext;
	}
	
	// Initialise USER32.DLL for this thread
	{
		MSG msgDummy;
		PeekMessage(&msgDummy, 0, WM_USER, WM_USER, PM_NOREMOVE);
		
		// Signal this thread ready for input
		SetEvent(playercontext.m_pBaseEngineParams->m_hEvtThreadReady);
	}
	
	do
	{
		// Process any pending messages
		BOOL bForceRefill = FALSE;
		MSG msg;
		DWORD dwWaitResult;
		
		while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			// Decode engine message
			switch (msg.message)
			{
			
				case CPTM_QUIT:
					bTerminateThread = TRUE;
					break;
					
				case CPTM_OPENFILE:
				{
					char* pcFilename = (char*)msg.wParam;
					
					// If there is another pending openfile then ignore this one
					// This helps when this thread is non responsive (on an http connect for example)
					// and the user is hammering the hell out of the play button (as I always
					// do) - this will cause a number of open/closes to be placed into the
					// message queue which will tie up this thread for ages!!
					MSG msg2;
					
					if (PeekMessage(&msg2, NULL, CPTM_OPENFILE, CPTM_OPENFILE, PM_NOREMOVE) == FALSE)
					{
						CPs_CoDecModule* pNewCoDec;
						
						// If there is a CoDec playing then shut it down
						
						if (playercontext.m_pCurrentOutputModule->m_pCoDec)
						{
							playercontext.m_pCurrentOutputModule->m_pCoDec->CloseFile(playercontext.m_pCurrentOutputModule->m_pCoDec);
							playercontext.m_pCurrentOutputModule->m_pCoDec = NULL;
						}
						
						CP_TRACE1("Openfile \"%s\"", pcFilename);
						
						pNewCoDec = OpenCoDec(&playercontext, pcFilename);
						
						// If the open failed then request a new stream from the interface
						
						if (pNewCoDec == NULL)
						{
							PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsEndOfStream, 0);
						}
						
						// Check the file's format - if the sample rate, nChannels or sample size has changed
						// then clear the current output and shutdown output device (this will cause a gap
						// - but only when the format changes)
						
						else if (playercontext.m_bOutputActive == TRUE)
						{
							CPs_FileInfo FileInfo;
							pNewCoDec->GetFileInfo(pNewCoDec, &FileInfo);
							
							if (FileInfo.m_iFreq_Hz != playercontext.m_iOpenDevice_Freq_Hz
									|| FileInfo.m_bStereo != playercontext.m_bOpenDevice_Stereo
									|| FileInfo.m_b16bit != playercontext.m_bOpenDevice_16bit)
							{
								CP_TRACE0("Stream format changes - clearing stream");
								EmptyOutputStream(&playercontext);
								StartPlay(pNewCoDec, &playercontext);
								bForceRefill = TRUE;
							}
						}
						
						playercontext.m_pCurrentOutputModule->m_pCoDec = pNewCoDec;
					}
					
					#ifdef _DEBUG
					else
					{
						CP_TRACE1("Openfile of \"%s\" ignored due to other opens in the queue", pcFilename);
					}
					#endif
					
					// Cleanup
					free(pcFilename);
				}
				
				break;
				
				case CPTM_SEEK:
				
					if (playercontext.m_bOutputActive == TRUE)
					{
						// Ignore message if there is another on it's way!
						MSG msg2;
						
						if (PeekMessage(&msg2, NULL, CPTM_SEEK, CPTM_SEEK, PM_NOREMOVE) == FALSE)
						{
							if (playercontext.m_pCurrentOutputModule->m_pCoDec)
								playercontext.m_pCurrentOutputModule->m_pCoDec->Seek(playercontext.m_pCurrentOutputModule->m_pCoDec, (int)msg.wParam, (int)msg.lParam);
								
							playercontext.m_pCurrentOutputModule->Flush(playercontext.m_pCurrentOutputModule);
							
							bForceRefill = TRUE;
						}
					}
					// FALLTHROUGH - to let coolplayer know playing has resumed (bugfix from seeking when paused)  */
					
				case CPTM_PLAY:
					if (playercontext.m_pCurrentOutputModule->m_pCoDec)
					{
						// If we don't have an output stage - initialise one now
						if (playercontext.m_bOutputActive == FALSE)
						{
							StartPlay(playercontext.m_pCurrentOutputModule->m_pCoDec, &playercontext);
							bForceRefill = TRUE;
						}
						
						playercontext.m_pCurrentOutputModule->SetPause(playercontext.m_pCurrentOutputModule, FALSE);
						
						PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsPlaying, 0);
						playercontext.m_iLastSentTime_Secs = -1;
						playercontext.m_iLastSentTime_Proportion = -1;
						UpdateProgress(&playercontext);
					}
					
					break;
					
				case CPTM_STOP:
				
					if (playercontext.m_pCurrentOutputModule->m_pCoDec)
					{
						playercontext.m_pCurrentOutputModule->m_pCoDec->CloseFile(playercontext.m_pCurrentOutputModule->m_pCoDec);
						playercontext.m_pCurrentOutputModule->m_pCoDec = NULL;
					}
					
					if (playercontext.m_bOutputActive == TRUE)
					{
						playercontext.m_bOutputActive = FALSE;
						playercontext.m_pCurrentOutputModule->Uninitialise(playercontext.m_pCurrentOutputModule);
					}
					
					PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsStopped, 0);
					
					break;
					
				case CPTM_PAUSE:
					CP_TRACE0("Pause");
					
					if (playercontext.m_bOutputActive == TRUE)
						playercontext.m_pCurrentOutputModule->SetPause(playercontext.m_pCurrentOutputModule, TRUE);
						
					PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsPaused, 0);
					
					break;
					
				case CPTM_SETPROGRESSTRACKLENGTH:
					playercontext.m_iProportion_TrackLength = (int)msg.wParam;
					
					break;
					
				case CPTM_SENDSYNCCOOKIE:
					PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_SYNCCOOKIE, msg.wParam, 0);
					
					break;
					
				case CPTM_BLOCKMSGUNTILENDOFSTREAM:
					EmptyOutputStream(&playercontext);
					
					break;
					
				case CPTM_ENUMOUTPUTDEVICES:
					EnumOutputDevices(&playercontext);
					
					break;
					
				case CPTM_SETEQSETTINGS:
				{
					MSG msg2;
					CPs_EQSettings* pEQ = (CPs_EQSettings*)msg.wParam;
					
					// If there is another pending EQ message do no processing for this one (try to reduce noise)
					
					if (PeekMessage(&msg2, NULL, CPTM_SETEQSETTINGS, CPTM_OPENFILE, PM_NOREMOVE) == FALSE)
					{
						BOOL bEQEnableStateChanged;
						playercontext.m_Equaliser.ApplySettings(&playercontext.m_Equaliser, pEQ, &bEQEnableStateChanged);
						
						// Empty the buffers (this will cause a discontinuity in the music but at least
						// the EQ setting change will be immediate
						
						if (playercontext.m_bOutputActive == TRUE && playercontext.m_pCurrentOutputModule->OnEQChanged)
							playercontext.m_pCurrentOutputModule->OnEQChanged(playercontext.m_pCurrentOutputModule);
					}
					
					free(pEQ);
				}
				
				break;
				
				case CPTM_ONOUTPUTMODULECHANGE:
				{
					playercontext.m_dwCurrentOutputModule = options.decoder_output_mode;
					SetCurrentOutputModule(&playercontext, NULL, &bForceRefill);
				}
				
				break;
				
				case CPTM_ASSOCIATEFILEEXTENSIONS:
					AssociateFileExtensions(&playercontext);
					break;
					
				case CPTM_SETINTERNALVOLUME:
					playercontext.m_iInternalVolume = (int)msg.wParam;
					
					if (playercontext.m_bOutputActive == TRUE && playercontext.m_pCurrentOutputModule->SetInternalVolume)
						playercontext.m_pCurrentOutputModule->SetInternalVolume(playercontext.m_pCurrentOutputModule, playercontext.m_iInternalVolume);
						
					break;
			}
		}
		
		if (bTerminateThread)
			break;
			
		// Wait for either another message or a buffer expiry (if we have a player)
		if (playercontext.m_bOutputActive)
		{
			dwWaitResult = 0L;
			
			if (bForceRefill == FALSE)
			{
				if (playercontext.m_pCurrentOutputModule->m_evtBlockFree)
					dwWaitResult = MsgWaitForMultipleObjects(1, &playercontext.m_pCurrentOutputModule->m_evtBlockFree, FALSE, 1000, QS_POSTMESSAGE);
				else
					dwWaitResult = WAIT_OBJECT_0;
			}
			
			// If the buffer event is signaled then request a refill
			
			if (bForceRefill == TRUE || dwWaitResult == WAIT_OBJECT_0)
			{
				if (playercontext.m_pCurrentOutputModule->m_pCoDec)
				{
					playercontext.m_pCurrentOutputModule->RefillBuffers(playercontext.m_pCurrentOutputModule);
					
					if (playercontext.m_pCurrentOutputModule->m_pCoDec == NULL)
					{
						// Tell UI that we need another file to play
						PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsEndOfStream, 0);
					}
					
					else
						UpdateProgress(&playercontext);
				}
				
				// If output has finished everything that it was doing - close the engine
				
				else if (playercontext.m_pCurrentOutputModule->IsOutputComplete(playercontext.m_pCurrentOutputModule) == TRUE)
				{
					playercontext.m_bOutputActive = FALSE;
					playercontext.m_pCurrentOutputModule->Uninitialise(playercontext.m_pCurrentOutputModule);
					PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsStopped, 0);
				}
			}
		}
		
		else
		{
			WaitMessage();
		}
	}
	
	while (bTerminateThread == FALSE);
	
	// Clean up output (if it's still active)
	if (playercontext.m_pCurrentOutputModule->m_pCoDec)
	{
		playercontext.m_pCurrentOutputModule->m_pCoDec->CloseFile(playercontext.m_pCurrentOutputModule->m_pCoDec);
		playercontext.m_pCurrentOutputModule->m_pCoDec = NULL;
	}
	
	if (playercontext.m_bOutputActive == TRUE)
		playercontext.m_pCurrentOutputModule->Uninitialise(playercontext.m_pCurrentOutputModule);
		
	// Clean up modules
	playercontext.m_Equaliser.Uninitialise(&playercontext.m_Equaliser);
	
	CleanupCoDecs(&playercontext);
	
	if (hr_ComState == S_OK)
		CoUninitialize();
		
	CP_TRACE0("Cooler Engine terminating");
	
	return 0;
}
Exemplo n.º 5
0
BOOL CircleBufferRead(CPs_CircleBuffer* pCBuffer, void* pDestBuffer, const unsigned int _iBytesToRead, unsigned int* pbBytesRead)
{
	unsigned int iBytesToRead = _iBytesToRead;
	unsigned int iBytesRead = 0;
	DWORD dwWaitResult;
	BOOL bComplete = FALSE;
	CP_CHECKOBJECT(pCBuffer);
	
	while (iBytesToRead > 0 && bComplete == FALSE)
	{
		dwWaitResult = WaitForSingleObject(pCBuffer->m_evtDataAvailable, CIC_WAITTIMEOUT);
		
		if (dwWaitResult == WAIT_TIMEOUT)
		{
			CP_TRACE0("Circle buffer - did not fill in time!");
			*pbBytesRead = iBytesRead;
			return FALSE;
		}
		
		EnterCriticalSection(&pCBuffer->m_csCircleBuffer);
		
		// Take what we can from the CBuffer
		
		if (pCBuffer->m_iReadCursor > pCBuffer->m_iWriteCursor)
		{
			unsigned int iChunkSize = pCBuffer->m_iBufferSize - pCBuffer->m_iReadCursor;
			
			if (iChunkSize > iBytesToRead)
				iChunkSize = iBytesToRead;
				
			// Perform the read
			memcpy((BYTE*)pDestBuffer + iBytesRead,
				   pCBuffer->m_pBuffer + pCBuffer->m_iReadCursor,
				   iChunkSize);
			       
			iBytesRead += iChunkSize;
			iBytesToRead -= iChunkSize;
			
			pCBuffer->m_iReadCursor += iChunkSize;
			
			if (pCBuffer->m_iReadCursor >= pCBuffer->m_iBufferSize)
				pCBuffer->m_iReadCursor -= pCBuffer->m_iBufferSize;
		}
		
		if (iBytesToRead && pCBuffer->m_iReadCursor < pCBuffer->m_iWriteCursor)
		{
			unsigned int iChunkSize = pCBuffer->m_iWriteCursor - pCBuffer->m_iReadCursor;
			
			if (iChunkSize > iBytesToRead)
				iChunkSize = iBytesToRead;
				
			// Perform the read
			memcpy((BYTE*)pDestBuffer + iBytesRead,
				   pCBuffer->m_pBuffer + pCBuffer->m_iReadCursor,
				   iChunkSize);
			       
			iBytesRead += iChunkSize;
			iBytesToRead -= iChunkSize;
			pCBuffer->m_iReadCursor += iChunkSize;
		}
		
		// Is there any more data to read
		
		if (pCBuffer->m_iReadCursor == pCBuffer->m_iWriteCursor)
		{
			if (pCBuffer->m_bComplete)
				bComplete = TRUE;
		}
		
		else
			SetEvent(pCBuffer->m_evtDataAvailable);
			
		LeaveCriticalSection(&pCBuffer->m_csCircleBuffer);
	}
	
	*pbBytesRead = iBytesRead;
	
	return bComplete ? FALSE : TRUE;
}
Exemplo n.º 6
0
unsigned int _stdcall EP_FillerThread(void* _pContext)
{
	CPs_BufferFillerContext* pContext = (CPs_BufferFillerContext*)_pContext;
	HINTERNET hInternet;
	HINTERNET hURLStream;
	DWORD dwTimeout;
	BOOL bStreamComplete = FALSE;
	INTERNET_BUFFERS internetbuffer;
	BYTE bReadBuffer[CIC_READCHUNKSIZE];
	
	CP_CHECKOBJECT(pContext);
	
	PostMessage(pContext->m_hWndNotify, CPNM_SETSTREAMINGSTATE, (WPARAM)TRUE, (LPARAM)0);
	
	// Check that we can open this file
	hInternet = InternetOpen("CoolPlayer",
							 INTERNET_OPEN_TYPE_PRECONFIG,
							 NULL, NULL, 0L);
	                         
	if (hInternet == NULL)
	{
		pContext->m_pCircleBuffer->SetComplete(pContext->m_pCircleBuffer);
		CP_TRACE0("EP_FillerThread::NoInternetOpen");
		return 0;
	}
	
	dwTimeout = 2000;
	InternetSetOption(hInternet, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout));
	
	hURLStream = InternetOpenUrl(hInternet,
								 pContext->m_pcFlexiURL,
								 NULL,
								 0,
								 INTERNET_FLAG_NO_CACHE_WRITE
								 | INTERNET_FLAG_PRAGMA_NOCACHE,
								 0);
	                             
	if (hURLStream == NULL)
	{
		InternetCloseHandle(hInternet);
		pContext->m_pCircleBuffer->SetComplete(pContext->m_pCircleBuffer);
		CP_TRACE1("EP_FillerThread::NoOpenURL %s", pContext->m_pcFlexiURL);
		return 0;
	}
	
	// Setup the internet buffer
	internetbuffer.dwStructSize = sizeof(internetbuffer);
	internetbuffer.Next = NULL;
	internetbuffer.lpcszHeader = NULL;
	internetbuffer.lpvBuffer = bReadBuffer;
	internetbuffer.dwBufferLength = CIC_READCHUNKSIZE;
	
	// Perform reading
	while (pContext->m_bTerminate == FALSE && bStreamComplete == FALSE)
	{
		BOOL bReadResult;
		
		// Is our circle buffer full?
		
		if (pContext->m_pCircleBuffer->GetFreeSize(pContext->m_pCircleBuffer) < CIC_READCHUNKSIZE)
		{
			Sleep(20);
			continue;
		}
		
		// Read in another chunk - if we don't get any data that's ok - we would rather poll the
		// buffers than just hang on the socket (so the stream can be shutdown if needed)
		internetbuffer.dwBufferLength = CIC_READCHUNKSIZE;
		bReadResult = InternetReadFileEx(hURLStream, &internetbuffer, IRF_NO_WAIT, 0);
		
		if (bReadResult == FALSE)
			bStreamComplete = TRUE;
			
		if (internetbuffer.dwBufferLength)
		{
			pContext->m_pCircleBuffer->Write(pContext->m_pCircleBuffer,
											 internetbuffer.lpvBuffer,
											 internetbuffer.dwBufferLength);
			                                 
			PostMessage(pContext->m_hWndNotify,
						CPNM_SETSTREAMINGSTATE,
						(WPARAM)TRUE,
						(LPARAM)(pContext->m_pCircleBuffer->GetUsedSize(pContext->m_pCircleBuffer)*100) / CIC_STREAMBUFFERSIZE);
		}
		
		else
			Sleep(20);
	}
	
	InternetCloseHandle(hURLStream);
	
	InternetCloseHandle(hInternet);
	
	pContext->m_pCircleBuffer->SetComplete(pContext->m_pCircleBuffer);
	PostMessage(pContext->m_hWndNotify, CPNM_SETSTREAMINGSTATE, (WPARAM)FALSE, (LPARAM)0);
	CP_TRACE0("EP_FillerThread normal shutdown");
	return 0;
}