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 if ((wsize_code < 15) || (wsize_code > 21)) { 943 fprintf(stderr, "window size must be between 15 and 21 inclusive\n"); 944 return CHMC_EINVAL; 945 } 946 947 lzx_info.chm = chm; 948 lzx_info.section = chm->sections[sect_id]; 949 lzx_info.done = 0; 950 lzx_info.todo = lzx_info.section->offset; 951 lzx_info.pos = chm->entries_list.next; 952 lzx_info.error = 0; 953 lzx_info.eof = 0; 954 955 lzx_info.fd = -1; 956 lzx_info.fd_offset = 0; 957 958 chmc_compressed_add_mark(lzx_info.chm, 0); 959 lzx_info.section->reset_table_header.block_count++; 960 961 /* undocumented fact, according to Caie -- 962 block size cannot exceed window size. (why not?) */ 963 /* The block size must not be larger than the window size. 964 While the compressor will create apparently-valid LZX files 965 if this restriction is violated, some decompressors 966 will not handle them. */ 967 968 block_size = 1 << wsize_code; 969 970 // lzx_info.section->control_data.windowSize = wsize_code; 971 // lzx_info.section->control_data.windowsPerReset = block_size; 972 973 lzx_init(&lzxd, wsize_code, 974 _lzx_get_bytes, &lzx_info, _lzx_at_eof, 975 _lzx_put_bytes, &lzx_info, 976 _lzx_mark_frame, &lzx_info); 977 978 while(! _lzx_at_eof(&lzx_info)) { 979 if (do_reset) 980 lzx_reset(lzxd); 981 lzx_compress_block(lzxd, block_size, subd_ok); 982 } 983 lzx_finish(lzxd, &lzxr); 984 985 return CHMC_NOERR; 986 } 987 988 static int _lzx_at_eof(void *arg) 989 { 990 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 991 992 return lzx_info->error || lzx_info->done >= lzx_info->todo || lzx_info->eof; 993 } 994 995 static int _lzx_put_bytes(void *arg, int n, void *buf) 996 { 997 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 998 struct chmcSect0 *sect0 = &lzx_info->chm->sect0; 999 int wx; 1000 static int counter = 0; 1001 1002 counter += n; 1003 wx = write(lzx_info->section->fd, buf, n); 1004 sect0->file_len += wx; 1005 lzx_info->section->len += wx; 1006 1007 return wx; 1008 } 1009 1010 static void _lzx_mark_frame(void *arg, uint32_t uncomp, uint32_t comp) 1011 { 1012 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 1013 struct chmcSection *section = lzx_info->chm->sections[1]; 1014 1015 UInt64 compressed; 1016 1017 chmc_dump( "Aligned data at %d(in compressed stream, %d) (%lu/%lu)\n", 1018 uncomp, comp, (unsigned long)lzx_info->done, (unsigned long)lzx_info->todo ); 1019 1020 compressed = comp; 1021 1022 section->reset_table_header.block_count++; 1023 1024 chmc_compressed_add_mark( lzx_info->chm, compressed ); 1025 1026 section->reset_table_header.uncompressed_len = uncomp; 1027 section->reset_table_header.compressed_len = comp; 1028 } 1029 1030 static int _lzx_get_bytes(void *arg, int n, void *buf) 1031 { 1032 struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg; 1033 struct chmcFile *chm = lzx_info->chm; 1034 struct chmcTreeNode *node; 1035 1036 int todo; 1037 int done; 1038 int toread; 1039 int rx; 1040 1041 todo = n; 1042 done = 0; 1043 1044 // compression state machine 1045 // lzx compressor ask for block input bytes 1046 // need to keep current entry file and offset trought blocks 1047 // until last entry 1048 while (todo) { 1049 // end of entries reached? 1050 if (lzx_info->pos == &chm->entries_list) { 1051 lzx_info->eof = 1; 1052 break; 1053 } 1054 1055 node = list_entry( lzx_info->pos, struct chmcTreeNode, list ); 1056 1057 // skip empty files and directories 1058 if (node->len == 0 1059 || strcmp("MSCompressed", chm->sections[node->sect_id]->name)) { 1060 lzx_info->pos = lzx_info->pos->next; 1061 continue; 1062 } 1063 else 1064 if (node->buf) { 1065 // have len and buffer, it's mallocated not file 1066 } 1067 else 1068 if (lzx_info->fd == -1) { 1069 // open file if it isn't 1070 lzx_info->fd = open(node->name, O_RDONLY | O_BINARY); 1071 if (lzx_info->fd < 0) { 1072 chmc_error("%s: %d: error %d: '%s' %s\n", 1073 __FILE__, __LINE__, 1074 errno, node->name, strerror(errno)); 1075 lzx_info->error = 1; 1076 break; 1077 } 1078 } 1079 1080 // read till the end of the file or till the lzx buffer is filled 1081 toread = node->len - lzx_info->fd_offset; 1082 if (toread > todo) 1083 toread = todo; 1084 1085 if (toread <= 0) 1086 continue; 1087 1088 // read input 1089 if (node->buf) { 1090 memcpy((char *)buf + (n - todo), &node->buf[lzx_info->fd_offset], toread); 1091 rx = toread; 1092 } 1093 else 1094 { 1095 rx = read(lzx_info->fd, (char *)buf + (n - todo), toread); 1096 if (rx <= 0) { 1097 int temp = errno; 1098 chmc_error("read error %s \n", strerror(temp)); 1099 lzx_info->error = 2; 1100 break; 1101 } 1102 } 1103 1104 todo -= rx; 1105 lzx_info->fd_offset += rx; 1106 done += rx; 1107 lzx_info->done += rx; 1108 1109 // end of current file reached, goto next entry 1110 if (lzx_info->fd_offset == node->len) { 1111 if (lzx_info->fd > -1) 1112 close(lzx_info->fd); 1113 lzx_info->fd = -1; 1114 lzx_info->fd_offset = 0; 1115 lzx_info->pos = lzx_info->pos->next; 1116 } 1117 } 1118 1119 return done; 1120 } 1121 1122 int chmc_compressed_add_mark(struct chmcFile *chm, UInt64 at) 1123 { 1124 struct chmcSection *section; 1125 struct chmcResetTableMark *mark; 1126 1127 assert(chm); 1128 1129 section = chm->sections[1]; 1130 1131 mark = malloc(_CHMC_RSTTBL_MARK); 1132 if (mark) { 1133 mark->at = at; 1134 chmc_dump("[%d] at: %jd\n", section->mark_count, at); 1135 list_add_tail(&mark->list, §ion->mark_list); 1136 section->mark_count++; 1137 return CHMC_NOERR; 1138 } 1139 1140 return CHMC_ENOMEM; 1141 } 1142 1143 int chmc_control_data_done(struct chmcFile *chm) 1144 { 1145 struct chmcTreeNode *ctrl; 1146 1147 ctrl = chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/ControlData", 1148 0, (UChar *)&chm->sections[1]->control_data, 1149 _CHMC_LZXC_V2_LEN); 1150 1151 if (ctrl) { 1152 ctrl->flags |= CHMC_TNFL_STATIC; 1153 return CHMC_NOERR; 1154 } 1155 1156 return CHMC_ENOMEM; 1157 } 1158 1159 int chmc_reset_table_done(struct chmcFile *chm) 1160 { 1161 struct chmcSection *section; 1162 struct chmcLzxcResetTable *reset_table; 1163 struct list_head *pos; 1164 struct chmcResetTableMark *mark; 1165 1166 UInt64 *at; 1167 int i, len; 1168 1169 section = chm->sections[1]; 1170 1171 len = _CHMC_LZXC_RESETTABLE_V1_LEN + (section->mark_count * sizeof(UInt64)); 1172 1173 reset_table = malloc(len); 1174 1175 if (reset_table) { 1176 memcpy(reset_table, §ion->reset_table_header, 1177 _CHMC_LZXC_RESETTABLE_V1_LEN); 1178 at = (void *)((char *)reset_table + _CHMC_LZXC_RESETTABLE_V1_LEN); 1179 1180 i = 0; 1181 list_for_each(pos, §ion->mark_list) { 1182 mark = list_entry(pos, struct chmcResetTableMark, list); 1183 at[i++] = mark->at; 1184 } 1185 1186 chmc_add_dir(chm, "::DataSpace/Storage/MSCompressed/Transform/" 1187 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/"); 1188 chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Transform/" 1189 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}" 1190 "/InstanceData/ResetTable", 1191 0, (UChar *)reset_table, len); 1192 1193 { // TODO FIXME do better 1194 UInt64 *uncompressed_len = malloc(8); 1195 if (uncompressed_len) { 1196 *uncompressed_len = reset_table->uncompressed_len; 1197 chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/SpanInfo", 1198 0, (UChar *)uncompressed_len, 8); 1199 } 1200 } 1201 1202 return CHMC_NOERR; 1203 } 1204 1205 return CHMC_ENOMEM; 1206 } 1207 1208 void chmc_entries_qsort(struct chmcFile *chm) 1209 { 1210 struct chmcTreeNode *node; 1211 struct list_head *pos; 1212 int i; 1213 1214 assert(chm); 1215 1216 chm->sort_entries = malloc(sizeof(struct chmcTreeNode *) 1217 * chm->entries_num); 1218 1219 i = 0; 1220 list_for_each(pos, &chm->entries_list) { 1221 node = list_entry(pos, struct chmcTreeNode, list); 1222 chm->sort_entries[i++] = node; 1223 } 1224 1225 qsort(chm->sort_entries, chm->entries_num, sizeof(struct chmcTreeNode *), 1226 _entry_cmp); 1227 } 1228 1229 static int _entry_cmp(const void *pva, const void *pvb) 1230 { 1231 const struct chmcTreeNode * const *pa = pva; 1232 const struct chmcTreeNode * const *pb = pvb; 1233 const struct chmcTreeNode *a = *pa, *b = *pb; 1234 1235 return strcmp( &a->name[a->prefixlen], &b->name[b->prefixlen] ); 1236 } 1237 1238 int chmc_uncompressed_done(struct chmcFile *chm) 1239 { 1240 struct chmcSect0 *sect0 = &chm->sect0; 1241 struct chmcTreeNode *node; 1242 struct list_head *pos; 1243 int wx; 1244 1245 list_for_each(pos, &chm->entries_list) { 1246 node = list_entry( pos, struct chmcTreeNode, list ); 1247 1248 if (strcmp( "MSCompressed", chm->sections[node->sect_id]->name ) == 0) 1249 continue; 1250 1251 if ((node->buf) && (node->len > 0)) { 1252 wx = write(chm->sections[node->sect_id]->fd, node->buf, node->len); 1253 sect0->file_len += wx; 1254 } 1255 } 1256 1257 return CHMC_NOERR; 1258 } 1259 1260 void chmc_pmgl_done(struct chmcFile *chm) 1261 { 1262 struct chmcTreeNode *entry; 1263 int i; 1264 1265 assert(chm); 1266 1267 for(i=0; i < chm->entries_num; i++) { 1268 entry = chm->sort_entries[i]; 1269 chmc_pmgl_add_entry(chm, entry); 1270 } 1271 } 1272 1273 int chmc_pmgl_add_entry(struct chmcFile *chm, struct chmcTreeNode *entry) 1274 { 1275 struct chmcPmglChunkNode *pmgl; 1276 struct chmcPmglChunk *chunk; 1277 struct chmcSection *section; 1278 struct chmcItspHeader *itsp = &chm->itsp; 1279 1280 UChar *p; 1281 UInt16 *idx; 1282 int name_len; 1283 int outlen; 1284 int should_idx, idx_intlv; 1285 int free; 1286 1287 assert(chm); 1288 assert(entry); 1289 1290 // check section bound 1291 section = chmc_section_lookup(chm, entry->sect_id); 1292 if (!section) 1293 chmcerr_set_return(CHMC_ENOMEM, "section %d lookup failed: ", 1294 entry->sect_id); 1295 1296 // check chunk space for new entry 1297 name_len = strlen(&entry->name[entry->prefixlen]); 1298 1299 outlen = chmc_encint_len(name_len); 1300 outlen += name_len; 1301 outlen += chmc_encint_len(entry->sect_id); 1302 outlen += chmc_encint_len(entry->offset); 1303 outlen += chmc_encint_len(entry->len); 1304 1305 // look for current pmgl chunk, create if doesn't exist 1306 if (!chm->pmgl_last) { 1307 pmgl = chmc_pmgl_create(); 1308 if (pmgl) 1309 chmc_pmgl_add(chm, pmgl); 1310 else 1311 chmcerr_set_return(CHMC_ENOMEM, "pmgl chunk: "); 1312 } 1313 else 1314 pmgl = chm->pmgl_last; 1315 1316 do { 1317 1318 chunk = &chm->pmgl_last->chunk; 1319 1320 idx_intlv = 1 + ( 1 << itsp->blockidx_intvl ); 1321 should_idx = ( ( chunk->entries_count > 0 ) 1322 && ! ( ( chunk->entries_count + 1 ) % idx_intlv ) 1323 ? 2 : 0 ); 1324 1325 free = sizeof(chunk->data) - pmgl->data_len - pmgl->index_len 1326 - should_idx; 1327 1328 // current(last) chunk doesn't have enough room? force new one 1329 if (outlen + should_idx > free) { 1330 //chm->pmgl_last = NULL; 1331 pmgl = chmc_pmgl_create(); 1332 if ( pmgl ) 1333 chmc_pmgl_add(chm, pmgl); 1334 else 1335 chmcerr_set_return(CHMC_ENOMEM, "pmgl chunk: "); 1336 1337 continue; 1338 } 1339 1340 p = (void *)&chunk->data[pmgl->data_len]; 1341 1342 if (should_idx) { 1343 idx = (void *)((char *)&chunk->data[CHMC_PMGL_DATA_LEN] - pmgl->index_len); 1344 *idx = (char *)p - (char *)&chunk->data; 1345 } 1346 1347 p += chmc_encint(name_len, p); 1348 memcpy(p, &entry->name[entry->prefixlen], name_len); 1349 p += name_len; 1350 p += chmc_encint(entry->sect_id, p); 1351 p += chmc_encint(entry->offset, p); 1352 p += chmc_encint(entry->len, p); 1353 1354 pmgl->data_len += outlen; 1355 pmgl->index_len += should_idx; 1356 1357 chunk->entries_count++; 1358 chunk->header.free_space -= outlen; 1359 break; 1360 1361 } while (1); 1362 1363 return CHMC_NOERR; 1364 } 1365 1366 struct chmcSection *chmc_section_lookup(struct chmcFile *chm, int id) 1367 { 1368 struct chmcSection *current; 1369 struct list_head *pos; 1370 int i; 1371 1372 assert(chm); 1373 1374 i = 0; 1375 list_for_each(pos, &chm->sections_list) { 1376 current = list_entry(pos, struct chmcSection, list); 1377 if (i == id) 1378 return current; 1379 i++; 1380 } 1381 1382 return NULL; 1383 } 1384 1385 struct chmcPmglChunkNode *chmc_pmgl_create(void) 1386 { 1387 struct chmcPmglChunkNode *node; 1388 1389 node = malloc(sizeof(struct chmcPmglChunkNode)); 1390 if (node) 1391 chmc_pmgl_init(node); 1392 1393 return node; 1394 } 1395 1396 void chmc_pmgl_init(struct chmcPmglChunkNode *node) 1397 { 1398 struct chmcPmglChunk *chunk; 1399 1400 assert(node); 1401 1402 node->data_len = 0; 1403 node->index_len = 0; 1404 1405 chunk = &node->chunk; 1406 1407 memcpy(chunk->header.signature, "PMGL", 4); 1408 1409 // FIXME check it is the right len 1410 chunk->header.free_space = CHMC_PMGL_DATA_LEN + 2; 1411 chunk->header.unknown_0008 = 0; 1412 chunk->header.block_prev = -1; 1413 chunk->header.block_next = -1; 1414 1415 memset(chunk->data, 0, CHMC_PMGL_DATA_LEN); 1416 } 1417 1418 void chmc_pmgi_init(struct chmcPmgiChunkNode *node) 1419 { 1420 struct chmcPmgiChunk *chunk; 1421 1422 assert(node); 1423 1424 node->data_len = 0; 1425 node->index_len = 0; 1426 1427 chunk = &node->chunk; 1428 1429 memcpy(chunk->header.signature, "PMGI", 4); 1430 1431 // FIXME check it is the right len 1432 chunk->header.free_space = CHMC_PMGI_DATA_LEN + 2; 1433 // chunk->header.unknown_0008 = 0; 1434 // chunk->header.block_prev = -1; 1435 // chunk->header.block_next = -1; 1436 1437 memset(chunk->data, 0, CHMC_PMGI_DATA_LEN); 1438 } 1439 1440 1441 1442 struct chmcPmgiChunkNode *chmc_pmgi_create(void) 1443 { 1444 struct chmcPmgiChunkNode *node; 1445 1446 node = malloc(sizeof(struct chmcPmgiChunkNode)); 1447 if (node) 1448 chmc_pmgi_init(node); 1449 1450 return node; 1451 } 1452 1453 void chmc_pmgl_destroy(struct chmcPmglChunkNode *node) 1454 { 1455 assert(node); 1456 free(node); 1457 } 1458 1459 void chmc_pmgi_destroy(struct chmcPmgiChunkNode *node) 1460 { 1461 assert(node); 1462 free(node); 1463 } 1464 1465 void chmc_pmgl_add(struct chmcFile *chm, struct chmcPmglChunkNode *pmgl) 1466 { 1467 struct chmcItspHeader *itsp = &chm->itsp; 1468 struct chmcPmglHeader *hdr; 1469 1470 assert(chm); 1471 assert(pmgl); 1472 1473 list_add_tail(&pmgl->list, &chm->pmgl_list); 1474 1475 itsp->index_last = itsp->num_blocks; 1476 1477 hdr = &pmgl->chunk.header; 1478 hdr->block_prev = itsp->num_blocks - 1; 1479 1480 if (chm->pmgl_last) { 1481 hdr = &chm->pmgl_last->chunk.header; 1482 hdr->block_next = itsp->num_blocks; 1483 } 1484 1485 itsp->num_blocks++; 1486 1487 chm->pmgl_last = pmgl; 1488 } 1489 1490 int chmc_pmgi_done(struct chmcFile *chm) 1491 { 1492 struct chmcItspHeader *itsp = &chm->itsp; 1493 struct chmcPmglChunkNode *pmgl; 1494 struct list_head *pos; 1495 1496 int i, j; 1497 char name[256]; //FIXME use malloc 1498 UInt32 name_len; 1499 1500 assert(chm); 1501 1502 // only one pml, omitted pmgi 1503 if (itsp->num_blocks == 1) { 1504 itsp->index_depth = 1; 1505 itsp->index_root = -1; 1506 itsp->index_last = 0; 1507 return CHMC_NOERR; 1508 } 1509 1510 itsp->index_root = itsp->num_blocks; 1511 1512 i = 0; 1513 list_for_each(pos, &chm->pmgl_list) { 1514 pmgl = list_entry(pos, struct chmcPmglChunkNode, list); 1515 j = chmc_decint(&pmgl->chunk.data[0], &name_len); 1516 if (name_len <= 255) { 1517 memcpy(name, &pmgl->chunk.data[j], name_len); 1518 name[name_len] = '\0'; 1519 chmc_pmgi_add_entry(chm, name, i); 1520 } 1521 else 1522 BUG_ON("name_len >= 255(%lu) %.*s\n", (unsigned long)name_len, 255, 1523 &pmgl->chunk.data[j]); 1524 i++; 1525 } 1526 1527 return CHMC_NOERR; 1528 } 1529 1530 int chmc_pmgi_add_entry(struct chmcFile *chm, const char *name, int pmgl_id) 1531 { 1532 struct chmcPmgiChunkNode *pmgi; 1533 struct chmcPmgiChunk *chunk; 1534 struct chmcItspHeader *itsp = &chm->itsp; 1535 1536 UChar *p; 1537 UInt16 *idx; 1538 int name_len; 1539 int outlen; 1540 int should_idx, idx_intlv; 1541 int free; 1542 1543 assert(chm); 1544 1545 // check chunk space for new entry 1546 name_len = strlen(name); 1547 1548 outlen = chmc_encint_len(name_len); 1549 outlen += name_len; 1550 outlen += chmc_encint_len(pmgl_id); 1551 1552 // look for current pmgi chunk, create if doesn't exist 1553 if (!chm->pmgi_last) { 1554 pmgi = chmc_pmgi_create(); 1555 if (pmgi) 1556 chmc_pmgi_add(chm, pmgi); 1557 else 1558 chmcerr_set_return(CHMC_ENOMEM, "pmgi chunk: "); 1559 } 1560 else 1561 pmgi = chm->pmgi_last; 1562 1563 do { 1564 1565 chunk = &chm->pmgi_last->chunk; 1566 1567 idx_intlv = 1 + ( 1 << itsp->blockidx_intvl ); 1568 should_idx = ( ( chunk->entries_count > 0 ) 1569 && ! ( ( chunk->entries_count + 1 ) % idx_intlv ) 1570 ? 2 : 0 ); 1571 1572 free = sizeof(chunk->data) - pmgi->data_len - 1573 pmgi->index_len - should_idx; 1574 1575 // current(last) chunk doesn't have enough room? force new one 1576 if (outlen + should_idx > free) { 1577 pmgi = chmc_pmgi_create(); 1578 if (pmgi) 1579 chmc_pmgi_add(chm, pmgi); 1580 else 1581 chmcerr_set_return(CHMC_ENOMEM, "pmgi chunk: "); 1582 1583 continue; 1584 } 1585 1586 p = (void *)&chunk->data[pmgi->data_len]; 1587 1588 if (should_idx) { 1589 idx = (void *)((char *)&chunk->data[CHMC_PMGI_DATA_LEN] - pmgi->index_len); 1590 *idx = (char *)p - (char *)&chunk->data; 1591 } 1592 1593 p += chmc_encint(name_len, p); 1594 memcpy(p, name, name_len); 1595 p += name_len; 1596 p += chmc_encint(pmgl_id, p); 1597 1598 pmgi->data_len += outlen; 1599 pmgi->index_len += should_idx; 1600 1601 chunk->entries_count++; 1602 chunk->header.free_space -= outlen; 1603 break; 1604 1605 } while (1); 1606 1607 return CHMC_NOERR; 1608 } 1609 1610 void chmc_pmgi_add(struct chmcFile *chm, struct chmcPmgiChunkNode *pmgi) 1611 { 1612 struct chmcItspHeader *itsp = &chm->itsp; 1613 1614 assert(chm); 1615 assert(pmgi); 1616 1617 list_add_tail(&pmgi->list, &chm->pmgi_list); 1618 itsp->num_blocks++; 1619 1620 chm->pmgi_last = pmgi; 1621 } 1622 1623 int chmc_write(struct chmcFile *chm) 1624 { 1625 struct chmcItsfHeader *itsf = &chm->itsf; 1626 struct chmcSect0 *sect0 = &chm->sect0; 1627 struct chmcItspHeader *itsp = &chm->itsp; 1628 1629 struct chmcPmglChunkNode *pmgl; 1630 struct chmcPmgiChunkNode *pmgi; 1631 struct list_head *pos; 1632 1633 assert(chm); 1634 1635 chmc_dump("write itsf %d\n", _CHMC_ITSF_V3_LEN); 1636 write(chm->fd, itsf, _CHMC_ITSF_V3_LEN); 1637 chmc_dump("write sect0 %d\n", _CHMC_SECT0_LEN); 1638 write(chm->fd, sect0, _CHMC_SECT0_LEN); 1639 chmc_dump("write itsp %d\n", _CHMC_ITSP_V1_LEN); 1640 write(chm->fd, itsp, _CHMC_ITSP_V1_LEN); 1641 1642 list_for_each(pos, &chm->pmgl_list) { 1643 pmgl = list_entry(pos, struct chmcPmglChunkNode, list); 1644 chmc_dump("write pmgl %d\n", _CHMC_CHUNK_LEN); 1645 write(chm->fd, &pmgl->chunk, _CHMC_CHUNK_LEN); 1646 } 1647 1648 chmc_dump("itsp->num_blocks %d", itsp->num_blocks); 1649 if (itsp->num_blocks > 1) { 1650 list_for_each( pos, &chm->pmgi_list ) { 1651 pmgi = list_entry(pos, struct chmcPmgiChunkNode, list); 1652 chmc_dump("write pmgi %d\n", _CHMC_CHUNK_LEN); 1653 write(chm->fd, &pmgi->chunk, _CHMC_CHUNK_LEN); 1654 } 1655 } 1656 1657 return CHMC_NOERR; 1658 } 1659 1660 int chmc_appendfile(struct chmcFile *chm, const char *filename, void *buf, 1661 size_t size ) 1662 { 1663 struct stat statbuf; 1664 int in; 1665 off_t todo, toread; 1666 int rx; 1667 1668 if (stat(filename, &statbuf) < 0) 1669 return errno; 1670 1671 in = open(filename, O_RDONLY | O_BINARY); 1672 if (in >= 0) { 1673 todo = statbuf.st_size; 1674 1675 while (todo) { 1676 toread = size; 1677 if (toread > todo) 1678 toread = todo; 1679 1680 rx = read(in, buf, toread); 1681 if (rx > 0) { 1682 write(chm->fd, buf, rx); 1683 todo -= rx; 1684 } 1685 } 1686 1687 close(in); 1688 } 1689 else 1690 BUG_ON("open %s\n", filename); 1691 1692 return CHMC_NOERR; 1693 } 1694