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 UInt16 code, len; 672 const char *entry_val; 673 674 p = chmc_syscat_mem(sysp, &system->version, sizeof(system->version)); 675 676 val = 0; 677 p = chmc_syscat_entry(SIEC_TIMESTAMP, p, &val, sizeof(val)); 678 p = chmc_syscat_entry(SIEC_COMPVER, p, 679 /*"HHA Version 4.74.8702"*/ 680 PACKAGE_STRING, 681 sizeof(PACKAGE_STRING) 682 /*strlen("HHA Version 4.74.8702")+1*/); 683 p = chmc_syscat_entry(SIEC_SYSINFO, p, 684 sysinfo, sizeof(struct chmcSystemInfo)); 685 686 if (chm->config != NULL && chm->config->deftopic != NULL) 687 entry_val = chm->config->deftopic; 688 else 689 entry_val = "index.htm"; 690 p = chmc_syscat_entry(SIEC_DEFTOPIC, p, (void *)entry_val, 691 strlen(entry_val)+1); 692 693 if (chm->config != NULL && chm->config->title != NULL) 694 entry_val = chm->config->title; 695 else 696 entry_val = "untitled"; 697 p = chmc_syscat_entry(SIEC_TITLE, p, (void *)entry_val, 698 strlen(entry_val)+1); 699 // p = chmc_syscat_entry(SIEC_DEFFONT, p, &val, sizeof(val)); 700 p = chmc_syscat_entry(SIEC_LCASEFILE, p, "siec_lcasefile", 701 strlen("siec_lcasefile")+1); 702 p = chmc_syscat_entry(SIEC_DEFWINDOW, p, 703 "MsdnHelp", strlen("MsdnHelp")+1); 704 705 val = 0; 706 p = chmc_syscat_entry(SIEC_NUMOFINFOT, p, &val, sizeof(val)); 707 708 p = chmc_syscat_entry(SIEC_IDXHDR, p, 709 idxhdr, sizeof(struct chmcIndexHeader)); 710 711 712 val = 0; 713 p = chmc_syscat_entry(SIEC_INFOCHKSUM, p, &val, sizeof(val)); 714 715 system->_size = (char *)p - (char *)sysp; 716 chmc_add_meta(chm, "/#SYSTEM", 0, sysp, system->_size); 717 return CHMC_NOERR; 718 } 719 720 chmcerr_set(CHMC_ENOMEM, "system done: malloc %d bytes", 721 system->_size); 722 723 return CHMC_ENOMEM; 724 } 725 726 int chmc_tree_done( struct chmcFile *chm ) 727 { 728 struct chmcItsfHeader *itsf; 729 struct chmcSect0 *sect0; 730 struct chmcItspHeader *itsp; 731 struct chmcTreeNode *ctrl; 732 UInt32 str_index; 733 const char *val; 734 735 assert(chm); 736 737 itsf = &chm->itsf; 738 sect0 = &chm->sect0; 739 itsp = &chm->itsp; 740 741 chmc_add_dir(chm, "/"); 742 743 ctrl = chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Transform/List", 744 0, (UChar *)chmc_transform_list, 745 sizeof(chmc_transform_list)); 746 if (ctrl) 747 ctrl->flags |= CHMC_TNFL_STATIC; 748 749 chmc_system_done(chm); 750 751 if (chm->config != NULL && chm->config->deftopic != NULL) 752 val = chm->config->deftopic; 753 else 754 val = "index.htm"; 755 756 str_index = chmc_strings_add(chm, val); 757 758 #if 0 759 // FIXME just a test 760 { 761 UChar *p; 762 int len; 763 struct chmcTopicEntry topicEntry; 764 // struct chmcUrlStrEntry urlStrEntry; 765 766 p = malloc(4096); 767 if (p) { 768 memset(p, 0, 4096); 769 len = 0; 770 771 topicEntry.tocidx_offset = 4096; 772 topicEntry.strings_offset = -1; 773 topicEntry.urltbl_offset = 0; 774 topicEntry.in_content = 6; 775 topicEntry.unknown = 0; 776 777 memcpy(p, &topicEntry, sizeof(struct chmcTopicEntry)); 778 len += sizeof(struct chmcTopicEntry); 779 780 chm->idxhdr.num_of_topic++; 781 782 chmc_add_meta(chm, "/#TOPICS", 1, (UChar *)p, len); 783 } else 784 BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__); 785 } 786 #endif 787 788 ctrl = chmc_add_meta(chm, "/#IDXHDR", 1, (void *)&chm->idxhdr, 789 sizeof(struct chmcIndexHeader)); 790 if (ctrl) 791 ctrl->flags |= CHMC_TNFL_STATIC; 792 793 { 794 UInt32 *p; 795 p = malloc(8+196); 796 if (p) { 797 const char *val; 798 memset(p+2, 0, 196); 799 800 p[0] = 1; 801 p[1] = 196; 802 803 p[2+0] = 196; 804 // p[2+2] = 1; 805 // p[2+3] = 0x00000532; 806 // p[2+4] = 0x00062520; 807 808 // p[2+8] = 86; 809 // p[2+9] = 51; 810 // p[2+10] = 872; 811 // p[2+11] = 558; 812 813 // p[2+19] = 220; 814 815 // p[2+27] = 0x00000041; 816 // p[2+28] = 14462; 817 818 if (chm->config != NULL && chm->config->title != NULL) 819 val = chm->config->title; 820 else 821 val = "untitled"; 822 p[2+5] = chmc_strings_add(chm, val); 823 824 if (chm->config != NULL && chm->config->hhc != NULL) 825 val = chm->config->hhc; 826 else 827 val = "toc.hhc"; 828 p[2+24] = chmc_strings_add(chm, val); 829 830 if (chm->config != NULL && chm->config->hhk != NULL) 831 val = chm->config->hhc; 832 else 833 val = "toc.hhk"; 834 p[2+25] = chmc_strings_add(chm, val); 835 p[2+26] = str_index; 836 837 chmc_add_meta(chm, "/#WINDOWS", 1, (UChar *)p, 8+196); 838 } else 839 BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__); 840 } 841 842 ctrl = chmc_add_meta(chm, "/#STRINGS", 1, (void *)chm->strings, 843 chm->strings_len); 844 if (ctrl) 845 ctrl->flags |= CHMC_TNFL_STATIC; 846 847 #if 0 848 // FIXME just a test 849 { 850 UChar *p; 851 int len; 852 struct chmcUrlStrEntry urlStrEntry; 853 854 urlStrEntry.url_offset = 0; 855 urlStrEntry.framename_offset = 0; 856 857 p = malloc(4096); 858 if (p) { 859 memset(p, 0, 4096); 860 *p = 0x42; 861 len = 1; 862 863 memcpy(p + len, &urlStrEntry, sizeof(struct chmcUrlStrEntry)); 864 len += sizeof(struct chmcUrlStrEntry); 865 len += sprintf(p + len, "index.htm" ) + 1; 866 867 memcpy(p + len, &urlStrEntry, sizeof(struct chmcUrlStrEntry)); 868 len += sizeof(struct chmcUrlStrEntry); 869 len += sprintf(p + len, "test.htm" ) + 1; 870 871 chmc_add_meta(chm, "/#URLSTR", 1, (UChar *)p, len); 872 } else 873 BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__); 874 } 875 #endif 876 877 // chmc_add_entry(chm, "/#URLTBL", 0, 1, NULL, 0, 0); 878 // chmc_add_entry(chm, "/#TOPICS", 0, 1, NULL, 0, 0); 879 880 // NOTE NOTE NOTE add any meta compressed before crunch ;-) 881 882 chmc_crunch_lzx(chm, 1); 883 884 chmc_control_data_done(chm); 885 chmc_reset_table_done(chm); 886 887 chmc_add_empty(chm, "/#ITBITS"); 888 889 // NOTE in this implementation compressed Content should be the last file 890 // added to section 0 891 892 chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Content", 0, NULL, 893 chm->sections[1]->offset); 894 895 chmc_entries_qsort(chm); 896 chmc_uncompressed_done(chm); 897 chmc_pmgl_done(chm); 898 899 chmc_pmgi_done(chm); 900 901 itsf->dir_len = _CHMC_ITSP_V1_LEN 902 + (_CHMC_CHUNK_LEN * itsp->num_blocks); 903 904 itsf->data_offset = _CHMC_ITSF_V3_LEN 905 + _CHMC_SECT0_LEN 906 + _CHMC_ITSP_V1_LEN 907 + (_CHMC_CHUNK_LEN * itsp->num_blocks); 908 909 sect0->file_len += _CHMC_CHUNK_LEN * itsp->num_blocks; 910 911 chmc_write(chm); 912 913 { 914 struct chmcSection *section; 915 struct list_head *pos; 916 UChar buf[4096]; 917 918 list_for_each(pos, &chm->sections_list) { 919 section = list_entry(pos, struct chmcSection, list); 920 chmc_appendfile(chm, section->filename, buf, 4096); 921 } 922 } 923 924 return CHMC_NOERR; 925 } 926 927 int chmc_crunch_lzx(struct chmcFile *chm, int sect_id) 928 { 929 struct chmcLzxInfo lzx_info; 930 931 lzx_data *lzxd; 932 int subd_ok = 1; 933 int do_reset = 1; 934 int block_size; 935 lzx_results lzxr; 936 int wsize_code = 16; 937 938 assert(chm); 939 940 if ((wsize_code < 15) || (wsize_code > 21)) { 941 fprintf(stderr, "window size must be between 15 and 21 inclusive\n"); 942 return CHMC_EINVAL; 943 } 944 945 lzx_info.chm = chm; 946 lzx_info.section = chm->sections[sect_id]; 947 lzx_info.done = 0; 948 lzx_info.todo = lzx_info.section->offset; 949 lzx_info.pos = chm->entries_list.next; 950 lzx_info.error = 0; 951 lzx_info.eof = 0; 952 953 lzx_info.fd = -1; 954 lzx_info.fd_offset = 0; 955 956 chmc_compressed_add_mark(lzx_info.chm, 0); 957 lzx_info.section->reset_table_header.block_count++; 958 959 /* undocumented fact, according to Caie -- 960 block size cannot exceed window size. (why not?) */ 961 /* The block size must not be larger than the window size. 962 While the compressor will create apparently-valid LZX files 963 if this restriction is violated, some decompressors 964 will not handle them. */ 965 966 block_size = 1 << wsize_code; 967 968 // lzx_info.section->control_data.windowSize = wsize_code; 969 // lzx_info.section->control_data.windowsPerReset = block_size; 970 971 lzx_init(&lzxd, wsize_code, 972 _lzx_get_bytes, &lzx_info, _lzx_at_eof, 973 _lzx_put_bytes, &lzx_info, 974 _lzx_mark_frame, &lzx_info); 975 976 while(! _lzx_at_eof(&lzx_info)) { 977 if (do_reset) 978 lzx_reset(lzxd); 979 lzx_compress_block(lzxd, block_size, subd_ok); 980 } 981 lzx_finish(lzxd, &lzxr); 982 983 return CHMC_NOERR; 984 } 985 986 static int _lzx_at_eof(void *arg) 987 { 988 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 989 990 return lzx_info->error || lzx_info->done >= lzx_info->todo || lzx_info->eof; 991 } 992 993 static int _lzx_put_bytes(void *arg, int n, void *buf) 994 { 995 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 996 struct chmcSect0 *sect0 = &lzx_info->chm->sect0; 997 int wx; 998 static int counter = 0; 999 1000 counter += n; 1001 wx = write(lzx_info->section->fd, buf, n); 1002 sect0->file_len += wx; 1003 lzx_info->section->len += wx; 1004 1005 return wx; 1006 } 1007 1008 static void _lzx_mark_frame(void *arg, uint32_t uncomp, uint32_t comp) 1009 { 1010 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 1011 struct chmcSection *section = lzx_info->chm->sections[1]; 1012 1013 UInt64 compressed; 1014 1015 chmc_dump( "Aligned data at %d(in compressed stream, %d) (%lu/%lu)\n", 1016 uncomp, comp, (unsigned long)lzx_info->done, (unsigned long)lzx_info->todo ); 1017 1018 compressed = comp; 1019 1020 section->reset_table_header.block_count++; 1021 1022 chmc_compressed_add_mark( lzx_info->chm, compressed ); 1023 1024 section->reset_table_header.uncompressed_len = uncomp; 1025 section->reset_table_header.compressed_len = comp; 1026 } 1027 1028 static int _lzx_get_bytes(void *arg, int n, void *buf) 1029 { 1030 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 1031 struct chmcFile *chm = lzx_info->chm; 1032 struct chmcTreeNode *node; 1033 1034 int todo; 1035 int done; 1036 int toread; 1037 int rx; 1038 1039 todo = n; 1040 done = 0; 1041 1042 // compression state machine 1043 // lzx compressor ask for block input bytes 1044 // need to keep current entry file and offset trought blocks 1045 // until last entry 1046 while (todo) { 1047 // end of entries reached? 1048 if (lzx_info->pos == &chm->entries_list) { 1049 lzx_info->eof = 1; 1050 break; 1051 } 1052 1053 node = list_entry( lzx_info->pos, struct chmcTreeNode, list ); 1054 1055 // skip empty files and directories 1056 if (node->len == 0 1057 || strcmp("MSCompressed", chm->sections[node->sect_id]->name)) { 1058 lzx_info->pos = lzx_info->pos->next; 1059 continue; 1060 } 1061 else 1062 if (node->buf) { 1063 // have len and buffer, it's mallocated not file 1064 } 1065 else 1066 if (lzx_info->fd == -1) { 1067 // open file if it isn't 1068 lzx_info->fd = open(node->name, O_RDONLY | O_BINARY); 1069 if (lzx_info->fd < 0) { 1070 chmc_error("%s: %d: error %d: '%s' %s\n", 1071 __FILE__, __LINE__, 1072 errno, node->name, strerror(errno)); 1073 lzx_info->error = 1; 1074 break; 1075 } 1076 } 1077 1078 // read till the end of the file or till the lzx buffer is filled 1079 toread = node->len - lzx_info->fd_offset; 1080 if (toread > todo) 1081 toread = todo; 1082 1083 if (toread <= 0) 1084 continue; 1085 1086 // read input 1087 if (node->buf) { 1088 memcpy((char *)buf + (n - todo), &node->buf[lzx_info->fd_offset], toread); 1089 rx = toread; 1090 } 1091 else 1092 { 1093 rx = read(lzx_info->fd, (char *)buf + (n - todo), toread); 1094 if (rx <= 0) { 1095 int temp = errno; 1096 chmc_error("read error %s \n", strerror(temp)); 1097 lzx_info->error = 2; 1098 break; 1099 } 1100 } 1101 1102 todo -= rx; 1103 lzx_info->fd_offset += rx; 1104 done += rx; 1105 lzx_info->done += rx; 1106 1107 // end of current file reached, goto next entry 1108 if (lzx_info->fd_offset == node->len) { 1109 if (lzx_info->fd > -1) 1110 close(lzx_info->fd); 1111 lzx_info->fd = -1; 1112 lzx_info->fd_offset = 0; 1113 lzx_info->pos = lzx_info->pos->next; 1114 } 1115 } 1116 1117 return done; 1118 } 1119 1120 int chmc_compressed_add_mark(struct chmcFile *chm, UInt64 at) 1121 { 1122 struct chmcSection *section; 1123 struct chmcResetTableMark *mark; 1124 1125 assert(chm); 1126 1127 section = chm->sections[1]; 1128 1129 mark = malloc(_CHMC_RSTTBL_MARK); 1130 if (mark) { 1131 mark->at = at; 1132 chmc_dump("[%d] at: %jd\n", section->mark_count, at); 1133 list_add_tail(&mark->list, §ion->mark_list); 1134 section->mark_count++; 1135 return CHMC_NOERR; 1136 } 1137 1138 return CHMC_ENOMEM; 1139 } 1140 1141 int chmc_control_data_done(struct chmcFile *chm) 1142 { 1143 struct chmcTreeNode *ctrl; 1144 1145 ctrl = chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/ControlData", 1146 0, (UChar *)&chm->sections[1]->control_data, 1147 _CHMC_LZXC_V2_LEN); 1148 1149 if (ctrl) { 1150 ctrl->flags |= CHMC_TNFL_STATIC; 1151 return CHMC_NOERR; 1152 } 1153 1154 return CHMC_ENOMEM; 1155 } 1156 1157 int chmc_reset_table_done(struct chmcFile *chm) 1158 { 1159 struct chmcSection *section; 1160 struct chmcLzxcResetTable *reset_table; 1161 struct list_head *pos; 1162 struct chmcResetTableMark *mark; 1163 1164 UInt64 *at; 1165 int i, len; 1166 1167 section = chm->sections[1]; 1168 1169 len = _CHMC_LZXC_RESETTABLE_V1_LEN + (section->mark_count * sizeof(UInt64)); 1170 1171 reset_table = malloc(len); 1172 1173 if (reset_table) { 1174 memcpy(reset_table, §ion->reset_table_header, 1175 _CHMC_LZXC_RESETTABLE_V1_LEN); 1176 at = (void *)((char *)reset_table + _CHMC_LZXC_RESETTABLE_V1_LEN); 1177 1178 i = 0; 1179 list_for_each(pos, §ion->mark_list) { 1180 mark = list_entry(pos, struct chmcResetTableMark, list); 1181 at[i++] = mark->at; 1182 } 1183 1184 chmc_add_dir(chm, "::DataSpace/Storage/MSCompressed/Transform/" 1185 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/"); 1186 chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Transform/" 1187 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}" 1188 "/InstanceData/ResetTable", 1189 0, (UChar *)reset_table, len); 1190 1191 { // TODO FIXME do better 1192 UInt64 *uncompressed_len = malloc(8); 1193 if (uncompressed_len) { 1194 *uncompressed_len = reset_table->uncompressed_len; 1195 chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/SpanInfo", 1196 0, (UChar *)uncompressed_len, 8); 1197 } 1198 } 1199 1200 return CHMC_NOERR; 1201 } 1202 1203 return CHMC_ENOMEM; 1204 } 1205 1206 void chmc_entries_qsort(struct chmcFile *chm) 1207 { 1208 struct chmcTreeNode *node; 1209 struct list_head *pos; 1210 int i; 1211 1212 assert(chm); 1213 1214 chm->sort_entries = malloc(sizeof(struct chmcTreeNode *) 1215 * chm->entries_num); 1216 1217 i = 0; 1218 list_for_each(pos, &chm->entries_list) { 1219 node = list_entry(pos, struct chmcTreeNode, list); 1220 chm->sort_entries[i++] = node; 1221 } 1222 1223 qsort(chm->sort_entries, chm->entries_num, sizeof(struct chmcTreeNode *), 1224 _entry_cmp); 1225 } 1226 1227 static int _entry_cmp(const void *pva, const void *pvb) 1228 { 1229 const struct chmcTreeNode * const *pa = pva; 1230 const struct chmcTreeNode * const *pb = pvb; 1231 const struct chmcTreeNode *a = *pa, *b = *pb; 1232 1233 return strcmp( &a->name[a->prefixlen], &b->name[b->prefixlen] ); 1234 } 1235 1236 int chmc_uncompressed_done(struct chmcFile *chm) 1237 { 1238 struct chmcSect0 *sect0 = &chm->sect0; 1239 struct chmcTreeNode *node; 1240 struct list_head *pos; 1241 int wx; 1242 1243 list_for_each(pos, &chm->entries_list) { 1244 node = list_entry( pos, struct chmcTreeNode, list ); 1245 1246 if (strcmp( "MSCompressed", chm->sections[node->sect_id]->name ) == 0) 1247 continue; 1248 1249 if ((node->buf) && (node->len > 0)) { 1250 wx = write(chm->sections[node->sect_id]->fd, node->buf, node->len); 1251 sect0->file_len += wx; 1252 } 1253 } 1254 1255 return CHMC_NOERR; 1256 } 1257 1258 void chmc_pmgl_done(struct chmcFile *chm) 1259 { 1260 struct chmcTreeNode *entry; 1261 int i; 1262 1263 assert(chm); 1264 1265 for(i=0; i < chm->entries_num; i++) { 1266 entry = chm->sort_entries[i]; 1267 chmc_pmgl_add_entry(chm, entry); 1268 } 1269 } 1270 1271 int chmc_pmgl_add_entry(struct chmcFile *chm, struct chmcTreeNode *entry) 1272 { 1273 struct chmcPmglChunkNode *pmgl; 1274 struct chmcPmglChunk *chunk; 1275 struct chmcSection *section; 1276 struct chmcItspHeader *itsp = &chm->itsp; 1277 1278 UChar *p; 1279 UInt16 *idx; 1280 int name_len; 1281 int outlen; 1282 int should_idx, idx_intlv; 1283 int free; 1284 1285 assert(chm); 1286 assert(entry); 1287 1288 // check section bound 1289 section = chmc_section_lookup(chm, entry->sect_id); 1290 if (!section) 1291 chmcerr_set_return(CHMC_ENOMEM, "section %d lookup failed: ", 1292 entry->sect_id); 1293 1294 // check chunk space for new entry 1295 name_len = strlen(&entry->name[entry->prefixlen]); 1296 1297 outlen = chmc_encint_len(name_len); 1298 outlen += name_len; 1299 outlen += chmc_encint_len(entry->sect_id); 1300 outlen += chmc_encint_len(entry->offset); 1301 outlen += chmc_encint_len(entry->len); 1302 1303 // look for current pmgl chunk, create if doesn't exist 1304 if (!chm->pmgl_last) { 1305 pmgl = chmc_pmgl_create(); 1306 if (pmgl) 1307 chmc_pmgl_add(chm, pmgl); 1308 else 1309 chmcerr_set_return(CHMC_ENOMEM, "pmgl chunk: "); 1310 } 1311 else 1312 pmgl = chm->pmgl_last; 1313 1314 do { 1315 1316 chunk = &chm->pmgl_last->chunk; 1317 1318 idx_intlv = 1 + ( 1 << itsp->blockidx_intvl ); 1319 should_idx = ( ( chunk->entries_count > 0 ) 1320 && ! ( ( chunk->entries_count + 1 ) % idx_intlv ) 1321 ? 2 : 0 ); 1322 1323 free = sizeof(chunk->data) - pmgl->data_len - pmgl->index_len 1324 - should_idx; 1325 1326 // current(last) chunk doesn't have enough room? force new one 1327 if (outlen + should_idx > free) { 1328 //chm->pmgl_last = NULL; 1329 pmgl = chmc_pmgl_create(); 1330 if ( pmgl ) 1331 chmc_pmgl_add(chm, pmgl); 1332 else 1333 chmcerr_set_return(CHMC_ENOMEM, "pmgl chunk: "); 1334 1335 continue; 1336 } 1337 1338 p = (void *)&chunk->data[pmgl->data_len]; 1339 1340 if (should_idx) { 1341 idx = (void *)((char *)&chunk->data[CHMC_PMGL_DATA_LEN] - pmgl->index_len); 1342 *idx = (char *)p - (char *)&chunk->data; 1343 } 1344 1345 p += chmc_encint(name_len, p); 1346 memcpy(p, &entry->name[entry->prefixlen], name_len); 1347 p += name_len; 1348 p += chmc_encint(entry->sect_id, p); 1349 p += chmc_encint(entry->offset, p); 1350 p += chmc_encint(entry->len, p); 1351 1352 pmgl->data_len += outlen; 1353 pmgl->index_len += should_idx; 1354 1355 chunk->entries_count++; 1356 chunk->header.free_space -= outlen; 1357 break; 1358 1359 } while (1); 1360 1361 return CHMC_NOERR; 1362 } 1363 1364 struct chmcSection *chmc_section_lookup(struct chmcFile *chm, int id) 1365 { 1366 struct chmcSection *current; 1367 struct list_head *pos; 1368 int i; 1369 1370 assert(chm); 1371 1372 i = 0; 1373 list_for_each(pos, &chm->sections_list) { 1374 current = list_entry(pos, struct chmcSection, list); 1375 if (i == id) 1376 return current; 1377 i++; 1378 } 1379 1380 return NULL; 1381 } 1382 1383 struct chmcPmglChunkNode *chmc_pmgl_create(void) 1384 { 1385 struct chmcPmglChunkNode *node; 1386 1387 node = malloc(sizeof(struct chmcPmglChunkNode)); 1388 if (node) 1389 chmc_pmgl_init(node); 1390 1391 return node; 1392 } 1393 1394 void chmc_pmgl_init(struct chmcPmglChunkNode *node) 1395 { 1396 struct chmcPmglChunk *chunk; 1397 1398 assert(node); 1399 1400 node->data_len = 0; 1401 node->index_len = 0; 1402 1403 chunk = &node->chunk; 1404 1405 memcpy(chunk->header.signature, "PMGL", 4); 1406 1407 // FIXME check it is the right len 1408 chunk->header.free_space = CHMC_PMGL_DATA_LEN + 2; 1409 chunk->header.unknown_0008 = 0; 1410 chunk->header.block_prev = -1; 1411 chunk->header.block_next = -1; 1412 1413 memset(chunk->data, 0, CHMC_PMGL_DATA_LEN); 1414 } 1415 1416 void chmc_pmgi_init(struct chmcPmgiChunkNode *node) 1417 { 1418 struct chmcPmgiChunk *chunk; 1419 1420 assert(node); 1421 1422 node->data_len = 0; 1423 node->index_len = 0; 1424 1425 chunk = &node->chunk; 1426 1427 memcpy(chunk->header.signature, "PMGI", 4); 1428 1429 // FIXME check it is the right len 1430 chunk->header.free_space = CHMC_PMGI_DATA_LEN + 2; 1431 // chunk->header.unknown_0008 = 0; 1432 // chunk->header.block_prev = -1; 1433 // chunk->header.block_next = -1; 1434 1435 memset(chunk->data, 0, CHMC_PMGI_DATA_LEN); 1436 } 1437 1438 1439 1440 struct chmcPmgiChunkNode *chmc_pmgi_create(void) 1441 { 1442 struct chmcPmgiChunkNode *node; 1443 1444 node = malloc(sizeof(struct chmcPmgiChunkNode)); 1445 if (node) 1446 chmc_pmgi_init(node); 1447 1448 return node; 1449 } 1450 1451 void chmc_pmgl_destroy(struct chmcPmglChunkNode *node) 1452 { 1453 assert(node); 1454 free(node); 1455 } 1456 1457 void chmc_pmgi_destroy(struct chmcPmgiChunkNode *node) 1458 { 1459 assert(node); 1460 free(node); 1461 } 1462 1463 void chmc_pmgl_add(struct chmcFile *chm, struct chmcPmglChunkNode *pmgl) 1464 { 1465 struct chmcItspHeader *itsp = &chm->itsp; 1466 struct chmcPmglHeader *hdr; 1467 1468 assert(chm); 1469 assert(pmgl); 1470 1471 list_add_tail(&pmgl->list, &chm->pmgl_list); 1472 1473 itsp->index_last = itsp->num_blocks; 1474 1475 hdr = &pmgl->chunk.header; 1476 hdr->block_prev = itsp->num_blocks - 1; 1477 1478 if (chm->pmgl_last) { 1479 hdr = &chm->pmgl_last->chunk.header; 1480 hdr->block_next = itsp->num_blocks; 1481 } 1482 1483 itsp->num_blocks++; 1484 1485 chm->pmgl_last = pmgl; 1486 } 1487 1488 int chmc_pmgi_done(struct chmcFile *chm) 1489 { 1490 struct chmcItspHeader *itsp = &chm->itsp; 1491 struct chmcPmglChunkNode *pmgl; 1492 struct list_head *pos; 1493 1494 int i, j; 1495 char name[256]; //FIXME use malloc 1496 UInt32 name_len; 1497 1498 assert(chm); 1499 1500 // only one pml, omitted pmgi 1501 if (itsp->num_blocks == 1) { 1502 itsp->index_depth = 1; 1503 itsp->index_root = -1; 1504 itsp->index_last = 0; 1505 return CHMC_NOERR; 1506 } 1507 1508 itsp->index_root = itsp->num_blocks; 1509 1510 i = 0; 1511 list_for_each(pos, &chm->pmgl_list) { 1512 pmgl = list_entry(pos, struct chmcPmglChunkNode, list); 1513 j = chmc_decint(&pmgl->chunk.data[0], &name_len); 1514 if (name_len <= 255) { 1515 memcpy(name, &pmgl->chunk.data[j], name_len); 1516 name[name_len] = '\0'; 1517 chmc_pmgi_add_entry(chm, name, i); 1518 } 1519 else 1520 BUG_ON("name_len >= 255(%lu) %.*s\n", (unsigned long)name_len, 255, 1521 &pmgl->chunk.data[j]); 1522 i++; 1523 } 1524 1525 return CHMC_NOERR; 1526 } 1527 1528 int chmc_pmgi_add_entry(struct chmcFile *chm, const char *name, int pmgl_id) 1529 { 1530 struct chmcPmgiChunkNode *pmgi; 1531 struct chmcPmgiChunk *chunk; 1532 struct chmcItspHeader *itsp = &chm->itsp; 1533 1534 UChar *p; 1535 UInt16 *idx; 1536 int name_len; 1537 int outlen; 1538 int should_idx, idx_intlv; 1539 int free; 1540 1541 assert(chm); 1542 1543 // check chunk space for new entry 1544 name_len = strlen(name); 1545 1546 outlen = chmc_encint_len(name_len); 1547 outlen += name_len; 1548 outlen += chmc_encint_len(pmgl_id); 1549 1550 // look for current pmgi chunk, create if doesn't exist 1551 if (!chm->pmgi_last) { 1552 pmgi = chmc_pmgi_create(); 1553 if (pmgi) 1554 chmc_pmgi_add(chm, pmgi); 1555 else 1556 chmcerr_set_return(CHMC_ENOMEM, "pmgi chunk: "); 1557 } 1558 else 1559 pmgi = chm->pmgi_last; 1560 1561 do { 1562 1563 chunk = &chm->pmgi_last->chunk; 1564 1565 idx_intlv = 1 + ( 1 << itsp->blockidx_intvl ); 1566 should_idx = ( ( chunk->entries_count > 0 ) 1567 && ! ( ( chunk->entries_count + 1 ) % idx_intlv ) 1568 ? 2 : 0 ); 1569 1570 free = sizeof(chunk->data) - pmgi->data_len - 1571 pmgi->index_len - should_idx; 1572 1573 // current(last) chunk doesn't have enough room? force new one 1574 if (outlen + should_idx > free) { 1575 pmgi = chmc_pmgi_create(); 1576 if (pmgi) 1577 chmc_pmgi_add(chm, pmgi); 1578 else 1579 chmcerr_set_return(CHMC_ENOMEM, "pmgi chunk: "); 1580 1581 continue; 1582 } 1583 1584 p = (void *)&chunk->data[pmgi->data_len]; 1585 1586 if (should_idx) { 1587 idx = (void *)((char *)&chunk->data[CHMC_PMGI_DATA_LEN] - pmgi->index_len); 1588 *idx = (char *)p - (char *)&chunk->data; 1589 } 1590 1591 p += chmc_encint(name_len, p); 1592 memcpy(p, name, name_len); 1593 p += name_len; 1594 p += chmc_encint(pmgl_id, p); 1595 1596 pmgi->data_len += outlen; 1597 pmgi->index_len += should_idx; 1598 1599 chunk->entries_count++; 1600 chunk->header.free_space -= outlen; 1601 break; 1602 1603 } while (1); 1604 1605 return CHMC_NOERR; 1606 } 1607 1608 void chmc_pmgi_add(struct chmcFile *chm, struct chmcPmgiChunkNode *pmgi) 1609 { 1610 struct chmcItspHeader *itsp = &chm->itsp; 1611 1612 assert(chm); 1613 assert(pmgi); 1614 1615 list_add_tail(&pmgi->list, &chm->pmgi_list); 1616 itsp->num_blocks++; 1617 1618 chm->pmgi_last = pmgi; 1619 } 1620 1621 int chmc_write(struct chmcFile *chm) 1622 { 1623 struct chmcItsfHeader *itsf = &chm->itsf; 1624 struct chmcSect0 *sect0 = &chm->sect0; 1625 struct chmcItspHeader *itsp = &chm->itsp; 1626 1627 struct chmcPmglChunkNode *pmgl; 1628 struct chmcPmgiChunkNode *pmgi; 1629 struct list_head *pos; 1630 1631 assert(chm); 1632 1633 chmc_dump("write itsf %d\n", _CHMC_ITSF_V3_LEN); 1634 write(chm->fd, itsf, _CHMC_ITSF_V3_LEN); 1635 chmc_dump("write sect0 %d\n", _CHMC_SECT0_LEN); 1636 write(chm->fd, sect0, _CHMC_SECT0_LEN); 1637 chmc_dump("write itsp %d\n", _CHMC_ITSP_V1_LEN); 1638 write(chm->fd, itsp, _CHMC_ITSP_V1_LEN); 1639 1640 list_for_each(pos, &chm->pmgl_list) { 1641 pmgl = list_entry(pos, struct chmcPmglChunkNode, list); 1642 chmc_dump("write pmgl %d\n", _CHMC_CHUNK_LEN); 1643 write(chm->fd, &pmgl->chunk, _CHMC_CHUNK_LEN); 1644 } 1645 1646 chmc_dump("itsp->num_blocks %d", itsp->num_blocks); 1647 if (itsp->num_blocks > 1) { 1648 list_for_each( pos, &chm->pmgi_list ) { 1649 pmgi = list_entry(pos, struct chmcPmgiChunkNode, list); 1650 chmc_dump("write pmgi %d\n", _CHMC_CHUNK_LEN); 1651 write(chm->fd, &pmgi->chunk, _CHMC_CHUNK_LEN); 1652 } 1653 } 1654 1655 return CHMC_NOERR; 1656 } 1657 1658 int chmc_appendfile(struct chmcFile *chm, const char *filename, void *buf, 1659 size_t size ) 1660 { 1661 struct stat statbuf; 1662 int in; 1663 off_t todo, toread; 1664 int rx; 1665 1666 if (stat(filename, &statbuf) < 0) 1667 return errno; 1668 1669 in = open(filename, O_RDONLY | O_BINARY); 1670 if (in >= 0) { 1671 todo = statbuf.st_size; 1672 1673 while (todo) { 1674 toread = size; 1675 if (toread > todo) 1676 toread = todo; 1677 1678 rx = read(in, buf, toread); 1679 if (rx > 0) { 1680 write(chm->fd, buf, rx); 1681 todo -= rx; 1682 } 1683 } 1684 1685 close(in); 1686 } 1687 else 1688 BUG_ON("open %s\n", filename); 1689 1690 return CHMC_NOERR; 1691 } 1692