Click here to Skip to main content
15,894,955 members
Articles / Mobile Apps / Windows Mobile

CHM Reader for Pocket PC 2003

Rate me:
Please Sign up or sign in to vote.
4.91/5 (65 votes)
29 Feb 2004CPOL3 min read 477.6K   960   87  
Allows the reading of CHM files on a Pocket PC2003.
// CHMFile.cpp: implementation of the CCHMFile class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CHMFile.h"
#include "AtlConv.h"
#include ".\chmfile.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CCHMFile::CCHMFile()
{	
	m_pCHMFile=NULL;
}

CCHMFile::~CCHMFile()
{
	ResetFileList();
	ResetTocList();
	if (m_pCHMFile)
		chm_close(m_pCHMFile);
	m_pCHMFile=NULL;
}

BOOL CCHMFile::ResetFileList()
{
	// Free Memory
	POSITION pos=m_FileList.GetHeadPosition();
	while(pos!=NULL)
	{
		CCHMFileInfo *pInfo=(CCHMFileInfo*)m_FileList.GetNext(pos);		
		delete pInfo;
	}
	m_FileList.RemoveAll();
	return true;
}

BOOL CCHMFile::ResetTocList()
{
	// Free Memory
	POSITION pos=m_TOC.GetHeadPosition();
	while(pos!=NULL)
	{
		CCHMToc *pInfo=(CCHMToc *)m_TOC.GetNext(pos);		
		delete pInfo;
	}
	m_TOC.RemoveAll();
	return true;
}

BOOL CCHMFile::Open(CString FileName)
{
	m_FileName=FileName;

	if (m_pCHMFile!=NULL)
		chm_close(m_pCHMFile);

	m_pCHMFile=NULL;
	ResetFileList();
	ResetTocList();

	USES_CONVERSION;
	m_pCHMFile=chm_open(T2A((LPTSTR)(LPCTSTR)FileName));
	if (m_pCHMFile==NULL)
		return false;

	m_Split.ResetData();
	m_Split.SplitPath(FileName);

	if (!ReadListOfFiles())
		return false;

	return true;
}

void CCHMFile::Close()
{
	if (m_pCHMFile)
	{
		chm_close(m_pCHMFile);
		m_pCHMFile=NULL;
	}
	ResetFileList();
	ResetTocList();
	DeleteFiles(_T("/Temp/"));
}

BOOL CCHMFile::Load(CString FileName)
{
	if (!Open(FileName))
		return false;		

	CCHMFileInfo* pHHCFile=GetHHCFile();
	if (pHHCFile!=NULL)
	{
		CString HHCText=GetTextFile(pHHCFile);
		
		CString Title;
		CString File;
		int Level=0;

		TRACE(_T("Processing HCC file : %s\n"),FileName);

		// Parse HHCText file to build TOC
		// <UL> = Go in Level		
		// </UL> = Go out level
		// <param name="Name" value="Cover"> = Title
		// <param name="Local" value="webappshtml/00co01a.htm">	= File to navigate to				

		CString Text;
		BOOL bNeedToAdd=false;
		for(int i=0;i<HHCText.GetLength();i++)
		{			
			if (HHCText.GetAt(i)==_T('<'))
			{
				if (HHCText.Mid(i,4).CompareNoCase(_T("<UL>"))==0)
				{
					if (bNeedToAdd)
					{
						m_TOC.AddTail(new CCHMToc(Title,"",Level));
						bNeedToAdd=false;
					}
					Level++;
				}
				if (HHCText.Mid(i,5).CompareNoCase(_T("</UL>"))==0)
				{
					Level--;
					if (bNeedToAdd)
					{
						m_TOC.AddTail(new CCHMToc(Title,"",Level));
						bNeedToAdd=false;
					}
				}
				if (HHCText.Mid(i,26).CompareNoCase(_T("<param name=\"Name\" value=\""))==0)
				{
					int idx=HHCText.Find(_T("\">"),i+26);
					Title=HHCText.Mid(i+26,idx-i-26);
					bNeedToAdd=true;
				}
				if (HHCText.Mid(i,27).CompareNoCase(_T("<param name=\"Local\" value=\""))==0)
				{
					int idx=HHCText.Find(_T("\">"),i+27);
					File=HHCText.Mid(i+27,idx-i-27);
					File=_T("/")+File;
					m_TOC.AddTail(new CCHMToc(Title,File,Level));
					bNeedToAdd=false;
				}
			}
		}		
	}
	else
		TRACE(_T("NO HCC Contents file found\n"));

	// All Done
	return true;
}

BOOL CCHMFile::GetFile(CString FileName, CString FilePath)
{
	CCHMFileInfo *pInfo=CheckFileExists(FileName);
	return GetFile(pInfo,FilePath);
}

// Get File from CHM & Store onto Disk using FilePath
BOOL CCHMFile::GetFile(CCHMFileInfo *pInfo, CString FilePath)
{	
	if (pInfo==NULL)
		return false;

	BYTE* pBuffer=GetFile(pInfo);
	if (pBuffer==NULL)
		return false;
	
	// Create File
	TRACE(_T("Get File : Saving file to %s as %s\n"),pInfo->m_FileName,FilePath);
	CFile F;
	if (F.Open(FilePath,CFile::modeWrite|CFile::modeCreate))
	{
		F.Write(pBuffer,pInfo->m_Length);
		F.Close();		
	}
	else
	{
		TRACE(_T("Failed to create file : %s as %s\n"),pInfo->m_FileName,FilePath);
	}

	return true;
}

CString CCHMFile::GetTextFile(CCHMFileInfo *pInfo)
{
	if (pInfo==NULL)
		return CString("");
	
	BYTE *pBuffer=GetFile(pInfo);
	if (pBuffer==NULL)
		return CString("");	
	
	CString FileText;

	CStringEx Text;
	for(unsigned long i=0;i<pInfo->m_Length;i++)
	{
		Text+=(TCHAR)pBuffer[i];
	}
	int idx=Text.FindNoCase(_T("UTF-8"));
	if ((idx>-1) && (idx<200))
	{
		DWORD dwSize=(pInfo->m_Length*sizeof(TCHAR))+100;
		BYTE *pBuffer2=new BYTE[dwSize];
		ZeroMemory(pBuffer2,dwSize);
		int nRes=MultiByteToWideChar(CP_UTF8,0,(LPCSTR)pBuffer,pInfo->m_Length,(LPWSTR)pBuffer2,dwSize);

		FileText.Format(_T("%s"),(LPWSTR)pBuffer2);

		delete pBuffer2;
		pBuffer2=NULL;
	}
	else
	{
		FileText=Text;
	}

	delete pBuffer;
	pBuffer=NULL;
	
	return CString(FileText);
}

BYTE* CCHMFile::GetFile(CCHMFileInfo *pInfo)
{	
	if (m_pCHMFile==NULL)
	{
		TRACE(_T("Get File : Failed - pCHMFile =NULL\n"));
		return NULL;
	}	
	if (pInfo==NULL)
	{
		TRACE(_T("Get File : File not found in List of files\n"));
		return NULL;
	}

	TRACE(_T("Get File : %s\n"),pInfo->m_FileName);
	struct chmUnitInfo ui;
	USES_CONVERSION;
	int rc=chm_resolve_object(m_pCHMFile, T2A((LPTSTR)(LPCTSTR)pInfo->m_FileName), &ui);	
	if (rc==1)
	{
		TRACE(_T("Get File : Failed to resolve file\n"));
		return NULL;
	}
	
	BYTE *pBuffer=new BYTE[pInfo->m_Length+10];
	LONGINT64 rc2=chm_retrieve_object(m_pCHMFile, &ui, pBuffer, 0, pInfo->m_Length);	

	return pBuffer;
}

int CCHMFile::EnumListOfFiles(struct chmFile *h,struct chmUnitInfo *ui,void *context)
{
	CCHMFile *pCHMFIle=(CCHMFile*)context;	
	pCHMFIle->AddFileInfo(ui->path,(DWORD)ui->length, (DWORD)ui->space, (DWORD)ui->start);
    return CHM_ENUMERATOR_CONTINUE;
}

BOOL CCHMFile::AddFileInfo(CString FileName, DWORD Length, DWORD Space, DWORD Start)
{
	CCHMFileInfo *pFileInfo=new CCHMFileInfo(FileName, Length, Space,Start);
	m_FileList.AddTail(pFileInfo);
	return true;
}

// Read CHM File List
BOOL CCHMFile::ReadListOfFiles(void)
{
	if (m_pCHMFile==NULL)
		return false;

	ResetFileList();

	// Read TOC	
	int rc=chm_enumerate(m_pCHMFile, CHM_ENUMERATE_ALL, CCHMFile::EnumListOfFiles, (void *)this);

	// All Done
	return true;
}

CCHMFileInfo* CCHMFile::CheckFileExists(CString FileName)
{
	if (FileName.Left(3).Compare(_T("/.."))==0)
		FileName=FileName.Mid(3);

	if (FileName.Left(7).CompareNoCase(_T("MS-ITS:"))==0)
	{		
		FileName=FileName.Mid(7);
		int idx=FileName.Find(_T("::"));
		CString CHMFileName=FileName.Left(idx);
		FileName=FileName.Mid(idx+2);

		if (CHMFileName.CompareNoCase(m_Split.GetFilename()+m_Split.GetExtension())!=0)
			return NULL;
	}
	if (FileName.Left(8).CompareNoCase(_T("/MS-ITS:"))==0)
	{		
		FileName=FileName.Mid(8);
		int idx=FileName.Find(_T("::"));
		CString CHMFileName=FileName.Left(idx);
		FileName=FileName.Mid(idx+2);
		if (CHMFileName.CompareNoCase(m_Split.GetFilename()+m_Split.GetExtension())!=0)
			return NULL;
	}

	// Ensure all filenames start with '/'
	if (FileName.Left(1).Compare(_T("/"))!=0)
		FileName=_T("/")+FileName;

	int idx=FileName.ReverseFind(_T('/'));	
	CString FileName2=FileName.Mid(idx+1);
	CString FileNameHTML;
	FileNameHTML.Format(_T("/html%s"),FileName);
	POSITION pos=m_FileList.GetHeadPosition();
	while(pos!=NULL)
	{
		CCHMFileInfo *pInfo=(CCHMFileInfo*)m_FileList.GetNext(pos);		
		if (pInfo)
		{
			if (pInfo->m_FileName.CompareNoCase(FileName)==0)
			{
				return pInfo;
			}
			if (pInfo->m_FileName.CompareNoCase(FileNameHTML)==0)
			{
				return pInfo;
			}
		}
	}	
	
	return NULL;
}


CCHMFileInfo* CCHMFile::GetHHCFile()
{
	POSITION pos=m_FileList.GetHeadPosition();
	while(pos!=NULL)
	{
		CCHMFileInfo *pInfo=(CCHMFileInfo*)m_FileList.GetNext(pos);		
		if (pInfo)
		{
			if (pInfo->m_FileName.Right(4).CompareNoCase(_T(".hhc"))==0)
			{
				return pInfo;
			}
		}
	}	
	return NULL;
}

int CCHMFile::CheckStringArray(CStringArray *pStrings, CString Item, BOOL bNoCase)
{
	if (pStrings==NULL)
		return -1;

	for(int i=0;i<pStrings->GetSize();i++)
	{
		if (bNoCase)
		{
			if (pStrings->GetAt(i).CompareNoCase(Item)==0)
				return i;
		}
		else
		{
			if (pStrings->GetAt(i).Compare(Item)==0)
				return i;
		}
	}
	return -1;
}


BOOL CCHMFile::DeleteFiles(CString Dir)
{	
	for(int i=0;i<m_ImageList.GetSize();i++)
	{
		CString FileName=m_ImageList.GetAt(i);

		int idx=FileName.ReverseFind(_T('/'));
		CString FilePath;
		FilePath.Format(_T("%s\\%s"),Dir,FileName.Mid(idx+1));
		DeleteFile(FilePath);
	}
	m_ImageList.RemoveAll();
	return true;
}

CString CCHMFile::GetHTMLFile(CString FileName, CString Dir)
{		
	CCHMFileInfo *pInfo=CheckFileExists(FileName);
	if (pInfo==NULL)
		return CString("");

	CStringEx HTMLText=GetTextFile(pInfo);
	if (HTMLText.IsEmpty())
		return CString ("");

	// Get Root Directory.

	// Store for later.

	TRACE(_T("Processing HTML Text file : %s\n"),FileName);
	m_ImageList.RemoveAll();		
	int iStartPos=0;
	int iLinkPos=-1;
	do
	{
		iLinkPos=HTMLText.FindNoCase(_T("<link"),iStartPos);
		if (iLinkPos!=-1)
		{
			int idx=HTMLText.FindNoCase(_T("href="),iLinkPos+5);
			if (idx>-1)
			{
				int idx2=HTMLText.Mid(idx+6).FindOneOf(_T("'\""));
				if (idx2>-1)
				{										
					CStringEx HRef=HTMLText.Mid(idx+6,idx2);					
					TRACE(_T("Found HRef : %s\n"),HRef);

					if (CheckStringArray(&m_ImageList,HRef, true)==-1)	
						m_ImageList.Add(HRef);

					CString Insert;
					if (HRef.FindNoCase(_T("MS-ITS:"))==0)
					{
						int idx3=HRef.Find(_T("::"));
						CString NewName=HRef.Mid(7,idx3-7);
						CString Insert;
						Insert.Format(_T("file://%s/%s"),Dir,HRef.Mid(idx3+3));
						HTMLText=HTMLText.Left(idx+6)+Insert+HTMLText.Mid(idx+idx2);
						iStartPos=idx+6+Insert.GetLength();
					}
					else
					{
						if (HRef.GetAt(0)!=_T('/'))
						{
							CSplitPath Split(pInfo->m_FileName,_T('/'));
							HRef=Split.GetPath()+HRef;
						}					
						Insert.Format(_T("file://%s"),Dir);
						HTMLText.Insert(idx+6,Insert);				
						CString Text=HTMLText.Mid(idx);
						iStartPos=idx+Insert.GetLength()+2;
					}				
				}
			}			
			continue;
		}

		// Remove Script entries
		iLinkPos=HTMLText.FindNoCase(_T("<script"),iStartPos);
		if (iLinkPos!=-1)
		{
			int idx=HTMLText.FindNoCase(_T("</script>"),iLinkPos);
			HTMLText=HTMLText.Left(iLinkPos)+HTMLText.Mid(idx+9);
			continue;
		}

		// Remove XML Entries.
		iLinkPos=HTMLText.FindNoCase(_T("<xml"),iStartPos);
		if (iLinkPos!=-1)
		{
			int idx=HTMLText.FindNoCase(_T("</xml>"),iLinkPos);
			HTMLText=HTMLText.Left(iLinkPos)+HTMLText.Mid(idx+6);
			continue;
		}

	}
	while (iLinkPos!=-1);
	
	// Extract StyleSheet Files
	for(int i=0;i<m_ImageList.GetSize();i++)
	{		
		CStringEx FN=m_ImageList.GetAt(i);
		CCHMFileInfo *pInfo=CheckFileExists(FN);

		CCHMFile ExternalCHM;											
		CCHMFile *pCHMFile=this;	
		if (pInfo==NULL)
		{
			CSplitPath Split(FileName,_T('/'));
			CString NameEx;						
			NameEx=Split.GetPath()+FN;						
			pInfo=CheckFileExists(NameEx);
		}
		if ((pInfo==NULL) && (FN.FindNoCase(_T("MS-ITS:"))==0))
		{
			CString ExternalCHMName;				
			int idx=FN.Find(_T("::"));
			ExternalCHMName=FN.Mid(7,idx-7);

			CStringEx Text;			
			int idx2=FN.ReverseFind(_T('/'));
			Text=FN.Right(FN.GetLength()-idx2-1);

			// Get file from external source.						
			if (ExternalCHM.Open(GetSplit().GetPath()+ExternalCHMName))
			{	
				pInfo=ExternalCHM.CheckFileExists(Text);
				pCHMFile=&ExternalCHM;
			}		
		}

		if (pInfo)
		{			
			CStringEx Text;			
			int idx=pInfo->m_FileName.ReverseFind(_T('/'));
			Text=pInfo->m_FileName.Right(pInfo->m_FileName.GetLength()-idx-1);

			CString FilePath;
			FilePath.Format(_T("%s%s"),Dir,Text);
			pCHMFile->GetFile(pInfo,FilePath);			
		}
	}

	WORD w=0;
	w=(WORD)HTMLText.GetAt(0);
	if (w==0xfeff)
		HTMLText=HTMLText.Mid(1);

	return CString(HTMLText);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions