1 /* 2 3 Copyright(C) 2010 Alex Andreotti <alex.andreotti@gmail.com> 4 5 This file is part of chmc. 6 7 chmc is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 chmc is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with chmc. If not, see <http://www.gnu.org/licenses/>. 19 20 */ 21 #include "chmc.h" 22 23 #include <fcntl.h> 24 25 #include <errno.h> 26 #include <string.h> 27 #include <assert.h> 28 29 #include <stdlib.h> 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include "../../port/port.h" 33 34 #ifdef _WIN32 35 #include <io.h> 36 #else 37 #include <unistd.h> 38 #endif 39 40 #include "err.h" 41 42 43 #include "encint.h" 44 45 #include <stdint.h> 46 #include "../lzx_compress/lzx_config.h" 47 #include "../lzx_compress/lzx_compress.h" 48 49 #define PACKAGE_STRING "hhpcomp development version" 50 51 /* if O_BINARY is not defined, the system is probably not expecting any such flag */ 52 #ifndef O_BINARY 53 #define O_BINARY 0 54 #endif 55 56 int chmc_section_add(struct chmcFile *chm, const char *name); 57 struct chmcSection * chmc_section_create(struct chmcFile *chm, 58 const char *name); 59 void chmc_reset_table_init(struct chmcLzxcResetTable *reset_table); 60 void chmc_control_data_init(struct chmcLzxcControlData *control_data); 61 int chmc_namelist_create(struct chmcFile *chm, int len); 62 struct chmcTreeNode * chmc_add_meta(struct chmcFile *chm, 63 const char *metaname, int sect_id, 64 UChar *buf, UInt64 len); 65 struct chmcTreeNode *chmc_add_entry(struct chmcFile *chm, const char *name, 66 UInt16 prefixlen, int sect_id, 67 UChar *buf, UInt64 offset, UInt64 len); 68 void chmc_sections_free(struct chmcFile *chm); 69 void chmc_section_destroy(struct chmcSection *section); 70 void chmc_pmgi_free(struct chmcFile *chm); 71 void chmc_pmgl_free(struct chmcFile *chm); 72 void chmc_pmgl_destroy(struct chmcPmglChunkNode *node); 73 void chmc_pmgi_destroy(struct chmcPmgiChunkNode *node); 74 void chmc_entries_free(struct chmcFile *chm); 75 void chmc_entry_destroy(struct chmcTreeNode *node); 76 int chmc_add_tree(struct chmcFile *chm, const char *dir); 77 struct chmcTreeNode *chmc_add_file(struct chmcFile *chm, const char *filename, 78 UInt16 prefixlen, int sect_id, UChar *buf, 79 UInt64 len); 80 struct chmcTreeNode *chmc_add_dir(struct chmcFile *chm, const char *dir); 81 struct chmcTreeNode *chmc_add_empty(struct chmcFile *chm, const char *file); 82 83 int chmc_crunch_lzx(struct chmcFile *chm, int sect_id); 84 static int _lzx_at_eof(void *arg); 85 static int _lzx_put_bytes(void *arg, int n, void *buf); 86 static void _lzx_mark_frame(void *arg, uint32_t uncomp, uint32_t comp); 87 static int _lzx_get_bytes(void *arg, int n, void *buf); 88 89 int chmc_compressed_add_mark(struct chmcFile *chm, UInt64 at); 90 int chmc_control_data_done(struct chmcFile *chm); 91 int chmc_reset_table_done(struct chmcFile *chm); 92 void chmc_pmgl_done(struct chmcFile *chm); 93 94 void chmc_entries_qsort(struct chmcFile *chm); 95 static int _entry_cmp(const void *pva, const void *pvb); 96 97 struct chmcSection *chmc_section_lookup(struct chmcFile *chm, int id); 98 99 struct chmcPmglChunkNode *chmc_pmgl_create(void); 100 void chmc_pmgl_add(struct chmcFile *chm, struct chmcPmglChunkNode *pmgl); 101 void chmc_pmgl_init(struct chmcPmglChunkNode *node); 102 int chmc_pmgi_add_entry(struct chmcFile *chm, const char *name, int pmgl_id); 103 void chmc_pmgi_add(struct chmcFile *chm, struct chmcPmgiChunkNode *pmgi); 104 void chmc_string_init(struct chmcStringChunk *node); 105 106 #ifdef __REACTOS__ 107 int chmc_uncompressed_done(struct chmcFile *chm); 108 int chmc_pmgi_done(struct chmcFile *chm); 109 int chmc_write(struct chmcFile *chm); 110 int chmc_appendfile(struct chmcFile *chm, const char *filename, void *buf, 111 size_t size ); 112 int chmc_pmgl_add_entry(struct chmcFile *chm, struct chmcTreeNode *entry); 113 #endif /* __REACTOS__ */ 114 115 struct chmcLzxInfo 116 { 117 struct chmcFile *chm; 118 struct chmcSection *section; 119 int fd; 120 UInt32 fd_offset; 121 UInt32 done; 122 UInt32 todo; 123 struct list_head *pos; 124 int error; 125 int eof; 126 }; 127 128 static const short chmc_transform_list[] = { 129 0x7b, 0x37, 0x46, 0x43, 0x32, 0x38, 0x39, 130 0x34, 0x30, 0x2d, 0x39, 0x44, 0x33, 0x31, 131 0x2d, 0x31, 0x31, 0x44, 0x30 }; 132 133 int chmc_init(struct chmcFile *chm, const char *filename, 134 struct chmcConfig *config) 135 { 136 struct chmcItsfHeader *itsf = &chm->itsf; 137 struct chmcSect0 *sect0 = &chm->sect0; 138 struct chmcItspHeader *itsp = &chm->itsp; 139 struct chmcSystem *system = &chm->system; 140 struct chmcSystemInfo *sysinfo = &chm->system.info; 141 struct chmcIndexHeader *idxhdr = &chm->idxhdr; 142 143 assert(chm); 144 assert(filename); 145 146 chmcerr_clean(); 147 148 memset(chm, 0, sizeof(struct chmcFile)); 149 150 chm->config = config; 151 152 if (strcmp(filename, "-") != 0) { 153 chm->fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644); 154 if (chm->fd < 0) { 155 chmcerr_set(errno, strerror(errno)); 156 chmcerr_return_msg("creat file '%s'", filename); 157 } 158 } else { 159 chm->fd = fileno(stdout); 160 } 161 162 memcpy(itsf->signature, "ITSF", 4); 163 itsf->version = 3; 164 itsf->header_len = _CHMC_ITSF_V3_LEN; 165 itsf->unknown_000c = 1; 166 167 itsf->lang_id = chm->config->language; 168 memcpy(itsf->dir_uuid, CHMC_DIR_UUID, 16); 169 memcpy(itsf->stream_uuid, CHMC_STREAM_UUID, 16); 170 itsf->dir_offset = _CHMC_ITSF_V3_LEN + _CHMC_SECT0_LEN; 171 172 itsf->sect0_offset = _CHMC_ITSF_V3_LEN; 173 itsf->sect0_len = _CHMC_SECT0_LEN; 174 175 sect0->file_len = _CHMC_ITSF_V3_LEN 176 + _CHMC_SECT0_LEN 177 + _CHMC_ITSP_V1_LEN; 178 179 sect0->unknown_0000 = 510; 180 181 memcpy(itsp->signature, "ITSP", 4); 182 itsp->version = 1; 183 itsp->header_len = _CHMC_ITSP_V1_LEN; 184 itsp->unknown_000c = 10; 185 itsp->block_len = _CHMC_CHUNK_LEN; 186 itsp->blockidx_intvl = CHM_IDX_INTVL; 187 itsp->index_depth = 2; 188 189 itsp->unknown_0028 = -1; 190 itsp->lang_id = CHMC_MS_LCID_EN_US; 191 memcpy(itsp->system_uuid, CHMC_SYSTEM_UUID, 16); 192 itsp->header_len2 = _CHMC_ITSP_V1_LEN; 193 memset(itsp->unknown_0048, -1, 12); 194 195 system->version = 3; 196 system->_size = _CHMC_SYSTEM_HDR_LEN + sizeof(struct chmcIndexHeader); 197 198 sysinfo->lcid = CHMC_MS_LCID_EN_US; 199 200 memcpy(idxhdr->signature, "T#SM", 4); 201 idxhdr->unknown_4 = 28582569; // FIXME got from some chm 202 idxhdr->unknown_8 = 1; 203 // idxhdr->full_search = 1; 204 // idxhdr->klinks = 1; 205 // idxhdr->alinks = 0; 206 // idxhdr->timestamp = ???; 207 208 // idxhdr->num_of_topic = 2; // sorry?? 209 idxhdr->off_img_list = -1; 210 // idxhdr->img_type_folder; 211 idxhdr->background = -1; 212 idxhdr->foreground = -1; 213 idxhdr->off_font = -1; 214 idxhdr->win_style = -1; 215 idxhdr->ex_win_style = -1; 216 idxhdr->unknown_34 = -1; 217 idxhdr->off_frame_name = -1; 218 idxhdr->off_win_name = -1; 219 // idxhdr->num_of_info; 220 idxhdr->unknown_44 = 1; 221 // idxhdr->num_of_merge_files; 222 // idxhdr->unknown_4c; 223 224 INIT_LIST_HEAD(&chm->sections_list); 225 INIT_LIST_HEAD(&chm->pmgl_list); 226 INIT_LIST_HEAD(&chm->entries_list); 227 INIT_LIST_HEAD(&chm->pmgi_list); 228 229 chm->strings = malloc(4096); 230 memset(chm->strings, 0, 4096); 231 chm->strings_len = 4096; 232 chm->strings_offset = 1; 233 234 if (chmc_section_add(chm, "Uncompressed") != CHMC_NOERR) 235 chmcerr_return_msg("adding section: Uncompressed"); 236 237 if (chmc_section_add(chm, "MSCompressed") != CHMC_NOERR) 238 chmcerr_return_msg("adding section: MSCompressed"); 239 240 chmc_sections_done(chm); 241 242 return CHMC_NOERR; 243 } 244 245 int chmc_section_add(struct chmcFile *chm, const char *name) 246 { 247 struct chmcSection *section; 248 249 assert(chm); 250 assert(name); 251 252 section = chmc_section_create(chm, name); 253 if (!section) 254 return chmcerr_code(); 255 256 list_add_tail(§ion->list, &chm->sections_list); 257 chm->sections_num++; 258 259 return CHMC_NOERR; 260 } 261 262 struct chmcSection *chmc_section_create(struct chmcFile *chm, 263 const char *name) 264 { 265 struct chmcSection *section; 266 267 assert(name); 268 269 section = calloc(1, sizeof(struct chmcSection)); 270 if (section) { 271 const char *tmpdir; 272 int len; 273 274 len = strlen(name); 275 memcpy(section->name, name, len + 1); 276 section->offset = 0; 277 section->len = 0; 278 279 tmpdir = NULL; 280 if (chm->config != NULL) 281 tmpdir = chm->config->tmpdir; 282 if (tmpdir == NULL) 283 tmpdir = "/tmp/"; 284 285 len = strlen(tmpdir); 286 if (len >= PATH_MAX - 12) { 287 chmcerr_set(errno, strerror(errno)); 288 chmcerr_msg("tmpdir too long: '%s'", tmpdir); 289 goto fail; 290 } 291 292 strcat(section->filename, tmpdir); 293 if (section->filename[len - 1] != '/') 294 strcat(section->filename, "/"); 295 296 if (strcmp("MSCompressed", name) == 0) 297 strcat(section->filename, "chmcCXXXXXX"); 298 else 299 strcat(section->filename, "chmcUXXXXXX"); 300 301 section->fd = mkstemps(section->filename, 0); 302 fprintf(stderr, "temp file: %s\n", section->filename); 303 if (section->fd < 0) { 304 chmcerr_set(errno, strerror(errno)); 305 chmcerr_msg("creat() file '%s'", section->filename); 306 goto fail; 307 } 308 else if (strcmp(section->name, "MSCompressed") == 0) { 309 chmc_reset_table_init(§ion->reset_table_header); 310 chmc_control_data_init(§ion->control_data); 311 INIT_LIST_HEAD(§ion->mark_list); 312 section->mark_count = 0; 313 } 314 } else { 315 chmcerr_set(errno, strerror(errno)); 316 chmcerr_msg("section '%s' allocation failed", name); 317 } 318 319 return section; 320 321 fail: 322 free(section); 323 return NULL; 324 } 325 326 void chmc_reset_table_init(struct chmcLzxcResetTable *reset_table) 327 { 328 reset_table->version = 2; 329 reset_table->block_count = 0; 330 reset_table->entry_size = 8; 331 reset_table->table_offset = _CHMC_LZXC_RESETTABLE_V1_LEN; 332 reset_table->uncompressed_len = 0; 333 reset_table->compressed_len = 0; 334 reset_table->block_len = 0x8000; 335 } 336 337 void chmc_control_data_init(struct chmcLzxcControlData *control_data) 338 { 339 control_data->size = 6; 340 memcpy(control_data->signature, "LZXC", 4); 341 control_data->version = 2; 342 control_data->resetInterval = 2; 343 control_data->windowSize = 2; 344 control_data->windowsPerReset = 1; 345 control_data->unknown_18 = 0; 346 } 347 348 void chmc_sections_done(struct chmcFile *chm) 349 { 350 int len; 351 int i; 352 353 assert(chm); 354 355 chm->sections = malloc(sizeof(struct chmcSection *) * chm->sections_num); 356 if (chm->sections) { 357 struct chmcSection *section; 358 struct list_head *pos; 359 360 i = 0; 361 len = 4; 362 list_for_each(pos, &chm->sections_list) { 363 section = list_entry(pos, struct chmcSection, list); 364 len += 4 + strlen(section->name) * 2; 365 chm->sections[i++] = section; 366 } 367 chmc_namelist_create(chm, len); 368 } else 369 BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__); 370 } 371 372 int chmc_namelist_create(struct chmcFile *chm, int len) 373 { 374 UInt16 *namelist; 375 376 namelist = malloc(len); 377 if (namelist) { 378 struct chmcSection *section; 379 int i, j, k, name_len; 380 381 k = 0; 382 namelist[k++] = len >> 1; 383 namelist[k++] = chm->sections_num; 384 for( i=0; i < chm->sections_num; i++ ) { 385 section = chm->sections[i]; 386 387 name_len = strlen(section->name); 388 namelist[k++] = name_len; 389 for( j=0; j < name_len; j++ ) 390 namelist[k++] = section->name[j]; 391 namelist[k++] = 0; 392 } 393 chmc_add_meta(chm, "::DataSpace/NameList", 0, (UChar *)namelist, len); 394 } 395 else 396 return CHMC_ENOMEM; 397 398 return CHMC_NOERR; 399 } 400 401 struct chmcTreeNode *chmc_add_empty(struct chmcFile *chm, const char *file) 402 { 403 assert(chm); 404 return chmc_add_entry(chm, file, 0, 0, NULL, 0, 0); 405 } 406 407 struct chmcTreeNode *chmc_add_meta(struct chmcFile *chm, const char *metaname, 408 int sect_id, 409 UChar *buf, UInt64 len) 410 { 411 struct chmcSection *section; 412 struct chmcTreeNode *node; 413 414 assert(chm); 415 416 if (sect_id >= chm->sections_num) 417 return NULL; 418 419 section = chm->sections[sect_id]; 420 421 node = chmc_add_entry(chm, metaname, 0, sect_id, buf, section->offset, len); 422 423 if ((node) && (len > 0)) 424 section->offset += len; 425 426 return node; 427 } 428 429 struct chmcTreeNode *chmc_add_entry(struct chmcFile *chm, const char *name, 430 UInt16 prefixlen, int sect_id, UChar *buf, 431 UInt64 offset, UInt64 len) 432 { 433 struct chmcTreeNode *node; 434 435 assert(chm); 436 437 if (sect_id >= (chm->sections_num)) { 438 fprintf(stderr,"sect_id %d >= chm->sections_num %d\n", 439 sect_id, chm->sections_num); 440 return NULL; 441 } 442 443 node = malloc(sizeof(struct chmcTreeNode)); 444 if (node) { 445 node->flags = 0; 446 node->name = strdup( name ); 447 node->prefixlen = prefixlen; 448 node->sect_id = sect_id; 449 node->buf = buf; 450 node->offset = offset; 451 node->len = len; 452 list_add_tail(&node->list, &chm->entries_list); 453 chm->entries_num++; 454 } 455 else 456 BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__); 457 458 return node; 459 } 460 461 void chmc_term(struct chmcFile *chm) 462 { 463 assert(chm); 464 assert(chm->fd > -1); 465 466 free(chm->strings); 467 468 chmc_entries_free(chm); 469 chmc_pmgl_free(chm); 470 chmc_pmgi_free(chm); 471 if (chm->sections) 472 free(chm->sections); 473 chmc_sections_free(chm); 474 475 if (chm->fd != fileno(stdout)) 476 close(chm->fd); 477 } 478 479 void chmc_sections_free(struct chmcFile *chm) 480 { 481 struct chmcSection *section; 482 struct list_head *pos, *q; 483 484 assert(chm); 485 486 list_for_each_safe(pos, q, &chm->sections_list) { 487 section = list_entry(pos, struct chmcSection, list); 488 list_del(pos); 489 chmc_section_destroy(section); 490 } 491 } 492 493 void chmc_section_destroy(struct chmcSection *section) 494 { 495 assert(section); 496 assert(section->fd > -1); 497 498 if (strcmp(section->name, "MSCompressed") == 0) { 499 struct list_head *pos, *q; 500 struct chmcResetTableMark *mark; 501 502 list_for_each_safe(pos, q, §ion->mark_list) { 503 mark = list_entry(pos, struct chmcResetTableMark, list); 504 list_del(pos); 505 free(mark); 506 } 507 } 508 509 close(section->fd); 510 unlink(section->filename); 511 free(section); 512 } 513 514 void chmc_pmgi_free(struct chmcFile *chm) 515 { 516 struct chmcPmgiChunkNode *node; 517 struct list_head *pos, *q; 518 519 assert(chm); 520 521 list_for_each_safe(pos, q, &chm->pmgi_list) { 522 node = list_entry(pos, struct chmcPmgiChunkNode, list); 523 list_del(pos); 524 chmc_pmgi_destroy(node); 525 } 526 } 527 528 void chmc_pmgl_free(struct chmcFile *chm) 529 { 530 struct chmcPmglChunkNode *node; 531 struct list_head *pos, *q; 532 533 assert(chm); 534 535 list_for_each_safe(pos, q, &chm->pmgl_list) { 536 node = list_entry(pos, struct chmcPmglChunkNode, list); 537 list_del(pos); 538 chmc_pmgl_destroy(node); 539 } 540 } 541 542 void chmc_entries_free( struct chmcFile *chm ) 543 { 544 struct chmcTreeNode *node; 545 struct list_head *pos, *q; 546 547 assert(chm); 548 549 list_for_each_safe(pos, q, &chm->entries_list) { 550 node = list_entry(pos, struct chmcTreeNode, list); 551 list_del(pos); 552 chmc_entry_destroy(node); 553 } 554 555 free(chm->sort_entries); 556 } 557 558 UInt32 chmc_strings_add( struct chmcFile *chm, const char *s) 559 { 560 UInt32 len, off; 561 562 /* FIXME null are errors */ 563 564 if (!s || *s == '\0') 565 return 0; 566 567 len = strlen(s); 568 569 off = chm->strings_offset; 570 571 if (off + len + 1 < chm->strings_len) { 572 573 memcpy(&chm->strings[off], s, len + 1); 574 chm->strings_offset += len + 1; 575 576 } else { 577 /* realloc strings */ 578 /* if the string truncate copy til end of chunk 579 then re-copy from 0 of new */ 580 BUG_ON("FIXME: %s: %d: handle more chunk for strings\n", 581 __FILE__, __LINE__); 582 } 583 584 return off; 585 } 586 587 void chmc_entry_destroy( struct chmcTreeNode *node ) 588 { 589 assert(node); 590 assert(node->name); 591 592 free(node->name); 593 if (node->buf && !(node->flags & CHMC_TNFL_STATIC)) 594 free(node->buf); 595 free(node); 596 } 597 598 struct chmcTreeNode *chmc_add_file(struct chmcFile *chm, const char *filename, 599 UInt16 prefixlen, int sect_id, UChar *buf, 600 UInt64 len) 601 { 602 struct chmcSection *section; 603 struct chmcTreeNode *node; 604 605 assert(chm); 606 607 if (sect_id >= chm->sections_num) 608 return NULL; 609 610 section = chm->sections[sect_id]; 611 612 node = chmc_add_entry(chm, filename, prefixlen, sect_id, NULL, 613 section->offset, len); 614 615 if ((node) && (len > 0)) 616 section->offset += len; 617 618 return node; 619 } 620 621 struct chmcTreeNode *chmc_add_dir(struct chmcFile *chm, const char *dir) 622 { 623 assert(chm); 624 625 return chmc_add_entry(chm, dir, 0, 0, NULL, 0, 0); 626 } 627 628 static inline void *chmc_syscat_mem(void *d, void *s, unsigned long len) 629 { 630 memcpy(d, s, len); 631 632 return (char *)d + len; 633 } 634 635 static void *chmc_syscat_entry(Int16 code, void *d, void *s, Int16 len) 636 { 637 d = chmc_syscat_mem(d, &code, 2); 638 d = chmc_syscat_mem(d, &len, 2); 639 640 return chmc_syscat_mem(d, s, len); 641 } 642 643 /* #define DEFAULT_TOPIC "index.htm" */ 644 /* #define TITLE "hello world" */ 645 /* #define LCASEFILE "test" */ 646 647 int chmc_system_done(struct chmcFile *chm) 648 { 649 struct chmcSystem *system; 650 struct chmcSystemInfo *sysinfo; 651 struct chmcIndexHeader *idxhdr; 652 void *sysp, *p; 653 654 assert(chm); 655 656 system = &chm->system; 657 sysinfo = &system->info; 658 idxhdr = &chm->idxhdr; 659 660 // TODO should be set from application 661 // system->_size += (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(UInt32)) /* timestamp */ 662 // + (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(PACKAGE_STRING)) /* compiler */ 663 // + (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(UInt32)) /* eof */ 664 // + (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(DEFAULT_TOPIC)) 665 // + (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(TITLE)) 666 // + 32; 667 668 sysp = malloc(16384); 669 if (sysp) { 670 UInt32 val; 671 #ifndef __REACTOS__ 672 UInt16 code, len; 673 #endif 674 const char *entry_val; 675 676 p = chmc_syscat_mem(sysp, &system->version, sizeof(system->version)); 677 678 val = 0; 679 p = chmc_syscat_entry(SIEC_TIMESTAMP, p, &val, sizeof(val)); 680 p = chmc_syscat_entry(SIEC_COMPVER, p, 681 /*"HHA Version 4.74.8702"*/ 682 PACKAGE_STRING, 683 sizeof(PACKAGE_STRING) 684 /*strlen("HHA Version 4.74.8702")+1*/); 685 p = chmc_syscat_entry(SIEC_SYSINFO, p, 686 sysinfo, sizeof(struct chmcSystemInfo)); 687 688 if (chm->config != NULL && chm->config->deftopic != NULL) 689 entry_val = chm->config->deftopic; 690 else 691 entry_val = "index.htm"; 692 p = chmc_syscat_entry(SIEC_DEFTOPIC, p, (void *)entry_val, 693 strlen(entry_val)+1); 694 695 if (chm->config != NULL && chm->config->title != NULL) 696 entry_val = chm->config->title; 697 else 698 entry_val = "untitled"; 699 p = chmc_syscat_entry(SIEC_TITLE, p, (void *)entry_val, 700 strlen(entry_val)+1); 701 // p = chmc_syscat_entry(SIEC_DEFFONT, p, &val, sizeof(val)); 702 p = chmc_syscat_entry(SIEC_LCASEFILE, p, "siec_lcasefile", 703 strlen("siec_lcasefile")+1); 704 p = chmc_syscat_entry(SIEC_DEFWINDOW, p, 705 "MsdnHelp", strlen("MsdnHelp")+1); 706 707 val = 0; 708 p = chmc_syscat_entry(SIEC_NUMOFINFOT, p, &val, sizeof(val)); 709 710 p = chmc_syscat_entry(SIEC_IDXHDR, p, 711 idxhdr, sizeof(struct chmcIndexHeader)); 712 713 714 val = 0; 715 p = chmc_syscat_entry(SIEC_INFOCHKSUM, p, &val, sizeof(val)); 716 717 system->_size = (char *)p - (char *)sysp; 718 chmc_add_meta(chm, "/#SYSTEM", 0, sysp, system->_size); 719 return CHMC_NOERR; 720 } 721 722 chmcerr_set(CHMC_ENOMEM, "system done: malloc %d bytes", 723 system->_size); 724 725 return CHMC_ENOMEM; 726 } 727 728 int chmc_tree_done( struct chmcFile *chm ) 729 { 730 struct chmcItsfHeader *itsf; 731 struct chmcSect0 *sect0; 732 struct chmcItspHeader *itsp; 733 struct chmcTreeNode *ctrl; 734 UInt32 str_index; 735 const char *val; 736 737 assert(chm); 738 739 itsf = &chm->itsf; 740 sect0 = &chm->sect0; 741 itsp = &chm->itsp; 742 743 chmc_add_dir(chm, "/"); 744 745 ctrl = chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Transform/List", 746 0, (UChar *)chmc_transform_list, 747 sizeof(chmc_transform_list)); 748 if (ctrl) 749 ctrl->flags |= CHMC_TNFL_STATIC; 750 751 chmc_system_done(chm); 752 753 if (chm->config != NULL && chm->config->deftopic != NULL) 754 val = chm->config->deftopic; 755 else 756 val = "index.htm"; 757 758 str_index = chmc_strings_add(chm, val); 759 760 #if 0 761 // FIXME just a test 762 { 763 UChar *p; 764 int len; 765 struct chmcTopicEntry topicEntry; 766 // struct chmcUrlStrEntry urlStrEntry; 767 768 p = malloc(4096); 769 if (p) { 770 memset(p, 0, 4096); 771 len = 0; 772 773 topicEntry.tocidx_offset = 4096; 774 topicEntry.strings_offset = -1; 775 topicEntry.urltbl_offset = 0; 776 topicEntry.in_content = 6; 777 topicEntry.unknown = 0; 778 779 memcpy(p, &topicEntry, sizeof(struct chmcTopicEntry)); 780 len += sizeof(struct chmcTopicEntry); 781 782 chm->idxhdr.num_of_topic++; 783 784 chmc_add_meta(chm, "/#TOPICS", 1, (UChar *)p, len); 785 } else 786 BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__); 787 } 788 #endif 789 790 ctrl = chmc_add_meta(chm, "/#IDXHDR", 1, (void *)&chm->idxhdr, 791 sizeof(struct chmcIndexHeader)); 792 if (ctrl) 793 ctrl->flags |= CHMC_TNFL_STATIC; 794 795 { 796 UInt32 *p; 797 p = malloc(8+196); 798 if (p) { 799 const char *val; 800 memset(p+2, 0, 196); 801 802 p[0] = 1; 803 p[1] = 196; 804 805 p[2+0] = 196; 806 // p[2+2] = 1; 807 // p[2+3] = 0x00000532; 808 // p[2+4] = 0x00062520; 809 810 // p[2+8] = 86; 811 // p[2+9] = 51; 812 // p[2+10] = 872; 813 // p[2+11] = 558; 814 815 // p[2+19] = 220; 816 817 // p[2+27] = 0x00000041; 818 // p[2+28] = 14462; 819 820 if (chm->config != NULL && chm->config->title != NULL) 821 val = chm->config->title; 822 else 823 val = "untitled"; 824 p[2+5] = chmc_strings_add(chm, val); 825 826 if (chm->config != NULL && chm->config->hhc != NULL) 827 val = chm->config->hhc; 828 else 829 val = "toc.hhc"; 830 p[2+24] = chmc_strings_add(chm, val); 831 832 if (chm->config != NULL && chm->config->hhk != NULL) 833 val = chm->config->hhc; 834 else 835 val = "toc.hhk"; 836 p[2+25] = chmc_strings_add(chm, val); 837 p[2+26] = str_index; 838 839 chmc_add_meta(chm, "/#WINDOWS", 1, (UChar *)p, 8+196); 840 } else 841 BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__); 842 } 843 844 ctrl = chmc_add_meta(chm, "/#STRINGS", 1, (void *)chm->strings, 845 chm->strings_len); 846 if (ctrl) 847 ctrl->flags |= CHMC_TNFL_STATIC; 848 849 #if 0 850 // FIXME just a test 851 { 852 UChar *p; 853 int len; 854 struct chmcUrlStrEntry urlStrEntry; 855 856 urlStrEntry.url_offset = 0; 857 urlStrEntry.framename_offset = 0; 858 859 p = malloc(4096); 860 if (p) { 861 memset(p, 0, 4096); 862 *p = 0x42; 863 len = 1; 864 865 memcpy(p + len, &urlStrEntry, sizeof(struct chmcUrlStrEntry)); 866 len += sizeof(struct chmcUrlStrEntry); 867 len += sprintf(p + len, "index.htm" ) + 1; 868 869 memcpy(p + len, &urlStrEntry, sizeof(struct chmcUrlStrEntry)); 870 len += sizeof(struct chmcUrlStrEntry); 871 len += sprintf(p + len, "test.htm" ) + 1; 872 873 chmc_add_meta(chm, "/#URLSTR", 1, (UChar *)p, len); 874 } else 875 BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__); 876 } 877 #endif 878 879 // chmc_add_entry(chm, "/#URLTBL", 0, 1, NULL, 0, 0); 880 // chmc_add_entry(chm, "/#TOPICS", 0, 1, NULL, 0, 0); 881 882 // NOTE NOTE NOTE add any meta compressed before crunch ;-) 883 884 chmc_crunch_lzx(chm, 1); 885 886 chmc_control_data_done(chm); 887 chmc_reset_table_done(chm); 888 889 chmc_add_empty(chm, "/#ITBITS"); 890 891 // NOTE in this implementation compressed Content should be the last file 892 // added to section 0 893 894 chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Content", 0, NULL, 895 chm->sections[1]->offset); 896 897 chmc_entries_qsort(chm); 898 chmc_uncompressed_done(chm); 899 chmc_pmgl_done(chm); 900 901 chmc_pmgi_done(chm); 902 903 itsf->dir_len = _CHMC_ITSP_V1_LEN 904 + (_CHMC_CHUNK_LEN * itsp->num_blocks); 905 906 itsf->data_offset = _CHMC_ITSF_V3_LEN 907 + _CHMC_SECT0_LEN 908 + _CHMC_ITSP_V1_LEN 909 + (_CHMC_CHUNK_LEN * itsp->num_blocks); 910 911 sect0->file_len += _CHMC_CHUNK_LEN * itsp->num_blocks; 912 913 chmc_write(chm); 914 915 { 916 struct chmcSection *section; 917 struct list_head *pos; 918 UChar buf[4096]; 919 920 list_for_each(pos, &chm->sections_list) { 921 section = list_entry(pos, struct chmcSection, list); 922 chmc_appendfile(chm, section->filename, buf, 4096); 923 } 924 } 925 926 return CHMC_NOERR; 927 } 928 929 int chmc_crunch_lzx(struct chmcFile *chm, int sect_id) 930 { 931 struct chmcLzxInfo lzx_info; 932 933 lzx_data *lzxd; 934 int subd_ok = 1; 935 int do_reset = 1; 936 int block_size; 937 lzx_results lzxr; 938 int wsize_code = 16; 939 940 assert(chm); 941 942 #ifndef __REACTOS__ 943 if ((wsize_code < 15) || (wsize_code > 21)) { 944 fprintf(stderr, "window size must be between 15 and 21 inclusive\n"); 945 return CHMC_EINVAL; 946 } 947 #endif 948 949 lzx_info.chm = chm; 950 lzx_info.section = chm->sections[sect_id]; 951 lzx_info.done = 0; 952 lzx_info.todo = lzx_info.section->offset; 953 lzx_info.pos = chm->entries_list.next; 954 lzx_info.error = 0; 955 lzx_info.eof = 0; 956 957 lzx_info.fd = -1; 958 lzx_info.fd_offset = 0; 959 960 chmc_compressed_add_mark(lzx_info.chm, 0); 961 lzx_info.section->reset_table_header.block_count++; 962 963 /* undocumented fact, according to Caie -- 964 block size cannot exceed window size. (why not?) */ 965 /* The block size must not be larger than the window size. 966 While the compressor will create apparently-valid LZX files 967 if this restriction is violated, some decompressors 968 will not handle them. */ 969 970 block_size = 1 << wsize_code; 971 972 // lzx_info.section->control_data.windowSize = wsize_code; 973 // lzx_info.section->control_data.windowsPerReset = block_size; 974 975 lzx_init(&lzxd, wsize_code, 976 _lzx_get_bytes, &lzx_info, _lzx_at_eof, 977 _lzx_put_bytes, &lzx_info, 978 _lzx_mark_frame, &lzx_info); 979 980 while(! _lzx_at_eof(&lzx_info)) { 981 if (do_reset) 982 lzx_reset(lzxd); 983 lzx_compress_block(lzxd, block_size, subd_ok); 984 } 985 lzx_finish(lzxd, &lzxr); 986 987 return CHMC_NOERR; 988 } 989 990 static int _lzx_at_eof(void *arg) 991 { 992 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 993 994 return lzx_info->error || lzx_info->done >= lzx_info->todo || lzx_info->eof; 995 } 996 997 static int _lzx_put_bytes(void *arg, int n, void *buf) 998 { 999 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 1000 struct chmcSect0 *sect0 = &lzx_info->chm->sect0; 1001 int wx; 1002 static int counter = 0; 1003 1004 counter += n; 1005 wx = write(lzx_info->section->fd, buf, n); 1006 sect0->file_len += wx; 1007 lzx_info->section->len += wx; 1008 1009 return wx; 1010 } 1011 1012 static void _lzx_mark_frame(void *arg, uint32_t uncomp, uint32_t comp) 1013 { 1014 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 1015 struct chmcSection *section = lzx_info->chm->sections[1]; 1016 1017 UInt64 compressed; 1018 1019 chmc_dump( "Aligned data at %d(in compressed stream, %d) (%lu/%lu)\n", 1020 uncomp, comp, (unsigned long)lzx_info->done, (unsigned long)lzx_info->todo ); 1021 1022 compressed = comp; 1023 1024 section->reset_table_header.block_count++; 1025 1026 chmc_compressed_add_mark( lzx_info->chm, compressed ); 1027 1028 section->reset_table_header.uncompressed_len = uncomp; 1029 section->reset_table_header.compressed_len = comp; 1030 } 1031 1032 static int _lzx_get_bytes(void *arg, int n, void *buf) 1033 { 1034 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 1035 struct chmcFile *chm = lzx_info->chm; 1036 struct chmcTreeNode *node; 1037 1038 int todo; 1039 int done; 1040 int toread; 1041 int rx; 1042 1043 todo = n; 1044 done = 0; 1045 1046 // compression state machine 1047 // lzx compressor ask for block input bytes 1048 // need to keep current entry file and offset trought blocks 1049 // until last entry 1050 while (todo) { 1051 // end of entries reached? 1052 if (lzx_info->pos == &chm->entries_list) { 1053 lzx_info->eof = 1; 1054 break; 1055 } 1056 1057 node = list_entry( lzx_info->pos, struct chmcTreeNode, list ); 1058 1059 // skip empty files and directories 1060 if (node->len == 0 1061 || strcmp("MSCompressed", chm->sections[node->sect_id]->name)) { 1062 lzx_info->pos = lzx_info->pos->next; 1063 continue; 1064 } 1065 else 1066 if (node->buf) { 1067 // have len and buffer, it's mallocated not file 1068 } 1069 else 1070 if (lzx_info->fd == -1) { 1071 // open file if it isn't 1072 lzx_info->fd = open(node->name, O_RDONLY | O_BINARY); 1073 if (lzx_info->fd < 0) { 1074 chmc_error("%s: %d: error %d: '%s' %s\n", 1075 __FILE__, __LINE__, 1076 errno, node->name, strerror(errno)); 1077 lzx_info->error = 1; 1078 break; 1079 } 1080 } 1081 1082 // read till the end of the file or till the lzx buffer is filled 1083 toread = node->len - lzx_info->fd_offset; 1084 if (toread > todo) 1085 toread = todo; 1086 1087 if (toread <= 0) 1088 continue; 1089 1090 // read input 1091 if (node->buf) { 1092 memcpy((char *)buf + (n - todo), &node->buf[lzx_info->fd_offset], toread); 1093 rx = toread; 1094 } 1095 else 1096 { 1097 rx = read(lzx_info->fd, (char *)buf + (n - todo), toread); 1098 if (rx <= 0) { 1099 int temp = errno; 1100 chmc_error("read error %s \n", strerror(temp)); 1101 lzx_info->error = 2; 1102 break; 1103 } 1104 } 1105 1106 todo -= rx; 1107 lzx_info->fd_offset += rx; 1108 done += rx; 1109 lzx_info->done += rx; 1110 1111 // end of current file reached, goto next entry 1112 if (lzx_info->fd_offset == node->len) { 1113 if (lzx_info->fd > -1) 1114 close(lzx_info->fd); 1115 lzx_info->fd = -1; 1116 lzx_info->fd_offset = 0; 1117 lzx_info->pos = lzx_info->pos->next; 1118 } 1119 } 1120 1121 return done; 1122 } 1123 1124 int chmc_compressed_add_mark(struct chmcFile *chm, UInt64 at) 1125 { 1126 struct chmcSection *section; 1127 struct chmcResetTableMark *mark; 1128 1129 assert(chm); 1130 1131 section = chm->sections[1]; 1132 1133 mark = malloc(_CHMC_RSTTBL_MARK); 1134 if (mark) { 1135 mark->at = at; 1136 chmc_dump("[%d] at: %jd\n", section->mark_count, at); 1137 list_add_tail(&mark->list, §ion->mark_list); 1138 section->mark_count++; 1139 return CHMC_NOERR; 1140 } 1141 1142 return CHMC_ENOMEM; 1143 } 1144 1145 int chmc_control_data_done(struct chmcFile *chm) 1146 { 1147 struct chmcTreeNode *ctrl; 1148 1149 ctrl = chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/ControlData", 1150 0, (UChar *)&chm->sections[1]->control_data, 1151 _CHMC_LZXC_V2_LEN); 1152 1153 if (ctrl) { 1154 ctrl->flags |= CHMC_TNFL_STATIC; 1155 return CHMC_NOERR; 1156 } 1157 1158 return CHMC_ENOMEM; 1159 } 1160 1161 int chmc_reset_table_done(struct chmcFile *chm) 1162 { 1163 struct chmcSection *section; 1164 struct chmcLzxcResetTable *reset_table; 1165 struct list_head *pos; 1166 struct chmcResetTableMark *mark; 1167 1168 UInt64 *at; 1169 int i, len; 1170 1171 section = chm->sections[1]; 1172 1173 len = _CHMC_LZXC_RESETTABLE_V1_LEN + (section->mark_count * sizeof(UInt64)); 1174 1175 reset_table = malloc(len); 1176 1177 if (reset_table) { 1178 memcpy(reset_table, §ion->reset_table_header, 1179 _CHMC_LZXC_RESETTABLE_V1_LEN); 1180 at = (void *)((char *)reset_table + _CHMC_LZXC_RESETTABLE_V1_LEN); 1181 1182 i = 0; 1183 list_for_each(pos, §ion->mark_list) { 1184 mark = list_entry(pos, struct chmcResetTableMark, list); 1185 at[i++] = mark->at; 1186 } 1187 1188 chmc_add_dir(chm, "::DataSpace/Storage/MSCompressed/Transform/" 1189 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/"); 1190 chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Transform/" 1191 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}" 1192 "/InstanceData/ResetTable", 1193 0, (UChar *)reset_table, len); 1194 1195 { // TODO FIXME do better 1196 UInt64 *uncompressed_len = malloc(8); 1197 if (uncompressed_len) { 1198 *uncompressed_len = reset_table->uncompressed_len; 1199 chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/SpanInfo", 1200 0, (UChar *)uncompressed_len, 8); 1201 } 1202 } 1203 1204 return CHMC_NOERR; 1205 } 1206 1207 return CHMC_ENOMEM; 1208 } 1209 1210 void chmc_entries_qsort(struct chmcFile *chm) 1211 { 1212 struct chmcTreeNode *node; 1213 struct list_head *pos; 1214 int i; 1215 1216 assert(chm); 1217 1218 chm->sort_entries = malloc(sizeof(struct chmcTreeNode *) 1219 * chm->entries_num); 1220 1221 i = 0; 1222 list_for_each(pos, &chm->entries_list) { 1223 node = list_entry(pos, struct chmcTreeNode, list); 1224 chm->sort_entries[i++] = node; 1225 } 1226 1227 qsort(chm->sort_entries, chm->entries_num, sizeof(struct chmcTreeNode *), 1228 _entry_cmp); 1229 } 1230 1231 static int _entry_cmp(const void *pva, const void *pvb) 1232 { 1233 const struct chmcTreeNode * const *pa = pva; 1234 const struct chmcTreeNode * const *pb = pvb; 1235 const struct chmcTreeNode *a = *pa, *b = *pb; 1236 1237 return strcmp( &a->name[a->prefixlen], &b->name[b->prefixlen] ); 1238 } 1239 1240 int chmc_uncompressed_done(struct chmcFile *chm) 1241 { 1242 struct chmcSect0 *sect0 = &chm->sect0; 1243 struct chmcTreeNode *node; 1244 struct list_head *pos; 1245 int wx; 1246 1247 list_for_each(pos, &chm->entries_list) { 1248 node = list_entry( pos, struct chmcTreeNode, list ); 1249 1250 if (strcmp( "MSCompressed", chm->sections[node->sect_id]->name ) == 0) 1251 continue; 1252 1253 if ((node->buf) && (node->len > 0)) { 1254 wx = write(chm->sections[node->sect_id]->fd, node->buf, node->len); 1255 sect0->file_len += wx; 1256 } 1257 } 1258 1259 return CHMC_NOERR; 1260 } 1261 1262 void chmc_pmgl_done(struct chmcFile *chm) 1263 { 1264 struct chmcTreeNode *entry; 1265 int i; 1266 1267 assert(chm); 1268 1269 for(i=0; i < chm->entries_num; i++) { 1270 entry = chm->sort_entries[i]; 1271 chmc_pmgl_add_entry(chm, entry); 1272 } 1273 } 1274 1275 int chmc_pmgl_add_entry(struct chmcFile *chm, struct chmcTreeNode *entry) 1276 { 1277 struct chmcPmglChunkNode *pmgl; 1278 struct chmcPmglChunk *chunk; 1279 struct chmcSection *section; 1280 struct chmcItspHeader *itsp = &chm->itsp; 1281 1282 UChar *p; 1283 UInt16 *idx; 1284 int name_len; 1285 int outlen; 1286 int should_idx, idx_intlv; 1287 int free; 1288 1289 assert(chm); 1290 assert(entry); 1291 1292 // check section bound 1293 section = chmc_section_lookup(chm, entry->sect_id); 1294 if (!section) 1295 chmcerr_set_return(CHMC_ENOMEM, "section %d lookup failed: ", 1296 entry->sect_id); 1297 1298 // check chunk space for new entry 1299 name_len = strlen(&entry->name[entry->prefixlen]); 1300 1301 outlen = chmc_encint_len(name_len); 1302 outlen += name_len; 1303 outlen += chmc_encint_len(entry->sect_id); 1304 outlen += chmc_encint_len(entry->offset); 1305 outlen += chmc_encint_len(entry->len); 1306 1307 // look for current pmgl chunk, create if doesn't exist 1308 if (!chm->pmgl_last) { 1309 pmgl = chmc_pmgl_create(); 1310 if (pmgl) 1311 chmc_pmgl_add(chm, pmgl); 1312 else 1313 chmcerr_set_return(CHMC_ENOMEM, "pmgl chunk: "); 1314 } 1315 else 1316 pmgl = chm->pmgl_last; 1317 1318 do { 1319 1320 chunk = &chm->pmgl_last->chunk; 1321 1322 idx_intlv = 1 + ( 1 << itsp->blockidx_intvl ); 1323 should_idx = ( ( chunk->entries_count > 0 ) 1324 && ! ( ( chunk->entries_count + 1 ) % idx_intlv ) 1325 ? 2 : 0 ); 1326 1327 free = sizeof(chunk->data) - pmgl->data_len - pmgl->index_len 1328 - should_idx; 1329 1330 // current(last) chunk doesn't have enough room? force new one 1331 if (outlen + should_idx > free) { 1332 //chm->pmgl_last = NULL; 1333 pmgl = chmc_pmgl_create(); 1334 if ( pmgl ) 1335 chmc_pmgl_add(chm, pmgl); 1336 else 1337 chmcerr_set_return(CHMC_ENOMEM, "pmgl chunk: "); 1338 1339 continue; 1340 } 1341 1342 p = (void *)&chunk->data[pmgl->data_len]; 1343 1344 if (should_idx) { 1345 idx = (void *)((char *)&chunk->data[CHMC_PMGL_DATA_LEN] - pmgl->index_len); 1346 *idx = (char *)p - (char *)&chunk->data; 1347 } 1348 1349 p += chmc_encint(name_len, p); 1350 memcpy(p, &entry->name[entry->prefixlen], name_len); 1351 p += name_len; 1352 p += chmc_encint(entry->sect_id, p); 1353 p += chmc_encint(entry->offset, p); 1354 p += chmc_encint(entry->len, p); 1355 1356 pmgl->data_len += outlen; 1357 pmgl->index_len += should_idx; 1358 1359 chunk->entries_count++; 1360 chunk->header.free_space -= outlen; 1361 break; 1362 1363 } while (1); 1364 1365 return CHMC_NOERR; 1366 } 1367 1368 struct chmcSection *chmc_section_lookup(struct chmcFile *chm, int id) 1369 { 1370 struct chmcSection *current; 1371 struct list_head *pos; 1372 int i; 1373 1374 assert(chm); 1375 1376 i = 0; 1377 list_for_each(pos, &chm->sections_list) { 1378 current = list_entry(pos, struct chmcSection, list); 1379 if (i == id) 1380 return current; 1381 i++; 1382 } 1383 1384 return NULL; 1385 } 1386 1387 struct chmcPmglChunkNode *chmc_pmgl_create(void) 1388 { 1389 struct chmcPmglChunkNode *node; 1390 1391 node = malloc(sizeof(struct chmcPmglChunkNode)); 1392 if (node) 1393 chmc_pmgl_init(node); 1394 1395 return node; 1396 } 1397 1398 void chmc_pmgl_init(struct chmcPmglChunkNode *node) 1399 { 1400 struct chmcPmglChunk *chunk; 1401 1402 assert(node); 1403 1404 node->data_len = 0; 1405 node->index_len = 0; 1406 1407 chunk = &node->chunk; 1408 1409 memcpy(chunk->header.signature, "PMGL", 4); 1410 1411 // FIXME check it is the right len 1412 chunk->header.free_space = CHMC_PMGL_DATA_LEN + 2; 1413 chunk->header.unknown_0008 = 0; 1414 chunk->header.block_prev = -1; 1415 chunk->header.block_next = -1; 1416 1417 memset(chunk->data, 0, CHMC_PMGL_DATA_LEN); 1418 } 1419 1420 void chmc_pmgi_init(struct chmcPmgiChunkNode *node) 1421 { 1422 struct chmcPmgiChunk *chunk; 1423 1424 assert(node); 1425 1426 node->data_len = 0; 1427 node->index_len = 0; 1428 1429 chunk = &node->chunk; 1430 1431 memcpy(chunk->header.signature, "PMGI", 4); 1432 1433 // FIXME check it is the right len 1434 chunk->header.free_space = CHMC_PMGI_DATA_LEN + 2; 1435 // chunk->header.unknown_0008 = 0; 1436 // chunk->header.block_prev = -1; 1437 // chunk->header.block_next = -1; 1438 1439 memset(chunk->data, 0, CHMC_PMGI_DATA_LEN); 1440 } 1441 1442 1443 1444 struct chmcPmgiChunkNode *chmc_pmgi_create(void) 1445 { 1446 struct chmcPmgiChunkNode *node; 1447 1448 node = malloc(sizeof(struct chmcPmgiChunkNode)); 1449 if (node) 1450 chmc_pmgi_init(node); 1451 1452 return node; 1453 } 1454 1455 void chmc_pmgl_destroy(struct chmcPmglChunkNode *node) 1456 { 1457 assert(node); 1458 free(node); 1459 } 1460 1461 void chmc_pmgi_destroy(struct chmcPmgiChunkNode *node) 1462 { 1463 assert(node); 1464 free(node); 1465 } 1466 1467 void chmc_pmgl_add(struct chmcFile *chm, struct chmcPmglChunkNode *pmgl) 1468 { 1469 struct chmcItspHeader *itsp = &chm->itsp; 1470 struct chmcPmglHeader *hdr; 1471 1472 assert(chm); 1473 assert(pmgl); 1474 1475 list_add_tail(&pmgl->list, &chm->pmgl_list); 1476 1477 itsp->index_last = itsp->num_blocks; 1478 1479 hdr = &pmgl->chunk.header; 1480 hdr->block_prev = itsp->num_blocks - 1; 1481 1482 if (chm->pmgl_last) { 1483 hdr = &chm->pmgl_last->chunk.header; 1484 hdr->block_next = itsp->num_blocks; 1485 } 1486 1487 itsp->num_blocks++; 1488 1489 chm->pmgl_last = pmgl; 1490 } 1491 1492 int chmc_pmgi_done(struct chmcFile *chm) 1493 { 1494 struct chmcItspHeader *itsp = &chm->itsp; 1495 struct chmcPmglChunkNode *pmgl; 1496 struct list_head *pos; 1497 1498 int i, j; 1499 char name[256]; //FIXME use malloc 1500 UInt32 name_len; 1501 1502 assert(chm); 1503 1504 // only one pml, omitted pmgi 1505 if (itsp->num_blocks == 1) { 1506 itsp->index_depth = 1; 1507 itsp->index_root = -1; 1508 itsp->index_last = 0; 1509 return CHMC_NOERR; 1510 } 1511 1512 itsp->index_root = itsp->num_blocks; 1513 1514 i = 0; 1515 list_for_each(pos, &chm->pmgl_list) { 1516 pmgl = list_entry(pos, struct chmcPmglChunkNode, list); 1517 j = chmc_decint(&pmgl->chunk.data[0], &name_len); 1518 if (name_len <= 255) { 1519 memcpy(name, &pmgl->chunk.data[j], name_len); 1520 name[name_len] = '\0'; 1521 chmc_pmgi_add_entry(chm, name, i); 1522 } 1523 else 1524 BUG_ON("name_len >= 255(%lu) %.*s\n", (unsigned long)name_len, 255, 1525 &pmgl->chunk.data[j]); 1526 i++; 1527 } 1528 1529 return CHMC_NOERR; 1530 } 1531 1532 int chmc_pmgi_add_entry(struct chmcFile *chm, const char *name, int pmgl_id) 1533 { 1534 struct chmcPmgiChunkNode *pmgi; 1535 struct chmcPmgiChunk *chunk; 1536 struct chmcItspHeader *itsp = &chm->itsp; 1537 1538 UChar *p; 1539 UInt16 *idx; 1540 int name_len; 1541 int outlen; 1542 int should_idx, idx_intlv; 1543 int free; 1544 1545 assert(chm); 1546 1547 // check chunk space for new entry 1548 name_len = strlen(name); 1549 1550 outlen = chmc_encint_len(name_len); 1551 outlen += name_len; 1552 outlen += chmc_encint_len(pmgl_id); 1553 1554 // look for current pmgi chunk, create if doesn't exist 1555 if (!chm->pmgi_last) { 1556 pmgi = chmc_pmgi_create(); 1557 if (pmgi) 1558 chmc_pmgi_add(chm, pmgi); 1559 else 1560 chmcerr_set_return(CHMC_ENOMEM, "pmgi chunk: "); 1561 } 1562 else 1563 pmgi = chm->pmgi_last; 1564 1565 do { 1566 1567 chunk = &chm->pmgi_last->chunk; 1568 1569 idx_intlv = 1 + ( 1 << itsp->blockidx_intvl ); 1570 should_idx = ( ( chunk->entries_count > 0 ) 1571 && ! ( ( chunk->entries_count + 1 ) % idx_intlv ) 1572 ? 2 : 0 ); 1573 1574 free = sizeof(chunk->data) - pmgi->data_len - 1575 pmgi->index_len - should_idx; 1576 1577 // current(last) chunk doesn't have enough room? force new one 1578 if (outlen + should_idx > free) { 1579 pmgi = chmc_pmgi_create(); 1580 if (pmgi) 1581 chmc_pmgi_add(chm, pmgi); 1582 else 1583 chmcerr_set_return(CHMC_ENOMEM, "pmgi chunk: "); 1584 1585 continue; 1586 } 1587 1588 p = (void *)&chunk->data[pmgi->data_len]; 1589 1590 if (should_idx) { 1591 idx = (void *)((char *)&chunk->data[CHMC_PMGI_DATA_LEN] - pmgi->index_len); 1592 *idx = (char *)p - (char *)&chunk->data; 1593 } 1594 1595 p += chmc_encint(name_len, p); 1596 memcpy(p, name, name_len); 1597 p += name_len; 1598 p += chmc_encint(pmgl_id, p); 1599 1600 pmgi->data_len += outlen; 1601 pmgi->index_len += should_idx; 1602 1603 chunk->entries_count++; 1604 chunk->header.free_space -= outlen; 1605 break; 1606 1607 } while (1); 1608 1609 return CHMC_NOERR; 1610 } 1611 1612 void chmc_pmgi_add(struct chmcFile *chm, struct chmcPmgiChunkNode *pmgi) 1613 { 1614 struct chmcItspHeader *itsp = &chm->itsp; 1615 1616 assert(chm); 1617 assert(pmgi); 1618 1619 list_add_tail(&pmgi->list, &chm->pmgi_list); 1620 itsp->num_blocks++; 1621 1622 chm->pmgi_last = pmgi; 1623 } 1624 1625 int chmc_write(struct chmcFile *chm) 1626 { 1627 struct chmcItsfHeader *itsf = &chm->itsf; 1628 struct chmcSect0 *sect0 = &chm->sect0; 1629 struct chmcItspHeader *itsp = &chm->itsp; 1630 1631 struct chmcPmglChunkNode *pmgl; 1632 struct chmcPmgiChunkNode *pmgi; 1633 struct list_head *pos; 1634 1635 assert(chm); 1636 1637 chmc_dump("write itsf %d\n", _CHMC_ITSF_V3_LEN); 1638 write(chm->fd, itsf, _CHMC_ITSF_V3_LEN); 1639 chmc_dump("write sect0 %d\n", _CHMC_SECT0_LEN); 1640 write(chm->fd, sect0, _CHMC_SECT0_LEN); 1641 chmc_dump("write itsp %d\n", _CHMC_ITSP_V1_LEN); 1642 write(chm->fd, itsp, _CHMC_ITSP_V1_LEN); 1643 1644 list_for_each(pos, &chm->pmgl_list) { 1645 pmgl = list_entry(pos, struct chmcPmglChunkNode, list); 1646 chmc_dump("write pmgl %d\n", _CHMC_CHUNK_LEN); 1647 write(chm->fd, &pmgl->chunk, _CHMC_CHUNK_LEN); 1648 } 1649 1650 chmc_dump("itsp->num_blocks %d", itsp->num_blocks); 1651 if (itsp->num_blocks > 1) { 1652 list_for_each( pos, &chm->pmgi_list ) { 1653 pmgi = list_entry(pos, struct chmcPmgiChunkNode, list); 1654 chmc_dump("write pmgi %d\n", _CHMC_CHUNK_LEN); 1655 write(chm->fd, &pmgi->chunk, _CHMC_CHUNK_LEN); 1656 } 1657 } 1658 1659 return CHMC_NOERR; 1660 } 1661 1662 int chmc_appendfile(struct chmcFile *chm, const char *filename, void *buf, 1663 size_t size ) 1664 { 1665 struct stat statbuf; 1666 int in; 1667 off_t todo, toread; 1668 int rx; 1669 1670 if (stat(filename, &statbuf) < 0) 1671 return errno; 1672 1673 in = open(filename, O_RDONLY | O_BINARY); 1674 if (in >= 0) { 1675 todo = statbuf.st_size; 1676 1677 while (todo) { 1678 toread = size; 1679 if (toread > todo) 1680 toread = todo; 1681 1682 rx = read(in, buf, toread); 1683 if (rx > 0) { 1684 write(chm->fd, buf, rx); 1685 todo -= rx; 1686 } 1687 } 1688 1689 close(in); 1690 } 1691 else 1692 BUG_ON("open %s\n", filename); 1693 1694 return CHMC_NOERR; 1695 } 1696