www.pudn.com > eMule0.42e-Sources.zip > ArchiveRecovery.cpp


//this file is part of eMule 
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net ) 
// 
//This program is free software; you can redistribute it and/or 
//modify it under the terms of the GNU General Public License 
//as published by the Free Software Foundation; either 
//version 2 of the License, or (at your option) any later version. 
// 
//This program is distributed in the hope that it will be useful, 
//but WITHOUT ANY WARRANTY; without even the implied warranty of 
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
//GNU General Public License for more details. 
// 
//You should have received a copy of the GNU General Public License 
//along with this program; if not, write to the Free Software 
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
#include "stdafx.h" 
#include "emule.h" 
#include "ArchiveRecovery.h" 
#include "OtherFunctions.h" 
#include "Preferences.h" 
#include "PartFile.h" 
#include  
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
 
// At some point it may be worth displaying messages to alert the user if there were errors, or where to find the file. 
 
void CArchiveRecovery::recover(CPartFile *partFile, bool preview) 
{ 
	if (partFile->m_bPreviewing || partFile->m_bRecoveringArchive) 
		return; 
	partFile->m_bRecoveringArchive = true; 
 
	AddLogLine(true, GetResString(IDS_ATTEMPTING_RECOVERY) ); 
 
	// Get the current filled list for this file 
	CTypedPtrList *filled = new CTypedPtrList; 
	partFile->GetFilledList(filled); 
 
	// The rest of the work can be safely done in a new thread 
	ThreadParam *tp = new ThreadParam; 
	tp->partFile = partFile; 
	tp->filled = filled; 
	tp->preview = preview; 
	// - do NOT use Windows API 'CreateThread' to create a thread which uses MFC/CRT -> lot of mem leaks! 
	if (!AfxBeginThread(run, (LPVOID)tp)){ 
		partFile->m_bRecoveringArchive = false; 
		AddLogLine(true, GetResString(IDS_RECOVERY_FAILED)); 
		// Need to delete the memory here as won't be done in thread 
		DeleteMemory(tp); 
	} 
} 
 
UINT AFX_CDECL CArchiveRecovery::run(LPVOID lpParam) 
{ 
	ThreadParam *tp = (ThreadParam *)lpParam; 
	DbgSetThreadName("ArchiveRecovery"); 
 
	//::SetThreadLocale(thePrefs.GetLanguageID()); // 01.06.03 EC - ArchiveRecovery Thread wasn't localized 
 
	if (!performRecovery(tp->partFile, tp->filled, tp->preview)) 
		AddLogLine(true, GetResString(IDS_RECOVERY_FAILED)); 
 
	tp->partFile->m_bRecoveringArchive = false; 
 
	// Delete memory used by copied gap list 
	DeleteMemory(tp); 
 
	return 0; 
} 
 
bool CArchiveRecovery::performRecovery(CPartFile *partFile, CTypedPtrList *filled, bool preview) 
{ 
	bool success = false; 
	try 
	{ 
		// Copy the file 
		CString tempFileName = CString(thePrefs.GetTempDir()) + CString("\\") + CString(partFile->GetFileName()).Mid(0,5) + CString("-rec.tmp"); 
		if (!CopyFile(partFile, filled, tempFileName)) 
			return false; 
 
		// Open temp file for reading 
		CFile temp; 
		if (!temp.Open(tempFileName, CFile::modeRead)) 
			return false; 
 
		// Open the output file 
		CString ext = CString(partFile->GetFileName()).Right(4); 
		CString outputFileName = CString(thePrefs.GetTempDir()) + CString("\\") + CString(partFile->GetFileName()).Mid(0,5) + CString("-rec") + ext; 
		CFile output; 
		if (output.Open(outputFileName, CFile::modeWrite | CFile::shareExclusive | CFile::modeCreate)) 
		{ 
			// Process the output file 
			if (ext.CompareNoCase(".zip") == 0) 
				success = recoverZip(&temp, &output, filled, (temp.GetLength() == partFile->GetFileSize())); 
			else if (ext.CompareNoCase(".rar") == 0) 
				success = recoverRar(&temp, &output, filled); 
 
			// Close output 
			output.Close(); 
		} 
		// Close temp file 
		temp.Close(); 
 
		// Report success 
		if (success) 
		{ 
			AddLogLine(true, GetResString(IDS_RECOVERY_SUCCESSFUL)); 
			// Preview file if required 
			if (preview) 
			{ 
				SHELLEXECUTEINFO SE; 
				memset(&SE,0,sizeof(SE)); 
				SE.fMask = SEE_MASK_NOCLOSEPROCESS ; 
				SE.lpVerb = "open"; 
				SE.lpFile = outputFileName.GetBuffer(); 
				SE.nShow = SW_SHOW; 
				SE.cbSize = sizeof(SE); 
				ShellExecuteEx(&SE); 
				if (SE.hProcess) 
				{ 
					WaitForSingleObject(SE.hProcess, INFINITE); 
					CloseHandle(SE.hProcess); 
				} 
				CFile::Remove(outputFileName); 
			} 
		} 
 
		// Remove temp file 
		CFile::Remove(tempFileName); 
	} 
	catch (CFileException* error){ 
		error->Delete(); 
	} 
	catch (...){ 
		ASSERT(0); 
	} 
	return success; 
} 
 
bool CArchiveRecovery::recoverZip(CFile *zipInput, CFile *zipOutput, CTypedPtrList *filled, bool fullSize) 
{ 
	bool retVal = false; 
	long fileCount = 0; 
	try 
	{ 
		CTypedPtrList centralDirectoryEntries; 
		Gap_Struct *fill; 
 
		// If the central directory is intact this is simple 
		if (fullSize && readZipCentralDirectory(zipInput, ¢ralDirectoryEntries, filled)) 
		{ 
			if (centralDirectoryEntries.GetCount() == 0) 
				return false; 
			ZIP_CentralDirectory *cdEntry; 
			POSITION pos = centralDirectoryEntries.GetHeadPosition(); 
			bool deleteCD; 
			for (int i=centralDirectoryEntries.GetCount(); i>0; i--) 
			{ 
				deleteCD = false; 
				cdEntry = centralDirectoryEntries.GetAt(pos); 
				uint32 lenEntry = sizeof(ZIP_Entry) + cdEntry->lenFilename + cdEntry->lenExtraField + cdEntry->lenCompressed; 
				if (IsFilled(cdEntry->relativeOffsetOfLocalHeader, cdEntry->relativeOffsetOfLocalHeader + lenEntry, filled)) 
				{ 
					zipInput->Seek(cdEntry->relativeOffsetOfLocalHeader, CFile::begin); 
					// Update offset 
					cdEntry->relativeOffsetOfLocalHeader = zipOutput->GetPosition(); 
					if (!processZipEntry(zipInput, zipOutput, lenEntry, NULL)) 
						deleteCD = true; 
				} 
				else 
					deleteCD = true; 
 
				if (deleteCD) 
				{ 
					delete [] cdEntry->filename; 
					if (cdEntry->lenExtraField > 0) 
						delete [] cdEntry->extraField; 
					if (cdEntry->lenComment > 0) 
						delete [] cdEntry->comment; 
					delete cdEntry; 
					POSITION del = pos; 
					centralDirectoryEntries.GetNext(pos); 
					centralDirectoryEntries.RemoveAt(del); 
				} 
				else 
					centralDirectoryEntries.GetNext(pos); 
			} 
		} 
		else // Have to scan the file the hard way 
		{		 
			// Loop through filled areas of the file looking for entries 
			POSITION pos = filled->GetHeadPosition(); 
			while (pos != NULL) 
			{ 
				fill = filled->GetNext(pos); 
				uint32 filePos = zipInput->GetPosition(); 
				// The file may have been positioned to the next entry in ScanForMarker() or processZipEntry() 
				if (filePos > fill->end) 
					continue; 
				if (filePos < fill->start) 
					zipInput->Seek(fill->start, CFile::begin); 
 
				// If there is any problem, then don't bother checking the rest of this part 
				for (;;) 
				{ 
					// Scan for entry marker within this filled area 
					if (!scanForZipMarker(zipInput, (uint32)ZIP_LOCAL_HEADER_MAGIC, (fill->end - zipInput->GetPosition() + 1))) 
						break; 
					if (zipInput->GetPosition() > fill->end) 
						break; 
					if (!processZipEntry(zipInput, zipOutput, (fill->end - zipInput->GetPosition() + 1), ¢ralDirectoryEntries)) 
						break; 
				} 
			} 
		} 
 
		// Remember offset before CD entries 
		uint32 startOffset = zipOutput->GetPosition(); 
 
		// Write all central directory entries 
		fileCount = centralDirectoryEntries.GetCount(); 
		if (fileCount > 0) 
		{ 
			ZIP_CentralDirectory *cdEntry; 
			POSITION pos = centralDirectoryEntries.GetHeadPosition(); 
			while (pos != NULL) 
			{ 
				cdEntry = centralDirectoryEntries.GetNext(pos); 
 
				writeUInt32(zipOutput, ZIP_CD_MAGIC); 
				writeUInt16(zipOutput, cdEntry->versionMadeBy); 
				writeUInt16(zipOutput, cdEntry->versionToExtract); 
				writeUInt16(zipOutput, cdEntry->generalPurposeFlag); 
				writeUInt16(zipOutput, cdEntry->compressionMethod); 
				writeUInt16(zipOutput, cdEntry->lastModFileTime); 
				writeUInt16(zipOutput, cdEntry->lastModFileDate); 
				writeUInt32(zipOutput, cdEntry->crc32); 
				writeUInt32(zipOutput, cdEntry->lenCompressed); 
				writeUInt32(zipOutput, cdEntry->lenUnompressed); 
				writeUInt16(zipOutput, cdEntry->lenFilename); 
				writeUInt16(zipOutput, cdEntry->lenExtraField); 
				writeUInt16(zipOutput, cdEntry->lenComment); 
				writeUInt16(zipOutput, 0); // Disk number start 
				writeUInt16(zipOutput, cdEntry->internalFileAttributes); 
				writeUInt32(zipOutput, cdEntry->externalFileAttributes); 
				writeUInt32(zipOutput, cdEntry->relativeOffsetOfLocalHeader); 
				zipOutput->Write(cdEntry->filename, cdEntry->lenFilename); 
				if (cdEntry->lenExtraField > 0) 
					zipOutput->Write(cdEntry->extraField, cdEntry->lenExtraField); 
				if (cdEntry->lenComment > 0) 
					zipOutput->Write(cdEntry->comment, cdEntry->lenComment); 
 
				delete [] cdEntry->filename; 
				if (cdEntry->lenExtraField > 0) 
					delete [] cdEntry->extraField; 
				if (cdEntry->lenComment > 0) 
					delete [] cdEntry->comment; 
				delete cdEntry; 
			} 
 
			// Remember offset before CD entries 
			uint32 endOffset = zipOutput->GetPosition(); 
 
			// Write end of central directory 
			writeUInt32(zipOutput, ZIP_END_CD_MAGIC); 
			writeUInt16(zipOutput, 0); // Number of this disk 
			writeUInt16(zipOutput, 0); // Number of the disk with the start of the central directory 
			writeUInt16(zipOutput, fileCount); 
			writeUInt16(zipOutput, fileCount); 
			writeUInt32(zipOutput, endOffset - startOffset); 
			writeUInt32(zipOutput, startOffset); 
			writeUInt16(zipOutput, strlen(ZIP_COMMENT)); 
			zipOutput->Write(ZIP_COMMENT, strlen(ZIP_COMMENT)); 
 
			centralDirectoryEntries.RemoveAll(); 
		} 
		retVal = true; 
	} 
	catch (CFileException* error){ 
		error->Delete(); 
	} 
	catch (...){ 
		ASSERT(0); 
	} 
 
	// Tell the user how many files were recovered 
	CString msg;	 
	if (fileCount == 1) 
		msg = GetResString(IDS_RECOVER_SINGLE); 
	else 
		msg.Format(GetResString(IDS_RECOVER_MULTIPLE), fileCount); 
	//AfxMessageBox(msg, MB_OK | MB_ICONINFORMATION); 
	AddLogLine(true, _T("%s"), msg); 
 
	return retVal; 
} 
 
bool CArchiveRecovery::readZipCentralDirectory(CFile *zipInput, CTypedPtrList *centralDirectoryEntries, CTypedPtrList *filled) 
{ 
	bool retVal = false; 
	try 
	{ 
		// Ideally this zip file will not have a comment and the End-CD will be easy to find 
		zipInput->Seek(-22, CFile::end); 
		if (!(readUInt32(zipInput) == ZIP_END_CD_MAGIC)) 
		{ 
			// Have to look for it, comment could be up to 65535 chars but only try with less than 1k 
			zipInput->Seek(-1046, CFile::end); 
			if (!scanForZipMarker(zipInput, (uint32)ZIP_END_CD_MAGIC, 1046)) 
				return false; 
			// Skip it again 
			readUInt32(zipInput); 
		} 
 
		// Found End-CD 
		// Only interested in offset of first CD 
		zipInput->Seek(12, CFile::current); 
		uint32 startOffset = readUInt32(zipInput); 
		if (!IsFilled(startOffset, zipInput->GetLength(), filled)) 
			return false; 
 
		// Goto first CD and start reading 
		zipInput->Seek(startOffset, CFile::begin); 
		ZIP_CentralDirectory *cdEntry; 
		while (readUInt32(zipInput) == ZIP_CD_MAGIC) 
		{ 
			cdEntry = new ZIP_CentralDirectory; 
			cdEntry->versionMadeBy				= readUInt16(zipInput); 
			cdEntry->versionToExtract			= readUInt16(zipInput); 
			cdEntry->generalPurposeFlag			= readUInt16(zipInput); 
			cdEntry->compressionMethod			= readUInt16(zipInput); 
			cdEntry->lastModFileTime			= readUInt16(zipInput); 
			cdEntry->lastModFileDate			= readUInt16(zipInput); 
			cdEntry->crc32						= readUInt32(zipInput); 
			cdEntry->lenCompressed				= readUInt32(zipInput); 
			cdEntry->lenUnompressed				= readUInt32(zipInput); 
			cdEntry->lenFilename				= readUInt16(zipInput); 
			cdEntry->lenExtraField				= readUInt16(zipInput); 
			cdEntry->lenComment					= readUInt16(zipInput); 
			cdEntry->diskNumberStart			= readUInt16(zipInput); 
			cdEntry->internalFileAttributes		= readUInt16(zipInput); 
			cdEntry->externalFileAttributes		= readUInt32(zipInput); 
			cdEntry->relativeOffsetOfLocalHeader= readUInt32(zipInput); 
 
			if (cdEntry->lenFilename > 0) 
			{ 
				cdEntry->filename					= new BYTE[cdEntry->lenFilename]; 
				zipInput->Read(cdEntry->filename, cdEntry->lenFilename); 
			} 
			if (cdEntry->lenExtraField > 0) 
			{ 
				cdEntry->extraField					= new BYTE[cdEntry->lenExtraField]; 
				zipInput->Read(cdEntry->extraField, cdEntry->lenExtraField); 
			} 
			if (cdEntry->lenComment > 0) 
			{ 
				cdEntry->comment					= new BYTE[cdEntry->lenComment]; 
				zipInput->Read(cdEntry->comment, cdEntry->lenComment); 
			} 
 
			centralDirectoryEntries->AddTail(cdEntry); 
		} 
 
		retVal = true; 
	} 
	catch (CFileException* error){ 
		error->Delete(); 
	} 
	catch (...){ 
		ASSERT(0); 
	} 
	return retVal; 
} 
 
bool CArchiveRecovery::processZipEntry(CFile *zipInput, CFile *zipOutput, uint32 available, CTypedPtrList *centralDirectoryEntries) 
{ 
	if (available < 26) 
		return false; 
 
	bool retVal = false; 
	try 
	{ 
		// Need to know where it started 
		long startOffset = zipOutput->GetPosition(); 
 
		// Entry format : 
		//  4      2 bytes  Version needed to extract 
		//  6      2 bytes  General purpose bit flag 
		//  8      2 bytes  Compression method 
		// 10      2 bytes  Last mod file time 
		// 12      2 bytes  Last mod file date 
		// 14      4 bytes  CRC-32 
		// 18      4 bytes  Compressed size (n) 
		// 22      4 bytes  Uncompressed size 
		// 26      2 bytes  Filename length (f) 
		// 28      2 bytes  Extra field length (e) 
		//        (f)bytes  Filename 
		//        (e)bytes  Extra field 
		//        (n)bytes  Compressed data 
 
		// Read header 
		if (readUInt32(zipInput) != ZIP_LOCAL_HEADER_MAGIC) 
			return false; 
 
		ZIP_Entry entry={0}; 
		entry.versionToExtract		= readUInt16(zipInput); 
		entry.generalPurposeFlag	= readUInt16(zipInput); 
		entry.compressionMethod		= readUInt16(zipInput); 
		entry.lastModFileTime		= readUInt16(zipInput); 
		entry.lastModFileDate		= readUInt16(zipInput); 
		entry.crc32					= readUInt32(zipInput); 
		entry.lenCompressed			= readUInt32(zipInput); 
		entry.lenUncompressed		= readUInt32(zipInput); 
		entry.lenFilename			= readUInt16(zipInput); 
		entry.lenExtraField			= readUInt16(zipInput); 
		 
		// Do some quick checks at this stage that data is looking ok 
		if ((entry.crc32 == 0) || (entry.lenCompressed == 0) || (entry.lenUncompressed == 0) || (entry.lenFilename == 0)) 
			return false; 
 
		// Is this entry complete 
		if ((entry.lenFilename + entry.lenExtraField + entry.lenCompressed) > (available - 26)) 
		{ 
			// Move the file pointer to the start of the next entry 
			zipInput->Seek((entry.lenFilename + entry.lenExtraField + entry.lenCompressed), CFile::current); 
			return false; 
		} 
 
		// Filename 
		if (entry.lenFilename > MAX_PATH) 
			return false; // Possibly corrupt, don't allocate lots of memory 
		entry.filename = new BYTE[entry.lenFilename]; 
		if (zipInput->Read(entry.filename, entry.lenFilename) != entry.lenFilename) 
		{ 
			delete [] entry.filename; 
			return false; 
		} 
 
		// Extra data 
		if (entry.lenExtraField > 0) 
		{ 
			entry.extraField = new BYTE[entry.lenExtraField]; 
			zipInput->Read(entry.extraField, entry.lenExtraField); 
		} 
 
		// Output 
		writeUInt32(zipOutput, ZIP_LOCAL_HEADER_MAGIC); 
		writeUInt16(zipOutput, entry.versionToExtract); 
		writeUInt16(zipOutput, entry.generalPurposeFlag); 
		writeUInt16(zipOutput, entry.compressionMethod); 
		writeUInt16(zipOutput, entry.lastModFileTime); 
		writeUInt16(zipOutput, entry.lastModFileDate); 
		writeUInt32(zipOutput, entry.crc32); 
		writeUInt32(zipOutput, entry.lenCompressed); 
		writeUInt32(zipOutput, entry.lenUncompressed); 
		writeUInt16(zipOutput, entry.lenFilename); 
		writeUInt16(zipOutput, entry.lenExtraField); 
		if (entry.lenFilename > 0) 
			zipOutput->Write(entry.filename, entry.lenFilename); 
		if (entry.lenExtraField > 0) 
			zipOutput->Write(entry.extraField, entry.lenExtraField); 
 
		// Read and write compressed data to avoid reading all into memory 
		uint32 written = 0; 
		BYTE buf[4096]; 
		uint32 lenChunk = 4096; 
		while (written < entry.lenCompressed) 
		{ 
			lenChunk  = (entry.lenCompressed - written); 
			if (lenChunk > 4096) 
				lenChunk = 4096; 
			lenChunk = zipInput->Read(buf, lenChunk); 
			if (lenChunk == 0) 
				break; 
			written += lenChunk; 
			zipOutput->Write(buf, lenChunk); 
		} 
		zipOutput->Flush(); 
 
		//Central directory: 
		if (centralDirectoryEntries != NULL) 
		{ 
			ZIP_CentralDirectory *cdEntry = new ZIP_CentralDirectory; 
			cdEntry->header = ZIP_CD_MAGIC; 
			cdEntry->versionMadeBy = entry.versionToExtract; 
			cdEntry->versionToExtract = entry.versionToExtract; 
			cdEntry->generalPurposeFlag = entry.generalPurposeFlag; 
			cdEntry->compressionMethod = entry.compressionMethod; 
			cdEntry->lastModFileTime = entry.lastModFileTime; 
			cdEntry->lastModFileDate = entry.lastModFileDate; 
			cdEntry->crc32 = entry.crc32; 
			cdEntry->lenCompressed = entry.lenCompressed; 
			cdEntry->lenUnompressed = entry.lenUncompressed; 
			cdEntry->lenFilename = entry.lenFilename; 
			cdEntry->lenExtraField = entry.lenExtraField; 
			cdEntry->lenComment = strlen(ZIP_COMMENT); 
			cdEntry->diskNumberStart = 0; 
			cdEntry->internalFileAttributes = 1; 
			cdEntry->externalFileAttributes = 0x81B60020; 
			cdEntry->relativeOffsetOfLocalHeader = startOffset; 
			cdEntry->filename = entry.filename; 
			if (entry.lenExtraField > 0) 
				cdEntry->extraField = entry.extraField; 
			cdEntry->comment = new BYTE[cdEntry->lenComment]; 
			memcpy(cdEntry->comment, ZIP_COMMENT, cdEntry->lenComment); 
 
			centralDirectoryEntries->AddTail(cdEntry); 
		} 
		else 
		{ 
			delete [] entry.filename; 
			if (entry.lenExtraField > 0) 
				delete [] entry.extraField; 
		} 
		retVal = true; 
	} 
	catch (CFileException* error){ 
		error->Delete(); 
	} 
	catch (...){ 
		ASSERT(0); 
	} 
	return retVal; 
} 
 
void CArchiveRecovery::DeleteMemory(ThreadParam *tp) 
{ 
	POSITION pos = tp->filled->GetHeadPosition(); 
	while (pos != NULL) 
		delete tp->filled->GetNext(pos); 
	tp->filled->RemoveAll(); 
	delete tp->filled; 
	delete tp; 
} 
 
bool CArchiveRecovery::CopyFile(CPartFile *partFile, CTypedPtrList *filled, CString tempFileName) 
{ 
	bool retVal = false; 
	CFile *srcFile = NULL; 
	try 
	{ 
		// Get a new handle to the part file 
		srcFile = partFile->m_hpartfile.Duplicate(); 
 
		// Open destination file and set length to last filled end position 
		CFile destFile; 
		destFile.Open(tempFileName, CFile::modeWrite | CFile::shareExclusive | CFile::modeCreate); 
		Gap_Struct *fill = filled->GetTail(); 
		destFile.SetLength(fill->end); 
 
		BYTE buffer[4096]; 
		uint32 read; 
		uint32 copied; 
 
		// Loop through filled areas and copy data 
		partFile->m_bPreviewing = true; 
		POSITION pos = filled->GetHeadPosition(); 
		while (pos != NULL) 
		{ 
			fill = filled->GetNext(pos); 
			copied = 0; 
			srcFile->Seek(fill->start, CFile::begin); 
			destFile.Seek(fill->start, CFile::begin); 
			while ((read = srcFile->Read(buffer, 4096)) > 0) 
			{ 
				destFile.Write(buffer, read); 
				copied += read; 
				// Stop when finished fill (don't worry abuot extra) 
				if (fill->start + copied >= fill->end) 
					break; 
			} 
		} 
		destFile.Close(); 
		srcFile->Close(); 
		partFile->m_bPreviewing = false; 
 
		retVal = true; 
	} 
	catch (CFileException* error){ 
		error->Delete(); 
	} 
	catch (...){ 
		ASSERT(0); 
	} 
 
	if (srcFile) 
		delete srcFile; 
 
	return retVal; 
} 
 
bool CArchiveRecovery::recoverRar(CFile *rarInput, CFile *rarOutput, CTypedPtrList *filled) 
{ 
	bool retVal = false; 
	long fileCount = 0; 
	try 
	{ 
		BYTE start[] = RAR_START_OF_FILE; 
		rarOutput->Write(start, sizeof(start)); 
 
		RAR_BlockFile *block; 
		while ((block = scanForRarFileHeader(rarInput, (uint32)rarInput->GetLength())) != NULL) 
		{ 
			if (IsFilled(block->offsetData, block->offsetData + block->dataLength, filled)) 
			{ 
				// Don't include directories in file count 
				if ((block->HEAD_FLAGS & 0xE0) != 0xE0)  
					fileCount++; 
				writeRarBlock(rarInput, rarOutput, block); 
			} 
			else 
			{ 
				rarInput->Seek(block->offsetData + block->dataLength, CFile::begin); 
			} 
			delete [] block->FILE_NAME; 
			delete block; 
		} 
		retVal = true; 
	} 
	catch (CFileException* error){ 
		error->Delete(); 
	} 
	catch (...){ 
		ASSERT(0); 
	} 
 
	// Tell the user how many files were recovered 
	CString msg;	 
	if (fileCount == 1) 
		msg = GetResString(IDS_RECOVER_SINGLE); 
	else 
		msg.Format(GetResString(IDS_RECOVER_MULTIPLE), fileCount); 
	//AfxMessageBox(msg, MB_OK | MB_ICONINFORMATION); 
	AddLogLine(true, _T("%s"), msg); 
 
	return retVal; 
} 
 
bool CArchiveRecovery::IsFilled(uint32 start, uint32 end, CTypedPtrList *filled) 
{ 
	POSITION pos = filled->GetHeadPosition(); 
	Gap_Struct *fill; 
	while (pos != NULL) 
	{ 
		fill = filled->GetNext(pos); 
		if (fill->start > start) 
			return false; 
		if (fill->end >= end) 
			return true; 
	} 
	return false; 
} 
 
// This will find the marker in the file and leave it positioned at the position to read the marker again 
bool CArchiveRecovery::scanForZipMarker(CFile *input, uint32 marker, uint32 available) 
{ 
	try 
	{ 
		//uint32 originalOffset = input->GetPosition(); 
		int lenChunk = 51200; // 50k buffer  
		BYTE chunk[51200]; 
		BYTE *foundPos = NULL; 
		int pos = 0; 
 
		while ((available > 0) && ((lenChunk = input->Read(chunk, lenChunk)) > 0)) 
		{ 
			available -= lenChunk; 
			foundPos = &chunk[0]; 
			// Move back one, will be incremented in loop 
			foundPos--; 
			while (foundPos != NULL) 
			{ 
				// Find first matching byte 
				foundPos = (BYTE*)memchr( foundPos+1, (marker & 0xFF), (lenChunk - (foundPos+1 - (&chunk[0]))) ); 
				if (foundPos == NULL) 
					continue; 
 
				// Test for end of buffer 
				pos = foundPos - (&chunk[0]); 
				if ((pos + 3) > lenChunk) 
				{ 
					// Re-read buffer starting from found first byte position 
					input->Seek(pos - lenChunk, CFile::current); 
					break; 
				} 
 
				// Check for other bytes 
				if (chunk[pos + 1] == ((marker >> 8) & 0xFF)) 
				{ 
					if (chunk[pos + 2] == ((marker >> 16) & 0xFF)) 
					{ 
						if (chunk[pos + 3] == ((marker >> 24) & 0xFF)) 
						{ 
							// Found it 
							input->Seek(pos - lenChunk, CFile::current); 
							return true; 
						} 
					} 
				} 
			} 
		} 
	} 
	catch (CFileException* error){ 
		error->Delete(); 
	} 
	catch (...){ 
		ASSERT(0); 
	} 
	return false; 
} 
 
// This will find a file block in the file and leave it positioned at the end of the filename 
RAR_BlockFile *CArchiveRecovery::scanForRarFileHeader(CFile *input, uint32 available) 
{ 
	RAR_BlockFile *retVal = NULL; 
	try 
	{ 
		int lenChunk = 51200; // 50k buffer  
		BYTE chunk[51200]; 
		BYTE *foundPos = NULL; 
		int pos = 0; 
		ULONGLONG searchOffset; 
		ULONGLONG foundOffset; 
		uint16 headCRC; 
		BYTE checkCRC[38]; 
		uint16 lenFileName; 
		BYTE *fileName; 
		uint32 crc; 
 
		while ((available > 0) && ((lenChunk = input->Read(chunk, lenChunk)) > 0)) 
		{ 
			available -= lenChunk; 
			foundPos = &chunk[0]; 
			searchOffset = input->GetPosition() - lenChunk; 
 
			// Move back one, will be incremented in loop 
			foundPos--; 
			while (foundPos != NULL) 
			{ 
				// Find rar head block marker 
				foundPos = (BYTE*)memchr( foundPos+1, RAR_HEAD_FILE, (lenChunk - (foundPos+1 - (&chunk[0]))) ); 
				if (foundPos == NULL) 
					continue; 
 
				// Move back 2 bytes to get crc and read block 
				pos = (int)(foundPos - (&chunk[0]) - 2); 
				input->Seek(pos - lenChunk, CFile::current); 
				foundOffset = input->GetPosition(); 
				headCRC = readUInt16(input); // CRC of fields from HEAD_TYPE to ATTR + filename 
				input->Read(&checkCRC[0], 30); 
				// Also need filename for crc 
				lenFileName = (((uint16)checkCRC[25]) << 8) + ((uint16)checkCRC[24]); 
				fileName = new BYTE[lenFileName]; 
				if (checkCRC[2] & 0x1) // If HEAD_FLAG & 0x100 
					input->Read(&checkCRC[30], 8); 
				input->Read(fileName, lenFileName); 
				crc = crc32(0, &checkCRC[0], 30); 
				crc = crc32(crc, fileName, lenFileName); 
				if ((crc & 0xFFFF) == headCRC) 
				{ 
					// Found valid crc, build block and return 
					// Note that it may still be invalid data, so more checks should be performed 
					retVal = new RAR_BlockFile; 
					retVal->HEAD_CRC		= headCRC; 
					retVal->HEAD_TYPE		= 0x74; 
					retVal->HEAD_FLAGS		= calcUInt16(&checkCRC[ 1]); 
					retVal->HEAD_SIZE		= calcUInt16(&checkCRC[ 3]); 
					retVal->PACK_SIZE		= calcUInt32(&checkCRC[ 5]); 
					retVal->UNP_SIZE		= calcUInt32(&checkCRC[ 9]); 
					retVal->HOST_OS			= checkCRC[13]; 
					retVal->FILE_CRC		= calcUInt32(&checkCRC[14]); 
					retVal->FTIME			= calcUInt32(&checkCRC[18]); 
					retVal->UNP_VER			= checkCRC[22]; 
					retVal->METHOD			= checkCRC[23]; 
					retVal->NAME_SIZE		= lenFileName; 
					retVal->ATTR			= calcUInt32(&checkCRC[26]); 
					// Optional values, present only if bit 0x100 in HEAD_FLAGS is set. 
					if ((retVal->HEAD_FLAGS & 0x100) == 0x100) 
					{ 
						retVal->HIGH_PACK_SIZE	= calcUInt32(&checkCRC[30]); 
						retVal->HIGH_UNP_SIZE	= calcUInt32(&checkCRC[34]); 
					} 
					retVal->FILE_NAME		= fileName; 
					// Run some quick checks 
					if (validateRarFileBlock(retVal)) 
					{ 
						// Set some useful markers in the block 
						retVal->offsetData = input->GetPosition(); 
						uint32 dataLength = retVal->PACK_SIZE; 
						// If comment present find length 
						if ((retVal->HEAD_FLAGS & 0x08) == 0x08) 
						{ 
							// Skip start of comment block 
							input->Seek(5, CFile::current); 
							// Read comment length 
							dataLength += readUInt16(input); 
						} 
						retVal->dataLength = dataLength; 
 
						return retVal; 
					} 
				} 
				// If not valid, return to original position, re-read and continue searching 
				delete [] fileName; 
				if (retVal != NULL) 
					delete retVal; 
				retVal = NULL; 
				input->Seek(searchOffset, CFile::begin); 
				input->Read(chunk, lenChunk); 
			} 
		} 
	} 
	catch (CFileException* error){ 
		error->Delete(); 
	} 
	catch (...){ 
		ASSERT(0); 
	} 
	return false; 
} 
 
// This assumes that head crc has already been checked 
bool CArchiveRecovery::validateRarFileBlock(RAR_BlockFile *block) 
{ 
	if (block->HEAD_TYPE != 0x74) 
		return false; 
	if (block->UNP_SIZE < block->PACK_SIZE) 
		return false; 
	if (block->HOST_OS > 5) 
		return false; 
	switch (block->METHOD) 
	{ 
		case 0x30: // storing 
		case 0x31: // fastest compression 
		case 0x32: // fast compression 
		case 0x33: // normal compression 
		case 0x34: // good compression 
		case 0x35: // best compression 
			break; 
		default: 
			return false; 
	} 
	if (block->NAME_SIZE > MAX_PATH) 
		return false; 
	// Check directory entry has no size 
	if (((block->HEAD_FLAGS & 0xE0) == 0xE0) && ((block->PACK_SIZE + block->UNP_SIZE + block->FILE_CRC) > 0)) 
		return false; 
 
	return true; 
} 
 
void CArchiveRecovery::writeRarBlock(CFile *input, CFile *output, RAR_BlockFile *block) 
{ 
	ULONGLONG offsetStart = output->GetPosition(); 
	try 
	{ 
		writeUInt16(output, block->HEAD_CRC); 
		output->Write(&block->HEAD_TYPE, 1); 
		writeUInt16(output, block->HEAD_FLAGS); 
		writeUInt16(output, block->HEAD_SIZE); 
		writeUInt32(output, block->PACK_SIZE); 
		writeUInt32(output, block->UNP_SIZE); 
		output->Write(&block->HOST_OS, 1); 
		writeUInt32(output, block->FILE_CRC); 
		writeUInt32(output, block->FTIME); 
		output->Write(&block->UNP_VER, 1); 
		output->Write(&block->METHOD, 1); 
		writeUInt16(output, block->NAME_SIZE); 
		writeUInt32(output, block->ATTR); 
		// Optional values, present only if bit 0x100 in HEAD_FLAGS is set. 
		if ((block->HEAD_FLAGS & 0x100) == 0x100) 
		{ 
			writeUInt32(output, block->HIGH_PACK_SIZE); 
			writeUInt32(output, block->HIGH_UNP_SIZE); 
		} 
		output->Write(block->FILE_NAME, block->NAME_SIZE); 
 
		// Now copy compressed data from input file 
		uint32 lenToCopy = block->dataLength; 
		if (lenToCopy > 0) 
		{ 
			input->Seek(block->offsetData, CFile::begin); 
			uint32 written = 0; 
			BYTE chunk[4096]; 
			uint32 lenChunk = 4096; 
			while (written < lenToCopy) 
			{ 
				lenChunk  = (lenToCopy - written); 
				if (lenChunk > 4096) 
					lenChunk = 4096; 
				lenChunk = input->Read(chunk, lenChunk); 
				if (lenChunk == 0) 
					break; 
				written += lenChunk; 
				output->Write(chunk, lenChunk); 
			} 
		} 
		output->Flush(); 
	} 
	catch (CFileException* error){ 
		error->Delete(); 
		try { output->SetLength(offsetStart); } catch (...) {ASSERT(0);} 
	} 
	catch (...){ 
		ASSERT(0); 
		try { output->SetLength(offsetStart); } catch (...) {ASSERT(0);} 
	} 
} 
 
uint16 CArchiveRecovery::readUInt16(CFile *input) 
{ 
	uint16 retVal = 0; 
	BYTE b[2]; 
	if (input->Read(&b, 2) > 0) 
		retVal = (b[1] << 8) + b[0]; 
	return retVal; 
} 
 
uint32 CArchiveRecovery::readUInt32(CFile *input) 
{ 
	uint32 retVal = 0; 
	BYTE b[4]; 
	if (input->Read(&b, 4) > 0) 
		retVal = (b[3] << 24) + (b[2] << 16) + (b[1] << 8) + b[0]; 
	return retVal; 
} 
 
uint16 CArchiveRecovery::calcUInt16(BYTE *input) 
{ 
	uint16 retVal = 0; 
	try 
	{ 
		retVal = (((uint16)input[1]) << 8) + ((uint16)input[0]); 
	} catch (...) {ASSERT(0);} 
	return retVal; 
} 
 
uint32 CArchiveRecovery::calcUInt32(BYTE *input) 
{ 
	uint32 retVal = 0; 
	try 
	{ 
		retVal = (((uint32)input[3]) << 24) + (((uint32)input[2]) << 16) + (((uint32)input[1]) << 8) + ((uint32)input[0]); 
	} catch (...) {ASSERT(0);} 
	return retVal; 
} 
 
void CArchiveRecovery::writeUInt16(CFile *output, uint16 val) 
{ 
	BYTE b[2]; 
	b[0] = (val & 0x000000ff); 
	b[1] = (val & 0x0000ff00) >>  8; 
	output->Write(&b, 2); 
} 
 
void CArchiveRecovery::writeUInt32(CFile *output, uint32 val) 
{ 
	BYTE b[4]; 
	b[0] = (val & 0x000000ff); 
	b[1] = (val & 0x0000ff00) >>  8; 
	b[2] = (val & 0x00ff0000) >> 16; 
	b[3] = (val & 0xff000000) >> 24; 
	output->Write(&b, 4); 
}