Include config.h and wine/port.h in files that use snprintf.
[wine/multimedia.git] / programs / winhelp / hlpfile.c
blob39b3081bf338383b49376833d9da192be41a21ff
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;
79 static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR);
80 static BOOL HLPFILE_ReadFileToBuffer(HFILE);
81 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**);
82 static BOOL HLPFILE_SystemCommands(HLPFILE*);
83 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end);
84 static BYTE* HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr);
85 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE*);
86 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE*);
87 static BOOL HLPFILE_Uncompress_Topic(HLPFILE*);
88 static BOOL HLPFILE_GetContext(HLPFILE*);
89 static BOOL HLPFILE_GetKeywords(HLPFILE*);
90 static BOOL HLPFILE_GetMap(HLPFILE*);
91 static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*, unsigned);
92 static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*, unsigned*);
93 static void HLPFILE_Uncompress2(const BYTE*, const BYTE*, BYTE*, const BYTE*);
94 static BOOL HLPFILE_Uncompress3(char*, const char*, const BYTE*, const BYTE*);
95 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz);
96 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile);
98 /***********************************************************************
100 * HLPFILE_PageByNumber
102 static HLPFILE_PAGE *HLPFILE_PageByNumber(HLPFILE* hlpfile, UINT wNum)
104 HLPFILE_PAGE *page;
105 UINT temp = wNum;
107 WINE_TRACE("<%s>[%u]\n", hlpfile->lpszPath, wNum);
109 for (page = hlpfile->first_page; page && temp; page = page->next) temp--;
110 if (!page)
111 WINE_ERR("Page of number %u not found in file %s\n", wNum, hlpfile->lpszPath);
112 return page;
115 /* FIXME:
116 * this finds the page containing the offset. The offset can either
117 * refer to the top of the page (offset == page->offset), or
118 * to some paragraph inside the page...
119 * As of today, we only return the page... we should also return
120 * a paragraph, and then, while opening a new page, compute the
121 * y-offset of the paragraph to be shown and scroll the window
122 * accordingly
124 /******************************************************************
125 * HLPFILE_PageByOffset
129 HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset)
131 HLPFILE_PAGE* page;
132 HLPFILE_PAGE* found;
134 if (!hlpfile) return 0;
136 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, offset);
138 if (offset == 0xFFFFFFFF) return NULL;
139 page = NULL;
141 for (found = NULL, page = hlpfile->first_page; page; page = page->next)
143 if (page->offset <= offset && (!found || found->offset < page->offset))
144 found = page;
146 if (!found)
147 WINE_ERR("Page of offset %u not found in file %s\n",
148 offset, hlpfile->lpszPath);
149 return found;
152 /**************************************************************************
153 * comp_PageByHash
155 * HLPFILE_BPTreeCompare function for '|CONTEXT' B+ tree file
158 static int comp_PageByHash(void *p, const void *key,
159 int leaf, void** next)
161 LONG lKey = (LONG)key;
162 LONG lTest = GET_UINT(p, 0);
164 *next = (char *)p+(leaf?8:6);
165 WINE_TRACE("Comparing '%u' with '%u'\n", lKey, lTest);
166 if (lTest < lKey) return -1;
167 if (lTest > lKey) return 1;
168 return 0;
171 /***********************************************************************
173 * HLPFILE_HlpFilePageByHash
175 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash)
177 BYTE *ptr;
179 if (!hlpfile) return 0;
181 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lHash);
183 /* For win 3.0 files hash values are really page numbers */
184 if (hlpfile->version <= 16)
185 return HLPFILE_PageByNumber(hlpfile, lHash);
187 ptr = HLPFILE_BPTreeSearch(hlpfile->Context, (void*)lHash, comp_PageByHash);
188 if (!ptr)
190 WINE_ERR("Page of hash %x not found in file %s\n", lHash, hlpfile->lpszPath);
191 return NULL;
194 return HLPFILE_PageByOffset(hlpfile, GET_UINT(ptr, 4));
197 /***********************************************************************
199 * HLPFILE_PageByMap
201 HLPFILE_PAGE *HLPFILE_PageByMap(HLPFILE* hlpfile, LONG lMap)
203 unsigned int i;
205 if (!hlpfile) return 0;
207 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lMap);
209 for (i = 0; i < hlpfile->wMapLen; i++)
211 if (hlpfile->Map[i].lMap == lMap)
212 return HLPFILE_PageByOffset(hlpfile, hlpfile->Map[i].offset);
215 WINE_ERR("Page of Map %x not found in file %s\n", lMap, hlpfile->lpszPath);
216 return NULL;
219 /***********************************************************************
221 * HLPFILE_Contents
223 HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile)
225 HLPFILE_PAGE* page = NULL;
227 if (!hlpfile) return NULL;
229 page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start);
230 if (!page) page = hlpfile->first_page;
231 return page;
234 /***********************************************************************
236 * HLPFILE_Hash
238 LONG HLPFILE_Hash(LPCSTR lpszContext)
240 LONG lHash = 0;
241 CHAR c;
243 while ((c = *lpszContext++))
245 CHAR x = 0;
246 if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
247 if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
248 if (c >= '1' && c <= '9') x = c - '0';
249 if (c == '0') x = 10;
250 if (c == '.') x = 12;
251 if (c == '_') x = 13;
252 if (x) lHash = lHash * 43 + x;
254 return lHash;
257 /***********************************************************************
259 * HLPFILE_ReadHlpFile
261 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
263 HLPFILE* hlpfile;
265 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
267 if (!strcmp(lpszPath, hlpfile->lpszPath))
269 hlpfile->wRefCount++;
270 return hlpfile;
274 hlpfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
275 sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
276 if (!hlpfile) return 0;
278 hlpfile->lpszPath = (char*)hlpfile + sizeof(HLPFILE);
279 hlpfile->contents_start = 0xFFFFFFFF;
280 hlpfile->next = first_hlpfile;
281 hlpfile->wRefCount = 1;
283 strcpy(hlpfile->lpszPath, lpszPath);
285 first_hlpfile = hlpfile;
286 if (hlpfile->next) hlpfile->next->prev = hlpfile;
288 phrases.offsets = NULL;
289 phrases.buffer = NULL;
290 topic.map = NULL;
291 topic.end = NULL;
292 file_buffer = NULL;
294 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
296 HLPFILE_FreeHlpFile(hlpfile);
297 hlpfile = 0;
300 HeapFree(GetProcessHeap(), 0, phrases.offsets);
301 HeapFree(GetProcessHeap(), 0, phrases.buffer);
302 HeapFree(GetProcessHeap(), 0, topic.map);
303 HeapFree(GetProcessHeap(), 0, file_buffer);
305 return hlpfile;
308 /***********************************************************************
310 * HLPFILE_DoReadHlpFile
312 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
314 BOOL ret;
315 HFILE hFile;
316 OFSTRUCT ofs;
317 BYTE* buf;
318 DWORD ref = 0x0C;
319 unsigned index, old_index, offset, len, offs;
321 hFile = OpenFile(lpszPath, &ofs, OF_READ);
322 if (hFile == HFILE_ERROR) return FALSE;
324 ret = HLPFILE_ReadFileToBuffer(hFile);
325 _lclose(hFile);
326 if (!ret) return FALSE;
328 if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
330 /* load phrases support */
331 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile))
332 HLPFILE_Uncompress_Phrases40(hlpfile);
334 if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE;
335 if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
337 buf = topic.map[0];
338 old_index = -1;
339 offs = 0;
342 BYTE* end;
344 if (hlpfile->version <= 16)
346 index = (ref - 0x0C) / hlpfile->dsize;
347 offset = (ref - 0x0C) % hlpfile->dsize;
349 else
351 index = (ref - 0x0C) >> 14;
352 offset = (ref - 0x0C) & 0x3FFF;
355 if (hlpfile->version <= 16 && index != old_index && index != 0)
357 /* we jumped to the next block, adjust pointers */
358 ref -= 12;
359 offset -= 12;
362 WINE_TRACE("ref=%08x => [%u/%u]\n", ref, index, offset);
364 if (index >= topic.wMapLen) {WINE_WARN("maplen\n"); break;}
365 buf = topic.map[index] + offset;
366 if (buf + 0x15 >= topic.end) {WINE_WARN("extra\n"); break;}
367 end = min(buf + GET_UINT(buf, 0), topic.end);
368 if (index != old_index) {offs = 0; old_index = index;}
370 switch (buf[0x14])
372 case 0x02:
373 if (!HLPFILE_AddPage(hlpfile, buf, end, index * 0x8000L + offs)) return FALSE;
374 break;
376 case 0x01:
377 case 0x20:
378 case 0x23:
379 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
380 offs += len;
381 break;
383 default:
384 WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
387 if (hlpfile->version <= 16)
389 ref += GET_UINT(buf, 0xc);
390 if (GET_UINT(buf, 0xc) == 0)
391 break;
393 else
394 ref = GET_UINT(buf, 0xc);
395 } while (ref != 0xffffffff);
397 HLPFILE_GetKeywords(hlpfile);
398 HLPFILE_GetMap(hlpfile);
399 if (hlpfile->version <= 16) return TRUE;
400 return HLPFILE_GetContext(hlpfile);
403 /***********************************************************************
405 * HLPFILE_AddPage
407 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned offset)
409 HLPFILE_PAGE* page;
410 BYTE* title;
411 UINT titlesize, blocksize, datalen;
412 char* ptr;
413 HLPFILE_MACRO*macro;
415 blocksize = GET_UINT(buf, 0);
416 datalen = GET_UINT(buf, 0x10);
417 title = buf + datalen;
418 if (title > end) {WINE_WARN("page2\n"); return FALSE;};
420 titlesize = GET_UINT(buf, 4);
421 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
422 if (!page) return FALSE;
423 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
425 if (titlesize > blocksize - datalen)
427 /* need to decompress */
428 if (hlpfile->hasPhrases)
429 HLPFILE_Uncompress2(title, end, (BYTE*)page->lpszTitle, (BYTE*)page->lpszTitle + titlesize);
430 else if (hlpfile->hasPhrases40)
431 HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize, title, end);
432 else
434 WINE_FIXME("Text size is too long, splitting\n");
435 titlesize = blocksize - datalen;
436 memcpy(page->lpszTitle, title, titlesize);
439 else
440 memcpy(page->lpszTitle, title, titlesize);
442 page->lpszTitle[titlesize] = '\0';
444 if (hlpfile->first_page)
446 hlpfile->last_page->next = page;
447 page->prev = hlpfile->last_page;
448 hlpfile->last_page = page;
450 else
452 hlpfile->first_page = page;
453 hlpfile->last_page = page;
454 page->prev = NULL;
457 page->file = hlpfile;
458 page->next = NULL;
459 page->first_paragraph = NULL;
460 page->first_macro = NULL;
461 page->wNumber = GET_UINT(buf, 0x21);
462 page->offset = offset;
464 page->browse_bwd = GET_UINT(buf, 0x19);
465 page->browse_fwd = GET_UINT(buf, 0x1D);
467 WINE_TRACE("Added page[%d]: title='%s' %08x << %08x >> %08x\n",
468 page->wNumber, page->lpszTitle,
469 page->browse_bwd, page->offset, page->browse_fwd);
471 memset(&attributes, 0, sizeof(attributes));
473 /* now load macros */
474 ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
475 while (ptr < page->lpszTitle + titlesize)
477 unsigned len = strlen(ptr);
478 char* macro_str;
480 WINE_TRACE("macro: %s\n", ptr);
481 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
482 macro->lpszMacro = macro_str = (char*)(macro + 1);
483 memcpy(macro_str, ptr, len + 1);
484 /* FIXME: shall we really link macro in reverse order ??
485 * may produce strange results when played at page opening
487 macro->next = page->first_macro;
488 page->first_macro = macro;
489 ptr += len + 1;
492 return TRUE;
495 static long fetch_long(BYTE** ptr)
497 long ret;
499 if (*(*ptr) & 1)
501 ret = (*(unsigned long*)(*ptr) - 0x80000000L) / 2;
502 (*ptr) += 4;
504 else
506 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
507 (*ptr) += 2;
510 return ret;
513 static unsigned long fetch_ulong(BYTE** ptr)
515 unsigned long ret;
517 if (*(*ptr) & 1)
519 ret = *(unsigned long*)(*ptr) / 2;
520 (*ptr) += 4;
522 else
524 ret = *(unsigned short*)(*ptr) / 2;
525 (*ptr) += 2;
527 return ret;
530 static short fetch_short(BYTE** ptr)
532 short ret;
534 if (*(*ptr) & 1)
536 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
537 (*ptr) += 2;
539 else
541 ret = (*(unsigned char*)(*ptr) - 0x80) / 2;
542 (*ptr)++;
544 return ret;
547 static unsigned short fetch_ushort(BYTE** ptr)
549 unsigned short ret;
551 if (*(*ptr) & 1)
553 ret = *(unsigned short*)(*ptr) / 2;
554 (*ptr) += 2;
556 else
558 ret = *(unsigned char*)(*ptr) / 2;
559 (*ptr)++;
561 return ret;
564 /******************************************************************
565 * HLPFILE_DecompressGfx
567 * Decompress the data part of a bitmap or a metafile
569 static BYTE* HLPFILE_DecompressGfx(BYTE* src, unsigned csz, unsigned sz, BYTE packing)
571 BYTE* dst;
572 BYTE* tmp;
573 BYTE* tmp2;
574 unsigned sz77;
576 WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing, csz, sz);
578 switch (packing)
580 case 0: /* uncompressed */
581 if (sz != csz)
582 WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz, csz);
583 dst = src;
584 break;
585 case 1: /* RunLen */
586 tmp = dst = HeapAlloc(GetProcessHeap(), 0, sz);
587 if (!dst) return NULL;
588 HLPFILE_UncompressRLE(src, src + csz, &tmp, sz);
589 if (tmp - dst != sz)
590 WINE_WARN("Bogus gfx sizes (RunLen): %u/%u\n", tmp - dst, sz);
591 break;
592 case 2: /* LZ77 */
593 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
594 dst = HeapAlloc(GetProcessHeap(), 0, sz77);
595 if (!dst) return NULL;
596 HLPFILE_UncompressLZ77(src, src + csz, dst);
597 if (sz77 != sz)
598 WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77, sz);
599 break;
600 case 3: /* LZ77 then RLE */
601 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
602 tmp = HeapAlloc(GetProcessHeap(), 0, sz77);
603 if (!tmp) return FALSE;
604 HLPFILE_UncompressLZ77(src, src + csz, tmp);
605 dst = tmp2 = HeapAlloc(GetProcessHeap(), 0, sz);
606 if (!dst)
608 HeapFree(GetProcessHeap(), 0, tmp);
609 return FALSE;
611 HLPFILE_UncompressRLE(tmp, tmp + sz77, &tmp2, sz);
612 if (tmp2 - dst != sz)
613 WINE_WARN("Bogus gfx sizes (LZ77+RunLen): %u / %u\n", tmp2 - dst, sz);
614 HeapFree(GetProcessHeap(), 0, tmp);
615 break;
616 default:
617 WINE_FIXME("Unsupported packing %u\n", packing);
618 return NULL;
620 return dst;
623 /******************************************************************
624 * HLPFILE_LoadBitmap
628 static BOOL HLPFILE_LoadBitmap(BYTE* beg, BYTE type, BYTE pack,
629 HLPFILE_PARAGRAPH* paragraph)
631 BYTE* ptr;
632 BYTE* pict_beg;
633 BITMAPINFO* bi;
634 unsigned long off, csz;
635 HDC hdc;
637 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
638 if (!bi) return FALSE;
640 ptr = beg + 2; /* for type and pack */
642 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
643 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
644 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
645 bi->bmiHeader.biPlanes = fetch_ushort(&ptr);
646 bi->bmiHeader.biBitCount = fetch_ushort(&ptr);
647 bi->bmiHeader.biWidth = fetch_ulong(&ptr);
648 bi->bmiHeader.biHeight = fetch_ulong(&ptr);
649 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr);
650 bi->bmiHeader.biClrImportant = fetch_ulong(&ptr);
651 bi->bmiHeader.biCompression = BI_RGB;
652 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
653 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
654 bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
655 WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n",
656 bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount,
657 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
659 csz = fetch_ulong(&ptr);
660 fetch_ulong(&ptr); /* hotspot size */
662 off = GET_UINT(ptr, 0); ptr += 4;
663 /* GET_UINT(ptr, 0); hotspot offset */ ptr += 4;
665 /* now read palette info */
666 if (type == 0x06)
668 unsigned nc = bi->bmiHeader.biClrUsed;
669 unsigned i;
671 /* not quite right, especially for bitfields type of compression */
672 if (!nc && bi->bmiHeader.biBitCount <= 8)
673 nc = 1 << bi->bmiHeader.biBitCount;
675 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
676 if (!bi) return FALSE;
677 for (i = 0; i < nc; i++)
679 bi->bmiColors[i].rgbBlue = ptr[0];
680 bi->bmiColors[i].rgbGreen = ptr[1];
681 bi->bmiColors[i].rgbRed = ptr[2];
682 bi->bmiColors[i].rgbReserved = 0;
683 ptr += 4;
686 pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack);
688 paragraph->u.gfx.u.bmp.hBitmap = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader,
689 CBM_INIT, pict_beg,
690 bi, DIB_RGB_COLORS);
691 ReleaseDC(0, hdc);
692 if (!paragraph->u.gfx.u.bmp.hBitmap)
693 WINE_ERR("Couldn't create bitmap\n");
695 HeapFree(GetProcessHeap(), 0, bi);
696 if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
698 return TRUE;
701 /******************************************************************
702 * HLPFILE_LoadMetaFile
706 static BOOL HLPFILE_LoadMetaFile(BYTE* beg, BYTE pack, HLPFILE_PARAGRAPH* paragraph)
708 BYTE* ptr;
709 unsigned long size, csize;
710 unsigned long off, hsoff;
711 BYTE* bits;
712 LPMETAFILEPICT lpmfp;
714 WINE_TRACE("Loading metafile\n");
716 ptr = beg + 2; /* for type and pack */
718 lpmfp = &paragraph->u.gfx.u.mfp;
719 lpmfp->mm = fetch_ushort(&ptr); /* mapping mode */
721 lpmfp->xExt = GET_USHORT(ptr, 0);
722 lpmfp->yExt = GET_USHORT(ptr, 2);
723 ptr += 4;
725 size = fetch_ulong(&ptr); /* decompressed size */
726 csize = fetch_ulong(&ptr); /* compressed size */
727 fetch_ulong(&ptr); /* hotspot size */
728 off = GET_UINT(ptr, 0);
729 hsoff = GET_UINT(ptr, 4);
730 ptr += 8;
732 WINE_TRACE("sz=%lu csz=%lu (%d,%d) offs=%lu/%u,%lu\n",
733 size, csize, lpmfp->xExt, lpmfp->yExt, off, ptr - beg, hsoff);
735 bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack);
736 if (!bits) return FALSE;
738 paragraph->cookie = para_metafile;
740 lpmfp->hMF = SetMetaFileBitsEx(size, bits);
742 if (!lpmfp->hMF)
743 WINE_FIXME("Couldn't load metafile\n");
745 if (bits != beg + off) HeapFree(GetProcessHeap(), 0, bits);
747 return TRUE;
750 /******************************************************************
751 * HLPFILE_LoadGfxByAddr
755 static BOOL HLPFILE_LoadGfxByAddr(HLPFILE *hlpfile, BYTE* ref,
756 unsigned long size,
757 HLPFILE_PARAGRAPH* paragraph)
759 unsigned i, numpict;
761 numpict = GET_USHORT(ref, 2);
762 WINE_TRACE("Got picture magic=%04x #=%d\n",
763 GET_USHORT(ref, 0), numpict);
765 for (i = 0; i < numpict; i++)
767 BYTE* beg;
768 BYTE* ptr;
769 BYTE type, pack;
771 WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4));
772 beg = ptr = ref + GET_UINT(ref, (1 + i) * 4);
774 type = *ptr++;
775 pack = *ptr++;
777 switch (type)
779 case 5: /* device dependent bmp */
780 case 6: /* device independent bmp */
781 HLPFILE_LoadBitmap(beg, type, pack, paragraph);
782 break;
783 case 8:
784 HLPFILE_LoadMetaFile(beg, pack, paragraph);
785 break;
786 default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
789 /* FIXME: hotspots */
791 /* FIXME: implement support for multiple picture format */
792 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
793 break;
795 return TRUE;
798 /******************************************************************
799 * HLPFILE_LoadGfxByIndex
803 static BOOL HLPFILE_LoadGfxByIndex(HLPFILE *hlpfile, unsigned index,
804 HLPFILE_PARAGRAPH* paragraph)
806 char tmp[16];
807 BYTE *ref, *end;
808 BOOL ret;
810 WINE_TRACE("Loading picture #%d\n", index);
812 if (index < hlpfile->numBmps && hlpfile->bmps[index] != NULL)
814 paragraph->u.gfx.u.bmp.hBitmap = hlpfile->bmps[index];
815 return TRUE;
818 sprintf(tmp, "|bm%u", index);
820 if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
822 ref += 9;
824 ret = HLPFILE_LoadGfxByAddr(hlpfile, ref, end - ref, paragraph);
826 /* cache bitmap */
827 if (ret && paragraph->cookie == para_bitmap)
829 if (index >= hlpfile->numBmps)
831 hlpfile->numBmps = index + 1;
832 if (hlpfile->bmps)
833 hlpfile->bmps = HeapReAlloc(GetProcessHeap(), 0, hlpfile->bmps,
834 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
835 else
836 hlpfile->bmps = HeapAlloc(GetProcessHeap(), 0,
837 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
840 hlpfile->bmps[index] = paragraph->u.gfx.u.bmp.hBitmap;
842 return ret;
845 /******************************************************************
846 * HLPFILE_AllocLink
850 static HLPFILE_LINK* HLPFILE_AllocLink(int cookie, const char* str, LONG hash,
851 BOOL clrChange, unsigned wnd)
853 HLPFILE_LINK* link;
854 char* link_str;
856 /* FIXME: should build a string table for the attributes.link.lpszPath
857 * they are reallocated for each link
859 link = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_LINK) + strlen(str) + 1);
860 if (!link) return NULL;
862 link->cookie = cookie;
863 link->lpszString = link_str = (char*)link + sizeof(HLPFILE_LINK);
864 strcpy(link_str, str);
865 link->lHash = hash;
866 link->bClrChange = clrChange ? 1 : 0;
867 link->window = wnd;
868 link->wRefCount = 1;
870 WINE_TRACE("Link[%d] to %s@%08x:%d\n",
871 link->cookie, link->lpszString,
872 link->lHash, link->window);
873 return link;
876 /***********************************************************************
878 * HLPFILE_AddParagraph
880 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
882 HLPFILE_PAGE *page;
883 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
884 UINT textsize;
885 BYTE *format, *format_end;
886 char *text, *text_base, *text_end;
887 long size, blocksize, datalen;
888 unsigned short bits;
889 unsigned nc, ncol = 1;
891 if (!hlpfile->last_page) {WINE_WARN("no page\n"); return FALSE;};
892 page = hlpfile->last_page;
893 for (paragraphptr = &page->first_paragraph; *paragraphptr;
894 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
896 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
898 blocksize = GET_UINT(buf, 0);
899 size = GET_UINT(buf, 0x4);
900 datalen = GET_UINT(buf, 0x10);
901 text = text_base = HeapAlloc(GetProcessHeap(), 0, size);
902 if (!text) return FALSE;
903 if (size > blocksize - datalen)
905 /* need to decompress */
906 if (hlpfile->hasPhrases)
907 HLPFILE_Uncompress2(buf + datalen, end, (BYTE*)text, (BYTE*)text + size);
908 else if (hlpfile->hasPhrases40)
909 HLPFILE_Uncompress3(text, text + size, buf + datalen, end);
910 else
912 WINE_FIXME("Text size is too long, splitting\n");
913 size = blocksize - datalen;
914 memcpy(text, buf + datalen, size);
917 else
918 memcpy(text, buf + datalen, size);
920 text_end = text + size;
922 format = buf + 0x15;
923 format_end = buf + GET_UINT(buf, 0x10);
925 if (buf[0x14] == 0x20 || buf[0x14] == 0x23)
927 fetch_long(&format);
928 *len = fetch_ushort(&format);
930 else *len = end-buf-15;
932 if (buf[0x14] == 0x23)
934 char type;
936 ncol = *format++;
938 WINE_TRACE("#cols %u\n", ncol);
939 type = *format++;
940 if (type == 0 || type == 2)
941 format += 2;
942 format += ncol * 4;
945 for (nc = 0; nc < ncol; nc++)
947 WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
948 if (buf[0x14] == 0x23)
949 format += 5;
950 if (buf[0x14] == 0x01)
951 format += 6;
952 else
953 format += 4;
954 bits = GET_USHORT(format, 0); format += 2;
955 if (bits & 0x0001) fetch_long(&format);
956 if (bits & 0x0002) fetch_short(&format);
957 if (bits & 0x0004) fetch_short(&format);
958 if (bits & 0x0008) fetch_short(&format);
959 if (bits & 0x0010) fetch_short(&format);
960 if (bits & 0x0020) fetch_short(&format);
961 if (bits & 0x0040) fetch_short(&format);
962 if (bits & 0x0100) format += 3;
963 if (bits & 0x0200)
965 int ntab = fetch_short(&format);
966 unsigned short ts;
968 while (ntab-- > 0)
970 ts = fetch_ushort(&format);
971 if (ts & 0x4000) fetch_ushort(&format);
974 /* 0x0400, 0x0800 and 0x1000 don't need space */
975 if ((bits & 0xE080) != 0)
976 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
978 while (text < text_end && format < format_end)
980 WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text), text, text_end, format, format_end);
981 textsize = strlen(text) + 1;
982 if (textsize > 1)
984 paragraph = HeapAlloc(GetProcessHeap(), 0,
985 sizeof(HLPFILE_PARAGRAPH) + textsize);
986 if (!paragraph) return FALSE;
987 *paragraphptr = paragraph;
988 paragraphptr = &paragraph->next;
990 paragraph->next = NULL;
991 paragraph->link = attributes.link;
992 if (paragraph->link) paragraph->link->wRefCount++;
993 paragraph->cookie = para_normal_text;
994 paragraph->u.text.wFont = attributes.wFont;
995 paragraph->u.text.wVSpace = attributes.wVSpace;
996 paragraph->u.text.wHSpace = attributes.wHSpace;
997 paragraph->u.text.wIndent = attributes.wIndent;
998 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
999 strcpy(paragraph->u.text.lpszText, text);
1001 attributes.wVSpace = 0;
1002 attributes.wHSpace = 0;
1004 /* else: null text, keep on storing attributes */
1005 text += textsize;
1007 if (*format == 0xff)
1009 format++;
1010 break;
1013 WINE_TRACE("format=%02x\n", *format);
1014 switch (*format)
1016 case 0x20:
1017 WINE_FIXME("NIY20\n");
1018 format += 5;
1019 break;
1021 case 0x21:
1022 WINE_FIXME("NIY21\n");
1023 format += 3;
1024 break;
1026 case 0x80:
1027 attributes.wFont = GET_USHORT(format, 1);
1028 WINE_TRACE("Changing font to %d\n", attributes.wFont);
1029 format += 3;
1030 break;
1032 case 0x81:
1033 attributes.wVSpace++;
1034 format += 1;
1035 break;
1037 case 0x82:
1038 attributes.wVSpace++;
1039 attributes.wIndent = 0;
1040 format += 1;
1041 break;
1043 case 0x83:
1044 attributes.wIndent++;
1045 format += 1;
1046 break;
1048 #if 0
1049 case 0x84:
1050 format += 3;
1051 break;
1052 #endif
1054 case 0x86:
1055 case 0x87:
1056 case 0x88:
1058 BYTE pos = (*format - 0x86);
1059 BYTE type = format[1];
1060 long size;
1062 format += 2;
1063 size = fetch_long(&format);
1065 paragraph = HeapAlloc(GetProcessHeap(), 0,
1066 sizeof(HLPFILE_PARAGRAPH) + textsize);
1067 if (!paragraph) return FALSE;
1068 *paragraphptr = paragraph;
1069 paragraphptr = &paragraph->next;
1071 paragraph->next = NULL;
1072 paragraph->link = attributes.link;
1073 if (paragraph->link) paragraph->link->wRefCount++;
1074 paragraph->cookie = para_bitmap;
1075 paragraph->u.gfx.pos = pos;
1076 switch (type)
1078 case 0x22:
1079 fetch_ushort(&format); /* hot spot */
1080 /* fall thru */
1081 case 0x03:
1082 switch (GET_SHORT(format, 0))
1084 case 0:
1085 HLPFILE_LoadGfxByIndex(hlpfile, GET_SHORT(format, 2),
1086 paragraph);
1087 break;
1088 case 1:
1089 WINE_FIXME("does it work ??? %x<%lu>#%u\n",
1090 GET_SHORT(format, 0),
1091 size, GET_SHORT(format, 2));
1092 HLPFILE_LoadGfxByAddr(hlpfile, format + 2, size - 4,
1093 paragraph);
1094 break;
1095 default:
1096 WINE_FIXME("??? %u\n", GET_SHORT(format, 0));
1097 break;
1099 break;
1100 case 0x05:
1101 WINE_FIXME("Got an embedded element %s\n", format + 6);
1102 break;
1103 default:
1104 WINE_FIXME("Got a type %d picture\n", type);
1105 break;
1107 if (attributes.wVSpace) paragraph->u.gfx.pos |= 0x8000;
1109 format += size;
1111 break;
1113 case 0x89:
1114 HLPFILE_FreeLink(attributes.link);
1115 attributes.link = NULL;
1116 format += 1;
1117 break;
1119 case 0x8B:
1120 case 0x8C:
1121 WINE_FIXME("NIY non-break space/hyphen\n");
1122 format += 1;
1123 break;
1125 #if 0
1126 case 0xA9:
1127 format += 2;
1128 break;
1129 #endif
1131 case 0xC8:
1132 case 0xCC:
1133 WINE_TRACE("macro => %s\n", format + 3);
1134 HLPFILE_FreeLink(attributes.link);
1135 attributes.link = HLPFILE_AllocLink(hlp_link_macro, (const char*)format + 3,
1136 0, !(*format & 4), -1);
1137 format += 3 + GET_USHORT(format, 1);
1138 break;
1140 case 0xE0:
1141 case 0xE1:
1142 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
1143 HLPFILE_FreeLink(attributes.link);
1144 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1145 hlpfile->lpszPath,
1146 GET_UINT(format, 1)-16,
1147 1, -1);
1150 format += 5;
1151 break;
1153 case 0xE2:
1154 case 0xE3:
1155 case 0xE6:
1156 case 0xE7:
1157 HLPFILE_FreeLink(attributes.link);
1158 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1159 hlpfile->lpszPath,
1160 GET_UINT(format, 1),
1161 !(*format & 4), -1);
1162 format += 5;
1163 break;
1165 case 0xEA:
1166 case 0xEB:
1167 case 0xEE:
1168 case 0xEF:
1170 char* ptr = (char*) format + 8;
1171 BYTE type = format[3];
1172 int wnd = -1;
1173 char* str;
1175 if (type == 1) wnd = *ptr++;
1176 if (type == 4 || type == 6)
1178 str = ptr;
1179 ptr += strlen(ptr) + 1;
1181 else
1182 str = hlpfile->lpszPath;
1183 if (type == 6)
1185 for (wnd = hlpfile->numWindows - 1; wnd >= 0; wnd--)
1187 if (!strcmp(ptr, hlpfile->windows[wnd].name)) break;
1189 if (wnd == -1)
1190 WINE_WARN("Couldn't find window info for %s\n", ptr);
1192 HLPFILE_FreeLink(attributes.link);
1193 attributes.link = HLPFILE_AllocLink((*format & 4) ? hlp_link_link : hlp_link_popup,
1194 str, GET_UINT(format, 4),
1195 !(*format & 1), wnd);
1197 format += 3 + GET_USHORT(format, 1);
1198 break;
1200 default:
1201 WINE_WARN("format %02x\n", *format);
1202 format++;
1206 HeapFree(GetProcessHeap(), 0, text_base);
1207 return TRUE;
1210 /******************************************************************
1211 * HLPFILE_ReadFont
1215 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
1217 BYTE *ref, *end;
1218 unsigned i, len, idx;
1219 unsigned face_num, dscr_num, face_offset, dscr_offset;
1220 BYTE flag, family;
1222 if (!HLPFILE_FindSubFile("|FONT", &ref, &end))
1224 WINE_WARN("no subfile FONT\n");
1225 hlpfile->numFonts = 0;
1226 hlpfile->fonts = NULL;
1227 return FALSE;
1230 ref += 9;
1232 face_num = GET_USHORT(ref, 0);
1233 dscr_num = GET_USHORT(ref, 2);
1234 face_offset = GET_USHORT(ref, 4);
1235 dscr_offset = GET_USHORT(ref, 6);
1237 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1238 face_num, face_offset, dscr_num, dscr_offset);
1240 hlpfile->numFonts = dscr_num;
1241 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
1243 len = (dscr_offset - face_offset) / face_num;
1244 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
1245 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1246 for (i = 0; i < dscr_num; i++)
1248 flag = ref[dscr_offset + i * 11 + 0];
1249 family = ref[dscr_offset + i * 11 + 2];
1251 hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2 - 3;
1252 hlpfile->fonts[i].LogFont.lfWidth = 0;
1253 hlpfile->fonts[i].LogFont.lfEscapement = 0;
1254 hlpfile->fonts[i].LogFont.lfOrientation = 0;
1255 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
1256 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
1257 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
1258 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
1259 hlpfile->fonts[i].LogFont.lfCharSet = DEFAULT_CHARSET;
1260 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1261 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1262 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
1263 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
1265 switch (family)
1267 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break;
1268 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break;
1269 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break;
1270 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break;
1271 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1272 default: WINE_FIXME("Unknown family %u\n", family);
1274 idx = GET_USHORT(ref, dscr_offset + i * 11 + 3);
1276 if (idx < face_num)
1278 memcpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1279 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1)] = '\0';
1281 else
1283 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1284 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1286 hlpfile->fonts[i].hFont = 0;
1287 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1288 ref[dscr_offset + i * 11 + 6],
1289 ref[dscr_offset + i * 11 + 7]);
1290 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1291 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
1292 i, flag,
1293 X(0, "bold"),
1294 X(1, "italic"),
1295 X(2, "underline"),
1296 X(3, "strikeOut"),
1297 X(4, "dblUnderline"),
1298 X(5, "smallCaps"),
1299 ref[dscr_offset + i * 11 + 1],
1300 family,
1301 hlpfile->fonts[i].LogFont.lfFaceName, idx,
1302 GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1304 return TRUE;
1307 /***********************************************************************
1309 * HLPFILE_ReadFileToBuffer
1311 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1313 BYTE header[16], dummy[1];
1315 if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
1317 /* sanity checks */
1318 if (GET_UINT(header, 0) != 0x00035F3F)
1319 {WINE_WARN("wrong header\n"); return FALSE;};
1321 file_buffer_size = GET_UINT(header, 12);
1322 file_buffer = HeapAlloc(GetProcessHeap(), 0, file_buffer_size + 1);
1323 if (!file_buffer) return FALSE;
1325 memcpy(file_buffer, header, 16);
1326 if (_hread(hFile, file_buffer + 16, file_buffer_size - 16) != file_buffer_size - 16)
1327 {WINE_WARN("filesize1\n"); return FALSE;};
1329 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1331 file_buffer[file_buffer_size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1333 return TRUE;
1336 /**************************************************************************
1337 * comp_FindSubFile
1339 * HLPFILE_BPTreeCompare function for HLPFILE directory.
1342 static int comp_FindSubFile(void *p, const void *key,
1343 int leaf, void** next)
1345 *next = (char *)p+strlen(p)+(leaf?5:3);
1346 WINE_TRACE("Comparing '%s' with '%s'\n", (char *)p, (char *)key);
1347 return strcmp(p, key);
1350 /***********************************************************************
1352 * HLPFILE_FindSubFile
1354 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1356 BYTE *ptr;
1358 WINE_TRACE("looking for file '%s'\n", name);
1359 ptr = HLPFILE_BPTreeSearch(file_buffer + GET_UINT(file_buffer, 4),
1360 name, comp_FindSubFile);
1361 if (!ptr) return FALSE;
1362 *subbuf = file_buffer + GET_UINT(ptr, strlen(name)+1);
1363 if (*subbuf >= file_buffer + file_buffer_size)
1365 WINE_ERR("internal file %s does not fit\n", name);
1366 return FALSE;
1368 *subend = *subbuf + GET_UINT(*subbuf, 0);
1369 if (*subend > file_buffer + file_buffer_size)
1371 WINE_ERR("internal file %s does not fit\n", name);
1372 return FALSE;
1374 if (GET_UINT(*subbuf, 0) < GET_UINT(*subbuf, 4) + 9)
1376 WINE_ERR("invalid size provided for internal file %s\n", name);
1377 return FALSE;
1379 return TRUE;
1382 /***********************************************************************
1384 * HLPFILE_SystemCommands
1386 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1388 BYTE *buf, *ptr, *end;
1389 HLPFILE_MACRO *macro, **m;
1390 LPSTR p;
1391 unsigned short magic, minor, major, flags;
1393 hlpfile->lpszTitle = NULL;
1395 if (!HLPFILE_FindSubFile("|SYSTEM", &buf, &end)) return FALSE;
1397 magic = GET_USHORT(buf + 9, 0);
1398 minor = GET_USHORT(buf + 9, 2);
1399 major = GET_USHORT(buf + 9, 4);
1400 /* gen date on 4 bytes */
1401 flags = GET_USHORT(buf + 9, 10);
1402 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1403 magic, major, minor, flags);
1404 if (magic != 0x036C || major != 1)
1405 {WINE_WARN("Wrong system header\n"); return FALSE;}
1406 if (minor <= 16)
1408 hlpfile->tbsize = 0x800;
1409 hlpfile->compressed = 0;
1411 else if (flags == 0)
1413 hlpfile->tbsize = 0x1000;
1414 hlpfile->compressed = 0;
1416 else if (flags == 4)
1418 hlpfile->tbsize = 0x1000;
1419 hlpfile->compressed = 1;
1421 else
1423 hlpfile->tbsize = 0x800;
1424 hlpfile->compressed = 1;
1427 if (hlpfile->compressed)
1428 hlpfile->dsize = 0x4000;
1429 else
1430 hlpfile->dsize = hlpfile->tbsize - 0x0C;
1432 hlpfile->version = minor;
1433 hlpfile->flags = flags;
1435 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1437 char *str = (char*) ptr + 4;
1438 switch (GET_USHORT(ptr, 0))
1440 case 1:
1441 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1442 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1443 if (!hlpfile->lpszTitle) return FALSE;
1444 lstrcpy(hlpfile->lpszTitle, str);
1445 WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1446 break;
1448 case 2:
1449 if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
1450 hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1451 if (!hlpfile->lpszCopyright) return FALSE;
1452 lstrcpy(hlpfile->lpszCopyright, str);
1453 WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
1454 break;
1456 case 3:
1457 if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
1458 hlpfile->contents_start = GET_UINT(ptr, 4);
1459 WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
1460 break;
1462 case 4:
1463 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(str) + 1);
1464 if (!macro) break;
1465 p = (char*)macro + sizeof(HLPFILE_MACRO);
1466 lstrcpy(p, str);
1467 macro->lpszMacro = p;
1468 macro->next = 0;
1469 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1470 *m = macro;
1471 break;
1473 case 6:
1474 if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
1476 if (hlpfile->windows)
1477 hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows,
1478 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1479 else
1480 hlpfile->windows = HeapAlloc(GetProcessHeap(), 0,
1481 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1483 if (hlpfile->windows)
1485 unsigned flags = GET_USHORT(ptr, 4);
1486 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
1488 if (flags & 0x0001) strcpy(wi->type, &str[2]);
1489 else wi->type[0] = '\0';
1490 if (flags & 0x0002) strcpy(wi->name, &str[12]);
1491 else wi->name[0] = '\0';
1492 if (flags & 0x0004) strcpy(wi->caption, &str[23]);
1493 else lstrcpynA(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
1494 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
1495 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
1496 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
1497 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
1498 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
1499 wi->win_style = WS_OVERLAPPEDWINDOW;
1500 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
1501 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
1502 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",
1503 flags & 0x0001 ? 'T' : 't',
1504 flags & 0x0002 ? 'N' : 'n',
1505 flags & 0x0004 ? 'C' : 'c',
1506 flags & 0x0008 ? 'X' : 'x',
1507 flags & 0x0010 ? 'Y' : 'y',
1508 flags & 0x0020 ? 'W' : 'w',
1509 flags & 0x0040 ? 'H' : 'h',
1510 flags & 0x0080 ? 'S' : 's',
1511 wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
1512 wi->size.cx, wi->size.cy);
1514 break;
1515 default:
1516 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1519 if (!hlpfile->lpszTitle)
1520 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1);
1521 return TRUE;
1524 /***********************************************************************
1526 * HLPFILE_UncompressedLZ77_Size
1528 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1530 int i, newsize = 0;
1532 while (ptr < end)
1534 int mask = *ptr++;
1535 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1537 if (mask & 1)
1539 int code = GET_USHORT(ptr, 0);
1540 int len = 3 + (code >> 12);
1541 newsize += len;
1542 ptr += 2;
1544 else newsize++, ptr++;
1548 return newsize;
1551 /***********************************************************************
1553 * HLPFILE_UncompressLZ77
1555 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1557 int i;
1559 while (ptr < end)
1561 int mask = *ptr++;
1562 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1564 if (mask & 1)
1566 int code = GET_USHORT(ptr, 0);
1567 int len = 3 + (code >> 12);
1568 int offset = code & 0xfff;
1570 * We must copy byte-by-byte here. We cannot use memcpy nor
1571 * memmove here. Just example:
1572 * a[]={1,2,3,4,5,6,7,8,9,10}
1573 * newptr=a+2;
1574 * offset=1;
1575 * We expect:
1576 * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12}
1578 for (; len>0; len--, newptr++) *newptr = *(newptr-offset-1);
1579 ptr += 2;
1581 else *newptr++ = *ptr++;
1585 return newptr;
1588 /***********************************************************************
1590 * HLPFILE_UncompressLZ77_Phrases
1592 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1594 UINT i, num, dec_size, head_size;
1595 BYTE *buf, *end;
1597 if (!HLPFILE_FindSubFile("|Phrases", &buf, &end)) return FALSE;
1599 if (hlpfile->version <= 16)
1600 head_size = 13;
1601 else
1602 head_size = 17;
1604 num = phrases.num = GET_USHORT(buf, 9);
1605 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1607 if (hlpfile->version <= 16)
1608 dec_size = end - buf - 15 - 2 * num;
1609 else
1610 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1612 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1613 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1614 if (!phrases.offsets || !phrases.buffer)
1616 HeapFree(GetProcessHeap(), 0, phrases.offsets);
1617 HeapFree(GetProcessHeap(), 0, phrases.buffer);
1618 return FALSE;
1621 for (i = 0; i <= num; i++)
1622 phrases.offsets[i] = GET_USHORT(buf, head_size + 2 * i) - 2 * num - 2;
1624 if (hlpfile->version <= 16)
1625 memcpy(phrases.buffer, buf + 15 + 2*num, dec_size);
1626 else
1627 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, (BYTE*)phrases.buffer);
1629 hlpfile->hasPhrases = TRUE;
1630 return TRUE;
1633 /***********************************************************************
1635 * HLPFILE_Uncompress_Phrases40
1637 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1639 UINT num;
1640 INT dec_size, cpr_size;
1641 BYTE *buf_idx, *end_idx;
1642 BYTE *buf_phs, *end_phs;
1643 long* ptr, mask = 0;
1644 unsigned int i;
1645 unsigned short bc, n;
1647 if (!HLPFILE_FindSubFile("|PhrIndex", &buf_idx, &end_idx) ||
1648 !HLPFILE_FindSubFile("|PhrImage", &buf_phs, &end_phs)) return FALSE;
1650 ptr = (long*)(buf_idx + 9 + 28);
1651 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1652 num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1654 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1655 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1656 GET_UINT(buf_idx, 9 + 0),
1657 GET_UINT(buf_idx, 9 + 4),
1658 GET_UINT(buf_idx, 9 + 8),
1659 GET_UINT(buf_idx, 9 + 12),
1660 GET_UINT(buf_idx, 9 + 16),
1661 GET_UINT(buf_idx, 9 + 20),
1662 GET_USHORT(buf_idx, 9 + 24),
1663 GET_USHORT(buf_idx, 9 + 26));
1665 dec_size = GET_UINT(buf_idx, 9 + 12);
1666 cpr_size = GET_UINT(buf_idx, 9 + 16);
1668 if (dec_size != cpr_size &&
1669 dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1671 WINE_WARN("size mismatch %u %u\n",
1672 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1673 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1676 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1677 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1678 if (!phrases.offsets || !phrases.buffer)
1680 HeapFree(GetProcessHeap(), 0, phrases.offsets);
1681 HeapFree(GetProcessHeap(), 0, phrases.buffer);
1682 return FALSE;
1685 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1687 phrases.offsets[0] = 0;
1688 for (i = 0; i < num; i++)
1690 for (n = 1; getbit(); n += 1 << bc);
1691 if (getbit()) n++;
1692 if (bc > 1 && getbit()) n += 2;
1693 if (bc > 2 && getbit()) n += 4;
1694 if (bc > 3 && getbit()) n += 8;
1695 if (bc > 4 && getbit()) n += 16;
1696 phrases.offsets[i + 1] = phrases.offsets[i] + n;
1698 #undef getbit
1700 if (dec_size == cpr_size)
1701 memcpy(phrases.buffer, buf_phs + 9, dec_size);
1702 else
1703 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, (BYTE*)phrases.buffer);
1705 hlpfile->hasPhrases40 = TRUE;
1706 return TRUE;
1709 /***********************************************************************
1711 * HLPFILE_Uncompress_Topic
1713 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
1715 BYTE *buf, *ptr, *end, *newptr;
1716 unsigned int i, newsize = 0;
1717 unsigned int topic_size;
1719 if (!HLPFILE_FindSubFile("|TOPIC", &buf, &end))
1720 {WINE_WARN("topic0\n"); return FALSE;}
1722 buf += 9; /* Skip file header */
1723 topic_size = end - buf;
1724 if (hlpfile->compressed)
1726 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1728 for (i = 0; i < topic.wMapLen; i++)
1730 ptr = buf + i * hlpfile->tbsize;
1732 /* I don't know why, it's necessary for printman.hlp */
1733 if (ptr + 0x44 > end) ptr = end - 0x44;
1735 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + hlpfile->tbsize));
1738 topic.map = HeapAlloc(GetProcessHeap(), 0,
1739 topic.wMapLen * sizeof(topic.map[0]) + newsize);
1740 if (!topic.map) return FALSE;
1741 newptr = (BYTE*)(topic.map + topic.wMapLen);
1742 topic.end = newptr + newsize;
1744 for (i = 0; i < topic.wMapLen; i++)
1746 ptr = buf + i * hlpfile->tbsize;
1747 if (ptr + 0x44 > end) ptr = end - 0x44;
1749 topic.map[i] = newptr;
1750 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + hlpfile->tbsize), newptr);
1753 else
1755 /* basically, we need to copy the TopicBlockSize byte pages
1756 * (removing the first 0x0C) in one single area in memory
1758 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1759 topic.map = HeapAlloc(GetProcessHeap(), 0,
1760 topic.wMapLen * (sizeof(topic.map[0]) + hlpfile->dsize));
1761 if (!topic.map) return FALSE;
1762 newptr = (BYTE*)(topic.map + topic.wMapLen);
1763 topic.end = newptr + topic_size;
1765 for (i = 0; i < topic.wMapLen; i++)
1767 topic.map[i] = newptr + i * hlpfile->dsize;
1768 memcpy(topic.map[i], buf + i * hlpfile->tbsize + 0x0C, hlpfile->dsize);
1771 return TRUE;
1774 /***********************************************************************
1776 * HLPFILE_Uncompress2
1779 static void HLPFILE_Uncompress2(const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
1781 BYTE *phptr, *phend;
1782 UINT code;
1783 UINT index;
1785 while (ptr < end && newptr < newend)
1787 if (!*ptr || *ptr >= 0x10)
1788 *newptr++ = *ptr++;
1789 else
1791 code = 0x100 * ptr[0] + ptr[1];
1792 index = (code - 0x100) / 2;
1794 phptr = (BYTE*)phrases.buffer + phrases.offsets[index];
1795 phend = (BYTE*)phrases.buffer + phrases.offsets[index + 1];
1797 if (newptr + (phend - phptr) > newend)
1799 WINE_FIXME("buffer overflow %p > %p for %d bytes\n",
1800 newptr, newend, phend - phptr);
1801 return;
1803 memcpy(newptr, phptr, phend - phptr);
1804 newptr += phend - phptr;
1805 if (code & 1) *newptr++ = ' ';
1807 ptr += 2;
1810 if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
1813 /******************************************************************
1814 * HLPFILE_Uncompress3
1818 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1819 const BYTE* src, const BYTE* src_end)
1821 unsigned int idx, len;
1823 for (; src < src_end; src++)
1825 if ((*src & 1) == 0)
1827 idx = *src / 2;
1828 if (idx > phrases.num)
1830 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1831 len = 0;
1833 else
1835 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1836 if (dst + len <= dst_end)
1837 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1840 else if ((*src & 0x03) == 0x01)
1842 idx = (*src + 1) * 64;
1843 idx += *++src;
1844 if (idx > phrases.num)
1846 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1847 len = 0;
1849 else
1851 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1852 if (dst + len <= dst_end)
1853 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1856 else if ((*src & 0x07) == 0x03)
1858 len = (*src / 8) + 1;
1859 if (dst + len <= dst_end)
1860 memcpy(dst, src + 1, len);
1861 src += len;
1863 else
1865 len = (*src / 16) + 1;
1866 if (dst + len <= dst_end)
1867 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1869 dst += len;
1872 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1873 return TRUE;
1876 /******************************************************************
1877 * HLPFILE_UncompressRLE
1881 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz)
1883 BYTE ch;
1884 BYTE* sdst = *dst + dstsz;
1886 while (src < end)
1888 ch = *src++;
1889 if (ch & 0x80)
1891 ch &= 0x7F;
1892 if ((*dst) + ch <= sdst)
1893 memcpy(*dst, src, ch);
1894 src += ch;
1896 else
1898 if ((*dst) + ch <= sdst)
1899 memset(*dst, (char)*src, ch);
1900 src++;
1902 *dst += ch;
1904 if (*dst != sdst)
1905 WINE_WARN("Buffer X-flow: d(%u) instead of d(%u)\n",
1906 *dst - (sdst - dstsz), dstsz);
1909 /**************************************************************************
1910 * HLPFILE_BPTreeSearch
1912 * Searches for an element in B+ tree
1914 * PARAMS
1915 * buf [I] pointer to the embedded file structured as a B+ tree
1916 * key [I] pointer to data to find
1917 * comp [I] compare function
1919 * RETURNS
1920 * Pointer to block identified by key, or NULL if failure.
1923 void* HLPFILE_BPTreeSearch(BYTE* buf, const void* key,
1924 HLPFILE_BPTreeCompare comp)
1926 unsigned magic;
1927 unsigned page_size;
1928 unsigned cur_page;
1929 unsigned level;
1930 BYTE *pages, *ptr, *newptr;
1931 int i, entries;
1932 int ret;
1934 magic = GET_USHORT(buf, 9);
1935 if (magic != 0x293B)
1937 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic);
1938 return NULL;
1940 page_size = GET_USHORT(buf, 9+4);
1941 cur_page = GET_USHORT(buf, 9+26);
1942 level = GET_USHORT(buf, 9+32);
1943 pages = buf + 9 + 38;
1944 while (--level > 0)
1946 ptr = pages + cur_page*page_size;
1947 entries = GET_SHORT(ptr, 2);
1948 ptr += 6;
1949 for (i = 0; i < entries; i++)
1951 if (comp(ptr, key, 0, (void **)&newptr) > 0) break;
1952 ptr = newptr;
1954 cur_page = GET_USHORT(ptr-2, 0);
1956 ptr = pages + cur_page*page_size;
1957 entries = GET_SHORT(ptr, 2);
1958 ptr += 8;
1959 for (i = 0; i < entries; i++)
1961 ret = comp(ptr, key, 1, (void **)&newptr);
1962 if (ret == 0) return ptr;
1963 if (ret > 0) return NULL;
1964 ptr = newptr;
1966 return NULL;
1969 /**************************************************************************
1970 * HLPFILE_BPTreeEnum
1972 * Enumerates elements in B+ tree.
1974 * PARAMS
1975 * buf [I] pointer to the embedded file structured as a B+ tree
1976 * cb [I] compare function
1977 * cookie [IO] cookie for cb function
1979 void HLPFILE_BPTreeEnum(BYTE* buf, HLPFILE_BPTreeCallback cb, void* cookie)
1981 unsigned magic;
1982 unsigned page_size;
1983 unsigned cur_page;
1984 unsigned level;
1985 BYTE *pages, *ptr, *newptr;
1986 int i, entries;
1988 magic = GET_USHORT(buf, 9);
1989 if (magic != 0x293B)
1991 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic);
1992 return;
1994 page_size = GET_USHORT(buf, 9+4);
1995 cur_page = GET_USHORT(buf, 9+26);
1996 level = GET_USHORT(buf, 9+32);
1997 pages = buf + 9 + 38;
1998 while (--level > 0)
2000 ptr = pages + cur_page*page_size;
2001 cur_page = GET_USHORT(ptr, 4);
2003 while (cur_page != 0xFFFF)
2005 ptr = pages + cur_page*page_size;
2006 entries = GET_SHORT(ptr, 2);
2007 ptr += 8;
2008 for (i = 0; i < entries; i++)
2010 cb(ptr, (void **)&newptr, cookie);
2011 ptr = newptr;
2013 cur_page = GET_USHORT(pages+cur_page*page_size, 6);
2018 /***********************************************************************
2020 * HLPFILE_GetContext
2022 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
2024 BYTE *cbuf, *cend;
2025 unsigned clen;
2027 if (!HLPFILE_FindSubFile("|CONTEXT", &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
2029 clen = cend - cbuf;
2030 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen);
2031 if (!hlpfile->Context) return FALSE;
2032 memcpy(hlpfile->Context, cbuf, clen);
2034 return TRUE;
2037 /***********************************************************************
2039 * HLPFILE_GetKeywords
2041 static BOOL HLPFILE_GetKeywords(HLPFILE *hlpfile)
2043 BYTE *cbuf, *cend;
2044 unsigned clen;
2046 if (!HLPFILE_FindSubFile("|KWBTREE", &cbuf, &cend)) return FALSE;
2047 clen = cend - cbuf;
2048 hlpfile->kwbtree = HeapAlloc(GetProcessHeap(), 0, clen);
2049 if (!hlpfile->kwbtree) return FALSE;
2050 memcpy(hlpfile->kwbtree, cbuf, clen);
2052 if (!HLPFILE_FindSubFile("|KWDATA", &cbuf, &cend))
2054 WINE_ERR("corrupted help file: kwbtree present but kwdata absent\n");
2055 HeapFree(GetProcessHeap(), 0, hlpfile->kwbtree);
2056 return FALSE;
2058 clen = cend - cbuf;
2059 hlpfile->kwdata = HeapAlloc(GetProcessHeap(), 0, clen);
2060 if (!hlpfile->kwdata)
2062 HeapFree(GetProcessHeap(), 0, hlpfile->kwdata);
2063 return FALSE;
2065 memcpy(hlpfile->kwdata, cbuf, clen);
2067 return TRUE;
2070 /***********************************************************************
2072 * HLPFILE_GetMap
2074 static BOOL HLPFILE_GetMap(HLPFILE *hlpfile)
2076 BYTE *cbuf, *cend;
2077 unsigned entries, i;
2079 if (!HLPFILE_FindSubFile("|CTXOMAP", &cbuf, &cend)) {WINE_WARN("no map section\n"); return FALSE;}
2081 entries = GET_USHORT(cbuf, 9);
2082 hlpfile->Map = HeapAlloc(GetProcessHeap(), 0, entries * sizeof(HLPFILE_MAP));
2083 if (!hlpfile->Map) return FALSE;
2084 hlpfile->wMapLen = entries;
2085 for (i = 0; i < entries; i++)
2087 hlpfile->Map[i].lMap = GET_UINT(cbuf+11,i*8);
2088 hlpfile->Map[i].offset = GET_UINT(cbuf+11,i*8+4);
2090 return TRUE;
2093 /******************************************************************
2094 * HLPFILE_DeleteLink
2098 void HLPFILE_FreeLink(HLPFILE_LINK* link)
2100 if (link && !--link->wRefCount)
2101 HeapFree(GetProcessHeap(), 0, link);
2104 /***********************************************************************
2106 * HLPFILE_DeleteParagraph
2108 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
2110 HLPFILE_PARAGRAPH* next;
2112 while (paragraph)
2114 next = paragraph->next;
2116 if (paragraph->cookie == para_metafile)
2117 DeleteMetaFile(paragraph->u.gfx.u.mfp.hMF);
2119 HLPFILE_FreeLink(paragraph->link);
2121 HeapFree(GetProcessHeap(), 0, paragraph);
2122 paragraph = next;
2126 /***********************************************************************
2128 * DeleteMacro
2130 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
2132 HLPFILE_MACRO* next;
2134 while (macro)
2136 next = macro->next;
2137 HeapFree(GetProcessHeap(), 0, macro);
2138 macro = next;
2142 /***********************************************************************
2144 * DeletePage
2146 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
2148 HLPFILE_PAGE* next;
2150 while (page)
2152 next = page->next;
2153 HLPFILE_DeleteParagraph(page->first_paragraph);
2154 HLPFILE_DeleteMacro(page->first_macro);
2155 HeapFree(GetProcessHeap(), 0, page);
2156 page = next;
2160 /***********************************************************************
2162 * HLPFILE_FreeHlpFile
2164 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
2166 unsigned i;
2168 if (!hlpfile || --hlpfile->wRefCount > 0) return;
2170 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
2171 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
2172 else first_hlpfile = hlpfile->next;
2174 if (hlpfile->numFonts)
2176 for (i = 0; i < hlpfile->numFonts; i++)
2178 DeleteObject(hlpfile->fonts[i].hFont);
2180 HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
2183 if (hlpfile->numBmps)
2185 for (i = 0; i < hlpfile->numBmps; i++)
2187 DeleteObject(hlpfile->bmps[i]);
2189 HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
2192 HLPFILE_DeletePage(hlpfile->first_page);
2193 HLPFILE_DeleteMacro(hlpfile->first_macro);
2195 if (hlpfile->numWindows) HeapFree(GetProcessHeap(), 0, hlpfile->windows);
2196 HeapFree(GetProcessHeap(), 0, hlpfile->Context);
2197 HeapFree(GetProcessHeap(), 0, hlpfile->Map);
2198 HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
2199 HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
2200 HeapFree(GetProcessHeap(), 0, hlpfile);