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