3 Copyright(C) 2010 Alex Andreotti <alex.andreotti@gmail.com>
5 This file is part of chmc.
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.
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.
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/>.
29 #if defined(_WIN32) || defined(__APPLE__)
31 #include <sys/types.h>
43 #include "../lzx_compress/lzx_config.h"
44 #include "../lzx_compress/lzx_compress.h"
46 #define PACKAGE_STRING "hhpcomp development version"
48 /* if O_BINARY is not defined, the system is probably not expecting any such flag */
53 int chmc_section_add(struct chmcFile
*chm
, const char *name
);
54 struct chmcSection
* chmc_section_create(struct chmcFile
*chm
,
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
,
77 struct chmcTreeNode
*chmc_add_dir(struct chmcFile
*chm
, const char *dir
);
78 struct chmcTreeNode
*chmc_add_empty(struct chmcFile
*chm
, const char *file
);
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
);
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
);
91 void chmc_entries_qsort(struct chmcFile
*chm
);
92 static int _entry_cmp(const void *pva
, const void *pvb
);
94 struct chmcSection
*chmc_section_lookup(struct chmcFile
*chm
, int id
);
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
);
105 struct chmcFile
*chm
;
106 struct chmcSection
*section
;
111 struct list_head
*pos
;
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 };
121 int chmc_init(struct chmcFile
*chm
, const char *filename
,
122 struct chmcConfig
*config
)
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
;
136 memset(chm
, 0, sizeof(struct chmcFile
));
138 chm
->config
= config
;
140 if (strcmp(filename
, "-") != 0) {
141 chm
->fd
= open(filename
, O_RDWR
| O_CREAT
| O_TRUNC
| O_BINARY
, 0644);
143 chmcerr_set(errno
, strerror(errno
));
144 chmcerr_return_msg("creat file '%s'", filename
);
147 chm
->fd
= fileno(stdout
);
150 memcpy(itsf
->signature
, "ITSF", 4);
152 itsf
->header_len
= _CHMC_ITSF_V3_LEN
;
153 itsf
->unknown_000c
= 1;
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
;
160 itsf
->sect0_offset
= _CHMC_ITSF_V3_LEN
;
161 itsf
->sect0_len
= _CHMC_SECT0_LEN
;
163 sect0
->file_len
= _CHMC_ITSF_V3_LEN
167 sect0
->unknown_0000
= 510;
169 memcpy(itsp
->signature
, "ITSP", 4);
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;
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);
184 system
->_size
= _CHMC_SYSTEM_HDR_LEN
+ sizeof(struct chmcIndexHeader
);
186 sysinfo
->lcid
= CHMC_MS_LCID_EN_US
;
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 = ???;
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;
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
);
217 chm
->strings
= malloc(4096);
218 memset(chm
->strings
, 0, 4096);
219 chm
->strings_len
= 4096;
220 chm
->strings_offset
= 1;
222 if (chmc_section_add(chm
, "Uncompressed") != CHMC_NOERR
)
223 chmcerr_return_msg("adding section: Uncompressed");
225 if (chmc_section_add(chm
, "MSCompressed") != CHMC_NOERR
)
226 chmcerr_return_msg("adding section: MSCompressed");
228 chmc_sections_done(chm
);
233 int chmc_section_add(struct chmcFile
*chm
, const char *name
)
235 struct chmcSection
*section
;
240 section
= chmc_section_create(chm
, name
);
242 return chmcerr_code();
244 list_add_tail(§ion
->list
, &chm
->sections_list
);
250 struct chmcSection
*chmc_section_create(struct chmcFile
*chm
,
253 struct chmcSection
*section
;
257 section
= calloc(1, sizeof(struct chmcSection
));
263 memcpy(section
->name
, name
, len
+ 1);
268 if (chm
->config
!= NULL
)
269 tmpdir
= chm
->config
->tmpdir
;
273 len
= strlen(tmpdir
);
274 if (len
>= PATH_MAX
- 12) {
275 chmcerr_set(errno
, strerror(errno
));
276 chmcerr_msg("tmpdir too long: '%s'", tmpdir
);
280 strcat(section
->filename
, tmpdir
);
281 if (section
->filename
[len
- 1] != '/')
282 strcat(section
->filename
, "/");
284 if (strcmp("MSCompressed", name
) == 0)
285 strcat(section
->filename
, "chmcCXXXXXX");
287 strcat(section
->filename
, "chmcUXXXXXX");
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
);
296 else if (strcmp(section
->name
, "MSCompressed") == 0) {
297 chmc_reset_table_init(§ion
->reset_table_header
);
298 chmc_control_data_init(§ion
->control_data
);
299 INIT_LIST_HEAD(§ion
->mark_list
);
300 section
->mark_count
= 0;
303 chmcerr_set(errno
, strerror(errno
));
304 chmcerr_msg("section '%s' allocation failed", name
);
314 void chmc_reset_table_init(struct chmcLzxcResetTable
*reset_table
)
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;
325 void chmc_control_data_init(struct chmcLzxcControlData
*control_data
)
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;
336 void chmc_sections_done(struct chmcFile
*chm
)
343 chm
->sections
= malloc(sizeof(struct chmcSection
*) * chm
->sections_num
);
345 struct chmcSection
*section
;
346 struct list_head
*pos
;
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
;
355 chmc_namelist_create(chm
, len
);
357 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
360 int chmc_namelist_create(struct chmcFile
*chm
, int len
)
364 namelist
= malloc(len
);
366 struct chmcSection
*section
;
367 int i
, j
, k
, name_len
;
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
];
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
];
381 chmc_add_meta(chm
, "::DataSpace/NameList", 0, (UChar
*)namelist
, len
);
389 struct chmcTreeNode
*chmc_add_empty(struct chmcFile
*chm
, const char *file
)
392 return chmc_add_entry(chm
, file
, 0, 0, NULL
, 0, 0);
395 struct chmcTreeNode
*chmc_add_meta(struct chmcFile
*chm
, const char *metaname
,
397 UChar
*buf
, UInt64 len
)
399 struct chmcSection
*section
;
400 struct chmcTreeNode
*node
;
404 if (sect_id
>= chm
->sections_num
)
407 section
= chm
->sections
[sect_id
];
409 node
= chmc_add_entry(chm
, metaname
, 0, sect_id
, buf
, section
->offset
, len
);
411 if ((node
) && (len
> 0))
412 section
->offset
+= len
;
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
)
421 struct chmcTreeNode
*node
;
425 if (sect_id
>= (chm
->sections_num
)) {
426 fprintf(stderr
,"sect_id %d >= chm->sections_num %d\n",
427 sect_id
, chm
->sections_num
);
431 node
= malloc(sizeof(struct chmcTreeNode
));
434 node
->name
= strdup( name
);
435 node
->prefixlen
= prefixlen
;
436 node
->sect_id
= sect_id
;
438 node
->offset
= offset
;
440 list_add_tail(&node
->list
, &chm
->entries_list
);
444 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
449 void chmc_term(struct chmcFile
*chm
)
452 assert(chm
->fd
> -1);
456 chmc_entries_free(chm
);
461 chmc_sections_free(chm
);
463 if (chm
->fd
!= fileno(stdout
))
467 void chmc_sections_free(struct chmcFile
*chm
)
469 struct chmcSection
*section
;
470 struct list_head
*pos
, *q
;
474 list_for_each_safe(pos
, q
, &chm
->sections_list
) {
475 section
= list_entry(pos
, struct chmcSection
, list
);
477 chmc_section_destroy(section
);
481 void chmc_section_destroy(struct chmcSection
*section
)
484 assert(section
->fd
> -1);
486 if (strcmp(section
->name
, "MSCompressed") == 0) {
487 struct list_head
*pos
, *q
;
488 struct chmcResetTableMark
*mark
;
490 list_for_each_safe(pos
, q
, §ion
->mark_list
) {
491 mark
= list_entry(pos
, struct chmcResetTableMark
, list
);
498 unlink(section
->filename
);
502 void chmc_pmgi_free(struct chmcFile
*chm
)
504 struct chmcPmgiChunkNode
*node
;
505 struct list_head
*pos
, *q
;
509 list_for_each_safe(pos
, q
, &chm
->pmgi_list
) {
510 node
= list_entry(pos
, struct chmcPmgiChunkNode
, list
);
512 chmc_pmgi_destroy(node
);
516 void chmc_pmgl_free(struct chmcFile
*chm
)
518 struct chmcPmglChunkNode
*node
;
519 struct list_head
*pos
, *q
;
523 list_for_each_safe(pos
, q
, &chm
->pmgl_list
) {
524 node
= list_entry(pos
, struct chmcPmglChunkNode
, list
);
526 chmc_pmgl_destroy(node
);
530 void chmc_entries_free( struct chmcFile
*chm
)
532 struct chmcTreeNode
*node
;
533 struct list_head
*pos
, *q
;
537 list_for_each_safe(pos
, q
, &chm
->entries_list
) {
538 node
= list_entry(pos
, struct chmcTreeNode
, list
);
540 chmc_entry_destroy(node
);
543 free(chm
->sort_entries
);
546 UInt32
chmc_strings_add( struct chmcFile
*chm
, const char *s
)
550 /* FIXME null are errors */
552 if (!s
|| *s
== '\0')
557 off
= chm
->strings_offset
;
559 if (off
+ len
+ 1 < chm
->strings_len
) {
561 memcpy(&chm
->strings
[off
], s
, len
+ 1);
562 chm
->strings_offset
+= len
+ 1;
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",
575 void chmc_entry_destroy( struct chmcTreeNode
*node
)
581 if (node
->buf
&& !(node
->flags
& CHMC_TNFL_STATIC
))
586 struct chmcTreeNode
*chmc_add_file(struct chmcFile
*chm
, const char *filename
,
587 UInt16 prefixlen
, int sect_id
, UChar
*buf
,
590 struct chmcSection
*section
;
591 struct chmcTreeNode
*node
;
595 if (sect_id
>= chm
->sections_num
)
598 section
= chm
->sections
[sect_id
];
600 node
= chmc_add_entry(chm
, filename
, prefixlen
, sect_id
, NULL
,
601 section
->offset
, len
);
603 if ((node
) && (len
> 0))
604 section
->offset
+= len
;
609 struct chmcTreeNode
*chmc_add_dir(struct chmcFile
*chm
, const char *dir
)
613 return chmc_add_entry(chm
, dir
, 0, 0, NULL
, 0, 0);
616 static inline void *chmc_syscat_mem(void *d
, void *s
, unsigned long len
)
620 return (char *)d
+ len
;
623 static void *chmc_syscat_entry(Int16 code
, void *d
, void *s
, Int16 len
)
625 d
= chmc_syscat_mem(d
, &code
, 2);
626 d
= chmc_syscat_mem(d
, &len
, 2);
628 return chmc_syscat_mem(d
, s
, len
);
631 /* #define DEFAULT_TOPIC "index.htm" */
632 /* #define TITLE "hello world" */
633 /* #define LCASEFILE "test" */
635 int chmc_system_done(struct chmcFile
*chm
)
637 struct chmcSystem
*system
;
638 struct chmcSystemInfo
*sysinfo
;
639 struct chmcIndexHeader
*idxhdr
;
644 system
= &chm
->system
;
645 sysinfo
= &system
->info
;
646 idxhdr
= &chm
->idxhdr
;
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))
656 sysp
= malloc(16384);
660 const char *entry_val
;
662 p
= chmc_syscat_mem(sysp
, &system
->version
, sizeof(system
->version
));
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"*/
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
));
674 if (chm
->config
!= NULL
&& chm
->config
->deftopic
!= NULL
)
675 entry_val
= chm
->config
->deftopic
;
677 entry_val
= "index.htm";
678 p
= chmc_syscat_entry(SIEC_DEFTOPIC
, p
, (void *)entry_val
,
679 strlen(entry_val
)+1);
681 if (chm
->config
!= NULL
&& chm
->config
->title
!= NULL
)
682 entry_val
= chm
->config
->title
;
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);
694 p
= chmc_syscat_entry(SIEC_NUMOFINFOT
, p
, &val
, sizeof(val
));
696 p
= chmc_syscat_entry(SIEC_IDXHDR
, p
,
697 idxhdr
, sizeof(struct chmcIndexHeader
));
701 p
= chmc_syscat_entry(SIEC_INFOCHKSUM
, p
, &val
, sizeof(val
));
703 system
->_size
= (char *)p
- (char *)sysp
;
704 chmc_add_meta(chm
, "/#SYSTEM", 0, sysp
, system
->_size
);
708 chmcerr_set(CHMC_ENOMEM
, "system done: malloc %d bytes",
714 int chmc_tree_done( struct chmcFile
*chm
)
716 struct chmcItsfHeader
*itsf
;
717 struct chmcSect0
*sect0
;
718 struct chmcItspHeader
*itsp
;
719 struct chmcTreeNode
*ctrl
;
729 chmc_add_dir(chm
, "/");
731 ctrl
= chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/Transform/List",
732 0, (UChar
*)chmc_transform_list
,
733 sizeof(chmc_transform_list
));
735 ctrl
->flags
|= CHMC_TNFL_STATIC
;
737 chmc_system_done(chm
);
739 if (chm
->config
!= NULL
&& chm
->config
->deftopic
!= NULL
)
740 val
= chm
->config
->deftopic
;
744 str_index
= chmc_strings_add(chm
, val
);
751 struct chmcTopicEntry topicEntry
;
752 // struct chmcUrlStrEntry urlStrEntry;
759 topicEntry
.tocidx_offset
= 4096;
760 topicEntry
.strings_offset
= -1;
761 topicEntry
.urltbl_offset
= 0;
762 topicEntry
.in_content
= 6;
763 topicEntry
.unknown
= 0;
765 memcpy(p
, &topicEntry
, sizeof(struct chmcTopicEntry
));
766 len
+= sizeof(struct chmcTopicEntry
);
768 chm
->idxhdr
.num_of_topic
++;
770 chmc_add_meta(chm
, "/#TOPICS", 1, (UChar
*)p
, len
);
772 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
776 ctrl
= chmc_add_meta(chm
, "/#IDXHDR", 1, (void *)&chm
->idxhdr
,
777 sizeof(struct chmcIndexHeader
));
779 ctrl
->flags
|= CHMC_TNFL_STATIC
;
793 // p[2+3] = 0x00000532;
794 // p[2+4] = 0x00062520;
803 // p[2+27] = 0x00000041;
806 if (chm
->config
!= NULL
&& chm
->config
->title
!= NULL
)
807 val
= chm
->config
->title
;
810 p
[2+5] = chmc_strings_add(chm
, val
);
812 if (chm
->config
!= NULL
&& chm
->config
->hhc
!= NULL
)
813 val
= chm
->config
->hhc
;
816 p
[2+24] = chmc_strings_add(chm
, val
);
818 if (chm
->config
!= NULL
&& chm
->config
->hhk
!= NULL
)
819 val
= chm
->config
->hhc
;
822 p
[2+25] = chmc_strings_add(chm
, val
);
825 chmc_add_meta(chm
, "/#WINDOWS", 1, (UChar
*)p
, 8+196);
827 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
830 ctrl
= chmc_add_meta(chm
, "/#STRINGS", 1, (void *)chm
->strings
,
833 ctrl
->flags
|= CHMC_TNFL_STATIC
;
840 struct chmcUrlStrEntry urlStrEntry
;
842 urlStrEntry
.url_offset
= 0;
843 urlStrEntry
.framename_offset
= 0;
851 memcpy(p
+ len
, &urlStrEntry
, sizeof(struct chmcUrlStrEntry
));
852 len
+= sizeof(struct chmcUrlStrEntry
);
853 len
+= sprintf(p
+ len
, "index.htm" ) + 1;
855 memcpy(p
+ len
, &urlStrEntry
, sizeof(struct chmcUrlStrEntry
));
856 len
+= sizeof(struct chmcUrlStrEntry
);
857 len
+= sprintf(p
+ len
, "test.htm" ) + 1;
859 chmc_add_meta(chm
, "/#URLSTR", 1, (UChar
*)p
, len
);
861 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
865 // chmc_add_entry(chm, "/#URLTBL", 0, 1, NULL, 0, 0);
866 // chmc_add_entry(chm, "/#TOPICS", 0, 1, NULL, 0, 0);
868 // NOTE NOTE NOTE add any meta compressed before crunch ;-)
870 chmc_crunch_lzx(chm
, 1);
872 chmc_control_data_done(chm
);
873 chmc_reset_table_done(chm
);
875 chmc_add_empty(chm
, "/#ITBITS");
877 // NOTE in this implementation compressed Content should be the last file
878 // added to section 0
880 chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/Content", 0, NULL
,
881 chm
->sections
[1]->offset
);
883 chmc_entries_qsort(chm
);
884 chmc_uncompressed_done(chm
);
889 itsf
->dir_len
= _CHMC_ITSP_V1_LEN
890 + (_CHMC_CHUNK_LEN
* itsp
->num_blocks
);
892 itsf
->data_offset
= _CHMC_ITSF_V3_LEN
895 + (_CHMC_CHUNK_LEN
* itsp
->num_blocks
);
897 sect0
->file_len
+= _CHMC_CHUNK_LEN
* itsp
->num_blocks
;
902 struct chmcSection
*section
;
903 struct list_head
*pos
;
906 list_for_each(pos
, &chm
->sections_list
) {
907 section
= list_entry(pos
, struct chmcSection
, list
);
908 chmc_appendfile(chm
, section
->filename
, buf
, 4096);
915 int chmc_crunch_lzx(struct chmcFile
*chm
, int sect_id
)
917 struct chmcLzxInfo lzx_info
;
928 if ((wsize_code
< 15) || (wsize_code
> 21)) {
929 fprintf(stderr
, "window size must be between 15 and 21 inclusive\n");
934 lzx_info
.section
= chm
->sections
[sect_id
];
936 lzx_info
.todo
= lzx_info
.section
->offset
;
937 lzx_info
.pos
= chm
->entries_list
.next
;
942 lzx_info
.fd_offset
= 0;
944 chmc_compressed_add_mark(lzx_info
.chm
, 0);
945 lzx_info
.section
->reset_table_header
.block_count
++;
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. */
954 block_size
= 1 << wsize_code
;
956 // lzx_info.section->control_data.windowSize = wsize_code;
957 // lzx_info.section->control_data.windowsPerReset = block_size;
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
);
964 while(! _lzx_at_eof(&lzx_info
)) {
967 lzx_compress_block(lzxd
, block_size
, subd_ok
);
969 lzx_finish(lzxd
, &lzxr
);
974 static int _lzx_at_eof(void *arg
)
976 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
978 return lzx_info
->error
|| lzx_info
->done
>= lzx_info
->todo
|| lzx_info
->eof
;
981 static int _lzx_put_bytes(void *arg
, int n
, void *buf
)
983 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
984 struct chmcSect0
*sect0
= &lzx_info
->chm
->sect0
;
986 static int counter
= 0;
989 wx
= write(lzx_info
->section
->fd
, buf
, n
);
990 sect0
->file_len
+= wx
;
991 lzx_info
->section
->len
+= wx
;
996 static void _lzx_mark_frame(void *arg
, uint32_t uncomp
, uint32_t comp
)
998 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
999 struct chmcSection
*section
= lzx_info
->chm
->sections
[1];
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
);
1008 section
->reset_table_header
.block_count
++;
1010 chmc_compressed_add_mark( lzx_info
->chm
, compressed
);
1012 section
->reset_table_header
.uncompressed_len
= uncomp
;
1013 section
->reset_table_header
.compressed_len
= comp
;
1016 static int _lzx_get_bytes(void *arg
, int n
, void *buf
)
1018 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
1019 struct chmcFile
*chm
= lzx_info
->chm
;
1020 struct chmcTreeNode
*node
;
1030 // compression state machine
1031 // lzx compressor ask for block input bytes
1032 // need to keep current entry file and offset trought blocks
1035 // end of entries reached?
1036 if (lzx_info
->pos
== &chm
->entries_list
) {
1041 node
= list_entry( lzx_info
->pos
, struct chmcTreeNode
, list
);
1043 // skip empty files and directories
1045 || strcmp("MSCompressed", chm
->sections
[node
->sect_id
]->name
)) {
1046 lzx_info
->pos
= lzx_info
->pos
->next
;
1051 // have len and buffer, it's mallocated not file
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",
1060 errno
, node
->name
, strerror(errno
));
1061 lzx_info
->error
= 1;
1066 // read till the end of the file or till the lzx buffer is filled
1067 toread
= node
->len
- lzx_info
->fd_offset
;
1076 memcpy((char *)buf
+ (n
- todo
), &node
->buf
[lzx_info
->fd_offset
], toread
);
1081 rx
= read(lzx_info
->fd
, (char *)buf
+ (n
- todo
), toread
);
1084 chmc_error("read error %s \n", strerror(temp
));
1085 lzx_info
->error
= 2;
1091 lzx_info
->fd_offset
+= rx
;
1093 lzx_info
->done
+= rx
;
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
);
1100 lzx_info
->fd_offset
= 0;
1101 lzx_info
->pos
= lzx_info
->pos
->next
;
1108 int chmc_compressed_add_mark(struct chmcFile
*chm
, UInt64 at
)
1110 struct chmcSection
*section
;
1111 struct chmcResetTableMark
*mark
;
1115 section
= chm
->sections
[1];
1117 mark
= malloc(_CHMC_RSTTBL_MARK
);
1120 chmc_dump("[%d] at: %jd\n", section
->mark_count
, at
);
1121 list_add_tail(&mark
->list
, §ion
->mark_list
);
1122 section
->mark_count
++;
1129 int chmc_control_data_done(struct chmcFile
*chm
)
1131 struct chmcTreeNode
*ctrl
;
1133 ctrl
= chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/ControlData",
1134 0, (UChar
*)&chm
->sections
[1]->control_data
,
1138 ctrl
->flags
|= CHMC_TNFL_STATIC
;
1145 int chmc_reset_table_done(struct chmcFile
*chm
)
1147 struct chmcSection
*section
;
1148 struct chmcLzxcResetTable
*reset_table
;
1149 struct list_head
*pos
;
1150 struct chmcResetTableMark
*mark
;
1155 section
= chm
->sections
[1];
1157 len
= _CHMC_LZXC_RESETTABLE_V1_LEN
+ (section
->mark_count
* sizeof(UInt64
));
1159 reset_table
= malloc(len
);
1162 memcpy(reset_table
, §ion
->reset_table_header
,
1163 _CHMC_LZXC_RESETTABLE_V1_LEN
);
1164 at
= (void *)((char *)reset_table
+ _CHMC_LZXC_RESETTABLE_V1_LEN
);
1167 list_for_each(pos
, §ion
->mark_list
) {
1168 mark
= list_entry(pos
, struct chmcResetTableMark
, list
);
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
);
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);
1194 void chmc_entries_qsort(struct chmcFile
*chm
)
1196 struct chmcTreeNode
*node
;
1197 struct list_head
*pos
;
1202 chm
->sort_entries
= malloc(sizeof(struct chmcTreeNode
*)
1203 * chm
->entries_num
);
1206 list_for_each(pos
, &chm
->entries_list
) {
1207 node
= list_entry(pos
, struct chmcTreeNode
, list
);
1208 chm
->sort_entries
[i
++] = node
;
1211 qsort(chm
->sort_entries
, chm
->entries_num
, sizeof(struct chmcTreeNode
*),
1215 static int _entry_cmp(const void *pva
, const void *pvb
)
1217 const struct chmcTreeNode
* const *pa
= pva
;
1218 const struct chmcTreeNode
* const *pb
= pvb
;
1219 const struct chmcTreeNode
*a
= *pa
, *b
= *pb
;
1221 return strcmp( &a
->name
[a
->prefixlen
], &b
->name
[b
->prefixlen
] );
1224 int chmc_uncompressed_done(struct chmcFile
*chm
)
1226 struct chmcSect0
*sect0
= &chm
->sect0
;
1227 struct chmcTreeNode
*node
;
1228 struct list_head
*pos
;
1231 list_for_each(pos
, &chm
->entries_list
) {
1232 node
= list_entry( pos
, struct chmcTreeNode
, list
);
1234 if (strcmp( "MSCompressed", chm
->sections
[node
->sect_id
]->name
) == 0)
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
;
1246 void chmc_pmgl_done(struct chmcFile
*chm
)
1248 struct chmcTreeNode
*entry
;
1253 for(i
=0; i
< chm
->entries_num
; i
++) {
1254 entry
= chm
->sort_entries
[i
];
1255 chmc_pmgl_add_entry(chm
, entry
);
1259 int chmc_pmgl_add_entry(struct chmcFile
*chm
, struct chmcTreeNode
*entry
)
1261 struct chmcPmglChunkNode
*pmgl
;
1262 struct chmcPmglChunk
*chunk
;
1263 struct chmcSection
*section
;
1264 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1270 int should_idx
, idx_intlv
;
1276 // check section bound
1277 section
= chmc_section_lookup(chm
, entry
->sect_id
);
1279 chmcerr_set_return(CHMC_ENOMEM
, "section %d lookup failed: ",
1282 // check chunk space for new entry
1283 name_len
= strlen(&entry
->name
[entry
->prefixlen
]);
1285 outlen
= chmc_encint_len(name_len
);
1287 outlen
+= chmc_encint_len(entry
->sect_id
);
1288 outlen
+= chmc_encint_len(entry
->offset
);
1289 outlen
+= chmc_encint_len(entry
->len
);
1291 // look for current pmgl chunk, create if doesn't exist
1292 if (!chm
->pmgl_last
) {
1293 pmgl
= chmc_pmgl_create();
1295 chmc_pmgl_add(chm
, pmgl
);
1297 chmcerr_set_return(CHMC_ENOMEM
, "pmgl chunk: ");
1300 pmgl
= chm
->pmgl_last
;
1304 chunk
= &chm
->pmgl_last
->chunk
;
1306 idx_intlv
= 1 + ( 1 << itsp
->blockidx_intvl
);
1307 should_idx
= ( ( chunk
->entries_count
> 0 )
1308 && ! ( ( chunk
->entries_count
+ 1 ) % idx_intlv
)
1311 free
= sizeof(chunk
->data
) - pmgl
->data_len
- pmgl
->index_len
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();
1319 chmc_pmgl_add(chm
, pmgl
);
1321 chmcerr_set_return(CHMC_ENOMEM
, "pmgl chunk: ");
1326 p
= (void *)&chunk
->data
[pmgl
->data_len
];
1329 idx
= (void *)((char *)&chunk
->data
[CHMC_PMGL_DATA_LEN
] - pmgl
->index_len
);
1330 *idx
= (char *)p
- (char *)&chunk
->data
;
1333 p
+= chmc_encint(name_len
, p
);
1334 memcpy(p
, &entry
->name
[entry
->prefixlen
], name_len
);
1336 p
+= chmc_encint(entry
->sect_id
, p
);
1337 p
+= chmc_encint(entry
->offset
, p
);
1338 p
+= chmc_encint(entry
->len
, p
);
1340 pmgl
->data_len
+= outlen
;
1341 pmgl
->index_len
+= should_idx
;
1343 chunk
->entries_count
++;
1344 chunk
->header
.free_space
-= outlen
;
1352 struct chmcSection
*chmc_section_lookup(struct chmcFile
*chm
, int id
)
1354 struct chmcSection
*current
;
1355 struct list_head
*pos
;
1361 list_for_each(pos
, &chm
->sections_list
) {
1362 current
= list_entry(pos
, struct chmcSection
, list
);
1371 struct chmcPmglChunkNode
*chmc_pmgl_create(void)
1373 struct chmcPmglChunkNode
*node
;
1375 node
= malloc(sizeof(struct chmcPmglChunkNode
));
1377 chmc_pmgl_init(node
);
1382 void chmc_pmgl_init(struct chmcPmglChunkNode
*node
)
1384 struct chmcPmglChunk
*chunk
;
1389 node
->index_len
= 0;
1391 chunk
= &node
->chunk
;
1393 memcpy(chunk
->header
.signature
, "PMGL", 4);
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;
1401 memset(chunk
->data
, 0, CHMC_PMGL_DATA_LEN
);
1404 void chmc_pmgi_init(struct chmcPmgiChunkNode
*node
)
1406 struct chmcPmgiChunk
*chunk
;
1411 node
->index_len
= 0;
1413 chunk
= &node
->chunk
;
1415 memcpy(chunk
->header
.signature
, "PMGI", 4);
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;
1423 memset(chunk
->data
, 0, CHMC_PMGI_DATA_LEN
);
1428 struct chmcPmgiChunkNode
*chmc_pmgi_create(void)
1430 struct chmcPmgiChunkNode
*node
;
1432 node
= malloc(sizeof(struct chmcPmgiChunkNode
));
1434 chmc_pmgi_init(node
);
1439 void chmc_pmgl_destroy(struct chmcPmglChunkNode
*node
)
1445 void chmc_pmgi_destroy(struct chmcPmgiChunkNode
*node
)
1451 void chmc_pmgl_add(struct chmcFile
*chm
, struct chmcPmglChunkNode
*pmgl
)
1453 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1454 struct chmcPmglHeader
*hdr
;
1459 list_add_tail(&pmgl
->list
, &chm
->pmgl_list
);
1461 itsp
->index_last
= itsp
->num_blocks
;
1463 hdr
= &pmgl
->chunk
.header
;
1464 hdr
->block_prev
= itsp
->num_blocks
- 1;
1466 if (chm
->pmgl_last
) {
1467 hdr
= &chm
->pmgl_last
->chunk
.header
;
1468 hdr
->block_next
= itsp
->num_blocks
;
1473 chm
->pmgl_last
= pmgl
;
1476 int chmc_pmgi_done(struct chmcFile
*chm
)
1478 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1479 struct chmcPmglChunkNode
*pmgl
;
1480 struct list_head
*pos
;
1483 char name
[256]; //FIXME use malloc
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;
1496 itsp
->index_root
= itsp
->num_blocks
;
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
);
1508 BUG_ON("name_len >= 255(%lu) %.*s\n", (unsigned long)name_len
, 255,
1509 &pmgl
->chunk
.data
[j
]);
1516 int chmc_pmgi_add_entry(struct chmcFile
*chm
, const char *name
, int pmgl_id
)
1518 struct chmcPmgiChunkNode
*pmgi
;
1519 struct chmcPmgiChunk
*chunk
;
1520 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1526 int should_idx
, idx_intlv
;
1531 // check chunk space for new entry
1532 name_len
= strlen(name
);
1534 outlen
= chmc_encint_len(name_len
);
1536 outlen
+= chmc_encint_len(pmgl_id
);
1538 // look for current pmgi chunk, create if doesn't exist
1539 if (!chm
->pmgi_last
) {
1540 pmgi
= chmc_pmgi_create();
1542 chmc_pmgi_add(chm
, pmgi
);
1544 chmcerr_set_return(CHMC_ENOMEM
, "pmgi chunk: ");
1547 pmgi
= chm
->pmgi_last
;
1551 chunk
= &chm
->pmgi_last
->chunk
;
1553 idx_intlv
= 1 + ( 1 << itsp
->blockidx_intvl
);
1554 should_idx
= ( ( chunk
->entries_count
> 0 )
1555 && ! ( ( chunk
->entries_count
+ 1 ) % idx_intlv
)
1558 free
= sizeof(chunk
->data
) - pmgi
->data_len
-
1559 pmgi
->index_len
- should_idx
;
1561 // current(last) chunk doesn't have enough room? force new one
1562 if (outlen
+ should_idx
> free
) {
1563 pmgi
= chmc_pmgi_create();
1565 chmc_pmgi_add(chm
, pmgi
);
1567 chmcerr_set_return(CHMC_ENOMEM
, "pmgi chunk: ");
1572 p
= (void *)&chunk
->data
[pmgi
->data_len
];
1575 idx
= (void *)((char *)&chunk
->data
[CHMC_PMGI_DATA_LEN
] - pmgi
->index_len
);
1576 *idx
= (char *)p
- (char *)&chunk
->data
;
1579 p
+= chmc_encint(name_len
, p
);
1580 memcpy(p
, name
, name_len
);
1582 p
+= chmc_encint(pmgl_id
, p
);
1584 pmgi
->data_len
+= outlen
;
1585 pmgi
->index_len
+= should_idx
;
1587 chunk
->entries_count
++;
1588 chunk
->header
.free_space
-= outlen
;
1596 void chmc_pmgi_add(struct chmcFile
*chm
, struct chmcPmgiChunkNode
*pmgi
)
1598 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1603 list_add_tail(&pmgi
->list
, &chm
->pmgi_list
);
1606 chm
->pmgi_last
= pmgi
;
1609 int chmc_write(struct chmcFile
*chm
)
1611 struct chmcItsfHeader
*itsf
= &chm
->itsf
;
1612 struct chmcSect0
*sect0
= &chm
->sect0
;
1613 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1615 struct chmcPmglChunkNode
*pmgl
;
1616 struct chmcPmgiChunkNode
*pmgi
;
1617 struct list_head
*pos
;
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
);
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
);
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
);
1646 int chmc_appendfile(struct chmcFile
*chm
, const char *filename
, void *buf
,
1649 struct stat statbuf
;
1654 if (stat(filename
, &statbuf
) < 0)
1657 in
= open(filename
, O_RDONLY
| O_BINARY
);
1659 todo
= statbuf
.st_size
;
1666 rx
= read(in
, buf
, toread
);
1668 write(chm
->fd
, buf
, rx
);
1676 BUG_ON("open %s\n", filename
);