| /* |
| * CHM Utility API |
| * |
| * Copyright 2005 James Hawkins |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winuser.h" |
| #include "winnls.h" |
| #include "winreg.h" |
| #include "ole2.h" |
| #include "htmlhelp.h" |
| |
| #include "initguid.h" |
| #include "chm.h" |
| |
| static LPWSTR CHM_ANSIToUnicode(LPCSTR ansi) |
| { |
| LPWSTR unicode; |
| int count; |
| |
| count = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0); |
| unicode = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR)); |
| MultiByteToWideChar(CP_ACP, 0, ansi, -1, unicode, count); |
| |
| return unicode; |
| } |
| |
| /* Reads a string from the #STRINGS section in the CHM file */ |
| LPWSTR CHM_ReadString(CHMInfo *pChmInfo, DWORD dwOffset) |
| { |
| LARGE_INTEGER liOffset; |
| IStorage *pStorage = pChmInfo->pStorage; |
| IStream *pStream; |
| DWORD cbRead; |
| ULONG iPos; |
| DWORD dwSize; |
| LPSTR szString; |
| LPWSTR stringW; |
| |
| const int CB_READ_BLOCK = 64; |
| static const WCHAR stringsW[] = {'#','S','T','R','I','N','G','S',0}; |
| |
| dwSize = CB_READ_BLOCK; |
| szString = HeapAlloc(GetProcessHeap(), 0, dwSize); |
| |
| if (FAILED(IStorage_OpenStream(pStorage, stringsW, NULL, STGM_READ, 0, &pStream))) |
| return NULL; |
| |
| liOffset.QuadPart = dwOffset; |
| |
| if (FAILED(IStream_Seek(pStream, liOffset, STREAM_SEEK_SET, NULL))) |
| { |
| IStream_Release(pStream); |
| return NULL; |
| } |
| |
| while (SUCCEEDED(IStream_Read(pStream, szString, CB_READ_BLOCK, &cbRead))) |
| { |
| if (!cbRead) |
| return NULL; |
| |
| for (iPos = 0; iPos < cbRead; iPos++) |
| { |
| if (!szString[iPos]) |
| { |
| stringW = CHM_ANSIToUnicode(szString); |
| HeapFree(GetProcessHeap(), 0, szString); |
| return stringW; |
| } |
| } |
| |
| dwSize *= 2; |
| szString = HeapReAlloc(GetProcessHeap(), 0, szString, dwSize); |
| szString += cbRead; |
| } |
| |
| /* didn't find a string */ |
| return NULL; |
| } |
| |
| /* Loads the HH_WINTYPE data from the CHM file |
| * |
| * FIXME: There may be more than one window type in the file, so |
| * add the ability to choose a certain window type |
| */ |
| BOOL CHM_LoadWinTypeFromCHM(CHMInfo *pChmInfo, HH_WINTYPEW *pHHWinType) |
| { |
| LARGE_INTEGER liOffset; |
| IStorage *pStorage = pChmInfo->pStorage; |
| IStream *pStream; |
| HRESULT hr; |
| DWORD cbRead; |
| |
| static const WCHAR windowsW[] = {'#','W','I','N','D','O','W','S',0}; |
| |
| hr = IStorage_OpenStream(pStorage, windowsW, NULL, STGM_READ, 0, &pStream); |
| if (FAILED(hr)) |
| return FALSE; |
| |
| /* jump past the #WINDOWS header */ |
| liOffset.QuadPart = sizeof(DWORD) * 2; |
| |
| hr = IStream_Seek(pStream, liOffset, STREAM_SEEK_SET, NULL); |
| if (FAILED(hr)) goto done; |
| |
| /* read the HH_WINTYPE struct data */ |
| hr = IStream_Read(pStream, pHHWinType, sizeof(*pHHWinType), &cbRead); |
| if (FAILED(hr)) goto done; |
| |
| /* convert the #STRINGS offsets to actual strings */ |
| pHHWinType->pszType = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszType); |
| pHHWinType->pszCaption = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszCaption); |
| pHHWinType->pszToc = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszToc); |
| pHHWinType->pszIndex = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszIndex); |
| pHHWinType->pszFile = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszFile); |
| pHHWinType->pszHome = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszHome); |
| pHHWinType->pszJump1 = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszJump1); |
| pHHWinType->pszJump2 = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszJump2); |
| pHHWinType->pszUrlJump1 = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszUrlJump1); |
| pHHWinType->pszUrlJump2 = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszUrlJump2); |
| |
| /* FIXME: pszCustomTabs is a list of multiple zero-terminated strings so ReadString won't |
| * work in this case |
| */ |
| #if 0 |
| pHHWinType->pszCustomTabs = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszCustomTabs); |
| #endif |
| |
| done: |
| IStream_Release(pStream); |
| |
| return SUCCEEDED(hr); |
| } |
| |
| /* Opens the CHM file for reading */ |
| BOOL CHM_OpenCHM(CHMInfo *pChmInfo, LPCWSTR szFile) |
| { |
| pChmInfo->szFile = szFile; |
| |
| if (FAILED(CoCreateInstance(&CLSID_ITStorage, NULL, CLSCTX_INPROC_SERVER, |
| &IID_IITStorage, (void **) &pChmInfo->pITStorage))) |
| return FALSE; |
| |
| if (FAILED(IITStorage_StgOpenStorage(pChmInfo->pITStorage, szFile, NULL, |
| STGM_READ | STGM_SHARE_DENY_WRITE, |
| NULL, 0, &pChmInfo->pStorage))) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| void CHM_CloseCHM(CHMInfo *pCHMInfo) |
| { |
| IITStorage_Release(pCHMInfo->pITStorage); |
| IStorage_Release(pCHMInfo->pStorage); |
| } |
| |
| /* Creates a Url of a CHM file that can be used with WB_Navigate */ |
| void CHM_CreateITSUrl(CHMInfo *pChmInfo, LPCWSTR szIndex, LPWSTR szUrl) |
| { |
| static const WCHAR formatW[] = { |
| 'i','t','s',':','%','s',':',':','%','s',0 |
| }; |
| |
| wsprintfW(szUrl, formatW, pChmInfo->szFile, szIndex); |
| } |