clit18/0000755000175000017500000000000010072023716010374 5ustar kyzkyzclit18/clit.c0000644000175000017500000003004510072020054011465 0ustar kyzkyz/*--[clit18.c]---------------------------------------------------------------- | Copyright (C) 2002, 2003 Dan A. Jackson | | This file is part of the "clit" (Convert LIT) program. | | Convert LIT 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #ifdef _MSC_VER #include #include #endif #include #include #include #include #include #include #include #ifndef _MSC_VER #include #endif #include "litlib.h" extern int drm5_callback(void *, U8 *, int size); #ifdef _MSC_VER #include #define mkdir(x,y) _mkdir(x) #define stat _stat #endif #ifndef MAX_PATH #include #define MAX_PATH PATH_MAX #endif extern int display_lit(lit_file * litfile, int public_only); extern int explode_lit(lit_file * litfile, char * litName, char * pathOutput); extern int transmute_lit(lit_file * litfile, char * newlitfile, char * ); static U8 * readat(void * v, U32 offset, int size); /* Must not be static, linked into litlib */ void lit_error(int what, char * fmt, ...); static int windowed_mode; static int printed_error = 0; char * readingFilename = NULL; char * writingFilename = NULL; int disable_directory_support = 0; char dir_program[MAX_PATH]; char dir_lit_file[MAX_PATH]; char * key_file = NULL; const char * sTitle = "+---[ ConvertLIT (Version 1.8) ]---------------"\ "[ Copyright (c) 2002,2003 ]---\n"\ "ConvertLIT comes with ABSOLUTELY NO WARRANTY; for details\n"\ "see the COPYING file or visit \"http://www.gnu.org/license/gpl.html\". \n"\ "This is free software, and you are welcome to redistribute it under \n"\ "certain conditions. See the GPL license for details.\n"; const char * sUsage = \ "\nThis program has three modes of operation: \n"\ "First, is ** EXPLOSION **, or the expanding of a .LIT file into an \n"\ "OEBPS compliant package. \n"\ "To explode, you type: clit \\\n"\ "For Example:\n"\ "\tclit ebook-propietary.lit ebook-oebps\\ \n"\ "If the directory does not exist, you MUST put a trailing \\ or / after it!\n"\ "\nTo disable creating multiple subdirectories, use the \"-d\" flag.\n"\ "\nSecond, is the DOWNCONVERTING of a .LIT file down to \"Sealed\", \n"\ "or DRM1 format for reading on handheld devices. \n"\ "To downconvert, you type: clit \n"\ "For Example:\n"\ "\tclit \"drm5 ebook.lit\" ebook-open.lit\n"\ "\nThird, is the INSCRIBING of a .LIT file which allows you to label \n"\ "your ebooks. This is very similar to downconverting, you just add \n"\ "a third argument: clit \n"\ "For example: \n"\ "\tclit ebook.lit inscribed.lit \"the Library of Basil "\ "Frankweiler\"\n"\ "\nDRM5 is supported if you have a \"keys.txt\" file that contains\n"\ "the private key(s) for your passport(s) in either the CLIT program\n"\ "directory or the current directory. \n"\ "Use the -k flag to force the location of your keys.txt\n\n"\ "This is a tool for **YOUR OWN FAIR USE** and not for stealing \n"\ "other people's ebooks.\n"\ "\nPlease do not use this program to distrbute illegal copies of ebooks.\n"\ "\t... that would make Baby Jesus sad.\n"; int main(int argc, char ** argv) { int status; char * path = NULL; lit_file lit; char * filename, * output, * inscription, c; FILE * fh; struct stat statbuf; int task = 0; int base, i; int done_args = 0; /* Save the current directory for later */ strncpy(dir_program, argv[0], MAX_PATH-1); dir_program[MAX_PATH] = '\0'; for (i = strlen(dir_program); i >= 0; i--) { if ((dir_program[i] == '/') || (dir_program[i] == '\\')) { dir_program[i+1] = '\0'; break; } } printf(sTitle); if (argc < 3) { printf(sUsage); return -1; } base = 1; while ((argv[base][0] == '-') && (!done_args)) { int i; for (i = 1; i < (int)strlen(argv[base]); i++) { switch (argv[base][i]) { case 'd': case 'D': disable_directory_support = 1; break; case 'k': case 'K': key_file = &argv[base][i+1]; i = 0; break; case '-': done_args = 1; } if (!i) break; } base++; } base--; inscription = NULL; if (argc == (4+base)) { inscription = argv[base+3]; } filename = argv[base+1]; strncpy(dir_lit_file, filename, MAX_PATH-1); dir_lit_file[MAX_PATH-1] = '\0'; for (i = strlen(dir_lit_file); i >= 0; i--) { if ((dir_lit_file[i] == '/') || (dir_lit_file[i] == '\\')) { dir_lit_file[i+1] = '\0'; break; } } output = argv[base+2]; readingFilename = filename; fh = fopen(filename, "rb"); if (!fh) { lit_error(ERR_LIBC|ERR_R,"Unable to open file."); return -1; } memset(&lit, 0, sizeof(lit)); lit.file_pointer = (void *)fh; lit.readat = readat; lit.drm5_callback= drm5_callback; lit.drm5_data = (void *)&lit; (void)fseek(fh, 0, SEEK_END); lit.filesize = ftell(fh); status = lit_read_from_file(&lit); if ((status) && (status != E_LIT_DRM_ERROR)) { lit_close(&lit); exit(-1); } printf("LIT INFORMATION.........\n"); if (lit.drmlevel >= 0) printf("DRM = %d \n", lit.drmlevel); printf("Timestamp = %08lx \n", lit.timestamp); printf("Creator = %08lx \n", lit.creator_id); printf("Language = %08lx \n", lit.language_id); if (strlen(output) == 0) { if (status) display_lit(&lit, 1); else display_lit(&lit, 0); lit_close(&lit); exit(-1); } else if (status) { printf("\nDECRYPTION FAILED - No keys available for this title.\n"); lit_close(&lit); exit(-1); } status = stat(output, &statbuf); if (status < 0) { if (errno == ENOENT) { c = output[strlen(output) - 1]; if ((c == '/') || (c == '\\')) { status = mkdir(output,0755); if (!status) { task = 1; } else { lit_error(ERR_LIBC, "Unable to create directory \"%s\"!", output); } } else { task = 2; } } else if (errno == ENOTDIR) { fprintf(stderr,"Cannot make directory \"%s\" -- " " a file already exists!\n", output); exit(-1); }else { lit_error(ERR_LIBC,"stat() failed unexpectedly"); exit(-1); } } else { if (statbuf.st_mode & S_IFDIR) { c = output[strlen(output) - 1]; if ( (c != '/') && (c != '\\')) { path = malloc(strlen(output) + 1); if (!path) { fprintf(stderr,"Malloc(%d) failed!\n", strlen(output) + 1); exit(-1); } strcpy(path, output); strcat(path, "/"); output = path; } task = 1; } else { status = -1; fprintf(stderr,"Output file \"%s\" already exists. " "Delete it first!\n", output); task = 0; } } if (task == 1) { status = explode_lit(&lit, filename, output); if (status != 0) { if (status > 0) { printf("Finished exploding \"%s\", but with errors.", filename); } } else { printf("Exploded \"%s\" into \"%s\".\n", filename, output); } } else if (task == 2) { status = transmute_lit(&lit, output, inscription); if (status != 0) { printf("<><><><><> FAILED TO %s \"%s\"!", (inscription)?"INSCRIBE":"CONVERT",filename); } else { printf("Converted \"%s\" --> \"%s\".\n", filename, output); } } if (task && status) { if (!printed_error) { lit_error(ERR_R, "Encountered an error, but didn't print a message! Bad programmer!"); } } lit_close(&lit); if (path) free(path); return status; } U8 * readat(void * v, U32 offset, int size) { U8 *mem; int read_size; FILE * f; f = (FILE *)v; if (!f) { lit_error(0,"No filehandle passed in!"); return NULL; } mem = malloc(size); if (!mem) { lit_error(ERR_LIBC,"malloc(%d) failed.", size); return NULL; } memset(mem, 0, size); if (fseek(f, offset, SEEK_SET) != 0) { free(mem); lit_error(ERR_LIBC|ERR_R,"fseek() failed."); return NULL; } read_size = fread(mem, 1, size, f); if (read_size != size) { if (feof(f)) { fprintf(stderr, "\nWARNING: Read went past the end of file by %d bytes. \n"\ "If the conversion process doesn't work, try to redownload the file.\n", size - read_size); return mem; } free(mem); lit_error(ERR_LIBC|ERR_R,"Unable to read %d chars at position %ld.", size, offset); return NULL; } return mem; } void show_information(char * fmt, ...) { va_list ap; if (!windowed_mode) { va_start(ap, fmt); vprintf(fmt, ap); printf("\n"); } else { #ifdef _MSC_VER #endif } } #ifdef _MSC_VER void DisplayErrorText( DWORD dwLastError ) { LPSTR MessageBuffer; DWORD dwBufferLength; DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM; if(dwBufferLength = FormatMessageA(dwFormatFlags, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &MessageBuffer, 0, NULL)) { DWORD dwBytesWritten; WriteFile( GetStdHandle(STD_ERROR_HANDLE), MessageBuffer, dwBufferLength, &dwBytesWritten, NULL); LocalFree(MessageBuffer); } } #endif void lit_error(int what, char * fmt, ...) { va_list ap; unsigned long int w32err; va_start(ap, fmt); printed_error++; w32err = 0; #ifdef _MSC_VER w32err = GetLastError(); #endif if (!windowed_mode) { if (((what & ERR_RW) == ERR_R) && readingFilename) fprintf(stderr,"Error reading file \"%s\" -- \n\t", readingFilename); if (((what & ERR_RW) == ERR_W) && writingFilename) fprintf(stderr,"Error writing file \"%s\" -- \n\t", writingFilename); vfprintf(stderr,fmt, ap); printf("\n"); if (what & ERR_LIBC) perror("\tLIBC reports"); #ifdef _MSC_VER if (what & ERR_WIN32) DisplayErrorText(w32err); #endif } else { #ifdef _MSC_VER #endif } } clit18/Makefile0000644000175000017500000000042710072023656012042 0ustar kyzkyzall: clit CFLAGS=-funsigned-char -Wall -O2 -I ../libtommath-0.30/ -I ../lib -I ../lib/des -I . clean: rm -f *.o clit clit: clit.o hexdump.o drm5.o explode.o transmute.o display.o utils.o manifest.o ../lib/openclit.a gcc -o clit $^ ../libtommath-0.30/libtommath.a clit18/clit.h0000644000175000017500000000226407701553512011513 0ustar kyzkyz /*--[clit.h]------------------------------------------------------------------- | Copyright (C) 2002, 2003 Dan A. Jackson | | This file is part of "clit" (Convert LIT). | | "clit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #ifndef CLIT_H #define CLIT_H extern int printed_error; extern char * readingFilename; extern char * writingFilename; void show_error(int what, char * fmt, ...); void show_information(char * fmt, ...); #endif clit18/drm5.c0000644000175000017500000003320310072020004011373 0ustar kyzkyz/*--[drm5.c]------------------------------------------------------------------- | Copyright (C) 2002, 2003 Dan A. Jackson | | This file is part of the "clit" (Convert LIT) program. | | Convert LIT 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ /* This file decrypts a DRM5 encrypted title, assuming the private key * is provided. This allows Convert Lit to execute much faster. * * Note that the July 4th update added only a trivial amount of complexity * to this process. */ #include #include #include #include #include #include "litlib.h" #include "utils.h" #include "d3des.h" #include static const char * license_string = "/DRMStorage/Licenses/EUL"; const unsigned long int obfuscation_keys[] = {0,0x6E12CF4A,0x36A2732C}; U8 * read_whole_file(char * filename, int * size_ptr) { U8 *mem = NULL; size_t size; FILE *f; f = fopen(filename, "rb"); if (!f) { lit_error(ERR_LIBC,"WARNING: Unable to open file \"%s\".", filename); return NULL; } if (fseek(f, 0, SEEK_END) != 0) { free(mem); fclose(f); lit_error(ERR_LIBC,"fseek() failed."); return NULL; } size = ftell(f); fseek(f, 0, SEEK_SET); mem = malloc(size+1); if (!mem) { fclose(f); lit_error(ERR_LIBC,"malloc(%d) failed.", size); return NULL; } if (fread(mem, size, 1, f) != 1) { fclose(f); free(mem); lit_error(ERR_LIBC,"Unable to read %d chars.", size); return NULL; } fclose(f); mem[size] = '\0'; if (size_ptr) *size_ptr = size; return mem; } extern char * key_file; extern char dir_lit_file[]; extern char dir_program[]; /* | get_next_key | returns 1 if there's a private_key/modulus pair available | (and, by implication, there is more) | returns 0 if there isn't. */ int get_next_key(char * private_key_ptr, char * modulus_ptr) { static int which_file = 0,got_one_file; static U8 * pFile = NULL; static int remaining; static U8 * pKey, * pModulus, * p; static char * sKeysFile = "keys.txt"; U8 * s; do { while (!pFile) { switch (which_file) { case 0: got_one_file = 0; if (key_file) pFile = read_whole_file(key_file, &remaining); break; case 1: pFile = read_whole_file(sKeysFile, &remaining); break; case 2: pFile = NULL; s = strmerge(dir_program, sKeysFile, NULL); if (s) pFile = read_whole_file(s, &remaining); if (s) free(s); break; case 3: pFile = NULL; s = strmerge(dir_lit_file, sKeysFile, NULL); if (s) pFile = read_whole_file(s, &remaining); if (s) free(s); break; default: if (!got_one_file) { lit_error(0,"No \"keys.txt\" files found. Can't decrypt."); } return 0; /* No more files */ } which_file++; if (pFile) { got_one_file++; } pModulus = NULL; pKey = p = pFile; } if (!pFile) return 0; while (remaining) { if (*p == '\n') { *p = '\0'; if ((!pKey) || (pModulus)) { if (pModulus && (p - pModulus) > 1) { strcpy(private_key_ptr, pKey); strcpy(modulus_ptr, pModulus); pKey = p+1; pModulus = NULL; return 1; } pKey = p+1; pModulus = NULL; } else if (!pModulus) { if ((p - pKey) > 1) pModulus = p+1; else pKey = p+1; } } if ((*p == '\r') || (*p == '\n') || (*p == ' ') || (*p == '\t') || (!*p)) { *p = 0; } else if ( (*p < '0') || (*p > '9')) { pKey = pModulus = NULL; } p++; remaining--; } if (pKey && pModulus) { strcpy(modulus_ptr, pModulus); strcpy(private_key_ptr, pKey); pModulus = pKey = NULL; /* Next time, we come through, and skip this fork */ return 1; } free(pFile); pFile = p = NULL; } while (1); return 0; } int ms_base64_decode(char * str, unsigned char *buff, int bufflen) { char *cp; char *ocp; int val, count, block, ocount; ocp = buff; count = 0; block = 0; ocount = 0; for (cp = str; *cp != '\0'; cp++) { if (ocount >= bufflen) { return -1; } if ((*cp >= 'A') && (*cp <= 'Z')) val = *cp - 'A'; else if ((*cp >= 'a') && (*cp <= 'z')) val = *cp - 'a' + 26; else if ((*cp >= '0') && (*cp <= '9')) val = *cp - '0' + 52; else if ((*cp == '+') || (*cp == '!')) val = 62; else if ((*cp == '/') || (*cp == '*')) val = 63; else if (*cp == '=') { if (count == 2) { *ocp++ = block >> 4; ocount++; } else { *ocp++ = (block >> 10); *ocp++ = (block >> 2) & 0xff; ocount += 2; } break; } else val = 1; if (val >= 0) { block = (block << 6) | val; if (++count == 4) { *ocp++ = block >> 16; *ocp++ = (block >> 8) & 0xff; *ocp++ = block & 0xff; ocount += 3; count = 0; } } } return ocount; } /* Stupid little fake XML parser. */ static char *find_close(char * str) { while ((*str != '\0') && (*str != '>')) { if (*str == '"') { if ((str = strchr(str + 1, '"')) == NULL) return NULL; } str++; } if (*str == '\0') return NULL; else return str + 1; } char *get_element(char * tag, char * str) { int len = strlen(tag); char *tmptag; char *start, *end; char *rval = NULL; char *tmp; if ((tmptag = malloc((len + 4) * sizeof(char))) == NULL) { lit_error(ERR_R, "Memory allocation failed in get_element()"); return NULL; } strcpy(tmptag, "<"); strcat(tmptag, tag); while (1) { if ((start = strstr(str, tmptag)) == NULL) goto exit; if (!isalnum(start[len + 1])) break; str = start + len + 1; } strcpy(tmptag, ""); end = strstr(str, tmptag); if (end == NULL) { goto exit; } else { char *realstart = find_close(start); if ((realstart == NULL) || (realstart > end)) goto exit; tmp = malloc((end - realstart + 1) * sizeof(char)); if (tmp == NULL) { lit_error(ERR_R, "memory allocation #2 failed in get_element()"); return NULL; } memcpy(tmp, realstart, (end - realstart) * sizeof(char)); tmp[end - realstart] = '\0'; rval = tmp; } exit: free(tmptag); return rval; } int my_mp_to_unsigned_bin(mp_int * a, unsigned char * b, int len) { int x, res; mp_int t; memset(b, 0, len); if ((res = mp_init_copy(&t, a)) != MP_OKAY) { return res; } x = 0; while ((mp_iszero(&t) == 0) && (x < len)) { b[x++] = (unsigned char)(t.dp[0] & 255); if ((res = mp_div_2d(&t, 8, &t, NULL)) != MP_OKAY) { lit_error(0,"MP_DIV_2D FAILED! %d\n", res); mp_clear(&t); return res; } } mp_clear(&t); return 0; } void little2big(unsigned char * p, int len) { int i; len--; for (i = 0; i < (len/2)+1; i++) { if (i != (len -i)) { p[i] ^= p[len - i]; p[len - i] ^= p[i]; p[i] ^= p[len - i]; } } } unsigned char drm5_des_key[16]; char sModulus[512/3]; char sPrivateKey[512/3]; unsigned char keyData[256]; int drm5_handle_key(lit_file * plf) { U8 * pData; int size, r, i, j; char *ebits = NULL, * value = NULL; int keySize, status; U8 * pValidation; mp_int c, d, p, mod; char des_key[64]; status = lit_get_file(plf,license_string,&pData,&size); if (status) { lit_error(ERR_R, "Was unable to find an End-User-License (\"%s\") in the LIT file.", license_string); return status; } { for (i = 0; i < (size/2); i++) pData[i] = pData[i*2]; pData[i] = '\0'; size /= 2; } pValidation = NULL; ebits = get_element("ENABLINGBITS", (char *)pData); if (ebits) value = get_element("VALUE", ebits); if (!value) { lit_error(ERR_R, "I couldn't find an encrypted key in the End User License file."); free(pData); return -1; } keySize = ms_base64_decode(value, keyData, sizeof(keyData)); little2big(keyData, keySize); r = E_LIT_DRM_ERROR; while (get_next_key(sPrivateKey, sModulus)) { int res; res = mp_init(&c); if (!res) res = mp_init(&p); if (!res) res = mp_init(&mod); if (!res) res = mp_init(&d); if (!res) res = mp_read_radix(&p, sPrivateKey, 10); if (!res) res = mp_read_radix(&mod, sModulus, 10); if (!res) res = mp_read_unsigned_bin(&c, keyData, keySize); if (!res) res = mp_exptmod(&c,&p,&mod, &d); if (!res) res = my_mp_to_unsigned_bin(&d, des_key, sizeof(des_key)); if (res) { lit_error(0,"MP Library error doing RSA decryption = %d.\n",res); return -1; } for (j = 8; j < 62; j++) if (des_key[j]) { break; } if (j != 62) { continue; } r = -1; for (j = 0; j < sizeof(obfuscation_keys)/sizeof(obfuscation_keys[0]); j++) { U32 x; memcpy(drm5_des_key, des_key, 8); x = obfuscation_keys[j]*2; for (i = 2; i < 6; i++) { drm5_des_key[i] ^= (x&0xff); x >>= 8; } x = obfuscation_keys[j]; for (i = 4; i < 8; i++) { drm5_des_key[i] ^= (x&0xff); x >>= 8; } x = obfuscation_keys[j]; if (x) { x = (~x) >> 1; for (i = 0; i < 4; i++) { drm5_des_key[i] ^= (x&0xff); x >>= 8; } } status = lit_get_file(plf,"/DRMStorage/ValidationStream", &pValidation, &size); if ((status) || (!pValidation) || (!size)) { lit_error(ERR_R,"Unable to read validation stream."); free(pData); return -1; } if (strncmp(pValidation,"MSReader",8) != 0) { /* Cleanse the bad sections */ for (i = 0; i < plf->num_sections; i++) { if ((plf->sections[i].data_pointer) && (plf->sections[i].data_pointer != (U8 *)-1)) { free(plf->sections[i].data_pointer); plf->sections[i].data_pointer = NULL; } } free(pValidation); pValidation = NULL; } else { r = 0; break; } } if (!r) break; } #if 0 if (r != 0) { lit_error(0,"Failed to decrypt this title!"); } #endif free(pData); if (pValidation) free(pValidation); return r; } int drm5_decrypt(U8 *ptr, int size) { int ofs; deskey(drm5_des_key, DE1); for (ofs = 0; ofs < size; ofs += 8) { des( (ptr+ofs), (ptr+ofs)); } return 0; } /*--[drm5_callback]------------------------------------------------------------ | | This routine will handle interfacing to the main lit library. | | If p is NULL, then initialize.. | Otherwise, decrypt and hand back the data. | */ int drm5_callback(void * pv, U8 * p, int size) { int status = 0; if (!p) { if (!status) status = drm5_handle_key((lit_file *)pv); } else { status = drm5_decrypt(p, size); }; return status; } clit18/COPYING0000644000175000017500000004365507701616654011462 0ustar kyzkyz GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program 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. This program 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. clit18/display.c0000644000175000017500000000561510071177216012220 0ustar kyzkyz/*--[display.c]-------------------------------------------------------------- | Copyright (C) 2004, Digital Rights Software | | This file is part of the "clit" (Convert LIT) program. | | Convert LIT 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #include #include #include "litlib.h" extern void hexdump( unsigned char * ptr, int size ); /* | This file contains code to display information about a LIT file to | faciliate debugging. */ int display_lit(lit_file * lit, int public_only) { entry_type * entry; int size; unsigned char * p; int real_size; int err; entry = lit->entry; while (entry) { printf("\"%s\" S: %llx Offset: %llx Size: %llx\n", &entry->name, entry->section, entry->offset, entry->size); /* Must skip the "/" entry, it has an absurd size */ if ( ((public_only == 0) || (entry->section == 0)) && ((entry->size) && (strcmp(&entry->name,"/") != 0)) ) { err = lit_get_file(lit,&entry->name,&p,&real_size); if (err) { fprintf(stderr, "Error displaying %s: %d\n", &entry->name, err); } else { size = (unsigned int)(entry->size); /* Very simplistic way to go UTF16->UTF8*/ if ( (real_size > 4) && (p[0] == 0xFF) && (p[1] == 0xFE) && (p[3] == 0) ) { int i, j; j = 0; for (i = 2; i < real_size; i+=2 ) { p[j++] = p[i]; } size = j; } if (size > 8192) size = 8192; if (p) { hexdump(p, size); free(p); } } } entry = entry->next; } return 0; } clit18/explode.c0000644000175000017500000004326210071713300012201 0ustar kyzkyz/*--[explode.c]---------------------------------------------------------------- | Copyright (C) 2002, 2003 Dan A. Jackson | | This file is part of "clit" (Convert LIT). | | "clit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #include #include #include #include #include "clit.h" #include "litlib.h" #include "manifest.h" #include "utils.h" #ifdef _MSC_VER #include #define mkdir(x,y) _mkdir(x) #define strncasecmp _strnicmp #endif /* | Herein is the routines to handle "exploding" of .LIT files into their | component pieces. | Some features, most noteably parsing the "/manifest" and expanding the | tag-compressed HTML are included in the library. */ extern int disable_directory_support; manifest_type manifest; extern char * writingFilename; /* Leave off the newline intentionally */ const char * meta_string = "\n\ "; const char * xhtml_string = "\n\ "; int directory_flag = 0; int create_placeholders(char * pathOutput,manifest_type * pManifest); char * get_opf_name(char * pathOutput, char * litname); int write_htmlish_file(lit_file * litfile, char *, char *, char *); int write_raw_file(lit_file * litfile,char *pathInternal, char *pathExternal); int create_full_path(char * pathStarting, char * pathFile); int explode_lit(lit_file * litfile, char * litName, char * pathOutput) { char * pathOPF = NULL; char * pathInternal = NULL; char * path = NULL; int status; int stage, i; status = lit_read_manifest(litfile, &manifest); if (status) goto bad; status = create_placeholders(pathOutput,&manifest); if (status) goto bad; pathOPF = get_opf_name(pathOutput,litName); writingFilename = pathOPF; status = write_htmlish_file(litfile, "/meta", pathOPF, NULL); free(pathOPF); pathOPF = NULL; writingFilename = NULL; if (status) goto bad; for (stage = 0; stage < NUM_MANIFEST_MAPS; stage++) { name_mapping * map; if (!manifest.mappings[stage]) continue; map = manifest.mappings[stage]; for (i = 0; i < manifest.num_mappings[stage]; i++) { path = strmerge(pathOutput,(char *)map->sOriginal,NULL); if (!path) { status = E_LIT_OUT_OF_MEMORY; goto bad; } writingFilename = path; show_information("Writing out \"%s\" as \"%s\" ...", map->sInternal, map->sOriginal); status = E_LIT_INTERNAL_ERROR; switch (stage) { case 0: case 1: pathInternal = NULL; /* Generated inside htmlish code */ status = write_htmlish_file(litfile,(char *)map->sInternal, path,(char *)map->sOriginal); break; case 2: case 3: pathInternal = strmerge("/data/",(char *)map->sInternal,0); if (!pathInternal) { status = E_LIT_OUT_OF_MEMORY; goto bad; } status = write_raw_file(litfile,pathInternal,path); break; } if (status) { if (status > 0) { show_information("Wrote out \"%s\", but with errors.\n", path); } if (status < 0) return -1; } else { show_information("Successfully written to \"%s\".\n", path); } if (path) { free(path); path = NULL;} if (pathInternal) { free(pathInternal); pathInternal = NULL; } writingFilename = NULL; map++; } } return 0; bad: if (path) free(path); if (pathOPF) free(pathOPF); if (pathInternal) free(pathInternal); return status; } /* | Return the length (in "points" not characters!) that is common between all | entries. */ int find_common_length(manifest_type * pManifest) { mapping_state state; name_mapping * map; U8 * sFirst; int common_length; mapping_init(&state, pManifest); /** * First, find the maximum number of characters that is common between * _all_ original filenames. This will be 0 except for some rare cases. */ map = mapping_next(&state); sFirst = NULL; common_length = 0; /* Common length is in UTF8 units */ if (map) { sFirst = map->sOriginal; common_length = utf8_strlen(sFirst); } while ((map = mapping_next(&state)) != NULL) { int new_common; new_common = utf8_strmatch(sFirst, map->sOriginal); if (new_common < common_length) common_length = new_common; } return common_length; } /* | Create a single file from a name mapping, | Creates directories as needed. */ U8 * create_file_path(U8 * pathOutput, U8 * pathFile) { U32 c; int error, len, status; utf8_iter iter; U8 * sFiltered, * sDirname, * sTemp, * s, * p; FILE * f; sFiltered = sTemp = sDirname = NULL; do { sFiltered = strmerge(pathFile, NULL,NULL); if (!sFiltered) return NULL; sDirname = strmerge(pathFile, NULL,NULL); if (!sDirname) return NULL; error = 0; /* Loop one.. * Filter */ utf8_start(&iter, pathFile); s = sFiltered; do { c = utf8_next(&iter); if (!c) { *s = '\0'; } /* Filter out things which wouldn't be in DOS filenames */ else if ((c < ' ')|| (c == '?')|| (c == '<')|| (c == '>')|| (c == '\"')|| (c == '*')|| (c == '|')|| (c == ';')|| (c ==0x7f) || (c == ':')) { /* By definition, this can't be UTF8! */ *(s++) = '_'; } else if ((c == '/') || (c == '\\')) { /* Use common slash, This is differentated later */ *(s++) = '/'; } else { len = utf8_store(s, &iter); s += len; } } while (c); s = sDirname; utf8_start(&iter, sFiltered); do { p = utf8_next_token(&iter, '/', &len); if (!len) break; /* Skip dots. */ if ((utf8_strmatch(p,"..") == 2) || (utf8_strmatch(p,".") == 1)) { continue; } memcpy(s, p, len); s += len; *s = '\0'; /* This isn't a directory, continue on to the file check */ if (utf8_peek(&iter) == 0) break; sTemp = strmerge(pathOutput,sDirname, NULL); if (!sTemp) { error = 9; break; } directory_flag++; if (disable_directory_support) { /* Shouldn't be here, because the other check takes * care of it. Just in case.... */ error = 11; break; } status = mkdir(sTemp,0755); if (status && (errno != EEXIST)) { lit_error(ERR_LIBC,"WARNING: mkdir() failed for \"%s\".\n", sTemp); free(sTemp); sTemp = NULL; error = 10; break; } free(sTemp); sTemp = NULL; #ifdef _MSC_VER *(s++) = '\\'; #else *(s++) = '/'; #endif } while (1); free(sFiltered); sFiltered = NULL; /* If an error occurred, go on to the next (pathless) potential name */ if (error) break; /* Str should now contain a full qualified path, try to make the * file. */ f = NULL; sTemp = strmerge(pathOutput, sDirname, NULL); if (sTemp) { f = fopen(sTemp, "r"); if (f) { error = 1; } else { f = fopen(sTemp,"w"); if (f) { fclose(f); error = 0; } else { error = 2; } } } else { error = -1; break; } } while (0); if (sTemp) free(sTemp); if (sFiltered) free(sFiltered); if (error) { if (sDirname) free(sDirname); sDirname = NULL; } return sDirname; } char * int2string(int num) { static char sNum[10]; char *sNumPtr; int temp = abs(num); sNumPtr = &sNum[sizeof(sNum)/sizeof(sNum[0]) - 1]; *(sNumPtr--) = '\0'; do { *sNumPtr = '0' + (temp%10); sNumPtr--; temp /= 10; } while (temp && (sNumPtr > sNum)); if (num < 0) *sNumPtr = '-'; else sNumPtr++; return sNumPtr; } /* | This routine is responsible for converting from the "original" names | stored in the .LIT file into names on the local file system. | | To fix duplicates, this CREATES all the files as it goes along. | It also permits recreating a directory tree. | | Steps: | 1. Filter out the common portions. | (Workaround for a buggy generator which stores the originals as: | C:\Something\Else\Books\ISBN\index.html). | | 2. Starting at the unique portion, create the subdirectories | Subdirectories aren't handled at the moment, due to relative pathing. | Filter out backwards references. | | 3. Finally, create the file. if it exists, try some numbers. | | 4. If all the numbers are taken, start over with the Internal filename | */ int create_placeholders(char * pathOutput,manifest_type * pManifest) { int units2skip; mapping_state state; name_mapping * map; units2skip = find_common_length(pManifest); /* For each entry, find the unique portion and start the games */ mapping_init(&state, pManifest); while ((map = mapping_next(&state)) != NULL) { int bytes, units, len, bytes_after_dot; U8 *p, * slash, * dot; U32 c; /* Skip to the last slash */ if (disable_directory_support) units2skip = 0x7FFFF; slash = p = map->sOriginal; bytes = strlen(p)+1; units = 0; while (units < units2skip) { len = read_utf8_char(p, bytes, &c); if ((!c) || (len <= 0)) break; if ((c == '/') || (c == '\\')) slash = (p+len); units++; p += len; } p = dot = slash; while (1) { len = read_utf8_char(p, bytes, &c); if ((!c) || (len <= 0)) break; if (c == '.') dot = p; units++; p += len; } bytes_after_dot = p - dot; if (!bytes_after_dot) { dot = NULL; } /* Start at the previous slash or the beginning */ p = create_file_path(pathOutput, slash); if (!p) { U8 * s; int count; s = strmerge(map->sInternal, dot, NULL); if (s) p = create_file_path(pathOutput, s); if (!p) { if (s) free(s); for (count = 1; count < 1000; count++) { s = strmerge(map->sInternal, int2string(-count), dot); if (s) { p = create_file_path(pathOutput,s); } if (p) break; } if (count == 1000) { lit_error(0, "Unable to create a DOS file for \"%s\". \n", map->sInternal); return -1; } } } if (p) { free(map->sOriginal); map->sOriginal = p; } } return 0; } char * get_opf_name(char * pathOutput, char * litname) { int idx, len; char * str; idx = len = strlen(litname); /* assume that litname is a valid LIT file */ while (idx) { if ((litname[idx] == '/') || (litname[idx] == '\\') || (litname[idx] == ':')) { idx++; break; } idx--; } if (strncasecmp(&litname[len-4],".lit",4) == 0) { str = strmerge(pathOutput,&litname[idx],NULL); if (!str) return str; strcpy(&str[strlen(str)-3], "opf"); return str; } return strmerge(pathOutput,&litname[idx],".opf"); } int write_callback(void * v, U8 * pData, int nBytes) { FILE * f; int r; if (!v) { f = stdout; } else { f = (FILE *)v; } r = fwrite(pData,1, nBytes,f); if (r < 0) { lit_error(ERR_W|ERR_LIBC,"fwrite of %d bytes failed!",nBytes); } return r; } int write_htmlish_file(lit_file * litfile, char * source_name, char * pathExternal, char * pathOriginal) { U8 * p; lit_atom_list * listAtoms; int nbytes; int status; FILE * fOut; char * pathInternal; manifest_type * pmanifest, relative_manifest; pmanifest = &manifest; p = NULL; fOut = NULL; listAtoms = NULL; pathInternal = NULL; status = -1; do { if (pathOriginal != NULL) { pathInternal = strmerge("/data/",source_name, "/content"); } else { /* the meta files, don't have the 3-piece string */ /* So, copy anyway - it only happens once */ pathInternal = strmerge(source_name, 0, 0 ); } if (!pathInternal) { status = E_LIT_OUT_OF_MEMORY; break; } if (pathOriginal) { listAtoms = lit_read_atoms(litfile, source_name); } status = lit_get_file(litfile,pathInternal,&p, &nbytes); if (status) { break; } if ((directory_flag) && pathOriginal && !disable_directory_support) { status = make_relative_manifest(pathOriginal,&relative_manifest, pmanifest); if (status != 0) { status = -20; break;} pmanifest = &relative_manifest; } fOut = fopen(pathExternal, "w"); if (!fOut) { lit_error(ERR_LIBC|ERR_W,"fopen(%s) failed!", pathExternal); status = -1; break; } if (pathOriginal == NULL) { status = write_callback((void *)fOut,(U8 *)meta_string, strlen(meta_string)); if ((size_t)status != strlen(meta_string)) { status = -1; break; } status = lit_reconstitute_html(p, 0, nbytes, 1, pmanifest, listAtoms, write_callback, (void *)fOut); } else { status = write_callback((void *)fOut,(U8 *)xhtml_string, strlen(xhtml_string)); if ((size_t)status != strlen(xhtml_string)) { status = -1; break;} status = lit_reconstitute_html(p, 0, nbytes, 0, pmanifest, listAtoms, write_callback, (void *)fOut); } if ((status > 0) && (status != nbytes)) { lit_error(ERR_R, "Warning - Couldn't fully expand \"%s\", decoded %d out of %d.\n", pathInternal, status, (int)nbytes); status = 1; } else if (status < 0) { lit_error(ERR_R, "ERROR - Failure during tag expansion of \"%s\" Code: %d!\n", pathInternal, status); } else status = 0; } while (0); if (fOut != NULL) fclose(fOut); if (p) free(p); if (pathInternal) free(pathInternal); if (listAtoms) { lit_free_atoms(listAtoms); } return status; } int write_raw_file(lit_file * litfile,char * pathInternal, char * pathExternal) { int status; int nbytes; U8 * p; FILE * fOut; status = lit_get_file(litfile,pathInternal,&p,&nbytes); if (status) return status; fOut = fopen(pathExternal, "wb"); if (!fOut) { free(p); lit_error(ERR_LIBC|ERR_W,"fopen(%s) failed!", pathExternal); return -1; } status = fwrite(p,1, nbytes,fOut); free(p); p = NULL; if (status < 0) { lit_error(ERR_W|ERR_LIBC,"Failed to write %d bytes.",nbytes); return status; } if (status != nbytes) { lit_error(ERR_W,"Partial write, wrote %d out of %d.",status,nbytes); return -1; } fclose(fOut); return 0; } clit18/hexdump.c0000644000175000017500000000256410051241612012213 0ustar kyzkyz /* * This code is released into the public domain. */ /*------------------------------------------------------------------------------ | | Hexdump | In: ptr -> pointing to region to dump | size -> number of bytes to dump | | Result goes to stdout | */ #include #include #include typedef unsigned char byte; #define numBytesPerLine 16 #define numSpaces 5 #define numBytesForHex numBytesPerLine*3 #define numBytesInString numBytesForHex+numSpaces+numBytesPerLine void hexdump( byte * ptr, int size ) { char strbuffer[ numBytesInString+1 ]; char * curStr; int numBytes; int idx; strbuffer[ numBytesInString ] = '\0'; while ( size ) { memset( strbuffer, ' ', numBytesInString ); numBytes = (size>numBytesPerLine) ? numBytesPerLine : size; curStr = strbuffer; for ( idx = 0; idx < numBytes ; idx++ ) { char c1, c2; c2 = ( *(ptr + idx) & 0xF ) + '0'; c1 = (( *(ptr + idx) & 0xF0) >> 4) + '0'; if ( c1 > '9' ) c1 += ('A'-'9'-1); if ( c2 > '9' ) c2 += ('A'-'9'-1); *(curStr++) = c1; *(curStr++) = c2; curStr++; } curStr = strbuffer + numBytesForHex + numSpaces; for ( idx = 0; idx < numBytes ; idx++ ) { if ( isprint( *(ptr+idx) ) ) { *(curStr++) = *(ptr+idx ); } else { *(curStr++) = '.'; } } puts( strbuffer ); size -= numBytes; ptr += numBytes; } } clit18/manifest.c0000644000175000017500000001474510071707600012361 0ustar kyzkyz/*--[manifest.c]-------------------------------------------------------------- | Copyright (C) 2004 Digital Rights Software | | This file is part of "clit" (Convert LIT). | | "clit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #include #include #include "litlib.h" #include "manifest.h" #include "utils.h" /*-- Initialize the walk ------------*/ void mapping_init(mapping_state * m, manifest_type * pManifest) { memset(m, 0, sizeof(mapping_state)); m->p = pManifest; m->magic = 1984; m->group = -1; } /*-- Get each mapping, until NULL --*/ name_mapping * mapping_next(mapping_state * m) { if ((!m) || (m->magic != 1984)) return NULL; if (m->index < m->max_index) { m->map++; m->index++; return m->map; } else { m->group++; m->index = 0; m->map = NULL; } do { if (m->group >= NUM_MANIFEST_MAPS) { m->magic = 0; return NULL; } m->map = m->p->mappings[m->group]; if (!m->map) m->group++; } while (!m->map); m->max_index = m->p->num_mappings[m->group]; m->index = 1; return m->map; } int duplicate_manifest(manifest_type * pDest, manifest_type * pSource) { int stage; int n; U8 * s; name_mapping * mapSrc, * mapDst; memset(pDest, 0, sizeof(manifest_type)); for (stage = 0; stage < NUM_MANIFEST_MAPS; stage++) { if (!pSource->mappings[stage]) { pDest->mappings[stage] = NULL; continue; } mapSrc = pSource->mappings[stage]; n = pSource->num_mappings[stage] * sizeof(name_mapping); mapDst = malloc(n); if (!mapDst) { lit_error(0,"Not enough memory for relative manifest.\n"); lit_free_manifest(pDest); return -1; } memset(mapDst, 0, n); pDest->mappings[stage] = mapDst; for (n = 0; n < pSource->num_mappings[stage]; n++) { s = strmerge((char *)mapSrc->sOriginal, NULL, NULL); mapDst->sOriginal = s; if (!s) { lit_free_manifest(pDest); return -1;} s = strmerge((char *)mapSrc->sInternal, NULL, NULL); mapDst->sInternal = s; if (!s) { lit_free_manifest(pDest); return -1;} s = strmerge((char *)mapSrc->sType, NULL, NULL); mapDst->sType = s; if (!s) { lit_free_manifest(pDest); return -1;} mapDst->offset = mapSrc->offset; mapSrc++; mapDst++; /* Allow freeing as we go */ pDest->num_mappings[stage] = (n + 1); } } return 0; } /* | Here I want to make a path from "me" to "you", for each file of all types | in the manifest. | "me" - the current HTML file | "you" - _EVERY_ other mapping. | | So, first I have to go back to a common directory. | Then, I'll be able to add the rest of "you" on. And that's my link | */ int make_relative_manifest(U8 * file, manifest_type * pRelative, manifest_type * pSource) { int err; mapping_state state; name_mapping * map; utf8_iter iterMe, iterYou; U8 *slashMe, *slashYou, *sRelative, *s; U32 c, d; err = duplicate_manifest(pRelative, pSource); if (err) { return err; } mapping_init(&state, pRelative); err = -300; while ((map = mapping_next(&state)) != NULL) { s = NULL; utf8_start(&iterMe,file); utf8_start(&iterYou, map->sOriginal); slashMe = file; slashYou = map->sOriginal; /* while the two paths are matched, skip up to the last slash */ do { c = utf8_next(&iterMe); d = utf8_next(&iterYou); if (c != d) break; if ((c == '/') || (c =='\\')) { slashMe = utf8_ptr(&iterMe); slashYou = utf8_ptr(&iterYou); } } while (c); utf8_start(&iterYou, slashYou); /* Create a new string, replacing each directory with "../" */ s = NULL; sRelative = NULL; do { c = utf8_next(&iterMe); if ( (c == '/') || (c == '\\')) { if (c == '/') { s = strmerge("../",sRelative, NULL); } else s = strmerge("..\\",sRelative, NULL); if (sRelative) free(sRelative); sRelative = s; } } while (c); if (sRelative) { s = strmerge(sRelative,utf8_ptr(&iterYou),NULL); free(sRelative); sRelative = s; } else { sRelative = strmerge(utf8_ptr(&iterYou), NULL, NULL); } if (!sRelative) { break; } free(map->sOriginal); map->sOriginal = sRelative; err = 0; } if (err) { lit_free_manifest(pRelative); } else { display_manifest(pRelative); } return err; } void display_manifest(manifest_type * pManifest) { #if 0 mapping_state state; name_mapping * map; printf("Manifest at %08lx\n", pManifest); mapping_init(&state, pManifest); while ((map = mapping_next(&state)) != NULL) { printf("--> Map %08lx\n", map); printf(".............. (%08lx) \"%s\" \n", map->sOriginal, map->sOriginal); printf(".............. (%08lx) \"%s\" \n", map->sInternal, map->sInternal); printf(".............. (%08lx) \"%s\" \n", map->sType, map->sType); } printf("[Done manifest dump]\n"); #endif } clit18/manifest.h0000644000175000017500000000274710071423074012365 0ustar kyzkyz/*--[manifest.h]-------------------------------------------------------------- | Copyright (C) 2004 Digital Rights Software | | This file is part of "clit" (Convert LIT). | | "clit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ typedef struct mapping_state { int magic; manifest_type * p; int group, index; int max_index; name_mapping * map; } mapping_state; void mapping_init(mapping_state * m, manifest_type * pManifest); name_mapping * mapping_next(mapping_state * m); void display_manifest(manifest_type * pManifest); int duplicate_manifest(manifest_type * pDest, manifest_type * pSource); int make_relative_manifest(U8 * , manifest_type * , manifest_type * ); clit18/transmute.c0000644000175000017500000000602407701615616012577 0ustar kyzkyz/*--[transmute.c]-------------------------------------------------------------- | Copyright (C) 2002, 2003 Dan A. Jackson | | This file is part of the "clit" (Convert LIT) program. | | Convert LIT 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #include #include #include "litlib.h" extern char * writingFilename; /* | This file contains the routines to change DRM level and otherwise | write out a replacement .LIT file */ int writeat(void * v, U32 offset, U8 * ptr, int size); int transmute_lit(lit_file * litfile, char * newlitfile, char * inscription) { int newlevel, status, i; U8 * drm_data = NULL; int drm_size = 0; FILE * fOut; lit_file outlit; writingFilename = newlitfile; fOut = fopen(newlitfile,"wb"); if (!fOut) { lit_error(ERR_W|ERR_LIBC,"Unable to open \"%s\"!", newlitfile); return -1; } newlevel = 1; if (inscription) { newlevel = 3; drm_size = 2*(strlen(inscription)+1); drm_data = malloc(drm_size); if (!drm_data) { lit_error(0,"Unable to allocate memory for inscription."); return E_LIT_OUT_OF_MEMORY; } memset(drm_data, 0, drm_size); for (i = 0; (size_t)i < strlen(inscription); i++) { drm_data[i*2] = inscription[i]; } } status = lit_clone(litfile, &outlit); if (!status) { /* this is necessary because recrypt_section needs the callback | but it was on the original file. */ outlit.drm5_callback = litfile->drm5_callback; outlit.drm5_data = litfile->drm5_data; status = lit_change_drm_level(&outlit,newlevel,drm_data,drm_size); } if (!status) { outlit.file_pointer = (void *)fOut; outlit.writeat = writeat; status = lit_write_to_file(&outlit); } if (drm_data) free(drm_data); lit_close(&outlit); return status; } int writeat(void * v, U32 offset, U8 * ptr, int size) { FILE * f; int r; f = (FILE *)v; if (!f) { lit_error(0,"No filehandle passed in!"); return -1; } if (fseek(f, offset, SEEK_SET) != 0) { lit_error(ERR_LIBC|ERR_R,"fseek() failed."); return -1; } r = fwrite(ptr, 1, size, f); return r; } clit18/utils.c0000644000175000017500000001405010071622506011701 0ustar kyzkyz/*--[utils.c]---------------------------------------------------------------- | Copyright (C) 2004 Digital Rights Software | | This file is part of "clit" (Convert LIT). | | "clit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #include #include #include #include "litlib.h" #include "clit.h" #include "utils.h" char * strmerge(char * head, char * body, char * tail) { int len; char * str; len = 0; if (head) len += strlen(head); if (body) len += strlen(body); if (tail) len += strlen(tail); len++; str = malloc(len); if (!str) { lit_error(0,"Unable to malloc %d bytes for a string.",len); return NULL; } str[0] = '\0'; if (head) strcat(str,head); if (body) strcat(str,body); if (tail) strcat(str,tail); return str; } /*--[read_utf8_char]----------------------------------------------------------- | | This reads a single UTF8 character from a data stream, returning the | number of bytes consumed (element size) and filling in the Integer | value. */ int read_utf8_char(U8 * pdata, int nBytes, U32 * pvalue) { U32 c; unsigned char mask; int elsize, i; if (pvalue) *pvalue = -1; if (nBytes < 1) return -1; c = *(pdata); mask = 0x80; if (c & mask) { elsize = 0; while (c & mask) { mask >>= 1; elsize++;} if ((mask <= 1) || (mask == 0x40)) return -1; } else { elsize = 1; } if (elsize > 1) { if ((elsize) > nBytes) { return -1; } c &= (mask - 1); for (i = 1; i < elsize; i++) { if ( (*(pdata + i) & 0xC0) != 0x80) return -1; c = (c << 6) | ( *(pdata + i) & 0x3F ); } } if (pvalue) *pvalue = c; return elsize; } /*---------------------------------------------------------------------------- | utf8_strlen(UTF8 * string) +--------------------------------------------------------------------------*/ int utf8_strlen(U8 * p) { int units, bytes, len; U32 c; bytes = strlen(p)+1; units = 0; while (1) { len = read_utf8_char(p, bytes, &c); if ((!c) || (len <= 0)) break; units++; p += len; } return units; } /*---------------------------------------------------------------------------- | utf8_strmatch(UTF8 * s1, UTF8 * s2) +--------------------------------------------------------------------------*/ int utf8_strmatch(U8 * s1, U8 * s2) { int units, bytes1, bytes2, len; U32 c1, c2; bytes1 = strlen(s1)+1; bytes2 = strlen(s2)+1; units = 0; while (1) { len = read_utf8_char(s1, bytes1, &c1); if ((!c1) || (len <= 0)) break; s1 += len; bytes1 -= len; len = read_utf8_char(s2, bytes2, &c2); if ((!c2) || (len <= 0)) break; s2 += len; bytes2 -= len; if (c1 == c2) units++; else break; } return units; } /*--[utf8_start]-------------------------------------------------------------- | This starts the iteration of an UTF8 string. */ void utf8_start(utf8_iter * iter, U8 * string) { memset(iter, 0, sizeof(utf8_iter)); iter->ptr = string; iter->bytes = strlen(string); } /*--[utf8_peek]-------------------------------------------------------------- | Reads the next UNICODE code point from an UTF8 string. | Doesn't update the pointer */ U32 utf8_peek(utf8_iter * iter) { int len; U32 c; len = read_utf8_char(iter->ptr,iter->bytes, &c); if (len <= 0) { c = 0; } return c; } /*--[utf8_next]-------------------------------------------------------------- | Reads the next UNICODE code point from an UTF8 string. | Updates the pointer to the next value */ U32 utf8_next(utf8_iter * iter) { U32 c; iter->last_ptr = iter->ptr; iter->len = read_utf8_char(iter->ptr,iter->bytes, &c); if (iter->len <= 0) { c = 0; iter->last_ptr = NULL; iter->len = 0; iter->bytes = 0; } if (c != 0) { iter->bytes -= iter->len; iter->ptr += iter->len; } return c; } /* --[utf8_store]-------------------------------------------------------------- | Stores the previously read character into the output stream and returns | a length. */ int utf8_store(U8 * dest, utf8_iter * iter) { if (!iter->last_ptr) { *dest = 0; return 1; } memcpy(dest, iter->last_ptr, iter->len); return iter->len; } U8 * utf8_ptr(utf8_iter * iter) { return iter->ptr; } /*--[utf8_next_token]---------------------------------------------------------- | Reads a token (seperated by a single seperator) from the UTF8 iterator. | Returns the length (in bytes!) and the pointer to the beginning. | The length DOESN'T include the seperator, and the iterator SKIPS | the seperator. */ U8 * utf8_next_token(utf8_iter * iter, U32 seperator, int * pbytes) { U8 * start; int bytes; U32 c; start = utf8_ptr(iter); bytes = 0; do { c = utf8_next(iter); if (c == seperator) break; bytes += iter->len; } while (c); if (pbytes) *pbytes = bytes; return start; } clit18/utils.h0000644000175000017500000000300410071420722011677 0ustar kyzkyz/*--[utils.h]---------------------------------------------------------------- | Copyright (C) 2004 Digital Rights Software | | This file is part of "clit" (Convert LIT). | | "clit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ typedef struct utf8_iter { U8 * ptr; int bytes; U8 * last_ptr; int len; } utf8_iter; void utf8_start(utf8_iter *,U8 * string); U32 utf8_next(utf8_iter *); U8 * utf8_next_token(utf8_iter * iter, U32 seperator, int * pbytes); U32 utf8_peek(utf8_iter *); U8 * utf8_ptr(utf8_iter *); int utf8_store(U8 *, utf8_iter *); char * strmerge(char * head, char * body, char * tail); int read_utf8_char(U8 * pdata, int nBytes, U32 * pvalue); int utf8_strlen(U8 * p); int utf8_strmatch(U8 * s1, U8 * s2); clit18/win32.mak0000644000175000017500000000077110071705302012032 0ustar kyzkyzall: clit.exe OBJS=clit.obj explode.obj transmute.obj drm5.obj hexdump.obj display.obj utils.obj manifest.obj LIBS=advapi32.lib kernel32.lib msvcrt.lib ..\lib\openclit.lib ..\libtommath-0.30\tommath.lib CFLAGS=/D_DLL /Fo$*.obj /c /W3 /Ogsi1 /O1 /G6yAFs /DWIN32_LEAN_AND_MEAN -I..\lib -I..\libtommath-0.30 -I..\lib\des -I. AFLAGS=/Fo$*.obj /coff clean: -del $(OBJS) clit.exe clit.exe: $(OBJS) ../lib/openclit.lib link /map /subsystem:console /NODEFAULTLIB /OUT:$*.exe $** $(LIBS) lib/0000755000175000017500000000000010072017332010033 5ustar kyzkyzlib/litatom.c0000644000175000017500000001361410071622370011660 0ustar kyzkyz /*--[litatom.c]-------------------------------------------------------------- | Copyright (C) 2004 Digital Rights Software | | This file is part of the "openclit" library for processing .LIT files. | | "Openclit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #include #include #include "litlib.h" #include "litinternal.h" /* * This file contains routines to read and destroy atom (aka custom tag) * maps. */ /* | This is in the "/data//atom" internal file. | | Internal file format (no attributes) | +-----+---+-------------+---+------------+ ... +-+-+ | |count|len|name 0 (len) |len|name 1 (len)| |0|0| | +-----+---+-------------+---+------------+ ... +-+-+ | | With attributes: | +-+-+ | |0|0| (end of structure) | +-+-+ | becomes | +-------+---------+-----------+ | | count | length | attr name | ... | +-------+---------+-----------+ | | Strings are NOT zero terminated. | There is nothing after the last attribute name. */ /*--[lit_read_atoms]----------------------------------------------------------- | | This reads the atom list for a specified chunk of content. | The absence of an atom list is not an error -- one may not be present. | */ lit_atom_list * lit_read_atoms(lit_file * litfile, char * content_name) { lit_atom_list * list; char * pathAtoms, *s; int status, nbytes; int nEntries, len, idx; U8 * p, * pAtoms; list = NULL; pathAtoms = lit_i_strmerge("/data/",content_name,"/atom",NULL); if (!pathAtoms) { return NULL; } pAtoms = NULL; /* See if the file exists first... */ status = lit_get_file(litfile, pathAtoms, NULL, NULL); if (!status) { status = lit_get_file(litfile, pathAtoms, &pAtoms, &nbytes); if (nbytes <= 4) { free(pAtoms); pAtoms = NULL; } } p = pAtoms; free(pathAtoms); pathAtoms = NULL; if (!p) return NULL; nEntries = READ_U32(p); len = nEntries * sizeof(char *) + sizeof(lit_atom_list); list = (lit_atom_list *)malloc(len); if (!list) { lit_error(0,"Unable to malloc %d bytes for atoms.",len); free(pAtoms); return NULL; } memset( (void *)list, 0, len); list->num_atoms = nEntries; nbytes -= 4; p += 4; status = -1; idx = 0; while (idx < nEntries) { if (nbytes <= 1) break; len = *(p); if (!len) break; nbytes--; if (len > nbytes) { break;} s = (char *)malloc(len + 1); if (!s) { lit_error(0,"Error allocating %d bytes of memory for strings.\n", len); break; } strncpy(s,p + 1, len); s[len] = '\0'; list->atom_names[idx++] = s; nbytes -= len; p += (len + 1); status = 0; } if (idx != nEntries) { lit_error(0, "Warning - Unable to read custom tags - Expected %d entries, only read %d.\n", nEntries, idx); list->num_atoms = idx; } if (status) { lit_free_atoms(list); free(pAtoms); return NULL; } /* No attribute map, all done here */ if (nbytes < 4) { free(pAtoms); return list;} /* Now, start on the attribute map */ nEntries = READ_U32(p); p += 4; nbytes -= 4; len = sizeof(lit_attr_map) * (nEntries + 1); list->attrmap = (lit_attr_map *)malloc(len); if (!list->attrmap) { /* Fatal error -- otherwise, those attributes can't be decoded! */ lit_error(0,"Error allocating %d bytes for custom attributes.\n",len); lit_free_atoms(list); free(pAtoms); return NULL; } memset((void *)list->attrmap, 0, len); list->num_attrs = nEntries; idx = 0; while (idx < nEntries) { if (nbytes < 4) break; len = READ_U32(p); p += 4; nbytes -= 4; if (len > nbytes) { break;} s = (char *)malloc(len + 1); if (!s) { lit_error(0,"Error allocating %d bytes of memory for strings.\n", len); break; } strncpy(s,p, len); s[len] = '\0'; list->attrmap[idx].name = s; list->attrmap[idx].id = idx + 1; idx++; nbytes -= len; p += len; } if (idx != nEntries) { lit_error(0, "Warning - Unable to read custom attributes - Expected %d entries, read %d.\n", nEntries, idx); list->num_attrs = idx; } /* must add ending entry */ list->attrmap[idx].name = NULL; list->attrmap[idx].id = 0; free(pAtoms); pAtoms = NULL; return list; } /*--[lit_free_atoms]--------------------------------------------------------- | | This deallocates the memory associated with an atom list | */ void lit_free_atoms(lit_atom_list * atoms) { int i; if (atoms) { if (atoms->attrmap) { for (i = 0; i < atoms->num_attrs; i++) free((void *)atoms->attrmap[i].name); free(atoms->attrmap); } for (i = 0; i < atoms->num_atoms; i++) free(atoms->atom_names[i]); free(atoms); } } lib/Makefile0000644000175000017500000000055210072017332011475 0ustar kyzkyzall: openclit.a CFLAGS=-O3 -Wall -Ides -Isha -Inewlzx -I. clean: rm -f *.o openclit.a des/*.o lzx/*.o sha/*.o openclit.a: litatom.o litdrm.o litlib.o litembiggen.o littags.o litmetatags.o litmanifest.o litdirectory.o litsections.o litheaders.o litutil.o sha/mssha1.o des/des.o newlzx/lzxglue.o newlzx/lzxd.o -rm -f openclit.a ar rv openclit.a $^ lib/litdirectory.c0000644000175000017500000005507507701551600012736 0ustar kyzkyz /*--[litdirectory.c]----------------------------------------------------------- | Copyright (C) 2002 Dan A. Jackson | | This file is part of the "openclit" library for processing .LIT files. | | "Openclit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #include #include #include "litlib.h" #include "litinternal.h" /* This file contains routines for reading and writing to directory | structures. */ /* ... see comment further down in the file .... */ typedef struct aolx_struct { U8 * pBase; int total_size; int index; int entries; int remaining; } aolx_struct; typedef struct ifcm_struct { U8 * pData; int total_size; int chunk_size; int chunk; aolx_struct AOLL; aolx_struct AOLI; int aoll_chunk; int aoli_chunk; } ifcm_struct; static int aoli_add_name(aolx_struct * ,entry_type * , int ); static void aoli_finish(aolx_struct * , int ); static int aoll_add_entry(aolx_struct * , entry_type * ); static int aoll_add_index(aolx_struct * ,int ,int ,int ); static void aoll_finish(aolx_struct * ,int , int , int , int ); static int ifcm_free(ifcm_struct * ); static int ifcm_init(ifcm_struct *, int , U32 ); static int ifcm_new_aolx(ifcm_struct *, U32 ); static void read_encint(U8 **handle, int *remaining, U64 *value); static entry_type * read_entry(U8 **handle, int *remaining); static int write_encint(U8 * p, int index,int total, U64 x); /*--[read_encint]-------------------------------------------------------------- | | This reads an encoded integer from the data stream. | - ENCINT data type is a variable length integer, where the MSB | indicates if another byte is needed and the lower 7 bits represent | the data. | | For Example: | +-+-------------+ | 0x7F stays 0x7F |0|1 1 1 1 1 1 1| | +-+-------------+ | | +-+-------------+ +-+-------------+ | 0xFE becomes 0xFF 0x00 |1|1 1 1 1 1 1 1| |0|0 0 0 0 0 0 0| | +-+-------------+ +-+-------------+ | */ void read_encint(U8 **handle, int *remaining, U64 *value) { U8 b, *p; p = *handle; *value = 0; while (*remaining > 0) { b = *(p++); (*remaining)--; (*value) <<= 7; (*value) |= (b & 0x7f); if (!(b & 0x80)) break; } *handle = p; } /*--[write_encint]------------------------------------------------------------- | | Store an encoded integer into a location. */ int write_encint(U8 * p,int index,int total,U64 x) { int len,i; U64 mask; len = 0; mask = 0x7f; while (x > mask) { len++; mask = (mask << 7)|0x7f; } len++; if (len > (total - index)) return -1; for (i = 1; i < len; i++) { *(p+index) = 0x80 + (U8)((x & mask) >> ((len - i) * 7)); mask >>= 7; index++; } *(p+index) = (U8)((x & mask) &0xff); return len; } /*--[read_entry]--------------------------------------------------------------- | | This routine reads an entry given a pointer and the number of bytes | remaining. | | A directory entry is: | ENCODED_INTEGER name_length | BYTES name | ENCODED_INTEGER section | ENCODED_INTEGER offset | ENCODED_INTEGER size | */ entry_type * read_entry(U8 **handle, int *remaining) { int namelen; U64 long_namelen; U8 * nameptr; entry_type * entry; read_encint(handle, remaining, &long_namelen); namelen = (int)(long_namelen & 0x7fffffff); if (long_namelen != namelen) { lit_error(ERR_R,"Directory entry had 64-bit name length!"); return NULL; } nameptr = *handle; if ((namelen + 3) <= *remaining) { (*handle)+= namelen; *remaining -= namelen; } else { lit_error(ERR_R,"Read past end of directory chunk!"); return NULL; } entry = malloc(sizeof(entry_type)+namelen + 1); if (!entry) { lit_error(ERR_LIBC,"malloc(%d) failed!", sizeof(entry_type)+namelen+1); return NULL; } entry->next = NULL; entry->data_source = ENTRY_SOURCE_LITFILE; entry->data_pointer = NULL; entry->namelen = namelen; memcpy(&entry->name, nameptr, namelen); /* Avoid warnings about going past the end of an array */ *( (U8 *)(&entry->name) + namelen) = '\0'; read_encint(handle,remaining,&entry->section); read_encint(handle,remaining,&entry->offset); read_encint(handle,remaining,&entry->size); return entry; } /*--[write_entry]-------------------------------------------------------------- | | Similar to read entry above. | */ int write_entry(U8 * p,int base_index,int total,entry_type * entry) { int len,index = base_index; U32 q; q = entry->namelen; /* GCC gives garbage in high Dword without this */ len = write_encint(p, index, total, q); if (len >= 0) { index += len; len = entry->namelen; if ( (index+len) < total) { memcpy(p+index, &entry->name,len); } else len = -1; } if (len >= 0) { index += len; len = write_encint(p, index, total, entry->section); } if (len >= 0) { index += len; len = write_encint(p, index, total, entry->offset); } if (len >= 0) { index += len; len = write_encint(p, index, total, entry->size); } if (len >= 0) index += len; if (len < 0) { if ((index - base_index) > 0) memset(p+base_index, 0, index - base_index); return E_LIT_NOT_ENOUGH_BYTES; } return (index - base_index); } /*** DIRECTORY STRUCTURES ***************************************************** +-----------------------------+ | | | Directory header | | | +-----------------------------+ | | | Directory Entries ----> | | | +-----------------------------+ | | | | | <---- QuickRef Entries | +-----------------------------+ +-------------------------------+--------------------------------+ | Block Tag "IFCM" | Version (typically 1) | +-------------------------------+--------------------------------+ | "Chunk Size" | Unknown (0x100000 or 0x20000) | +-------------------------------+--------------------------------+ | 0xFFFFFFFF 0xFFFFFFFF (Unknown) | +-------------------------------+--------------------------------+ | Number of chunks | +----------------------------------------------------------------+ +-------------------------------+--------------------------------+ | Second Tag "AOLL" | Number of bytes after entries | +-------------------------------+--------------------------------+ | Current chunk id (0 based) | +----------------------------------------------------------------+ | Previous AOLL chunk id | +----------------------------------------------------------------+ | Next AOLL chunk id (0 based) | +----------------------------------------------------------------+ | Entries so far | +----------------------------------------------------------------+ | 1 (Distance in chunks to next) | +----------------------------------------------------------------+ | Directory entries +-------------------------------..... ******************************************************************************/ #define IFCM_TAG 0x4D434649 #define AOLL_TAG 0x4C4C4F41 #define AOLI_TAG 0x494C4F41 /*--[lit_i_read_directory]----------------------------------------------------- | | This reads the directory chunks from the piece and converts the list into | entry elements in the lit_file structure. | */ int lit_i_read_directory(lit_file * litfile, U8 * piece, int piece_size) { U8 * p = NULL; int nEntries, nRemaining, nChunks, chunk, idx, sizeChunk; entry_type * entry, * prev; if (!piece || (READ_U32(piece) != IFCM_TAG)) { lit_error(ERR_R, "Header Piece #3 is not the main directory! (TAG=%08lx)", (piece)?(READ_U32(piece)):0); return E_LIT_FORMAT_ERROR; } sizeChunk = READ_INT32(piece + 8); nChunks = READ_INT32(piece + 24); if ((32 + (nChunks * sizeChunk)) != piece_size) { lit_error(ERR_R, "IFCM HEADER (%d chunks of %d bytes) != %d total.", nChunks, sizeChunk, piece_size - 32); return E_LIT_FORMAT_ERROR; } prev = NULL; for (chunk = 0; chunk < nChunks; chunk++) { p = piece + 32 + (chunk * sizeChunk); /* This can either be AOLL or AOLI. * AOLI isn't useful for reading */ if (READ_U32(p) != AOLL_TAG) continue; nRemaining = READ_INT32(p + 4); if (nRemaining >= sizeChunk) { lit_error(ERR_R, "AOLL remaining count is NEGATIVE! (%d of %d) %x\n", (int)nRemaining, (int)sizeChunk, (int)nRemaining); return E_LIT_FORMAT_ERROR; } nRemaining = sizeChunk - (nRemaining + 48); nEntries = READ_U16(p + sizeChunk - 2); /* Sometimes, the nEntries doesn't get written. When this happens, * I don't know how many to read. Fortunately, there is "nRemaining", * and if everything is working fine, read_entry will consume JUST * enough bytes */ if (!nEntries) nEntries = 65535; p += 48; if (nRemaining < 0) return E_LIT_FORMAT_ERROR; for (idx = 0; idx < nEntries; idx ++) { if (nRemaining <= 0) break; entry = read_entry(&p, &nRemaining); if (!entry) { return E_LIT_FORMAT_ERROR; } if (!prev) { litfile->entry = entry; prev = entry; } else { prev->next = entry; prev = entry; } } } return 0; } /*--[lit_i_free_dir_type]------------------------------------------------------ | | releases memory associated with a dir_type | */ void lit_i_free_dir_type(dir_type * dirtype) { if (dirtype->entry_ptr) free(dirtype->entry_ptr); if (dirtype->count_ptr) free(dirtype->count_ptr); memset(dirtype, 0, sizeof(dir_type)); } /*--[aoli_add_name]------------------------------------------------------------ | */ int aoli_add_name(aolx_struct * aolx,entry_type * entry, int chunk) { int len; int base_index; U64 q; base_index = aolx->index; if ((aolx->entries % 5) == 0) { aolx->remaining -= 2; WRITE_U32(aolx->pBase+aolx->remaining, aolx->index - 48); } q = entry->namelen; len = write_encint(aolx->pBase,aolx->index,aolx->remaining, q); if (len >= 0) { aolx->index += len; len = entry->namelen; if ( (aolx->index+len) < aolx->remaining) { memcpy(aolx->pBase+aolx->index, &entry->name,len); } else len = -1; } if (len >= 0) { aolx->index += len; q = chunk; len = write_encint(aolx->pBase,aolx->index,aolx->remaining, q); } if (len >= 0) aolx->index += len; if (len < 0) { if ((aolx->index - base_index) > 0) memset(aolx->pBase+base_index, 0, aolx->index - base_index); return E_LIT_NOT_ENOUGH_SPACE; } aolx->entries++; return 0; } /*--[aoli_finish]-------------------------------------------------------------- | */ void aoli_finish(aolx_struct * aolx, int param) { WRITE_U16(aolx->pBase + (aolx->total_size - 2), aolx->entries); WRITE_U32(aolx->pBase + 4, aolx->total_size - aolx->index); WRITE_U32(aolx->pBase + 8, param); } /*--[aoll_add_entry]----------------------------------------------------------- | */ int aoll_add_entry(aolx_struct * aolx, entry_type * entry) { int nbytes; if ((aolx->entries % 5) == 0) { aolx->remaining -= 2; WRITE_U16(aolx->pBase+aolx->remaining, aolx->index - 48); } nbytes = write_entry(aolx->pBase,aolx->index,aolx->remaining,entry); if (nbytes < 0) { return nbytes; } aolx->index += nbytes; aolx->entries++; return 0; } /*--[aoll_add_index]----------------------------------------------------------- | */ int aoll_add_index(aolx_struct * aolx,int so_far,int current,int chunk) { int len; int base_index; U64 q; base_index = aolx->index; if ((aolx->entries % 5) == 0) { aolx->remaining -= 2; WRITE_U16(aolx->pBase+aolx->remaining, aolx->index - 48); } q = so_far; len = write_encint(aolx->pBase,aolx->index,aolx->remaining,q); if (len >= 0) { aolx->index += len; q = current; len = write_encint(aolx->pBase,aolx->index,aolx->remaining, q); } if (len >= 0) { aolx->index += len; q = chunk; len = write_encint(aolx->pBase,aolx->index,aolx->remaining, q); } if (len >= 0) aolx->index += len; if (len < 0) { if ((aolx->index - base_index) > 0) memset(aolx->pBase+base_index, 0, aolx->index - base_index); return E_LIT_NOT_ENOUGH_SPACE; } aolx->entries++; return 0; } /*--[aoll_finish]-------------------------------------------------------------- | */ void aoll_finish(aolx_struct * aolx, int entries_so_far, int prev, int cur, int next) { WRITE_U16(aolx->pBase + (aolx->total_size - 2), aolx->entries); WRITE_U32(aolx->pBase + 4, aolx->total_size - aolx->index); if (cur >= 0) { WRITE_U32(aolx->pBase + 8, cur); WRITE_U32(aolx->pBase + 12, 0); } else { WRITE_U32(aolx->pBase + 8, 0xFFFFFFFF); WRITE_U32(aolx->pBase + 12, 0xFFFFFFFF); } if (prev >= 0) { WRITE_U32(aolx->pBase + 16, prev); WRITE_U32(aolx->pBase + 20, 0); } else { WRITE_U32(aolx->pBase + 16, 0xFFFFFFFF); WRITE_U32(aolx->pBase + 20, 0xFFFFFFFF); } if (next >= 0) { WRITE_U32(aolx->pBase + 24, next); WRITE_U32(aolx->pBase + 28, 0); } else { WRITE_U32(aolx->pBase + 24, 0xFFFFFFFF); WRITE_U32(aolx->pBase + 28, 0xFFFFFFFF); } WRITE_U32(aolx->pBase + 32, entries_so_far); if (next-cur > 0) { WRITE_U32(aolx->pBase + 40, next-cur); } else { WRITE_U32(aolx->pBase + 40, 1); } } /*--[lit_i_make_directories]--------------------------------------------------- | | This creates new directory structures and fills in the dir_type structure | with the created header information. | */ int lit_i_make_directories(lit_file * litfile, dir_type * dirtype) { entry_type *entry; int r, entries_so_far, last_chunk, next_chunk, chunk; int has_aoli; ifcm_struct ifcmIndex, ifcmDir; U32 total_content_length; if ((!litfile->entry_chunklen) || (!litfile->count_chunklen)) { lit_error(ERR_W,"Invalid chunk lengths when making directories!\n"); return E_LIT_BAD_STRUCT; } r = ifcm_init(&ifcmIndex, litfile->count_chunklen, litfile->count_unknown); if (!r) r = ifcm_init(&ifcmDir, litfile->entry_chunklen, litfile->entry_unknown); if (r) { lit_error(ERR_W,"Error (%d) initializing IFCM structures.", r); goto bad; } r = ifcm_new_aolx(&ifcmDir, AOLL_TAG); if (!r) r = ifcm_new_aolx(&ifcmIndex, AOLL_TAG); if (r) { lit_error(ERR_W,"Error (%d) creating AOLL chunks.", r); goto bad; } has_aoli = 0; last_chunk = -1; chunk = 0; next_chunk = chunk+1; total_content_length = 0; entries_so_far = 0; entry = litfile->entry; while (entry) { if (((entry->namelen > 1) || (entry->name != '/')) && (entry->section == 0) ) { entry->offset = total_content_length; total_content_length += (size_t)entry->size; } r = aoll_add_entry(&ifcmDir.AOLL, entry); if (r) { if (!has_aoli) { r = ifcm_new_aolx(&ifcmDir,AOLI_TAG); if (r) { lit_error(ERR_W,"Error (%d) while making first AOLI.",r); goto bad; } /* This points to the first file */ r = aoli_add_name(&ifcmDir.AOLI,litfile->entry, 0); if (r) { /* WTF! * Allocate a new AOLI here? * Are the AOLI links to/from this? */ lit_error(ERR_W, "Too many files in AOLI, and I can't create a second AOLI block!\n"); goto bad; } has_aoli++; next_chunk++; } aoll_finish(&ifcmDir.AOLL,entries_so_far,last_chunk, chunk,next_chunk); r = aoll_add_index(&ifcmIndex.AOLL,entries_so_far, ifcmDir.AOLL.entries,chunk); if (r) { lit_error(ERR_W, "Too many count entries in AOLL - This case should not happen!\n"); goto bad; } last_chunk = chunk; chunk = next_chunk; next_chunk++; entries_so_far += ifcmDir.AOLL.entries; r = ifcm_new_aolx(&ifcmDir,AOLL_TAG); if (r) { lit_error(ERR_W,"Error (%d) making additional AOLL.", r); goto bad; } r = aoli_add_name(&ifcmDir.AOLI,entry, chunk); if (r) { lit_error(ERR_W, "Unable to add first entry of new chunk to AOLI.\n"); goto bad; } /* Should work this time */ r = aoll_add_entry(&ifcmDir.AOLL, entry); if (r) { lit_error(ERR_W,"Writing to new directory failed!"); goto bad; } } entry = entry->next; } aoll_finish(&ifcmDir.AOLL, entries_so_far, last_chunk, chunk, -1); aoll_add_index(&ifcmIndex.AOLL,entries_so_far,ifcmDir.AOLL.entries,chunk); if (has_aoli) { aoli_finish(&ifcmDir.AOLI, 1); } aoll_finish(&ifcmIndex.AOLL, 0, -1, 0, -1); if (!dirtype) return 0; /* Note - last_chunk only works BECAUSE I put the AOLI in the middle | (following LITGEN.DLL) and not at the end (which AEBIN does). */ dirtype->entry_ptr = ifcmDir.pData; dirtype->entry_size = ifcmDir.total_size; dirtype->entry_last_chunk = ifcmDir.chunk; dirtype->count_ptr = ifcmIndex.pData; dirtype->count_size = ifcmIndex.total_size; dirtype->count_last_chunk = ifcmIndex.chunk; dirtype->num_entries = entries_so_far; dirtype->num_counts = ifcmIndex.AOLL.entries; dirtype->total_content_size = total_content_length; /* Again.. Assuming one AOLI index at a particular spot! */ if (has_aoli) { dirtype->entry_aoli_idx = 1; } else { dirtype->entry_aoli_idx = -1; } return 0; bad: ifcm_free(&ifcmDir); ifcm_free(&ifcmIndex); return r; } /*--[ifcm_free]---------------------------------------------------------------- | */ int ifcm_free(ifcm_struct * ifcm) { if (ifcm->pData) free(ifcm->pData); return 0; } /*--[ifcm_init]---------------------------------------------------------------- | */ int ifcm_init(ifcm_struct * ifcm, int chunk_size, U32 param) { unsigned char *p; p = malloc(chunk_size + 32); if (!p) return E_LIT_OUT_OF_MEMORY; WRITE_U32(p, IFCM_TAG); WRITE_U32(p+ 4, 1); WRITE_U32(p+ 8, chunk_size); WRITE_U32(p+12, param); WRITE_U32(p+16, 0xFFFFFFFF ); WRITE_U32(p+20, 0xFFFFFFFF ); WRITE_U32(p+24, 1 ); WRITE_U32(p+28, 0 ); ifcm->total_size = chunk_size + 32; ifcm->chunk_size = chunk_size; ifcm->chunk = 0; ifcm->pData = p; ifcm->aoll_chunk = -1; ifcm->aoli_chunk = -1; memset(&ifcm->AOLL, 0, sizeof(aolx_struct)); memset(&ifcm->AOLI, 0, sizeof(aolx_struct)); return 0; } /*--[ifcm_new_aolx]------------------------------------------------------------ | | the IFCM (outer block) reallocates itself every time in needs to | add on another AOLx structure. | | Fortunately, once an AOLX structure is done, it never needs to be | directly accessed again. */ int ifcm_new_aolx(ifcm_struct * ifcm, U32 tag) { aolx_struct * aolx; U8 * pBase; if (ifcm->chunk > 0) { ifcm->pData = (unsigned char *)realloc(ifcm->pData, (ifcm->chunk_size * (ifcm->chunk + 1)) + 32); if (!ifcm->pData) { lit_error(ERR_W,"Unable to realloc from %d to %d.\n", (ifcm->chunk_size * ifcm->chunk) + 32, (ifcm->chunk_size * (ifcm->chunk + 1)) + 32); return E_LIT_OUT_OF_MEMORY; } ifcm->total_size = (ifcm->chunk_size * (ifcm->chunk + 1)) + 32; } pBase = ifcm->pData + (ifcm->chunk_size * ifcm->chunk) + 32; if (tag == AOLL_TAG) { aolx = &ifcm->AOLL; memset(pBase+16, 0xff, 16); memset(pBase+32, 0, 16); aolx->index = 48; ifcm->aoll_chunk = ifcm->chunk; } else { /* AOLI */ aolx = &ifcm->AOLI; aolx->index = 16; ifcm->aoli_chunk = ifcm->chunk; } if (ifcm->aoli_chunk >= 0) ifcm->AOLI.pBase = ifcm->pData+32+(ifcm->chunk_size*ifcm->aoli_chunk); if (ifcm->aoll_chunk >= 0) ifcm->AOLL.pBase = ifcm->pData+32+(ifcm->chunk_size*ifcm->aoll_chunk); memset(pBase, 0, 16); WRITE_U32(pBase, tag); memset(pBase + aolx->index, 0, ifcm->chunk_size - aolx->index); WRITE_U32(ifcm->pData + 24, ifcm->chunk + 1); aolx->total_size = ifcm->chunk_size; aolx->entries = 0; /* Reserve space for total count. */ aolx->remaining = ifcm->chunk_size - 2; ifcm->chunk++; return 0; } lib/win32.mak0000644000175000017500000000071510071707532011501 0ustar kyzkyzall: openclit.lib OBJS=litatom.obj litdrm.obj litlib.obj litembiggen.obj littags.obj litmetatags.obj litmanifest.obj litdirectory.obj litsections.obj litheaders.obj litutil.obj sha\mssha1.obj des\des.obj newlzx\lzxglue.obj newlzx\lzxd.obj CFLAGS=/D_DLL /Fo$*.obj /c /W3 /Ogsi1 /O1 /G6yAFs /DWIN32_LEAN_AND_MEAN -Ides -Isha -Inewlzx -I. clean: -del $(OBJS) openclit.lib openclit.lib: $(OBJS) -del openclit.lib lib /OUT:openclit.lib $** lib/litdrm.c0000644000175000017500000002706110051715770011510 0ustar kyzkyz/*****************************************************************************/ /*--[litdrm.c]----------------------------------------------------------------- | Copyright (C) 2002 Dan A. Jackson | | This file is part of the "openclit" library for processing .LIT files. | | "Openclit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ #include #include #include #include "litlib.h" #include "litinternal.h" #include "d3des.h" #include "sha.h" static int calculate_deskey(lit_file *, U8 key[8]); static void hash_bytes(SHA_CTX * pctx, U8 * pdata, int length, int padding); static void random_data(U8 * pData, int len); #define SEALED_SIZE 16 /* | This file contains routines to deal with the two standard DRM levels. | Those levels are | DRM1 - "Sealed" | "/DRMStorage/DRMSealed" contains a DES key that is encrypted with | a DES key generated from "/meta" and "/DRMStorage/DRMSource" | DRM3 - "Inscribed" | "/DRMStorage/DRMSealed" contains a DES key that is encrypted with | a DES key generated from "/meta","/DRMStorage/DRMSource", and | "/DRMStorage/DRMBookplate" (the inscribed text in Unicode). | | "/DRMStorage/ValidationStream" should always be "MSReader" if the | decryption works. */ static const char * drmsource_string = "/DRMStorage/DRMSource"; static const char * sealed_string = "/DRMStorage/DRMSealed"; static const char * bookplate_string = "/DRMStorage/DRMBookplate"; static const char * validation_string = "/DRMStorage/ValidationStream"; static const char * license_string = "/DRMStorage/Licenses/EUL"; static const char * license_path_string = "/DRMStorage/Licenses"; static const char * meta_string = "/meta"; static const char * msreader_string = "MSReader"; /*--[lit_i_read_drm]----------------------------------------------------------- | | This routine checks for the presence of DRM and initializes the DRM level | and bookkey appropriately. | */ int lit_i_read_drm(lit_file * litfile) { U8 * ptr, tmpkey[8]; int size, ofs; int status; status = lit_get_file(litfile, meta_string, NULL, NULL); if (status) { lit_error(ERR_R,"Bad LIT directory - no \"%s\" file!",meta_string); return status; } litfile->drmlevel = 0; status = lit_get_file(litfile, license_string, NULL, NULL); if (!status) litfile->drmlevel = 5; if (status) { status = lit_get_file(litfile,bookplate_string, NULL, NULL); if (!status) litfile->drmlevel = 3; } if (status) { status = lit_get_file(litfile,sealed_string, NULL, NULL); if (!status) litfile->drmlevel = 1; } if (!litfile->drmlevel) return 0; if (litfile->drmlevel < 5) { status = calculate_deskey(litfile, tmpkey); if (status) return status; status = lit_get_file(litfile, sealed_string, &ptr, &size); if (status) return status; deskey(tmpkey, DE1); for (ofs = 0; ofs < size; ofs += 8) { des( (ptr+ofs), (ptr+ofs)); } if (*ptr) { lit_error(ERR_R, "Unable to decrypt title key! Check value = %02x\n" "This may be a newer or corrupted .LIT file!", *ptr); litfile->drmlevel = DRM_LEVEL_INVALID; free(ptr); return E_LIT_DRM_ERROR; } /* the key is the 8 bytes, skipping the first (should be 0) */ memcpy(litfile->bookkey, ptr+1, 8); free(ptr); ptr = NULL; } else { /* DRM level is 5 */ if (litfile->drm5_callback) { litfile->drmlevel = 5; status = litfile->drm5_callback(litfile->drm5_data,NULL,0); return status; } else { lit_error(ERR_R, "DRM5 is not supported!"); litfile->drmlevel = 5; return E_LIT_DRM_ERROR; } } status = lit_get_file(litfile,validation_string,&ptr,&size); if (status) { litfile->drmlevel = DRM_LEVEL_INVALID; return status; } if (strncmp(msreader_string,ptr,strlen(msreader_string)) != 0) { lit_error(ERR_R, "Decryption is incorrect -- the Validation string is wrong!"); free(ptr); litfile->drmlevel = DRM_LEVEL_INVALID; return E_LIT_DRM_ERROR; } free(ptr); return 0; } /*--[lit_i_encrypt]------------------------------------------------------------ | | This routine encrypts the section with the specified key | */ int lit_i_encrypt(U8 * pContent, int sizeContent, U8 * new_key) { int ofs; deskey(new_key, EN0); for (ofs = 0; ofs < sizeContent; ofs+= 8) { des( (pContent+ofs), (pContent+ofs)); } return 0; } /*--[lit_i_decrypt]------------------------------------------------------------ | | This routine handles decryption of sections. | */ int lit_i_decrypt(lit_file * litfile, U8 * pContent, int sizeContent) { int status; int ofs; if (litfile->drmlevel < 0) { return E_LIT_DRM_ERROR; } if (litfile->drmlevel == 5) { status = E_LIT_DRM_ERROR; if (litfile->drm5_callback) { status = litfile->drm5_callback(litfile->drm5_data, pContent, sizeContent); } else { lit_error(ERR_R, "This version is unable to decrypt owner-exclusive content!"); } return status; } deskey(litfile->bookkey, DE1); for (ofs = 0; ofs < sizeContent; ofs+= 8) { des( (pContent+ofs), (pContent+ofs)); } return 0; } /*--[lit_change_drm_level]----------------------------------------------------- | | This routine changes the DRM on the file. | Specifically, this can go from 5 or 3 to 1, and from 1 to 3 (inscribing). | DRM2 and DRM4 are undefined. | and DRM0 is unsupported for now */ int lit_change_drm_level(lit_file * litfile, int newlevel, U8 * drm_data, int data_size) { int i, status; U8 * new_sealed, * pBookplate, new_key[8]; if (litfile->drmlevel == 0) { lit_error(ERR_W,"Changing DRM0 is not yet supported."); return E_LIT_UNSUPPORTED; } if (newlevel == 0) { lit_error(ERR_W,"Converting TO DRM0 is not yet supported."); return E_LIT_UNSUPPORTED; } if (newlevel == 3) { if (!drm_data) { lit_error(ERR_W,"No inscription data supplied!"); return E_LIT_NULL_POINTER; } } lit_remove_files(litfile, license_path_string); switch (newlevel) { case 1: lit_remove_files(litfile, bookplate_string); break; case 3: pBookplate = malloc(data_size); if (!pBookplate) { lit_error(ERR_W,"Can't allocate %d bytes for copy of inscription.", data_size); return E_LIT_OUT_OF_MEMORY; } memcpy(pBookplate, drm_data, data_size); status = lit_put_file(litfile,bookplate_string,pBookplate,data_size, 0); if (status) return status; break; default: lit_error(ERR_W,"Unable to change DRM level to %d.", newlevel); return E_LIT_UNSUPPORTED; } if ((litfile->drmlevel == 5) || (litfile->drmlevel == 0)) { /* Need a new bookkey */ random_data(new_key, 8); for (i = 1; i < litfile->num_sections; i++) { status = lit_i_encrypt_section(litfile,litfile->sections[i].name, &new_key[0]); } memcpy(litfile->bookkey, new_key, 8); } new_sealed = malloc(SEALED_SIZE); if (!new_sealed) { lit_error(ERR_W,"Not enough space for new sealed."); return E_LIT_OUT_OF_MEMORY; } memset(new_sealed, 0, SEALED_SIZE); memcpy(new_sealed+1, litfile->bookkey, 8); litfile->drmlevel = newlevel; status = calculate_deskey(litfile,new_key); if (status ) { lit_error(ERR_W, "Unable to make new \"Sealed\" key."); free(new_sealed); return status; } deskey(new_key, EN0); for (i = 0; i < SEALED_SIZE; i+=8) { des( (new_sealed+i), (new_sealed+i)); } status = lit_put_file(litfile,sealed_string,new_sealed, SEALED_SIZE,0); if (status) { free(new_sealed); return status; } return 0; } /*--[hash_bytes]--------------------------------------------------------------- | | This is a "special" hash routine which has slightly different properties | than just calling SHA_Update. | Note that the SHA routine isn't exactly standard either. */ void hash_bytes(SHA_CTX * pctx, U8 * pdata, int length, int padding) { /* This buffer size is FIXED as per the algorithm */ U8 buffer[64]; int pos, avail, offset; if (padding > sizeof(buffer)) { return; } memset(buffer, 0, padding); pos = padding; offset = 0; while (length > 0) { avail = sizeof(buffer) - pos; if (length >= avail) { memcpy(&buffer[pos],pdata+offset, avail); length -= avail; offset += avail; } else { /* Length + 0's */ memcpy(&buffer[pos],pdata+offset, length); memset(&buffer[pos+length],0, avail - length); /* offset is meaningless now */ length = 0; } SHA1_Update(pctx, buffer, sizeof(buffer)); pos = 0; } } /*--[calculate_deskey]--------------------------------------------------------- | | This routine is responsible for calculating a DES key from the internal | files. | See the comment above for which files affect the calculation process. | Things are done in a particular way for various reasons and small changes | will invalidate the key calculation process. | */ int calculate_deskey(lit_file * litfile, U8 key[8]) { int nbytes; int status, i, pad; U8 *p, digest[20]; SHA_CTX ctx; const char *hashfiles[] = {meta_string,drmsource_string, bookplate_string,NULL}; const char **s; SHA1_Init(&ctx); /* Remove the bookplate if it is not present. */ if (litfile->drmlevel != 3) { hashfiles[2] = 0; } s = hashfiles; /* First hash has two bytes of padding */ pad = 2; while (*s) { status = lit_get_file(litfile, *s, &p, &nbytes); if (status) { return status; } hash_bytes(&ctx, p, (int)nbytes, pad); free(p); pad = 0; s++; } SHA1_Final(digest, &ctx); memset(key, 0, 8); for (i = 0; i < sizeof(digest); i++) { key[i%8] ^= digest[i]; } return 0; } /*--[random_data]-------------------------------------------------------------- | | Random data for creating book key's. | FUTUREFIX - This is very non-random. */ void random_data(U8 * pData, int length) { int i, r; srand(time(NULL)); for (i = 0; i < length; i++) { r = rand() ^ (rand() >> 7); pData[i] = (r & 0xff); } } lib/litembiggen.c0000644000175000017500000006315610071645472012513 0ustar kyzkyz/*--[litembiggen.c]------------------------------------------------------------ | Copyright (C) 2002 Dan A. Jackson | | This file is part of the "openclit" library for processing .LIT files. | | "Openclit" 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. | | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. | | The GNU General Public License may also be available at the following | URL: http://www.gnu.org/licenses/gpl.html */ /* This file concerns the transformation of "evaporated" html files into | close to their original format. */ #include #include #include #include "litlib.h" #include "littags.h" /* | Warning - Fixed sized buffers here-in. Don't change U32's to U64 without | being very sure what you are doing. */ char *lit_lookup_mapping(manifest_type * pmanifest,U8 * s,int size); static int read_utf8_char(U8 * pdata, int nBytes, U32 * pvalue); static int write_entity(U32 c, HTMLWRITEFCN htmlwrite, void * write_data); typedef struct entity_map { char * name; unsigned long int id; } entity_map; entity_map entities[] = {{"amp",38}, {"lt",60}, {"gt",62}}; #define STATE_TEXT 0 #define STATE_GET_FLAGS 1 #define STATE_GET_TAG 2 #define STATE_GET_ATTR 3 #define STATE_GET_VALUE 4 #define STATE_GET_VALUE_LENGTH 5 #define STATE_GET_CUSTOM 6 #define STATE_GET_CUSTOM_LENGTH 7 #define STATE_GET_CUSTOM_ATTR 8 #define STATE_GET_ATTR_LENGTH 9 #define STATE_GET_HREF_LENGTH 10 #define STATE_GET_HREF 11 #define WRITE_CHAR(c) { \ ch = c;\ status = htmlwrite(write_data,&ch,1);\ if (status < 0) goto write_error;\ } #define WRITE_STRING(s) { \ status = htmlwrite(write_data,s,strlen(s));\ if (status < 0) goto write_error;\ } /** EVAPORATED HTML ********************************************************* Assumed to start in text, although all documents should start with a tag. Basically each name and element are 0-deliminated. so, roughly: Text<0>name<0>attr-name<0>attr-value<0><0> where flag: 1xxx - This is inside (and inclusive) of the element> x1xx - "Block" tags. Varies between verious of the DLL though. xx1x - Closing Tag xxx1 - Opening tag xx11 - Empty Tag (should be 1xxxx - Use a name from "atoms" array There's more for custom tag names and attributes. ***************************************************************************/ #define FLAG_OPENING 1 #define FLAG_CLOSING 2 #define FLAG_BLOCK 4 #define FLAG_HEAD 8 #define FLAG_ATOM 16 /*--[lit_reconstitute_html]---------------------------------------------------- | | I read UTF8 from the buffer, and expand the internal compressed element | representation used by the IHtmlElement interface. | | I am recursive to handle nested tags, and return the number of bytes | consumed. | | | Whitespace | This has been the source for bugs in every previous version of CLIT, | so I am rewriting how this is handled. | | Key principle: Don't throw away whitespace from the file, since that | leads to irritation. Any "missing" whitespace can be added back by Tidy | anyway. | | Rules for XML/XHTML whitespace | A. Whitespace is SPACE, TAB, CR, LF | B. Sequences of one or more whitespace characters are turned into one | C. white space immediately after a tag should be ignored | D. white space immediately before a end tag should be ignored | (E) MSXML converts all whitespace into ' ' | | Rule D doesn't really seem to work as I expect though. | | Rule B doesn't apply cases where I am inside xml:space="preserve" tags. | Since I don't want to scan for attributes, this means I cannot arbitrarily | add spaces except at the beginning and end! | | Anything | should always be convertable to: | | \t\tAnything | since all initial whitespace is ignored! | | To avoid doing this for , type tags, I'll use blocking. This is | a "convenience" choice -- is still rendered as text ! | text | Complications: | /meta (html_type = 1) doesn't use the upper 2 bits - force everything | into "block" mode. | | The "RIGHT" solution would use HTMLTidy - but that wouldn't be in following | with the "unzip" style approach | */ static int depth = 0; static int pending_indent = 0; static int was_in_text = 0; /* had non-whitespace text in the last block */ /* Kludge due to wierd recursion model. I'm not going to rewrite that to fix * spacing. */ static int lingering_space = 0; int lit_reconstitute_html(U8 * pHtml, int base, int nBytes, int html_type, manifest_type * pManifest, lit_atom_list * pAtoms, HTMLWRITEFCN htmlwrite, void * write_data ) { int elsize, state, index; int i, status, tag_index, char_count, href_base; U32 c, flags, tag; int is_goingdown = 0; attrmap * current_map = NULL, * tmp_map; char * tag_name = NULL, ch, numbuf[20]; int dynamic_tag = 0; U32 nTagRefs; char ** mapTag2Name; attrmap ** mapTag2Attrs; attrmap * mapAttrs; int errors = 0; int in_censorship = 0; int space_enabled = 1, saved_space_enabled = 0; /* Was this tag indented? If so, newline/indent the end */ int was_indented = 0; status = char_count = tag_index = flags = href_base = 0; /* Reinitialize stateful variables only the first time */ if (!depth) { was_in_text = pending_indent = lingering_space = 0; } switch (html_type) { case 1: nTagRefs = (U32)meta_tagcount; mapTag2Name = (char **)meta_tagtoname; mapTag2Attrs= (attrmap **)meta_tagtoattr; mapAttrs = (attrmap *)meta_attr; break; default: nTagRefs = (U32)tagcount; mapTag2Name = (char **)tagtoname; mapTag2Attrs= (attrmap **)tagtoattr; mapAttrs = (attrmap *)tagmap0; break; } state = STATE_TEXT; index = base; while (index < (int)nBytes) { elsize = read_utf8_char(pHtml+index,(int)nBytes-index,&c); if (elsize < 1) { lit_error(ERR_R, "Invalid UTF8 character at " "position %d, %d bytes remain.", index, (int)nBytes - index); return E_LIT_BAD_UTF8; } /** printf("\nS: %02d Char: %ld, %lx, %c (Depth = %d)\n", state, c, c, c, depth); **/ switch (state) { case STATE_TEXT: if (!c) { state = STATE_GET_FLAGS; break; } /* Treat as xml:space="preserve" for the moment, * so the first white space ends expansion */ if ((!was_in_text) || (space_enabled)) { space_enabled = 0; if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')) space_enabled++; else was_in_text = 1; } /* Special case, CTRL-K becomes a literal linefeed, * only in
 blocks */
            if (c == '\v') c = '\n'; 

            /* Anything, even whitespace, negates this */
            pending_indent = 0;
               
            status = write_entity(c, htmlwrite, write_data);
            if (status < 0) goto write_error;
            break;
        case STATE_GET_FLAGS:
            if (!c) { state = STATE_TEXT; break; }
            flags = c;
#if 0
/* This is a don't care -- worse that happens is the output will be 
 * wierd. */
            if ((c > 15) || ((c & 0x03) == 0)) {
                lit_error(ERR_R,
"Invalid \"flags\" byte (0x%lx) at position %ld.\n"
"\tA flag byte ranges from 0x01 to 0x0f, except 0x04,0x08,0x0C.\n",
                c, index);
            } 
#endif
#if 0
            {
                char buf[32];
                sprintf(buf,"", 
                    (flags>>7)&1,(flags>>6)&1,
                    (flags>>5)&1,(flags>>4)&1,
                    (flags>>3)&1,(flags>>2)&1,
                    (flags>>1)&1,flags&1, in_censorship); 
                WRITE_STRING(buf);
            }
#endif
            state = STATE_GET_TAG;
            break;
        case STATE_GET_TAG:
            if (!c) { state = STATE_TEXT; }
            else state = STATE_GET_ATTR;
 
            if (flags & FLAG_OPENING) {

                /* Can do pending indents if spaces are enabled... */
                if (space_enabled)
                {
                   if ((!was_in_text) || (flags & (FLAG_BLOCK|FLAG_HEAD)))
                   {
                      pending_indent++;
                   } 
                }
                /* 
                 | 1.Pending indents..
                 | 2. /meta Always generates an 
                 |    extra newline at the beginning -- good thing there
                 |    is a DTD there! 
                 */
                if ((pending_indent) || (html_type == 1))
                {
                    was_indented++;
                    WRITE_CHAR('\n');
                   
#if 1 
                    for (i = 0; i < depth; i++)
                        WRITE_CHAR(' ');  
#endif
                    pending_indent = 0;
                }
                /* This sets up an indent for the next tag. 
                 * This MUST be cancelled as soon as any text happens.
                 * See rule C above */
                if ((flags & FLAG_HEAD) || (flags & FLAG_BLOCK)||
                    (html_type == 1) || (depth == 0))  {
                        pending_indent = 1; 
                }

                tag = c;

                WRITE_CHAR('<'); 

                if (!(flags & FLAG_CLOSING)) is_goingdown = 1; 
                if (tag == 0x8000) {
                    state = STATE_GET_CUSTOM_LENGTH;

                    break; 
                }
                /* Tag is a 1-based index into the atom names list */
                if (flags & FLAG_ATOM) {
                    if (!pAtoms || !tag || (tag > pAtoms->num_atoms)) { 
                        lit_error(ERR_R,
"Error - Custom tag %d at %ld isn't in Atom List (%08lx:%d)\n",
                            tag,index,pAtoms,pAtoms?pAtoms->num_atoms:0);	
                        return -1;
                    }
                    tag_name = pAtoms->atom_names[tag-1];
                    current_map = pAtoms->attrmap;
                }
                else if ((tag < nTagRefs) && (mapTag2Name[tag])) {
                    tag_name = mapTag2Name[tag]; 
                    current_map = mapTag2Attrs[tag];
                } else {
                    tag_name = malloc(20);
                    if (!tag_name) goto malloc_error;
                    dynamic_tag++;
                    sprintf(tag_name, "?%ld?", tag);

                    /* FUTUREFIX - This should be a "warning"?  */
                    lit_error(ERR_R,
"Unknown or unrecognized tag %08lx at position %ld.\n"
"\tCurrent depth is %d. ", tag, index, depth);
                    errors++;
                       
                    current_map = mapTag2Attrs[tag];
                } 
                WRITE_STRING(tag_name);
            } else if (flags & FLAG_CLOSING) {
                if (!depth) {
                    lit_error(ERR_R,
"Unbalanced HTML - extra ending tag at position %ld.",index);
                    return -1;
                }
                lingering_space = space_enabled;
                return ((index + elsize) - base);
            }
            break;
        case STATE_GET_ATTR:
            in_censorship = 0;

            if (!c) {
                if (!is_goingdown)  {
                    /* no need for tag_name anymore */
                    if (dynamic_tag && tag_name) free(tag_name); 
                    tag_name = NULL; 
                    dynamic_tag = 0;
                    WRITE_CHAR(' '); 
                    WRITE_CHAR('/');
                    WRITE_CHAR('>');
                } 
                else  /* is_going_down */
                {
                    WRITE_CHAR('>'); 
 
#if 0
                    WRITE_CHAR('[');
                    WRITE_CHAR('0'+space_enabled);
                    WRITE_CHAR(']');
#endif

                    if ((html_type == 0) && (flags & (FLAG_BLOCK|FLAG_HEAD))) 
                        pending_indent++;

                    if (depth > 1000) {
                        lit_error(ERR_R,
"Attempted to recurse too deeply at position %ld.", index);
                        return -2;
                    }
                    depth++; 
                    status = lit_reconstitute_html(pHtml,index+elsize,
                        (int)nBytes-elsize, html_type, pManifest, pAtoms,
			htmlwrite, write_data);
                      
                    depth--;
                    if (status < 0) return status;
                    index += status;
                    is_goingdown = 0;
                    if (!tag_name) {
                        lit_error(ERR_R,
"Unbalanced HTML - tag ends before it begins at position %ld.", index);
                        return -1;
                    }

                    /* Borrow the space enabled from the recursed routine
                     * until the tag is finished.  At that point, use the
                     * one that was saved... */
                    saved_space_enabled = space_enabled;
                    space_enabled = lingering_space; 

#if 0
                    WRITE_CHAR('(');
                    WRITE_CHAR('0'+space_enabled);
                    WRITE_CHAR('0'+saved_space_enabled);
                    WRITE_CHAR(')');
#endif
                    /* Can safely insert newlines here. 
                     * (Rule D) 
                     * Should only be for block tags though, and
                     * Conveince - don't do this if we were in text. 
                     *  (looks better...) 
                     * ** OOPS ** Seems like I _can't_. 
                     *  (unless rule B applies...) 
                     *  (and 
 be damned!)
                     */
                    if (space_enabled && was_indented  && (!was_in_text))
                    {
                        WRITE_CHAR('\n');
#if 1
                        for (i = 0; i < depth; i++) 
                            WRITE_CHAR(' ');
#endif
                    }
                    WRITE_CHAR('<');
                    WRITE_CHAR('/');
                    WRITE_STRING(tag_name);
                    WRITE_CHAR('>');

                    /* New lines here as well... But only with spaces !*/
                    if ( space_enabled &&
                        ((html_type == 1)||(flags & (FLAG_BLOCK|FLAG_HEAD))))
                    { 
                        pending_indent++;
                    }
                    if (dynamic_tag) free(tag_name); 
                    dynamic_tag = 0;
                    tag_name = NULL;

                    space_enabled = saved_space_enabled;
                }
                was_in_text = 0;
                state = STATE_TEXT;
                break;
            } else {
                i = 0;
                tmp_map = current_map;

                if (c == 0x8000) {
                    state = STATE_GET_ATTR_LENGTH;
                    break;
                }

                while (tmp_map && tmp_map->id)
                {
                    if (tmp_map->id == c)
                        break; 
                    tmp_map++;
                }
                if ((!tmp_map) || (!tmp_map->id))
                {
                    tmp_map = &mapAttrs[0]; 
                    while (tmp_map && tmp_map->id)
                    {
                        if (tmp_map->id == c) break;
                        tmp_map++;
                    }
                }


                if ((!tmp_map) || (!tmp_map->id)) {
                    lit_error(ERR_R,
"Unrecognized attribute (0x%08lx) at position %ld.\n"
"\tCurrently processing tag: \"%s\".  Depth is %d.  ",
                        c, index, tag_name, depth);
                    sprintf(numbuf,"?%ld?",c);
                    WRITE_CHAR(' ');
                    WRITE_STRING(numbuf);
                }
                else if (tmp_map->name[0] == '%') {
                    in_censorship = 1;
                    /* Invisible tag starting */
                    state = STATE_GET_VALUE_LENGTH;
                    break;
                }
                else {   
                    WRITE_CHAR(' ');
                    WRITE_STRING((char *)tmp_map->name);
                }
                if (status < 0) goto write_error;

                WRITE_CHAR('=');

                /* Two special cases. */
                
                if (tmp_map && tmp_map->name && 
                    ((strcmp(tmp_map->name,"href") == 0) || 
                    (strcmp(tmp_map->name,"src") == 0))) {
                    state = STATE_GET_HREF_LENGTH;
                    break;
                }
                state = STATE_GET_VALUE_LENGTH;
            }
            break;
        case STATE_GET_VALUE_LENGTH:
            if (!in_censorship) {
                WRITE_CHAR('\"');
            }
            char_count = (int)c - 1;
            if (!char_count) {
                if (!in_censorship) {
                    WRITE_CHAR('\"'); 
                }
                in_censorship = 0;
                state = STATE_GET_ATTR;
                break;
            }
            state = STATE_GET_VALUE;
            if (c == 0xffff) break;
            if ((char_count < 0) || (char_count > ((int)nBytes - index))) {
                lit_error(ERR_R,
"attribute had invalid length (%ld) at position %ld.",c, index);
                return -1;
            }
            break;
        case STATE_GET_VALUE:
            if (char_count == 0xfffe) {
                if (!in_censorship) {
                    /* Yes! There is no opening quote. */
                    sprintf(numbuf,"%ld\"",c-1);
                    WRITE_STRING(numbuf); 
                }
                in_censorship = 0;
                state = STATE_GET_ATTR; 
            }
            else if (char_count) {
                if (!in_censorship) { 
                    status = write_entity(c, htmlwrite, write_data);
                    if (status < 0) goto write_error; 
                }
                char_count--; 

            }
            if (!char_count) {
                if (!in_censorship) {
                    WRITE_CHAR('\"');
                }
                in_censorship = 0;
                state = STATE_GET_ATTR;
            } 
            break;
        case STATE_GET_CUSTOM_LENGTH:
            char_count = c - 1;
            if ((char_count <= 0) || (char_count > ((int)nBytes - index))) {
                lit_error(ERR_R,
"custom element had invalid length (%ld) at position %ld.", c, index);
                return -1;
            }
            tag_index = 0; 
            tag_name = malloc(char_count+1);
            dynamic_tag++;
            if (!tag_name)  goto malloc_error;
            state = STATE_GET_CUSTOM;
            break;
        case STATE_GET_CUSTOM:

            /* Yes, this throws away data. 
             | I hope that UTF8 isn't valid in tags, otherwise this will
             | result in unexpected behavior. */
            tag_name[tag_index++] = (char)(c&0x7F);
            char_count--;
            if (!char_count) {
                tag_name[tag_index] = '\0';
                WRITE_STRING(tag_name);
                state = STATE_GET_ATTR;
            }
            break;  
        case STATE_GET_ATTR_LENGTH:
            char_count = c - 1;
            if ((char_count <= 0) || (char_count > ((int)nBytes - index))) {
                lit_error(ERR_R,
"custom attribute had invalid length (%ld) at position %ld.",
                c, index);
                return -1;
            }
            WRITE_CHAR(' ');
            state = STATE_GET_CUSTOM_ATTR;
            break;
        case STATE_GET_CUSTOM_ATTR:
            status = write_entity(c, htmlwrite, write_data);
            if (status < 0) goto write_error;
            char_count--;
            if (!char_count) {
                WRITE_CHAR('=');
                state = STATE_GET_VALUE_LENGTH;
            }
            break;
        case STATE_GET_HREF_LENGTH:
            char_count = c - 1;
            if ((char_count <= 0) || (char_count > ((int)nBytes - index))) {
                lit_error(ERR_R,
"HREF tag has invalid length (%ld) at position %ld.\n", c, index);
                return -1;
            }
            href_base = index + elsize;
            state = STATE_GET_HREF;
            break;
        case STATE_GET_HREF:
            char_count--;
            if (!char_count) {
                int href_size;
                U8 * href_value, * hash_ptr, * new_href;

                href_size = (index + elsize) - href_base - 1;
                href_value = malloc(href_size + 1);
                if (!href_value) goto malloc_error;
    
                memcpy(href_value,pHtml + href_base + 1, href_size);
                href_value[href_size] = '\0';

                hash_ptr = strchr(href_value,'#');
                if (hash_ptr) 
                    new_href = lit_lookup_mapping(pManifest, href_value, 
                        hash_ptr - href_value);
                else 
                    new_href = lit_lookup_mapping(pManifest, href_value, 
                        href_size);
                WRITE_CHAR('\"');
                if (new_href) {
                    WRITE_STRING(new_href);
                    if (hash_ptr) {
                        WRITE_STRING(hash_ptr);
                    }

                } else {
                    WRITE_STRING(href_value); 
                }
                WRITE_CHAR('\"');
                free(href_value);
                state = STATE_GET_ATTR;
            }
            break;  
        default:
            lit_error(ERR_R,
"Reached an invalid internal state (%d) at position %ld.\n", state, index);
            return -1;
            break;
        }
        index += elsize;
    } 
    lingering_space = space_enabled;
    return (index - base);

write_error:
    lit_error(ERR_R, "Unexpected write error!");
    /* the htmlwrite routine should return a status value */
    return status;
malloc_error:
    lit_error(ERR_R,"Ran out of memory processing an HTML file!");
    return E_LIT_OUT_OF_MEMORY;
 
} 


/*--[write_entity]------------------------------------------------------------
 |
 | This converts an entity into a string representation fit for display, 
 | and writes it out to the callback.
 | 
 | This function returns the number of chars written
 */
int write_entity(U32 c, HTMLWRITEFCN htmlwrite, void * write_data)
{
    int     i, found;
    int     len, status;
    char    ent_buffer[14], ch;


    found = -1;

    for (i = 0; i < sizeof(entities)/sizeof(entities[0]); i++)
    {
        if (entities[i].id == c) { found = i; break;}
    }
    len = 0;
    if (found > -1)
    {
        ch = '&';
        status = htmlwrite(write_data,&ch, 1);
        if (status < 0) return status;
        len += status;

        status = htmlwrite(write_data,entities[found].name,
            strlen(entities[found].name));
        if (status < 0) return status;
        len += status;
        ch = ';';
        status = htmlwrite(write_data,&ch, 1);
        if (status < 0) return status;
        return (len + status);
    }
    else {
        if (c < 0x80) {
            ent_buffer[0] = (char)(c & 0x7f);
            len = 1;
        }
        else  {
            /* 32 bit value, so assumed to never be more than 10! */
            len = sprintf(ent_buffer,"&#%ld;",c);
        }
        return htmlwrite(write_data,ent_buffer,len);
    }
#if 0
/* I don't know if I should ever be writing out raw UTF8. */
        unsigned long int mask;
        mask = (1 << 11) - 1;
        len = 1;
        while (c & ~mask) {
            len++;
            mask = ((mask << 5) - 1)|(1 << 5);
        }
        status = fputc((0xff << (7 - len)) | (c >> (6*len)),fh);
        if (status < 0) return status;
        while (len) {
            status = fputc(0x80 | ((c >> (6*(--len))) & 0x3F), fh);
            if (status < 0) return status;
        }
    }
#endif
    return 0;
}


/*--[read_utf8_char]-----------------------------------------------------------
 |
 | This reads a single UTF8 character from a data stream, returning the
 | number of bytes consumed (element size) and filling in the Integer 
 | value. 
*/
int  read_utf8_char(U8 * pdata, int nBytes, U32 * pvalue)
{
    U32  c;
    unsigned char mask;
    int elsize, i;

    if (pvalue) *pvalue = -1;

    if (nBytes < 1) return -1;
    c = *(pdata);
    mask = 0x80;
    if (c & mask) {
        elsize = 0;
        while (c & mask) { mask >>= 1; elsize++;}
        if ((mask <= 1) || (mask == 0x40))  return -1;
    } else {
        elsize = 1;
    }

    if (elsize > 1) {
        if ((elsize) > nBytes) { return -1; }
        c &= (mask - 1);
        for (i = 1; i < elsize; i++) {
            if ( (*(pdata + i) & 0xC0) != 0x80)  return -1;
            c = (c << 6) | ( *(pdata + i) & 0x3F );
        }
    }
    if (pvalue) *pvalue = c;
    return elsize;       
}
lib/litheaders.c0000644000175000017500000007622607701607060012350 0ustar  kyzkyz/*--[litheaders.c]-------------------------------------------------------------
 | Copyright (C) 2002  Dan A. Jackson 
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/

/* This file contains routines for reading and writing to the .LIT headers */

#include 
#include 
#include 
#include "litlib.h"
#include "litinternal.h"



int make_secondary_header(lit_file * litfile, int sizeHeaders, 
    dir_type * dir,U8 ** pPtr,int * psize);


/* LIT HEADER STRUCTURE *****************************************************
     +-----------------------------+
     |                             |
     | Primary Header              |
     |                             |
     +-----------------------------+
     | Piece Structures            |
     | -- Offset                   |*-------------------+
     | -- Length                   |*---------------+   |
     |                             |*-----------+   |   |
     |                             |*-------+   |   |   |
     |                             |*---+   |   |   |   |
     +-----------------------------+    |   |   |   |   |
     |                             |    |   |   |   |   |
     | Secondary Header            |    |   |   |   |   |
  +-*|   (Content Length)          |    |   |   |   |   |
  |  +-----------------------------+    |   |   |   |   |
  |                                     |   |   |   |   |
  |                                     |   |   |   |   |
  |  +-----------------------------+    |   |   |   |   |
  |  | Piece 0 Data                |<---+   |   |   |   |
  |  |   Total File Length         |        |   |   |   |
  |  +-----------------------------+        |   |   |   |
  |                                         |   |   |   |
  |                                         |   |   |   |
  |  +-----------------------------+        |   |   |   |
  |  | Piece 1 Data                |<-------+   |   |   |
  |  |   Internal Directory        |            |   |   |
  |  |                             |            |   |   |
  |  +-----------------------------+            |   |   |
  |                                             |   |   |
  |                                             |   |   |
  |  +-----------------------------+            |   |   |
  |  | Piece 2 Data                |<-----------+   |   |
  |  |   Index information for     |                |   |
  |  |   internal directory        |                |   |
  |  +-----------------------------+                |   |
  |                                                 |   |
  |                                                 |   |
  |  +-----------------------------+                |   |
  |  | Piece 3 Data                |<---------------+   |
  |  |   GUID (0A9007C3-7640-D311- |                    |
  |  |         87890000F8105754}   |                    |
  |  +-----------------------------+                    |
  |                                                     |
  |  +-----------------------------+                    |
  |  | Piece 4 Data                |<-------------------+
  |  |   GUID (0A9007C4-7640-D311- |
  |  |         87890000F8105754}   |
  |  +-----------------------------+
  |
  |
  +->+-----------------------------+
     | Section 0                   |
     |                             |
     .                             .
     . ... remainder of the file   .
     .                             .
     |                             |
     |                             |
     +-----------------------------+

**************************************************************************/

/** Primary Header *******************************************************
 +-------------------------------+--------------------------------+
 |                     'ITOLITLS'  signature                      |
 +-------------------------------+--------------------------------+
 | Version                       | Primary Header Length          |
 +-------------------------------+--------------------------------+
 | Number of Pieces              | Secondary Header Length        |
 +-------------------------------+--------------------------------+
 | File GUID (0A9007C1-7640-D311-87890000F8105754}                |
 +                                                                +
 |                                                                |
 +-------------------------------+--------------------------------+
**************************************************************************/

#define PRI_VERSION     8
#define PRI_HDRLEN      12
#define PRI_NUMPIECES   16
#define PRI_SECHDRLEN   20
#define PRI_GUID        24
#define PRI_SIZE        40

static const char * lit_magic_string = "ITOLITLS";

/** Piece structures ******************************************************
 +----------------------------------------------------------------+
 | Piece Offset (Low Dword)      . Piece Offset (High Dword)      |
 +----------------------------------------------------------------+
 | Piece Size (Low Dword)        . Piece Size   (High Dword)      |
 +----------------------------------------------------------------+
**************************************************************************/
#define PIECE_LOW_OFFSET    0
#define PIECE_HIGH_OFFSET   4
#define PIECE_LOW_SIZE      8
#define PIECE_HIGH_SIZE     12
#define PIECE_SIZE          16

/** PIECE 0 of the header -- File size ***********************************
 +-------------------------------+--------------------------------+
 | 0x1FE (Tag)                   | 0 (Version)                    |
 +-------------------------------+--------------------------------+
 | File Length                                                    |
 +-------------------------------+--------------------------------+
 | 0 -- Unknown                                                   |
 +----------------------------------------------------------------+

Version 2 -- Speculation only, because I have no idea how to get the 
"real" file length from this version. 
 +-------------------------------+--------------------------------+
 | 0x1FE (Tag)                   | 2 (Version)                    |
 +-------------------------------+--------------------------------+
 | Content Offset (? Could be a coincidence !)                    |
 +-------------------------------+--------------------------------+
 | 0 -- Unknown                                                   |
 +----------------------------------------------------------------+
 | Offset (points to a version 0, 0x1FE block but smaller length  |
 +----------------------------------------------------------------+
 | Size (0x18), normal for verison 0, 0x1FE block                 |
 +----------------------------------------------------------------+
 | Offset (points to an **empty** IFCM block)                     |
 +----------------------------------------------------------------+
 | Size (0x20), minimum size for an IFCM block)                   |
 +----------------------------------------------------------------+
  
*************************************************************************/

#define SIZEPIECE_TAG       0x1FE
#define SIZEPIECE_SIZE      24
#define SIZEPIECE_VERSION   4
#define SIZEPIECE_FILESIZE  8      


/*** COMMON BLOCK FORMAT **************************************************

 +-------------------------------+--------------------------------+
 | Block Type                    | Block Version                  |
 +-------------------------------+--------------------------------+
 | Block Size (bytes)            |
 +-------------------------------+
*************************************************************************/
#define LIT_BLOCK_TAG   0
#define LIT_BLOCK_VER   4
#define LIT_BLOCK_LEN   8


/*--[lit_i_read_headers]-------------------------------------------------------
 | Here I read the header area of the .LIT file into the working structure.
 | The main information extracted from the header:
 |      Content Offset
 |      Directory Table
 |      Total file length
 |
*/
int lit_i_read_headers(lit_file * litfile)
{
    U8      * header = NULL, * cur_piece = NULL;
    int     r;
    int     i, total_hdr_size;

    /* Primary Header Fields */
    U32     version; 
    int     sec_hdr_len, hdr_len, num_pieces;
    /* Piece information */
    U32     offset;
    int     size;

    if (litfile->filesize < PRI_SIZE) {
        lit_error(ERR_R,"LIT FORMAT ERROR: File is too small. "
            "Is %d bytes, must be at least %d bytes!", litfile->filesize,
            PRI_SIZE);
        return E_LIT_FORMAT_ERROR;
    }

    header = litfile->readat(litfile->file_pointer, 0, PRI_SIZE);
    if (!header) {
        return E_LIT_READ_ERROR;
    }

    if (strncmp(header, lit_magic_string, strlen(lit_magic_string)) != 0) {
        lit_error(ERR_R,"This file does not appear to be a .LIT file.");
        free(header); 
        return E_LIT_FORMAT_ERROR;
    }
 
    version = READ_U32(header+PRI_VERSION);
    hdr_len = READ_INT32(header+PRI_HDRLEN);
    num_pieces = READ_INT32(header+PRI_NUMPIECES);
    sec_hdr_len = READ_INT32(header+PRI_SECHDRLEN);
    if (version != 1) {
        lit_error(ERR_R,
            "I only handle version 1, this file is version %d.",
            (int)version);
        free(header);
        return E_LIT_NEWER_VERSION;
    }
    memcpy(litfile->header_guid, header + PRI_GUID, 16);

    free(header);
    header = NULL;

    total_hdr_size = hdr_len + num_pieces*PIECE_SIZE + sec_hdr_len;
    if ((size_t)total_hdr_size >= litfile->filesize) {
        lit_error(ERR_R, "LIT FORMAT ERROR: File is too small for header.");
        return E_LIT_FORMAT_ERROR;
    }
   
    header = litfile->readat(litfile->file_pointer, 0, total_hdr_size);
    if (!header) return E_LIT_READ_ERROR;
    r = lit_i_read_secondary_header(litfile,
        header+hdr_len+num_pieces*PIECE_SIZE, sec_hdr_len);
    if (r) {
        free(header);
        return r;
    }
    for (i = 0; i < num_pieces; i++)
    {
        if ( READ_U32(header + hdr_len + i*PIECE_SIZE + PIECE_HIGH_OFFSET) ||
             READ_U32(header + hdr_len + i*PIECE_SIZE + PIECE_HIGH_SIZE) ) {
            lit_error(ERR_R," Piece %i has 64bit value -- Offset: %lx:%lx, "
                "Size: %lx:%lx", i,
                READ_U32(header + hdr_len +i*PIECE_SIZE+PIECE_HIGH_OFFSET),
                READ_U32(header + hdr_len +i*PIECE_SIZE+PIECE_LOW_OFFSET),
                READ_U32(header + hdr_len +i*PIECE_SIZE+PIECE_HIGH_SIZE),
                READ_U32(header + hdr_len +i*PIECE_SIZE+PIECE_LOW_SIZE));
            free(header);
            return E_LIT_64BIT_VALUE;
        }
        offset = READ_U32(header + hdr_len + i*PIECE_SIZE + PIECE_LOW_OFFSET);
        size = READ_INT32(header + hdr_len + i*PIECE_SIZE + PIECE_LOW_SIZE);

        if ( (offset+size) > litfile->filesize) {
            lit_error(ERR_R,"LIT FORMAT ERROR:"
                "PIECE %d (Ofs:%lx Size %d) is outside length %d!",
                i, offset, size, (int)litfile->filesize);
            free(header);
            return E_LIT_FORMAT_ERROR;
        }
        cur_piece = litfile->readat(litfile->file_pointer, offset, size);
        if (!cur_piece) {
            free(header);
            return E_LIT_READ_ERROR;
        }
        switch (i)
        {
        case 0:
/* Handling this is too much bother,
 * Version 2 of the header appears to have a second level of piece tables
 * which is just more trouble than it is worth.  
 * "freeLIT" can create files with version 2 for those interested.
 * Since I already know what the length SHOULD be (unless someone mangled
 * the file with an ASCII ftp transfer, and that would break other things), 
 * there is really little reason YET to process this piece.  */
#if 0 
            if ((size < SIZEPIECE_SIZE) ||
                (READ_U32(cur_piece) != SIZEPIECE_TAG))
            {
                lit_error(ERR_R,"LIT FORMAT ERROR:"
                    "PIECE %d has invalid size (%d) or tag value (%08lx).",
                    i, size, READ_U32(cur_piece));
                free(header); 
                free(cur_piece);
                return E_LIT_FORMAT_ERROR;
            }
            if (READ_U32(cur_piece + SIZEPIECE_FILESIZE + 4)) {
                lit_error(ERR_R,"LIT FILE HAS 64-BIT FILE SIZE:  %08lx:%08lx!",
                    READ_U32(cur_piece+SIZEPIECE_FILESIZE+4),
                    READ_U32(cur_piece+SIZEPIECE_FILESIZE));
                free(header);
                free(cur_piece);
                return E_LIT_64BIT_VALUE;
            }
            filesize = READ_U32(cur_piece + SIZEPIECE_FILESIZE);    

            /* Having more bytes isn't a problem, at least not for 
             * this code. MSREADER doesn't like it though.  */
            if (filesize < litfile->filesize) {
                lit_error(ERR_R,"LIT FILE ERROR - "
                    "File thinks it has %d bytes, but I really have %d.",
                    (int)filesize, litfile->filesize); 
                free(header);
                free(cur_piece);
                return E_LIT_FORMAT_ERROR;
            }
#endif

            break;
        case 1:
            if ((READ_U32(cur_piece + 8)!= litfile->entry_chunklen) ||
                (READ_U32(cur_piece + 12)!=litfile->entry_unknown) ) {
                lit_error(ERR_R,"LIT FORMAT ERROR: Secondary header is wrong."
                    "(SecHdr) Len: %lx, Unk: %lx  (Piece) Len: %lx Unk: %lx\n",
                    litfile->entry_chunklen, litfile->entry_unknown,
                    READ_U32(cur_piece + 8),
                    READ_U32(cur_piece + 12));
                return E_LIT_FORMAT_ERROR;
            }
            r = lit_i_read_directory(litfile,cur_piece,size);
            if (r) {
                free(header); free(cur_piece);
                return r;
            }
            break; 
        case 2:
            if ((READ_U32(cur_piece + 8)!= litfile->count_chunklen) ||
                (READ_U32(cur_piece + 12)!=litfile->count_unknown) ) {
                lit_error(ERR_R,"LIT FORMAT ERROR: Secondary header is wrong."
                    "(SecHdr) Len: %lx, Unk: %lx  (Piece) Len: %lx Unk: %lx\n",
                    litfile->count_chunklen, litfile->count_unknown,
                    READ_U32(cur_piece + 8),
                    READ_U32(cur_piece + 12));
                return E_LIT_FORMAT_ERROR;
            }
            /* There is no information I need from this piece */
            break;
        case 3:
            if (size != sizeof(litfile->piece3_guid)) {
                lit_error(ERR_R,"LIT FORMAT ERROR: Piece %d's size is %d, "
                    "expected %d!",i,size, sizeof(litfile->piece3_guid));
                free(header); 
                free(cur_piece);
                return E_LIT_FORMAT_ERROR; 
            }
            memcpy(litfile->piece3_guid, cur_piece, size);
            break;
        case 4:
            if (size != sizeof(litfile->piece4_guid)) {
                lit_error(ERR_R,"LIT FORMAT ERROR: Piece %d's size is %d, "
                    "expected %d!",i,size, sizeof(litfile->piece4_guid));
                free(header); 
                free(cur_piece);
                return E_LIT_FORMAT_ERROR; 
            }
            memcpy(litfile->piece4_guid, cur_piece, size);
            break;
        }
        free(cur_piece);
        cur_piece = NULL;
    }
    free(header);
    header = NULL;

    return 0; 
}

#define WRITE_HDR_PIECE(pointer,size) \
    {\
    WRITE_U32((p + PIECE_LOW_OFFSET), offset);\
    WRITE_U32((p + PIECE_HIGH_OFFSET), 0);\
    WRITE_U32((p + PIECE_LOW_SIZE), size);\
    WRITE_U32((p + PIECE_HIGH_SIZE), 0);\
    status = litfile->writeat(litfile->file_pointer,offset,pointer,\
        size);\
    if (status != size) goto bad;\
    p += PIECE_SIZE;\
    offset += size;\
    }

/*****************************************************************************/
/*--[lit_i_write_headers]------------------------------------------------------
 |
 | This writes out the headers of a .LIT file. 
*/
int lit_i_write_headers(lit_file * litfile)
{
    dir_type    dir;
    int         status;
    U8          * pHdr = NULL, * pSecHdr = NULL, * p;
    int         sizeHdr, offset, sizeHeaders, sizeSecHdr;

    /* this is small enough to fake */
    U8          sizePiece[SIZEPIECE_SIZE]; 
  
    memset(&dir, 0, sizeof(dir));

    status = lit_i_make_directories(litfile,&dir);
    if (status) goto bad;

    sizeHdr = PRI_SIZE + 5*PIECE_SIZE;
    pHdr = (U8 *)malloc( (int)sizeHdr);
    if (!pHdr) {
        lit_error(ERR_W|ERR_LIBC, "Out of memory writing headers (%d).\n", 
            sizeHdr);
        status = E_LIT_OUT_OF_MEMORY;
        goto bad; 
    }
    
    /* Need to figure out everything EXCEPT the secondary header size, 
     | which lit_i_make_secondary_header will know, and the total is the
     | content offset which gets stored in the secondary header */

    sizeHeaders = sizeHdr+SIZEPIECE_SIZE+dir.entry_size+dir.count_size+2*16;

    status = make_secondary_header(litfile,sizeHeaders, &dir,&pSecHdr,
        &sizeSecHdr);
    if (status) goto bad; 

    status = litfile->writeat(litfile->file_pointer,sizeHdr,pSecHdr,sizeSecHdr);
    if (status != sizeSecHdr) goto bad;

    sizeHeaders += sizeSecHdr;

    litfile->content_offset = sizeHeaders;


    p = sizePiece;
    memset(p, 0, SIZEPIECE_SIZE);
    WRITE_U32(p, SIZEPIECE_TAG);
    WRITE_U32(p + SIZEPIECE_FILESIZE, sizeHeaders + dir.total_content_size);

    /* Create Primary header */

    p = pHdr; 
    memcpy(p, lit_magic_string, strlen(lit_magic_string));
    WRITE_U32((p + PRI_VERSION), 1);
    WRITE_U32((p + PRI_HDRLEN),  PRI_SIZE);
    WRITE_U32((p + PRI_NUMPIECES), 5);
    WRITE_U32((p + PRI_SECHDRLEN), sizeSecHdr);
    memcpy(p+PRI_GUID, litfile->header_guid, 16);

    offset = sizeHdr + sizeSecHdr;
    p = pHdr + PRI_SIZE;

    WRITE_HDR_PIECE(sizePiece, SIZEPIECE_SIZE);
    WRITE_HDR_PIECE(dir.entry_ptr,(int)dir.entry_size);
    WRITE_HDR_PIECE(dir.count_ptr,(int)dir.count_size);
    WRITE_HDR_PIECE(litfile->piece3_guid, 16);
    WRITE_HDR_PIECE(litfile->piece4_guid, 16);

    status = litfile->writeat(litfile->file_pointer,0,pHdr,sizeHdr);
    if (status != sizeHdr) goto bad;

    free(pSecHdr);
    free(pHdr);
    lit_i_free_dir_type(&dir);
    return 0;
bad:
    lit_i_free_dir_type(&dir);
    if (pHdr) free(pHdr);
    if (pSecHdr) free(pSecHdr);
    return status;
}

/** SECONDARY HEADER FORMAT *********************************************

 Mostly in "COMMON BLOCK FORMAT" 

 +-------------------------------+--------------------------------+
 | Block Type                    | Block Version                  |
 +-------------------------------+--------------------------------+
 | Block Size (bytes)            |
 +-------------------------------+

 Specifically (Note the first block is missing the type... )

 +-------------------------------+--------------------------------+
 |  Version (2)                  | Block Length (0x98)            |
 +-------------------------------+--------------------------------+ +8
 | Chunk Index of first "AOLI" or "Index Chunk" in FILE           |
 | directory   (0xFFFFFFFF if no index chunk is present)          |
 +----------------------------------------------------------------+ +16
 | 0 -- Unknown                                                   |
 +----------------------------------------------------------------+ +24
 | Chunk index of LAST "AOLL" or "List Chunk" in FILE directory   |
 +----------------------------------------------------------------+ +32
 | 0 -- Unknown                                                   |
 +-------------------------------+--------------------------------+ +40
 | Chunk Size of List Chunks (?) | Unknown (always 2)             |
 | in FILE directory (0x2000)    |                                |
 +-------------------------------+--------------------------------+ +48
 | 0 -- Unknown                  | Depth of FILE directory        |
 |                               | (2 with an AOLI, 1 normally)   |
 +-------------------------------+--------------------------------+ +56
 | 0 -- Unknown                                                   |
 +-------------------------------+--------------------------------+ +64
 | Total number of FILE entries  | 0 -- Unknown                   |
 +-------------------------------+--------------------------------+ +72
 | Chunk Index of first "AOLI" or "Index Chunk" in ENTRY COUNTS   |
 | directory   (0xFFFFFFFF if no index chunk is present)          |
 +----------------------------------------------------------------+ +80
 | 0 -- Unknown                                                   |
 +----------------------------------------------------------------+ +88
 | Chunk index of LAST "AOLL" or "List Chunk" in COUNTS directory |
 +----------------------------------------------------------------+ +96
 | 0 -- Unknown                                                   |
 +-------------------------------+--------------------------------+ +104
 | Chunk Size of List Chunks (?) | Unknown (always 2)             |
 | in COUNTS directory (0x200)   |                                |
 +-------------------------------+--------------------------------+ +112
 | 0 -- Unknown                  | Depth of COUNTS directory      |
 |                               | (2 with an AOLI, 1 normally)   |
 +-------------------------------+--------------------------------+ +120
 | 0 -- Unknown                                                   |
 +-------------------------------+--------------------------------+ +128
 | Total number of COUNTS entries| 0 -- Unknown                   |
 +-------------------------------+--------------------------------+ +136
 | 0x100000 - Unknown, same as in| 0x20000 -- Unknown, same as in |
 | first IFCM of FILE directory  | first IFCM of COUNTS directory |
 +-------------------------------+--------------------------------+ +144
 | 0 -- Unknown                                                   |
 +----------------------------------------------------------------+ +152
 | 'CAOL' - Block Tag            | Block version (2)              |
 +-------------------------------+--------------------------------+
 | Block Length (0x50)           | Unknown (? - MSREADER IGNORES) |
 |                               | (may be a creator ID?)         |
 +-------------------------------+--------------------------------+
 | 0 -- Unknown                  | 0x2000 (Chunk size of Entries) |
 +-------------------------------+--------------------------------+
 | 0x200 (Chunk size of COUNTS)  | 0x100000 (Unknown, see above)  |
 +-------------------------------+--------------------------------+
 | 0x20000 (Unknown, like above) | 0 -- Unknown                   |
 +-------------------------------+--------------------------------+
 | 0 -- Unknown                                                   |
 +-------------------------------+--------------------------------+
 | 'ITSF' - Block Tag            | Block version (4)              |
 +-------------------------------+--------------------------------+
 | Block Length (0x20)           | 0x01 (Unknown)                 |
 +-------------------------------+--------------------------------+
 | Content Offset                                                 |
 +-------------------------------+--------------------------------+
 | Timestamp (?)                 | Windows Language ID (0x409)    |
 +-------------------------------+--------------------------------+
 
*********************************************************************/

#define SECHDR_BLOCK_VER         0
#define SECHDR_BLOCK_LEN         4
#define SECHDR_ENTRY_AOLI_IDX    8
#define SECHDR_ENTRY_LAST_AOLL  24
#define SECHDR_ENTRY_CHUNKLEN   40
#define SECHDR_ENTRY_TWO        44
#define SECHDR_ENTRY_DEPTH      52
#define SECHDR_ENTRY_ENTRIES    64 
#define SECHDR_COUNT_AOLI_IDX   72
#define SECHDR_COUNT_LAST_AOLL  88
#define SECHDR_COUNT_CHUNKLEN  104
#define SECHDR_COUNT_TWO       108
#define SECHDR_COUNT_DEPTH     116
#define SECHDR_COUNT_ENTRIES   128 
#define SECHDR_ENTRY_UNKNOWN   136
#define SECHDR_COUNT_UNKNOWN   140

#define SECHDR_VERSION          2
#define SECHDR_SIZE             152

#define CAOL_TAG                0x4c4f4143
#define CAOL_SIZE               48
#define CAOL_VERSION            2

#define CAOL_CREATOR_ID         12
#define CAOL_ENTRY_CHUNKLEN     20
#define CAOL_COUNT_CHUNKLEN     24
#define CAOL_ENTRY_UNKNOWN      28
#define CAOL_COUNT_UNKNOWN      32

#define ITSF_TAG                0x46535449
#define ITSF_VERSION             4
#define ITSF_SIZE               32
#define ITSF_UNKNOWN            12
#define ITSF_CONTENT_OFFSET     16
#define ITSF_TIMESTAMP          24
#define ITSF_LANGUAGE_ID        28     


/*--[lit_i_read_secondary_header]----------------------------------------------
 |
 | Reads useful fields from the secondary header into the lit_file block.
 | This processes the secondary header as if it were made up of subblocks, 
 | each with their own tag, version and length.  This may not be correct for
 | future versions.
 |
 | Note that a lot of these fields appear redundant, and are unnecessary
 | in processing .LIT files.  Possibly these are vestiges of the storage 
 | layer, or have some meaning on occasional files which haven't been 
 | observed yet. 
*/
int lit_i_read_secondary_header(lit_file * litfile, U8 * hdr, int len)
{
    int  blocklen, offset;
    U32  blocktype, blockver;

    litfile->content_offset = 0;

    /* All the fields in the main header are unnecessary from the simplistic
     * directory parsing methodology. 
     * These include the offsets to the index chunks for faster directory
     * processing, but with less than a hundred entries in most cases, there 
     * is really little point. */
    /* Note special case -- TAG is missing, so use -4 */
    offset = READ_INT32(hdr+LIT_BLOCK_LEN - 4);
    blocktype = 0;
    while (offset < len) {
        blocktype = READ_U32(hdr+offset+LIT_BLOCK_TAG);
        blockver = READ_U32(hdr+offset+LIT_BLOCK_VER);
        blocklen = READ_INT32(hdr+offset+LIT_BLOCK_LEN);
        if (blocklen < 0) break;

        switch (blocktype)
        {
        case CAOL_TAG:
            if (blockver != 2) {
                lit_error(ERR_R, "This file is a newer LIT format -"
                    "I expected CAOL version 2, this is %ld!", 
                    (U32)blockver);
                return E_LIT_NEWER_VERSION;
            }
            litfile->creator_id = READ_U32(hdr+offset+CAOL_CREATOR_ID);
            litfile->entry_chunklen = READ_U32(hdr+offset+CAOL_ENTRY_CHUNKLEN); 
            litfile->count_chunklen = READ_U32(hdr+offset+CAOL_COUNT_CHUNKLEN);
            litfile->entry_unknown  = READ_U32(hdr+offset+CAOL_ENTRY_UNKNOWN);
            litfile->count_unknown  = READ_U32(hdr+offset+CAOL_COUNT_UNKNOWN);

            /* Cheat - point to the embedded ITSF header */
            blocklen = 48;
            break;

        case ITSF_TAG:
            if (blockver != 4) {
                lit_error(ERR_R, "This file is a newer LIT format -"
                    " I expected ITSF version 4, this is %ld!", 
                    blockver);
                return E_LIT_NEWER_VERSION;
            }
            if (READ_U32(hdr+offset+ITSF_CONTENT_OFFSET+4)) {
                lit_error(ERR_R, "This file has a 64-bit content offset!");
                return E_LIT_64BIT_VALUE;
            }
            litfile->timestamp      = READ_U32(hdr+offset+ITSF_TIMESTAMP);
            litfile->language_id    = READ_U32(hdr+offset+ITSF_LANGUAGE_ID);
            litfile->content_offset = READ_U32(hdr+offset+ITSF_CONTENT_OFFSET);
            break;
        }
        offset += blocklen;
    }

    if (offset != len) {
        lit_error(ERR_R,"Secondary header offset %ld "
            "ended up outside length %ld.", (U32)offset, (U32)len);
        return E_LIT_FORMAT_ERROR;
    }

    if (litfile->content_offset == 0) {
        lit_error(ERR_R,"I was unable to figure out the content offset!");
        return E_LIT_FORMAT_ERROR;
    }
    return 0;
}

/*****************************************************************************/
/*--[make_secondary_header]----------------------------------------------------
 |
 | 
*/
int make_secondary_header(lit_file * litfile, int sizeHeaders, 
    dir_type * dir,U8 ** pPtr,int * pSize)
{
    U8    * pSecHdr, *p;
    int   size;

    size = SECHDR_SIZE + CAOL_SIZE + ITSF_SIZE;
    pSecHdr = malloc(size);
    if (!pSecHdr) {
        lit_error(ERR_R|ERR_LIBC,"Unable to malloc %d bytes for headers.\n",
            size);
        return E_LIT_OUT_OF_MEMORY;
    }
    memset(pSecHdr, 0, size);

    p = pSecHdr;
    WRITE_U32(p+SECHDR_BLOCK_VER, SECHDR_VERSION);
    WRITE_U32(p+SECHDR_BLOCK_LEN, SECHDR_SIZE);

    if (dir->entry_aoli_idx == (U32)-1) {
        WRITE_U32(p+SECHDR_ENTRY_AOLI_IDX+4, (U32)-1);
        WRITE_U32(p+SECHDR_ENTRY_DEPTH, 1);
    } else {    
        WRITE_U32(p+SECHDR_ENTRY_AOLI_IDX+4, (U32)0);
        WRITE_U32(p+SECHDR_ENTRY_DEPTH, 2);
    }
    WRITE_U32(p+SECHDR_ENTRY_AOLI_IDX, dir->entry_aoli_idx);

    WRITE_U32(p+SECHDR_ENTRY_LAST_AOLL, dir->entry_last_chunk);
    WRITE_U32(p+SECHDR_ENTRY_CHUNKLEN,  litfile->entry_chunklen);
    WRITE_U32(p+SECHDR_ENTRY_TWO,       2); 
    WRITE_U32(p+SECHDR_ENTRY_ENTRIES,   (U32)dir->num_entries);

    /* COUNT parameters are fixed -- there is no AOLI, and only ONE AOLL */
    WRITE_U32(p+SECHDR_COUNT_AOLI_IDX,      (U32)-1);
    WRITE_U32(p+SECHDR_COUNT_AOLI_IDX+4,    (U32)-1);
    WRITE_U32(p+SECHDR_COUNT_LAST_AOLL,     dir->count_last_chunk);
    WRITE_U32(p+SECHDR_COUNT_CHUNKLEN,      litfile->count_chunklen);
    WRITE_U32(p+SECHDR_COUNT_TWO,           2);
    WRITE_U32(p+SECHDR_COUNT_DEPTH,         1);
    WRITE_U32(p+SECHDR_COUNT_ENTRIES,       dir->num_counts);   
    WRITE_U32(p+SECHDR_ENTRY_UNKNOWN,       litfile->entry_unknown);
    WRITE_U32(p+SECHDR_COUNT_UNKNOWN,       litfile->count_unknown);

    p += SECHDR_SIZE;
    WRITE_U32(p+LIT_BLOCK_TAG,          CAOL_TAG);
    WRITE_U32(p+LIT_BLOCK_VER,          CAOL_VERSION);
    WRITE_U32(p+LIT_BLOCK_LEN,          CAOL_SIZE + ITSF_SIZE);
    
    WRITE_U32(p + CAOL_CREATOR_ID,      litfile->creator_id);
    WRITE_U32(p + CAOL_ENTRY_CHUNKLEN,  litfile->entry_chunklen);
    WRITE_U32(p + CAOL_COUNT_CHUNKLEN,  litfile->count_chunklen);
    WRITE_U32(p + CAOL_ENTRY_UNKNOWN,   litfile->entry_unknown);
    WRITE_U32(p + CAOL_COUNT_UNKNOWN,   litfile->count_unknown);
    
    p += CAOL_SIZE;
    WRITE_U32(p + LIT_BLOCK_TAG,        ITSF_TAG);
    WRITE_U32(p + LIT_BLOCK_VER,        ITSF_VERSION); 
    WRITE_U32(p + LIT_BLOCK_LEN,        ITSF_SIZE); 
    WRITE_U32(p + ITSF_UNKNOWN,         1);
    WRITE_U32(p + ITSF_CONTENT_OFFSET,  sizeHeaders + size);
    WRITE_U32(p + ITSF_TIMESTAMP,       litfile->timestamp);
    WRITE_U32(p + ITSF_LANGUAGE_ID,     litfile->language_id);

    if (pPtr) *pPtr = pSecHdr;
    if (pSize) *pSize = size;
    return 0;
}
lib/litinternal.h0000644000175000017500000000510710051720774012544 0ustar  kyzkyz/*--[litinternal.h]------------------------------------------------------------
 | Copyright (C) 2002 Dan A. Jackson 
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/

/*
 | This file contains definitions for internal routines and structures which
 | the LIT library doesn't wish to expose to the world at large
 */
#ifndef LITINTERNAL_H
#define LITINTERNAL_H

typedef struct dir_type {
    U8      * entry_ptr;
    U32     entry_size;
    U32     entry_last_chunk;
    U8      * count_ptr;
    U32     count_size;
    U32     count_last_chunk;
    int     num_entries;
    int     num_counts;
    U32     entry_aoli_idx;
    U32     total_content_size;
} dir_type;

int lit_i_read_directory(lit_file * litfile, U8 * piece, int piece_size);
int lit_i_make_directories(lit_file * litfile, dir_type * dirtype);
int lit_i_read_headers(lit_file * litfile);
int lit_i_write_headers(lit_file * litfile);
int lit_i_read_secondary_header(lit_file * litfile, U8 * hdr, int size);
int lit_i_cache_section(lit_file * litfile, section_type * pSection );
int lit_i_read_sections(lit_file * litfile );
void lit_i_free_dir_type(dir_type * dirtype);
int lit_i_encrypt_section(lit_file *,char *, U8 * new_key);
int lit_i_encrypt(U8 * ptr, int size, U8 * new_key);

/*
 | This routine will check for the presence of DRM and initialize
 | the DRM level and bookkey */
extern int lit_i_read_drm(lit_file *);

/*
 | This routine will do the decryption with the bookkey, kept off here
 | to get the non-DRM path pure */
extern int lit_i_decrypt(lit_file *, U8 * data_pointer, int size);

/*
 | Utility routines...
 */
char * lit_i_strmerge(const char * first, ...);
U8 * lit_i_read_utf8_string(U8 * p, int remaining, int * size);
int lit_i_utf8_len_to_bytes(U8 * src, int lenSrc, int sizeSrc);

#endif
lib/litlib.c0000644000175000017500000003353210051706444011472 0ustar  kyzkyz/*--[litlib.c]-----------------------------------------------------------------
 | Copyright (C) 2002 Dan A. Jackson 
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/
#include   
#include 
#include 
#include "litlib.h"
#include "litinternal.h"

#ifdef _MSC_VER
#define strncasecmp _strnicmp
#endif


/*****************************************************************************/
/*--[lit_close]----------------------------------------------------------------
 | This routine is responsible for freeing all memory used by the lit_file
 | structure (such as sections, entries, and so forth).
*/
int lit_close(lit_file * litfile)
{
    entry_type  *entry, *next;
    int         i;

    entry = litfile->entry;
    while (entry) {
        if ((entry->data_source == ENTRY_SOURCE_POINTER) &&
            (entry->data_pointer)) {
            free(entry->data_pointer);
        }
        next = entry->next;
        free(entry);
        entry = next;
    }
    litfile->entry = NULL;

    if (litfile->sections) {
        for (i = 0; i < litfile->num_sections; i++) {
            if ((litfile->sections[i].data_pointer) &&
                (litfile->sections[i].data_pointer != (U8 *)-1)) {
                free(litfile->sections[i].data_pointer);
                litfile->sections[i].data_pointer = NULL;
            }
        }
        free(litfile->sections);
        litfile->sections = NULL;
    }
    return 0;
}

/*****************************************************************************/
/*--[lit_read_from_file]-------------------------------------------------------
 |
 | This routine will read enough of the .LIT file structure for subsequent
 | routines to be able to access internal files. 
 | 
 | Currently this involves:
 |      1. Reading the headers
 |      2. Creating the entry list
 |      3. (As a side effect) reading nearly every section
 |      4. Determining the DRM level and verifying the decryption
*/
int lit_read_from_file(lit_file * litfile) 
{
    int     status;


    if (!litfile->readat) {
        lit_error(0,"Internal Error, no read callback supplied!");
        return E_LIT_BAD_STRUCT;
    }
    status = lit_i_read_headers(litfile);
    if (status) return status;

    status = lit_i_read_drm(litfile);
    return status;    
}


/*****************************************************************************/
/*--[lit_clone]----------------------------------------------------------------
 |
 | This routine clones a LIT file, creating a mirror image of the structure
 | with the associated files. 
 |
 | All necessary data pointers are allocated and copied over, so lit_close 
 | the new LIT file won't cause problems.
 |
 | HOWEVER, the entries themselves point to the OLD LIT file structure, and
 | closing the old litfile will severely reduce the usefulness of the new file. 
 | 
*/
int lit_clone(lit_file * litfile, lit_file * newlitfile)
{
    entry_type  *entry,*new,*newnext;

    memset(newlitfile, 0, sizeof(lit_file));

    newlitfile->drmlevel        = litfile->drmlevel;
    memcpy(&newlitfile->bookkey[0],&litfile->bookkey[0],8);
    memcpy(&newlitfile->header_guid[0],&litfile->header_guid[0],16);
    memcpy(&newlitfile->piece3_guid[0],&litfile->piece3_guid[0],16);
    memcpy(&newlitfile->piece4_guid[0],&litfile->piece4_guid[0],16);
    newlitfile->entry_chunklen  = litfile->entry_chunklen;
    newlitfile->count_chunklen  = litfile->count_chunklen;
    newlitfile->language_id     = litfile->language_id;
    newlitfile->creator_id      = litfile->creator_id;
    newlitfile->entry_unknown   = litfile->entry_unknown;
    newlitfile->count_unknown   = litfile->count_unknown;
    newlitfile->timestamp       = litfile->timestamp;

    entry = litfile->entry;
    newnext = NULL;
    while (entry) {
        new = malloc(sizeof(*entry)+entry->namelen+1);
        if (!new) { 
            lit_error(ERR_R|ERR_W,"Unable to clone directory structure -- "
                "out of memory!");
            return E_LIT_OUT_OF_MEMORY;
        }
        memcpy(new, entry, sizeof(*entry)+entry->namelen+1);
        new->data_source = ENTRY_SOURCE_LITFILE;
        new->data_pointer = (void *)litfile;
        if (!newnext) {
            newlitfile->entry = new;
            newnext = new;
        } 
        else {
            newnext->next = new;
            newnext = new;
        }
        entry = entry->next;
    }

    /* Reading the sections is a good idea here so code that uses the new
     | lit file can enumerate them. */
    return lit_i_read_sections(newlitfile);
}


/*--[lit_write_to_file]--------------------------------------------------------
 |
 | This is the externally visible routine to write the specified LIT file
 | to out via the supplied write function.
 | 
*/
int lit_write_to_file(lit_file * litfile) 
{
    int status;
    entry_type * entry;
    U8  * ptr = NULL; 
    int nbytes;

    if (!litfile->writeat) {
        lit_error(0,"Internal Error, no write callback supplied!");
        return E_LIT_BAD_STRUCT;
    }
    status = lit_i_write_headers(litfile);
    if (status) return status;

    entry = litfile->entry;
    while (entry)
    {
        if ((entry->section == 0) && (entry->size))
        {
            if ((entry->namelen == 1) && (entry->name == '/') ) {
                ;
            }
            else {
                status = lit_get_file(litfile,&entry->name,&ptr,&nbytes);
                if (status) return status;

                status = litfile->writeat(litfile->file_pointer,
                    (size_t)entry->offset + litfile->content_offset,
                    ptr, nbytes);
                free(ptr); 
                ptr = NULL;
                if (status != (int)entry->size) {
                    lit_error(ERR_W,
"Error copying files: Tried to write %d (Entry: %ld). Result = %d.",
                        nbytes, (size_t)entry->size, status);     
                    return E_LIT_WRITE_ERROR;
                }
            }
        }
        entry = entry->next;
    }
    return 0;
}

/*--[lit_get_file]-------------------------------------------------------------
 |
 | This routine is responsible for finding an internal filename. 
 | Ie, walk the linked list (created by read_lit_directory) and return 
 | the data (or just report on the presence if the ptr field is null).
 |
 | This routine will recursively call itself if another litfile is involved.
*/
int lit_get_file(lit_file * litfile, const char * name, U8 ** ptr, int * psize)
{
    entry_type  * entry;
    int         match, status;
    static int  depth = 0;
    U8          * newptr;
    section_type * section;

    if (depth > 10) {
        lit_error(ERR_R,"Recursed too deeply in lit_get_file!");
        return E_LIT_INTERNAL_ERROR;
    }
    if (ptr) *ptr = NULL;

    entry = litfile->entry;
    while (entry) {
        if (entry->namelen != (int)strlen(name)) match = -1;
        match = strncasecmp(&entry->name, name, entry->namelen);
        if ( (entry->namelen != (int)strlen(name)) && (!match)) {
            match = -1;
        }
        
        if (!match) break;

        entry = entry->next;
    }
    if (!entry) {
        /* This is only an error if the caller was interested in this 
         * file. Otherwise, its a warning (ie, looking for DRM3 entries in
         * a DRM1 litfile  */
        if (ptr) {
            lit_error(ERR_R,"Unable to find file \"%s\" in directory.",
                name);
        } 
        return E_LIT_FILE_NOT_FOUND;
    }
    if (psize) *psize = (int)(entry->size&0x7FFFFFFF);
    
    /* What are the contents of a 0-length file..? */
    if ((!ptr) || (!entry->size)) return 0; 


    /* Now, have to actually read that direntry */

    if ((entry->data_source == ENTRY_SOURCE_LITFILE) && 
        (entry->data_pointer)) {
        depth++;
        status = lit_get_file((lit_file *)entry->data_pointer,name,ptr,psize);
        depth--;
        return status;
    }
    if (entry->data_source == ENTRY_SOURCE_POINTER)
    {
        U8  * newptr;
        if (!entry->data_pointer) {
            lit_error(ERR_R,"Invalid pointer for file \"%s\".", name);
            return E_LIT_INTERNAL_ERROR;
        }
        newptr = malloc((size_t)entry->size);
        if (!newptr) {
            lit_error(ERR_R|ERR_LIBC,"Out of memory (malloc(%d) failed).",
                (size_t)entry->size);
            return E_LIT_OUT_OF_MEMORY;
        } 
        memcpy(newptr, entry->data_pointer, (size_t)entry->size);
        *ptr = newptr;
        return 0;
    }
    /* this should only leave reading from the current lit file */
    
    if (!entry->section) {
        if ((size_t)entry->size > litfile->filesize) {
            lit_error(ERR_R,
"Internal file had offset (%ld) outside filesize (%ld)!",
                (size_t)entry->size, litfile->filesize);
            return E_LIT_FORMAT_ERROR;
        }
        newptr = litfile->readat(litfile->file_pointer,
            litfile->content_offset+(size_t)entry->offset,(size_t)entry->size);
        if (!newptr) return E_LIT_READ_ERROR; 
        *ptr = newptr;
        return 0;
    } else {
        if (!litfile->sections) {
            status = lit_i_read_sections(litfile);
            if (status) return status;
        }
        if (entry->section > litfile->num_sections) {
            lit_error(ERR_R,"File \"%s\" has section %ld, greater then "
                "num_sections (%d)!\n",
                name, (U32)entry->section, litfile->num_sections);
            return E_LIT_FORMAT_ERROR;
        }

        section = &litfile->sections[entry->section];
 
        if (!section->data_pointer)
        {
            section->data_pointer = (U8 *)-1;
            status = lit_i_cache_section(litfile, section);
            if (status)  {
                section->data_pointer = NULL;
                return status;
            }
        }
        if ((entry->size+entry->offset) > (U64)section->size) {
            lit_error(ERR_R,
"Invalid Directory Entry (\"%.*s\", %d:%ld:%ld) \n"
"\t This is outside Section %d which is only %ld bytes long.",
                (int)entry->namelen, &entry->name, (int)entry->section,
                (U32)entry->offset, (U32)entry->size, (int)entry->section,
                (U32)section->size);
            return E_LIT_FORMAT_ERROR;
        }
        newptr = malloc((size_t)entry->size);
        if (!newptr) {
            lit_error(ERR_R|ERR_LIBC,"Malloc(%d) failed!", (size_t)entry->size);
            return E_LIT_OUT_OF_MEMORY;
        }
        memcpy(newptr,section->data_pointer+(size_t)entry->offset,
            (size_t)entry->size);
        *ptr = newptr;
    }
    return 0;
}


/*--[lit_remove_files]---------------------------------------------------------
 |
 | This removes all files that start with a specified prefix. 
 |
*/
int lit_remove_files(lit_file * litfile, const char * prefix)
{
    entry_type  * entry, * prev;
    int         match;

    entry = litfile->entry;
    prev = entry;
    if (!entry) return 0; 
    entry = entry->next;  /* Cannot remove the '/' entry! */

    while (entry) {
        match = strncasecmp(&entry->name,prefix, strlen(prefix));
        if (match == 0) {
            if ( (entry->data_source == ENTRY_SOURCE_POINTER) && 
                (entry->data_pointer)) {
                free(entry->data_pointer);
            }
            prev->next = entry->next;
            free(entry); 
            entry = prev;  
        }
        prev = entry;
        entry = entry->next;
    }
    return 0;
}


/*--[lit_put_file]-------------------------------------------------------------
 |
 | This puts a new file into the list.
 | This ONLY needs to know the size and the section -- offsets are taken 
 | care of elsewhere (depending on the section).
 |
 | Sections other than 0 won't work at the moment!
 | 
*/
int lit_put_file(lit_file * litfile, const char * name, U8 *ptr, int size, 
    int section)
{
    entry_type  * entry, * prev, * new;
    int         match;

    entry = litfile->entry;
    prev = entry;
    if (!entry) return 0;
    entry = entry->next;  /* Cannot remove the '/' entry! */

    while (entry) {
        if (entry->namelen != (int)strlen(name)) match = -1;
        match = strncasecmp(&entry->name, name, entry->namelen);
        if ( (entry->namelen != (int)strlen(name)) && (!match)) {
            match = -1;
        }

        if (!match) break;
        prev = entry;
        entry = entry->next;
    }
    if (entry) {
        if ((entry->data_source == ENTRY_SOURCE_POINTER) &&
            (entry->data_pointer))  free(entry->data_pointer); 
        entry->data_source = ENTRY_SOURCE_POINTER;
        entry->data_pointer = (void *)ptr;
        entry->size= size;
        entry->section = section;
        /* Everything else is the same! */        
    } else {
        new = malloc(sizeof(entry_type)+strlen(name));
        if (!new) {
            lit_error(ERR_R,"No memory for new entry."); 
            return E_LIT_OUT_OF_MEMORY;
        }
        new->data_source = ENTRY_SOURCE_POINTER;
        new->data_pointer = (void *)ptr;
        new->offset = 0;
        new->size = size;
        new->section = section;
        new->namelen = (int)strlen(name);
        strcpy(&new->name, name);
        new->next = prev->next;
        prev->next = new;
    }
    return 0;
}
lib/litlib.h0000644000175000017500000001647110051501754011477 0ustar  kyzkyz/*****************************************************************************/ /*--[litlib.h]-----------------------------------------------------------------
 | Copyright (C) 2002 Dan A. Jackson 
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/

#ifndef LITLIB_H
#define LITLIB_H

#include "littypes.h"


/* This function supplied by the client */
#define ERR_RW 3
#define ERR_R  1
#define ERR_W  2

#define ERR_LIBC  0x0080
#define ERR_WIN32 0x0100

void lit_error(int what, char * fmt, ...);

/* Return value is the number of chars written */
typedef int (* HTMLWRITEFCN)(void *, U8 *, int);

/* Return value is a "free-able" block of memory */
typedef U8* (* READFCN)(void *,U32, int);

/* Return value is the number of bytes written */
typedef int (* WRITEFCN)(void *,U32, U8 *,int);

/* 
 | this function is called with NULL to initialize DRM5, and then 
 | later with the data to decrypt 
 */
typedef int (* DRM5FCN)(void *,U8 *, int);


typedef struct lit_attr_map {
        unsigned int    id;
        const char * name;
} lit_attr_map;



/* Accesses for atoms continue off the end of this structure */
typedef struct lit_atom_list
{
    int num_attrs;
    lit_attr_map * attrmap;
    int num_atoms;
    char * atom_names[1];
} lit_atom_list; 


#define ENTRY_SOURCE_INVALID    0
#define ENTRY_SOURCE_LITFILE    1
#define ENTRY_SOURCE_POINTER    2

typedef struct entry_type
{
    struct  entry_type * next;

    /* Where this data is coming from */
    int     data_source;

    /* This means different things depending on what the source is --
     * if the source is ENTRY_SOURCE_LITFILE, then this is either NULL 
     * (ie, this is in the current LIT file), or points to a different
     * "lit_file" structure, in which case, reading this entry involves a
     * recursive call with the name with THAT lit_file structure.
     *
     * For ENTRY_SOURCE_POINTER this points directly to the data */
    void    * data_pointer;

    /* What section does this entry belong to. */
    U64     section;

    /* Offset into current file */
    U64     offset;

    /* Byte count of data (not necessarily valid for "ENTRY_SOURCE_LITFILE" */
    U64     size;

    /* Size of name (not including trailing zero */
    int     namelen;

    /* First character of null terminated name. */
    U8      name;

} entry_type;


typedef struct name_mapping {
    U8 * sInternal;
    U8 * sOriginal;
    U8 * sType; 
    U32  offset;   /* Totally redundant, but useful for writing */
} name_mapping;

#define NUM_MANIFEST_MAPS  4
typedef struct manifest_type {
    int num_mappings[NUM_MANIFEST_MAPS];    
    name_mapping * mappings[NUM_MANIFEST_MAPS]; 
} manifest_type;

#define MAX_SECTION_NAME    128
typedef struct section_type 
{
    U8     * data_pointer;
    int    size;
    U8     name[MAX_SECTION_NAME+1];
} section_type;

typedef struct lit_file
{
    /* Abstract pointer to file information, passed into "open" */
    void        * file_pointer;

    /* Function pointer to read from a particular location */
    READFCN     readat;
    /* Function pointer to write at a particular location */
    WRITEFCN    writeat;

    /* The extent of DRM5 support in the library */
    DRM5FCN     drm5_callback;
    void        * drm5_data; 

    /* Total size of the file in bytes */
    U32         filesize;

    /* Offset into the file where content starts */
    U32         content_offset;

    /* Number of entries in the internal directory structure */
    int         num_file_entries;
    /* Pointer to first entry (typically '/') in the internal
     * directory structure */
    entry_type  * entry;

    /* Number of "sections" in the name list -- 
     * Typically there will be 4 sections, which are
     *   0 -  Uncompressed    (LIT format files are in this section)
     *   1 -  MSCompressed    (Not used for these files)
     *   2 -  EbEncryptDS     (The ebook files themselves)
     *   3 -  EbEncryptOnlyDS (Encryption validation strings, mainly)
     */
    int     num_sections;
    section_type * sections;

    /* Currently valid are:
     *      1 - Sealed
     *      3 - Inscribed
     *      5 - Owner Exclusive (Not Handled)
     */
    int     drmlevel;

    /* DES key for the book itself */
    U8      bookkey[8];

    /* GUIDs from the header */
    U8      header_guid[16];
    U8      piece3_guid[16];
    U8      piece4_guid[16];

    /* LIT fields that are mostly fixed.. */
    U32     entry_chunklen; /* usually 0x2000 */
    U32     count_chunklen; /* Usually 0x200  */

    /* Lit informational fields, filled in by a lit creator... */
    U32     language_id;


    /* These fields are uncertain, and will almost definitely be 
     * renamed at some point in the future */
    U32     creator_id;
    U32     entry_unknown;
    U32     count_unknown;
    /* Almost certainly wrong! */
    U32     timestamp;
} lit_file;

#define	DRM_LEVEL_INVALID       -1234


#define E_LIT_BAD_STRUCT        -2000 
#define E_LIT_INTERNAL_ERROR    -2001
#define E_LIT_OUT_OF_MEMORY     -2002
#define E_LIT_NULL_POINTER      -2003

#define E_LIT_READ_ERROR        -2010
#define E_LIT_LZX_ERROR         -2011 
#define E_LIT_WRITE_ERROR       -2012

#define E_LIT_FILE_NOT_FOUND    -2030

#define E_LIT_FORMAT_ERROR      -2100
#define E_LIT_NEWER_VERSION     -2110
#define E_LIT_64BIT_VALUE       -2120
#define E_LIT_DECRYPT_FAIL      -2130
#define E_LIT_BAD_UTF8          -2140
#define E_LIT_NOT_ENOUGH_SPACE  -2150
#define E_LIT_NOT_ENOUGH_BYTES  -2160

#define E_LIT_UNSUPPORTED       -2500

#define E_LIT_DRM_ERROR         -2600 

int lit_change_drm_level(lit_file * litfile, int newlevel, U8 * drm_data,
                    int data_size);
int lit_close(lit_file * litfile);
int lit_clone(lit_file * litfile, lit_file * newlitfile);
int lit_read_from_file(lit_file * litfile);
void lit_free_atoms(lit_atom_list * atoms);
int lit_get_file(lit_file * litfile, const char * name, U8 ** ptr, int * psize);
char *lit_lookup_mapping(manifest_type * pmanifest,U8 *,int size);
int lit_put_file(lit_file * litfile, const char * name, U8 * ptr, int size, 
        int section);
lit_atom_list * lit_read_atoms(lit_file * litfile, char * content_name);
int lit_read_from_file(lit_file * litfile);
int lit_read_manifest(lit_file * litfile, manifest_type * pmanifest);
int lit_remove_files(lit_file *, const char * prefix);
int lit_free_manifest(manifest_type * pmanifest);
int lit_reconstitute_html(U8 * pHtml,int base, int nBytes, int html_type,
        manifest_type * pmanifest, lit_atom_list * pCustomTags,
	HTMLWRITEFCN htmlwrite, void * writedata );
int lit_write_to_file(lit_file * litfile);
#endif
lib/litmanifest.c0000644000175000017500000001671610071650166012540 0ustar  kyzkyz/*--[litmanifest.c]------------------------------------------------------------
 | Copyright (C) 2002 Dan A. Jackson 
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/
/* 
 | This file contains the routines for processing the "/manifest" file
 */
#include 
#include 
#include 
#include "litlib.h"
#include "litinternal.h"

const char * manifest_string = "/manifest";

/*--[lit_free_manifest]--------------------------------------------------------
 | 
 | Here all memory used by a manifest structure is freed.
 | 
 */
int lit_free_manifest(manifest_type * pmanifest)
{
    int i, j;

    for (j = 0; j < 4; j++)
    {
        name_mapping * map;

        if (!pmanifest->mappings[j]) continue;
        map = pmanifest->mappings[j];
        for (i = 0; i < pmanifest->num_mappings[j]; i++)
        {
            if ((map) && (map->sOriginal))
                free(map->sOriginal);
            if ((map) && (map->sInternal))
                free(map->sInternal);
            if ((map) && (map->sType))
                free(map->sType);
            map++;
        }
        free(pmanifest->mappings[j]);
        pmanifest->mappings[j] = NULL;
        pmanifest->num_mappings[j] = 0;
    }
    return 0;
}

/**** "/manifest" format *****************************************************
 
 Lengths APPEAR to be single bytes. These MAY be ENCINTs, but that has not yet 
 been tested (nor have any been seen). All lengths are in UTF8 ELEMENTS, _NOT_ 
 in bytes! 

     BYTE   DirectoryLength(*)
     BYTES  DirectoryName
 
     DWORD  Number of entries in map 0 (HTML files in the spine?)
     For each entry
     DWORD  Offset into section 
     BYTE   ID Length(*)
     BYTES  ID 
     BYTE   HREF Length(*)
     BYTES  HREF
     BYTE   ContentType Length(*)
     BYTES  ContentType

     And so forth, for map 1, 2, 3
     Map 1 - These are additional HTML files (Not in the spine?) 
     Map 2 - Any CSS files
     Map 3 - Image files

 ***************************************************************************/

/*--[lit_read_manifest]--------------------------------------------------------
 |
 | This routine will parse the "/manifest" file, which contains a record of
 | the individual files which made up the .LIT files -- their original names,
 | the names used internally, and the type.
 |
*/
int lit_read_manifest(lit_file * litfile, manifest_type * pmanifest)
{
    U8      *pManifest, *p, * pEnd;
    int     size;
    int     num_files, i;
    int     offset, sizeManifest;
    int     state, status;
    U8      * sOriginal = NULL, * sInternal = NULL, *sContentType = NULL;
    name_mapping * map = NULL;

    status = lit_get_file(litfile,manifest_string,&pManifest,&sizeManifest);
    if (status) return status;

    if (!pmanifest) { return 0; }
    memset(pmanifest, 0, sizeof(manifest_type));

    p = pManifest;
    pEnd = p + sizeManifest;

    while ((pEnd - p) >= 1) {
        /* First, the directory */
        size = *(p++);

        if (!size) break;
        if ((p+size) > pEnd)    goto out_of_range;

        /* I ignore the directory information here, as it is useless for 
         | any purpose I can think of.  
         */
        p += size;

        for (state = 0; state < NUM_MANIFEST_MAPS; state++)
        {
            /* 0 -- HTML files in spine
             * 1 -- HTML files not in spine (??)
             * 2 -- CSS files
             * 3 -- Image files
             */
            size = 4;

            if ((p+size) > pEnd) goto out_of_range;
            num_files = READ_INT32(p);

            p += 4;

            if (!num_files) continue;

            map = malloc(sizeof(name_mapping)*num_files);
            if (!map) {
                lit_error(ERR_LIBC|ERR_R,"malloc(%d) failed for name_mapping!",
                    sizeof(name_mapping)*num_files); 
                free(pManifest);
                return E_LIT_OUT_OF_MEMORY;
            }

            for (i = 0; i < num_files; i++)
            {
                size = 5;
                if ((p+size) > pEnd) goto out_of_range;
                offset = READ_U32(p);
                p+=4;

                /*
                 . This is the internal reference to this file
                 */
                sInternal = lit_i_read_utf8_string(p,(pEnd - p), &size);
                if (!sInternal) goto error;
                p += size;

                /*
                 . Read source filename
                 */
                sOriginal = lit_i_read_utf8_string(p,(pEnd - p), &size);
                if (!sOriginal) goto error;
                p += size;

                /* This is the MIME type, with a trailing 0! */
                /* The MIME type should NEVER be UTF8, but why not.. */
                sContentType = lit_i_read_utf8_string(p,(pEnd - p), &size);
                if (!sContentType) goto error;
                p += size;
#if 0
                printf(".....  %s \"%s\"-- %s\n", sOriginal,
                    sContentType, sInternal ); 
#endif

                p++;
                /* I pray that was a asciiz string and there is not an
                 * optional fourth parameter here. */

                map[i].sOriginal = sOriginal;
                map[i].sInternal = sInternal;
                map[i].sType = sContentType;
                map[i].offset = offset;
                sOriginal = sInternal = sContentType = NULL;
            }
            pmanifest->mappings[state] = map;
            map = NULL;
            pmanifest->num_mappings[state] = num_files;
        }

    }
    free(pManifest); 
    pManifest = NULL;

    if ((pEnd - p)) {
        lit_error(ERR_R, "Data left over in \"/manifest\" (%d bytes)!\n",
            (pEnd - p));
        return E_LIT_FORMAT_ERROR;
    }

    return 0;
out_of_range:
    lit_error(ERR_R,
"LIT Format error: truncated \"/manifest\" (%d out of %d, reading %d)!\n",
                (p - pManifest), sizeManifest, size);
error:
    if (pManifest) free(pManifest);
    if (map) free(map);
    if (sOriginal) free(sOriginal);
    if (sInternal) free(sInternal);
    if (sContentType) free(sContentType);
    return -1;
}


/*--[lit_lookup_mapping]-------------------------------------------------------
 |
 | This walks the manifest mappings to convert the internal name back into
 | the external name.
*/
char * lit_lookup_mapping(manifest_type * pmanifest,U8 * s,int size)
{
    int i, j;

    for (j = 0; j < NUM_MANIFEST_MAPS; j++)
    {
        name_mapping * map;

        if (!pmanifest->mappings[j]) continue;
        map = pmanifest->mappings[j];
        for (i = 0; i < pmanifest->num_mappings[j]; i++)
        {
            if (strncmp(s, map->sInternal, size) == 0)
                return map->sOriginal;
            map++;
        }
    }
    return NULL;
}

lib/litmetatags.c0000644000175000017500000000514507701552064012535 0ustar  kyzkyz
/*--[litmetatags.h]------------------------------------------------------------
 | Copyright (C) 2002 Dan A. Jackson 
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/

/* Note - the '<' in an attribute means that it gets censored.  This 
 | resolves the "issue" of writing out an invalid OPF file.  */

#include "littags.h" 

attrmap meta_attr[] = { 
	{0x0001, "href"},	/* Never seen this, reported anonymously */
	{0x0002, "%never-used"},/* Never seen this either */
	{0x0003, "%guid"},
	{0x0004, "%minimum_level"},
	{0x0005, "%attr5"},
	{0x0006, "id"},
	{0x0007, "href"},
	{0x0008, "media-type"},
	{0x0009, "fallback"},
	{0x000A, "idref"},
	{0x000B, "xmlns:dc"},
	{0x000C, "xmlns:oebpackage"},
	{0x000D, "role"},
	{0x000E, "file-as"},
	{0x000F, "event"},
	{0x0010, "scheme"},
	{0x0011, "title"},
	{0x0012, "type"},
	{0x0013, "unique-identifier"},
	{0x0014, "name"},
	{0x0015, "content"},
        {0x0016, "xml:lang"},
	{0,0}};

char * meta_tagtoname[43] = {
 0,
        "package",
        "dc:Title",
        "dc:Creator",
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        "manifest",
        "item",
        "spine",
        "itemref",
        "metadata",
        "dc-metadata",
        "dc:Subject",
        "dc:Description",
        "dc:Publisher",
        "dc:Contributor",
        "dc:Date",
        "dc:Type",
        "dc:Format",
        "dc:Identifier",
        "dc:Source",
        "dc:Language",
        "dc:Relation",
        "dc:Coverage",
        "dc:Rights",
        "x-metadata",
        "meta",
        "tours",
        "tour",
        "site",
        "guide",
        "reference",
        NULL };

attrmap * meta_tagtoattr[43] = {0};
const int meta_tagcount = 43;
lib/litsections.c0000644000175000017500000004364110071747462012564 0ustar  kyzkyz/*--[litsections.c]------------------------------------------------------------
 | Copyright (C) 2002 Dan A. Jackson
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/

/* This file contains the code to read and manage "sections" 
 |
 | Sections are second-level files-within-a-file-within-a-file structures,
 | so that multiple internal files can be packed together to acheive better
 | compression.  */
#include 
#include 
#include 
#include "litlib.h"
#include "litinternal.h"
#include "lzx.h"

static int decompress_section(lit_file * litfile, char * section_name,
    U8 * pControl, int sizeControl, U8 * pContent, int sizeContent, 
    U8 ** ppUncompressed, int * psizeUncompressed);
static const char * guid2string(U8 * guid);

/* Constant names */
const char * content_tail   = "/Content";
const char * control_tail   = "/ControlData";
const char * namelist_string = "::DataSpace/NameList";
const char * storage_string = "::DataSpace/Storage/";
const char * transform_string = "/Transform/List";
const char * rt_tail  = "/InstanceData/ResetTable";
const char * desencrypt_guid = "{67F6E4A2-60BF-11D3-8540-00C04F58C3CF}";
const char * lzxcompress_guid= "{0A9007C6-4076-11D3-8789-0000F8105754}";
const U32 LZXC_TAG = 0x43585a4c;


/*--[guid2string]--------------------------------------------------------------
 |
 | Careful - uses a static string.  Will be awkward if called the same
 | time in a statement.
 | Assumes GUID is in Little Endian Order
 |
*/
const char * guid2string(U8 * guid)
{
    static char guid_buffer[7+8+4+4+4+12+1];

    sprintf(guid_buffer, "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
        (U32)READ_U32(guid), (U16)READ_U16((guid+4)),
        (U16)READ_U16((guid+6)),
        (U8)*(guid+8), (U8)*(guid+9), /*-*/
        (U8)*(guid+10), (U8)*(guid+11), (U8)*(guid+12),
        (U8)*(guid+13), (U8)*(guid+14),
        (U8)*(guid+15));
   return guid_buffer;
}



/*****************************************************************************

 Encryption ControlData
 +-------------------------------+--------------------------------+
 | Number of dwords following (3)| 0x1729  (Unknown, Tag?)        |
 +-------------------------------+--------------------------------+
 | 1  (Unknown, Constant)        | 0xA5A5  (Unknown, Constant)    |
 +-------------------------------+--------------------------------+

 Compression ControlData
 +-------------------------------+--------------------------------+
 | Number of dwords (always 7)   | 'LZXC'  Tag                    |
 +-------------------------------+--------------------------------+
 | 3 (Unknown, Constant)         | Encoded Window size (15 - 21)  |
 +-------------------------------+--------------------------------+
 | Same as encoded window size   | 0x02 (Unknown - Always constant|
 | See CHMFORMAT for speculation.|                                |
 +-------------------------------+--------------------------------+
 | 0 (Unknown)                                                    |
 +-------------------------------+--------------------------------+

 Reset Table 
 +-------------------------------+--------------------------------+
 | Version (3)                   | Number of reset table entries  |
 +-------------------------------+--------------------------------+
 | Unknown (always 8)            | Header length (0x28)           |
 +-------------------------------+--------------------------------+
 | Uncompressed length                                            |
 +-------------------------------+--------------------------------+
 | Compressed length (or close)                                   |
 +-------------------------------+--------------------------------+
 | Reset Interval                | Padding                        | 
 +-------------------------------+--------------------------------+
 | Reset location (0)                                             |
 +----------------------------------------------------------------+
 ... Successive reset locations follow ....



 ****************************************************************************/
#define LZXC_TAG 0x43585a4c

#define CONTROL_TAG              4
#define CONTROL_WINDOW_SIZE     12

#define RESET_NENTRIES            4
#define RESET_HDRLEN             12
#define RESET_UCLENGTH           16
#define RESET_INTERVAL           32


/*--[lit_i_cache_section]------------------------------------------------------
 |
 | Here is the code to actually read a section from a .LIT file -- this is 
 | fairly ugly, because the section is usually transformed (read encrypted
 | or compressed).  
 | 
*/
int lit_i_cache_section(lit_file * litfile, section_type * pSection )
{
    char        * path = NULL;
    int         status;
    const char  *guid;
    int         idxTransform, idxControl, nbytes;
    U8          * pList = NULL, * pContent = NULL, *ptr = NULL;
    U8          * pControl;
    int         sizeTransform, sizeContent, sizeControl, nDwords;
    
    path = lit_i_strmerge(storage_string,pSection->name,transform_string,0);
    if (!path) { return E_LIT_OUT_OF_MEMORY; }

    status = lit_get_file(litfile, path, &pList,&sizeTransform);
    if (status) goto bad;

    free(path); path = NULL;

    path = lit_i_strmerge(storage_string,pSection->name, content_tail,0);
    if (!path) { return E_LIT_OUT_OF_MEMORY; }

    status = lit_get_file(litfile, path, &pContent,&sizeContent);
    if (status) goto bad;

    free(path); path = NULL;

    path = lit_i_strmerge(storage_string,pSection->name, control_tail,0);
    if (!path) { return E_LIT_OUT_OF_MEMORY; }

    status = lit_get_file(litfile, path, &pControl,&sizeControl);
    if (status) goto bad;

    free(path); path = NULL;

    idxTransform = 0;
    idxControl = 0;
    while ((sizeTransform - idxTransform) >= 16)
    {
        nDwords = READ_INT32(pControl + idxControl) + 1;
        if (((idxControl + nDwords*4) > sizeControl) || (nDwords <= 0)) {
            lit_error(ERR_R,"ControlData is too short! (%d > %d), %d.\n",
                idxControl + nDwords*4, sizeControl, nDwords);
            status = E_LIT_FORMAT_ERROR;
            goto bad;
        }

        guid = guid2string(pList + idxTransform);

        if (strcmp(guid, desencrypt_guid) == 0)
        {
            status = lit_i_decrypt(litfile,pContent, sizeContent); 
            if (status) goto bad;
            idxControl += (nDwords * 4);

        } else if (strcmp(guid, lzxcompress_guid) == 0) {
            status = decompress_section(litfile, pSection->name,
                pControl+idxControl, sizeControl-idxControl,
                pContent, sizeContent, &ptr, &nbytes);
            if (status) goto bad;
            free(pContent);
            pContent = ptr;
            ptr = NULL;
            sizeContent = nbytes;
            idxControl += (nDwords * 4);
        } else {
            lit_error(ERR_R,"Unrecognized transform: \"%s\".", guid);
            status = E_LIT_UNSUPPORTED;
            goto bad;
        }

        idxTransform += 16;
    }
    pSection->data_pointer = pContent;
    pSection->size = sizeContent;

    if (pControl) free(pControl);
    if (path) free(path);
    if (pList) free(pList);
    return 0;
bad:
    if (ptr) free(ptr);
    if (path) free(path);
    if (pList) free(pList);
    if (pContent) free(pContent);
    if (pControl) free(pControl);
    return status;
}


/*--[lit_i_encrypt_section]----------------------------------------------------
 |
 | Encrypts (or re-encrypts) a particular section. 
 | This only works if the section already has an encryption transform in place.
 | 
*/
int lit_i_encrypt_section(lit_file * litfile, char * name, U8 * new_key)
{
    char        * path = NULL;
    int         status;
    const char  *guid;
    U8          * pList = NULL, * pContent = NULL, *ptr = NULL;
    int         sizeTransform, sizeContent, idxTransform;

    path = lit_i_strmerge(storage_string, name, transform_string, 0);
    if (!path) { return E_LIT_OUT_OF_MEMORY; }

    status = lit_get_file(litfile, path, &pList,&sizeTransform);
    if (status) goto bad;
 
    free(path); path = NULL;

    path = lit_i_strmerge(storage_string, name, content_tail, 0);

    status = lit_get_file(litfile, path, &pContent,&sizeContent);
    if (status) goto bad;
 
    idxTransform = 0;
    while ((sizeTransform - idxTransform) >= 16) {
        guid = guid2string(pList + idxTransform);
        if (strcmp(guid, desencrypt_guid) == 0) {
            ptr = malloc(sizeContent); 
            if (!ptr) { 
                lit_error(ERR_W,"Unable to make copy of section data "
                    " for \"%s\"!", name);
                return E_LIT_OUT_OF_MEMORY;
            }
            memcpy(ptr, pContent, sizeContent);
            status = lit_i_decrypt(litfile,ptr, sizeContent);
            if (status) goto bad;
            status = lit_put_file(litfile,path,ptr,sizeContent,0);
            if (status) goto bad;
            lit_i_encrypt(ptr,sizeContent,new_key);



            free(pContent); 
            pContent = NULL;
        } 
        idxTransform += 16;
    }
    if (path) free(path);
    if (pList) free(pList);
    return 0;
bad:
    if (ptr) free(ptr);
    if (path) free(path);
    if (pList) free(pList);
    if (pContent) free(pContent);
    return status;
}


/*--[decompress_section]-------------------------------------------------------
 |
 | A somewhat tricky routine as it handles interfacing with the external 
 | LZX library. There is a reason for using the ResetTable here, as 
 | otherwise certain files will not be correctly handled! 
 | 
 | Generally -- 
 | I need to get the LZXC control information to get the window size.  
 | This lets me initialize the LZX library. 
 |  
 | From there, I read the ResetTable to know the uncompressed length and
 | each "LZXReset()" point.  
 |
 | Note that the LZXReset() that this routine requires is not part of the
 | standard lzx distribution. The version that works with these files is from
 | the chmlib implementation and already has been modified.
*/
int decompress_section(lit_file * litfile, char * section_name,
    U8 * pControl, int sizeControl, U8 * pContent, int sizeContent, 
    U8 ** ppUncompressed, int * psizeUncompressed)
{
    char        *path;
    int         sizeRT, ofsEntry, base, dst, u;
    int         bytesRemaining, uclength, window_bytes, accum, size;
    U8          * ptr, * pRT;
    int         window_size, status;

    if ((sizeControl < 32) || (READ_U32(pControl+CONTROL_TAG) != LZXC_TAG)) {
        lit_error(ERR_R, "Invalid ControlData tag value %08lx should be %08lx!",
            (sizeControl > 8)?READ_U32(pControl+CONTROL_TAG):0, 
            LZXC_TAG);
        return E_LIT_FORMAT_ERROR;
    }

    window_size = 14;
    u = READ_U32(pControl + CONTROL_WINDOW_SIZE); 
    while (u) { 
        u >>= 1; 
        window_size++; 
    }
    if ((window_size < 15) || (window_size > 21)) {
        lit_error(ERR_R, "Invalid window in ControlData - %d from %lx.",
            window_size, READ_U32(pControl+CONTROL_WINDOW_SIZE));
        return -1;
    }

    status = LZXinit(window_size);
    if (status) {
        lit_error(ERR_R, "LZXinit(%d) failed, status = %d.",
            window_size, status);
        return E_LIT_LZX_ERROR;
    } 

    path = lit_i_strmerge(storage_string,section_name,"/Transform/",
      lzxcompress_guid, rt_tail, NULL);
    if (!path) {
        return E_LIT_OUT_OF_MEMORY;
    }
    status = lit_get_file(litfile, path, &pRT, &sizeRT);
    if (status) { free(path); return status;}

    free(path);
    path = NULL;

    if (sizeRT < (RESET_INTERVAL+8)) {
        lit_error(ERR_R, "Reset table is too short (%d bytes).",
            sizeRT);
        free(pRT);
        return E_LIT_FORMAT_ERROR;
    }
    if (READ_U32(pRT + RESET_UCLENGTH + 4)) {
        lit_error(ERR_R,"Reset table has 64bit value for UCLENGTH!");
        free(pRT);
        return E_LIT_64BIT_VALUE;
    }

    /* Skip first entry -- always 0! */
    ofsEntry = READ_INT32(pRT + RESET_HDRLEN) + 8;
    uclength = READ_INT32(pRT + RESET_UCLENGTH);
    accum    = READ_INT32(pRT + RESET_INTERVAL);

    ptr = malloc(uclength+1); 
    /* Check for corruption */
    ptr[uclength] = 0xCC;

    if (!ptr) { 
        lit_error(ERR_R, "Unable to malloc uc length (%d bytes)!",
            uclength); 
        free(pRT);
        return E_LIT_OUT_OF_MEMORY;
    }
    bytesRemaining = uclength;
    window_bytes = (1 << window_size);

    base = 0;
    dst  = 0;

    while (ofsEntry  < sizeRT)
    {
        if (accum == window_bytes) {
            accum = 0;

            size = READ_INT32(pRT + ofsEntry);
            u = READ_INT32(pRT + ofsEntry + 4);
            if (u) {
                lit_error(ERR_R, "Reset table entry greater than 32 bits!");
                free(pRT); free(ptr);
                return E_LIT_64BIT_VALUE;
            }
            if (size >= sizeContent) {
                lit_error(ERR_R, "ResetTable entry out of bounds, %lx. (%d)",
                    size, ofsEntry);
                free(ptr); free(pRT); return E_LIT_FORMAT_ERROR;
            }
            status = 0;
            if (bytesRemaining >= window_bytes) {
                LZXreset();
                status = LZXdecompress(pContent + base, ptr+dst,
                    size - base, window_bytes);
                bytesRemaining -= window_bytes;
                dst += window_bytes;
                base = size;
            }
            if (status) {
                lit_error(ERR_R, "LZXdecompress failed, status = %d.",
                    status);
                free(ptr);
                free(pRT);
                return -1;
            }
        }
        accum += READ_INT32(pRT + RESET_INTERVAL);
        ofsEntry += 8;
    }
    free(pRT);
    if (bytesRemaining < window_bytes) {
        LZXreset();
        status = LZXdecompress(pContent + base, ptr + dst,
            sizeContent - base, bytesRemaining);
        bytesRemaining = 0;
    }
    if (ptr[uclength] != 0xCC)
    {
        lit_error(ERR_R,
"LZXdecompress overflowed memory. (%02x). \n"\
"This is a serious bug, please report this.\n", ptr[uclength]);

        /* Can't really free, anything may be corrupted at this point */
        return -1;
    }
    if (status) {
        lit_error(ERR_R, "LZXdecompress failed, status = %d.", status);
        free(ptr);
        return E_LIT_LZX_ERROR;
    }

    if (bytesRemaining) {
        lit_error(ERR_R, "Failed to completely decompress section! (%d left).",            bytesRemaining);
        free(ptr);
        return E_LIT_LZX_ERROR;
    }

    if (ppUncompressed) *ppUncompressed = ptr;
    else free(ptr);
    if (psizeUncompressed) *psizeUncompressed = uclength;
    return 0;
}


/*--[lit_i_read_sections]------------------------------------------------------
 |
 | This routine reads the directory of "sections" from the 
 | "::DataSpace/NameList" file, and stores them for later. The real work 
 | of reading a section occurs in the "cache_section" routine.
 |
 | NameList format: 
 |      U16     NUMBER OF SECTIONS
 |      U16     Length of section name 0
 |      U16s    (UTF16) section name
 |      U16     Length of section name 1, and so forth
*/
#define MAX_SECTION_NAME    128
int lit_i_read_sections(lit_file * litfile )
{
    U8          *pNamelist;
    int         status;
    int         nbytes, num_sections, size, section, idx;

    status = lit_get_file(litfile, namelist_string, &pNamelist,&nbytes);
    if (status) return status;

    idx = 2;
    size = 2;
    if ((idx + size) > nbytes) goto bad;
    num_sections = READ_U16(pNamelist+idx);
    idx += 2;

    litfile->sections = malloc(num_sections * sizeof(section_type));
    if (!litfile->sections) {
        lit_error(ERR_R|ERR_LIBC,"malloc(%d) failed!\n",
            num_sections*sizeof(section_type));
        free(pNamelist); 
        return E_LIT_OUT_OF_MEMORY;
    }
    memset(litfile->sections, 0, num_sections * sizeof(section_type));

    litfile->num_sections = num_sections;

    for (section = 0; section < num_sections; section++) {
        int j;

        size = 2;
        if ((idx + size) > nbytes) goto bad;
        size = READ_U16(pNamelist + idx);
        idx += 2;

        if (size > MAX_SECTION_NAME) {
            lit_error(ERR_R,
"litlib.c cannot handle a %d byte section name, fix MAX_SECTION_NAME (%d)\n",
                size, MAX_SECTION_NAME);
            return -1;
        }
        size = (size * 2) + 2;

        if ((idx + size) > nbytes) goto bad;

        for (j = 0; j < size; j+=2) {
            litfile->sections[section].name[j/2] = pNamelist[idx + j];
        }
        litfile->sections[section].name[j/2] = '\0';
        idx += size;
    }
    free(pNamelist);
    return 0;
bad:
    lit_error(ERR_R,
        "Invalid or corrupt ::DataSpace/Namelist!\n"
        "\nTried to read %d bytes past length of (%ld)\n", size,nbytes);
    if (pNamelist) free(pNamelist);
    return -1;
}

lib/littags.c0000644000175000017500000004716607604420464011677 0ustar  kyzkyz
/*--[littags.c]----------------------------------------------------------------
 | Copyright (C) 2002  Dan A. Jackson 
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/
#include "littags.h" 

attrmap tagmap0[] = { 
	{0x8010, "tabindex"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x804d, "disabled"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x83fe, "datafld"},
	{0x83ff, "datasrc"},
	{0x8400, "dataformatas"},
	{0x87d6, "accesskey"},
	{0x9392, "lang"},
	{0x93ed, "language"},
	{0x93fe, "dir"},
	{0x9771, "onmouseover"},
	{0x9772, "onmouseout"},
	{0x9773, "onmousedown"},
	{0x9774, "onmouseup"},
	{0x9775, "onmousemove"},
	{0x9776, "onkeydown"},
	{0x9777, "onkeyup"},
	{0x9778, "onkeypress"},
	{0x9779, "onclick"},
	{0x977a, "ondblclick"},
	{0x977e, "onhelp"},
	{0x977f, "onfocus"},
	{0x9780, "onblur"},
	{0x9783, "onrowexit"},
	{0x9784, "onrowenter"},
	{0x9786, "onbeforeupdate"},
	{0x9787, "onafterupdate"},
	{0x978a, "onreadystatechange"},
	{0x9790, "onscroll"},
	{0x9794, "ondragstart"},
	{0x9795, "onresize"},
	{0x9796, "onselectstart"},
	{0x9797, "onerrorupdate"},
	{0x9799, "ondatasetchanged"},
	{0x979a, "ondataavailable"},
	{0x979b, "ondatasetcomplete"},
	{0x979c, "onfilterchange"},
	{0x979f, "onlosecapture"},
	{0x97a0, "onpropertychange"},
	{0x97a2, "ondrag"},
	{0x97a3, "ondragend"},
	{0x97a4, "ondragenter"},
	{0x97a5, "ondragover"},
	{0x97a6, "ondragleave"},
	{0x97a7, "ondrop"},
	{0x97a8, "oncut"},
	{0x97a9, "oncopy"},
	{0x97aa, "onpaste"},
	{0x97ab, "onbeforecut"},
	{0x97ac, "onbeforecopy"},
	{0x97ad, "onbeforepaste"},
	{0x97af, "onrowsdelete"},
	{0x97b0, "onrowsinserted"},
	{0x97b1, "oncellchange"},
	{0x97b2, "oncontextmenu"},
	{0x97b6, "onbeforeeditfocus"},
	{0,0}};
attrmap tagmap3[] = { 
	{0x0001, "href"},
	{0x03ec, "target"},
	{0x03ee, "rel"},
	{0x03ef, "rev"},
	{0x03f0, "urn"},
	{0x03f1, "methods"},
	{0x8001, "name"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap5[] = { 
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap6[] = { 
	{0x8001, "name"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x804a, "align"},
	{0x8bbb, "classid"},
	{0x8bbc, "data"},
	{0x8bbf, "codebase"},
	{0x8bc0, "codetype"},
	{0x8bc1, "code"},
	{0x8bc2, "type"},
	{0x8bc5, "vspace"},
	{0x8bc6, "hspace"},
	{0x978e, "onerror"},
	{0,0}};
attrmap tagmap7[] = { 
	{0x0001, "href"},
	{0x03ea, "shape"},
	{0x03eb, "coords"},
	{0x03ed, "target"},
	{0x03ee, "alt"},
	{0x03ef, "nohref"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap8[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap9[] = { 
	{0x03ec, "href"},
	{0x03ed, "target"},
	{0,0}};
attrmap tagmap10[] = { 
	{0x938b, "color"},
	{0x939b, "face"},
	{0x93a3, "size"},
	{0,0}};
attrmap tagmap12[] = { 
	{0x03ea, "src"},
	{0x03eb, "loop"},
	{0x03ec, "volume"},
	{0x03ed, "balance"},
	{0,0}};
attrmap tagmap13[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap15[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap16[] = { 
	{0x07db, "link"},
	{0x07dc, "alink"},
	{0x07dd, "vlink"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x938a, "background"},
	{0x938b, "text"},
	{0x938e, "nowrap"},
	{0x93ae, "topmargin"},
	{0x93af, "rightmargin"},
	{0x93b0, "bottommargin"},
	{0x93b1, "leftmargin"},
	{0x93b6, "bgproperties"},
	{0x93d8, "scroll"},
	{0x977b, "onselect"},
	{0x9791, "onload"},
	{0x9792, "onunload"},
	{0x9798, "onbeforeunload"},
	{0x97b3, "onbeforeprint"},
	{0x97b4, "onafterprint"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap17[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap18[] = { 
	{0x07d1, "type"},
	{0x8001, "name"},
	{0,0}};
attrmap tagmap19[] = { 
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x93a8, "valign"},
	{0,0}};
attrmap tagmap20[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap21[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap22[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap23[] = { 
	{0x03ea, "span"},
	{0x8006, "width"},
	{0x8049, "align"},
	{0x93a8, "valign"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap24[] = { 
	{0x03ea, "span"},
	{0x8006, "width"},
	{0x8049, "align"},
	{0x93a8, "valign"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap27[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x938e, "nowrap"},
	{0,0}};
attrmap tagmap29[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap31[] = { 
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x938e, "nowrap"},
	{0,0}};
attrmap tagmap32[] = { 
	{0x03ea, "compact"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap33[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x938e, "nowrap"},
	{0,0}};
attrmap tagmap34[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap35[] = { 
	{0x8001, "name"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x804a, "align"},
	{0x8bbd, "palette"},
	{0x8bbe, "pluginspage"},
	{0x8bbf, "codebase"},
	{0x8bbf, "src"},
	{0x8bc1, "units"},
	{0x8bc2, "type"},
	{0x8bc3, "hidden"},
	{0,0}};
attrmap tagmap36[] = { 
	{0x804a, "align"},
	{0,0}};
attrmap tagmap37[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x938b, "color"},
	{0x939b, "face"},
	{0x939c, "size"},
	{0,0}};
attrmap tagmap38[] = { 
	{0x03ea, "action"},
	{0x03ec, "enctype"},
	{0x03ed, "method"},
	{0x03ef, "target"},
	{0x03f4, "accept-charset"},
	{0x8001, "name"},
	{0x977c, "onsubmit"},
	{0x977d, "onreset"},
	{0,0}};
attrmap tagmap39[] = { 
	{0x8000, "align"},
	{0x8001, "name"},
	{0x8bb9, "src"},
	{0x8bbb, "border"},
	{0x8bbc, "frameborder"},
	{0x8bbd, "framespacing"},
	{0x8bbe, "marginwidth"},
	{0x8bbf, "marginheight"},
	{0x8bc0, "noresize"},
	{0x8bc1, "scrolling"},
	{0x8fa2, "bordercolor"},
	{0,0}};
attrmap tagmap40[] = { 
	{0x03e9, "rows"},
	{0x03ea, "cols"},
	{0x03eb, "border"},
	{0x03ec, "bordercolor"},
	{0x03ed, "frameborder"},
	{0x03ee, "framespacing"},
	{0x8001, "name"},
	{0x9791, "onload"},
	{0x9792, "onunload"},
	{0x9798, "onbeforeunload"},
	{0x97b3, "onbeforeprint"},
	{0x97b4, "onafterprint"},
	{0,0}};
attrmap tagmap42[] = { 
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap43[] = { 
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap44[] = { 
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap45[] = { 
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap46[] = { 
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap47[] = { 
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap49[] = { 
	{0x03ea, "noshade"},
	{0x8006, "width"},
	{0x8007, "size"},
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x938b, "color"},
	{0,0}};
attrmap tagmap51[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap52[] = { 
	{0x8001, "name"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x804a, "align"},
	{0x8bb9, "src"},
	{0x8bbb, "border"},
	{0x8bbc, "frameborder"},
	{0x8bbd, "framespacing"},
	{0x8bbe, "marginwidth"},
	{0x8bbf, "marginheight"},
	{0x8bc0, "noresize"},
	{0x8bc1, "scrolling"},
	{0x8fa2, "vspace"},
	{0x8fa3, "hspace"},
	{0,0}};
attrmap tagmap53[] = { 
	{0x03eb, "alt"},
	{0x03ec, "src"},
	{0x03ed, "border"},
	{0x03ee, "vspace"},
	{0x03ef, "hspace"},
	{0x03f0, "lowsrc"},
	{0x03f1, "vrml"},
	{0x03f2, "dynsrc"},
	{0x03f4, "loop"},
	{0x03f6, "start"},
	{0x07d3, "ismap"},
	{0x07d9, "usemap"},
	{0x8001, "name"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x8046, "title"},
	{0x804a, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x978d, "onabort"},
	{0x978e, "onerror"},
	{0x9791, "onload"},
	{0,0}};
attrmap tagmap54[] = { 
	{0x07d1, "type"},
	{0x07d3, "size"},
	{0x07d4, "maxlength"},
	{0x07d6, "readonly"},
	{0x07d8, "indeterminate"},
	{0x07da, "checked"},
	{0x07db, "alt"},
	{0x07dc, "src"},
	{0x07dd, "border"},
	{0x07de, "vspace"},
	{0x07df, "hspace"},
	{0x07e0, "lowsrc"},
	{0x07e1, "vrml"},
	{0x07e2, "dynsrc"},
	{0x07e4, "loop"},
	{0x07e5, "start"},
	{0x8001, "name"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x804a, "align"},
	{0x93ee, "value"},
	{0x977b, "onselect"},
	{0x978d, "onabort"},
	{0x978e, "onerror"},
	{0x978f, "onchange"},
	{0x9791, "onload"},
	{0,0}};
attrmap tagmap56[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap57[] = { 
	{0x03e9, "for"},
	{0,0}};
attrmap tagmap58[] = { 
	{0x804a, "align"},
	{0,0}};
attrmap tagmap59[] = { 
	{0x03ea, "value"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x939a, "type"},
	{0,0}};
attrmap tagmap60[] = { 
	{0x03ee, "href"},
	{0x03ef, "rel"},
	{0x03f0, "rev"},
	{0x03f1, "type"},
	{0x03f9, "media"},
	{0x03fa, "target"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x978e, "onerror"},
	{0x9791, "onload"},
	{0,0}};
attrmap tagmap61[] = { 
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap62[] = { 
	{0x8001, "name"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap63[] = { 
	{0x1771, "scrolldelay"},
	{0x1772, "direction"},
	{0x1773, "behavior"},
	{0x1774, "scrollamount"},
	{0x1775, "loop"},
	{0x1776, "vspace"},
	{0x1777, "hspace"},
	{0x1778, "truespeed"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x9785, "onbounce"},
	{0x978b, "onfinish"},
	{0x978c, "onstart"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap65[] = { 
	{0x03ea, "http-equiv"},
	{0x03eb, "content"},
	{0x03ec, "url"},
	{0x03f6, "charset"},
	{0x8001, "name"},
	{0,0}};
attrmap tagmap66[] = { 
	{0x03f5, "n"},
	{0,0}};
attrmap tagmap71[] = { 
	{0x8000, "border"},
	{0x8000, "usemap"},
	{0x8001, "name"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x8046, "title"},
	{0x804a, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x8bbb, "classid"},
	{0x8bbc, "data"},
	{0x8bbf, "codebase"},
	{0x8bc0, "codetype"},
	{0x8bc1, "code"},
	{0x8bc2, "type"},
	{0x8bc5, "vspace"},
	{0x8bc6, "hspace"},
	{0x978e, "onerror"},
	{0,0}};
attrmap tagmap72[] = { 
	{0x03eb, "compact"},
	{0x03ec, "start"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x939a, "type"},
	{0,0}};
attrmap tagmap73[] = { 
	{0x03ea, "selected"},
	{0x03eb, "value"},
	{0,0}};
attrmap tagmap74[] = { 
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap75[] = { 
	{0x8000, "name"},
	{0x8000, "value"},
	{0x8000, "type"},
	{0,0}};
attrmap tagmap76[] = { 
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap77[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x9399, "clear"},
	{0,0}};
attrmap tagmap78[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap82[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap83[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap84[] = { 
	{0x03ea, "src"},
	{0x03ed, "for"},
	{0x03ee, "event"},
	{0x03f0, "defer"},
	{0x03f2, "type"},
	{0x978e, "onerror"},
	{0,0}};
attrmap tagmap85[] = { 
	{0x03eb, "size"},
	{0x03ec, "multiple"},
	{0x8000, "align"},
	{0x8001, "name"},
	{0x978f, "onchange"},
	{0,0}};
attrmap tagmap86[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap87[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap88[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap89[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap90[] = { 
	{0x03eb, "type"},
	{0x03ef, "media"},
	{0x8046, "title"},
	{0x978e, "onerror"},
	{0x9791, "onload"},
	{0,0}};
attrmap tagmap91[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap92[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap93[] = { 
	{0x03ea, "cols"},
	{0x03eb, "border"},
	{0x03ec, "rules"},
	{0x03ed, "frame"},
	{0x03ee, "cellspacing"},
	{0x03ef, "cellpadding"},
	{0x03fa, "datapagesize"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x8046, "title"},
	{0x804a, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x938a, "background"},
	{0x93a5, "bordercolor"},
	{0x93a6, "bordercolorlight"},
	{0x93a7, "bordercolordark"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap94[] = { 
	{0x8049, "align"},
	{0x93a8, "valign"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap95[] = { 
	{0x8049, "align"},
	{0x93a8, "valign"},
	{0,0}};
attrmap tagmap96[] = { 
	{0x07d2, "rowspan"},
	{0x07d3, "colspan"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x938a, "background"},
	{0x938e, "nowrap"},
	{0x93a5, "bordercolor"},
	{0x93a6, "bordercolorlight"},
	{0x93a7, "bordercolordark"},
	{0x93a8, "valign"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap97[] = { 
	{0x1b5a, "rows"},
	{0x1b5b, "cols"},
	{0x1b5c, "wrap"},
	{0x1b5d, "readonly"},
	{0x8001, "name"},
	{0x977b, "onselect"},
	{0x978f, "onchange"},
	{0,0}};
attrmap tagmap98[] = { 
	{0x8049, "align"},
	{0x93a8, "valign"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap99[] = { 
	{0x07d2, "rowspan"},
	{0x07d3, "colspan"},
	{0x8006, "width"},
	{0x8007, "height"},
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x938a, "background"},
	{0x938e, "nowrap"},
	{0x93a5, "bordercolor"},
	{0x93a6, "bordercolorlight"},
	{0x93a7, "bordercolordark"},
	{0x93a8, "valign"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap100[] = { 
	{0x8049, "align"},
	{0x93a8, "valign"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap102[] = { 
	{0x8007, "height"},
	{0x8046, "title"},
	{0x8049, "align"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x93a5, "bordercolor"},
	{0x93a6, "bordercolorlight"},
	{0x93a7, "bordercolordark"},
	{0x93a8, "valign"},
	{0xfe0c, "bgcolor"},
	{0,0}};
attrmap tagmap103[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap104[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap105[] = { 
	{0x03eb, "compact"},
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0x939a, "type"},
	{0,0}};
attrmap tagmap106[] = { 
	{0x8046, "title"},
	{0x804b, "style"},
	{0x83ea, "class"},
	{0x83eb, "id"},
	{0,0}};
attrmap tagmap108[] = { 
	{0x9399, "clear"},
	{0,0}};
char * tagtoname[109] = { 
 0, 
	NULL,
	NULL,
	"a",
	"acronym",
	"address",
	"applet",
	"area",
	"b",
	"base",
	"basefont",
	"bdo",
	"bgsound",
	"big",
	"blink",
	"blockquote",
	"body",
	"br",
	"button",
	"caption",
	"center",
	"cite",
	"code",
	"col",
	"colgroup",
	NULL,
	NULL,
	"dd",
	"del",
	"dfn",
	"dir",
	"div",
	"dl",
	"dt",
	"em",
	"embed",
	"fieldset",
	"font",
	"form",
	"frame",
	"frameset",
	NULL,
	"h1",
	"h2",
	"h3",
	"h4",
	"h5",
	"h6",
	"head",
	"hr",
	"html",
	"i",
	"iframe",
	"img",
	"input",
	"ins",
	"kbd",
	"label",
	"legend",
	"li",
	"link",
	"tag61",
	"map",
	"tag63",
	"tag64",
	"meta",
	"nextid",
	"nobr",
	"noembed",
	"noframes",
	"noscript",
	"object",
	"ol",
	"option",
	"p",
	"param",
	"plaintext",
	"pre",
	"q",
	"rp",
	"rt",
	"ruby",
	"s",
	"samp",
	"script",
	"select",
	"small",
	"span",
	"strike",
	"strong",
	"style",
	"sub",
	"sup",
	"table",
	"tbody",
	"tc",
	"td",
	"textarea",
	"tfoot",
	"th",
	"thead",
	"title",
	"tr",
	"tt",
	"u",
	"ul",
	"var",
	"wbr",
	NULL };

attrmap * tagtoattr[109] = { 
 0, 
	NULL,
	NULL,
	tagmap3, /* a */ 
	NULL, /* acronym */ 
	tagmap5, /* address */ 
	tagmap6, /* applet */ 
	tagmap7, /* area */ 
	tagmap8, /* b */ 
	tagmap9, /* base */ 
	tagmap10, /* basefont */ 
	NULL, /* bdo */ 
	tagmap12, /* bgsound */ 
	tagmap13, /* big */ 
	NULL, /* blink */ 
	tagmap15, /* blockquote */ 
	tagmap16, /* body */ 
	tagmap17, /* br */ 
	tagmap18, /* button */ 
	tagmap19, /* caption */ 
	tagmap20, /* center */ 
	tagmap21, /* cite */ 
	tagmap22, /* code */ 
	tagmap23, /* col */ 
	tagmap24, /* colgroup */ 
	NULL,
	NULL,
	tagmap27, /* dd */ 
	NULL, /* del */ 
	tagmap29, /* dfn */ 
	NULL, /* dir */ 
	tagmap31, /* div */ 
	tagmap32, /* dl */ 
	tagmap33, /* dt */ 
	tagmap34, /* em */ 
	tagmap35, /* embed */ 
	tagmap36, /* fieldset */ 
	tagmap37, /* font */ 
	tagmap38, /* form */ 
	tagmap39, /* frame */ 
	tagmap40, /* frameset */ 
	NULL,
	tagmap42, /* h1 */ 
	tagmap43, /* h2 */ 
	tagmap44, /* h3 */ 
	tagmap45, /* h4 */ 
	tagmap46, /* h5 */ 
	tagmap47, /* h6 */ 
	NULL, /* head */ 
	tagmap49, /* hr */ 
	NULL, /* html */ 
	tagmap51, /* i */ 
	tagmap52, /* iframe */ 
	tagmap53, /* img */ 
	tagmap54, /* input */ 
	NULL, /* ins */ 
	tagmap56, /* kbd */ 
	tagmap57, /* label */ 
	tagmap58, /* legend */ 
	tagmap59, /* li */ 
	tagmap60, /* link */ 
	tagmap61, /* tag61 */ 
	tagmap62, /* map */ 
	tagmap63, /* tag63 */ 
	NULL, /* tag64 */ 
	tagmap65, /* meta */ 
	tagmap66, /* nextid */ 
	NULL, /* nobr */ 
	NULL, /* noembed */ 
	NULL, /* noframes */ 
	NULL, /* noscript */ 
	tagmap71, /* object */ 
	tagmap72, /* ol */ 
	tagmap73, /* option */ 
	tagmap74, /* p */ 
	tagmap75, /* param */ 
	tagmap76, /* plaintext */ 
	tagmap77, /* pre */ 
	tagmap78, /* q */ 
	NULL, /* rp */ 
	NULL, /* rt */ 
	NULL, /* ruby */ 
	tagmap82, /* s */ 
	tagmap83, /* samp */ 
	tagmap84, /* script */ 
	tagmap85, /* select */ 
	tagmap86, /* small */ 
	tagmap87, /* span */ 
	tagmap88, /* strike */ 
	tagmap89, /* strong */ 
	tagmap90, /* style */ 
	tagmap91, /* sub */ 
	tagmap92, /* sup */ 
	tagmap93, /* table */ 
	tagmap94, /* tbody */ 
	tagmap95, /* tc */ 
	tagmap96, /* td */ 
	tagmap97, /* textarea */ 
	tagmap98, /* tfoot */ 
	tagmap99, /* th */ 
	tagmap100, /* thead */ 
	NULL, /* title */ 
	tagmap102, /* tr */ 
	tagmap103, /* tt */ 
	tagmap104, /* u */ 
	tagmap105, /* ul */ 
	tagmap106, /* var */ 
	NULL, /* wbr */ 
0};

const int tagcount = 109;
lib/littags.h0000644000175000017500000000257010051503040011650 0ustar  kyzkyz/*--[littags.h]----------------------------------------------------------------
 | Copyright (C) 2002 Dan A. Jackson 
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/
#ifndef LITTAGS_H
#define LITTAGS_H

#include   /* need NULL */
#include "litlib.h"

typedef lit_attr_map attrmap;

extern attrmap tagmap0[];
extern const int tagcount;
extern attrmap * tagtoattr[109];
extern char * tagtoname[109];

extern attrmap meta_attr[];
extern const int meta_tagcount;
extern attrmap * meta_tagtoattr[43];
extern char * meta_tagtoname[43];

#endif
lib/littypes.h0000644000175000017500000000416307604420464012100 0ustar  kyzkyz
/*****************************************************************************/
/*--[littypes.h]---------------------------------------------------------------
 | Copyright (C) 2002 Dan A. Jackson 
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/
#ifndef LITTYPES_H
#define LITTYPES_H

typedef     unsigned char       U8;
typedef     unsigned short int  U16;
typedef     unsigned long int   U32;
#ifdef _MSC_VER
typedef     unsigned __int64    U64;
#else
typedef     unsigned long long  U64;
#endif

#define READ_U32(pv) (  (U32)*((U8 *)(pv))                + \
                        ((U32)(*((U8 *)(pv) + 1)) <<  8)  + \
                        ((U32)(*((U8 *)(pv) + 2)) << 16)  + \
                        ((U32)(*((U8 *)(pv) + 3)) << 24)  )

#define READ_INT32(pv)  ((int)(READ_U32(pv)&0x7FFFFFFF))

#define READ_U16(pv) (int)((U16)*((U8 *)(pv))                + \
                           ((U16)(*((U8 *)(pv) + 1)) <<  8)  )
                        

#define WRITE_U32(p,x) ( *((p)+0)=(U8)((x)&0xff),      \
                         *((p)+1)=(U8)(((x)>>8)&0xff), \
                         *((p)+2)=(U8)(((x)>>16)&0xff),\
                         *((p)+3)=(U8)(((x)>>24)&0xff) )

#define WRITE_U16(p,x) ( *((p)+0)=(U8)((x)&0xff),      \
                         *((p)+1)=(U8)(((x)>>8)&0xff)  )

#endif 
lib/litutil.c0000644000175000017500000001002710071177070011672 0ustar  kyzkyz/*--[litlib.c]-----------------------------------------------------------------
 | Copyright (C) 2004 Digital Rights Software
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/
#include   
#include 
#include 
#include 
#include "litlib.h"
#include "litinternal.h"

int lit_i_utf8_len_to_bytes(U8 * src, int lenSrc, int sizeSrc);

/*****************************************************************************/
/*--[strmerge]----------------------------------------------------------------
 | Routine to "safely" merge strings, while reporting errors on failure
 | This keeps merging strings until it finds a NULL.
 | Bad things happens to the user that forgets that final NULL! 
*/
char * lit_i_strmerge(const char * first, ...)
{
    va_list ap;
    const char * s;
    char    * new, *ptr; 
    int     len;

    s = first; 
    len = 0;
    va_start(ap, first);
    do {
        len += strlen(s); 
        s = va_arg(ap, char *);
    } while (s);

    ptr = new = (char *)malloc(len + 1); 
    if (!new) {
        lit_error(0,"Unable to malloc %d bytes for a string.",len);
        return NULL;
    }
    s = first;
    va_start(ap, first);
    do {
        strcpy(ptr, s);
        ptr += strlen(ptr);
        s = va_arg(ap, char *);
    } while (s);
    *(ptr) = '\0';

    va_end(ap);
    return new;
}

/*--[lit_i_read_utf8_string]---------------------------------------------------
 |
 | This routine allocates memory for and copies an UTF8 string from the
 | current location.
*/
U8 * lit_i_read_utf8_string(U8 * p, int remaining, int * size)
{
    U8      * sNew;
    int     len, nbytes;

    len = 1;
    if (remaining < 1) goto out_of_bytes;

    len = *p;

    nbytes = lit_i_utf8_len_to_bytes((p + 1), len, remaining - 1);
    if (nbytes < 0) goto out_of_bytes;

    sNew = (U8 *)malloc(nbytes + 1);
    if (!sNew) {
        lit_error(ERR_R|ERR_LIBC,"malloc(%d) failed!",nbytes+1);
        return NULL;
    }
    if (!sNew) return NULL;
    memcpy( sNew, (p+1), nbytes);
    sNew[nbytes] = '\0';
    if (size) *size = nbytes + 1;

    return sNew;
out_of_bytes:
    lit_error(ERR_R,
"utf8 reader tried to read past an internal file boundary!\n"
"\tTried to read %d characters, but only %d bytes are left.",
        len, remaining);
    return NULL;
}

/*--[lit_i_utf8_len_to_bytes]--------------------------------------------------
 |
 | This converts a count of UTF8 elements to the amount of bytes that it
 | consumes. This is for the parsing of '/manifest' files, where the length
 | field isn't the number of bytes as might be expected.
 |
 | Most other LIT strings (such as the directory) store byte counts for
 | the length.
*/
int lit_i_utf8_len_to_bytes(U8 * src, int lenSrc, int sizeSrc)
{
    int     nchars, nbytes;
    nbytes = 0;
    nchars = 0;
    while (nchars < lenSrc) {
        /* The only thing NOT counted in lenSrc is a character 10xx xxxx */

        if ((*(src + nbytes) & 0xC0) != 0x80) nchars++;
        nbytes++;
        if (nbytes > sizeSrc) {
            return -1;
        }
    }
    /* Copy over any trailing unicode bytes */
    while (( *(src + nbytes) & 0xC0) == 0x80) {
        nbytes++;
        if (nbytes > sizeSrc) {
            return -1;
        }
    }
    return nbytes;
}

lib/des/0000755000175000017500000000000010072003702010602 5ustar  kyzkyzlib/des/d3des.h0000644000175000017500000001076307603152222011774 0ustar  kyzkyz/* d3des.h -
 *
 * Headers and defines for d3des.c
 * Graven Imagery, 1992.
 *
 * THIS SOFTWARE PLACED IN THE PUBLIC DOMAIN BY THE AUTHOUR
 * 920825 19:42 EDST
 *
 * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
 *	(GEnie : OUTER; CIS : [71755,204])
 */
 
#undef D2_DES		
#undef D3_DES		
 
#ifdef D3_DES
#ifndef D2_DES
#define D2_DES		/* D2_DES is needed for D3_DES */
#endif
#endif
	
#define EN0	0		/* MODE == encrypt */
#define DE1	1		/* MODE == decrypt */
 
/* Useful on 68000-ish machines, but NOT USED here. */
 
typedef union {
	unsigned long blok[2];
	unsigned short word[4];
	unsigned char byte[8];
	} M68K;
 
typedef union {
	unsigned long dblok[4];
	unsigned short dword[8];
	unsigned char dbyte[16];
	} M68K2;
 
extern void deskey(unsigned char *, short);
/*		      hexkey[8]     MODE
 * Sets the internal key register according to the hexadecimal
 * key contained in the 8 bytes of hexkey, according to the DES,
 * for encryption or decryption according to MODE.
 */
 
extern void usekey(unsigned long *);
/*		    cookedkey[32]
 * Loads the internal key register with the data in cookedkey.
 */
 
extern void cpkey(unsigned long *);
/*		   cookedkey[32]
 * Copies the contents of the internal key register into the storage
 * located at &cookedkey[0].
 */
 
extern void des(unsigned char *, unsigned char *);
/*		    from[8]	      to[8]
 * Encrypts/Decrypts (according to the key currently loaded in the
 * internal key register) one block of eight bytes at address 'from'
 * into the block at address 'to'.  They can be the same.
 */
 
#ifdef D2_DES
 
#define desDkey(a,b)	des2key((a),(b))
extern void des2key(unsigned char *, short);
/*		      hexkey[16]     MODE
 * Sets the internal key registerS according to the hexadecimal
 * keyS contained in the 16 bytes of hexkey, according to the DES,
 * for DOUBLE encryption or decryption according to MODE.
 * NOTE: this clobbers all three key registers!
 */
 
extern void Ddes(unsigned char *, unsigned char *);
/*		    from[8]	      to[8]
 * Encrypts/Decrypts (according to the keyS currently loaded in the
 * internal key registerS) one block of eight bytes at address 'from'
 * into the block at address 'to'.  They can be the same.
 */
 
extern void D2des(unsigned char *, unsigned char *);
/*		    from[16]	      to[16]
 * Encrypts/Decrypts (according to the keyS currently loaded in the
 * internal key registerS) one block of SIXTEEN bytes at address 'from'
 * into the block at address 'to'.  They can be the same.
 */
 
extern void makekey(char *, unsigned char *);
/*		*password,	single-length key[8]
 * With a double-length default key, this routine hashes a NULL-terminated
 * string into an eight-byte random-looking key, suitable for use with the
 * deskey() routine.
 */
 
#define makeDkey(a,b)	make2key((a),(b))
extern void make2key(char *, unsigned char *);
/*		*password,	double-length key[16]
 * With a double-length default key, this routine hashes a NULL-terminated
 * string into a sixteen-byte random-looking key, suitable for use with the
 * des2key() routine.
 */
 
#ifndef D3_DES	/* D2_DES only */
 
#define useDkey(a)	use2key((a))
#define cpDkey(a)	cp2key((a))
 
extern void use2key(unsigned long *);
/*		    cookedkey[64]
 * Loads the internal key registerS with the data in cookedkey.
 * NOTE: this clobbers all three key registers!
 */
 
extern voi  cp2key(unsigned long *);
/*		   cookedkey[64]
 * Copies the contents of the internal key registerS into the storage
 * located at &cookedkey[0].
 */
 
#else	/* D3_DES too */
 
#define useDkey(a)	use3key((a))
#define cpDkey(a)	cp3key((a))
 
extern void des3key(unsigned char *, short);
/*		      hexkey[24]     MODE
 * Sets the internal key registerS according to the hexadecimal
 * keyS contained in the 24 bytes of hexkey, according to the DES,
 * for DOUBLE encryption or decryption according to MODE.
 */
 
extern void use3key(unsigned long *);
/*		    cookedkey[96]
 * Loads the 3 internal key registerS with the data in cookedkey.
 */
 
extern void cp3key(unsigned long *);
/*		   cookedkey[96]
 * Copies the contents of the 3 internal key registerS into the storage
 * located at &cookedkey[0].
 */
 
extern void make3key(char *, unsigned char *);
/*		*password,	triple-length key[24]
 * With a triple-length default key, this routine hashes a NULL-terminated
 * string into a twenty-four-byte random-looking key, suitable for use with
 * the des3key() routine.
 */
 
#endif	/* D3_DES */
#endif	/* D2_DES */
 
/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
 ********************************************************************/

lib/des/des.c0000644000175000017500000004674207603152212011545 0ustar  kyzkyz/* D3DES (V5.09) - 
 *
 * A portable, public domain, version of the Data Encryption Standard.
 *
 * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
 * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
 * code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
 * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
 * for humouring me on. 
 *
 * THIS SOFTWARE PLACED IN THE PUBLIC DOMAIN BY THE AUTHOUR
 * 920825 19:42 EDST
 *
 * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
 * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
 */
 
#include "d3des.h"
 
static void scrunch(unsigned char *, unsigned long *);
static void unscrun(unsigned long *, unsigned char *);
static void desfunc(unsigned long *, unsigned long *);
static void cookey(unsigned long *);
 
static unsigned long KnL[32] = { 0L };
/*
static unsigned long KnR[32] = { 0L };
static unsigned long Kn3[32] = { 0L };
static unsigned char Df_Key[24] = {
	0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
	0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
	0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 };
*/
 
static unsigned short bytebit[8]	= {
	0200, 0100, 040, 020, 010, 04, 02, 01 };
 
static unsigned long bigbyte[24] = {
	0x800000L,	0x400000L,	0x200000L, 	0x100000L,
	0x80000L,	0x40000L,	0x20000L,	0x10000L,
	0x8000L,	0x4000L,	0x2000L,	0x1000L,
	0x800L,		0x400L,		0x200L,		0x100L,
	0x80L,		0x40L,		0x20L,		0x10L,
	0x8L,		0x4L,		0x2L,		0x1L	};
 
/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
 
static unsigned char pc1[56] = {
	56, 48, 40, 32, 24, 16,  8,	 0, 57, 49, 41, 33, 25, 17,
	 9,  1, 58, 50, 42, 34, 26,	18, 10,  2, 59, 51, 43, 35,
	62, 54, 46, 38, 30, 22, 14,	 6, 61, 53, 45, 37, 29, 21,
	13,  5, 60, 52, 44, 36, 28,	20, 12,  4, 27, 19, 11,  3 };
 
static unsigned char totrot[16] = {
	1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
 
static unsigned char pc2[48] = {
	13, 16, 10, 23,  0,  4,	 2, 27, 14,  5, 20,  9,
	22, 18, 11,  3, 25,  7,	15,  6, 26, 19, 12,  1,
	40, 51, 30, 36, 46, 54,	29, 39, 50, 44, 32, 47,
	43, 48, 38, 55, 33, 52,	45, 41, 49, 35, 28, 31 };
 
void deskey(key, edf)	/* Thanks to James Gillogly & Phil Karn! */
unsigned char *key;
short edf;
{
	int i, j, l, m, n;
	unsigned char pc1m[56], pcr[56];
	unsigned long kn[32];
 
	for ( j = 0; j < 56; j++ ) {
		l = pc1[j];
		m = l & 07;
		pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
		}
	for( i = 0; i < 16; i++ ) {
		if( edf == DE1 ) m = (15 - i) << 1;
		else m = i << 1;
		n = m + 1;
		kn[m] = kn[n] = 0L;
		for( j = 0; j < 28; j++ ) {
			l = j + totrot[i];
			if( l < 28 ) pcr[j] = pc1m[l];
			else pcr[j] = pc1m[l - 28];
			}
		for( j = 28; j < 56; j++ ) {
		    l = j + totrot[i];
		    if( l < 56 ) pcr[j] = pc1m[l];
		    else pcr[j] = pc1m[l - 28];
		    }
		for( j = 0; j < 24; j++ ) {
			if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
			if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
			}
		}
	cookey(kn);
	return;
	}
 
static void cookey(raw1)
unsigned long *raw1;
{
	unsigned long *cook, *raw0;
	unsigned long dough[32];
	int i;
 
	cook = dough;
	for( i = 0; i < 16; i++, raw1++ ) {
		raw0 = raw1++;
		*cook	 = (*raw0 & 0x00fc0000L) << 6;
		*cook	|= (*raw0 & 0x00000fc0L) << 10;
		*cook	|= (*raw1 & 0x00fc0000L) >> 10;
		*cook++	|= (*raw1 & 0x00000fc0L) >> 6;
		*cook	 = (*raw0 & 0x0003f000L) << 12;
		*cook	|= (*raw0 & 0x0000003fL) << 16;
		*cook	|= (*raw1 & 0x0003f000L) >> 4;
		*cook++	|= (*raw1 & 0x0000003fL);
		}
	usekey(dough);
	return;
	}
 
void cpkey(into)
unsigned long *into;
{
	unsigned long *from, *endp;
 
	from = KnL, endp = &KnL[32];
	while( from < endp ) *into++ = *from++;
	return;
	}
 
void usekey(from)
unsigned long *from;
{
	unsigned long *to, *endp;
 
	to = KnL, endp = &KnL[32];
	while( to < endp ) *to++ = *from++;
	return;
	}
 
void des(inblock, outblock)
unsigned char *inblock, *outblock;
{
	unsigned long work[2];
 
	scrunch(inblock, work);
	desfunc(work, KnL);
	unscrun(work, outblock);
	return;
	}
 

static void scrunch(outof, into)
unsigned char *outof;
unsigned long *into;
{
	*into 	 = (*outof++ & 0xffL) << 24;
	*into 	|= (*outof++ & 0xffL) << 16;
	*into 	|= (*outof++ & 0xffL) << 8;
	*into++ |= (*outof++ & 0xffL);
	*into 	 = (*outof++ & 0xffL) << 24;
	*into 	|= (*outof++ & 0xffL) << 16;
	*into 	|= (*outof++ & 0xffL) << 8;
	*into	|= (*outof   & 0xffL);
	return;
	}

 
static void unscrun(outof, into)
unsigned long *outof;
unsigned char *into;
{
	*into++ = (*outof >> 24) & 0xffL;
	*into++ = (*outof >> 16) & 0xffL;
	*into++ = (*outof >>  8) & 0xffL;
	*into++ =  *outof++	 & 0xffL;
	*into++ = (*outof >> 24) & 0xffL;
	*into++ = (*outof >> 16) & 0xffL;
	*into++ = (*outof >>  8) & 0xffL;
	*into   =  *outof	 & 0xffL;
	return;
	}

#include "spr.h"
/* 
static unsigned long SP1[64] = {
	0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
	0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
	0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
	0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
	0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
	0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
	0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
	0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
	0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
	0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
	0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
	0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
	0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
	0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
	0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
	0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
 
static unsigned long SP2[64] = {
	0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
	0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
	0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
	0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
	0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
	0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
	0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
	0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
	0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
	0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
	0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
	0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
	0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
	0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
	0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
	0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
 
static unsigned long SP3[64] = {
	0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
	0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
	0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
	0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
	0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
	0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
	0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
	0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
	0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
	0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
	0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
	0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
	0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
	0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
	0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
	0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
 
static unsigned long SP4[64] = {
	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
	0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
	0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
	0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
	0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
	0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
	0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
	0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
	0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
	0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
	0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
	0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
	0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
	0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
	0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
 
static unsigned long SP5[64] = {
	0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
	0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
	0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
	0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
	0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
	0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
	0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
	0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
	0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
	0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
	0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
	0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
	0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
	0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
	0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
	0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
 
static unsigned long SP6[64] = {
	0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
	0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
	0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
	0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
	0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
	0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
	0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
	0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
	0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
	0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
	0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
	0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
	0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
	0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
	0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
	0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
 
static unsigned long SP7[64] = {
	0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
	0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
	0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
	0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
	0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
	0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
	0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
	0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
	0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
	0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
	0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
	0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
	0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
	0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
	0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
	0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
 
static unsigned long SP8[64] = {
	0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
	0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
	0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
	0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
	0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
	0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
	0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
	0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
	0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
	0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
	0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
	0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
	0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
	0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
	0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
	0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
*/

 
static void desfunc(block, keys)
unsigned long *block, *keys;
{
	unsigned long fval, work, right, leftt;
	int round;
	
	leftt = block[0];
	right = block[1];
	work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
	right ^= work;
	leftt ^= (work << 4);
	work = ((leftt >> 16) ^ right) & 0x0000ffffL;
	right ^= work;
	leftt ^= (work << 16);
	work = ((right >> 2) ^ leftt) & 0x33333333L;
	leftt ^= work;
	right ^= (work << 2);
	work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
	leftt ^= work;
	right ^= (work << 8);
	right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
	work = (leftt ^ right) & 0xaaaaaaaaL;
	leftt ^= work;
	right ^= work;
	leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
	
	for( round = 0; round < 8; round++ ) {
		work  = (right << 28) | (right >> 4);
		work ^= *keys++;
		fval  = SP7[ work		 & 0x3fL];
		fval |= SP5[(work >>  8) & 0x3fL];
		fval |= SP3[(work >> 16) & 0x3fL];
		fval |= SP1[(work >> 24) & 0x3fL];
		work  = right ^ *keys++;
		fval |= SP8[ work		 & 0x3fL];
		fval |= SP6[(work >>  8) & 0x3fL];
		fval |= SP4[(work >> 16) & 0x3fL];
		fval |= SP2[(work >> 24) & 0x3fL];
		leftt ^= fval;
		work  = (leftt << 28) | (leftt >> 4);
		work ^= *keys++;
		fval  = SP7[ work		 & 0x3fL];
		fval |= SP5[(work >>  8) & 0x3fL];
		fval |= SP3[(work >> 16) & 0x3fL];
		fval |= SP1[(work >> 24) & 0x3fL];
		work  = leftt ^ *keys++;
		fval |= SP8[ work		 & 0x3fL];
		fval |= SP6[(work >>  8) & 0x3fL];
		fval |= SP4[(work >> 16) & 0x3fL];
		fval |= SP2[(work >> 24) & 0x3fL];
		right ^= fval;
		}
		
	right = (right << 31) | (right >> 1);
	work = (leftt ^ right) & 0xaaaaaaaaL;
	leftt ^= work;
	right ^= work;
	leftt = (leftt << 31) | (leftt >> 1);
	work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
	right ^= work;
	leftt ^= (work << 8);
	work = ((leftt >> 2) ^ right) & 0x33333333L;
	right ^= work;
	leftt ^= (work << 2);
	work = ((right >> 16) ^ leftt) & 0x0000ffffL;
	leftt ^= work;
	right ^= (work << 16);
	work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
	leftt ^= work;
	right ^= (work << 4);
	*block++ = right;
	*block = leftt;
	return;
	}
 
#ifdef D2_DES
 
void des2key(hexkey, mode)		/* stomps on Kn3 too */
unsigned char *hexkey;			/* unsigned char[16] */
short mode;
{
	short revmod;
 
	revmod = (mode == EN0) ? DE1 : EN0;
	deskey(&hexkey[8], revmod);
	cpkey(KnR);
	deskey(hexkey, mode);
	cpkey(Kn3);					/* Kn3 = KnL */
	return;
	}
	
void Ddes(from, into)
unsigned char *from, *into;		/* unsigned char[8] */
{
	unsigned long work[2];
 
	scrunch(from, work);
	desfunc(work, KnL);
	desfunc(work, KnR);
	desfunc(work, Kn3);
	unscrun(work, into);
	return;
	}
 
void D2des(from, into)
unsigned char *from;			/* unsigned char[16] */
unsigned char *into;			/* unsigned char[16] */
{
	unsigned long *right, *l1, swap;
	unsigned long leftt[2], bufR[2];
	
	right = bufR;
	l1 = &leftt[1];
	scrunch(from, leftt);
	scrunch(&from[8], right);
	desfunc(leftt, KnL);
	desfunc(right, KnL);
	swap = *l1;
	*l1 = *right;
	*right = swap;
	desfunc(leftt, KnR);
	desfunc(right, KnR);
	swap = *l1;
	*l1 = *right;
	*right = swap;
	desfunc(leftt, Kn3);
	desfunc(right, Kn3);
	unscrun(leftt, into);
	unscrun(right, &into[8]);
	return;
	}
 
void makekey(aptr, kptr)
char *aptr;				/* NULL-terminated  */
unsigned char *kptr;		/* unsigned char[8] */
{
	unsigned char *store;
	int first, i;
	unsigned long savek[96];
 
	cpDkey(savek);
	des2key(Df_Key, EN0);
	for( i = 0; i < 8; i++ ) kptr[i] = Df_Key[i];
	first = 1;
	while( (*aptr != '\0') || first ) {
		store = kptr;
		for( i = 0; i < 8 && (*aptr != '\0'); i++ ) {
			*store++ ^= *aptr & 0x7f;
			*aptr++ = '\0';
			}
		Ddes(kptr, kptr);
		first = 0;
		}
	useDkey(savek);
	return;
	}
 
void make2key(aptr, kptr)
char *aptr;				/* NULL-terminated   */
unsigned char *kptr;		/* unsigned char[16] */
{
	unsigned char *store;
	int first, i;
	unsigned long savek[96];
 
	cpDkey(savek);
	des2key(Df_Key, EN0);
	for( i = 0; i < 16; i++ ) kptr[i] = Df_Key[i];
	first = 1;
	while( (*aptr != '\0') || first ) {
		store = kptr;
		for( i = 0; i < 16 && (*aptr != '\0'); i++ ) {
			*store++ ^= *aptr & 0x7f;
			*aptr++ = '\0';
			}
		D2des(kptr, kptr);
		first = 0;
		}
	useDkey(savek);
	return;
	}
 
#ifndef D3_DES	/* D2_DES only */
 
void cp2key(into)
unsigned long *into;	/* unsigned long[64] */
{
	unsigned long *from, *endp;
 
	cpkey(into);
	into = &into[32];
	from = KnR, endp = &KnR[32];
	while( from < endp ) *into++ = *from++;
	return;
	}
 
void use2key(from)				/* stomps on Kn3 too */
unsigned long *from;	/* unsigned long[64] */
{
	unsigned long *to, *endp;
 
	usekey(from);
	from = &from[32];
	to = KnR, endp = &KnR[32];
	while( to < endp ) *to++ = *from++;
	cpkey(Kn3);					/* Kn3 = KnL */
	return;
	}
 
#else	/* D3_DES too */
 
void des3key(hexkey, mode)
unsigned char *hexkey;			/* unsigned char[24] */
short mode;
{
	unsigned char *first, *third;
	short revmod;
 
	if( mode == EN0 ) {
		revmod = DE1;
		first = hexkey;
		third = &hexkey[16];
		}
	else {
		revmod = EN0;
		first = &hexkey[16];
		third = hexkey;
		}
	deskey(&hexkey[8], revmod);
	cpkey(KnR);
	deskey(third, mode);
	cpkey(Kn3);
	deskey(first, mode);
	return;
	}
 
void cp3key(into)
unsigned long *into;	/* unsigned long[96] */
{
	unsigned long *from, *endp;
 
	cpkey(into);
	into = &into[32];
	from = KnR, endp = &KnR[32];
	while( from < endp ) *into++ = *from++;
	from = Kn3, endp = &Kn3[32];
	while( from < endp ) *into++ = *from++;
	return;
	}
 
void use3key(from)
unsigned long *from;	/* unsigned long[96] */
{
	unsigned long *to, *endp;
 
	usekey(from);
	from = &from[32];
	to = KnR, endp = &KnR[32];
	while( to < endp ) *to++ = *from++;
	to = Kn3, endp = &Kn3[32];
	while( to < endp ) *to++ = *from++;
	return;
	}
 
static void D3des(unsigned char *, unsigned char *);
 
static void D3des(from, into)	/* amateur theatrics */
unsigned char *from;			/* unsigned char[24] */
unsigned char *into;			/* unsigned char[24] */
{
	unsigned long swap, leftt[2], middl[2], right[2];
	
	scrunch(from, leftt);
	scrunch(&from[8], middl);
	scrunch(&from[16], right);
	desfunc(leftt, KnL);
	desfunc(middl, KnL);
	desfunc(right, KnL);
	swap = leftt[1];
	leftt[1] = middl[0];
	middl[0] = swap;
	swap = middl[1];
	middl[1] = right[0];
	right[0] = swap;
	desfunc(leftt, KnR);
	desfunc(middl, KnR);
	desfunc(right, KnR);
	swap = leftt[1];
	leftt[1] = middl[0];
	middl[0] = swap;
	swap = middl[1];
	middl[1] = right[0];
	right[0] = swap;
	desfunc(leftt, Kn3);
	desfunc(middl, Kn3);
	desfunc(right, Kn3);
	unscrun(leftt, into);
	unscrun(middl, &into[8]);
	unscrun(right, &into[16]);
	return;
	}	
	
void make3key(aptr, kptr)
char *aptr;				/* NULL-terminated   */
unsigned char *kptr;		/* unsigned char[24] */
{
	unsigned char *store;
	int first, i;
	unsigned long savek[96];
 
	cp3key(savek);
	des3key(Df_Key, EN0);
	for( i = 0; i < 24; i++ ) kptr[i] = Df_Key[i];
	first = 1;
	while( (*aptr != '\0') || first ) {
		store = kptr;
		for( i = 0; i < 24 && (*aptr != '\0'); i++ ) {
			*store++ ^= *aptr & 0x7f;
			*aptr++ = '\0';
			}
		D3des(kptr, kptr);
		first = 0;
		}
	use3key(savek);
	return;
	}
 
#endif	/* D3_DES */
#endif	/* D2_DES */
 
/* Validation sets:
 *
 * Single-length key, single-length plaintext -
 * Key    : 0123 4567 89ab cdef
 * Plain  : 0123 4567 89ab cde7
 * Cipher : c957 4425 6a5e d31d
 *
 * Double-length key, single-length plaintext -
 * Key    : 0123 4567 89ab cdef fedc ba98 7654 3210
 * Plain  : 0123 4567 89ab cde7
 * Cipher : 7f1d 0a77 826b 8aff
 *
 * Double-length key, double-length plaintext -
 * Key    : 0123 4567 89ab cdef fedc ba98 7654 3210
 * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
 * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
 *
 * Triple-length key, single-length plaintext -
 * Key    : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
 * Plain  : 0123 4567 89ab cde7
 * Cipher : de0b 7c06 ae5e 0ed5
 *
 * Triple-length key, double-length plaintext -
 * Key    : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
 * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
 * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
 *
 * d3des V5.09 rwo 9208.04 20:31 Graven Imagery
 **********************************************************************/
 
lib/des/spr.h0000644000175000017500000002362407603152302011575 0ustar  kyzkyz/* crypto/des/spr.h */
/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@cryptsoft.com).
 * The implementation was written so as to conform with Netscapes SSL.
 * 
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are aheared to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
 * 
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from 
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 * 
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

static unsigned long SP1[64] = {
0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L,
0x02000000L, 0x00080802L, 0x00080002L, 0x02000002L,
0x00080802L, 0x02080800L, 0x02080000L, 0x00000802L,
0x02000802L, 0x02000000L, 0x00000000L, 0x00080002L,
0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L,
0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L,
0x00000002L, 0x00000800L, 0x00080800L, 0x02080002L,
0x00000800L, 0x02000802L, 0x02080002L, 0x00000000L,
0x00000000L, 0x02080802L, 0x02000800L, 0x00080002L,
0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L,
0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L,
0x00080802L, 0x00000002L, 0x02000002L, 0x02080000L,
0x02080802L, 0x00080800L, 0x02080000L, 0x02000802L,
0x02000000L, 0x00000802L, 0x00080002L, 0x00000000L,
0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L,
0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L
};
static unsigned long SP2[64] = {
0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L,
0x40000010L, 0x00008010L, 0x40008000L, 0x00108000L,
0x00008000L, 0x40100010L, 0x00000010L, 0x40008000L,
0x00100010L, 0x40108000L, 0x40100000L, 0x00000010L,
0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L,
0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L,
0x40008010L, 0x00108010L, 0x40108000L, 0x40000010L,
0x40000000L, 0x00100000L, 0x00008010L, 0x40108010L,
0x00100010L, 0x40108000L, 0x40008000L, 0x00108010L,
0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L,
0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L,
0x00008000L, 0x40000000L, 0x00108010L, 0x40008010L,
0x40108000L, 0x00008000L, 0x00000000L, 0x40000010L,
0x00000010L, 0x40108010L, 0x00108000L, 0x40100000L,
0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L,
0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L
};
static unsigned long SP3[64] = {
0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L,
0x00040001L, 0x04000000L, 0x04000101L, 0x00040100L,
0x04000100L, 0x00040000L, 0x04040000L, 0x00000001L,
0x04040101L, 0x00000101L, 0x00000001L, 0x04040001L,
0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L,
0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L,
0x04040001L, 0x04000100L, 0x00040101L, 0x04040000L,
0x00040100L, 0x00000000L, 0x04000000L, 0x00040101L,
0x04040100L, 0x00000100L, 0x00000001L, 0x00040000L,
0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L,
0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L,
0x00040001L, 0x04000000L, 0x04040101L, 0x00000001L,
0x00040101L, 0x04000001L, 0x04000000L, 0x04040101L,
0x00040000L, 0x04000100L, 0x04000101L, 0x00040100L,
0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L,
0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L
};
static unsigned long SP4[64] = {
0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L,
0x00000000L, 0x10400000L, 0x10001008L, 0x00400008L,
0x10401000L, 0x10000008L, 0x10000000L, 0x00001008L,
0x10000008L, 0x00401008L, 0x00400000L, 0x10000000L,
0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L,
0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L,
0x00001008L, 0x00000000L, 0x00400008L, 0x10401000L,
0x10001000L, 0x10400008L, 0x10401008L, 0x00400000L,
0x10400008L, 0x00001008L, 0x00400000L, 0x10000008L,
0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L,
0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L,
0x00000000L, 0x10400008L, 0x10401000L, 0x00001000L,
0x10000000L, 0x10401008L, 0x00401008L, 0x00400000L,
0x10401008L, 0x00000008L, 0x10001000L, 0x00401008L,
0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L,
0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L
};
static unsigned long SP5[64] = {
0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L,
0x08010020L, 0x08000400L, 0x00010420L, 0x08010000L,
0x00010000L, 0x00000020L, 0x08000020L, 0x00010400L,
0x08000420L, 0x08010020L, 0x08010400L, 0x00000000L,
0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L,
0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L,
0x00000020L, 0x08000420L, 0x08010420L, 0x00010020L,
0x08010000L, 0x00000400L, 0x00000420L, 0x08010400L,
0x08010400L, 0x08000420L, 0x00010020L, 0x08010000L,
0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L,
0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L,
0x00010420L, 0x08000000L, 0x00000400L, 0x00010020L,
0x08000420L, 0x00000400L, 0x00000000L, 0x08010420L,
0x08010020L, 0x08010400L, 0x00000420L, 0x00010000L,
0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L,
0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L
};
static unsigned long SP6[64] = {
0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L,
0x00200040L, 0x00002000L, 0x80002040L, 0x00200000L,
0x00002040L, 0x80202040L, 0x00202000L, 0x80000000L,
0x80002000L, 0x80000040L, 0x80200000L, 0x00202040L,
0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L,
0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L,
0x80202040L, 0x80200000L, 0x80000000L, 0x00002040L,
0x00000040L, 0x00202000L, 0x00202040L, 0x80002000L,
0x00002040L, 0x80000000L, 0x80002000L, 0x00202040L,
0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L,
0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L,
0x00200040L, 0x80202040L, 0x00202000L, 0x00000040L,
0x80202040L, 0x00202000L, 0x00200000L, 0x80002040L,
0x80000040L, 0x80200000L, 0x00202040L, 0x00000000L,
0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L,
0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L,
};
static unsigned long SP7[64] = {
0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L,
0x01004204L, 0x00004004L, 0x00004200L, 0x00000000L,
0x01000000L, 0x01000204L, 0x00000204L, 0x01004000L,
0x00000004L, 0x01004200L, 0x01004000L, 0x00000204L,
0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L,
0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L,
0x01004004L, 0x00004204L, 0x01004200L, 0x00000004L,
0x00004204L, 0x01004004L, 0x00000200L, 0x01000000L,
0x00004204L, 0x01004000L, 0x01004004L, 0x00000204L,
0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L,
0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L,
0x00000200L, 0x01000004L, 0x00000004L, 0x01000200L,
0x00000000L, 0x01000204L, 0x01000200L, 0x00004200L,
0x00000204L, 0x00004000L, 0x01004204L, 0x01000000L,
0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L,
0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L,
};
static unsigned long SP8[64] = {
0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L,
0x20020000L, 0x00800080L, 0x20800000L, 0x20820080L,
0x00000080L, 0x20000000L, 0x00820000L, 0x00020080L,
0x00820080L, 0x20020080L, 0x20000080L, 0x20800000L,
0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L,
0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L,
0x20000000L, 0x00800000L, 0x20020080L, 0x20800080L,
0x00800000L, 0x00020000L, 0x20820000L, 0x00000080L,
0x00800000L, 0x00020000L, 0x20000080L, 0x20820080L,
0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L,
0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L,
0x20820000L, 0x00000080L, 0x00800080L, 0x20020000L,
0x20820080L, 0x00800000L, 0x20800000L, 0x20000080L,
0x00820000L, 0x00020080L, 0x20020080L, 0x20800000L,
0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L,
0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L,
};
lib/lzx/0000755000175000017500000000000010071672644010664 5ustar  kyzkyzlib/lzx/lzx.c0000644000175000017500000006050310071516462011644 0ustar  kyzkyz/* cabextract 0.2 - a program to extract Microsoft Cabinet files
 * (C) 2000-2001 Stuart Caie 
 *
 * This program 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.
 *
 * This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include 
#include 
#include 
#include "lzx.h"
#include "lzx_int.h"

/* LZX decruncher */

/* This LZX decruncher was pulled out of the program cabextract 0.2 by 
   Stuart Caie  and modified to be useful as an LZX decruncher 
   outside the context of CAB files.  I do not claim any copyright on the
   (minor) modifications.
      -- Matthew T. Russotto
*/

/* Microsoft's LZX document and their implementation of the
 * com.ms.util.cab Java package do not concur.
 * 
 * Correlation between window size and number of position slots: In
 * the LZX document, 1MB window = 40 slots, 2MB window = 42 slots. In
 * the implementation, 1MB = 42 slots, 2MB = 50 slots. (The actual
 * calculation is 'find the first slot whose position base is equal to
 * or more than the required window size'). This would explain why
 * other tables in the document refer to 50 slots rather than 42.
 *
 * The constant NUM_PRIMARY_LENGTHS used in the decompression
 * pseudocode is not defined in the specification, although it could
 * be derived from the section on encoding match lengths.
 *
 * The LZX document does not state the uncompressed block has an
 * uncompressed length. Where does this length field come from, so we
 * can know how large the block is? The implementation suggests that
 * it's in the 24 bits proceeding the 3 blocktype bits, before the
 * alignment padding.
 *
 * The LZX document states that aligned offset blocks have their
 * aligned offset huffman tree AFTER the main and length tree. The
 * implementation suggests that the aligned offset tree is BEFORE the
 * main and length trees.
 *
 * The LZX document decoding algorithim states that, in an aligned
 * offset block, if an extra_bits value is 1, 2 or 3, then that number
 * of bits should be read and the result added to the match
 * offset. This is correct for 1 and 2, but not 3 bits, where only an
 * aligned symbol should be read.
 *
 * Regarding the E8 preprocessing, the LZX document states 'No
 * translation may be performed on the last 6 bytes of the input
 * block'. This is correct. However, the pseudocode provided checks
 * for the *E8 leader* up to the last 6 bytes. If the leader appears
 * between -10 and -7 bytes from the end, this would cause the next
 * four bytes to be modified, at least one of which would be in the
 * last 6 bytes, which is not allowed according to the spec.
 *
 * The specification states that the huffman trees must always contain
 * at least one element. However, many CAB files badly compressed
 * sections where the length tree is completely empty (because there
 * are no matches), and this is expected to succeed.
 */


/* LZX uses what it calls 'position slots' to represent match offsets.
 * What this means is that a small 'position slot' number and a small
 * offset from that slot are encoded instead of one large offset for
 * every match.
 * - position_base is an index to the position slot bases
 * - extra_bits states how many bits of offset-from-base data is needed.
 */
static ULONG position_base[51];
static UBYTE extra_bits[52];


int LZXinit(int window) {
  unsigned int wndsize = 1 << window;
  int i, j, posn_slots;

  /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */
  /* if a previously allocated window is big enough, keep it     */
  if (window < 15 || window > 21) return DECR_DATAFORMAT;
  if (LZX(actual_size) < wndsize) {
    if (LZX(window)) free(LZX(window));
    LZX(window) = NULL;
  }
  if (!LZX(window)) {
    if (!(LZX(window) = malloc(wndsize))) return DECR_NOMEMORY;
    LZX(actual_size) = wndsize;
  }
  LZX(window_size) = wndsize;

  /* initialise static tables */
  for (i=0, j=0; i <= 50; i += 2) {
    extra_bits[i] = extra_bits[i+1] = j; /* 0,0,0,0,1,1,2,2,3,3... */
    if ((i != 0) && (j < 17)) j++; /* 0,0,1,2,3,4...15,16,17,17,17,17... */
  }
  for (i=0, j=0; i <= 50; i++) {
    position_base[i] = j; /* 0,1,2,3,4,6,8,12,16,24,32,... */
    j += 1 << extra_bits[i]; /* 1,1,1,1,2,2,4,4,8,8,16,16,32,32,... */
  }

  /* calculate required position slots */
       if (window == 20) posn_slots = 42;
  else if (window == 21) posn_slots = 50;
  else posn_slots = window << 1;

  /*posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */
  

  LZX(R0)  =  LZX(R1)  = LZX(R2) = 1;
  LZX(main_elements)   = LZX_NUM_CHARS + (posn_slots << 3);
  LZX(header_read)     = 0;
  LZX(frames_read)     = 0;
  LZX(block_remaining) = 0;
  LZX(block_type)      = LZX_BLOCKTYPE_INVALID;
  LZX(intel_curpos)    = 0;
  LZX(intel_started)   = 0;
  LZX(window_posn)     = 0;

  /* initialise tables to 0 (because deltas will be applied to them) */
  for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) LZX(MAINTREE_len)[i] = 0;
  for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)   LZX(LENGTH_len)[i]   = 0;

  return DECR_OK;
}


/* Bitstream reading macros:
 *
 * INIT_BITSTREAM    should be used first to set up the system
 * READ_BITS(var,n)  takes N bits from the buffer and puts them in var
 *
 * ENSURE_BITS(n)    ensures there are at least N bits in the bit buffer
 * PEEK_BITS(n)      extracts (without removing) N bits from the bit buffer
 * REMOVE_BITS(n)    removes N bits from the bit buffer
 *
 * These bit access routines work by using the area beyond the MSB and the
 * LSB as a free source of zeroes. This avoids having to mask any bits.
 * So we have to know the bit width of the bitbuffer variable. This is
 * sizeof(ULONG) * 8, also defined as ULONG_BITS
 */

/* number of bits in ULONG. Note: This must be at multiple of 16, and at
 * least 32 for the bitbuffer code to work (ie, it must be able to ensure
 * up to 17 bits - that's adding 16 bits when there's one bit left, or
 * adding 32 bits when there are no bits left. The code should work fine
 * for machines where ULONG >= 32 bits.
 */
#define ULONG_BITS (sizeof(ULONG)<<3)

#define INIT_BITSTREAM do { bitsleft = 0; bitbuf = 0; } while (0)

#define ENSURE_BITS(n)							\
  while (bitsleft < (n)) {						\
    bitbuf |= ((inpos[1]<<8)|inpos[0]) << (ULONG_BITS-16 - bitsleft);	\
    bitsleft += 16; inpos+=2;						\
  }

#define PEEK_BITS(n)   (bitbuf >> (ULONG_BITS - (n)))
#define REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n)))

#define READ_BITS(v,n) do {						\
  ENSURE_BITS(n);							\
  (v) = PEEK_BITS(n);							\
  REMOVE_BITS(n);							\
} while (0)


/* Huffman macros */

#define TABLEBITS(tbl)   (LZX_##tbl##_TABLEBITS)
#define MAXSYMBOLS(tbl)  (LZX_##tbl##_MAXSYMBOLS)
#define SYMTABLE(tbl)    (LZX(tbl##_table))
#define LENTABLE(tbl)    (LZX(tbl##_len))

/* BUILD_TABLE(tablename) builds a huffman lookup table from code lengths.
 * In reality, it just calls make_decode_table() with the appropriate
 * values - they're all fixed by some #defines anyway, so there's no point
 * writing each call out in full by hand.
 */
#define BUILD_TABLE(tbl)						\
  if (make_decode_table(						\
    MAXSYMBOLS(tbl), TABLEBITS(tbl), LENTABLE(tbl), SYMTABLE(tbl)	\
  )) { return 200+DECR_ILLEGALDATA; }


/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the
 * bitstream using the stated table and puts it in var.
 */
#define READ_HUFFSYM(tbl,var) do {					\
  ENSURE_BITS(16);							\
  hufftbl = SYMTABLE(tbl);						\
  if ((i = hufftbl[PEEK_BITS(TABLEBITS(tbl))]) >= MAXSYMBOLS(tbl)) {	\
    j = 1 << (ULONG_BITS - TABLEBITS(tbl));				\
    do {								\
      j >>= 1; i <<= 1; i |= (bitbuf & j) ? 1 : 0;			\
      if (!j) { return 300+DECR_ILLEGALDATA; }	                        \
    } while ((i = hufftbl[i]) >= MAXSYMBOLS(tbl));			\
  }									\
  j = LENTABLE(tbl)[(var) = i];						\
  REMOVE_BITS(j);							\
} while (0)


/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols
 * first to last in the given table. The code lengths are stored in their
 * own special LZX way.
 */
#define READ_LENGTHS(tbl,first,last) do { \
  lb.bb = bitbuf; lb.bl = bitsleft; lb.ip = inpos; \
  if (lzx_read_lens(LENTABLE(tbl),(first),(last),&lb)) { \
    return 400+DECR_ILLEGALDATA; \
  } \
  bitbuf = lb.bb; bitsleft = lb.bl; inpos = lb.ip; \
} while (0)


/* make_decode_table(nsyms, nbits, length[], table[])
 *
 * This function was coded by David Tritscher. It builds a fast huffman
 * decoding table out of just a canonical huffman code lengths table.
 *
 * nsyms  = total number of symbols in this huffman tree.
 * nbits  = any symbols with a code length of nbits or less can be decoded
 *          in one lookup of the table.
 * length = A table to get code lengths from [0 to syms-1]
 * table  = The table to fill up with decoded symbols and pointers.
 *
 * Returns 0 for OK or 1 for error
 */

int make_decode_table(int nsyms, int nbits, UBYTE *length, UWORD *table) {
  register UWORD sym;
  register ULONG leaf;
  register UBYTE bit_num = 1;
  ULONG fill;
  ULONG pos         = 0; /* the current position in the decode table */
  ULONG table_mask  = 1 << nbits;
  ULONG bit_mask    = table_mask >> 1; /* don't do 0 length codes */
  ULONG next_symbol = bit_mask; /* base of allocation for long codes */

  /* fill entries for codes short enough for a direct mapping */
  while (bit_num <= nbits) {
    for (sym = 0; sym < nsyms; sym++) {
      if (length[sym] == bit_num) {
        leaf = pos;

        if((pos += bit_mask) > table_mask) return 1; /* table overrun */

        /* fill all possible lookups of this symbol with the symbol itself */
        fill = bit_mask;
        while (fill-- > 0) table[leaf++] = sym;
      }
    }
    bit_mask >>= 1;
    bit_num++;
  }

  /* if there are any codes longer than nbits */
  if (pos != table_mask) {
    /* clear the remainder of the table */
    for (sym = pos; sym < table_mask; sym++) table[sym] = (unsigned short)0;

    /* give ourselves room for codes to grow by up to 16 more bits */
    pos <<= 16;
    table_mask <<= 16;
    bit_mask = 1 << 15;

    while (bit_num <= 16) {
      for (sym = 0; sym < nsyms; sym++) {
        if (length[sym] == bit_num) {
          leaf = pos >> 16;
          for (fill = 0; fill < bit_num - nbits; fill++) {
            /* if this path hasn't been taken yet, 'allocate' two entries */
            if (table[leaf] == 0) {
              table[(next_symbol << 1)] = 0;
              table[(next_symbol << 1) + 1] = 0;
              table[leaf] = next_symbol++;
            }
            /* follow the path and select either left or right for next bit */
            leaf = table[leaf] << 1;
            if ((pos >> (15-fill)) & 1) leaf++;
          }
          table[leaf] = sym;

          if ((pos += bit_mask) > table_mask) return 1; /* table overflow */
        }
      }
      bit_mask >>= 1;
      bit_num++;
    }
  }

  /* full table? */
  if (pos == table_mask) return 0;

  /* either erroneous table, or all elements are 0 - let's find out. */
  for (sym = 0; sym < nsyms; sym++) if (length[sym]) return 1;
  return 0;
}

struct lzx_bits {
  ULONG bb;
  int bl;
  UBYTE *ip;
};

int lzx_read_lens(UBYTE *lens, int first, int last, struct lzx_bits *lb) {
  ULONG i,j, x,y;
  int z;

  register ULONG bitbuf = lb->bb;
  register int bitsleft = lb->bl;
  UBYTE *inpos = lb->ip;
  UWORD *hufftbl;
  
  for (x = 0; x < 20; x++) {
    READ_BITS(y, 4);
    LENTABLE(PRETREE)[x] = y;
  }
  BUILD_TABLE(PRETREE);

  for (x = first; x < last; ) {
    READ_HUFFSYM(PRETREE, z);
    if (z == 17) {
      READ_BITS(y, 4); y += 4;
      while (y--) lens[x++] = 0;
    }
    else if (z == 18) {
      READ_BITS(y, 5); y += 20;
      while (y--) lens[x++] = 0;
    }
    else if (z == 19) {
      READ_BITS(y, 1); y += 4;
      READ_HUFFSYM(PRETREE, z);
      z = lens[x] - z; if (z < 0) z += 17;
      while (y--) lens[x++] = z;
    }
    else {
      z = lens[x] - z; if (z < 0) z += 17;
      lens[x++] = z;
    }
  }

  lb->bb = bitbuf;
  lb->bl = bitsleft;
  lb->ip = inpos;
  return 0;
}

void LZXreset(void) {
    int i;

    LZX(block_type) = 0;
    LZX(block_length) = 0;
    LZX(header_read) = 0;
    for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) LZX(MAINTREE_len)[i] = 0;
    for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)   LZX(LENGTH_len)[i]   = 0;
    LZX(R0) = LZX(R1) = LZX(R2) = 1;
}



int LZXdecompress(UBYTE *inbuf, UBYTE *outbuf, ULONG inlen, ULONG outlen) {
  UBYTE *inpos  = inbuf;
  UBYTE *outpos  = outbuf;
  UBYTE *endinp = inpos + inlen;
  UBYTE *window = LZX(window);
  UBYTE *runsrc, *rundest;
  UWORD *hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */

  ULONG window_posn = LZX(window_posn);
  ULONG last_window_posn = 0;
  ULONG window_size = LZX(window_size);
  ULONG R0 = LZX(R0);
  ULONG R1 = LZX(R1);
  ULONG R2 = LZX(R2);

  register ULONG bitbuf;
  register int bitsleft;
  ULONG match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */
  struct lzx_bits lb; /* used in READ_LENGTHS macro */

  int togo = outlen, this_run, main_element, aligned_bits;
  int match_length, length_footer, extra, verbatim_bits;
  int space_remaining = outlen, bytes_to_write;

  INIT_BITSTREAM;

  /* main decoding loop */
  while (togo > 0) {
    /* last block finished, new block expected */
    if (LZX(block_remaining) == 0) {
      if (LZX(block_type) == LZX_BLOCKTYPE_UNCOMPRESSED) {
        if (LZX(block_length) & 1) inpos++; /* realign bitstream to word */
        INIT_BITSTREAM;
      }

      /* Moved header read check inside loop and added the code to reset
	 the huffman tables and re-read the Intel preprocessing info
	 every time a window-size boundary is reached. This is actually not 
	 correct, there's a separate parameter for when the tables get reset.
	 But all existing CHM files I've seen have that parameter the same
	 as the window size.  Consider this a FIXME -- MTR */

      if (window_posn == window_size) {
	LZX(header_read) = 0;
	for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) LZX(MAINTREE_len)[i] = 0;
	for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)   LZX(LENGTH_len)[i]   = 0;
	R0 = R1 = R2 = 1;
      }
      
      /* read header if necessary */
      if (!LZX(header_read)) {
	i = j = 0;
	READ_BITS(k, 1); if (k) { READ_BITS(i,16); READ_BITS(j,16); }
	LZX(intel_filesize) = (i << 16) | j; /* or 0 if not encoded */
	LZX(header_read) = 1;
      }
      

      READ_BITS(LZX(block_type), 3);
      READ_BITS(i, 16);
      READ_BITS(j, 8);
      LZX(block_remaining) = LZX(block_length) = (i << 8) | j;

      switch (LZX(block_type)) {
      case LZX_BLOCKTYPE_ALIGNED:
        for (i = 0; i < 8; i++) { READ_BITS(j, 3); LENTABLE(ALIGNED)[i] = j; }
        BUILD_TABLE(ALIGNED);
        /* rest of aligned header is same as verbatim */

      case LZX_BLOCKTYPE_VERBATIM:
        READ_LENGTHS(MAINTREE, 0, 256);
        READ_LENGTHS(MAINTREE, 256, LZX(main_elements));
        BUILD_TABLE(MAINTREE);
        if (LENTABLE(MAINTREE)[0xE8] != 0) LZX(intel_started) = 1;

        READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS);
        BUILD_TABLE(LENGTH);
        break;

      case LZX_BLOCKTYPE_UNCOMPRESSED:
        LZX(intel_started) = 1; /* because we can't assume otherwise */
        ENSURE_BITS(16); /* get up to 16 pad bits into the buffer */
        if (bitsleft > 16) inpos -= 2; /* and align the bitstream! */
        R0=inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
        R1=inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
        R2=inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
        break;

      default:
        return 500+DECR_ILLEGALDATA;
      }
    }

    /* buffer exhaustion check */
    if (inpos > endinp) {
      /* it's possible to have a file where the next run is less than
       * 16 bits in size. In this case, the READ_HUFFSYM() macro used
       * in building the tables will exhaust the buffer, so we should
       * allow for this, but not allow those accidentally read bits to
       * be used (so we check that there are at least 16 bits
       * remaining - in this boundary case they aren't really part of
       * the compressed data)
       */
      if (inpos > (endinp+2) || bitsleft < 16) return 600+DECR_ILLEGALDATA;
    }

    while ((this_run = LZX(block_remaining)) > 0 && togo > 0) {
      if (this_run > togo) this_run = togo;
      togo -= this_run;
      LZX(block_remaining) -= this_run;

      /* apply 2^x-1 mask */
      last_window_posn = window_posn;
      window_posn &= window_size - 1;
      /* runs can't straddle the window wraparound */
      if ((window_posn + this_run) > window_size)
        return DECR_DATAFORMAT;

      switch (LZX(block_type)) {

      case LZX_BLOCKTYPE_VERBATIM:
        while (this_run > 0) {
          READ_HUFFSYM(MAINTREE, main_element);
          if (main_element < LZX_NUM_CHARS) {
            /* literal: 0 to LZX_NUM_CHARS-1 */
            window[window_posn++] = main_element;
            this_run--;
          }
          else {
            /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
            main_element -= LZX_NUM_CHARS;
  
            match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
            if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
              READ_HUFFSYM(LENGTH, length_footer);
              match_length += length_footer;
            }
            match_length += LZX_MIN_MATCH;
  
            match_offset = main_element >> 3;
  
            if (match_offset > 2) {
              /* not repeated offset */
              if (match_offset != 3) {
                extra = extra_bits[match_offset];
                READ_BITS(verbatim_bits, extra);
                match_offset = position_base[match_offset] - 2 + verbatim_bits;
              }
              else {
                match_offset = 1;
              }
  
              /* update repeated offset LRU queue */
              R2 = R1; R1 = R0; R0 = match_offset;
            }
            else if (match_offset == 0) {
              match_offset = R0;
            }
            else if (match_offset == 1) {
              match_offset = R1;
              R1 = R0; R0 = match_offset;
            }
            else /* match_offset == 2 */ {
              match_offset = R2;
              R2 = R0; R0 = match_offset;
            }

            rundest = window + window_posn;
            runsrc  = rundest - match_offset;
            window_posn += match_length;
            this_run -= match_length;

            /* copy any wrapped around source data */
            while ((runsrc < window) && (match_length-- > 0)) {
             *rundest++ = *(runsrc + window_size); runsrc++;
            }
            /* copy match data - no worries about destination wraps */
            while (match_length-- > 0) *rundest++ = *runsrc++;

          }
	  if ((window_posn % 32768 == 0) && (window_posn != 0)) {
	    int b = (bitsleft<16)?bitsleft:(bitsleft-16);
	    REMOVE_BITS(b);
	    }
        }
        break;

      case LZX_BLOCKTYPE_ALIGNED:
        while (this_run > 0) {
          READ_HUFFSYM(MAINTREE, main_element);
  
          if (main_element < LZX_NUM_CHARS) {
            /* literal: 0 to LZX_NUM_CHARS-1 */
            window[window_posn++] = main_element;
            this_run--;
          }
          else {
            /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
            main_element -= LZX_NUM_CHARS;
  
            match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
            if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
              READ_HUFFSYM(LENGTH, length_footer);
              match_length += length_footer;
            }
            match_length += LZX_MIN_MATCH;
  
            match_offset = main_element >> 3;
  
            if (match_offset > 2) {
              /* not repeated offset */
              extra = extra_bits[match_offset];
              match_offset = position_base[match_offset] - 2;
              if (extra > 3) {
                /* verbatim and aligned bits */
                extra -= 3;
                READ_BITS(verbatim_bits, extra);
                match_offset += (verbatim_bits << 3);
                READ_HUFFSYM(ALIGNED, aligned_bits);
                match_offset += aligned_bits;
              }
              else if (extra == 3) {
                /* aligned bits only */
                READ_HUFFSYM(ALIGNED, aligned_bits);
                match_offset += aligned_bits;
              }
              else if (extra > 0) { /* extra==1, extra==2 */
                /* verbatim bits only */
                READ_BITS(verbatim_bits, extra);
                match_offset += verbatim_bits;
              }
              else /* extra == 0 */ {
                /* ??? */
                match_offset = 1;
              }
  
              /* update repeated offset LRU queue */
              R2 = R1; R1 = R0; R0 = match_offset;
            }
            else if (match_offset == 0) {
              match_offset = R0;
            }
            else if (match_offset == 1) {
              match_offset = R1;
              R1 = R0; R0 = match_offset;
            }
            else /* match_offset == 2 */ {
              match_offset = R2;
              R2 = R0; R0 = match_offset;
            }

            rundest = window + window_posn;
            runsrc  = rundest - match_offset;
            window_posn += match_length;
            this_run -= match_length;

            /* copy any wrapped around source data */
            while ((runsrc < window) && (match_length-- > 0)) {
             *rundest++ = *(runsrc + window_size); runsrc++;
            }
            /* copy match data - no worries about destination wraps */
            while (match_length-- > 0) *rundest++ = *runsrc++;

          }
	  if ((window_posn % 32768 == 0) && (window_posn != 0)) {
	    int b = (bitsleft<16)?bitsleft:(bitsleft-16);
	    REMOVE_BITS(b);
	    /* -- MTR -- adjust for 32768 block boundaries */
	  }
        }
        break;

      case LZX_BLOCKTYPE_UNCOMPRESSED:
        if ((inpos + this_run) > endinp) return 700+DECR_ILLEGALDATA;
        memcpy(window + window_posn, inpos, this_run);
        inpos += this_run; window_posn += this_run;
        break;

      default:
        return 800+DECR_ILLEGALDATA; /* might as well */
      }

    }
    if (window_posn > last_window_posn) {
      bytes_to_write = window_posn - last_window_posn;
      if (bytes_to_write > space_remaining) bytes_to_write = space_remaining;
      memcpy(outpos, window + last_window_posn,  bytes_to_write);
      outpos += window_posn - last_window_posn;
      space_remaining -= bytes_to_write;
    }
    else {
      bytes_to_write = window_size - last_window_posn;
      if (bytes_to_write > space_remaining) bytes_to_write = space_remaining;
      memcpy(outpos, window + last_window_posn, bytes_to_write);
      outpos += window_size - last_window_posn;
      space_remaining -= bytes_to_write;
      
      bytes_to_write = window_posn;
      if (bytes_to_write > space_remaining) bytes_to_write = space_remaining;
      
      memcpy(outpos, window,  bytes_to_write);
	fflush(stdout);
      outpos += window_posn;
      space_remaining -= bytes_to_write;
    }
  }

  if (togo != 0) return 900+DECR_ILLEGALDATA;

  LZX(window_posn) = window_posn;
  LZX(R0) = R0;
  LZX(R1) = R1;
  LZX(R2) = R2;

  /* intel E8 decoding */
  if ((LZX(frames_read)++ < 32768) && LZX(intel_filesize) != 0) {
    if (outlen <= 6 || !LZX(intel_started)) {
      LZX(intel_curpos) += outlen;
    }
    else {
      UBYTE *data    = outbuf;
      UBYTE *dataend = data + outlen - 10;
      LONG curpos    = LZX(intel_curpos);
      LONG filesize  = LZX(intel_filesize);
      LONG abs_off, rel_off;

      LZX(intel_curpos) = curpos + outlen;

      while (data < dataend) {
        if (*data++ != 0xE8) { curpos++; continue; }
        abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
        if ((abs_off >= -curpos) && (abs_off < filesize)) {
          rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
          data[0] = (UBYTE) rel_off;
          data[1] = (UBYTE) (rel_off >> 8);
          data[2] = (UBYTE) (rel_off >> 16);
          data[3] = (UBYTE) (rel_off >> 24);
        }
        data += 4;
        curpos += 5;
      }
    }
  }
  return DECR_OK;
}
lib/lzx/lzx.h0000644000175000017500000000206510071622312011640 0ustar  kyzkyz/* Using standard integer types is necessary for AMD64 support */
#ifdef __STDC_VERSION__
#include 

#if !defined(INT8_MAX)
typedef int_least8_t    int8_t;
#endif
#if !defined(INT16_MAX)
typedef int_least16_t    int16_t;
#endif
#if !defined(INT32_MAX)
typedef int_least32_t    int32_t;
#endif
#if !defined(UINT8_MAX)
typedef uint_least32_t    uint8_t;
#endif
#if !defined(UINT16_MAX)
typedef uint_least16_t    uint16_t;
#endif
#if !defined(UINT32_MAX)
typedef uint_least32_t    uint32_t;
#endif

#elif defined _XOPEN_VERSION && _XOPEN_VERSION >= 500
#    include 
#else 
typedef signed char        int8_t;
typedef signed short int   int16_t;
typedef signed int         int32_t;

typedef unsigned char      uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int       uint32_t;
#endif

typedef uint8_t UBYTE;
typedef uint16_t UWORD;
typedef uint32_t ULONG;
typedef int8_t BYTE;
typedef int16_t WORD;
typedef int32_t LONG;

int LZXinit(int window);
int LZXdecompress(UBYTE *inbuf, UBYTE *outbuf, ULONG inlen, ULONG outlen);
void LZXreset(void);
lib/lzx/lzx_int.h0000644000175000017500000000751007603152322012520 0ustar  kyzkyz
/* generic stuff */
//#define CAB(x) (decomp_state.x)
//#define ZIP(x) (decomp_state.methods.zip.x)
//#define QTM(x) (decomp_state.methods.qtm.x)
#define LZX(x) (decomp_state.methods.lzx.x)
#define DECR_OK           (0)
#define DECR_DATAFORMAT   (1)
#define DECR_ILLEGALDATA  (2)
#define DECR_NOMEMORY     (3)
#define DECR_CHECKSUM     (4)
#define DECR_INPUT        (5)
#define DECR_OUTPUT       (6)

/* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed
 * blocks have zero growth. MSZIP guarantees that it won't grow above
 * uncompressed size by more than 12 bytes. LZX guarantees it won't grow
 * more than 6144 bytes.
 */
#define CAB_BLOCKMAX (32768)
#define CAB_INPUTMAX (CAB_BLOCKMAX+6144)

/* LZX stuff */

/* some constants defined by the LZX specification */
#define LZX_MIN_MATCH                (2)
#define LZX_MAX_MATCH                (257)
#define LZX_NUM_CHARS                (256)
#define LZX_BLOCKTYPE_INVALID        (0)   /* also blocktypes 4-7 invalid */
#define LZX_BLOCKTYPE_VERBATIM       (1)
#define LZX_BLOCKTYPE_ALIGNED        (2)
#define LZX_BLOCKTYPE_UNCOMPRESSED   (3)
#define LZX_PRETREE_NUM_ELEMENTS     (20)
#define LZX_ALIGNED_NUM_ELEMENTS     (8)   /* aligned offset tree #elements */
#define LZX_NUM_PRIMARY_LENGTHS      (7)   /* this one missing from spec! */
#define LZX_NUM_SECONDARY_LENGTHS    (249) /* length tree #elements */

/* LZX huffman defines: tweak tablebits as desired */
#define LZX_PRETREE_MAXSYMBOLS  (LZX_PRETREE_NUM_ELEMENTS)
#define LZX_PRETREE_TABLEBITS   (6)
#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50*8)
#define LZX_MAINTREE_TABLEBITS  (12)
#define LZX_LENGTH_MAXSYMBOLS   (LZX_NUM_SECONDARY_LENGTHS+1)
#define LZX_LENGTH_TABLEBITS    (12)
#define LZX_ALIGNED_MAXSYMBOLS  (LZX_ALIGNED_NUM_ELEMENTS)
#define LZX_ALIGNED_TABLEBITS   (7)

#define LZX_LENTABLE_SAFETY (64) /* we allow length table decoding overruns */

#define LZX_DECLARE_TABLE(tbl) \
  UWORD tbl##_table[(1<

#ifndef MSPACK_LZX_H
#define MSPACK_LZX_H 1

/* LZX compression / decompression definitions */

/* some constants defined by the LZX specification */
#define LZX_MIN_MATCH                (2)
#define LZX_MAX_MATCH                (257)
#define LZX_NUM_CHARS                (256)
#define LZX_BLOCKTYPE_INVALID        (0)   /* also blocktypes 4-7 invalid */
#define LZX_BLOCKTYPE_VERBATIM       (1)
#define LZX_BLOCKTYPE_ALIGNED        (2)
#define LZX_BLOCKTYPE_UNCOMPRESSED   (3)
#define LZX_PRETREE_NUM_ELEMENTS     (20)
#define LZX_ALIGNED_NUM_ELEMENTS     (8)   /* aligned offset tree #elements */
#define LZX_NUM_PRIMARY_LENGTHS      (7)   /* this one missing from spec! */
#define LZX_NUM_SECONDARY_LENGTHS    (249) /* length tree #elements */

/* LZX huffman defines: tweak tablebits as desired */
#define LZX_PRETREE_MAXSYMBOLS  (LZX_PRETREE_NUM_ELEMENTS)
#define LZX_PRETREE_TABLEBITS   (6)
#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50*8)
#define LZX_MAINTREE_TABLEBITS  (12)
#define LZX_LENGTH_MAXSYMBOLS   (LZX_NUM_SECONDARY_LENGTHS+1)
#define LZX_LENGTH_TABLEBITS    (12)
#define LZX_ALIGNED_MAXSYMBOLS  (LZX_ALIGNED_NUM_ELEMENTS)
#define LZX_ALIGNED_TABLEBITS   (7)
#define LZX_LENTABLE_SAFETY (64)  /* table decoding overruns are allowed */

#define LZX_FRAME_SIZE (32768) /* the size of a frame in LZX */

struct lzxd_stream {
  struct mspack_system *sys;      /* I/O routines                            */
  struct mspack_file   *input;    /* input file handle                       */
  struct mspack_file   *output;   /* output file handle                      */

  off_t   offset;                 /* number of bytes actually output         */
  off_t   length;                 /* overall decompressed length of stream   */

  unsigned char *window;          /* decoding window                         */
  unsigned int   window_size;     /* window size                             */
  unsigned int   window_posn;     /* decompression offset within window      */
  unsigned int   frame_posn;      /* current frame offset within in window   */
  unsigned int   frame;           /* the number of 32kb frames processed     */
  unsigned int   reset_interval;  /* which frame do we reset the compressor? */

  unsigned int   R0, R1, R2;      /* for the LRU offset system               */
  unsigned int   block_length;    /* uncompressed length of this LZX block   */
  unsigned int   block_remaining; /* uncompressed bytes still left to decode */

  signed int     intel_filesize;  /* magic header value used for transform   */
  signed int     intel_curpos;    /* current offset in transform space       */

  unsigned char  intel_started;   /* has intel E8 decoding started?          */
  unsigned char  block_type;      /* type of the current block               */
  unsigned char  header_read;     /* have we started decoding at all yet?    */
  unsigned char  posn_slots;      /* how many posn slots in stream?          */
  unsigned char  input_end;       /* have we reached the end of input?       */

  int error;

  /* I/O buffering */
  unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end;
  unsigned int  bit_buffer, bits_left, inbuf_size;

  /* huffman code lengths */
  unsigned char PRETREE_len  [LZX_PRETREE_MAXSYMBOLS  + LZX_LENTABLE_SAFETY];
  unsigned char MAINTREE_len [LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
  unsigned char LENGTH_len   [LZX_LENGTH_MAXSYMBOLS   + LZX_LENTABLE_SAFETY];
  unsigned char ALIGNED_len  [LZX_ALIGNED_MAXSYMBOLS  + LZX_LENTABLE_SAFETY];

  /* huffman decoding tables */
  unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) +
				(LZX_PRETREE_MAXSYMBOLS * 2)];
  unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) +
				(LZX_MAINTREE_MAXSYMBOLS * 2)];
  unsigned short LENGTH_table  [(1 << LZX_LENGTH_TABLEBITS) +
				(LZX_LENGTH_MAXSYMBOLS * 2)];
  unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) +
				(LZX_ALIGNED_MAXSYMBOLS * 2)];

  /* this is used purely for doing the intel E8 transform */
  unsigned char  e8_buf[LZX_FRAME_SIZE];
};

/* allocates LZX decompression state for decoding the given stream.
 *
 * - returns NULL if window_bits is outwith the range 15 to 21 (inclusive).
 *
 * - uses system->alloc() to allocate memory
 *
 * - returns NULL if not enough memory
 *
 * - window_bits is the size of the LZX window, from 32Kb (15) to 2Mb (21).
 *
 * - reset_interval is how often the bitstream is reset, measured in
 *   multiples of 32Kb bytes output. For CAB LZX streams, this is always 0
 *   (does not occur).
 *
 * - input_buffer_size is how many bytes to use as an input bitstream buffer
 *
 * - output_length is the length in bytes of the entirely decompressed
 *   output stream, if known in advance. It is used to correctly perform
 *   the Intel E8 transformation, which must stop 6 bytes before the very
 *   end of the decompressed stream. It is not otherwise used or adhered
 *   to. If the full decompressed length is known in advance, set it here.
 *   If it is NOT known, use the value 0, and call lzxd_set_output_length()
 *   once it is known. If never set, 4 of the final 6 bytes of the output
 *   stream may be incorrect.
 */
extern struct lzxd_stream *lzxd_init(struct mspack_system *system,
				     struct mspack_file *input,
				     struct mspack_file *output,
				     int window_bits,
				     int reset_interval,
				     int input_buffer_size,
				     off_t output_length);

/* see description of output_length in lzxd_init() */
extern void lzxd_set_output_length(struct lzxd_stream *lzx,
				   off_t output_length);

/* decompresses, or decompresses more of, an LZX stream.
 *
 * - out_bytes of data will be decompressed and the function will return
 *   with an MSPACK_ERR_OK return code.
 *
 * - decompressing will stop as soon as out_bytes is reached. if the true
 *   amount of bytes decoded spills over that amount, they will be kept for
 *   a later invocation of lzxd_decompress().
 *
 * - the output bytes will be passed to the system->write() function given in
 *   lzxd_init(), using the output file handle given in lzxd_init(). More
 *   than one call may be made to system->write().
 *
 * - LZX will read input bytes as necessary using the system->read() function
 *   given in lzxd_init(), using the input file handle given in lzxd_init().
 *   This will continue until system->read() returns 0 bytes, or an error.
 *   input streams should convey an "end of input stream" by refusing to
 *   supply all the bytes that LZX asks for when they reach the end of the
 *   stream, rather than return an error code.
 *
 * - if an error code other than MSPACK_ERR_OK is returned, the stream should
 *   be considered unusable and lzxd_decompress() should not be called again
 *   on this stream.
 */
extern int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes);

/* frees all state associated with an LZX data stream
 *
 * - calls system->free() using the system pointer given in lzxd_init()
 */
void lzxd_free(struct lzxd_stream *lzx);

#endif
lib/newlzx/lzxd.c0000644000175000017500000007740710071716424012535 0ustar  kyzkyz/* This file is part of libmspack.
 * (C) 2003-2004 Stuart Caie.
 *
 * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
 * by Microsoft Corporation.
 *
 * libmspack is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
 *
 * For further details, see the file COPYING.LIB distributed with libmspack
 */

/* LZX decompression implementation */

#ifdef HAVE_CONFIG_H
#include 
#endif

#include 
#include 
#include 

/* Microsoft's LZX document and their implementation of the
 * com.ms.util.cab Java package do not concur.
 *
 * In the LZX document, there is a table showing the correlation between
 * window size and the number of position slots. It states that the 1MB
 * window = 40 slots and the 2MB window = 42 slots. In the implementation,
 * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the
 * first slot whose position base is equal to or more than the required
 * window size'. This would explain why other tables in the document refer
 * to 50 slots rather than 42.
 *
 * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
 * is not defined in the specification.
 *
 * The LZX document does not state the uncompressed block has an
 * uncompressed length field. Where does this length field come from, so
 * we can know how large the block is? The implementation has it as the 24
 * bits following after the 3 blocktype bits, before the alignment
 * padding.
 *
 * The LZX document states that aligned offset blocks have their aligned
 * offset huffman tree AFTER the main and length trees. The implementation
 * suggests that the aligned offset tree is BEFORE the main and length
 * trees.
 *
 * The LZX document decoding algorithm states that, in an aligned offset
 * block, if an extra_bits value is 1, 2 or 3, then that number of bits
 * should be read and the result added to the match offset. This is
 * correct for 1 and 2, but not 3, where just a huffman symbol (using the
 * aligned tree) should be read.
 *
 * Regarding the E8 preprocessing, the LZX document states 'No translation
 * may be performed on the last 6 bytes of the input block'. This is
 * correct.  However, the pseudocode provided checks for the *E8 leader*
 * up to the last 6 bytes. If the leader appears between -10 and -7 bytes
 * from the end, this would cause the next four bytes to be modified, at
 * least one of which would be in the last 6 bytes, which is not allowed
 * according to the spec.
 *
 * The specification states that the huffman trees must always contain at
 * least one element. However, many CAB files contain blocks where the
 * length tree is completely empty (because there are no matches), and
 * this is expected to succeed.
 */


/* LZX decompressor input macros
 *
 * STORE_BITS        stores bitstream state in lzxd_stream structure
 * RESTORE_BITS      restores bitstream state from lzxd_stream structure
 * READ_BITS(var,n)  takes N bits from the buffer and puts them in var
 * ENSURE_BITS(n)    ensures there are at least N bits in the bit buffer.
 * PEEK_BITS(n)      extracts without removing N bits from the bit buffer
 * REMOVE_BITS(n)    removes N bits from the bit buffer
 *
 * These bit access routines work by using the area beyond the MSB and the
 * LSB as a free source of zeroes when shifting. This avoids having to
 * mask any bits. So we have to know the bit width of the bit buffer
 * variable.
 *
 * The bit buffer datatype should be at least 32 bits wide: it must be
 * possible to ENSURE_BITS(16), so it must be possible to add 16 new bits
 * to the bit buffer when the bit buffer already has 1 to 15 bits left.
 */

#if HAVE_LIMITS_H
# include 
#endif
#ifndef CHAR_BIT
# define CHAR_BIT (8)
#endif
#define BITBUF_WIDTH (sizeof(bit_buffer) * CHAR_BIT)

#define STORE_BITS do {                                                 \
  lzx->i_ptr      = i_ptr;                                              \
  lzx->i_end      = i_end;                                              \
  lzx->bit_buffer = bit_buffer;                                         \
  lzx->bits_left  = bits_left;                                          \
} while (0)

#define RESTORE_BITS do {                                               \
  i_ptr      = lzx->i_ptr;                                              \
  i_end      = lzx->i_end;                                              \
  bit_buffer = lzx->bit_buffer;                                         \
  bits_left  = lzx->bits_left;                                          \
} while (0)

#define ENSURE_BITS(nbits)                                              \
  while (bits_left < (nbits)) {                                         \
    if (i_ptr >= i_end) {                                               \
      if (lzxd_read_input(lzx)) return lzx->error;                      \
      i_ptr = lzx->i_ptr;                                               \
      i_end = lzx->i_end;                                               \
    }                                                                   \
    bit_buffer |= ((i_ptr[1] << 8) | i_ptr[0])                          \
                  << (BITBUF_WIDTH - 16 - bits_left);                   \
    bits_left  += 16;                                                   \
    i_ptr      += 2;                                                    \
  }

#define PEEK_BITS(nbits) (bit_buffer >> (BITBUF_WIDTH - (nbits)))

#define REMOVE_BITS(nbits) ((bit_buffer <<= (nbits)), (bits_left -= (nbits)))

#define READ_BITS(val, nbits) do {                                      \
  ENSURE_BITS(nbits);                                                   \
  (val) = PEEK_BITS(nbits);                                             \
  REMOVE_BITS(nbits);                                                   \
} while (0)

static int lzxd_read_input(struct lzxd_stream *lzx) {
  int read = lzx->sys->read(lzx->input, &lzx->inbuf[0], (int)lzx->inbuf_size);
  if (read < 0) return lzx->error = MSPACK_ERR_READ;

  /* huff decode's ENSURE_BYTES(16) might overrun the input stream, even
   * if those bits aren't used, so fake 2 more bytes */
  if (read == 0) {
    if (lzx->input_end) {
      D(("out of input bytes"))
      return lzx->error = MSPACK_ERR_READ;
    }
    else {
      read = 2;
      lzx->inbuf[0] = lzx->inbuf[1] = 0;
      lzx->input_end = 1;
    }
  }

  lzx->i_ptr = &lzx->inbuf[0];
  lzx->i_end = &lzx->inbuf[read];

  return MSPACK_ERR_OK;
}

/* Huffman decoding macros */

/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the
 * bitstream using the stated table and puts it in var.
 */
#define READ_HUFFSYM(tbl, var) do {                                     \
  /* huffman symbols can be up to 16 bits long */                       \
  ENSURE_BITS(16);                                                      \
  /* immediate table lookup of [tablebits] bits of the code */          \
  sym = lzx->tbl##_table[PEEK_BITS(LZX_##tbl##_TABLEBITS)];             \
  /* is the symbol is longer than [tablebits] bits? (i=node index) */   \
  if (sym >= LZX_##tbl##_MAXSYMBOLS) {                                  \
    /* decode remaining bits by tree traversal */                       \
    i = 1 << (BITBUF_WIDTH - LZX_##tbl##_TABLEBITS);                    \
    do {                                                                \
      /* one less bit. error if we run out of bits before decode */     \
      i >>= 1;                                                          \
      if (i == 0) {                                                     \
        D(("out of bits in huffman decode"))                            \
        return lzx->error = MSPACK_ERR_DECRUNCH;                        \
      }                                                                 \
      /* double node index and add 0 (left branch) or 1 (right) */      \
      sym <<= 1; sym |= (bit_buffer & i) ? 1 : 0;                       \
      /* hop to next node index / decoded symbol */                     \
      sym = lzx->tbl##_table[sym];                                      \
      /* while we are still in node indicies, not decoded symbols */    \
    } while (sym >= LZX_##tbl##_MAXSYMBOLS);                            \
  }                                                                     \
  /* result */                                                          \
  (var) = sym;                                                          \
  /* look up the code length of that symbol and discard those bits */   \
  i = lzx->tbl##_len[sym];                                              \
  REMOVE_BITS(i);                                                       \
} while (0)

/* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */
#define BUILD_TABLE(tbl)                                                \
  if (make_decode_table(LZX_##tbl##_MAXSYMBOLS, LZX_##tbl##_TABLEBITS,  \
			&lzx->tbl##_len[0], &lzx->tbl##_table[0]))      \
  {                                                                     \
    D(("failed to build %s table", #tbl))                               \
    return lzx->error = MSPACK_ERR_DECRUNCH;                            \
  }

/* make_decode_table(nsyms, nbits, length[], table[])
 *
 * This function was coded by David Tritscher. It builds a fast huffman
 * decoding table from a canonical huffman code lengths table.
 *
 * nsyms  = total number of symbols in this huffman tree.
 * nbits  = any symbols with a code length of nbits or less can be decoded
 *          in one lookup of the table.
 * length = A table to get code lengths from [0 to syms-1]
 * table  = The table to fill up with decoded symbols and pointers.
 *
 * Returns 0 for OK or 1 for error
 */

static int make_decode_table(unsigned int nsyms, unsigned int nbits,
			     unsigned char *length, unsigned short *table)
{
  register unsigned short sym;
  register unsigned int leaf, fill;
  register unsigned char bit_num;
  unsigned int pos         = 0; /* the current position in the decode table */
  unsigned int table_mask  = 1 << nbits;
  unsigned int bit_mask    = table_mask >> 1; /* don't do 0 length codes */
  unsigned int next_symbol = bit_mask; /* base of allocation for long codes */

  /* fill entries for codes short enough for a direct mapping */
  for (bit_num = 1; bit_num <= nbits; bit_num++) {
    for (sym = 0; sym < nsyms; sym++) {
      if (length[sym] != bit_num) continue;
      leaf = pos;
      if((pos += bit_mask) > table_mask) return 1; /* table overrun */
      /* fill all possible lookups of this symbol with the symbol itself */
      for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym;
    }
    bit_mask >>= 1;
  }

  /* full table already? */
  if (pos == table_mask) return 0;

  /* clear the remainder of the table */
  for (sym = pos; sym < table_mask; sym++) table[sym] = 0xFFFF;

  /* allow codes to be up to nbits+16 long, instead of nbits */
  pos <<= 16;
  table_mask <<= 16;
  bit_mask = 1 << 15;

  for (bit_num = nbits+1; bit_num <= 16; bit_num++) {
    for (sym = 0; sym < nsyms; sym++) {
      if (length[sym] != bit_num) continue;

      leaf = pos >> 16;
      for (fill = 0; fill < bit_num - nbits; fill++) {
	/* if this path hasn't been taken yet, 'allocate' two entries */
	if (table[leaf] == 0xFFFF) {
	  table[(next_symbol << 1)] = 0xFFFF;
	  table[(next_symbol << 1) + 1] = 0xFFFF;
	  table[leaf] = next_symbol++;
	}
	/* follow the path and select either left or right for next bit */
	leaf = table[leaf] << 1;
	if ((pos >> (15-fill)) & 1) leaf++;
      }
      table[leaf] = sym;

      if ((pos += bit_mask) > table_mask) return 1; /* table overflow */
    }
    bit_mask >>= 1;
  }

  /* full table? */
  if (pos == table_mask) return 0;

  /* either erroneous table, or all elements are 0 - let's find out. */
  for (sym = 0; sym < nsyms; sym++) if (length[sym]) return 1;
  return 0;
}


/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols
 * first to last in the given table. The code lengths are stored in their
 * own special LZX way.
 */
#define READ_LENGTHS(tbl, first, last) do {                            \
  STORE_BITS;                                                          \
  if (lzxd_read_lens(lzx, &lzx->tbl##_len[0], (first),                 \
    (unsigned int)(last))) return lzx->error;                          \
  RESTORE_BITS;                                                        \
} while (0)

static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens,
			  unsigned int first, unsigned int last)
{
  /* bit buffer and huffman symbol decode variables */
  register unsigned int bit_buffer;
  register int bits_left, i;
  register unsigned short sym;
  unsigned char *i_ptr, *i_end;

  unsigned int x, y;
  int z;

  RESTORE_BITS;
  
  /* read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) */
  for (x = 0; x < 20; x++) {
    READ_BITS(y, 4);
    lzx->PRETREE_len[x] = y;
  }
  BUILD_TABLE(PRETREE);

  for (x = first; x < last; ) {
    READ_HUFFSYM(PRETREE, z);
    if (z == 17) {
      /* code = 17, run of ([read 4 bits]+4) zeros */
      READ_BITS(y, 4); y += 4;
      while (y--) lens[x++] = 0;
    }
    else if (z == 18) {
      /* code = 18, run of ([read 5 bits]+20) zeros */
      READ_BITS(y, 5); y += 20;
      while (y--) lens[x++] = 0;
    }
    else if (z == 19) {
      /* code = 19, run of ([read 1 bit]+4) [read huffman symbol] */
      READ_BITS(y, 1); y += 4;
      READ_HUFFSYM(PRETREE, z);
      z = lens[x] - z; if (z < 0) z += 17;
      while (y--) lens[x++] = z;
    }
    else {
      /* code = 0 to 16, delta current length entry */
      z = lens[x] - z; if (z < 0) z += 17;
      lens[x++] = z;
    }
  }

  STORE_BITS;

  return MSPACK_ERR_OK;
}

/* LZX static data tables:
 *
 * LZX uses 'position slots' to represent match offsets.  For every match,
 * a small 'position slot' number and a small offset from that slot are
 * encoded instead of one large offset.
 *
 * position_base[] is an index to the position slot bases
 *
 * extra_bits[] states how many bits of offset-from-base data is needed.
 */
static unsigned int  position_base[51];
static unsigned char extra_bits[51];

static void lzxd_static_init() {
  int i, j;

  for (i = 0, j = 0; i < 51; i += 2) {
    extra_bits[i]   = j; /* 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7... */
    extra_bits[i+1] = j;
    if ((i != 0) && (j < 17)) j++; /* 0,0,1,2,3,4...15,16,17,17,17,17... */
  }

  for (i = 0, j = 0; i < 51; i++) {
    position_base[i] = j; /* 0,1,2,3,4,6,8,12,16,24,32,... */
    j += 1 << extra_bits[i]; /* 1,1,1,1,2,2,4,4,8,8,16,16,32,32,... */
  }
}

static void lzxd_reset_state(struct lzxd_stream *lzx) {
  int i;

  lzx->R0              = 1;
  lzx->R1              = 1;
  lzx->R2              = 1;
  lzx->header_read     = 0;
  lzx->block_remaining = 0;
  lzx->block_type      = LZX_BLOCKTYPE_INVALID;

  /* initialise tables to 0 (because deltas will be applied to them) */
  for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) lzx->MAINTREE_len[i] = 0;
  for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)   lzx->LENGTH_len[i]   = 0;
}

/*-------- main LZX code --------*/

struct lzxd_stream *lzxd_init(struct mspack_system *system,
			      struct mspack_file *input,
			      struct mspack_file *output,
			      int window_bits,
			      int reset_interval,
			      int input_buffer_size,
			      off_t output_length)
{
  unsigned int window_size = 1 << window_bits;
  struct lzxd_stream *lzx;

  if (!system) return NULL;

  /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */
  if (window_bits < 15 || window_bits > 21) return NULL;

  input_buffer_size = (input_buffer_size + 1) & -2;
  if (!input_buffer_size) return NULL;

  /* initialise static data */
  lzxd_static_init();

  /* allocate decompression state */
  if (!(lzx = system->alloc(system, sizeof(struct lzxd_stream)))) {
    return NULL;
  }

  /* allocate decompression window and input buffer */
  lzx->window = system->alloc(system, (size_t) window_size);
  lzx->inbuf  = system->alloc(system, (size_t) input_buffer_size);
  if (!lzx->window || !lzx->inbuf) {
    system->free(lzx->window);
    system->free(lzx->inbuf);
    system->free(lzx);
    return NULL;
  }

  /* initialise decompression state */
  lzx->sys             = system;
  lzx->input           = input;
  lzx->output          = output;
  lzx->offset          = 0;
  lzx->length          = output_length;

  lzx->inbuf_size      = input_buffer_size;
  lzx->window_size     = 1 << window_bits;
  lzx->window_posn     = 0;
  lzx->frame_posn      = 0;
  lzx->frame           = 0;
  lzx->reset_interval  = reset_interval;
  lzx->intel_filesize  = 0;
  lzx->intel_curpos    = 0;

  /* window bits:    15  16  17  18  19  20  21
   * position slots: 30  32  34  36  38  42  50  */
  lzx->posn_slots      = ((window_bits == 21) ? 50 :
			  ((window_bits == 20) ? 42 : (window_bits << 1)));
  lzx->intel_started   = 0;
  lzx->input_end       = 0;

  lzx->error = MSPACK_ERR_OK;

  lzx->i_ptr = lzx->i_end = &lzx->inbuf[0];
  lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0];
  lzx->bit_buffer = lzx->bits_left = 0;

  lzxd_reset_state(lzx);
  return lzx;
}

void lzxd_set_output_length(struct lzxd_stream *lzx, off_t out_bytes) {
  if (lzx) lzx->length = out_bytes;
}

int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) {
  /* bitstream reading and huffman variables */
  register unsigned int bit_buffer;
  register int bits_left, i=0;
  register unsigned short sym;
  unsigned char *i_ptr, *i_end;

  int match_length, length_footer, extra, verbatim_bits, bytes_todo;
  int this_run, main_element, aligned_bits, j;
  unsigned char *window, *runsrc, *rundest, buf[12];
  unsigned int frame_size=0, end_frame, match_offset, window_posn;
  unsigned int R0, R1, R2;

  /* easy answers */
  if (!lzx || (out_bytes < 0)) return MSPACK_ERR_ARGS;
  if (lzx->error) return lzx->error;

  /* flush out any stored-up bytes before we begin */
  i = lzx->o_end - lzx->o_ptr;
  if ((off_t) i > out_bytes) i = (int) out_bytes;
  if (i) {
    if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) {
      return lzx->error = MSPACK_ERR_WRITE;
    }
    lzx->o_ptr  += i;
    lzx->offset += i;
    out_bytes   -= i;
  }
  if (out_bytes == 0) return MSPACK_ERR_OK;

  /* restore local state */
  RESTORE_BITS;
  window = lzx->window;
  window_posn = lzx->window_posn;
  R0 = lzx->R0;
  R1 = lzx->R1;
  R2 = lzx->R2;

  end_frame = (unsigned int)((lzx->offset + out_bytes) / LZX_FRAME_SIZE) + 1;

  while (lzx->frame < end_frame) {
    /* have we reached the reset interval? (if there is one?) */
    if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) {
      if (lzx->block_remaining) {
	D(("%d bytes remaining at reset interval", lzx->block_remaining))
	return lzx->error = MSPACK_ERR_DECRUNCH;
      }

      /* re-read the intel header and reset the huffman lengths */
      lzxd_reset_state(lzx);
    }

    /* read header if necessary */
    if (!lzx->header_read) {
      /* read 1 bit. if bit=0, intel filesize = 0.
       * if bit=1, read intel filesize (32 bits) */
      j = 0; READ_BITS(i, 1); if (i) { READ_BITS(i, 16); READ_BITS(j, 16); }
      lzx->intel_filesize = (i << 16) | j;
      lzx->header_read = 1;
    } 

    /* calculate size of frame: all frames are 32k except the final frame
     * which is 32kb or less. this can only be calculated when lzx->length
     * has been filled in. */
    frame_size = LZX_FRAME_SIZE;
    if (lzx->length && (lzx->length - lzx->offset) < (off_t)frame_size) {
      frame_size = lzx->length - lzx->offset;
    }

    /* decode until one more frame is available */
    bytes_todo = lzx->frame_posn + frame_size - window_posn;
    while (bytes_todo > 0) {
      /* initialise new block, if one is needed */
      if (lzx->block_remaining == 0) {
	/* realign if previous block was an odd-sized UNCOMPRESSED block */
	if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) &&
	    (lzx->block_length & 1))
	{
	  if (i_ptr == i_end) {
	    if (lzxd_read_input(lzx)) return lzx->error;
	    i_ptr = lzx->i_ptr;
	    i_end = lzx->i_end;
	  }
	  i_ptr++;
	}

	/* read block type (3 bits) and block length (24 bits) */
	READ_BITS(lzx->block_type, 3);
	READ_BITS(i, 16); READ_BITS(j, 8);
	lzx->block_remaining = lzx->block_length = (i << 8) | j;
	/*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/

	/* read individual block headers */
	switch (lzx->block_type) {
	case LZX_BLOCKTYPE_ALIGNED:
	  /* read lengths of and build aligned huffman decoding tree */
	  for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; }
	  BUILD_TABLE(ALIGNED);
	  /* no break -- rest of aligned header is same as verbatim */
	case LZX_BLOCKTYPE_VERBATIM:
	  /* read lengths of and build main huffman decoding tree */
	  READ_LENGTHS(MAINTREE, 0, 256);
	  READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + (lzx->posn_slots << 3));
	  BUILD_TABLE(MAINTREE);
	  /* if the literal 0xE8 is anywhere in the block... */
	  if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1;
	  /* read lengths of and build lengths huffman decoding tree */
	  READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS);
	  BUILD_TABLE(LENGTH);
	  break;

	case LZX_BLOCKTYPE_UNCOMPRESSED:
	  /* because we can't assume otherwise */
	  lzx->intel_started = 1;

	  /* read 1-16 (not 0-15) bits to align to bytes */
	  ENSURE_BITS(16);
	  if (bits_left > 16) i_ptr -= 2;
	  bits_left = 0; bit_buffer = 0;

	  /* read 12 bytes of stored R0 / R1 / R2 values */
	  for (rundest = &buf[0], i = 0; i < 12; i++) {
	    if (i_ptr == i_end) {
	      if (lzxd_read_input(lzx)) return lzx->error;
	      i_ptr = lzx->i_ptr;
	      i_end = lzx->i_end;
	    }
	    *rundest++ = *i_ptr++;
	  }
	  R0 = buf[0] | (buf[1] << 8) | (buf[2]  << 16) | (buf[3]  << 24);
	  R1 = buf[4] | (buf[5] << 8) | (buf[6]  << 16) | (buf[7]  << 24);
	  R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24);
	  break;

	default:
	  D(("bad block type"))
	  return lzx->error = MSPACK_ERR_DECRUNCH;
	}
      }

      /* decode more of the block:
       * run = min(what's available, what's needed) */
      this_run = lzx->block_remaining;
      if (this_run > bytes_todo) this_run = bytes_todo;

      /* assume we decode exactly this_run bytes, for now */
      bytes_todo           -= this_run;
      lzx->block_remaining -= this_run;

      /* decode at least this_run bytes */
      switch (lzx->block_type) {
      case LZX_BLOCKTYPE_VERBATIM:
	while (this_run > 0) {
	  READ_HUFFSYM(MAINTREE, main_element);
	  if (main_element < LZX_NUM_CHARS) {
	    /* literal: 0 to LZX_NUM_CHARS-1 */
	    window[window_posn++] = main_element;
	    this_run--;
	  }
	  else {
	    /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
	    main_element -= LZX_NUM_CHARS;

	    /* get match length */
	    match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
	    if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
	      READ_HUFFSYM(LENGTH, length_footer);
	      match_length += length_footer;
	    }
	    match_length += LZX_MIN_MATCH;
	  
	    /* get match offset */
	    switch ((match_offset = (main_element >> 3))) {
	    case 0: match_offset = R0;                                  break;
	    case 1: match_offset = R1; R1=R0;        R0 = match_offset; break;
	    case 2: match_offset = R2; R2=R0;        R0 = match_offset; break;
	    case 3: match_offset = 1;  R2=R1; R1=R0; R0 = match_offset; break;
	    default:
	      extra = extra_bits[match_offset];
	      READ_BITS(verbatim_bits, extra);
	      match_offset = position_base[match_offset] - 2 + verbatim_bits;
	      R2 = R1; R1 = R0; R0 = match_offset;
	    }

	    if ((window_posn + match_length) > lzx->window_size) {
	      D(("match ran over window wrap"))
	      return lzx->error = MSPACK_ERR_DECRUNCH;
	    }
	    
	    /* copy match */
	    rundest = &window[window_posn];
	    i = match_length;
	    /* does match offset wrap the window? */
	    if (match_offset > window_posn) {
	      /* j = length from match offset to end of window */
	      j = match_offset - window_posn;
	      if (j > (int) lzx->window_size) {
		D(("match offset beyond window boundaries"))
		return lzx->error = MSPACK_ERR_DECRUNCH;
	      }
	      runsrc = &window[lzx->window_size - j];
	      if (j < i) {
		/* if match goes over the window edge, do two copy runs */
		i -= j; while (j-- > 0) *rundest++ = *runsrc++;
		runsrc = window;
	      }
	      while (i-- > 0) *rundest++ = *runsrc++;
	    }
	    else {
	      runsrc = rundest - match_offset;
	      while (i-- > 0) *rundest++ = *runsrc++;
	    }

	    this_run    -= match_length;
	    window_posn += match_length;
	  }
	} /* while (this_run > 0) */
	break;

      case LZX_BLOCKTYPE_ALIGNED:
	while (this_run > 0) {
	  READ_HUFFSYM(MAINTREE, main_element);
	  if (main_element < LZX_NUM_CHARS) {
	    /* literal: 0 to LZX_NUM_CHARS-1 */
	    window[window_posn++] = main_element;
	    this_run--;
	  }
	  else {
	    /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
	    main_element -= LZX_NUM_CHARS;

	    /* get match length */
	    match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
	    if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
	      READ_HUFFSYM(LENGTH, length_footer);
	      match_length += length_footer;
	    }
	    match_length += LZX_MIN_MATCH;

	    /* get match offset */
	    switch ((match_offset = (main_element >> 3))) {
	    case 0: match_offset = R0;                             break;
	    case 1: match_offset = R1; R1 = R0; R0 = match_offset; break;
	    case 2: match_offset = R2; R2 = R0; R0 = match_offset; break;
	    default:
	      extra = extra_bits[match_offset];
	      match_offset = position_base[match_offset] - 2;
	      if (extra > 3) {
		/* verbatim and aligned bits */
		extra -= 3;
		READ_BITS(verbatim_bits, extra);
		match_offset += (verbatim_bits << 3);
		READ_HUFFSYM(ALIGNED, aligned_bits);
		match_offset += aligned_bits;
	      }
	      else if (extra == 3) {
		/* aligned bits only */
		READ_HUFFSYM(ALIGNED, aligned_bits);
		match_offset += aligned_bits;
	      }
	      else if (extra > 0) { /* extra==1, extra==2 */
		/* verbatim bits only */
		READ_BITS(verbatim_bits, extra);
		match_offset += verbatim_bits;
	      }
	      else /* extra == 0 */ {
		/* ??? not defined in LZX specification! */
		match_offset = 1;
	      }
	      /* update repeated offset LRU queue */
	      R2 = R1; R1 = R0; R0 = match_offset;
	    }

	    if ((window_posn + match_length) > lzx->window_size) {
	      D(("match ran over window wrap"))
	      return lzx->error = MSPACK_ERR_DECRUNCH;
	    }

	    /* copy match */
	    rundest = &window[window_posn];
	    i = match_length;
	    /* does match offset wrap the window? */
	    if (match_offset > window_posn) {
	      /* j = length from match offset to end of window */
	      j = match_offset - window_posn;
	      if (j > (int) lzx->window_size) {
		D(("match offset beyond window boundaries"))
		return lzx->error = MSPACK_ERR_DECRUNCH;
	      }
	      runsrc = &window[lzx->window_size - j];
	      if (j < i) {
		/* if match goes over the window edge, do two copy runs */
		i -= j; while (j-- > 0) *rundest++ = *runsrc++;
		runsrc = window;
	      }
	      while (i-- > 0) *rundest++ = *runsrc++;
	    }
	    else {
	      runsrc = rundest - match_offset;
	      while (i-- > 0) *rundest++ = *runsrc++;
	    }

	    this_run    -= match_length;
	    window_posn += match_length;
	  }
	} /* while (this_run > 0) */
	break;

      case LZX_BLOCKTYPE_UNCOMPRESSED:
	/* as this_run is limited not to wrap a frame, this also means it
	 * won't wrap the window (as the window is a multiple of 32k) */
	rundest = &window[window_posn];
	window_posn += this_run;
	while (this_run > 0) {
	  if ((i = i_end - i_ptr)) {
	    if (i > this_run) i = this_run;
	    lzx->sys->copy(i_ptr, rundest, (size_t) i);
	    rundest  += i;
	    i_ptr    += i;
	    this_run -= i;
	  }
	  else {
	    if (lzxd_read_input(lzx)) return lzx->error;
	    i_ptr = lzx->i_ptr;
	    i_end = lzx->i_end;
	  }
	}
	break;

      default:
        D(("Default Here."));
	return lzx->error = MSPACK_ERR_DECRUNCH; /* might as well */
      }

      /* did the final match overrun our desired this_run length? */
      if (this_run < 0) {
	if ((unsigned int)(-this_run) > lzx->block_remaining) {
	  D(("overrun went past end of block by %d (%d remaining)",
	     -this_run, lzx->block_remaining ))
	  return lzx->error = MSPACK_ERR_DECRUNCH;
	}
	lzx->block_remaining -= -this_run;
      }
    } /* while (bytes_todo > 0) */

    /* streams don't extend over frame boundaries */
    if ((window_posn - lzx->frame_posn) != frame_size) {
      D(("decode beyond output frame limits! %d != %d",
	 window_posn - lzx->frame_posn, frame_size))
     /* Ignored */
#if 0
      	return lzx->error = MSPACK_ERR_DECRUNCH; 
#endif
    }

    /* re-align input bitstream */
    if (bits_left > 0) ENSURE_BITS(16);
    if (bits_left & 15) REMOVE_BITS(bits_left & 15);

    /* check that we've used all of the previous frame first */
    if (lzx->o_ptr != lzx->o_end) {
      D(("%d avail bytes, new %d frame", lzx->o_end-lzx->o_ptr, frame_size))
      return lzx->error = MSPACK_ERR_DECRUNCH;
    }

    /* does this intel block _really_ need decoding? */
    if (lzx->intel_started && lzx->intel_filesize &&
	(lzx->frame <= 32768) && (frame_size > 10))
    {
      unsigned char *data    = &lzx->e8_buf[0];
      unsigned char *dataend = &lzx->e8_buf[frame_size - 10];
      signed int curpos      = lzx->intel_curpos;
      signed int filesize    = lzx->intel_filesize;
      signed int abs_off, rel_off;

      /* copy e8 block to the e8 buffer and tweak if needed */
      lzx->o_ptr = data;
      lzx->sys->copy(&lzx->window[lzx->frame_posn], data, frame_size);

      while (data < dataend) {
	if (*data++ != 0xE8) { curpos++; continue; }
	abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
	if ((abs_off >= -curpos) && (abs_off < filesize)) {
	  rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
	  data[0] = (unsigned char) rel_off;
	  data[1] = (unsigned char) (rel_off >> 8);
	  data[2] = (unsigned char) (rel_off >> 16);
	  data[3] = (unsigned char) (rel_off >> 24);
	}
	data += 4;
	curpos += 5;
      }
      lzx->intel_curpos += frame_size;
    }
    else {
      lzx->o_ptr = &lzx->window[lzx->frame_posn];
      if (lzx->intel_filesize) lzx->intel_curpos += frame_size;
    }
    lzx->o_end = &lzx->o_ptr[frame_size];

    /* write a frame */
    i = (out_bytes < (off_t)frame_size) ? (unsigned int)out_bytes : frame_size;
    if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) {
      return lzx->error = MSPACK_ERR_WRITE;
    }
    lzx->o_ptr  += i;
    lzx->offset += i;
    out_bytes   -= i;

    /* advance frame start position */
    lzx->frame_posn += frame_size;
    lzx->frame++;

    /* wrap window / frame position pointers */
    if (window_posn == lzx->window_size)     window_posn = 0;
    if (lzx->frame_posn == lzx->window_size) lzx->frame_posn = 0;

  } /* while (lzx->frame < end_frame) */

  if (out_bytes) {
    D(("bytes left to output"))
    return lzx->error = MSPACK_ERR_DECRUNCH;
  }

  /* store local state */
  STORE_BITS;
  lzx->window_posn = window_posn;
  lzx->R0 = R0;
  lzx->R1 = R1;
  lzx->R2 = R2;

  return MSPACK_ERR_OK;
}

void lzxd_free(struct lzxd_stream *lzx) {
  struct mspack_system *sys;
  if (lzx) {
    sys = lzx->sys;
    sys->free(lzx->inbuf);
    sys->free(lzx->window);
    sys->free(lzx);
  }
}
lib/newlzx/lzxglue.c0000644000175000017500000001062110071741042013221 0ustar  kyzkyz/*--[lzxglue.c]----------------------------------------------------------------
 | Copyright (C) 2004 DRS
 |
 | This file is part of the "openclit" library for processing .LIT files.
 |
 | "Openclit" 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.
 |
 | This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 |
 | The GNU General Public License may also be available at the following
 | URL: http://www.gnu.org/licenses/gpl.html
*/

/* This provides a "glue" between Stuart Caie's libmspack library and the
 * Openclit calls to the earlier LZX library.  
 * 
 * This way, I should be able to use the files unmodified.
 */
#include 
#include 
#include "litlib.h"
#include "mspack.h"
#include "lzx.h"

typedef struct memory_file
{
    unsigned int magic;	/* 0xB5 */
    void * buffer;
    int total_bytes;
    int current_bytes;
} memory_file;


void * glue_alloc(struct mspack_system *this, size_t bytes)
{
    void * p;
    p = (void *)malloc(bytes);
    if (p == NULL)  {
        lit_error(ERR_R|ERR_LIBC,"Malloc(%d) failed!", bytes); 
    }
    return p;
}

void glue_free(void * p)
{
    free(p);
}

void glue_copy(void *src, void *dest, size_t bytes)
{
    memcpy(dest, src, bytes);
}

struct mspack_file * glue_open(struct mspack_system *this, char *filename,
    int mode)
{
    lit_error(0,"MSPACK_OPEN unsupported!");
    return NULL;
}

void glue_close(struct mspack_file * file) {
    return;
}


int glue_read(struct mspack_file * file, void * buffer, int bytes)
{
    memory_file * mem;
    int remaining;

    mem = (memory_file *)file;
    if (mem->magic != 0xB5) return -1;
  
    remaining = mem->total_bytes - mem->current_bytes;
    if (!remaining)  return 0;
    if (bytes > remaining) bytes = remaining;
    memcpy(buffer, (unsigned char *)mem->buffer+mem->current_bytes, bytes);
    mem->current_bytes += bytes;
    return bytes;
}

int glue_write(struct mspack_file * file, void * buffer, int bytes)
{
    memory_file * mem;
    int remaining;

    mem = (memory_file *)file;
    if (mem->magic != 0xB5) return -1;
  
    remaining = mem->total_bytes - mem->current_bytes;
    if (!remaining)  return 0;
    if (bytes > remaining) { 
        lit_error(0,"MSPACK_READ tried to write %d bytes, only %d left.",
            bytes, remaining);
        bytes = remaining;
    }
    memcpy((unsigned char *)mem->buffer+mem->current_bytes, buffer, bytes);
    mem->current_bytes += bytes;
    return bytes;
}

struct mspack_system lzxglue_system = 
{
    glue_open, 
    glue_close,
    glue_read,   /* Read */
    glue_write,  /* Write */
    NULL,   /* Seek */
    NULL,   /* Tell */
    NULL,   /* Message */
    glue_alloc,
    glue_free,
    glue_copy,
    NULL    /* Termination */
};

int LZXwindow;
struct lzxd_stream * lzx_stream = NULL;


/* Can't really init here,don't know enough */
int LZXinit(int window) 
{
    LZXwindow = window;
    lzx_stream = NULL;

    return 0;
}

/* Doesn't exist. Oh well, reinitialize state every time anyway */
void LZXreset(void)
{
    return;
}

int LZXdecompress(unsigned char *inbuf, unsigned char *outbuf, 
    unsigned int inlen, unsigned int outlen)
{
    int err;
    memory_file source;
    memory_file dest;

    source.magic = 0xB5;
    source.buffer = inbuf;
    source.current_bytes = 0;
    source.total_bytes = inlen;

    dest.magic = 0xB5;
    dest.buffer = outbuf;
    dest.current_bytes = 0;
    dest.total_bytes = outlen;
    
    lzx_stream = lzxd_init(&lzxglue_system, (struct mspack_file *)&source,
        (struct mspack_file *)&dest, LZXwindow, 
        0x7fff /* Never reset, I do it */, 4096, outlen);
    err = -1;
    if (lzx_stream) err = lzxd_decompress(lzx_stream, outlen);

    lzxd_free(lzx_stream);
    lzx_stream = NULL;
    return err;
}
lib/newlzx/mspack.h0000644000175000017500000015242610071740452013030 0ustar  kyzkyz/* libmspack -- a library for working with Microsoft compression formats.
 * (C) 2003-2004 Stuart Caie 
 *
 * libmspack is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/** \mainpage
 *
 * \section intro Introduction
 *
 * libmspack is a library which provides compressors and decompressors,
 * archivers and dearchivers for Microsoft compression formats.
 *
 * \section formats Formats supported
 *
 * The following file formats are supported:
 * - SZDD files, which use LZSS compression
 * - KWAJ files, which use LZSS, LZSS+Huffman or deflate compression
 * - .HLP (MS Help) files, which use LZSS compression
 * - .CAB (MS Cabinet) files, which use deflate, LZX or Quantum compression
 * - .CHM (HTML Help) files, which use LZX compression
 * - .LIT (MS EBook) files, which use LZX compression and DES encryption
 *
 * To determine the capabilities of the library, and the binary
 * compatibility version of any particular compressor or decompressor, use
 * the mspack_version() function. The UNIX library interface version is
 * defined as the highest-versioned library component.
 *
 * \section starting Getting started
 *
 * The macro MSPACK_SYS_SELFTEST() should be used to ensure the library can
 * be used. In particular, it checks if the caller is using 32-bit file I/O
 * when the library is compiled for 64-bit file I/O and vice versa.
 *
 * If compiled normally, the library includes basic file I/O and memory
 * management functionality using the standard C library. This can be
 * customised and replaced entirely by creating a mspack_system structure.
 *
 * A compressor or decompressor for the required format must be
 * instantiated before it can be used. Each construction function takes
 * one parameter, which is either a pointer to a custom mspack_system
 * structure, or NULL to use the default. The instantiation returned, if
 * not NULL, contains function pointers (methods) to work with the given
 * file format.
 * 
 * For compression:
 * - mspack_create_cab_compressor() creates a mscab_compressor
 * - mspack_create_chm_compressor() creates a mschm_compressor
 * - mspack_create_lit_compressor() creates a mslit_compressor
 * - mspack_create_hlp_compressor() creates a mshlp_compressor
 * - mspack_create_szdd_compressor() creates a msszdd_compressor
 * - mspack_create_kwaj_compressor() creates a mskwaj_compressor
 *
 * For decompression:
 * - mspack_create_cab_decompressor() creates a mscab_decompressor
 * - mspack_create_chm_decompressor() creates a mschm_decompressor
 * - mspack_create_lit_decompressor() creates a mslit_decompressor
 * - mspack_create_hlp_decompressor() creates a mshlp_decompressor
 * - mspack_create_szdd_decompressor() creates a msszdd_decompressor
 * - mspack_create_kwaj_decompressor() creates a mskwaj_decompressor
 *
 * Once finished working with a format, each kind of
 * compressor/decompressor has its own specific destructor:
 * - mspack_destroy_cab_compressor()
 * - mspack_destroy_cab_decompressor()
 * - mspack_destroy_chm_compressor()
 * - mspack_destroy_chm_decompressor()
 * - mspack_destroy_lit_compressor()
 * - mspack_destroy_lit_decompressor()
 * - mspack_destroy_hlp_compressor()
 * - mspack_destroy_hlp_decompressor()
 * - mspack_destroy_szdd_compressor()
 * - mspack_destroy_szdd_decompressor()
 * - mspack_destroy_kwaj_compressor()
 * - mspack_destroy_kwaj_decompressor()
 *
 * Destroying a compressor or decompressor does not destroy any objects,
 * structures or handles that have been created using that compressor or
 * decompressor. Ensure that everything created or opened is destroyed or
 * closed before compressor/decompressor is itself destroyed.
 *
 * \section errors Error codes
 *
 * All compressors and decompressors use the same set of error codes. Most
 * methods return an error code directly. For methods which do not
 * return error codes directly, the error code can be obtained with the
 * last_error() method.
 *
 * - #MSPACK_ERR_OK is used to indicate success. This error code is defined
 *   as zero, all other code are non-zero.
 * - #MSPACK_ERR_ARGS indicates that a method was called with inappropriate
 *   arguments.
 * - #MSPACK_ERR_OPEN indicates that mspack_system::open() failed.
 * - #MSPACK_ERR_READ indicates that mspack_system::read() failed.
 * - #MSPACK_ERR_WRITE indicates that mspack_system::write() failed.
 * - #MSPACK_ERR_SEEK indicates that mspack_system::seek() failed.
 * - #MSPACK_ERR_NOMEMORY indicates that mspack_system::alloc() failed.
 * - #MSPACK_ERR_SIGNATURE indicates that the file being read does not
 *   have the correct "signature". It is probably not a valid file for
 *   whatever format is being read.
 * - #MSPACK_ERR_DATAFORMAT indicates that the file being used or read
 *   is corrupt.
 * - #MSPACK_ERR_CHECKSUM indicates that a data checksum has failed.
 * - #MSPACK_ERR_CRUNCH indicates an error occured during compression.
 * - #MSPACK_ERR_DECRUNCH indicates an error occured during decompression.
 */

#ifndef LIB_MSPACK_H
#define LIB_MSPACK_H 1

#ifdef __cplusplus
extern "C" {
#endif

#include 
#ifdef _MSC_VER
#include 
#else
#include 
#endif
/**
 * System self-test function, to ensure both library and calling program
 * can use one another.
 *
 * A result of MSPACK_ERR_OK means the library and caller are
 * compatible. Any other result indicates that the library and caller are
 * not compatible and should not be used. In particular, a value of
 * MSPACK_ERR_SEEK means the library and caller use different off_t
 * datatypes.
 *
 * It should be used like so:
 *
 * @code
 * int selftest_result;
 * MSPACK_SYS_SELFTEST(selftest_result);
 * if (selftest_result != MSPACK_ERR_OK) {
 *   fprintf(stderr, "incompatible with this build of libmspack\n");
 *   exit(0);
 * }
 * @endcode
 *
 * @param  result   an int variable to store the result of the self-test
 */
#define MSPACK_SYS_SELFTEST(result)  do { \
  (result) = mspack_sys_selftest_internal(sizeof(off_t)); \
} while (0)

/** Part of the MSPACK_SYS_SELFTEST() macro, must not be used directly. */
extern int mspack_sys_selftest_internal(int);

/**
 * Enquire about the binary compatibility version of a specific interface in
 * the library. Currently, the following interfaces are defined:
 *
 * - #MSPACK_VER_LIBRARY: the overall library
 * - #MSPACK_VER_SYSTEM: the mspack_system interface
 * - #MSPACK_VER_MSCABD: the mscab_decompressor interface
 * - #MSPACK_VER_MSCABC: the mscab_compressor interface
 * - #MSPACK_VER_MSCHMD: the mschm_decompressor interface
 * - #MSPACK_VER_MSCHMC: the mschm_compressor interface
 * - #MSPACK_VER_MSLITD: the mslit_decompressor interface
 * - #MSPACK_VER_MSLITC: the mslit_compressor interface
 * - #MSPACK_VER_MSHLPD: the mshlp_decompressor interface
 * - #MSPACK_VER_MSHLPC: the mshlp_compressor interface
 * - #MSPACK_VER_MSSZDDD: the msszdd_decompressor interface
 * - #MSPACK_VER_MSSZDDC: the msszdd_compressor interface
 * - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface
 * - #MSPACK_VER_MSKWAJC: the mskwaj_compressor interface
 *
 * The result of the function should be interpreted as follows:
 * - -1: this interface is completely unknown to the library
 * - 0: this interface is known, but non-functioning
 * - 1: this interface has all basic functionality
 * - 2, 3, ...: this interface has additional functionality, clearly marked
 *   in the documentation as "version 2", "version 3" and so on.
 *
 * @param interface the interface to request current version of
 * @return the version of the requested interface
 */
extern int mspack_version(int interface);

/** Pass to mspack_version() to get the overall library version */
#define MSPACK_VER_LIBRARY   (0)
/** Pass to mspack_version() to get the mspack_system version */
#define MSPACK_VER_SYSTEM    (1)
/** Pass to mspack_version() to get the mscab_decompressor version */
#define MSPACK_VER_MSCABD    (2)
/** Pass to mspack_version() to get the mscab_compressor version */
#define MSPACK_VER_MSCABC    (3)
/** Pass to mspack_version() to get the mschm_decompressor version */
#define MSPACK_VER_MSCHMD    (4)
/** Pass to mspack_version() to get the mschm_compressor version */
#define MSPACK_VER_MSCHMC    (5)
/** Pass to mspack_version() to get the mslit_decompressor version */
#define MSPACK_VER_MSLITD    (6)
/** Pass to mspack_version() to get the mslit_compressor version */
#define MSPACK_VER_MSLITC    (7)
/** Pass to mspack_version() to get the mshlp_decompressor version */
#define MSPACK_VER_MSHLPD    (8)
/** Pass to mspack_version() to get the mshlp_compressor version */
#define MSPACK_VER_MSHLPC    (9)
/** Pass to mspack_version() to get the msszdd_decompressor version */
#define MSPACK_VER_MSSZDDD   (10)
/** Pass to mspack_version() to get the msszdd_compressor version */
#define MSPACK_VER_MSSZDDC   (11)
/** Pass to mspack_version() to get the mskwaj_decompressor version */
#define MSPACK_VER_MSKWAJD   (12)
/** Pass to mspack_version() to get the mskwaj_compressor version */
#define MSPACK_VER_MSKWAJC   (13)

/* --- file I/O abstraction ------------------------------------------------ */

/**
 * A structure which abstracts file I/O and memory management.
 *
 * The library always uses the mspack_system structure for interaction
 * with the file system and to allocate, free and copy all memory. It also
 * uses it to send literal messages to the library user.
 *
 * When the library is compiled normally, passing NULL to a compressor or
 * decompressor constructor will result in a default mspack_system being
 * used, where all methods are implemented with the standard C library.
 * However, all constructors support being given a custom created
 * mspack_system structure, with the library user's own methods. This
 * allows for more abstract interaction, such as reading and writing files
 * directly to memory, or from a network socket or pipe.
 *
 * Implementors of an mspack_system structure should read all
 * documentation entries for every structure member, and write methods
 * which conform to those standards.
 */
struct mspack_system {
  /**
   * Opens a file for reading, writing, appending or updating.
   *
   * @param this     a self-referential pointer to the mspack_system
   *                 structure whose open() method is being called. If
   *                 this pointer is required by close(), read(), write(),
   *                 seek() or tell(), it should be stored in the result
   *                 structure at this time.
   * @param filename the file to be opened. It is passed directly from the
   *                 library caller without being modified, so it is up to
   *                 the caller what this parameter actually represents.
   * @param mode     one of #MSPACK_SYS_OPEN_READ (open an existing file
   *                 for reading), #MSPACK_SYS_OPEN_WRITE (open a new file
   *                 for writing), #MSPACK_SYS_OPEN_UPDATE (open an existing
   *                 file for reading/writing from the start of the file) or
   *                 #MSPACK_SYS_OPEN_APPEND (open an existing file for
   *                 reading/writing from the end of the file)
   * @return a pointer to a mspack_file structure. This structure officially
   *         contains no members, its true contents are up to the
   *         mspack_system implementor. It should contain whatever is needed
   *         for other mspack_system methods to operate.
   * @see close(), read(), write(), seek(), tell(), message()
   */
  struct mspack_file * (*open)(struct mspack_system *this,
			       char *filename,
			       int mode);

  /**
   * Closes a previously opened file. If any memory was allocated for this
   * particular file handle, it should be freed at this time.
   * 
   * @param file the file to close
   * @see open()
   */
  void (*close)(struct mspack_file *file);

  /**
   * Reads a given number of bytes from an open file.
   *
   * @param file    the file to read from
   * @param buffer  the location where the read bytes should be stored
   * @param bytes   the number of bytes to read from the file.
   * @return the number of bytes successfully read (this can be less than
   *         the number requested), zero to mark the end of file, or less
   *         than zero to indicate an error.
   * @see open(), write()
   */
  int (*read)(struct mspack_file *file,
	      void *buffer,
	      int bytes);

  /**
   * Writes a given number of bytes to an open file.
   *
   * @param file    the file to write to
   * @param buffer  the location where the written bytes should be read from
   * @param bytes   the number of bytes to write to the file.
   * @return the number of bytes successfully written, this can be less
   *         than the number requested. Zero or less can indicate an error
   *         where no bytes at all could be written. All cases where less
   *         bytes were written than requested are considered by the library
   *         to be an error.
   * @see open(), read()
   */
  int (*write)(struct mspack_file *file,
	       void *buffer,
	       int bytes);

  /**
   * Seeks to a specific file offset within an open file.
   *
   * Sometimes the library needs to know the length of a file. It does
   * this by seeking to the end of the file with seek(file, 0,
   * MSPACK_SYS_SEEK_END), then calling tell(). Implementations may want
   * to make a special case for this.
   *
   * Due to the potentially varying 32/64 bit datatype off_t on some
   * architectures, the #MSPACK_SYS_SELFTEST macro MUST be used before
   * using the library. If not, the error caused by the library passing an
   * inappropriate stackframe to seek() is subtle and hard to trace.
   *
   * @param file   the file to be seeked
   * @param offset an offset to seek, measured in bytes
   * @param mode   one of #MSPACK_SYS_SEEK_START (the offset should be
   *               measured from the start of the file), #MSPACK_SYS_SEEK_CUR
   *               (the offset should be measured from the current file offset)
   *               or #MSPACK_SYS_SEEK_END (the offset should be measured from
   *               the end of the file)
   * @return zero for success, non-zero for an error
   * @see open(), tell()
   */
  int (*seek)(struct mspack_file *file,
	      off_t offset,
	      int mode);

  /**
   * Returns the current file position (in bytes) of the given file.
   *
   * @param file the file whose file position is wanted
   * @return the current file position of the file
   * @see open(), seek()
   */
  off_t (*tell)(struct mspack_file *file);
  
  /**
   * Used to send messages from the library to the user.
   *
   * Occasionally, the library generates warnings or other messages in
   * plain english to inform the human user. These are informational only
   * and can be ignored if not wanted.
   *
   * @param file   may be a file handle returned from open() if this message
   *               pertains to a specific open file, or NULL if not related to
   *               a specific file.
   * @param format a printf() style format string. It does NOT include a
   *               trailing newline.
   * @see open()
   */
  void (*message)(struct mspack_file *file,
		  char *format,
		  ...);

  /**
   * Allocates memory.
   *
   * @param this     a self-referential pointer to the mspack_system
   *                 structure whose alloc() method is being called.
   * @param bytes    the number of bytes to allocate
   * @result a pointer to the requested number of bytes, or NULL if
   *         not enough memory is available
   * @see free()
   */
  void * (*alloc)(struct mspack_system *this,
		  size_t bytes);
  
  /**
   * Frees memory.
   * 
   * @param ptr the memory to be freed.
   * @see alloc()
   */
  void (*free)(void *ptr);

  /**
   * Copies from one region of memory to another.
   * 
   * The regions of memory are guaranteed not to overlap, are usually less
   * than 256 bytes, and may not be aligned. Please note that the source
   * parameter comes before the destination parameter, unlike the standard
   * C function memcpy().
   *
   * @param src   the region of memory to copy from
   * @param dest  the region of memory to copy to
   * @param bytes the size of the memory region, in bytes
   */
  void (*copy)(void *src,
	       void *dest,
	       size_t bytes);

  /**
   * A null pointer to mark the end of mspack_system. It must equal NULL.
   *
   * Should the mspack_system structure extend in the future, this NULL
   * will be seen, rather than have an invalid method pointer called.
   */
  void *null_ptr;
};

/** mspack_system::open() mode: open existing file for reading. */
#define MSPACK_SYS_OPEN_READ   (0)
/** mspack_system::open() mode: open new file for writing */
#define MSPACK_SYS_OPEN_WRITE  (1)
/** mspack_system::open() mode: open existing file for writing */
#define MSPACK_SYS_OPEN_UPDATE (2)
/** mspack_system::open() mode: open existing file for writing */
#define MSPACK_SYS_OPEN_APPEND (3)

/** mspack_system::seek() mode: seek relative to start of file */
#define MSPACK_SYS_SEEK_START  (0)
/** mspack_system::seek() mode: seek relative to current offset */
#define MSPACK_SYS_SEEK_CUR    (1)
/** mspack_system::seek() mode: seek relative to end of file */
#define MSPACK_SYS_SEEK_END    (2)

/** 
 * A structure which represents an open file handle. The contents of this
 * structure are determined by the implementation of the
 * mspack_system::open() method.
 */
struct mspack_file {
  int dummy;
};

/* --- error codes --------------------------------------------------------- */

/** Error code: no error */
#define MSPACK_ERR_OK          (0)
/** Error code: bad arguments to method */
#define MSPACK_ERR_ARGS        (1)
/** Error code: error opening file */
#define MSPACK_ERR_OPEN        (2)
/** Error code: error reading file */
#define MSPACK_ERR_READ        (3)
/** Error code: error writing file */
#define MSPACK_ERR_WRITE       (4)
/** Error code: seek error */
#define MSPACK_ERR_SEEK        (5)
/** Error code: out of memory */
#define MSPACK_ERR_NOMEMORY    (6)
/** Error code: bad "magic id" in file */
#define MSPACK_ERR_SIGNATURE   (7)
/** Error code: bad or corrupt file format */
#define MSPACK_ERR_DATAFORMAT  (8)
/** Error code: bad checksum or CRC */
#define MSPACK_ERR_CHECKSUM    (9)
/** Error code: error during compression */
#define MSPACK_ERR_CRUNCH      (10)
/** Error code: error during decompression */
#define MSPACK_ERR_DECRUNCH    (11)

/* --- functions available in library -------------------------------------- */

/** Creates a new CAB compressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mscab_compressor or NULL
 */
extern struct mscab_compressor *
  mspack_create_cab_compressor(struct mspack_system *sys);

/** Creates a new CAB decompressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mscab_decompressor or NULL
 */
extern struct mscab_decompressor *
  mspack_create_cab_decompressor(struct mspack_system *sys);

/** Destroys an existing CAB compressor.
 * @param this the #mscab_compressor to destroy
 */
extern void mspack_destroy_cab_compressor(struct mscab_compressor *this);

/** Destroys an existing CAB decompressor.
 * @param this the #mscab_decompressor to destroy
 */
extern void mspack_destroy_cab_decompressor(struct mscab_decompressor *this);


/** Creates a new CHM compressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mschm_compressor or NULL
 */
extern struct mschm_compressor *
  mspack_create_chm_compressor(struct mspack_system *sys);

/** Creates a new CHM decompressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mschm_decompressor or NULL
 */
extern struct mschm_decompressor *
  mspack_create_chm_decompressor(struct mspack_system *sys);

/** Destroys an existing CHM compressor.
 * @param this the #mschm_compressor to destroy
 */
extern void mspack_destroy_chm_compressor(struct mschm_compressor *this);

/** Destroys an existing CHM decompressor.
 * @param this the #mschm_decompressor to destroy
 */
extern void mspack_destroy_chm_decompressor(struct mschm_decompressor *this);


/** Creates a new LIT compressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mslit_compressor or NULL
 */
extern struct mslit_compressor *
  mspack_create_lit_compressor(struct mspack_system *sys);

/** Creates a new LIT decompressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mslit_decompressor or NULL
 */
extern struct mslit_decompressor *
  mspack_create_lit_decompressor(struct mspack_system *sys);

/** Destroys an existing LIT compressor.
 * @param this the #mslit_compressor to destroy
 */
extern void mspack_destroy_lit_compressor(struct mslit_compressor *this);

/** Destroys an existing LIT decompressor.
 * @param this the #mslit_decompressor to destroy
 */
extern void mspack_destroy_lit_decompressor(struct mslit_decompressor *this);


/** Creates a new HLP compressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mshlp_compressor or NULL
 */
extern struct mshlp_compressor *
  mspack_create_hlp_compressor(struct mspack_system *sys);

/** Creates a new HLP decompressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mshlp_decompressor or NULL
 */
extern struct mshlp_decompressor *
  mspack_create_hlp_decompressor(struct mspack_system *sys);

/** Destroys an existing hlp compressor.
 * @param this the #mshlp_compressor to destroy
 */
extern void mspack_destroy_hlp_compressor(struct mshlp_compressor *this);

/** Destroys an existing hlp decompressor.
 * @param this the #mshlp_decompressor to destroy
 */
extern void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *this);


/** Creates a new SZDD compressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #msszdd_compressor or NULL
 */
extern struct msszdd_compressor *
  mspack_create_szdd_compressor(struct mspack_system *sys);

/** Creates a new SZDD decompressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #msszdd_decompressor or NULL
 */
extern struct msszdd_decompressor *
  mspack_create_szdd_decompressor(struct mspack_system *sys);

/** Destroys an existing SZDD compressor.
 * @param this the #msszdd_compressor to destroy
 */
extern void mspack_destroy_szdd_compressor(struct msszdd_compressor *this);

/** Destroys an existing SZDD decompressor.
 * @param this the #msszdd_decompressor to destroy
 */
extern void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *this);


/** Creates a new KWAJ compressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mskwaj_compressor or NULL
 */
extern struct mskwaj_compressor *
  mspack_create_kwaj_compressor(struct mspack_system *sys);

/** Creates a new KWAJ decompressor.
 * @param sys a custom mspack_system structure, or NULL to use the default
 * @return a #mskwaj_decompressor or NULL
 */
extern struct mskwaj_decompressor *
  mspack_create_kwaj_decompressor(struct mspack_system *sys);

/** Destroys an existing KWAJ compressor.
 * @param this the #mskwaj_compressor to destroy
 */
extern void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *this);

/** Destroys an existing KWAJ decompressor.
 * @param this the #mskwaj_decompressor to destroy
 */
extern void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *this);


/* --- support for .CAB (MS Cabinet) file format --------------------------- */

/**
 * A structure which represents a single cabinet file.
 *
 * All fields are READ ONLY.
 *
 * If this cabinet is part of a merged cabinet set, the #files and #folders
 * fields are common to all cabinets in the set, and will be identical.
 *
 * @see mscab_decompressor::open(), mscab_decompressor::close(),
 *      mscab_decompressor::search()
 */
struct mscabd_cabinet {
  /**
   * The next cabinet in a chained list, if this cabinet was opened with
   * mscab_decompressor::search(). May be NULL to mark the end of the
   * list.
   */
  struct mscabd_cabinet *next;

  /**
   * The filename of the cabinet. More correctly, the filename of the
   * physical file that the cabinet resides in. This is given by the
   * library user and may be in any format.
   */
  char *filename;
  
  /** The file offset of cabinet within the physical file it resides in. */
  off_t base_offset;

  /** The length of the cabinet file in bytes. */
  unsigned int length;

  /** The previous cabinet in a cabinet set, or NULL. */
  struct mscabd_cabinet *prevcab;

  /** The next cabinet in a cabinet set, or NULL. */
  struct mscabd_cabinet *nextcab;

  /** The filename of the previous cabinet in a cabinet set, or NULL. */
  char *prevname;

  /** The filename of the next cabinet in a cabinet set, or NULL. */
  char *nextname;

  /** The name of the disk containing the previous cabinet in a cabinet
   * set, or NULL.
   */
  char *previnfo;

  /** The name of the disk containing the next cabinet in a cabinet set,
   * or NULL.
   */
  char *nextinfo;

  /** A list of all files in the cabinet or cabinet set. */
  struct mscabd_file *files;

  /** A list of all folders in the cabinet or cabinet set. */
  struct mscabd_folder *folders;

  /** 
   * The set ID of the cabinet. All cabinets in the same set should have
   * the same set ID.
   */
  unsigned short set_id;

  /**
   * The index number of the cabinet within the set. Numbering should
   * start from 0 for the first cabinet in the set, and increment by 1 for
   * each following cabinet.
   */
  unsigned short set_index;

  /**
   * The number of bytes reserved in the header area of the cabinet.
   *
   * If this is non-zero and flags has MSCAB_HDR_RESV set, this data can
   * be read by the calling application. It is of the given length,
   * located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the
   * cabinet file.
   *
   * @see flags
   */
  unsigned short header_resv;

  /**
   * Header flags.
   *
   * - MSCAB_HDR_PREVCAB indicates the cabinet is part of a cabinet set, and
   *                     has a predecessor cabinet.
   * - MSCAB_HDR_NEXTCAB indicates the cabinet is part of a cabinet set, and
   *                     has a successor cabinet.
   * - MSCAB_HDR_RESV indicates the cabinet has reserved header space.
   *
   * @see prevname, previnfo, nextname, nextinfo, header_resv
   */
  int flags;
};

/** Offset from start of cabinet to the reserved header data (if present). */
#define MSCAB_HDR_RESV_OFFSET (0x28)

/** Cabinet header flag: cabinet has a predecessor */
#define MSCAB_HDR_PREVCAB (0x01)
/** Cabinet header flag: cabinet has a successor */
#define MSCAB_HDR_NEXTCAB (0x02)
/** Cabinet header flag: cabinet has reserved header space */
#define MSCAB_HDR_RESV    (0x04)

/**
 * A structure which represents a single folder in a cabinet or cabinet set.
 *
 * All fields are READ ONLY.
 *
 * A folder is a single compressed stream of data. When uncompressed, it
 * holds the data of one or more files. A folder may be split across more
 * than one cabinet.
 */
struct mscabd_folder {
  /**
   * A pointer to the next folder in this cabinet or cabinet set, or NULL
   * if this is the final folder.
   */
  struct mscabd_folder *next;

  /** 
   * The compression format used by this folder.
   *
   * The macro MSCABD_COMP_METHOD() should be used on this field to get
   * the algorithm used. The macro MSCABD_COMP_LEVEL() should be used to get
   * the "compression level".
   *
   * @see MSCABD_COMP_METHOD(), MSCABD_COMP_LEVEL()
   */
  int comp_type;

  /**
   * The total number of data blocks used by this folder. This includes
   * data blocks present in other files, if this folder spans more than
   * one cabinet.
   */
  unsigned int num_blocks;
};

/**
 * Returns the compression method used by a folder.
 *
 * @param comp_type a mscabd_folder::comp_type value
 * @return one of #MSCAB_COMP_NONE, #MSCAB_COMP_MSZIP, #MSCAB_COMP_QUANTUM
 *         or #MSCAB_COMP_LZX
 */
#define MSCABD_COMP_METHOD(comp_type) ((comp_type) & 0x0F)
/**
 * Returns the compression level used by a folder.
 *
 * @param comp_type a mscabd_folder::comp_type value
 * @return the compression level. This is only defined by LZX and Quantum
 *         compression
 */
#define MSCABD_COMP_LEVEL(comp_type) (((comp_type) >> 8) & 0x1F)

/** Compression mode: no compression. */
#define MSCAB_COMP_NONE       (0)
/** Compression mode: MSZIP (deflate) compression. */
#define MSCAB_COMP_MSZIP      (1)
/** Compression mode: Quantum compression */
#define MSCAB_COMP_QUANTUM    (2)
/** Compression mode: LZX compression */
#define MSCAB_COMP_LZX        (3)

/**
 * A structure which represents a single file in a cabinet or cabinet set.
 *
 * All fields are READ ONLY.
 */
struct mscabd_file {
  /**
   * The next file in the cabinet or cabinet set, or NULL if this is the
   * final file.
   */
  struct mscabd_file *next;

  /**
   * The filename of the file.
   *
   * A null terminated string of up to 255 bytes in length, it may be in
   * either ISO-8859-1 or UTF8 format, depending on the file attributes.
   *
   * @see attribs
   */
  char *filename;

  /** The uncompressed length of the file, in bytes. */
  unsigned int length;

  /**
   * File attributes.
   *
   * The following attributes are defined:
   * - #MSCAB_ATTRIB_RDONLY indicates the file is write protected.
   * - #MSCAB_ATTRIB_HIDDEN indicates the file is hidden.
   * - #MSCAB_ATTRIB_SYSTEM indicates the file is a operating system file.
   * - #MSCAB_ATTRIB_ARCH indicates the file is "archived".
   * - #MSCAB_ATTRIB_EXEC indicates the file is an executable program.
   * - #MSCAB_ATTRIB_UTF_NAME indicates the filename is in UTF8 format rather
   *   than ISO-8859-1.
   */
  int attribs;

  /** File's last modified time, hour field. */
  char time_h;
  /** File's last modified time, minute field. */
  char time_m;
  /** File's last modified time, second field. */
  char time_s;

  /** File's last modified date, day field. */
  char date_d;
  /** File's last modified date, month field. */
  char date_m;
  /** File's last modified date, year field. */
  int date_y;

  /** A pointer to the folder that contains this file. */
  struct mscabd_folder *folder;

  /** The uncompressed offset of this file in its folder. */
  unsigned int offset;
};

/** mscabd_file::attribs attribute: file is read-only. */
#define MSCAB_ATTRIB_RDONLY   (0x01)
/** mscabd_file::attribs attribute: file is hidden. */
#define MSCAB_ATTRIB_HIDDEN   (0x02)
/** mscabd_file::attribs attribute: file is an operating system file. */
#define MSCAB_ATTRIB_SYSTEM   (0x04)
/** mscabd_file::attribs attribute: file is "archived". */
#define MSCAB_ATTRIB_ARCH     (0x20)
/** mscabd_file::attribs attribute: file is an executable program. */
#define MSCAB_ATTRIB_EXEC     (0x40)
/** mscabd_file::attribs attribute: filename is UTF8, not ISO-8859-1. */
#define MSCAB_ATTRIB_UTF_NAME (0x80)

/** mscab_decompressor::set_param() parameter: search buffer size. */
#define MSCABD_PARAM_SEARCHBUF (0)
/** mscab_decompressor::set_param() parameter: repair MS-ZIP streams? */
#define MSCABD_PARAM_FIXMSZIP  (1)
/** mscab_decompressor::set_param() parameter: size of decompression buffer */
#define MSCABD_PARAM_DECOMPBUF (2)

/** TODO */
struct mscab_compressor {
  int dummy; 
};

/**
 * A decompressor for .CAB (Microsoft Cabinet) files
 *
 * All fields are READ ONLY.
 *
 * @see mspack_create_cab_decompressor(), mspack_destroy_cab_decompressor()
 */
struct mscab_decompressor {
  /**
   * Opens a cabinet file and reads its contents.
   *
   * If the file opened is a valid cabinet file, all headers will be read
   * and a mscabd_cabinet structure will be returned, with a full list of
   * folders and files.
   *
   * In the case of an error occuring, NULL is returned and the error code
   * is available from last_error().
   *
   * The filename pointer should be considered "in use" until close() is
   * called on the cabinet.
   *
   * @param  this     a self-referential pointer to the mscab_decompressor
   *                  instance being called
   * @param  filename the filename of the cabinet file. This is passed
   *                  directly to mspack_system::open().
   * @return a pointer to a mscabd_cabinet structure, or NULL on failure
   * @see close(), search(), last_error()
   */
  struct mscabd_cabinet * (*open) (struct mscab_decompressor *this,
				   char *filename);

  /**
   * Closes a previously opened cabinet or cabinet set.
   *
   * This closes a cabinet, all cabinets associated with it via the
   * mscabd_cabinet::next, mscabd_cabinet::prevcab and
   * mscabd_cabinet::nextcab pointers, and all folders and files. All
   * memory used by these entities is freed.
   *
   * The cabinet pointer is now invalid and cannot be used again. All
   * mscabd_folder and mscabd_file pointers from that cabinet or cabinet
   * set are also now invalid, and cannot be used again.
   *
   * If the cabinet pointer given was created using search(), it MUST be
   * the cabinet pointer returned by search() and not one of the later
   * cabinet pointers further along the mscabd_cabinet::next chain.

   * If extra cabinets have been added using append() or prepend(), these
   * will all be freed, even if the cabinet pointer given is not the first
   * cabinet in the set. Do NOT close() more than one cabinet in the set.
   *
   * The mscabd_cabinet::filename is not freed by the library, as it is
   * not allocated by the library. The caller should free this itself if
   * necessary, before it is lost forever.
   *
   * @param  this     a self-referential pointer to the mscab_decompressor
   *                  instance being called
   * @param  cab      the cabinet to close
   * @see open(), search(), append(), prepend()
   */
  void (*close)(struct mscab_decompressor *this,
		struct mscabd_cabinet *cab);

  /**
   * Searches a regular file for embedded cabinets.
   *
   * This opens a normal file with the given filename and will search the
   * entire file for embedded cabinet files
   *
   * If any cabinets are found, the equivalent of open() is called on each
   * potential cabinet file at the offset it was found. All successfully
   * open()ed cabinets are kept in a list.
   *
   * The first cabinet found will be returned directly as the result of
   * this method. Any further cabinets found will be chained in a list
   * using the mscabd_cabinet::next field.
   *
   * In the case of an error occuring anywhere other than the simulated
   * open(), NULL is returned and the error code is available from
   * last_error().
   *
   * If no error occurs, but no cabinets can be found in the file, NULL is
   * returned and last_error() returns MSPACK_ERR_OK.
   *
   * The filename pointer should be considered in use until close() is
   * called on the cabinet.
   *
   * close() should only be called on the result of search(), not on any
   * subsequent cabinets in the mscabd_cabinet::next chain.
   *
   * @param  this     a self-referential pointer to the mscab_decompressor
   *                  instance being called
   * @param  filename the filename of the file to search for cabinets. This
   *                  is passed directly to mspack_system::open().
   * @return a pointer to a mscabd_cabinet structure, or NULL
   * @see close(), open(), last_error()
   */
  struct mscabd_cabinet * (*search) (struct mscab_decompressor *this,
				     char *filename);

  /**
   * Appends one mscabd_cabinet to another, forming or extending a cabinet
   * set.
   *
   * This will attempt to append one cabinet to another such that
   * (cab->nextcab == nextcab) && (nextcab->prevcab == cab) and
   * any folders split between the two cabinets are merged.
   *
   * The cabinets MUST be part of a cabinet set -- a cabinet set is a
   * cabinet that spans more than one physical cabinet file on disk -- and
   * must be appropriately matched.
   *
   * It can be determined if a cabinet has further parts to load by
   * examining the mscabd_cabinet::flags field:
   *
   * - if (flags & MSCAB_HDR_PREVCAB) is non-zero, there is a
   *   predecessor cabinet to open() and prepend(). Its MS-DOS
   *   case-insensitive filename is mscabd_cabinet::prevname
   * - if (flags & MSCAB_HDR_NEXTCAB) is non-zero, there is a
   *   successor cabinet to open() and append(). Its MS-DOS case-insensitive
   *   filename is mscabd_cabinet::nextname
   *
   * If the cabinets do not match, an error code will be returned. Neither
   * cabinet has been altered, and both should be closed seperately.
   *
   * Files and folders in a cabinet set are a single entity. All cabinets
   * in a set use the same file list, which is updated as cabinets in the
   * set are added. All pointers to mscabd_folder and mscabd_file
   * structures in either cabinet must be discarded and re-obtained after
   * merging.
   *
   * @param  this     a self-referential pointer to the mscab_decompressor
   *                  instance being called
   * @param  cab      the cabinet which will be appended to,
   *                  predecessor of nextcab
   * @param  nextcab  the cabinet which will be appended,
   *                  successor of cab
   * @return an error code, or MSPACK_ERR_OK if successful
   * @see prepend(), open(), close()
   */
  int (*append) (struct mscab_decompressor *this,
		 struct mscabd_cabinet *cab,
		 struct mscabd_cabinet *nextcab);

  /**
   * Prepends one mscabd_cabinet to another, forming or extending a
   * cabinet set.
   *
   * This will attempt to prepend one cabinet to another, such that
   * (cab->prevcab == prevcab) && (prevcab->nextcab == cab). In
   * all other respects, it is identical to append(). See append() for the
   * full documentation.
   *
   * @param  this     a self-referential pointer to the mscab_decompressor
   *                  instance being called
   * @param  cab      the cabinet which will be prepended to,
   *                  successor of prevcab
   * @param  prevcab  the cabinet which will be prepended,
   *                  predecessor of cab
   * @return an error code, or MSPACK_ERR_OK if successful
   * @see append(), open(), close()
   */
  int (*prepend) (struct mscab_decompressor *this,
		  struct mscabd_cabinet *cab,
		  struct mscabd_cabinet *prevcab);

  /**
   * Extracts a file from a cabinet or cabinet set.
   *
   * This extracts a compressed file in a cabinet and writes it to the given
   * filename.
   *
   * The MS-DOS filename of the file, mscabd_file::filename, is NOT USED
   * by extract(). The caller must examine this MS-DOS filename, copy and
   * change it as necessary, create directories as necessary, and provide
   * the correct filename as a parameter, which will be passed unchanged
   * to the decompressor's mspack_system::open()
   *
   * If the file belongs to a split folder in a multi-part cabinet set,
   * and not enough parts of the cabinet set have been loaded and appended
   * or prepended, an error will be returned immediately.
   *
   * @param  this     a self-referential pointer to the mscab_decompressor
   *                  instance being called
   * @param  file     the file to be decompressed
   * @param  filename the filename of the file being written to
   * @return an error code, or MSPACK_ERR_OK if successful
   */
  int (*extract)(struct mscab_decompressor *this,
		 struct mscabd_file *file,
		 char *filename);

  /**
   * Sets a CAB decompression engine parameter.
   *
   * The following parameters are defined:
   * - #MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a
   *   buffer when using search()? The minimum value is 4.  The default
   *   value is 32768.
   * - #MSCABD_PARAM_FIXMSZIP: If non-zero, extract() will ignore bad
   *   checksums and recover from decompression errors in MS-ZIP
   *   compressed folders. The default value is 0 (don't recover).
   * - #MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input
   *   bit buffer by decompressors? The minimum value is 4. The default
   *   value is 4096.
   *
   * @param  this     a self-referential pointer to the mscab_decompressor
   *                  instance being called
   * @param  param    the parameter to set
   * @param  value    the value to set the parameter to
   * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there
   *         is a problem with either parameter or value.
   * @see search(), extract()
   */
  int (*set_param)(struct mscab_decompressor *this,
		   int param,
		   int value);

  /**
   * Returns the error code set by the most recently called method.
   *
   * This is useful for open() and search(), which do not return an error
   * code directly.
   *
   * @param  this     a self-referential pointer to the mscab_decompressor
   *                  instance being called
   * @return the most recent error code
   * @see open(), search()
   */
  int (*last_error)(struct mscab_decompressor *);
};

/* --- support for .CHM (HTMLHelp) file format ----------------------------- */

/**
 * A structure which represents a section of a CHM helpfile.
 *
 * All fields are READ ONLY.
 *
 * Not used directly, but used as a generic base type for
 * mschmd_sec_uncompressed and mschmd_sec_mscompressed.
 */
struct mschmd_section {
  /** A pointer to the CHM helpfile that contains this section. */
  struct mschmd_header *chm;

  /**
   * The section ID. Either 0 for the uncompressed section
   * mschmd_sec_uncompressed, or 1 for the LZX compressed section
   * mschmd_sec_mscompressed. No other section IDs are known.
   */
  unsigned int id;
};

/**
 * A structure which represents the uncompressed section of a CHM helpfile.
 * 
 * All fields are READ ONLY.
 */
struct mschmd_sec_uncompressed {
  /** Generic section data. */
  struct mschmd_section base;

  /** The file offset of where this section begins in the CHM helpfile. */
  off_t offset;
};

/**
 * A structure which represents the compressed section of a CHM helpfile. 
 * 
 * All fields are READ ONLY.
 */
struct mschmd_sec_mscompressed {
  /** Generic section data. */
  struct mschmd_section base;

  /** A pointer to the meta-file which represents all LZX compressed data. */
  struct mschmd_file *content;

  /** A pointer to the file which contains the LZX control data. */
  struct mschmd_file *control;

  /** A pointer to the file which contains the LZX reset table. */
  struct mschmd_file *rtable;
};

/**
 * A structure which represents a CHM helpfile.
 * 
 * All fields are READ ONLY.
 */
struct mschmd_header {
  /** The version of the CHM file format used in this file. */
  unsigned int version;

  /**
   * The "timestamp" of the CHM helpfile. 
   *
   * It is the lower 32 bits of a 64-bit value representing the number of
   * centiseconds since 1601-01-01 00:00:00 UTC, plus 42. It is not useful
   * as a timestamp, but it is useful as a semi-unique ID.
   */
  unsigned int timestamp;

      
  /**
   * The default Language and Country ID (LCID) of the user who ran the
   * HTMLHelp Compiler. This is not the language of the CHM file itself.
   */
  unsigned int language;

  /**
   * The filename of the CHM helpfile. This is given by the library user
   * and may be in any format.
   */
  char *filename;

  /** The length of the CHM helpfile, in bytes. */
  off_t length;

  /** A list of all non-system files in the CHM helpfile. */
  struct mschmd_file *files;

  /**
   * A list of all system files in the CHM helpfile.
   *
   * System files are files which begin with "::". They are meta-files
   * generated by the CHM creation process.
   */
  struct mschmd_file *sysfiles;

  /** The section 0 (uncompressed) data in this CHM helpfile. */
  struct mschmd_sec_uncompressed sec0;

  /** The section 1 (MSCompressed) data in this CHM helpfile. */
  struct mschmd_sec_mscompressed sec1;

  /** The file offset of the first PMGL/PMGI directory chunk. */
  off_t dir_offset;

  /** The number of PMGL/PMGI directory chunks in this CHM helpfile. */
  unsigned int num_chunks;

  /** The size of each PMGL/PMGI chunk, in bytes. */
  unsigned int chunk_size;

  /** The "density" of the quick-reference section in PMGL/PMGI chunks. */
  unsigned int density;

  /** The depth of the index tree.
   *
   * - if 1, there are no PMGI chunks, only PMGL chunks.
   * - if 2, there is 1 PMGI chunk. All chunk indices point to PMGL chunks.
   * - if 3, the root PMGI chunk points to secondary PMGI chunks, which in
   *         turn point to PMGL chunks.
   * - and so on...
   */
  unsigned int depth;

  /**
   * The number of the root PGMI chunk.
   *
   * If there is no index in the CHM helpfile, this will be 0xFFFFFFFF.
   */
  unsigned int index_root;
};

/**
 * A structure which represents a file stored in a CHM helpfile.
 * 
 * All fields are READ ONLY.
 */
struct mschmd_file {
  /**
   * A pointer to the next file in the list, or NULL if this is the final
   * file.
   */
  struct mschmd_file *next;

  /**
   * A pointer to the section that this file is located in. Indirectly,
   * it also points to the CHM helpfile the file is located in.
   */
  struct mschmd_section *section;

  /** The offset within the section data that this file is located at. */
  off_t offset;

  /** The length of this file, in bytes */
  off_t length;

  /** The filename of this file -- a null terminated string in UTF8. */
  char *filename;
};

/** TODO */
struct mschm_compressor {
  int dummy;
};

/**
 * A decompressor for .CHM (Microsoft HTMLHelp) files
 *
 * All fields are READ ONLY.
 *
 * @see mspack_create_chm_decompressor(), mspack_destroy_chm_decompressor()
 */
struct mschm_decompressor {
  /**
   * Opens a CHM helpfile and reads its contents.
   *
   * If the file opened is a valid CHM helpfile, all headers will be read
   * and a mschmd_header structure will be returned, with a full list of
   * files.
   *
   * In the case of an error occuring, NULL is returned and the error code
   * is available from last_error().
   *
   * The filename pointer should be considered "in use" until close() is
   * called on the CHM helpfile.
   *
   * @param  this     a self-referential pointer to the mschm_decompressor
   *                  instance being called
   * @param  filename the filename of the CHM helpfile. This is passed
   *                  directly to mspack_system::open().
   * @return a pointer to a mschmd_header structure, or NULL on failure
   * @see close()
   */
  struct mschmd_header *(*open)(struct mschm_decompressor *this,
				char *filename);

  /**
   * Closes a previously opened CHM helpfile.
   *
   * This closes a CHM helpfile, frees the mschmd_header and all
   * mschmd_file structures associated with it (if any). This works on
   * both helpfiles opened with open() and helpfiles opened with
   * fast_open().
   *
   * The CHM header pointer is now invalid and cannot be used again. All
   * mschmd_file pointers referencing that CHM are also now invalid, and
   * cannot be used again.
   *
   * @param  this     a self-referential pointer to the mschm_decompressor
   *                  instance being called
   * @param  chm      the CHM helpfile to close
   * @see open(), fast_open()
   */
  void (*close)(struct mschm_decompressor *this,
		struct mschmd_header *chm);

  /**
   * Extracts a file from a CHM helpfile.
   *
   * This extracts a file from a CHM helpfile and writes it to the given
   * filename. The filename of the file, mscabd_file::filename, is not
   * used by extract(), but can be used by the caller as a guide for
   * constructing an appropriate filename.
   *
   * This method works both with files found in the mschmd_header::files
   * and mschmd_header::sysfiles list and mschmd_file structures generated
   * on the fly by fast_find().
   *
   * @param  this     a self-referential pointer to the mscab_decompressor
   *                  instance being called
   * @param  file     the file to be decompressed
   * @param  filename the filename of the file being written to
   * @return an error code, or MSPACK_ERR_OK if successful
   */
  int (*extract)(struct mschm_decompressor *this,
		 struct mschmd_file *file,
		 char *filename);

  /**
   * Returns the error code set by the most recently called method.
   *
   * This is useful for open() and fast_open(), which do not return an
   * error code directly.
   *
   * @param  this     a self-referential pointer to the mschm_decompressor
   *                  instance being called
   * @return the most recent error code
   * @see open(), search()
   */
  int (*last_error)(struct mschm_decompressor *this);

  /**
   * Opens a CHM helpfile quickly.
   *
   * If the file opened is a valid CHM helpfile, only essential headers
   * will be read. A mschmd_header structure will be still be returned, as
   * with open(), but the mschmd_header::files field will be NULL. No
   * files details will be automatically read.  The fast_find() method
   * must be used to obtain file details.
   *
   * In the case of an error occuring, NULL is returned and the error code
   * is available from last_error().
   *
   * The filename pointer should be considered "in use" until close() is
   * called on the CHM helpfile.
   *
   * @param  this     a self-referential pointer to the mschm_decompressor
   *                  instance being called
   * @param  filename the filename of the CHM helpfile. This is passed
   *                  directly to mspack_system::open().
   * @return a pointer to a mschmd_header structure, or NULL on failure
   * @see open(), close(), fast_find(), extract()
   */
  struct mschmd_header *(*fast_open)(struct mschm_decompressor *this,
				     char *filename);

  /**
   * Finds file details quickly.
   *
   * Instead of reading all CHM helpfile headers and building a list of
   * files, fast_open() and fast_find() are intended for finding file
   * details only when they are needed. The CHM file format includes an
   * on-disk file index to allow this.
   *
   * Given a case-sensitive filename, fast_find() will search the on-disk
   * index for that file.
   *
   * If the file was found, the caller-provided mschmd_file structure will
   * be filled out like so:
   * - section: the correct value for the found file
   * - offset: the correct value for the found file
   * - length: the correct value for the found file
   * - all other structure elements: NULL or 0
   *
   * If the file was not found, MSPACK_ERR_OK will still be returned as the
   * result, but the caller-provided structure will be filled out like so:
   * - section: NULL
   * - offset: 0
   * - length: 0
   * - all other structure elements: NULL or 0
   *
   * This method is intended to be used in conjunction with CHM helpfiles
   * opened with fast_open(), but it also works with helpfiles opened
   * using the regular open().
   *
   * @param  this     a self-referential pointer to the mschm_decompressor
   *                  instance being called
   * @param  chm      the CHM helpfile to search for the file
   * @param  filename the filename of the file to search for
   * @param  f_ptr    a pointer to a caller-provded mschmd_file structure
   * @param  f_size   sizeof(struct mschmd_file)
   * @return MSPACK_ERR_OK, or an error code
   * @see open(), close(), fast_find(), extract()
   */
  int (*fast_find)(struct mschm_decompressor *this,
		   struct mschmd_header *chm,
		   char *filename,
		   struct mschmd_file *f_ptr,
		   int f_size);
};

/* --- support for .LIT (EBook) file format -------------------------------- */

/** TODO */
struct mslit_compressor {
  int dummy; 
};

/** TODO */
struct mslit_decompressor {
  int dummy; 
};


/* --- support for .HLP (MS Help) file format ------------------------------ */

/** TODO */
struct mshlp_compressor {
  int dummy; 
};

/** TODO */
struct mshlp_decompressor {
  int dummy; 
};


/* --- support for SZDD file format ---------------------------------------- */

/** TODO */
struct msszdd_compressor {
  int dummy; 
};

/** TODO */
struct msszdd_decompressor {
  int dummy; 
};

/* --- support for KWAJ file format ---------------------------------------- */

/** TODO */
struct mskwaj_compressor {
  int dummy; 
};

/** TODO */
struct mskwaj_decompressor {
  int dummy; 
};

#ifdef __cplusplus
};
#endif

#endif
lib/newlzx/system.h0000644000175000017500000000373610071740646013102 0ustar  kyzkyz/* This file is part of libmspack.
 * (C) 2003-2004 Stuart Caie.
 *
 * libmspack is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
 *
 * For further details, see the file COPYING.LIB distributed with libmspack
 */

#ifndef MSPACK_SYSTEM_H
#define MSPACK_SYSTEM_H 1

#ifdef _MSC_VER
#define inline
#endif

#ifdef DEBUG
# include 
# define D(x) do { printf("%s:%d (%s) ",__FILE__, __LINE__, __FUNCTION__); \
                   printf x ; fputc('\n', stdout); fflush(stdout);} while (0);
#else
# define D(x)
#endif

/* endian-neutral reading of little-endian data */
#define __egi32(a,n) ( (((a)[n+3]) << 24) | (((a)[n+2]) << 16) | \
		       (((a)[n+1]) <<  8) |  ((a)[n+0])        )
#define EndGetI64(a) ((((unsigned long long int) __egi32(a,4)) << 32) | \
		      ((unsigned int) __egi32(a,0)))
#define EndGetI32(a) __egi32(a,0)
#define EndGetI16(a) ((((a)[1])<<8)|((a)[0]))

/* endian-neutral reading of big-endian data */
#define EndGetM32(a) ((((a)[0])<<24)|(((a)[1])<<16)|(((a)[2])<<8)|((a)[3]))
#define EndGetM16(a) ((((a)[0])<<8)|((a)[1]))

extern struct mspack_system *mspack_default_system;

/* returns the length of a file opened for reading */
extern int mspack_sys_filelen(struct mspack_system *system,
			      struct mspack_file *file, off_t *length);

/* validates a system structure */
extern int mspack_valid_system(struct mspack_system *sys);

/* Can't redfine intrinsics in Microsoft Visual C */
#ifndef _MSC_VER

/* inline memcmp() */
static inline int memcmp(const void *s1, const void *s2, size_t n) {
  unsigned char *c1 = (unsigned char *) s1;
  unsigned char *c2 = (unsigned char *) s2;
  if (n == 0) return 0;
  while (--n && (*c1 == *c2)) c1++, c2++;
  return *c1 - *c2;
}

/* inline strlen() */
static inline size_t strlen(const char *s) {
  const char *e = s;
  while (*e) e++;
  return e - s;
}
#endif
            
#endif
lib/sha/0000755000175000017500000000000010072016430010604 5ustar  kyzkyzlib/sha/mssha1.c0000644000175000017500000001465210071510730012155 0ustar  kyzkyz/*
 * sha1.c
 *
 * Originally witten by Steve Reid 
 * 
 * Modified by Aaron D. Gifford 
 *
 * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN
 *
 * The original unmodified version is available at:
 *    ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* Modified to handle Microsoft Security-Through-Obscurity in the LIT format */

#include 
#include "sha.h"

#if 1
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */

#define	blk0(i) (block->l[i] = ((block->c[i*4	 ] << 24) | \
		 		(block->c[i*4 + 1] << 16) | \
		 		(block->c[i*4 + 2] <<  8) | \
		 		(block->c[i*4 + 3]      )))
		 
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
	^block->l[(i+2)&15]^block->l[i&15],1))

#endif

/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);

typedef union _BYTE64QUAD16 {
	sha1_byte c[64];
	sha1_quadbyte l[16];
} BYTE64QUAD16;

/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1_Transform(sha1_quadbyte state[5], sha1_byte buffer[64]) {
	sha1_quadbyte	a, b, c, d, e;
	BYTE64QUAD16	*block;

	block = (BYTE64QUAD16*)buffer;
	/* Copy context->state[] to working vars */
	a = state[0];
	b = state[1];
	c = state[2];
	d = state[3];
	e = state[4];
	/* 4 rounds of 20 operations each. Loop unrolled. */
	R0(a,b,c,d,e, 0); 
       	R0(e,a,b,c,d, 1); 
	R0(d,e,a,b,c, 2);
	b+=(d^e^a)+blk0(3)+0x5A827999 + rol(c,5);
	d=rol(d,30);
	
	R0(b,c,d,e,a, 4);
	R0(a,b,c,d,e, 5); 
	d+=((a+b)^b)+blk0(6)+0x5A827999+rol(e,5);
	a=rol(a,30);

	R0(d,e,a,b,c, 7);
	R0(c,d,e,a,b, 8); 
	R0(b,c,d,e,a, 9); 
	e+=(b^c^d)+blk0(10)+0x5A827999+rol(a,5);
	b=rol(b,30);

	R0(e,a,b,c,d,11);
	R0(d,e,a,b,c,12); 
	R0(c,d,e,a,b,13); 
	R0(b,c,d,e,a,14); 
	e+=(b^c^d)+blk0(15)+0x5a827999+rol(a,5);
	b=rol(b,30);

	R1(e,a,b,c,d,16); 
	R1(d,e,a,b,c,17); 
	R1(c,d,e,a,b,18); 
	R1(b,c,d,e,a,19);

	R2(a,b,c,d,e,20); 
	R2(e,a,b,c,d,21); 
	R2(d,e,a,b,c,22); 
	R2(c,d,e,a,b,23);
	R2(b,c,d,e,a,24); 
	R2(a,b,c,d,e,25); 
	d+=((a&(b^c))^c)+blk(26)+0x6ED9EBA1+rol(e,5);
	a=rol(a,30);

	R2(d,e,a,b,c,27);
	R2(c,d,e,a,b,28);
	R2(b,c,d,e,a,29);
        R2(a,b,c,d,e,30);
	d+=(((a|b)&c)|(a&b))+blk(31)+0x6ED9EBA1+rol(e,5);
        a=rol(a,30);

	R2(d,e,a,b,c,32); 
	R2(c,d,e,a,b,33); 
	R2(b,c,d,e,a,34); 
	R2(a,b,c,d,e,35);
	R2(e,a,b,c,d,36); 
	R2(d,e,a,b,c,37); 
	R2(c,d,e,a,b,38); 
	R2(b,c,d,e,a,39);

	R3(a,b,c,d,e,40); 
	R3(e,a,b,c,d,41); 
	c+=((e+a)^a)+blk(42)+0x8F1BBCDC+rol(d,5);
	e=rol(e,30);

	R3(c,d,e,a,b,43);
	R3(b,c,d,e,a,44); 
	R3(a,b,c,d,e,45); 
	R3(e,a,b,c,d,46); 
	R3(d,e,a,b,c,47);
	R3(c,d,e,a,b,48); 
	R3(b,c,d,e,a,49); 
	R3(a,b,c,d,e,50); 
	d+=(a^b^c)+blk(51)+0x8F1BBCDC+rol(e,5);
	a=rol(a,30);

	R3(d,e,a,b,c,52); 
	R3(c,d,e,a,b,53); 
	R3(b,c,d,e,a,54); 
	R3(a,b,c,d,e,55);
	R3(e,a,b,c,d,56); 
	R3(d,e,a,b,c,57); 
	R3(c,d,e,a,b,58); 
	R3(b,c,d,e,a,59);

	R4(a,b,c,d,e,60); 
	R4(e,a,b,c,d,61); 
	R4(d,e,a,b,c,62); 
	R4(c,d,e,a,b,63);
	R4(b,c,d,e,a,64); 
	R4(a,b,c,d,e,65); 
	R4(e,a,b,c,d,66); 
	R4(d,e,a,b,c,67);
	b+=((d&(e^a))^a)+blk(68)+0xCA62C1D6+rol(c,5);
	d=rol(d,30);

	R4(b,c,d,e,a,69); 
	R4(a,b,c,d,e,70); 
	R4(e,a,b,c,d,71);
	R4(d,e,a,b,c,72); 
	R4(c,d,e,a,b,73); 
	R4(b,c,d,e,a,74); 
	R4(a,b,c,d,e,75);
	R4(e,a,b,c,d,76); 
	R4(d,e,a,b,c,77); 
	R4(c,d,e,a,b,78); 
	R4(b,c,d,e,a,79);

	/* Add the working vars back into context.state[] */
	state[0] += a;
	state[1] += b;
	state[2] += c;
	state[3] += d;
	state[4] += e;
	/* Wipe variables */
	a = b = c = d = e = 0;
}


/* SHA1_Init - Initialize new context */
void SHA1_Init(SHA_CTX* context) {
	/* SHA1 initialization constants */
	context->state[0] = 0x32107654;
	context->state[1] = 0x23016745;
	context->state[2] = 0xc4e680a2;
	context->state[3] = 0xdc679823;
	context->state[4] = 0xd0857a34;
	context->count[0] = context->count[1] = 0;
}

/* Run your data through this. */
void SHA1_Update(SHA_CTX *context, sha1_byte *data, unsigned int len) {
	unsigned int	i, j;

	j = (context->count[0] >> 3) & 63;
	if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
	context->count[1] += (len >> 29);
	if ((j + len) > 63) {
	    memcpy(&context->buffer[j], data, (i = 64-j));
	    SHA1_Transform(context->state, context->buffer);
	    for ( ; i + 63 < len; i += 64) {
	        SHA1_Transform(context->state, &data[i]);
	    }
	    j = 0;
	}
	else i = 0;
	memcpy(&context->buffer[j], &data[i], len - i);
}


/* Add padding and return the message digest. */
void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX *context) {
	sha1_quadbyte	i, j;
	sha1_byte	finalcount[8];

	for (i = 0; i < 8; i++) {
	    finalcount[i] = (sha1_byte)((context->count[(i >= 4 ? 0 : 1)]
	     >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
	}
	SHA1_Update(context, (sha1_byte *)"\200", 1);
	while ((context->count[0] & 504) != 448) {
	    SHA1_Update(context, (sha1_byte *)"\0", 1);
	}
	/* Should cause a SHA1_Transform() */
	SHA1_Update(context, finalcount, 8);
	for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
	    digest[i] = (sha1_byte)
	     ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
	}
	/* Wipe variables */
	i = j = 0;
	memset(context->buffer, 0, SHA1_BLOCK_LENGTH);
	memset(context->state, 0, SHA1_DIGEST_LENGTH);
	memset(context->count, 0, 8);
	memset(&finalcount, 0, 8);
}

lib/sha/sha.h0000644000175000017500000000355110051221762011537 0ustar  kyzkyz/*
 * sha.h
 *
 * Originally taken from the public domain SHA1 implementation
 * written by by Steve Reid 
 * 
 * Modified by Aaron D. Gifford 
 *
 * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN
 *
 * The original unmodified version is available at:
 *    ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef __SHA1_H__
#define __SHA1_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Make sure you define these types for your architecture: */
typedef unsigned int sha1_quadbyte;	/* 4 byte type */
typedef unsigned char sha1_byte;	/* single byte type */

#define SHA1_BLOCK_LENGTH	64
#define SHA1_DIGEST_LENGTH	20

/* The SHA1 structure: */
typedef struct _SHA_CTX {
	sha1_quadbyte	state[5];
	sha1_quadbyte	count[2];
	sha1_byte	buffer[SHA1_BLOCK_LENGTH];
} SHA_CTX;

#ifndef NOPROTO
void SHA1_Init(SHA_CTX *context);
void SHA1_Update(SHA_CTX *context, sha1_byte *data, unsigned int len);
void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX* context);
#else
void SHA1_Init();
void SHA1_Update();
void SHA1_Final();
#endif

#ifdef	__cplusplus
}
#endif

#endif

README0000644000175000017500000000254710071436762010170 0ustar  kyzkyz
This is the source code package of ConvertLIT 1.8

In this version, most of the outstanding bug reports should be resolved:
  - Cope with buggy generators which write out truncated files 
  - Added dictionary support (disable with -d flag) 
  - Handle duplicated filenames 
  - Completely changed whitespace expansion 
    (Thanks to all the help from people who reported this problem)
  - Fixed very rare LZX Decompression buffer overruns 
  - Should work on MAC OS/X and other big endian hardware
    (If not, send a bug report)
  - Better non-english filename support 

DRM5 support is enhanced in this version

  DRM5 support is now implemented through a "keys.txt" file which should 
  contain the private keys from your activation components.  You create this
  file once with "ReaderKeyRecoveryTool.exe" from the Windows binary package
  and from then on you can use those keys under any environment.

Build changes:

This package now requires "LIBTOMMATH" from math.libtomcrypt.org

To build under Linux:
	cd lib
	make
	cd ../clit16
	make

To build under Windows, Visual C*.
	cd lib
	nmake -f win32.mak
	cd ..\clit16
	nmake -f win32.mak

Now that VC++ 7 is available for "free", supporting MINGW is left as an 
exercise for the interested reader.  Note that getting the free VC++ to the 
point where it can compile ConvertLIT isn't trivial and no support 
will be provided.