⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chmlib.c

📁 CHMTools
💻 C
字号:
/* CHMtools v0.1 *//* Copyright 2001 Matthew T. Russotto *//*      This file is part of CHMtools    CHMtools 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 2 of the License, or    (at your option) any later version.        CHMtools 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 this program; if not, write to the Free Software    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA*/#include <stdlib.h>#include "chmlib.h"#include "fixendian.h"#define FILELEN_HSECT 0#define DIR_HSECT 1#define CONTENT_FORMAT "::DataSpace/Storage/%s/Content"#define CONTROLDATA_FORMAT "::DataSpace/Storage/%s/ControlData"#define SPANINFO_FORMAT "::DataSpace/Storage/%s/SpanInfo"#define LIST_FORMAT "::DataSpace/Storage/%s/Transform/List"#define INSTANCEDATA_FORMAT "::DataSpace/Storage/%s/Transform/%s/InstanceData/"#define RT_FORMAT "::DataSpace/Storage/%s/Transform/%s/InstanceData/ResetTable"#define TRANSFORM_FORMAT "::Transform/%s/"#define NAMELIST "::DataSpace/NameList"#ifdef DEBUG#define DPRINTF fprintf#else#define DPRINTF while (0) fprintf#endifstatic void get_guid(ubyte *buf, guid_t *guid){    memcpy(guid, buf, sizeof(guid_t));    FIXENDIAN32(guid->guid1);    FIXENDIAN16(guid->guid2[0]);    FIXENDIAN16(guid->guid2[1]);}static voidmake_guid_string(guid_t *guid, char *s){    sprintf(s, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",	    guid->guid1, guid->guid2[0], guid->guid2[1],	    guid->guid3[0], guid->guid3[1], guid->guid3[2], guid->guid3[3],	    guid->guid3[4], guid->guid3[5], guid->guid3[6], guid->guid3[7]);}static void guid_fix_endian(guid_t *guid) {  FIXENDIAN32(guid->guid1);  FIXENDIAN16(guid->guid2[0]);  FIXENDIAN16(guid->guid2[1]);}static int readheader(chmfile *c){  int ret;  fseek(c->cf, 0, SEEK_SET);  ret = fread(&c->ch, 1, sizeof(chmheader), c->cf);  guid_fix_endian(&c->ch.unk_guid1);  guid_fix_endian(&c->ch.unk_guid2);  FIXENDIAN32(c->ch.tot_hdrlen);  fseek(c->cf, 0x58, SEEK_SET);  fread(&c->content_offset, 1, sizeof(c->content_offset), c->cf);  FIXENDIAN32(c->content_offset);#ifdef DEBUG  {    char str[40];    fprintf(stderr, "itsf: %-4.4s\n", c->ch.itsf);    fprintf(stderr, "tot_hdrlen: %08x\n", c->ch.tot_hdrlen);    fprintf(stderr, "content_offset: %08x\n", c->content_offset);    make_guid_string(&c->ch.unk_guid1, str);    fprintf(stderr, "guid1: %s\n", str);    make_guid_string(&c->ch.unk_guid2, str);    fprintf(stderr, "guid2: %s\n", str);  }#endif  fseek(c->cf, sizeof(chmheader), SEEK_SET);  return ret;}static int readhsectable(chmfile *c){  int i;  int result;  int nhsecs = 2;  result = fread(c->hs, nhsecs, sizeof(hsecentry), c->cf);  for (i = 0; i < nhsecs; i++) {    FIXENDIAN32(c->hs[i].offset);    FIXENDIAN32(c->hs[i].length);  }  return result;}static int readdirheader(chmfile *c){  int ret;  int result;  fseek(c->cf, c->hs[DIR_HSECT].offset, SEEK_SET);  result = fread(&c->dh, 1, sizeof(dirheader), c->cf);  FIXENDIAN32(c->dh.chunksize);  FIXENDIAN32(c->dh.indexchunk);  FIXENDIAN32(c->dh.ndirchunks);  FIXENDIAN32(c->dh.firstpmglchunk);  FIXENDIAN32(c->dh.lastpmglchunk);  FIXENDIAN32(c->dh.length);  guid_fix_endian(&c->dh.unk_guid1);#ifdef DEBUG  {    char str[40];    fprintf(stderr, "directory header length: %x\n", c->dh.length);    fprintf(stderr, "chunksize: %x\n", c->dh.chunksize);    fprintf(stderr, "ndirchunks: %x\n", c->dh.ndirchunks);    fprintf(stderr, "indexchunk: %x\n", c->dh.indexchunk);    fprintf(stderr, "firstpmglchunk: %x\n", c->dh.firstpmglchunk);    fprintf(stderr, "lastpmglchunk: %x\n", c->dh.lastpmglchunk);    make_guid_string(&c->dh.unk_guid1, str);    fprintf(stderr, "guid1: %s\n", str);  }#endif  return result;}static ulong getencint(ubyte **p){  ulong accum = 0;  while ((**p) & 0x80) {    accum = (accum << 7) | ((*(*p)++)&0x7F);  }  accum = (accum << 7) | (*(*p)++);  return accum;}static int read_chm_dir(chmfile *c){  int length;  dirheader dh;  ubyte *buf;  ubyte *bufend;  ubyte *p, *oldp;  int namelen;  int i;  unsigned long section, offset, dlength;  int nentries;  int nchunks;  pmglchunkheader pmglch;  fpos_t chunkstart;    readdirheader(c);  chunkstart = ftell(c->cf);  length = c->dh.chunksize * c->dh.ndirchunks;  p = buf = malloc(length);  bufend = buf;  nchunks = 0;  pmglch.next_chunk = 0;  do {    fseek(c->cf, chunkstart + pmglch.next_chunk * c->dh.chunksize, SEEK_SET);    fread(&pmglch, 1, sizeof(pmglch), c->cf);    FIXENDIAN32(pmglch.next_chunk);    FIXENDIAN32(pmglch.prev_chunk);    FIXENDIAN32(pmglch.quickreflen);#ifdef DEBUG    fprintf(stderr, "prev_chunk: %d\n", pmglch.prev_chunk);    fprintf(stderr, "next_chunk: %d\n", pmglch.next_chunk);    fprintf(stderr, "quickreflen: %x\n", pmglch.quickreflen);#endif    length = fread(bufend, 1, c->dh.chunksize - sizeof(pmglch) - pmglch.quickreflen, c->cf);    bufend += length;    nchunks++;  }  while ((pmglch.next_chunk != -1) && (nchunks < c->dh.ndirchunks));  nentries = 0;    while (p < bufend) {    nentries++;    namelen = *p++;    p += namelen;    section = getencint(&p);    offset = getencint(&p);    dlength = getencint(&p);  }#ifdef DEBUG  fprintf(stderr, "nentries (calculated): %x\n", nentries);#endif    c->dir = (chm_dir *)malloc(sizeof(chm_dir) + (nentries-1) * sizeof(direntry));  p = buf;  for (i = 0; i < nentries; i++) {    oldp = p;    memset(&c->dir->entry[i], 0, sizeof(direntry));    namelen = *p++;    memcpy(c->dir->entry[i].name, p, namelen);    c->dir->entry[i].name[namelen] = 0;    p+=namelen;    c->dir->entry[i].section = getencint(&p);    offset = getencint(&p);    dlength = getencint(&p);    c->dir->entry[i].offset = offset;    c->dir->entry[i].length = dlength;  }#ifdef DEBUG  for (i = 0; i < nentries; i++)    fprintf(stderr, "%08lx %08lx %08lx %s\n", c->dir->entry[i].section,	   c->dir->entry[i].offset, c->dir->entry[i].length,	   c->dir->entry[i].name);#endif  c->dir->nentries = nentries;  free(buf);  return 0;}static direntry *getdirentry(char *name, chm_dir *dir){  int i;  for (i = 0; i < dir->nentries; i++)	if (!strcmp(name, dir->entry[i].name))	  return &dir->entry[i];  return NULL;}int chm_getfile(chmfile *c, char *name, ulong *length,		ubyte **outbuf){  int i, j;  int section;  int offset = c->content_offset;  direntry *de;  FILE *addf;  *length = 0;  *outbuf = NULL;  de = getdirentry(name, c->dir);  DPRINTF(stderr, "Getting %s, de = %08x %s\n", name, de, de?de->name:"");  if (!de)      return -1;  section = de->section;  if (!c->cs || !c->cs->entry[section].iscompressed) {      if (c->cs)	  offset += c->cs->entry[section].offset;            fseek(c->cf, de->offset + offset, SEEK_SET);      *length = de->length;      *outbuf = malloc(*length);      fread(*outbuf, 1, *length, c->cf);  }  else if (c->cs->entry[section].cache) {      *length = de->length;      *outbuf = malloc(*length);      memcpy(*outbuf,	     c->cs->entry[section].cache + de->offset, *length);  }  else {      char fname[4096];      char guid_str[80];      ubyte *lbp;      ulong flength;      ubyte *rtfile;      ubyte *cdfile;      ubyte *cbp;      ubyte *contbuf;      ubyte *secbuf;      ulong rtindex;      ulong uclength, clength;      ulong contlength;      ulong rtlength;      ulong window_size;      guid_t guid;      int result;            sprintf(fname, CONTROLDATA_FORMAT, c->cs->entry[section].name);      /* get controldata */      chm_getfile(c, fname, &flength, &cdfile);      if (!cdfile)	return -1;      cbp = cdfile;      if (memcmp(cbp+4, "LZXC", 4)) {	fprintf(stderr, "Compression method not LZXC: %-4.4s\n", cbp+4);	free(cdfile);	return -1;      }      window_size = *(ulong *)(cbp+0x10);      FIXENDIAN32(window_size);      free(cdfile);      switch(window_size) {      case 1: window_size = 15; break;      case 2: window_size = 16; break;      case 4: window_size = 17; break;      case 8: window_size = 18; break;      case 0x10: window_size = 19; break;      case 0x20: window_size = 20; break;      case 0x40: window_size = 21; break;      default:	fprintf(stderr, "Window size invalid: %x\n", window_size);	return -1;      }      strcpy(guid_str, "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}");      /* hardcoded string because transform list is broken */      sprintf(fname, RT_FORMAT, c->cs->entry[section].name, guid_str);      DPRINTF(stderr, "%s\n", fname);      chm_getfile(c, fname, &rtlength, &rtfile);      if (rtfile) {	uclength = (rtfile[0x10]) | (rtfile[0x11]<<8) |	  (rtfile[0x12]<<16) | (rtfile[0x13] << 24);		clength = (rtfile[0x18]) | (rtfile[0x19]<<8) |	  (rtfile[0x1a]<<16) | (rtfile[0x1b] << 24);	DPRINTF(stderr, "uclength = %x, clength = %x\n", uclength, clength);	sprintf(fname, CONTENT_FORMAT, c->cs->entry[section].name);	chm_getfile(c,fname, &contlength, &contbuf);	if (clength != contlength)	  fprintf(stderr, "Warning: Content Length not same as Compressed Length (without padding) %d %d \n", contlength, clength);		secbuf = malloc(uclength);	/* uncompress it */	LZXinit(window_size);	result = LZXdecompress(contbuf, secbuf, clength, uclength);	DPRINTF(stderr, "LZXResult: %d\n", result);	free (contbuf);	free(rtfile);	if (result != 0) {	  free(secbuf);	  return -1;	}	/* and get the file we want out of it */	DPRINTF(stderr, "offset = %x\n", de->offset);	DPRINTF(stderr, "length = %x\n", de->length);	*length = de->length;	*outbuf = malloc(*length);	memcpy(*outbuf, secbuf + de->offset, *length);	//			free(secbuf);	c->cs->entry[section].cache = secbuf;	c->cs->entry[section].cachesize = uclength;      }  }  return 0;}static int readcontsecs(chmfile *c){  ulong length;  ubyte *buf;  ubyte *bufptr;  int nentries;  int nmlen;  int i,j;  char secname[4096];    chm_getfile(c, NAMELIST, &length, &buf);  nentries = buf[2] | (buf[3]<<8);  c->cs = (contsecs *)malloc(sizeof (contsecs) + 			     (nentries-1)*sizeof(contsecentry));  c->cs->nentries = nentries;  bufptr = buf + 4;  for (i = 0; i < nentries; i++) {	nmlen = bufptr[0] | (bufptr[1]<<8);	bufptr += 2;	for (j = 0; j <= nmlen; j++) {  /* this is the lazy way to do wide characters.  Since the string is pretty much always "MSCompressed" or "Uncompressed", it'll do */	  c->cs->entry[i].name[j] = *bufptr; 	  bufptr += 2; 	}	DPRINTF(stderr, "name = %s\n", c->cs->entry[i].name);	if (!strcmp(c->cs->entry[i].name, "MSCompressed")) {	  c->cs->entry[i].iscompressed = 1;	  /* this is the wrong way to figure out if a file is compressed.	     The real way is to examine the transform.  But the transform	     list is corrupt in most (all?) CHM files, apparently because	     the length was calculated in characters whereas the guid	     is recorded as wide characters	  */	}	else {	  c->cs->entry[i].iscompressed = 0;	}	c->cs->entry[i].cache = NULL;	sprintf(secname, CONTENT_FORMAT, c->cs->entry[i].name);	c->cs->entry[i].offset = 0;	if (i == 0) continue;	for (j = 0; j < c->dir->nentries; j++) {	  if (!strcmp(secname, c->dir->entry[j].name)) {		c->cs->entry[i].offset = c->dir->entry[j].offset;	  }	}  }  free(buf);  return 0;}chmfile *chm_openfile(char *fname){    chmfile *result;    FILE *f;        f = fopen(fname, "rb");    if (!f)	return NULL;    result = calloc(1, sizeof(chmfile));    result->cf = f;    readheader(result);    readhsectable(result);    read_chm_dir(result);    readcontsecs(result);    return result;}voidchm_close(chmfile *c){  int i;  fclose(c->cf);  for (i = 0; i < c->cs->nentries; i++) {    if (c->cs->entry[i].cache)      free(c->cs->entry[i].cache);  }  free(c->cs);  free(c->dir);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -