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