winhelp: Rewrite Context support using B+ tree search.
[wine.git] / programs / winhelp / hlpfile.c
blob27d5555e35d48221afd9427f5b78e80cc10b772b
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;
55 static struct
57 UINT num;
58 unsigned* offsets;
59 char* buffer;
60 } phrases;
62 static struct
64 BYTE** map;
65 BYTE* end;
66 UINT wMapLen;
67 } topic;
69 static struct
71 UINT wFont;
72 UINT wIndent;
73 UINT wHSpace;
74 UINT wVSpace;
75 HLPFILE_LINK* link;
76 } attributes;
79 * Compare function type for HLPFILE_BPTreeSearch function.
81 * PARAMS
82 * p [I] pointer to testing block (key + data)
83 * key [I] pointer to key value to look for
84 * leaf [I] whether this function called for index of leaf page
85 * next [O] pointer to pointer to next block
87 typedef int (*HLPFILE_BPTreeCompare)(void *p, const void *key,
88 int leaf, void **next);
90 static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR);
91 static BOOL HLPFILE_ReadFileToBuffer(HFILE);
92 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**);
93 static BOOL HLPFILE_SystemCommands(HLPFILE*);
94 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end);
95 static BYTE* HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr);
96 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE*);
97 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE*);
98 static BOOL HLPFILE_Uncompress_Topic(HLPFILE*);
99 static BOOL HLPFILE_GetContext(HLPFILE*);
100 static BOOL HLPFILE_GetMap(HLPFILE*);
101 static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*, unsigned);
102 static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*, unsigned*);
103 static void HLPFILE_Uncompress2(const BYTE*, const BYTE*, BYTE*, const BYTE*);
104 static BOOL HLPFILE_Uncompress3(char*, const char*, const BYTE*, const BYTE*);
105 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz);
106 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile);
108 static void* HLPFILE_BPTreeSearch(BYTE*, const void*, HLPFILE_BPTreeCompare);
110 /***********************************************************************
112 * HLPFILE_PageByNumber
114 static HLPFILE_PAGE *HLPFILE_PageByNumber(HLPFILE* hlpfile, UINT wNum)
116 HLPFILE_PAGE *page;
117 UINT temp = wNum;
119 WINE_TRACE("<%s>[%u]\n", hlpfile->lpszPath, wNum);
121 for (page = hlpfile->first_page; page && temp; page = page->next) temp--;
122 if (!page)
123 WINE_ERR("Page of number %u not found in file %s\n", wNum, hlpfile->lpszPath);
124 return page;
127 /* FIXME:
128 * this finds the page containing the offset. The offset can either
129 * refer to the top of the page (offset == page->offset), or
130 * to some paragraph inside the page...
131 * As of today, we only return the page... we should also return
132 * a paragraph, and then, while opening a new page, compute the
133 * y-offset of the paragraph to be shown and scroll the window
134 * accordinly
136 /******************************************************************
137 * HLPFILE_PageByOffset
141 HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset)
143 HLPFILE_PAGE* page;
144 HLPFILE_PAGE* found;
146 if (!hlpfile) return 0;
148 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, offset);
150 if (offset == 0xFFFFFFFF) return NULL;
151 page = NULL;
153 for (found = NULL, page = hlpfile->first_page; page; page = page->next)
155 if (page->offset <= offset && (!found || found->offset < page->offset))
156 found = page;
158 if (!found)
159 WINE_ERR("Page of offset %u not found in file %s\n",
160 offset, hlpfile->lpszPath);
161 return found;
164 /**************************************************************************
165 * comp_PageByHash
167 * HLPFILE_BPTreeCompare function for '|CONTEXT' B+ tree file
170 static int comp_PageByHash(void *p, const void *key,
171 int leaf, void** next)
173 LONG lKey = (LONG)key;
174 LONG lTest = GET_UINT(p, 0);
176 *next = (char *)p+(leaf?8:6);
177 WINE_TRACE("Comparing '%u' with '%u'\n", lKey, lTest);
178 if (lTest < lKey) return -1;
179 if (lTest > lKey) return 1;
180 return 0;
183 /***********************************************************************
185 * HLPFILE_HlpFilePageByHash
187 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash)
189 BYTE *ptr;
191 if (!hlpfile) return 0;
193 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lHash);
195 /* For win 3.0 files hash values are really page numbers */
196 if (hlpfile->version <= 16)
197 return HLPFILE_PageByNumber(hlpfile, lHash);
199 ptr = HLPFILE_BPTreeSearch(hlpfile->Context, (void*)lHash, comp_PageByHash);
200 if (!ptr)
202 WINE_ERR("Page of hash %x not found in file %s\n", lHash, hlpfile->lpszPath);
203 return NULL;
206 return HLPFILE_PageByOffset(hlpfile, GET_UINT(ptr, 4));
209 /***********************************************************************
211 * HLPFILE_PageByMap
213 HLPFILE_PAGE *HLPFILE_PageByMap(HLPFILE* hlpfile, LONG lMap)
215 unsigned int i;
217 if (!hlpfile) return 0;
219 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lMap);
221 for (i = 0; i < hlpfile->wMapLen; i++)
223 if (hlpfile->Map[i].lMap == lMap)
224 return HLPFILE_PageByOffset(hlpfile, hlpfile->Map[i].offset);
227 WINE_ERR("Page of Map %x not found in file %s\n", lMap, hlpfile->lpszPath);
228 return NULL;
231 /***********************************************************************
233 * HLPFILE_Contents
235 HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile)
237 HLPFILE_PAGE* page = NULL;
239 if (!hlpfile) return NULL;
241 page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start);
242 if (!page) page = hlpfile->first_page;
243 return page;
246 /***********************************************************************
248 * HLPFILE_Hash
250 LONG HLPFILE_Hash(LPCSTR lpszContext)
252 LONG lHash = 0;
253 CHAR c;
255 while ((c = *lpszContext++))
257 CHAR x = 0;
258 if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
259 if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
260 if (c >= '1' && c <= '9') x = c - '0';
261 if (c == '0') x = 10;
262 if (c == '.') x = 12;
263 if (c == '_') x = 13;
264 if (x) lHash = lHash * 43 + x;
266 return lHash;
269 /***********************************************************************
271 * HLPFILE_ReadHlpFile
273 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
275 HLPFILE* hlpfile;
277 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
279 if (!strcmp(lpszPath, hlpfile->lpszPath))
281 hlpfile->wRefCount++;
282 return hlpfile;
286 hlpfile = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
287 if (!hlpfile) return 0;
289 hlpfile->lpszPath = (char*)hlpfile + sizeof(HLPFILE);
290 hlpfile->lpszTitle = NULL;
291 hlpfile->lpszCopyright = NULL;
292 hlpfile->first_page = NULL;
293 hlpfile->first_macro = NULL;
294 hlpfile->Context = NULL;
295 hlpfile->wMapLen = 0;
296 hlpfile->Map = NULL;
297 hlpfile->contents_start = 0xFFFFFFFF;
298 hlpfile->prev = NULL;
299 hlpfile->next = first_hlpfile;
300 hlpfile->wRefCount = 1;
302 hlpfile->numBmps = 0;
303 hlpfile->bmps = NULL;
305 hlpfile->numFonts = 0;
306 hlpfile->fonts = NULL;
308 hlpfile->numWindows = 0;
309 hlpfile->windows = NULL;
311 strcpy(hlpfile->lpszPath, lpszPath);
313 first_hlpfile = hlpfile;
314 if (hlpfile->next) hlpfile->next->prev = hlpfile;
316 phrases.offsets = NULL;
317 phrases.buffer = NULL;
318 topic.map = NULL;
319 topic.end = NULL;
320 file_buffer = NULL;
322 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
324 HLPFILE_FreeHlpFile(hlpfile);
325 hlpfile = 0;
328 HeapFree(GetProcessHeap(), 0, phrases.offsets);
329 HeapFree(GetProcessHeap(), 0, phrases.buffer);
330 HeapFree(GetProcessHeap(), 0, topic.map);
331 HeapFree(GetProcessHeap(), 0, file_buffer);
333 return hlpfile;
336 /***********************************************************************
338 * HLPFILE_DoReadHlpFile
340 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
342 BOOL ret;
343 HFILE hFile;
344 OFSTRUCT ofs;
345 BYTE* buf;
346 DWORD ref = 0x0C;
347 unsigned index, old_index, offset, len, offs;
349 hFile = OpenFile(lpszPath, &ofs, OF_READ);
350 if (hFile == HFILE_ERROR) return FALSE;
352 ret = HLPFILE_ReadFileToBuffer(hFile);
353 _lclose(hFile);
354 if (!ret) return FALSE;
356 if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
358 /* load phrases support */
359 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile))
360 HLPFILE_Uncompress_Phrases40(hlpfile);
362 if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE;
363 if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
365 buf = topic.map[0];
366 old_index = -1;
367 offs = 0;
370 BYTE* end;
372 index = (ref - 0x0C) / hlpfile->dsize;
373 offset = (ref - 0x0C) % hlpfile->dsize;
375 if (hlpfile->version <= 16 && index != old_index && index != 0)
377 /* we jumped to the next block, adjust pointers */
378 ref -= 12;
379 offset -= 12;
382 WINE_TRACE("ref=%08x => [%u/%u]\n", ref, index, offset);
384 if (index >= topic.wMapLen) {WINE_WARN("maplen\n"); break;}
385 buf = topic.map[index] + offset;
386 if (buf + 0x15 >= topic.end) {WINE_WARN("extra\n"); break;}
387 end = min(buf + GET_UINT(buf, 0), topic.end);
388 if (index != old_index) {offs = 0; old_index = index;}
390 switch (buf[0x14])
392 case 0x02:
393 if (!HLPFILE_AddPage(hlpfile, buf, end, index * 0x8000L + offs)) return FALSE;
394 break;
396 case 0x01:
397 case 0x20:
398 case 0x23:
399 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
400 offs += len;
401 break;
403 default:
404 WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
407 if (hlpfile->version <= 16)
409 ref += GET_UINT(buf, 0xc);
410 if (GET_UINT(buf, 0xc) == 0)
411 break;
413 else
414 ref = GET_UINT(buf, 0xc);
415 } while (ref != 0xffffffff);
417 HLPFILE_GetMap(hlpfile);
418 if (hlpfile->version <= 16) return TRUE;
419 return HLPFILE_GetContext(hlpfile);
422 /***********************************************************************
424 * HLPFILE_AddPage
426 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned offset)
428 HLPFILE_PAGE* page;
429 BYTE* title;
430 UINT titlesize;
431 char* ptr;
432 HLPFILE_MACRO*macro;
434 title = buf + GET_UINT(buf, 0x10);
435 if (title > end) {WINE_WARN("page2\n"); return FALSE;};
437 titlesize = GET_UINT(buf, 4);
438 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
439 if (!page) return FALSE;
440 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
442 if (hlpfile->hasPhrases)
444 HLPFILE_Uncompress2(title, end, (BYTE*)page->lpszTitle, (BYTE*)page->lpszTitle + titlesize);
446 else
448 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
450 /* need to decompress */
451 HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize,
452 title, end);
454 else
456 memcpy(page->lpszTitle, title, titlesize);
460 page->lpszTitle[titlesize] = '\0';
462 if (hlpfile->first_page)
464 HLPFILE_PAGE *p;
466 for (p = hlpfile->first_page; p->next; p = p->next);
467 page->prev = p;
468 p->next = page;
470 else
472 hlpfile->first_page = page;
473 page->prev = NULL;
476 page->file = hlpfile;
477 page->next = NULL;
478 page->first_paragraph = NULL;
479 page->first_macro = NULL;
480 page->wNumber = GET_UINT(buf, 0x21);
481 page->offset = offset;
483 page->browse_bwd = GET_UINT(buf, 0x19);
484 page->browse_fwd = GET_UINT(buf, 0x1D);
486 WINE_TRACE("Added page[%d]: title='%s' %08x << %08x >> %08x\n",
487 page->wNumber, page->lpszTitle,
488 page->browse_bwd, page->offset, page->browse_fwd);
490 memset(&attributes, 0, sizeof(attributes));
492 /* now load macros */
493 ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
494 while (ptr < page->lpszTitle + titlesize)
496 unsigned len = strlen(ptr);
497 char* macro_str;
499 WINE_TRACE("macro: %s\n", ptr);
500 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
501 macro->lpszMacro = macro_str = (char*)(macro + 1);
502 memcpy(macro_str, ptr, len + 1);
503 /* FIXME: shall we really link macro in reverse order ??
504 * may produce strange results when played at page opening
506 macro->next = page->first_macro;
507 page->first_macro = macro;
508 ptr += len + 1;
511 return TRUE;
514 static long fetch_long(BYTE** ptr)
516 long ret;
518 if (*(*ptr) & 1)
520 ret = (*(unsigned long*)(*ptr) - 0x80000000L) / 2;
521 (*ptr) += 4;
523 else
525 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
526 (*ptr) += 2;
529 return ret;
532 static unsigned long fetch_ulong(BYTE** ptr)
534 unsigned long ret;
536 if (*(*ptr) & 1)
538 ret = *(unsigned long*)(*ptr) / 2;
539 (*ptr) += 4;
541 else
543 ret = *(unsigned short*)(*ptr) / 2;
544 (*ptr) += 2;
546 return ret;
549 static short fetch_short(BYTE** ptr)
551 short ret;
553 if (*(*ptr) & 1)
555 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
556 (*ptr) += 2;
558 else
560 ret = (*(unsigned char*)(*ptr) - 0x80) / 2;
561 (*ptr)++;
563 return ret;
566 static unsigned short fetch_ushort(BYTE** ptr)
568 unsigned short ret;
570 if (*(*ptr) & 1)
572 ret = *(unsigned short*)(*ptr) / 2;
573 (*ptr) += 2;
575 else
577 ret = *(unsigned char*)(*ptr) / 2;
578 (*ptr)++;
580 return ret;
583 /******************************************************************
584 * HLPFILE_DecompressGfx
586 * Decompress the data part of a bitmap or a metafile
588 static BYTE* HLPFILE_DecompressGfx(BYTE* src, unsigned csz, unsigned sz, BYTE packing)
590 BYTE* dst;
591 BYTE* tmp;
592 BYTE* tmp2;
593 unsigned sz77;
595 WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing, csz, sz);
597 switch (packing)
599 case 0: /* uncompressed */
600 if (sz != csz)
601 WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz, csz);
602 dst = src;
603 break;
604 case 1: /* RunLen */
605 tmp = dst = HeapAlloc(GetProcessHeap(), 0, sz);
606 if (!dst) return NULL;
607 HLPFILE_UncompressRLE(src, src + csz, &tmp, sz);
608 if (tmp - dst != sz)
609 WINE_WARN("Bogus gfx sizes (RunLen): %u/%u\n", tmp - dst, sz);
610 break;
611 case 2: /* LZ77 */
612 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
613 dst = HeapAlloc(GetProcessHeap(), 0, sz77);
614 if (!dst) return NULL;
615 HLPFILE_UncompressLZ77(src, src + csz, dst);
616 if (sz77 != sz)
617 WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77, sz);
618 break;
619 case 3: /* LZ77 then RLE */
620 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
621 tmp = HeapAlloc(GetProcessHeap(), 0, sz77);
622 if (!tmp) return FALSE;
623 HLPFILE_UncompressLZ77(src, src + csz, tmp);
624 dst = tmp2 = HeapAlloc(GetProcessHeap(), 0, sz);
625 if (!dst)
627 HeapFree(GetProcessHeap(), 0, tmp);
628 return FALSE;
630 HLPFILE_UncompressRLE(tmp, tmp + sz77, &tmp2, sz);
631 if (tmp2 - dst != sz)
632 WINE_WARN("Bogus gfx sizes (LZ77+RunLen): %u / %u\n", tmp2 - dst, sz);
633 HeapFree(GetProcessHeap(), 0, tmp);
634 break;
635 default:
636 WINE_FIXME("Unsupported packing %u\n", packing);
637 return NULL;
639 return dst;
642 /******************************************************************
643 * HLPFILE_LoadBitmap
647 static BOOL HLPFILE_LoadBitmap(BYTE* beg, BYTE type, BYTE pack,
648 HLPFILE_PARAGRAPH* paragraph)
650 BYTE* ptr;
651 BYTE* pict_beg;
652 BITMAPINFO* bi;
653 unsigned long off, csz;
654 HDC hdc;
656 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
657 if (!bi) return FALSE;
659 ptr = beg + 2; /* for type and pack */
661 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
662 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
663 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
664 bi->bmiHeader.biPlanes = fetch_ushort(&ptr);
665 bi->bmiHeader.biBitCount = fetch_ushort(&ptr);
666 bi->bmiHeader.biWidth = fetch_ulong(&ptr);
667 bi->bmiHeader.biHeight = fetch_ulong(&ptr);
668 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr);
669 bi->bmiHeader.biClrImportant = fetch_ulong(&ptr);
670 bi->bmiHeader.biCompression = BI_RGB;
671 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
672 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
673 bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
674 WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n",
675 bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount,
676 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
678 csz = fetch_ulong(&ptr);
679 fetch_ulong(&ptr); /* hotspot size */
681 off = GET_UINT(ptr, 0); ptr += 4;
682 /* GET_UINT(ptr, 0); hotspot offset */ ptr += 4;
684 /* now read palette info */
685 if (type == 0x06)
687 unsigned nc = bi->bmiHeader.biClrUsed;
688 unsigned i;
690 /* not quite right, especially for bitfields type of compression */
691 if (!nc && bi->bmiHeader.biBitCount <= 8)
692 nc = 1 << bi->bmiHeader.biBitCount;
694 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
695 if (!bi) return FALSE;
696 for (i = 0; i < nc; i++)
698 bi->bmiColors[i].rgbBlue = ptr[0];
699 bi->bmiColors[i].rgbGreen = ptr[1];
700 bi->bmiColors[i].rgbRed = ptr[2];
701 bi->bmiColors[i].rgbReserved = 0;
702 ptr += 4;
705 pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack);
707 paragraph->u.gfx.u.bmp.hBitmap = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader,
708 CBM_INIT, pict_beg,
709 bi, DIB_RGB_COLORS);
710 ReleaseDC(0, hdc);
711 if (!paragraph->u.gfx.u.bmp.hBitmap)
712 WINE_ERR("Couldn't create bitmap\n");
714 HeapFree(GetProcessHeap(), 0, bi);
715 if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
717 return TRUE;
720 /******************************************************************
721 * HLPFILE_LoadMetaFile
725 static BOOL HLPFILE_LoadMetaFile(BYTE* beg, BYTE pack, HLPFILE_PARAGRAPH* paragraph)
727 BYTE* ptr;
728 unsigned long size, csize;
729 unsigned long off, hsoff;
730 BYTE* bits;
731 LPMETAFILEPICT lpmfp;
733 WINE_TRACE("Loading metafile\n");
735 ptr = beg + 2; /* for type and pack */
737 lpmfp = &paragraph->u.gfx.u.mfp;
738 lpmfp->mm = fetch_ushort(&ptr); /* mapping mode */
740 lpmfp->xExt = GET_USHORT(ptr, 0);
741 lpmfp->yExt = GET_USHORT(ptr, 2);
742 ptr += 4;
744 size = fetch_ulong(&ptr); /* decompressed size */
745 csize = fetch_ulong(&ptr); /* compressed size */
746 fetch_ulong(&ptr); /* hotspot size */
747 off = GET_UINT(ptr, 0);
748 hsoff = GET_UINT(ptr, 4);
749 ptr += 8;
751 WINE_TRACE("sz=%lu csz=%lu (%d,%d) offs=%lu/%u,%lu\n",
752 size, csize, lpmfp->xExt, lpmfp->yExt, off, ptr - beg, hsoff);
754 bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack);
755 if (!bits) return FALSE;
757 paragraph->cookie = para_metafile;
759 lpmfp->hMF = SetMetaFileBitsEx(size, bits);
761 if (!lpmfp->hMF)
762 WINE_FIXME("Couldn't load metafile\n");
764 if (bits != beg + off) HeapFree(GetProcessHeap(), 0, bits);
766 return TRUE;
769 /******************************************************************
770 * HLPFILE_LoadGfxByAddr
774 static BOOL HLPFILE_LoadGfxByAddr(HLPFILE *hlpfile, BYTE* ref,
775 unsigned long size,
776 HLPFILE_PARAGRAPH* paragraph)
778 unsigned i, numpict;
780 numpict = GET_USHORT(ref, 2);
781 WINE_TRACE("Got picture magic=%04x #=%d\n",
782 GET_USHORT(ref, 0), numpict);
784 for (i = 0; i < numpict; i++)
786 BYTE* beg;
787 BYTE* ptr;
788 BYTE type, pack;
790 WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4));
791 beg = ptr = ref + GET_UINT(ref, (1 + i) * 4);
793 type = *ptr++;
794 pack = *ptr++;
796 switch (type)
798 case 5: /* device dependent bmp */
799 case 6: /* device independent bmp */
800 HLPFILE_LoadBitmap(beg, type, pack, paragraph);
801 break;
802 case 8:
803 HLPFILE_LoadMetaFile(beg, pack, paragraph);
804 break;
805 default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
808 /* FIXME: hotspots */
810 /* FIXME: implement support for multiple picture format */
811 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
812 break;
814 return TRUE;
817 /******************************************************************
818 * HLPFILE_LoadGfxByIndex
822 static BOOL HLPFILE_LoadGfxByIndex(HLPFILE *hlpfile, unsigned index,
823 HLPFILE_PARAGRAPH* paragraph)
825 char tmp[16];
826 BYTE *ref, *end;
827 BOOL ret;
829 WINE_TRACE("Loading picture #%d\n", index);
831 if (index < hlpfile->numBmps && hlpfile->bmps[index] != NULL)
833 paragraph->u.gfx.u.bmp.hBitmap = hlpfile->bmps[index];
834 return TRUE;
837 sprintf(tmp, "|bm%u", index);
839 if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
841 ref += 9;
843 ret = HLPFILE_LoadGfxByAddr(hlpfile, ref, end - ref, paragraph);
845 /* cache bitmap */
846 if (ret && paragraph->cookie == para_bitmap)
848 if (index >= hlpfile->numBmps)
850 hlpfile->numBmps = index + 1;
851 if (hlpfile->bmps)
852 hlpfile->bmps = HeapReAlloc(GetProcessHeap(), 0, hlpfile->bmps,
853 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
854 else
855 hlpfile->bmps = HeapAlloc(GetProcessHeap(), 0,
856 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
859 hlpfile->bmps[index] = paragraph->u.gfx.u.bmp.hBitmap;
861 return ret;
864 /******************************************************************
865 * HLPFILE_AllocLink
869 static HLPFILE_LINK* HLPFILE_AllocLink(int cookie, const char* str, LONG hash,
870 BOOL clrChange, unsigned wnd)
872 HLPFILE_LINK* link;
873 char* link_str;
875 /* FIXME: should build a string table for the attributes.link.lpszPath
876 * they are reallocated for each link
878 link = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_LINK) + strlen(str) + 1);
879 if (!link) return NULL;
881 link->cookie = cookie;
882 link->lpszString = link_str = (char*)link + sizeof(HLPFILE_LINK);
883 strcpy(link_str, str);
884 link->lHash = hash;
885 link->bClrChange = clrChange ? 1 : 0;
886 link->window = wnd;
887 link->wRefCount = 1;
889 WINE_TRACE("Link[%d] to %s@%08x:%d\n",
890 link->cookie, link->lpszString,
891 link->lHash, link->window);
892 return link;
895 /***********************************************************************
897 * HLPFILE_AddParagraph
899 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
901 HLPFILE_PAGE *page;
902 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
903 UINT textsize;
904 BYTE *format, *format_end;
905 char *text, *text_base, *text_end;
906 long size;
907 unsigned short bits;
908 unsigned nc, ncol = 1;
910 if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;};
912 for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
913 for (paragraphptr = &page->first_paragraph; *paragraphptr;
914 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
916 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
918 size = GET_UINT(buf, 0x4);
919 text = text_base = HeapAlloc(GetProcessHeap(), 0, size);
920 if (!text) return FALSE;
921 if (hlpfile->hasPhrases)
923 HLPFILE_Uncompress2(buf + GET_UINT(buf, 0x10), end, (BYTE*)text, (BYTE*)text + size);
925 else
927 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
929 /* block is compressed */
930 HLPFILE_Uncompress3(text, text + size, buf + GET_UINT(buf, 0x10), end);
932 else
934 text = (char*)buf + GET_UINT(buf, 0x10);
937 text_end = text + size;
939 format = buf + 0x15;
940 format_end = buf + GET_UINT(buf, 0x10);
942 if (buf[0x14] == 0x20 || buf[0x14] == 0x23)
944 fetch_long(&format);
945 *len = fetch_ushort(&format);
947 else *len = end-buf-15;
949 if (buf[0x14] == 0x23)
951 char type;
953 ncol = *format++;
955 WINE_TRACE("#cols %u\n", ncol);
956 type = *format++;
957 if (type == 0 || type == 2)
958 format += 2;
959 format += ncol * 4;
962 for (nc = 0; nc < ncol; nc++)
964 WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
965 if (buf[0x14] == 0x23)
966 format += 5;
967 if (buf[0x14] == 0x01)
968 format += 6;
969 else
970 format += 4;
971 bits = GET_USHORT(format, 0); format += 2;
972 if (bits & 0x0001) fetch_long(&format);
973 if (bits & 0x0002) fetch_short(&format);
974 if (bits & 0x0004) fetch_short(&format);
975 if (bits & 0x0008) fetch_short(&format);
976 if (bits & 0x0010) fetch_short(&format);
977 if (bits & 0x0020) fetch_short(&format);
978 if (bits & 0x0040) fetch_short(&format);
979 if (bits & 0x0100) format += 3;
980 if (bits & 0x0200)
982 int ntab = fetch_short(&format);
983 unsigned short ts;
985 while (ntab-- > 0)
987 ts = fetch_ushort(&format);
988 if (ts & 0x4000) fetch_ushort(&format);
991 /* 0x0400, 0x0800 and 0x1000 don't need space */
992 if ((bits & 0xE080) != 0)
993 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
995 while (text < text_end && format < format_end)
997 WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text), text, text_end, format, format_end);
998 textsize = strlen(text) + 1;
999 if (textsize > 1)
1001 paragraph = HeapAlloc(GetProcessHeap(), 0,
1002 sizeof(HLPFILE_PARAGRAPH) + textsize);
1003 if (!paragraph) return FALSE;
1004 *paragraphptr = paragraph;
1005 paragraphptr = &paragraph->next;
1007 paragraph->next = NULL;
1008 paragraph->link = attributes.link;
1009 if (paragraph->link) paragraph->link->wRefCount++;
1010 paragraph->cookie = para_normal_text;
1011 paragraph->u.text.wFont = attributes.wFont;
1012 paragraph->u.text.wVSpace = attributes.wVSpace;
1013 paragraph->u.text.wHSpace = attributes.wHSpace;
1014 paragraph->u.text.wIndent = attributes.wIndent;
1015 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
1016 strcpy(paragraph->u.text.lpszText, text);
1018 attributes.wVSpace = 0;
1019 attributes.wHSpace = 0;
1021 /* else: null text, keep on storing attributes */
1022 text += textsize;
1024 if (*format == 0xff)
1026 format++;
1027 break;
1030 WINE_TRACE("format=%02x\n", *format);
1031 switch (*format)
1033 case 0x20:
1034 WINE_FIXME("NIY20\n");
1035 format += 5;
1036 break;
1038 case 0x21:
1039 WINE_FIXME("NIY21\n");
1040 format += 3;
1041 break;
1043 case 0x80:
1044 attributes.wFont = GET_USHORT(format, 1);
1045 WINE_TRACE("Changing font to %d\n", attributes.wFont);
1046 format += 3;
1047 break;
1049 case 0x81:
1050 attributes.wVSpace++;
1051 format += 1;
1052 break;
1054 case 0x82:
1055 attributes.wVSpace++;
1056 attributes.wIndent = 0;
1057 format += 1;
1058 break;
1060 case 0x83:
1061 attributes.wIndent++;
1062 format += 1;
1063 break;
1065 #if 0
1066 case 0x84:
1067 format += 3;
1068 break;
1069 #endif
1071 case 0x86:
1072 case 0x87:
1073 case 0x88:
1075 BYTE pos = (*format - 0x86);
1076 BYTE type = format[1];
1077 long size;
1079 format += 2;
1080 size = fetch_long(&format);
1082 paragraph = HeapAlloc(GetProcessHeap(), 0,
1083 sizeof(HLPFILE_PARAGRAPH) + textsize);
1084 if (!paragraph) return FALSE;
1085 *paragraphptr = paragraph;
1086 paragraphptr = &paragraph->next;
1088 paragraph->next = NULL;
1089 paragraph->link = attributes.link;
1090 if (paragraph->link) paragraph->link->wRefCount++;
1091 paragraph->cookie = para_bitmap;
1092 paragraph->u.gfx.pos = pos;
1093 switch (type)
1095 case 0x22:
1096 fetch_ushort(&format); /* hot spot */
1097 /* fall thru */
1098 case 0x03:
1099 switch (GET_SHORT(format, 0))
1101 case 0:
1102 HLPFILE_LoadGfxByIndex(hlpfile, GET_SHORT(format, 2),
1103 paragraph);
1104 break;
1105 case 1:
1106 WINE_FIXME("does it work ??? %x<%lu>#%u\n",
1107 GET_SHORT(format, 0),
1108 size, GET_SHORT(format, 2));
1109 HLPFILE_LoadGfxByAddr(hlpfile, format + 2, size - 4,
1110 paragraph);
1111 break;
1112 default:
1113 WINE_FIXME("??? %u\n", GET_SHORT(format, 0));
1114 break;
1116 break;
1117 case 0x05:
1118 WINE_FIXME("Got an embedded element %s\n", format + 6);
1119 break;
1120 default:
1121 WINE_FIXME("Got a type %d picture\n", type);
1122 break;
1124 if (attributes.wVSpace) paragraph->u.gfx.pos |= 0x8000;
1126 format += size;
1128 break;
1130 case 0x89:
1131 HLPFILE_FreeLink(attributes.link);
1132 attributes.link = NULL;
1133 format += 1;
1134 break;
1136 case 0x8B:
1137 case 0x8C:
1138 WINE_FIXME("NIY non-break space/hyphen\n");
1139 format += 1;
1140 break;
1142 #if 0
1143 case 0xA9:
1144 format += 2;
1145 break;
1146 #endif
1148 case 0xC8:
1149 case 0xCC:
1150 WINE_TRACE("macro => %s\n", format + 3);
1151 HLPFILE_FreeLink(attributes.link);
1152 attributes.link = HLPFILE_AllocLink(hlp_link_macro, (const char*)format + 3,
1153 0, !(*format & 4), -1);
1154 format += 3 + GET_USHORT(format, 1);
1155 break;
1157 case 0xE0:
1158 case 0xE1:
1159 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
1160 HLPFILE_FreeLink(attributes.link);
1161 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1162 hlpfile->lpszPath,
1163 GET_UINT(format, 1)-16,
1164 1, -1);
1167 format += 5;
1168 break;
1170 case 0xE2:
1171 case 0xE3:
1172 case 0xE6:
1173 case 0xE7:
1174 HLPFILE_FreeLink(attributes.link);
1175 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1176 hlpfile->lpszPath,
1177 GET_UINT(format, 1),
1178 !(*format & 4), -1);
1179 format += 5;
1180 break;
1182 case 0xEA:
1183 case 0xEB:
1184 case 0xEE:
1185 case 0xEF:
1187 char* ptr = (char*) format + 8;
1188 BYTE type = format[3];
1189 int wnd = -1;
1190 char* str;
1192 if (type == 1) wnd = *ptr++;
1193 if (type == 4 || type == 6)
1195 str = ptr;
1196 ptr += strlen(ptr) + 1;
1198 else
1199 str = hlpfile->lpszPath;
1200 if (type == 6)
1202 for (wnd = hlpfile->numWindows - 1; wnd >= 0; wnd--)
1204 if (!strcmp(ptr, hlpfile->windows[wnd].name)) break;
1206 if (wnd == -1)
1207 WINE_WARN("Couldn't find window info for %s\n", ptr);
1209 HLPFILE_FreeLink(attributes.link);
1210 attributes.link = HLPFILE_AllocLink((*format & 4) ? hlp_link_link : hlp_link_popup,
1211 str, GET_UINT(format, 4),
1212 !(*format & 1), wnd);
1214 format += 3 + GET_USHORT(format, 1);
1215 break;
1217 default:
1218 WINE_WARN("format %02x\n", *format);
1219 format++;
1223 HeapFree(GetProcessHeap(), 0, text_base);
1224 return TRUE;
1227 /******************************************************************
1228 * HLPFILE_ReadFont
1232 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
1234 BYTE *ref, *end;
1235 unsigned i, len, idx;
1236 unsigned face_num, dscr_num, face_offset, dscr_offset;
1237 BYTE flag, family;
1239 if (!HLPFILE_FindSubFile("|FONT", &ref, &end))
1241 WINE_WARN("no subfile FONT\n");
1242 hlpfile->numFonts = 0;
1243 hlpfile->fonts = NULL;
1244 return FALSE;
1247 ref += 9;
1249 face_num = GET_USHORT(ref, 0);
1250 dscr_num = GET_USHORT(ref, 2);
1251 face_offset = GET_USHORT(ref, 4);
1252 dscr_offset = GET_USHORT(ref, 6);
1254 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1255 face_num, face_offset, dscr_num, dscr_offset);
1257 hlpfile->numFonts = dscr_num;
1258 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
1260 len = (dscr_offset - face_offset) / face_num;
1261 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
1262 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1263 for (i = 0; i < dscr_num; i++)
1265 flag = ref[dscr_offset + i * 11 + 0];
1266 family = ref[dscr_offset + i * 11 + 2];
1268 hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2;
1269 hlpfile->fonts[i].LogFont.lfWidth = 0;
1270 hlpfile->fonts[i].LogFont.lfEscapement = 0;
1271 hlpfile->fonts[i].LogFont.lfOrientation = 0;
1272 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
1273 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
1274 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
1275 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
1276 hlpfile->fonts[i].LogFont.lfCharSet = ANSI_CHARSET;
1277 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1278 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1279 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
1280 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
1282 switch (family)
1284 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break;
1285 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break;
1286 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break;
1287 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break;
1288 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1289 default: WINE_FIXME("Unknown family %u\n", family);
1291 idx = GET_USHORT(ref, dscr_offset + i * 11 + 3);
1293 if (idx < face_num)
1295 memcpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1296 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1)] = '\0';
1298 else
1300 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1301 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1303 hlpfile->fonts[i].hFont = 0;
1304 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1305 ref[dscr_offset + i * 11 + 6],
1306 ref[dscr_offset + i * 11 + 7]);
1307 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1308 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
1309 i, flag,
1310 X(0, "bold"),
1311 X(1, "italic"),
1312 X(2, "underline"),
1313 X(3, "strikeOut"),
1314 X(4, "dblUnderline"),
1315 X(5, "smallCaps"),
1316 ref[dscr_offset + i * 11 + 1],
1317 family,
1318 hlpfile->fonts[i].LogFont.lfFaceName, idx,
1319 GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1321 return TRUE;
1324 /***********************************************************************
1326 * HLPFILE_ReadFileToBuffer
1328 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1330 BYTE header[16], dummy[1];
1331 UINT size;
1333 if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
1335 /* sanity checks */
1336 if (GET_UINT(header, 0) != 0x00035F3F)
1337 {WINE_WARN("wrong header\n"); return FALSE;};
1339 size = GET_UINT(header, 12);
1340 file_buffer = HeapAlloc(GetProcessHeap(), 0, size + 1);
1341 if (!file_buffer) return FALSE;
1343 memcpy(file_buffer, header, 16);
1344 if (_hread(hFile, file_buffer + 16, size - 16) != size - 16)
1345 {WINE_WARN("filesize1\n"); return FALSE;};
1347 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1349 file_buffer[size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1351 return TRUE;
1354 /***********************************************************************
1356 * HLPFILE_FindSubFile
1358 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1360 BYTE *root = file_buffer + GET_UINT(file_buffer, 4);
1361 BYTE *end = file_buffer + GET_UINT(file_buffer, 12);
1362 BYTE *ptr;
1363 BYTE *bth;
1365 unsigned pgsize;
1366 unsigned pglast;
1367 unsigned nentries;
1368 unsigned i, n;
1370 bth = root + 9;
1372 /* FIXME: this should be using the EnumBTree functions from this file */
1373 pgsize = GET_USHORT(bth, 4);
1374 WINE_TRACE("%s => pgsize=%u #pg=%u rootpg=%u #lvl=%u\n",
1375 name, pgsize, GET_USHORT(bth, 30), GET_USHORT(bth, 26), GET_USHORT(bth, 32));
1377 ptr = bth + 38 + GET_USHORT(bth, 26) * pgsize;
1379 for (n = 1; n < GET_USHORT(bth, 32); n++)
1381 nentries = GET_USHORT(ptr, 2);
1382 pglast = GET_USHORT(ptr, 4);
1383 WINE_TRACE("[%u]: #entries=%u next=%u\n", n, nentries, pglast);
1385 ptr += 6;
1386 for (i = 0; i < nentries; i++)
1388 char *str = (char*) ptr;
1389 WINE_TRACE("<= %s\n", str);
1390 if (strcmp(name, str) < 0) break;
1391 ptr += strlen(str) + 1;
1392 pglast = GET_USHORT(ptr, 0);
1393 ptr += 2;
1395 ptr = bth + 38 + pglast * pgsize;
1398 nentries = GET_USHORT(ptr, 2);
1399 ptr += 8;
1400 for (i = 0; i < nentries; i++)
1402 char* fname = (char*)ptr;
1403 ptr += strlen(fname) + 1;
1404 WINE_TRACE("\\- %s\n", fname);
1405 if (strcmp(fname, name) == 0)
1407 *subbuf = file_buffer + GET_UINT(ptr, 0);
1408 *subend = *subbuf + GET_UINT(*subbuf, 0);
1409 if (file_buffer > *subbuf || *subbuf > *subend || *subend > end)
1411 WINE_WARN("size mismatch\n");
1412 return FALSE;
1414 return TRUE;
1416 ptr += 4;
1419 return FALSE;
1422 /***********************************************************************
1424 * HLPFILE_SystemCommands
1426 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1428 BYTE *buf, *ptr, *end;
1429 HLPFILE_MACRO *macro, **m;
1430 LPSTR p;
1431 unsigned short magic, minor, major, flags;
1433 hlpfile->lpszTitle = NULL;
1435 if (!HLPFILE_FindSubFile("|SYSTEM", &buf, &end)) return FALSE;
1437 magic = GET_USHORT(buf + 9, 0);
1438 minor = GET_USHORT(buf + 9, 2);
1439 major = GET_USHORT(buf + 9, 4);
1440 /* gen date on 4 bytes */
1441 flags = GET_USHORT(buf + 9, 10);
1442 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1443 magic, major, minor, flags);
1444 if (magic != 0x036C || major != 1)
1445 {WINE_WARN("Wrong system header\n"); return FALSE;}
1446 if (minor <= 16)
1448 hlpfile->tbsize = 0x800;
1449 hlpfile->compressed = 0;
1451 else if (flags == 0)
1453 hlpfile->tbsize = 0x1000;
1454 hlpfile->compressed = 0;
1456 else if (flags == 4)
1458 hlpfile->tbsize = 0x1000;
1459 hlpfile->compressed = 1;
1461 else
1463 hlpfile->tbsize = 0x800;
1464 hlpfile->compressed = 1;
1467 if (hlpfile->compressed)
1468 hlpfile->dsize = 0x4000;
1469 else
1470 hlpfile->dsize = hlpfile->tbsize - 0x0C;
1472 hlpfile->version = minor;
1473 hlpfile->flags = flags;
1475 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1477 char *str = (char*) ptr + 4;
1478 switch (GET_USHORT(ptr, 0))
1480 case 1:
1481 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1482 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1483 if (!hlpfile->lpszTitle) return FALSE;
1484 lstrcpy(hlpfile->lpszTitle, str);
1485 WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1486 break;
1488 case 2:
1489 if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
1490 hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1491 if (!hlpfile->lpszCopyright) return FALSE;
1492 lstrcpy(hlpfile->lpszCopyright, str);
1493 WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
1494 break;
1496 case 3:
1497 if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
1498 hlpfile->contents_start = GET_UINT(ptr, 4);
1499 WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
1500 break;
1502 case 4:
1503 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(str) + 1);
1504 if (!macro) break;
1505 p = (char*)macro + sizeof(HLPFILE_MACRO);
1506 lstrcpy(p, str);
1507 macro->lpszMacro = p;
1508 macro->next = 0;
1509 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1510 *m = macro;
1511 break;
1513 case 6:
1514 if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
1516 if (hlpfile->windows)
1517 hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows,
1518 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1519 else
1520 hlpfile->windows = HeapAlloc(GetProcessHeap(), 0,
1521 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1523 if (hlpfile->windows)
1525 unsigned flags = GET_USHORT(ptr, 4);
1526 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
1528 if (flags & 0x0001) strcpy(wi->type, &str[2]);
1529 else wi->type[0] = '\0';
1530 if (flags & 0x0002) strcpy(wi->name, &str[12]);
1531 else wi->name[0] = '\0';
1532 if (flags & 0x0004) strcpy(wi->caption, &str[23]);
1533 else lstrcpynA(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
1534 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
1535 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
1536 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
1537 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
1538 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
1539 wi->win_style = WS_OVERLAPPEDWINDOW;
1540 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
1541 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
1542 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",
1543 flags & 0x0001 ? 'T' : 't',
1544 flags & 0x0002 ? 'N' : 'n',
1545 flags & 0x0004 ? 'C' : 'c',
1546 flags & 0x0008 ? 'X' : 'x',
1547 flags & 0x0010 ? 'Y' : 'y',
1548 flags & 0x0020 ? 'W' : 'w',
1549 flags & 0x0040 ? 'H' : 'h',
1550 flags & 0x0080 ? 'S' : 's',
1551 wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
1552 wi->size.cx, wi->size.cy);
1554 break;
1555 default:
1556 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1559 if (!hlpfile->lpszTitle)
1560 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1);
1561 return TRUE;
1564 /***********************************************************************
1566 * HLPFILE_UncompressedLZ77_Size
1568 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1570 int i, newsize = 0;
1572 while (ptr < end)
1574 int mask = *ptr++;
1575 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1577 if (mask & 1)
1579 int code = GET_USHORT(ptr, 0);
1580 int len = 3 + (code >> 12);
1581 newsize += len;
1582 ptr += 2;
1584 else newsize++, ptr++;
1588 return newsize;
1591 /***********************************************************************
1593 * HLPFILE_UncompressLZ77
1595 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1597 int i;
1599 while (ptr < end)
1601 int mask = *ptr++;
1602 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1604 if (mask & 1)
1606 int code = GET_USHORT(ptr, 0);
1607 int len = 3 + (code >> 12);
1608 int offset = code & 0xfff;
1610 * We must copy byte-by-byte here. We cannot use memcpy nor
1611 * memmove here. Just example:
1612 * a[]={1,2,3,4,5,6,7,8,9,10}
1613 * newptr=a+2;
1614 * offset=1;
1615 * We expect:
1616 * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12}
1618 for (; len>0; len--, newptr++) *newptr = *(newptr-offset-1);
1619 ptr += 2;
1621 else *newptr++ = *ptr++;
1625 return newptr;
1628 /***********************************************************************
1630 * HLPFILE_UncompressLZ77_Phrases
1632 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1634 UINT i, num, dec_size, head_size;
1635 BYTE *buf, *end;
1637 if (!HLPFILE_FindSubFile("|Phrases", &buf, &end)) return FALSE;
1639 if (hlpfile->version <= 16)
1640 head_size = 13;
1641 else
1642 head_size = 17;
1644 num = phrases.num = GET_USHORT(buf, 9);
1645 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1647 if (hlpfile->version <= 16)
1648 dec_size = end - buf - 15 - 2 * num;
1649 else
1650 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1652 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1653 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1654 if (!phrases.offsets || !phrases.buffer) return FALSE;
1656 for (i = 0; i <= num; i++)
1657 phrases.offsets[i] = GET_USHORT(buf, head_size + 2 * i) - 2 * num - 2;
1659 if (hlpfile->version <= 16)
1660 memcpy(phrases.buffer, buf + 15 + 2*num, dec_size);
1661 else
1662 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, (BYTE*)phrases.buffer);
1664 hlpfile->hasPhrases = TRUE;
1665 return TRUE;
1668 /***********************************************************************
1670 * HLPFILE_Uncompress_Phrases40
1672 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1674 UINT num;
1675 INT dec_size, cpr_size;
1676 BYTE *buf_idx, *end_idx;
1677 BYTE *buf_phs, *end_phs;
1678 long* ptr, mask = 0;
1679 unsigned int i;
1680 unsigned short bc, n;
1682 if (!HLPFILE_FindSubFile("|PhrIndex", &buf_idx, &end_idx) ||
1683 !HLPFILE_FindSubFile("|PhrImage", &buf_phs, &end_phs)) return FALSE;
1685 ptr = (long*)(buf_idx + 9 + 28);
1686 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1687 num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1689 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1690 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1691 GET_UINT(buf_idx, 9 + 0),
1692 GET_UINT(buf_idx, 9 + 4),
1693 GET_UINT(buf_idx, 9 + 8),
1694 GET_UINT(buf_idx, 9 + 12),
1695 GET_UINT(buf_idx, 9 + 16),
1696 GET_UINT(buf_idx, 9 + 20),
1697 GET_USHORT(buf_idx, 9 + 24),
1698 GET_USHORT(buf_idx, 9 + 26));
1700 dec_size = GET_UINT(buf_idx, 9 + 12);
1701 cpr_size = GET_UINT(buf_idx, 9 + 16);
1703 if (dec_size != cpr_size &&
1704 dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1706 WINE_WARN("size mismatch %u %u\n",
1707 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1708 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1711 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1712 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1713 if (!phrases.offsets || !phrases.buffer) return FALSE;
1715 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1717 phrases.offsets[0] = 0;
1718 for (i = 0; i < num; i++)
1720 for (n = 1; getbit(); n += 1 << bc);
1721 if (getbit()) n++;
1722 if (bc > 1 && getbit()) n += 2;
1723 if (bc > 2 && getbit()) n += 4;
1724 if (bc > 3 && getbit()) n += 8;
1725 if (bc > 4 && getbit()) n += 16;
1726 phrases.offsets[i + 1] = phrases.offsets[i] + n;
1728 #undef getbit
1730 if (dec_size == cpr_size)
1731 memcpy(phrases.buffer, buf_phs + 9, dec_size);
1732 else
1733 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, (BYTE*)phrases.buffer);
1735 hlpfile->hasPhrases = FALSE;
1736 return TRUE;
1739 /***********************************************************************
1741 * HLPFILE_Uncompress_Topic
1743 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
1745 BYTE *buf, *ptr, *end, *newptr;
1746 unsigned int i, newsize = 0;
1747 unsigned int topic_size;
1749 if (!HLPFILE_FindSubFile("|TOPIC", &buf, &end))
1750 {WINE_WARN("topic0\n"); return FALSE;}
1752 buf += 9; /* Skip file header */
1753 topic_size = end - buf;
1754 if (hlpfile->compressed)
1756 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1758 for (i = 0; i < topic.wMapLen; i++)
1760 ptr = buf + i * hlpfile->tbsize;
1762 /* I don't know why, it's necessary for printman.hlp */
1763 if (ptr + 0x44 > end) ptr = end - 0x44;
1765 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + hlpfile->tbsize));
1768 topic.map = HeapAlloc(GetProcessHeap(), 0,
1769 topic.wMapLen * sizeof(topic.map[0]) + newsize);
1770 if (!topic.map) return FALSE;
1771 newptr = (BYTE*)(topic.map + topic.wMapLen);
1772 topic.end = newptr + newsize;
1774 for (i = 0; i < topic.wMapLen; i++)
1776 ptr = buf + i * hlpfile->tbsize;
1777 if (ptr + 0x44 > end) ptr = end - 0x44;
1779 topic.map[i] = newptr;
1780 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + hlpfile->tbsize), newptr);
1783 else
1785 /* basically, we need to copy the TopicBlockSize byte pages
1786 * (removing the first 0x0C) in one single area in memory
1788 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1789 topic.map = HeapAlloc(GetProcessHeap(), 0,
1790 topic.wMapLen * (sizeof(topic.map[0]) + hlpfile->dsize));
1791 if (!topic.map) return FALSE;
1792 newptr = (BYTE*)(topic.map + topic.wMapLen);
1793 topic.end = newptr + topic_size;
1795 for (i = 0; i < topic.wMapLen; i++)
1797 topic.map[i] = newptr + i * hlpfile->dsize;
1798 memcpy(topic.map[i], buf + i * hlpfile->tbsize + 0x0C, hlpfile->dsize);
1801 return TRUE;
1804 /***********************************************************************
1806 * HLPFILE_Uncompress2
1809 static void HLPFILE_Uncompress2(const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
1811 BYTE *phptr, *phend;
1812 UINT code;
1813 UINT index;
1815 while (ptr < end && newptr < newend)
1817 if (!*ptr || *ptr >= 0x10)
1818 *newptr++ = *ptr++;
1819 else
1821 code = 0x100 * ptr[0] + ptr[1];
1822 index = (code - 0x100) / 2;
1824 phptr = (BYTE*)phrases.buffer + phrases.offsets[index];
1825 phend = (BYTE*)phrases.buffer + phrases.offsets[index + 1];
1827 if (newptr + (phend - phptr) > newend)
1829 WINE_FIXME("buffer overflow %p > %p for %d bytes\n",
1830 newptr, newend, phend - phptr);
1831 return;
1833 memcpy(newptr, phptr, phend - phptr);
1834 newptr += phend - phptr;
1835 if (code & 1) *newptr++ = ' ';
1837 ptr += 2;
1840 if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
1843 /******************************************************************
1844 * HLPFILE_Uncompress3
1848 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1849 const BYTE* src, const BYTE* src_end)
1851 unsigned int idx, len;
1853 for (; src < src_end; src++)
1855 if ((*src & 1) == 0)
1857 idx = *src / 2;
1858 if (idx > phrases.num)
1860 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1861 len = 0;
1863 else
1865 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1866 if (dst + len <= dst_end)
1867 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1870 else if ((*src & 0x03) == 0x01)
1872 idx = (*src + 1) * 64;
1873 idx += *++src;
1874 if (idx > phrases.num)
1876 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1877 len = 0;
1879 else
1881 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1882 if (dst + len <= dst_end)
1883 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1886 else if ((*src & 0x07) == 0x03)
1888 len = (*src / 8) + 1;
1889 if (dst + len <= dst_end)
1890 memcpy(dst, src + 1, len);
1891 src += len;
1893 else
1895 len = (*src / 16) + 1;
1896 if (dst + len <= dst_end)
1897 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1899 dst += len;
1902 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1903 return TRUE;
1906 /******************************************************************
1907 * HLPFILE_UncompressRLE
1911 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz)
1913 BYTE ch;
1914 BYTE* sdst = *dst + dstsz;
1916 while (src < end)
1918 ch = *src++;
1919 if (ch & 0x80)
1921 ch &= 0x7F;
1922 if ((*dst) + ch <= sdst)
1923 memcpy(*dst, src, ch);
1924 src += ch;
1926 else
1928 if ((*dst) + ch <= sdst)
1929 memset(*dst, (char)*src, ch);
1930 src++;
1932 *dst += ch;
1934 if (*dst != sdst)
1935 WINE_WARN("Buffer X-flow: d(%u) instead of d(%u)\n",
1936 *dst - (sdst - dstsz), dstsz);
1939 /**************************************************************************
1940 * HLPFILE_BPTreeSearch
1942 * Searches for an element in B+ tree
1944 * PARAMS
1945 * buf [I] pointer to the embedded file structured as a B+ tree
1946 * key [I] pointer to data to find
1947 * comp [I] compare function
1949 * RETURNS
1950 * Pointer to block identified by key, or NULL if failure.
1953 static void* HLPFILE_BPTreeSearch(BYTE* buf, const void* key,
1954 HLPFILE_BPTreeCompare comp)
1956 unsigned magic;
1957 unsigned page_size;
1958 unsigned cur_page;
1959 unsigned level;
1960 BYTE *pages, *ptr, *newptr;
1961 int i, entries;
1962 int ret;
1964 magic = GET_USHORT(buf, 9);
1965 if (magic != 0x293B)
1967 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic);
1968 return NULL;
1970 page_size = GET_USHORT(buf, 9+4);
1971 cur_page = GET_USHORT(buf, 9+26);
1972 level = GET_USHORT(buf, 9+32);
1973 pages = buf + 9 + 38;
1974 while (--level > 0)
1976 ptr = pages + cur_page*page_size;
1977 entries = GET_SHORT(ptr, 2);
1978 ptr += 6;
1979 for (i = 0; i < entries; i++)
1981 if (comp(ptr, key, 0, (void **)&newptr) > 0) break;
1982 ptr = newptr;
1984 cur_page = GET_USHORT(ptr-2, 0);
1986 ptr = pages + cur_page*page_size;
1987 entries = GET_SHORT(ptr, 2);
1988 ptr += 8;
1989 for (i = 0; i < entries; i++)
1991 ret = comp(ptr, key, 1, (void **)&newptr);
1992 if (ret == 0) return ptr;
1993 if (ret > 0) return NULL;
1994 ptr = newptr;
1996 return NULL;
2000 /***********************************************************************
2002 * HLPFILE_GetContext
2004 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
2006 BYTE *cbuf, *cend;
2007 unsigned clen;
2009 if (!HLPFILE_FindSubFile("|CONTEXT", &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
2011 clen = cend - cbuf;
2012 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen);
2013 if (!hlpfile->Context) return FALSE;
2014 memcpy(hlpfile->Context, cbuf, clen);
2016 return TRUE;
2019 /***********************************************************************
2021 * HLPFILE_GetMap
2023 static BOOL HLPFILE_GetMap(HLPFILE *hlpfile)
2025 BYTE *cbuf, *cend;
2026 unsigned entries, i;
2028 if (!HLPFILE_FindSubFile("|CTXOMAP", &cbuf, &cend)) {WINE_WARN("no map section\n"); return FALSE;}
2030 entries = GET_USHORT(cbuf, 9);
2031 hlpfile->Map = HeapAlloc(GetProcessHeap(), 0, entries * sizeof(HLPFILE_MAP));
2032 if (!hlpfile->Map) return FALSE;
2033 hlpfile->wMapLen = entries;
2034 for (i = 0; i < entries; i++)
2036 hlpfile->Map[i].lMap = GET_UINT(cbuf+11,i*8);
2037 hlpfile->Map[i].offset = GET_UINT(cbuf+11,i*8+4);
2039 return TRUE;
2042 /******************************************************************
2043 * HLPFILE_DeleteLink
2047 void HLPFILE_FreeLink(HLPFILE_LINK* link)
2049 if (link && !--link->wRefCount)
2050 HeapFree(GetProcessHeap(), 0, link);
2053 /***********************************************************************
2055 * HLPFILE_DeleteParagraph
2057 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
2059 HLPFILE_PARAGRAPH* next;
2061 while (paragraph)
2063 next = paragraph->next;
2065 if (paragraph->cookie == para_metafile)
2066 DeleteMetaFile(paragraph->u.gfx.u.mfp.hMF);
2068 HLPFILE_FreeLink(paragraph->link);
2070 HeapFree(GetProcessHeap(), 0, paragraph);
2071 paragraph = next;
2075 /***********************************************************************
2077 * DeleteMacro
2079 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
2081 HLPFILE_MACRO* next;
2083 while (macro)
2085 next = macro->next;
2086 HeapFree(GetProcessHeap(), 0, macro);
2087 macro = next;
2091 /***********************************************************************
2093 * DeletePage
2095 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
2097 HLPFILE_PAGE* next;
2099 while (page)
2101 next = page->next;
2102 HLPFILE_DeleteParagraph(page->first_paragraph);
2103 HLPFILE_DeleteMacro(page->first_macro);
2104 HeapFree(GetProcessHeap(), 0, page);
2105 page = next;
2109 /***********************************************************************
2111 * HLPFILE_FreeHlpFile
2113 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
2115 unsigned i;
2117 if (!hlpfile || --hlpfile->wRefCount > 0) return;
2119 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
2120 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
2121 else first_hlpfile = hlpfile->next;
2123 if (hlpfile->numFonts)
2125 for (i = 0; i < hlpfile->numFonts; i++)
2127 DeleteObject(hlpfile->fonts[i].hFont);
2129 HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
2132 if (hlpfile->numBmps)
2134 for (i = 0; i < hlpfile->numBmps; i++)
2136 DeleteObject(hlpfile->bmps[i]);
2138 HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
2141 HLPFILE_DeletePage(hlpfile->first_page);
2142 HLPFILE_DeleteMacro(hlpfile->first_macro);
2144 if (hlpfile->numWindows) HeapFree(GetProcessHeap(), 0, hlpfile->windows);
2145 HeapFree(GetProcessHeap(), 0, hlpfile->Context);
2146 HeapFree(GetProcessHeap(), 0, hlpfile->Map);
2147 HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
2148 HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
2149 HeapFree(GetProcessHeap(), 0, hlpfile);