Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Help Viewer |
| 3 | * |
| 4 | * Copyright 1996 Ulrich Schmid |
Alexandre Julliard | 0799c1a | 2002-03-09 23:29:33 +0000 | [diff] [blame] | 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2.1 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, write to the Free Software |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 19 | */ |
| 20 | |
| 21 | #include <stdio.h> |
Niels Kristian Bech Jensen | 52be93c | 2000-03-19 21:49:49 +0000 | [diff] [blame] | 22 | #include <string.h> |
Alexandre Julliard | 9ea19e5 | 1997-01-01 17:29:55 +0000 | [diff] [blame] | 23 | #include "windows.h" |
Niels Kristian Bech Jensen | 52be93c | 2000-03-19 21:49:49 +0000 | [diff] [blame] | 24 | #include "windowsx.h" |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 25 | #include "winhelp.h" |
| 26 | |
| 27 | static void Report(LPCSTR str) |
| 28 | { |
| 29 | #if 0 |
| 30 | fprintf(stderr, "%s\n", str); |
| 31 | #endif |
| 32 | } |
| 33 | |
| 34 | #define GET_USHORT(buffer, i)\ |
| 35 | (((BYTE)((buffer)[(i)]) + 0x100 * (BYTE)((buffer)[(i)+1]))) |
| 36 | #define GET_SHORT(buffer, i)\ |
| 37 | (((BYTE)((buffer)[(i)]) + 0x100 * (signed char)((buffer)[(i)+1]))) |
| 38 | #define GET_UINT(buffer, i)\ |
| 39 | GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i+2) |
| 40 | |
| 41 | static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR); |
| 42 | static BOOL HLPFILE_ReadFileToBuffer(HFILE); |
| 43 | static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**); |
| 44 | static VOID HLPFILE_SystemCommands(HLPFILE*); |
| 45 | static BOOL HLPFILE_Uncompress1_Phrases(); |
| 46 | static BOOL HLPFILE_Uncompress1_Topic(); |
| 47 | static BOOL HLPFILE_GetContext(HLPFILE*); |
| 48 | static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*); |
| 49 | static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*); |
| 50 | static UINT HLPFILE_Uncompressed2_Size(BYTE*, BYTE*); |
| 51 | static VOID HLPFILE_Uncompress2(BYTE**, BYTE*, BYTE*); |
| 52 | |
| 53 | static HLPFILE *first_hlpfile = 0; |
| 54 | static HGLOBAL hFileBuffer; |
| 55 | static BYTE *file_buffer; |
| 56 | |
| 57 | static struct |
| 58 | { |
| 59 | UINT num; |
| 60 | BYTE *buf; |
| 61 | HGLOBAL hBuffer; |
| 62 | } phrases; |
| 63 | |
| 64 | static struct |
| 65 | { |
| 66 | BYTE **map; |
| 67 | BYTE *end; |
| 68 | UINT wMapLen; |
| 69 | HGLOBAL hMap; |
| 70 | HGLOBAL hBuffer; |
| 71 | } topic; |
| 72 | |
| 73 | static struct |
| 74 | { |
| 75 | UINT bDebug; |
| 76 | UINT wFont; |
| 77 | UINT wIndent; |
| 78 | UINT wHSpace; |
| 79 | UINT wVSpace; |
| 80 | UINT wVBackSpace; |
| 81 | HLPFILE_LINK link; |
| 82 | } attributes; |
| 83 | |
| 84 | /*********************************************************************** |
| 85 | * |
| 86 | * HLPFILE_Contents |
| 87 | */ |
| 88 | |
| 89 | HLPFILE_PAGE *HLPFILE_Contents(LPCSTR lpszPath) |
| 90 | { |
| 91 | HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath); |
| 92 | |
| 93 | if (!hlpfile) return(0); |
| 94 | |
| 95 | return(hlpfile->first_page); |
| 96 | } |
| 97 | |
| 98 | /*********************************************************************** |
| 99 | * |
| 100 | * HLPFILE_PageByNumber |
| 101 | */ |
| 102 | |
| 103 | HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum) |
| 104 | { |
| 105 | HLPFILE_PAGE *page; |
| 106 | HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath); |
| 107 | |
| 108 | if (!hlpfile) return(0); |
| 109 | |
| 110 | for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--; |
| 111 | |
| 112 | return page; |
| 113 | } |
| 114 | |
| 115 | /*********************************************************************** |
| 116 | * |
| 117 | * HLPFILE_HlpFilePageByHash |
| 118 | */ |
| 119 | |
| 120 | HLPFILE_PAGE *HLPFILE_PageByHash(LPCSTR lpszPath, LONG lHash) |
| 121 | { |
| 122 | INT i; |
| 123 | UINT wNum; |
| 124 | HLPFILE_PAGE *page; |
| 125 | HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath); |
| 126 | |
| 127 | if (!hlpfile) return(0); |
| 128 | |
| 129 | for (i = 0; i < hlpfile->wContextLen; i++) |
| 130 | if (hlpfile->Context[i].lHash == lHash) break; |
| 131 | |
| 132 | if (i >= hlpfile->wContextLen) |
| 133 | { |
| 134 | HLPFILE_FreeHlpFile(hlpfile); |
| 135 | return(0); |
| 136 | } |
| 137 | |
| 138 | wNum = hlpfile->Context[i].wPage; |
| 139 | for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--; |
| 140 | |
| 141 | return page; |
| 142 | } |
| 143 | |
| 144 | /*********************************************************************** |
| 145 | * |
| 146 | * HLPFILE_Hash |
| 147 | */ |
| 148 | |
| 149 | LONG HLPFILE_Hash(LPCSTR lpszContext) |
| 150 | { |
| 151 | LONG lHash = 0; |
| 152 | CHAR c; |
| 153 | while((c = *lpszContext++)) |
| 154 | { |
| 155 | CHAR x = 0; |
| 156 | if (c >= 'A' && c <= 'Z') x = c - 'A' + 17; |
| 157 | if (c >= 'a' && c <= 'z') x = c - 'a' + 17; |
| 158 | if (c >= '1' && c <= '9') x = c - '0'; |
| 159 | if (c == '0') x = 10; |
| 160 | if (c == '.') x = 12; |
| 161 | if (c == '_') x = 13; |
| 162 | if (x) lHash = lHash * 43 + x; |
| 163 | } |
| 164 | return lHash; |
| 165 | } |
| 166 | |
| 167 | /*********************************************************************** |
| 168 | * |
| 169 | * HLPFILE_ReadHlpFile |
| 170 | */ |
| 171 | |
| 172 | HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath) |
| 173 | { |
| 174 | HGLOBAL hHlpFile; |
| 175 | HLPFILE *hlpfile; |
| 176 | |
| 177 | for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next) |
| 178 | if (!lstrcmp(hlpfile->lpszPath, lpszPath)) |
| 179 | { |
| 180 | hlpfile->wRefCount++; |
| 181 | return(hlpfile); |
| 182 | } |
| 183 | |
| 184 | hHlpFile = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE) + lstrlen(lpszPath) + 1); |
| 185 | if (!hHlpFile) return(0); |
| 186 | |
| 187 | hlpfile = GlobalLock(hHlpFile); |
| 188 | hlpfile->hSelf = hHlpFile; |
| 189 | hlpfile->wRefCount = 1; |
| 190 | hlpfile->hTitle = 0; |
| 191 | hlpfile->hContext = 0; |
| 192 | hlpfile->wContextLen = 0; |
| 193 | hlpfile->first_page = 0; |
| 194 | hlpfile->first_macro = 0; |
| 195 | hlpfile->prev = 0; |
| 196 | hlpfile->next = first_hlpfile; |
| 197 | first_hlpfile = hlpfile; |
| 198 | if (hlpfile->next) hlpfile->next->prev = hlpfile; |
| 199 | |
| 200 | hlpfile->lpszPath = GlobalLock(hHlpFile); |
| 201 | hlpfile->lpszPath += sizeof(HLPFILE); |
Alexandre Julliard | 18f92e7 | 1996-07-17 20:02:21 +0000 | [diff] [blame] | 202 | strcpy(hlpfile->lpszPath, lpszPath); |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 203 | |
| 204 | phrases.hBuffer = topic.hBuffer = hFileBuffer = 0; |
| 205 | |
| 206 | if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath)) |
| 207 | { |
| 208 | HLPFILE_FreeHlpFile(hlpfile); |
| 209 | hlpfile = 0; |
| 210 | } |
| 211 | |
| 212 | if (phrases.hBuffer) GlobalFree(phrases.hBuffer); |
| 213 | if (topic.hBuffer) GlobalFree(topic.hBuffer); |
| 214 | if (topic.hMap) GlobalFree(topic.hMap); |
| 215 | if (hFileBuffer) GlobalFree(hFileBuffer); |
| 216 | |
| 217 | return(hlpfile); |
| 218 | } |
| 219 | |
| 220 | /*********************************************************************** |
| 221 | * |
| 222 | * HLPFILE_DoReadHlpFile |
| 223 | */ |
| 224 | |
| 225 | static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath) |
| 226 | { |
| 227 | BOOL ret; |
| 228 | HFILE hFile; |
| 229 | OFSTRUCT ofs; |
| 230 | BYTE *buf; |
| 231 | |
| 232 | hFile=OpenFile(lpszPath, &ofs, OF_READ | OF_SEARCH); |
| 233 | if (hFile == HFILE_ERROR) return FALSE; |
| 234 | |
| 235 | ret = HLPFILE_ReadFileToBuffer(hFile); |
| 236 | _lclose(hFile); |
| 237 | if (!ret) return FALSE; |
| 238 | |
| 239 | HLPFILE_SystemCommands(hlpfile); |
| 240 | if (!HLPFILE_Uncompress1_Phrases()) return FALSE; |
| 241 | if (!HLPFILE_Uncompress1_Topic()) return FALSE; |
| 242 | |
| 243 | buf = topic.map[0] + 0xc; |
| 244 | while(buf + 0xc < topic.end) |
| 245 | { |
Francois Gouget | 6d77d3a | 2000-03-25 21:44:35 +0000 | [diff] [blame] | 246 | BYTE *end = min(buf + GET_UINT(buf, 0), topic.end); |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 247 | UINT next, index, offset; |
| 248 | |
| 249 | switch (buf[0x14]) |
| 250 | { |
| 251 | case 0x02: |
| 252 | if (!HLPFILE_AddPage(hlpfile, buf, end)) return(FALSE); |
| 253 | break; |
| 254 | |
| 255 | case 0x20: |
| 256 | if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE); |
| 257 | break; |
| 258 | |
| 259 | case 0x23: |
| 260 | if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE); |
| 261 | break; |
| 262 | |
| 263 | default: |
| 264 | fprintf(stderr, "buf[0x14] = %x\n", buf[0x14]); |
| 265 | } |
| 266 | |
| 267 | next = GET_UINT(buf, 0xc); |
| 268 | if (next == 0xffffffff) break; |
| 269 | |
| 270 | index = next >> 14; |
| 271 | offset = next & 0x3fff; |
| 272 | if (index > topic.wMapLen) {Report("maplen"); break;} |
| 273 | buf = topic.map[index] + offset; |
| 274 | } |
| 275 | |
| 276 | return(HLPFILE_GetContext(hlpfile)); |
| 277 | } |
| 278 | |
| 279 | /*********************************************************************** |
| 280 | * |
| 281 | * HLPFILE_AddPage |
| 282 | */ |
| 283 | |
| 284 | static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end) |
| 285 | { |
| 286 | HGLOBAL hPage; |
| 287 | HLPFILE_PAGE *page, **pageptr; |
| 288 | BYTE *title; |
| 289 | UINT titlesize; |
| 290 | |
| 291 | for (pageptr = &hlpfile->first_page; *pageptr; pageptr = &(*pageptr)->next) |
| 292 | /* Nothing */; |
| 293 | |
| 294 | if (buf + 0x31 > end) {Report("page1"); return(FALSE);}; |
| 295 | title = buf + GET_UINT(buf, 0x10); |
| 296 | if (title > end) {Report("page2"); return(FALSE);}; |
| 297 | |
| 298 | titlesize = HLPFILE_Uncompressed2_Size(title, end); |
| 299 | hPage = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_PAGE) + titlesize); |
| 300 | if (!hPage) return FALSE; |
| 301 | page = *pageptr = GlobalLock(hPage); |
| 302 | pageptr = &page->next; |
| 303 | page->hSelf = hPage; |
| 304 | page->file = hlpfile; |
| 305 | page->next = 0; |
| 306 | page->first_paragraph = 0; |
| 307 | |
| 308 | page->lpszTitle = GlobalLock(hPage); |
| 309 | page->lpszTitle += sizeof(HLPFILE_PAGE); |
| 310 | HLPFILE_Uncompress2(&title, end, page->lpszTitle); |
| 311 | |
| 312 | page->wNumber = GET_UINT(buf, 0x21); |
| 313 | |
| 314 | attributes.bDebug = 0; |
| 315 | attributes.wFont = 0; |
| 316 | attributes.wVSpace = 0; |
| 317 | attributes.wVBackSpace = 0; |
| 318 | attributes.wHSpace = 0; |
| 319 | attributes.wIndent = 0; |
| 320 | attributes.link.lpszPath = 0; |
| 321 | |
| 322 | return TRUE; |
| 323 | } |
| 324 | |
| 325 | /*********************************************************************** |
| 326 | * |
| 327 | * HLPFILE_AddParagraph |
| 328 | */ |
| 329 | |
| 330 | static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end) |
| 331 | { |
| 332 | HGLOBAL hParagraph; |
| 333 | HLPFILE_PAGE *page; |
| 334 | HLPFILE_PARAGRAPH *paragraph, **paragraphptr; |
| 335 | UINT textsize; |
| 336 | BYTE *format, *text; |
| 337 | BOOL format_header = TRUE; |
| 338 | BOOL format_end = FALSE; |
| 339 | UINT mask, i; |
| 340 | |
| 341 | if (!hlpfile->first_page) {Report("paragraph1"); return(FALSE);}; |
| 342 | |
| 343 | for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */; |
| 344 | for (paragraphptr = &page->first_paragraph; *paragraphptr; |
| 345 | paragraphptr = &(*paragraphptr)->next) /* Nothing */; |
| 346 | |
| 347 | if (buf + 0x19 > end) {Report("paragraph2"); return(FALSE);}; |
| 348 | |
| 349 | if (buf[0x14] == 0x02) return TRUE; |
| 350 | |
| 351 | text = buf + GET_UINT(buf, 0x10); |
| 352 | |
| 353 | switch (buf[0x14]) |
| 354 | { |
| 355 | case 0x20: |
| 356 | format = buf + 0x18; |
| 357 | while (*format) format++; |
| 358 | format += 4; |
| 359 | break; |
| 360 | |
| 361 | case 0x23: |
| 362 | format = buf + 0x2b; |
| 363 | if (buf[0x17] & 1) format++; |
| 364 | break; |
| 365 | |
| 366 | default: |
| 367 | Report("paragraph3"); |
| 368 | return FALSE; |
| 369 | } |
| 370 | |
| 371 | while (text < end) |
| 372 | { |
| 373 | if (format_header) |
| 374 | { |
| 375 | format_header = FALSE; |
| 376 | |
| 377 | mask = GET_USHORT(format, 0); |
| 378 | mask &= 0x3ff; |
| 379 | format += 2; |
| 380 | |
| 381 | for (i = 0; i < 10; i++, mask = mask >> 1) |
| 382 | { |
| 383 | if (mask & 1) |
| 384 | { |
| 385 | BOOL twoargs = FALSE; |
| 386 | CHAR prefix0 = ' '; |
| 387 | CHAR prefix1 = '*'; |
| 388 | |
| 389 | if (i == 9 && !twoargs) |
| 390 | { |
| 391 | switch (*format++) |
| 392 | { |
| 393 | default: |
| 394 | prefix0 = prefix1 = '?'; |
| 395 | break; |
| 396 | |
| 397 | case 0x82: |
| 398 | prefix0 = prefix1 = 'x'; |
| 399 | break; |
| 400 | |
| 401 | case 0x84: |
| 402 | prefix0 = prefix1 = 'X'; |
| 403 | twoargs = TRUE; |
| 404 | } |
| 405 | } |
| 406 | |
| 407 | if (*format & 1) |
| 408 | switch(*format) |
| 409 | { |
| 410 | default: |
| 411 | format += 2; |
| 412 | break; |
| 413 | } |
| 414 | else |
| 415 | switch(*format) |
| 416 | { |
| 417 | |
| 418 | default: |
| 419 | format++; |
| 420 | break; |
| 421 | |
| 422 | case 0x08: |
| 423 | format += 3; |
| 424 | break; |
| 425 | } |
| 426 | |
| 427 | if (twoargs) format += (*format & 1) ? 2 : 1; |
| 428 | } |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | for (; !format_header && text < end && format < end && !*text; text++) |
| 433 | { |
| 434 | switch(*format) |
| 435 | { |
| 436 | case 0x80: |
| 437 | attributes.wFont = GET_USHORT(format, 1); |
| 438 | format += 3; |
| 439 | break; |
| 440 | |
| 441 | case 0x81: |
| 442 | attributes.wVSpace++; |
| 443 | format += 1; |
| 444 | break; |
| 445 | |
| 446 | case 0x82: |
| 447 | attributes.wVSpace += 2 - attributes.wVBackSpace; |
| 448 | attributes.wVBackSpace = 0; |
| 449 | attributes.wIndent = 0; |
| 450 | format += 1; |
| 451 | break; |
| 452 | |
| 453 | case 0x83: |
| 454 | attributes.wIndent++; |
| 455 | format += 1; |
| 456 | break; |
| 457 | |
| 458 | case 0x84: |
| 459 | format += 3; |
| 460 | break; |
| 461 | |
| 462 | case 0x86: |
| 463 | case 0x87: |
| 464 | case 0x88: |
| 465 | format += 9; |
| 466 | break; |
| 467 | |
| 468 | case 0x89: |
| 469 | attributes.wVBackSpace++; |
| 470 | format += 1; |
| 471 | break; |
| 472 | |
| 473 | case 0xa9: |
| 474 | format += 2; |
| 475 | break; |
| 476 | |
| 477 | case 0xe2: |
| 478 | case 0xe3: |
| 479 | attributes.link.lpszPath = hlpfile->lpszPath; |
| 480 | attributes.link.lHash = GET_UINT(format, 1); |
| 481 | attributes.link.bPopup = !(*format & 1); |
| 482 | format += 5; |
| 483 | break; |
| 484 | |
| 485 | case 0xea: |
| 486 | attributes.link.lpszPath = format + 8; |
| 487 | attributes.link.lHash = GET_UINT(format, 4); |
| 488 | attributes.link.bPopup = !(*format & 1); |
| 489 | format += 3 + GET_USHORT(format, 1); |
| 490 | break; |
| 491 | |
| 492 | case 0xff: |
| 493 | if (buf[0x14] != 0x23 || GET_USHORT(format, 1) == 0xffff) |
| 494 | { |
| 495 | if (format_end) Report("format_end"); |
| 496 | format_end = TRUE; |
| 497 | break; |
| 498 | } |
| 499 | else |
| 500 | { |
| 501 | format_header = TRUE; |
| 502 | format += 10; |
| 503 | break; |
| 504 | } |
| 505 | |
| 506 | default: |
| 507 | Report("format"); |
| 508 | format++; |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | if (text > end || format > end) {Report("paragraph_end"); return(FALSE);}; |
| 513 | if (text == end && !format_end) Report("text_end"); |
| 514 | |
| 515 | if (text == end) break; |
| 516 | |
| 517 | textsize = HLPFILE_Uncompressed2_Size(text, end); |
| 518 | hParagraph = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_PARAGRAPH) + textsize); |
| 519 | if (!hParagraph) return FALSE; |
| 520 | paragraph = *paragraphptr = GlobalLock(hParagraph); |
| 521 | paragraphptr = ¶graph->next; |
| 522 | paragraph->hSelf = hParagraph; |
| 523 | paragraph->next = 0; |
| 524 | paragraph->link = 0; |
| 525 | |
| 526 | paragraph->lpszText = GlobalLock(hParagraph); |
| 527 | paragraph->lpszText += sizeof(HLPFILE_PARAGRAPH); |
| 528 | HLPFILE_Uncompress2(&text, end, paragraph->lpszText); |
| 529 | |
| 530 | paragraph->bDebug = attributes.bDebug; |
| 531 | paragraph->wFont = attributes.wFont; |
| 532 | paragraph->wVSpace = attributes.wVSpace; |
| 533 | paragraph->wHSpace = attributes.wHSpace; |
| 534 | paragraph->wIndent = attributes.wIndent; |
| 535 | if (attributes.link.lpszPath) |
| 536 | { |
| 537 | LPSTR ptr; |
| 538 | HGLOBAL handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_LINK) + |
| 539 | strlen(attributes.link.lpszPath) + 1); |
| 540 | if (!handle) return FALSE; |
| 541 | paragraph->link = GlobalLock(handle); |
| 542 | paragraph->link->hSelf = handle; |
| 543 | |
| 544 | ptr = GlobalLock(handle); |
| 545 | ptr += sizeof(HLPFILE_LINK); |
| 546 | lstrcpy(ptr, (LPSTR) attributes.link.lpszPath); |
| 547 | |
| 548 | paragraph->link->lpszPath = ptr; |
| 549 | paragraph->link->lHash = attributes.link.lHash; |
| 550 | paragraph->link->bPopup = attributes.link.bPopup; |
| 551 | } |
| 552 | |
| 553 | attributes.bDebug = 0; |
| 554 | attributes.wVSpace = 0; |
| 555 | attributes.wHSpace = 0; |
| 556 | attributes.link.lpszPath = 0; |
| 557 | } |
| 558 | |
| 559 | return TRUE; |
| 560 | } |
| 561 | |
| 562 | /*********************************************************************** |
| 563 | * |
| 564 | * HLPFILE_ReadFileToBuffer |
| 565 | */ |
| 566 | |
| 567 | static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile) |
| 568 | { |
| 569 | BYTE header[16], dummy[1]; |
| 570 | UINT size; |
| 571 | |
| 572 | if (_hread(hFile, header, 16) != 16) {Report("header"); return(FALSE);}; |
| 573 | |
| 574 | size = GET_UINT(header, 12); |
| 575 | hFileBuffer = GlobalAlloc(GMEM_FIXED, size + 1); |
| 576 | if (!hFileBuffer) return FALSE; |
| 577 | file_buffer = GlobalLock(hFileBuffer); |
| 578 | |
| 579 | memcpy(file_buffer, header, 16); |
| 580 | if (_hread(hFile, file_buffer + 16, size - 16) != size - 16) |
| 581 | {Report("filesize1"); return(FALSE);}; |
| 582 | |
| 583 | if (_hread(hFile, dummy, 1) != 0) Report("filesize2"); |
| 584 | |
| 585 | file_buffer[size] = '0'; |
| 586 | |
| 587 | return TRUE; |
| 588 | } |
| 589 | |
| 590 | /*********************************************************************** |
| 591 | * |
| 592 | * HLPFILE_FindSubFile |
| 593 | */ |
| 594 | |
| 595 | static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend) |
| 596 | { |
| 597 | BYTE *root = file_buffer + GET_UINT(file_buffer, 4); |
| 598 | BYTE *end = file_buffer + GET_UINT(file_buffer, 12); |
| 599 | BYTE *ptr = root + 0x37; |
| 600 | |
| 601 | while (ptr < end && ptr[0] == 0x7c) |
| 602 | { |
| 603 | BYTE *fname = ptr + 1; |
| 604 | ptr += strlen(ptr) + 1; |
| 605 | if (!lstrcmpi(fname, name)) |
| 606 | { |
| 607 | *subbuf = file_buffer + GET_UINT(ptr, 0); |
| 608 | *subend = *subbuf + GET_UINT(*subbuf, 0); |
| 609 | if (file_buffer > *subbuf || *subbuf > *subend || *subend >= end) |
| 610 | { |
| 611 | Report("subfile"); |
| 612 | return FALSE; |
| 613 | } |
| 614 | return TRUE; |
| 615 | } |
| 616 | else ptr += 4; |
| 617 | } |
| 618 | return FALSE; |
| 619 | } |
| 620 | |
| 621 | /*********************************************************************** |
| 622 | * |
| 623 | * HLPFILE_SystemCommands |
| 624 | */ |
| 625 | static VOID HLPFILE_SystemCommands(HLPFILE* hlpfile) |
| 626 | { |
| 627 | BYTE *buf, *ptr, *end; |
| 628 | HGLOBAL handle; |
| 629 | HLPFILE_MACRO *macro, **m; |
| 630 | LPSTR p; |
| 631 | |
| 632 | hlpfile->lpszTitle = ""; |
| 633 | |
| 634 | if (!HLPFILE_FindSubFile("SYSTEM", &buf, &end)) return; |
| 635 | |
| 636 | for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4) |
| 637 | { |
| 638 | switch (GET_USHORT(ptr, 0)) |
| 639 | { |
| 640 | case 1: |
| 641 | if (hlpfile->hTitle) {Report("title"); break;} |
| 642 | hlpfile->hTitle = GlobalAlloc(GMEM_FIXED, strlen(ptr + 4) + 1); |
| 643 | if (!hlpfile->hTitle) return; |
| 644 | hlpfile->lpszTitle = GlobalLock(hlpfile->hTitle); |
| 645 | lstrcpy(hlpfile->lpszTitle, ptr + 4); |
| 646 | break; |
| 647 | |
| 648 | case 2: |
| 649 | if (GET_USHORT(ptr, 2) != 1 || ptr[4] != 0) Report("system2"); |
| 650 | break; |
| 651 | |
| 652 | case 3: |
| 653 | if (GET_USHORT(ptr, 2) != 4 || GET_UINT(ptr, 4) != 0) Report("system3"); |
| 654 | break; |
| 655 | |
| 656 | case 4: |
| 657 | handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_MACRO) + lstrlen(ptr + 4) + 1); |
| 658 | if (!handle) break; |
| 659 | macro = GlobalLock(handle); |
| 660 | macro->hSelf = handle; |
| 661 | p = GlobalLock(handle); |
| 662 | p += sizeof(HLPFILE_MACRO); |
| 663 | lstrcpy(p, (LPSTR) ptr + 4); |
| 664 | macro->lpszMacro = p; |
| 665 | macro->next = 0; |
Alexandre Julliard | 566a52a | 2001-03-05 19:34:17 +0000 | [diff] [blame] | 666 | m = &hlpfile->first_macro; |
| 667 | while (*m) m = &(*m)->next; |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 668 | *m = macro; |
| 669 | break; |
| 670 | |
| 671 | default: |
| 672 | Report("system"); |
| 673 | } |
| 674 | } |
| 675 | } |
| 676 | |
| 677 | /*********************************************************************** |
| 678 | * |
| 679 | * HLPFILE_Uncompressed1_Size |
| 680 | */ |
| 681 | |
| 682 | static INT HLPFILE_Uncompressed1_Size(BYTE *ptr, BYTE *end) |
| 683 | { |
| 684 | INT i, newsize = 0; |
| 685 | |
| 686 | while (ptr < end) |
| 687 | { |
| 688 | INT mask=*ptr++; |
| 689 | for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1) |
| 690 | { |
| 691 | if (mask & 1) |
| 692 | { |
| 693 | INT code = GET_USHORT(ptr, 0); |
| 694 | INT len = 3 + (code >> 12); |
| 695 | newsize += len; |
| 696 | ptr += 2; |
| 697 | } |
| 698 | else newsize++, ptr++; |
| 699 | } |
| 700 | } |
| 701 | |
| 702 | return(newsize); |
| 703 | } |
| 704 | |
| 705 | /*********************************************************************** |
| 706 | * |
| 707 | * HLPFILE_Uncompress1 |
| 708 | */ |
| 709 | |
| 710 | static BYTE *HLPFILE_Uncompress1(BYTE *ptr, BYTE *end, BYTE *newptr) |
| 711 | { |
| 712 | INT i; |
| 713 | |
| 714 | while (ptr < end) |
| 715 | { |
| 716 | INT mask=*ptr++; |
| 717 | for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1) |
| 718 | { |
| 719 | if (mask & 1) |
| 720 | { |
| 721 | INT code = GET_USHORT(ptr, 0); |
| 722 | INT len = 3 + (code >> 12); |
| 723 | INT offset = code & 0xfff; |
Rein Klazes | 9cc9d59 | 2000-06-20 20:34:11 +0000 | [diff] [blame] | 724 | memcpy(newptr, newptr - offset - 1, len); |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 725 | newptr += len; |
| 726 | ptr += 2; |
| 727 | } |
| 728 | else *newptr++ = *ptr++; |
| 729 | } |
| 730 | } |
| 731 | |
| 732 | return(newptr); |
| 733 | } |
| 734 | |
| 735 | /*********************************************************************** |
| 736 | * |
| 737 | * HLPFILE_Uncompress1_Phrases |
| 738 | */ |
| 739 | |
| 740 | static BOOL HLPFILE_Uncompress1_Phrases() |
| 741 | { |
| 742 | UINT i, num, newsize; |
| 743 | BYTE *buf, *end, *newbuf; |
| 744 | |
| 745 | if (!HLPFILE_FindSubFile("Phrases", &buf, &end)) {Report("phrases0"); return FALSE;} |
| 746 | |
| 747 | num = phrases.num = GET_USHORT(buf, 9); |
| 748 | if (buf + 2 * num + 0x13 >= end) {Report("uncompress1a"); return(FALSE);}; |
| 749 | |
| 750 | newsize = 2 * num + 2; |
| 751 | newsize += HLPFILE_Uncompressed1_Size(buf + 0x13 + 2 * num, end); |
| 752 | phrases.hBuffer = GlobalAlloc(GMEM_FIXED, newsize); |
| 753 | if (!phrases.hBuffer) return FALSE; |
| 754 | newbuf = phrases.buf = GlobalLock(phrases.hBuffer); |
| 755 | |
Rein Klazes | 9cc9d59 | 2000-06-20 20:34:11 +0000 | [diff] [blame] | 756 | memcpy(newbuf, buf + 0x11, 2 * num + 2); |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 757 | HLPFILE_Uncompress1(buf + 0x13 + 2 * num, end, newbuf + 2 * num + 2); |
| 758 | |
| 759 | for (i = 0; i < num; i++) |
| 760 | { |
| 761 | INT i0 = GET_USHORT(newbuf, 2 * i); |
| 762 | INT i1 = GET_USHORT(newbuf, 2 * i + 2); |
| 763 | if (i1 < i0 || i1 > newsize) {Report("uncompress1b"); return(FALSE);}; |
| 764 | } |
| 765 | return TRUE; |
| 766 | } |
| 767 | |
| 768 | /*********************************************************************** |
| 769 | * |
| 770 | * HLPFILE_Uncompress1_Topic |
| 771 | */ |
| 772 | |
| 773 | static BOOL HLPFILE_Uncompress1_Topic() |
| 774 | { |
| 775 | BYTE *buf, *ptr, *end, *newptr; |
| 776 | INT i, newsize = 0; |
| 777 | |
| 778 | if (!HLPFILE_FindSubFile("TOPIC", &buf, &end)) {Report("topic0"); return FALSE;} |
| 779 | |
| 780 | buf += 9; |
| 781 | topic.wMapLen = (end - buf - 1) / 0x1000 + 1; |
| 782 | |
| 783 | for (i = 0; i < topic.wMapLen; i++) |
| 784 | { |
| 785 | ptr = buf + i * 0x1000; |
| 786 | |
| 787 | /* I don't know why, it's necessary for printman.hlp */ |
| 788 | if (ptr + 0x44 > end) ptr = end - 0x44; |
| 789 | |
Francois Gouget | 6d77d3a | 2000-03-25 21:44:35 +0000 | [diff] [blame] | 790 | newsize += HLPFILE_Uncompressed1_Size(ptr + 0xc, min(end, ptr + 0x1000)); |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 791 | } |
| 792 | |
| 793 | topic.hMap = GlobalAlloc(GMEM_FIXED, topic.wMapLen * sizeof(topic.map[0])); |
| 794 | topic.hBuffer = GlobalAlloc(GMEM_FIXED, newsize); |
| 795 | if (!topic.hMap || !topic.hBuffer) return FALSE; |
| 796 | topic.map = GlobalLock(topic.hMap); |
| 797 | newptr = GlobalLock(topic.hBuffer); |
| 798 | topic.end = newptr + newsize; |
| 799 | |
| 800 | for (i = 0; i < topic.wMapLen; i++) |
| 801 | { |
| 802 | ptr = buf + i * 0x1000; |
| 803 | if (ptr + 0x44 > end) ptr = end - 0x44; |
| 804 | |
| 805 | topic.map[i] = newptr - 0xc; |
Francois Gouget | 6d77d3a | 2000-03-25 21:44:35 +0000 | [diff] [blame] | 806 | newptr = HLPFILE_Uncompress1(ptr + 0xc, min(end, ptr + 0x1000), newptr); |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 807 | } |
| 808 | |
| 809 | return TRUE; |
| 810 | } |
| 811 | |
| 812 | /*********************************************************************** |
| 813 | * |
| 814 | * HLPFILE_Uncompressed2_Size |
| 815 | */ |
| 816 | |
| 817 | static UINT HLPFILE_Uncompressed2_Size(BYTE *ptr, BYTE *end) |
| 818 | { |
| 819 | UINT wSize = 0; |
| 820 | |
| 821 | while (ptr < end && *ptr) |
| 822 | { |
| 823 | if (*ptr >= 0x20) |
| 824 | wSize++, ptr++; |
| 825 | else |
| 826 | { |
| 827 | BYTE *phptr, *phend; |
| 828 | UINT code = 0x100 * ptr[0] + ptr[1]; |
| 829 | UINT index = (code - 0x100) / 2; |
| 830 | BOOL space = code & 1; |
| 831 | |
| 832 | if (index < phrases.num) |
| 833 | { |
| 834 | phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index); |
| 835 | phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2); |
| 836 | |
| 837 | if (phend < phptr) Report("uncompress2a"); |
| 838 | |
| 839 | wSize += phend - phptr; |
| 840 | if (space) wSize++; |
| 841 | } |
| 842 | else Report("uncompress2b"); |
| 843 | |
| 844 | ptr += 2; |
| 845 | } |
| 846 | } |
| 847 | |
| 848 | return(wSize + 1); |
| 849 | } |
| 850 | |
| 851 | /*********************************************************************** |
| 852 | * |
| 853 | * HLPFILE_Uncompress2 |
| 854 | */ |
| 855 | |
| 856 | static VOID HLPFILE_Uncompress2(BYTE **pptr, BYTE *end, BYTE *newptr) |
| 857 | { |
| 858 | BYTE *ptr = *pptr; |
| 859 | |
| 860 | while (ptr < end && *ptr) |
| 861 | { |
| 862 | if (*ptr >= 0x20) |
| 863 | *newptr++ = *ptr++; |
| 864 | else |
| 865 | { |
| 866 | BYTE *phptr, *phend; |
| 867 | UINT code = 0x100 * ptr[0] + ptr[1]; |
| 868 | UINT index = (code - 0x100) / 2; |
| 869 | BOOL space = code & 1; |
| 870 | |
| 871 | phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index); |
| 872 | phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2); |
| 873 | |
Rein Klazes | 9cc9d59 | 2000-06-20 20:34:11 +0000 | [diff] [blame] | 874 | memcpy(newptr, phptr, phend - phptr); |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 875 | newptr += phend - phptr; |
| 876 | if (space) *newptr++ = ' '; |
| 877 | |
| 878 | ptr += 2; |
| 879 | } |
| 880 | } |
| 881 | *newptr = '\0'; |
| 882 | *pptr = ptr; |
| 883 | } |
| 884 | |
| 885 | /*********************************************************************** |
| 886 | * |
| 887 | * HLPFILE_GetContext |
| 888 | */ |
| 889 | |
| 890 | static BOOL HLPFILE_GetContext(HLPFILE *hlpfile) |
| 891 | { |
| 892 | UINT i, j, clen, tlen; |
| 893 | BYTE *cbuf, *cptr, *cend, *tbuf, *tptr, *tend; |
| 894 | |
| 895 | if (!HLPFILE_FindSubFile("CONTEXT", &cbuf, &cend)) {Report("context0"); return FALSE;} |
| 896 | if (cbuf + 0x37 > cend) {Report("context1"); return(FALSE);}; |
| 897 | clen = GET_UINT(cbuf, 0x2b); |
| 898 | if (cbuf + 0x37 + 8 * hlpfile->wContextLen > cend) {Report("context2"); return(FALSE);}; |
| 899 | |
| 900 | if (!HLPFILE_FindSubFile("TTLBTREE", &tbuf, &tend)) {Report("ttlb0"); return FALSE;} |
| 901 | if (tbuf + 0x37 > tend) {Report("ttlb1"); return(FALSE);}; |
| 902 | tlen = GET_UINT(tbuf, 0x2b); |
| 903 | |
| 904 | hlpfile->hContext = GlobalAlloc(GMEM_FIXED, clen * sizeof(HLPFILE_CONTEXT)); |
| 905 | if (!hlpfile->hContext) return FALSE; |
| 906 | hlpfile->Context = GlobalLock(hlpfile->hContext); |
| 907 | hlpfile->wContextLen = clen; |
| 908 | |
| 909 | cptr = cbuf + 0x37; |
| 910 | for (i = 0; i < clen; i++, cptr += 8) |
| 911 | { |
| 912 | tptr = tbuf + 0x37; |
| 913 | for (j = 0; j < tlen; j++, tptr += 5 + strlen(tptr + 4)) |
| 914 | { |
| 915 | if (tptr + 4 >= tend) {Report("ttlb2"); return(FALSE);}; |
| 916 | if (GET_UINT(tptr, 0) == GET_UINT(cptr, 4)) break; |
| 917 | } |
| 918 | if (j >= tlen) |
| 919 | { |
| 920 | Report("ttlb3"); |
| 921 | j = 0; |
| 922 | } |
| 923 | hlpfile->Context[i].lHash = GET_UINT(cptr, 0); |
| 924 | hlpfile->Context[i].wPage = j; |
| 925 | } |
| 926 | |
| 927 | return TRUE; |
| 928 | } |
| 929 | |
| 930 | /*********************************************************************** |
| 931 | * |
| 932 | * HLPFILE_DeleteParagraph |
| 933 | */ |
| 934 | |
| 935 | static VOID HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph) |
| 936 | { |
| 937 | if (!paragraph) return; |
| 938 | |
| 939 | if (paragraph->link) GlobalFree(paragraph->link->hSelf); |
| 940 | |
| 941 | HLPFILE_DeleteParagraph(paragraph->next); |
| 942 | GlobalFree(paragraph->hSelf); |
| 943 | } |
| 944 | |
| 945 | /*********************************************************************** |
| 946 | * |
| 947 | * DeletePage |
| 948 | */ |
| 949 | |
| 950 | static VOID HLPFILE_DeletePage(HLPFILE_PAGE* page) |
| 951 | { |
| 952 | if (!page) return; |
| 953 | |
| 954 | HLPFILE_DeletePage(page->next); |
| 955 | HLPFILE_DeleteParagraph(page->first_paragraph); |
| 956 | GlobalFree(page->hSelf); |
| 957 | } |
| 958 | |
| 959 | /*********************************************************************** |
| 960 | * |
| 961 | * DeleteMacro |
| 962 | */ |
| 963 | |
| 964 | static VOID HLPFILE_DeleteMacro(HLPFILE_MACRO* macro) |
| 965 | { |
| 966 | if (!macro) return; |
| 967 | |
| 968 | HLPFILE_DeleteMacro(macro->next); |
| 969 | GlobalFree(macro->hSelf); |
| 970 | } |
| 971 | |
| 972 | /*********************************************************************** |
| 973 | * |
| 974 | * HLPFILE_FreeHlpFile |
| 975 | */ |
| 976 | |
| 977 | VOID HLPFILE_FreeHlpFile(HLPFILE* hlpfile) |
| 978 | { |
| 979 | if (!hlpfile) return; |
| 980 | if (--hlpfile->wRefCount) return; |
| 981 | |
| 982 | if (hlpfile->next) hlpfile->next->prev = hlpfile->prev; |
| 983 | if (hlpfile->prev) hlpfile->prev->next = hlpfile->next; |
| 984 | else first_hlpfile = 0; |
| 985 | |
| 986 | HLPFILE_DeletePage(hlpfile->first_page); |
| 987 | HLPFILE_DeleteMacro(hlpfile->first_macro); |
Alexandre Julliard | 2d93d00 | 1996-05-21 15:01:41 +0000 | [diff] [blame] | 988 | if (hlpfile->hContext) GlobalFree(hlpfile->hContext); |
| 989 | if (hlpfile->hTitle) GlobalFree(hlpfile->hTitle); |
Alexandre Julliard | 1285c2f | 1996-05-06 16:06:24 +0000 | [diff] [blame] | 990 | GlobalFree(hlpfile->hSelf); |
| 991 | } |
| 992 | |
| 993 | /*********************************************************************** |
| 994 | * |
| 995 | * FreeHlpFilePage |
| 996 | */ |
| 997 | |
| 998 | VOID HLPFILE_FreeHlpFilePage(HLPFILE_PAGE* page) |
| 999 | { |
| 1000 | if (!page) return; |
| 1001 | HLPFILE_FreeHlpFile(page->file); |
| 1002 | } |
| 1003 | |
| 1004 | /* Local Variables: */ |
| 1005 | /* c-file-style: "GNU" */ |
| 1006 | /* End: */ |