winhelp: There are files without Phrases or Phrases40 compression, add support for...
[wine.git] / programs / winhelp / hlpfile.c
blobf9013e65cae96651ec5f7acdab3888f0ea969864
1 /*
2 * Help Viewer
4 * Copyright 1996 Ulrich Schmid
5 * 2002 Eric Pouech
6 * 2007 Kirill K. Smirnov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "winhelp.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
37 static inline unsigned short GET_USHORT(const BYTE* buffer, unsigned i)
39 return (BYTE)buffer[i] + 0x100 * (BYTE)buffer[i + 1];
42 static inline short GET_SHORT(const BYTE* buffer, unsigned i)
44 return (BYTE)buffer[i] + 0x100 * (signed char)buffer[i+1];
47 static inline unsigned GET_UINT(const BYTE* buffer, unsigned i)
49 return GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i + 2);
52 static HLPFILE *first_hlpfile = 0;
53 static BYTE *file_buffer;
54 static UINT file_buffer_size;
56 static struct
58 UINT num;
59 unsigned* offsets;
60 char* buffer;
61 } phrases;
63 static struct
65 BYTE** map;
66 BYTE* end;
67 UINT wMapLen;
68 } topic;
70 static struct
72 UINT wFont;
73 UINT wIndent;
74 UINT wHSpace;
75 UINT wVSpace;
76 HLPFILE_LINK* link;
77 } attributes;
80 * Compare function type for HLPFILE_BPTreeSearch function.
82 * PARAMS
83 * p [I] pointer to testing block (key + data)
84 * key [I] pointer to key value to look for
85 * leaf [I] whether this function called for index of leaf page
86 * next [O] pointer to pointer to next block
88 typedef int (*HLPFILE_BPTreeCompare)(void *p, const void *key,
89 int leaf, void **next);
91 static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR);
92 static BOOL HLPFILE_ReadFileToBuffer(HFILE);
93 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**);
94 static BOOL HLPFILE_SystemCommands(HLPFILE*);
95 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end);
96 static BYTE* HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr);
97 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE*);
98 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE*);
99 static BOOL HLPFILE_Uncompress_Topic(HLPFILE*);
100 static BOOL HLPFILE_GetContext(HLPFILE*);
101 static BOOL HLPFILE_GetMap(HLPFILE*);
102 static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*, unsigned);
103 static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*, unsigned*);
104 static void HLPFILE_Uncompress2(const BYTE*, const BYTE*, BYTE*, const BYTE*);
105 static BOOL HLPFILE_Uncompress3(char*, const char*, const BYTE*, const BYTE*);
106 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz);
107 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile);
109 static void* HLPFILE_BPTreeSearch(BYTE*, const void*, HLPFILE_BPTreeCompare);
111 /***********************************************************************
113 * HLPFILE_PageByNumber
115 static HLPFILE_PAGE *HLPFILE_PageByNumber(HLPFILE* hlpfile, UINT wNum)
117 HLPFILE_PAGE *page;
118 UINT temp = wNum;
120 WINE_TRACE("<%s>[%u]\n", hlpfile->lpszPath, wNum);
122 for (page = hlpfile->first_page; page && temp; page = page->next) temp--;
123 if (!page)
124 WINE_ERR("Page of number %u not found in file %s\n", wNum, hlpfile->lpszPath);
125 return page;
128 /* FIXME:
129 * this finds the page containing the offset. The offset can either
130 * refer to the top of the page (offset == page->offset), or
131 * to some paragraph inside the page...
132 * As of today, we only return the page... we should also return
133 * a paragraph, and then, while opening a new page, compute the
134 * y-offset of the paragraph to be shown and scroll the window
135 * accordinly
137 /******************************************************************
138 * HLPFILE_PageByOffset
142 HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset)
144 HLPFILE_PAGE* page;
145 HLPFILE_PAGE* found;
147 if (!hlpfile) return 0;
149 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, offset);
151 if (offset == 0xFFFFFFFF) return NULL;
152 page = NULL;
154 for (found = NULL, page = hlpfile->first_page; page; page = page->next)
156 if (page->offset <= offset && (!found || found->offset < page->offset))
157 found = page;
159 if (!found)
160 WINE_ERR("Page of offset %u not found in file %s\n",
161 offset, hlpfile->lpszPath);
162 return found;
165 /**************************************************************************
166 * comp_PageByHash
168 * HLPFILE_BPTreeCompare function for '|CONTEXT' B+ tree file
171 static int comp_PageByHash(void *p, const void *key,
172 int leaf, void** next)
174 LONG lKey = (LONG)key;
175 LONG lTest = GET_UINT(p, 0);
177 *next = (char *)p+(leaf?8:6);
178 WINE_TRACE("Comparing '%u' with '%u'\n", lKey, lTest);
179 if (lTest < lKey) return -1;
180 if (lTest > lKey) return 1;
181 return 0;
184 /***********************************************************************
186 * HLPFILE_HlpFilePageByHash
188 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash)
190 BYTE *ptr;
192 if (!hlpfile) return 0;
194 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lHash);
196 /* For win 3.0 files hash values are really page numbers */
197 if (hlpfile->version <= 16)
198 return HLPFILE_PageByNumber(hlpfile, lHash);
200 ptr = HLPFILE_BPTreeSearch(hlpfile->Context, (void*)lHash, comp_PageByHash);
201 if (!ptr)
203 WINE_ERR("Page of hash %x not found in file %s\n", lHash, hlpfile->lpszPath);
204 return NULL;
207 return HLPFILE_PageByOffset(hlpfile, GET_UINT(ptr, 4));
210 /***********************************************************************
212 * HLPFILE_PageByMap
214 HLPFILE_PAGE *HLPFILE_PageByMap(HLPFILE* hlpfile, LONG lMap)
216 unsigned int i;
218 if (!hlpfile) return 0;
220 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lMap);
222 for (i = 0; i < hlpfile->wMapLen; i++)
224 if (hlpfile->Map[i].lMap == lMap)
225 return HLPFILE_PageByOffset(hlpfile, hlpfile->Map[i].offset);
228 WINE_ERR("Page of Map %x not found in file %s\n", lMap, hlpfile->lpszPath);
229 return NULL;
232 /***********************************************************************
234 * HLPFILE_Contents
236 HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile)
238 HLPFILE_PAGE* page = NULL;
240 if (!hlpfile) return NULL;
242 page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start);
243 if (!page) page = hlpfile->first_page;
244 return page;
247 /***********************************************************************
249 * HLPFILE_Hash
251 LONG HLPFILE_Hash(LPCSTR lpszContext)
253 LONG lHash = 0;
254 CHAR c;
256 while ((c = *lpszContext++))
258 CHAR x = 0;
259 if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
260 if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
261 if (c >= '1' && c <= '9') x = c - '0';
262 if (c == '0') x = 10;
263 if (c == '.') x = 12;
264 if (c == '_') x = 13;
265 if (x) lHash = lHash * 43 + x;
267 return lHash;
270 /***********************************************************************
272 * HLPFILE_ReadHlpFile
274 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
276 HLPFILE* hlpfile;
278 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
280 if (!strcmp(lpszPath, hlpfile->lpszPath))
282 hlpfile->wRefCount++;
283 return hlpfile;
287 hlpfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
288 sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
289 if (!hlpfile) return 0;
291 hlpfile->lpszPath = (char*)hlpfile + sizeof(HLPFILE);
292 hlpfile->contents_start = 0xFFFFFFFF;
293 hlpfile->next = first_hlpfile;
294 hlpfile->wRefCount = 1;
296 strcpy(hlpfile->lpszPath, lpszPath);
298 first_hlpfile = hlpfile;
299 if (hlpfile->next) hlpfile->next->prev = hlpfile;
301 phrases.offsets = NULL;
302 phrases.buffer = NULL;
303 topic.map = NULL;
304 topic.end = NULL;
305 file_buffer = NULL;
307 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
309 HLPFILE_FreeHlpFile(hlpfile);
310 hlpfile = 0;
313 HeapFree(GetProcessHeap(), 0, phrases.offsets);
314 HeapFree(GetProcessHeap(), 0, phrases.buffer);
315 HeapFree(GetProcessHeap(), 0, topic.map);
316 HeapFree(GetProcessHeap(), 0, file_buffer);
318 return hlpfile;
321 /***********************************************************************
323 * HLPFILE_DoReadHlpFile
325 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
327 BOOL ret;
328 HFILE hFile;
329 OFSTRUCT ofs;
330 BYTE* buf;
331 DWORD ref = 0x0C;
332 unsigned index, old_index, offset, len, offs;
334 hFile = OpenFile(lpszPath, &ofs, OF_READ);
335 if (hFile == HFILE_ERROR) return FALSE;
337 ret = HLPFILE_ReadFileToBuffer(hFile);
338 _lclose(hFile);
339 if (!ret) return FALSE;
341 if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
343 /* load phrases support */
344 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile))
345 HLPFILE_Uncompress_Phrases40(hlpfile);
347 if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE;
348 if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
350 buf = topic.map[0];
351 old_index = -1;
352 offs = 0;
355 BYTE* end;
357 index = (ref - 0x0C) / hlpfile->dsize;
358 offset = (ref - 0x0C) % hlpfile->dsize;
360 if (hlpfile->version <= 16 && index != old_index && index != 0)
362 /* we jumped to the next block, adjust pointers */
363 ref -= 12;
364 offset -= 12;
367 WINE_TRACE("ref=%08x => [%u/%u]\n", ref, index, offset);
369 if (index >= topic.wMapLen) {WINE_WARN("maplen\n"); break;}
370 buf = topic.map[index] + offset;
371 if (buf + 0x15 >= topic.end) {WINE_WARN("extra\n"); break;}
372 end = min(buf + GET_UINT(buf, 0), topic.end);
373 if (index != old_index) {offs = 0; old_index = index;}
375 switch (buf[0x14])
377 case 0x02:
378 if (!HLPFILE_AddPage(hlpfile, buf, end, index * 0x8000L + offs)) return FALSE;
379 break;
381 case 0x01:
382 case 0x20:
383 case 0x23:
384 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
385 offs += len;
386 break;
388 default:
389 WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
392 if (hlpfile->version <= 16)
394 ref += GET_UINT(buf, 0xc);
395 if (GET_UINT(buf, 0xc) == 0)
396 break;
398 else
399 ref = GET_UINT(buf, 0xc);
400 } while (ref != 0xffffffff);
402 HLPFILE_GetMap(hlpfile);
403 if (hlpfile->version <= 16) return TRUE;
404 return HLPFILE_GetContext(hlpfile);
407 /***********************************************************************
409 * HLPFILE_AddPage
411 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned offset)
413 HLPFILE_PAGE* page;
414 BYTE* title;
415 UINT titlesize, blocksize, datalen;
416 char* ptr;
417 HLPFILE_MACRO*macro;
419 blocksize = GET_UINT(buf, 0);
420 datalen = GET_UINT(buf, 0x10);
421 title = buf + datalen;
422 if (title > end) {WINE_WARN("page2\n"); return FALSE;};
424 titlesize = GET_UINT(buf, 4);
425 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
426 if (!page) return FALSE;
427 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
429 if (titlesize > blocksize - datalen)
431 /* need to decompress */
432 if (hlpfile->hasPhrases)
433 HLPFILE_Uncompress2(title, end, (BYTE*)page->lpszTitle, (BYTE*)page->lpszTitle + titlesize);
434 else if (hlpfile->hasPhrases40)
435 HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize, title, end);
436 else
438 WINE_FIXME("Text size is too long, splitting\n");
439 titlesize = blocksize - datalen;
440 memcpy(page->lpszTitle, title, titlesize);
443 else
444 memcpy(page->lpszTitle, title, titlesize);
446 page->lpszTitle[titlesize] = '\0';
448 if (hlpfile->first_page)
450 HLPFILE_PAGE *p;
452 for (p = hlpfile->first_page; p->next; p = p->next);
453 page->prev = p;
454 p->next = page;
456 else
458 hlpfile->first_page = page;
459 page->prev = NULL;
462 page->file = hlpfile;
463 page->next = NULL;
464 page->first_paragraph = NULL;
465 page->first_macro = NULL;
466 page->wNumber = GET_UINT(buf, 0x21);
467 page->offset = offset;
469 page->browse_bwd = GET_UINT(buf, 0x19);
470 page->browse_fwd = GET_UINT(buf, 0x1D);
472 WINE_TRACE("Added page[%d]: title='%s' %08x << %08x >> %08x\n",
473 page->wNumber, page->lpszTitle,
474 page->browse_bwd, page->offset, page->browse_fwd);
476 memset(&attributes, 0, sizeof(attributes));
478 /* now load macros */
479 ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
480 while (ptr < page->lpszTitle + titlesize)
482 unsigned len = strlen(ptr);
483 char* macro_str;
485 WINE_TRACE("macro: %s\n", ptr);
486 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
487 macro->lpszMacro = macro_str = (char*)(macro + 1);
488 memcpy(macro_str, ptr, len + 1);
489 /* FIXME: shall we really link macro in reverse order ??
490 * may produce strange results when played at page opening
492 macro->next = page->first_macro;
493 page->first_macro = macro;
494 ptr += len + 1;
497 return TRUE;
500 static long fetch_long(BYTE** ptr)
502 long ret;
504 if (*(*ptr) & 1)
506 ret = (*(unsigned long*)(*ptr) - 0x80000000L) / 2;
507 (*ptr) += 4;
509 else
511 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
512 (*ptr) += 2;
515 return ret;
518 static unsigned long fetch_ulong(BYTE** ptr)
520 unsigned long ret;
522 if (*(*ptr) & 1)
524 ret = *(unsigned long*)(*ptr) / 2;
525 (*ptr) += 4;
527 else
529 ret = *(unsigned short*)(*ptr) / 2;
530 (*ptr) += 2;
532 return ret;
535 static short fetch_short(BYTE** ptr)
537 short ret;
539 if (*(*ptr) & 1)
541 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
542 (*ptr) += 2;
544 else
546 ret = (*(unsigned char*)(*ptr) - 0x80) / 2;
547 (*ptr)++;
549 return ret;
552 static unsigned short fetch_ushort(BYTE** ptr)
554 unsigned short ret;
556 if (*(*ptr) & 1)
558 ret = *(unsigned short*)(*ptr) / 2;
559 (*ptr) += 2;
561 else
563 ret = *(unsigned char*)(*ptr) / 2;
564 (*ptr)++;
566 return ret;
569 /******************************************************************
570 * HLPFILE_DecompressGfx
572 * Decompress the data part of a bitmap or a metafile
574 static BYTE* HLPFILE_DecompressGfx(BYTE* src, unsigned csz, unsigned sz, BYTE packing)
576 BYTE* dst;
577 BYTE* tmp;
578 BYTE* tmp2;
579 unsigned sz77;
581 WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing, csz, sz);
583 switch (packing)
585 case 0: /* uncompressed */
586 if (sz != csz)
587 WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz, csz);
588 dst = src;
589 break;
590 case 1: /* RunLen */
591 tmp = dst = HeapAlloc(GetProcessHeap(), 0, sz);
592 if (!dst) return NULL;
593 HLPFILE_UncompressRLE(src, src + csz, &tmp, sz);
594 if (tmp - dst != sz)
595 WINE_WARN("Bogus gfx sizes (RunLen): %u/%u\n", tmp - dst, sz);
596 break;
597 case 2: /* LZ77 */
598 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
599 dst = HeapAlloc(GetProcessHeap(), 0, sz77);
600 if (!dst) return NULL;
601 HLPFILE_UncompressLZ77(src, src + csz, dst);
602 if (sz77 != sz)
603 WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77, sz);
604 break;
605 case 3: /* LZ77 then RLE */
606 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
607 tmp = HeapAlloc(GetProcessHeap(), 0, sz77);
608 if (!tmp) return FALSE;
609 HLPFILE_UncompressLZ77(src, src + csz, tmp);
610 dst = tmp2 = HeapAlloc(GetProcessHeap(), 0, sz);
611 if (!dst)
613 HeapFree(GetProcessHeap(), 0, tmp);
614 return FALSE;
616 HLPFILE_UncompressRLE(tmp, tmp + sz77, &tmp2, sz);
617 if (tmp2 - dst != sz)
618 WINE_WARN("Bogus gfx sizes (LZ77+RunLen): %u / %u\n", tmp2 - dst, sz);
619 HeapFree(GetProcessHeap(), 0, tmp);
620 break;
621 default:
622 WINE_FIXME("Unsupported packing %u\n", packing);
623 return NULL;
625 return dst;
628 /******************************************************************
629 * HLPFILE_LoadBitmap
633 static BOOL HLPFILE_LoadBitmap(BYTE* beg, BYTE type, BYTE pack,
634 HLPFILE_PARAGRAPH* paragraph)
636 BYTE* ptr;
637 BYTE* pict_beg;
638 BITMAPINFO* bi;
639 unsigned long off, csz;
640 HDC hdc;
642 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
643 if (!bi) return FALSE;
645 ptr = beg + 2; /* for type and pack */
647 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
648 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
649 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
650 bi->bmiHeader.biPlanes = fetch_ushort(&ptr);
651 bi->bmiHeader.biBitCount = fetch_ushort(&ptr);
652 bi->bmiHeader.biWidth = fetch_ulong(&ptr);
653 bi->bmiHeader.biHeight = fetch_ulong(&ptr);
654 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr);
655 bi->bmiHeader.biClrImportant = fetch_ulong(&ptr);
656 bi->bmiHeader.biCompression = BI_RGB;
657 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
658 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
659 bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
660 WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n",
661 bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount,
662 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
664 csz = fetch_ulong(&ptr);
665 fetch_ulong(&ptr); /* hotspot size */
667 off = GET_UINT(ptr, 0); ptr += 4;
668 /* GET_UINT(ptr, 0); hotspot offset */ ptr += 4;
670 /* now read palette info */
671 if (type == 0x06)
673 unsigned nc = bi->bmiHeader.biClrUsed;
674 unsigned i;
676 /* not quite right, especially for bitfields type of compression */
677 if (!nc && bi->bmiHeader.biBitCount <= 8)
678 nc = 1 << bi->bmiHeader.biBitCount;
680 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
681 if (!bi) return FALSE;
682 for (i = 0; i < nc; i++)
684 bi->bmiColors[i].rgbBlue = ptr[0];
685 bi->bmiColors[i].rgbGreen = ptr[1];
686 bi->bmiColors[i].rgbRed = ptr[2];
687 bi->bmiColors[i].rgbReserved = 0;
688 ptr += 4;
691 pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack);
693 paragraph->u.gfx.u.bmp.hBitmap = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader,
694 CBM_INIT, pict_beg,
695 bi, DIB_RGB_COLORS);
696 ReleaseDC(0, hdc);
697 if (!paragraph->u.gfx.u.bmp.hBitmap)
698 WINE_ERR("Couldn't create bitmap\n");
700 HeapFree(GetProcessHeap(), 0, bi);
701 if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
703 return TRUE;
706 /******************************************************************
707 * HLPFILE_LoadMetaFile
711 static BOOL HLPFILE_LoadMetaFile(BYTE* beg, BYTE pack, HLPFILE_PARAGRAPH* paragraph)
713 BYTE* ptr;
714 unsigned long size, csize;
715 unsigned long off, hsoff;
716 BYTE* bits;
717 LPMETAFILEPICT lpmfp;
719 WINE_TRACE("Loading metafile\n");
721 ptr = beg + 2; /* for type and pack */
723 lpmfp = &paragraph->u.gfx.u.mfp;
724 lpmfp->mm = fetch_ushort(&ptr); /* mapping mode */
726 lpmfp->xExt = GET_USHORT(ptr, 0);
727 lpmfp->yExt = GET_USHORT(ptr, 2);
728 ptr += 4;
730 size = fetch_ulong(&ptr); /* decompressed size */
731 csize = fetch_ulong(&ptr); /* compressed size */
732 fetch_ulong(&ptr); /* hotspot size */
733 off = GET_UINT(ptr, 0);
734 hsoff = GET_UINT(ptr, 4);
735 ptr += 8;
737 WINE_TRACE("sz=%lu csz=%lu (%d,%d) offs=%lu/%u,%lu\n",
738 size, csize, lpmfp->xExt, lpmfp->yExt, off, ptr - beg, hsoff);
740 bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack);
741 if (!bits) return FALSE;
743 paragraph->cookie = para_metafile;
745 lpmfp->hMF = SetMetaFileBitsEx(size, bits);
747 if (!lpmfp->hMF)
748 WINE_FIXME("Couldn't load metafile\n");
750 if (bits != beg + off) HeapFree(GetProcessHeap(), 0, bits);
752 return TRUE;
755 /******************************************************************
756 * HLPFILE_LoadGfxByAddr
760 static BOOL HLPFILE_LoadGfxByAddr(HLPFILE *hlpfile, BYTE* ref,
761 unsigned long size,
762 HLPFILE_PARAGRAPH* paragraph)
764 unsigned i, numpict;
766 numpict = GET_USHORT(ref, 2);
767 WINE_TRACE("Got picture magic=%04x #=%d\n",
768 GET_USHORT(ref, 0), numpict);
770 for (i = 0; i < numpict; i++)
772 BYTE* beg;
773 BYTE* ptr;
774 BYTE type, pack;
776 WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4));
777 beg = ptr = ref + GET_UINT(ref, (1 + i) * 4);
779 type = *ptr++;
780 pack = *ptr++;
782 switch (type)
784 case 5: /* device dependent bmp */
785 case 6: /* device independent bmp */
786 HLPFILE_LoadBitmap(beg, type, pack, paragraph);
787 break;
788 case 8:
789 HLPFILE_LoadMetaFile(beg, pack, paragraph);
790 break;
791 default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
794 /* FIXME: hotspots */
796 /* FIXME: implement support for multiple picture format */
797 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
798 break;
800 return TRUE;
803 /******************************************************************
804 * HLPFILE_LoadGfxByIndex
808 static BOOL HLPFILE_LoadGfxByIndex(HLPFILE *hlpfile, unsigned index,
809 HLPFILE_PARAGRAPH* paragraph)
811 char tmp[16];
812 BYTE *ref, *end;
813 BOOL ret;
815 WINE_TRACE("Loading picture #%d\n", index);
817 if (index < hlpfile->numBmps && hlpfile->bmps[index] != NULL)
819 paragraph->u.gfx.u.bmp.hBitmap = hlpfile->bmps[index];
820 return TRUE;
823 sprintf(tmp, "|bm%u", index);
825 if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
827 ref += 9;
829 ret = HLPFILE_LoadGfxByAddr(hlpfile, ref, end - ref, paragraph);
831 /* cache bitmap */
832 if (ret && paragraph->cookie == para_bitmap)
834 if (index >= hlpfile->numBmps)
836 hlpfile->numBmps = index + 1;
837 if (hlpfile->bmps)
838 hlpfile->bmps = HeapReAlloc(GetProcessHeap(), 0, hlpfile->bmps,
839 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
840 else
841 hlpfile->bmps = HeapAlloc(GetProcessHeap(), 0,
842 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
845 hlpfile->bmps[index] = paragraph->u.gfx.u.bmp.hBitmap;
847 return ret;
850 /******************************************************************
851 * HLPFILE_AllocLink
855 static HLPFILE_LINK* HLPFILE_AllocLink(int cookie, const char* str, LONG hash,
856 BOOL clrChange, unsigned wnd)
858 HLPFILE_LINK* link;
859 char* link_str;
861 /* FIXME: should build a string table for the attributes.link.lpszPath
862 * they are reallocated for each link
864 link = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_LINK) + strlen(str) + 1);
865 if (!link) return NULL;
867 link->cookie = cookie;
868 link->lpszString = link_str = (char*)link + sizeof(HLPFILE_LINK);
869 strcpy(link_str, str);
870 link->lHash = hash;
871 link->bClrChange = clrChange ? 1 : 0;
872 link->window = wnd;
873 link->wRefCount = 1;
875 WINE_TRACE("Link[%d] to %s@%08x:%d\n",
876 link->cookie, link->lpszString,
877 link->lHash, link->window);
878 return link;
881 /***********************************************************************
883 * HLPFILE_AddParagraph
885 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
887 HLPFILE_PAGE *page;
888 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
889 UINT textsize;
890 BYTE *format, *format_end;
891 char *text, *text_base, *text_end;
892 long size, blocksize, datalen;
893 unsigned short bits;
894 unsigned nc, ncol = 1;
896 if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;};
898 for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
899 for (paragraphptr = &page->first_paragraph; *paragraphptr;
900 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
902 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
904 blocksize = GET_UINT(buf, 0);
905 size = GET_UINT(buf, 0x4);
906 datalen = GET_UINT(buf, 0x10);
907 text = text_base = HeapAlloc(GetProcessHeap(), 0, size);
908 if (!text) return FALSE;
909 if (size > blocksize - datalen)
911 /* need to decompress */
912 if (hlpfile->hasPhrases)
913 HLPFILE_Uncompress2(buf + datalen, end, (BYTE*)text, (BYTE*)text + size);
914 else if (hlpfile->hasPhrases40)
915 HLPFILE_Uncompress3(text, text + size, buf + datalen, end);
916 else
918 WINE_FIXME("Text size is too long, splitting\n");
919 size = blocksize - datalen;
920 memcpy(text, buf + datalen, size);
923 else
924 memcpy(text, buf + datalen, size);
926 text_end = text + size;
928 format = buf + 0x15;
929 format_end = buf + GET_UINT(buf, 0x10);
931 if (buf[0x14] == 0x20 || buf[0x14] == 0x23)
933 fetch_long(&format);
934 *len = fetch_ushort(&format);
936 else *len = end-buf-15;
938 if (buf[0x14] == 0x23)
940 char type;
942 ncol = *format++;
944 WINE_TRACE("#cols %u\n", ncol);
945 type = *format++;
946 if (type == 0 || type == 2)
947 format += 2;
948 format += ncol * 4;
951 for (nc = 0; nc < ncol; nc++)
953 WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
954 if (buf[0x14] == 0x23)
955 format += 5;
956 if (buf[0x14] == 0x01)
957 format += 6;
958 else
959 format += 4;
960 bits = GET_USHORT(format, 0); format += 2;
961 if (bits & 0x0001) fetch_long(&format);
962 if (bits & 0x0002) fetch_short(&format);
963 if (bits & 0x0004) fetch_short(&format);
964 if (bits & 0x0008) fetch_short(&format);
965 if (bits & 0x0010) fetch_short(&format);
966 if (bits & 0x0020) fetch_short(&format);
967 if (bits & 0x0040) fetch_short(&format);
968 if (bits & 0x0100) format += 3;
969 if (bits & 0x0200)
971 int ntab = fetch_short(&format);
972 unsigned short ts;
974 while (ntab-- > 0)
976 ts = fetch_ushort(&format);
977 if (ts & 0x4000) fetch_ushort(&format);
980 /* 0x0400, 0x0800 and 0x1000 don't need space */
981 if ((bits & 0xE080) != 0)
982 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
984 while (text < text_end && format < format_end)
986 WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text), text, text_end, format, format_end);
987 textsize = strlen(text) + 1;
988 if (textsize > 1)
990 paragraph = HeapAlloc(GetProcessHeap(), 0,
991 sizeof(HLPFILE_PARAGRAPH) + textsize);
992 if (!paragraph) return FALSE;
993 *paragraphptr = paragraph;
994 paragraphptr = &paragraph->next;
996 paragraph->next = NULL;
997 paragraph->link = attributes.link;
998 if (paragraph->link) paragraph->link->wRefCount++;
999 paragraph->cookie = para_normal_text;
1000 paragraph->u.text.wFont = attributes.wFont;
1001 paragraph->u.text.wVSpace = attributes.wVSpace;
1002 paragraph->u.text.wHSpace = attributes.wHSpace;
1003 paragraph->u.text.wIndent = attributes.wIndent;
1004 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
1005 strcpy(paragraph->u.text.lpszText, text);
1007 attributes.wVSpace = 0;
1008 attributes.wHSpace = 0;
1010 /* else: null text, keep on storing attributes */
1011 text += textsize;
1013 if (*format == 0xff)
1015 format++;
1016 break;
1019 WINE_TRACE("format=%02x\n", *format);
1020 switch (*format)
1022 case 0x20:
1023 WINE_FIXME("NIY20\n");
1024 format += 5;
1025 break;
1027 case 0x21:
1028 WINE_FIXME("NIY21\n");
1029 format += 3;
1030 break;
1032 case 0x80:
1033 attributes.wFont = GET_USHORT(format, 1);
1034 WINE_TRACE("Changing font to %d\n", attributes.wFont);
1035 format += 3;
1036 break;
1038 case 0x81:
1039 attributes.wVSpace++;
1040 format += 1;
1041 break;
1043 case 0x82:
1044 attributes.wVSpace++;
1045 attributes.wIndent = 0;
1046 format += 1;
1047 break;
1049 case 0x83:
1050 attributes.wIndent++;
1051 format += 1;
1052 break;
1054 #if 0
1055 case 0x84:
1056 format += 3;
1057 break;
1058 #endif
1060 case 0x86:
1061 case 0x87:
1062 case 0x88:
1064 BYTE pos = (*format - 0x86);
1065 BYTE type = format[1];
1066 long size;
1068 format += 2;
1069 size = fetch_long(&format);
1071 paragraph = HeapAlloc(GetProcessHeap(), 0,
1072 sizeof(HLPFILE_PARAGRAPH) + textsize);
1073 if (!paragraph) return FALSE;
1074 *paragraphptr = paragraph;
1075 paragraphptr = &paragraph->next;
1077 paragraph->next = NULL;
1078 paragraph->link = attributes.link;
1079 if (paragraph->link) paragraph->link->wRefCount++;
1080 paragraph->cookie = para_bitmap;
1081 paragraph->u.gfx.pos = pos;
1082 switch (type)
1084 case 0x22:
1085 fetch_ushort(&format); /* hot spot */
1086 /* fall thru */
1087 case 0x03:
1088 switch (GET_SHORT(format, 0))
1090 case 0:
1091 HLPFILE_LoadGfxByIndex(hlpfile, GET_SHORT(format, 2),
1092 paragraph);
1093 break;
1094 case 1:
1095 WINE_FIXME("does it work ??? %x<%lu>#%u\n",
1096 GET_SHORT(format, 0),
1097 size, GET_SHORT(format, 2));
1098 HLPFILE_LoadGfxByAddr(hlpfile, format + 2, size - 4,
1099 paragraph);
1100 break;
1101 default:
1102 WINE_FIXME("??? %u\n", GET_SHORT(format, 0));
1103 break;
1105 break;
1106 case 0x05:
1107 WINE_FIXME("Got an embedded element %s\n", format + 6);
1108 break;
1109 default:
1110 WINE_FIXME("Got a type %d picture\n", type);
1111 break;
1113 if (attributes.wVSpace) paragraph->u.gfx.pos |= 0x8000;
1115 format += size;
1117 break;
1119 case 0x89:
1120 HLPFILE_FreeLink(attributes.link);
1121 attributes.link = NULL;
1122 format += 1;
1123 break;
1125 case 0x8B:
1126 case 0x8C:
1127 WINE_FIXME("NIY non-break space/hyphen\n");
1128 format += 1;
1129 break;
1131 #if 0
1132 case 0xA9:
1133 format += 2;
1134 break;
1135 #endif
1137 case 0xC8:
1138 case 0xCC:
1139 WINE_TRACE("macro => %s\n", format + 3);
1140 HLPFILE_FreeLink(attributes.link);
1141 attributes.link = HLPFILE_AllocLink(hlp_link_macro, (const char*)format + 3,
1142 0, !(*format & 4), -1);
1143 format += 3 + GET_USHORT(format, 1);
1144 break;
1146 case 0xE0:
1147 case 0xE1:
1148 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
1149 HLPFILE_FreeLink(attributes.link);
1150 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1151 hlpfile->lpszPath,
1152 GET_UINT(format, 1)-16,
1153 1, -1);
1156 format += 5;
1157 break;
1159 case 0xE2:
1160 case 0xE3:
1161 case 0xE6:
1162 case 0xE7:
1163 HLPFILE_FreeLink(attributes.link);
1164 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1165 hlpfile->lpszPath,
1166 GET_UINT(format, 1),
1167 !(*format & 4), -1);
1168 format += 5;
1169 break;
1171 case 0xEA:
1172 case 0xEB:
1173 case 0xEE:
1174 case 0xEF:
1176 char* ptr = (char*) format + 8;
1177 BYTE type = format[3];
1178 int wnd = -1;
1179 char* str;
1181 if (type == 1) wnd = *ptr++;
1182 if (type == 4 || type == 6)
1184 str = ptr;
1185 ptr += strlen(ptr) + 1;
1187 else
1188 str = hlpfile->lpszPath;
1189 if (type == 6)
1191 for (wnd = hlpfile->numWindows - 1; wnd >= 0; wnd--)
1193 if (!strcmp(ptr, hlpfile->windows[wnd].name)) break;
1195 if (wnd == -1)
1196 WINE_WARN("Couldn't find window info for %s\n", ptr);
1198 HLPFILE_FreeLink(attributes.link);
1199 attributes.link = HLPFILE_AllocLink((*format & 4) ? hlp_link_link : hlp_link_popup,
1200 str, GET_UINT(format, 4),
1201 !(*format & 1), wnd);
1203 format += 3 + GET_USHORT(format, 1);
1204 break;
1206 default:
1207 WINE_WARN("format %02x\n", *format);
1208 format++;
1212 HeapFree(GetProcessHeap(), 0, text_base);
1213 return TRUE;
1216 /******************************************************************
1217 * HLPFILE_ReadFont
1221 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
1223 BYTE *ref, *end;
1224 unsigned i, len, idx;
1225 unsigned face_num, dscr_num, face_offset, dscr_offset;
1226 BYTE flag, family;
1228 if (!HLPFILE_FindSubFile("|FONT", &ref, &end))
1230 WINE_WARN("no subfile FONT\n");
1231 hlpfile->numFonts = 0;
1232 hlpfile->fonts = NULL;
1233 return FALSE;
1236 ref += 9;
1238 face_num = GET_USHORT(ref, 0);
1239 dscr_num = GET_USHORT(ref, 2);
1240 face_offset = GET_USHORT(ref, 4);
1241 dscr_offset = GET_USHORT(ref, 6);
1243 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1244 face_num, face_offset, dscr_num, dscr_offset);
1246 hlpfile->numFonts = dscr_num;
1247 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
1249 len = (dscr_offset - face_offset) / face_num;
1250 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
1251 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1252 for (i = 0; i < dscr_num; i++)
1254 flag = ref[dscr_offset + i * 11 + 0];
1255 family = ref[dscr_offset + i * 11 + 2];
1257 hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2 - 3;
1258 hlpfile->fonts[i].LogFont.lfWidth = 0;
1259 hlpfile->fonts[i].LogFont.lfEscapement = 0;
1260 hlpfile->fonts[i].LogFont.lfOrientation = 0;
1261 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
1262 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
1263 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
1264 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
1265 hlpfile->fonts[i].LogFont.lfCharSet = ANSI_CHARSET;
1266 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1267 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1268 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
1269 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
1271 switch (family)
1273 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break;
1274 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break;
1275 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break;
1276 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break;
1277 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1278 default: WINE_FIXME("Unknown family %u\n", family);
1280 idx = GET_USHORT(ref, dscr_offset + i * 11 + 3);
1282 if (idx < face_num)
1284 memcpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1285 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1)] = '\0';
1287 else
1289 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1290 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1292 hlpfile->fonts[i].hFont = 0;
1293 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1294 ref[dscr_offset + i * 11 + 6],
1295 ref[dscr_offset + i * 11 + 7]);
1296 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1297 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
1298 i, flag,
1299 X(0, "bold"),
1300 X(1, "italic"),
1301 X(2, "underline"),
1302 X(3, "strikeOut"),
1303 X(4, "dblUnderline"),
1304 X(5, "smallCaps"),
1305 ref[dscr_offset + i * 11 + 1],
1306 family,
1307 hlpfile->fonts[i].LogFont.lfFaceName, idx,
1308 GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1310 return TRUE;
1313 /***********************************************************************
1315 * HLPFILE_ReadFileToBuffer
1317 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1319 BYTE header[16], dummy[1];
1321 if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
1323 /* sanity checks */
1324 if (GET_UINT(header, 0) != 0x00035F3F)
1325 {WINE_WARN("wrong header\n"); return FALSE;};
1327 file_buffer_size = GET_UINT(header, 12);
1328 file_buffer = HeapAlloc(GetProcessHeap(), 0, file_buffer_size + 1);
1329 if (!file_buffer) return FALSE;
1331 memcpy(file_buffer, header, 16);
1332 if (_hread(hFile, file_buffer + 16, file_buffer_size - 16) != file_buffer_size - 16)
1333 {WINE_WARN("filesize1\n"); return FALSE;};
1335 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1337 file_buffer[file_buffer_size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1339 return TRUE;
1342 /**************************************************************************
1343 * comp_FindSubFile
1345 * HLPFILE_BPTreeCompare function for HLPFILE directory.
1348 static int comp_FindSubFile(void *p, const void *key,
1349 int leaf, void** next)
1351 *next = (char *)p+strlen(p)+(leaf?5:3);
1352 WINE_TRACE("Comparing '%s' with '%s'\n", (char *)p, (char *)key);
1353 return strcmp(p, key);
1356 /***********************************************************************
1358 * HLPFILE_FindSubFile
1360 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1362 BYTE *ptr;
1364 WINE_TRACE("looking for file '%s'\n", name);
1365 ptr = HLPFILE_BPTreeSearch(file_buffer + GET_UINT(file_buffer, 4),
1366 name, comp_FindSubFile);
1367 if (!ptr) return FALSE;
1368 *subbuf = file_buffer + GET_UINT(ptr, strlen(name)+1);
1369 if (*subbuf >= file_buffer + file_buffer_size)
1371 WINE_ERR("internal file %s does not fit\n", name);
1372 return FALSE;
1374 *subend = *subbuf + GET_UINT(*subbuf, 0);
1375 if (*subend > file_buffer + file_buffer_size)
1377 WINE_ERR("internal file %s does not fit\n", name);
1378 return FALSE;
1380 if (GET_UINT(*subbuf, 0) < GET_UINT(*subbuf, 4) + 9)
1382 WINE_ERR("invalid size provided for internal file %s\n", name);
1383 return FALSE;
1385 return TRUE;
1388 /***********************************************************************
1390 * HLPFILE_SystemCommands
1392 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1394 BYTE *buf, *ptr, *end;
1395 HLPFILE_MACRO *macro, **m;
1396 LPSTR p;
1397 unsigned short magic, minor, major, flags;
1399 hlpfile->lpszTitle = NULL;
1401 if (!HLPFILE_FindSubFile("|SYSTEM", &buf, &end)) return FALSE;
1403 magic = GET_USHORT(buf + 9, 0);
1404 minor = GET_USHORT(buf + 9, 2);
1405 major = GET_USHORT(buf + 9, 4);
1406 /* gen date on 4 bytes */
1407 flags = GET_USHORT(buf + 9, 10);
1408 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1409 magic, major, minor, flags);
1410 if (magic != 0x036C || major != 1)
1411 {WINE_WARN("Wrong system header\n"); return FALSE;}
1412 if (minor <= 16)
1414 hlpfile->tbsize = 0x800;
1415 hlpfile->compressed = 0;
1417 else if (flags == 0)
1419 hlpfile->tbsize = 0x1000;
1420 hlpfile->compressed = 0;
1422 else if (flags == 4)
1424 hlpfile->tbsize = 0x1000;
1425 hlpfile->compressed = 1;
1427 else
1429 hlpfile->tbsize = 0x800;
1430 hlpfile->compressed = 1;
1433 if (hlpfile->compressed)
1434 hlpfile->dsize = 0x4000;
1435 else
1436 hlpfile->dsize = hlpfile->tbsize - 0x0C;
1438 hlpfile->version = minor;
1439 hlpfile->flags = flags;
1441 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1443 char *str = (char*) ptr + 4;
1444 switch (GET_USHORT(ptr, 0))
1446 case 1:
1447 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1448 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1449 if (!hlpfile->lpszTitle) return FALSE;
1450 lstrcpy(hlpfile->lpszTitle, str);
1451 WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1452 break;
1454 case 2:
1455 if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
1456 hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1457 if (!hlpfile->lpszCopyright) return FALSE;
1458 lstrcpy(hlpfile->lpszCopyright, str);
1459 WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
1460 break;
1462 case 3:
1463 if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
1464 hlpfile->contents_start = GET_UINT(ptr, 4);
1465 WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
1466 break;
1468 case 4:
1469 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(str) + 1);
1470 if (!macro) break;
1471 p = (char*)macro + sizeof(HLPFILE_MACRO);
1472 lstrcpy(p, str);
1473 macro->lpszMacro = p;
1474 macro->next = 0;
1475 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1476 *m = macro;
1477 break;
1479 case 6:
1480 if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
1482 if (hlpfile->windows)
1483 hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows,
1484 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1485 else
1486 hlpfile->windows = HeapAlloc(GetProcessHeap(), 0,
1487 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1489 if (hlpfile->windows)
1491 unsigned flags = GET_USHORT(ptr, 4);
1492 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
1494 if (flags & 0x0001) strcpy(wi->type, &str[2]);
1495 else wi->type[0] = '\0';
1496 if (flags & 0x0002) strcpy(wi->name, &str[12]);
1497 else wi->name[0] = '\0';
1498 if (flags & 0x0004) strcpy(wi->caption, &str[23]);
1499 else lstrcpynA(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
1500 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
1501 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
1502 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
1503 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
1504 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
1505 wi->win_style = WS_OVERLAPPEDWINDOW;
1506 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
1507 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
1508 WINE_TRACE("System-Window: flags=%c%c%c%c%c%c%c%c type=%s name=%s caption=%s (%d,%d)x(%d,%d)\n",
1509 flags & 0x0001 ? 'T' : 't',
1510 flags & 0x0002 ? 'N' : 'n',
1511 flags & 0x0004 ? 'C' : 'c',
1512 flags & 0x0008 ? 'X' : 'x',
1513 flags & 0x0010 ? 'Y' : 'y',
1514 flags & 0x0020 ? 'W' : 'w',
1515 flags & 0x0040 ? 'H' : 'h',
1516 flags & 0x0080 ? 'S' : 's',
1517 wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
1518 wi->size.cx, wi->size.cy);
1520 break;
1521 default:
1522 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1525 if (!hlpfile->lpszTitle)
1526 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1);
1527 return TRUE;
1530 /***********************************************************************
1532 * HLPFILE_UncompressedLZ77_Size
1534 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1536 int i, newsize = 0;
1538 while (ptr < end)
1540 int mask = *ptr++;
1541 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1543 if (mask & 1)
1545 int code = GET_USHORT(ptr, 0);
1546 int len = 3 + (code >> 12);
1547 newsize += len;
1548 ptr += 2;
1550 else newsize++, ptr++;
1554 return newsize;
1557 /***********************************************************************
1559 * HLPFILE_UncompressLZ77
1561 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1563 int i;
1565 while (ptr < end)
1567 int mask = *ptr++;
1568 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1570 if (mask & 1)
1572 int code = GET_USHORT(ptr, 0);
1573 int len = 3 + (code >> 12);
1574 int offset = code & 0xfff;
1576 * We must copy byte-by-byte here. We cannot use memcpy nor
1577 * memmove here. Just example:
1578 * a[]={1,2,3,4,5,6,7,8,9,10}
1579 * newptr=a+2;
1580 * offset=1;
1581 * We expect:
1582 * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12}
1584 for (; len>0; len--, newptr++) *newptr = *(newptr-offset-1);
1585 ptr += 2;
1587 else *newptr++ = *ptr++;
1591 return newptr;
1594 /***********************************************************************
1596 * HLPFILE_UncompressLZ77_Phrases
1598 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1600 UINT i, num, dec_size, head_size;
1601 BYTE *buf, *end;
1603 if (!HLPFILE_FindSubFile("|Phrases", &buf, &end)) return FALSE;
1605 if (hlpfile->version <= 16)
1606 head_size = 13;
1607 else
1608 head_size = 17;
1610 num = phrases.num = GET_USHORT(buf, 9);
1611 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1613 if (hlpfile->version <= 16)
1614 dec_size = end - buf - 15 - 2 * num;
1615 else
1616 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1618 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1619 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1620 if (!phrases.offsets || !phrases.buffer) return FALSE;
1622 for (i = 0; i <= num; i++)
1623 phrases.offsets[i] = GET_USHORT(buf, head_size + 2 * i) - 2 * num - 2;
1625 if (hlpfile->version <= 16)
1626 memcpy(phrases.buffer, buf + 15 + 2*num, dec_size);
1627 else
1628 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, (BYTE*)phrases.buffer);
1630 hlpfile->hasPhrases = TRUE;
1631 return TRUE;
1634 /***********************************************************************
1636 * HLPFILE_Uncompress_Phrases40
1638 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1640 UINT num;
1641 INT dec_size, cpr_size;
1642 BYTE *buf_idx, *end_idx;
1643 BYTE *buf_phs, *end_phs;
1644 long* ptr, mask = 0;
1645 unsigned int i;
1646 unsigned short bc, n;
1648 if (!HLPFILE_FindSubFile("|PhrIndex", &buf_idx, &end_idx) ||
1649 !HLPFILE_FindSubFile("|PhrImage", &buf_phs, &end_phs)) return FALSE;
1651 ptr = (long*)(buf_idx + 9 + 28);
1652 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1653 num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1655 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1656 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1657 GET_UINT(buf_idx, 9 + 0),
1658 GET_UINT(buf_idx, 9 + 4),
1659 GET_UINT(buf_idx, 9 + 8),
1660 GET_UINT(buf_idx, 9 + 12),
1661 GET_UINT(buf_idx, 9 + 16),
1662 GET_UINT(buf_idx, 9 + 20),
1663 GET_USHORT(buf_idx, 9 + 24),
1664 GET_USHORT(buf_idx, 9 + 26));
1666 dec_size = GET_UINT(buf_idx, 9 + 12);
1667 cpr_size = GET_UINT(buf_idx, 9 + 16);
1669 if (dec_size != cpr_size &&
1670 dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1672 WINE_WARN("size mismatch %u %u\n",
1673 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1674 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1677 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1678 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1679 if (!phrases.offsets || !phrases.buffer) return FALSE;
1681 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1683 phrases.offsets[0] = 0;
1684 for (i = 0; i < num; i++)
1686 for (n = 1; getbit(); n += 1 << bc);
1687 if (getbit()) n++;
1688 if (bc > 1 && getbit()) n += 2;
1689 if (bc > 2 && getbit()) n += 4;
1690 if (bc > 3 && getbit()) n += 8;
1691 if (bc > 4 && getbit()) n += 16;
1692 phrases.offsets[i + 1] = phrases.offsets[i] + n;
1694 #undef getbit
1696 if (dec_size == cpr_size)
1697 memcpy(phrases.buffer, buf_phs + 9, dec_size);
1698 else
1699 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, (BYTE*)phrases.buffer);
1701 hlpfile->hasPhrases40 = TRUE;
1702 return TRUE;
1705 /***********************************************************************
1707 * HLPFILE_Uncompress_Topic
1709 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
1711 BYTE *buf, *ptr, *end, *newptr;
1712 unsigned int i, newsize = 0;
1713 unsigned int topic_size;
1715 if (!HLPFILE_FindSubFile("|TOPIC", &buf, &end))
1716 {WINE_WARN("topic0\n"); return FALSE;}
1718 buf += 9; /* Skip file header */
1719 topic_size = end - buf;
1720 if (hlpfile->compressed)
1722 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1724 for (i = 0; i < topic.wMapLen; i++)
1726 ptr = buf + i * hlpfile->tbsize;
1728 /* I don't know why, it's necessary for printman.hlp */
1729 if (ptr + 0x44 > end) ptr = end - 0x44;
1731 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + hlpfile->tbsize));
1734 topic.map = HeapAlloc(GetProcessHeap(), 0,
1735 topic.wMapLen * sizeof(topic.map[0]) + newsize);
1736 if (!topic.map) return FALSE;
1737 newptr = (BYTE*)(topic.map + topic.wMapLen);
1738 topic.end = newptr + newsize;
1740 for (i = 0; i < topic.wMapLen; i++)
1742 ptr = buf + i * hlpfile->tbsize;
1743 if (ptr + 0x44 > end) ptr = end - 0x44;
1745 topic.map[i] = newptr;
1746 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + hlpfile->tbsize), newptr);
1749 else
1751 /* basically, we need to copy the TopicBlockSize byte pages
1752 * (removing the first 0x0C) in one single area in memory
1754 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1755 topic.map = HeapAlloc(GetProcessHeap(), 0,
1756 topic.wMapLen * (sizeof(topic.map[0]) + hlpfile->dsize));
1757 if (!topic.map) return FALSE;
1758 newptr = (BYTE*)(topic.map + topic.wMapLen);
1759 topic.end = newptr + topic_size;
1761 for (i = 0; i < topic.wMapLen; i++)
1763 topic.map[i] = newptr + i * hlpfile->dsize;
1764 memcpy(topic.map[i], buf + i * hlpfile->tbsize + 0x0C, hlpfile->dsize);
1767 return TRUE;
1770 /***********************************************************************
1772 * HLPFILE_Uncompress2
1775 static void HLPFILE_Uncompress2(const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
1777 BYTE *phptr, *phend;
1778 UINT code;
1779 UINT index;
1781 while (ptr < end && newptr < newend)
1783 if (!*ptr || *ptr >= 0x10)
1784 *newptr++ = *ptr++;
1785 else
1787 code = 0x100 * ptr[0] + ptr[1];
1788 index = (code - 0x100) / 2;
1790 phptr = (BYTE*)phrases.buffer + phrases.offsets[index];
1791 phend = (BYTE*)phrases.buffer + phrases.offsets[index + 1];
1793 if (newptr + (phend - phptr) > newend)
1795 WINE_FIXME("buffer overflow %p > %p for %d bytes\n",
1796 newptr, newend, phend - phptr);
1797 return;
1799 memcpy(newptr, phptr, phend - phptr);
1800 newptr += phend - phptr;
1801 if (code & 1) *newptr++ = ' ';
1803 ptr += 2;
1806 if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
1809 /******************************************************************
1810 * HLPFILE_Uncompress3
1814 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1815 const BYTE* src, const BYTE* src_end)
1817 unsigned int idx, len;
1819 for (; src < src_end; src++)
1821 if ((*src & 1) == 0)
1823 idx = *src / 2;
1824 if (idx > phrases.num)
1826 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1827 len = 0;
1829 else
1831 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1832 if (dst + len <= dst_end)
1833 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1836 else if ((*src & 0x03) == 0x01)
1838 idx = (*src + 1) * 64;
1839 idx += *++src;
1840 if (idx > phrases.num)
1842 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1843 len = 0;
1845 else
1847 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1848 if (dst + len <= dst_end)
1849 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1852 else if ((*src & 0x07) == 0x03)
1854 len = (*src / 8) + 1;
1855 if (dst + len <= dst_end)
1856 memcpy(dst, src + 1, len);
1857 src += len;
1859 else
1861 len = (*src / 16) + 1;
1862 if (dst + len <= dst_end)
1863 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1865 dst += len;
1868 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1869 return TRUE;
1872 /******************************************************************
1873 * HLPFILE_UncompressRLE
1877 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz)
1879 BYTE ch;
1880 BYTE* sdst = *dst + dstsz;
1882 while (src < end)
1884 ch = *src++;
1885 if (ch & 0x80)
1887 ch &= 0x7F;
1888 if ((*dst) + ch <= sdst)
1889 memcpy(*dst, src, ch);
1890 src += ch;
1892 else
1894 if ((*dst) + ch <= sdst)
1895 memset(*dst, (char)*src, ch);
1896 src++;
1898 *dst += ch;
1900 if (*dst != sdst)
1901 WINE_WARN("Buffer X-flow: d(%u) instead of d(%u)\n",
1902 *dst - (sdst - dstsz), dstsz);
1905 /**************************************************************************
1906 * HLPFILE_BPTreeSearch
1908 * Searches for an element in B+ tree
1910 * PARAMS
1911 * buf [I] pointer to the embedded file structured as a B+ tree
1912 * key [I] pointer to data to find
1913 * comp [I] compare function
1915 * RETURNS
1916 * Pointer to block identified by key, or NULL if failure.
1919 static void* HLPFILE_BPTreeSearch(BYTE* buf, const void* key,
1920 HLPFILE_BPTreeCompare comp)
1922 unsigned magic;
1923 unsigned page_size;
1924 unsigned cur_page;
1925 unsigned level;
1926 BYTE *pages, *ptr, *newptr;
1927 int i, entries;
1928 int ret;
1930 magic = GET_USHORT(buf, 9);
1931 if (magic != 0x293B)
1933 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic);
1934 return NULL;
1936 page_size = GET_USHORT(buf, 9+4);
1937 cur_page = GET_USHORT(buf, 9+26);
1938 level = GET_USHORT(buf, 9+32);
1939 pages = buf + 9 + 38;
1940 while (--level > 0)
1942 ptr = pages + cur_page*page_size;
1943 entries = GET_SHORT(ptr, 2);
1944 ptr += 6;
1945 for (i = 0; i < entries; i++)
1947 if (comp(ptr, key, 0, (void **)&newptr) > 0) break;
1948 ptr = newptr;
1950 cur_page = GET_USHORT(ptr-2, 0);
1952 ptr = pages + cur_page*page_size;
1953 entries = GET_SHORT(ptr, 2);
1954 ptr += 8;
1955 for (i = 0; i < entries; i++)
1957 ret = comp(ptr, key, 1, (void **)&newptr);
1958 if (ret == 0) return ptr;
1959 if (ret > 0) return NULL;
1960 ptr = newptr;
1962 return NULL;
1966 /***********************************************************************
1968 * HLPFILE_GetContext
1970 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
1972 BYTE *cbuf, *cend;
1973 unsigned clen;
1975 if (!HLPFILE_FindSubFile("|CONTEXT", &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
1977 clen = cend - cbuf;
1978 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen);
1979 if (!hlpfile->Context) return FALSE;
1980 memcpy(hlpfile->Context, cbuf, clen);
1982 return TRUE;
1985 /***********************************************************************
1987 * HLPFILE_GetMap
1989 static BOOL HLPFILE_GetMap(HLPFILE *hlpfile)
1991 BYTE *cbuf, *cend;
1992 unsigned entries, i;
1994 if (!HLPFILE_FindSubFile("|CTXOMAP", &cbuf, &cend)) {WINE_WARN("no map section\n"); return FALSE;}
1996 entries = GET_USHORT(cbuf, 9);
1997 hlpfile->Map = HeapAlloc(GetProcessHeap(), 0, entries * sizeof(HLPFILE_MAP));
1998 if (!hlpfile->Map) return FALSE;
1999 hlpfile->wMapLen = entries;
2000 for (i = 0; i < entries; i++)
2002 hlpfile->Map[i].lMap = GET_UINT(cbuf+11,i*8);
2003 hlpfile->Map[i].offset = GET_UINT(cbuf+11,i*8+4);
2005 return TRUE;
2008 /******************************************************************
2009 * HLPFILE_DeleteLink
2013 void HLPFILE_FreeLink(HLPFILE_LINK* link)
2015 if (link && !--link->wRefCount)
2016 HeapFree(GetProcessHeap(), 0, link);
2019 /***********************************************************************
2021 * HLPFILE_DeleteParagraph
2023 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
2025 HLPFILE_PARAGRAPH* next;
2027 while (paragraph)
2029 next = paragraph->next;
2031 if (paragraph->cookie == para_metafile)
2032 DeleteMetaFile(paragraph->u.gfx.u.mfp.hMF);
2034 HLPFILE_FreeLink(paragraph->link);
2036 HeapFree(GetProcessHeap(), 0, paragraph);
2037 paragraph = next;
2041 /***********************************************************************
2043 * DeleteMacro
2045 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
2047 HLPFILE_MACRO* next;
2049 while (macro)
2051 next = macro->next;
2052 HeapFree(GetProcessHeap(), 0, macro);
2053 macro = next;
2057 /***********************************************************************
2059 * DeletePage
2061 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
2063 HLPFILE_PAGE* next;
2065 while (page)
2067 next = page->next;
2068 HLPFILE_DeleteParagraph(page->first_paragraph);
2069 HLPFILE_DeleteMacro(page->first_macro);
2070 HeapFree(GetProcessHeap(), 0, page);
2071 page = next;
2075 /***********************************************************************
2077 * HLPFILE_FreeHlpFile
2079 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
2081 unsigned i;
2083 if (!hlpfile || --hlpfile->wRefCount > 0) return;
2085 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
2086 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
2087 else first_hlpfile = hlpfile->next;
2089 if (hlpfile->numFonts)
2091 for (i = 0; i < hlpfile->numFonts; i++)
2093 DeleteObject(hlpfile->fonts[i].hFont);
2095 HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
2098 if (hlpfile->numBmps)
2100 for (i = 0; i < hlpfile->numBmps; i++)
2102 DeleteObject(hlpfile->bmps[i]);
2104 HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
2107 HLPFILE_DeletePage(hlpfile->first_page);
2108 HLPFILE_DeleteMacro(hlpfile->first_macro);
2110 if (hlpfile->numWindows) HeapFree(GetProcessHeap(), 0, hlpfile->windows);
2111 HeapFree(GetProcessHeap(), 0, hlpfile->Context);
2112 HeapFree(GetProcessHeap(), 0, hlpfile->Map);
2113 HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
2114 HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
2115 HeapFree(GetProcessHeap(), 0, hlpfile);