winhelp: Enlarge font size by 3 as native winhelp does.
[wine.git] / programs / winhelp / hlpfile.c
blobf9a6cc1fc522a611b7c2983473f5e3c7170a73a4
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(), 0, sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
288 if (!hlpfile) return 0;
290 hlpfile->lpszPath = (char*)hlpfile + sizeof(HLPFILE);
291 hlpfile->lpszTitle = NULL;
292 hlpfile->lpszCopyright = NULL;
293 hlpfile->first_page = NULL;
294 hlpfile->first_macro = NULL;
295 hlpfile->Context = NULL;
296 hlpfile->wMapLen = 0;
297 hlpfile->Map = NULL;
298 hlpfile->contents_start = 0xFFFFFFFF;
299 hlpfile->prev = NULL;
300 hlpfile->next = first_hlpfile;
301 hlpfile->wRefCount = 1;
303 hlpfile->numBmps = 0;
304 hlpfile->bmps = NULL;
306 hlpfile->numFonts = 0;
307 hlpfile->fonts = NULL;
309 hlpfile->numWindows = 0;
310 hlpfile->windows = NULL;
312 strcpy(hlpfile->lpszPath, lpszPath);
314 first_hlpfile = hlpfile;
315 if (hlpfile->next) hlpfile->next->prev = hlpfile;
317 phrases.offsets = NULL;
318 phrases.buffer = NULL;
319 topic.map = NULL;
320 topic.end = NULL;
321 file_buffer = NULL;
323 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
325 HLPFILE_FreeHlpFile(hlpfile);
326 hlpfile = 0;
329 HeapFree(GetProcessHeap(), 0, phrases.offsets);
330 HeapFree(GetProcessHeap(), 0, phrases.buffer);
331 HeapFree(GetProcessHeap(), 0, topic.map);
332 HeapFree(GetProcessHeap(), 0, file_buffer);
334 return hlpfile;
337 /***********************************************************************
339 * HLPFILE_DoReadHlpFile
341 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
343 BOOL ret;
344 HFILE hFile;
345 OFSTRUCT ofs;
346 BYTE* buf;
347 DWORD ref = 0x0C;
348 unsigned index, old_index, offset, len, offs;
350 hFile = OpenFile(lpszPath, &ofs, OF_READ);
351 if (hFile == HFILE_ERROR) return FALSE;
353 ret = HLPFILE_ReadFileToBuffer(hFile);
354 _lclose(hFile);
355 if (!ret) return FALSE;
357 if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
359 /* load phrases support */
360 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile))
361 HLPFILE_Uncompress_Phrases40(hlpfile);
363 if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE;
364 if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
366 buf = topic.map[0];
367 old_index = -1;
368 offs = 0;
371 BYTE* end;
373 index = (ref - 0x0C) / hlpfile->dsize;
374 offset = (ref - 0x0C) % hlpfile->dsize;
376 if (hlpfile->version <= 16 && index != old_index && index != 0)
378 /* we jumped to the next block, adjust pointers */
379 ref -= 12;
380 offset -= 12;
383 WINE_TRACE("ref=%08x => [%u/%u]\n", ref, index, offset);
385 if (index >= topic.wMapLen) {WINE_WARN("maplen\n"); break;}
386 buf = topic.map[index] + offset;
387 if (buf + 0x15 >= topic.end) {WINE_WARN("extra\n"); break;}
388 end = min(buf + GET_UINT(buf, 0), topic.end);
389 if (index != old_index) {offs = 0; old_index = index;}
391 switch (buf[0x14])
393 case 0x02:
394 if (!HLPFILE_AddPage(hlpfile, buf, end, index * 0x8000L + offs)) return FALSE;
395 break;
397 case 0x01:
398 case 0x20:
399 case 0x23:
400 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
401 offs += len;
402 break;
404 default:
405 WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
408 if (hlpfile->version <= 16)
410 ref += GET_UINT(buf, 0xc);
411 if (GET_UINT(buf, 0xc) == 0)
412 break;
414 else
415 ref = GET_UINT(buf, 0xc);
416 } while (ref != 0xffffffff);
418 HLPFILE_GetMap(hlpfile);
419 if (hlpfile->version <= 16) return TRUE;
420 return HLPFILE_GetContext(hlpfile);
423 /***********************************************************************
425 * HLPFILE_AddPage
427 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned offset)
429 HLPFILE_PAGE* page;
430 BYTE* title;
431 UINT titlesize;
432 char* ptr;
433 HLPFILE_MACRO*macro;
435 title = buf + GET_UINT(buf, 0x10);
436 if (title > end) {WINE_WARN("page2\n"); return FALSE;};
438 titlesize = GET_UINT(buf, 4);
439 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
440 if (!page) return FALSE;
441 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
443 if (hlpfile->hasPhrases)
445 HLPFILE_Uncompress2(title, end, (BYTE*)page->lpszTitle, (BYTE*)page->lpszTitle + titlesize);
447 else
449 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
451 /* need to decompress */
452 HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize,
453 title, end);
455 else
457 memcpy(page->lpszTitle, title, titlesize);
461 page->lpszTitle[titlesize] = '\0';
463 if (hlpfile->first_page)
465 HLPFILE_PAGE *p;
467 for (p = hlpfile->first_page; p->next; p = p->next);
468 page->prev = p;
469 p->next = page;
471 else
473 hlpfile->first_page = page;
474 page->prev = NULL;
477 page->file = hlpfile;
478 page->next = NULL;
479 page->first_paragraph = NULL;
480 page->first_macro = NULL;
481 page->wNumber = GET_UINT(buf, 0x21);
482 page->offset = offset;
484 page->browse_bwd = GET_UINT(buf, 0x19);
485 page->browse_fwd = GET_UINT(buf, 0x1D);
487 WINE_TRACE("Added page[%d]: title='%s' %08x << %08x >> %08x\n",
488 page->wNumber, page->lpszTitle,
489 page->browse_bwd, page->offset, page->browse_fwd);
491 memset(&attributes, 0, sizeof(attributes));
493 /* now load macros */
494 ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
495 while (ptr < page->lpszTitle + titlesize)
497 unsigned len = strlen(ptr);
498 char* macro_str;
500 WINE_TRACE("macro: %s\n", ptr);
501 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
502 macro->lpszMacro = macro_str = (char*)(macro + 1);
503 memcpy(macro_str, ptr, len + 1);
504 /* FIXME: shall we really link macro in reverse order ??
505 * may produce strange results when played at page opening
507 macro->next = page->first_macro;
508 page->first_macro = macro;
509 ptr += len + 1;
512 return TRUE;
515 static long fetch_long(BYTE** ptr)
517 long ret;
519 if (*(*ptr) & 1)
521 ret = (*(unsigned long*)(*ptr) - 0x80000000L) / 2;
522 (*ptr) += 4;
524 else
526 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
527 (*ptr) += 2;
530 return ret;
533 static unsigned long fetch_ulong(BYTE** ptr)
535 unsigned long ret;
537 if (*(*ptr) & 1)
539 ret = *(unsigned long*)(*ptr) / 2;
540 (*ptr) += 4;
542 else
544 ret = *(unsigned short*)(*ptr) / 2;
545 (*ptr) += 2;
547 return ret;
550 static short fetch_short(BYTE** ptr)
552 short ret;
554 if (*(*ptr) & 1)
556 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
557 (*ptr) += 2;
559 else
561 ret = (*(unsigned char*)(*ptr) - 0x80) / 2;
562 (*ptr)++;
564 return ret;
567 static unsigned short fetch_ushort(BYTE** ptr)
569 unsigned short ret;
571 if (*(*ptr) & 1)
573 ret = *(unsigned short*)(*ptr) / 2;
574 (*ptr) += 2;
576 else
578 ret = *(unsigned char*)(*ptr) / 2;
579 (*ptr)++;
581 return ret;
584 /******************************************************************
585 * HLPFILE_DecompressGfx
587 * Decompress the data part of a bitmap or a metafile
589 static BYTE* HLPFILE_DecompressGfx(BYTE* src, unsigned csz, unsigned sz, BYTE packing)
591 BYTE* dst;
592 BYTE* tmp;
593 BYTE* tmp2;
594 unsigned sz77;
596 WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing, csz, sz);
598 switch (packing)
600 case 0: /* uncompressed */
601 if (sz != csz)
602 WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz, csz);
603 dst = src;
604 break;
605 case 1: /* RunLen */
606 tmp = dst = HeapAlloc(GetProcessHeap(), 0, sz);
607 if (!dst) return NULL;
608 HLPFILE_UncompressRLE(src, src + csz, &tmp, sz);
609 if (tmp - dst != sz)
610 WINE_WARN("Bogus gfx sizes (RunLen): %u/%u\n", tmp - dst, sz);
611 break;
612 case 2: /* LZ77 */
613 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
614 dst = HeapAlloc(GetProcessHeap(), 0, sz77);
615 if (!dst) return NULL;
616 HLPFILE_UncompressLZ77(src, src + csz, dst);
617 if (sz77 != sz)
618 WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77, sz);
619 break;
620 case 3: /* LZ77 then RLE */
621 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
622 tmp = HeapAlloc(GetProcessHeap(), 0, sz77);
623 if (!tmp) return FALSE;
624 HLPFILE_UncompressLZ77(src, src + csz, tmp);
625 dst = tmp2 = HeapAlloc(GetProcessHeap(), 0, sz);
626 if (!dst)
628 HeapFree(GetProcessHeap(), 0, tmp);
629 return FALSE;
631 HLPFILE_UncompressRLE(tmp, tmp + sz77, &tmp2, sz);
632 if (tmp2 - dst != sz)
633 WINE_WARN("Bogus gfx sizes (LZ77+RunLen): %u / %u\n", tmp2 - dst, sz);
634 HeapFree(GetProcessHeap(), 0, tmp);
635 break;
636 default:
637 WINE_FIXME("Unsupported packing %u\n", packing);
638 return NULL;
640 return dst;
643 /******************************************************************
644 * HLPFILE_LoadBitmap
648 static BOOL HLPFILE_LoadBitmap(BYTE* beg, BYTE type, BYTE pack,
649 HLPFILE_PARAGRAPH* paragraph)
651 BYTE* ptr;
652 BYTE* pict_beg;
653 BITMAPINFO* bi;
654 unsigned long off, csz;
655 HDC hdc;
657 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
658 if (!bi) return FALSE;
660 ptr = beg + 2; /* for type and pack */
662 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
663 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
664 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
665 bi->bmiHeader.biPlanes = fetch_ushort(&ptr);
666 bi->bmiHeader.biBitCount = fetch_ushort(&ptr);
667 bi->bmiHeader.biWidth = fetch_ulong(&ptr);
668 bi->bmiHeader.biHeight = fetch_ulong(&ptr);
669 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr);
670 bi->bmiHeader.biClrImportant = fetch_ulong(&ptr);
671 bi->bmiHeader.biCompression = BI_RGB;
672 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
673 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
674 bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
675 WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n",
676 bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount,
677 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
679 csz = fetch_ulong(&ptr);
680 fetch_ulong(&ptr); /* hotspot size */
682 off = GET_UINT(ptr, 0); ptr += 4;
683 /* GET_UINT(ptr, 0); hotspot offset */ ptr += 4;
685 /* now read palette info */
686 if (type == 0x06)
688 unsigned nc = bi->bmiHeader.biClrUsed;
689 unsigned i;
691 /* not quite right, especially for bitfields type of compression */
692 if (!nc && bi->bmiHeader.biBitCount <= 8)
693 nc = 1 << bi->bmiHeader.biBitCount;
695 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
696 if (!bi) return FALSE;
697 for (i = 0; i < nc; i++)
699 bi->bmiColors[i].rgbBlue = ptr[0];
700 bi->bmiColors[i].rgbGreen = ptr[1];
701 bi->bmiColors[i].rgbRed = ptr[2];
702 bi->bmiColors[i].rgbReserved = 0;
703 ptr += 4;
706 pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack);
708 paragraph->u.gfx.u.bmp.hBitmap = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader,
709 CBM_INIT, pict_beg,
710 bi, DIB_RGB_COLORS);
711 ReleaseDC(0, hdc);
712 if (!paragraph->u.gfx.u.bmp.hBitmap)
713 WINE_ERR("Couldn't create bitmap\n");
715 HeapFree(GetProcessHeap(), 0, bi);
716 if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
718 return TRUE;
721 /******************************************************************
722 * HLPFILE_LoadMetaFile
726 static BOOL HLPFILE_LoadMetaFile(BYTE* beg, BYTE pack, HLPFILE_PARAGRAPH* paragraph)
728 BYTE* ptr;
729 unsigned long size, csize;
730 unsigned long off, hsoff;
731 BYTE* bits;
732 LPMETAFILEPICT lpmfp;
734 WINE_TRACE("Loading metafile\n");
736 ptr = beg + 2; /* for type and pack */
738 lpmfp = &paragraph->u.gfx.u.mfp;
739 lpmfp->mm = fetch_ushort(&ptr); /* mapping mode */
741 lpmfp->xExt = GET_USHORT(ptr, 0);
742 lpmfp->yExt = GET_USHORT(ptr, 2);
743 ptr += 4;
745 size = fetch_ulong(&ptr); /* decompressed size */
746 csize = fetch_ulong(&ptr); /* compressed size */
747 fetch_ulong(&ptr); /* hotspot size */
748 off = GET_UINT(ptr, 0);
749 hsoff = GET_UINT(ptr, 4);
750 ptr += 8;
752 WINE_TRACE("sz=%lu csz=%lu (%d,%d) offs=%lu/%u,%lu\n",
753 size, csize, lpmfp->xExt, lpmfp->yExt, off, ptr - beg, hsoff);
755 bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack);
756 if (!bits) return FALSE;
758 paragraph->cookie = para_metafile;
760 lpmfp->hMF = SetMetaFileBitsEx(size, bits);
762 if (!lpmfp->hMF)
763 WINE_FIXME("Couldn't load metafile\n");
765 if (bits != beg + off) HeapFree(GetProcessHeap(), 0, bits);
767 return TRUE;
770 /******************************************************************
771 * HLPFILE_LoadGfxByAddr
775 static BOOL HLPFILE_LoadGfxByAddr(HLPFILE *hlpfile, BYTE* ref,
776 unsigned long size,
777 HLPFILE_PARAGRAPH* paragraph)
779 unsigned i, numpict;
781 numpict = GET_USHORT(ref, 2);
782 WINE_TRACE("Got picture magic=%04x #=%d\n",
783 GET_USHORT(ref, 0), numpict);
785 for (i = 0; i < numpict; i++)
787 BYTE* beg;
788 BYTE* ptr;
789 BYTE type, pack;
791 WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4));
792 beg = ptr = ref + GET_UINT(ref, (1 + i) * 4);
794 type = *ptr++;
795 pack = *ptr++;
797 switch (type)
799 case 5: /* device dependent bmp */
800 case 6: /* device independent bmp */
801 HLPFILE_LoadBitmap(beg, type, pack, paragraph);
802 break;
803 case 8:
804 HLPFILE_LoadMetaFile(beg, pack, paragraph);
805 break;
806 default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
809 /* FIXME: hotspots */
811 /* FIXME: implement support for multiple picture format */
812 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
813 break;
815 return TRUE;
818 /******************************************************************
819 * HLPFILE_LoadGfxByIndex
823 static BOOL HLPFILE_LoadGfxByIndex(HLPFILE *hlpfile, unsigned index,
824 HLPFILE_PARAGRAPH* paragraph)
826 char tmp[16];
827 BYTE *ref, *end;
828 BOOL ret;
830 WINE_TRACE("Loading picture #%d\n", index);
832 if (index < hlpfile->numBmps && hlpfile->bmps[index] != NULL)
834 paragraph->u.gfx.u.bmp.hBitmap = hlpfile->bmps[index];
835 return TRUE;
838 sprintf(tmp, "|bm%u", index);
840 if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
842 ref += 9;
844 ret = HLPFILE_LoadGfxByAddr(hlpfile, ref, end - ref, paragraph);
846 /* cache bitmap */
847 if (ret && paragraph->cookie == para_bitmap)
849 if (index >= hlpfile->numBmps)
851 hlpfile->numBmps = index + 1;
852 if (hlpfile->bmps)
853 hlpfile->bmps = HeapReAlloc(GetProcessHeap(), 0, hlpfile->bmps,
854 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
855 else
856 hlpfile->bmps = HeapAlloc(GetProcessHeap(), 0,
857 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
860 hlpfile->bmps[index] = paragraph->u.gfx.u.bmp.hBitmap;
862 return ret;
865 /******************************************************************
866 * HLPFILE_AllocLink
870 static HLPFILE_LINK* HLPFILE_AllocLink(int cookie, const char* str, LONG hash,
871 BOOL clrChange, unsigned wnd)
873 HLPFILE_LINK* link;
874 char* link_str;
876 /* FIXME: should build a string table for the attributes.link.lpszPath
877 * they are reallocated for each link
879 link = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_LINK) + strlen(str) + 1);
880 if (!link) return NULL;
882 link->cookie = cookie;
883 link->lpszString = link_str = (char*)link + sizeof(HLPFILE_LINK);
884 strcpy(link_str, str);
885 link->lHash = hash;
886 link->bClrChange = clrChange ? 1 : 0;
887 link->window = wnd;
888 link->wRefCount = 1;
890 WINE_TRACE("Link[%d] to %s@%08x:%d\n",
891 link->cookie, link->lpszString,
892 link->lHash, link->window);
893 return link;
896 /***********************************************************************
898 * HLPFILE_AddParagraph
900 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
902 HLPFILE_PAGE *page;
903 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
904 UINT textsize;
905 BYTE *format, *format_end;
906 char *text, *text_base, *text_end;
907 long size;
908 unsigned short bits;
909 unsigned nc, ncol = 1;
911 if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;};
913 for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
914 for (paragraphptr = &page->first_paragraph; *paragraphptr;
915 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
917 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
919 size = GET_UINT(buf, 0x4);
920 text = text_base = HeapAlloc(GetProcessHeap(), 0, size);
921 if (!text) return FALSE;
922 if (hlpfile->hasPhrases)
924 HLPFILE_Uncompress2(buf + GET_UINT(buf, 0x10), end, (BYTE*)text, (BYTE*)text + size);
926 else
928 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
930 /* block is compressed */
931 HLPFILE_Uncompress3(text, text + size, buf + GET_UINT(buf, 0x10), end);
933 else
935 text = (char*)buf + GET_UINT(buf, 0x10);
938 text_end = text + size;
940 format = buf + 0x15;
941 format_end = buf + GET_UINT(buf, 0x10);
943 if (buf[0x14] == 0x20 || buf[0x14] == 0x23)
945 fetch_long(&format);
946 *len = fetch_ushort(&format);
948 else *len = end-buf-15;
950 if (buf[0x14] == 0x23)
952 char type;
954 ncol = *format++;
956 WINE_TRACE("#cols %u\n", ncol);
957 type = *format++;
958 if (type == 0 || type == 2)
959 format += 2;
960 format += ncol * 4;
963 for (nc = 0; nc < ncol; nc++)
965 WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
966 if (buf[0x14] == 0x23)
967 format += 5;
968 if (buf[0x14] == 0x01)
969 format += 6;
970 else
971 format += 4;
972 bits = GET_USHORT(format, 0); format += 2;
973 if (bits & 0x0001) fetch_long(&format);
974 if (bits & 0x0002) fetch_short(&format);
975 if (bits & 0x0004) fetch_short(&format);
976 if (bits & 0x0008) fetch_short(&format);
977 if (bits & 0x0010) fetch_short(&format);
978 if (bits & 0x0020) fetch_short(&format);
979 if (bits & 0x0040) fetch_short(&format);
980 if (bits & 0x0100) format += 3;
981 if (bits & 0x0200)
983 int ntab = fetch_short(&format);
984 unsigned short ts;
986 while (ntab-- > 0)
988 ts = fetch_ushort(&format);
989 if (ts & 0x4000) fetch_ushort(&format);
992 /* 0x0400, 0x0800 and 0x1000 don't need space */
993 if ((bits & 0xE080) != 0)
994 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
996 while (text < text_end && format < format_end)
998 WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text), text, text_end, format, format_end);
999 textsize = strlen(text) + 1;
1000 if (textsize > 1)
1002 paragraph = HeapAlloc(GetProcessHeap(), 0,
1003 sizeof(HLPFILE_PARAGRAPH) + textsize);
1004 if (!paragraph) return FALSE;
1005 *paragraphptr = paragraph;
1006 paragraphptr = &paragraph->next;
1008 paragraph->next = NULL;
1009 paragraph->link = attributes.link;
1010 if (paragraph->link) paragraph->link->wRefCount++;
1011 paragraph->cookie = para_normal_text;
1012 paragraph->u.text.wFont = attributes.wFont;
1013 paragraph->u.text.wVSpace = attributes.wVSpace;
1014 paragraph->u.text.wHSpace = attributes.wHSpace;
1015 paragraph->u.text.wIndent = attributes.wIndent;
1016 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
1017 strcpy(paragraph->u.text.lpszText, text);
1019 attributes.wVSpace = 0;
1020 attributes.wHSpace = 0;
1022 /* else: null text, keep on storing attributes */
1023 text += textsize;
1025 if (*format == 0xff)
1027 format++;
1028 break;
1031 WINE_TRACE("format=%02x\n", *format);
1032 switch (*format)
1034 case 0x20:
1035 WINE_FIXME("NIY20\n");
1036 format += 5;
1037 break;
1039 case 0x21:
1040 WINE_FIXME("NIY21\n");
1041 format += 3;
1042 break;
1044 case 0x80:
1045 attributes.wFont = GET_USHORT(format, 1);
1046 WINE_TRACE("Changing font to %d\n", attributes.wFont);
1047 format += 3;
1048 break;
1050 case 0x81:
1051 attributes.wVSpace++;
1052 format += 1;
1053 break;
1055 case 0x82:
1056 attributes.wVSpace++;
1057 attributes.wIndent = 0;
1058 format += 1;
1059 break;
1061 case 0x83:
1062 attributes.wIndent++;
1063 format += 1;
1064 break;
1066 #if 0
1067 case 0x84:
1068 format += 3;
1069 break;
1070 #endif
1072 case 0x86:
1073 case 0x87:
1074 case 0x88:
1076 BYTE pos = (*format - 0x86);
1077 BYTE type = format[1];
1078 long size;
1080 format += 2;
1081 size = fetch_long(&format);
1083 paragraph = HeapAlloc(GetProcessHeap(), 0,
1084 sizeof(HLPFILE_PARAGRAPH) + textsize);
1085 if (!paragraph) return FALSE;
1086 *paragraphptr = paragraph;
1087 paragraphptr = &paragraph->next;
1089 paragraph->next = NULL;
1090 paragraph->link = attributes.link;
1091 if (paragraph->link) paragraph->link->wRefCount++;
1092 paragraph->cookie = para_bitmap;
1093 paragraph->u.gfx.pos = pos;
1094 switch (type)
1096 case 0x22:
1097 fetch_ushort(&format); /* hot spot */
1098 /* fall thru */
1099 case 0x03:
1100 switch (GET_SHORT(format, 0))
1102 case 0:
1103 HLPFILE_LoadGfxByIndex(hlpfile, GET_SHORT(format, 2),
1104 paragraph);
1105 break;
1106 case 1:
1107 WINE_FIXME("does it work ??? %x<%lu>#%u\n",
1108 GET_SHORT(format, 0),
1109 size, GET_SHORT(format, 2));
1110 HLPFILE_LoadGfxByAddr(hlpfile, format + 2, size - 4,
1111 paragraph);
1112 break;
1113 default:
1114 WINE_FIXME("??? %u\n", GET_SHORT(format, 0));
1115 break;
1117 break;
1118 case 0x05:
1119 WINE_FIXME("Got an embedded element %s\n", format + 6);
1120 break;
1121 default:
1122 WINE_FIXME("Got a type %d picture\n", type);
1123 break;
1125 if (attributes.wVSpace) paragraph->u.gfx.pos |= 0x8000;
1127 format += size;
1129 break;
1131 case 0x89:
1132 HLPFILE_FreeLink(attributes.link);
1133 attributes.link = NULL;
1134 format += 1;
1135 break;
1137 case 0x8B:
1138 case 0x8C:
1139 WINE_FIXME("NIY non-break space/hyphen\n");
1140 format += 1;
1141 break;
1143 #if 0
1144 case 0xA9:
1145 format += 2;
1146 break;
1147 #endif
1149 case 0xC8:
1150 case 0xCC:
1151 WINE_TRACE("macro => %s\n", format + 3);
1152 HLPFILE_FreeLink(attributes.link);
1153 attributes.link = HLPFILE_AllocLink(hlp_link_macro, (const char*)format + 3,
1154 0, !(*format & 4), -1);
1155 format += 3 + GET_USHORT(format, 1);
1156 break;
1158 case 0xE0:
1159 case 0xE1:
1160 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
1161 HLPFILE_FreeLink(attributes.link);
1162 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1163 hlpfile->lpszPath,
1164 GET_UINT(format, 1)-16,
1165 1, -1);
1168 format += 5;
1169 break;
1171 case 0xE2:
1172 case 0xE3:
1173 case 0xE6:
1174 case 0xE7:
1175 HLPFILE_FreeLink(attributes.link);
1176 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1177 hlpfile->lpszPath,
1178 GET_UINT(format, 1),
1179 !(*format & 4), -1);
1180 format += 5;
1181 break;
1183 case 0xEA:
1184 case 0xEB:
1185 case 0xEE:
1186 case 0xEF:
1188 char* ptr = (char*) format + 8;
1189 BYTE type = format[3];
1190 int wnd = -1;
1191 char* str;
1193 if (type == 1) wnd = *ptr++;
1194 if (type == 4 || type == 6)
1196 str = ptr;
1197 ptr += strlen(ptr) + 1;
1199 else
1200 str = hlpfile->lpszPath;
1201 if (type == 6)
1203 for (wnd = hlpfile->numWindows - 1; wnd >= 0; wnd--)
1205 if (!strcmp(ptr, hlpfile->windows[wnd].name)) break;
1207 if (wnd == -1)
1208 WINE_WARN("Couldn't find window info for %s\n", ptr);
1210 HLPFILE_FreeLink(attributes.link);
1211 attributes.link = HLPFILE_AllocLink((*format & 4) ? hlp_link_link : hlp_link_popup,
1212 str, GET_UINT(format, 4),
1213 !(*format & 1), wnd);
1215 format += 3 + GET_USHORT(format, 1);
1216 break;
1218 default:
1219 WINE_WARN("format %02x\n", *format);
1220 format++;
1224 HeapFree(GetProcessHeap(), 0, text_base);
1225 return TRUE;
1228 /******************************************************************
1229 * HLPFILE_ReadFont
1233 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
1235 BYTE *ref, *end;
1236 unsigned i, len, idx;
1237 unsigned face_num, dscr_num, face_offset, dscr_offset;
1238 BYTE flag, family;
1240 if (!HLPFILE_FindSubFile("|FONT", &ref, &end))
1242 WINE_WARN("no subfile FONT\n");
1243 hlpfile->numFonts = 0;
1244 hlpfile->fonts = NULL;
1245 return FALSE;
1248 ref += 9;
1250 face_num = GET_USHORT(ref, 0);
1251 dscr_num = GET_USHORT(ref, 2);
1252 face_offset = GET_USHORT(ref, 4);
1253 dscr_offset = GET_USHORT(ref, 6);
1255 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1256 face_num, face_offset, dscr_num, dscr_offset);
1258 hlpfile->numFonts = dscr_num;
1259 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
1261 len = (dscr_offset - face_offset) / face_num;
1262 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
1263 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1264 for (i = 0; i < dscr_num; i++)
1266 flag = ref[dscr_offset + i * 11 + 0];
1267 family = ref[dscr_offset + i * 11 + 2];
1269 hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2 - 3;
1270 hlpfile->fonts[i].LogFont.lfWidth = 0;
1271 hlpfile->fonts[i].LogFont.lfEscapement = 0;
1272 hlpfile->fonts[i].LogFont.lfOrientation = 0;
1273 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
1274 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
1275 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
1276 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
1277 hlpfile->fonts[i].LogFont.lfCharSet = ANSI_CHARSET;
1278 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1279 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1280 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
1281 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
1283 switch (family)
1285 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break;
1286 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break;
1287 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break;
1288 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break;
1289 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1290 default: WINE_FIXME("Unknown family %u\n", family);
1292 idx = GET_USHORT(ref, dscr_offset + i * 11 + 3);
1294 if (idx < face_num)
1296 memcpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1297 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1)] = '\0';
1299 else
1301 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1302 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1304 hlpfile->fonts[i].hFont = 0;
1305 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1306 ref[dscr_offset + i * 11 + 6],
1307 ref[dscr_offset + i * 11 + 7]);
1308 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1309 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
1310 i, flag,
1311 X(0, "bold"),
1312 X(1, "italic"),
1313 X(2, "underline"),
1314 X(3, "strikeOut"),
1315 X(4, "dblUnderline"),
1316 X(5, "smallCaps"),
1317 ref[dscr_offset + i * 11 + 1],
1318 family,
1319 hlpfile->fonts[i].LogFont.lfFaceName, idx,
1320 GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1322 return TRUE;
1325 /***********************************************************************
1327 * HLPFILE_ReadFileToBuffer
1329 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1331 BYTE header[16], dummy[1];
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 file_buffer_size = GET_UINT(header, 12);
1340 file_buffer = HeapAlloc(GetProcessHeap(), 0, file_buffer_size + 1);
1341 if (!file_buffer) return FALSE;
1343 memcpy(file_buffer, header, 16);
1344 if (_hread(hFile, file_buffer + 16, file_buffer_size - 16) != file_buffer_size - 16)
1345 {WINE_WARN("filesize1\n"); return FALSE;};
1347 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1349 file_buffer[file_buffer_size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1351 return TRUE;
1354 /**************************************************************************
1355 * comp_FindSubFile
1357 * HLPFILE_BPTreeCompare function for HLPFILE directory.
1360 static int comp_FindSubFile(void *p, const void *key,
1361 int leaf, void** next)
1363 *next = (char *)p+strlen(p)+(leaf?5:3);
1364 WINE_TRACE("Comparing '%s' with '%s'\n", (char *)p, (char *)key);
1365 return strcmp(p, key);
1368 /***********************************************************************
1370 * HLPFILE_FindSubFile
1372 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1374 BYTE *ptr;
1376 WINE_TRACE("looking for file '%s'\n", name);
1377 ptr = HLPFILE_BPTreeSearch(file_buffer + GET_UINT(file_buffer, 4),
1378 name, comp_FindSubFile);
1379 if (!ptr) return FALSE;
1380 *subbuf = file_buffer + GET_UINT(ptr, strlen(name)+1);
1381 if (*subbuf >= file_buffer + file_buffer_size)
1383 WINE_ERR("internal file %s does not fit\n", name);
1384 return FALSE;
1386 *subend = *subbuf + GET_UINT(*subbuf, 0);
1387 if (*subend > file_buffer + file_buffer_size)
1389 WINE_ERR("internal file %s does not fit\n", name);
1390 return FALSE;
1392 if (GET_UINT(*subbuf, 0) < GET_UINT(*subbuf, 4) + 9)
1394 WINE_ERR("invalid size provided for internal file %s\n", name);
1395 return FALSE;
1397 return TRUE;
1400 /***********************************************************************
1402 * HLPFILE_SystemCommands
1404 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1406 BYTE *buf, *ptr, *end;
1407 HLPFILE_MACRO *macro, **m;
1408 LPSTR p;
1409 unsigned short magic, minor, major, flags;
1411 hlpfile->lpszTitle = NULL;
1413 if (!HLPFILE_FindSubFile("|SYSTEM", &buf, &end)) return FALSE;
1415 magic = GET_USHORT(buf + 9, 0);
1416 minor = GET_USHORT(buf + 9, 2);
1417 major = GET_USHORT(buf + 9, 4);
1418 /* gen date on 4 bytes */
1419 flags = GET_USHORT(buf + 9, 10);
1420 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1421 magic, major, minor, flags);
1422 if (magic != 0x036C || major != 1)
1423 {WINE_WARN("Wrong system header\n"); return FALSE;}
1424 if (minor <= 16)
1426 hlpfile->tbsize = 0x800;
1427 hlpfile->compressed = 0;
1429 else if (flags == 0)
1431 hlpfile->tbsize = 0x1000;
1432 hlpfile->compressed = 0;
1434 else if (flags == 4)
1436 hlpfile->tbsize = 0x1000;
1437 hlpfile->compressed = 1;
1439 else
1441 hlpfile->tbsize = 0x800;
1442 hlpfile->compressed = 1;
1445 if (hlpfile->compressed)
1446 hlpfile->dsize = 0x4000;
1447 else
1448 hlpfile->dsize = hlpfile->tbsize - 0x0C;
1450 hlpfile->version = minor;
1451 hlpfile->flags = flags;
1453 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1455 char *str = (char*) ptr + 4;
1456 switch (GET_USHORT(ptr, 0))
1458 case 1:
1459 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1460 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1461 if (!hlpfile->lpszTitle) return FALSE;
1462 lstrcpy(hlpfile->lpszTitle, str);
1463 WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1464 break;
1466 case 2:
1467 if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
1468 hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1469 if (!hlpfile->lpszCopyright) return FALSE;
1470 lstrcpy(hlpfile->lpszCopyright, str);
1471 WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
1472 break;
1474 case 3:
1475 if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
1476 hlpfile->contents_start = GET_UINT(ptr, 4);
1477 WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
1478 break;
1480 case 4:
1481 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(str) + 1);
1482 if (!macro) break;
1483 p = (char*)macro + sizeof(HLPFILE_MACRO);
1484 lstrcpy(p, str);
1485 macro->lpszMacro = p;
1486 macro->next = 0;
1487 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1488 *m = macro;
1489 break;
1491 case 6:
1492 if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
1494 if (hlpfile->windows)
1495 hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows,
1496 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1497 else
1498 hlpfile->windows = HeapAlloc(GetProcessHeap(), 0,
1499 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1501 if (hlpfile->windows)
1503 unsigned flags = GET_USHORT(ptr, 4);
1504 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
1506 if (flags & 0x0001) strcpy(wi->type, &str[2]);
1507 else wi->type[0] = '\0';
1508 if (flags & 0x0002) strcpy(wi->name, &str[12]);
1509 else wi->name[0] = '\0';
1510 if (flags & 0x0004) strcpy(wi->caption, &str[23]);
1511 else lstrcpynA(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
1512 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
1513 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
1514 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
1515 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
1516 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
1517 wi->win_style = WS_OVERLAPPEDWINDOW;
1518 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
1519 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
1520 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",
1521 flags & 0x0001 ? 'T' : 't',
1522 flags & 0x0002 ? 'N' : 'n',
1523 flags & 0x0004 ? 'C' : 'c',
1524 flags & 0x0008 ? 'X' : 'x',
1525 flags & 0x0010 ? 'Y' : 'y',
1526 flags & 0x0020 ? 'W' : 'w',
1527 flags & 0x0040 ? 'H' : 'h',
1528 flags & 0x0080 ? 'S' : 's',
1529 wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
1530 wi->size.cx, wi->size.cy);
1532 break;
1533 default:
1534 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1537 if (!hlpfile->lpszTitle)
1538 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1);
1539 return TRUE;
1542 /***********************************************************************
1544 * HLPFILE_UncompressedLZ77_Size
1546 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1548 int i, newsize = 0;
1550 while (ptr < end)
1552 int mask = *ptr++;
1553 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1555 if (mask & 1)
1557 int code = GET_USHORT(ptr, 0);
1558 int len = 3 + (code >> 12);
1559 newsize += len;
1560 ptr += 2;
1562 else newsize++, ptr++;
1566 return newsize;
1569 /***********************************************************************
1571 * HLPFILE_UncompressLZ77
1573 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1575 int i;
1577 while (ptr < end)
1579 int mask = *ptr++;
1580 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1582 if (mask & 1)
1584 int code = GET_USHORT(ptr, 0);
1585 int len = 3 + (code >> 12);
1586 int offset = code & 0xfff;
1588 * We must copy byte-by-byte here. We cannot use memcpy nor
1589 * memmove here. Just example:
1590 * a[]={1,2,3,4,5,6,7,8,9,10}
1591 * newptr=a+2;
1592 * offset=1;
1593 * We expect:
1594 * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12}
1596 for (; len>0; len--, newptr++) *newptr = *(newptr-offset-1);
1597 ptr += 2;
1599 else *newptr++ = *ptr++;
1603 return newptr;
1606 /***********************************************************************
1608 * HLPFILE_UncompressLZ77_Phrases
1610 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1612 UINT i, num, dec_size, head_size;
1613 BYTE *buf, *end;
1615 if (!HLPFILE_FindSubFile("|Phrases", &buf, &end)) return FALSE;
1617 if (hlpfile->version <= 16)
1618 head_size = 13;
1619 else
1620 head_size = 17;
1622 num = phrases.num = GET_USHORT(buf, 9);
1623 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1625 if (hlpfile->version <= 16)
1626 dec_size = end - buf - 15 - 2 * num;
1627 else
1628 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1630 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1631 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1632 if (!phrases.offsets || !phrases.buffer) return FALSE;
1634 for (i = 0; i <= num; i++)
1635 phrases.offsets[i] = GET_USHORT(buf, head_size + 2 * i) - 2 * num - 2;
1637 if (hlpfile->version <= 16)
1638 memcpy(phrases.buffer, buf + 15 + 2*num, dec_size);
1639 else
1640 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, (BYTE*)phrases.buffer);
1642 hlpfile->hasPhrases = TRUE;
1643 return TRUE;
1646 /***********************************************************************
1648 * HLPFILE_Uncompress_Phrases40
1650 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1652 UINT num;
1653 INT dec_size, cpr_size;
1654 BYTE *buf_idx, *end_idx;
1655 BYTE *buf_phs, *end_phs;
1656 long* ptr, mask = 0;
1657 unsigned int i;
1658 unsigned short bc, n;
1660 if (!HLPFILE_FindSubFile("|PhrIndex", &buf_idx, &end_idx) ||
1661 !HLPFILE_FindSubFile("|PhrImage", &buf_phs, &end_phs)) return FALSE;
1663 ptr = (long*)(buf_idx + 9 + 28);
1664 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1665 num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1667 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1668 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1669 GET_UINT(buf_idx, 9 + 0),
1670 GET_UINT(buf_idx, 9 + 4),
1671 GET_UINT(buf_idx, 9 + 8),
1672 GET_UINT(buf_idx, 9 + 12),
1673 GET_UINT(buf_idx, 9 + 16),
1674 GET_UINT(buf_idx, 9 + 20),
1675 GET_USHORT(buf_idx, 9 + 24),
1676 GET_USHORT(buf_idx, 9 + 26));
1678 dec_size = GET_UINT(buf_idx, 9 + 12);
1679 cpr_size = GET_UINT(buf_idx, 9 + 16);
1681 if (dec_size != cpr_size &&
1682 dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1684 WINE_WARN("size mismatch %u %u\n",
1685 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1686 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1689 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1690 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1691 if (!phrases.offsets || !phrases.buffer) return FALSE;
1693 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1695 phrases.offsets[0] = 0;
1696 for (i = 0; i < num; i++)
1698 for (n = 1; getbit(); n += 1 << bc);
1699 if (getbit()) n++;
1700 if (bc > 1 && getbit()) n += 2;
1701 if (bc > 2 && getbit()) n += 4;
1702 if (bc > 3 && getbit()) n += 8;
1703 if (bc > 4 && getbit()) n += 16;
1704 phrases.offsets[i + 1] = phrases.offsets[i] + n;
1706 #undef getbit
1708 if (dec_size == cpr_size)
1709 memcpy(phrases.buffer, buf_phs + 9, dec_size);
1710 else
1711 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, (BYTE*)phrases.buffer);
1713 hlpfile->hasPhrases = FALSE;
1714 return TRUE;
1717 /***********************************************************************
1719 * HLPFILE_Uncompress_Topic
1721 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
1723 BYTE *buf, *ptr, *end, *newptr;
1724 unsigned int i, newsize = 0;
1725 unsigned int topic_size;
1727 if (!HLPFILE_FindSubFile("|TOPIC", &buf, &end))
1728 {WINE_WARN("topic0\n"); return FALSE;}
1730 buf += 9; /* Skip file header */
1731 topic_size = end - buf;
1732 if (hlpfile->compressed)
1734 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1736 for (i = 0; i < topic.wMapLen; i++)
1738 ptr = buf + i * hlpfile->tbsize;
1740 /* I don't know why, it's necessary for printman.hlp */
1741 if (ptr + 0x44 > end) ptr = end - 0x44;
1743 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + hlpfile->tbsize));
1746 topic.map = HeapAlloc(GetProcessHeap(), 0,
1747 topic.wMapLen * sizeof(topic.map[0]) + newsize);
1748 if (!topic.map) return FALSE;
1749 newptr = (BYTE*)(topic.map + topic.wMapLen);
1750 topic.end = newptr + newsize;
1752 for (i = 0; i < topic.wMapLen; i++)
1754 ptr = buf + i * hlpfile->tbsize;
1755 if (ptr + 0x44 > end) ptr = end - 0x44;
1757 topic.map[i] = newptr;
1758 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + hlpfile->tbsize), newptr);
1761 else
1763 /* basically, we need to copy the TopicBlockSize byte pages
1764 * (removing the first 0x0C) in one single area in memory
1766 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1767 topic.map = HeapAlloc(GetProcessHeap(), 0,
1768 topic.wMapLen * (sizeof(topic.map[0]) + hlpfile->dsize));
1769 if (!topic.map) return FALSE;
1770 newptr = (BYTE*)(topic.map + topic.wMapLen);
1771 topic.end = newptr + topic_size;
1773 for (i = 0; i < topic.wMapLen; i++)
1775 topic.map[i] = newptr + i * hlpfile->dsize;
1776 memcpy(topic.map[i], buf + i * hlpfile->tbsize + 0x0C, hlpfile->dsize);
1779 return TRUE;
1782 /***********************************************************************
1784 * HLPFILE_Uncompress2
1787 static void HLPFILE_Uncompress2(const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
1789 BYTE *phptr, *phend;
1790 UINT code;
1791 UINT index;
1793 while (ptr < end && newptr < newend)
1795 if (!*ptr || *ptr >= 0x10)
1796 *newptr++ = *ptr++;
1797 else
1799 code = 0x100 * ptr[0] + ptr[1];
1800 index = (code - 0x100) / 2;
1802 phptr = (BYTE*)phrases.buffer + phrases.offsets[index];
1803 phend = (BYTE*)phrases.buffer + phrases.offsets[index + 1];
1805 if (newptr + (phend - phptr) > newend)
1807 WINE_FIXME("buffer overflow %p > %p for %d bytes\n",
1808 newptr, newend, phend - phptr);
1809 return;
1811 memcpy(newptr, phptr, phend - phptr);
1812 newptr += phend - phptr;
1813 if (code & 1) *newptr++ = ' ';
1815 ptr += 2;
1818 if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
1821 /******************************************************************
1822 * HLPFILE_Uncompress3
1826 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1827 const BYTE* src, const BYTE* src_end)
1829 unsigned int idx, len;
1831 for (; src < src_end; src++)
1833 if ((*src & 1) == 0)
1835 idx = *src / 2;
1836 if (idx > phrases.num)
1838 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1839 len = 0;
1841 else
1843 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1844 if (dst + len <= dst_end)
1845 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1848 else if ((*src & 0x03) == 0x01)
1850 idx = (*src + 1) * 64;
1851 idx += *++src;
1852 if (idx > phrases.num)
1854 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1855 len = 0;
1857 else
1859 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1860 if (dst + len <= dst_end)
1861 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1864 else if ((*src & 0x07) == 0x03)
1866 len = (*src / 8) + 1;
1867 if (dst + len <= dst_end)
1868 memcpy(dst, src + 1, len);
1869 src += len;
1871 else
1873 len = (*src / 16) + 1;
1874 if (dst + len <= dst_end)
1875 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1877 dst += len;
1880 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1881 return TRUE;
1884 /******************************************************************
1885 * HLPFILE_UncompressRLE
1889 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz)
1891 BYTE ch;
1892 BYTE* sdst = *dst + dstsz;
1894 while (src < end)
1896 ch = *src++;
1897 if (ch & 0x80)
1899 ch &= 0x7F;
1900 if ((*dst) + ch <= sdst)
1901 memcpy(*dst, src, ch);
1902 src += ch;
1904 else
1906 if ((*dst) + ch <= sdst)
1907 memset(*dst, (char)*src, ch);
1908 src++;
1910 *dst += ch;
1912 if (*dst != sdst)
1913 WINE_WARN("Buffer X-flow: d(%u) instead of d(%u)\n",
1914 *dst - (sdst - dstsz), dstsz);
1917 /**************************************************************************
1918 * HLPFILE_BPTreeSearch
1920 * Searches for an element in B+ tree
1922 * PARAMS
1923 * buf [I] pointer to the embedded file structured as a B+ tree
1924 * key [I] pointer to data to find
1925 * comp [I] compare function
1927 * RETURNS
1928 * Pointer to block identified by key, or NULL if failure.
1931 static void* HLPFILE_BPTreeSearch(BYTE* buf, const void* key,
1932 HLPFILE_BPTreeCompare comp)
1934 unsigned magic;
1935 unsigned page_size;
1936 unsigned cur_page;
1937 unsigned level;
1938 BYTE *pages, *ptr, *newptr;
1939 int i, entries;
1940 int ret;
1942 magic = GET_USHORT(buf, 9);
1943 if (magic != 0x293B)
1945 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic);
1946 return NULL;
1948 page_size = GET_USHORT(buf, 9+4);
1949 cur_page = GET_USHORT(buf, 9+26);
1950 level = GET_USHORT(buf, 9+32);
1951 pages = buf + 9 + 38;
1952 while (--level > 0)
1954 ptr = pages + cur_page*page_size;
1955 entries = GET_SHORT(ptr, 2);
1956 ptr += 6;
1957 for (i = 0; i < entries; i++)
1959 if (comp(ptr, key, 0, (void **)&newptr) > 0) break;
1960 ptr = newptr;
1962 cur_page = GET_USHORT(ptr-2, 0);
1964 ptr = pages + cur_page*page_size;
1965 entries = GET_SHORT(ptr, 2);
1966 ptr += 8;
1967 for (i = 0; i < entries; i++)
1969 ret = comp(ptr, key, 1, (void **)&newptr);
1970 if (ret == 0) return ptr;
1971 if (ret > 0) return NULL;
1972 ptr = newptr;
1974 return NULL;
1978 /***********************************************************************
1980 * HLPFILE_GetContext
1982 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
1984 BYTE *cbuf, *cend;
1985 unsigned clen;
1987 if (!HLPFILE_FindSubFile("|CONTEXT", &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
1989 clen = cend - cbuf;
1990 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen);
1991 if (!hlpfile->Context) return FALSE;
1992 memcpy(hlpfile->Context, cbuf, clen);
1994 return TRUE;
1997 /***********************************************************************
1999 * HLPFILE_GetMap
2001 static BOOL HLPFILE_GetMap(HLPFILE *hlpfile)
2003 BYTE *cbuf, *cend;
2004 unsigned entries, i;
2006 if (!HLPFILE_FindSubFile("|CTXOMAP", &cbuf, &cend)) {WINE_WARN("no map section\n"); return FALSE;}
2008 entries = GET_USHORT(cbuf, 9);
2009 hlpfile->Map = HeapAlloc(GetProcessHeap(), 0, entries * sizeof(HLPFILE_MAP));
2010 if (!hlpfile->Map) return FALSE;
2011 hlpfile->wMapLen = entries;
2012 for (i = 0; i < entries; i++)
2014 hlpfile->Map[i].lMap = GET_UINT(cbuf+11,i*8);
2015 hlpfile->Map[i].offset = GET_UINT(cbuf+11,i*8+4);
2017 return TRUE;
2020 /******************************************************************
2021 * HLPFILE_DeleteLink
2025 void HLPFILE_FreeLink(HLPFILE_LINK* link)
2027 if (link && !--link->wRefCount)
2028 HeapFree(GetProcessHeap(), 0, link);
2031 /***********************************************************************
2033 * HLPFILE_DeleteParagraph
2035 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
2037 HLPFILE_PARAGRAPH* next;
2039 while (paragraph)
2041 next = paragraph->next;
2043 if (paragraph->cookie == para_metafile)
2044 DeleteMetaFile(paragraph->u.gfx.u.mfp.hMF);
2046 HLPFILE_FreeLink(paragraph->link);
2048 HeapFree(GetProcessHeap(), 0, paragraph);
2049 paragraph = next;
2053 /***********************************************************************
2055 * DeleteMacro
2057 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
2059 HLPFILE_MACRO* next;
2061 while (macro)
2063 next = macro->next;
2064 HeapFree(GetProcessHeap(), 0, macro);
2065 macro = next;
2069 /***********************************************************************
2071 * DeletePage
2073 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
2075 HLPFILE_PAGE* next;
2077 while (page)
2079 next = page->next;
2080 HLPFILE_DeleteParagraph(page->first_paragraph);
2081 HLPFILE_DeleteMacro(page->first_macro);
2082 HeapFree(GetProcessHeap(), 0, page);
2083 page = next;
2087 /***********************************************************************
2089 * HLPFILE_FreeHlpFile
2091 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
2093 unsigned i;
2095 if (!hlpfile || --hlpfile->wRefCount > 0) return;
2097 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
2098 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
2099 else first_hlpfile = hlpfile->next;
2101 if (hlpfile->numFonts)
2103 for (i = 0; i < hlpfile->numFonts; i++)
2105 DeleteObject(hlpfile->fonts[i].hFont);
2107 HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
2110 if (hlpfile->numBmps)
2112 for (i = 0; i < hlpfile->numBmps; i++)
2114 DeleteObject(hlpfile->bmps[i]);
2116 HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
2119 HLPFILE_DeletePage(hlpfile->first_page);
2120 HLPFILE_DeleteMacro(hlpfile->first_macro);
2122 if (hlpfile->numWindows) HeapFree(GetProcessHeap(), 0, hlpfile->windows);
2123 HeapFree(GetProcessHeap(), 0, hlpfile->Context);
2124 HeapFree(GetProcessHeap(), 0, hlpfile->Map);
2125 HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
2126 HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
2127 HeapFree(GetProcessHeap(), 0, hlpfile);