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/>.
30 #include <sys/types.h>
32 #include "../../port/port.h"
46 #include "../lzx_compress/lzx_config.h"
47 #include "../lzx_compress/lzx_compress.h"
49 #define PACKAGE_STRING "hhpcomp development version"
51 /* if O_BINARY is not defined, the system is probably not expecting any such flag */
56 int chmc_section_add(struct chmcFile
*chm
, const char *name
);
57 struct chmcSection
* chmc_section_create(struct chmcFile
*chm
,
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
,
80 struct chmcTreeNode
*chmc_add_dir(struct chmcFile
*chm
, const char *dir
);
81 struct chmcTreeNode
*chmc_add_empty(struct chmcFile
*chm
, const char *file
);
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
);
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
);
94 void chmc_entries_qsort(struct chmcFile
*chm
);
95 static int _entry_cmp(const void *pva
, const void *pvb
);
97 struct chmcSection
*chmc_section_lookup(struct chmcFile
*chm
, int id
);
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
);
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
,
112 int chmc_pmgl_add_entry(struct chmcFile
*chm
, struct chmcTreeNode
*entry
);
113 #endif /* __REACTOS__ */
117 struct chmcFile
*chm
;
118 struct chmcSection
*section
;
123 struct list_head
*pos
;
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 };
133 int chmc_init(struct chmcFile
*chm
, const char *filename
,
134 struct chmcConfig
*config
)
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
;
148 memset(chm
, 0, sizeof(struct chmcFile
));
150 chm
->config
= config
;
152 if (strcmp(filename
, "-") != 0) {
153 chm
->fd
= open(filename
, O_RDWR
| O_CREAT
| O_TRUNC
| O_BINARY
, 0644);
155 chmcerr_set(errno
, strerror(errno
));
156 chmcerr_return_msg("creat file '%s'", filename
);
159 chm
->fd
= fileno(stdout
);
162 memcpy(itsf
->signature
, "ITSF", 4);
164 itsf
->header_len
= _CHMC_ITSF_V3_LEN
;
165 itsf
->unknown_000c
= 1;
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
;
172 itsf
->sect0_offset
= _CHMC_ITSF_V3_LEN
;
173 itsf
->sect0_len
= _CHMC_SECT0_LEN
;
175 sect0
->file_len
= _CHMC_ITSF_V3_LEN
179 sect0
->unknown_0000
= 510;
181 memcpy(itsp
->signature
, "ITSP", 4);
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;
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);
196 system
->_size
= _CHMC_SYSTEM_HDR_LEN
+ sizeof(struct chmcIndexHeader
);
198 sysinfo
->lcid
= CHMC_MS_LCID_EN_US
;
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 = ???;
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;
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
);
229 chm
->strings
= malloc(4096);
230 memset(chm
->strings
, 0, 4096);
231 chm
->strings_len
= 4096;
232 chm
->strings_offset
= 1;
234 if (chmc_section_add(chm
, "Uncompressed") != CHMC_NOERR
)
235 chmcerr_return_msg("adding section: Uncompressed");
237 if (chmc_section_add(chm
, "MSCompressed") != CHMC_NOERR
)
238 chmcerr_return_msg("adding section: MSCompressed");
240 chmc_sections_done(chm
);
245 int chmc_section_add(struct chmcFile
*chm
, const char *name
)
247 struct chmcSection
*section
;
252 section
= chmc_section_create(chm
, name
);
254 return chmcerr_code();
256 list_add_tail(§ion
->list
, &chm
->sections_list
);
262 struct chmcSection
*chmc_section_create(struct chmcFile
*chm
,
265 struct chmcSection
*section
;
269 section
= calloc(1, sizeof(struct chmcSection
));
275 memcpy(section
->name
, name
, len
+ 1);
280 if (chm
->config
!= NULL
)
281 tmpdir
= chm
->config
->tmpdir
;
285 len
= strlen(tmpdir
);
286 if (len
>= PATH_MAX
- 12) {
287 chmcerr_set(errno
, strerror(errno
));
288 chmcerr_msg("tmpdir too long: '%s'", tmpdir
);
292 strcat(section
->filename
, tmpdir
);
293 if (section
->filename
[len
- 1] != '/')
294 strcat(section
->filename
, "/");
296 if (strcmp("MSCompressed", name
) == 0)
297 strcat(section
->filename
, "chmcCXXXXXX");
299 strcat(section
->filename
, "chmcUXXXXXX");
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
);
308 else if (strcmp(section
->name
, "MSCompressed") == 0) {
309 chmc_reset_table_init(§ion
->reset_table_header
);
310 chmc_control_data_init(§ion
->control_data
);
311 INIT_LIST_HEAD(§ion
->mark_list
);
312 section
->mark_count
= 0;
315 chmcerr_set(errno
, strerror(errno
));
316 chmcerr_msg("section '%s' allocation failed", name
);
326 void chmc_reset_table_init(struct chmcLzxcResetTable
*reset_table
)
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;
337 void chmc_control_data_init(struct chmcLzxcControlData
*control_data
)
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;
348 void chmc_sections_done(struct chmcFile
*chm
)
355 chm
->sections
= malloc(sizeof(struct chmcSection
*) * chm
->sections_num
);
357 struct chmcSection
*section
;
358 struct list_head
*pos
;
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
;
367 chmc_namelist_create(chm
, len
);
369 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
372 int chmc_namelist_create(struct chmcFile
*chm
, int len
)
376 namelist
= malloc(len
);
378 struct chmcSection
*section
;
379 int i
, j
, k
, name_len
;
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
];
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
];
393 chmc_add_meta(chm
, "::DataSpace/NameList", 0, (UChar
*)namelist
, len
);
401 struct chmcTreeNode
*chmc_add_empty(struct chmcFile
*chm
, const char *file
)
404 return chmc_add_entry(chm
, file
, 0, 0, NULL
, 0, 0);
407 struct chmcTreeNode
*chmc_add_meta(struct chmcFile
*chm
, const char *metaname
,
409 UChar
*buf
, UInt64 len
)
411 struct chmcSection
*section
;
412 struct chmcTreeNode
*node
;
416 if (sect_id
>= chm
->sections_num
)
419 section
= chm
->sections
[sect_id
];
421 node
= chmc_add_entry(chm
, metaname
, 0, sect_id
, buf
, section
->offset
, len
);
423 if ((node
) && (len
> 0))
424 section
->offset
+= len
;
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
)
433 struct chmcTreeNode
*node
;
437 if (sect_id
>= (chm
->sections_num
)) {
438 fprintf(stderr
,"sect_id %d >= chm->sections_num %d\n",
439 sect_id
, chm
->sections_num
);
443 node
= malloc(sizeof(struct chmcTreeNode
));
446 node
->name
= strdup( name
);
447 node
->prefixlen
= prefixlen
;
448 node
->sect_id
= sect_id
;
450 node
->offset
= offset
;
452 list_add_tail(&node
->list
, &chm
->entries_list
);
456 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
461 void chmc_term(struct chmcFile
*chm
)
464 assert(chm
->fd
> -1);
468 chmc_entries_free(chm
);
473 chmc_sections_free(chm
);
475 if (chm
->fd
!= fileno(stdout
))
479 void chmc_sections_free(struct chmcFile
*chm
)
481 struct chmcSection
*section
;
482 struct list_head
*pos
, *q
;
486 list_for_each_safe(pos
, q
, &chm
->sections_list
) {
487 section
= list_entry(pos
, struct chmcSection
, list
);
489 chmc_section_destroy(section
);
493 void chmc_section_destroy(struct chmcSection
*section
)
496 assert(section
->fd
> -1);
498 if (strcmp(section
->name
, "MSCompressed") == 0) {
499 struct list_head
*pos
, *q
;
500 struct chmcResetTableMark
*mark
;
502 list_for_each_safe(pos
, q
, §ion
->mark_list
) {
503 mark
= list_entry(pos
, struct chmcResetTableMark
, list
);
510 unlink(section
->filename
);
514 void chmc_pmgi_free(struct chmcFile
*chm
)
516 struct chmcPmgiChunkNode
*node
;
517 struct list_head
*pos
, *q
;
521 list_for_each_safe(pos
, q
, &chm
->pmgi_list
) {
522 node
= list_entry(pos
, struct chmcPmgiChunkNode
, list
);
524 chmc_pmgi_destroy(node
);
528 void chmc_pmgl_free(struct chmcFile
*chm
)
530 struct chmcPmglChunkNode
*node
;
531 struct list_head
*pos
, *q
;
535 list_for_each_safe(pos
, q
, &chm
->pmgl_list
) {
536 node
= list_entry(pos
, struct chmcPmglChunkNode
, list
);
538 chmc_pmgl_destroy(node
);
542 void chmc_entries_free( struct chmcFile
*chm
)
544 struct chmcTreeNode
*node
;
545 struct list_head
*pos
, *q
;
549 list_for_each_safe(pos
, q
, &chm
->entries_list
) {
550 node
= list_entry(pos
, struct chmcTreeNode
, list
);
552 chmc_entry_destroy(node
);
555 free(chm
->sort_entries
);
558 UInt32
chmc_strings_add( struct chmcFile
*chm
, const char *s
)
562 /* FIXME null are errors */
564 if (!s
|| *s
== '\0')
569 off
= chm
->strings_offset
;
571 if (off
+ len
+ 1 < chm
->strings_len
) {
573 memcpy(&chm
->strings
[off
], s
, len
+ 1);
574 chm
->strings_offset
+= len
+ 1;
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",
587 void chmc_entry_destroy( struct chmcTreeNode
*node
)
593 if (node
->buf
&& !(node
->flags
& CHMC_TNFL_STATIC
))
598 struct chmcTreeNode
*chmc_add_file(struct chmcFile
*chm
, const char *filename
,
599 UInt16 prefixlen
, int sect_id
, UChar
*buf
,
602 struct chmcSection
*section
;
603 struct chmcTreeNode
*node
;
607 if (sect_id
>= chm
->sections_num
)
610 section
= chm
->sections
[sect_id
];
612 node
= chmc_add_entry(chm
, filename
, prefixlen
, sect_id
, NULL
,
613 section
->offset
, len
);
615 if ((node
) && (len
> 0))
616 section
->offset
+= len
;
621 struct chmcTreeNode
*chmc_add_dir(struct chmcFile
*chm
, const char *dir
)
625 return chmc_add_entry(chm
, dir
, 0, 0, NULL
, 0, 0);
628 static inline void *chmc_syscat_mem(void *d
, void *s
, unsigned long len
)
632 return (char *)d
+ len
;
635 static void *chmc_syscat_entry(Int16 code
, void *d
, void *s
, Int16 len
)
637 d
= chmc_syscat_mem(d
, &code
, 2);
638 d
= chmc_syscat_mem(d
, &len
, 2);
640 return chmc_syscat_mem(d
, s
, len
);
643 /* #define DEFAULT_TOPIC "index.htm" */
644 /* #define TITLE "hello world" */
645 /* #define LCASEFILE "test" */
647 int chmc_system_done(struct chmcFile
*chm
)
649 struct chmcSystem
*system
;
650 struct chmcSystemInfo
*sysinfo
;
651 struct chmcIndexHeader
*idxhdr
;
656 system
= &chm
->system
;
657 sysinfo
= &system
->info
;
658 idxhdr
= &chm
->idxhdr
;
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))
668 sysp
= malloc(16384);
674 const char *entry_val
;
676 p
= chmc_syscat_mem(sysp
, &system
->version
, sizeof(system
->version
));
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"*/
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
));
688 if (chm
->config
!= NULL
&& chm
->config
->deftopic
!= NULL
)
689 entry_val
= chm
->config
->deftopic
;
691 entry_val
= "index.htm";
692 p
= chmc_syscat_entry(SIEC_DEFTOPIC
, p
, (void *)entry_val
,
693 strlen(entry_val
)+1);
695 if (chm
->config
!= NULL
&& chm
->config
->title
!= NULL
)
696 entry_val
= chm
->config
->title
;
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);
708 p
= chmc_syscat_entry(SIEC_NUMOFINFOT
, p
, &val
, sizeof(val
));
710 p
= chmc_syscat_entry(SIEC_IDXHDR
, p
,
711 idxhdr
, sizeof(struct chmcIndexHeader
));
715 p
= chmc_syscat_entry(SIEC_INFOCHKSUM
, p
, &val
, sizeof(val
));
717 system
->_size
= (char *)p
- (char *)sysp
;
718 chmc_add_meta(chm
, "/#SYSTEM", 0, sysp
, system
->_size
);
722 chmcerr_set(CHMC_ENOMEM
, "system done: malloc %d bytes",
728 int chmc_tree_done( struct chmcFile
*chm
)
730 struct chmcItsfHeader
*itsf
;
731 struct chmcSect0
*sect0
;
732 struct chmcItspHeader
*itsp
;
733 struct chmcTreeNode
*ctrl
;
743 chmc_add_dir(chm
, "/");
745 ctrl
= chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/Transform/List",
746 0, (UChar
*)chmc_transform_list
,
747 sizeof(chmc_transform_list
));
749 ctrl
->flags
|= CHMC_TNFL_STATIC
;
751 chmc_system_done(chm
);
753 if (chm
->config
!= NULL
&& chm
->config
->deftopic
!= NULL
)
754 val
= chm
->config
->deftopic
;
758 str_index
= chmc_strings_add(chm
, val
);
765 struct chmcTopicEntry topicEntry
;
766 // struct chmcUrlStrEntry urlStrEntry;
773 topicEntry
.tocidx_offset
= 4096;
774 topicEntry
.strings_offset
= -1;
775 topicEntry
.urltbl_offset
= 0;
776 topicEntry
.in_content
= 6;
777 topicEntry
.unknown
= 0;
779 memcpy(p
, &topicEntry
, sizeof(struct chmcTopicEntry
));
780 len
+= sizeof(struct chmcTopicEntry
);
782 chm
->idxhdr
.num_of_topic
++;
784 chmc_add_meta(chm
, "/#TOPICS", 1, (UChar
*)p
, len
);
786 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
790 ctrl
= chmc_add_meta(chm
, "/#IDXHDR", 1, (void *)&chm
->idxhdr
,
791 sizeof(struct chmcIndexHeader
));
793 ctrl
->flags
|= CHMC_TNFL_STATIC
;
807 // p[2+3] = 0x00000532;
808 // p[2+4] = 0x00062520;
817 // p[2+27] = 0x00000041;
820 if (chm
->config
!= NULL
&& chm
->config
->title
!= NULL
)
821 val
= chm
->config
->title
;
824 p
[2+5] = chmc_strings_add(chm
, val
);
826 if (chm
->config
!= NULL
&& chm
->config
->hhc
!= NULL
)
827 val
= chm
->config
->hhc
;
830 p
[2+24] = chmc_strings_add(chm
, val
);
832 if (chm
->config
!= NULL
&& chm
->config
->hhk
!= NULL
)
833 val
= chm
->config
->hhc
;
836 p
[2+25] = chmc_strings_add(chm
, val
);
839 chmc_add_meta(chm
, "/#WINDOWS", 1, (UChar
*)p
, 8+196);
841 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
844 ctrl
= chmc_add_meta(chm
, "/#STRINGS", 1, (void *)chm
->strings
,
847 ctrl
->flags
|= CHMC_TNFL_STATIC
;
854 struct chmcUrlStrEntry urlStrEntry
;
856 urlStrEntry
.url_offset
= 0;
857 urlStrEntry
.framename_offset
= 0;
865 memcpy(p
+ len
, &urlStrEntry
, sizeof(struct chmcUrlStrEntry
));
866 len
+= sizeof(struct chmcUrlStrEntry
);
867 len
+= sprintf(p
+ len
, "index.htm" ) + 1;
869 memcpy(p
+ len
, &urlStrEntry
, sizeof(struct chmcUrlStrEntry
));
870 len
+= sizeof(struct chmcUrlStrEntry
);
871 len
+= sprintf(p
+ len
, "test.htm" ) + 1;
873 chmc_add_meta(chm
, "/#URLSTR", 1, (UChar
*)p
, len
);
875 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
879 // chmc_add_entry(chm, "/#URLTBL", 0, 1, NULL, 0, 0);
880 // chmc_add_entry(chm, "/#TOPICS", 0, 1, NULL, 0, 0);
882 // NOTE NOTE NOTE add any meta compressed before crunch ;-)
884 chmc_crunch_lzx(chm
, 1);
886 chmc_control_data_done(chm
);
887 chmc_reset_table_done(chm
);
889 chmc_add_empty(chm
, "/#ITBITS");
891 // NOTE in this implementation compressed Content should be the last file
892 // added to section 0
894 chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/Content", 0, NULL
,
895 chm
->sections
[1]->offset
);
897 chmc_entries_qsort(chm
);
898 chmc_uncompressed_done(chm
);
903 itsf
->dir_len
= _CHMC_ITSP_V1_LEN
904 + (_CHMC_CHUNK_LEN
* itsp
->num_blocks
);
906 itsf
->data_offset
= _CHMC_ITSF_V3_LEN
909 + (_CHMC_CHUNK_LEN
* itsp
->num_blocks
);
911 sect0
->file_len
+= _CHMC_CHUNK_LEN
* itsp
->num_blocks
;
916 struct chmcSection
*section
;
917 struct list_head
*pos
;
920 list_for_each(pos
, &chm
->sections_list
) {
921 section
= list_entry(pos
, struct chmcSection
, list
);
922 chmc_appendfile(chm
, section
->filename
, buf
, 4096);
929 int chmc_crunch_lzx(struct chmcFile
*chm
, int sect_id
)
931 struct chmcLzxInfo lzx_info
;
943 if ((wsize_code
< 15) || (wsize_code
> 21)) {
944 fprintf(stderr
, "window size must be between 15 and 21 inclusive\n");
950 lzx_info
.section
= chm
->sections
[sect_id
];
952 lzx_info
.todo
= lzx_info
.section
->offset
;
953 lzx_info
.pos
= chm
->entries_list
.next
;
958 lzx_info
.fd_offset
= 0;
960 chmc_compressed_add_mark(lzx_info
.chm
, 0);
961 lzx_info
.section
->reset_table_header
.block_count
++;
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. */
970 block_size
= 1 << wsize_code
;
972 // lzx_info.section->control_data.windowSize = wsize_code;
973 // lzx_info.section->control_data.windowsPerReset = block_size;
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
);
980 while(! _lzx_at_eof(&lzx_info
)) {
983 lzx_compress_block(lzxd
, block_size
, subd_ok
);
985 lzx_finish(lzxd
, &lzxr
);
990 static int _lzx_at_eof(void *arg
)
992 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
994 return lzx_info
->error
|| lzx_info
->done
>= lzx_info
->todo
|| lzx_info
->eof
;
997 static int _lzx_put_bytes(void *arg
, int n
, void *buf
)
999 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
1000 struct chmcSect0
*sect0
= &lzx_info
->chm
->sect0
;
1002 static int counter
= 0;
1005 wx
= write(lzx_info
->section
->fd
, buf
, n
);
1006 sect0
->file_len
+= wx
;
1007 lzx_info
->section
->len
+= wx
;
1012 static void _lzx_mark_frame(void *arg
, uint32_t uncomp
, uint32_t comp
)
1014 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
1015 struct chmcSection
*section
= lzx_info
->chm
->sections
[1];
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
);
1024 section
->reset_table_header
.block_count
++;
1026 chmc_compressed_add_mark( lzx_info
->chm
, compressed
);
1028 section
->reset_table_header
.uncompressed_len
= uncomp
;
1029 section
->reset_table_header
.compressed_len
= comp
;
1032 static int _lzx_get_bytes(void *arg
, int n
, void *buf
)
1034 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
1035 struct chmcFile
*chm
= lzx_info
->chm
;
1036 struct chmcTreeNode
*node
;
1046 // compression state machine
1047 // lzx compressor ask for block input bytes
1048 // need to keep current entry file and offset trought blocks
1051 // end of entries reached?
1052 if (lzx_info
->pos
== &chm
->entries_list
) {
1057 node
= list_entry( lzx_info
->pos
, struct chmcTreeNode
, list
);
1059 // skip empty files and directories
1061 || strcmp("MSCompressed", chm
->sections
[node
->sect_id
]->name
)) {
1062 lzx_info
->pos
= lzx_info
->pos
->next
;
1067 // have len and buffer, it's mallocated not file
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",
1076 errno
, node
->name
, strerror(errno
));
1077 lzx_info
->error
= 1;
1082 // read till the end of the file or till the lzx buffer is filled
1083 toread
= node
->len
- lzx_info
->fd_offset
;
1092 memcpy((char *)buf
+ (n
- todo
), &node
->buf
[lzx_info
->fd_offset
], toread
);
1097 rx
= read(lzx_info
->fd
, (char *)buf
+ (n
- todo
), toread
);
1100 chmc_error("read error %s \n", strerror(temp
));
1101 lzx_info
->error
= 2;
1107 lzx_info
->fd_offset
+= rx
;
1109 lzx_info
->done
+= rx
;
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
);
1116 lzx_info
->fd_offset
= 0;
1117 lzx_info
->pos
= lzx_info
->pos
->next
;
1124 int chmc_compressed_add_mark(struct chmcFile
*chm
, UInt64 at
)
1126 struct chmcSection
*section
;
1127 struct chmcResetTableMark
*mark
;
1131 section
= chm
->sections
[1];
1133 mark
= malloc(_CHMC_RSTTBL_MARK
);
1136 chmc_dump("[%d] at: %jd\n", section
->mark_count
, at
);
1137 list_add_tail(&mark
->list
, §ion
->mark_list
);
1138 section
->mark_count
++;
1145 int chmc_control_data_done(struct chmcFile
*chm
)
1147 struct chmcTreeNode
*ctrl
;
1149 ctrl
= chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/ControlData",
1150 0, (UChar
*)&chm
->sections
[1]->control_data
,
1154 ctrl
->flags
|= CHMC_TNFL_STATIC
;
1161 int chmc_reset_table_done(struct chmcFile
*chm
)
1163 struct chmcSection
*section
;
1164 struct chmcLzxcResetTable
*reset_table
;
1165 struct list_head
*pos
;
1166 struct chmcResetTableMark
*mark
;
1171 section
= chm
->sections
[1];
1173 len
= _CHMC_LZXC_RESETTABLE_V1_LEN
+ (section
->mark_count
* sizeof(UInt64
));
1175 reset_table
= malloc(len
);
1178 memcpy(reset_table
, §ion
->reset_table_header
,
1179 _CHMC_LZXC_RESETTABLE_V1_LEN
);
1180 at
= (void *)((char *)reset_table
+ _CHMC_LZXC_RESETTABLE_V1_LEN
);
1183 list_for_each(pos
, §ion
->mark_list
) {
1184 mark
= list_entry(pos
, struct chmcResetTableMark
, list
);
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
);
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);
1210 void chmc_entries_qsort(struct chmcFile
*chm
)
1212 struct chmcTreeNode
*node
;
1213 struct list_head
*pos
;
1218 chm
->sort_entries
= malloc(sizeof(struct chmcTreeNode
*)
1219 * chm
->entries_num
);
1222 list_for_each(pos
, &chm
->entries_list
) {
1223 node
= list_entry(pos
, struct chmcTreeNode
, list
);
1224 chm
->sort_entries
[i
++] = node
;
1227 qsort(chm
->sort_entries
, chm
->entries_num
, sizeof(struct chmcTreeNode
*),
1231 static int _entry_cmp(const void *pva
, const void *pvb
)
1233 const struct chmcTreeNode
* const *pa
= pva
;
1234 const struct chmcTreeNode
* const *pb
= pvb
;
1235 const struct chmcTreeNode
*a
= *pa
, *b
= *pb
;
1237 return strcmp( &a
->name
[a
->prefixlen
], &b
->name
[b
->prefixlen
] );
1240 int chmc_uncompressed_done(struct chmcFile
*chm
)
1242 struct chmcSect0
*sect0
= &chm
->sect0
;
1243 struct chmcTreeNode
*node
;
1244 struct list_head
*pos
;
1247 list_for_each(pos
, &chm
->entries_list
) {
1248 node
= list_entry( pos
, struct chmcTreeNode
, list
);
1250 if (strcmp( "MSCompressed", chm
->sections
[node
->sect_id
]->name
) == 0)
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
;
1262 void chmc_pmgl_done(struct chmcFile
*chm
)
1264 struct chmcTreeNode
*entry
;
1269 for(i
=0; i
< chm
->entries_num
; i
++) {
1270 entry
= chm
->sort_entries
[i
];
1271 chmc_pmgl_add_entry(chm
, entry
);
1275 int chmc_pmgl_add_entry(struct chmcFile
*chm
, struct chmcTreeNode
*entry
)
1277 struct chmcPmglChunkNode
*pmgl
;
1278 struct chmcPmglChunk
*chunk
;
1279 struct chmcSection
*section
;
1280 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1286 int should_idx
, idx_intlv
;
1292 // check section bound
1293 section
= chmc_section_lookup(chm
, entry
->sect_id
);
1295 chmcerr_set_return(CHMC_ENOMEM
, "section %d lookup failed: ",
1298 // check chunk space for new entry
1299 name_len
= strlen(&entry
->name
[entry
->prefixlen
]);
1301 outlen
= chmc_encint_len(name_len
);
1303 outlen
+= chmc_encint_len(entry
->sect_id
);
1304 outlen
+= chmc_encint_len(entry
->offset
);
1305 outlen
+= chmc_encint_len(entry
->len
);
1307 // look for current pmgl chunk, create if doesn't exist
1308 if (!chm
->pmgl_last
) {
1309 pmgl
= chmc_pmgl_create();
1311 chmc_pmgl_add(chm
, pmgl
);
1313 chmcerr_set_return(CHMC_ENOMEM
, "pmgl chunk: ");
1316 pmgl
= chm
->pmgl_last
;
1320 chunk
= &chm
->pmgl_last
->chunk
;
1322 idx_intlv
= 1 + ( 1 << itsp
->blockidx_intvl
);
1323 should_idx
= ( ( chunk
->entries_count
> 0 )
1324 && ! ( ( chunk
->entries_count
+ 1 ) % idx_intlv
)
1327 free
= sizeof(chunk
->data
) - pmgl
->data_len
- pmgl
->index_len
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();
1335 chmc_pmgl_add(chm
, pmgl
);
1337 chmcerr_set_return(CHMC_ENOMEM
, "pmgl chunk: ");
1342 p
= (void *)&chunk
->data
[pmgl
->data_len
];
1345 idx
= (void *)((char *)&chunk
->data
[CHMC_PMGL_DATA_LEN
] - pmgl
->index_len
);
1346 *idx
= (char *)p
- (char *)&chunk
->data
;
1349 p
+= chmc_encint(name_len
, p
);
1350 memcpy(p
, &entry
->name
[entry
->prefixlen
], name_len
);
1352 p
+= chmc_encint(entry
->sect_id
, p
);
1353 p
+= chmc_encint(entry
->offset
, p
);
1354 p
+= chmc_encint(entry
->len
, p
);
1356 pmgl
->data_len
+= outlen
;
1357 pmgl
->index_len
+= should_idx
;
1359 chunk
->entries_count
++;
1360 chunk
->header
.free_space
-= outlen
;
1368 struct chmcSection
*chmc_section_lookup(struct chmcFile
*chm
, int id
)
1370 struct chmcSection
*current
;
1371 struct list_head
*pos
;
1377 list_for_each(pos
, &chm
->sections_list
) {
1378 current
= list_entry(pos
, struct chmcSection
, list
);
1387 struct chmcPmglChunkNode
*chmc_pmgl_create(void)
1389 struct chmcPmglChunkNode
*node
;
1391 node
= malloc(sizeof(struct chmcPmglChunkNode
));
1393 chmc_pmgl_init(node
);
1398 void chmc_pmgl_init(struct chmcPmglChunkNode
*node
)
1400 struct chmcPmglChunk
*chunk
;
1405 node
->index_len
= 0;
1407 chunk
= &node
->chunk
;
1409 memcpy(chunk
->header
.signature
, "PMGL", 4);
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;
1417 memset(chunk
->data
, 0, CHMC_PMGL_DATA_LEN
);
1420 void chmc_pmgi_init(struct chmcPmgiChunkNode
*node
)
1422 struct chmcPmgiChunk
*chunk
;
1427 node
->index_len
= 0;
1429 chunk
= &node
->chunk
;
1431 memcpy(chunk
->header
.signature
, "PMGI", 4);
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;
1439 memset(chunk
->data
, 0, CHMC_PMGI_DATA_LEN
);
1444 struct chmcPmgiChunkNode
*chmc_pmgi_create(void)
1446 struct chmcPmgiChunkNode
*node
;
1448 node
= malloc(sizeof(struct chmcPmgiChunkNode
));
1450 chmc_pmgi_init(node
);
1455 void chmc_pmgl_destroy(struct chmcPmglChunkNode
*node
)
1461 void chmc_pmgi_destroy(struct chmcPmgiChunkNode
*node
)
1467 void chmc_pmgl_add(struct chmcFile
*chm
, struct chmcPmglChunkNode
*pmgl
)
1469 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1470 struct chmcPmglHeader
*hdr
;
1475 list_add_tail(&pmgl
->list
, &chm
->pmgl_list
);
1477 itsp
->index_last
= itsp
->num_blocks
;
1479 hdr
= &pmgl
->chunk
.header
;
1480 hdr
->block_prev
= itsp
->num_blocks
- 1;
1482 if (chm
->pmgl_last
) {
1483 hdr
= &chm
->pmgl_last
->chunk
.header
;
1484 hdr
->block_next
= itsp
->num_blocks
;
1489 chm
->pmgl_last
= pmgl
;
1492 int chmc_pmgi_done(struct chmcFile
*chm
)
1494 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1495 struct chmcPmglChunkNode
*pmgl
;
1496 struct list_head
*pos
;
1499 char name
[256]; //FIXME use malloc
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;
1512 itsp
->index_root
= itsp
->num_blocks
;
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
);
1524 BUG_ON("name_len >= 255(%lu) %.*s\n", (unsigned long)name_len
, 255,
1525 &pmgl
->chunk
.data
[j
]);
1532 int chmc_pmgi_add_entry(struct chmcFile
*chm
, const char *name
, int pmgl_id
)
1534 struct chmcPmgiChunkNode
*pmgi
;
1535 struct chmcPmgiChunk
*chunk
;
1536 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1542 int should_idx
, idx_intlv
;
1547 // check chunk space for new entry
1548 name_len
= strlen(name
);
1550 outlen
= chmc_encint_len(name_len
);
1552 outlen
+= chmc_encint_len(pmgl_id
);
1554 // look for current pmgi chunk, create if doesn't exist
1555 if (!chm
->pmgi_last
) {
1556 pmgi
= chmc_pmgi_create();
1558 chmc_pmgi_add(chm
, pmgi
);
1560 chmcerr_set_return(CHMC_ENOMEM
, "pmgi chunk: ");
1563 pmgi
= chm
->pmgi_last
;
1567 chunk
= &chm
->pmgi_last
->chunk
;
1569 idx_intlv
= 1 + ( 1 << itsp
->blockidx_intvl
);
1570 should_idx
= ( ( chunk
->entries_count
> 0 )
1571 && ! ( ( chunk
->entries_count
+ 1 ) % idx_intlv
)
1574 free
= sizeof(chunk
->data
) - pmgi
->data_len
-
1575 pmgi
->index_len
- should_idx
;
1577 // current(last) chunk doesn't have enough room? force new one
1578 if (outlen
+ should_idx
> free
) {
1579 pmgi
= chmc_pmgi_create();
1581 chmc_pmgi_add(chm
, pmgi
);
1583 chmcerr_set_return(CHMC_ENOMEM
, "pmgi chunk: ");
1588 p
= (void *)&chunk
->data
[pmgi
->data_len
];
1591 idx
= (void *)((char *)&chunk
->data
[CHMC_PMGI_DATA_LEN
] - pmgi
->index_len
);
1592 *idx
= (char *)p
- (char *)&chunk
->data
;
1595 p
+= chmc_encint(name_len
, p
);
1596 memcpy(p
, name
, name_len
);
1598 p
+= chmc_encint(pmgl_id
, p
);
1600 pmgi
->data_len
+= outlen
;
1601 pmgi
->index_len
+= should_idx
;
1603 chunk
->entries_count
++;
1604 chunk
->header
.free_space
-= outlen
;
1612 void chmc_pmgi_add(struct chmcFile
*chm
, struct chmcPmgiChunkNode
*pmgi
)
1614 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1619 list_add_tail(&pmgi
->list
, &chm
->pmgi_list
);
1622 chm
->pmgi_last
= pmgi
;
1625 int chmc_write(struct chmcFile
*chm
)
1627 struct chmcItsfHeader
*itsf
= &chm
->itsf
;
1628 struct chmcSect0
*sect0
= &chm
->sect0
;
1629 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1631 struct chmcPmglChunkNode
*pmgl
;
1632 struct chmcPmgiChunkNode
*pmgi
;
1633 struct list_head
*pos
;
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
);
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
);
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
);
1662 int chmc_appendfile(struct chmcFile
*chm
, const char *filename
, void *buf
,
1665 struct stat statbuf
;
1670 if (stat(filename
, &statbuf
) < 0)
1673 in
= open(filename
, O_RDONLY
| O_BINARY
);
1675 todo
= statbuf
.st_size
;
1682 rx
= read(in
, buf
, toread
);
1684 write(chm
->fd
, buf
, rx
);
1692 BUG_ON("open %s\n", filename
);