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