// 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);
}