1 /* 2 * Help Viewer 3 * 4 * Copyright 1996 Ulrich Schmid 5 * 2002, 2008 Eric Pouech 6 * 2007 Kirill K. Smirnov 7 * 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. 12 * 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. 17 * 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 21 */ 22 23 #include "winhelp.h" 24 25 static inline unsigned short GET_USHORT(const BYTE* buffer, unsigned i) 26 { 27 return (BYTE)buffer[i] + 0x100 * (BYTE)buffer[i + 1]; 28 } 29 30 static inline short GET_SHORT(const BYTE* buffer, unsigned i) 31 { 32 return (BYTE)buffer[i] + 0x100 * (signed char)buffer[i+1]; 33 } 34 35 static inline unsigned GET_UINT(const BYTE* buffer, unsigned i) 36 { 37 return GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i + 2); 38 } 39 40 static HLPFILE *first_hlpfile = 0; 41 42 43 /************************************************************************** 44 * HLPFILE_BPTreeSearch 45 * 46 * Searches for an element in B+ tree 47 * 48 * PARAMS 49 * buf [I] pointer to the embedded file structured as a B+ tree 50 * key [I] pointer to data to find 51 * comp [I] compare function 52 * 53 * RETURNS 54 * Pointer to block identified by key, or NULL if failure. 55 * 56 */ 57 static void* HLPFILE_BPTreeSearch(BYTE* buf, const void* key, 58 HLPFILE_BPTreeCompare comp) 59 { 60 unsigned magic; 61 unsigned page_size; 62 unsigned cur_page; 63 unsigned level; 64 BYTE *pages, *ptr, *newptr; 65 int i, entries; 66 int ret; 67 68 magic = GET_USHORT(buf, 9); 69 if (magic != 0x293B) 70 { 71 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic); 72 return NULL; 73 } 74 page_size = GET_USHORT(buf, 9+4); 75 cur_page = GET_USHORT(buf, 9+26); 76 level = GET_USHORT(buf, 9+32); 77 pages = buf + 9 + 38; 78 while (--level > 0) 79 { 80 ptr = pages + cur_page*page_size; 81 entries = GET_SHORT(ptr, 2); 82 ptr += 6; 83 for (i = 0; i < entries; i++) 84 { 85 if (comp(ptr, key, 0, (void **)&newptr) > 0) break; 86 ptr = newptr; 87 } 88 cur_page = GET_USHORT(ptr-2, 0); 89 } 90 ptr = pages + cur_page*page_size; 91 entries = GET_SHORT(ptr, 2); 92 ptr += 8; 93 for (i = 0; i < entries; i++) 94 { 95 ret = comp(ptr, key, 1, (void **)&newptr); 96 if (ret == 0) return ptr; 97 if (ret > 0) return NULL; 98 ptr = newptr; 99 } 100 return NULL; 101 } 102 103 /************************************************************************** 104 * HLPFILE_BPTreeEnum 105 * 106 * Enumerates elements in B+ tree. 107 * 108 * PARAMS 109 * buf [I] pointer to the embedded file structured as a B+ tree 110 * cb [I] compare function 111 * cookie [IO] cookie for cb function 112 */ 113 void HLPFILE_BPTreeEnum(BYTE* buf, HLPFILE_BPTreeCallback cb, void* cookie) 114 { 115 unsigned magic; 116 unsigned page_size; 117 unsigned cur_page; 118 unsigned level; 119 BYTE *pages, *ptr, *newptr; 120 int i, entries; 121 122 magic = GET_USHORT(buf, 9); 123 if (magic != 0x293B) 124 { 125 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic); 126 return; 127 } 128 page_size = GET_USHORT(buf, 9+4); 129 cur_page = GET_USHORT(buf, 9+26); 130 level = GET_USHORT(buf, 9+32); 131 pages = buf + 9 + 38; 132 while (--level > 0) 133 { 134 ptr = pages + cur_page*page_size; 135 cur_page = GET_USHORT(ptr, 4); 136 } 137 while (cur_page != 0xFFFF) 138 { 139 ptr = pages + cur_page*page_size; 140 entries = GET_SHORT(ptr, 2); 141 ptr += 8; 142 for (i = 0; i < entries; i++) 143 { 144 cb(ptr, (void **)&newptr, cookie); 145 ptr = newptr; 146 } 147 cur_page = GET_USHORT(pages+cur_page*page_size, 6); 148 } 149 } 150 151 152 /*********************************************************************** 153 * 154 * HLPFILE_UncompressedLZ77_Size 155 */ 156 static INT HLPFILE_UncompressedLZ77_Size(const BYTE *ptr, const BYTE *end) 157 { 158 int i, newsize = 0; 159 160 while (ptr < end) 161 { 162 int mask = *ptr++; 163 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1) 164 { 165 if (mask & 1) 166 { 167 int code = GET_USHORT(ptr, 0); 168 int len = 3 + (code >> 12); 169 newsize += len; 170 ptr += 2; 171 } 172 else newsize++, ptr++; 173 } 174 } 175 176 return newsize; 177 } 178 179 /*********************************************************************** 180 * 181 * HLPFILE_UncompressLZ77 182 */ 183 static BYTE *HLPFILE_UncompressLZ77(const BYTE *ptr, const BYTE *end, BYTE *newptr) 184 { 185 int i; 186 187 while (ptr < end) 188 { 189 int mask = *ptr++; 190 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1) 191 { 192 if (mask & 1) 193 { 194 int code = GET_USHORT(ptr, 0); 195 int len = 3 + (code >> 12); 196 int offset = code & 0xfff; 197 /* 198 * We must copy byte-by-byte here. We cannot use memcpy nor 199 * memmove here. Just example: 200 * a[]={1,2,3,4,5,6,7,8,9,10} 201 * newptr=a+2; 202 * offset=1; 203 * We expect: 204 * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12} 205 */ 206 for (; len>0; len--, newptr++) *newptr = *(newptr-offset-1); 207 ptr += 2; 208 } 209 else *newptr++ = *ptr++; 210 } 211 } 212 213 return newptr; 214 } 215 216 /*********************************************************************** 217 * 218 * HLPFILE_Uncompress2 219 */ 220 221 static void HLPFILE_Uncompress2(HLPFILE* hlpfile, const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend) 222 { 223 BYTE *phptr, *phend; 224 UINT code; 225 UINT index; 226 227 while (ptr < end && newptr < newend) 228 { 229 if (!*ptr || *ptr >= 0x10) 230 *newptr++ = *ptr++; 231 else 232 { 233 code = 0x100 * ptr[0] + ptr[1]; 234 index = (code - 0x100) / 2; 235 236 phptr = (BYTE*)hlpfile->phrases_buffer + hlpfile->phrases_offsets[index]; 237 phend = (BYTE*)hlpfile->phrases_buffer + hlpfile->phrases_offsets[index + 1]; 238 239 if (newptr + (phend - phptr) > newend) 240 { 241 WINE_FIXME("buffer overflow %p > %p for %lu bytes\n", 242 newptr, newend, (SIZE_T)(phend - phptr)); 243 return; 244 } 245 memcpy(newptr, phptr, phend - phptr); 246 newptr += phend - phptr; 247 if (code & 1) *newptr++ = ' '; 248 249 ptr += 2; 250 } 251 } 252 if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend); 253 } 254 255 /****************************************************************** 256 * HLPFILE_Uncompress3 257 * 258 * 259 */ 260 static BOOL HLPFILE_Uncompress3(HLPFILE* hlpfile, char* dst, const char* dst_end, 261 const BYTE* src, const BYTE* src_end) 262 { 263 unsigned int idx, len; 264 265 for (; src < src_end; src++) 266 { 267 if ((*src & 1) == 0) 268 { 269 idx = *src / 2; 270 if (idx > hlpfile->num_phrases) 271 { 272 WINE_ERR("index in phrases %d/%d\n", idx, hlpfile->num_phrases); 273 len = 0; 274 } 275 else 276 { 277 len = hlpfile->phrases_offsets[idx + 1] - hlpfile->phrases_offsets[idx]; 278 if (dst + len <= dst_end) 279 memcpy(dst, &hlpfile->phrases_buffer[hlpfile->phrases_offsets[idx]], len); 280 } 281 } 282 else if ((*src & 0x03) == 0x01) 283 { 284 idx = (*src + 1) * 64; 285 idx += *++src; 286 if (idx > hlpfile->num_phrases) 287 { 288 WINE_ERR("index in phrases %d/%d\n", idx, hlpfile->num_phrases); 289 len = 0; 290 } 291 else 292 { 293 len = hlpfile->phrases_offsets[idx + 1] - hlpfile->phrases_offsets[idx]; 294 if (dst + len <= dst_end) 295 memcpy(dst, &hlpfile->phrases_buffer[hlpfile->phrases_offsets[idx]], len); 296 } 297 } 298 else if ((*src & 0x07) == 0x03) 299 { 300 len = (*src / 8) + 1; 301 if (dst + len <= dst_end) 302 memcpy(dst, src + 1, len); 303 src += len; 304 } 305 else 306 { 307 len = (*src / 16) + 1; 308 if (dst + len <= dst_end) 309 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len); 310 } 311 dst += len; 312 } 313 314 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end); 315 return TRUE; 316 } 317 318 /****************************************************************** 319 * HLPFILE_UncompressRLE 320 * 321 * 322 */ 323 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE* dst, unsigned dstsz) 324 { 325 BYTE ch; 326 BYTE* sdst = dst + dstsz; 327 328 while (src < end) 329 { 330 ch = *src++; 331 if (ch & 0x80) 332 { 333 ch &= 0x7F; 334 if (dst + ch <= sdst) 335 memcpy(dst, src, ch); 336 src += ch; 337 } 338 else 339 { 340 if (dst + ch <= sdst) 341 memset(dst, (char)*src, ch); 342 src++; 343 } 344 dst += ch; 345 } 346 if (dst != sdst) 347 WINE_WARN("Buffer X-flow: d(%lu) instead of d(%u)\n", 348 (SIZE_T)(dst - (sdst - dstsz)), dstsz); 349 } 350 351 352 /****************************************************************** 353 * HLPFILE_PageByOffset 354 * 355 * 356 */ 357 HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset, ULONG* relative) 358 { 359 HLPFILE_PAGE* page; 360 HLPFILE_PAGE* found; 361 362 if (!hlpfile) return 0; 363 364 WINE_TRACE("<%s>[%x]\n", debugstr_a(hlpfile->lpszPath), offset); 365 366 if (offset == 0xFFFFFFFF) return NULL; 367 page = NULL; 368 369 for (found = NULL, page = hlpfile->first_page; page; page = page->next) 370 { 371 if (page->offset <= offset && (!found || found->offset < page->offset)) 372 { 373 *relative = offset - page->offset; 374 found = page; 375 } 376 } 377 if (!found) 378 WINE_ERR("Page of offset %u not found in file %s\n", 379 offset, debugstr_a(hlpfile->lpszPath)); 380 return found; 381 } 382 383 /*********************************************************************** 384 * 385 * HLPFILE_Contents 386 */ 387 static HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile, ULONG* relative) 388 { 389 HLPFILE_PAGE* page = NULL; 390 391 if (!hlpfile) return NULL; 392 393 page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start, relative); 394 if (!page) 395 { 396 page = hlpfile->first_page; 397 *relative = 0; 398 } 399 return page; 400 } 401 402 /************************************************************************** 403 * comp_PageByHash 404 * 405 * HLPFILE_BPTreeCompare function for '|CONTEXT' B+ tree file 406 * 407 */ 408 static int comp_PageByHash(void *p, const void *key, 409 int leaf, void** next) 410 { 411 LONG lKey = (LONG_PTR)key; 412 LONG lTest = (INT)GET_UINT(p, 0); 413 414 *next = (char *)p+(leaf?8:6); 415 WINE_TRACE("Comparing '%d' with '%d'\n", lKey, lTest); 416 if (lTest < lKey) return -1; 417 if (lTest > lKey) return 1; 418 return 0; 419 } 420 421 /*********************************************************************** 422 * 423 * HLPFILE_PageByHash 424 */ 425 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash, ULONG* relative) 426 { 427 BYTE *ptr; 428 429 if (!hlpfile) return NULL; 430 if (!lHash) return HLPFILE_Contents(hlpfile, relative); 431 432 WINE_TRACE("<%s>[%x]\n", debugstr_a(hlpfile->lpszPath), lHash); 433 434 /* For win 3.0 files hash values are really page numbers */ 435 if (hlpfile->version <= 16) 436 { 437 if (lHash >= hlpfile->wTOMapLen) return NULL; 438 return HLPFILE_PageByOffset(hlpfile, hlpfile->TOMap[lHash], relative); 439 } 440 441 ptr = HLPFILE_BPTreeSearch(hlpfile->Context, LongToPtr(lHash), comp_PageByHash); 442 if (!ptr) 443 { 444 WINE_ERR("Page of hash %x not found in file %s\n", lHash, debugstr_a(hlpfile->lpszPath)); 445 return NULL; 446 } 447 448 return HLPFILE_PageByOffset(hlpfile, GET_UINT(ptr, 4), relative); 449 } 450 451 /*********************************************************************** 452 * 453 * HLPFILE_PageByMap 454 */ 455 HLPFILE_PAGE *HLPFILE_PageByMap(HLPFILE* hlpfile, LONG lMap, ULONG* relative) 456 { 457 unsigned int i; 458 459 if (!hlpfile) return 0; 460 461 WINE_TRACE("<%s>[%x]\n", debugstr_a(hlpfile->lpszPath), lMap); 462 463 for (i = 0; i < hlpfile->wMapLen; i++) 464 { 465 if (hlpfile->Map[i].lMap == lMap) 466 return HLPFILE_PageByOffset(hlpfile, hlpfile->Map[i].offset, relative); 467 } 468 469 WINE_ERR("Page of Map %x not found in file %s\n", lMap, debugstr_a(hlpfile->lpszPath)); 470 return NULL; 471 } 472 473 /************************************************************************** 474 * comp_FindSubFile 475 * 476 * HLPFILE_BPTreeCompare function for HLPFILE directory. 477 * 478 */ 479 static int comp_FindSubFile(void *p, const void *key, 480 int leaf, void** next) 481 { 482 *next = (char *)p+strlen(p)+(leaf?5:3); 483 WINE_TRACE("Comparing %s with %s\n", debugstr_a((char *)p), debugstr_a((const char *)key)); 484 return strcmp(p, key); 485 } 486 487 /*********************************************************************** 488 * 489 * HLPFILE_FindSubFile 490 */ 491 static BOOL HLPFILE_FindSubFile(HLPFILE* hlpfile, LPCSTR name, BYTE **subbuf, BYTE **subend) 492 { 493 BYTE *ptr; 494 495 WINE_TRACE("looking for file %s\n", debugstr_a(name)); 496 ptr = HLPFILE_BPTreeSearch(hlpfile->file_buffer + GET_UINT(hlpfile->file_buffer, 4), 497 name, comp_FindSubFile); 498 if (!ptr) 499 { /* Subfiles with bitmap images are usually prefixed with '|', but sometimes not. 500 Unfortunately, there is no consensus among different pieces of unofficial 501 documentation. So remove leading '|' and try again. */ 502 CHAR c = *name++; 503 if (c == '|') 504 { 505 WINE_TRACE("not found. try %s\n", debugstr_a(name)); 506 ptr = HLPFILE_BPTreeSearch(hlpfile->file_buffer + GET_UINT(hlpfile->file_buffer, 4), 507 name, comp_FindSubFile); 508 } 509 } 510 if (!ptr) return FALSE; 511 *subbuf = hlpfile->file_buffer + GET_UINT(ptr, strlen(name)+1); 512 if (*subbuf >= hlpfile->file_buffer + hlpfile->file_buffer_size) 513 { 514 WINE_ERR("internal file %s does not fit\n", debugstr_a(name)); 515 return FALSE; 516 } 517 *subend = *subbuf + GET_UINT(*subbuf, 0); 518 if (*subend > hlpfile->file_buffer + hlpfile->file_buffer_size) 519 { 520 WINE_ERR("internal file %s does not fit\n", debugstr_a(name)); 521 return FALSE; 522 } 523 if (GET_UINT(*subbuf, 0) < GET_UINT(*subbuf, 4) + 9) 524 { 525 WINE_ERR("invalid size provided for internal file %s\n", debugstr_a(name)); 526 return FALSE; 527 } 528 return TRUE; 529 } 530 531 /*********************************************************************** 532 * 533 * HLPFILE_Hash 534 */ 535 LONG HLPFILE_Hash(LPCSTR lpszContext) 536 { 537 LONG lHash = 0; 538 CHAR c; 539 540 while ((c = *lpszContext++)) 541 { 542 CHAR x = 0; 543 if (c >= 'A' && c <= 'Z') x = c - 'A' + 17; 544 if (c >= 'a' && c <= 'z') x = c - 'a' + 17; 545 if (c >= '1' && c <= '9') x = c - '0'; 546 if (c == '0') x = 10; 547 if (c == '.') x = 12; 548 if (c == '_') x = 13; 549 if (x) lHash = lHash * 43 + x; 550 } 551 return lHash; 552 } 553 554 static LONG fetch_long(const BYTE** ptr) 555 { 556 LONG ret; 557 558 if (*(*ptr) & 1) 559 { 560 ret = (*(const ULONG*)(*ptr) - 0x80000000) / 2; 561 (*ptr) += 4; 562 } 563 else 564 { 565 ret = (*(const USHORT*)(*ptr) - 0x8000) / 2; 566 (*ptr) += 2; 567 } 568 569 return ret; 570 } 571 572 static ULONG fetch_ulong(const BYTE** ptr) 573 { 574 ULONG ret; 575 576 if (*(*ptr) & 1) 577 { 578 ret = *(const ULONG*)(*ptr) / 2; 579 (*ptr) += 4; 580 } 581 else 582 { 583 ret = *(const USHORT*)(*ptr) / 2; 584 (*ptr) += 2; 585 } 586 return ret; 587 } 588 589 static short fetch_short(const BYTE** ptr) 590 { 591 short ret; 592 593 if (*(*ptr) & 1) 594 { 595 ret = (*(const unsigned short*)(*ptr) - 0x8000) / 2; 596 (*ptr) += 2; 597 } 598 else 599 { 600 ret = (*(const unsigned char*)(*ptr) - 0x80) / 2; 601 (*ptr)++; 602 } 603 return ret; 604 } 605 606 static unsigned short fetch_ushort(const BYTE** ptr) 607 { 608 unsigned short ret; 609 610 if (*(*ptr) & 1) 611 { 612 ret = *(const unsigned short*)(*ptr) / 2; 613 (*ptr) += 2; 614 } 615 else 616 { 617 ret = *(const unsigned char*)(*ptr) / 2; 618 (*ptr)++; 619 } 620 return ret; 621 } 622 623 /****************************************************************** 624 * HLPFILE_DecompressGfx 625 * 626 * Decompress the data part of a bitmap or a metafile 627 */ 628 static const BYTE* HLPFILE_DecompressGfx(const BYTE* src, unsigned csz, unsigned sz, BYTE packing, 629 BYTE** alloc) 630 { 631 const BYTE* dst; 632 BYTE* tmp; 633 unsigned sz77; 634 635 WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing, csz, sz); 636 637 switch (packing) 638 { 639 case 0: /* uncompressed */ 640 if (sz != csz) 641 WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz, csz); 642 dst = src; 643 *alloc = NULL; 644 break; 645 case 1: /* RunLen */ 646 dst = *alloc = HeapAlloc(GetProcessHeap(), 0, sz); 647 if (!dst) return NULL; 648 HLPFILE_UncompressRLE(src, src + csz, *alloc, sz); 649 break; 650 case 2: /* LZ77 */ 651 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz); 652 dst = *alloc = HeapAlloc(GetProcessHeap(), 0, sz77); 653 if (!dst) return NULL; 654 HLPFILE_UncompressLZ77(src, src + csz, *alloc); 655 if (sz77 != sz) 656 WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77, sz); 657 break; 658 case 3: /* LZ77 then RLE */ 659 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz); 660 tmp = HeapAlloc(GetProcessHeap(), 0, sz77); 661 if (!tmp) return FALSE; 662 HLPFILE_UncompressLZ77(src, src + csz, tmp); 663 dst = *alloc = HeapAlloc(GetProcessHeap(), 0, sz); 664 if (!dst) 665 { 666 HeapFree(GetProcessHeap(), 0, tmp); 667 return FALSE; 668 } 669 HLPFILE_UncompressRLE(tmp, tmp + sz77, *alloc, sz); 670 HeapFree(GetProcessHeap(), 0, tmp); 671 break; 672 default: 673 WINE_FIXME("Unsupported packing %u\n", packing); 674 return NULL; 675 } 676 return dst; 677 } 678 679 static BOOL HLPFILE_RtfAddRawString(struct RtfData* rd, const char* str, size_t sz) 680 { 681 if (rd->ptr + sz >= rd->data + rd->allocated) 682 { 683 char* new = HeapReAlloc(GetProcessHeap(), 0, rd->data, rd->allocated *= 2); 684 if (!new) return FALSE; 685 rd->ptr = new + (rd->ptr - rd->data); 686 rd->data = new; 687 } 688 memcpy(rd->ptr, str, sz); 689 rd->ptr += sz; 690 691 return TRUE; 692 } 693 694 static BOOL HLPFILE_RtfAddControl(struct RtfData* rd, const char* str) 695 { 696 WINE_TRACE("%s\n", debugstr_a(str)); 697 if (*str == '\\' || *str == '{') rd->in_text = FALSE; 698 else if (*str == '}') rd->in_text = TRUE; 699 return HLPFILE_RtfAddRawString(rd, str, strlen(str)); 700 } 701 702 static BOOL HLPFILE_RtfAddText(struct RtfData* rd, const char* str) 703 { 704 const char* p; 705 const char* last; 706 const char* replace; 707 unsigned rlen; 708 709 if (!rd->in_text) 710 { 711 if (!HLPFILE_RtfAddRawString(rd, " ", 1)) return FALSE; 712 rd->in_text = TRUE; 713 } 714 for (last = p = str; *p; p++) 715 { 716 if (*p & 0x80) /* escape non-ASCII chars */ 717 { 718 static char xx[8]; 719 rlen = sprintf(xx, "\\'%x", *(const BYTE*)p); 720 replace = xx; 721 } 722 else switch (*p) 723 { 724 case '{': rlen = 2; replace = "\\{"; break; 725 case '}': rlen = 2; replace = "\\}"; break; 726 case '\\': rlen = 2; replace = "\\\\"; break; 727 default: continue; 728 } 729 if ((p != last && !HLPFILE_RtfAddRawString(rd, last, p - last)) || 730 !HLPFILE_RtfAddRawString(rd, replace, rlen)) return FALSE; 731 last = p + 1; 732 } 733 return HLPFILE_RtfAddRawString(rd, last, p - last); 734 } 735 736 /****************************************************************** 737 * RtfAddHexBytes 738 * 739 */ 740 static BOOL HLPFILE_RtfAddHexBytes(struct RtfData* rd, const void* _ptr, unsigned sz) 741 { 742 char tmp[512]; 743 unsigned i, step; 744 const BYTE* ptr = _ptr; 745 static const char* _2hex = "0123456789abcdef"; 746 747 if (!rd->in_text) 748 { 749 if (!HLPFILE_RtfAddRawString(rd, " ", 1)) return FALSE; 750 rd->in_text = TRUE; 751 } 752 for (; sz; sz -= step) 753 { 754 step = min(256, sz); 755 for (i = 0; i < step; i++) 756 { 757 tmp[2 * i + 0] = _2hex[*ptr >> 4]; 758 tmp[2 * i + 1] = _2hex[*ptr++ & 0xF]; 759 } 760 if (!HLPFILE_RtfAddRawString(rd, tmp, 2 * step)) return FALSE; 761 } 762 return TRUE; 763 } 764 765 static HLPFILE_LINK* HLPFILE_AllocLink(struct RtfData* rd, int cookie, 766 const char* str, unsigned len, LONG hash, 767 BOOL clrChange, BOOL bHotSpot, unsigned wnd); 768 769 /****************************************************************** 770 * HLPFILE_AddHotSpotLinks 771 * 772 */ 773 static void HLPFILE_AddHotSpotLinks(struct RtfData* rd, HLPFILE* file, 774 const BYTE* start, ULONG hs_size, ULONG hs_offset) 775 { 776 unsigned i, hs_num; 777 ULONG hs_macro; 778 const char* str; 779 780 if (hs_size == 0 || hs_offset == 0) return; 781 782 start += hs_offset; 783 /* always 1 ?? */ 784 hs_num = GET_USHORT(start, 1); 785 hs_macro = GET_UINT(start, 3); 786 787 str = (const char*)start + 7 + 15 * hs_num + hs_macro; 788 /* FIXME: should use hs_size to prevent out of bounds reads */ 789 for (i = 0; i < hs_num; i++) 790 { 791 HLPFILE_HOTSPOTLINK* hslink; 792 793 WINE_TRACE("%02x-%02x%02x {%s,%s}\n", 794 start[7 + 15 * i + 0], start[7 + 15 * i + 1], start[7 + 15 * i + 2], 795 debugstr_a(str), debugstr_a(str + strlen(str) + 1)); 796 /* str points to two null terminated strings: 797 * hotspot name, then link name 798 */ 799 str += strlen(str) + 1; /* skip hotspot name */ 800 801 hslink = NULL; 802 switch (start[7 + 15 * i + 0]) 803 /* The next two chars always look like 0x04 0x00 ??? 804 * What are they for ? 805 */ 806 { 807 case 0xC8: 808 hslink = (HLPFILE_HOTSPOTLINK*) 809 HLPFILE_AllocLink(rd, hlp_link_macro, str, -1, 0, FALSE, TRUE, -1); 810 break; 811 812 case 0xE6: 813 case 0xE7: 814 hslink = (HLPFILE_HOTSPOTLINK*) 815 HLPFILE_AllocLink(rd, (start[7 + 15 * i + 0] & 1) ? hlp_link_link : hlp_link_popup, 816 file->lpszPath, -1, HLPFILE_Hash(str), 817 FALSE, TRUE, -1); 818 break; 819 820 case 0xEE: 821 case 0xEF: 822 { 823 const char* win = strchr(str, '>'); 824 int wnd = -1; 825 char* tgt = NULL; 826 827 if (win) 828 { 829 for (wnd = file->numWindows - 1; wnd >= 0; wnd--) 830 { 831 if (!strcmp(win + 1, file->windows[wnd].name)) break; 832 } 833 if (wnd == -1) 834 WINE_WARN("Couldn't find window info for %s\n", debugstr_a(win)); 835 if ((tgt = HeapAlloc(GetProcessHeap(), 0, win - str + 1))) 836 { 837 memcpy(tgt, str, win - str); 838 tgt[win - str] = '\0'; 839 } 840 } 841 hslink = (HLPFILE_HOTSPOTLINK*) 842 HLPFILE_AllocLink(rd, (start[7 + 15 * i + 0] & 1) ? hlp_link_link : hlp_link_popup, 843 file->lpszPath, -1, HLPFILE_Hash(tgt ? tgt : str), FALSE, TRUE, wnd); 844 HeapFree(GetProcessHeap(), 0, tgt); 845 break; 846 } 847 default: 848 WINE_FIXME("unknown hotsport target 0x%x\n", start[7 + 15 * i + 0]); 849 } 850 if (hslink) 851 { 852 hslink->x = GET_USHORT(start, 7 + 15 * i + 3); 853 hslink->y = GET_USHORT(start, 7 + 15 * i + 5); 854 hslink->width = GET_USHORT(start, 7 + 15 * i + 7); 855 hslink->height = GET_USHORT(start, 7 + 15 * i + 9); 856 /* target = GET_UINT(start, 7 + 15 * i + 11); */ 857 } 858 str += strlen(str) + 1; 859 } 860 } 861 862 /****************************************************************** 863 * HLPFILE_RtfAddTransparentBitmap 864 * 865 * We'll transform a transparent bitmap into an metafile that 866 * we then transform into RTF 867 */ 868 static BOOL HLPFILE_RtfAddTransparentBitmap(struct RtfData* rd, const BITMAPINFO* bi, 869 const void* pict, unsigned nc) 870 { 871 HDC hdc, hdcMask, hdcMem, hdcEMF; 872 HBITMAP hbm, hbmMask, hbmOldMask, hbmOldMem; 873 HENHMETAFILE hEMF; 874 BOOL ret = FALSE; 875 void* data; 876 UINT sz; 877 878 hbm = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader, 879 CBM_INIT, pict, bi, DIB_RGB_COLORS); 880 881 hdcMem = CreateCompatibleDC(hdc); 882 hbmOldMem = SelectObject(hdcMem, hbm); 883 884 /* create the mask bitmap from the main bitmap */ 885 hdcMask = CreateCompatibleDC(hdc); 886 hbmMask = CreateBitmap(bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, 1, 1, NULL); 887 hbmOldMask = SelectObject(hdcMask, hbmMask); 888 SetBkColor(hdcMem, 889 RGB(bi->bmiColors[nc - 1].rgbRed, 890 bi->bmiColors[nc - 1].rgbGreen, 891 bi->bmiColors[nc - 1].rgbBlue)); 892 BitBlt(hdcMask, 0, 0, bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, hdcMem, 0, 0, SRCCOPY); 893 894 /* sets to RGB(0,0,0) the transparent bits in main bitmap */ 895 SetBkColor(hdcMem, RGB(0,0,0)); 896 SetTextColor(hdcMem, RGB(255,255,255)); 897 BitBlt(hdcMem, 0, 0, bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, hdcMask, 0, 0, SRCAND); 898 899 SelectObject(hdcMask, hbmOldMask); 900 DeleteDC(hdcMask); 901 902 SelectObject(hdcMem, hbmOldMem); 903 DeleteDC(hdcMem); 904 905 /* we create the bitmap on the fly */ 906 hdcEMF = CreateEnhMetaFileW(NULL, NULL, NULL, NULL); 907 hdcMem = CreateCompatibleDC(hdcEMF); 908 909 /* sets to RGB(0,0,0) the transparent bits in final bitmap */ 910 hbmOldMem = SelectObject(hdcMem, hbmMask); 911 SetBkColor(hdcEMF, RGB(255, 255, 255)); 912 SetTextColor(hdcEMF, RGB(0, 0, 0)); 913 BitBlt(hdcEMF, 0, 0, bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, hdcMem, 0, 0, SRCAND); 914 915 /* and copy the remaining bits of main bitmap */ 916 SelectObject(hdcMem, hbm); 917 BitBlt(hdcEMF, 0, 0, bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, hdcMem, 0, 0, SRCPAINT); 918 SelectObject(hdcMem, hbmOldMem); 919 DeleteDC(hdcMem); 920 921 /* do the cleanup */ 922 ReleaseDC(0, hdc); 923 DeleteObject(hbmMask); 924 DeleteObject(hbm); 925 926 hEMF = CloseEnhMetaFile(hdcEMF); 927 928 /* generate rtf stream */ 929 sz = GetEnhMetaFileBits(hEMF, 0, NULL); 930 if (sz && (data = HeapAlloc(GetProcessHeap(), 0, sz))) 931 { 932 if (sz == GetEnhMetaFileBits(hEMF, sz, data)) 933 { 934 ret = HLPFILE_RtfAddControl(rd, "{\\pict\\emfblip") && 935 HLPFILE_RtfAddHexBytes(rd, data, sz) && 936 HLPFILE_RtfAddControl(rd, "}"); 937 } 938 HeapFree(GetProcessHeap(), 0, data); 939 } 940 DeleteEnhMetaFile(hEMF); 941 942 return ret; 943 } 944 945 /****************************************************************** 946 * HLPFILE_RtfAddBitmap 947 * 948 */ 949 static BOOL HLPFILE_RtfAddBitmap(struct RtfData* rd, HLPFILE* file, const BYTE* beg, BYTE type, BYTE pack) 950 { 951 const BYTE* ptr; 952 const BYTE* pict_beg; 953 BYTE* alloc = NULL; 954 BITMAPINFO* bi; 955 ULONG off, csz; 956 unsigned nc = 0; 957 BOOL clrImportant = FALSE; 958 BOOL ret = FALSE; 959 char tmp[256]; 960 unsigned hs_size, hs_offset; 961 962 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi)); 963 if (!bi) return FALSE; 964 965 ptr = beg + 2; /* for type and pack */ 966 967 bi->bmiHeader.biSize = sizeof(bi->bmiHeader); 968 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr); 969 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr); 970 bi->bmiHeader.biPlanes = fetch_ushort(&ptr); 971 bi->bmiHeader.biBitCount = fetch_ushort(&ptr); 972 bi->bmiHeader.biWidth = fetch_ulong(&ptr); 973 bi->bmiHeader.biHeight = fetch_ulong(&ptr); 974 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr); 975 clrImportant = fetch_ulong(&ptr); 976 bi->bmiHeader.biClrImportant = (clrImportant > 1) ? clrImportant : 0; 977 bi->bmiHeader.biCompression = BI_RGB; 978 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount); 979 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes); 980 bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight; 981 WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n", 982 bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount, 983 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight); 984 985 csz = fetch_ulong(&ptr); 986 hs_size = fetch_ulong(&ptr); 987 988 off = GET_UINT(ptr, 0); ptr += 4; 989 hs_offset = GET_UINT(ptr, 0); ptr += 4; 990 HLPFILE_AddHotSpotLinks(rd, file, beg, hs_size, hs_offset); 991 992 /* now read palette info */ 993 if (type == 0x06) 994 { 995 unsigned i; 996 997 nc = bi->bmiHeader.biClrUsed; 998 /* not quite right, especially for bitfields type of compression */ 999 if (!nc && bi->bmiHeader.biBitCount <= 8) 1000 nc = 1 << bi->bmiHeader.biBitCount; 1001 1002 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD)); 1003 if (!bi) return FALSE; 1004 for (i = 0; i < nc; i++) 1005 { 1006 bi->bmiColors[i].rgbBlue = ptr[0]; 1007 bi->bmiColors[i].rgbGreen = ptr[1]; 1008 bi->bmiColors[i].rgbRed = ptr[2]; 1009 bi->bmiColors[i].rgbReserved = 0; 1010 ptr += 4; 1011 } 1012 } 1013 pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack, &alloc); 1014 1015 if (clrImportant == 1 && nc > 0) 1016 { 1017 ret = HLPFILE_RtfAddTransparentBitmap(rd, bi, pict_beg, nc); 1018 goto done; 1019 } 1020 if (!HLPFILE_RtfAddControl(rd, "{\\pict")) goto done; 1021 if (type == 0x06) 1022 { 1023 sprintf(tmp, "\\dibitmap0\\picw%d\\pich%d", 1024 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight); 1025 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1026 if (!HLPFILE_RtfAddHexBytes(rd, bi, sizeof(*bi) + nc * sizeof(RGBQUAD))) goto done; 1027 } 1028 else 1029 { 1030 sprintf(tmp, "\\wbitmap0\\wbmbitspixel%d\\wbmplanes%d\\picw%d\\pich%d", 1031 bi->bmiHeader.biBitCount, bi->bmiHeader.biPlanes, 1032 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight); 1033 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1034 } 1035 if (!HLPFILE_RtfAddHexBytes(rd, pict_beg, bi->bmiHeader.biSizeImage)) goto done; 1036 if (!HLPFILE_RtfAddControl(rd, "}")) goto done; 1037 1038 ret = TRUE; 1039 done: 1040 HeapFree(GetProcessHeap(), 0, bi); 1041 HeapFree(GetProcessHeap(), 0, alloc); 1042 1043 return ret; 1044 } 1045 1046 /****************************************************************** 1047 * HLPFILE_RtfAddMetaFile 1048 * 1049 */ 1050 static BOOL HLPFILE_RtfAddMetaFile(struct RtfData* rd, HLPFILE* file, const BYTE* beg, BYTE pack) 1051 { 1052 ULONG size, csize, off, hs_offset, hs_size; 1053 const BYTE* ptr; 1054 const BYTE* bits; 1055 BYTE* alloc = NULL; 1056 char tmp[256]; 1057 unsigned mm; 1058 BOOL ret; 1059 1060 WINE_TRACE("Loading metafile\n"); 1061 1062 ptr = beg + 2; /* for type and pack */ 1063 1064 mm = fetch_ushort(&ptr); /* mapping mode */ 1065 sprintf(tmp, "{\\pict\\wmetafile%u\\picw%u\\pich%u", 1066 mm, GET_USHORT(ptr, 0), GET_USHORT(ptr, 2)); 1067 if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE; 1068 ptr += 4; 1069 1070 size = fetch_ulong(&ptr); /* decompressed size */ 1071 csize = fetch_ulong(&ptr); /* compressed size */ 1072 hs_size = fetch_ulong(&ptr); /* hotspot size */ 1073 off = GET_UINT(ptr, 0); 1074 hs_offset = GET_UINT(ptr, 4); 1075 ptr += 8; 1076 1077 HLPFILE_AddHotSpotLinks(rd, file, beg, hs_size, hs_offset); 1078 1079 WINE_TRACE("sz=%u csz=%u offs=%u/%u,%u/%u\n", 1080 size, csize, off, (ULONG)(ptr - beg), hs_size, hs_offset); 1081 1082 bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack, &alloc); 1083 if (!bits) return FALSE; 1084 1085 ret = HLPFILE_RtfAddHexBytes(rd, bits, size) && 1086 HLPFILE_RtfAddControl(rd, "}"); 1087 1088 HeapFree(GetProcessHeap(), 0, alloc); 1089 1090 return ret; 1091 } 1092 1093 /****************************************************************** 1094 * HLPFILE_RtfAddGfxByAddr 1095 * 1096 */ 1097 static BOOL HLPFILE_RtfAddGfxByAddr(struct RtfData* rd, HLPFILE *hlpfile, 1098 const BYTE* ref, ULONG size) 1099 { 1100 unsigned i, numpict; 1101 1102 numpict = GET_USHORT(ref, 2); 1103 WINE_TRACE("Got picture magic=%04x #=%d\n", GET_USHORT(ref, 0), numpict); 1104 1105 for (i = 0; i < numpict; i++) 1106 { 1107 const BYTE* beg; 1108 const BYTE* ptr; 1109 BYTE type, pack; 1110 1111 WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4)); 1112 beg = ptr = ref + GET_UINT(ref, (1 + i) * 4); 1113 1114 type = *ptr++; 1115 pack = *ptr++; 1116 1117 switch (type) 1118 { 1119 case 5: /* device dependent bmp */ 1120 case 6: /* device independent bmp */ 1121 HLPFILE_RtfAddBitmap(rd, hlpfile, beg, type, pack); 1122 break; 1123 case 8: 1124 HLPFILE_RtfAddMetaFile(rd, hlpfile, beg, pack); 1125 break; 1126 default: WINE_FIXME("Unknown type %u\n", type); return FALSE; 1127 } 1128 1129 /* FIXME: hotspots */ 1130 1131 /* FIXME: implement support for multiple picture format */ 1132 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n"); 1133 break; 1134 } 1135 return TRUE; 1136 } 1137 1138 /****************************************************************** 1139 * HLPFILE_RtfAddGfxByIndex 1140 * 1141 * 1142 */ 1143 static BOOL HLPFILE_RtfAddGfxByIndex(struct RtfData* rd, HLPFILE *hlpfile, 1144 unsigned index) 1145 { 1146 char tmp[16]; 1147 BYTE *ref, *end; 1148 1149 WINE_TRACE("Loading picture #%d\n", index); 1150 1151 sprintf(tmp, "|bm%u", index); 1152 1153 if (!HLPFILE_FindSubFile(hlpfile, tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;} 1154 1155 ref += 9; 1156 return HLPFILE_RtfAddGfxByAddr(rd, hlpfile, ref, end - ref); 1157 } 1158 1159 /****************************************************************** 1160 * HLPFILE_AllocLink 1161 * 1162 * 1163 */ 1164 static HLPFILE_LINK* HLPFILE_AllocLink(struct RtfData* rd, int cookie, 1165 const char* str, unsigned len, LONG hash, 1166 BOOL clrChange, BOOL bHotSpot, unsigned wnd) 1167 { 1168 HLPFILE_LINK* link; 1169 char* link_str; 1170 unsigned asz = bHotSpot ? sizeof(HLPFILE_HOTSPOTLINK) : sizeof(HLPFILE_LINK); 1171 1172 /* FIXME: should build a string table for the attributes.link.lpszPath 1173 * they are reallocated for each link 1174 */ 1175 if (len == -1) len = strlen(str); 1176 link = HeapAlloc(GetProcessHeap(), 0, asz + len + 1); 1177 if (!link) return NULL; 1178 1179 link->cookie = cookie; 1180 link->string = link_str = (char*)link + asz; 1181 memcpy(link_str, str, len); 1182 link_str[len] = '\0'; 1183 link->hash = hash; 1184 link->bClrChange = clrChange; 1185 link->bHotSpot = bHotSpot; 1186 link->window = wnd; 1187 link->next = rd->first_link; 1188 rd->first_link = link; 1189 link->cpMin = rd->char_pos; 1190 rd->force_color = clrChange; 1191 if (rd->current_link) WINE_FIXME("Pending link\n"); 1192 if (bHotSpot) 1193 link->cpMax = rd->char_pos; 1194 else 1195 rd->current_link = link; 1196 1197 WINE_TRACE("Link[%d] to %s@%08x:%d\n", 1198 link->cookie, debugstr_a(link->string), link->hash, link->window); 1199 return link; 1200 } 1201 1202 static unsigned HLPFILE_HalfPointsScale(HLPFILE_PAGE* page, unsigned pts) 1203 { 1204 return pts * page->file->scale - page->file->rounderr; 1205 } 1206 1207 /*********************************************************************** 1208 * 1209 * HLPFILE_BrowseParagraph 1210 */ 1211 static BOOL HLPFILE_BrowseParagraph(HLPFILE_PAGE* page, struct RtfData* rd, 1212 BYTE *buf, BYTE* end, unsigned* parlen) 1213 { 1214 UINT textsize; 1215 const BYTE *format, *format_end; 1216 char *text, *text_base, *text_end; 1217 LONG size, blocksize, datalen; 1218 unsigned short bits; 1219 unsigned ncol = 1; 1220 short nc, lastcol, table_width; 1221 char tmp[256]; 1222 BOOL ret = FALSE; 1223 1224 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;}; 1225 1226 *parlen = 0; 1227 blocksize = GET_UINT(buf, 0); 1228 size = GET_UINT(buf, 0x4); 1229 datalen = GET_UINT(buf, 0x10); 1230 text = text_base = HeapAlloc(GetProcessHeap(), 0, size); 1231 if (!text) return FALSE; 1232 if (size > blocksize - datalen) 1233 { 1234 /* need to decompress */ 1235 if (page->file->hasPhrases) 1236 HLPFILE_Uncompress2(page->file, buf + datalen, end, (BYTE*)text, (BYTE*)text + size); 1237 else if (page->file->hasPhrases40) 1238 HLPFILE_Uncompress3(page->file, text, text + size, buf + datalen, end); 1239 else 1240 { 1241 WINE_FIXME("Text size is too long, splitting\n"); 1242 size = blocksize - datalen; 1243 memcpy(text, buf + datalen, size); 1244 } 1245 } 1246 else 1247 memcpy(text, buf + datalen, size); 1248 1249 text_end = text + size; 1250 1251 format = buf + 0x15; 1252 format_end = buf + GET_UINT(buf, 0x10); 1253 1254 WINE_TRACE("Record type (buf[0x14]) = 0x%x\n", buf[0x14]); 1255 1256 if (buf[0x14] == HLP_DISPLAY || buf[0x14] == HLP_TABLE) 1257 { 1258 fetch_long(&format); 1259 *parlen = fetch_ushort(&format); 1260 } 1261 1262 if (buf[0x14] == HLP_TABLE) 1263 { 1264 unsigned char type; 1265 1266 ncol = *format++; 1267 1268 if (!HLPFILE_RtfAddControl(rd, "\\trowd")) goto done; 1269 type = *format++; 1270 if (type == 0 || type == 2) 1271 { 1272 table_width = GET_SHORT(format, 0); 1273 format += 2; 1274 if (!HLPFILE_RtfAddControl(rd, "\\trqc")) goto done; 1275 } 1276 else 1277 table_width = 32767; 1278 WINE_TRACE("New table: cols=%d type=%x width=%d\n", 1279 ncol, type, table_width); 1280 if (ncol > 1) 1281 { 1282 int pos; 1283 sprintf(tmp, "\\trgaph%d\\trleft%d", 1284 MulDiv(HLPFILE_HalfPointsScale(page, GET_SHORT(format, 6)), table_width, 32767), 1285 MulDiv(HLPFILE_HalfPointsScale(page, GET_SHORT(format, 2) - GET_SHORT(format, 6)), table_width, 32767) - 1); 1286 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1287 pos = GET_SHORT(format, 6) / 2; 1288 for (nc = 0; nc < ncol; nc++) 1289 { 1290 WINE_TRACE("column(%d/%d) gap=%d width=%d\n", 1291 nc, ncol, GET_SHORT(format, nc*4), 1292 GET_SHORT(format, nc*4+2)); 1293 pos += GET_SHORT(format, nc * 4) + GET_SHORT(format, nc * 4 + 2); 1294 sprintf(tmp, "\\cellx%d", 1295 MulDiv(HLPFILE_HalfPointsScale(page, pos), table_width, 32767)); 1296 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1297 } 1298 } 1299 else 1300 { 1301 WINE_TRACE("column(0/%d) gap=%d width=%d\n", 1302 ncol, GET_SHORT(format, 0), GET_SHORT(format, 2)); 1303 sprintf(tmp, "\\trleft%d\\cellx%d ", 1304 MulDiv(HLPFILE_HalfPointsScale(page, GET_SHORT(format, 2)), table_width, 32767) - 1, 1305 MulDiv(HLPFILE_HalfPointsScale(page, GET_SHORT(format, 0)), table_width, 32767)); 1306 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1307 } 1308 format += ncol * 4; 1309 } 1310 1311 lastcol = -1; 1312 for (nc = 0; nc < ncol; /**/) 1313 { 1314 WINE_TRACE("looking for format at offset %lu in column %d\n", (SIZE_T)(format - (buf + 0x15)), nc); 1315 if (!HLPFILE_RtfAddControl(rd, "\\pard")) goto done; 1316 if (buf[0x14] == HLP_TABLE) 1317 { 1318 nc = lastcol = GET_SHORT(format, 0); 1319 if (nc == -1) /* last column */ 1320 { 1321 if (!HLPFILE_RtfAddControl(rd, "\\row")) goto done; 1322 rd->char_pos += 2; 1323 break; 1324 } 1325 format += 5; 1326 if (!HLPFILE_RtfAddControl(rd, "\\intbl")) goto done; 1327 } 1328 else nc++; 1329 if (buf[0x14] == HLP_DISPLAY30) 1330 format += 6; 1331 else 1332 format += 4; 1333 bits = GET_USHORT(format, 0); format += 2; 1334 if (bits & 0x0001) fetch_long(&format); 1335 if (bits & 0x0002) 1336 { 1337 sprintf(tmp, "\\sb%d", HLPFILE_HalfPointsScale(page, fetch_short(&format))); 1338 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1339 } 1340 if (bits & 0x0004) 1341 { 1342 sprintf(tmp, "\\sa%d", HLPFILE_HalfPointsScale(page, fetch_short(&format))); 1343 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1344 } 1345 if (bits & 0x0008) 1346 { 1347 sprintf(tmp, "\\sl%d", HLPFILE_HalfPointsScale(page, fetch_short(&format))); 1348 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1349 } 1350 if (bits & 0x0010) 1351 { 1352 sprintf(tmp, "\\li%d", HLPFILE_HalfPointsScale(page, fetch_short(&format))); 1353 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1354 } 1355 if (bits & 0x0020) 1356 { 1357 sprintf(tmp, "\\ri%d", HLPFILE_HalfPointsScale(page, fetch_short(&format))); 1358 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1359 } 1360 if (bits & 0x0040) 1361 { 1362 sprintf(tmp, "\\fi%d", HLPFILE_HalfPointsScale(page, fetch_short(&format))); 1363 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1364 } 1365 if (bits & 0x0100) 1366 { 1367 BYTE brdr = *format++; 1368 short w; 1369 1370 if ((brdr & 0x01) && !HLPFILE_RtfAddControl(rd, "\\box")) goto done; 1371 if ((brdr & 0x02) && !HLPFILE_RtfAddControl(rd, "\\brdrt")) goto done; 1372 if ((brdr & 0x04) && !HLPFILE_RtfAddControl(rd, "\\brdrl")) goto done; 1373 if ((brdr & 0x08) && !HLPFILE_RtfAddControl(rd, "\\brdrb")) goto done; 1374 if ((brdr & 0x10) && !HLPFILE_RtfAddControl(rd, "\\brdrr")) goto done; 1375 if ((brdr & 0x20) && !HLPFILE_RtfAddControl(rd, "\\brdrth")) goto done; 1376 if (!(brdr & 0x20) && !HLPFILE_RtfAddControl(rd, "\\brdrs")) goto done; 1377 if ((brdr & 0x40) && !HLPFILE_RtfAddControl(rd, "\\brdrdb")) goto done; 1378 /* 0x80: unknown */ 1379 1380 w = GET_SHORT(format, 0); format += 2; 1381 if (w) 1382 { 1383 sprintf(tmp, "\\brdrw%d", HLPFILE_HalfPointsScale(page, w)); 1384 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1385 } 1386 } 1387 if (bits & 0x0200) 1388 { 1389 int i, ntab = fetch_short(&format); 1390 unsigned tab, ts; 1391 const char* kind; 1392 1393 for (i = 0; i < ntab; i++) 1394 { 1395 tab = fetch_ushort(&format); 1396 ts = (tab & 0x4000) ? fetch_ushort(&format) : 0 /* left */; 1397 switch (ts) 1398 { 1399 default: WINE_FIXME("Unknown tab style %x\n", ts); 1400 /* fall through */ 1401 case 0: kind = ""; break; 1402 case 1: kind = "\\tqr"; break; 1403 case 2: kind = "\\tqc"; break; 1404 } 1405 /* FIXME: do kind */ 1406 sprintf(tmp, "%s\\tx%d", 1407 kind, HLPFILE_HalfPointsScale(page, tab & 0x3FFF)); 1408 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1409 } 1410 } 1411 switch (bits & 0xc00) 1412 { 1413 default: WINE_FIXME("Unsupported alignment 0xC00\n"); break; 1414 case 0: if (!HLPFILE_RtfAddControl(rd, "\\ql")) goto done; break; 1415 case 0x400: if (!HLPFILE_RtfAddControl(rd, "\\qr")) goto done; break; 1416 case 0x800: if (!HLPFILE_RtfAddControl(rd, "\\qc")) goto done; break; 1417 } 1418 1419 /* 0x1000 doesn't need space */ 1420 if ((bits & 0x1000) && !HLPFILE_RtfAddControl(rd, "\\keep")) goto done; 1421 if ((bits & 0xE080) != 0) 1422 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits); 1423 1424 while (text < text_end && format < format_end) 1425 { 1426 WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", debugstr_a(text), text, text_end, format, format_end); 1427 textsize = strlen(text); 1428 if (textsize) 1429 { 1430 if (rd->force_color) 1431 { 1432 if ((rd->current_link->cookie == hlp_link_popup) ? 1433 !HLPFILE_RtfAddControl(rd, "{\\uld\\cf1") : 1434 !HLPFILE_RtfAddControl(rd, "{\\ul\\cf1")) goto done; 1435 } 1436 if (!HLPFILE_RtfAddText(rd, text)) goto done; 1437 if (rd->force_color && !HLPFILE_RtfAddControl(rd, "}")) goto done; 1438 rd->char_pos += textsize; 1439 } 1440 /* else: null text, keep on storing attributes */ 1441 text += textsize + 1; 1442 1443 WINE_TRACE("format=0x%02x\n", *format); 1444 if (*format == 0xff) 1445 { 1446 format++; 1447 break; 1448 } 1449 1450 switch (*format) 1451 { 1452 case 0x20: 1453 WINE_FIXME("NIY20\n"); 1454 format += 5; 1455 break; 1456 1457 case 0x21: 1458 WINE_FIXME("NIY21\n"); 1459 format += 3; 1460 break; 1461 1462 case 0x80: 1463 { 1464 unsigned font = GET_USHORT(format, 1); 1465 unsigned fs; 1466 1467 WINE_TRACE("Changing font to %d\n", font); 1468 format += 3; 1469 /* Font size in hlpfile is given in the same units as 1470 rtf control word \fs uses (half-points). */ 1471 switch (rd->font_scale) 1472 { 1473 case 0: fs = page->file->fonts[font].LogFont.lfHeight - 4; break; 1474 default: 1475 case 1: fs = page->file->fonts[font].LogFont.lfHeight; break; 1476 case 2: fs = page->file->fonts[font].LogFont.lfHeight + 4; break; 1477 } 1478 /* FIXME: colors are missing, at a minimum; also, the bold attribute loses information */ 1479 1480 sprintf(tmp, "\\f%u\\cf%u\\fs%u%s%s%s%s", 1481 font, font + 2, fs, 1482 page->file->fonts[font].LogFont.lfWeight > 400 ? "\\b" : "\\b0", 1483 page->file->fonts[font].LogFont.lfItalic ? "\\i" : "\\i0", 1484 page->file->fonts[font].LogFont.lfUnderline ? "\\ul" : "\\ul0", 1485 page->file->fonts[font].LogFont.lfStrikeOut ? "\\strike" : "\\strike0"); 1486 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; 1487 } 1488 break; 1489 1490 case 0x81: 1491 if (!HLPFILE_RtfAddControl(rd, "\\line")) goto done; 1492 format += 1; 1493 rd->char_pos++; 1494 break; 1495 1496 case 0x82: 1497 if (buf[0x14] == HLP_TABLE) 1498 { 1499 if (format[1] != 0xFF) 1500 { 1501 if (!HLPFILE_RtfAddControl(rd, "\\par\\intbl")) goto done; 1502 } 1503 else if (GET_SHORT(format, 2) == -1) 1504 { 1505 if (!HLPFILE_RtfAddControl(rd, "\\cell\\intbl\\row")) goto done; 1506 rd->char_pos += 2; 1507 } 1508 else if (GET_SHORT(format, 2) == lastcol) 1509 { 1510 if (!HLPFILE_RtfAddControl(rd, "\\par\\pard")) goto done; 1511 } 1512 else 1513 { 1514 if (!HLPFILE_RtfAddControl(rd, "\\cell\\pard")) goto done; 1515 } 1516 } 1517 else if (!HLPFILE_RtfAddControl(rd, "\\par")) goto done; 1518 format += 1; 1519 rd->char_pos++; 1520 break; 1521 1522 case 0x83: 1523 if (!HLPFILE_RtfAddControl(rd, "\\tab")) goto done; 1524 format += 1; 1525 rd->char_pos++; 1526 break; 1527 1528 #if 0 1529 case 0x84: 1530 format += 3; 1531 break; 1532 #endif 1533 1534 case 0x86: 1535 case 0x87: 1536 case 0x88: 1537 { 1538 BYTE type = format[1]; 1539 1540 /* FIXME: we don't use 'BYTE pos = (*format - 0x86);' for the image position */ 1541 format += 2; 1542 size = fetch_long(&format); 1543 1544 switch (type) 1545 { 1546 case 0x22: 1547 fetch_ushort(&format); /* hot spot */ 1548 /* fall through */ 1549 case 0x03: 1550 switch (GET_SHORT(format, 0)) 1551 { 1552 case 0: 1553 HLPFILE_RtfAddGfxByIndex(rd, page->file, GET_SHORT(format, 2)); 1554 rd->char_pos++; 1555 break; 1556 case 1: 1557 WINE_FIXME("does it work ??? %x<%u>#%u\n", 1558 GET_SHORT(format, 0), 1559 size, GET_SHORT(format, 2)); 1560 HLPFILE_RtfAddGfxByAddr(rd, page->file, format + 2, size - 4); 1561 rd->char_pos++; 1562 break; 1563 default: 1564 WINE_FIXME("??? %u\n", GET_SHORT(format, 0)); 1565 break; 1566 } 1567 break; 1568 case 0x05: 1569 WINE_FIXME("Got an embedded element %s\n", debugstr_a((char *)format + 6)); 1570 break; 1571 default: 1572 WINE_FIXME("Got a type %d picture\n", type); 1573 break; 1574 } 1575 format += size; 1576 } 1577 break; 1578 1579 case 0x89: 1580 format += 1; 1581 if (!rd->current_link) 1582 WINE_FIXME("No existing link\n"); 1583 rd->current_link->cpMax = rd->char_pos; 1584 rd->current_link = NULL; 1585 rd->force_color = FALSE; 1586 break; 1587 1588 case 0x8B: 1589 if (!HLPFILE_RtfAddControl(rd, "\\~")) goto done; 1590 format += 1; 1591 rd->char_pos++; 1592 break; 1593 1594 case 0x8C: 1595 if (!HLPFILE_RtfAddControl(rd, "\\_")) goto done; 1596 /* FIXME: it could be that hyphen is also in input stream !! */ 1597 format += 1; 1598 rd->char_pos++; 1599 break; 1600 1601 #if 0 1602 case 0xA9: 1603 format += 2; 1604 break; 1605 #endif 1606 1607 case 0xC8: 1608 case 0xCC: 1609 WINE_TRACE("macro => %s\n", debugstr_a((char *)format + 3)); 1610 HLPFILE_AllocLink(rd, hlp_link_macro, (const char*)format + 3, 1611 GET_USHORT(format, 1), 0, !(*format & 4), FALSE, -1); 1612 format += 3 + GET_USHORT(format, 1); 1613 break; 1614 1615 case 0xE0: 1616 case 0xE1: 1617 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1)); 1618 HLPFILE_AllocLink(rd, (*format & 1) ? hlp_link_link : hlp_link_popup, 1619 page->file->lpszPath, -1, GET_UINT(format, 1), TRUE, FALSE, -1); 1620 1621 1622 format += 5; 1623 break; 1624 1625 case 0xE2: 1626 case 0xE3: 1627 case 0xE6: 1628 case 0xE7: 1629 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1)); 1630 HLPFILE_AllocLink(rd, (*format & 1) ? hlp_link_link : hlp_link_popup, 1631 page->file->lpszPath, -1, GET_UINT(format, 1), 1632 !(*format & 4), FALSE, -1); 1633 format += 5; 1634 break; 1635 1636 case 0xEA: 1637 case 0xEB: 1638 case 0xEE: 1639 case 0xEF: 1640 { 1641 const char* ptr = (const char*) format + 8; 1642 BYTE type = format[3]; 1643 int wnd = -1; 1644 1645 switch (type) 1646 { 1647 case 1: 1648 wnd = *ptr; 1649 /* fall through */ 1650 case 0: 1651 ptr = page->file->lpszPath; 1652 break; 1653 case 6: 1654 for (wnd = page->file->numWindows - 1; wnd >= 0; wnd--) 1655 { 1656 if (!strcmp(ptr, page->file->windows[wnd].name)) break; 1657 } 1658 if (wnd == -1) 1659 WINE_WARN("Couldn't find window info for %s\n", debugstr_a(ptr)); 1660 ptr += strlen(ptr) + 1; 1661 /* fall through */ 1662 case 4: 1663 break; 1664 default: 1665 WINE_WARN("Unknown link type %d\n", type); 1666 break; 1667 } 1668 HLPFILE_AllocLink(rd, (*format & 1) ? hlp_link_link : hlp_link_popup, 1669 ptr, -1, GET_UINT(format, 4), !(*format & 4), FALSE, wnd); 1670 } 1671 format += 3 + GET_USHORT(format, 1); 1672 break; 1673 1674 default: 1675 WINE_WARN("format %02x\n", *format); 1676 format++; 1677 } 1678 } 1679 } 1680 ret = TRUE; 1681 done: 1682 1683 HeapFree(GetProcessHeap(), 0, text_base); 1684 return ret; 1685 } 1686 1687 /****************************************************************** 1688 * HLPFILE_BrowsePage 1689 * 1690 */ 1691 BOOL HLPFILE_BrowsePage(HLPFILE_PAGE* page, struct RtfData* rd, 1692 unsigned font_scale, unsigned relative) 1693 { 1694 HLPFILE *hlpfile = page->file; 1695 BYTE *buf, *end; 1696 DWORD ref = page->reference; 1697 unsigned index, old_index = -1, offset, count = 0, offs = 0; 1698 unsigned cpg, parlen; 1699 char tmp[1024]; 1700 const char* ck = NULL; 1701 1702 rd->in_text = TRUE; 1703 rd->data = rd->ptr = HeapAlloc(GetProcessHeap(), 0, rd->allocated = 32768); 1704 rd->char_pos = 0; 1705 rd->first_link = rd->current_link = NULL; 1706 rd->force_color = FALSE; 1707 rd->font_scale = font_scale; 1708 rd->relative = relative; 1709 rd->char_pos_rel = 0; 1710 1711 switch (hlpfile->charset) 1712 { 1713 case DEFAULT_CHARSET: 1714 case ANSI_CHARSET: cpg = 1252; break; 1715 case SHIFTJIS_CHARSET: cpg = 932; break; 1716 case HANGEUL_CHARSET: cpg = 949; break; 1717 case GB2312_CHARSET: cpg = 936; break; 1718 case CHINESEBIG5_CHARSET: cpg = 950; break; 1719 case GREEK_CHARSET: cpg = 1253; break; 1720 case TURKISH_CHARSET: cpg = 1254; break; 1721 case HEBREW_CHARSET: cpg = 1255; break; 1722 case ARABIC_CHARSET: cpg = 1256; break; 1723 case BALTIC_CHARSET: cpg = 1257; break; 1724 case VIETNAMESE_CHARSET: cpg = 1258; break; 1725 case RUSSIAN_CHARSET: cpg = 1251; break; 1726 case EE_CHARSET: cpg = 1250; break; 1727 case THAI_CHARSET: cpg = 874; break; 1728 case JOHAB_CHARSET: cpg = 1361; break; 1729 case MAC_CHARSET: ck = "mac"; break; 1730 default: 1731 WINE_FIXME("Unsupported charset %u\n", hlpfile->charset); 1732 cpg = 1252; 1733 } 1734 if (ck) 1735 { 1736 sprintf(tmp, "{\\rtf1\\%s\\deff0", ck); 1737 if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE; 1738 } 1739 else 1740 { 1741 sprintf(tmp, "{\\rtf1\\ansi\\ansicpg%d\\deff0", cpg); 1742 if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE; 1743 } 1744 1745 /* generate font table */ 1746 if (!HLPFILE_RtfAddControl(rd, "{\\fonttbl")) return FALSE; 1747 for (index = 0; index < hlpfile->numFonts; index++) 1748 { 1749 const char* family; 1750 switch (hlpfile->fonts[index].LogFont.lfPitchAndFamily & 0xF0) 1751 { 1752 case FF_MODERN: family = "modern"; break; 1753 case FF_ROMAN: family = "roman"; break; 1754 case FF_SWISS: family = "swiss"; break; 1755 case FF_SCRIPT: family = "script"; break; 1756 case FF_DECORATIVE: family = "decor"; break; 1757 default: family = "nil"; break; 1758 } 1759 sprintf(tmp, "{\\f%d\\f%s\\fprq%d\\fcharset%d %s;}", 1760 index, family, 1761 hlpfile->fonts[index].LogFont.lfPitchAndFamily & 0x0F, 1762 hlpfile->fonts[index].LogFont.lfCharSet, 1763 hlpfile->fonts[index].LogFont.lfFaceName); 1764 if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE; 1765 } 1766 if (!HLPFILE_RtfAddControl(rd, "}")) return FALSE; 1767 /* generate color table */ 1768 if (!HLPFILE_RtfAddControl(rd, "{\\colortbl ;\\red0\\green128\\blue0;")) return FALSE; 1769 for (index = 0; index < hlpfile->numFonts; index++) 1770 { 1771 sprintf(tmp, "\\red%d\\green%d\\blue%d;", 1772 GetRValue(hlpfile->fonts[index].color), 1773 GetGValue(hlpfile->fonts[index].color), 1774 GetBValue(hlpfile->fonts[index].color)); 1775 if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE; 1776 } 1777 if (!HLPFILE_RtfAddControl(rd, "}")) return FALSE; 1778 1779 do 1780 { 1781 if (hlpfile->version <= 16) 1782 { 1783 index = (ref - 0x0C) / hlpfile->dsize; 1784 offset = (ref - 0x0C) % hlpfile->dsize; 1785 } 1786 else 1787 { 1788 index = (ref - 0x0C) >> 14; 1789 offset = (ref - 0x0C) & 0x3FFF; 1790 } 1791 1792 if (hlpfile->version <= 16 && index != old_index && old_index != -1) 1793 { 1794 /* we jumped to the next block, adjust pointers */ 1795 ref -= 12; 1796 offset -= 12; 1797 } 1798 1799 if (index >= hlpfile->topic_maplen) {WINE_WARN("maplen\n"); break;} 1800 buf = hlpfile->topic_map[index] + offset; 1801 if (buf + 0x15 >= hlpfile->topic_end) {WINE_WARN("extra\n"); break;} 1802 end = min(buf + GET_UINT(buf, 0), hlpfile->topic_end); 1803 if (index != old_index) {offs = 0; old_index = index;} 1804 1805 switch (buf[0x14]) 1806 { 1807 case HLP_TOPICHDR: 1808 if (count++) goto done; 1809 break; 1810 case HLP_DISPLAY30: 1811 case HLP_DISPLAY: 1812 case HLP_TABLE: 1813 if (!HLPFILE_BrowseParagraph(page, rd, buf, end, &parlen)) return FALSE; 1814 if (relative > index * 0x8000 + offs) 1815 rd->char_pos_rel = rd->char_pos; 1816 offs += parlen; 1817 break; 1818 default: 1819 WINE_ERR("buf[0x14] = %x\n", buf[0x14]); 1820 } 1821 if (hlpfile->version <= 16) 1822 { 1823 ref += GET_UINT(buf, 0xc); 1824 if (GET_UINT(buf, 0xc) == 0) 1825 break; 1826 } 1827 else 1828 ref = GET_UINT(buf, 0xc); 1829 } while (ref != 0xffffffff); 1830 done: 1831 page->first_link = rd->first_link; 1832 return HLPFILE_RtfAddControl(rd, "}"); 1833 } 1834 1835 /****************************************************************** 1836 * HLPFILE_ReadFont 1837 * 1838 * 1839 */ 1840 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile) 1841 { 1842 BYTE *ref, *end; 1843 unsigned i, len, idx; 1844 unsigned face_num, dscr_num, face_offset, dscr_offset; 1845 BYTE flag, family; 1846 1847 if (!HLPFILE_FindSubFile(hlpfile, "|FONT", &ref, &end)) 1848 { 1849 WINE_WARN("no subfile FONT\n"); 1850 hlpfile->numFonts = 0; 1851 hlpfile->fonts = NULL; 1852 return FALSE; 1853 } 1854 1855 ref += 9; 1856 1857 face_num = GET_USHORT(ref, 0); 1858 dscr_num = GET_USHORT(ref, 2); 1859 face_offset = GET_USHORT(ref, 4); 1860 dscr_offset = GET_USHORT(ref, 6); 1861 1862 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n", 1863 face_num, face_offset, dscr_num, dscr_offset); 1864 1865 hlpfile->numFonts = dscr_num; 1866 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num); 1867 1868 len = (dscr_offset - face_offset) / face_num; 1869 1870 /* mvb font */ 1871 if (face_offset >= 16) 1872 { 1873 hlpfile->scale = 1; 1874 hlpfile->rounderr = 0; 1875 WINE_FIXME("mvb font: not implemented\n"); 1876 return FALSE; 1877 } 1878 /* new font */ 1879 if (face_offset >= 12) 1880 { 1881 hlpfile->scale = 1; 1882 hlpfile->rounderr = 0; 1883 WINE_FIXME("new font: not implemented\n"); 1884 return FALSE; 1885 } 1886 /* old font */ 1887 hlpfile->scale = 10; 1888 hlpfile->rounderr = 5; 1889 /* EPP for (i = face_offset; i < dscr_offset; i += len) */ 1890 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */ 1891 for (i = 0; i < dscr_num; i++) 1892 { 1893 flag = ref[dscr_offset + i * 11 + 0]; 1894 family = ref[dscr_offset + i * 11 + 2]; 1895 1896 hlpfile->fonts[i].LogFont.lfHeight = ref[dscr_offset + i * 11 + 1]; 1897 hlpfile->fonts[i].LogFont.lfWidth = 0; 1898 hlpfile->fonts[i].LogFont.lfEscapement = 0; 1899 hlpfile->fonts[i].LogFont.lfOrientation = 0; 1900 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400; 1901 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) != 0; 1902 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) != 0; 1903 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) != 0; 1904 hlpfile->fonts[i].LogFont.lfCharSet = hlpfile->charset; 1905 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS; 1906 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; 1907 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY; 1908 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH; 1909 1910 switch (family) 1911 { 1912 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break; 1913 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break; 1914 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break; 1915 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break; 1916 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break; 1917 default: WINE_FIXME("Unknown family %u\n", family); 1918 } 1919 idx = GET_USHORT(ref, dscr_offset + i * 11 + 3); 1920 1921 if (idx < face_num) 1922 { 1923 memcpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1)); 1924 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1)] = '\0'; 1925 } 1926 else 1927 { 1928 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num); 1929 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv"); 1930 } 1931 hlpfile->fonts[i].hFont = 0; 1932 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5], 1933 ref[dscr_offset + i * 11 + 6], 1934 ref[dscr_offset + i * 11 + 7]); 1935 #define X(b,s) ((flag & (1 << b)) ? "-"s: "") 1936 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n", 1937 i, flag, 1938 X(0, "bold"), 1939 X(1, "italic"), 1940 X(2, "underline"), 1941 X(3, "strikeOut"), 1942 X(4, "dblUnderline"), 1943 X(5, "smallCaps"), 1944 ref[dscr_offset + i * 11 + 1], 1945 family, 1946 debugstr_a(hlpfile->fonts[i].LogFont.lfFaceName), idx, 1947 GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF); 1948 } 1949 return TRUE; 1950 } 1951 1952 /*********************************************************************** 1953 * 1954 * HLPFILE_ReadFileToBuffer 1955 */ 1956 static BOOL HLPFILE_ReadFileToBuffer(HLPFILE* hlpfile, HFILE hFile) 1957 { 1958 BYTE header[16], dummy[1]; 1959 1960 if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;}; 1961 1962 /* sanity checks */ 1963 if (GET_UINT(header, 0) != 0x00035F3F) 1964 {WINE_WARN("wrong header\n"); return FALSE;}; 1965 1966 hlpfile->file_buffer_size = GET_UINT(header, 12); 1967 hlpfile->file_buffer = HeapAlloc(GetProcessHeap(), 0, hlpfile->file_buffer_size + 1); 1968 if (!hlpfile->file_buffer) return FALSE; 1969 1970 memcpy(hlpfile->file_buffer, header, 16); 1971 if (_hread(hFile, hlpfile->file_buffer + 16, hlpfile->file_buffer_size - 16) !=hlpfile->file_buffer_size - 16) 1972 {WINE_WARN("filesize1\n"); return FALSE;}; 1973 1974 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n"); 1975 1976 hlpfile->file_buffer[hlpfile->file_buffer_size] = '\0'; /* FIXME: was '0', sounds backwards to me */ 1977 1978 return TRUE; 1979 } 1980 1981 /*********************************************************************** 1982 * 1983 * HLPFILE_SystemCommands 1984 */ 1985 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile) 1986 { 1987 BYTE *buf, *ptr, *end; 1988 HLPFILE_MACRO *macro, **m; 1989 LPSTR p; 1990 unsigned short magic, minor, major, flags; 1991 1992 hlpfile->lpszTitle = NULL; 1993 1994 if (!HLPFILE_FindSubFile(hlpfile, "|SYSTEM", &buf, &end)) return FALSE; 1995 1996 magic = GET_USHORT(buf + 9, 0); 1997 minor = GET_USHORT(buf + 9, 2); 1998 major = GET_USHORT(buf + 9, 4); 1999 /* gen date on 4 bytes */ 2000 flags = GET_USHORT(buf + 9, 10); 2001 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n", 2002 magic, major, minor, flags); 2003 if (magic != 0x036C || major != 1) 2004 {WINE_WARN("Wrong system header\n"); return FALSE;} 2005 if (minor <= 16) 2006 { 2007 hlpfile->tbsize = 0x800; 2008 hlpfile->compressed = FALSE; 2009 } 2010 else if (flags == 0) 2011 { 2012 hlpfile->tbsize = 0x1000; 2013 hlpfile->compressed = FALSE; 2014 } 2015 else if (flags == 4) 2016 { 2017 hlpfile->tbsize = 0x1000; 2018 hlpfile->compressed = TRUE; 2019 } 2020 else 2021 { 2022 hlpfile->tbsize = 0x800; 2023 hlpfile->compressed = TRUE; 2024 } 2025 2026 if (hlpfile->compressed) 2027 hlpfile->dsize = 0x4000; 2028 else 2029 hlpfile->dsize = hlpfile->tbsize - 0x0C; 2030 2031 hlpfile->version = minor; 2032 hlpfile->flags = flags; 2033 hlpfile->charset = DEFAULT_CHARSET; 2034 2035 if (hlpfile->version <= 16) 2036 { 2037 char *str = (char*)buf + 0x15; 2038 2039 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1); 2040 if (!hlpfile->lpszTitle) return FALSE; 2041 strcpy(hlpfile->lpszTitle, str); 2042 WINE_TRACE("Title: %s\n", debugstr_a(hlpfile->lpszTitle)); 2043 /* Nothing more to parse */ 2044 return TRUE; 2045 } 2046 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4) 2047 { 2048 char *str = (char*) ptr + 4; 2049 switch (GET_USHORT(ptr, 0)) 2050 { 2051 case 1: 2052 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;} 2053 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1); 2054 if (!hlpfile->lpszTitle) return FALSE; 2055 strcpy(hlpfile->lpszTitle, str); 2056 WINE_TRACE("Title: %s\n", debugstr_a(hlpfile->lpszTitle)); 2057 break; 2058 2059 case 2: 2060 if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;} 2061 hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1); 2062 if (!hlpfile->lpszCopyright) return FALSE; 2063 strcpy(hlpfile->lpszCopyright, str); 2064 WINE_TRACE("Copyright: %s\n", debugstr_a(hlpfile->lpszCopyright)); 2065 break; 2066 2067 case 3: 2068 if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;} 2069 hlpfile->contents_start = GET_UINT(ptr, 4); 2070 WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start); 2071 break; 2072 2073 case 4: 2074 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + strlen(str) + 1); 2075 if (!macro) break; 2076 p = (char*)macro + sizeof(HLPFILE_MACRO); 2077 strcpy(p, str); 2078 macro->lpszMacro = p; 2079 macro->next = 0; 2080 for (m = &hlpfile->first_macro; *m; m = &(*m)->next); 2081 *m = macro; 2082 break; 2083 2084 case 5: 2085 if (GET_USHORT(ptr, 4 + 4) != 1) 2086 WINE_FIXME("More than one icon, picking up first\n"); 2087 /* 0x16 is sizeof(CURSORICONDIR), see user32/user_private.h */ 2088 hlpfile->hIcon = CreateIconFromResourceEx(ptr + 4 + 0x16, 2089 GET_USHORT(ptr, 2) - 0x16, TRUE, 2090 0x30000, 0, 0, 0); 2091 break; 2092 2093 case 6: 2094 if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;} 2095 2096 if (hlpfile->windows) 2097 hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows, 2098 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows); 2099 else 2100 hlpfile->windows = HeapAlloc(GetProcessHeap(), 0, 2101 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows); 2102 2103 if (hlpfile->windows) 2104 { 2105 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1]; 2106 2107 flags = GET_USHORT(ptr, 4); 2108 if (flags & 0x0001) strcpy(wi->type, &str[2]); 2109 else wi->type[0] = '\0'; 2110 if (flags & 0x0002) strcpy(wi->name, &str[12]); 2111 else wi->name[0] = '\0'; 2112 if (flags & 0x0004) strcpy(wi->caption, &str[21]); 2113 else lstrcpynA(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption)); 2114 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT; 2115 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT; 2116 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT; 2117 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT; 2118 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW; 2119 wi->win_style = WS_OVERLAPPEDWINDOW; 2120 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF; 2121 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF; 2122 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", 2123 flags & 0x0001 ? 'T' : 't', 2124 flags & 0x0002 ? 'N' : 'n', 2125 flags & 0x0004 ? 'C' : 'c', 2126 flags & 0x0008 ? 'X' : 'x', 2127 flags & 0x0010 ? 'Y' : 'y', 2128 flags & 0x0020 ? 'W' : 'w', 2129 flags & 0x0040 ? 'H' : 'h', 2130 flags & 0x0080 ? 'S' : 's', 2131 debugstr_a(wi->type), debugstr_a(wi->name), debugstr_a(wi->caption), wi->origin.x, wi->origin.y, 2132 wi->size.cx, wi->size.cy); 2133 } 2134 break; 2135 case 8: 2136 WINE_WARN("Citation: %s\n", debugstr_a((char *)ptr + 4)); 2137 break; 2138 case 11: 2139 hlpfile->charset = ptr[4]; 2140 WINE_TRACE("Charset: %d\n", hlpfile->charset); 2141 break; 2142 default: 2143 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0)); 2144 } 2145 } 2146 if (!hlpfile->lpszTitle) 2147 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1); 2148 return TRUE; 2149 } 2150 2151 /*********************************************************************** 2152 * 2153 * HLPFILE_GetContext 2154 */ 2155 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile) 2156 { 2157 BYTE *cbuf, *cend; 2158 unsigned clen; 2159 2160 if (!HLPFILE_FindSubFile(hlpfile, "|CONTEXT", &cbuf, &cend)) 2161 {WINE_WARN("context0\n"); return FALSE;} 2162 2163 clen = cend - cbuf; 2164 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen); 2165 if (!hlpfile->Context) return FALSE; 2166 memcpy(hlpfile->Context, cbuf, clen); 2167 2168 return TRUE; 2169 } 2170 2171 /*********************************************************************** 2172 * 2173 * HLPFILE_GetKeywords 2174 */ 2175 static BOOL HLPFILE_GetKeywords(HLPFILE *hlpfile) 2176 { 2177 BYTE *cbuf, *cend; 2178 unsigned clen; 2179 2180 if (!HLPFILE_FindSubFile(hlpfile, "|KWBTREE", &cbuf, &cend)) return FALSE; 2181 clen = cend - cbuf; 2182 hlpfile->kwbtree = HeapAlloc(GetProcessHeap(), 0, clen); 2183 if (!hlpfile->kwbtree) return FALSE; 2184 memcpy(hlpfile->kwbtree, cbuf, clen); 2185 2186 if (!HLPFILE_FindSubFile(hlpfile, "|KWDATA", &cbuf, &cend)) 2187 { 2188 WINE_ERR("corrupted help file: kwbtree present but kwdata absent\n"); 2189 HeapFree(GetProcessHeap(), 0, hlpfile->kwbtree); 2190 return FALSE; 2191 } 2192 clen = cend - cbuf; 2193 hlpfile->kwdata = HeapAlloc(GetProcessHeap(), 0, clen); 2194 if (!hlpfile->kwdata) 2195 { 2196 HeapFree(GetProcessHeap(), 0, hlpfile->kwdata); 2197 return FALSE; 2198 } 2199 memcpy(hlpfile->kwdata, cbuf, clen); 2200 2201 return TRUE; 2202 } 2203 2204 /*********************************************************************** 2205 * 2206 * HLPFILE_GetMap 2207 */ 2208 static BOOL HLPFILE_GetMap(HLPFILE *hlpfile) 2209 { 2210 BYTE *cbuf, *cend; 2211 unsigned entries, i; 2212 2213 if (!HLPFILE_FindSubFile(hlpfile, "|CTXOMAP", &cbuf, &cend)) 2214 {WINE_WARN("no map section\n"); return FALSE;} 2215 2216 entries = GET_USHORT(cbuf, 9); 2217 hlpfile->Map = HeapAlloc(GetProcessHeap(), 0, entries * sizeof(HLPFILE_MAP)); 2218 if (!hlpfile->Map) return FALSE; 2219 hlpfile->wMapLen = entries; 2220 for (i = 0; i < entries; i++) 2221 { 2222 hlpfile->Map[i].lMap = GET_UINT(cbuf+11,i*8); 2223 hlpfile->Map[i].offset = GET_UINT(cbuf+11,i*8+4); 2224 } 2225 return TRUE; 2226 } 2227 2228 /*********************************************************************** 2229 * 2230 * HLPFILE_GetTOMap 2231 */ 2232 static BOOL HLPFILE_GetTOMap(HLPFILE *hlpfile) 2233 { 2234 BYTE *cbuf, *cend; 2235 unsigned clen; 2236 2237 if (!HLPFILE_FindSubFile(hlpfile, "|TOMAP", &cbuf, &cend)) 2238 {WINE_WARN("no tomap section\n"); return FALSE;} 2239 2240 clen = cend - cbuf - 9; 2241 hlpfile->TOMap = HeapAlloc(GetProcessHeap(), 0, clen); 2242 if (!hlpfile->TOMap) return FALSE; 2243 memcpy(hlpfile->TOMap, cbuf+9, clen); 2244 hlpfile->wTOMapLen = clen/4; 2245 return TRUE; 2246 } 2247 2248 /*********************************************************************** 2249 * 2250 * DeleteMacro 2251 */ 2252 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro) 2253 { 2254 HLPFILE_MACRO* next; 2255 2256 while (macro) 2257 { 2258 next = macro->next; 2259 HeapFree(GetProcessHeap(), 0, macro); 2260 macro = next; 2261 } 2262 } 2263 2264 /*********************************************************************** 2265 * 2266 * DeletePage 2267 */ 2268 static void HLPFILE_DeletePage(HLPFILE_PAGE* page) 2269 { 2270 HLPFILE_PAGE* next; 2271 2272 while (page) 2273 { 2274 next = page->next; 2275 HLPFILE_DeleteMacro(page->first_macro); 2276 HeapFree(GetProcessHeap(), 0, page); 2277 page = next; 2278 } 2279 } 2280 2281 /*********************************************************************** 2282 * 2283 * HLPFILE_FreeHlpFile 2284 */ 2285 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile) 2286 { 2287 unsigned i; 2288 2289 if (!hlpfile || --hlpfile->wRefCount > 0) return; 2290 2291 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev; 2292 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next; 2293 else first_hlpfile = hlpfile->next; 2294 2295 if (hlpfile->numFonts) 2296 { 2297 for (i = 0; i < hlpfile->numFonts; i++) 2298 { 2299 DeleteObject(hlpfile->fonts[i].hFont); 2300 } 2301 HeapFree(GetProcessHeap(), 0, hlpfile->fonts); 2302 } 2303 2304 if (hlpfile->numBmps) 2305 { 2306 for (i = 0; i < hlpfile->numBmps; i++) 2307 { 2308 DeleteObject(hlpfile->bmps[i]); 2309 } 2310 HeapFree(GetProcessHeap(), 0, hlpfile->bmps); 2311 } 2312 2313 HLPFILE_DeletePage(hlpfile->first_page); 2314 HLPFILE_DeleteMacro(hlpfile->first_macro); 2315 2316 DestroyIcon(hlpfile->hIcon); 2317 if (hlpfile->numWindows) HeapFree(GetProcessHeap(), 0, hlpfile->windows); 2318 HeapFree(GetProcessHeap(), 0, hlpfile->Context); 2319 HeapFree(GetProcessHeap(), 0, hlpfile->Map); 2320 HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle); 2321 HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright); 2322 HeapFree(GetProcessHeap(), 0, hlpfile->file_buffer); 2323 HeapFree(GetProcessHeap(), 0, hlpfile->phrases_offsets); 2324 HeapFree(GetProcessHeap(), 0, hlpfile->phrases_buffer); 2325 HeapFree(GetProcessHeap(), 0, hlpfile->topic_map); 2326 HeapFree(GetProcessHeap(), 0, hlpfile->help_on_file); 2327 HeapFree(GetProcessHeap(), 0, hlpfile); 2328 } 2329 2330 /*********************************************************************** 2331 * 2332 * HLPFILE_UncompressLZ77_Phrases 2333 */ 2334 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile) 2335 { 2336 UINT i, num, dec_size, head_size; 2337 BYTE *buf, *end; 2338 2339 if (!HLPFILE_FindSubFile(hlpfile, "|Phrases", &buf, &end)) return FALSE; 2340 2341 if (hlpfile->version <= 16) 2342 head_size = 13; 2343 else 2344 head_size = 17; 2345 2346 num = hlpfile->num_phrases = GET_USHORT(buf, 9); 2347 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;}; 2348 2349 if (hlpfile->version <= 16) 2350 dec_size = end - buf - 15 - 2 * num; 2351 else 2352 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end); 2353 2354 hlpfile->phrases_offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1)); 2355 hlpfile->phrases_buffer = HeapAlloc(GetProcessHeap(), 0, dec_size); 2356 if (!hlpfile->phrases_offsets || !hlpfile->phrases_buffer) 2357 { 2358 HeapFree(GetProcessHeap(), 0, hlpfile->phrases_offsets); 2359 HeapFree(GetProcessHeap(), 0, hlpfile->phrases_buffer); 2360 return FALSE; 2361 } 2362 2363 for (i = 0; i <= num; i++) 2364 hlpfile->phrases_offsets[i] = GET_USHORT(buf, head_size + 2 * i) - 2 * num - 2; 2365 2366 if (hlpfile->version <= 16) 2367 memcpy(hlpfile->phrases_buffer, buf + 15 + 2*num, dec_size); 2368 else 2369 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, (BYTE*)hlpfile->phrases_buffer); 2370 2371 hlpfile->hasPhrases = TRUE; 2372 return TRUE; 2373 } 2374 2375 /*********************************************************************** 2376 * 2377 * HLPFILE_Uncompress_Phrases40 2378 */ 2379 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile) 2380 { 2381 UINT num; 2382 INT dec_size, cpr_size; 2383 BYTE *buf_idx, *end_idx; 2384 BYTE *buf_phs, *end_phs; 2385 ULONG* ptr, mask = 0; 2386 unsigned int i; 2387 unsigned short bc, n; 2388 2389 if (!HLPFILE_FindSubFile(hlpfile, "|PhrIndex", &buf_idx, &end_idx) || 2390 !HLPFILE_FindSubFile(hlpfile, "|PhrImage", &buf_phs, &end_phs)) return FALSE; 2391 2392 ptr = (ULONG*)(buf_idx + 9 + 28); 2393 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F; 2394 num = hlpfile->num_phrases = GET_USHORT(buf_idx, 9 + 4); 2395 2396 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n" 2397 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n", 2398 GET_UINT(buf_idx, 9 + 0), 2399 GET_UINT(buf_idx, 9 + 4), 2400 GET_UINT(buf_idx, 9 + 8), 2401 GET_UINT(buf_idx, 9 + 12), 2402 GET_UINT(buf_idx, 9 + 16), 2403 GET_UINT(buf_idx, 9 + 20), 2404 GET_USHORT(buf_idx, 9 + 24), 2405 GET_USHORT(buf_idx, 9 + 26)); 2406 2407 dec_size = GET_UINT(buf_idx, 9 + 12); 2408 cpr_size = GET_UINT(buf_idx, 9 + 16); 2409 2410 if (dec_size != cpr_size && 2411 dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs)) 2412 { 2413 WINE_WARN("size mismatch %u %u\n", 2414 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs)); 2415 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs)); 2416 } 2417 2418 hlpfile->phrases_offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1)); 2419 hlpfile->phrases_buffer = HeapAlloc(GetProcessHeap(), 0, dec_size); 2420 if (!hlpfile->phrases_offsets || !hlpfile->phrases_buffer) 2421 { 2422 HeapFree(GetProcessHeap(), 0, hlpfile->phrases_offsets); 2423 HeapFree(GetProcessHeap(), 0, hlpfile->phrases_buffer); 2424 return FALSE; 2425 } 2426 2427 #define getbit() ((mask <<= 1) ? (*ptr & mask) != 0: (*++ptr & (mask=1)) != 0) 2428 2429 hlpfile->phrases_offsets[0] = 0; 2430 ptr--; /* as we'll first increment ptr because mask is 0 on first getbit() call */ 2431 for (i = 0; i < num; i++) 2432 { 2433 for (n = 1; getbit(); n += 1 << bc); 2434 if (getbit()) n++; 2435 if (bc > 1 && getbit()) n += 2; 2436 if (bc > 2 && getbit()) n += 4; 2437 if (bc > 3 && getbit()) n += 8; 2438 if (bc > 4 && getbit()) n += 16; 2439 hlpfile->phrases_offsets[i + 1] = hlpfile->phrases_offsets[i] + n; 2440 } 2441 #undef getbit 2442 2443 if (dec_size == cpr_size) 2444 memcpy(hlpfile->phrases_buffer, buf_phs + 9, dec_size); 2445 else 2446 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, (BYTE*)hlpfile->phrases_buffer); 2447 2448 hlpfile->hasPhrases40 = TRUE; 2449 return TRUE; 2450 } 2451 2452 /*********************************************************************** 2453 * 2454 * HLPFILE_Uncompress_Topic 2455 */ 2456 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile) 2457 { 2458 BYTE *buf, *ptr, *end, *newptr; 2459 unsigned int i, newsize = 0; 2460 unsigned int topic_size; 2461 2462 if (!HLPFILE_FindSubFile(hlpfile, "|TOPIC", &buf, &end)) 2463 {WINE_WARN("topic0\n"); return FALSE;} 2464 2465 buf += 9; /* Skip file header */ 2466 topic_size = end - buf; 2467 if (hlpfile->compressed) 2468 { 2469 hlpfile->topic_maplen = (topic_size - 1) / hlpfile->tbsize + 1; 2470 2471 for (i = 0; i < hlpfile->topic_maplen; i++) 2472 { 2473 ptr = buf + i * hlpfile->tbsize; 2474 2475 /* I don't know why, it's necessary for printman.hlp */ 2476 if (ptr + 0x44 > end) ptr = end - 0x44; 2477 2478 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + hlpfile->tbsize)); 2479 } 2480 2481 hlpfile->topic_map = HeapAlloc(GetProcessHeap(), 0, 2482 hlpfile->topic_maplen * sizeof(hlpfile->topic_map[0]) + newsize); 2483 if (!hlpfile->topic_map) return FALSE; 2484 newptr = (BYTE*)(hlpfile->topic_map + hlpfile->topic_maplen); 2485 hlpfile->topic_end = newptr + newsize; 2486 2487 for (i = 0; i < hlpfile->topic_maplen; i++) 2488 { 2489 ptr = buf + i * hlpfile->tbsize; 2490 if (ptr + 0x44 > end) ptr = end - 0x44; 2491 2492 hlpfile->topic_map[i] = newptr; 2493 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + hlpfile->tbsize), newptr); 2494 } 2495 } 2496 else 2497 { 2498 /* basically, we need to copy the TopicBlockSize byte pages 2499 * (removing the first 0x0C) in one single area in memory 2500 */ 2501 hlpfile->topic_maplen = (topic_size - 1) / hlpfile->tbsize + 1; 2502 hlpfile->topic_map = HeapAlloc(GetProcessHeap(), 0, 2503 hlpfile->topic_maplen * (sizeof(hlpfile->topic_map[0]) + hlpfile->dsize)); 2504 if (!hlpfile->topic_map) return FALSE; 2505 newptr = (BYTE*)(hlpfile->topic_map + hlpfile->topic_maplen); 2506 hlpfile->topic_end = newptr + topic_size; 2507 2508 for (i = 0; i < hlpfile->topic_maplen; i++) 2509 { 2510 hlpfile->topic_map[i] = newptr + i * hlpfile->dsize; 2511 memcpy(hlpfile->topic_map[i], buf + i * hlpfile->tbsize + 0x0C, hlpfile->dsize); 2512 } 2513 } 2514 return TRUE; 2515 } 2516 2517 /*********************************************************************** 2518 * 2519 * HLPFILE_AddPage 2520 */ 2521 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, const BYTE *buf, const BYTE *end, unsigned ref, unsigned offset) 2522 { 2523 HLPFILE_PAGE* page; 2524 const BYTE* title; 2525 UINT titlesize, blocksize, datalen; 2526 char* ptr; 2527 HLPFILE_MACRO*macro; 2528 2529 blocksize = GET_UINT(buf, 0); 2530 datalen = GET_UINT(buf, 0x10); 2531 title = buf + datalen; 2532 if (title > end) {WINE_WARN("page2\n"); return FALSE;}; 2533 2534 titlesize = GET_UINT(buf, 4); 2535 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1); 2536 if (!page) return FALSE; 2537 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE); 2538 2539 if (titlesize > blocksize - datalen) 2540 { 2541 /* need to decompress */ 2542 if (hlpfile->hasPhrases) 2543 HLPFILE_Uncompress2(hlpfile, title, end, (BYTE*)page->lpszTitle, (BYTE*)page->lpszTitle + titlesize); 2544 else if (hlpfile->hasPhrases40) 2545 HLPFILE_Uncompress3(hlpfile, page->lpszTitle, page->lpszTitle + titlesize, title, end); 2546 else 2547 { 2548 WINE_FIXME("Text size is too long, splitting\n"); 2549 titlesize = blocksize - datalen; 2550 memcpy(page->lpszTitle, title, titlesize); 2551 } 2552 } 2553 else 2554 memcpy(page->lpszTitle, title, titlesize); 2555 2556 page->lpszTitle[titlesize] = '\0'; 2557 2558 if (hlpfile->first_page) 2559 { 2560 hlpfile->last_page->next = page; 2561 page->prev = hlpfile->last_page; 2562 hlpfile->last_page = page; 2563 } 2564 else 2565 { 2566 hlpfile->first_page = page; 2567 hlpfile->last_page = page; 2568 page->prev = NULL; 2569 } 2570 2571 page->file = hlpfile; 2572 page->next = NULL; 2573 page->first_macro = NULL; 2574 page->first_link = NULL; 2575 page->wNumber = GET_UINT(buf, 0x21); 2576 page->offset = offset; 2577 page->reference = ref; 2578 2579 page->browse_bwd = GET_UINT(buf, 0x19); 2580 page->browse_fwd = GET_UINT(buf, 0x1D); 2581 2582 if (hlpfile->version <= 16) 2583 { 2584 if (page->browse_bwd == 0xFFFF || page->browse_bwd == 0xFFFFFFFF) 2585 page->browse_bwd = 0xFFFFFFFF; 2586 else 2587 page->browse_bwd = hlpfile->TOMap[page->browse_bwd]; 2588 2589 if (page->browse_fwd == 0xFFFF || page->browse_fwd == 0xFFFFFFFF) 2590 page->browse_fwd = 0xFFFFFFFF; 2591 else 2592 page->browse_fwd = hlpfile->TOMap[page->browse_fwd]; 2593 } 2594 2595 WINE_TRACE("Added page[%d]: title=%s %08x << %08x >> %08x\n", 2596 page->wNumber, debugstr_a(page->lpszTitle), 2597 page->browse_bwd, page->offset, page->browse_fwd); 2598 2599 /* now load macros */ 2600 ptr = page->lpszTitle + strlen(page->lpszTitle) + 1; 2601 while (ptr < page->lpszTitle + titlesize) 2602 { 2603 unsigned len = strlen(ptr); 2604 char* macro_str; 2605 2606 WINE_TRACE("macro: %s\n", debugstr_a(ptr)); 2607 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1); 2608 macro->lpszMacro = macro_str = (char*)(macro + 1); 2609 memcpy(macro_str, ptr, len + 1); 2610 /* FIXME: shall we really link macro in reverse order ?? 2611 * may produce strange results when played at page opening 2612 */ 2613 macro->next = page->first_macro; 2614 page->first_macro = macro; 2615 ptr += len + 1; 2616 } 2617 2618 return TRUE; 2619 } 2620 2621 /*********************************************************************** 2622 * 2623 * HLPFILE_SkipParagraph 2624 */ 2625 static BOOL HLPFILE_SkipParagraph(HLPFILE *hlpfile, const BYTE *buf, const BYTE *end, unsigned* len) 2626 { 2627 const BYTE *tmp; 2628 2629 if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;}; 2630 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;}; 2631 2632 tmp = buf + 0x15; 2633 if (buf[0x14] == HLP_DISPLAY || buf[0x14] == HLP_TABLE) 2634 { 2635 fetch_long(&tmp); 2636 *len = fetch_ushort(&tmp); 2637 } 2638 else *len = end-buf-15; 2639 2640 return TRUE; 2641 } 2642 2643 /*********************************************************************** 2644 * 2645 * HLPFILE_DoReadHlpFile 2646 */ 2647 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath) 2648 { 2649 BOOL ret; 2650 HFILE hFile; 2651 OFSTRUCT ofs; 2652 BYTE* buf; 2653 DWORD ref = 0x0C; 2654 unsigned index, old_index, offset, len, offs, topicoffset; 2655 2656 hFile = OpenFile(lpszPath, &ofs, OF_READ); 2657 if (hFile == HFILE_ERROR) return FALSE; 2658 2659 ret = HLPFILE_ReadFileToBuffer(hlpfile, hFile); 2660 _lclose(hFile); 2661 if (!ret) return FALSE; 2662 2663 if (!HLPFILE_SystemCommands(hlpfile)) return FALSE; 2664 2665 if (hlpfile->version <= 16 && !HLPFILE_GetTOMap(hlpfile)) return FALSE; 2666 2667 /* load phrases support */ 2668 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile)) 2669 HLPFILE_Uncompress_Phrases40(hlpfile); 2670 2671 if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE; 2672 if (!HLPFILE_ReadFont(hlpfile)) return FALSE; 2673 2674 old_index = -1; 2675 offs = 0; 2676 do 2677 { 2678 BYTE* end; 2679 2680 if (hlpfile->version <= 16) 2681 { 2682 index = (ref - 0x0C) / hlpfile->dsize; 2683 offset = (ref - 0x0C) % hlpfile->dsize; 2684 } 2685 else 2686 { 2687 index = (ref - 0x0C) >> 14; 2688 offset = (ref - 0x0C) & 0x3FFF; 2689 } 2690 2691 if (hlpfile->version <= 16 && index != old_index && old_index != -1) 2692 { 2693 /* we jumped to the next block, adjust pointers */ 2694 ref -= 12; 2695 offset -= 12; 2696 } 2697 2698 WINE_TRACE("ref=%08x => [%u/%u]\n", ref, index, offset); 2699 2700 if (index >= hlpfile->topic_maplen) {WINE_WARN("maplen\n"); break;} 2701 buf = hlpfile->topic_map[index] + offset; 2702 if (buf + 0x15 >= hlpfile->topic_end) {WINE_WARN("extra\n"); break;} 2703 end = min(buf + GET_UINT(buf, 0), hlpfile->topic_end); 2704 if (index != old_index) {offs = 0; old_index = index;} 2705 2706 switch (buf[0x14]) 2707 { 2708 case HLP_TOPICHDR: /* Topic Header */ 2709 if (hlpfile->version <= 16) 2710 topicoffset = ref + index * 12; 2711 else 2712 topicoffset = index * 0x8000 + offs; 2713 if (!HLPFILE_AddPage(hlpfile, buf, end, ref, topicoffset)) return FALSE; 2714 break; 2715 2716 case HLP_DISPLAY30: 2717 case HLP_DISPLAY: 2718 case HLP_TABLE: 2719 if (!HLPFILE_SkipParagraph(hlpfile, buf, end, &len)) return FALSE; 2720 offs += len; 2721 break; 2722 2723 default: 2724 WINE_ERR("buf[0x14] = %x\n", buf[0x14]); 2725 } 2726 2727 if (hlpfile->version <= 16) 2728 { 2729 ref += GET_UINT(buf, 0xc); 2730 if (GET_UINT(buf, 0xc) == 0) 2731 break; 2732 } 2733 else 2734 ref = GET_UINT(buf, 0xc); 2735 } while (ref != 0xffffffff); 2736 2737 HLPFILE_GetKeywords(hlpfile); 2738 HLPFILE_GetMap(hlpfile); 2739 if (hlpfile->version <= 16) return TRUE; 2740 return HLPFILE_GetContext(hlpfile); 2741 } 2742 2743 /*********************************************************************** 2744 * 2745 * HLPFILE_ReadHlpFile 2746 */ 2747 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath) 2748 { 2749 HLPFILE* hlpfile; 2750 2751 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next) 2752 { 2753 if (!strcmp(lpszPath, hlpfile->lpszPath)) 2754 { 2755 hlpfile->wRefCount++; 2756 return hlpfile; 2757 } 2758 } 2759 2760 hlpfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2761 sizeof(HLPFILE) + strlen(lpszPath) + 1); 2762 if (!hlpfile) return 0; 2763 2764 hlpfile->lpszPath = (char*)hlpfile + sizeof(HLPFILE); 2765 hlpfile->contents_start = 0xFFFFFFFF; 2766 hlpfile->next = first_hlpfile; 2767 hlpfile->wRefCount = 1; 2768 2769 strcpy(hlpfile->lpszPath, lpszPath); 2770 2771 first_hlpfile = hlpfile; 2772 if (hlpfile->next) hlpfile->next->prev = hlpfile; 2773 2774 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath)) 2775 { 2776 HLPFILE_FreeHlpFile(hlpfile); 2777 hlpfile = 0; 2778 } 2779 2780 return hlpfile; 2781 } 2782