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 #include "stream.h"
24 #ifdef __REACTOS__
25 #include "resource.h"
26 #endif
27
28 #include "winreg.h"
29 #include "shlwapi.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
33
34 /* Reads a string from the #STRINGS section in the CHM file */
GetChmString(CHMInfo * chm,DWORD offset)35 static LPCSTR GetChmString(CHMInfo *chm, DWORD offset)
36 {
37 LPCSTR str;
38
39 if(!chm->strings_stream)
40 return NULL;
41
42 if(chm->strings_size <= (offset >> BLOCK_BITS)) {
43 chm->strings_size = (offset >> BLOCK_BITS)+1;
44 if(chm->strings)
45 chm->strings = heap_realloc_zero(chm->strings,
46 chm->strings_size*sizeof(char*));
47 else
48 chm->strings = heap_alloc_zero(
49 chm->strings_size*sizeof(char*));
50
51 }
52
53 if(!chm->strings[offset >> BLOCK_BITS]) {
54 LARGE_INTEGER pos;
55 DWORD read;
56 HRESULT hres;
57
58 pos.QuadPart = offset & ~BLOCK_MASK;
59 hres = IStream_Seek(chm->strings_stream, pos, STREAM_SEEK_SET, NULL);
60 if(FAILED(hres)) {
61 WARN("Seek failed: %08x\n", hres);
62 return NULL;
63 }
64
65 chm->strings[offset >> BLOCK_BITS] = heap_alloc(BLOCK_SIZE);
66
67 hres = IStream_Read(chm->strings_stream, chm->strings[offset >> BLOCK_BITS],
68 BLOCK_SIZE, &read);
69 if(FAILED(hres)) {
70 WARN("Read failed: %08x\n", hres);
71 heap_free(chm->strings[offset >> BLOCK_BITS]);
72 chm->strings[offset >> BLOCK_BITS] = NULL;
73 return NULL;
74 }
75 }
76
77 str = chm->strings[offset >> BLOCK_BITS] + (offset & BLOCK_MASK);
78 TRACE("offset %#x => %s\n", offset, debugstr_a(str));
79 return str;
80 }
81
ReadChmSystem(CHMInfo * chm)82 static BOOL ReadChmSystem(CHMInfo *chm)
83 {
84 IStream *stream;
85 DWORD ver=0xdeadbeef, read, buf_size;
86 char *buf;
87 HRESULT hres;
88
89 struct {
90 WORD code;
91 WORD len;
92 } entry;
93
94 static const WCHAR wszSYSTEM[] = {'#','S','Y','S','T','E','M',0};
95
96 hres = IStorage_OpenStream(chm->pStorage, wszSYSTEM, NULL, STGM_READ, 0, &stream);
97 if(FAILED(hres)) {
98 WARN("Could not open #SYSTEM stream: %08x\n", hres);
99 return FALSE;
100 }
101
102 IStream_Read(stream, &ver, sizeof(ver), &read);
103 TRACE("version is %x\n", ver);
104
105 buf = heap_alloc(8*sizeof(DWORD));
106 buf_size = 8*sizeof(DWORD);
107
108 while(1) {
109 hres = IStream_Read(stream, &entry, sizeof(entry), &read);
110 if(hres != S_OK)
111 break;
112
113 if(entry.len > buf_size)
114 buf = heap_realloc(buf, buf_size=entry.len);
115
116 hres = IStream_Read(stream, buf, entry.len, &read);
117 if(hres != S_OK)
118 break;
119
120 switch(entry.code) {
121 case 0x0:
122 TRACE("TOC is %s\n", debugstr_an(buf, entry.len));
123 heap_free(chm->defToc);
124 chm->defToc = strdupnAtoW(buf, entry.len);
125 break;
126 case 0x2:
127 TRACE("Default topic is %s\n", debugstr_an(buf, entry.len));
128 heap_free(chm->defTopic);
129 chm->defTopic = strdupnAtoW(buf, entry.len);
130 break;
131 case 0x3:
132 TRACE("Title is %s\n", debugstr_an(buf, entry.len));
133 heap_free(chm->defTitle);
134 chm->defTitle = strdupnAtoW(buf, entry.len);
135 break;
136 case 0x4:
137 /* TODO: Currently only the Locale ID is loaded from this field */
138 TRACE("Locale is: %d\n", *(LCID*)&buf[0]);
139 if(!GetLocaleInfoW(*(LCID*)&buf[0], LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER,
140 (WCHAR *)&chm->codePage, sizeof(chm->codePage)/sizeof(WCHAR)))
141 chm->codePage = CP_ACP;
142 break;
143 case 0x5:
144 TRACE("Window name is %s\n", debugstr_an(buf, entry.len));
145 chm->defWindow = strdupnAtoW(buf, entry.len);
146 break;
147 case 0x6:
148 TRACE("Compiled file is %s\n", debugstr_an(buf, entry.len));
149 heap_free(chm->compiledFile);
150 chm->compiledFile = strdupnAtoW(buf, entry.len);
151 break;
152 case 0x9:
153 TRACE("Version is %s\n", debugstr_an(buf, entry.len));
154 break;
155 case 0xa:
156 TRACE("Time is %08x\n", *(DWORD*)buf);
157 break;
158 case 0xc:
159 TRACE("Number of info types: %d\n", *(DWORD*)buf);
160 break;
161 case 0xf:
162 TRACE("Check sum: %x\n", *(DWORD*)buf);
163 break;
164 default:
165 TRACE("unhandled code %x, size %x\n", entry.code, entry.len);
166 }
167 }
168
169 heap_free(buf);
170 IStream_Release(stream);
171
172 return SUCCEEDED(hres);
173 }
174
FindContextAlias(CHMInfo * chm,DWORD index)175 LPWSTR FindContextAlias(CHMInfo *chm, DWORD index)
176 {
177 IStream *ivb_stream;
178 DWORD size, read, i;
179 DWORD *buf;
180 LPCSTR ret = NULL;
181 HRESULT hres;
182
183 static const WCHAR wszIVB[] = {'#','I','V','B',0};
184
185 hres = IStorage_OpenStream(chm->pStorage, wszIVB, NULL, STGM_READ, 0, &ivb_stream);
186 if(FAILED(hres)) {
187 WARN("Could not open #IVB stream: %08x\n", hres);
188 return NULL;
189 }
190
191 hres = IStream_Read(ivb_stream, &size, sizeof(size), &read);
192 if(FAILED(hres)) {
193 WARN("Read failed: %08x\n", hres);
194 IStream_Release(ivb_stream);
195 return NULL;
196 }
197
198 buf = heap_alloc(size);
199 hres = IStream_Read(ivb_stream, buf, size, &read);
200 IStream_Release(ivb_stream);
201 if(FAILED(hres)) {
202 WARN("Read failed: %08x\n", hres);
203 heap_free(buf);
204 return NULL;
205 }
206
207 size /= 2*sizeof(DWORD);
208
209 for(i=0; i<size; i++) {
210 if(buf[2*i] == index) {
211 ret = GetChmString(chm, buf[2*i+1]);
212 break;
213 }
214 }
215
216 heap_free(buf);
217
218 TRACE("returning %s\n", debugstr_a(ret));
219 return strdupAtoW(ret);
220 }
221
222 /*
223 * Tests if the file <chmfile>.<ext> exists, used for loading Indices, Table of Contents, etc.
224 * when these files are not available from the HH_WINTYPE structure.
225 */
FindHTMLHelpSetting(HHInfo * info,const WCHAR * extW)226 static WCHAR *FindHTMLHelpSetting(HHInfo *info, const WCHAR *extW)
227 {
228 static const WCHAR periodW[] = {'.',0};
229 IStorage *pStorage = info->pCHMInfo->pStorage;
230 IStream *pStream;
231 WCHAR *filename;
232 HRESULT hr;
233
234 filename = heap_alloc( (lstrlenW(info->pCHMInfo->compiledFile)
235 + lstrlenW(periodW) + lstrlenW(extW) + 1) * sizeof(WCHAR) );
236 lstrcpyW(filename, info->pCHMInfo->compiledFile);
237 lstrcatW(filename, periodW);
238 lstrcatW(filename, extW);
239 hr = IStorage_OpenStream(pStorage, filename, NULL, STGM_READ, 0, &pStream);
240 if (FAILED(hr))
241 {
242 heap_free(filename);
243 return strdupAtoW("");
244 }
245 IStream_Release(pStream);
246 return filename;
247 }
248
MergeChmString(LPCWSTR src,WCHAR ** dst)249 static inline WCHAR *MergeChmString(LPCWSTR src, WCHAR **dst)
250 {
251 if(*dst == NULL)
252 *dst = strdupW(src);
253 return *dst;
254 }
255
MergeChmProperties(HH_WINTYPEW * src,HHInfo * info,BOOL override)256 void MergeChmProperties(HH_WINTYPEW *src, HHInfo *info, BOOL override)
257 {
258 DWORD unhandled_params = src->fsValidMembers & ~(HHWIN_PARAM_PROPERTIES|HHWIN_PARAM_STYLES
259 |HHWIN_PARAM_EXSTYLES|HHWIN_PARAM_RECT|HHWIN_PARAM_NAV_WIDTH
260 |HHWIN_PARAM_SHOWSTATE|HHWIN_PARAM_INFOTYPES|HHWIN_PARAM_TB_FLAGS
261 |HHWIN_PARAM_EXPANSION|HHWIN_PARAM_TABPOS|HHWIN_PARAM_TABORDER
262 |HHWIN_PARAM_HISTORY_COUNT|HHWIN_PARAM_CUR_TAB);
263 HH_WINTYPEW *dst = &info->WinType;
264 DWORD merge = override ? src->fsValidMembers : src->fsValidMembers & ~dst->fsValidMembers;
265
266 if (unhandled_params)
267 FIXME("Unsupported fsValidMembers fields: 0x%x\n", unhandled_params);
268
269 dst->fsValidMembers |= merge;
270 if (dst->cbStruct == 0)
271 {
272 /* If the structure has not been filled in yet then use all of the values */
273 dst->cbStruct = sizeof(HH_WINTYPEW);
274 merge = ~0;
275 }
276 if (merge & HHWIN_PARAM_PROPERTIES) dst->fsWinProperties = src->fsWinProperties;
277 if (merge & HHWIN_PARAM_STYLES) dst->dwStyles = src->dwStyles;
278 if (merge & HHWIN_PARAM_EXSTYLES) dst->dwExStyles = src->dwExStyles;
279 if (merge & HHWIN_PARAM_RECT) dst->rcWindowPos = src->rcWindowPos;
280 if (merge & HHWIN_PARAM_NAV_WIDTH) dst->iNavWidth = src->iNavWidth;
281 if (merge & HHWIN_PARAM_SHOWSTATE) dst->nShowState = src->nShowState;
282 if (merge & HHWIN_PARAM_INFOTYPES) dst->paInfoTypes = src->paInfoTypes;
283 if (merge & HHWIN_PARAM_TB_FLAGS) dst->fsToolBarFlags = src->fsToolBarFlags;
284 if (merge & HHWIN_PARAM_EXPANSION) dst->fNotExpanded = src->fNotExpanded;
285 if (merge & HHWIN_PARAM_TABPOS) dst->tabpos = src->tabpos;
286 if (merge & HHWIN_PARAM_TABORDER) memcpy(dst->tabOrder, src->tabOrder, sizeof(src->tabOrder));
287 if (merge & HHWIN_PARAM_HISTORY_COUNT) dst->cHistory = src->cHistory;
288 if (merge & HHWIN_PARAM_CUR_TAB) dst->curNavType = src->curNavType;
289
290 /*
291 * Note: We assume that hwndHelp, hwndCaller, hwndToolBar, hwndNavigation, and hwndHTML cannot be
292 * modified by the user. rcHTML and rcMinSize are not currently supported, so don't bother to copy them.
293 */
294
295 dst->pszType = MergeChmString(src->pszType, &info->stringsW.pszType);
296 dst->pszFile = MergeChmString(src->pszFile, &info->stringsW.pszFile);
297 dst->pszToc = MergeChmString(src->pszToc, &info->stringsW.pszToc);
298 dst->pszIndex = MergeChmString(src->pszIndex, &info->stringsW.pszIndex);
299 dst->pszCaption = MergeChmString(src->pszCaption, &info->stringsW.pszCaption);
300 dst->pszHome = MergeChmString(src->pszHome, &info->stringsW.pszHome);
301 dst->pszJump1 = MergeChmString(src->pszJump1, &info->stringsW.pszJump1);
302 dst->pszJump2 = MergeChmString(src->pszJump2, &info->stringsW.pszJump2);
303 dst->pszUrlJump1 = MergeChmString(src->pszUrlJump1, &info->stringsW.pszUrlJump1);
304 dst->pszUrlJump2 = MergeChmString(src->pszUrlJump2, &info->stringsW.pszUrlJump2);
305
306 /* FIXME: pszCustomTabs is a list of multiple zero-terminated strings so ReadString won't
307 * work in this case
308 */
309 #if 0
310 dst->pszCustomTabs = MergeChmString(src->pszCustomTabs, &info->pszCustomTabs);
311 #endif
312 }
313
ConvertChmString(HHInfo * info,DWORD id)314 static inline WCHAR *ConvertChmString(HHInfo *info, DWORD id)
315 {
316 WCHAR *ret = NULL;
317
318 if(id)
319 ret = strdupAtoW(GetChmString(info->pCHMInfo, id));
320 return ret;
321 }
322
wintype_free(HH_WINTYPEW * wintype)323 static inline void wintype_free(HH_WINTYPEW *wintype)
324 {
325 heap_free((void *)wintype->pszType);
326 heap_free((void *)wintype->pszCaption);
327 heap_free(wintype->paInfoTypes);
328 heap_free((void *)wintype->pszToc);
329 heap_free((void *)wintype->pszIndex);
330 heap_free((void *)wintype->pszFile);
331 heap_free((void *)wintype->pszHome);
332 heap_free((void *)wintype->pszJump1);
333 heap_free((void *)wintype->pszJump2);
334 heap_free((void *)wintype->pszUrlJump1);
335 heap_free((void *)wintype->pszUrlJump2);
336 heap_free((void *)wintype->pszCustomTabs);
337 }
338
339 /* Loads the HH_WINTYPE data from the CHM file
340 *
341 * FIXME: There may be more than one window type in the file, so
342 * add the ability to choose a certain window type
343 */
LoadWinTypeFromCHM(HHInfo * info)344 BOOL LoadWinTypeFromCHM(HHInfo *info)
345 {
346 LARGE_INTEGER liOffset;
347 IStorage *pStorage = info->pCHMInfo->pStorage;
348 IStream *pStream = NULL;
349 HH_WINTYPEW wintype;
350 HRESULT hr;
351 DWORD cbRead;
352 BOOL ret = FALSE;
353
354 static const WCHAR empty[] = {0};
355 static const WCHAR toc_extW[] = {'h','h','c',0};
356 static const WCHAR index_extW[] = {'h','h','k',0};
357 static const WCHAR windowsW[] = {'#','W','I','N','D','O','W','S',0};
358
359 /* HH_WINTYPE as stored on disk. It's identical to HH_WINTYPE except that the pointer fields
360 have been changed to DWORDs, so that the layout on 64-bit remains unchanged. */
361 struct file_wintype
362 {
363 int cbStruct;
364 BOOL fUniCodeStrings;
365 DWORD pszType;
366 DWORD fsValidMembers;
367 DWORD fsWinProperties;
368 DWORD pszCaption;
369 DWORD dwStyles;
370 DWORD dwExStyles;
371 RECT rcWindowPos;
372 int nShowState;
373 DWORD hwndHelp;
374 DWORD hwndCaller;
375 DWORD paInfoTypes;
376 DWORD hwndToolBar;
377 DWORD hwndNavigation;
378 DWORD hwndHTML;
379 int iNavWidth;
380 RECT rcHTML;
381 DWORD pszToc;
382 DWORD pszIndex;
383 DWORD pszFile;
384 DWORD pszHome;
385 DWORD fsToolBarFlags;
386 BOOL fNotExpanded;
387 int curNavType;
388 int tabpos;
389 int idNotify;
390 BYTE tabOrder[HH_MAX_TABS+1];
391 int cHistory;
392 DWORD pszJump1;
393 DWORD pszJump2;
394 DWORD pszUrlJump1;
395 DWORD pszUrlJump2;
396 RECT rcMinSize;
397 int cbInfoTypes;
398 DWORD pszCustomTabs;
399 } file_wintype;
400
401 memset(&wintype, 0, sizeof(wintype));
402 wintype.cbStruct = sizeof(wintype);
403 wintype.fUniCodeStrings = TRUE;
404
405 hr = IStorage_OpenStream(pStorage, windowsW, NULL, STGM_READ, 0, &pStream);
406 if (SUCCEEDED(hr))
407 {
408 /* jump past the #WINDOWS header */
409 liOffset.QuadPart = sizeof(DWORD) * 2;
410
411 hr = IStream_Seek(pStream, liOffset, STREAM_SEEK_SET, NULL);
412 if (FAILED(hr)) goto done;
413
414 /* read the HH_WINTYPE struct data */
415 hr = IStream_Read(pStream, &file_wintype, sizeof(file_wintype), &cbRead);
416 if (FAILED(hr)) goto done;
417
418 /* convert the #STRINGS offsets to actual strings */
419 wintype.pszType = ConvertChmString(info, file_wintype.pszType);
420 wintype.fsValidMembers = file_wintype.fsValidMembers;
421 wintype.fsWinProperties = file_wintype.fsWinProperties;
422 wintype.pszCaption = ConvertChmString(info, file_wintype.pszCaption);
423 wintype.dwStyles = file_wintype.dwStyles;
424 wintype.dwExStyles = file_wintype.dwExStyles;
425 wintype.rcWindowPos = file_wintype.rcWindowPos;
426 wintype.nShowState = file_wintype.nShowState;
427 wintype.iNavWidth = file_wintype.iNavWidth;
428 wintype.rcHTML = file_wintype.rcHTML;
429 wintype.pszToc = ConvertChmString(info, file_wintype.pszToc);
430 wintype.pszIndex = ConvertChmString(info, file_wintype.pszIndex);
431 wintype.pszFile = ConvertChmString(info, file_wintype.pszFile);
432 wintype.pszHome = ConvertChmString(info, file_wintype.pszHome);
433 wintype.fsToolBarFlags = file_wintype.fsToolBarFlags;
434 wintype.fNotExpanded = file_wintype.fNotExpanded;
435 wintype.curNavType = file_wintype.curNavType;
436 wintype.tabpos = file_wintype.tabpos;
437 wintype.idNotify = file_wintype.idNotify;
438 memcpy(&wintype.tabOrder, file_wintype.tabOrder, sizeof(wintype.tabOrder));
439 wintype.cHistory = file_wintype.cHistory;
440 wintype.pszJump1 = ConvertChmString(info, file_wintype.pszJump1);
441 wintype.pszJump2 = ConvertChmString(info, file_wintype.pszJump2);
442 wintype.pszUrlJump1 = ConvertChmString(info, file_wintype.pszUrlJump1);
443 wintype.pszUrlJump2 = ConvertChmString(info, file_wintype.pszUrlJump2);
444 wintype.rcMinSize = file_wintype.rcMinSize;
445 wintype.cbInfoTypes = file_wintype.cbInfoTypes;
446 }
447 else
448 {
449 /* no defined window types so use (hopefully) sane defaults */
450 static const WCHAR defaultwinW[] = {'d','e','f','a','u','l','t','w','i','n','\0'};
451 wintype.pszType = strdupW(info->pCHMInfo->defWindow ? info->pCHMInfo->defWindow : defaultwinW);
452 wintype.pszToc = strdupW(info->pCHMInfo->defToc ? info->pCHMInfo->defToc : empty);
453 wintype.pszIndex = strdupW(empty);
454 wintype.fsValidMembers = 0;
455 wintype.fsWinProperties = HHWIN_PROP_TRI_PANE;
456 wintype.dwStyles = WS_POPUP;
457 wintype.dwExStyles = 0;
458 wintype.nShowState = SW_SHOW;
459 wintype.curNavType = HHWIN_NAVTYPE_TOC;
460 }
461
462 /* merge the new data with any pre-existing HH_WINTYPE structure */
463 MergeChmProperties(&wintype, info, FALSE);
464 if (!info->WinType.pszCaption)
465 #ifdef __REACTOS__
466 info->WinType.pszCaption = info->stringsW.pszCaption = (info->pCHMInfo->defTitle ? strdupW(info->pCHMInfo->defTitle) : HH_LoadString(IDS_DEFTITLE));
467 #else
468 info->WinType.pszCaption = info->stringsW.pszCaption = strdupW(info->pCHMInfo->defTitle ? info->pCHMInfo->defTitle : empty);
469 #endif
470 if (!info->WinType.pszFile)
471 info->WinType.pszFile = info->stringsW.pszFile = strdupW(info->pCHMInfo->defTopic ? info->pCHMInfo->defTopic : empty);
472 if (!info->WinType.pszToc)
473 info->WinType.pszToc = info->stringsW.pszToc = FindHTMLHelpSetting(info, toc_extW);
474 if (!info->WinType.pszIndex)
475 info->WinType.pszIndex = info->stringsW.pszIndex = FindHTMLHelpSetting(info, index_extW);
476
477 wintype_free(&wintype);
478 ret = TRUE;
479
480 done:
481 if (pStream)
482 IStream_Release(pStream);
483
484 return ret;
485 }
486
skip_schema(LPCWSTR url)487 LPCWSTR skip_schema(LPCWSTR url)
488 {
489 static const WCHAR its_schema[] = {'i','t','s',':'};
490 static const WCHAR msits_schema[] = {'m','s','-','i','t','s',':'};
491 static const WCHAR mk_schema[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':'};
492
493 if(!_wcsnicmp(its_schema, url, ARRAY_SIZE(its_schema)))
494 return url + ARRAY_SIZE(its_schema);
495 if(!_wcsnicmp(msits_schema, url, ARRAY_SIZE(msits_schema)))
496 return url + ARRAY_SIZE(msits_schema);
497 if(!_wcsnicmp(mk_schema, url, ARRAY_SIZE(mk_schema)))
498 return url + ARRAY_SIZE(mk_schema);
499
500 return url;
501 }
502
SetChmPath(ChmPath * file,LPCWSTR base_file,LPCWSTR path)503 void SetChmPath(ChmPath *file, LPCWSTR base_file, LPCWSTR path)
504 {
505 LPCWSTR ptr;
506 static const WCHAR separatorW[] = {':',':',0};
507
508 path = skip_schema(path);
509
510 ptr = wcsstr(path, separatorW);
511 if(ptr) {
512 WCHAR chm_file[MAX_PATH];
513 WCHAR rel_path[MAX_PATH];
514 WCHAR base_path[MAX_PATH];
515 LPWSTR p;
516
517 lstrcpyW(base_path, base_file);
518 p = wcsrchr(base_path, '\\');
519 if(p)
520 *p = 0;
521
522 memcpy(rel_path, path, (ptr-path)*sizeof(WCHAR));
523 rel_path[ptr-path] = 0;
524
525 PathCombineW(chm_file, base_path, rel_path);
526
527 file->chm_file = strdupW(chm_file);
528 ptr += 2;
529 }else {
530 file->chm_file = strdupW(base_file);
531 ptr = path;
532 }
533
534 file->chm_index = strdupW(ptr);
535
536 TRACE("ChmFile = {%s %s}\n", debugstr_w(file->chm_file), debugstr_w(file->chm_index));
537 }
538
GetChmStream(CHMInfo * info,LPCWSTR parent_chm,ChmPath * chm_file)539 IStream *GetChmStream(CHMInfo *info, LPCWSTR parent_chm, ChmPath *chm_file)
540 {
541 IStorage *storage;
542 IStream *stream = NULL;
543 HRESULT hres;
544
545 TRACE("%s (%s :: %s)\n", debugstr_w(parent_chm), debugstr_w(chm_file->chm_file),
546 debugstr_w(chm_file->chm_index));
547
548 if(parent_chm || chm_file->chm_file) {
549 hres = IITStorage_StgOpenStorage(info->pITStorage,
550 chm_file->chm_file ? chm_file->chm_file : parent_chm, NULL,
551 STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &storage);
552 if(FAILED(hres)) {
553 WARN("Could not open storage: %08x\n", hres);
554 return NULL;
555 }
556 }else {
557 storage = info->pStorage;
558 IStorage_AddRef(info->pStorage);
559 }
560
561 hres = IStorage_OpenStream(storage, chm_file->chm_index, NULL, STGM_READ, 0, &stream);
562 IStorage_Release(storage);
563 if(FAILED(hres))
564 WARN("Could not open stream: %08x\n", hres);
565
566 return stream;
567 }
568
569 /*
570 * Retrieve a CHM document and parse the data from the <title> element to get the document's title.
571 */
GetDocumentTitle(CHMInfo * info,LPCWSTR document)572 WCHAR *GetDocumentTitle(CHMInfo *info, LPCWSTR document)
573 {
574 strbuf_t node, node_name, content;
575 WCHAR *document_title = NULL;
576 IStream *str = NULL;
577 IStorage *storage;
578 stream_t stream;
579 HRESULT hres;
580
581 TRACE("%s\n", debugstr_w(document));
582
583 storage = info->pStorage;
584 if(!storage) {
585 WARN("Could not open storage to obtain the title for a document.\n");
586 return NULL;
587 }
588 IStorage_AddRef(storage);
589
590 hres = IStorage_OpenStream(storage, document, NULL, STGM_READ, 0, &str);
591 IStorage_Release(storage);
592 if(FAILED(hres))
593 WARN("Could not open stream: %08x\n", hres);
594
595 stream_init(&stream, str);
596 strbuf_init(&node);
597 strbuf_init(&content);
598 strbuf_init(&node_name);
599
600 while(next_node(&stream, &node)) {
601 get_node_name(&node, &node_name);
602
603 TRACE("%s\n", node.buf);
604
605 if(!_strnicmp(node_name.buf, "title", -1)) {
606 if(next_content(&stream, &content) && content.len > 1)
607 {
608 document_title = strdupnAtoW(&content.buf[1], content.len-1);
609 FIXME("magic: %s\n", debugstr_w(document_title));
610 break;
611 }
612 }
613
614 strbuf_zero(&node);
615 }
616
617 strbuf_free(&node);
618 strbuf_free(&content);
619 strbuf_free(&node_name);
620 IStream_Release(str);
621
622 return document_title;
623 }
624
625 /* Opens the CHM file for reading */
OpenCHM(LPCWSTR szFile)626 CHMInfo *OpenCHM(LPCWSTR szFile)
627 {
628 HRESULT hres;
629 CHMInfo *ret;
630
631 static const WCHAR wszSTRINGS[] = {'#','S','T','R','I','N','G','S',0};
632
633 if (!(ret = heap_alloc_zero(sizeof(CHMInfo))))
634 return NULL;
635 ret->codePage = CP_ACP;
636
637 if (!(ret->szFile = strdupW(szFile))) {
638 heap_free(ret);
639 return NULL;
640 }
641
642 hres = CoCreateInstance(&CLSID_ITStorage, NULL, CLSCTX_INPROC_SERVER,
643 &IID_IITStorage, (void **) &ret->pITStorage) ;
644 if(FAILED(hres)) {
645 WARN("Could not create ITStorage: %08x\n", hres);
646 return CloseCHM(ret);
647 }
648
649 hres = IITStorage_StgOpenStorage(ret->pITStorage, szFile, NULL,
650 STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &ret->pStorage);
651 if(FAILED(hres)) {
652 WARN("Could not open storage: %08x\n", hres);
653 return CloseCHM(ret);
654 }
655 hres = IStorage_OpenStream(ret->pStorage, wszSTRINGS, NULL, STGM_READ, 0,
656 &ret->strings_stream);
657 if(FAILED(hres)) {
658 WARN("Could not open #STRINGS stream: %08x\n", hres);
659 /* It's not critical, so we pass */
660 }
661
662 if(!ReadChmSystem(ret)) {
663 WARN("Could not read #SYSTEM\n");
664 return CloseCHM(ret);
665 }
666
667 return ret;
668 }
669
CloseCHM(CHMInfo * chm)670 CHMInfo *CloseCHM(CHMInfo *chm)
671 {
672 if(chm->pITStorage)
673 IITStorage_Release(chm->pITStorage);
674
675 if(chm->pStorage)
676 IStorage_Release(chm->pStorage);
677
678 if(chm->strings_stream)
679 IStream_Release(chm->strings_stream);
680
681 if(chm->strings_size) {
682 DWORD i;
683
684 for(i=0; i<chm->strings_size; i++)
685 heap_free(chm->strings[i]);
686 }
687
688 heap_free(chm->strings);
689 heap_free(chm->defWindow);
690 heap_free(chm->defTitle);
691 heap_free(chm->defTopic);
692 heap_free(chm->defToc);
693 heap_free(chm->szFile);
694 heap_free(chm->compiledFile);
695 heap_free(chm);
696
697 return NULL;
698 }
699