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);
672 const char *entry_val
;
674 p
= chmc_syscat_mem(sysp
, &system
->version
, sizeof(system
->version
));
677 p
= chmc_syscat_entry(SIEC_TIMESTAMP
, p
, &val
, sizeof(val
));
678 p
= chmc_syscat_entry(SIEC_COMPVER
, p
,
679 /*"HHA Version 4.74.8702"*/
681 sizeof(PACKAGE_STRING
)
682 /*strlen("HHA Version 4.74.8702")+1*/);
683 p
= chmc_syscat_entry(SIEC_SYSINFO
, p
,
684 sysinfo
, sizeof(struct chmcSystemInfo
));
686 if (chm
->config
!= NULL
&& chm
->config
->deftopic
!= NULL
)
687 entry_val
= chm
->config
->deftopic
;
689 entry_val
= "index.htm";
690 p
= chmc_syscat_entry(SIEC_DEFTOPIC
, p
, (void *)entry_val
,
691 strlen(entry_val
)+1);
693 if (chm
->config
!= NULL
&& chm
->config
->title
!= NULL
)
694 entry_val
= chm
->config
->title
;
696 entry_val
= "untitled";
697 p
= chmc_syscat_entry(SIEC_TITLE
, p
, (void *)entry_val
,
698 strlen(entry_val
)+1);
699 // p = chmc_syscat_entry(SIEC_DEFFONT, p, &val, sizeof(val));
700 p
= chmc_syscat_entry(SIEC_LCASEFILE
, p
, "siec_lcasefile",
701 strlen("siec_lcasefile")+1);
702 p
= chmc_syscat_entry(SIEC_DEFWINDOW
, p
,
703 "MsdnHelp", strlen("MsdnHelp")+1);
706 p
= chmc_syscat_entry(SIEC_NUMOFINFOT
, p
, &val
, sizeof(val
));
708 p
= chmc_syscat_entry(SIEC_IDXHDR
, p
,
709 idxhdr
, sizeof(struct chmcIndexHeader
));
713 p
= chmc_syscat_entry(SIEC_INFOCHKSUM
, p
, &val
, sizeof(val
));
715 system
->_size
= (char *)p
- (char *)sysp
;
716 chmc_add_meta(chm
, "/#SYSTEM", 0, sysp
, system
->_size
);
720 chmcerr_set(CHMC_ENOMEM
, "system done: malloc %d bytes",
726 int chmc_tree_done( struct chmcFile
*chm
)
728 struct chmcItsfHeader
*itsf
;
729 struct chmcSect0
*sect0
;
730 struct chmcItspHeader
*itsp
;
731 struct chmcTreeNode
*ctrl
;
741 chmc_add_dir(chm
, "/");
743 ctrl
= chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/Transform/List",
744 0, (UChar
*)chmc_transform_list
,
745 sizeof(chmc_transform_list
));
747 ctrl
->flags
|= CHMC_TNFL_STATIC
;
749 chmc_system_done(chm
);
751 if (chm
->config
!= NULL
&& chm
->config
->deftopic
!= NULL
)
752 val
= chm
->config
->deftopic
;
756 str_index
= chmc_strings_add(chm
, val
);
763 struct chmcTopicEntry topicEntry
;
764 // struct chmcUrlStrEntry urlStrEntry;
771 topicEntry
.tocidx_offset
= 4096;
772 topicEntry
.strings_offset
= -1;
773 topicEntry
.urltbl_offset
= 0;
774 topicEntry
.in_content
= 6;
775 topicEntry
.unknown
= 0;
777 memcpy(p
, &topicEntry
, sizeof(struct chmcTopicEntry
));
778 len
+= sizeof(struct chmcTopicEntry
);
780 chm
->idxhdr
.num_of_topic
++;
782 chmc_add_meta(chm
, "/#TOPICS", 1, (UChar
*)p
, len
);
784 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
788 ctrl
= chmc_add_meta(chm
, "/#IDXHDR", 1, (void *)&chm
->idxhdr
,
789 sizeof(struct chmcIndexHeader
));
791 ctrl
->flags
|= CHMC_TNFL_STATIC
;
805 // p[2+3] = 0x00000532;
806 // p[2+4] = 0x00062520;
815 // p[2+27] = 0x00000041;
818 if (chm
->config
!= NULL
&& chm
->config
->title
!= NULL
)
819 val
= chm
->config
->title
;
822 p
[2+5] = chmc_strings_add(chm
, val
);
824 if (chm
->config
!= NULL
&& chm
->config
->hhc
!= NULL
)
825 val
= chm
->config
->hhc
;
828 p
[2+24] = chmc_strings_add(chm
, val
);
830 if (chm
->config
!= NULL
&& chm
->config
->hhk
!= NULL
)
831 val
= chm
->config
->hhc
;
834 p
[2+25] = chmc_strings_add(chm
, val
);
837 chmc_add_meta(chm
, "/#WINDOWS", 1, (UChar
*)p
, 8+196);
839 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
842 ctrl
= chmc_add_meta(chm
, "/#STRINGS", 1, (void *)chm
->strings
,
845 ctrl
->flags
|= CHMC_TNFL_STATIC
;
852 struct chmcUrlStrEntry urlStrEntry
;
854 urlStrEntry
.url_offset
= 0;
855 urlStrEntry
.framename_offset
= 0;
863 memcpy(p
+ len
, &urlStrEntry
, sizeof(struct chmcUrlStrEntry
));
864 len
+= sizeof(struct chmcUrlStrEntry
);
865 len
+= sprintf(p
+ len
, "index.htm" ) + 1;
867 memcpy(p
+ len
, &urlStrEntry
, sizeof(struct chmcUrlStrEntry
));
868 len
+= sizeof(struct chmcUrlStrEntry
);
869 len
+= sprintf(p
+ len
, "test.htm" ) + 1;
871 chmc_add_meta(chm
, "/#URLSTR", 1, (UChar
*)p
, len
);
873 BUG_ON("FIXME: %s: %d\n", __FILE__
, __LINE__
);
877 // chmc_add_entry(chm, "/#URLTBL", 0, 1, NULL, 0, 0);
878 // chmc_add_entry(chm, "/#TOPICS", 0, 1, NULL, 0, 0);
880 // NOTE NOTE NOTE add any meta compressed before crunch ;-)
882 chmc_crunch_lzx(chm
, 1);
884 chmc_control_data_done(chm
);
885 chmc_reset_table_done(chm
);
887 chmc_add_empty(chm
, "/#ITBITS");
889 // NOTE in this implementation compressed Content should be the last file
890 // added to section 0
892 chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/Content", 0, NULL
,
893 chm
->sections
[1]->offset
);
895 chmc_entries_qsort(chm
);
896 chmc_uncompressed_done(chm
);
901 itsf
->dir_len
= _CHMC_ITSP_V1_LEN
902 + (_CHMC_CHUNK_LEN
* itsp
->num_blocks
);
904 itsf
->data_offset
= _CHMC_ITSF_V3_LEN
907 + (_CHMC_CHUNK_LEN
* itsp
->num_blocks
);
909 sect0
->file_len
+= _CHMC_CHUNK_LEN
* itsp
->num_blocks
;
914 struct chmcSection
*section
;
915 struct list_head
*pos
;
918 list_for_each(pos
, &chm
->sections_list
) {
919 section
= list_entry(pos
, struct chmcSection
, list
);
920 chmc_appendfile(chm
, section
->filename
, buf
, 4096);
927 int chmc_crunch_lzx(struct chmcFile
*chm
, int sect_id
)
929 struct chmcLzxInfo lzx_info
;
940 if ((wsize_code
< 15) || (wsize_code
> 21)) {
941 fprintf(stderr
, "window size must be between 15 and 21 inclusive\n");
946 lzx_info
.section
= chm
->sections
[sect_id
];
948 lzx_info
.todo
= lzx_info
.section
->offset
;
949 lzx_info
.pos
= chm
->entries_list
.next
;
954 lzx_info
.fd_offset
= 0;
956 chmc_compressed_add_mark(lzx_info
.chm
, 0);
957 lzx_info
.section
->reset_table_header
.block_count
++;
959 /* undocumented fact, according to Caie --
960 block size cannot exceed window size. (why not?) */
961 /* The block size must not be larger than the window size.
962 While the compressor will create apparently-valid LZX files
963 if this restriction is violated, some decompressors
964 will not handle them. */
966 block_size
= 1 << wsize_code
;
968 // lzx_info.section->control_data.windowSize = wsize_code;
969 // lzx_info.section->control_data.windowsPerReset = block_size;
971 lzx_init(&lzxd
, wsize_code
,
972 _lzx_get_bytes
, &lzx_info
, _lzx_at_eof
,
973 _lzx_put_bytes
, &lzx_info
,
974 _lzx_mark_frame
, &lzx_info
);
976 while(! _lzx_at_eof(&lzx_info
)) {
979 lzx_compress_block(lzxd
, block_size
, subd_ok
);
981 lzx_finish(lzxd
, &lzxr
);
986 static int _lzx_at_eof(void *arg
)
988 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
990 return lzx_info
->error
|| lzx_info
->done
>= lzx_info
->todo
|| lzx_info
->eof
;
993 static int _lzx_put_bytes(void *arg
, int n
, void *buf
)
995 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
996 struct chmcSect0
*sect0
= &lzx_info
->chm
->sect0
;
998 static int counter
= 0;
1001 wx
= write(lzx_info
->section
->fd
, buf
, n
);
1002 sect0
->file_len
+= wx
;
1003 lzx_info
->section
->len
+= wx
;
1008 static void _lzx_mark_frame(void *arg
, uint32_t uncomp
, uint32_t comp
)
1010 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
1011 struct chmcSection
*section
= lzx_info
->chm
->sections
[1];
1015 chmc_dump( "Aligned data at %d(in compressed stream, %d) (%lu/%lu)\n",
1016 uncomp
, comp
, (unsigned long)lzx_info
->done
, (unsigned long)lzx_info
->todo
);
1020 section
->reset_table_header
.block_count
++;
1022 chmc_compressed_add_mark( lzx_info
->chm
, compressed
);
1024 section
->reset_table_header
.uncompressed_len
= uncomp
;
1025 section
->reset_table_header
.compressed_len
= comp
;
1028 static int _lzx_get_bytes(void *arg
, int n
, void *buf
)
1030 struct chmcLzxInfo
*lzx_info
= (struct chmcLzxInfo
*)arg
;
1031 struct chmcFile
*chm
= lzx_info
->chm
;
1032 struct chmcTreeNode
*node
;
1042 // compression state machine
1043 // lzx compressor ask for block input bytes
1044 // need to keep current entry file and offset trought blocks
1047 // end of entries reached?
1048 if (lzx_info
->pos
== &chm
->entries_list
) {
1053 node
= list_entry( lzx_info
->pos
, struct chmcTreeNode
, list
);
1055 // skip empty files and directories
1057 || strcmp("MSCompressed", chm
->sections
[node
->sect_id
]->name
)) {
1058 lzx_info
->pos
= lzx_info
->pos
->next
;
1063 // have len and buffer, it's mallocated not file
1066 if (lzx_info
->fd
== -1) {
1067 // open file if it isn't
1068 lzx_info
->fd
= open(node
->name
, O_RDONLY
| O_BINARY
);
1069 if (lzx_info
->fd
< 0) {
1070 chmc_error("%s: %d: error %d: '%s' %s\n",
1072 errno
, node
->name
, strerror(errno
));
1073 lzx_info
->error
= 1;
1078 // read till the end of the file or till the lzx buffer is filled
1079 toread
= node
->len
- lzx_info
->fd_offset
;
1088 memcpy((char *)buf
+ (n
- todo
), &node
->buf
[lzx_info
->fd_offset
], toread
);
1093 rx
= read(lzx_info
->fd
, (char *)buf
+ (n
- todo
), toread
);
1096 chmc_error("read error %s \n", strerror(temp
));
1097 lzx_info
->error
= 2;
1103 lzx_info
->fd_offset
+= rx
;
1105 lzx_info
->done
+= rx
;
1107 // end of current file reached, goto next entry
1108 if (lzx_info
->fd_offset
== node
->len
) {
1109 if (lzx_info
->fd
> -1)
1110 close(lzx_info
->fd
);
1112 lzx_info
->fd_offset
= 0;
1113 lzx_info
->pos
= lzx_info
->pos
->next
;
1120 int chmc_compressed_add_mark(struct chmcFile
*chm
, UInt64 at
)
1122 struct chmcSection
*section
;
1123 struct chmcResetTableMark
*mark
;
1127 section
= chm
->sections
[1];
1129 mark
= malloc(_CHMC_RSTTBL_MARK
);
1132 chmc_dump("[%d] at: %jd\n", section
->mark_count
, at
);
1133 list_add_tail(&mark
->list
, §ion
->mark_list
);
1134 section
->mark_count
++;
1141 int chmc_control_data_done(struct chmcFile
*chm
)
1143 struct chmcTreeNode
*ctrl
;
1145 ctrl
= chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/ControlData",
1146 0, (UChar
*)&chm
->sections
[1]->control_data
,
1150 ctrl
->flags
|= CHMC_TNFL_STATIC
;
1157 int chmc_reset_table_done(struct chmcFile
*chm
)
1159 struct chmcSection
*section
;
1160 struct chmcLzxcResetTable
*reset_table
;
1161 struct list_head
*pos
;
1162 struct chmcResetTableMark
*mark
;
1167 section
= chm
->sections
[1];
1169 len
= _CHMC_LZXC_RESETTABLE_V1_LEN
+ (section
->mark_count
* sizeof(UInt64
));
1171 reset_table
= malloc(len
);
1174 memcpy(reset_table
, §ion
->reset_table_header
,
1175 _CHMC_LZXC_RESETTABLE_V1_LEN
);
1176 at
= (void *)((char *)reset_table
+ _CHMC_LZXC_RESETTABLE_V1_LEN
);
1179 list_for_each(pos
, §ion
->mark_list
) {
1180 mark
= list_entry(pos
, struct chmcResetTableMark
, list
);
1184 chmc_add_dir(chm
, "::DataSpace/Storage/MSCompressed/Transform/"
1185 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/");
1186 chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/Transform/"
1187 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}"
1188 "/InstanceData/ResetTable",
1189 0, (UChar
*)reset_table
, len
);
1191 { // TODO FIXME do better
1192 UInt64
*uncompressed_len
= malloc(8);
1193 if (uncompressed_len
) {
1194 *uncompressed_len
= reset_table
->uncompressed_len
;
1195 chmc_add_meta(chm
, "::DataSpace/Storage/MSCompressed/SpanInfo",
1196 0, (UChar
*)uncompressed_len
, 8);
1206 void chmc_entries_qsort(struct chmcFile
*chm
)
1208 struct chmcTreeNode
*node
;
1209 struct list_head
*pos
;
1214 chm
->sort_entries
= malloc(sizeof(struct chmcTreeNode
*)
1215 * chm
->entries_num
);
1218 list_for_each(pos
, &chm
->entries_list
) {
1219 node
= list_entry(pos
, struct chmcTreeNode
, list
);
1220 chm
->sort_entries
[i
++] = node
;
1223 qsort(chm
->sort_entries
, chm
->entries_num
, sizeof(struct chmcTreeNode
*),
1227 static int _entry_cmp(const void *pva
, const void *pvb
)
1229 const struct chmcTreeNode
* const *pa
= pva
;
1230 const struct chmcTreeNode
* const *pb
= pvb
;
1231 const struct chmcTreeNode
*a
= *pa
, *b
= *pb
;
1233 return strcmp( &a
->name
[a
->prefixlen
], &b
->name
[b
->prefixlen
] );
1236 int chmc_uncompressed_done(struct chmcFile
*chm
)
1238 struct chmcSect0
*sect0
= &chm
->sect0
;
1239 struct chmcTreeNode
*node
;
1240 struct list_head
*pos
;
1243 list_for_each(pos
, &chm
->entries_list
) {
1244 node
= list_entry( pos
, struct chmcTreeNode
, list
);
1246 if (strcmp( "MSCompressed", chm
->sections
[node
->sect_id
]->name
) == 0)
1249 if ((node
->buf
) && (node
->len
> 0)) {
1250 wx
= write(chm
->sections
[node
->sect_id
]->fd
, node
->buf
, node
->len
);
1251 sect0
->file_len
+= wx
;
1258 void chmc_pmgl_done(struct chmcFile
*chm
)
1260 struct chmcTreeNode
*entry
;
1265 for(i
=0; i
< chm
->entries_num
; i
++) {
1266 entry
= chm
->sort_entries
[i
];
1267 chmc_pmgl_add_entry(chm
, entry
);
1271 int chmc_pmgl_add_entry(struct chmcFile
*chm
, struct chmcTreeNode
*entry
)
1273 struct chmcPmglChunkNode
*pmgl
;
1274 struct chmcPmglChunk
*chunk
;
1275 struct chmcSection
*section
;
1276 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1282 int should_idx
, idx_intlv
;
1288 // check section bound
1289 section
= chmc_section_lookup(chm
, entry
->sect_id
);
1291 chmcerr_set_return(CHMC_ENOMEM
, "section %d lookup failed: ",
1294 // check chunk space for new entry
1295 name_len
= strlen(&entry
->name
[entry
->prefixlen
]);
1297 outlen
= chmc_encint_len(name_len
);
1299 outlen
+= chmc_encint_len(entry
->sect_id
);
1300 outlen
+= chmc_encint_len(entry
->offset
);
1301 outlen
+= chmc_encint_len(entry
->len
);
1303 // look for current pmgl chunk, create if doesn't exist
1304 if (!chm
->pmgl_last
) {
1305 pmgl
= chmc_pmgl_create();
1307 chmc_pmgl_add(chm
, pmgl
);
1309 chmcerr_set_return(CHMC_ENOMEM
, "pmgl chunk: ");
1312 pmgl
= chm
->pmgl_last
;
1316 chunk
= &chm
->pmgl_last
->chunk
;
1318 idx_intlv
= 1 + ( 1 << itsp
->blockidx_intvl
);
1319 should_idx
= ( ( chunk
->entries_count
> 0 )
1320 && ! ( ( chunk
->entries_count
+ 1 ) % idx_intlv
)
1323 free
= sizeof(chunk
->data
) - pmgl
->data_len
- pmgl
->index_len
1326 // current(last) chunk doesn't have enough room? force new one
1327 if (outlen
+ should_idx
> free
) {
1328 //chm->pmgl_last = NULL;
1329 pmgl
= chmc_pmgl_create();
1331 chmc_pmgl_add(chm
, pmgl
);
1333 chmcerr_set_return(CHMC_ENOMEM
, "pmgl chunk: ");
1338 p
= (void *)&chunk
->data
[pmgl
->data_len
];
1341 idx
= (void *)((char *)&chunk
->data
[CHMC_PMGL_DATA_LEN
] - pmgl
->index_len
);
1342 *idx
= (char *)p
- (char *)&chunk
->data
;
1345 p
+= chmc_encint(name_len
, p
);
1346 memcpy(p
, &entry
->name
[entry
->prefixlen
], name_len
);
1348 p
+= chmc_encint(entry
->sect_id
, p
);
1349 p
+= chmc_encint(entry
->offset
, p
);
1350 p
+= chmc_encint(entry
->len
, p
);
1352 pmgl
->data_len
+= outlen
;
1353 pmgl
->index_len
+= should_idx
;
1355 chunk
->entries_count
++;
1356 chunk
->header
.free_space
-= outlen
;
1364 struct chmcSection
*chmc_section_lookup(struct chmcFile
*chm
, int id
)
1366 struct chmcSection
*current
;
1367 struct list_head
*pos
;
1373 list_for_each(pos
, &chm
->sections_list
) {
1374 current
= list_entry(pos
, struct chmcSection
, list
);
1383 struct chmcPmglChunkNode
*chmc_pmgl_create(void)
1385 struct chmcPmglChunkNode
*node
;
1387 node
= malloc(sizeof(struct chmcPmglChunkNode
));
1389 chmc_pmgl_init(node
);
1394 void chmc_pmgl_init(struct chmcPmglChunkNode
*node
)
1396 struct chmcPmglChunk
*chunk
;
1401 node
->index_len
= 0;
1403 chunk
= &node
->chunk
;
1405 memcpy(chunk
->header
.signature
, "PMGL", 4);
1407 // FIXME check it is the right len
1408 chunk
->header
.free_space
= CHMC_PMGL_DATA_LEN
+ 2;
1409 chunk
->header
.unknown_0008
= 0;
1410 chunk
->header
.block_prev
= -1;
1411 chunk
->header
.block_next
= -1;
1413 memset(chunk
->data
, 0, CHMC_PMGL_DATA_LEN
);
1416 void chmc_pmgi_init(struct chmcPmgiChunkNode
*node
)
1418 struct chmcPmgiChunk
*chunk
;
1423 node
->index_len
= 0;
1425 chunk
= &node
->chunk
;
1427 memcpy(chunk
->header
.signature
, "PMGI", 4);
1429 // FIXME check it is the right len
1430 chunk
->header
.free_space
= CHMC_PMGI_DATA_LEN
+ 2;
1431 // chunk->header.unknown_0008 = 0;
1432 // chunk->header.block_prev = -1;
1433 // chunk->header.block_next = -1;
1435 memset(chunk
->data
, 0, CHMC_PMGI_DATA_LEN
);
1440 struct chmcPmgiChunkNode
*chmc_pmgi_create(void)
1442 struct chmcPmgiChunkNode
*node
;
1444 node
= malloc(sizeof(struct chmcPmgiChunkNode
));
1446 chmc_pmgi_init(node
);
1451 void chmc_pmgl_destroy(struct chmcPmglChunkNode
*node
)
1457 void chmc_pmgi_destroy(struct chmcPmgiChunkNode
*node
)
1463 void chmc_pmgl_add(struct chmcFile
*chm
, struct chmcPmglChunkNode
*pmgl
)
1465 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1466 struct chmcPmglHeader
*hdr
;
1471 list_add_tail(&pmgl
->list
, &chm
->pmgl_list
);
1473 itsp
->index_last
= itsp
->num_blocks
;
1475 hdr
= &pmgl
->chunk
.header
;
1476 hdr
->block_prev
= itsp
->num_blocks
- 1;
1478 if (chm
->pmgl_last
) {
1479 hdr
= &chm
->pmgl_last
->chunk
.header
;
1480 hdr
->block_next
= itsp
->num_blocks
;
1485 chm
->pmgl_last
= pmgl
;
1488 int chmc_pmgi_done(struct chmcFile
*chm
)
1490 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1491 struct chmcPmglChunkNode
*pmgl
;
1492 struct list_head
*pos
;
1495 char name
[256]; //FIXME use malloc
1500 // only one pml, omitted pmgi
1501 if (itsp
->num_blocks
== 1) {
1502 itsp
->index_depth
= 1;
1503 itsp
->index_root
= -1;
1504 itsp
->index_last
= 0;
1508 itsp
->index_root
= itsp
->num_blocks
;
1511 list_for_each(pos
, &chm
->pmgl_list
) {
1512 pmgl
= list_entry(pos
, struct chmcPmglChunkNode
, list
);
1513 j
= chmc_decint(&pmgl
->chunk
.data
[0], &name_len
);
1514 if (name_len
<= 255) {
1515 memcpy(name
, &pmgl
->chunk
.data
[j
], name_len
);
1516 name
[name_len
] = '\0';
1517 chmc_pmgi_add_entry(chm
, name
, i
);
1520 BUG_ON("name_len >= 255(%lu) %.*s\n", (unsigned long)name_len
, 255,
1521 &pmgl
->chunk
.data
[j
]);
1528 int chmc_pmgi_add_entry(struct chmcFile
*chm
, const char *name
, int pmgl_id
)
1530 struct chmcPmgiChunkNode
*pmgi
;
1531 struct chmcPmgiChunk
*chunk
;
1532 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1538 int should_idx
, idx_intlv
;
1543 // check chunk space for new entry
1544 name_len
= strlen(name
);
1546 outlen
= chmc_encint_len(name_len
);
1548 outlen
+= chmc_encint_len(pmgl_id
);
1550 // look for current pmgi chunk, create if doesn't exist
1551 if (!chm
->pmgi_last
) {
1552 pmgi
= chmc_pmgi_create();
1554 chmc_pmgi_add(chm
, pmgi
);
1556 chmcerr_set_return(CHMC_ENOMEM
, "pmgi chunk: ");
1559 pmgi
= chm
->pmgi_last
;
1563 chunk
= &chm
->pmgi_last
->chunk
;
1565 idx_intlv
= 1 + ( 1 << itsp
->blockidx_intvl
);
1566 should_idx
= ( ( chunk
->entries_count
> 0 )
1567 && ! ( ( chunk
->entries_count
+ 1 ) % idx_intlv
)
1570 free
= sizeof(chunk
->data
) - pmgi
->data_len
-
1571 pmgi
->index_len
- should_idx
;
1573 // current(last) chunk doesn't have enough room? force new one
1574 if (outlen
+ should_idx
> free
) {
1575 pmgi
= chmc_pmgi_create();
1577 chmc_pmgi_add(chm
, pmgi
);
1579 chmcerr_set_return(CHMC_ENOMEM
, "pmgi chunk: ");
1584 p
= (void *)&chunk
->data
[pmgi
->data_len
];
1587 idx
= (void *)((char *)&chunk
->data
[CHMC_PMGI_DATA_LEN
] - pmgi
->index_len
);
1588 *idx
= (char *)p
- (char *)&chunk
->data
;
1591 p
+= chmc_encint(name_len
, p
);
1592 memcpy(p
, name
, name_len
);
1594 p
+= chmc_encint(pmgl_id
, p
);
1596 pmgi
->data_len
+= outlen
;
1597 pmgi
->index_len
+= should_idx
;
1599 chunk
->entries_count
++;
1600 chunk
->header
.free_space
-= outlen
;
1608 void chmc_pmgi_add(struct chmcFile
*chm
, struct chmcPmgiChunkNode
*pmgi
)
1610 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1615 list_add_tail(&pmgi
->list
, &chm
->pmgi_list
);
1618 chm
->pmgi_last
= pmgi
;
1621 int chmc_write(struct chmcFile
*chm
)
1623 struct chmcItsfHeader
*itsf
= &chm
->itsf
;
1624 struct chmcSect0
*sect0
= &chm
->sect0
;
1625 struct chmcItspHeader
*itsp
= &chm
->itsp
;
1627 struct chmcPmglChunkNode
*pmgl
;
1628 struct chmcPmgiChunkNode
*pmgi
;
1629 struct list_head
*pos
;
1633 chmc_dump("write itsf %d\n", _CHMC_ITSF_V3_LEN
);
1634 write(chm
->fd
, itsf
, _CHMC_ITSF_V3_LEN
);
1635 chmc_dump("write sect0 %d\n", _CHMC_SECT0_LEN
);
1636 write(chm
->fd
, sect0
, _CHMC_SECT0_LEN
);
1637 chmc_dump("write itsp %d\n", _CHMC_ITSP_V1_LEN
);
1638 write(chm
->fd
, itsp
, _CHMC_ITSP_V1_LEN
);
1640 list_for_each(pos
, &chm
->pmgl_list
) {
1641 pmgl
= list_entry(pos
, struct chmcPmglChunkNode
, list
);
1642 chmc_dump("write pmgl %d\n", _CHMC_CHUNK_LEN
);
1643 write(chm
->fd
, &pmgl
->chunk
, _CHMC_CHUNK_LEN
);
1646 chmc_dump("itsp->num_blocks %d", itsp
->num_blocks
);
1647 if (itsp
->num_blocks
> 1) {
1648 list_for_each( pos
, &chm
->pmgi_list
) {
1649 pmgi
= list_entry(pos
, struct chmcPmgiChunkNode
, list
);
1650 chmc_dump("write pmgi %d\n", _CHMC_CHUNK_LEN
);
1651 write(chm
->fd
, &pmgi
->chunk
, _CHMC_CHUNK_LEN
);
1658 int chmc_appendfile(struct chmcFile
*chm
, const char *filename
, void *buf
,
1661 struct stat statbuf
;
1666 if (stat(filename
, &statbuf
) < 0)
1669 in
= open(filename
, O_RDONLY
| O_BINARY
);
1671 todo
= statbuf
.st_size
;
1678 rx
= read(in
, buf
, toread
);
1680 write(chm
->fd
, buf
, rx
);
1688 BUG_ON("open %s\n", filename
);