diff options
Diffstat (limited to 'rbutil/rbutilqt/mspack/chmd.c')
-rw-r--r-- | rbutil/rbutilqt/mspack/chmd.c | 667 |
1 files changed, 349 insertions, 318 deletions
diff --git a/rbutil/rbutilqt/mspack/chmd.c b/rbutil/rbutilqt/mspack/chmd.c index 416156e742..6c8481db14 100644 --- a/rbutil/rbutilqt/mspack/chmd.c +++ b/rbutil/rbutilqt/mspack/chmd.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* This file is part of libmspack. | 1 | /* This file is part of libmspack. |
2 | * (C) 2003-2011 Stuart Caie. | 2 | * (C) 2003-2018 Stuart Caie. |
3 | * | 3 | * |
4 | * libmspack is free software; you can redistribute it and/or modify it under | 4 | * libmspack is free software; you can redistribute it and/or modify it under |
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | 5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
@@ -44,7 +44,7 @@ static int chmd_init_decomp( | |||
44 | struct mschm_decompressor_p *self, struct mschmd_file *file); | 44 | struct mschm_decompressor_p *self, struct mschmd_file *file); |
45 | static int read_reset_table( | 45 | static int read_reset_table( |
46 | struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, | 46 | struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, |
47 | int entry, off_t *length_ptr, off_t *offset_ptr); | 47 | unsigned int entry, off_t *length_ptr, off_t *offset_ptr); |
48 | static int read_spaninfo( | 48 | static int read_spaninfo( |
49 | struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, | 49 | struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, |
50 | off_t *length_ptr); | 50 | off_t *length_ptr); |
@@ -121,7 +121,7 @@ void mspack_destroy_chm_decompressor(struct mschm_decompressor *base) { | |||
121 | * Calls chmd_real_open() with entire=1. | 121 | * Calls chmd_real_open() with entire=1. |
122 | */ | 122 | */ |
123 | static struct mschmd_header *chmd_open(struct mschm_decompressor *base, | 123 | static struct mschmd_header *chmd_open(struct mschm_decompressor *base, |
124 | const char *filename) | 124 | const char *filename) |
125 | { | 125 | { |
126 | return chmd_real_open(base, filename, 1); | 126 | return chmd_real_open(base, filename, 1); |
127 | } | 127 | } |
@@ -133,7 +133,7 @@ static struct mschmd_header *chmd_open(struct mschm_decompressor *base, | |||
133 | * the file headers. Calls chmd_real_open() with entire=0 | 133 | * the file headers. Calls chmd_real_open() with entire=0 |
134 | */ | 134 | */ |
135 | static struct mschmd_header *chmd_fast_open(struct mschm_decompressor *base, | 135 | static struct mschmd_header *chmd_fast_open(struct mschm_decompressor *base, |
136 | const char *filename) | 136 | const char *filename) |
137 | { | 137 | { |
138 | return chmd_real_open(base, filename, 0); | 138 | return chmd_real_open(base, filename, 0); |
139 | } | 139 | } |
@@ -146,7 +146,7 @@ static struct mschmd_header *chmd_fast_open(struct mschm_decompressor *base, | |||
146 | * either read all headers, or a bare mininum. | 146 | * either read all headers, or a bare mininum. |
147 | */ | 147 | */ |
148 | static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base, | 148 | static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base, |
149 | const char *filename, int entire) | 149 | const char *filename, int entire) |
150 | { | 150 | { |
151 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | 151 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; |
152 | struct mschmd_header *chm = NULL; | 152 | struct mschmd_header *chm = NULL; |
@@ -162,16 +162,16 @@ static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base, | |||
162 | chm->filename = filename; | 162 | chm->filename = filename; |
163 | error = chmd_read_headers(sys, fh, chm, entire); | 163 | error = chmd_read_headers(sys, fh, chm, entire); |
164 | if (error) { | 164 | if (error) { |
165 | /* if the error is DATAFORMAT, and there are some results, return | 165 | /* if the error is DATAFORMAT, and there are some results, return |
166 | * partial results with a warning, rather than nothing */ | 166 | * partial results with a warning, rather than nothing */ |
167 | if (error == MSPACK_ERR_DATAFORMAT && (chm->files || chm->sysfiles)) { | 167 | if (error == MSPACK_ERR_DATAFORMAT && (chm->files || chm->sysfiles)) { |
168 | sys->message(fh, "WARNING; contents are corrupt"); | 168 | sys->message(fh, "WARNING; contents are corrupt"); |
169 | error = MSPACK_ERR_OK; | 169 | error = MSPACK_ERR_OK; |
170 | } | 170 | } |
171 | else { | 171 | else { |
172 | chmd_close(base, chm); | 172 | chmd_close(base, chm); |
173 | chm = NULL; | 173 | chm = NULL; |
174 | } | 174 | } |
175 | } | 175 | } |
176 | self->error = error; | 176 | self->error = error; |
177 | } | 177 | } |
@@ -192,7 +192,7 @@ static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base, | |||
192 | * frees all memory associated with a given mschmd_header | 192 | * frees all memory associated with a given mschmd_header |
193 | */ | 193 | */ |
194 | static void chmd_close(struct mschm_decompressor *base, | 194 | static void chmd_close(struct mschm_decompressor *base, |
195 | struct mschmd_header *chm) | 195 | struct mschmd_header *chm) |
196 | { | 196 | { |
197 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | 197 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; |
198 | struct mschmd_file *fi, *nfi; | 198 | struct mschmd_file *fi, *nfi; |
@@ -251,16 +251,16 @@ static const unsigned char guids[32] = { | |||
251 | 251 | ||
252 | /* reads an encoded integer into a variable; 7 bits of data per byte, | 252 | /* reads an encoded integer into a variable; 7 bits of data per byte, |
253 | * the high bit is used to indicate that there is another byte */ | 253 | * the high bit is used to indicate that there is another byte */ |
254 | #define READ_ENCINT(var) do { \ | 254 | #define READ_ENCINT(var) do { \ |
255 | (var) = 0; \ | 255 | (var) = 0; \ |
256 | do { \ | 256 | do { \ |
257 | if (p > end) goto chunk_end; \ | 257 | if (p >= end) goto chunk_end; \ |
258 | (var) = ((var) << 7) | (*p & 0x7F); \ | 258 | (var) = ((var) << 7) | (*p & 0x7F); \ |
259 | } while (*p++ & 0x80); \ | 259 | } while (*p++ & 0x80); \ |
260 | } while (0) | 260 | } while (0) |
261 | 261 | ||
262 | static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, | 262 | static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, |
263 | struct mschmd_header *chm, int entire) | 263 | struct mschmd_header *chm, int entire) |
264 | { | 264 | { |
265 | unsigned int section, name_len, x, errors, num_chunks; | 265 | unsigned int section, name_len, x, errors, num_chunks; |
266 | unsigned char buf[0x54], *chunk = NULL, *name, *p, *end; | 266 | unsigned char buf[0x54], *chunk = NULL, *name, *p, *end; |
@@ -292,7 +292,7 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, | |||
292 | } | 292 | } |
293 | 293 | ||
294 | /* check both header GUIDs */ | 294 | /* check both header GUIDs */ |
295 | if (mspack_memcmp(&buf[chmhead_GUID1], &guids[0], 32L) != 0) { | 295 | if (memcmp(&buf[chmhead_GUID1], &guids[0], 32L) != 0) { |
296 | D(("incorrect GUIDs")) | 296 | D(("incorrect GUIDs")) |
297 | return MSPACK_ERR_SIGNATURE; | 297 | return MSPACK_ERR_SIGNATURE; |
298 | } | 298 | } |
@@ -356,8 +356,53 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, | |||
356 | chm->sec0.offset = chm->dir_offset + (chm->chunk_size * chm->num_chunks); | 356 | chm->sec0.offset = chm->dir_offset + (chm->chunk_size * chm->num_chunks); |
357 | } | 357 | } |
358 | 358 | ||
359 | /* ensure chunk size is large enough for signature and num_entries */ | 359 | /* check if content offset or file size is wrong */ |
360 | if (chm->sec0.offset > chm->length) { | ||
361 | D(("content section begins after file has ended")) | ||
362 | return MSPACK_ERR_DATAFORMAT; | ||
363 | } | ||
364 | |||
365 | /* ensure there are chunks and that chunk size is | ||
366 | * large enough for signature and num_entries */ | ||
360 | if (chm->chunk_size < (pmgl_Entries + 2)) { | 367 | if (chm->chunk_size < (pmgl_Entries + 2)) { |
368 | D(("chunk size not large enough")) | ||
369 | return MSPACK_ERR_DATAFORMAT; | ||
370 | } | ||
371 | if (chm->num_chunks == 0) { | ||
372 | D(("no chunks")) | ||
373 | return MSPACK_ERR_DATAFORMAT; | ||
374 | } | ||
375 | |||
376 | /* The chunk_cache data structure is not great; large values for num_chunks | ||
377 | * or num_chunks*chunk_size can exhaust all memory. Until a better chunk | ||
378 | * cache is implemented, put arbitrary limits on num_chunks and chunk size. | ||
379 | */ | ||
380 | if (chm->num_chunks > 100000) { | ||
381 | D(("more than 100,000 chunks")) | ||
382 | return MSPACK_ERR_DATAFORMAT; | ||
383 | } | ||
384 | if (chm->chunk_size > 8192) { | ||
385 | D(("chunk size over 8192 (get in touch if this is valid)")) | ||
386 | return MSPACK_ERR_DATAFORMAT; | ||
387 | } | ||
388 | if ((off_t)chm->chunk_size * (off_t)chm->num_chunks > chm->length) { | ||
389 | D(("chunks larger than entire file")) | ||
390 | return MSPACK_ERR_DATAFORMAT; | ||
391 | } | ||
392 | |||
393 | /* common sense checks on header section 1 fields */ | ||
394 | if (chm->chunk_size != 4096) { | ||
395 | sys->message(fh, "WARNING; chunk size is not 4096"); | ||
396 | } | ||
397 | if (chm->first_pmgl != 0) { | ||
398 | sys->message(fh, "WARNING; first PMGL chunk is not zero"); | ||
399 | } | ||
400 | if (chm->first_pmgl > chm->last_pmgl) { | ||
401 | D(("first pmgl chunk is after last pmgl chunk")) | ||
402 | return MSPACK_ERR_DATAFORMAT; | ||
403 | } | ||
404 | if (chm->index_root != 0xFFFFFFFF && chm->index_root >= chm->num_chunks) { | ||
405 | D(("index_root outside valid range")) | ||
361 | return MSPACK_ERR_DATAFORMAT; | 406 | return MSPACK_ERR_DATAFORMAT; |
362 | } | 407 | } |
363 | 408 | ||
@@ -394,7 +439,7 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, | |||
394 | sys->message(fh, "WARNING; PMGL quickref area is too small"); | 439 | sys->message(fh, "WARNING; PMGL quickref area is too small"); |
395 | } | 440 | } |
396 | if (EndGetI32(&chunk[pmgl_QuickRefSize]) > | 441 | if (EndGetI32(&chunk[pmgl_QuickRefSize]) > |
397 | ((int)chm->chunk_size - pmgl_Entries)) | 442 | (chm->chunk_size - pmgl_Entries)) |
398 | { | 443 | { |
399 | sys->message(fh, "WARNING; PMGL quickref area is too large"); | 444 | sys->message(fh, "WARNING; PMGL quickref area is too large"); |
400 | } | 445 | } |
@@ -404,60 +449,63 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, | |||
404 | num_entries = EndGetI16(end); | 449 | num_entries = EndGetI16(end); |
405 | 450 | ||
406 | while (num_entries--) { | 451 | while (num_entries--) { |
407 | READ_ENCINT(name_len); name = p; p += name_len; | 452 | READ_ENCINT(name_len); |
453 | if (name_len > (unsigned int) (end - p)) goto chunk_end; | ||
454 | name = p; p += name_len; | ||
408 | READ_ENCINT(section); | 455 | READ_ENCINT(section); |
409 | READ_ENCINT(offset); | 456 | READ_ENCINT(offset); |
410 | READ_ENCINT(length); | 457 | READ_ENCINT(length); |
411 | 458 | ||
459 | /* ignore blank or one-char (e.g. "/") filenames we'd return as blank */ | ||
460 | if (name_len < 2 || !name[0] || !name[1]) continue; | ||
461 | |||
412 | /* empty files and directory names are stored as a file entry at | 462 | /* empty files and directory names are stored as a file entry at |
413 | * offset 0 with length 0. We want to keep empty files, but not | 463 | * offset 0 with length 0. We want to keep empty files, but not |
414 | * directory names, which end with a "/" */ | 464 | * directory names, which end with a "/" */ |
415 | if ((offset == 0) && (length == 0)) { | 465 | if ((offset == 0) && (length == 0)) { |
416 | if ((name_len > 0) && (name[name_len-1] == '/')) continue; | 466 | if ((name_len > 0) && (name[name_len-1] == '/')) continue; |
417 | } | 467 | } |
418 | 468 | ||
419 | if (section > 1) { | 469 | if (section > 1) { |
420 | sys->message(fh, "invalid section number '%u'.", section); | 470 | sys->message(fh, "invalid section number '%u'.", section); |
421 | continue; | 471 | continue; |
422 | } | 472 | } |
423 | 473 | ||
424 | if (!(fi = (struct mschmd_file *) sys->alloc(sys, sizeof(struct mschmd_file) + name_len + 1))) { | 474 | if (!(fi = (struct mschmd_file *) sys->alloc(sys, sizeof(struct mschmd_file) + name_len + 1))) { |
425 | sys->free(chunk); | 475 | sys->free(chunk); |
426 | return MSPACK_ERR_NOMEMORY; | 476 | return MSPACK_ERR_NOMEMORY; |
427 | } | 477 | } |
428 | 478 | ||
429 | fi->next = NULL; | 479 | fi->next = NULL; |
430 | fi->filename = (char *) &fi[1]; | 480 | fi->filename = (char *) &fi[1]; |
431 | fi->section = ((section == 0) ? (struct mschmd_section *) (&chm->sec0) | 481 | fi->section = ((section == 0) ? (struct mschmd_section *) (&chm->sec0) |
432 | : (struct mschmd_section *) (&chm->sec1)); | 482 | : (struct mschmd_section *) (&chm->sec1)); |
433 | fi->offset = offset; | 483 | fi->offset = offset; |
434 | fi->length = length; | 484 | fi->length = length; |
435 | sys->copy(name, fi->filename, (size_t) name_len); | 485 | sys->copy(name, fi->filename, (size_t) name_len); |
436 | fi->filename[name_len] = '\0'; | 486 | fi->filename[name_len] = '\0'; |
437 | 487 | ||
438 | if (name[0] == ':' && name[1] == ':') { | 488 | if (name[0] == ':' && name[1] == ':') { |
439 | /* system file */ | 489 | /* system file */ |
440 | if (mspack_memcmp(&name[2], &content_name[2], 31L) == 0) { | 490 | if (name_len == 40 && memcmp(name, content_name, 40) == 0) { |
441 | if (mspack_memcmp(&name[33], &content_name[33], 8L) == 0) { | 491 | chm->sec1.content = fi; |
442 | chm->sec1.content = fi; | 492 | } |
443 | } | 493 | else if (name_len == 44 && memcmp(name, control_name, 44) == 0) { |
444 | else if (mspack_memcmp(&name[33], &control_name[33], 11L) == 0) { | 494 | chm->sec1.control = fi; |
445 | chm->sec1.control = fi; | 495 | } |
446 | } | 496 | else if (name_len == 41 && memcmp(name, spaninfo_name, 41) == 0) { |
447 | else if (mspack_memcmp(&name[33], &spaninfo_name[33], 8L) == 0) { | 497 | chm->sec1.spaninfo = fi; |
448 | chm->sec1.spaninfo = fi; | 498 | } |
449 | } | 499 | else if (name_len == 105 && memcmp(name, rtable_name, 105) == 0) { |
450 | else if (mspack_memcmp(&name[33], &rtable_name[33], 72L) == 0) { | 500 | chm->sec1.rtable = fi; |
451 | chm->sec1.rtable = fi; | 501 | } |
452 | } | 502 | fi->next = chm->sysfiles; |
453 | } | 503 | chm->sysfiles = fi; |
454 | fi->next = chm->sysfiles; | ||
455 | chm->sysfiles = fi; | ||
456 | } | 504 | } |
457 | else { | 505 | else { |
458 | /* normal file */ | 506 | /* normal file */ |
459 | if (link) link->next = fi; else chm->files = fi; | 507 | if (link) link->next = fi; else chm->files = fi; |
460 | link = fi; | 508 | link = fi; |
461 | } | 509 | } |
462 | } | 510 | } |
463 | 511 | ||
@@ -481,21 +529,24 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, | |||
481 | * directly from the on-disk index. | 529 | * directly from the on-disk index. |
482 | * | 530 | * |
483 | * TODO: protect against infinite loops in chunks (where pgml_NextChunk | 531 | * TODO: protect against infinite loops in chunks (where pgml_NextChunk |
484 | * or a PGMI index entry point to an already visited chunk) | 532 | * or a PMGI index entry point to an already visited chunk) |
485 | */ | 533 | */ |
486 | static int chmd_fast_find(struct mschm_decompressor *base, | 534 | static int chmd_fast_find(struct mschm_decompressor *base, |
487 | struct mschmd_header *chm, const char *filename, | 535 | struct mschmd_header *chm, const char *filename, |
488 | struct mschmd_file *f_ptr, int f_size) | 536 | struct mschmd_file *f_ptr, int f_size) |
489 | { | 537 | { |
490 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | 538 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; |
491 | struct mspack_system *sys; | 539 | struct mspack_system *sys; |
492 | struct mspack_file *fh; | 540 | struct mspack_file *fh; |
493 | const unsigned char *chunk, *p, *end; | 541 | /* p and end are initialised to prevent MSVC warning about "potentially" |
542 | * uninitialised usage. This is provably untrue, but MS won't fix: | ||
543 | * https://developercommunity.visualstudio.com/content/problem/363489/c4701-false-positive-warning.html */ | ||
544 | const unsigned char *chunk, *p = NULL, *end = NULL; | ||
494 | int err = MSPACK_ERR_OK, result = -1; | 545 | int err = MSPACK_ERR_OK, result = -1; |
495 | unsigned int n, sec; | 546 | unsigned int n, sec; |
496 | 547 | ||
497 | if (!self || !chm || !f_ptr || (f_size != sizeof(struct mschmd_file))) { | 548 | if (!self || !chm || !f_ptr || (f_size != sizeof(struct mschmd_file))) { |
498 | return MSPACK_ERR_ARGS; | 549 | return MSPACK_ERR_ARGS; |
499 | } | 550 | } |
500 | sys = self->system; | 551 | sys = self->system; |
501 | 552 | ||
@@ -503,54 +554,59 @@ static int chmd_fast_find(struct mschm_decompressor *base, | |||
503 | memset(f_ptr, 0, f_size); | 554 | memset(f_ptr, 0, f_size); |
504 | 555 | ||
505 | if (!(fh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ))) { | 556 | if (!(fh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ))) { |
506 | return MSPACK_ERR_OPEN; | 557 | return MSPACK_ERR_OPEN; |
507 | } | 558 | } |
508 | 559 | ||
509 | /* go through PMGI chunk hierarchy to reach PMGL chunk */ | 560 | /* go through PMGI chunk hierarchy to reach PMGL chunk */ |
510 | if (chm->index_root < chm->num_chunks) { | 561 | if (chm->index_root < chm->num_chunks) { |
511 | n = chm->index_root; | 562 | n = chm->index_root; |
512 | for (;;) { | 563 | for (;;) { |
513 | if (!(chunk = read_chunk(self, chm, fh, n))) { | 564 | if (!(chunk = read_chunk(self, chm, fh, n))) { |
514 | sys->close(fh); | 565 | sys->close(fh); |
515 | return self->error; | 566 | return self->error; |
516 | } | 567 | } |
517 | 568 | ||
518 | /* search PMGI/PMGL chunk. exit early if no entry found */ | 569 | /* search PMGI/PMGL chunk. exit early if no entry found */ |
519 | if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) { | 570 | if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) { |
520 | break; | 571 | break; |
521 | } | 572 | } |
522 | 573 | ||
523 | /* found result. loop around for next chunk if this is PMGI */ | 574 | /* found result. loop around for next chunk if this is PMGI */ |
524 | if (chunk[3] == 0x4C) break; else READ_ENCINT(n); | 575 | if (chunk[3] == 0x4C) break; else READ_ENCINT(n); |
525 | } | 576 | } |
526 | } | 577 | } |
527 | else { | 578 | else { |
528 | /* PMGL chunks only, search from first_pmgl to last_pmgl */ | 579 | /* PMGL chunks only, search from first_pmgl to last_pmgl */ |
529 | for (n = chm->first_pmgl; n <= chm->last_pmgl; | 580 | for (n = chm->first_pmgl; n <= chm->last_pmgl; |
530 | n = EndGetI32(&chunk[pmgl_NextChunk])) | 581 | n = EndGetI32(&chunk[pmgl_NextChunk])) |
531 | { | 582 | { |
532 | if (!(chunk = read_chunk(self, chm, fh, n))) { | 583 | if (!(chunk = read_chunk(self, chm, fh, n))) { |
533 | err = self->error; | 584 | err = self->error; |
534 | break; | 585 | break; |
535 | } | 586 | } |
536 | 587 | ||
537 | /* search PMGL chunk. exit if file found */ | 588 | /* search PMGL chunk. exit if file found */ |
538 | if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) { | 589 | if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) { |
539 | break; | 590 | break; |
540 | } | 591 | } |
541 | } | 592 | |
593 | /* stop simple infinite loops: can't visit the same chunk twice */ | ||
594 | if (n == EndGetI32(&chunk[pmgl_NextChunk])) { | ||
595 | break; | ||
596 | } | ||
597 | } | ||
542 | } | 598 | } |
543 | 599 | ||
544 | /* if we found a file, read it */ | 600 | /* if we found a file, read it */ |
545 | if (result > 0) { | 601 | if (result > 0) { |
546 | READ_ENCINT(sec); | 602 | READ_ENCINT(sec); |
547 | f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0 | 603 | f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0 |
548 | : (struct mschmd_section *) &chm->sec1; | 604 | : (struct mschmd_section *) &chm->sec1; |
549 | READ_ENCINT(f_ptr->offset); | 605 | READ_ENCINT(f_ptr->offset); |
550 | READ_ENCINT(f_ptr->length); | 606 | READ_ENCINT(f_ptr->length); |
551 | } | 607 | } |
552 | else if (result < 0) { | 608 | else if (result < 0) { |
553 | err = MSPACK_ERR_DATAFORMAT; | 609 | err = MSPACK_ERR_DATAFORMAT; |
554 | } | 610 | } |
555 | 611 | ||
556 | sys->close(fh); | 612 | sys->close(fh); |
@@ -566,24 +622,24 @@ static int chmd_fast_find(struct mschm_decompressor *base, | |||
566 | * so it doesn't need to be read from disk more than once | 622 | * so it doesn't need to be read from disk more than once |
567 | */ | 623 | */ |
568 | static unsigned char *read_chunk(struct mschm_decompressor_p *self, | 624 | static unsigned char *read_chunk(struct mschm_decompressor_p *self, |
569 | struct mschmd_header *chm, | 625 | struct mschmd_header *chm, |
570 | struct mspack_file *fh, | 626 | struct mspack_file *fh, |
571 | unsigned int chunk_num) | 627 | unsigned int chunk_num) |
572 | { | 628 | { |
573 | struct mspack_system *sys = self->system; | 629 | struct mspack_system *sys = self->system; |
574 | unsigned char *buf; | 630 | unsigned char *buf; |
575 | 631 | ||
576 | /* check arguments - most are already checked by chmd_fast_find */ | 632 | /* check arguments - most are already checked by chmd_fast_find */ |
577 | if (chunk_num > chm->num_chunks) return NULL; | 633 | if (chunk_num >= chm->num_chunks) return NULL; |
578 | 634 | ||
579 | /* ensure chunk cache is available */ | 635 | /* ensure chunk cache is available */ |
580 | if (!chm->chunk_cache) { | 636 | if (!chm->chunk_cache) { |
581 | size_t size = sizeof(unsigned char *) * chm->num_chunks; | 637 | size_t size = sizeof(unsigned char *) * chm->num_chunks; |
582 | if (!(chm->chunk_cache = (unsigned char **) sys->alloc(sys, size))) { | 638 | if (!(chm->chunk_cache = (unsigned char **) sys->alloc(sys, size))) { |
583 | self->error = MSPACK_ERR_NOMEMORY; | 639 | self->error = MSPACK_ERR_NOMEMORY; |
584 | return NULL; | 640 | return NULL; |
585 | } | 641 | } |
586 | memset(chm->chunk_cache, 0, size); | 642 | memset(chm->chunk_cache, 0, size); |
587 | } | 643 | } |
588 | 644 | ||
589 | /* try to answer out of chunk cache */ | 645 | /* try to answer out of chunk cache */ |
@@ -591,31 +647,31 @@ static unsigned char *read_chunk(struct mschm_decompressor_p *self, | |||
591 | 647 | ||
592 | /* need to read chunk - allocate memory for it */ | 648 | /* need to read chunk - allocate memory for it */ |
593 | if (!(buf = (unsigned char *) sys->alloc(sys, chm->chunk_size))) { | 649 | if (!(buf = (unsigned char *) sys->alloc(sys, chm->chunk_size))) { |
594 | self->error = MSPACK_ERR_NOMEMORY; | 650 | self->error = MSPACK_ERR_NOMEMORY; |
595 | return NULL; | 651 | return NULL; |
596 | } | 652 | } |
597 | 653 | ||
598 | /* seek to block and read it */ | 654 | /* seek to block and read it */ |
599 | if (sys->seek(fh, (off_t) (chm->dir_offset + (chunk_num * chm->chunk_size)), | 655 | if (sys->seek(fh, (off_t) (chm->dir_offset + (chunk_num * chm->chunk_size)), |
600 | MSPACK_SYS_SEEK_START)) | 656 | MSPACK_SYS_SEEK_START)) |
601 | { | 657 | { |
602 | self->error = MSPACK_ERR_SEEK; | 658 | self->error = MSPACK_ERR_SEEK; |
603 | sys->free(buf); | 659 | sys->free(buf); |
604 | return NULL; | 660 | return NULL; |
605 | } | 661 | } |
606 | if (sys->read(fh, buf, (int)chm->chunk_size) != (int)chm->chunk_size) { | 662 | if (sys->read(fh, buf, (int)chm->chunk_size) != (int)chm->chunk_size) { |
607 | self->error = MSPACK_ERR_READ; | 663 | self->error = MSPACK_ERR_READ; |
608 | sys->free(buf); | 664 | sys->free(buf); |
609 | return NULL; | 665 | return NULL; |
610 | } | 666 | } |
611 | 667 | ||
612 | /* check the signature. Is is PMGL or PMGI? */ | 668 | /* check the signature. Is is PMGL or PMGI? */ |
613 | if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) && | 669 | if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) && |
614 | ((buf[3] == 0x4C) || (buf[3] == 0x49)))) | 670 | ((buf[3] == 0x4C) || (buf[3] == 0x49)))) |
615 | { | 671 | { |
616 | self->error = MSPACK_ERR_SEEK; | 672 | self->error = MSPACK_ERR_SEEK; |
617 | sys->free(buf); | 673 | sys->free(buf); |
618 | return NULL; | 674 | return NULL; |
619 | } | 675 | } |
620 | 676 | ||
621 | /* all OK. Store chunk in cache and return it */ | 677 | /* all OK. Store chunk in cache and return it */ |
@@ -633,14 +689,14 @@ static unsigned char *read_chunk(struct mschm_decompressor_p *self, | |||
633 | * chunk that may eventually contain that entry has been found. | 689 | * chunk that may eventually contain that entry has been found. |
634 | */ | 690 | */ |
635 | static int search_chunk(struct mschmd_header *chm, | 691 | static int search_chunk(struct mschmd_header *chm, |
636 | const unsigned char *chunk, | 692 | const unsigned char *chunk, |
637 | const char *filename, | 693 | const char *filename, |
638 | const unsigned char **result, | 694 | const unsigned char **result, |
639 | const unsigned char **result_end) | 695 | const unsigned char **result_end) |
640 | { | 696 | { |
641 | const unsigned char *start, *end, *p; | 697 | const unsigned char *start, *end, *p; |
642 | unsigned int qr_size, num_entries, qr_entries, qr_density, name_len; | 698 | unsigned int qr_size, num_entries, qr_entries, qr_density, name_len; |
643 | unsigned int L, R, M, sec, fname_len, entries_off, is_pmgl; | 699 | unsigned int L, R, M, fname_len, entries_off, is_pmgl; |
644 | int cmp; | 700 | int cmp; |
645 | 701 | ||
646 | fname_len = strlen(filename); | 702 | fname_len = strlen(filename); |
@@ -648,12 +704,12 @@ static int search_chunk(struct mschmd_header *chm, | |||
648 | /* PMGL chunk or PMGI chunk? (note: read_chunk() has already | 704 | /* PMGL chunk or PMGI chunk? (note: read_chunk() has already |
649 | * checked the rest of the characters in the chunk signature) */ | 705 | * checked the rest of the characters in the chunk signature) */ |
650 | if (chunk[3] == 0x4C) { | 706 | if (chunk[3] == 0x4C) { |
651 | is_pmgl = 1; | 707 | is_pmgl = 1; |
652 | entries_off = pmgl_Entries; | 708 | entries_off = pmgl_Entries; |
653 | } | 709 | } |
654 | else { | 710 | else { |
655 | is_pmgl = 0; | 711 | is_pmgl = 0; |
656 | entries_off = pmgi_Entries; | 712 | entries_off = pmgi_Entries; |
657 | } | 713 | } |
658 | 714 | ||
659 | /* Step 1: binary search first filename of each QR entry | 715 | /* Step 1: binary search first filename of each QR entry |
@@ -674,55 +730,55 @@ static int search_chunk(struct mschmd_header *chm, | |||
674 | qr_entries = (num_entries + qr_density-1) / qr_density; | 730 | qr_entries = (num_entries + qr_density-1) / qr_density; |
675 | 731 | ||
676 | if (num_entries == 0) { | 732 | if (num_entries == 0) { |
677 | D(("chunk has no entries")) | 733 | D(("chunk has no entries")) |
678 | return -1; | 734 | return -1; |
679 | } | 735 | } |
680 | 736 | ||
681 | if (qr_size > chm->chunk_size) { | 737 | if (qr_size > chm->chunk_size) { |
682 | D(("quickref size > chunk size")) | 738 | D(("quickref size > chunk size")) |
683 | return -1; | 739 | return -1; |
684 | } | 740 | } |
685 | 741 | ||
686 | *result_end = end; | 742 | *result_end = end; |
687 | 743 | ||
688 | if (((int)qr_entries * 2) > (start - end)) { | 744 | if (((int)qr_entries * 2) > (start - end)) { |
689 | D(("WARNING; more quickrefs than quickref space")) | 745 | D(("WARNING; more quickrefs than quickref space")) |
690 | qr_entries = 0; /* but we can live with it */ | 746 | qr_entries = 0; /* but we can live with it */ |
691 | } | 747 | } |
692 | 748 | ||
693 | if (qr_entries > 0) { | 749 | if (qr_entries > 0) { |
694 | L = 0; | 750 | L = 0; |
695 | R = qr_entries - 1; | 751 | R = qr_entries - 1; |
696 | do { | 752 | do { |
697 | /* pick new midpoint */ | 753 | /* pick new midpoint */ |
698 | M = (L + R) >> 1; | 754 | M = (L + R) >> 1; |
699 | 755 | ||
700 | /* compare filename with entry QR points to */ | 756 | /* compare filename with entry QR points to */ |
701 | p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; | 757 | p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; |
702 | READ_ENCINT(name_len); | 758 | READ_ENCINT(name_len); |
703 | if (p + name_len > end) goto chunk_end; | 759 | if (name_len > (unsigned int) (end - p)) goto chunk_end; |
704 | cmp = compare(filename, (char *)p, fname_len, name_len); | 760 | cmp = compare(filename, (char *)p, fname_len, name_len); |
705 | 761 | ||
706 | if (cmp == 0) break; | 762 | if (cmp == 0) break; |
707 | else if (cmp < 0) { if (M) R = M - 1; else return 0; } | 763 | else if (cmp < 0) { if (M) R = M - 1; else return 0; } |
708 | else if (cmp > 0) L = M + 1; | 764 | else if (cmp > 0) L = M + 1; |
709 | } while (L <= R); | 765 | } while (L <= R); |
710 | M = (L + R) >> 1; | 766 | M = (L + R) >> 1; |
711 | 767 | ||
712 | if (cmp == 0) { | 768 | if (cmp == 0) { |
713 | /* exact match! */ | 769 | /* exact match! */ |
714 | p += name_len; | 770 | p += name_len; |
715 | *result = p; | 771 | *result = p; |
716 | return 1; | 772 | return 1; |
717 | } | 773 | } |
718 | 774 | ||
719 | /* otherwise, read the group of entries for QR entry M */ | 775 | /* otherwise, read the group of entries for QR entry M */ |
720 | p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; | 776 | p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; |
721 | num_entries -= (M * qr_density); | 777 | num_entries -= (M * qr_density); |
722 | if (num_entries > qr_density) num_entries = qr_density; | 778 | if (num_entries > qr_density) num_entries = qr_density; |
723 | } | 779 | } |
724 | else { | 780 | else { |
725 | p = &chunk[entries_off]; | 781 | p = &chunk[entries_off]; |
726 | } | 782 | } |
727 | 783 | ||
728 | /* Step 2: linear search through the set of entries reached in step 1. | 784 | /* Step 2: linear search through the set of entries reached in step 1. |
@@ -736,32 +792,32 @@ static int search_chunk(struct mschmd_header *chm, | |||
736 | */ | 792 | */ |
737 | *result = NULL; | 793 | *result = NULL; |
738 | while (num_entries-- > 0) { | 794 | while (num_entries-- > 0) { |
739 | READ_ENCINT(name_len); | 795 | READ_ENCINT(name_len); |
740 | if (p + name_len > end) goto chunk_end; | 796 | if (name_len > (unsigned int) (end - p)) goto chunk_end; |
741 | cmp = compare(filename, (char *)p, fname_len, name_len); | 797 | cmp = compare(filename, (char *)p, fname_len, name_len); |
742 | p += name_len; | 798 | p += name_len; |
743 | 799 | ||
744 | if (cmp == 0) { | 800 | if (cmp == 0) { |
745 | /* entry found */ | 801 | /* entry found */ |
746 | *result = p; | 802 | *result = p; |
747 | return 1; | 803 | return 1; |
748 | } | 804 | } |
749 | 805 | ||
750 | if (cmp < 0) { | 806 | if (cmp < 0) { |
751 | /* entry not found (PMGL) / maybe found (PMGI) */ | 807 | /* entry not found (PMGL) / maybe found (PMGI) */ |
752 | break; | 808 | break; |
753 | } | 809 | } |
754 | 810 | ||
755 | /* read and ignore the rest of this entry */ | 811 | /* read and ignore the rest of this entry */ |
756 | if (is_pmgl) { | 812 | if (is_pmgl) { |
757 | READ_ENCINT(R); /* skip section */ | 813 | READ_ENCINT(R); /* skip section */ |
758 | READ_ENCINT(R); /* skip offset */ | 814 | READ_ENCINT(R); /* skip offset */ |
759 | READ_ENCINT(R); /* skip length */ | 815 | READ_ENCINT(R); /* skip length */ |
760 | } | 816 | } |
761 | else { | 817 | else { |
762 | *result = p; /* store potential final result */ | 818 | *result = p; /* store potential final result */ |
763 | READ_ENCINT(R); /* skip chunk number */ | 819 | READ_ENCINT(R); /* skip chunk number */ |
764 | } | 820 | } |
765 | } | 821 | } |
766 | 822 | ||
767 | /* PMGL? not found. PMGI? maybe found */ | 823 | /* PMGL? not found. PMGI? maybe found */ |
@@ -773,66 +829,34 @@ static int search_chunk(struct mschmd_header *chm, | |||
773 | } | 829 | } |
774 | 830 | ||
775 | #if HAVE_TOWLOWER | 831 | #if HAVE_TOWLOWER |
776 | # if HAVE_WCTYPE_H | 832 | # include <wctype.h> |
777 | # include <wctype.h> | ||
778 | # endif | ||
779 | # define TOLOWER(x) towlower(x) | 833 | # define TOLOWER(x) towlower(x) |
780 | #elif HAVE_TOLOWER | ||
781 | # if HAVE_CTYPE_H | ||
782 | # include <ctype.h> | ||
783 | # endif | ||
784 | # define TOLOWER(x) tolower(x) | ||
785 | #else | 834 | #else |
786 | # define TOLOWER(x) (((x)<0||(x)>256)?(x):mspack_tolower_map[(x)]) | 835 | # include <ctype.h> |
787 | /* Map of char -> lowercase char for the first 256 chars. Generated with: | 836 | # define TOLOWER(x) tolower(x) |
788 | * LC_CTYPE=en_GB.utf-8 perl -Mlocale -le 'print map{ord(lc chr).","} 0..255' | ||
789 | */ | ||
790 | static const unsigned char mspack_tolower_map[256] = { | ||
791 | 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, | ||
792 | 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52, | ||
793 | 53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103,104,105,106, | ||
794 | 107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94, | ||
795 | 95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114, | ||
796 | 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133, | ||
797 | 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152, | ||
798 | 153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171, | ||
799 | 172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, | ||
800 | 191,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241, | ||
801 | 242,243,244,245,246,215,248,249,250,251,252,253,254,223,224,225,226,227,228, | ||
802 | 229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, | ||
803 | 248,249,250,251,252,253,254,255 | ||
804 | }; | ||
805 | #endif | 837 | #endif |
806 | 838 | ||
807 | /* decodes a UTF-8 character from s[] into c. Will not read past e. */ | 839 | /* decodes a UTF-8 character from s[] into c. Will not read past e. |
808 | #define GET_UTF8_CHAR(s, e, c) do { \ | 840 | * doesn't test that extension bytes are %10xxxxxx. |
809 | unsigned char x = *s++; \ | 841 | * allows some overlong encodings. |
810 | if (x < 0x80) c = x; \ | 842 | */ |
811 | else if (x < 0xC0) c = -1; \ | 843 | #define GET_UTF8_CHAR(s, e, c) do { \ |
812 | else if (x < 0xE0) { \ | 844 | unsigned char x = *s++; \ |
813 | c = (s >= e) ? -1 : ((x & 0x1F) << 6) | (*s++ & 0x3F); \ | 845 | if (x < 0x80) c = x; \ |
814 | } \ | 846 | else if (x >= 0xC2 && x < 0xE0 && s < e) { \ |
815 | else if (x < 0xF0) { \ | 847 | c = (x & 0x1F) << 6 | (*s++ & 0x3F); \ |
816 | c = (s+2 > e) ? -1 : ((x & 0x0F) << 12) | ((s[0] & 0x3F) << 6) \ | 848 | } \ |
817 | | (s[1] & 0x3F); \ | 849 | else if (x >= 0xE0 && x < 0xF0 && s+1 < e) { \ |
818 | s += 2; \ | 850 | c = (x & 0x0F) << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); \ |
819 | } \ | 851 | s += 2; \ |
820 | else if (x < 0xF8) { \ | 852 | } \ |
821 | c = (s+3 > e) ? -1 : ((x & 0x07) << 18) | ((s[0] & 0x3F) << 12) \ | 853 | else if (x >= 0xF0 && x <= 0xF5 && s+2 < e) { \ |
822 | | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F); \ | 854 | c = (x & 0x07) << 18 | (s[0] & 0x3F) << 12 | \ |
823 | s += 3; \ | 855 | (s[1] & 0x3F) << 6 | (s[2] & 0x3F); \ |
824 | } \ | 856 | if (c > 0x10FFFF) c = 0xFFFD; \ |
825 | else if (x < 0xFC) { \ | 857 | s += 3; \ |
826 | c = (s+4 > e) ? -1 : ((x & 0x03) << 24) | ((s[0] & 0x3F) << 18) \ | 858 | } \ |
827 | | ((s[1] & 0x3F) << 12)|((s[2] & 0x3F) << 6)|(s[3] & 0x3F); \ | 859 | else c = 0xFFFD; \ |
828 | s += 4; \ | ||
829 | } \ | ||
830 | else if (x < 0xFE) { \ | ||
831 | c = (s+5>e)?-1:((x&1)<<30)|((s[0]&0x3F)<<24)|((s[1]&0x3F)<<18)| \ | ||
832 | ((s[2] & 0x3F) << 12) | ((s[3] & 0x3F) << 6)|(s[4] & 0x3F); \ | ||
833 | s += 5; \ | ||
834 | } \ | ||
835 | else c = -1; \ | ||
836 | } while (0) | 860 | } while (0) |
837 | 861 | ||
838 | /* case-insensitively compares two UTF8 encoded strings. String length for | 862 | /* case-insensitively compares two UTF8 encoded strings. String length for |
@@ -844,12 +868,12 @@ static inline int compare(const char *s1, const char *s2, int l1, int l2) { | |||
844 | int c1, c2; | 868 | int c1, c2; |
845 | 869 | ||
846 | while (p1 < e1 && p2 < e2) { | 870 | while (p1 < e1 && p2 < e2) { |
847 | GET_UTF8_CHAR(p1, e1, c1); | 871 | GET_UTF8_CHAR(p1, e1, c1); |
848 | GET_UTF8_CHAR(p2, e2, c2); | 872 | GET_UTF8_CHAR(p2, e2, c2); |
849 | if (c1 == c2) continue; | 873 | if (c1 == c2) continue; |
850 | c1 = TOLOWER(c1); | 874 | c1 = TOLOWER(c1); |
851 | c2 = TOLOWER(c2); | 875 | c2 = TOLOWER(c2); |
852 | if (c1 != c2) return c1 - c2; | 876 | if (c1 != c2) return c1 - c2; |
853 | } | 877 | } |
854 | return l1 - l2; | 878 | return l1 - l2; |
855 | } | 879 | } |
@@ -861,7 +885,7 @@ static inline int compare(const char *s1, const char *s2, int l1, int l2) { | |||
861 | * extracts a file from a CHM helpfile | 885 | * extracts a file from a CHM helpfile |
862 | */ | 886 | */ |
863 | static int chmd_extract(struct mschm_decompressor *base, | 887 | static int chmd_extract(struct mschm_decompressor *base, |
864 | struct mschmd_file *file, const char *filename) | 888 | struct mschmd_file *file, const char *filename) |
865 | { | 889 | { |
866 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | 890 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; |
867 | struct mspack_system *sys; | 891 | struct mspack_system *sys; |
@@ -915,7 +939,7 @@ static int chmd_extract(struct mschm_decompressor *base, | |||
915 | case 0: /* Uncompressed section file */ | 939 | case 0: /* Uncompressed section file */ |
916 | /* simple seek + copy */ | 940 | /* simple seek + copy */ |
917 | if (sys->seek(self->d->infh, file->section->chm->sec0.offset | 941 | if (sys->seek(self->d->infh, file->section->chm->sec0.offset |
918 | + file->offset, MSPACK_SYS_SEEK_START)) | 942 | + file->offset, MSPACK_SYS_SEEK_START)) |
919 | { | 943 | { |
920 | self->error = MSPACK_ERR_SEEK; | 944 | self->error = MSPACK_ERR_SEEK; |
921 | } | 945 | } |
@@ -923,17 +947,17 @@ static int chmd_extract(struct mschm_decompressor *base, | |||
923 | unsigned char buf[512]; | 947 | unsigned char buf[512]; |
924 | off_t length = file->length; | 948 | off_t length = file->length; |
925 | while (length > 0) { | 949 | while (length > 0) { |
926 | int run = sizeof(buf); | 950 | int run = sizeof(buf); |
927 | if ((off_t)run > length) run = (int)length; | 951 | if ((off_t)run > length) run = (int)length; |
928 | if (sys->read(self->d->infh, &buf[0], run) != run) { | 952 | if (sys->read(self->d->infh, &buf[0], run) != run) { |
929 | self->error = MSPACK_ERR_READ; | 953 | self->error = MSPACK_ERR_READ; |
930 | break; | 954 | break; |
931 | } | 955 | } |
932 | if (sys->write(fh, &buf[0], run) != run) { | 956 | if (sys->write(fh, &buf[0], run) != run) { |
933 | self->error = MSPACK_ERR_WRITE; | 957 | self->error = MSPACK_ERR_WRITE; |
934 | break; | 958 | break; |
935 | } | 959 | } |
936 | length -= run; | 960 | length -= run; |
937 | } | 961 | } |
938 | } | 962 | } |
939 | break; | 963 | break; |
@@ -944,8 +968,8 @@ static int chmd_extract(struct mschm_decompressor *base, | |||
944 | */ | 968 | */ |
945 | if (!self->d->state || (file->offset < self->d->offset)) { | 969 | if (!self->d->state || (file->offset < self->d->offset)) { |
946 | if (self->d->state) { | 970 | if (self->d->state) { |
947 | lzxd_free(self->d->state); | 971 | lzxd_free(self->d->state); |
948 | self->d->state = NULL; | 972 | self->d->state = NULL; |
949 | } | 973 | } |
950 | if (chmd_init_decomp(self, file)) break; | 974 | if (chmd_init_decomp(self, file)) break; |
951 | } | 975 | } |
@@ -1009,7 +1033,7 @@ static int chmd_sys_write(struct mspack_file *file, void *buffer, int bytes) { | |||
1009 | * file. | 1033 | * file. |
1010 | */ | 1034 | */ |
1011 | static int chmd_init_decomp(struct mschm_decompressor_p *self, | 1035 | static int chmd_init_decomp(struct mschm_decompressor_p *self, |
1012 | struct mschmd_file *file) | 1036 | struct mschmd_file *file) |
1013 | { | 1037 | { |
1014 | int window_size, window_bits, reset_interval, entry, err; | 1038 | int window_size, window_bits, reset_interval, entry, err; |
1015 | struct mspack_system *sys = self->system; | 1039 | struct mspack_system *sys = self->system; |
@@ -1077,7 +1101,7 @@ static int chmd_init_decomp(struct mschm_decompressor_p *self, | |||
1077 | } | 1101 | } |
1078 | 1102 | ||
1079 | /* validate reset_interval */ | 1103 | /* validate reset_interval */ |
1080 | if (reset_interval % LZX_FRAME_SIZE) { | 1104 | if (reset_interval == 0 || reset_interval % LZX_FRAME_SIZE) { |
1081 | D(("bad controldata reset interval")) | 1105 | D(("bad controldata reset interval")) |
1082 | return self->error = MSPACK_ERR_DATAFORMAT; | 1106 | return self->error = MSPACK_ERR_DATAFORMAT; |
1083 | } | 1107 | } |
@@ -1116,9 +1140,9 @@ static int chmd_init_decomp(struct mschm_decompressor_p *self, | |||
1116 | 1140 | ||
1117 | /* initialise LZX stream */ | 1141 | /* initialise LZX stream */ |
1118 | self->d->state = lzxd_init(&self->d->sys, self->d->infh, | 1142 | self->d->state = lzxd_init(&self->d->sys, self->d->infh, |
1119 | (struct mspack_file *) self, window_bits, | 1143 | (struct mspack_file *) self, window_bits, |
1120 | reset_interval / LZX_FRAME_SIZE, | 1144 | reset_interval / LZX_FRAME_SIZE, |
1121 | 4096, length); | 1145 | 4096, length, 0); |
1122 | if (!self->d->state) self->error = MSPACK_ERR_NOMEMORY; | 1146 | if (!self->d->state) self->error = MSPACK_ERR_NOMEMORY; |
1123 | return self->error; | 1147 | return self->error; |
1124 | } | 1148 | } |
@@ -1131,12 +1155,13 @@ static int chmd_init_decomp(struct mschm_decompressor_p *self, | |||
1131 | * Returns non-zero for success, zero for failure. | 1155 | * Returns non-zero for success, zero for failure. |
1132 | */ | 1156 | */ |
1133 | static int read_reset_table(struct mschm_decompressor_p *self, | 1157 | static int read_reset_table(struct mschm_decompressor_p *self, |
1134 | struct mschmd_sec_mscompressed *sec, | 1158 | struct mschmd_sec_mscompressed *sec, |
1135 | int entry, off_t *length_ptr, off_t *offset_ptr) | 1159 | unsigned int entry, |
1160 | off_t *length_ptr, off_t *offset_ptr) | ||
1136 | { | 1161 | { |
1137 | struct mspack_system *sys = self->system; | 1162 | struct mspack_system *sys = self->system; |
1138 | unsigned char *data; | 1163 | unsigned char *data; |
1139 | int pos, entrysize; | 1164 | unsigned int pos, entrysize; |
1140 | 1165 | ||
1141 | /* do we have a ResetTable file? */ | 1166 | /* do we have a ResetTable file? */ |
1142 | int err = find_sys_file(self, sec, &sec->rtable, rtable_name); | 1167 | int err = find_sys_file(self, sec, &sec->rtable, rtable_name); |
@@ -1144,25 +1169,25 @@ static int read_reset_table(struct mschm_decompressor_p *self, | |||
1144 | 1169 | ||
1145 | /* read ResetTable file */ | 1170 | /* read ResetTable file */ |
1146 | if (sec->rtable->length < lzxrt_headerSIZEOF) { | 1171 | if (sec->rtable->length < lzxrt_headerSIZEOF) { |
1147 | D(("ResetTable file is too short")) | 1172 | D(("ResetTable file is too short")) |
1148 | return 0; | 1173 | return 0; |
1149 | } | 1174 | } |
1150 | if (!(data = read_sys_file(self, sec->rtable))) { | 1175 | if (!(data = read_sys_file(self, sec->rtable))) { |
1151 | D(("can't read reset table")) | 1176 | D(("can't read reset table")) |
1152 | return 0; | 1177 | return 0; |
1153 | } | 1178 | } |
1154 | 1179 | ||
1155 | /* check sanity of reset table */ | 1180 | /* check sanity of reset table */ |
1156 | if (EndGetI32(&data[lzxrt_FrameLen]) != LZX_FRAME_SIZE) { | 1181 | if (EndGetI32(&data[lzxrt_FrameLen]) != LZX_FRAME_SIZE) { |
1157 | D(("bad reset table frame length")) | 1182 | D(("bad reset table frame length")) |
1158 | sys->free(data); | 1183 | sys->free(data); |
1159 | return 0; | 1184 | return 0; |
1160 | } | 1185 | } |
1161 | 1186 | ||
1162 | /* get the uncompressed length of the LZX stream */ | 1187 | /* get the uncompressed length of the LZX stream */ |
1163 | if (read_off64(length_ptr, data, sys, self->d->infh)) { | 1188 | if (read_off64(length_ptr, &data[lzxrt_UncompLen], sys, self->d->infh)) { |
1164 | sys->free(data); | 1189 | sys->free(data); |
1165 | return 0; | 1190 | return 0; |
1166 | } | 1191 | } |
1167 | 1192 | ||
1168 | entrysize = EndGetI32(&data[lzxrt_EntrySize]); | 1193 | entrysize = EndGetI32(&data[lzxrt_EntrySize]); |
@@ -1170,25 +1195,25 @@ static int read_reset_table(struct mschm_decompressor_p *self, | |||
1170 | 1195 | ||
1171 | /* ensure reset table entry for this offset exists */ | 1196 | /* ensure reset table entry for this offset exists */ |
1172 | if (entry < EndGetI32(&data[lzxrt_NumEntries]) && | 1197 | if (entry < EndGetI32(&data[lzxrt_NumEntries]) && |
1173 | ((pos + entrysize) <= sec->rtable->length)) | 1198 | pos <= (sec->rtable->length - entrysize)) |
1174 | { | 1199 | { |
1175 | switch (entrysize) { | 1200 | switch (entrysize) { |
1176 | case 4: | 1201 | case 4: |
1177 | *offset_ptr = EndGetI32(&data[pos]); | 1202 | *offset_ptr = EndGetI32(&data[pos]); |
1178 | err = 0; | 1203 | err = 0; |
1179 | break; | 1204 | break; |
1180 | case 8: | 1205 | case 8: |
1181 | err = read_off64(offset_ptr, &data[pos], sys, self->d->infh); | 1206 | err = read_off64(offset_ptr, &data[pos], sys, self->d->infh); |
1182 | break; | 1207 | break; |
1183 | default: | 1208 | default: |
1184 | D(("reset table entry size neither 4 nor 8")) | 1209 | D(("reset table entry size neither 4 nor 8")) |
1185 | err = 1; | 1210 | err = 1; |
1186 | break; | 1211 | break; |
1187 | } | 1212 | } |
1188 | } | 1213 | } |
1189 | else { | 1214 | else { |
1190 | D(("bad reset interval")) | 1215 | D(("bad reset interval")) |
1191 | err = 1; | 1216 | err = 1; |
1192 | } | 1217 | } |
1193 | 1218 | ||
1194 | /* free the reset table */ | 1219 | /* free the reset table */ |
@@ -1205,8 +1230,8 @@ static int read_reset_table(struct mschm_decompressor_p *self, | |||
1205 | * Returns zero for success or a non-zero error code for failure. | 1230 | * Returns zero for success or a non-zero error code for failure. |
1206 | */ | 1231 | */ |
1207 | static int read_spaninfo(struct mschm_decompressor_p *self, | 1232 | static int read_spaninfo(struct mschm_decompressor_p *self, |
1208 | struct mschmd_sec_mscompressed *sec, | 1233 | struct mschmd_sec_mscompressed *sec, |
1209 | off_t *length_ptr) | 1234 | off_t *length_ptr) |
1210 | { | 1235 | { |
1211 | struct mspack_system *sys = self->system; | 1236 | struct mspack_system *sys = self->system; |
1212 | unsigned char *data; | 1237 | unsigned char *data; |
@@ -1217,21 +1242,27 @@ static int read_spaninfo(struct mschm_decompressor_p *self, | |||
1217 | 1242 | ||
1218 | /* check it's large enough */ | 1243 | /* check it's large enough */ |
1219 | if (sec->spaninfo->length != 8) { | 1244 | if (sec->spaninfo->length != 8) { |
1220 | D(("SpanInfo file is wrong size")) | 1245 | D(("SpanInfo file is wrong size")) |
1221 | return MSPACK_ERR_DATAFORMAT; | 1246 | return MSPACK_ERR_DATAFORMAT; |
1222 | } | 1247 | } |
1223 | 1248 | ||
1224 | /* read the SpanInfo file */ | 1249 | /* read the SpanInfo file */ |
1225 | if (!(data = read_sys_file(self, sec->spaninfo))) { | 1250 | if (!(data = read_sys_file(self, sec->spaninfo))) { |
1226 | D(("can't read SpanInfo file")) | 1251 | D(("can't read SpanInfo file")) |
1227 | return self->error; | 1252 | return self->error; |
1228 | } | 1253 | } |
1229 | 1254 | ||
1230 | /* get the uncompressed length of the LZX stream */ | 1255 | /* get the uncompressed length of the LZX stream */ |
1231 | err = read_off64(length_ptr, data, sys, self->d->infh); | 1256 | err = read_off64(length_ptr, data, sys, self->d->infh); |
1232 | |||
1233 | sys->free(data); | 1257 | sys->free(data); |
1234 | return (err) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK; | 1258 | if (err) return MSPACK_ERR_DATAFORMAT; |
1259 | |||
1260 | if (*length_ptr <= 0) { | ||
1261 | D(("output length is invalid")) | ||
1262 | return MSPACK_ERR_DATAFORMAT; | ||
1263 | } | ||
1264 | |||
1265 | return MSPACK_ERR_OK; | ||
1235 | } | 1266 | } |
1236 | 1267 | ||
1237 | /*************************************** | 1268 | /*************************************** |
@@ -1242,8 +1273,8 @@ static int read_spaninfo(struct mschm_decompressor_p *self, | |||
1242 | * for success, non-zero for both failure and the file not existing. | 1273 | * for success, non-zero for both failure and the file not existing. |
1243 | */ | 1274 | */ |
1244 | static int find_sys_file(struct mschm_decompressor_p *self, | 1275 | static int find_sys_file(struct mschm_decompressor_p *self, |
1245 | struct mschmd_sec_mscompressed *sec, | 1276 | struct mschmd_sec_mscompressed *sec, |
1246 | struct mschmd_file **f_ptr, const char *name) | 1277 | struct mschmd_file **f_ptr, const char *name) |
1247 | { | 1278 | { |
1248 | struct mspack_system *sys = self->system; | 1279 | struct mspack_system *sys = self->system; |
1249 | struct mschmd_file result; | 1280 | struct mschmd_file result; |
@@ -1254,13 +1285,13 @@ static int find_sys_file(struct mschm_decompressor_p *self, | |||
1254 | /* try using fast_find to find the file - return DATAFORMAT error if | 1285 | /* try using fast_find to find the file - return DATAFORMAT error if |
1255 | * it fails, or successfully doesn't find the file */ | 1286 | * it fails, or successfully doesn't find the file */ |
1256 | if (chmd_fast_find((struct mschm_decompressor *) self, sec->base.chm, | 1287 | if (chmd_fast_find((struct mschm_decompressor *) self, sec->base.chm, |
1257 | name, &result, (int)sizeof(result)) || !result.section) | 1288 | name, &result, (int)sizeof(result)) || !result.section) |
1258 | { | 1289 | { |
1259 | return MSPACK_ERR_DATAFORMAT; | 1290 | return MSPACK_ERR_DATAFORMAT; |
1260 | } | 1291 | } |
1261 | 1292 | ||
1262 | if (!(*f_ptr = (struct mschmd_file *) sys->alloc(sys, sizeof(result)))) { | 1293 | if (!(*f_ptr = (struct mschmd_file *) sys->alloc(sys, sizeof(result)))) { |
1263 | return MSPACK_ERR_NOMEMORY; | 1294 | return MSPACK_ERR_NOMEMORY; |
1264 | } | 1295 | } |
1265 | 1296 | ||
1266 | /* copy result */ | 1297 | /* copy result */ |
@@ -1280,7 +1311,7 @@ static int find_sys_file(struct mschm_decompressor_p *self, | |||
1280 | * memory. | 1311 | * memory. |
1281 | */ | 1312 | */ |
1282 | static unsigned char *read_sys_file(struct mschm_decompressor_p *self, | 1313 | static unsigned char *read_sys_file(struct mschm_decompressor_p *self, |
1283 | struct mschmd_file *file) | 1314 | struct mschmd_file *file) |
1284 | { | 1315 | { |
1285 | struct mspack_system *sys = self->system; | 1316 | struct mspack_system *sys = self->system; |
1286 | unsigned char *data = NULL; | 1317 | unsigned char *data = NULL; |
@@ -1298,7 +1329,7 @@ static unsigned char *read_sys_file(struct mschm_decompressor_p *self, | |||
1298 | return NULL; | 1329 | return NULL; |
1299 | } | 1330 | } |
1300 | if (sys->seek(self->d->infh, file->section->chm->sec0.offset | 1331 | if (sys->seek(self->d->infh, file->section->chm->sec0.offset |
1301 | + file->offset, MSPACK_SYS_SEEK_START)) | 1332 | + file->offset, MSPACK_SYS_SEEK_START)) |
1302 | { | 1333 | { |
1303 | self->error = MSPACK_ERR_SEEK; | 1334 | self->error = MSPACK_ERR_SEEK; |
1304 | sys->free(data); | 1335 | sys->free(data); |
@@ -1331,15 +1362,15 @@ static int chmd_error(struct mschm_decompressor *base) { | |||
1331 | * are accepted, offsets beyond that cause an error message. | 1362 | * are accepted, offsets beyond that cause an error message. |
1332 | */ | 1363 | */ |
1333 | static int read_off64(off_t *var, unsigned char *mem, | 1364 | static int read_off64(off_t *var, unsigned char *mem, |
1334 | struct mspack_system *sys, struct mspack_file *fh) | 1365 | struct mspack_system *sys, struct mspack_file *fh) |
1335 | { | 1366 | { |
1336 | #ifdef LARGEFILE_SUPPORT | 1367 | #if LARGEFILE_SUPPORT |
1337 | *var = EndGetI64(mem); | 1368 | *var = EndGetI64(mem); |
1338 | #else | 1369 | #else |
1339 | *var = EndGetI32(mem); | 1370 | *var = EndGetI32(mem); |
1340 | if ((*var & 0x80000000) || EndGetI32(mem+4)) { | 1371 | if ((*var & 0x80000000) || EndGetI32(mem+4)) { |
1341 | sys->message(fh, (char *)largefile_msg); | 1372 | sys->message(fh, (char *)largefile_msg); |
1342 | return 1; | 1373 | return 1; |
1343 | } | 1374 | } |
1344 | #endif | 1375 | #endif |
1345 | return 0; | 1376 | return 0; |