Wrote back & history support.
[wine/wine-kai.git] / programs / winhelp / hlpfile.c
blob60e815934e975091949622dc482fab1dbec75f64
1 /*
2 * Help Viewer
4 * Copyright 1996 Ulrich Schmid
5 * 2002 Eric Pouech
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <stdio.h>
23 #include <string.h>
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winhelp.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
33 #define GET_USHORT(buffer, i)\
34 (((BYTE)((buffer)[(i)]) + 0x100 * (BYTE)((buffer)[(i)+1])))
35 #define GET_SHORT(buffer, i)\
36 (((BYTE)((buffer)[(i)]) + 0x100 * (signed char)((buffer)[(i)+1])))
37 #define GET_UINT(buffer, i)\
38 GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i+2)
40 static HLPFILE *first_hlpfile = 0;
41 static BYTE *file_buffer;
43 static struct
45 UINT num;
46 unsigned* offsets;
47 char* buffer;
48 } phrases;
50 static struct
52 BYTE** map;
53 BYTE* end;
54 UINT wMapLen;
55 } topic;
57 static struct
59 UINT bDebug;
60 UINT wFont;
61 UINT wIndent;
62 UINT wHSpace;
63 UINT wVSpace;
64 UINT wVBackSpace;
65 HLPFILE_LINK link;
66 HBITMAP hBitmap;
67 UINT bmpPos;
68 } attributes;
70 static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR);
71 static BOOL HLPFILE_ReadFileToBuffer(HFILE);
72 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**);
73 static BOOL HLPFILE_SystemCommands(HLPFILE*);
74 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end);
75 static BYTE* HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr);
76 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE*);
77 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE*);
78 static BOOL HLPFILE_Uncompress_Topic(HLPFILE*);
79 static BOOL HLPFILE_GetContext(HLPFILE*);
80 static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*, unsigned);
81 static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*, unsigned*);
82 static void HLPFILE_Uncompress2(const BYTE*, const BYTE*, BYTE*, const BYTE*);
83 static BOOL HLPFILE_Uncompress3(char*, const char*, const BYTE*, const BYTE*);
84 static void HLPFILE_UncompressRLE(const BYTE* src, unsigned sz, BYTE** dst);
85 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile);
87 /***********************************************************************
89 * HLPFILE_PageByNumber
91 HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum)
93 HLPFILE_PAGE *page;
94 HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath);
96 if (!hlpfile) return 0;
98 WINE_TRACE("[%s/%u]\n", lpszPath, wNum);
100 for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--;
102 return page;
105 /* FIXME:
106 * this finds the page containing the offset. The offset can either
107 * refer to the top of the page (offset == page->offset), or
108 * to some paragraph inside the page...
109 * As of today, we only return the page... we should also return
110 * a paragraph, and then, while opening a new page, compute the
111 * y-offset of the paragraph to be shown and scroll the window
112 * accordinly
114 /******************************************************************
115 * HLPFILE_PageByOffset
119 HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset)
121 HLPFILE_PAGE* page;
122 HLPFILE_PAGE* found;
124 if (!hlpfile) return 0;
126 WINE_TRACE("<%s>[%lx]\n", hlpfile->lpszPath, offset);
128 if (offset == 0xFFFFFFFF) return NULL;
129 page = NULL;
131 for (found = NULL, page = hlpfile->first_page; page; page = page->next)
133 if (page->offset <= offset && (!found || found->offset < page->offset))
134 found = page;
136 if (!found)
137 WINE_ERR("Page of offset %lu not found in file %s\n",
138 offset, hlpfile->lpszPath);
139 return found;
142 /***********************************************************************
144 * HLPFILE_HlpFilePageByHash
146 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash)
148 int i;
150 if (!hlpfile) return 0;
152 WINE_TRACE("<%s>[%lx]\n", hlpfile->lpszPath, lHash);
154 for (i = 0; i < hlpfile->wContextLen; i++)
156 if (hlpfile->Context[i].lHash == lHash)
157 return HLPFILE_PageByOffset(hlpfile, hlpfile->Context[i].offset);
160 WINE_ERR("Page of hash %lx not found in file %s\n", lHash, hlpfile->lpszPath);
161 return NULL;
164 /***********************************************************************
166 * HLPFILE_Contents
168 HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile)
170 HLPFILE_PAGE* page = NULL;
172 if (!hlpfile) return NULL;
174 page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start);
175 if (!page) page = hlpfile->first_page;
176 return page;
179 /***********************************************************************
181 * HLPFILE_Hash
183 LONG HLPFILE_Hash(LPCSTR lpszContext)
185 LONG lHash = 0;
186 CHAR c;
188 while ((c = *lpszContext++))
190 CHAR x = 0;
191 if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
192 if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
193 if (c >= '1' && c <= '9') x = c - '0';
194 if (c == '0') x = 10;
195 if (c == '.') x = 12;
196 if (c == '_') x = 13;
197 if (x) lHash = lHash * 43 + x;
199 return lHash;
202 /***********************************************************************
204 * HLPFILE_ReadHlpFile
206 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
208 HLPFILE* hlpfile;
210 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
212 if (!lstrcmp(hlpfile->lpszPath, lpszPath))
214 hlpfile->wRefCount++;
215 return hlpfile;
219 hlpfile = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
220 if (!hlpfile) return 0;
222 hlpfile->lpszPath = (char*)hlpfile + sizeof(HLPFILE);
223 hlpfile->lpszTitle = NULL;
224 hlpfile->lpszCopyright = NULL;
225 hlpfile->first_page = NULL;
226 hlpfile->first_macro = NULL;
227 hlpfile->wContextLen = 0;
228 hlpfile->Context = NULL;
229 hlpfile->contents_start = 0xFFFFFFFF;
230 hlpfile->prev = NULL;
231 hlpfile->next = first_hlpfile;
232 hlpfile->wRefCount = 1;
234 hlpfile->numBmps = 0;
235 hlpfile->bmps = NULL;
237 hlpfile->numFonts = 0;
238 hlpfile->fonts = NULL;
240 hlpfile->numWindows = 0;
241 hlpfile->windows = NULL;
243 strcpy(hlpfile->lpszPath, lpszPath);
245 first_hlpfile = hlpfile;
246 if (hlpfile->next) hlpfile->next->prev = hlpfile;
248 phrases.offsets = NULL;
249 phrases.buffer = NULL;
250 topic.map = NULL;
251 topic.end = NULL;
252 file_buffer = NULL;
254 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
256 HLPFILE_FreeHlpFile(hlpfile);
257 hlpfile = 0;
260 if (phrases.offsets) HeapFree(GetProcessHeap(), 0, phrases.offsets);
261 if (phrases.buffer) HeapFree(GetProcessHeap(), 0, phrases.buffer);
262 if (topic.map) HeapFree(GetProcessHeap(), 0, topic.map);
263 if (file_buffer) HeapFree(GetProcessHeap(), 0, file_buffer);
265 return hlpfile;
268 /***********************************************************************
270 * HLPFILE_DoReadHlpFile
272 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
274 BOOL ret;
275 HFILE hFile;
276 OFSTRUCT ofs;
277 BYTE* buf;
278 DWORD ref = 0x0C;
279 unsigned index, old_index, offset, len, offs;
281 hFile = OpenFile(lpszPath, &ofs, OF_READ | OF_SEARCH);
282 if (hFile == HFILE_ERROR) return FALSE;
284 ret = HLPFILE_ReadFileToBuffer(hFile);
285 _lclose(hFile);
286 if (!ret) return FALSE;
288 if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
290 /* load phrases support */
291 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile))
292 HLPFILE_Uncompress_Phrases40(hlpfile);
294 if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE;
295 if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
297 buf = topic.map[0];
298 old_index = -1;
299 offs = 0;
302 BYTE* end;
304 /* FIXME this depends on the blocksize, can be 2k in some cases */
305 index = (ref - 0x0C) >> 14;
306 offset = (ref - 0x0C) & 0x3fff;
308 WINE_TRACE("ref=%08lx => [%u/%u]\n", ref, index, offset);
310 if (index >= topic.wMapLen) {WINE_WARN("maplen\n"); break;}
311 buf = topic.map[index] + offset;
312 if (buf + 0x15 >= topic.end) {WINE_WARN("extra\n"); break;}
313 end = min(buf + GET_UINT(buf, 0), topic.end);
314 if (index != old_index) {offs = 0; old_index = index;}
316 switch (buf[0x14])
318 case 0x02:
319 if (!HLPFILE_AddPage(hlpfile, buf, end, index * 0x8000L + offs)) return FALSE;
320 break;
322 case 0x20:
323 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
324 offs += len;
325 break;
327 case 0x23:
328 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
329 offs += len;
330 break;
332 default:
333 WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
336 ref = GET_UINT(buf, 0xc);
337 } while (ref != 0xffffffff);
339 return HLPFILE_GetContext(hlpfile);
342 /***********************************************************************
344 * HLPFILE_AddPage
346 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned offset)
348 HLPFILE_PAGE* page;
349 BYTE* title;
350 UINT titlesize;
351 char* ptr;
352 HLPFILE_MACRO*macro;
354 if (buf + 0x31 > end) {WINE_WARN("page1\n"); return FALSE;};
355 title = buf + GET_UINT(buf, 0x10);
356 if (title > end) {WINE_WARN("page2\n"); return FALSE;};
358 titlesize = GET_UINT(buf, 4);
359 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
360 if (!page) return FALSE;
361 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
363 if (hlpfile->hasPhrases)
365 HLPFILE_Uncompress2(title, end, page->lpszTitle, page->lpszTitle + titlesize);
367 else
369 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
371 /* need to decompress */
372 HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize,
373 title, end);
375 else
377 memcpy(page->lpszTitle, title, titlesize);
381 page->lpszTitle[titlesize] = '\0';
383 if (hlpfile->first_page)
385 HLPFILE_PAGE *p;
387 for (p = hlpfile->first_page; p->next; p = p->next);
388 page->prev = p;
389 p->next = page;
391 else
393 hlpfile->first_page = page;
394 page->prev = NULL;
397 page->file = hlpfile;
398 page->next = NULL;
399 page->first_paragraph = NULL;
400 page->first_macro = NULL;
401 page->wNumber = GET_UINT(buf, 0x21);
402 page->offset = offset;
404 page->browse_bwd = GET_UINT(buf, 0x19);
405 page->browse_fwd = GET_UINT(buf, 0x1D);
407 WINE_TRACE("Added page[%d]: title='%s' %08lx << %08x >> %08lx\n",
408 page->wNumber, page->lpszTitle,
409 page->browse_bwd, page->offset, page->browse_fwd);
411 memset(&attributes, 0, sizeof(attributes));
413 /* now load macros */
414 ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
415 while (ptr < page->lpszTitle + titlesize)
417 unsigned len = strlen(ptr);
418 WINE_TRACE("macro: %s\n", ptr);
419 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
420 macro->lpszMacro = (char*)(macro + 1);
421 memcpy((char*)macro->lpszMacro, ptr, len + 1);
422 /* FIXME: shall we really link macro in reverse order ??
423 * may produce strange results when played at page opening
425 macro->next = page->first_macro;
426 page->first_macro = macro;
427 ptr += len + 1;
430 return TRUE;
433 static long fetch_long(BYTE** ptr)
435 long ret;
437 if (*(*ptr) & 1)
439 ret = (*(unsigned long*)(*ptr) - 0x80000000L) / 2;
440 (*ptr) += 4;
442 else
444 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
445 (*ptr) += 2;
448 return ret;
451 static unsigned long fetch_ulong(BYTE** ptr)
453 unsigned long ret;
455 if (*(*ptr) & 1)
457 ret = *(unsigned long*)(*ptr) / 2;
458 (*ptr) += 4;
460 else
462 ret = *(unsigned short*)(*ptr) / 2;
463 (*ptr) += 2;
465 return ret;
468 static short fetch_short(BYTE** ptr)
470 short ret;
472 if (*(*ptr) & 1)
474 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
475 (*ptr) += 2;
477 else
479 ret = (*(unsigned char*)(*ptr) - 0x80) / 2;
480 (*ptr)++;
482 return ret;
485 static unsigned short fetch_ushort(BYTE** ptr)
487 unsigned short ret;
489 if (*(*ptr) & 1)
491 ret = *(unsigned short*)(*ptr) / 2;
492 (*ptr) += 2;
494 else
496 ret = *(unsigned char*)(*ptr) / 2;
497 (*ptr)++;
499 return ret;
502 /******************************************************************
503 * HLPFILE_LoadPictureByAddr
507 static BOOL HLPFILE_LoadPictureByAddr(HLPFILE *hlpfile, char* ref,
508 unsigned long size, unsigned pos)
510 unsigned i, numpict;
512 numpict = *(unsigned short*)(ref + 2);
513 WINE_TRACE("Got picture magic=%04x #=%d\n",
514 *(unsigned short*)ref, numpict);
516 for (i = 0; i < numpict; i++)
518 BYTE *beg, *ptr;
519 BYTE *pict_beg;
520 BYTE type, pack;
521 BITMAPINFO* bi;
522 unsigned long off, sz;
524 WINE_TRACE("Offset[%d] = %lx\n", i, ((unsigned long*)ref)[1 + i]);
525 ptr = beg = ref + ((unsigned long*)ref)[1 + i];
527 type = *ptr++;
528 pack = *ptr++;
530 switch (type)
532 case 5: /* device dependent bmp */ break;
533 case 6: /* device independent bmp */ break;
534 case 8: WINE_FIXME("Unsupported metafile\n"); return FALSE;
535 default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
538 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
539 if (!bi) return FALSE;
541 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
542 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
543 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
544 bi->bmiHeader.biPlanes = fetch_ushort(&ptr);
545 bi->bmiHeader.biBitCount = fetch_ushort(&ptr);
546 bi->bmiHeader.biWidth = fetch_ulong(&ptr);
547 bi->bmiHeader.biHeight = fetch_ulong(&ptr);
548 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr);
549 bi->bmiHeader.biClrImportant = fetch_ulong(&ptr);
550 bi->bmiHeader.biCompression = BI_RGB;
551 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
552 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
553 bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
555 sz = fetch_ulong(&ptr);
556 fetch_ulong(&ptr); /* hotspot size */
558 off = *(unsigned long*)ptr; ptr += 4;
559 /* *(unsigned long*)ptr; hotspot offset */ ptr += 4;
561 /* now read palette info */
562 if (type == 0x06)
564 unsigned nc = bi->bmiHeader.biClrUsed;
565 unsigned i;
567 /* not quite right, especially for bitfields type of compression */
568 if (!nc && bi->bmiHeader.biBitCount <= 8)
569 nc = 1 << bi->bmiHeader.biBitCount;
571 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
572 if (!bi) return FALSE;
573 for (i = 0; i < nc; i++)
575 bi->bmiColors[i].rgbBlue = ptr[0];
576 bi->bmiColors[i].rgbGreen = ptr[1];
577 bi->bmiColors[i].rgbRed = ptr[2];
578 bi->bmiColors[i].rgbReserved = 0;
579 ptr += 4;
583 switch (pack)
585 case 0: /* uncompressed */
586 pict_beg = beg + off;
587 if (sz != bi->bmiHeader.biSizeImage)
588 WINE_WARN("Bogus image sizes: %lu / %lu [sz=(%lu,%lu) bc=%u pl=%u]\n",
589 sz, bi->bmiHeader.biSizeImage,
590 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight,
591 bi->bmiHeader.biBitCount, bi->bmiHeader.biPlanes);
592 break;
593 case 1: /* RunLen */
595 BYTE* dst;
597 dst = pict_beg = HeapAlloc(GetProcessHeap(), 0, bi->bmiHeader.biSizeImage);
598 if (!pict_beg) return FALSE;
599 HLPFILE_UncompressRLE(beg + off, sz, &dst);
600 if (dst - pict_beg != bi->bmiHeader.biSizeImage)
601 WINE_FIXME("buffer XXX-flow (%u/%lu)\n", dst - pict_beg, bi->bmiHeader.biSizeImage);
603 break;
604 case 2: /* LZ77 */
606 unsigned long esz;
607 esz = HLPFILE_UncompressedLZ77_Size(beg + off, beg + off + sz);
608 pict_beg = HeapAlloc(GetProcessHeap(), 0, esz);
609 if (!pict_beg) return FALSE;
610 HLPFILE_UncompressLZ77(beg + off, beg + off + sz, pict_beg);
611 if (esz != bi->bmiHeader.biSizeImage)
612 WINE_WARN("Bogus image sizes: %lu / %lu [sz=(%lu,%lu) bc=%u pl=%u]\n",
613 esz, bi->bmiHeader.biSizeImage,
614 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight,
615 bi->bmiHeader.biBitCount, bi->bmiHeader.biPlanes);
617 break;
618 case 3: /* LZ77 then RLE */
620 BYTE* tmp;
621 unsigned long sz77;
622 BYTE* dst;
624 sz77 = HLPFILE_UncompressedLZ77_Size(beg + off, beg + off + sz);
625 tmp = HeapAlloc(GetProcessHeap(), 0, bi->bmiHeader.biSizeImage);
626 if (!tmp) return FALSE;
627 HLPFILE_UncompressLZ77(beg + off, beg + off + sz, tmp);
628 pict_beg = dst = HeapAlloc(GetProcessHeap(), 0, bi->bmiHeader.biSizeImage);
629 if (!pict_beg) return FALSE;
630 HLPFILE_UncompressRLE(tmp, sz77, &dst);
631 if (dst - pict_beg != bi->bmiHeader.biSizeImage)
632 WINE_WARN("Bogus image sizes: %u / %lu [sz=(%lu,%lu) bc=%u pl=%u]\n",
633 dst - pict_beg, bi->bmiHeader.biSizeImage,
634 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight,
635 bi->bmiHeader.biBitCount, bi->bmiHeader.biPlanes);
636 HeapFree(GetProcessHeap(), 0, tmp);
638 break;
639 default:
640 WINE_FIXME("Unsupported packing %u\n", pack);
641 return FALSE;
644 attributes.hBitmap = CreateDIBitmap(GetDC(0), &bi->bmiHeader, CBM_INIT,
645 pict_beg, bi, DIB_RGB_COLORS);
646 if (!attributes.hBitmap)
647 WINE_ERR("Couldn't create bitmap\n");
648 attributes.bmpPos = pos;
650 HeapFree(GetProcessHeap(), 0, bi);
651 if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
653 /* FIXME: implement support for multiple picture format */
654 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
655 break;
657 return TRUE;
660 /******************************************************************
661 * HLPFILE_LoadPictureByIndex
665 static BOOL HLPFILE_LoadPictureByIndex(HLPFILE *hlpfile, unsigned index, unsigned pos)
667 char tmp[16];
668 BYTE *ref, *end;
669 BOOL ret;
670 WINE_TRACE("Loading picture #%d\n", index);
672 if (index < hlpfile->numBmps && hlpfile->bmps[index] != NULL)
674 attributes.hBitmap = hlpfile->bmps[index];
675 attributes.bmpPos = pos;
676 return TRUE;
679 sprintf(tmp, "|bm%u", index);
681 if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
683 ref += 9;
685 ret = HLPFILE_LoadPictureByAddr(hlpfile, ref, end - ref, pos);
687 /* cache bitmap */
688 if (ret)
690 if (index >= hlpfile->numBmps)
692 hlpfile->numBmps = index + 1;
693 hlpfile->bmps = HeapReAlloc(GetProcessHeap(), 0, hlpfile->bmps,
694 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
696 hlpfile->bmps[index] = attributes.hBitmap;
698 return ret;
701 /***********************************************************************
703 * HLPFILE_AddParagraph
705 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
707 HLPFILE_PAGE *page;
708 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
709 UINT textsize;
710 BYTE *format, *format_end, *text, *text_end;
711 long size;
712 unsigned short bits;
713 unsigned nc, ncol = 1;
715 if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;};
717 for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */;
718 for (paragraphptr = &page->first_paragraph; *paragraphptr;
719 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
721 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
723 size = GET_UINT(buf, 0x4);
724 text = HeapAlloc(GetProcessHeap(), 0, size);
725 if (!text) return FALSE;
726 if (hlpfile->hasPhrases)
728 HLPFILE_Uncompress2(buf + GET_UINT(buf, 0x10), end, text, text + size);
730 else
732 if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
734 /* block is compressed */
735 HLPFILE_Uncompress3(text, text + size, buf + GET_UINT(buf, 0x10), end);
737 else
739 text = buf + GET_UINT(buf, 0x10);
742 text_end = text + size;
744 format = buf + 0x15;
745 format_end = buf + GET_UINT(buf, 0x10);
747 fetch_long(&format);
748 *len = fetch_ushort(&format);
750 if (buf[0x14] == 0x23)
752 char type;
754 ncol = *format++;
756 WINE_TRACE("#cols %u\n", ncol);
757 type = *format++;
758 if (type == 0 || type == 2)
759 format += 2;
760 format += ncol * 4;
763 for (nc = 0; nc < ncol; nc++)
765 WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
766 if (buf[0x14] == 0x23)
767 format += 5;
768 format += 4;
769 bits = *(unsigned short*)format; format += 2;
770 if (bits & 0x0001) fetch_long(&format);
771 if (bits & 0x0002) fetch_short(&format);
772 if (bits & 0x0004) fetch_short(&format);
773 if (bits & 0x0008) fetch_short(&format);
774 if (bits & 0x0010) fetch_short(&format);
775 if (bits & 0x0020) fetch_short(&format);
776 if (bits & 0x0040) fetch_short(&format);
777 if (bits & 0x0100) format += 3;
778 if (bits & 0x0200)
780 int ntab = fetch_short(&format);
781 unsigned short ts;
783 while (ntab-- > 0)
785 ts = fetch_ushort(&format);
786 if (ts & 0x4000) fetch_ushort(&format);
789 /* 0x0400, 0x0800 and 0x1000 don't need space */
790 if ((bits & 0xE080) != 0)
791 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
793 while (text < text_end && format < format_end)
795 WINE_TRACE("Got text: '%s' (%p/%p - %p/%p)\n", text, text, text_end, format, format_end);
796 textsize = strlen(text) + 1;
797 if (textsize > 1 || attributes.hBitmap)
799 paragraph = HeapAlloc(GetProcessHeap(), 0,
800 sizeof(HLPFILE_PARAGRAPH) + textsize);
801 if (!paragraph) return FALSE;
802 *paragraphptr = paragraph;
803 paragraphptr = &paragraph->next;
805 paragraph->next = NULL;
806 paragraph->link = NULL;
808 if (attributes.hBitmap)
810 paragraph->cookie = para_image;
811 paragraph->u.image.hBitmap = attributes.hBitmap;
812 paragraph->u.image.pos = attributes.bmpPos;
813 if (attributes.wVSpace) paragraph->u.image.pos |= 0x8000;
815 else
817 paragraph->cookie = (attributes.bDebug) ? para_debug_text : para_normal_text;
818 paragraph->u.text.wFont = attributes.wFont;
819 paragraph->u.text.wVSpace = attributes.wVSpace;
820 paragraph->u.text.wHSpace = attributes.wHSpace;
821 paragraph->u.text.wIndent = attributes.wIndent;
822 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
823 strcpy(paragraph->u.text.lpszText, text);
826 if (attributes.link.lpszString)
828 /* FIXME: should build a string table for the attributes.link.lpszPath
829 * they are reallocated for each link
831 paragraph->link = HeapAlloc(GetProcessHeap(), 0,
832 sizeof(HLPFILE_LINK) + strlen(attributes.link.lpszString) + 1);
833 if (!paragraph->link) return FALSE;
835 paragraph->link->cookie = attributes.link.cookie;
836 paragraph->link->lpszString = (char*)paragraph->link + sizeof(HLPFILE_LINK);
837 strcpy((char*)paragraph->link->lpszString, attributes.link.lpszString);
838 paragraph->link->lHash = attributes.link.lHash;
839 paragraph->link->bClrChange = attributes.link.bClrChange;
840 paragraph->link->window = attributes.link.window;
842 WINE_TRACE("Link[%d] to %s@%08lx:%d\n",
843 paragraph->link->cookie, paragraph->link->lpszString,
844 paragraph->link->lHash, paragraph->link->window);
846 attributes.hBitmap = 0;
847 attributes.link.lpszString = NULL;
848 attributes.link.bClrChange = FALSE;
849 attributes.link.lHash = 0;
850 attributes.link.window = -1;
851 attributes.wVSpace = 0;
852 attributes.wHSpace = 0;
853 attributes.wIndent = 0;
855 /* else: null text, keep on storing attributes */
856 text += textsize;
858 if (*format == 0xff)
860 format++;
861 break;
864 WINE_TRACE("format=%02x\n", *format);
865 switch (*format)
867 case 0x20:
868 WINE_FIXME("NIY20\n");
869 format += 5;
870 break;
872 case 0x21:
873 WINE_FIXME("NIY21\n");
874 format += 3;
875 break;
877 case 0x80:
878 attributes.wFont = GET_USHORT(format, 1);
879 WINE_TRACE("Changing font to %d\n", attributes.wFont);
880 format += 3;
881 break;
883 case 0x81:
884 attributes.wVSpace++;
885 format += 1;
886 break;
888 case 0x82:
889 attributes.wVSpace += 2 - attributes.wVBackSpace;
890 attributes.wVBackSpace = 0;
891 attributes.wIndent = 0;
892 format += 1;
893 break;
895 case 0x83:
896 attributes.wIndent++;
897 format += 1;
898 break;
900 #if 0
901 case 0x84:
902 format += 3;
903 break;
904 #endif
906 case 0x86:
907 case 0x87:
908 case 0x88:
910 BYTE pos = (*format - 0x86);
911 BYTE type = format[1];
912 long size;
914 format += 2;
915 size = fetch_long(&format);
917 switch (type)
919 case 0x22:
920 fetch_ushort(&format); /* hot spot */
921 /* fall thru */
922 case 0x03:
923 switch (*(short*)format)
925 case 0:
926 HLPFILE_LoadPictureByIndex(hlpfile,
927 *(short*)(format + 2), pos);
928 break;
929 case 1:
930 WINE_FIXME("does it work ??? %x<%lu>#%u\n",
931 *(short*)format, size, *(short*)(format+2));
932 HLPFILE_LoadPictureByAddr(hlpfile, format + 2,
933 size - 4, pos);
934 break;
935 default:
936 WINE_FIXME("??? %u\n", *(short*)format);
937 break;
939 break;
940 case 0x05:
941 WINE_FIXME("Got an embedded element %s\n", format + 6);
942 break;
943 default:
944 WINE_FIXME("Got a type %d picture\n", type);
945 break;
947 format += size;
949 break;
951 case 0x89:
952 attributes.wVBackSpace++;
953 format += 1;
954 break;
956 case 0x8B:
957 case 0x8C:
958 WINE_FIXME("NIY non-break space/hyphen\n");
959 format += 1;
960 break;
962 #if 0
963 case 0xA9:
964 format += 2;
965 break;
966 #endif
968 case 0xC8:
969 case 0xCC:
970 WINE_TRACE("macro => %s\n", format + 3);
971 attributes.link.bClrChange = !(*format & 4);
972 attributes.link.cookie = hlp_link_macro;
973 attributes.link.lpszString = format + 3;
974 format += 3 + GET_USHORT(format, 1);
975 break;
977 case 0xE0:
978 case 0xE1:
979 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
980 format += 5;
981 break;
983 case 0xE2:
984 case 0xE3:
985 attributes.link.bClrChange = TRUE;
986 /* fall thru */
987 case 0xE6:
988 case 0xE7:
989 attributes.link.cookie = (*format & 1) ? hlp_link_link : hlp_link_popup;
990 attributes.link.lpszString = hlpfile->lpszPath;
991 attributes.link.lHash = GET_UINT(format, 1);
992 format += 5;
993 break;
995 case 0xEA:
996 case 0xEB:
997 case 0xEE:
998 case 0xEF:
1000 char* ptr = format + 8;
1001 BYTE type = format[3];
1003 attributes.link.cookie = hlp_link_link;
1004 attributes.link.lHash = GET_UINT(format, 4);
1005 attributes.link.bClrChange = !(*format & 1);
1007 if (type == 1)
1008 attributes.link.window = *ptr++;
1009 if (type == 4 || type == 6)
1011 attributes.link.lpszString = ptr;
1012 ptr += strlen(ptr) + 1;
1014 else
1015 attributes.link.lpszString = hlpfile->lpszPath;
1016 if (type == 6)
1018 int i;
1020 for (i = 0; i < hlpfile->numWindows; i++)
1022 if (!strcmp(ptr, hlpfile->windows[i].name))
1024 attributes.link.window = i;
1025 break;
1028 if (attributes.link.window == -1)
1029 WINE_WARN("Couldn't find window info for %s\n", ptr);
1032 format += 3 + GET_USHORT(format, 1);
1033 break;
1035 default:
1036 WINE_WARN("format %02x\n", *format);
1037 format++;
1041 if (text_end != buf + GET_UINT(buf, 0x10) + size)
1042 HeapFree(GetProcessHeap(), 0, text_end - size);
1043 return TRUE;
1046 /******************************************************************
1047 * HLPFILE_ReadFont
1051 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
1053 BYTE *ref, *end;
1054 unsigned i, len, idx;
1055 unsigned face_num, dscr_num, face_offset, dscr_offset;
1056 BYTE flag, family;
1058 if (!HLPFILE_FindSubFile("|FONT", &ref, &end))
1060 WINE_WARN("no subfile FONT\n");
1061 hlpfile->numFonts = 0;
1062 hlpfile->fonts = NULL;
1063 return FALSE;
1066 ref += 9;
1068 face_num = GET_USHORT(ref, 0);
1069 dscr_num = GET_USHORT(ref, 2);
1070 face_offset = GET_USHORT(ref, 4);
1071 dscr_offset = GET_USHORT(ref, 6);
1073 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1074 face_num, face_offset, dscr_num, dscr_offset);
1076 hlpfile->numFonts = dscr_num;
1077 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
1079 len = (dscr_offset - face_offset) / face_num;
1080 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
1081 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1082 for (i = 0; i < dscr_num; i++)
1084 flag = ref[dscr_offset + i * 11 + 0];
1085 family = ref[dscr_offset + i * 11 + 2];
1087 hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2;
1088 hlpfile->fonts[i].LogFont.lfWidth = 0;
1089 hlpfile->fonts[i].LogFont.lfEscapement = 0;
1090 hlpfile->fonts[i].LogFont.lfOrientation = 0;
1091 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
1092 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
1093 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
1094 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
1095 hlpfile->fonts[i].LogFont.lfCharSet = ANSI_CHARSET;
1096 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1097 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1098 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
1099 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
1101 switch (family)
1103 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break;
1104 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break;
1105 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break;
1106 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break;
1107 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1108 default: WINE_FIXME("Unknown family %u\n", family);
1110 idx = *(unsigned short*)(ref + dscr_offset + i * 11 + 3);
1112 if (idx < face_num)
1114 strncpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1115 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1) + 1] = '\0';
1117 else
1119 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1120 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1122 hlpfile->fonts[i].hFont = (HANDLE)0;
1123 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1124 ref[dscr_offset + i * 11 + 6],
1125 ref[dscr_offset + i * 11 + 7]);
1126 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1127 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08lx\n",
1128 i, flag,
1129 X(0, "bold"),
1130 X(1, "italic"),
1131 X(2, "underline"),
1132 X(3, "strikeOut"),
1133 X(4, "dblUnderline"),
1134 X(5, "smallCaps"),
1135 ref[dscr_offset + i * 11 + 1],
1136 family,
1137 hlpfile->fonts[i].LogFont.lfFaceName, idx,
1138 *(unsigned long*)(ref + dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1140 return TRUE;
1143 /***********************************************************************
1145 * HLPFILE_ReadFileToBuffer
1147 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1149 BYTE header[16], dummy[1];
1150 UINT size;
1152 if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
1154 /* sanity checks */
1155 if (GET_UINT(header, 0) != 0x00035F3F)
1156 {WINE_WARN("wrong header\n"); return FALSE;};
1158 size = GET_UINT(header, 12);
1159 file_buffer = HeapAlloc(GetProcessHeap(), 0, size + 1);
1160 if (!file_buffer) return FALSE;
1162 memcpy(file_buffer, header, 16);
1163 if (_hread(hFile, file_buffer + 16, size - 16) != size - 16)
1164 {WINE_WARN("filesize1\n"); return FALSE;};
1166 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1168 file_buffer[size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1170 return TRUE;
1173 /***********************************************************************
1175 * HLPFILE_FindSubFile
1177 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1179 BYTE *root = file_buffer + GET_UINT(file_buffer, 4);
1180 BYTE *end = file_buffer + GET_UINT(file_buffer, 12);
1181 BYTE *ptr;
1182 BYTE *bth;
1184 unsigned pgsize;
1185 unsigned pglast;
1186 unsigned nentries;
1187 unsigned i, n;
1189 bth = root + 9;
1191 /* FIXME: this should be using the EnumBTree functions from this file */
1192 pgsize = GET_USHORT(bth, 4);
1193 WINE_TRACE("%s => pgsize=%u #pg=%u rootpg=%u #lvl=%u\n",
1194 name, pgsize, GET_USHORT(bth, 30), GET_USHORT(bth, 26), GET_USHORT(bth, 32));
1196 ptr = bth + 38 + GET_USHORT(bth, 26) * pgsize;
1198 for (n = 1; n < GET_USHORT(bth, 32); n++)
1200 nentries = GET_USHORT(ptr, 2);
1201 pglast = GET_USHORT(ptr, 4);
1202 WINE_TRACE("[%u]: #entries=%u next=%u\n", n, nentries, pglast);
1204 ptr += 6;
1205 for (i = 0; i < nentries; i++)
1207 WINE_TRACE("<= %s\n", ptr);
1208 if (strcmp(name, ptr) < 0) break;
1209 ptr += strlen(ptr) + 1;
1210 pglast = GET_USHORT(ptr, 0);
1211 ptr += 2;
1213 ptr = bth + 38 + pglast * pgsize;
1216 nentries = GET_USHORT(ptr, 2);
1217 ptr += 8;
1218 for (i = 0; i < nentries; i++)
1220 char* fname = ptr;
1221 ptr += strlen(fname) + 1;
1222 WINE_TRACE("\\- %s\n", fname);
1223 if (strcmp(fname, name) == 0)
1225 *subbuf = file_buffer + GET_UINT(ptr, 0);
1226 *subend = *subbuf + GET_UINT(*subbuf, 0);
1227 if (file_buffer > *subbuf || *subbuf > *subend || *subend > end)
1229 WINE_WARN("size mismatch\n");
1230 return FALSE;
1232 return TRUE;
1234 ptr += 4;
1237 return FALSE;
1240 /***********************************************************************
1242 * HLPFILE_SystemCommands
1244 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1246 BYTE *buf, *ptr, *end;
1247 HLPFILE_MACRO *macro, **m;
1248 LPSTR p;
1249 unsigned short magic, minor, major, flags;
1251 hlpfile->lpszTitle = NULL;
1253 if (!HLPFILE_FindSubFile("|SYSTEM", &buf, &end)) return FALSE;
1255 magic = GET_USHORT(buf + 9, 0);
1256 minor = GET_USHORT(buf + 9, 2);
1257 major = GET_USHORT(buf + 9, 4);
1258 /* gen date on 4 bytes */
1259 flags = GET_USHORT(buf + 9, 10);
1260 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1261 magic, major, minor, flags);
1262 if (magic != 0x036C || major != 1)
1263 {WINE_WARN("Wrong system header\n"); return FALSE;}
1264 if (minor <= 16) {WINE_WARN("too old file format (NIY)\n"); return FALSE;}
1265 if (flags & 8) {WINE_WARN("Unsupported yet page size\n"); return FALSE;}
1267 hlpfile->version = minor;
1268 hlpfile->flags = flags;
1270 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1272 switch (GET_USHORT(ptr, 0))
1274 case 1:
1275 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1276 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(ptr + 4) + 1);
1277 if (!hlpfile->lpszTitle) return FALSE;
1278 lstrcpy(hlpfile->lpszTitle, ptr + 4);
1279 WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1280 break;
1282 case 2:
1283 if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
1284 hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(ptr + 4) + 1);
1285 if (!hlpfile->lpszCopyright) return FALSE;
1286 lstrcpy(hlpfile->lpszCopyright, ptr + 4);
1287 WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
1288 break;
1290 case 3:
1291 if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
1292 hlpfile->contents_start = GET_UINT(ptr, 4);
1293 WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
1294 break;
1296 case 4:
1297 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(ptr + 4) + 1);
1298 if (!macro) break;
1299 p = (char*)macro + sizeof(HLPFILE_MACRO);
1300 lstrcpy(p, (LPSTR)ptr + 4);
1301 macro->lpszMacro = p;
1302 macro->next = 0;
1303 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1304 *m = macro;
1305 break;
1307 case 6:
1308 if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
1309 hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows,
1310 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1311 if (hlpfile->windows)
1313 unsigned flags = GET_USHORT(ptr, 4);
1314 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
1316 if (flags & 0x0001) strcpy(wi->type, ptr + 6); else wi->type[0] = '\0';
1317 if (flags & 0x0002) strcpy(wi->name, ptr + 16); else wi->name[0] = '\0';
1318 if (flags & 0x0004) strcpy(wi->caption, ptr + 25); else strncpy(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
1319 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
1320 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
1321 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
1322 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
1323 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
1324 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
1325 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
1326 WINE_FIXME("System-Window: flags=%c%c%c%c%c%c%c%c type=%s name=%s caption=%s (%ld,%ld)x(%ld,%ld)\n",
1327 flags & 0x0001 ? 'T' : 't',
1328 flags & 0x0002 ? 'N' : 'n',
1329 flags & 0x0004 ? 'C' : 'c',
1330 flags & 0x0008 ? 'X' : 'x',
1331 flags & 0x0010 ? 'Y' : 'y',
1332 flags & 0x0020 ? 'W' : 'w',
1333 flags & 0x0040 ? 'H' : 'h',
1334 flags & 0x0080 ? 'S' : 's',
1335 wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
1336 wi->size.cx, wi->size.cy);
1338 break;
1339 default:
1340 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1343 return TRUE;
1346 /***********************************************************************
1348 * HLPFILE_UncompressedLZ77_Size
1350 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1352 int i, newsize = 0;
1354 while (ptr < end)
1356 int mask = *ptr++;
1357 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1359 if (mask & 1)
1361 int code = GET_USHORT(ptr, 0);
1362 int len = 3 + (code >> 12);
1363 newsize += len;
1364 ptr += 2;
1366 else newsize++, ptr++;
1370 return newsize;
1373 /***********************************************************************
1375 * HLPFILE_UncompressLZ77
1377 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1379 int i;
1381 while (ptr < end)
1383 int mask = *ptr++;
1384 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1386 if (mask & 1)
1388 int code = GET_USHORT(ptr, 0);
1389 int len = 3 + (code >> 12);
1390 int offset = code & 0xfff;
1391 memcpy(newptr, newptr - offset - 1, len);
1392 newptr += len;
1393 ptr += 2;
1395 else *newptr++ = *ptr++;
1399 return newptr;
1402 /***********************************************************************
1404 * HLPFILE_UncompressLZ77_Phrases
1406 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1408 UINT i, num, dec_size;
1409 BYTE *buf, *end;
1411 if (!HLPFILE_FindSubFile("|Phrases", &buf, &end)) return FALSE;
1413 num = phrases.num = GET_USHORT(buf, 9);
1414 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1416 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1418 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1419 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1420 if (!phrases.offsets || !phrases.buffer) return FALSE;
1422 for (i = 0; i <= num; i++)
1423 phrases.offsets[i] = GET_USHORT(buf, 0x11 + 2 * i) - 2 * num - 2;
1425 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, phrases.buffer);
1427 hlpfile->hasPhrases = TRUE;
1428 return TRUE;
1431 /***********************************************************************
1433 * HLPFILE_Uncompress_Phrases40
1435 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1437 UINT num, dec_size, cpr_size;
1438 BYTE *buf_idx, *end_idx;
1439 BYTE *buf_phs, *end_phs;
1440 short i, n;
1441 long* ptr, mask = 0;
1442 unsigned short bc;
1444 if (!HLPFILE_FindSubFile("|PhrIndex", &buf_idx, &end_idx) ||
1445 !HLPFILE_FindSubFile("|PhrImage", &buf_phs, &end_phs)) return FALSE;
1447 ptr = (long*)(buf_idx + 9 + 28);
1448 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1449 num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1451 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1452 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1453 GET_UINT(buf_idx, 9 + 0),
1454 GET_UINT(buf_idx, 9 + 4),
1455 GET_UINT(buf_idx, 9 + 8),
1456 GET_UINT(buf_idx, 9 + 12),
1457 GET_UINT(buf_idx, 9 + 16),
1458 GET_UINT(buf_idx, 9 + 20),
1459 GET_USHORT(buf_idx, 9 + 24),
1460 GET_USHORT(buf_idx, 9 + 26));
1462 dec_size = GET_UINT(buf_idx, 9 + 12);
1463 cpr_size = GET_UINT(buf_idx, 9 + 16);
1465 if (dec_size != cpr_size &&
1466 dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1468 WINE_WARN("size mismatch %u %u\n",
1469 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1470 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1473 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1474 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1475 if (!phrases.offsets || !phrases.buffer) return FALSE;
1477 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1479 phrases.offsets[0] = 0;
1480 for (i = 0; i < num; i++)
1482 for (n = 1; getbit(); n += 1 << bc);
1483 if (getbit()) n++;
1484 if (bc > 1 && getbit()) n += 2;
1485 if (bc > 2 && getbit()) n += 4;
1486 if (bc > 3 && getbit()) n += 8;
1487 if (bc > 4 && getbit()) n += 16;
1488 phrases.offsets[i + 1] = phrases.offsets[i] + n;
1490 #undef getbit
1492 if (dec_size == cpr_size)
1493 memcpy(phrases.buffer, buf_phs + 9, dec_size);
1494 else
1495 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, phrases.buffer);
1497 hlpfile->hasPhrases = FALSE;
1498 return TRUE;
1501 /***********************************************************************
1503 * HLPFILE_Uncompress_Topic
1505 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
1507 BYTE *buf, *ptr, *end, *newptr;
1508 int i, newsize = 0;
1510 if (!HLPFILE_FindSubFile("|TOPIC", &buf, &end))
1511 {WINE_WARN("topic0\n"); return FALSE;}
1513 switch (hlpfile->flags & (8|4))
1515 case 8:
1516 WINE_FIXME("Unsupported format\n");
1517 return FALSE;
1518 case 4:
1519 buf += 9;
1520 topic.wMapLen = (end - buf - 1) / 0x1000 + 1;
1522 for (i = 0; i < topic.wMapLen; i++)
1524 ptr = buf + i * 0x1000;
1526 /* I don't know why, it's necessary for printman.hlp */
1527 if (ptr + 0x44 > end) ptr = end - 0x44;
1529 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + 0x1000));
1532 topic.map = HeapAlloc(GetProcessHeap(), 0,
1533 topic.wMapLen * sizeof(topic.map[0]) + newsize);
1534 if (!topic.map) return FALSE;
1535 newptr = (char*)(topic.map + topic.wMapLen);
1536 topic.end = newptr + newsize;
1538 for (i = 0; i < topic.wMapLen; i++)
1540 ptr = buf + i * 0x1000;
1541 if (ptr + 0x44 > end) ptr = end - 0x44;
1543 topic.map[i] = newptr;
1544 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + 0x1000), newptr);
1546 break;
1547 case 0:
1548 /* basically, we need to copy the 0x1000 byte pages (removing the first 0x0C) in
1549 * one single are in memory
1551 #define DST_LEN (0x1000 - 0x0C)
1552 buf += 9;
1553 newsize = end - buf;
1554 /* number of destination pages */
1555 topic.wMapLen = (newsize - 1) / DST_LEN + 1;
1556 topic.map = HeapAlloc(GetProcessHeap(), 0,
1557 topic.wMapLen * (sizeof(topic.map[0]) + DST_LEN));
1558 if (!topic.map) return FALSE;
1559 newptr = (char*)(topic.map + topic.wMapLen);
1560 topic.end = newptr + newsize;
1562 for (i = 0; i < topic.wMapLen; i++)
1564 topic.map[i] = newptr + i * DST_LEN;
1565 memcpy(topic.map[i], buf + i * 0x1000 + 0x0C, DST_LEN);
1567 #undef DST_LEN
1568 break;
1570 return TRUE;
1573 /***********************************************************************
1575 * HLPFILE_Uncompress2
1578 static void HLPFILE_Uncompress2(const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
1580 BYTE *phptr, *phend;
1581 UINT code;
1582 UINT index;
1584 while (ptr < end && newptr < newend)
1586 if (!*ptr || *ptr >= 0x10)
1587 *newptr++ = *ptr++;
1588 else
1590 code = 0x100 * ptr[0] + ptr[1];
1591 index = (code - 0x100) / 2;
1593 phptr = phrases.buffer + phrases.offsets[index];
1594 phend = phrases.buffer + phrases.offsets[index + 1];
1596 if (newptr + (phend - phptr) > newend)
1598 WINE_FIXME("buffer overflow %p > %p for %d bytes\n",
1599 newptr, newend, phend - phptr);
1600 return;
1602 memcpy(newptr, phptr, phend - phptr);
1603 newptr += phend - phptr;
1604 if (code & 1) *newptr++ = ' ';
1606 ptr += 2;
1609 if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
1612 /******************************************************************
1613 * HLPFILE_Uncompress3
1617 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1618 const BYTE* src, const BYTE* src_end)
1620 int idx, len;
1622 for (; src < src_end; src++)
1624 if ((*src & 1) == 0)
1626 idx = *src / 2;
1627 if (idx > phrases.num)
1629 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1630 len = 0;
1632 else
1634 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1635 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1638 else if ((*src & 0x03) == 0x01)
1640 idx = (*src + 1) * 64;
1641 idx += *++src;
1642 if (idx > phrases.num)
1644 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1645 len = 0;
1647 else
1649 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1650 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1653 else if ((*src & 0x07) == 0x03)
1655 len = (*src / 8) + 1;
1656 memcpy(dst, src + 1, len);
1657 src += len;
1659 else
1661 len = (*src / 16) + 1;
1662 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1664 dst += len;
1667 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1668 return TRUE;
1671 /******************************************************************
1672 * HLPFILE_UncompressRLE
1676 static void HLPFILE_UncompressRLE(const BYTE* src, unsigned sz, BYTE** dst)
1678 unsigned i;
1679 BYTE ch;
1681 for (i = 0; i < sz; i++)
1683 ch = src[i];
1684 if (ch & 0x80)
1686 ch &= 0x7F;
1687 memcpy(*dst, src + i + 1, ch);
1688 i += ch;
1690 else
1692 memset(*dst, (char)src[i + 1], ch);
1693 i++;
1695 *dst += ch;
1699 /******************************************************************
1700 * HLPFILE_EnumBTreeLeaves
1704 static void HLPFILE_EnumBTreeLeaves(const BYTE* buf, const BYTE* end, unsigned (*fn)(const BYTE*, void*), void* user)
1706 unsigned psize, pnext;
1707 unsigned num, nlvl;
1708 const BYTE* ptr;
1710 num = GET_UINT(buf, 9 + 34);
1711 psize = GET_USHORT(buf, 9 + 4);
1712 nlvl = GET_USHORT(buf, 9 + 32);
1713 pnext = GET_USHORT(buf, 9 + 26);
1715 WINE_TRACE("BTree: #entries=%u pagSize=%u #levels=%u #pages=%u root=%u struct%16s\n",
1716 num, psize, nlvl, GET_USHORT(buf, 9 + 30), pnext, buf + 9 + 6);
1717 if (!num) return;
1719 while (--nlvl > 0)
1721 ptr = (buf + 9 + 38) + pnext * psize;
1722 WINE_TRACE("BTree: (index[%u]) unused=%u #entries=%u <%u\n",
1723 pnext, GET_USHORT(ptr, 0), GET_USHORT(ptr, 2), GET_USHORT(ptr, 4));
1724 pnext = GET_USHORT(ptr, 4);
1726 while (pnext != 0xFFFF)
1728 const BYTE* node_page;
1729 unsigned short limit;
1731 node_page = ptr = (buf + 9 + 38) + pnext * psize;
1732 limit = GET_USHORT(ptr, 2);
1733 WINE_TRACE("BTree: (leaf [%u]) unused=%u #entries=%u <%u >%u\n",
1734 pnext, GET_USHORT(ptr, 0), limit, GET_USHORT(ptr, 4), GET_USHORT(ptr, 6));
1735 ptr += 8;
1736 while (limit--)
1737 ptr += (fn)(ptr, user);
1738 pnext = GET_USHORT(node_page, 6);
1742 struct myfncb {
1743 HLPFILE* hlpfile;
1744 int i;
1747 static unsigned myfn(const BYTE* ptr, void* user)
1749 struct myfncb* m = user;
1751 m->hlpfile->Context[m->i].lHash = GET_UINT(ptr, 0);
1752 m->hlpfile->Context[m->i].offset = GET_UINT(ptr, 4);
1753 m->i++;
1754 return 8;
1757 /***********************************************************************
1759 * HLPFILE_GetContext
1761 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
1763 BYTE *cbuf, *cend;
1764 struct myfncb m;
1765 unsigned clen;
1767 if (!HLPFILE_FindSubFile("|CONTEXT", &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
1769 clen = GET_UINT(cbuf, 0x2b);
1770 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen * sizeof(HLPFILE_CONTEXT));
1771 if (!hlpfile->Context) return FALSE;
1772 hlpfile->wContextLen = clen;
1774 m.hlpfile = hlpfile;
1775 m.i = 0;
1776 HLPFILE_EnumBTreeLeaves(cbuf, cend, myfn, &m);
1778 return TRUE;
1781 /***********************************************************************
1783 * HLPFILE_DeleteParagraph
1785 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
1787 HLPFILE_PARAGRAPH* next;
1789 while (paragraph)
1791 next = paragraph->next;
1792 if (paragraph->link) HeapFree(GetProcessHeap(), 0, paragraph->link);
1794 HeapFree(GetProcessHeap(), 0, paragraph);
1795 paragraph = next;
1799 /***********************************************************************
1801 * DeleteMacro
1803 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
1805 HLPFILE_MACRO* next;
1807 while (macro)
1809 next = macro->next;
1810 HeapFree(GetProcessHeap(), 0, macro);
1811 macro = next;
1815 /***********************************************************************
1817 * DeletePage
1819 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
1821 HLPFILE_PAGE* next;
1823 while (page)
1825 next = page->next;
1826 HLPFILE_DeleteParagraph(page->first_paragraph);
1827 HLPFILE_DeleteMacro(page->first_macro);
1828 HeapFree(GetProcessHeap(), 0, page);
1829 page = next;
1833 /***********************************************************************
1835 * HLPFILE_FreeHlpFile
1837 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
1839 unsigned i;
1841 if (!hlpfile || --hlpfile->wRefCount > 0) return;
1843 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
1844 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
1845 else first_hlpfile = hlpfile->next;
1847 if (hlpfile->numFonts)
1849 for (i = 0; i < hlpfile->numFonts; i++)
1851 DeleteObject(hlpfile->fonts[i].hFont);
1853 HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
1856 if (hlpfile->numBmps)
1858 for (i = 0; i < hlpfile->numBmps; i++)
1860 DeleteObject(hlpfile->bmps[i]);
1862 HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
1865 HLPFILE_DeletePage(hlpfile->first_page);
1866 HLPFILE_DeleteMacro(hlpfile->first_macro);
1868 if (hlpfile->numWindows) HeapFree(GetProcessHeap(), 0, hlpfile->windows);
1869 if (hlpfile->Context) HeapFree(GetProcessHeap(), 0, hlpfile->Context);
1870 if (hlpfile->lpszTitle) HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
1871 if (hlpfile->lpszCopyright) HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
1872 HeapFree(GetProcessHeap(), 0, hlpfile);