mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 21:13:52 +00:00
1692 lines
40 KiB
C
1692 lines
40 KiB
C
/*
|
|
|
|
Copyright(C) 2010 Alex Andreotti <alex.andreotti@gmail.com>
|
|
|
|
This file is part of chmc.
|
|
|
|
chmc is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
chmc is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with chmc. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
#include "chmc.h"
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#if defined(_WIN32) || defined(__APPLE__)
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#else
|
|
#ifdef __REACTOS__
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#endif /* __REACTOS__ */
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "err.h"
|
|
|
|
|
|
#include "encint.h"
|
|
|
|
#include <stdint.h>
|
|
#include "../lzx_compress/lzx_config.h"
|
|
#include "../lzx_compress/lzx_compress.h"
|
|
|
|
#define PACKAGE_STRING "hhpcomp development version"
|
|
|
|
/* if O_BINARY is not defined, the system is probably not expecting any such flag */
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
int chmc_section_add(struct chmcFile *chm, const char *name);
|
|
struct chmcSection * chmc_section_create(struct chmcFile *chm,
|
|
const char *name);
|
|
void chmc_reset_table_init(struct chmcLzxcResetTable *reset_table);
|
|
void chmc_control_data_init(struct chmcLzxcControlData *control_data);
|
|
int chmc_namelist_create(struct chmcFile *chm, int len);
|
|
struct chmcTreeNode * chmc_add_meta(struct chmcFile *chm,
|
|
const char *metaname, int sect_id,
|
|
UChar *buf, UInt64 len);
|
|
struct chmcTreeNode *chmc_add_entry(struct chmcFile *chm, const char *name,
|
|
UInt16 prefixlen, int sect_id,
|
|
UChar *buf, UInt64 offset, UInt64 len);
|
|
void chmc_sections_free(struct chmcFile *chm);
|
|
void chmc_section_destroy(struct chmcSection *section);
|
|
void chmc_pmgi_free(struct chmcFile *chm);
|
|
void chmc_pmgl_free(struct chmcFile *chm);
|
|
void chmc_pmgl_destroy(struct chmcPmglChunkNode *node);
|
|
void chmc_pmgi_destroy(struct chmcPmgiChunkNode *node);
|
|
void chmc_entries_free(struct chmcFile *chm);
|
|
void chmc_entry_destroy(struct chmcTreeNode *node);
|
|
int chmc_add_tree(struct chmcFile *chm, const char *dir);
|
|
struct chmcTreeNode *chmc_add_file(struct chmcFile *chm, const char *filename,
|
|
UInt16 prefixlen, int sect_id, UChar *buf,
|
|
UInt64 len);
|
|
struct chmcTreeNode *chmc_add_dir(struct chmcFile *chm, const char *dir);
|
|
struct chmcTreeNode *chmc_add_empty(struct chmcFile *chm, const char *file);
|
|
|
|
int chmc_crunch_lzx(struct chmcFile *chm, int sect_id);
|
|
static int _lzx_at_eof(void *arg);
|
|
static int _lzx_put_bytes(void *arg, int n, void *buf);
|
|
static void _lzx_mark_frame(void *arg, uint32_t uncomp, uint32_t comp);
|
|
static int _lzx_get_bytes(void *arg, int n, void *buf);
|
|
|
|
int chmc_compressed_add_mark(struct chmcFile *chm, UInt64 at);
|
|
int chmc_control_data_done(struct chmcFile *chm);
|
|
int chmc_reset_table_done(struct chmcFile *chm);
|
|
void chmc_pmgl_done(struct chmcFile *chm);
|
|
|
|
void chmc_entries_qsort(struct chmcFile *chm);
|
|
static int _entry_cmp(const void *pva, const void *pvb);
|
|
|
|
struct chmcSection *chmc_section_lookup(struct chmcFile *chm, int id);
|
|
|
|
struct chmcPmglChunkNode *chmc_pmgl_create(void);
|
|
void chmc_pmgl_add(struct chmcFile *chm, struct chmcPmglChunkNode *pmgl);
|
|
void chmc_pmgl_init(struct chmcPmglChunkNode *node);
|
|
int chmc_pmgi_add_entry(struct chmcFile *chm, const char *name, int pmgl_id);
|
|
void chmc_pmgi_add(struct chmcFile *chm, struct chmcPmgiChunkNode *pmgi);
|
|
void chmc_string_init(struct chmcStringChunk *node);
|
|
|
|
#ifdef __REACTOS__
|
|
int chmc_uncompressed_done(struct chmcFile *chm);
|
|
int chmc_pmgi_done(struct chmcFile *chm);
|
|
int chmc_write(struct chmcFile *chm);
|
|
int chmc_appendfile(struct chmcFile *chm, const char *filename, void *buf,
|
|
size_t size );
|
|
int chmc_pmgl_add_entry(struct chmcFile *chm, struct chmcTreeNode *entry);
|
|
#endif /* __REACTOS__ */
|
|
|
|
struct chmcLzxInfo
|
|
{
|
|
struct chmcFile *chm;
|
|
struct chmcSection *section;
|
|
int fd;
|
|
UInt32 fd_offset;
|
|
UInt32 done;
|
|
UInt32 todo;
|
|
struct list_head *pos;
|
|
int error;
|
|
int eof;
|
|
};
|
|
|
|
static const short chmc_transform_list[] = {
|
|
0x7b, 0x37, 0x46, 0x43, 0x32, 0x38, 0x39,
|
|
0x34, 0x30, 0x2d, 0x39, 0x44, 0x33, 0x31,
|
|
0x2d, 0x31, 0x31, 0x44, 0x30 };
|
|
|
|
int chmc_init(struct chmcFile *chm, const char *filename,
|
|
struct chmcConfig *config)
|
|
{
|
|
struct chmcItsfHeader *itsf = &chm->itsf;
|
|
struct chmcSect0 *sect0 = &chm->sect0;
|
|
struct chmcItspHeader *itsp = &chm->itsp;
|
|
struct chmcSystem *system = &chm->system;
|
|
struct chmcSystemInfo *sysinfo = &chm->system.info;
|
|
struct chmcIndexHeader *idxhdr = &chm->idxhdr;
|
|
|
|
assert(chm);
|
|
assert(filename);
|
|
|
|
chmcerr_clean();
|
|
|
|
memset(chm, 0, sizeof(struct chmcFile));
|
|
|
|
chm->config = config;
|
|
|
|
if (strcmp(filename, "-") != 0) {
|
|
chm->fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
|
if (chm->fd < 0) {
|
|
chmcerr_set(errno, strerror(errno));
|
|
chmcerr_return_msg("creat file '%s'", filename);
|
|
}
|
|
} else {
|
|
chm->fd = fileno(stdout);
|
|
}
|
|
|
|
memcpy(itsf->signature, "ITSF", 4);
|
|
itsf->version = 3;
|
|
itsf->header_len = _CHMC_ITSF_V3_LEN;
|
|
itsf->unknown_000c = 1;
|
|
|
|
itsf->lang_id = chm->config->language;
|
|
memcpy(itsf->dir_uuid, CHMC_DIR_UUID, 16);
|
|
memcpy(itsf->stream_uuid, CHMC_STREAM_UUID, 16);
|
|
itsf->dir_offset = _CHMC_ITSF_V3_LEN + _CHMC_SECT0_LEN;
|
|
|
|
itsf->sect0_offset = _CHMC_ITSF_V3_LEN;
|
|
itsf->sect0_len = _CHMC_SECT0_LEN;
|
|
|
|
sect0->file_len = _CHMC_ITSF_V3_LEN
|
|
+ _CHMC_SECT0_LEN
|
|
+ _CHMC_ITSP_V1_LEN;
|
|
|
|
sect0->unknown_0000 = 510;
|
|
|
|
memcpy(itsp->signature, "ITSP", 4);
|
|
itsp->version = 1;
|
|
itsp->header_len = _CHMC_ITSP_V1_LEN;
|
|
itsp->unknown_000c = 10;
|
|
itsp->block_len = _CHMC_CHUNK_LEN;
|
|
itsp->blockidx_intvl = CHM_IDX_INTVL;
|
|
itsp->index_depth = 2;
|
|
|
|
itsp->unknown_0028 = -1;
|
|
itsp->lang_id = CHMC_MS_LCID_EN_US;
|
|
memcpy(itsp->system_uuid, CHMC_SYSTEM_UUID, 16);
|
|
itsp->header_len2 = _CHMC_ITSP_V1_LEN;
|
|
memset(itsp->unknown_0048, -1, 12);
|
|
|
|
system->version = 3;
|
|
system->_size = _CHMC_SYSTEM_HDR_LEN + sizeof(struct chmcIndexHeader);
|
|
|
|
sysinfo->lcid = CHMC_MS_LCID_EN_US;
|
|
|
|
memcpy(idxhdr->signature, "T#SM", 4);
|
|
idxhdr->unknown_4 = 28582569; // FIXME got from some chm
|
|
idxhdr->unknown_8 = 1;
|
|
// idxhdr->full_search = 1;
|
|
// idxhdr->klinks = 1;
|
|
// idxhdr->alinks = 0;
|
|
// idxhdr->timestamp = ???;
|
|
|
|
// idxhdr->num_of_topic = 2; // sorry??
|
|
idxhdr->off_img_list = -1;
|
|
// idxhdr->img_type_folder;
|
|
idxhdr->background = -1;
|
|
idxhdr->foreground = -1;
|
|
idxhdr->off_font = -1;
|
|
idxhdr->win_style = -1;
|
|
idxhdr->ex_win_style = -1;
|
|
idxhdr->unknown_34 = -1;
|
|
idxhdr->off_frame_name = -1;
|
|
idxhdr->off_win_name = -1;
|
|
// idxhdr->num_of_info;
|
|
idxhdr->unknown_44 = 1;
|
|
// idxhdr->num_of_merge_files;
|
|
// idxhdr->unknown_4c;
|
|
|
|
INIT_LIST_HEAD(&chm->sections_list);
|
|
INIT_LIST_HEAD(&chm->pmgl_list);
|
|
INIT_LIST_HEAD(&chm->entries_list);
|
|
INIT_LIST_HEAD(&chm->pmgi_list);
|
|
|
|
chm->strings = malloc(4096);
|
|
memset(chm->strings, 0, 4096);
|
|
chm->strings_len = 4096;
|
|
chm->strings_offset = 1;
|
|
|
|
if (chmc_section_add(chm, "Uncompressed") != CHMC_NOERR)
|
|
chmcerr_return_msg("adding section: Uncompressed");
|
|
|
|
if (chmc_section_add(chm, "MSCompressed") != CHMC_NOERR)
|
|
chmcerr_return_msg("adding section: MSCompressed");
|
|
|
|
chmc_sections_done(chm);
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
int chmc_section_add(struct chmcFile *chm, const char *name)
|
|
{
|
|
struct chmcSection *section;
|
|
|
|
assert(chm);
|
|
assert(name);
|
|
|
|
section = chmc_section_create(chm, name);
|
|
if (!section)
|
|
return chmcerr_code();
|
|
|
|
list_add_tail(§ion->list, &chm->sections_list);
|
|
chm->sections_num++;
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
struct chmcSection *chmc_section_create(struct chmcFile *chm,
|
|
const char *name)
|
|
{
|
|
struct chmcSection *section;
|
|
|
|
assert(name);
|
|
|
|
section = calloc(1, sizeof(struct chmcSection));
|
|
if (section) {
|
|
const char *tmpdir;
|
|
int len;
|
|
|
|
len = strlen(name);
|
|
memcpy(section->name, name, len + 1);
|
|
section->offset = 0;
|
|
section->len = 0;
|
|
|
|
tmpdir = NULL;
|
|
if (chm->config != NULL)
|
|
tmpdir = chm->config->tmpdir;
|
|
if (tmpdir == NULL)
|
|
tmpdir = "/tmp/";
|
|
|
|
len = strlen(tmpdir);
|
|
if (len >= PATH_MAX - 12) {
|
|
chmcerr_set(errno, strerror(errno));
|
|
chmcerr_msg("tmpdir too long: '%s'", tmpdir);
|
|
goto fail;
|
|
}
|
|
|
|
strcat(section->filename, tmpdir);
|
|
if (section->filename[len - 1] != '/')
|
|
strcat(section->filename, "/");
|
|
|
|
if (strcmp("MSCompressed", name) == 0)
|
|
strcat(section->filename, "chmcCXXXXXX");
|
|
else
|
|
strcat(section->filename, "chmcUXXXXXX");
|
|
|
|
section->fd = mkstemps(section->filename, 0);
|
|
fprintf(stderr, "temp file: %s\n", section->filename);
|
|
if (section->fd < 0) {
|
|
chmcerr_set(errno, strerror(errno));
|
|
chmcerr_msg("creat() file '%s'", section->filename);
|
|
goto fail;
|
|
}
|
|
else if (strcmp(section->name, "MSCompressed") == 0) {
|
|
chmc_reset_table_init(§ion->reset_table_header);
|
|
chmc_control_data_init(§ion->control_data);
|
|
INIT_LIST_HEAD(§ion->mark_list);
|
|
section->mark_count = 0;
|
|
}
|
|
} else {
|
|
chmcerr_set(errno, strerror(errno));
|
|
chmcerr_msg("section '%s' allocation failed", name);
|
|
}
|
|
|
|
return section;
|
|
|
|
fail:
|
|
free(section);
|
|
return NULL;
|
|
}
|
|
|
|
void chmc_reset_table_init(struct chmcLzxcResetTable *reset_table)
|
|
{
|
|
reset_table->version = 2;
|
|
reset_table->block_count = 0;
|
|
reset_table->entry_size = 8;
|
|
reset_table->table_offset = _CHMC_LZXC_RESETTABLE_V1_LEN;
|
|
reset_table->uncompressed_len = 0;
|
|
reset_table->compressed_len = 0;
|
|
reset_table->block_len = 0x8000;
|
|
}
|
|
|
|
void chmc_control_data_init(struct chmcLzxcControlData *control_data)
|
|
{
|
|
control_data->size = 6;
|
|
memcpy(control_data->signature, "LZXC", 4);
|
|
control_data->version = 2;
|
|
control_data->resetInterval = 2;
|
|
control_data->windowSize = 2;
|
|
control_data->windowsPerReset = 1;
|
|
control_data->unknown_18 = 0;
|
|
}
|
|
|
|
void chmc_sections_done(struct chmcFile *chm)
|
|
{
|
|
int len;
|
|
int i;
|
|
|
|
assert(chm);
|
|
|
|
chm->sections = malloc(sizeof(struct chmcSection *) * chm->sections_num);
|
|
if (chm->sections) {
|
|
struct chmcSection *section;
|
|
struct list_head *pos;
|
|
|
|
i = 0;
|
|
len = 4;
|
|
list_for_each(pos, &chm->sections_list) {
|
|
section = list_entry(pos, struct chmcSection, list);
|
|
len += 4 + strlen(section->name) * 2;
|
|
chm->sections[i++] = section;
|
|
}
|
|
chmc_namelist_create(chm, len);
|
|
} else
|
|
BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__);
|
|
}
|
|
|
|
int chmc_namelist_create(struct chmcFile *chm, int len)
|
|
{
|
|
UInt16 *namelist;
|
|
|
|
namelist = malloc(len);
|
|
if (namelist) {
|
|
struct chmcSection *section;
|
|
int i, j, k, name_len;
|
|
|
|
k = 0;
|
|
namelist[k++] = len >> 1;
|
|
namelist[k++] = chm->sections_num;
|
|
for( i=0; i < chm->sections_num; i++ ) {
|
|
section = chm->sections[i];
|
|
|
|
name_len = strlen(section->name);
|
|
namelist[k++] = name_len;
|
|
for( j=0; j < name_len; j++ )
|
|
namelist[k++] = section->name[j];
|
|
namelist[k++] = 0;
|
|
}
|
|
chmc_add_meta(chm, "::DataSpace/NameList", 0, (UChar *)namelist, len);
|
|
}
|
|
else
|
|
return CHMC_ENOMEM;
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
struct chmcTreeNode *chmc_add_empty(struct chmcFile *chm, const char *file)
|
|
{
|
|
assert(chm);
|
|
return chmc_add_entry(chm, file, 0, 0, NULL, 0, 0);
|
|
}
|
|
|
|
struct chmcTreeNode *chmc_add_meta(struct chmcFile *chm, const char *metaname,
|
|
int sect_id,
|
|
UChar *buf, UInt64 len)
|
|
{
|
|
struct chmcSection *section;
|
|
struct chmcTreeNode *node;
|
|
|
|
assert(chm);
|
|
|
|
if (sect_id >= chm->sections_num)
|
|
return NULL;
|
|
|
|
section = chm->sections[sect_id];
|
|
|
|
node = chmc_add_entry(chm, metaname, 0, sect_id, buf, section->offset, len);
|
|
|
|
if ((node) && (len > 0))
|
|
section->offset += len;
|
|
|
|
return node;
|
|
}
|
|
|
|
struct chmcTreeNode *chmc_add_entry(struct chmcFile *chm, const char *name,
|
|
UInt16 prefixlen, int sect_id, UChar *buf,
|
|
UInt64 offset, UInt64 len)
|
|
{
|
|
struct chmcTreeNode *node;
|
|
|
|
assert(chm);
|
|
|
|
if (sect_id >= (chm->sections_num)) {
|
|
fprintf(stderr,"sect_id %d >= chm->sections_num %d\n",
|
|
sect_id, chm->sections_num);
|
|
return NULL;
|
|
}
|
|
|
|
node = malloc(sizeof(struct chmcTreeNode));
|
|
if (node) {
|
|
node->flags = 0;
|
|
node->name = strdup( name );
|
|
node->prefixlen = prefixlen;
|
|
node->sect_id = sect_id;
|
|
node->buf = buf;
|
|
node->offset = offset;
|
|
node->len = len;
|
|
list_add_tail(&node->list, &chm->entries_list);
|
|
chm->entries_num++;
|
|
}
|
|
else
|
|
BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__);
|
|
|
|
return node;
|
|
}
|
|
|
|
void chmc_term(struct chmcFile *chm)
|
|
{
|
|
assert(chm);
|
|
assert(chm->fd > -1);
|
|
|
|
free(chm->strings);
|
|
|
|
chmc_entries_free(chm);
|
|
chmc_pmgl_free(chm);
|
|
chmc_pmgi_free(chm);
|
|
if (chm->sections)
|
|
free(chm->sections);
|
|
chmc_sections_free(chm);
|
|
|
|
if (chm->fd != fileno(stdout))
|
|
close(chm->fd);
|
|
}
|
|
|
|
void chmc_sections_free(struct chmcFile *chm)
|
|
{
|
|
struct chmcSection *section;
|
|
struct list_head *pos, *q;
|
|
|
|
assert(chm);
|
|
|
|
list_for_each_safe(pos, q, &chm->sections_list) {
|
|
section = list_entry(pos, struct chmcSection, list);
|
|
list_del(pos);
|
|
chmc_section_destroy(section);
|
|
}
|
|
}
|
|
|
|
void chmc_section_destroy(struct chmcSection *section)
|
|
{
|
|
assert(section);
|
|
assert(section->fd > -1);
|
|
|
|
if (strcmp(section->name, "MSCompressed") == 0) {
|
|
struct list_head *pos, *q;
|
|
struct chmcResetTableMark *mark;
|
|
|
|
list_for_each_safe(pos, q, §ion->mark_list) {
|
|
mark = list_entry(pos, struct chmcResetTableMark, list);
|
|
list_del(pos);
|
|
free(mark);
|
|
}
|
|
}
|
|
|
|
close(section->fd);
|
|
unlink(section->filename);
|
|
free(section);
|
|
}
|
|
|
|
void chmc_pmgi_free(struct chmcFile *chm)
|
|
{
|
|
struct chmcPmgiChunkNode *node;
|
|
struct list_head *pos, *q;
|
|
|
|
assert(chm);
|
|
|
|
list_for_each_safe(pos, q, &chm->pmgi_list) {
|
|
node = list_entry(pos, struct chmcPmgiChunkNode, list);
|
|
list_del(pos);
|
|
chmc_pmgi_destroy(node);
|
|
}
|
|
}
|
|
|
|
void chmc_pmgl_free(struct chmcFile *chm)
|
|
{
|
|
struct chmcPmglChunkNode *node;
|
|
struct list_head *pos, *q;
|
|
|
|
assert(chm);
|
|
|
|
list_for_each_safe(pos, q, &chm->pmgl_list) {
|
|
node = list_entry(pos, struct chmcPmglChunkNode, list);
|
|
list_del(pos);
|
|
chmc_pmgl_destroy(node);
|
|
}
|
|
}
|
|
|
|
void chmc_entries_free( struct chmcFile *chm )
|
|
{
|
|
struct chmcTreeNode *node;
|
|
struct list_head *pos, *q;
|
|
|
|
assert(chm);
|
|
|
|
list_for_each_safe(pos, q, &chm->entries_list) {
|
|
node = list_entry(pos, struct chmcTreeNode, list);
|
|
list_del(pos);
|
|
chmc_entry_destroy(node);
|
|
}
|
|
|
|
free(chm->sort_entries);
|
|
}
|
|
|
|
UInt32 chmc_strings_add( struct chmcFile *chm, const char *s)
|
|
{
|
|
UInt32 len, off;
|
|
|
|
/* FIXME null are errors */
|
|
|
|
if (!s || *s == '\0')
|
|
return 0;
|
|
|
|
len = strlen(s);
|
|
|
|
off = chm->strings_offset;
|
|
|
|
if (off + len + 1 < chm->strings_len) {
|
|
|
|
memcpy(&chm->strings[off], s, len + 1);
|
|
chm->strings_offset += len + 1;
|
|
|
|
} else {
|
|
/* realloc strings */
|
|
/* if the string truncate copy til end of chunk
|
|
then re-copy from 0 of new */
|
|
BUG_ON("FIXME: %s: %d: handle more chunk for strings\n",
|
|
__FILE__, __LINE__);
|
|
}
|
|
|
|
return off;
|
|
}
|
|
|
|
void chmc_entry_destroy( struct chmcTreeNode *node )
|
|
{
|
|
assert(node);
|
|
assert(node->name);
|
|
|
|
free(node->name);
|
|
if (node->buf && !(node->flags & CHMC_TNFL_STATIC))
|
|
free(node->buf);
|
|
free(node);
|
|
}
|
|
|
|
struct chmcTreeNode *chmc_add_file(struct chmcFile *chm, const char *filename,
|
|
UInt16 prefixlen, int sect_id, UChar *buf,
|
|
UInt64 len)
|
|
{
|
|
struct chmcSection *section;
|
|
struct chmcTreeNode *node;
|
|
|
|
assert(chm);
|
|
|
|
if (sect_id >= chm->sections_num)
|
|
return NULL;
|
|
|
|
section = chm->sections[sect_id];
|
|
|
|
node = chmc_add_entry(chm, filename, prefixlen, sect_id, NULL,
|
|
section->offset, len);
|
|
|
|
if ((node) && (len > 0))
|
|
section->offset += len;
|
|
|
|
return node;
|
|
}
|
|
|
|
struct chmcTreeNode *chmc_add_dir(struct chmcFile *chm, const char *dir)
|
|
{
|
|
assert(chm);
|
|
|
|
return chmc_add_entry(chm, dir, 0, 0, NULL, 0, 0);
|
|
}
|
|
|
|
static inline void *chmc_syscat_mem(void *d, void *s, unsigned long len)
|
|
{
|
|
memcpy(d, s, len);
|
|
|
|
return (char *)d + len;
|
|
}
|
|
|
|
static void *chmc_syscat_entry(Int16 code, void *d, void *s, Int16 len)
|
|
{
|
|
d = chmc_syscat_mem(d, &code, 2);
|
|
d = chmc_syscat_mem(d, &len, 2);
|
|
|
|
return chmc_syscat_mem(d, s, len);
|
|
}
|
|
|
|
/* #define DEFAULT_TOPIC "index.htm" */
|
|
/* #define TITLE "hello world" */
|
|
/* #define LCASEFILE "test" */
|
|
|
|
int chmc_system_done(struct chmcFile *chm)
|
|
{
|
|
struct chmcSystem *system;
|
|
struct chmcSystemInfo *sysinfo;
|
|
struct chmcIndexHeader *idxhdr;
|
|
void *sysp, *p;
|
|
|
|
assert(chm);
|
|
|
|
system = &chm->system;
|
|
sysinfo = &system->info;
|
|
idxhdr = &chm->idxhdr;
|
|
|
|
// TODO should be set from application
|
|
// system->_size += (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(UInt32)) /* timestamp */
|
|
// + (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(PACKAGE_STRING)) /* compiler */
|
|
// + (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(UInt32)) /* eof */
|
|
// + (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(DEFAULT_TOPIC))
|
|
// + (_CHMC_SYS_ENTRY_HDR_LEN + sizeof(TITLE))
|
|
// + 32;
|
|
|
|
sysp = malloc(16384);
|
|
if (sysp) {
|
|
UInt32 val;
|
|
UInt16 code, len;
|
|
const char *entry_val;
|
|
|
|
p = chmc_syscat_mem(sysp, &system->version, sizeof(system->version));
|
|
|
|
val = 0;
|
|
p = chmc_syscat_entry(SIEC_TIMESTAMP, p, &val, sizeof(val));
|
|
p = chmc_syscat_entry(SIEC_COMPVER, p,
|
|
/*"HHA Version 4.74.8702"*/
|
|
PACKAGE_STRING,
|
|
sizeof(PACKAGE_STRING)
|
|
/*strlen("HHA Version 4.74.8702")+1*/);
|
|
p = chmc_syscat_entry(SIEC_SYSINFO, p,
|
|
sysinfo, sizeof(struct chmcSystemInfo));
|
|
|
|
if (chm->config != NULL && chm->config->deftopic != NULL)
|
|
entry_val = chm->config->deftopic;
|
|
else
|
|
entry_val = "index.htm";
|
|
p = chmc_syscat_entry(SIEC_DEFTOPIC, p, (void *)entry_val,
|
|
strlen(entry_val)+1);
|
|
|
|
if (chm->config != NULL && chm->config->title != NULL)
|
|
entry_val = chm->config->title;
|
|
else
|
|
entry_val = "untitled";
|
|
p = chmc_syscat_entry(SIEC_TITLE, p, (void *)entry_val,
|
|
strlen(entry_val)+1);
|
|
// p = chmc_syscat_entry(SIEC_DEFFONT, p, &val, sizeof(val));
|
|
p = chmc_syscat_entry(SIEC_LCASEFILE, p, "siec_lcasefile",
|
|
strlen("siec_lcasefile")+1);
|
|
p = chmc_syscat_entry(SIEC_DEFWINDOW, p,
|
|
"MsdnHelp", strlen("MsdnHelp")+1);
|
|
|
|
val = 0;
|
|
p = chmc_syscat_entry(SIEC_NUMOFINFOT, p, &val, sizeof(val));
|
|
|
|
p = chmc_syscat_entry(SIEC_IDXHDR, p,
|
|
idxhdr, sizeof(struct chmcIndexHeader));
|
|
|
|
|
|
val = 0;
|
|
p = chmc_syscat_entry(SIEC_INFOCHKSUM, p, &val, sizeof(val));
|
|
|
|
system->_size = (char *)p - (char *)sysp;
|
|
chmc_add_meta(chm, "/#SYSTEM", 0, sysp, system->_size);
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
chmcerr_set(CHMC_ENOMEM, "system done: malloc %d bytes",
|
|
system->_size);
|
|
|
|
return CHMC_ENOMEM;
|
|
}
|
|
|
|
int chmc_tree_done( struct chmcFile *chm )
|
|
{
|
|
struct chmcItsfHeader *itsf;
|
|
struct chmcSect0 *sect0;
|
|
struct chmcItspHeader *itsp;
|
|
struct chmcTreeNode *ctrl;
|
|
UInt32 str_index;
|
|
const char *val;
|
|
|
|
assert(chm);
|
|
|
|
itsf = &chm->itsf;
|
|
sect0 = &chm->sect0;
|
|
itsp = &chm->itsp;
|
|
|
|
chmc_add_dir(chm, "/");
|
|
|
|
ctrl = chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Transform/List",
|
|
0, (UChar *)chmc_transform_list,
|
|
sizeof(chmc_transform_list));
|
|
if (ctrl)
|
|
ctrl->flags |= CHMC_TNFL_STATIC;
|
|
|
|
chmc_system_done(chm);
|
|
|
|
if (chm->config != NULL && chm->config->deftopic != NULL)
|
|
val = chm->config->deftopic;
|
|
else
|
|
val = "index.htm";
|
|
|
|
str_index = chmc_strings_add(chm, val);
|
|
|
|
#if 0
|
|
// FIXME just a test
|
|
{
|
|
UChar *p;
|
|
int len;
|
|
struct chmcTopicEntry topicEntry;
|
|
// struct chmcUrlStrEntry urlStrEntry;
|
|
|
|
p = malloc(4096);
|
|
if (p) {
|
|
memset(p, 0, 4096);
|
|
len = 0;
|
|
|
|
topicEntry.tocidx_offset = 4096;
|
|
topicEntry.strings_offset = -1;
|
|
topicEntry.urltbl_offset = 0;
|
|
topicEntry.in_content = 6;
|
|
topicEntry.unknown = 0;
|
|
|
|
memcpy(p, &topicEntry, sizeof(struct chmcTopicEntry));
|
|
len += sizeof(struct chmcTopicEntry);
|
|
|
|
chm->idxhdr.num_of_topic++;
|
|
|
|
chmc_add_meta(chm, "/#TOPICS", 1, (UChar *)p, len);
|
|
} else
|
|
BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__);
|
|
}
|
|
#endif
|
|
|
|
ctrl = chmc_add_meta(chm, "/#IDXHDR", 1, (void *)&chm->idxhdr,
|
|
sizeof(struct chmcIndexHeader));
|
|
if (ctrl)
|
|
ctrl->flags |= CHMC_TNFL_STATIC;
|
|
|
|
{
|
|
UInt32 *p;
|
|
p = malloc(8+196);
|
|
if (p) {
|
|
const char *val;
|
|
memset(p+2, 0, 196);
|
|
|
|
p[0] = 1;
|
|
p[1] = 196;
|
|
|
|
p[2+0] = 196;
|
|
// p[2+2] = 1;
|
|
// p[2+3] = 0x00000532;
|
|
// p[2+4] = 0x00062520;
|
|
|
|
// p[2+8] = 86;
|
|
// p[2+9] = 51;
|
|
// p[2+10] = 872;
|
|
// p[2+11] = 558;
|
|
|
|
// p[2+19] = 220;
|
|
|
|
// p[2+27] = 0x00000041;
|
|
// p[2+28] = 14462;
|
|
|
|
if (chm->config != NULL && chm->config->title != NULL)
|
|
val = chm->config->title;
|
|
else
|
|
val = "untitled";
|
|
p[2+5] = chmc_strings_add(chm, val);
|
|
|
|
if (chm->config != NULL && chm->config->hhc != NULL)
|
|
val = chm->config->hhc;
|
|
else
|
|
val = "toc.hhc";
|
|
p[2+24] = chmc_strings_add(chm, val);
|
|
|
|
if (chm->config != NULL && chm->config->hhk != NULL)
|
|
val = chm->config->hhc;
|
|
else
|
|
val = "toc.hhk";
|
|
p[2+25] = chmc_strings_add(chm, val);
|
|
p[2+26] = str_index;
|
|
|
|
chmc_add_meta(chm, "/#WINDOWS", 1, (UChar *)p, 8+196);
|
|
} else
|
|
BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__);
|
|
}
|
|
|
|
ctrl = chmc_add_meta(chm, "/#STRINGS", 1, (void *)chm->strings,
|
|
chm->strings_len);
|
|
if (ctrl)
|
|
ctrl->flags |= CHMC_TNFL_STATIC;
|
|
|
|
#if 0
|
|
// FIXME just a test
|
|
{
|
|
UChar *p;
|
|
int len;
|
|
struct chmcUrlStrEntry urlStrEntry;
|
|
|
|
urlStrEntry.url_offset = 0;
|
|
urlStrEntry.framename_offset = 0;
|
|
|
|
p = malloc(4096);
|
|
if (p) {
|
|
memset(p, 0, 4096);
|
|
*p = 0x42;
|
|
len = 1;
|
|
|
|
memcpy(p + len, &urlStrEntry, sizeof(struct chmcUrlStrEntry));
|
|
len += sizeof(struct chmcUrlStrEntry);
|
|
len += sprintf(p + len, "index.htm" ) + 1;
|
|
|
|
memcpy(p + len, &urlStrEntry, sizeof(struct chmcUrlStrEntry));
|
|
len += sizeof(struct chmcUrlStrEntry);
|
|
len += sprintf(p + len, "test.htm" ) + 1;
|
|
|
|
chmc_add_meta(chm, "/#URLSTR", 1, (UChar *)p, len);
|
|
} else
|
|
BUG_ON("FIXME: %s: %d\n", __FILE__, __LINE__);
|
|
}
|
|
#endif
|
|
|
|
// chmc_add_entry(chm, "/#URLTBL", 0, 1, NULL, 0, 0);
|
|
// chmc_add_entry(chm, "/#TOPICS", 0, 1, NULL, 0, 0);
|
|
|
|
// NOTE NOTE NOTE add any meta compressed before crunch ;-)
|
|
|
|
chmc_crunch_lzx(chm, 1);
|
|
|
|
chmc_control_data_done(chm);
|
|
chmc_reset_table_done(chm);
|
|
|
|
chmc_add_empty(chm, "/#ITBITS");
|
|
|
|
// NOTE in this implementation compressed Content should be the last file
|
|
// added to section 0
|
|
|
|
chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Content", 0, NULL,
|
|
chm->sections[1]->offset);
|
|
|
|
chmc_entries_qsort(chm);
|
|
chmc_uncompressed_done(chm);
|
|
chmc_pmgl_done(chm);
|
|
|
|
chmc_pmgi_done(chm);
|
|
|
|
itsf->dir_len = _CHMC_ITSP_V1_LEN
|
|
+ (_CHMC_CHUNK_LEN * itsp->num_blocks);
|
|
|
|
itsf->data_offset = _CHMC_ITSF_V3_LEN
|
|
+ _CHMC_SECT0_LEN
|
|
+ _CHMC_ITSP_V1_LEN
|
|
+ (_CHMC_CHUNK_LEN * itsp->num_blocks);
|
|
|
|
sect0->file_len += _CHMC_CHUNK_LEN * itsp->num_blocks;
|
|
|
|
chmc_write(chm);
|
|
|
|
{
|
|
struct chmcSection *section;
|
|
struct list_head *pos;
|
|
UChar buf[4096];
|
|
|
|
list_for_each(pos, &chm->sections_list) {
|
|
section = list_entry(pos, struct chmcSection, list);
|
|
chmc_appendfile(chm, section->filename, buf, 4096);
|
|
}
|
|
}
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
int chmc_crunch_lzx(struct chmcFile *chm, int sect_id)
|
|
{
|
|
struct chmcLzxInfo lzx_info;
|
|
|
|
lzx_data *lzxd;
|
|
int subd_ok = 1;
|
|
int do_reset = 1;
|
|
int block_size;
|
|
lzx_results lzxr;
|
|
int wsize_code = 16;
|
|
|
|
assert(chm);
|
|
|
|
if ((wsize_code < 15) || (wsize_code > 21)) {
|
|
fprintf(stderr, "window size must be between 15 and 21 inclusive\n");
|
|
return CHMC_EINVAL;
|
|
}
|
|
|
|
lzx_info.chm = chm;
|
|
lzx_info.section = chm->sections[sect_id];
|
|
lzx_info.done = 0;
|
|
lzx_info.todo = lzx_info.section->offset;
|
|
lzx_info.pos = chm->entries_list.next;
|
|
lzx_info.error = 0;
|
|
lzx_info.eof = 0;
|
|
|
|
lzx_info.fd = -1;
|
|
lzx_info.fd_offset = 0;
|
|
|
|
chmc_compressed_add_mark(lzx_info.chm, 0);
|
|
lzx_info.section->reset_table_header.block_count++;
|
|
|
|
/* undocumented fact, according to Caie --
|
|
block size cannot exceed window size. (why not?) */
|
|
/* The block size must not be larger than the window size.
|
|
While the compressor will create apparently-valid LZX files
|
|
if this restriction is violated, some decompressors
|
|
will not handle them. */
|
|
|
|
block_size = 1 << wsize_code;
|
|
|
|
// lzx_info.section->control_data.windowSize = wsize_code;
|
|
// lzx_info.section->control_data.windowsPerReset = block_size;
|
|
|
|
lzx_init(&lzxd, wsize_code,
|
|
_lzx_get_bytes, &lzx_info, _lzx_at_eof,
|
|
_lzx_put_bytes, &lzx_info,
|
|
_lzx_mark_frame, &lzx_info);
|
|
|
|
while(! _lzx_at_eof(&lzx_info)) {
|
|
if (do_reset)
|
|
lzx_reset(lzxd);
|
|
lzx_compress_block(lzxd, block_size, subd_ok);
|
|
}
|
|
lzx_finish(lzxd, &lzxr);
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
static int _lzx_at_eof(void *arg)
|
|
{
|
|
struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg;
|
|
|
|
return lzx_info->error || lzx_info->done >= lzx_info->todo || lzx_info->eof;
|
|
}
|
|
|
|
static int _lzx_put_bytes(void *arg, int n, void *buf)
|
|
{
|
|
struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg;
|
|
struct chmcSect0 *sect0 = &lzx_info->chm->sect0;
|
|
int wx;
|
|
static int counter = 0;
|
|
|
|
counter += n;
|
|
wx = write(lzx_info->section->fd, buf, n);
|
|
sect0->file_len += wx;
|
|
lzx_info->section->len += wx;
|
|
|
|
return wx;
|
|
}
|
|
|
|
static void _lzx_mark_frame(void *arg, uint32_t uncomp, uint32_t comp)
|
|
{
|
|
struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg;
|
|
struct chmcSection *section = lzx_info->chm->sections[1];
|
|
|
|
UInt64 compressed;
|
|
|
|
chmc_dump( "Aligned data at %d(in compressed stream, %d) (%lu/%lu)\n",
|
|
uncomp, comp, (unsigned long)lzx_info->done, (unsigned long)lzx_info->todo );
|
|
|
|
compressed = comp;
|
|
|
|
section->reset_table_header.block_count++;
|
|
|
|
chmc_compressed_add_mark( lzx_info->chm, compressed );
|
|
|
|
section->reset_table_header.uncompressed_len = uncomp;
|
|
section->reset_table_header.compressed_len = comp;
|
|
}
|
|
|
|
static int _lzx_get_bytes(void *arg, int n, void *buf)
|
|
{
|
|
struct chmcLzxInfo *lzx_info = (struct chmcLzxInfo *)arg;
|
|
struct chmcFile *chm = lzx_info->chm;
|
|
struct chmcTreeNode *node;
|
|
|
|
int todo;
|
|
int done;
|
|
int toread;
|
|
int rx;
|
|
|
|
todo = n;
|
|
done = 0;
|
|
|
|
// compression state machine
|
|
// lzx compressor ask for block input bytes
|
|
// need to keep current entry file and offset trought blocks
|
|
// until last entry
|
|
while (todo) {
|
|
// end of entries reached?
|
|
if (lzx_info->pos == &chm->entries_list) {
|
|
lzx_info->eof = 1;
|
|
break;
|
|
}
|
|
|
|
node = list_entry( lzx_info->pos, struct chmcTreeNode, list );
|
|
|
|
// skip empty files and directories
|
|
if (node->len == 0
|
|
|| strcmp("MSCompressed", chm->sections[node->sect_id]->name)) {
|
|
lzx_info->pos = lzx_info->pos->next;
|
|
continue;
|
|
}
|
|
else
|
|
if (node->buf) {
|
|
// have len and buffer, it's mallocated not file
|
|
}
|
|
else
|
|
if (lzx_info->fd == -1) {
|
|
// open file if it isn't
|
|
lzx_info->fd = open(node->name, O_RDONLY | O_BINARY);
|
|
if (lzx_info->fd < 0) {
|
|
chmc_error("%s: %d: error %d: '%s' %s\n",
|
|
__FILE__, __LINE__,
|
|
errno, node->name, strerror(errno));
|
|
lzx_info->error = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// read till the end of the file or till the lzx buffer is filled
|
|
toread = node->len - lzx_info->fd_offset;
|
|
if (toread > todo)
|
|
toread = todo;
|
|
|
|
if (toread <= 0)
|
|
continue;
|
|
|
|
// read input
|
|
if (node->buf) {
|
|
memcpy((char *)buf + (n - todo), &node->buf[lzx_info->fd_offset], toread);
|
|
rx = toread;
|
|
}
|
|
else
|
|
{
|
|
rx = read(lzx_info->fd, (char *)buf + (n - todo), toread);
|
|
if (rx <= 0) {
|
|
int temp = errno;
|
|
chmc_error("read error %s \n", strerror(temp));
|
|
lzx_info->error = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
todo -= rx;
|
|
lzx_info->fd_offset += rx;
|
|
done += rx;
|
|
lzx_info->done += rx;
|
|
|
|
// end of current file reached, goto next entry
|
|
if (lzx_info->fd_offset == node->len) {
|
|
if (lzx_info->fd > -1)
|
|
close(lzx_info->fd);
|
|
lzx_info->fd = -1;
|
|
lzx_info->fd_offset = 0;
|
|
lzx_info->pos = lzx_info->pos->next;
|
|
}
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
int chmc_compressed_add_mark(struct chmcFile *chm, UInt64 at)
|
|
{
|
|
struct chmcSection *section;
|
|
struct chmcResetTableMark *mark;
|
|
|
|
assert(chm);
|
|
|
|
section = chm->sections[1];
|
|
|
|
mark = malloc(_CHMC_RSTTBL_MARK);
|
|
if (mark) {
|
|
mark->at = at;
|
|
chmc_dump("[%d] at: %jd\n", section->mark_count, at);
|
|
list_add_tail(&mark->list, §ion->mark_list);
|
|
section->mark_count++;
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
return CHMC_ENOMEM;
|
|
}
|
|
|
|
int chmc_control_data_done(struct chmcFile *chm)
|
|
{
|
|
struct chmcTreeNode *ctrl;
|
|
|
|
ctrl = chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/ControlData",
|
|
0, (UChar *)&chm->sections[1]->control_data,
|
|
_CHMC_LZXC_V2_LEN);
|
|
|
|
if (ctrl) {
|
|
ctrl->flags |= CHMC_TNFL_STATIC;
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
return CHMC_ENOMEM;
|
|
}
|
|
|
|
int chmc_reset_table_done(struct chmcFile *chm)
|
|
{
|
|
struct chmcSection *section;
|
|
struct chmcLzxcResetTable *reset_table;
|
|
struct list_head *pos;
|
|
struct chmcResetTableMark *mark;
|
|
|
|
UInt64 *at;
|
|
int i, len;
|
|
|
|
section = chm->sections[1];
|
|
|
|
len = _CHMC_LZXC_RESETTABLE_V1_LEN + (section->mark_count * sizeof(UInt64));
|
|
|
|
reset_table = malloc(len);
|
|
|
|
if (reset_table) {
|
|
memcpy(reset_table, §ion->reset_table_header,
|
|
_CHMC_LZXC_RESETTABLE_V1_LEN);
|
|
at = (void *)((char *)reset_table + _CHMC_LZXC_RESETTABLE_V1_LEN);
|
|
|
|
i = 0;
|
|
list_for_each(pos, §ion->mark_list) {
|
|
mark = list_entry(pos, struct chmcResetTableMark, list);
|
|
at[i++] = mark->at;
|
|
}
|
|
|
|
chmc_add_dir(chm, "::DataSpace/Storage/MSCompressed/Transform/"
|
|
"{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/");
|
|
chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/Transform/"
|
|
"{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}"
|
|
"/InstanceData/ResetTable",
|
|
0, (UChar *)reset_table, len);
|
|
|
|
{ // TODO FIXME do better
|
|
UInt64 *uncompressed_len = malloc(8);
|
|
if (uncompressed_len) {
|
|
*uncompressed_len = reset_table->uncompressed_len;
|
|
chmc_add_meta(chm, "::DataSpace/Storage/MSCompressed/SpanInfo",
|
|
0, (UChar *)uncompressed_len, 8);
|
|
}
|
|
}
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
return CHMC_ENOMEM;
|
|
}
|
|
|
|
void chmc_entries_qsort(struct chmcFile *chm)
|
|
{
|
|
struct chmcTreeNode *node;
|
|
struct list_head *pos;
|
|
int i;
|
|
|
|
assert(chm);
|
|
|
|
chm->sort_entries = malloc(sizeof(struct chmcTreeNode *)
|
|
* chm->entries_num);
|
|
|
|
i = 0;
|
|
list_for_each(pos, &chm->entries_list) {
|
|
node = list_entry(pos, struct chmcTreeNode, list);
|
|
chm->sort_entries[i++] = node;
|
|
}
|
|
|
|
qsort(chm->sort_entries, chm->entries_num, sizeof(struct chmcTreeNode *),
|
|
_entry_cmp);
|
|
}
|
|
|
|
static int _entry_cmp(const void *pva, const void *pvb)
|
|
{
|
|
const struct chmcTreeNode * const *pa = pva;
|
|
const struct chmcTreeNode * const *pb = pvb;
|
|
const struct chmcTreeNode *a = *pa, *b = *pb;
|
|
|
|
return strcmp( &a->name[a->prefixlen], &b->name[b->prefixlen] );
|
|
}
|
|
|
|
int chmc_uncompressed_done(struct chmcFile *chm)
|
|
{
|
|
struct chmcSect0 *sect0 = &chm->sect0;
|
|
struct chmcTreeNode *node;
|
|
struct list_head *pos;
|
|
int wx;
|
|
|
|
list_for_each(pos, &chm->entries_list) {
|
|
node = list_entry( pos, struct chmcTreeNode, list );
|
|
|
|
if (strcmp( "MSCompressed", chm->sections[node->sect_id]->name ) == 0)
|
|
continue;
|
|
|
|
if ((node->buf) && (node->len > 0)) {
|
|
wx = write(chm->sections[node->sect_id]->fd, node->buf, node->len);
|
|
sect0->file_len += wx;
|
|
}
|
|
}
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
void chmc_pmgl_done(struct chmcFile *chm)
|
|
{
|
|
struct chmcTreeNode *entry;
|
|
int i;
|
|
|
|
assert(chm);
|
|
|
|
for(i=0; i < chm->entries_num; i++) {
|
|
entry = chm->sort_entries[i];
|
|
chmc_pmgl_add_entry(chm, entry);
|
|
}
|
|
}
|
|
|
|
int chmc_pmgl_add_entry(struct chmcFile *chm, struct chmcTreeNode *entry)
|
|
{
|
|
struct chmcPmglChunkNode *pmgl;
|
|
struct chmcPmglChunk *chunk;
|
|
struct chmcSection *section;
|
|
struct chmcItspHeader *itsp = &chm->itsp;
|
|
|
|
UChar *p;
|
|
UInt16 *idx;
|
|
int name_len;
|
|
int outlen;
|
|
int should_idx, idx_intlv;
|
|
int free;
|
|
|
|
assert(chm);
|
|
assert(entry);
|
|
|
|
// check section bound
|
|
section = chmc_section_lookup(chm, entry->sect_id);
|
|
if (!section)
|
|
chmcerr_set_return(CHMC_ENOMEM, "section %d lookup failed: ",
|
|
entry->sect_id);
|
|
|
|
// check chunk space for new entry
|
|
name_len = strlen(&entry->name[entry->prefixlen]);
|
|
|
|
outlen = chmc_encint_len(name_len);
|
|
outlen += name_len;
|
|
outlen += chmc_encint_len(entry->sect_id);
|
|
outlen += chmc_encint_len(entry->offset);
|
|
outlen += chmc_encint_len(entry->len);
|
|
|
|
// look for current pmgl chunk, create if doesn't exist
|
|
if (!chm->pmgl_last) {
|
|
pmgl = chmc_pmgl_create();
|
|
if (pmgl)
|
|
chmc_pmgl_add(chm, pmgl);
|
|
else
|
|
chmcerr_set_return(CHMC_ENOMEM, "pmgl chunk: ");
|
|
}
|
|
else
|
|
pmgl = chm->pmgl_last;
|
|
|
|
do {
|
|
|
|
chunk = &chm->pmgl_last->chunk;
|
|
|
|
idx_intlv = 1 + ( 1 << itsp->blockidx_intvl );
|
|
should_idx = ( ( chunk->entries_count > 0 )
|
|
&& ! ( ( chunk->entries_count + 1 ) % idx_intlv )
|
|
? 2 : 0 );
|
|
|
|
free = sizeof(chunk->data) - pmgl->data_len - pmgl->index_len
|
|
- should_idx;
|
|
|
|
// current(last) chunk doesn't have enough room? force new one
|
|
if (outlen + should_idx > free) {
|
|
//chm->pmgl_last = NULL;
|
|
pmgl = chmc_pmgl_create();
|
|
if ( pmgl )
|
|
chmc_pmgl_add(chm, pmgl);
|
|
else
|
|
chmcerr_set_return(CHMC_ENOMEM, "pmgl chunk: ");
|
|
|
|
continue;
|
|
}
|
|
|
|
p = (void *)&chunk->data[pmgl->data_len];
|
|
|
|
if (should_idx) {
|
|
idx = (void *)((char *)&chunk->data[CHMC_PMGL_DATA_LEN] - pmgl->index_len);
|
|
*idx = (char *)p - (char *)&chunk->data;
|
|
}
|
|
|
|
p += chmc_encint(name_len, p);
|
|
memcpy(p, &entry->name[entry->prefixlen], name_len);
|
|
p += name_len;
|
|
p += chmc_encint(entry->sect_id, p);
|
|
p += chmc_encint(entry->offset, p);
|
|
p += chmc_encint(entry->len, p);
|
|
|
|
pmgl->data_len += outlen;
|
|
pmgl->index_len += should_idx;
|
|
|
|
chunk->entries_count++;
|
|
chunk->header.free_space -= outlen;
|
|
break;
|
|
|
|
} while (1);
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
struct chmcSection *chmc_section_lookup(struct chmcFile *chm, int id)
|
|
{
|
|
struct chmcSection *current;
|
|
struct list_head *pos;
|
|
int i;
|
|
|
|
assert(chm);
|
|
|
|
i = 0;
|
|
list_for_each(pos, &chm->sections_list) {
|
|
current = list_entry(pos, struct chmcSection, list);
|
|
if (i == id)
|
|
return current;
|
|
i++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct chmcPmglChunkNode *chmc_pmgl_create(void)
|
|
{
|
|
struct chmcPmglChunkNode *node;
|
|
|
|
node = malloc(sizeof(struct chmcPmglChunkNode));
|
|
if (node)
|
|
chmc_pmgl_init(node);
|
|
|
|
return node;
|
|
}
|
|
|
|
void chmc_pmgl_init(struct chmcPmglChunkNode *node)
|
|
{
|
|
struct chmcPmglChunk *chunk;
|
|
|
|
assert(node);
|
|
|
|
node->data_len = 0;
|
|
node->index_len = 0;
|
|
|
|
chunk = &node->chunk;
|
|
|
|
memcpy(chunk->header.signature, "PMGL", 4);
|
|
|
|
// FIXME check it is the right len
|
|
chunk->header.free_space = CHMC_PMGL_DATA_LEN + 2;
|
|
chunk->header.unknown_0008 = 0;
|
|
chunk->header.block_prev = -1;
|
|
chunk->header.block_next = -1;
|
|
|
|
memset(chunk->data, 0, CHMC_PMGL_DATA_LEN);
|
|
}
|
|
|
|
void chmc_pmgi_init(struct chmcPmgiChunkNode *node)
|
|
{
|
|
struct chmcPmgiChunk *chunk;
|
|
|
|
assert(node);
|
|
|
|
node->data_len = 0;
|
|
node->index_len = 0;
|
|
|
|
chunk = &node->chunk;
|
|
|
|
memcpy(chunk->header.signature, "PMGI", 4);
|
|
|
|
// FIXME check it is the right len
|
|
chunk->header.free_space = CHMC_PMGI_DATA_LEN + 2;
|
|
// chunk->header.unknown_0008 = 0;
|
|
// chunk->header.block_prev = -1;
|
|
// chunk->header.block_next = -1;
|
|
|
|
memset(chunk->data, 0, CHMC_PMGI_DATA_LEN);
|
|
}
|
|
|
|
|
|
|
|
struct chmcPmgiChunkNode *chmc_pmgi_create(void)
|
|
{
|
|
struct chmcPmgiChunkNode *node;
|
|
|
|
node = malloc(sizeof(struct chmcPmgiChunkNode));
|
|
if (node)
|
|
chmc_pmgi_init(node);
|
|
|
|
return node;
|
|
}
|
|
|
|
void chmc_pmgl_destroy(struct chmcPmglChunkNode *node)
|
|
{
|
|
assert(node);
|
|
free(node);
|
|
}
|
|
|
|
void chmc_pmgi_destroy(struct chmcPmgiChunkNode *node)
|
|
{
|
|
assert(node);
|
|
free(node);
|
|
}
|
|
|
|
void chmc_pmgl_add(struct chmcFile *chm, struct chmcPmglChunkNode *pmgl)
|
|
{
|
|
struct chmcItspHeader *itsp = &chm->itsp;
|
|
struct chmcPmglHeader *hdr;
|
|
|
|
assert(chm);
|
|
assert(pmgl);
|
|
|
|
list_add_tail(&pmgl->list, &chm->pmgl_list);
|
|
|
|
itsp->index_last = itsp->num_blocks;
|
|
|
|
hdr = &pmgl->chunk.header;
|
|
hdr->block_prev = itsp->num_blocks - 1;
|
|
|
|
if (chm->pmgl_last) {
|
|
hdr = &chm->pmgl_last->chunk.header;
|
|
hdr->block_next = itsp->num_blocks;
|
|
}
|
|
|
|
itsp->num_blocks++;
|
|
|
|
chm->pmgl_last = pmgl;
|
|
}
|
|
|
|
int chmc_pmgi_done(struct chmcFile *chm)
|
|
{
|
|
struct chmcItspHeader *itsp = &chm->itsp;
|
|
struct chmcPmglChunkNode *pmgl;
|
|
struct list_head *pos;
|
|
|
|
int i, j;
|
|
char name[256]; //FIXME use malloc
|
|
UInt32 name_len;
|
|
|
|
assert(chm);
|
|
|
|
// only one pml, omitted pmgi
|
|
if (itsp->num_blocks == 1) {
|
|
itsp->index_depth = 1;
|
|
itsp->index_root = -1;
|
|
itsp->index_last = 0;
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
itsp->index_root = itsp->num_blocks;
|
|
|
|
i = 0;
|
|
list_for_each(pos, &chm->pmgl_list) {
|
|
pmgl = list_entry(pos, struct chmcPmglChunkNode, list);
|
|
j = chmc_decint(&pmgl->chunk.data[0], &name_len);
|
|
if (name_len <= 255) {
|
|
memcpy(name, &pmgl->chunk.data[j], name_len);
|
|
name[name_len] = '\0';
|
|
chmc_pmgi_add_entry(chm, name, i);
|
|
}
|
|
else
|
|
BUG_ON("name_len >= 255(%lu) %.*s\n", (unsigned long)name_len, 255,
|
|
&pmgl->chunk.data[j]);
|
|
i++;
|
|
}
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
int chmc_pmgi_add_entry(struct chmcFile *chm, const char *name, int pmgl_id)
|
|
{
|
|
struct chmcPmgiChunkNode *pmgi;
|
|
struct chmcPmgiChunk *chunk;
|
|
struct chmcItspHeader *itsp = &chm->itsp;
|
|
|
|
UChar *p;
|
|
UInt16 *idx;
|
|
int name_len;
|
|
int outlen;
|
|
int should_idx, idx_intlv;
|
|
int free;
|
|
|
|
assert(chm);
|
|
|
|
// check chunk space for new entry
|
|
name_len = strlen(name);
|
|
|
|
outlen = chmc_encint_len(name_len);
|
|
outlen += name_len;
|
|
outlen += chmc_encint_len(pmgl_id);
|
|
|
|
// look for current pmgi chunk, create if doesn't exist
|
|
if (!chm->pmgi_last) {
|
|
pmgi = chmc_pmgi_create();
|
|
if (pmgi)
|
|
chmc_pmgi_add(chm, pmgi);
|
|
else
|
|
chmcerr_set_return(CHMC_ENOMEM, "pmgi chunk: ");
|
|
}
|
|
else
|
|
pmgi = chm->pmgi_last;
|
|
|
|
do {
|
|
|
|
chunk = &chm->pmgi_last->chunk;
|
|
|
|
idx_intlv = 1 + ( 1 << itsp->blockidx_intvl );
|
|
should_idx = ( ( chunk->entries_count > 0 )
|
|
&& ! ( ( chunk->entries_count + 1 ) % idx_intlv )
|
|
? 2 : 0 );
|
|
|
|
free = sizeof(chunk->data) - pmgi->data_len -
|
|
pmgi->index_len - should_idx;
|
|
|
|
// current(last) chunk doesn't have enough room? force new one
|
|
if (outlen + should_idx > free) {
|
|
pmgi = chmc_pmgi_create();
|
|
if (pmgi)
|
|
chmc_pmgi_add(chm, pmgi);
|
|
else
|
|
chmcerr_set_return(CHMC_ENOMEM, "pmgi chunk: ");
|
|
|
|
continue;
|
|
}
|
|
|
|
p = (void *)&chunk->data[pmgi->data_len];
|
|
|
|
if (should_idx) {
|
|
idx = (void *)((char *)&chunk->data[CHMC_PMGI_DATA_LEN] - pmgi->index_len);
|
|
*idx = (char *)p - (char *)&chunk->data;
|
|
}
|
|
|
|
p += chmc_encint(name_len, p);
|
|
memcpy(p, name, name_len);
|
|
p += name_len;
|
|
p += chmc_encint(pmgl_id, p);
|
|
|
|
pmgi->data_len += outlen;
|
|
pmgi->index_len += should_idx;
|
|
|
|
chunk->entries_count++;
|
|
chunk->header.free_space -= outlen;
|
|
break;
|
|
|
|
} while (1);
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
void chmc_pmgi_add(struct chmcFile *chm, struct chmcPmgiChunkNode *pmgi)
|
|
{
|
|
struct chmcItspHeader *itsp = &chm->itsp;
|
|
|
|
assert(chm);
|
|
assert(pmgi);
|
|
|
|
list_add_tail(&pmgi->list, &chm->pmgi_list);
|
|
itsp->num_blocks++;
|
|
|
|
chm->pmgi_last = pmgi;
|
|
}
|
|
|
|
int chmc_write(struct chmcFile *chm)
|
|
{
|
|
struct chmcItsfHeader *itsf = &chm->itsf;
|
|
struct chmcSect0 *sect0 = &chm->sect0;
|
|
struct chmcItspHeader *itsp = &chm->itsp;
|
|
|
|
struct chmcPmglChunkNode *pmgl;
|
|
struct chmcPmgiChunkNode *pmgi;
|
|
struct list_head *pos;
|
|
|
|
assert(chm);
|
|
|
|
chmc_dump("write itsf %d\n", _CHMC_ITSF_V3_LEN);
|
|
write(chm->fd, itsf, _CHMC_ITSF_V3_LEN);
|
|
chmc_dump("write sect0 %d\n", _CHMC_SECT0_LEN);
|
|
write(chm->fd, sect0, _CHMC_SECT0_LEN);
|
|
chmc_dump("write itsp %d\n", _CHMC_ITSP_V1_LEN);
|
|
write(chm->fd, itsp, _CHMC_ITSP_V1_LEN);
|
|
|
|
list_for_each(pos, &chm->pmgl_list) {
|
|
pmgl = list_entry(pos, struct chmcPmglChunkNode, list);
|
|
chmc_dump("write pmgl %d\n", _CHMC_CHUNK_LEN);
|
|
write(chm->fd, &pmgl->chunk, _CHMC_CHUNK_LEN);
|
|
}
|
|
|
|
chmc_dump("itsp->num_blocks %d", itsp->num_blocks);
|
|
if (itsp->num_blocks > 1) {
|
|
list_for_each( pos, &chm->pmgi_list ) {
|
|
pmgi = list_entry(pos, struct chmcPmgiChunkNode, list);
|
|
chmc_dump("write pmgi %d\n", _CHMC_CHUNK_LEN);
|
|
write(chm->fd, &pmgi->chunk, _CHMC_CHUNK_LEN);
|
|
}
|
|
}
|
|
|
|
return CHMC_NOERR;
|
|
}
|
|
|
|
int chmc_appendfile(struct chmcFile *chm, const char *filename, void *buf,
|
|
size_t size )
|
|
{
|
|
struct stat statbuf;
|
|
int in;
|
|
off_t todo, toread;
|
|
int rx;
|
|
|
|
if (stat(filename, &statbuf) < 0)
|
|
return errno;
|
|
|
|
in = open(filename, O_RDONLY | O_BINARY);
|
|
if (in >= 0) {
|
|
todo = statbuf.st_size;
|
|
|
|
while (todo) {
|
|
toread = size;
|
|
if (toread > todo)
|
|
toread = todo;
|
|
|
|
rx = read(in, buf, toread);
|
|
if (rx > 0) {
|
|
write(chm->fd, buf, rx);
|
|
todo -= rx;
|
|
}
|
|
}
|
|
|
|
close(in);
|
|
}
|
|
else
|
|
BUG_ON("open %s\n", filename);
|
|
|
|
return CHMC_NOERR;
|
|
}
|