hhctrl.ocx: Handle memory allocation in OpenCHM and CloseCHM.
[wine] / dlls / hhctrl.ocx / chm.c
1 /*
2  * CHM Utility API
3  *
4  * Copyright 2005 James Hawkins
5  * Copyright 2007 Jacek Caban
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "hhctrl.h"
23
24 #include "wine/debug.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
27
28 #define BLOCK_BITS 12
29 #define BLOCK_SIZE (1 << BLOCK_BITS)
30 #define BLOCK_MASK (BLOCK_SIZE-1)
31
32 /* Reads a string from the #STRINGS section in the CHM file */
33 static LPCSTR GetChmString(CHMInfo *chm, DWORD offset)
34 {
35     if(!chm->strings_stream)
36         return NULL;
37
38     if(chm->strings_size <= (offset >> BLOCK_BITS)) {
39         if(chm->strings)
40             chm->strings = hhctrl_realloc_zero(chm->strings,
41                     chm->strings_size = ((offset >> BLOCK_BITS)+1)*sizeof(char*));
42         else
43             chm->strings = hhctrl_alloc_zero(
44                     chm->strings_size = ((offset >> BLOCK_BITS)+1)*sizeof(char*));
45
46     }
47
48     if(!chm->strings[offset >> BLOCK_BITS]) {
49         LARGE_INTEGER pos;
50         DWORD read;
51         HRESULT hres;
52
53         pos.QuadPart = offset & ~BLOCK_MASK;
54         hres = IStream_Seek(chm->strings_stream, pos, STREAM_SEEK_SET, NULL);
55         if(FAILED(hres)) {
56             WARN("Seek failed: %08x\n", hres);
57             return NULL;
58         }
59
60         chm->strings[offset >> BLOCK_BITS] = hhctrl_alloc(BLOCK_SIZE);
61
62         hres = IStream_Read(chm->strings_stream, chm->strings[offset >> BLOCK_BITS],
63                             BLOCK_SIZE, &read);
64         if(FAILED(hres)) {
65             WARN("Read failed: %08x\n", hres);
66             hhctrl_free(chm->strings[offset >> BLOCK_BITS]);
67             chm->strings[offset >> BLOCK_BITS] = NULL;
68             return NULL;
69         }
70     }
71
72     return chm->strings[offset >> BLOCK_BITS] + (offset & BLOCK_MASK);
73 }
74
75 /* Loads the HH_WINTYPE data from the CHM file
76  *
77  * FIXME: There may be more than one window type in the file, so
78  *        add the ability to choose a certain window type
79  */
80 BOOL CHM_LoadWinTypeFromCHM(CHMInfo *pChmInfo, HH_WINTYPEW *pHHWinType)
81 {
82     LARGE_INTEGER liOffset;
83     IStorage *pStorage = pChmInfo->pStorage;
84     IStream *pStream;
85     HRESULT hr;
86     DWORD cbRead;
87
88     static const WCHAR windowsW[] = {'#','W','I','N','D','O','W','S',0};
89
90     hr = IStorage_OpenStream(pStorage, windowsW, NULL, STGM_READ, 0, &pStream);
91     if (FAILED(hr))
92         return FALSE;
93
94     /* jump past the #WINDOWS header */
95     liOffset.QuadPart = sizeof(DWORD) * 2;
96
97     hr = IStream_Seek(pStream, liOffset, STREAM_SEEK_SET, NULL);
98     if (FAILED(hr)) goto done;
99
100     /* read the HH_WINTYPE struct data */
101     hr = IStream_Read(pStream, pHHWinType, sizeof(*pHHWinType), &cbRead);
102     if (FAILED(hr)) goto done;
103
104     /* convert the #STRINGS offsets to actual strings */
105     pHHWinType->pszType = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszType));
106     pHHWinType->pszCaption = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszCaption));
107     pHHWinType->pszToc = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszToc));
108     pHHWinType->pszIndex = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszIndex));
109     pHHWinType->pszFile = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszFile));
110     pHHWinType->pszHome = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszHome));
111     pHHWinType->pszJump1 = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszJump1));
112     pHHWinType->pszJump2 = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszJump2));
113     pHHWinType->pszUrlJump1 = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszUrlJump1));
114     pHHWinType->pszUrlJump2 = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszUrlJump2));
115     
116     /* FIXME: pszCustomTabs is a list of multiple zero-terminated strings so ReadString won't
117      * work in this case
118      */
119 #if 0
120     pHHWinType->pszCustomTabs = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszCustomTabs);
121 #endif
122
123 done:
124     IStream_Release(pStream);
125
126     return SUCCEEDED(hr);
127 }
128
129 /* Opens the CHM file for reading */
130 CHMInfo *OpenCHM(LPCWSTR szFile)
131 {
132     HRESULT hres;
133
134     static const WCHAR wszSTRINGS[] = {'#','S','T','R','I','N','G','S',0};
135
136     CHMInfo *ret = hhctrl_alloc_zero(sizeof(CHMInfo));
137
138     ret->szFile = szFile;
139
140     hres = CoCreateInstance(&CLSID_ITStorage, NULL, CLSCTX_INPROC_SERVER,
141             &IID_IITStorage, (void **) &ret->pITStorage) ;
142     if(FAILED(hres)) {
143         WARN("Could not create ITStorage: %08x\n", hres);
144         return CloseCHM(ret);
145     }
146
147     hres = IITStorage_StgOpenStorage(ret->pITStorage, szFile, NULL,
148             STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &ret->pStorage);
149     if(FAILED(hres)) {
150         WARN("Could not open storage: %08x\n", hres);
151         return CloseCHM(ret);
152     }
153
154     hres = IStorage_OpenStream(ret->pStorage, wszSTRINGS, NULL, STGM_READ, 0,
155             &ret->strings_stream);
156     if(FAILED(hres)) {
157         WARN("Could not open #STRINGS stream: %08x\n", hres);
158         return CloseCHM(ret);
159     }
160
161     return ret;
162 }
163
164 CHMInfo *CloseCHM(CHMInfo *chm)
165 {
166     if(chm->pITStorage)
167         IITStorage_Release(chm->pITStorage);
168
169     if(chm->pStorage)
170         IStorage_Release(chm->pStorage);
171
172     if(chm->strings_stream)
173         IStream_Release(chm->strings_stream);
174
175     if(chm->strings_size) {
176         int i;
177
178         for(i=0; i<chm->strings_size; i++)
179             hhctrl_free(chm->strings[i]);
180     }
181
182     hhctrl_free(chm->strings);
183     hhctrl_free(chm);
184
185     return NULL;
186 }