dbghelp: Allow to add an alternate file_map for an ELF file (where to look for its...
[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 static BOOL ReadChmSystem(CHMInfo *chm)
76 {
77     IStream *stream;
78     DWORD ver=0xdeadbeef, read, buf_size;
79     char *buf;
80     HRESULT hres;
81
82     struct {
83         WORD code;
84         WORD len;
85     } entry;
86
87     static const WCHAR wszSYSTEM[] = {'#','S','Y','S','T','E','M',0};
88
89     hres = IStorage_OpenStream(chm->pStorage, wszSYSTEM, NULL, STGM_READ, 0, &stream);
90     if(FAILED(hres)) {
91         WARN("Could not open #SYSTEM stream: %08x\n", hres);
92         return FALSE;
93     }
94
95     IStream_Read(stream, &ver, sizeof(ver), &read);
96     TRACE("version is %x\n", ver);
97
98     buf = hhctrl_alloc(8*sizeof(DWORD));
99     buf_size = 8*sizeof(DWORD);
100
101     while(1) {
102         hres = IStream_Read(stream, &entry, sizeof(entry), &read);
103         if(hres != S_OK)
104             break;
105
106         if(entry.len > buf_size)
107             buf = hhctrl_realloc(buf, buf_size=entry.len);
108
109         hres = IStream_Read(stream, buf, entry.len, &read);
110         if(hres != S_OK)
111             break;
112
113         switch(entry.code) {
114         case 0x2:
115             TRACE("Default topic is %s\n", debugstr_an(buf, entry.len));
116             break;
117         case 0x3:
118             TRACE("Title is %s\n", debugstr_an(buf, entry.len));
119             break;
120         case 0x5:
121             TRACE("Default window is %s\n", debugstr_an(buf, entry.len));
122             break;
123         case 0x6:
124             TRACE("Compiled file is %s\n", debugstr_an(buf, entry.len));
125             break;
126         case 0x9:
127             TRACE("Version is %s\n", debugstr_an(buf, entry.len));
128             break;
129         case 0xa:
130             TRACE("Time is %08x\n", *(DWORD*)buf);
131             break;
132         case 0xc:
133             TRACE("Number of info types: %d\n", *(DWORD*)buf);
134             break;
135         case 0xf:
136             TRACE("Check sum: %x\n", *(DWORD*)buf);
137             break;
138         default:
139             TRACE("unhandled code %x, size %x\n", entry.code, entry.len);
140         }
141     }
142
143     hhctrl_free(buf);
144     IStream_Release(stream);
145
146     return SUCCEEDED(hres);
147 }
148
149 /* Loads the HH_WINTYPE data from the CHM file
150  *
151  * FIXME: There may be more than one window type in the file, so
152  *        add the ability to choose a certain window type
153  */
154 BOOL CHM_LoadWinTypeFromCHM(CHMInfo *pChmInfo, HH_WINTYPEW *pHHWinType)
155 {
156     LARGE_INTEGER liOffset;
157     IStorage *pStorage = pChmInfo->pStorage;
158     IStream *pStream;
159     HRESULT hr;
160     DWORD cbRead;
161
162     static const WCHAR windowsW[] = {'#','W','I','N','D','O','W','S',0};
163
164     hr = IStorage_OpenStream(pStorage, windowsW, NULL, STGM_READ, 0, &pStream);
165     if (FAILED(hr))
166         return FALSE;
167
168     /* jump past the #WINDOWS header */
169     liOffset.QuadPart = sizeof(DWORD) * 2;
170
171     hr = IStream_Seek(pStream, liOffset, STREAM_SEEK_SET, NULL);
172     if (FAILED(hr)) goto done;
173
174     /* read the HH_WINTYPE struct data */
175     hr = IStream_Read(pStream, pHHWinType, sizeof(*pHHWinType), &cbRead);
176     if (FAILED(hr)) goto done;
177
178     /* convert the #STRINGS offsets to actual strings */
179     pHHWinType->pszType = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszType));
180     pHHWinType->pszCaption = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszCaption));
181     pHHWinType->pszToc = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszToc));
182     pHHWinType->pszIndex = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszIndex));
183     pHHWinType->pszFile = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszFile));
184     pHHWinType->pszHome = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszHome));
185     pHHWinType->pszJump1 = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszJump1));
186     pHHWinType->pszJump2 = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszJump2));
187     pHHWinType->pszUrlJump1 = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszUrlJump1));
188     pHHWinType->pszUrlJump2 = strdupAtoW(GetChmString(pChmInfo, (DWORD)pHHWinType->pszUrlJump2));
189     
190     /* FIXME: pszCustomTabs is a list of multiple zero-terminated strings so ReadString won't
191      * work in this case
192      */
193 #if 0
194     pHHWinType->pszCustomTabs = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszCustomTabs);
195 #endif
196
197 done:
198     IStream_Release(pStream);
199
200     return SUCCEEDED(hr);
201 }
202
203 /* Opens the CHM file for reading */
204 CHMInfo *OpenCHM(LPCWSTR szFile)
205 {
206     HRESULT hres;
207
208     static const WCHAR wszSTRINGS[] = {'#','S','T','R','I','N','G','S',0};
209
210     CHMInfo *ret = hhctrl_alloc_zero(sizeof(CHMInfo));
211
212     ret->szFile = szFile;
213
214     hres = CoCreateInstance(&CLSID_ITStorage, NULL, CLSCTX_INPROC_SERVER,
215             &IID_IITStorage, (void **) &ret->pITStorage) ;
216     if(FAILED(hres)) {
217         WARN("Could not create ITStorage: %08x\n", hres);
218         return CloseCHM(ret);
219     }
220
221     hres = IITStorage_StgOpenStorage(ret->pITStorage, szFile, NULL,
222             STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &ret->pStorage);
223     if(FAILED(hres)) {
224         WARN("Could not open storage: %08x\n", hres);
225         return CloseCHM(ret);
226     }
227
228     hres = IStorage_OpenStream(ret->pStorage, wszSTRINGS, NULL, STGM_READ, 0,
229             &ret->strings_stream);
230     if(FAILED(hres)) {
231         WARN("Could not open #STRINGS stream: %08x\n", hres);
232         return CloseCHM(ret);
233     }
234
235     if(!ReadChmSystem(ret)) {
236         WARN("Could not read #SYSTEM\n");
237         return CloseCHM(ret);
238     }
239
240     return ret;
241 }
242
243 CHMInfo *CloseCHM(CHMInfo *chm)
244 {
245     if(chm->pITStorage)
246         IITStorage_Release(chm->pITStorage);
247
248     if(chm->pStorage)
249         IStorage_Release(chm->pStorage);
250
251     if(chm->strings_stream)
252         IStream_Release(chm->strings_stream);
253
254     if(chm->strings_size) {
255         int i;
256
257         for(i=0; i<chm->strings_size; i++)
258             hhctrl_free(chm->strings[i]);
259     }
260
261     hhctrl_free(chm->strings);
262     hhctrl_free(chm);
263
264     return NULL;
265 }